diff --git a/crates/hir-def/src/expr_store/lower.rs b/crates/hir-def/src/expr_store/lower.rs index c13a7e92cce5..e3bfc5b753ae 100644 --- a/crates/hir-def/src/expr_store/lower.rs +++ b/crates/hir-def/src/expr_store/lower.rs @@ -2410,7 +2410,10 @@ impl ExprCollector<'_> { let start = range_part_lower(p.start()); let end = range_part_lower(p.end()); // FIXME: Exclusive ended pattern range is stabilised - Pat::Range { start, end } + match p.op_kind() { + Some(range_type) => Pat::Range { start, end, range_type }, + None => Pat::Missing, + } } }; let ptr = AstPtr::new(&pat); diff --git a/crates/hir-def/src/expr_store/pretty.rs b/crates/hir-def/src/expr_store/pretty.rs index 3b3188398e06..4ba70938d08c 100644 --- a/crates/hir-def/src/expr_store/pretty.rs +++ b/crates/hir-def/src/expr_store/pretty.rs @@ -9,7 +9,7 @@ use std::{ use hir_expand::{Lookup, mod_path::PathKind}; use itertools::Itertools; use span::Edition; -use syntax::ast::HasName; +use syntax::ast::{HasName, RangeOp}; use crate::{ AdtId, DefWithBodyId, GenericDefId, TypeParamId, VariantId, @@ -735,8 +735,8 @@ impl Printer<'_> { self.print_expr_in(prec, *lhs); } match range_type { - ast::RangeOp::Exclusive => w!(self, ".."), - ast::RangeOp::Inclusive => w!(self, "..="), + RangeOp::Exclusive => w!(self, ".."), + RangeOp::Inclusive => w!(self, "..="), }; if let Some(rhs) = rhs { self.print_expr_in(prec, *rhs); @@ -937,11 +937,14 @@ impl Printer<'_> { }); w!(self, "}}"); } - Pat::Range { start, end } => { + Pat::Range { start, end, range_type } => { if let Some(start) = start { self.print_expr_in(prec, *start); } - w!(self, "..="); + match range_type { + RangeOp::Inclusive => w!(self, "..="), + RangeOp::Exclusive => w!(self, ".."), + } if let Some(end) = end { self.print_expr_in(prec, *end); } diff --git a/crates/hir-def/src/expr_store/tests/body.rs b/crates/hir-def/src/expr_store/tests/body.rs index 0a982b9e39f7..4a775568bc22 100644 --- a/crates/hir-def/src/expr_store/tests/body.rs +++ b/crates/hir-def/src/expr_store/tests/body.rs @@ -580,7 +580,7 @@ const fn f(x: i32) -> i32 { let MatchArm { pat, .. } = mtch_arms[1]; match body[pat] { - Pat::Range { start, end } => { + Pat::Range { start, end, range_type: _ } => { let hir_start = &body[start.unwrap()]; let hir_end = &body[end.unwrap()]; diff --git a/crates/hir-def/src/hir.rs b/crates/hir-def/src/hir.rs index 8ca8308512d1..66eade224575 100644 --- a/crates/hir-def/src/hir.rs +++ b/crates/hir-def/src/hir.rs @@ -661,6 +661,7 @@ pub enum Pat { Range { start: Option, end: Option, + range_type: RangeOp, }, Slice { prefix: Box<[PatId]>, diff --git a/crates/hir-ty/src/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs index 50bc70f4ee50..7487660a7618 100644 --- a/crates/hir-ty/src/infer/expr.rs +++ b/crates/hir-ty/src/infer/expr.rs @@ -772,8 +772,7 @@ impl<'db> InferenceContext<'_, 'db> { 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.as_ref().map_or_else(Expectation::none, |ty| Expectation::has_type(*ty)); + 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( diff --git a/crates/hir-ty/src/infer/pat.rs b/crates/hir-ty/src/infer/pat.rs index b3cf94aef4bd..d8b02dea15f4 100644 --- a/crates/hir-ty/src/infer/pat.rs +++ b/crates/hir-ty/src/infer/pat.rs @@ -11,6 +11,7 @@ use hir_expand::name::Name; use rustc_ast_ir::Mutability; use rustc_type_ir::inherent::{GenericArg as _, GenericArgs as _, IntoKind, SliceLike, Ty as _}; use stdx::TupleExt; +use syntax::ast::RangeOp; use crate::{ DeclContext, DeclOrigin, InferenceDiagnostic, @@ -349,9 +350,51 @@ impl<'db> InferenceContext<'_, 'db> { self.infer_slice_pat(expected, prefix, *slice, suffix, default_bm, decl) } Pat::Wild => expected, - Pat::Range { .. } => { - // FIXME: do some checks here. - expected + Pat::Range { start, end, range_type } => { + // FIXME: Expectation + let lhs_expectation = Expectation::none(); + let lhs_ty = + start.map(|start| self.infer_expr(start, &lhs_expectation, ExprIsRead::Yes)); + let rhs_expectation = lhs_ty.map_or_else(Expectation::none, Expectation::HasType); + let rhs_ty = end.map(|end| self.infer_expr(end, &rhs_expectation, ExprIsRead::Yes)); + let single_arg_adt = |adt, ty: Ty<'db>| { + Ty::new_adt( + self.interner(), + adt, + GenericArgs::new_from_iter(self.interner(), [ty.into()]), + ) + }; + 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_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(), + } } &Pat::Lit(expr) => { // Don't emit type mismatches again, the expression lowering already did that. diff --git a/crates/hir-ty/src/mir/lower/pattern_matching.rs b/crates/hir-ty/src/mir/lower/pattern_matching.rs index c722a807d751..c3a4814a3ab4 100644 --- a/crates/hir-ty/src/mir/lower/pattern_matching.rs +++ b/crates/hir-ty/src/mir/lower/pattern_matching.rs @@ -207,7 +207,7 @@ impl<'db> MirLowerCtx<'_, 'db> { mode, )? } - Pat::Range { start, end } => { + Pat::Range { start, end, range_type: _ } => { let mut add_check = |l: &ExprId, binop| -> Result<'db, ()> { let lv = self.lower_literal_or_const_to_operand(self.infer[pattern], l)?; let else_target = *current_else.get_or_insert_with(|| self.new_basic_block()); diff --git a/crates/hir-ty/src/tests/patterns.rs b/crates/hir-ty/src/tests/patterns.rs index 5d81d52ec701..5e150e2bcc6b 100644 --- a/crates/hir-ty/src/tests/patterns.rs +++ b/crates/hir-ty/src/tests/patterns.rs @@ -189,24 +189,33 @@ fn infer_literal_pattern() { fn infer_range_pattern() { check_infer_with_mismatches( r#" - fn test(x: &i32) { - if let 1..76 = 2u32 {} - if let 1..=76 = 2u32 {} - } +//- minicore: range +fn test(x..y: &core::ops::Range) { + if let 1..76 = 2u32 {} + if let 1..=76 = 2u32 {} +} "#, expect![[r#" - 8..9 'x': &'? i32 - 17..75 '{ ...2 {} }': () - 23..45 'if let...u32 {}': () - 26..42 'let 1....= 2u32': bool - 30..35 '1..76': u32 - 38..42 '2u32': u32 - 43..45 '{}': () - 50..73 'if let...u32 {}': () - 53..70 'let 1....= 2u32': bool - 57..63 '1..=76': u32 - 66..70 '2u32': u32 - 71..73 '{}': () + 8..9 'x': u32 + 8..12 'x..y': Range + 11..12 'y': u32 + 38..96 '{ ...2 {} }': () + 44..66 'if let...u32 {}': () + 47..63 'let 1....= 2u32': bool + 51..52 '1': i32 + 51..56 '1..76': Range + 54..56 '76': i32 + 59..63 '2u32': u32 + 64..66 '{}': () + 71..94 'if let...u32 {}': () + 74..91 'let 1....= 2u32': bool + 78..79 '1': i32 + 78..84 '1..=76': RangeInclusive + 82..84 '76': i32 + 87..91 '2u32': u32 + 92..94 '{}': () + 51..56: expected u32, got Range + 78..84: expected u32, got RangeInclusive "#]], ); }