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`
This commit is contained in:
Michael Goulet 2025-03-06 12:22:10 -05:00 committed by GitHub
commit 6ac714d526
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
39 changed files with 467 additions and 281 deletions

View File

@ -136,6 +136,7 @@ struct LoweringContext<'a, 'hir> {
allow_try_trait: Arc<[Symbol]>,
allow_gen_future: Arc<[Symbol]>,
allow_pattern_type: Arc<[Symbol]>,
allow_async_iterator: Arc<[Symbol]>,
allow_for_await: Arc<[Symbol]>,
allow_async_fn_traits: Arc<[Symbol]>,
@ -176,6 +177,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
impl_trait_defs: Vec::new(),
impl_trait_bounds: Vec::new(),
allow_try_trait: [sym::try_trait_v2, sym::yeet_desugar_details].into(),
allow_pattern_type: [sym::pattern_types, sym::pattern_type_range_trait].into(),
allow_gen_future: if tcx.features().async_fn_track_caller() {
[sym::gen_future, sym::closure_track_caller].into()
} else {
@ -1365,7 +1367,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
}
}
TyKind::Pat(ty, pat) => {
hir::TyKind::Pat(self.lower_ty(ty, itctx), self.lower_ty_pat(pat))
hir::TyKind::Pat(self.lower_ty(ty, itctx), self.lower_ty_pat(pat, ty.span))
}
TyKind::MacCall(_) => {
span_bug!(t.span, "`TyKind::MacCall` should have been expanded by now")

View File

@ -3,11 +3,11 @@ use std::sync::Arc;
use rustc_ast::ptr::P;
use rustc_ast::*;
use rustc_data_structures::stack::ensure_sufficient_stack;
use rustc_hir as hir;
use rustc_hir::def::Res;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::{self as hir, LangItem};
use rustc_middle::span_bug;
use rustc_span::source_map::{Spanned, respan};
use rustc_span::{Ident, Span};
use rustc_span::{DesugaringKind, Ident, Span, kw};
use super::errors::{
ArbitraryExpressionInPattern, ExtraDoubleDot, MisplacedDoubleDot, SubTupleBinding,
@ -430,22 +430,124 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
self.arena.alloc(hir::PatExpr { hir_id: self.lower_node_id(expr.id), span, kind })
}
pub(crate) fn lower_ty_pat(&mut self, pattern: &TyPat) -> &'hir hir::TyPat<'hir> {
self.arena.alloc(self.lower_ty_pat_mut(pattern))
pub(crate) fn lower_ty_pat(
&mut self,
pattern: &TyPat,
base_type: Span,
) -> &'hir hir::TyPat<'hir> {
self.arena.alloc(self.lower_ty_pat_mut(pattern, base_type))
}
fn lower_ty_pat_mut(&mut self, pattern: &TyPat) -> hir::TyPat<'hir> {
fn lower_ty_pat_mut(&mut self, pattern: &TyPat, base_type: Span) -> hir::TyPat<'hir> {
// loop here to avoid recursion
let pat_hir_id = self.lower_node_id(pattern.id);
let node = match &pattern.kind {
TyPatKind::Range(e1, e2, Spanned { node: end, .. }) => hir::TyPatKind::Range(
e1.as_deref().map(|e| self.lower_anon_const_to_const_arg(e)),
e2.as_deref().map(|e| self.lower_anon_const_to_const_arg(e)),
self.lower_range_end(end, e2.is_some()),
TyPatKind::Range(e1, e2, Spanned { node: end, span }) => hir::TyPatKind::Range(
e1.as_deref().map(|e| self.lower_anon_const_to_const_arg(e)).unwrap_or_else(|| {
self.lower_ty_pat_range_end(
hir::LangItem::RangeMin,
span.shrink_to_lo(),
base_type,
)
}),
e2.as_deref()
.map(|e| match end {
RangeEnd::Included(..) => self.lower_anon_const_to_const_arg(e),
RangeEnd::Excluded => self.lower_excluded_range_end(e),
})
.unwrap_or_else(|| {
self.lower_ty_pat_range_end(
hir::LangItem::RangeMax,
span.shrink_to_hi(),
base_type,
)
}),
),
TyPatKind::Err(guar) => hir::TyPatKind::Err(*guar),
};
hir::TyPat { hir_id: pat_hir_id, kind: node, span: self.lower_span(pattern.span) }
}
/// 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, e: &AnonConst) -> &'hir hir::ConstArg<'hir> {
let span = self.lower_span(e.value.span);
let unstable_span = self.mark_span_with_reason(
DesugaringKind::PatTyRange,
span,
Some(Arc::clone(&self.allow_pattern_type)),
);
let anon_const = self.with_new_scopes(span, |this| {
let def_id = this.local_def_id(e.id);
let hir_id = this.lower_node_id(e.id);
let body = this.lower_body(|this| {
// Need to use a custom function as we can't just subtract `1` from a `char`.
let kind = hir::ExprKind::Path(this.make_lang_item_qpath(
hir::LangItem::RangeSub,
unstable_span,
None,
));
let fn_def = this.arena.alloc(hir::Expr { hir_id: this.next_id(), kind, span });
let args = this.arena.alloc([this.lower_expr_mut(&e.value)]);
(
&[],
hir::Expr {
hir_id: this.next_id(),
kind: hir::ExprKind::Call(fn_def, args),
span,
},
)
});
hir::AnonConst { def_id, hir_id, body, span }
});
self.arena.alloc(hir::ConstArg {
hir_id: self.next_id(),
kind: hir::ConstArgKind::Anon(self.arena.alloc(anon_const)),
})
}
/// 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: LangItem,
span: Span,
base_type: Span,
) -> &'hir hir::ConstArg<'hir> {
let parent_def_id = self.current_hir_id_owner.def_id;
let node_id = self.next_node_id();
// Add a definition for the in-band const def.
// We're generating a range end that didn't exist in the AST,
// so the def collector didn't create the def ahead of time. That's why we have to do
// it here.
let def_id = self.create_def(parent_def_id, node_id, kw::Empty, DefKind::AnonConst, span);
let hir_id = self.lower_node_id(node_id);
let unstable_span = self.mark_span_with_reason(
DesugaringKind::PatTyRange,
self.lower_span(span),
Some(Arc::clone(&self.allow_pattern_type)),
);
let span = self.lower_span(base_type);
let path_expr = hir::Expr {
hir_id: self.next_id(),
kind: hir::ExprKind::Path(self.make_lang_item_qpath(lang_item, unstable_span, None)),
span,
};
let ct = self.with_new_scopes(span, |this| {
self.arena.alloc(hir::AnonConst {
def_id,
hir_id,
body: this.lower_body(|_this| (&[], path_expr)),
span,
})
});
let hir_id = self.next_id();
self.arena.alloc(hir::ConstArg { kind: hir::ConstArgKind::Anon(ct), hir_id })
}
}

View File

@ -1600,7 +1600,7 @@ pub struct PatField<'hir> {
pub span: Span,
}
#[derive(Copy, Clone, PartialEq, Debug, HashStable_Generic)]
#[derive(Copy, Clone, PartialEq, Debug, HashStable_Generic, Hash, Eq, Encodable, Decodable)]
pub enum RangeEnd {
Included,
Excluded,
@ -1668,7 +1668,7 @@ pub enum PatExprKind<'hir> {
#[derive(Debug, Clone, Copy, HashStable_Generic)]
pub enum TyPatKind<'hir> {
/// A range pattern (e.g., `1..=2` or `1..2`).
Range(Option<&'hir ConstArg<'hir>>, Option<&'hir ConstArg<'hir>>, RangeEnd),
Range(&'hir ConstArg<'hir>, &'hir ConstArg<'hir>),
/// A placeholder for a pattern that wasn't well formed in some way.
Err(ErrorGuaranteed),

View File

@ -708,9 +708,9 @@ pub fn walk_arm<'v, V: Visitor<'v>>(visitor: &mut V, arm: &'v Arm<'v>) -> V::Res
pub fn walk_ty_pat<'v, V: Visitor<'v>>(visitor: &mut V, pattern: &'v TyPat<'v>) -> V::Result {
try_visit!(visitor.visit_id(pattern.hir_id));
match pattern.kind {
TyPatKind::Range(lower_bound, upper_bound, _) => {
visit_opt!(visitor, visit_const_arg_unambig, lower_bound);
visit_opt!(visitor, visit_const_arg_unambig, upper_bound);
TyPatKind::Range(lower_bound, upper_bound) => {
try_visit!(visitor.visit_const_arg_unambig(lower_bound));
try_visit!(visitor.visit_const_arg_unambig(upper_bound));
}
TyPatKind::Err(_) => (),
}

View File

@ -418,6 +418,9 @@ language_item_table! {
Range, sym::Range, range_struct, Target::Struct, GenericRequirement::None;
RangeToInclusive, sym::RangeToInclusive, range_to_inclusive_struct, Target::Struct, GenericRequirement::None;
RangeTo, sym::RangeTo, range_to_struct, Target::Struct, GenericRequirement::None;
RangeMax, sym::RangeMax, range_max, Target::AssocConst, GenericRequirement::Exact(0);
RangeMin, sym::RangeMin, range_min, Target::AssocConst, GenericRequirement::Exact(0);
RangeSub, sym::RangeSub, range_sub, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::Exact(0);
// `new_range` types that are `Copy + IntoIterator`
RangeFromCopy, sym::RangeFromCopy, range_from_copy_struct, Target::Struct, GenericRequirement::None;

View File

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

View File

@ -11,8 +11,6 @@ use rustc_middle::ty::Ty;
use rustc_span::{Ident, Span, Symbol};
use crate::fluent_generated as fluent;
mod pattern_types;
pub(crate) use pattern_types::*;
pub(crate) mod wrong_number_of_generic_args;
mod precise_captures;

View File

@ -1,14 +0,0 @@
use rustc_macros::Diagnostic;
use rustc_middle::ty::Ty;
use rustc_span::Span;
#[derive(Diagnostic)]
#[diag(hir_analysis_invalid_base_type)]
pub(crate) struct InvalidBaseType<'tcx> {
pub ty: Ty<'tcx>,
#[primary_span]
pub ty_span: Span,
pub pat: &'static str,
#[note]
pub pat_span: Span,
}

View File

@ -55,9 +55,7 @@ use tracing::{debug, instrument};
use self::errors::assoc_kind_str;
use crate::check::check_abi_fn_ptr;
use crate::errors::{
AmbiguousLifetimeBound, BadReturnTypeNotation, InvalidBaseType, NoVariantNamed,
};
use crate::errors::{AmbiguousLifetimeBound, BadReturnTypeNotation, NoVariantNamed};
use crate::hir_ty_lowering::errors::{GenericsArgsErrExtend, prohibit_assoc_item_constraint};
use crate::hir_ty_lowering::generics::{check_generic_arg_count, lower_generic_args};
use crate::middle::resolve_bound_vars as rbv;
@ -2692,28 +2690,26 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
let ty_span = ty.span;
let ty = self.lower_ty(ty);
let pat_ty = match pat.kind {
hir::TyPatKind::Range(start, end, include_end) => {
let ty = match ty.kind() {
ty::Int(_) | ty::Uint(_) | ty::Char => ty,
_ => Ty::new_error(
tcx,
self.dcx().emit_err(InvalidBaseType {
ty,
pat: "range",
hir::TyPatKind::Range(start, end) => {
let (ty, start, end) = match ty.kind() {
// Keep this list of types in sync with the list of types that
// the `RangePattern` trait is implemented for.
ty::Int(_) | ty::Uint(_) | ty::Char => {
let start = self.lower_const_arg(start, FeedConstTy::No);
let end = self.lower_const_arg(end, FeedConstTy::No);
(ty, start, end)
}
_ => {
let guar = self.dcx().span_delayed_bug(
ty_span,
pat_span: pat.span,
}),
),
};
let start = start.map(|expr| self.lower_const_arg(expr, FeedConstTy::No));
let end = end.map(|expr| self.lower_const_arg(expr, FeedConstTy::No));
let include_end = match include_end {
hir::RangeEnd::Included => true,
hir::RangeEnd::Excluded => false,
"invalid base type for range pattern",
);
let errc = ty::Const::new_error(tcx, guar);
(Ty::new_error(tcx, guar), errc, errc)
}
};
let pat = tcx.mk_pat(ty::PatternKind::Range { start, end, include_end });
let pat = tcx.mk_pat(ty::PatternKind::Range { start, end });
Ty::new_pat(tcx, ty, pat)
}
hir::TyPatKind::Err(e) => Ty::new_error(tcx, e),

View File

@ -252,13 +252,9 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> {
ty::Pat(typ, pat) => {
match *pat {
ty::PatternKind::Range { start, end, include_end: _ } => {
if let Some(start) = start {
self.add_constraints_from_const(current, start, variance);
}
if let Some(end) = end {
self.add_constraints_from_const(current, end, variance);
}
ty::PatternKind::Range { start, end } => {
self.add_constraints_from_const(current, start, variance);
self.add_constraints_from_const(current, end, variance);
}
}
self.add_constraints_from_ty(current, typ, variance);

View File

@ -1943,17 +1943,10 @@ impl<'a> State<'a> {
// Pat isn't normalized, but the beauty of it
// is that it doesn't matter
match pat.kind {
TyPatKind::Range(begin, end, end_kind) => {
if let Some(expr) = begin {
self.print_const_arg(expr);
}
match end_kind {
RangeEnd::Included => self.word("..."),
RangeEnd::Excluded => self.word(".."),
}
if let Some(expr) = end {
self.print_const_arg(expr);
}
TyPatKind::Range(begin, end) => {
self.print_const_arg(begin);
self.word("..=");
self.print_const_arg(end);
}
TyPatKind::Err(_) => {
self.popen();

View File

@ -882,27 +882,13 @@ fn ty_is_known_nonnull<'tcx>(
|| Option::unwrap_or_default(
try {
match **pat {
ty::PatternKind::Range { start, end, include_end } => {
match (start, end) {
(Some(start), None) => {
start.try_to_value()?.try_to_bits(tcx, typing_env)? > 0
}
(Some(start), Some(end)) => {
let start =
start.try_to_value()?.try_to_bits(tcx, typing_env)?;
let end =
end.try_to_value()?.try_to_bits(tcx, typing_env)?;
ty::PatternKind::Range { start, end } => {
let start = start.try_to_value()?.try_to_bits(tcx, typing_env)?;
let end = end.try_to_value()?.try_to_bits(tcx, typing_env)?;
if include_end {
// This also works for negative numbers, as we just need
// to ensure we aren't wrapping over zero.
start > 0 && end >= start
} else {
start > 0 && end > start
}
}
_ => false,
}
// This also works for negative numbers, as we just need
// to ensure we aren't wrapping over zero.
start > 0 && end >= start
}
}
},

View File

@ -220,13 +220,9 @@ impl FlagComputation {
&ty::Pat(ty, pat) => {
self.add_ty(ty);
match *pat {
ty::PatternKind::Range { start, end, include_end: _ } => {
if let Some(start) = start {
self.add_const(start)
}
if let Some(end) = end {
self.add_const(end)
}
ty::PatternKind::Range { start, end } => {
self.add_const(start);
self.add_const(end);
}
}
}

View File

@ -26,18 +26,30 @@ impl<'tcx> fmt::Debug for Pattern<'tcx> {
impl<'tcx> fmt::Debug for PatternKind<'tcx> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
PatternKind::Range { start, end, include_end } => {
if let Some(start) = start {
write!(f, "{start}")?;
PatternKind::Range { start, end } => {
write!(f, "{start}")?;
if let Some(c) = end.try_to_value() {
let end = c.valtree.unwrap_leaf();
let size = end.size();
let max = match c.ty.kind() {
ty::Int(_) => {
Some(ty::ScalarInt::truncate_from_int(size.signed_int_max(), size))
}
ty::Uint(_) => {
Some(ty::ScalarInt::truncate_from_uint(size.unsigned_int_max(), size))
}
ty::Char => Some(ty::ScalarInt::truncate_from_uint(char::MAX, size)),
_ => None,
};
if let Some((max, _)) = max
&& end == max
{
return write!(f, "..");
}
}
write!(f, "..")?;
if include_end {
write!(f, "=")?;
}
if let Some(end) = end {
write!(f, "{end}")?;
}
Ok(())
write!(f, "..={end}")
}
}
}
@ -46,5 +58,5 @@ impl<'tcx> fmt::Debug for PatternKind<'tcx> {
#[derive(Clone, PartialEq, Eq, Hash)]
#[derive(HashStable, TyEncodable, TyDecodable, TypeVisitable, TypeFoldable)]
pub enum PatternKind<'tcx> {
Range { start: Option<ty::Const<'tcx>>, end: Option<ty::Const<'tcx>>, include_end: bool },
Range { start: ty::Const<'tcx>, end: ty::Const<'tcx> },
}

View File

@ -51,22 +51,12 @@ impl<'tcx> Relate<TyCtxt<'tcx>> for ty::Pattern<'tcx> {
) -> RelateResult<'tcx, Self> {
match (&*a, &*b) {
(
&ty::PatternKind::Range { start: start_a, end: end_a, include_end: inc_a },
&ty::PatternKind::Range { start: start_b, end: end_b, include_end: inc_b },
&ty::PatternKind::Range { start: start_a, end: end_a },
&ty::PatternKind::Range { start: start_b, end: end_b },
) => {
// FIXME(pattern_types): make equal patterns equal (`0..=` is the same as `..=`).
let mut relate_opt_const = |a, b| match (a, b) {
(None, None) => Ok(None),
(Some(a), Some(b)) => relation.relate(a, b).map(Some),
// FIXME(pattern_types): report a better error
_ => Err(TypeError::Mismatch),
};
let start = relate_opt_const(start_a, start_b)?;
let end = relate_opt_const(end_a, end_b)?;
if inc_a != inc_b {
todo!()
}
Ok(relation.cx().mk_pat(ty::PatternKind::Range { start, end, include_end: inc_a }))
let start = relation.relate(start_a, start_b)?;
let end = relation.relate(end_a, end_b)?;
Ok(relation.cx().mk_pat(ty::PatternKind::Range { start, end }))
}
}
}

View File

@ -284,6 +284,7 @@ TrivialTypeTraversalImpls! {
rustc_hir::def_id::LocalDefId,
rustc_hir::HirId,
rustc_hir::MatchSource,
rustc_hir::RangeEnd,
rustc_span::Ident,
rustc_span::Span,
rustc_span::Symbol,

View File

@ -137,9 +137,9 @@ fn push_inner<'tcx>(stack: &mut TypeWalkerStack<'tcx>, parent: GenericArg<'tcx>)
ty::Pat(ty, pat) => {
match *pat {
ty::PatternKind::Range { start, end, include_end: _ } => {
stack.extend(end.map(Into::into));
stack.extend(start.map(Into::into));
ty::PatternKind::Range { start, end } => {
stack.push(end.into());
stack.push(start.into());
}
}
stack.push(ty.into());

View File

@ -88,10 +88,9 @@ impl RustcInternal for Pattern {
type T<'tcx> = rustc_ty::Pattern<'tcx>;
fn internal<'tcx>(&self, tables: &mut Tables<'_>, tcx: TyCtxt<'tcx>) -> Self::T<'tcx> {
tcx.mk_pat(match self {
Pattern::Range { start, end, include_end } => rustc_ty::PatternKind::Range {
start: start.as_ref().map(|c| c.internal(tables, tcx)),
end: end.as_ref().map(|c| c.internal(tables, tcx)),
include_end: *include_end,
Pattern::Range { start, end, include_end: _ } => rustc_ty::PatternKind::Range {
start: start.as_ref().unwrap().internal(tables, tcx),
end: end.as_ref().unwrap().internal(tables, tcx),
},
})
}

View File

@ -405,10 +405,11 @@ impl<'tcx> Stable<'tcx> for ty::Pattern<'tcx> {
fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
match **self {
ty::PatternKind::Range { start, end, include_end } => stable_mir::ty::Pattern::Range {
start: start.stable(tables),
end: end.stable(tables),
include_end,
ty::PatternKind::Range { start, end } => stable_mir::ty::Pattern::Range {
// FIXME(SMIR): update data structures to not have an Option here anymore
start: Some(start.stable(tables)),
end: Some(end.stable(tables)),
include_end: true,
},
}
}

View File

@ -1173,6 +1173,8 @@ pub enum DesugaringKind {
BoundModifier,
/// Calls to contract checks (`#[requires]` to precond, `#[ensures]` to postcond)
Contract,
/// A pattern type range start/end
PatTyRange,
}
impl DesugaringKind {
@ -1190,6 +1192,7 @@ impl DesugaringKind {
DesugaringKind::WhileLoop => "`while` loop",
DesugaringKind::BoundModifier => "trait bound modifier",
DesugaringKind::Contract => "contract check",
DesugaringKind::PatTyRange => "pattern type",
}
}
}

View File

@ -308,6 +308,9 @@ symbols! {
RangeFull,
RangeInclusive,
RangeInclusiveCopy,
RangeMax,
RangeMin,
RangeSub,
RangeTo,
RangeToInclusive,
Rc,
@ -1522,6 +1525,7 @@ symbols! {
pattern_complexity_limit,
pattern_parentheses,
pattern_type,
pattern_type_range_trait,
pattern_types,
permissions_from_mode,
phantom_data,

View File

@ -413,12 +413,8 @@ impl<'tcx> Printer<'tcx> for SymbolMangler<'tcx> {
}
ty::Pat(ty, pat) => match *pat {
ty::PatternKind::Range { start, end, include_end } => {
let consts = [
start.unwrap_or(self.tcx.consts.unit),
end.unwrap_or(self.tcx.consts.unit),
ty::Const::from_bool(self.tcx, include_end),
];
ty::PatternKind::Range { start, end } => {
let consts = [start, end];
// HACK: Represent as tuple until we have something better.
// HACK: constants are used in arrays, even if the types don't match.
self.push("T");

View File

@ -708,7 +708,7 @@ impl<'a, 'tcx> TypeVisitor<TyCtxt<'tcx>> for WfPredicates<'a, 'tcx> {
ty::Pat(subty, pat) => {
self.require_sized(subty, ObligationCauseCode::Misc);
match *pat {
ty::PatternKind::Range { start, end, include_end: _ } => {
ty::PatternKind::Range { start, end } => {
let mut check = |c| {
let cause = self.cause(ObligationCauseCode::Misc);
self.out.push(traits::Obligation::with_depth(
@ -738,12 +738,8 @@ impl<'a, 'tcx> TypeVisitor<TyCtxt<'tcx>> for WfPredicates<'a, 'tcx> {
}
}
};
if let Some(start) = start {
check(start)
}
if let Some(end) = end {
check(end)
}
check(start);
check(end);
}
}
}

View File

@ -205,24 +205,17 @@ fn layout_of_uncached<'tcx>(
let layout = cx.layout_of(ty)?.layout;
let mut layout = LayoutData::clone(&layout.0);
match *pat {
ty::PatternKind::Range { start, end, include_end } => {
ty::PatternKind::Range { start, end } => {
if let BackendRepr::Scalar(scalar) | BackendRepr::ScalarPair(scalar, _) =
&mut layout.backend_repr
{
if let Some(start) = start {
scalar.valid_range_mut().start = extract_const_value(cx, ty, start)?
.try_to_bits(tcx, cx.typing_env)
.ok_or_else(|| error(cx, LayoutError::Unknown(ty)))?;
}
if let Some(end) = end {
let mut end = extract_const_value(cx, ty, end)?
.try_to_bits(tcx, cx.typing_env)
.ok_or_else(|| error(cx, LayoutError::Unknown(ty)))?;
if !include_end {
end = end.wrapping_sub(1);
}
scalar.valid_range_mut().end = end;
}
scalar.valid_range_mut().start = extract_const_value(cx, ty, start)?
.try_to_bits(tcx, cx.typing_env)
.ok_or_else(|| error(cx, LayoutError::Unknown(ty)))?;
scalar.valid_range_mut().end = extract_const_value(cx, ty, end)?
.try_to_bits(tcx, cx.typing_env)
.ok_or_else(|| error(cx, LayoutError::Unknown(ty)))?;
let niche = Niche {
offset: Size::ZERO,

View File

@ -12,3 +12,65 @@ macro_rules! pattern_type {
/* compiler built-in */
};
}
/// A trait implemented for integer types and `char`.
/// Useful in the future for generic pattern types, but
/// used right now to simplify ast lowering of pattern type ranges.
#[unstable(feature = "pattern_type_range_trait", issue = "123646")]
#[rustc_const_unstable(feature = "pattern_type_range_trait", issue = "123646")]
#[const_trait]
#[diagnostic::on_unimplemented(
message = "`{Self}` is not a valid base type for range patterns",
label = "only integer types and `char` are supported"
)]
pub trait RangePattern {
/// Trait version of the inherent `MIN` assoc const.
#[cfg_attr(not(bootstrap), lang = "RangeMin")]
const MIN: Self;
/// Trait version of the inherent `MIN` assoc const.
#[cfg_attr(not(bootstrap), lang = "RangeMax")]
const MAX: Self;
/// A compile-time helper to subtract 1 for exclusive ranges.
#[cfg_attr(not(bootstrap), lang = "RangeSub")]
#[track_caller]
fn sub_one(self) -> Self;
}
macro_rules! impl_range_pat {
($($ty:ty,)*) => {
$(
#[rustc_const_unstable(feature = "pattern_type_range_trait", issue = "123646")]
impl const RangePattern for $ty {
const MIN: $ty = <$ty>::MIN;
const MAX: $ty = <$ty>::MAX;
fn sub_one(self) -> Self {
match self.checked_sub(1) {
Some(val) => val,
None => panic!("exclusive range end at minimum value of type")
}
}
}
)*
}
}
impl_range_pat! {
i8, i16, i32, i64, i128, isize,
u8, u16, u32, u64, u128, usize,
}
#[rustc_const_unstable(feature = "pattern_type_range_trait", issue = "123646")]
impl const RangePattern for char {
const MIN: Self = char::MIN;
const MAX: Self = char::MAX;
fn sub_one(self) -> Self {
match char::from_u32(self as u32 - 1) {
None => panic!("exclusive range to start of valid chars"),
Some(val) => val,
}
}
}

View File

@ -1108,14 +1108,9 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
pub fn hash_ty_pat(&mut self, pat: &TyPat<'_>) {
std::mem::discriminant(&pat.kind).hash(&mut self.s);
match pat.kind {
TyPatKind::Range(s, e, i) => {
if let Some(s) = s {
self.hash_const_arg(s);
}
if let Some(e) = e {
self.hash_const_arg(e);
}
std::mem::discriminant(&i).hash(&mut self.s);
TyPatKind::Range(s, e) => {
self.hash_const_arg(s);
self.hash_const_arg(e);
},
TyPatKind::Err(_) => {},
}

View File

@ -16,7 +16,7 @@ pub fn bar() {
// CHECK: call pattern_type_symbols::foo::<u32>
// CHECK: call void @_RINvC[[CRATE_IDENT:[a-zA-Z0-9]{12}]]_20pattern_type_symbols3foomEB2_
foo::<u32>();
// CHECK: call pattern_type_symbols::foo::<(u32, [(); 0], [(); 999999999], [(); true])>
// CHECK: call void @_RINvC[[CRATE_IDENT]]_20pattern_type_symbols3fooTmAum0_Aum3b9ac9ff_Aub1_EEB2_
// CHECK: call pattern_type_symbols::foo::<(u32, [(); 0], [(); 999999999])>
// CHECK: call void @_RINvC[[CRATE_IDENT]]_20pattern_type_symbols3fooTmAum0_Aum3b9ac9ff_EEB2_
foo::<NanoU32>();
}

View File

@ -3,9 +3,9 @@
fn main() -> () {
let mut _0: ();
scope 1 {
debug x => const 2_u32 is 1..=;
debug x => const 2_u32 is 1..;
scope 2 {
debug y => const {transmute(0x00000000): (u32) is 1..=};
debug y => const {transmute(0x00000000): (u32) is 1..};
}
}

View File

@ -5,8 +5,8 @@ use std::pat::pattern_type;
// EMIT_MIR pattern_types.main.PreCodegen.after.mir
fn main() {
// CHECK: debug x => const 2_u32 is 1..=
// CHECK: debug x => const 2_u32 is 1..
let x: pattern_type!(u32 is 1..) = unsafe { std::mem::transmute(2) };
// CHECK: debug y => const {transmute(0x00000000): (u32) is 1..=}
// CHECK: debug y => const {transmute(0x00000000): (u32) is 1..}
let y: pattern_type!(u32 is 1..) = unsafe { std::mem::transmute(0) };
}

View File

@ -17,7 +17,7 @@ LL | fn hidden_niche_unsafe_cell() -> Option<UnsafeCell<NonZero<usiz
= help: consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum
= note: enum has no representation hint
warning: `extern` block uses type `Option<(usize) is 0..=>`, which is not FFI-safe
warning: `extern` block uses type `Option<(usize) is 0..>`, which is not FFI-safe
--> $DIR/clashing-extern-fn.rs:502:54
|
LL | fn pt_non_zero_usize_opt_full_range() -> Option<pattern_type!(usize is 0..)>;

View File

@ -18,10 +18,12 @@ const BAD_NESTING2: pattern_type!(pattern_type!(i32 is 1..) is ..=-1) = todo!();
const BAD_NESTING3: pattern_type!(pattern_type!(i32 is 1..) is ..0) = todo!();
//~^ ERROR: not a valid base type for range patterns
//~| ERROR: not a valid base type for range patterns
//~| ERROR: mismatched types
const BAD_NESTING4: pattern_type!(() is ..0) = todo!();
//~^ ERROR: not a valid base type for range patterns
//~| ERROR: not a valid base type for range patterns
//~| ERROR: mismatched types
const BAD_NESTING5: pattern_type!(f32 is 1.0 .. 2.0) = todo!();

View File

@ -1,96 +1,184 @@
error: `(u32) is 1..=` is not a valid base type for range patterns
--> $DIR/nested.rs:10:34
|
LL | const BAD_NESTING: pattern_type!(pattern_type!(u32 is 1..) is 0..) = todo!();
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: range patterns only support integers
--> $DIR/nested.rs:10:63
|
LL | const BAD_NESTING: pattern_type!(pattern_type!(u32 is 1..) is 0..) = todo!();
| ^^^
error: `(i32) is 1..=` is not a valid base type for range patterns
--> $DIR/nested.rs:15:35
|
LL | const BAD_NESTING2: pattern_type!(pattern_type!(i32 is 1..) is ..=-1) = todo!();
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: range patterns only support integers
--> $DIR/nested.rs:15:64
|
LL | const BAD_NESTING2: pattern_type!(pattern_type!(i32 is 1..) is ..=-1) = todo!();
| ^^^^^
error: `(i32) is 1..=` is not a valid base type for range patterns
--> $DIR/nested.rs:19:35
|
LL | const BAD_NESTING3: pattern_type!(pattern_type!(i32 is 1..) is ..0) = todo!();
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: range patterns only support integers
--> $DIR/nested.rs:19:64
|
LL | const BAD_NESTING3: pattern_type!(pattern_type!(i32 is 1..) is ..0) = todo!();
| ^^^
error: `()` is not a valid base type for range patterns
--> $DIR/nested.rs:23:35
|
LL | const BAD_NESTING4: pattern_type!(() is ..0) = todo!();
| ^^
|
note: range patterns only support integers
--> $DIR/nested.rs:23:41
|
LL | const BAD_NESTING4: pattern_type!(() is ..0) = todo!();
| ^^^
error: `f32` is not a valid base type for range patterns
--> $DIR/nested.rs:27:35
|
LL | const BAD_NESTING5: pattern_type!(f32 is 1.0 .. 2.0) = todo!();
| ^^^
|
note: range patterns only support integers
--> $DIR/nested.rs:27:42
|
LL | const BAD_NESTING5: pattern_type!(f32 is 1.0 .. 2.0) = todo!();
| ^^^^^^^^^^
error[E0308]: mismatched types
--> $DIR/nested.rs:10:63
|
LL | const BAD_NESTING: pattern_type!(pattern_type!(u32 is 1..) is 0..) = todo!();
| ^ expected `(u32) is 1..=`, found integer
| ^ expected `(u32) is 1..`, found integer
|
= note: expected pattern type `(u32) is 1..=`
= note: expected pattern type `(u32) is 1..`
found type `{integer}`
error[E0277]: `(u32) is 1..` is not a valid base type for range patterns
--> $DIR/nested.rs:10:34
|
LL | const BAD_NESTING: pattern_type!(pattern_type!(u32 is 1..) is 0..) = todo!();
| ^^^^^^^^^^^^^^^^^^^^^^^^^ only integer types and `char` are supported
|
= help: the trait `core::pat::RangePattern` is not implemented for `(u32) is 1..`
= help: the following other types implement trait `core::pat::RangePattern`:
char
i128
i16
i32
i64
i8
isize
u128
and 5 others
error[E0277]: `(i32) is 1..` is not a valid base type for range patterns
--> $DIR/nested.rs:15:35
|
LL | const BAD_NESTING2: pattern_type!(pattern_type!(i32 is 1..) is ..=-1) = todo!();
| ^^^^^^^^^^^^^^^^^^^^^^^^^ only integer types and `char` are supported
|
= help: the trait `core::pat::RangePattern` is not implemented for `(i32) is 1..`
= help: the following other types implement trait `core::pat::RangePattern`:
char
i128
i16
i32
i64
i8
isize
u128
and 5 others
error[E0308]: mismatched types
--> $DIR/nested.rs:15:67
|
LL | const BAD_NESTING2: pattern_type!(pattern_type!(i32 is 1..) is ..=-1) = todo!();
| ^^ expected `(i32) is 1..=`, found integer
| ^^ expected `(i32) is 1..`, found integer
|
= note: expected pattern type `(i32) is 1..=`
= note: expected pattern type `(i32) is 1..`
found type `{integer}`
error[E0277]: `(i32) is 1..` is not a valid base type for range patterns
--> $DIR/nested.rs:19:35
|
LL | const BAD_NESTING3: pattern_type!(pattern_type!(i32 is 1..) is ..0) = todo!();
| ^^^^^^^^^^^^^^^^^^^^^^^^^ only integer types and `char` are supported
|
= help: the trait `core::pat::RangePattern` is not implemented for `(i32) is 1..`
= help: the following other types implement trait `core::pat::RangePattern`:
char
i128
i16
i32
i64
i8
isize
u128
and 5 others
error[E0308]: mismatched types
--> $DIR/nested.rs:19:66
|
LL | const BAD_NESTING3: pattern_type!(pattern_type!(i32 is 1..) is ..0) = todo!();
| ^ expected `(i32) is 1..=`, found integer
| ^
| |
| expected `(i32) is 1..`, found integer
| arguments to this function are incorrect
|
= note: expected pattern type `(i32) is 1..=`
= note: expected pattern type `(i32) is 1..`
found type `{integer}`
help: the return type of this call is `{integer}` due to the type of the argument passed
--> $DIR/nested.rs:19:66
|
LL | const BAD_NESTING3: pattern_type!(pattern_type!(i32 is 1..) is ..0) = todo!();
| ^ this argument influences the return type of `RangeSub`
note: method defined here
--> $SRC_DIR/core/src/pat.rs:LL:COL
error[E0308]: mismatched types
--> $DIR/nested.rs:23:43
error[E0277]: `(i32) is 1..` is not a valid base type for range patterns
--> $DIR/nested.rs:19:66
|
LL | const BAD_NESTING3: pattern_type!(pattern_type!(i32 is 1..) is ..0) = todo!();
| ^ only integer types and `char` are supported
|
= help: the trait `core::pat::RangePattern` is not implemented for `(i32) is 1..`
= help: the following other types implement trait `core::pat::RangePattern`:
char
i128
i16
i32
i64
i8
isize
u128
and 5 others
error[E0277]: `()` is not a valid base type for range patterns
--> $DIR/nested.rs:24:35
|
LL | const BAD_NESTING4: pattern_type!(() is ..0) = todo!();
| ^ expected `()`, found integer
| ^^ only integer types and `char` are supported
|
= help: the trait `core::pat::RangePattern` is not implemented for `()`
= help: the following other types implement trait `core::pat::RangePattern`:
char
i128
i16
i32
i64
i8
isize
u128
and 5 others
error: aborting due to 9 previous errors
error[E0308]: mismatched types
--> $DIR/nested.rs:24:43
|
LL | const BAD_NESTING4: pattern_type!(() is ..0) = todo!();
| ^
| |
| expected `()`, found integer
| arguments to this function are incorrect
|
help: the return type of this call is `{integer}` due to the type of the argument passed
--> $DIR/nested.rs:24:43
|
LL | const BAD_NESTING4: pattern_type!(() is ..0) = todo!();
| ^ this argument influences the return type of `RangeSub`
note: method defined here
--> $SRC_DIR/core/src/pat.rs:LL:COL
For more information about this error, try `rustc --explain E0308`.
error[E0277]: `()` is not a valid base type for range patterns
--> $DIR/nested.rs:24:43
|
LL | const BAD_NESTING4: pattern_type!(() is ..0) = todo!();
| ^ only integer types and `char` are supported
|
= help: the trait `core::pat::RangePattern` is not implemented for `()`
= help: the following other types implement trait `core::pat::RangePattern`:
char
i128
i16
i32
i64
i8
isize
u128
and 5 others
error[E0277]: `f32` is not a valid base type for range patterns
--> $DIR/nested.rs:29:49
|
LL | const BAD_NESTING5: pattern_type!(f32 is 1.0 .. 2.0) = todo!();
| ^^^ only integer types and `char` are supported
|
= help: the trait `core::pat::RangePattern` is not implemented for `f32`
= help: the following other types implement trait `core::pat::RangePattern`:
i128
i16
i32
i64
i8
isize
u128
u16
and 4 others
error: aborting due to 11 previous errors
Some errors have detailed explanations: E0277, E0308.
For more information about an error, try `rustc --explain E0277`.

View File

@ -44,7 +44,7 @@ error: layout_of(NonZero<u32>) = Layout {
LL | type X = std::num::NonZeroU32;
| ^^^^^^
error: layout_of((u32) is 1..=) = Layout {
error: layout_of((u32) is 1..) = Layout {
size: Size(4 bytes),
align: AbiAndPrefAlign {
abi: Align(4 bytes),
@ -83,7 +83,7 @@ error: layout_of((u32) is 1..=) = Layout {
LL | type Y = pattern_type!(u32 is 1..);
| ^^^^^^
error: layout_of(Option<(u32) is 1..=>) = Layout {
error: layout_of(Option<(u32) is 1..>) = Layout {
size: Size(4 bytes),
align: AbiAndPrefAlign {
abi: Align(4 bytes),

View File

@ -4,7 +4,7 @@ error[E0512]: cannot transmute between types of different sizes, or dependently-
LL | let _: Option<u32> = unsafe { std::mem::transmute(z) };
| ^^^^^^^^^^^^^^^^^^^
|
= note: source type: `Option<(u32) is 1..=>` (32 bits)
= note: source type: `Option<(u32) is 1..>` (32 bits)
= note: target type: `Option<u32>` (64 bits)
error: aborting due to 1 previous error

View File

@ -11,5 +11,5 @@ type Z = Option<pattern_type!(u32 is 1..)>;
fn main() {
let x: Y = unsafe { std::mem::transmute(42_u32) };
let x = x + 1_u32; //~ ERROR cannot add `u32` to `(u32) is 1..=`
let x = x + 1_u32; //~ ERROR cannot add `u32` to `(u32) is 1..`
}

View File

@ -1,10 +1,10 @@
error[E0369]: cannot add `u32` to `(u32) is 1..=`
error[E0369]: cannot add `u32` to `(u32) is 1..`
--> $DIR/range_patterns_unusable_math.rs:14:15
|
LL | let x = x + 1_u32;
| - ^ ----- u32
| |
| (u32) is 1..=
| (u32) is 1..
error: aborting due to 1 previous error

View File

@ -1,14 +1,11 @@
//! Check that the range start must be smaller than the range end
//@ known-bug: unknown
//@ failure-status: 101
//@ normalize-stderr: "note: .*\n\n" -> ""
//@ normalize-stderr: "thread 'rustc' panicked.*\n" -> ""
//@ normalize-stderr: "(error: internal compiler error: [^:]+):\d+:\d+: " -> "$1:LL:CC: "
//@ rustc-env:RUST_BACKTRACE=0
#![feature(pattern_types)]
#![feature(pattern_types, const_trait_impl, pattern_type_range_trait)]
#![feature(pattern_type_macro)]
use std::pat::pattern_type;
const NONE: pattern_type!(u8 is 1..0) = unsafe { std::mem::transmute(3_u8) };
//~^ NOTE: exclusive range end at minimum value of type
//~| ERROR: evaluation of constant value failed
fn main() {}

View File

@ -1,17 +1,9 @@
error[E0601]: `main` function not found in crate `reverse_range`
--> $DIR/reverse_range.rs:14:78
error[E0080]: evaluation of constant value failed
--> $DIR/reverse_range.rs:7:36
|
LL | const NONE: pattern_type!(u8 is 1..0) = unsafe { std::mem::transmute(3_u8) };
| ^ consider adding a `main` function to `$DIR/reverse_range.rs`
| ^ evaluation panicked: exclusive range end at minimum value of type
assertion failed: end <= max_value
error: the compiler unexpectedly panicked. this is a bug.
query stack during panic:
#0 [eval_to_allocation_raw] const-evaluating + checking `NONE`
#1 [eval_to_const_value_raw] simplifying constant for the type system `NONE`
... and 1 other queries... use `env RUST_BACKTRACE=1` to see the full query stack
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0601`.
For more information about this error, try `rustc --explain E0080`.

View File

@ -1,6 +1,6 @@
//! Check that pattern types have their validity checked
#![feature(pattern_types)]
#![feature(pattern_types, const_trait_impl, pattern_type_range_trait)]
#![feature(pattern_type_macro)]
use std::pat::pattern_type;