Unnamed repository; edit this file 'description' to name the repository.
| -rw-r--r-- | crates/hir-def/src/expr_store/lower.rs | 93 | ||||
| -rw-r--r-- | crates/hir-def/src/expr_store/pretty.rs | 1 | ||||
| -rw-r--r-- | crates/hir-def/src/lang_item.rs | 14 | ||||
| -rw-r--r-- | crates/hir-def/src/resolver.rs | 7 | ||||
| -rw-r--r-- | crates/hir-ty/src/consteval.rs | 10 | ||||
| -rw-r--r-- | crates/hir-ty/src/infer/path.rs | 25 | ||||
| -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 | 46 | ||||
| -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 | ||||
| -rw-r--r-- | crates/intern/src/symbol/symbols.rs | 3 | ||||
| -rw-r--r-- | crates/test-utils/src/minicore.rs | 48 |
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 }; } |