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/intern/src/symbol/symbols.rs | 3 | ||||
| -rw-r--r-- | crates/test-utils/src/minicore.rs | 48 |
8 files changed, 176 insertions, 25 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/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 }; } |