Unnamed repository; edit this file 'description' to name the repository.
Merge pull request #22368 from ChayimFriedman2/pat-ty
fix: Fully support pattern types
Chayim Refael Friedman 3 weeks ago
parent f05800d · parent 92bcb18 · commit 1dd030d
-rw-r--r--crates/hir-def/src/expr_store/lower.rs93
-rw-r--r--crates/hir-def/src/expr_store/pretty.rs1
-rw-r--r--crates/hir-def/src/lang_item.rs14
-rw-r--r--crates/hir-def/src/resolver.rs7
-rw-r--r--crates/hir-ty/src/consteval.rs10
-rw-r--r--crates/hir-ty/src/infer/path.rs25
-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.rs46
-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
-rw-r--r--crates/intern/src/symbol/symbols.rs3
-rw-r--r--crates/test-utils/src/minicore.rs48
14 files changed, 432 insertions, 60 deletions
diff --git a/crates/hir-def/src/expr_store/lower.rs b/crates/hir-def/src/expr_store/lower.rs
index 76503ac97c..33161c503e 100644
--- a/crates/hir-def/src/expr_store/lower.rs
+++ b/crates/hir-def/src/expr_store/lower.rs
@@ -35,8 +35,8 @@ use thin_vec::ThinVec;
use tt::TextRange;
use crate::{
- AdtId, BlockId, BlockLoc, DefWithBodyId, FunctionId, GenericDefId, ImplId, ItemContainerId,
- MacroId, ModuleDefId, ModuleId, TraitId, TypeAliasId, UnresolvedMacro,
+ AdtId, BlockId, BlockLoc, ConstId, DefWithBodyId, FunctionId, GenericDefId, ImplId,
+ ItemContainerId, MacroId, ModuleDefId, ModuleId, TraitId, TypeAliasId, UnresolvedMacro,
attrs::AttrFlags,
db::DefDatabase,
expr_store::{
@@ -721,7 +721,7 @@ impl<'db> ExprCollector<'db> {
),
ast::Type::PatternType(inner) => TypeRef::PatternType(
self.lower_type_ref_opt(inner.ty(), impl_trait_lower_fn),
- self.collect_pat_top(inner.pat()),
+ self.collect_ty_pat_opt(inner.pat()),
),
ast::Type::MacroType(mt) => match mt.macro_call() {
Some(mcall) => {
@@ -2912,6 +2912,93 @@ impl<'db> ExprCollector<'db> {
}
}
+ fn collect_ty_pat_opt(&mut self, pat: Option<ast::Pat>) -> PatId {
+ match pat {
+ Some(pat) => self.collect_ty_pat(pat),
+ None => self.missing_pat(),
+ }
+ }
+
+ fn collect_ty_pat(&mut self, pat: ast::Pat) -> PatId {
+ let ptr = AstPtr::new(&pat);
+ match pat {
+ ast::Pat::NotNull(_) => self.alloc_pat(Pat::NotNull, ptr),
+ ast::Pat::OrPat(pat) => {
+ let pat = pat.pats().map(|pat| self.collect_ty_pat(pat)).collect();
+ self.alloc_pat(Pat::Or(pat), ptr)
+ }
+ ast::Pat::RangePat(range_pat) => {
+ let start = range_pat
+ .start()
+ .map(|pat| {
+ self.with_fresh_binding_expr_root(|this| this.lower_ty_pat_range_side(pat))
+ })
+ .unwrap_or_else(|| self.lower_ty_pat_range_end(self.lang_items().RangeMin));
+ let end = range_pat
+ .end()
+ .map(|pat| match range_pat.op_kind() {
+ Some(ast::RangeOp::Inclusive) | None => self
+ .with_fresh_binding_expr_root(|this| this.lower_ty_pat_range_side(pat)),
+ Some(ast::RangeOp::Exclusive) => self.lower_excluded_range_end(pat),
+ })
+ .unwrap_or_else(|| self.lower_ty_pat_range_end(self.lang_items().RangeMax));
+ self.alloc_pat(
+ Pat::Range {
+ start: Some(start),
+ end: Some(end),
+ range_type: ast::RangeOp::Inclusive,
+ },
+ ptr,
+ )
+ }
+ ast::Pat::MacroPat(pat) => {
+ let Some(call) = pat.macro_call() else { return self.missing_pat() };
+ let ptr = AstPtr::new(&call);
+ self.collect_macro_call(call, ptr, true, |this, pat| this.collect_ty_pat_opt(pat))
+ }
+ _ => {
+ // FIXME: Emit an error.
+ self.alloc_pat(Pat::Missing, ptr)
+ }
+ }
+ }
+
+ fn lower_ty_pat_range_side(&mut self, pat: ast::Pat) -> ExprId {
+ match &pat {
+ ast::Pat::LiteralPat(it) => {
+ let Some((literal, _)) = pat_literal_to_hir(it) else { return self.missing_expr() };
+ self.alloc_expr_from_pat(Expr::Literal(literal), AstPtr::new(&pat))
+ }
+ _ => self.missing_expr(),
+ }
+ }
+
+ /// When a range has no end specified (`1..` or `1..=`) or no start specified (`..5` or `..=5`),
+ /// we instead use a constant of the MAX/MIN of the type.
+ /// This way the type system does not have to handle the lack of a start/end.
+ fn lower_ty_pat_range_end(&mut self, lang_item: Option<ConstId>) -> ExprId {
+ self.with_fresh_binding_expr_root(|this| {
+ this.alloc_expr_desugared(
+ this.lang_path(lang_item).map(Expr::Path).unwrap_or(Expr::Missing),
+ )
+ })
+ }
+
+ /// Lowers the range end of an exclusive range (`2..5`) to an inclusive range 2..=(5 - 1).
+ /// This way the type system doesn't have to handle the distinction between inclusive/exclusive ranges.
+ fn lower_excluded_range_end(&mut self, pat: ast::Pat) -> ExprId {
+ self.with_fresh_binding_expr_root(|this| {
+ let excluded_end = this.lower_ty_pat_range_side(pat);
+ let range_sub_path =
+ this.lang_path(this.lang_items().RangeSub).map(Expr::Path).unwrap_or(Expr::Missing);
+ let range_sub_path = this.alloc_expr_desugared(range_sub_path);
+ this.alloc_expr_desugared(Expr::Call {
+ callee: range_sub_path,
+ args: Box::new([excluded_end]),
+ })
+ })
+ }
+
// endregion: patterns
/// Returns `false` (and emits diagnostics) when `owner` if `#[cfg]`d out, and `true` when
diff --git a/crates/hir-def/src/expr_store/pretty.rs b/crates/hir-def/src/expr_store/pretty.rs
index 4dc7ebebbb..35e3fc44c3 100644
--- a/crates/hir-def/src/expr_store/pretty.rs
+++ b/crates/hir-def/src/expr_store/pretty.rs
@@ -1135,6 +1135,7 @@ impl Printer<'_> {
LangItemTarget::TypeAliasId(it) => write_name!(it),
LangItemTarget::TraitId(it) => write_name!(it),
LangItemTarget::EnumVariantId(it) => write_name!(it),
+ LangItemTarget::ConstId(it) => write_name!(it),
}
if let Some(s) = s {
diff --git a/crates/hir-def/src/lang_item.rs b/crates/hir-def/src/lang_item.rs
index 483c3f16c6..f9d5a843bd 100644
--- a/crates/hir-def/src/lang_item.rs
+++ b/crates/hir-def/src/lang_item.rs
@@ -7,7 +7,7 @@ use intern::{Symbol, sym};
use stdx::impl_from;
use crate::{
- AdtId, AssocItemId, AttrDefId, Crate, EnumId, EnumVariantId, FunctionId, ImplId,
+ AdtId, AssocItemId, AttrDefId, ConstId, Crate, EnumId, EnumVariantId, FunctionId, ImplId,
ItemContainerId, MacroId, ModuleDefId, StaticId, StructId, TraitId, TypeAliasId, UnionId,
attrs::AttrFlags,
db::DefDatabase,
@@ -25,10 +25,11 @@ pub enum LangItemTarget {
TypeAliasId(TypeAliasId),
TraitId(TraitId),
EnumVariantId(EnumVariantId),
+ ConstId(ConstId),
}
impl_from!(
- EnumId, FunctionId, ImplId, StaticId, StructId, UnionId, TypeAliasId, TraitId, EnumVariantId for LangItemTarget
+ EnumId, FunctionId, ImplId, StaticId, StructId, UnionId, TypeAliasId, TraitId, EnumVariantId, ConstId for LangItemTarget
);
/// Salsa query. This will look for lang items in a specific crate.
@@ -51,7 +52,7 @@ pub fn crate_lang_items(db: &dyn DefDatabase, krate: Crate) -> Option<Box<LangIt
match assoc {
AssocItemId::FunctionId(f) => lang_items.collect_lang_item(db, f),
AssocItemId::TypeAliasId(t) => lang_items.collect_lang_item(db, t),
- AssocItemId::ConstId(_) => (),
+ AssocItemId::ConstId(c) => lang_items.collect_lang_item(db, c),
}
}
}
@@ -68,7 +69,7 @@ pub fn crate_lang_items(db: &dyn DefDatabase, krate: Crate) -> Option<Box<LangIt
AssocItemId::TypeAliasId(alias) => {
lang_items.collect_lang_item(db, alias)
}
- AssocItemId::ConstId(_) => {}
+ AssocItemId::ConstId(c) => lang_items.collect_lang_item(db, c),
}
});
}
@@ -93,6 +94,7 @@ pub fn crate_lang_items(db: &dyn DefDatabase, krate: Crate) -> Option<Box<LangIt
ModuleDefId::TypeAliasId(t) => {
lang_items.collect_lang_item(db, t);
}
+ ModuleDefId::ConstId(c) => lang_items.collect_lang_item(db, c),
_ => {}
}
}
@@ -646,6 +648,10 @@ language_item_table! { LangItems =>
FieldBase, sym::field_base, TypeAliasId;
FieldType, sym::field_type, TypeAliasId;
+ RangeMin, sym::RangeMin, ConstId;
+ RangeMax, sym::RangeMax, ConstId;
+ RangeSub, sym::RangeSub, FunctionId;
+
@non_lang_core_traits:
core::default, Default;
core::fmt, Debug;
diff --git a/crates/hir-def/src/resolver.rs b/crates/hir-def/src/resolver.rs
index 8320221ffc..0062e6c170 100644
--- a/crates/hir-def/src/resolver.rs
+++ b/crates/hir-def/src/resolver.rs
@@ -194,7 +194,8 @@ impl<'db> Resolver<'db> {
LangItemTarget::TraitId(it) => TypeNs::TraitId(it),
LangItemTarget::FunctionId(_)
| LangItemTarget::ImplId(_)
- | LangItemTarget::StaticId(_) => return None,
+ | LangItemTarget::StaticId(_)
+ | LangItemTarget::ConstId(_) => return None,
};
return Some((
type_ns,
@@ -337,6 +338,7 @@ impl<'db> Resolver<'db> {
LangItemTarget::StaticId(it) => ValueNs::StaticId(it),
LangItemTarget::StructId(it) => ValueNs::StructId(it),
LangItemTarget::EnumVariantId(it) => ValueNs::EnumVariantId(it),
+ LangItemTarget::ConstId(it) => ValueNs::ConstId(it),
LangItemTarget::UnionId(_)
| LangItemTarget::ImplId(_)
| LangItemTarget::TypeAliasId(_)
@@ -356,7 +358,8 @@ impl<'db> Resolver<'db> {
LangItemTarget::TraitId(it) => TypeNs::TraitId(it),
LangItemTarget::FunctionId(_)
| LangItemTarget::ImplId(_)
- | LangItemTarget::StaticId(_) => return None,
+ | LangItemTarget::StaticId(_)
+ | LangItemTarget::ConstId(_) => return None,
};
// Remaining segments start from 0 because lang paths have no segments other than the remaining.
return Some((
diff --git a/crates/hir-ty/src/consteval.rs b/crates/hir-ty/src/consteval.rs
index 9a6e2a87f2..fb52813c52 100644
--- a/crates/hir-ty/src/consteval.rs
+++ b/crates/hir-ty/src/consteval.rs
@@ -303,6 +303,7 @@ pub(crate) enum CreateConstError<'db> {
UsedForbiddenParam,
ResolveToNonConst,
DoesNotResolve,
+ ConstHasGenerics,
UnderscoreExpr,
TypeMismatch {
#[expect(unused, reason = "will need this for diagnostics")]
@@ -321,9 +322,11 @@ pub(crate) fn path_to_const<'a, 'db>(
let resolution = resolver
.resolve_path_in_value_ns_fully(db, path, HygieneId::ROOT)
.ok_or(CreateConstError::DoesNotResolve)?;
+ let no_generics = |def| crate::generics::generics(db, def).has_no_params();
let konst = match resolution {
- ValueNs::ConstId(id) => GeneralConstId::ConstId(id),
+ ValueNs::ConstId(id) if no_generics(id.into()) => GeneralConstId::ConstId(id),
ValueNs::StaticId(id) => GeneralConstId::StaticId(id),
+ ValueNs::ConstId(_) => return Err(CreateConstError::ConstHasGenerics),
ValueNs::GenericParam(param) => {
let index = generics().type_or_const_param_idx(param.into());
if forbid_params_after.is_some_and(|forbid_after| index >= forbid_after) {
@@ -363,7 +366,10 @@ pub(crate) fn create_anon_const<'a, 'db>(
Expr::Path(path)
if let konst =
path_to_const(interner.db, resolver, generics, forbid_params_after, path)
- && !matches!(konst, Err(CreateConstError::DoesNotResolve)) =>
+ && !matches!(
+ konst,
+ Err(CreateConstError::DoesNotResolve | CreateConstError::ConstHasGenerics)
+ ) =>
{
konst
}
diff --git a/crates/hir-ty/src/infer/path.rs b/crates/hir-ty/src/infer/path.rs
index 704f15cc86..1c3d93ae6e 100644
--- a/crates/hir-ty/src/infer/path.rs
+++ b/crates/hir-ty/src/infer/path.rs
@@ -183,7 +183,30 @@ impl<'db> InferenceContext<'_, 'db> {
match value_or_partial {
ResolveValueResult::ValueNs(it) => {
drop_ctx(ctx, no_diagnostics);
- (it, None)
+
+ let args = if let Path::LangItem(..) = path {
+ let def_and_container = match it {
+ ValueNs::ConstId(it) => Some((it.into(), it.loc(self.db).container)),
+ ValueNs::FunctionId(it) => Some((it.into(), it.loc(self.db).container)),
+ _ => None,
+ };
+ let def_and_container =
+ def_and_container.and_then(|(def, container)| match container {
+ ItemContainerId::ImplId(it) => Some((def, it.into())),
+ ItemContainerId::TraitId(it) => Some((def, it.into())),
+ ItemContainerId::ExternBlockId(_)
+ | ItemContainerId::ModuleId(_) => None,
+ });
+ def_and_container.map(|(def, container)| {
+ let args = self.infcx().fresh_args_for_item(id.into(), container);
+ self.write_assoc_resolution(id, def, args);
+ args
+ })
+ } else {
+ None
+ };
+
+ (it, args)
}
ResolveValueResult::Partial(def, remaining_index) => {
// there may be more intermediate segments between the resolved one and
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 ec42b8a349..8beaa481b5 100644
--- a/crates/hir-ty/src/lower.rs
+++ b/crates/hir-ty/src/lower.rs
@@ -19,7 +19,7 @@ use hir_def::{
builtin_type::BuiltinType,
expr_store::{ExpressionStore, path::Path},
hir::{
- ExprId,
+ ExprId, PatId,
generics::{
GenericParamDataRef, GenericParams, LocalTypeOrConstParamId, TypeOrConstParamData,
TypeParamProvenance, WherePredicate,
@@ -63,8 +63,8 @@ use crate::{
next_solver::{
AliasTy, Binder, BoundExistentialPredicates, Clause, ClauseKind, Clauses, Const, ConstKind,
DbInterner, DefaultAny, EarlyBinder, EarlyParamRegion, ErrorGuaranteed, FnSigKind,
- FxIndexMap, GenericArg, GenericArgs, ParamConst, ParamEnv, Pattern, PolyFnSig, Predicate,
- Region, StoredClauses, StoredEarlyBinder, StoredGenericArg, StoredGenericArgs,
+ FxIndexMap, GenericArg, GenericArgs, ParamConst, ParamEnv, PatList, Pattern, PolyFnSig,
+ Predicate, Region, StoredClauses, StoredEarlyBinder, StoredGenericArg, StoredGenericArgs,
StoredPolyFnSig, StoredTraitRef, StoredTy, TraitPredicate, TraitRef, Ty, Tys, Unnormalized,
abi::Safety, util::BottomUpFolder,
},
@@ -541,20 +541,9 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> {
}
&TypeRef::PatternType(ty, pat) => {
let ty = self.lower_ty(ty);
- // FIXME: Properly do the lowering here
- let pat_kind = match self.store[pat] {
- hir_def::hir::Pat::Range {
- start: Some(start),
- end: Some(end),
- range_type: _,
- } => rustc_type_ir::PatternKind::Range {
- start: self.lower_expr_as_const(start, ty),
- end: self.lower_expr_as_const(end, ty),
- },
- hir_def::hir::Pat::NotNull => rustc_type_ir::PatternKind::NotNull,
- _ => rustc_type_ir::PatternKind::NotNull,
+ let Some(pat) = self.lower_pattern_type(pat, ty) else {
+ return (self.types.types.error, res);
};
- let pat = Pattern::new(self.interner, pat_kind);
Ty::new_pat(self.interner, ty, pat)
}
TypeRef::Error => self.types.types.error,
@@ -562,6 +551,31 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> {
(ty, res)
}
+ fn lower_pattern_type(&mut self, pat: PatId, ty: Ty<'db>) -> Option<Pattern<'db>> {
+ let pat_kind = match self.store[pat] {
+ hir_def::hir::Pat::Range { start: Some(start), end: Some(end), range_type: _ } => {
+ rustc_type_ir::PatternKind::Range {
+ start: self.lower_expr_as_const(start, ty),
+ end: self.lower_expr_as_const(end, ty),
+ }
+ }
+ hir_def::hir::Pat::NotNull => rustc_type_ir::PatternKind::NotNull,
+ hir_def::hir::Pat::Or(ref pats) => rustc_type_ir::PatternKind::Or(
+ PatList::new_from_iter(
+ self.interner,
+ pats.iter().map(|&pat| self.lower_pattern_type(pat, ty).ok_or(())),
+ )
+ .ok()?,
+ ),
+ 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))
+ }
+
fn lower_fn_ptr(&mut self, fn_: &FnType) -> Ty<'db> {
let interner = self.interner;
let (params, ret_ty) = fn_.split_params_and_ret();
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() {
diff --git a/crates/intern/src/symbol/symbols.rs b/crates/intern/src/symbol/symbols.rs
index 6ad8006d18..f9ba0582ab 100644
--- a/crates/intern/src/symbol/symbols.rs
+++ b/crates/intern/src/symbol/symbols.rs
@@ -671,4 +671,7 @@ define_symbols! {
deref_patterns,
mut_ref,
type_changing_struct_update,
+ RangeMin,
+ RangeMax,
+ RangeSub,
}
diff --git a/crates/test-utils/src/minicore.rs b/crates/test-utils/src/minicore.rs
index 190c59170f..bd15dca609 100644
--- a/crates/test-utils/src/minicore.rs
+++ b/crates/test-utils/src/minicore.rs
@@ -54,12 +54,12 @@
//! iterators: iterator, fn
//! manually_drop: drop
//! matches:
-//! non_null:
-//! non_zero: transmute, option
+//! non_null: pat
+//! non_zero: pat, transmute, option
//! option: panic
//! ord: eq, option
//! panic: fmt
-//! pat:
+//! pat: panic
//! phantom_data:
//! pin:
//! pointee: copy, send, sync, ord, hash, unpin, phantom_data
@@ -539,10 +539,10 @@ pub mod ptr {
// endregion:pointee
// region:non_null
- #[rustc_layout_scalar_valid_range_start(1)]
#[rustc_nonnull_optimization_guaranteed]
+ #[repr(transparent)]
pub struct NonNull<T: crate::marker::PointeeSized> {
- pointer: *const T,
+ pointer: crate::pattern_type!(*const T is !null),
}
// region:coerce_unsized
impl<T: crate::marker::PointeeSized, U: crate::marker::PointeeSized>
@@ -2327,28 +2327,50 @@ pub mod pat {
}
pub const trait RangePattern {
- /// Trait version of the inherent `MIN` assoc const.
#[lang = "RangeMin"]
const MIN: Self;
- /// Trait version of the inherent `MIN` assoc const.
#[lang = "RangeMax"]
const MAX: Self;
- /// A compile-time helper to subtract 1 for exclusive ranges.
#[lang = "RangeSub"]
- #[track_caller]
fn sub_one(self) -> Self;
}
+
+ impl const RangePattern for u8 {
+ const MIN: u8 = 0;
+ const MAX: u8 = 0xFF;
+ fn sub_one(self) -> Self {
+ if self == Self::MIN {
+ panic!("exclusive range end at minimum value of type")
+ } else {
+ self - 1
+ }
+ }
+ }
+
+ // region:coerce_unsized
+ impl<T: crate::marker::PointeeSized, U: crate::marker::PointeeSized>
+ crate::ops::CoerceUnsized<pattern_type!(*const U is !null)> for pattern_type!(*const T is !null)
+ where
+ T: crate::marker::Unsize<U>,
+ {
+ }
+ // endregion:coerce_unsized
+
+ // region:dispatch_from_dyn
+ impl<T: crate::ops::DispatchFromDyn<U>, U>
+ crate::ops::DispatchFromDyn<pattern_type!(U is !null)> for pattern_type!(T is !null)
+ {
+ }
+ // endregion:dispatch_from_dyn
}
// endregion:pat
// region:non_zero
pub mod num {
#[repr(transparent)]
- #[rustc_layout_scalar_valid_range_start(1)]
- #[rustc_nonnull_optimization_guaranteed]
- pub struct NonZeroU8(u8);
+ pub struct NonZeroU8(crate::pattern_type!(u8 is 1..));
}
// endregion:non_zero
@@ -2457,6 +2479,7 @@ pub mod prelude {
hash::derive::Hash, // :hash, derive
iter::{FromIterator, IntoIterator, Iterator}, // :iterator
macros::builtin::{derive, derive_const}, // :derive
+ macros::deref, // :deref_pat
marker::Copy, // :copy
marker::Send, // :send
marker::Sized, // :sized
@@ -2470,7 +2493,6 @@ pub mod prelude {
panic, // :panic
result::Result::{self, Err, Ok}, // :result
str::FromStr, // :str
- macros::deref, // :deref_pat
};
}