Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 6ac714d

Browse files
authoredMar 6, 2025
Rollup merge of #136922 - oli-obk:pattern-types-option-ends, r=BoxyUwU
Pattern types: Avoid having to handle an Option for range ends in the type system or the HIR Instead, 1. during hir_ty_lowering, we now generate constants for the min/max when the range doesn't have a start/end specified. 2. in a later commit we generate those constants during ast lowering, simplifying everything further by not having to handle the range end inclusivity anymore in the type system (and thus avoiding any issues of `0..5` being different from `0..=4` I think it makes all the type system code simpler, and the cost of the extra `ConstKind::Value` processing seems negligible. r? `@BoxyUwU` cc `@joshtriplett` `@scottmcm`
2 parents 5b07412 + a2c1211 commit 6ac714d

File tree

39 files changed

+460
-274
lines changed

39 files changed

+460
-274
lines changed
 

‎compiler/rustc_ast_lowering/src/lib.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ struct LoweringContext<'a, 'hir> {
136136

137137
allow_try_trait: Arc<[Symbol]>,
138138
allow_gen_future: Arc<[Symbol]>,
139+
allow_pattern_type: Arc<[Symbol]>,
139140
allow_async_iterator: Arc<[Symbol]>,
140141
allow_for_await: Arc<[Symbol]>,
141142
allow_async_fn_traits: Arc<[Symbol]>,
@@ -176,6 +177,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
176177
impl_trait_defs: Vec::new(),
177178
impl_trait_bounds: Vec::new(),
178179
allow_try_trait: [sym::try_trait_v2, sym::yeet_desugar_details].into(),
180+
allow_pattern_type: [sym::pattern_types, sym::pattern_type_range_trait].into(),
179181
allow_gen_future: if tcx.features().async_fn_track_caller() {
180182
[sym::gen_future, sym::closure_track_caller].into()
181183
} else {
@@ -1365,7 +1367,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
13651367
}
13661368
}
13671369
TyKind::Pat(ty, pat) => {
1368-
hir::TyKind::Pat(self.lower_ty(ty, itctx), self.lower_ty_pat(pat))
1370+
hir::TyKind::Pat(self.lower_ty(ty, itctx), self.lower_ty_pat(pat, ty.span))
13691371
}
13701372
TyKind::MacCall(_) => {
13711373
span_bug!(t.span, "`TyKind::MacCall` should have been expanded by now")

‎compiler/rustc_ast_lowering/src/pat.rs

+112-10
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@ use std::sync::Arc;
33
use rustc_ast::ptr::P;
44
use rustc_ast::*;
55
use rustc_data_structures::stack::ensure_sufficient_stack;
6-
use rustc_hir as hir;
7-
use rustc_hir::def::Res;
6+
use rustc_hir::def::{DefKind, Res};
7+
use rustc_hir::{self as hir, LangItem};
88
use rustc_middle::span_bug;
99
use rustc_span::source_map::{Spanned, respan};
10-
use rustc_span::{Ident, Span};
10+
use rustc_span::{DesugaringKind, Ident, Span, kw};
1111

1212
use super::errors::{
1313
ArbitraryExpressionInPattern, ExtraDoubleDot, MisplacedDoubleDot, SubTupleBinding,
@@ -430,22 +430,124 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
430430
self.arena.alloc(hir::PatExpr { hir_id: self.lower_node_id(expr.id), span, kind })
431431
}
432432

433-
pub(crate) fn lower_ty_pat(&mut self, pattern: &TyPat) -> &'hir hir::TyPat<'hir> {
434-
self.arena.alloc(self.lower_ty_pat_mut(pattern))
433+
pub(crate) fn lower_ty_pat(
434+
&mut self,
435+
pattern: &TyPat,
436+
base_type: Span,
437+
) -> &'hir hir::TyPat<'hir> {
438+
self.arena.alloc(self.lower_ty_pat_mut(pattern, base_type))
435439
}
436440

437-
fn lower_ty_pat_mut(&mut self, pattern: &TyPat) -> hir::TyPat<'hir> {
441+
fn lower_ty_pat_mut(&mut self, pattern: &TyPat, base_type: Span) -> hir::TyPat<'hir> {
438442
// loop here to avoid recursion
439443
let pat_hir_id = self.lower_node_id(pattern.id);
440444
let node = match &pattern.kind {
441-
TyPatKind::Range(e1, e2, Spanned { node: end, .. }) => hir::TyPatKind::Range(
442-
e1.as_deref().map(|e| self.lower_anon_const_to_const_arg(e)),
443-
e2.as_deref().map(|e| self.lower_anon_const_to_const_arg(e)),
444-
self.lower_range_end(end, e2.is_some()),
445+
TyPatKind::Range(e1, e2, Spanned { node: end, span }) => hir::TyPatKind::Range(
446+
e1.as_deref().map(|e| self.lower_anon_const_to_const_arg(e)).unwrap_or_else(|| {
447+
self.lower_ty_pat_range_end(
448+
hir::LangItem::RangeMin,
449+
span.shrink_to_lo(),
450+
base_type,
451+
)
452+
}),
453+
e2.as_deref()
454+
.map(|e| match end {
455+
RangeEnd::Included(..) => self.lower_anon_const_to_const_arg(e),
456+
RangeEnd::Excluded => self.lower_excluded_range_end(e),
457+
})
458+
.unwrap_or_else(|| {
459+
self.lower_ty_pat_range_end(
460+
hir::LangItem::RangeMax,
461+
span.shrink_to_hi(),
462+
base_type,
463+
)
464+
}),
445465
),
446466
TyPatKind::Err(guar) => hir::TyPatKind::Err(*guar),
447467
};
448468

449469
hir::TyPat { hir_id: pat_hir_id, kind: node, span: self.lower_span(pattern.span) }
450470
}
471+
472+
/// Lowers the range end of an exclusive range (`2..5`) to an inclusive range 2..=(5 - 1).
473+
/// This way the type system doesn't have to handle the distinction between inclusive/exclusive ranges.
474+
fn lower_excluded_range_end(&mut self, e: &AnonConst) -> &'hir hir::ConstArg<'hir> {
475+
let span = self.lower_span(e.value.span);
476+
let unstable_span = self.mark_span_with_reason(
477+
DesugaringKind::PatTyRange,
478+
span,
479+
Some(Arc::clone(&self.allow_pattern_type)),
480+
);
481+
let anon_const = self.with_new_scopes(span, |this| {
482+
let def_id = this.local_def_id(e.id);
483+
let hir_id = this.lower_node_id(e.id);
484+
let body = this.lower_body(|this| {
485+
// Need to use a custom function as we can't just subtract `1` from a `char`.
486+
let kind = hir::ExprKind::Path(this.make_lang_item_qpath(
487+
hir::LangItem::RangeSub,
488+
unstable_span,
489+
None,
490+
));
491+
let fn_def = this.arena.alloc(hir::Expr { hir_id: this.next_id(), kind, span });
492+
let args = this.arena.alloc([this.lower_expr_mut(&e.value)]);
493+
(
494+
&[],
495+
hir::Expr {
496+
hir_id: this.next_id(),
497+
kind: hir::ExprKind::Call(fn_def, args),
498+
span,
499+
},
500+
)
501+
});
502+
hir::AnonConst { def_id, hir_id, body, span }
503+
});
504+
self.arena.alloc(hir::ConstArg {
505+
hir_id: self.next_id(),
506+
kind: hir::ConstArgKind::Anon(self.arena.alloc(anon_const)),
507+
})
508+
}
509+
510+
/// When a range has no end specified (`1..` or `1..=`) or no start specified (`..5` or `..=5`),
511+
/// we instead use a constant of the MAX/MIN of the type.
512+
/// This way the type system does not have to handle the lack of a start/end.
513+
fn lower_ty_pat_range_end(
514+
&mut self,
515+
lang_item: LangItem,
516+
span: Span,
517+
base_type: Span,
518+
) -> &'hir hir::ConstArg<'hir> {
519+
let parent_def_id = self.current_hir_id_owner.def_id;
520+
let node_id = self.next_node_id();
521+
522+
// Add a definition for the in-band const def.
523+
// We're generating a range end that didn't exist in the AST,
524+
// so the def collector didn't create the def ahead of time. That's why we have to do
525+
// it here.
526+
let def_id = self.create_def(parent_def_id, node_id, kw::Empty, DefKind::AnonConst, span);
527+
let hir_id = self.lower_node_id(node_id);
528+
529+
let unstable_span = self.mark_span_with_reason(
530+
DesugaringKind::PatTyRange,
531+
self.lower_span(span),
532+
Some(Arc::clone(&self.allow_pattern_type)),
533+
);
534+
let span = self.lower_span(base_type);
535+
536+
let path_expr = hir::Expr {
537+
hir_id: self.next_id(),
538+
kind: hir::ExprKind::Path(self.make_lang_item_qpath(lang_item, unstable_span, None)),
539+
span,
540+
};
541+
542+
let ct = self.with_new_scopes(span, |this| {
543+
self.arena.alloc(hir::AnonConst {
544+
def_id,
545+
hir_id,
546+
body: this.lower_body(|_this| (&[], path_expr)),
547+
span,
548+
})
549+
});
550+
let hir_id = self.next_id();
551+
self.arena.alloc(hir::ConstArg { kind: hir::ConstArgKind::Anon(ct), hir_id })
552+
}
451553
}

‎compiler/rustc_hir/src/hir.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1600,7 +1600,7 @@ pub struct PatField<'hir> {
16001600
pub span: Span,
16011601
}
16021602

1603-
#[derive(Copy, Clone, PartialEq, Debug, HashStable_Generic)]
1603+
#[derive(Copy, Clone, PartialEq, Debug, HashStable_Generic, Hash, Eq, Encodable, Decodable)]
16041604
pub enum RangeEnd {
16051605
Included,
16061606
Excluded,
@@ -1668,7 +1668,7 @@ pub enum PatExprKind<'hir> {
16681668
#[derive(Debug, Clone, Copy, HashStable_Generic)]
16691669
pub enum TyPatKind<'hir> {
16701670
/// A range pattern (e.g., `1..=2` or `1..2`).
1671-
Range(Option<&'hir ConstArg<'hir>>, Option<&'hir ConstArg<'hir>>, RangeEnd),
1671+
Range(&'hir ConstArg<'hir>, &'hir ConstArg<'hir>),
16721672

16731673
/// A placeholder for a pattern that wasn't well formed in some way.
16741674
Err(ErrorGuaranteed),

‎compiler/rustc_hir/src/intravisit.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -708,9 +708,9 @@ pub fn walk_arm<'v, V: Visitor<'v>>(visitor: &mut V, arm: &'v Arm<'v>) -> V::Res
708708
pub fn walk_ty_pat<'v, V: Visitor<'v>>(visitor: &mut V, pattern: &'v TyPat<'v>) -> V::Result {
709709
try_visit!(visitor.visit_id(pattern.hir_id));
710710
match pattern.kind {
711-
TyPatKind::Range(lower_bound, upper_bound, _) => {
712-
visit_opt!(visitor, visit_const_arg_unambig, lower_bound);
713-
visit_opt!(visitor, visit_const_arg_unambig, upper_bound);
711+
TyPatKind::Range(lower_bound, upper_bound) => {
712+
try_visit!(visitor.visit_const_arg_unambig(lower_bound));
713+
try_visit!(visitor.visit_const_arg_unambig(upper_bound));
714714
}
715715
TyPatKind::Err(_) => (),
716716
}

‎compiler/rustc_hir/src/lang_items.rs

+3
Original file line numberDiff line numberDiff line change
@@ -418,6 +418,9 @@ language_item_table! {
418418
Range, sym::Range, range_struct, Target::Struct, GenericRequirement::None;
419419
RangeToInclusive, sym::RangeToInclusive, range_to_inclusive_struct, Target::Struct, GenericRequirement::None;
420420
RangeTo, sym::RangeTo, range_to_struct, Target::Struct, GenericRequirement::None;
421+
RangeMax, sym::RangeMax, range_max, Target::AssocConst, GenericRequirement::Exact(0);
422+
RangeMin, sym::RangeMin, range_min, Target::AssocConst, GenericRequirement::Exact(0);
423+
RangeSub, sym::RangeSub, range_sub, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::Exact(0);
421424

422425
// `new_range` types that are `Copy + IntoIterator`
423426
RangeFromCopy, sym::RangeFromCopy, range_from_copy_struct, Target::Struct, GenericRequirement::None;

‎compiler/rustc_hir_analysis/messages.ftl

-3
Original file line numberDiff line numberDiff line change
@@ -244,9 +244,6 @@ hir_analysis_inherent_ty_outside_relevant = cannot define inherent `impl` for a
244244
.help = consider moving this inherent impl into the crate defining the type if possible
245245
.span_help = alternatively add `#[rustc_allow_incoherent_impl]` to the relevant impl items
246246
247-
hir_analysis_invalid_base_type = `{$ty}` is not a valid base type for range patterns
248-
.note = range patterns only support integers
249-
250247
hir_analysis_invalid_generic_receiver_ty = invalid generic `self` parameter type: `{$receiver_ty}`
251248
.note = type of `self` must not be a method generic parameter type
252249

‎compiler/rustc_hir_analysis/src/errors.rs

-2
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,6 @@ use rustc_middle::ty::Ty;
1111
use rustc_span::{Ident, Span, Symbol};
1212

1313
use crate::fluent_generated as fluent;
14-
mod pattern_types;
15-
pub(crate) use pattern_types::*;
1614
pub(crate) mod wrong_number_of_generic_args;
1715

1816
mod precise_captures;

‎compiler/rustc_hir_analysis/src/errors/pattern_types.rs

-14
This file was deleted.

‎compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs

+18-22
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,7 @@ use tracing::{debug, instrument};
5555

5656
use self::errors::assoc_kind_str;
5757
use crate::check::check_abi_fn_ptr;
58-
use crate::errors::{
59-
AmbiguousLifetimeBound, BadReturnTypeNotation, InvalidBaseType, NoVariantNamed,
60-
};
58+
use crate::errors::{AmbiguousLifetimeBound, BadReturnTypeNotation, NoVariantNamed};
6159
use crate::hir_ty_lowering::errors::{GenericsArgsErrExtend, prohibit_assoc_item_constraint};
6260
use crate::hir_ty_lowering::generics::{check_generic_arg_count, lower_generic_args};
6361
use crate::middle::resolve_bound_vars as rbv;
@@ -2692,28 +2690,26 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
26922690
let ty_span = ty.span;
26932691
let ty = self.lower_ty(ty);
26942692
let pat_ty = match pat.kind {
2695-
hir::TyPatKind::Range(start, end, include_end) => {
2696-
let ty = match ty.kind() {
2697-
ty::Int(_) | ty::Uint(_) | ty::Char => ty,
2698-
_ => Ty::new_error(
2699-
tcx,
2700-
self.dcx().emit_err(InvalidBaseType {
2701-
ty,
2702-
pat: "range",
2693+
hir::TyPatKind::Range(start, end) => {
2694+
let (ty, start, end) = match ty.kind() {
2695+
// Keep this list of types in sync with the list of types that
2696+
// the `RangePattern` trait is implemented for.
2697+
ty::Int(_) | ty::Uint(_) | ty::Char => {
2698+
let start = self.lower_const_arg(start, FeedConstTy::No);
2699+
let end = self.lower_const_arg(end, FeedConstTy::No);
2700+
(ty, start, end)
2701+
}
2702+
_ => {
2703+
let guar = self.dcx().span_delayed_bug(
27032704
ty_span,
2704-
pat_span: pat.span,
2705-
}),
2706-
),
2707-
};
2708-
let start = start.map(|expr| self.lower_const_arg(expr, FeedConstTy::No));
2709-
let end = end.map(|expr| self.lower_const_arg(expr, FeedConstTy::No));
2710-
2711-
let include_end = match include_end {
2712-
hir::RangeEnd::Included => true,
2713-
hir::RangeEnd::Excluded => false,
2705+
"invalid base type for range pattern",
2706+
);
2707+
let errc = ty::Const::new_error(tcx, guar);
2708+
(Ty::new_error(tcx, guar), errc, errc)
2709+
}
27142710
};
27152711

2716-
let pat = tcx.mk_pat(ty::PatternKind::Range { start, end, include_end });
2712+
let pat = tcx.mk_pat(ty::PatternKind::Range { start, end });
27172713
Ty::new_pat(tcx, ty, pat)
27182714
}
27192715
hir::TyPatKind::Err(e) => Ty::new_error(tcx, e),

‎compiler/rustc_hir_analysis/src/variance/constraints.rs

+3-7
Original file line numberDiff line numberDiff line change
@@ -252,13 +252,9 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> {
252252

253253
ty::Pat(typ, pat) => {
254254
match *pat {
255-
ty::PatternKind::Range { start, end, include_end: _ } => {
256-
if let Some(start) = start {
257-
self.add_constraints_from_const(current, start, variance);
258-
}
259-
if let Some(end) = end {
260-
self.add_constraints_from_const(current, end, variance);
261-
}
255+
ty::PatternKind::Range { start, end } => {
256+
self.add_constraints_from_const(current, start, variance);
257+
self.add_constraints_from_const(current, end, variance);
262258
}
263259
}
264260
self.add_constraints_from_ty(current, typ, variance);

‎compiler/rustc_hir_pretty/src/lib.rs

+4-11
Original file line numberDiff line numberDiff line change
@@ -1943,17 +1943,10 @@ impl<'a> State<'a> {
19431943
// Pat isn't normalized, but the beauty of it
19441944
// is that it doesn't matter
19451945
match pat.kind {
1946-
TyPatKind::Range(begin, end, end_kind) => {
1947-
if let Some(expr) = begin {
1948-
self.print_const_arg(expr);
1949-
}
1950-
match end_kind {
1951-
RangeEnd::Included => self.word("..."),
1952-
RangeEnd::Excluded => self.word(".."),
1953-
}
1954-
if let Some(expr) = end {
1955-
self.print_const_arg(expr);
1956-
}
1946+
TyPatKind::Range(begin, end) => {
1947+
self.print_const_arg(begin);
1948+
self.word("..=");
1949+
self.print_const_arg(end);
19571950
}
19581951
TyPatKind::Err(_) => {
19591952
self.popen();

‎compiler/rustc_lint/src/types.rs

+7-21
Original file line numberDiff line numberDiff line change
@@ -882,27 +882,13 @@ fn ty_is_known_nonnull<'tcx>(
882882
|| Option::unwrap_or_default(
883883
try {
884884
match **pat {
885-
ty::PatternKind::Range { start, end, include_end } => {
886-
match (start, end) {
887-
(Some(start), None) => {
888-
start.try_to_value()?.try_to_bits(tcx, typing_env)? > 0
889-
}
890-
(Some(start), Some(end)) => {
891-
let start =
892-
start.try_to_value()?.try_to_bits(tcx, typing_env)?;
893-
let end =
894-
end.try_to_value()?.try_to_bits(tcx, typing_env)?;
895-
896-
if include_end {
897-
// This also works for negative numbers, as we just need
898-
// to ensure we aren't wrapping over zero.
899-
start > 0 && end >= start
900-
} else {
901-
start > 0 && end > start
902-
}
903-
}
904-
_ => false,
905-
}
885+
ty::PatternKind::Range { start, end } => {
886+
let start = start.try_to_value()?.try_to_bits(tcx, typing_env)?;
887+
let end = end.try_to_value()?.try_to_bits(tcx, typing_env)?;
888+
889+
// This also works for negative numbers, as we just need
890+
// to ensure we aren't wrapping over zero.
891+
start > 0 && end >= start
906892
}
907893
}
908894
},

‎compiler/rustc_middle/src/ty/flags.rs

+3-7
Original file line numberDiff line numberDiff line change
@@ -220,13 +220,9 @@ impl FlagComputation {
220220
&ty::Pat(ty, pat) => {
221221
self.add_ty(ty);
222222
match *pat {
223-
ty::PatternKind::Range { start, end, include_end: _ } => {
224-
if let Some(start) = start {
225-
self.add_const(start)
226-
}
227-
if let Some(end) = end {
228-
self.add_const(end)
229-
}
223+
ty::PatternKind::Range { start, end } => {
224+
self.add_const(start);
225+
self.add_const(end);
230226
}
231227
}
232228
}
There was a problem loading the remainder of the diff.

0 commit comments

Comments
 (0)
Failed to load comments.