Unnamed repository; edit this file 'description' to name the repository.
Compute layout for pattern types
| -rw-r--r-- | crates/hir-ty/src/layout.rs | 169 | ||||
| -rw-r--r-- | crates/hir-ty/src/layout/tests.rs | 1 | ||||
| -rw-r--r-- | crates/hir-ty/src/lower.rs | 7 | ||||
| -rw-r--r-- | crates/hir-ty/src/mir/eval.rs | 20 | ||||
| -rw-r--r-- | crates/hir-ty/src/next_solver/consts/valtree.rs | 29 | ||||
| -rw-r--r-- | crates/hir-ty/src/variance.rs | 26 |
6 files changed, 231 insertions, 21 deletions
diff --git a/crates/hir-ty/src/layout.rs b/crates/hir-ty/src/layout.rs index 78df93311e..2b0a93e61d 100644 --- a/crates/hir-ty/src/layout.rs +++ b/crates/hir-ty/src/layout.rs @@ -10,12 +10,12 @@ use hir_def::{ use la_arena::{Idx, RawIdx}; use rustc_abi::{ - AddressSpace, Float, Integer, LayoutCalculator, Primitive, ReprOptions, Scalar, StructKind, - TargetDataLayout, WrappingRange, + AddressSpace, BackendRepr, FieldsShape, Float, Integer, LayoutCalculator, Niche, Primitive, + ReprOptions, Scalar, Size, StructKind, TargetDataLayout, WrappingRange, }; use rustc_index::IndexVec; use rustc_type_ir::{ - FloatTy, IntTy, UintTy, + FloatTy, IntTy, TypeVisitableExt as _, UintTy, inherent::{GenericArgs as _, IntoKind}, }; use triomphe::Arc; @@ -25,7 +25,8 @@ use crate::{ consteval::try_const_usize, db::HirDatabase, next_solver::{ - DbInterner, GenericArgs, StoredTy, Ty, TyKind, TypingMode, + Const, ConstKind, DbInterner, GenericArgs, PatternKind, StoredTy, Ty, TyKind, TypingMode, + ValueConst, infer::{DbInternerInferExt, traits::ObligationCause}, }, traits::StoredParamEnvAndCrate, @@ -348,8 +349,145 @@ pub fn layout_of_ty_query( return Err(LayoutError::NotImplemented); } - TyKind::Pat(_ty, _pat) => { - return Err(LayoutError::NotImplemented); + TyKind::Pat(ty, pat) => { + let mut layout = (*db.layout_of_ty(ty.store(), trait_env.clone())?).clone(); + match pat.kind() { + PatternKind::Range { start, end } => { + if let BackendRepr::Scalar(scalar) = &mut layout.backend_repr { + scalar.valid_range_mut().start = extract_const_value(start)? + .try_to_bits(db, trait_env.as_ref()) + .ok_or(LayoutError::Unknown)?; + + scalar.valid_range_mut().end = extract_const_value(end)? + .try_to_bits(db, trait_env.as_ref()) + .ok_or(LayoutError::Unknown)?; + + // FIXME(pattern_types): create implied bounds from pattern types in signatures + // that require that the range end is >= the range start so that we can't hit + // this error anymore without first having hit a trait solver error. + // Very fuzzy on the details here, but pattern types are an internal impl detail, + // so we can just go with this for now + if scalar.is_signed() { + let range = scalar.valid_range_mut(); + let start = layout.size.sign_extend(range.start); + let end = layout.size.sign_extend(range.end); + if end < start { + return Err(LayoutError::HasErrorType); + } + } else { + let range = scalar.valid_range_mut(); + if range.end < range.start { + return Err(LayoutError::HasErrorType); + } + }; + + let niche = Niche { + offset: Size::ZERO, + value: scalar.primitive(), + valid_range: scalar.valid_range(target), + }; + + layout.largest_niche = Some(niche); + } else { + panic!("pattern type with range but not scalar layout: {ty:?}, {layout:?}") + } + } + PatternKind::NotNull => { + if let BackendRepr::Scalar(scalar) | BackendRepr::ScalarPair(scalar, _) = + &mut layout.backend_repr + { + scalar.valid_range_mut().start = 1; + let niche = Niche { + offset: Size::ZERO, + value: scalar.primitive(), + valid_range: scalar.valid_range(target), + }; + + layout.largest_niche = Some(niche); + } else { + panic!( + "pattern type with `!null` pattern but not scalar/pair layout: {ty:?}, {layout:?}" + ) + } + } + + PatternKind::Or(variants) => match variants[0].kind() { + PatternKind::Range { .. } => { + if let BackendRepr::Scalar(scalar) = &mut layout.backend_repr { + let variants: Result<Vec<_>, _> = variants + .iter() + .map(|pat| match pat.kind() { + PatternKind::Range { start, end } => Ok::<_, LayoutError>(( + extract_const_value(start) + .unwrap() + .try_to_bits(db, trait_env.as_ref()) + .ok_or(LayoutError::Unknown)?, + extract_const_value(end) + .unwrap() + .try_to_bits(db, trait_env.as_ref()) + .ok_or(LayoutError::Unknown)?, + )), + PatternKind::NotNull | PatternKind::Or(_) => { + unreachable!("mixed or patterns are not allowed") + } + }) + .collect(); + let mut variants = variants?; + if !scalar.is_signed() { + return Err(LayoutError::HasErrorType); + } + variants.sort(); + if variants.len() != 2 { + return Err(LayoutError::HasErrorType); + } + + // first is the one starting at the signed in range min + let mut first = variants[0]; + let mut second = variants[1]; + if second.0 + == layout.size.truncate(layout.size.signed_int_min() as u128) + { + (second, first) = (first, second); + } + + if layout.size.sign_extend(first.1) >= layout.size.sign_extend(second.0) + { + return Err(LayoutError::HasErrorType); + } + if layout.size.signed_int_max() as u128 != second.1 { + return Err(LayoutError::HasErrorType); + } + + // Now generate a wrapping range (which aren't allowed in surface syntax). + scalar.valid_range_mut().start = second.0; + scalar.valid_range_mut().end = first.1; + + let niche = Niche { + offset: Size::ZERO, + value: scalar.primitive(), + valid_range: scalar.valid_range(target), + }; + + layout.largest_niche = Some(niche); + } else { + panic!( + "pattern type with range but not scalar layout: {ty:?}, {layout:?}" + ) + } + } + PatternKind::NotNull => panic!("or patterns can't contain `!null` patterns"), + PatternKind::Or(..) => panic!("patterns cannot have nested or patterns"), + }, + } + // Pattern types contain their base as their sole field. + // This allows the rest of the compiler to process pattern types just like + // single field transparent Adts, and only the parts of the compiler that + // specifically care about pattern types will have to handle it. + layout.fields = FieldsShape::Arbitrary { + offsets: [Size::ZERO].into_iter().collect(), + in_memory_order: [RustcFieldIdx::new(0)].into_iter().collect(), + }; + layout } TyKind::UnsafeBinder(_) => { return Err(LayoutError::NotImplemented); @@ -376,6 +514,25 @@ pub(crate) fn layout_of_ty_cycle_result( Err(LayoutError::RecursiveTypeWithoutIndirection) } +fn extract_const_value<'db>(ct: Const<'db>) -> Result<ValueConst<'db>, LayoutError> { + match ct.kind() { + ConstKind::Value(cv) => Ok(cv), + ConstKind::Param(_) + | ConstKind::Expr(_) + | ConstKind::Unevaluated(_) + | ConstKind::Infer(_) + | ConstKind::Bound(..) + | ConstKind::Placeholder(_) => { + if ct.has_param() { + Err(LayoutError::HasPlaceholder) + } else { + Err(LayoutError::Unknown) + } + } + ConstKind::Error(_) => Err(LayoutError::HasErrorConst), + } +} + fn struct_tail_erasing_lifetimes<'a>(db: &'a dyn HirDatabase, pointee: Ty<'a>) -> Ty<'a> { match pointee.kind() { TyKind::Adt(def, args) => { diff --git a/crates/hir-ty/src/layout/tests.rs b/crates/hir-ty/src/layout/tests.rs index bc18f05790..482945f5f0 100644 --- a/crates/hir-ty/src/layout/tests.rs +++ b/crates/hir-ty/src/layout/tests.rs @@ -529,7 +529,6 @@ fn tuple_ptr_with_dst_tail() { } #[test] -#[ignore = "FIXME: We need to have proper pattern types"] fn non_zero_and_non_null() { size_and_align! { minicore: non_zero, non_null, option; diff --git a/crates/hir-ty/src/lower.rs b/crates/hir-ty/src/lower.rs index 64f3cbe06a..8beaa481b5 100644 --- a/crates/hir-ty/src/lower.rs +++ b/crates/hir-ty/src/lower.rs @@ -542,7 +542,6 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { &TypeRef::PatternType(ty, pat) => { let ty = self.lower_ty(ty); let Some(pat) = self.lower_pattern_type(pat, ty) else { - // FIXME: Report an error. return (self.types.types.error, res); }; Ty::new_pat(self.interner, ty, pat) @@ -568,7 +567,11 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { ) .ok()?, ), - _ => return None, + hir_def::hir::Pat::Missing => return None, + _ => { + never!("pattern type can only be Range, NotNull or Or"); + return None; + } }; Some(Pattern::new(self.interner, pat_kind)) } diff --git a/crates/hir-ty/src/mir/eval.rs b/crates/hir-ty/src/mir/eval.rs index e080d3d713..a8879521eb 100644 --- a/crates/hir-ty/src/mir/eval.rs +++ b/crates/hir-ty/src/mir/eval.rs @@ -1709,15 +1709,19 @@ impl<'a, 'db: 'a> Evaluator<'a, 'db> { if let Some(it) = goal(kind) { return Ok(it); } - if let TyKind::Adt(adt_ef, subst) = kind - && let AdtId::StructId(struct_id) = adt_ef.def_id() - { - let field_types = self.db.field_types(struct_id.into()); - if let Some(ty) = - field_types.iter().last().map(|it| it.1.get().instantiate(self.interner(), subst)) - { - return self.coerce_unsized_look_through_fields(ty.skip_norm_wip(), goal); + match kind { + TyKind::Adt(adt_ef, subst) if let AdtId::StructId(struct_id) = adt_ef.def_id() => { + let field_types = self.db.field_types(struct_id.into()); + if let Some(ty) = field_types + .iter() + .last() + .map(|it| it.1.get().instantiate(self.interner(), subst)) + { + return self.coerce_unsized_look_through_fields(ty.skip_norm_wip(), goal); + } } + TyKind::Pat(ty, _) => return self.coerce_unsized_look_through_fields(ty, goal), + _ => (), } Err(MirEvalError::CoerceUnsizedError(ty.store())) } diff --git a/crates/hir-ty/src/next_solver/consts/valtree.rs b/crates/hir-ty/src/next_solver/consts/valtree.rs index b856ee5a85..bef2386706 100644 --- a/crates/hir-ty/src/next_solver/consts/valtree.rs +++ b/crates/hir-ty/src/next_solver/consts/valtree.rs @@ -8,6 +8,7 @@ use stdx::never; use crate::{ MemoryMap, ParamEnvAndCrate, consteval, + db::HirDatabase, mir::pad16, next_solver::{Const, Consts, TyKind, WorldExposer}, }; @@ -32,6 +33,34 @@ impl<'db> ValueConst<'db> { let value = ValTree::new(kind); ValueConst { ty, value } } + + /// Attempts to convert to a `ValTreeKind::Leaf` value. + pub fn try_to_leaf(self) -> Option<ScalarInt> { + match self.value.inner() { + ValTreeKind::Leaf(s) => Some(*s), + ValTreeKind::Branch(_) => None, + } + } + + /// Attempts to extract the raw bits from the constant. + /// + /// Fails if the value can't be represented as bits (e.g. because it is a reference + /// or an aggregate). + #[inline] + pub fn try_to_bits( + self, + db: &'db dyn HirDatabase, + param_env: ParamEnvAndCrate<'db>, + ) -> Option<u128> { + let (TyKind::Bool | TyKind::Char | TyKind::Uint(_) | TyKind::Int(_) | TyKind::Float(_)) = + self.ty.kind() + else { + return None; + }; + let scalar = self.try_to_leaf()?; + let size = db.layout_of_ty(self.ty.store(), param_env.store()).ok()?.size; + Some(scalar.to_bits(size)) + } } pub(super) fn allocation_to_const<'db>( diff --git a/crates/hir-ty/src/variance.rs b/crates/hir-ty/src/variance.rs index 39d51dcee0..49dacc16eb 100644 --- a/crates/hir-ty/src/variance.rs +++ b/crates/hir-ty/src/variance.rs @@ -25,8 +25,8 @@ use crate::{ db::HirDatabase, generics::{Generics, generics}, next_solver::{ - Const, ConstKind, DbInterner, ExistentialPredicate, GenericArgKind, GenericArgs, Region, - RegionKind, StoredVariancesOf, TermKind, Ty, TyKind, VariancesOf, + Const, ConstKind, DbInterner, ExistentialPredicate, GenericArgKind, GenericArgs, Pattern, + PatternKind, Region, RegionKind, StoredVariancesOf, TermKind, Ty, TyKind, VariancesOf, }, }; @@ -249,17 +249,35 @@ impl<'db> Context<'db> { // we encounter this when walking the trait references for object // types, where we use Error as the Self type } + TyKind::Pat(typ, pat) => { + self.add_constraints_from_pat(pat); + self.add_constraints_from_ty(typ, variance); + } TyKind::Bound(..) => {} TyKind::CoroutineWitness(..) | TyKind::Placeholder(..) | TyKind::Infer(..) - | TyKind::UnsafeBinder(..) - | TyKind::Pat(..) => { + | TyKind::UnsafeBinder(..) => { never!("unexpected type encountered in variance inference: {:?}", ty) } } } + fn add_constraints_from_pat(&mut self, pat: Pattern<'db>) { + match pat.kind() { + PatternKind::Range { start, end } => { + self.add_constraints_from_const(start); + self.add_constraints_from_const(end); + } + PatternKind::NotNull => {} + PatternKind::Or(patterns) => { + for pat in patterns { + self.add_constraints_from_pat(pat) + } + } + } + } + fn add_constraints_from_invariant_args(&mut self, args: GenericArgs<'db>) { for k in args.iter() { match k.kind() { |