Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir-ty/src/mir/lower.rs')
| -rw-r--r-- | crates/hir-ty/src/mir/lower.rs | 191 |
1 files changed, 146 insertions, 45 deletions
diff --git a/crates/hir-ty/src/mir/lower.rs b/crates/hir-ty/src/mir/lower.rs index 4fc3c67a6e..d36d9e946c 100644 --- a/crates/hir-ty/src/mir/lower.rs +++ b/crates/hir-ty/src/mir/lower.rs @@ -4,16 +4,17 @@ use std::{iter, mem, sync::Arc}; use chalk_ir::{BoundVar, ConstData, DebruijnIndex, TyKind}; use hir_def::{ + adt::VariantData, body::Body, expr::{ Array, BindingAnnotation, BindingId, ExprId, LabelId, Literal, MatchArm, Pat, PatId, - RecordLitField, + RecordFieldPat, RecordLitField, }, lang_item::{LangItem, LangItemTarget}, layout::LayoutError, path::Path, resolver::{resolver_for_expr, ResolveValueResult, ValueNs}, - DefWithBodyId, EnumVariantId, HasModule, ItemContainerId, TraitId, + DefWithBodyId, EnumVariantId, HasModule, ItemContainerId, LocalFieldId, TraitId, }; use hir_expand::name::Name; use la_arena::ArenaMap; @@ -106,6 +107,12 @@ impl MirLowerError { type Result<T> = std::result::Result<T, MirLowerError>; +enum AdtPatternShape<'a> { + Tuple { args: &'a [PatId], ellipsis: Option<usize> }, + Record { args: &'a [RecordFieldPat] }, + Unit, +} + impl MirLowerCtx<'_> { fn temp(&mut self, ty: Ty) -> Result<LocalId> { if matches!(ty.kind(Interner), TyKind::Slice(_) | TyKind::Dyn(_)) { @@ -444,7 +451,8 @@ impl MirLowerCtx<'_> { current, pat.into(), Some(end), - &[pat], &None)?; + AdtPatternShape::Tuple { args: &[pat], ellipsis: None }, + )?; if let Some((_, block)) = this.lower_expr_as_place(current, body, true)? { this.set_goto(block, begin); } @@ -573,7 +581,17 @@ impl MirLowerCtx<'_> { Ok(None) } Expr::Yield { .. } => not_supported!("yield"), - Expr::RecordLit { fields, path, .. } => { + Expr::RecordLit { fields, path, spread, ellipsis: _, is_assignee_expr: _ } => { + let spread_place = match spread { + &Some(x) => { + let Some((p, c)) = self.lower_expr_as_place(current, x, true)? else { + return Ok(None); + }; + current = c; + Some(p) + }, + None => None, + }; let variant_id = self .infer .variant_resolution_for_expr(expr_id) @@ -603,9 +621,24 @@ impl MirLowerCtx<'_> { place, Rvalue::Aggregate( AggregateKind::Adt(variant_id, subst), - operands.into_iter().map(|x| x).collect::<Option<_>>().ok_or( - MirLowerError::TypeError("missing field in record literal"), - )?, + match spread_place { + Some(sp) => operands.into_iter().enumerate().map(|(i, x)| { + match x { + Some(x) => x, + None => { + let mut p = sp.clone(); + p.projection.push(ProjectionElem::Field(FieldId { + parent: variant_id, + local_id: LocalFieldId::from_raw(RawIdx::from(i as u32)), + })); + Operand::Copy(p) + }, + } + }).collect(), + None => operands.into_iter().map(|x| x).collect::<Option<_>>().ok_or( + MirLowerError::TypeError("missing field in record literal"), + )?, + }, ), expr_id.into(), ); @@ -1021,14 +1054,11 @@ impl MirLowerCtx<'_> { self.pattern_match_tuple_like( current, current_else, - args.iter().enumerate().map(|(i, x)| { - ( - PlaceElem::TupleField(i), - *x, - subst.at(Interner, i).assert_ty_ref(Interner).clone(), - ) - }), + args, *ellipsis, + subst.iter(Interner).enumerate().map(|(i, x)| { + (PlaceElem::TupleField(i), x.assert_ty_ref(Interner).clone()) + }), &cond_place, binding_mode, )? @@ -1062,7 +1092,21 @@ impl MirLowerCtx<'_> { } (then_target, current_else) } - Pat::Record { .. } => not_supported!("record pattern"), + Pat::Record { args, .. } => { + let Some(variant) = self.infer.variant_resolution_for_pat(pattern) else { + not_supported!("unresolved variant"); + }; + self.pattern_matching_variant( + cond_ty, + binding_mode, + cond_place, + variant, + current, + pattern.into(), + current_else, + AdtPatternShape::Record { args: &*args }, + )? + } Pat::Range { .. } => not_supported!("range pattern"), Pat::Slice { .. } => not_supported!("slice pattern"), Pat::Path(_) => { @@ -1077,8 +1121,7 @@ impl MirLowerCtx<'_> { current, pattern.into(), current_else, - &[], - &None, + AdtPatternShape::Unit, )? } Pat::Lit(l) => { @@ -1160,8 +1203,7 @@ impl MirLowerCtx<'_> { current, pattern.into(), current_else, - args, - ellipsis, + AdtPatternShape::Tuple { args, ellipsis: *ellipsis }, )? } Pat::Ref { .. } => not_supported!("& pattern"), @@ -1179,15 +1221,13 @@ impl MirLowerCtx<'_> { current: BasicBlockId, span: MirSpan, current_else: Option<BasicBlockId>, - args: &[PatId], - ellipsis: &Option<usize>, + shape: AdtPatternShape<'_>, ) -> Result<(BasicBlockId, Option<BasicBlockId>)> { pattern_matching_dereference(&mut cond_ty, &mut binding_mode, &mut cond_place); let subst = match cond_ty.kind(Interner) { TyKind::Adt(_, s) => s, _ => return Err(MirLowerError::TypeError("non adt type matched with tuple struct")), }; - let fields_type = self.db.field_types(variant); Ok(match variant { VariantId::EnumVariantId(v) => { let e = self.db.const_eval_discriminant(v)? as u128; @@ -1208,57 +1248,99 @@ impl MirLowerCtx<'_> { }, ); let enum_data = self.db.enum_data(v.parent); - let fields = - enum_data.variants[v.local_id].variant_data.fields().iter().map(|(x, _)| { - ( - PlaceElem::Field(FieldId { parent: v.into(), local_id: x }), - fields_type[x].clone().substitute(Interner, subst), - ) - }); - self.pattern_match_tuple_like( + self.pattern_matching_variant_fields( + shape, + &enum_data.variants[v.local_id].variant_data, + variant, + subst, next, Some(else_target), - args.iter().zip(fields).map(|(x, y)| (y.0, *x, y.1)), - *ellipsis, &cond_place, binding_mode, )? } VariantId::StructId(s) => { let struct_data = self.db.struct_data(s); - let fields = struct_data.variant_data.fields().iter().map(|(x, _)| { + self.pattern_matching_variant_fields( + shape, + &struct_data.variant_data, + variant, + subst, + current, + current_else, + &cond_place, + binding_mode, + )? + } + VariantId::UnionId(_) => { + return Err(MirLowerError::TypeError("pattern matching on union")) + } + }) + } + + fn pattern_matching_variant_fields( + &mut self, + shape: AdtPatternShape<'_>, + variant_data: &VariantData, + v: VariantId, + subst: &Substitution, + current: BasicBlockId, + current_else: Option<BasicBlockId>, + cond_place: &Place, + binding_mode: BindingAnnotation, + ) -> Result<(BasicBlockId, Option<BasicBlockId>)> { + let fields_type = self.db.field_types(v); + Ok(match shape { + AdtPatternShape::Record { args } => { + let it = args + .iter() + .map(|x| { + let field_id = + variant_data.field(&x.name).ok_or(MirLowerError::UnresolvedField)?; + Ok(( + PlaceElem::Field(FieldId { parent: v.into(), local_id: field_id }), + x.pat, + fields_type[field_id].clone().substitute(Interner, subst), + )) + }) + .collect::<Result<Vec<_>>>()?; + self.pattern_match_adt( + current, + current_else, + it.into_iter(), + cond_place, + binding_mode, + )? + } + AdtPatternShape::Tuple { args, ellipsis } => { + let fields = variant_data.fields().iter().map(|(x, _)| { ( - PlaceElem::Field(FieldId { parent: s.into(), local_id: x }), + PlaceElem::Field(FieldId { parent: v.into(), local_id: x }), fields_type[x].clone().substitute(Interner, subst), ) }); self.pattern_match_tuple_like( current, current_else, - args.iter().zip(fields).map(|(x, y)| (y.0, *x, y.1)), - *ellipsis, - &cond_place, + args, + ellipsis, + fields, + cond_place, binding_mode, )? } - VariantId::UnionId(_) => { - return Err(MirLowerError::TypeError("pattern matching on union")) - } + AdtPatternShape::Unit => (current, current_else), }) } - fn pattern_match_tuple_like( + fn pattern_match_adt( &mut self, mut current: BasicBlockId, mut current_else: Option<BasicBlockId>, args: impl Iterator<Item = (PlaceElem, PatId, Ty)>, - ellipsis: Option<usize>, cond_place: &Place, binding_mode: BindingAnnotation, ) -> Result<(BasicBlockId, Option<BasicBlockId>)> { - if ellipsis.is_some() { - not_supported!("tuple like pattern with ellipsis"); - } for (proj, arg, ty) in args { let mut cond_place = cond_place.clone(); cond_place.projection.push(proj); @@ -1268,6 +1350,25 @@ impl MirLowerCtx<'_> { Ok((current, current_else)) } + fn pattern_match_tuple_like( + &mut self, + current: BasicBlockId, + current_else: Option<BasicBlockId>, + args: &[PatId], + ellipsis: Option<usize>, + fields: impl DoubleEndedIterator<Item = (PlaceElem, Ty)> + Clone, + cond_place: &Place, + binding_mode: BindingAnnotation, + ) -> Result<(BasicBlockId, Option<BasicBlockId>)> { + let (al, ar) = args.split_at(ellipsis.unwrap_or(args.len())); + let it = al + .iter() + .zip(fields.clone()) + .chain(ar.iter().rev().zip(fields.rev())) + .map(|(x, y)| (y.0, *x, y.1)); + self.pattern_match_adt(current, current_else, it, cond_place, binding_mode) + } + fn discr_temp_place(&mut self) -> Place { match &self.discr_temp { Some(x) => x.clone(), |