Merge pull request #21460 from Veykril/push-vyqmuvkouywu

feat: Implement support for `feature(new_range)`
This commit is contained in:
Lukas Wirth 2026-01-31 10:31:38 +00:00 committed by GitHub
commit cc24765f3c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 246 additions and 128 deletions

View file

@ -499,6 +499,11 @@ language_item_table! { LangItems =>
RangeToInclusive, sym::RangeToInclusive, StructId;
RangeTo, sym::RangeTo, StructId;
RangeFromCopy, sym::RangeFromCopy, StructId;
RangeInclusiveCopy, sym::RangeInclusiveCopy, StructId;
RangeCopy, sym::RangeCopy, StructId;
RangeToInclusiveCopy, sym::RangeToInclusiveCopy, StructId;
String, sym::String, StructId;
CStr, sym::CStr, StructId;
Ordering, sym::Ordering, EnumId;

View file

@ -423,6 +423,10 @@ macro_rules! __known_path {
(core::ops::RangeTo) => {};
(core::ops::RangeToInclusive) => {};
(core::ops::RangeInclusive) => {};
(core::range::Range) => {};
(core::range::RangeFrom) => {};
(core::range::RangeInclusive) => {};
(core::range::RangeToInclusive) => {};
(core::future::Future) => {};
(core::future::IntoFuture) => {};
(core::fmt::Debug) => {};

View file

@ -1815,18 +1815,34 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
Some(struct_.into())
}
fn has_new_range_feature(&self) -> bool {
self.resolver.top_level_def_map().is_unstable_feature_enabled(&sym::new_range)
}
fn resolve_range(&self) -> Option<AdtId> {
let struct_ = self.lang_items.Range?;
let struct_ = if self.has_new_range_feature() {
self.lang_items.RangeCopy?
} else {
self.lang_items.Range?
};
Some(struct_.into())
}
fn resolve_range_inclusive(&self) -> Option<AdtId> {
let struct_ = self.lang_items.RangeInclusiveStruct?;
let struct_ = if self.has_new_range_feature() {
self.lang_items.RangeInclusiveCopy?
} else {
self.lang_items.RangeInclusiveStruct?
};
Some(struct_.into())
}
fn resolve_range_from(&self) -> Option<AdtId> {
let struct_ = self.lang_items.RangeFrom?;
let struct_ = if self.has_new_range_feature() {
self.lang_items.RangeFromCopy?
} else {
self.lang_items.RangeFrom?
};
Some(struct_.into())
}
@ -1836,7 +1852,11 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
}
fn resolve_range_to_inclusive(&self) -> Option<AdtId> {
let struct_ = self.lang_items.RangeToInclusive?;
let struct_ = if self.has_new_range_feature() {
self.lang_items.RangeToInclusiveCopy?
} else {
self.lang_items.RangeToInclusive?
};
Some(struct_.into())
}

View file

@ -13,11 +13,11 @@ fn infer_pattern() {
let a = z;
let (c, d) = (1, "hello");
for (e, f) in some_iter {
for (e, f) in [(0, 1)] {
let g = e;
}
if let [val] = opt {
if let [val] = [y] {
let h = val;
}
@ -33,7 +33,7 @@ fn infer_pattern() {
"#,
expect![[r#"
8..9 'x': &'? i32
17..400 '{ ...o_x; }': ()
17..399 '{ ...o_x; }': ()
27..28 'y': &'? i32
31..32 'x': &'? i32
42..44 '&z': &'? i32
@ -47,58 +47,62 @@ fn infer_pattern() {
82..94 '(1, "hello")': (i32, &'? str)
83..84 '1': i32
86..93 '"hello"': &'static str
101..151 'for (e... }': fn into_iter<{unknown}>({unknown}) -> <{unknown} as IntoIterator>::IntoIter
101..151 'for (e... }': <{unknown} as IntoIterator>::IntoIter
101..151 'for (e... }': !
101..151 'for (e... }': {unknown}
101..151 'for (e... }': &'? mut {unknown}
101..151 'for (e... }': fn next<{unknown}>(&'? mut {unknown}) -> Option<<{unknown} as Iterator>::Item>
101..151 'for (e... }': Option<<{unknown} as Iterator>::Item>
101..151 'for (e... }': ()
101..151 'for (e... }': ()
101..151 'for (e... }': ()
101..151 'for (e... }': ()
105..111 '(e, f)': ({unknown}, {unknown})
106..107 'e': {unknown}
109..110 'f': {unknown}
115..124 'some_iter': {unknown}
125..151 '{ ... }': ()
139..140 'g': {unknown}
143..144 'e': {unknown}
157..204 'if let... }': ()
160..175 'let [val] = opt': bool
164..169 '[val]': [{unknown}]
165..168 'val': {unknown}
172..175 'opt': [{unknown}]
176..204 '{ ... }': ()
190..191 'h': {unknown}
194..197 'val': {unknown}
210..236 'if let...rue {}': ()
213..233 'let x ... &true': bool
217..225 'x @ true': &'? bool
221..225 'true': bool
221..225 'true': bool
228..233 '&true': &'? bool
229..233 'true': bool
234..236 '{}': ()
246..252 'lambda': impl Fn(u64, u64, i32) -> i32
255..287 '|a: u6...b; c }': impl Fn(u64, u64, i32) -> i32
256..257 'a': u64
264..265 'b': u64
267..268 'c': i32
275..287 '{ a + b; c }': i32
277..278 'a': u64
277..282 'a + b': u64
281..282 'b': u64
284..285 'c': i32
298..310 'ref ref_to_x': &'? &'? i32
313..314 'x': &'? i32
324..333 'mut mut_x': &'? i32
336..337 'x': &'? i32
347..367 'ref mu...f_to_x': &'? mut &'? i32
370..371 'x': &'? i32
381..382 'k': &'? mut &'? i32
385..397 'mut_ref_to_x': &'? mut &'? i32
101..150 'for (e... }': fn into_iter<[(i32, i32); 1]>([(i32, i32); 1]) -> <[(i32, i32); 1] as IntoIterator>::IntoIter
101..150 'for (e... }': IntoIter<(i32, i32), 1>
101..150 'for (e... }': !
101..150 'for (e... }': IntoIter<(i32, i32), 1>
101..150 'for (e... }': &'? mut IntoIter<(i32, i32), 1>
101..150 'for (e... }': fn next<IntoIter<(i32, i32), 1>>(&'? mut IntoIter<(i32, i32), 1>) -> Option<<IntoIter<(i32, i32), 1> as Iterator>::Item>
101..150 'for (e... }': Option<(i32, i32)>
101..150 'for (e... }': ()
101..150 'for (e... }': ()
101..150 'for (e... }': ()
101..150 'for (e... }': ()
105..111 '(e, f)': (i32, i32)
106..107 'e': i32
109..110 'f': i32
115..123 '[(0, 1)]': [(i32, i32); 1]
116..122 '(0, 1)': (i32, i32)
117..118 '0': i32
120..121 '1': i32
124..150 '{ ... }': ()
138..139 'g': i32
142..143 'e': i32
156..203 'if let... }': ()
159..174 'let [val] = [y]': bool
163..168 '[val]': [&'? i32; 1]
164..167 'val': &'? i32
171..174 '[y]': [&'? i32; 1]
172..173 'y': &'? i32
175..203 '{ ... }': ()
189..190 'h': &'? i32
193..196 'val': &'? i32
209..235 'if let...rue {}': ()
212..232 'let x ... &true': bool
216..224 'x @ true': &'? bool
220..224 'true': bool
220..224 'true': bool
227..232 '&true': &'? bool
228..232 'true': bool
233..235 '{}': ()
245..251 'lambda': impl Fn(u64, u64, i32) -> i32
254..286 '|a: u6...b; c }': impl Fn(u64, u64, i32) -> i32
255..256 'a': u64
263..264 'b': u64
266..267 'c': i32
274..286 '{ a + b; c }': i32
276..277 'a': u64
276..281 'a + b': u64
280..281 'b': u64
283..284 'c': i32
297..309 'ref ref_to_x': &'? &'? i32
312..313 'x': &'? i32
323..332 'mut mut_x': &'? i32
335..336 'x': &'? i32
346..366 'ref mu...f_to_x': &'? mut &'? i32
369..370 'x': &'? i32
380..381 'k': &'? mut &'? i32
384..396 'mut_ref_to_x': &'? mut &'? i32
"#]],
);
}
@ -380,7 +384,7 @@ fn infer_pattern_match_string_literal() {
fn infer_pattern_match_byte_string_literal() {
check_infer_with_mismatches(
r#"
//- minicore: index
//- minicore: index, range
struct S;
impl<T, const N: usize> core::ops::Index<S> for [T; N] {
type Output = [u8];
@ -395,7 +399,7 @@ fn infer_pattern_match_byte_string_literal() {
"#,
expect![[r#"
105..109 'self': &'? [T; N]
111..116 'index': {unknown}
111..116 'index': RangeFull
157..180 '{ ... }': &'? [u8]
167..174 'loop {}': !
172..174 '{}': ()

View file

@ -64,20 +64,37 @@ fn type_alias_in_struct_lit() {
#[test]
fn infer_ranges() {
check_types(
check_no_mismatches(
r#"
//- minicore: range
fn test() {
let a = ..;
let b = 1..;
let c = ..2u32;
let d = 1..2usize;
let e = ..=10;
let f = 'a'..='z';
//- minicore: range, new_range
let t = (a, b, c, d, e, f);
t;
} //^ (RangeFull, RangeFrom<i32>, RangeTo<u32>, Range<usize>, RangeToInclusive<i32>, RangeInclusive<char>)
fn test() {
let _: core::ops::RangeFull = ..;
let _: core::ops::RangeFrom<i32> = 1..;
let _: core::ops::RangeTo<u32> = ..2u32;
let _: core::ops::Range<usize> = 1..2usize;
let _: core::ops::RangeToInclusive<i32> = ..=10;
let _: core::ops::RangeInclusive<char> = 'a'..='z';
}
"#,
);
}
#[test]
fn infer_ranges_new_range() {
check_no_mismatches(
r#"
//- minicore: range, new_range
#![feature(new_range)]
fn test() {
let _: core::ops::RangeFull = ..;
let _: core::range::RangeFrom<i32> = 1..;
let _: core::ops::RangeTo<u32> = ..2u32;
let _: core::range::Range<usize> = 1..2usize;
let _: core::range::RangeToInclusive<i32> = ..=10;
let _: core::range::RangeInclusive<char> = 'a'..='z';
}
"#,
);
}

View file

@ -532,18 +532,12 @@ impl<'db> SourceAnalyzer<'db> {
db: &'db dyn HirDatabase,
range_pat: &ast::RangePat,
) -> Option<StructId> {
let path: ModPath = match (range_pat.op_kind()?, range_pat.start(), range_pat.end()) {
(RangeOp::Exclusive, None, Some(_)) => path![core::ops::RangeTo],
(RangeOp::Exclusive, Some(_), None) => path![core::ops::RangeFrom],
(RangeOp::Exclusive, Some(_), Some(_)) => path![core::ops::Range],
(RangeOp::Inclusive, None, Some(_)) => path![core::ops::RangeToInclusive],
(RangeOp::Inclusive, Some(_), Some(_)) => path![core::ops::RangeInclusive],
(RangeOp::Exclusive, None, None) => return None,
(RangeOp::Inclusive, None, None) => return None,
(RangeOp::Inclusive, Some(_), None) => return None,
};
self.resolver.resolve_known_struct(db, &path)
self.resolve_range_struct(
db,
range_pat.op_kind()?,
range_pat.start().is_some(),
range_pat.end().is_some(),
)
}
pub(crate) fn resolve_range_expr(
@ -551,19 +545,59 @@ impl<'db> SourceAnalyzer<'db> {
db: &'db dyn HirDatabase,
range_expr: &ast::RangeExpr,
) -> Option<StructId> {
let path: ModPath = match (range_expr.op_kind()?, range_expr.start(), range_expr.end()) {
(RangeOp::Exclusive, None, None) => path![core::ops::RangeFull],
(RangeOp::Exclusive, None, Some(_)) => path![core::ops::RangeTo],
(RangeOp::Exclusive, Some(_), None) => path![core::ops::RangeFrom],
(RangeOp::Exclusive, Some(_), Some(_)) => path![core::ops::Range],
(RangeOp::Inclusive, None, Some(_)) => path![core::ops::RangeToInclusive],
(RangeOp::Inclusive, Some(_), Some(_)) => path![core::ops::RangeInclusive],
self.resolve_range_struct(
db,
range_expr.op_kind()?,
range_expr.start().is_some(),
range_expr.end().is_some(),
)
}
fn resolve_range_struct(
&self,
db: &'db dyn HirDatabase,
op_kind: RangeOp,
has_start: bool,
has_end: bool,
) -> Option<StructId> {
let has_new_range =
self.resolver.top_level_def_map().is_unstable_feature_enabled(&sym::new_range);
let lang_items = self.lang_items(db);
match (op_kind, has_start, has_end) {
(RangeOp::Exclusive, false, false) => lang_items.RangeFull,
(RangeOp::Exclusive, false, true) => lang_items.RangeTo,
(RangeOp::Exclusive, true, false) => {
if has_new_range {
lang_items.RangeFromCopy
} else {
lang_items.RangeFrom
}
}
(RangeOp::Exclusive, true, true) => {
if has_new_range {
lang_items.RangeCopy
} else {
lang_items.Range
}
}
(RangeOp::Inclusive, false, true) => {
if has_new_range {
lang_items.RangeToInclusiveCopy
} else {
lang_items.RangeToInclusive
}
}
(RangeOp::Inclusive, true, true) => {
if has_new_range {
lang_items.RangeInclusiveCopy
} else {
lang_items.RangeInclusiveStruct
}
}
// [E0586] inclusive ranges must be bounded at the end
(RangeOp::Inclusive, None, None) => return None,
(RangeOp::Inclusive, Some(_), None) => return None,
};
self.resolver.resolve_known_struct(db, &path)
(RangeOp::Inclusive, false, false) => None,
(RangeOp::Inclusive, true, false) => None,
}
}
pub(crate) fn resolve_await_to_poll(

View file

@ -526,6 +526,12 @@ define_symbols! {
arbitrary_self_types,
arbitrary_self_types_pointers,
supertrait_item_shadowing,
new_range,
range,
RangeCopy,
RangeFromCopy,
RangeInclusiveCopy,
RangeToInclusiveCopy,
hash,
partial_cmp,
cmp,

View file

@ -58,6 +58,7 @@
//! pin:
//! pointee: copy, send, sync, ord, hash, unpin, phantom_data
//! range:
//! new_range:
//! receiver: deref
//! result:
//! send: sized
@ -175,7 +176,9 @@ pub mod marker {
// region:clone
impl<T: PointeeSized> Clone for PhantomData<T> {
fn clone(&self) -> Self { Self }
fn clone(&self) -> Self {
Self
}
}
// endregion:clone
@ -1128,6 +1131,32 @@ pub mod ops {
// endregion:dispatch_from_dyn
}
// region:new_range
pub mod range {
#[lang = "RangeCopy"]
pub struct Range<Idx> {
pub start: Idx,
pub end: Idx,
}
#[lang = "RangeFromCopy"]
pub struct RangeFrom<Idx> {
pub start: Idx,
}
#[lang = "RangeInclusiveCopy"]
pub struct RangeInclusive<Idx> {
pub start: Idx,
pub end: Idx,
}
#[lang = "RangeToInclusiveCopy"]
pub struct RangeToInclusive<Idx> {
pub end: Idx,
}
}
// endregion:new_range
// region:eq
pub mod cmp {
use crate::marker::PointeeSized;
@ -1144,7 +1173,9 @@ pub mod cmp {
// region:builtin_impls
impl PartialEq for () {
fn eq(&self, other: &()) -> bool { true }
fn eq(&self, other: &()) -> bool {
true
}
}
// endregion:builtin_impls
@ -1567,10 +1598,7 @@ pub mod pin {
}
// endregion:dispatch_from_dyn
// region:coerce_unsized
impl<Ptr, U> crate::ops::CoerceUnsized<Pin<U>> for Pin<Ptr> where
Ptr: crate::ops::CoerceUnsized<U>
{
}
impl<Ptr, U> crate::ops::CoerceUnsized<Pin<U>> for Pin<Ptr> where Ptr: crate::ops::CoerceUnsized<U> {}
// endregion:coerce_unsized
}
// endregion:pin
@ -1792,9 +1820,9 @@ pub mod iter {
fn from_iter<T: IntoIterator<Item = A>>(iter: T) -> Self;
}
}
pub use self::collect::{IntoIterator, FromIterator};
pub use self::collect::{FromIterator, IntoIterator};
}
pub use self::traits::{IntoIterator, FromIterator, Iterator};
pub use self::traits::{FromIterator, IntoIterator, Iterator};
}
// endregion:iterator
@ -2091,30 +2119,30 @@ macro_rules! column {
pub mod prelude {
pub mod v1 {
pub use crate::{
clone::Clone, // :clone
cmp::{Eq, PartialEq}, // :eq
cmp::{Ord, PartialOrd}, // :ord
convert::AsMut, // :as_mut
convert::AsRef, // :as_ref
convert::{From, Into, TryFrom, TryInto}, // :from
default::Default, // :default
iter::{IntoIterator, Iterator, FromIterator}, // :iterator
macros::builtin::{derive, derive_const}, // :derive
marker::Copy, // :copy
marker::Send, // :send
marker::Sized, // :sized
marker::Sync, // :sync
mem::drop, // :drop
mem::size_of, // :size_of
ops::Drop, // :drop
ops::{AsyncFn, AsyncFnMut, AsyncFnOnce}, // :async_fn
ops::{Fn, FnMut, FnOnce}, // :fn
option::Option::{self, None, Some}, // :option
panic, // :panic
result::Result::{self, Err, Ok}, // :result
str::FromStr, // :str
fmt::derive::Debug, // :fmt, derive
hash::derive::Hash, // :hash, derive
clone::Clone, // :clone
cmp::{Eq, PartialEq}, // :eq
cmp::{Ord, PartialOrd}, // :ord
convert::AsMut, // :as_mut
convert::AsRef, // :as_ref
convert::{From, Into, TryFrom, TryInto}, // :from
default::Default, // :default
fmt::derive::Debug, // :fmt, derive
hash::derive::Hash, // :hash, derive
iter::{FromIterator, IntoIterator, Iterator}, // :iterator
macros::builtin::{derive, derive_const}, // :derive
marker::Copy, // :copy
marker::Send, // :send
marker::Sized, // :sized
marker::Sync, // :sync
mem::drop, // :drop
mem::size_of, // :size_of
ops::Drop, // :drop
ops::{AsyncFn, AsyncFnMut, AsyncFnOnce}, // :async_fn
ops::{Fn, FnMut, FnOnce}, // :fn
option::Option::{self, None, Some}, // :option
panic, // :panic
result::Result::{self, Err, Ok}, // :result
str::FromStr, // :str
};
}