Unnamed repository; edit this file 'description' to name the repository.
Compute layout for pattern types
Chayim Refael Friedman 3 weeks ago
parent d1253ff · commit 92bcb18
-rw-r--r--crates/hir-ty/src/layout.rs169
-rw-r--r--crates/hir-ty/src/layout/tests.rs1
-rw-r--r--crates/hir-ty/src/lower.rs7
-rw-r--r--crates/hir-ty/src/mir/eval.rs20
-rw-r--r--crates/hir-ty/src/next_solver/consts/valtree.rs29
-rw-r--r--crates/hir-ty/src/variance.rs26
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() {