From 975109051caf05de6d166779589e299d9aca9cd5 Mon Sep 17 00:00:00 2001 From: Dawer <7803845+iDawer@users.noreply.github.com> Date: Thu, 6 May 2021 09:42:00 +0500 Subject: Basic lowering hir_def::exrp::Pat -> typed HIR. Pattern arena is broken --- crates/hir_ty/src/diagnostics/pattern.rs | 191 +++++++++++++++++++++++++++++++ 1 file changed, 191 insertions(+) (limited to 'crates/hir_ty/src/diagnostics/pattern.rs') diff --git a/crates/hir_ty/src/diagnostics/pattern.rs b/crates/hir_ty/src/diagnostics/pattern.rs index 044506d66..6646826b3 100644 --- a/crates/hir_ty/src/diagnostics/pattern.rs +++ b/crates/hir_ty/src/diagnostics/pattern.rs @@ -2,8 +2,199 @@ #![allow(unused)] // todo remove mod deconstruct_pat; +// TODO: find a better place for this? +mod pat_util; pub mod usefulness; +use hir_def::{body::Body, EnumVariantId, LocalFieldId, VariantId}; +use la_arena::Idx; + +use crate::{db::HirDatabase, AdtId, InferenceResult, Interner, Substitution, Ty, TyKind}; + +use self::{deconstruct_pat::ToDo, pat_util::EnumerateAndAdjustIterator}; + +pub type PatId = Idx; + +#[derive(Clone, Debug)] +pub(crate) enum PatternError { + Unimplemented, +} + +#[derive(Clone, Debug, PartialEq)] +pub struct FieldPat { + pub field: LocalFieldId, + pub pattern: Pat, +} + +#[derive(Clone, Debug, PartialEq)] +pub struct Pat { + pub ty: Ty, + pub kind: Box, +} + +impl Pat { + pub(crate) fn wildcard_from_ty(ty: &Ty) -> Self { + Pat { ty: ty.clone(), kind: Box::new(PatKind::Wild) } + } +} + +#[derive(Clone, Debug, PartialEq)] +pub enum PatKind { + Wild, + + /// `x`, `ref x`, `x @ P`, etc. + Binding { + subpattern: Option, + // todo: ToDo, + }, + + /// `Foo(...)` or `Foo{...}` or `Foo`, where `Foo` is a variant name from an ADT with + /// multiple variants. + Variant { + substs: Substitution, + enum_variant: EnumVariantId, + subpatterns: Vec, + }, + + /// `(...)`, `Foo(...)`, `Foo{...}`, or `Foo`, where `Foo` is a variant name from an ADT with + /// a single variant. + Leaf { + subpatterns: Vec, + }, + + /// `box P`, `&P`, `&mut P`, etc. + Deref { + subpattern: Pat, + }, + + /// An or-pattern, e.g. `p | q`. + /// Invariant: `pats.len() >= 2`. + Or { + pats: Vec, + }, +} + +pub(crate) struct PatCtxt<'a> { + db: &'a dyn HirDatabase, + infer: &'a InferenceResult, + body: &'a Body, + pub(crate) errors: Vec, +} + +impl<'a> PatCtxt<'a> { + pub(crate) fn new(db: &'a dyn HirDatabase, infer: &'a InferenceResult, body: &'a Body) -> Self { + Self { db, infer, body, errors: Vec::new() } + } + + pub(crate) fn lower_pattern(&mut self, pat: hir_def::expr::PatId) -> Pat { + // TODO: pattern adjustments (implicit dereference) + // More info https://github.com/rust-lang/rust/issues/42640#issuecomment-313535089 + let unadjusted_pat = self.lower_pattern_unadjusted(pat); + unadjusted_pat + } + + fn lower_pattern_unadjusted(&mut self, pat: hir_def::expr::PatId) -> Pat { + let ty = &self.infer[pat]; + + let kind = match self.body[pat] { + hir_def::expr::Pat::Wild => PatKind::Wild, + + hir_def::expr::Pat::Tuple { ref args, ellipsis } => { + let arity = match *ty.kind(&Interner) { + TyKind::Tuple(arity, _) => arity, + _ => panic!("unexpected type for tuple pattern: {:?}", ty), + }; + let subpatterns = self.lower_tuple_subpats(args, arity, ellipsis); + PatKind::Leaf { subpatterns } + } + + hir_def::expr::Pat::TupleStruct { ref args, ellipsis, .. } => { + let variant_data = match self.infer.variant_resolution_for_pat(pat) { + Some(variant_id) => variant_id.variant_data(self.db.upcast()), + None => panic!("tuple struct pattern not applied to an ADT {:?}", ty), + }; + let subpatterns = + self.lower_tuple_subpats(args, variant_data.fields().len(), ellipsis); + self.lower_variant_or_leaf(pat, ty, subpatterns) + } + + hir_def::expr::Pat::Record { ref args, .. } => { + let variant_data = match self.infer.variant_resolution_for_pat(pat) { + Some(variant_id) => variant_id.variant_data(self.db.upcast()), + None => panic!("record pattern not applied to an ADT {:?}", ty), + }; + let subpatterns = args + .iter() + .map(|field| FieldPat { + // XXX(iDawer): field lookup is inefficient + field: variant_data.field(&field.name).unwrap_or_else(|| todo!()), + pattern: self.lower_pattern(field.pat), + }) + .collect(); + self.lower_variant_or_leaf(pat, ty, subpatterns) + } + + hir_def::expr::Pat::Or(ref pats) => PatKind::Or { pats: self.lower_patterns(pats) }, + + _ => { + self.errors.push(PatternError::Unimplemented); + PatKind::Wild + } + }; + + Pat { ty: ty.clone(), kind: Box::new(kind) } + } + + fn lower_tuple_subpats( + &mut self, + pats: &[hir_def::expr::PatId], + expected_len: usize, + ellipsis: Option, + ) -> Vec { + pats.iter() + .enumerate_and_adjust(expected_len, ellipsis) + .map(|(i, &subpattern)| FieldPat { + field: LocalFieldId::from_raw((i as u32).into()), + pattern: self.lower_pattern(subpattern), + }) + .collect() + } + + fn lower_patterns(&mut self, pats: &[hir_def::expr::PatId]) -> Vec { + pats.iter().map(|&p| self.lower_pattern(p)).collect() + } + + fn lower_variant_or_leaf( + &mut self, + pat: hir_def::expr::PatId, + ty: &Ty, + subpatterns: Vec, + ) -> PatKind { + let kind = match self.infer.variant_resolution_for_pat(pat) { + Some(variant_id) => { + if let VariantId::EnumVariantId(enum_variant) = variant_id { + let substs = match ty.kind(&Interner) { + TyKind::Adt(_, substs) | TyKind::FnDef(_, substs) => substs.clone(), + TyKind::Error => { + return PatKind::Wild; + } + _ => panic!("inappropriate type for def: {:?}", ty), + }; + PatKind::Variant { substs, enum_variant, subpatterns } + } else { + PatKind::Leaf { subpatterns } + } + } + None => { + self.errors.push(PatternError::Unimplemented); + PatKind::Wild + } + }; + // TODO: do we need PatKind::AscribeUserType ? + kind + } +} + #[cfg(test)] mod tests { use crate::diagnostics::tests::check_diagnostics; -- cgit v1.2.3