diff --git a/crates/hir-def/src/expr_store.rs b/crates/hir-def/src/expr_store.rs index 5e6633d1cec1..e95239026875 100644 --- a/crates/hir-def/src/expr_store.rs +++ b/crates/hir-def/src/expr_store.rs @@ -750,10 +750,6 @@ impl ExpressionStore { visitor.on_expr(*lhs); visitor.on_expr(*rhs); } - Expr::Range { lhs, rhs, range_type: _ } => { - visitor.on_expr_opt(*lhs); - visitor.on_expr_opt(*rhs); - } Expr::Index { base, index } => { visitor.on_expr(*base); visitor.on_expr(*index); diff --git a/crates/hir-def/src/expr_store/lower.rs b/crates/hir-def/src/expr_store/lower.rs index 781241a620da..91faafaf843e 100644 --- a/crates/hir-def/src/expr_store/lower.rs +++ b/crates/hir-def/src/expr_store/lower.rs @@ -62,6 +62,7 @@ use crate::{ ArrayType, ConstRef, FnType, LifetimeRef, LifetimeRefId, Mutability, PathId, Rawness, RefType, TraitBoundModifier, TraitRef, TypeBound, TypeRef, TypeRefId, UseArgRef, }, + unstable_features::UnstableFeatures, }; pub use self::path::hir_segment_to_ast_segment; @@ -1829,16 +1830,7 @@ impl<'db> ExprCollector<'db> { let index = self.collect_expr_opt(e.index()); self.alloc_expr(Expr::Index { base, index }, syntax_ptr) } - ast::Expr::RangeExpr(e) => { - let lhs = e.start().map(|lhs| self.collect_expr(lhs)); - let rhs = e.end().map(|rhs| self.collect_expr(rhs)); - match e.op_kind() { - Some(range_type) => { - self.alloc_expr(Expr::Range { lhs, rhs, range_type }, syntax_ptr) - } - None => self.alloc_expr(Expr::Missing, syntax_ptr), - } - } + ast::Expr::RangeExpr(e) => self.collect_range_expr(e, syntax_ptr), ast::Expr::MacroExpr(e) => { let e = e.macro_call()?; let macro_ptr = AstPtr::new(&e); @@ -1870,6 +1862,87 @@ impl<'db> ExprCollector<'db> { }) } + fn collect_range_expr(&mut self, e: ast::RangeExpr, syntax_ptr: AstPtr) -> ExprId { + let lhs = e.start().map(|lhs| self.collect_expr(lhs)); + let rhs = e.end().map(|rhs| self.collect_expr(rhs)); + let kind = e.op_kind().unwrap_or(ast::RangeOp::Exclusive); + let new_range = self.features().new_range; + let lang_items = self.lang_items(); + let lang_item = match (lhs, rhs, kind) { + (None, None, _) => lang_items.RangeFull, + (Some(..), None, ast::RangeOp::Exclusive) => { + if new_range { + lang_items.RangeFromCopy + } else { + lang_items.RangeFrom + } + } + (None, Some(..), ast::RangeOp::Exclusive) => lang_items.RangeTo, + (Some(..), Some(..), ast::RangeOp::Exclusive) => { + if new_range { + lang_items.RangeCopy + } else { + lang_items.Range + } + } + (None, Some(..), ast::RangeOp::Inclusive) => { + if new_range { + lang_items.RangeToInclusiveCopy + } else { + lang_items.RangeToInclusive + } + } + (Some(lhs), Some(rhs), ast::RangeOp::Inclusive) => { + if new_range { + lang_items.RangeInclusiveCopy + } else { + return self.collect_inclusive_range(syntax_ptr, lang_items, lhs, rhs); + } + } + (Some(..), None, ast::RangeOp::Inclusive) => { + if new_range { + lang_items.RangeFromCopy + } else { + lang_items.RangeFrom + } + } + }; + let Some(struct_path) = self.lang_path(lang_item) else { + return self.alloc_expr(Expr::Missing, syntax_ptr); + }; + let lhs = lhs.map(|lhs| (lhs, sym::start)); + let rhs = rhs.map(|rhs| { + ( + rhs, + if lang_item == lang_items.RangeInclusiveCopy + || lang_item == lang_items.RangeToInclusiveCopy + { + sym::last + } else { + sym::end + }, + ) + }); + let fields = std::iter::chain(lhs, rhs) + .map(|(expr, name)| RecordLitField { name: Name::new_symbol_root(name), expr }) + .collect(); + self.alloc_expr( + Expr::RecordLit { path: struct_path, fields, spread: RecordSpread::None }, + syntax_ptr, + ) + } + + fn collect_inclusive_range( + &mut self, + syntax_ptr: AstPtr, + lang_items: &LangItems, + lhs: ExprId, + rhs: ExprId, + ) -> ExprId { + let fn_path = self.alloc_expr_desugared(self.lang_path_expr(lang_items.RangeInclusiveNew)); + self.alloc_expr(Expr::Call { callee: fn_path, args: Box::new([lhs, rhs]) }, syntax_ptr) + } + fn collect_expr_path(&mut self, e: ast::PathExpr) -> Option<(Path, HygieneId)> { e.path().and_then(|path| { let path = self.lower_path(path, &mut Self::impl_trait_error_allocator)?; @@ -3327,6 +3400,10 @@ impl<'db> ExprCollector<'db> { Some(Path::LangItem(lang?.into(), None)) } + fn lang_path_expr(&self, lang: Option>) -> Expr { + self.lang_path(lang).map_or(Expr::Missing, Expr::Path) + } + fn ty_rel_lang_path( &self, lang: Option>, @@ -3354,6 +3431,10 @@ fn pat_literal_to_hir(lit: &ast::LiteralPat) -> Option<(Literal, ast::Literal)> } impl<'db> ExprCollector<'db> { + fn features(&self) -> &'db UnstableFeatures { + self.def_map.features() + } + fn with_fresh_binding_expr_root(&mut self, f: impl FnOnce(&mut Self) -> ExprId) -> ExprId { self.with_expr_root(|this| this.with_binding_owner(f)) } diff --git a/crates/hir-def/src/expr_store/pretty.rs b/crates/hir-def/src/expr_store/pretty.rs index 1c70922467e1..558693ee8ada 100644 --- a/crates/hir-def/src/expr_store/pretty.rs +++ b/crates/hir-def/src/expr_store/pretty.rs @@ -746,18 +746,6 @@ impl Printer<'_> { self.whitespace(); self.print_expr_in(prec, *rhs); } - Expr::Range { lhs, rhs, range_type } => { - if let Some(lhs) = lhs { - self.print_expr_in(prec, *lhs); - } - match range_type { - RangeOp::Exclusive => w!(self, ".."), - RangeOp::Inclusive => w!(self, "..="), - }; - if let Some(rhs) = rhs { - self.print_expr_in(prec, *rhs); - } - } Expr::Index { base, index } => { self.print_expr_in(prec, *base); w!(self, "["); diff --git a/crates/hir-def/src/expr_store/tests/body.rs b/crates/hir-def/src/expr_store/tests/body.rs index 3e4161dd9015..aa3165670be3 100644 --- a/crates/hir-def/src/expr_store/tests/body.rs +++ b/crates/hir-def/src/expr_store/tests/body.rs @@ -164,7 +164,7 @@ mod m { fn desugar_for_loop() { pretty_print( r#" -//- minicore: iterator +//- minicore: iterator, range fn main() { for ident in 0..10 { foo(); @@ -173,23 +173,26 @@ fn main() { } "#, expect![[r#" - fn main() { - match builtin#lang(into_iter)( - 0..10, - ) { - mut 0 => loop { - match builtin#lang(next)( - &mut 0, - ) { - builtin#lang(None) => break, - builtin#lang(Some)(ident) => { - foo(); - bar() - }, - } - }, - } - }"#]], + fn main() { + match builtin#lang(into_iter)( + builtin#lang(Range){ + start: 0, + end: 10, + }, + ) { + mut 0 => loop { + match builtin#lang(next)( + &mut 0, + ) { + builtin#lang(None) => break, + builtin#lang(Some)(ident) => { + foo(); + bar() + }, + } + }, + } + }"#]], ); } diff --git a/crates/hir-def/src/hir.rs b/crates/hir-def/src/hir.rs index a275780ffca1..0ac315ea50fc 100644 --- a/crates/hir-def/src/hir.rs +++ b/crates/hir-def/src/hir.rs @@ -370,11 +370,6 @@ pub enum Expr { target: PatId, value: ExprId, }, - Range { - lhs: Option, - rhs: Option, - range_type: RangeOp, - }, Index { base: ExprId, index: ExprId, @@ -459,8 +454,6 @@ impl Expr { | Expr::Yield { .. } => ExprPrecedence::Jump, Expr::Continue { .. } => ExprPrecedence::Unambiguous, - - Expr::Range { .. } => ExprPrecedence::Range, } } } diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs index 1dd6f95ac311..8192b6165eeb 100644 --- a/crates/hir-ty/src/infer.rs +++ b/crates/hir-ty/src/infer.rs @@ -2673,56 +2673,6 @@ impl<'db> InferenceContext<'db> { } } - fn resolve_range_full(&self) -> Option { - let struct_ = self.lang_items.RangeFull?; - Some(struct_.into()) - } - - fn has_new_range_feature(&self) -> bool { - self.features.new_range - } - - fn resolve_range(&self) -> Option { - 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 { - 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 { - let struct_ = if self.has_new_range_feature() { - self.lang_items.RangeFromCopy? - } else { - self.lang_items.RangeFrom? - }; - Some(struct_.into()) - } - - fn resolve_range_to(&self) -> Option { - let struct_ = self.lang_items.RangeTo?; - Some(struct_.into()) - } - - fn resolve_range_to_inclusive(&self) -> Option { - let struct_ = if self.has_new_range_feature() { - self.lang_items.RangeToInclusiveCopy? - } else { - self.lang_items.RangeToInclusive? - }; - Some(struct_.into()) - } - fn resolve_va_list(&self) -> Option { let struct_ = self.lang_items.VaList?; Some(struct_.into()) diff --git a/crates/hir-ty/src/infer/closure/analysis/expr_use_visitor.rs b/crates/hir-ty/src/infer/closure/analysis/expr_use_visitor.rs index 4d300f5048a5..78940783bd9b 100644 --- a/crates/hir-ty/src/infer/closure/analysis/expr_use_visitor.rs +++ b/crates/hir-ty/src/infer/closure/analysis/expr_use_visitor.rs @@ -684,15 +684,6 @@ impl<'a, 'db, D: Delegate<'db>> ExprUseVisitor<'a, 'db, D> { } } - Expr::Range { lhs, rhs, .. } => { - if let Some(lhs) = lhs { - self.consume_expr(lhs)?; - } - if let Some(rhs) = rhs { - self.consume_expr(rhs)?; - } - } - Expr::IncludeBytes => {} } Ok(()) diff --git a/crates/hir-ty/src/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs index ab5c222de405..48ee91d0c632 100644 --- a/crates/hir-ty/src/infer/expr.rs +++ b/crates/hir-ty/src/infer/expr.rs @@ -23,7 +23,6 @@ use rustc_type_ir::{ inherent::{IntoKind, Ty as _}, }; use stdx::never; -use syntax::ast::RangeOp; use tracing::debug; use crate::{ @@ -33,7 +32,7 @@ use crate::{ lower::lower_mutability, method_resolution::{self, CandidateId, MethodCallee, MethodError}, next_solver::{ - ClauseKind, FnSig, GenericArg, GenericArgs, Ty, TyKind, TypeError, + ClauseKind, FnSig, Ty, TyKind, TypeError, infer::{ BoundRegionConversionTime, InferOk, traits::{Obligation, ObligationCause}, @@ -269,7 +268,6 @@ impl<'db> InferenceContext<'db> { | Expr::Unsafe { .. } | Expr::Await { .. } | Expr::Ref { .. } - | Expr::Range { .. } | Expr::RecordLit { .. } | Expr::Yeet { .. } | Expr::Missing @@ -677,52 +675,6 @@ impl<'db> InferenceContext<'db> { self.types.types.unit } } - Expr::Range { lhs, rhs, range_type } => { - let lhs_ty = - lhs.map(|e| self.infer_expr_inner(e, &Expectation::none(), ExprIsRead::Yes)); - let rhs_expect = lhs_ty.map_or_else(Expectation::none, Expectation::has_type); - let rhs_ty = rhs.map(|e| self.infer_expr(e, &rhs_expect, ExprIsRead::Yes)); - let single_arg_adt = |adt, ty: Ty<'db>| { - Ty::new_adt( - self.interner(), - adt, - GenericArgs::new_from_slice(&[GenericArg::from(ty)]), - ) - }; - match (range_type, lhs_ty, rhs_ty) { - (RangeOp::Exclusive, None, None) => match self.resolve_range_full() { - Some(adt) => { - Ty::new_adt(self.interner(), adt, self.types.empty.generic_args) - } - None => self.err_ty(), - }, - (RangeOp::Exclusive, None, Some(ty)) => match self.resolve_range_to() { - Some(adt) => single_arg_adt(adt, ty), - None => self.err_ty(), - }, - (RangeOp::Inclusive, None, Some(ty)) => { - match self.resolve_range_to_inclusive() { - Some(adt) => single_arg_adt(adt, ty), - None => self.err_ty(), - } - } - (RangeOp::Exclusive, Some(_), Some(ty)) => match self.resolve_range() { - Some(adt) => single_arg_adt(adt, ty), - None => self.err_ty(), - }, - (RangeOp::Inclusive, Some(_), Some(ty)) => { - match self.resolve_range_inclusive() { - Some(adt) => single_arg_adt(adt, ty), - None => self.err_ty(), - } - } - (RangeOp::Exclusive, Some(ty), None) => match self.resolve_range_from() { - Some(adt) => single_arg_adt(adt, ty), - None => self.err_ty(), - }, - (RangeOp::Inclusive, _, None) => self.err_ty(), - } - } Expr::Index { base, index } => { let base_t = self.infer_expr_no_expect(*base, ExprIsRead::Yes); let idx_t = self.infer_expr_no_expect(*index, ExprIsRead::Yes); diff --git a/crates/hir-ty/src/infer/mutability.rs b/crates/hir-ty/src/infer/mutability.rs index 7d285a3a2102..9a6414ee2ba1 100644 --- a/crates/hir-ty/src/infer/mutability.rs +++ b/crates/hir-ty/src/infer/mutability.rs @@ -156,8 +156,6 @@ impl<'db> InferenceContext<'db> { self.infer_mut_expr(*expr, mutability); } Expr::UnaryOp { expr, op: _ } - | Expr::Range { lhs: Some(expr), rhs: None, range_type: _ } - | Expr::Range { rhs: Some(expr), lhs: None, range_type: _ } | Expr::Await { expr } | Expr::Loop { body: expr, label: _, source: _ } | Expr::Cast { expr, type_ref: _ } => { @@ -180,8 +178,7 @@ impl<'db> InferenceContext<'db> { self.infer_mut_expr(value, Mutability::Not); } Expr::Array(Array::Repeat { initializer: lhs, repeat: rhs }) - | Expr::BinaryOp { lhs, rhs, op: _ } - | Expr::Range { lhs: Some(lhs), rhs: Some(rhs), range_type: _ } => { + | Expr::BinaryOp { lhs, rhs, op: _ } => { self.infer_mut_expr(*lhs, Mutability::Not); self.infer_mut_expr(*rhs, Mutability::Not); } @@ -192,8 +189,7 @@ impl<'db> InferenceContext<'db> { self.infer_mut_not_expr_iter(exprs.iter().copied()); } // These don't need any action, as they don't have sub expressions - Expr::Range { lhs: None, rhs: None, range_type: _ } - | Expr::Literal(_) + Expr::Literal(_) | Expr::Path(_) | Expr::Continue { .. } | Expr::Underscore diff --git a/crates/hir-ty/src/mir/lower.rs b/crates/hir-ty/src/mir/lower.rs index 2537f64faf5e..ab0c25201a92 100644 --- a/crates/hir-ty/src/mir/lower.rs +++ b/crates/hir-ty/src/mir/lower.rs @@ -4,7 +4,7 @@ use std::{fmt::Write, iter, mem}; use base_db::Crate; use hir_def::{ - AdtId, DefWithBodyId, EnumVariantId, ExpressionStoreOwnerId, GenericParamId, HasModule, + DefWithBodyId, EnumVariantId, ExpressionStoreOwnerId, GenericParamId, HasModule, ItemContainerId, LocalFieldId, Lookup, TraitId, expr_store::{Body, ExpressionStore, HygieneId, body::Param, path::Path}, hir::{ @@ -1177,56 +1177,6 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { self.resolver.reset_to_guard(resolver_guard); Ok(Some(current)) } - &Expr::Range { lhs, rhs, range_type: _ } => { - let ty = self.expr_ty_without_adjust(expr_id); - let Some((adt, subst)) = ty.as_adt() else { - return Err(MirLowerError::TypeError("Range type is not adt")); - }; - let AdtId::StructId(st) = adt else { - return Err(MirLowerError::TypeError("Range type is not struct")); - }; - let mut lp = None; - let mut rp = None; - if let Some(it) = lhs { - let Some((o, c)) = self.lower_expr_to_some_operand(it, current)? else { - return Ok(None); - }; - lp = Some(o); - current = c; - } - if let Some(it) = rhs { - let Some((o, c)) = self.lower_expr_to_some_operand(it, current)? else { - return Ok(None); - }; - rp = Some(o); - current = c; - } - self.push_assignment( - current, - place, - Rvalue::Aggregate( - AggregateKind::Adt(st.into(), subst.store()), - st.fields(self.db) - .fields() - .iter() - .map(|it| { - let o = match it.1.name.as_str() { - "start" => lp.take(), - "end" => rp.take(), - "exhausted" => Some(Operand::from_bytes( - Box::new([0]), - Ty::new_bool(self.interner()), - )), - _ => None, - }; - o.ok_or(MirLowerError::UnresolvedField) - }) - .collect::>()?, - ), - expr_id.into(), - ); - Ok(Some(current)) - } Expr::Closure { closure_kind: ClosureKind::Closure, .. } => { let ty = self.expr_ty_without_adjust(expr_id); let TyKind::Closure(id, _) = ty.kind() else { diff --git a/crates/hir-ty/src/tests/macros.rs b/crates/hir-ty/src/tests/macros.rs index c0da6cfd307d..9d6d13a03493 100644 --- a/crates/hir-ty/src/tests/macros.rs +++ b/crates/hir-ty/src/tests/macros.rs @@ -140,7 +140,7 @@ fn infer_path_qualified_macros_expanded() { fn expr_macro_def_expanded_in_various_places() { check_infer( r#" - //- minicore: iterator + //- minicore: iterator, range macro spam() { 1isize } @@ -224,7 +224,7 @@ fn expr_macro_def_expanded_in_various_places() { 350..366 'spam!(... usize': usize 372..380 '&spam!()': &'? isize 386..394 '-spam!()': isize - 400..416 'spam!(...pam!()': {unknown} + 400..416 'spam!(...pam!()': Range 422..439 'spam!(...pam!()': isize "#]], ); @@ -234,7 +234,7 @@ fn expr_macro_def_expanded_in_various_places() { fn expr_macro_rules_expanded_in_various_places() { check_infer( r#" - //- minicore: iterator + //- minicore: iterator, range macro_rules! spam { () => (1isize); } @@ -318,7 +318,7 @@ fn expr_macro_rules_expanded_in_various_places() { 364..380 'spam!(... usize': usize 386..394 '&spam!()': &'? isize 400..408 '-spam!()': isize - 414..430 'spam!(...pam!()': {unknown} + 414..430 'spam!(...pam!()': Range 436..453 'spam!(...pam!()': isize "#]], ); diff --git a/crates/hir-ty/src/tests/regression.rs b/crates/hir-ty/src/tests/regression.rs index 6260722dd0f3..33e5ab315aa5 100644 --- a/crates/hir-ty/src/tests/regression.rs +++ b/crates/hir-ty/src/tests/regression.rs @@ -3148,3 +3148,15 @@ fn f<'a>(_: fn() -> &'a dyn Trait<'a>) {} "#, ); } + +#[test] +fn regression_23113() { + check_no_mismatches( + r#" +//- minicore: range +fn main() { + 0..loop {}; +} + "#, + ); +} diff --git a/crates/intern/src/symbol/symbols.rs b/crates/intern/src/symbol/symbols.rs index 9a566ee687e0..8acee5f832e6 100644 --- a/crates/intern/src/symbol/symbols.rs +++ b/crates/intern/src/symbol/symbols.rs @@ -552,6 +552,8 @@ define_symbols! { slice_len_fn, Some, start, + last, + end, std_panic, std, stdcall, diff --git a/crates/test-utils/src/minicore.rs b/crates/test-utils/src/minicore.rs index ca2d824f2c2b..e261c9f0fbfd 100644 --- a/crates/test-utils/src/minicore.rs +++ b/crates/test-utils/src/minicore.rs @@ -780,6 +780,13 @@ pub mod ops { pub(crate) exhausted: bool, } + impl RangeInclusive { + #[lang = "range_inclusive_new"] + pub const fn new(start: Idx, end: Idx) -> Self { + Self { start, end, exhausted: false } + } + } + #[lang = "RangeToInclusive"] pub struct RangeToInclusive { pub end: Idx, @@ -1250,12 +1257,12 @@ pub mod range { #[lang = "RangeInclusiveCopy"] pub struct RangeInclusive { pub start: Idx, - pub end: Idx, + pub last: Idx, } #[lang = "RangeToInclusiveCopy"] pub struct RangeToInclusive { - pub end: Idx, + pub last: Idx, } } // endregion:new_range