Skip to content

Commit e111586

Browse files
Auto merge of #144591 - RalfJung:pattern-valtrees, r=<try>
Patterns: represent constants as valtrees
2 parents e3514bd + b39c019 commit e111586

File tree

14 files changed

+159
-183
lines changed

14 files changed

+159
-183
lines changed

compiler/rustc_middle/src/thir.rs

Lines changed: 34 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -832,15 +832,18 @@ pub enum PatKind<'tcx> {
832832
},
833833

834834
/// One of the following:
835-
/// * `&str` (represented as a valtree), which will be handled as a string pattern and thus
835+
/// * `&str`, which will be handled as a string pattern and thus
836836
/// exhaustiveness checking will detect if you use the same string twice in different
837837
/// patterns.
838-
/// * integer, bool, char or float (represented as a valtree), which will be handled by
838+
/// * integer, bool, char or float, which will be handled by
839839
/// exhaustiveness to cover exactly its own value, similar to `&str`, but these values are
840840
/// much simpler.
841841
/// * `String`, if `string_deref_patterns` is enabled.
842842
Constant {
843-
value: mir::Const<'tcx>,
843+
// Not using `ty::Value` since this is conceptually not a type-level constant. In
844+
// particular, it can have raw pointers.
845+
ty: Ty<'tcx>,
846+
value: ty::ValTree<'tcx>,
844847
},
845848

846849
/// Pattern obtained by converting a constant (inline or named) to its pattern
@@ -932,17 +935,17 @@ impl<'tcx> PatRange<'tcx> {
932935
// Also, for performance, it's important to only do the second `try_to_bits` if necessary.
933936
let lo_is_min = match self.lo {
934937
PatRangeBoundary::NegInfinity => true,
935-
PatRangeBoundary::Finite(value) => {
936-
let lo = value.try_to_bits(size).unwrap() ^ bias;
938+
PatRangeBoundary::Finite(_ty, value) => {
939+
let lo = value.unwrap_leaf().to_bits(size) ^ bias;
937940
lo <= min
938941
}
939942
PatRangeBoundary::PosInfinity => false,
940943
};
941944
if lo_is_min {
942945
let hi_is_max = match self.hi {
943946
PatRangeBoundary::NegInfinity => false,
944-
PatRangeBoundary::Finite(value) => {
945-
let hi = value.try_to_bits(size).unwrap() ^ bias;
947+
PatRangeBoundary::Finite(_ty, value) => {
948+
let hi = value.unwrap_leaf().to_bits(size) ^ bias;
946949
hi > max || hi == max && self.end == RangeEnd::Included
947950
}
948951
PatRangeBoundary::PosInfinity => true,
@@ -955,22 +958,16 @@ impl<'tcx> PatRange<'tcx> {
955958
}
956959

957960
#[inline]
958-
pub fn contains(
959-
&self,
960-
value: mir::Const<'tcx>,
961-
tcx: TyCtxt<'tcx>,
962-
typing_env: ty::TypingEnv<'tcx>,
963-
) -> Option<bool> {
961+
pub fn contains(&self, value: ty::ValTree<'tcx>, tcx: TyCtxt<'tcx>) -> Option<bool> {
964962
use Ordering::*;
965-
debug_assert_eq!(self.ty, value.ty());
966963
let ty = self.ty;
967-
let value = PatRangeBoundary::Finite(value);
964+
let value = PatRangeBoundary::Finite(ty, value);
968965
// For performance, it's important to only do the second comparison if necessary.
969966
Some(
970-
match self.lo.compare_with(value, ty, tcx, typing_env)? {
967+
match self.lo.compare_with(value, ty, tcx)? {
971968
Less | Equal => true,
972969
Greater => false,
973-
} && match value.compare_with(self.hi, ty, tcx, typing_env)? {
970+
} && match value.compare_with(self.hi, ty, tcx)? {
974971
Less => true,
975972
Equal => self.end == RangeEnd::Included,
976973
Greater => false,
@@ -979,21 +976,16 @@ impl<'tcx> PatRange<'tcx> {
979976
}
980977

981978
#[inline]
982-
pub fn overlaps(
983-
&self,
984-
other: &Self,
985-
tcx: TyCtxt<'tcx>,
986-
typing_env: ty::TypingEnv<'tcx>,
987-
) -> Option<bool> {
979+
pub fn overlaps(&self, other: &Self, tcx: TyCtxt<'tcx>) -> Option<bool> {
988980
use Ordering::*;
989981
debug_assert_eq!(self.ty, other.ty);
990982
// For performance, it's important to only do the second comparison if necessary.
991983
Some(
992-
match other.lo.compare_with(self.hi, self.ty, tcx, typing_env)? {
984+
match other.lo.compare_with(self.hi, self.ty, tcx)? {
993985
Less => true,
994986
Equal => self.end == RangeEnd::Included,
995987
Greater => false,
996-
} && match self.lo.compare_with(other.hi, self.ty, tcx, typing_env)? {
988+
} && match self.lo.compare_with(other.hi, self.ty, tcx)? {
997989
Less => true,
998990
Equal => other.end == RangeEnd::Included,
999991
Greater => false,
@@ -1004,10 +996,13 @@ impl<'tcx> PatRange<'tcx> {
1004996

1005997
impl<'tcx> fmt::Display for PatRange<'tcx> {
1006998
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1007-
if let PatRangeBoundary::Finite(value) = &self.lo {
999+
if let &PatRangeBoundary::Finite(ty, value) = &self.lo {
1000+
// `ty::Value` has a reasonable pretty-printing implementation.
1001+
let value = ty::Value { ty, valtree: value };
10081002
write!(f, "{value}")?;
10091003
}
1010-
if let PatRangeBoundary::Finite(value) = &self.hi {
1004+
if let &PatRangeBoundary::Finite(ty, value) = &self.hi {
1005+
let value = ty::Value { ty, valtree: value };
10111006
write!(f, "{}", self.end)?;
10121007
write!(f, "{value}")?;
10131008
} else {
@@ -1022,7 +1017,7 @@ impl<'tcx> fmt::Display for PatRange<'tcx> {
10221017
/// If present, the const must be of a numeric type.
10231018
#[derive(Copy, Clone, Debug, PartialEq, HashStable, TypeVisitable)]
10241019
pub enum PatRangeBoundary<'tcx> {
1025-
Finite(mir::Const<'tcx>),
1020+
Finite(Ty<'tcx>, ty::ValTree<'tcx>),
10261021
NegInfinity,
10271022
PosInfinity,
10281023
}
@@ -1033,20 +1028,15 @@ impl<'tcx> PatRangeBoundary<'tcx> {
10331028
matches!(self, Self::Finite(..))
10341029
}
10351030
#[inline]
1036-
pub fn as_finite(self) -> Option<mir::Const<'tcx>> {
1031+
pub fn as_finite(self) -> Option<ty::ValTree<'tcx>> {
10371032
match self {
1038-
Self::Finite(value) => Some(value),
1033+
Self::Finite(_ty, value) => Some(value),
10391034
Self::NegInfinity | Self::PosInfinity => None,
10401035
}
10411036
}
1042-
pub fn eval_bits(
1043-
self,
1044-
ty: Ty<'tcx>,
1045-
tcx: TyCtxt<'tcx>,
1046-
typing_env: ty::TypingEnv<'tcx>,
1047-
) -> u128 {
1037+
pub fn eval_bits(self, ty: Ty<'tcx>, tcx: TyCtxt<'tcx>) -> u128 {
10481038
match self {
1049-
Self::Finite(value) => value.eval_bits(tcx, typing_env),
1039+
Self::Finite(_ty, value) => value.unwrap_leaf().to_bits_unchecked(),
10501040
Self::NegInfinity => {
10511041
// Unwrap is ok because the type is known to be numeric.
10521042
ty.numeric_min_and_max_as_bits(tcx).unwrap().0
@@ -1058,14 +1048,8 @@ impl<'tcx> PatRangeBoundary<'tcx> {
10581048
}
10591049
}
10601050

1061-
#[instrument(skip(tcx, typing_env), level = "debug", ret)]
1062-
pub fn compare_with(
1063-
self,
1064-
other: Self,
1065-
ty: Ty<'tcx>,
1066-
tcx: TyCtxt<'tcx>,
1067-
typing_env: ty::TypingEnv<'tcx>,
1068-
) -> Option<Ordering> {
1051+
#[instrument(skip(tcx), level = "debug", ret)]
1052+
pub fn compare_with(self, other: Self, ty: Ty<'tcx>, tcx: TyCtxt<'tcx>) -> Option<Ordering> {
10691053
use PatRangeBoundary::*;
10701054
match (self, other) {
10711055
// When comparing with infinities, we must remember that `0u8..` and `0u8..=255`
@@ -1079,7 +1063,9 @@ impl<'tcx> PatRangeBoundary<'tcx> {
10791063
// we can do scalar comparisons. E.g. `unicode-normalization` has
10801064
// many ranges such as '\u{037A}'..='\u{037F}', and chars can be compared
10811065
// in this way.
1082-
(Finite(a), Finite(b)) if matches!(ty.kind(), ty::Int(_) | ty::Uint(_) | ty::Char) => {
1066+
(Finite(_, a), Finite(_, b))
1067+
if matches!(ty.kind(), ty::Int(_) | ty::Uint(_) | ty::Char) =>
1068+
{
10831069
if let (Some(a), Some(b)) = (a.try_to_scalar_int(), b.try_to_scalar_int()) {
10841070
let sz = ty.primitive_size(tcx);
10851071
let cmp = match ty.kind() {
@@ -1093,8 +1079,8 @@ impl<'tcx> PatRangeBoundary<'tcx> {
10931079
_ => {}
10941080
}
10951081

1096-
let a = self.eval_bits(ty, tcx, typing_env);
1097-
let b = other.eval_bits(ty, tcx, typing_env);
1082+
let a = self.eval_bits(ty, tcx);
1083+
let b = other.eval_bits(ty, tcx);
10981084

10991085
match ty.kind() {
11001086
ty::Float(ty::FloatTy::F16) => {

compiler/rustc_middle/src/thir/visit.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -265,7 +265,7 @@ pub(crate) fn for_each_immediate_subpat<'a, 'tcx>(
265265
PatKind::Missing
266266
| PatKind::Wild
267267
| PatKind::Binding { subpattern: None, .. }
268-
| PatKind::Constant { value: _ }
268+
| PatKind::Constant { .. }
269269
| PatKind::Range(_)
270270
| PatKind::Never
271271
| PatKind::Error(_) => {}

compiler/rustc_middle/src/ty/consts/valtree.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@ use std::fmt;
22
use std::ops::Deref;
33

44
use rustc_data_structures::intern::Interned;
5+
use rustc_hir::def::Namespace;
56
use rustc_macros::{HashStable, Lift, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable};
67

78
use super::ScalarInt;
89
use crate::mir::interpret::{ErrorHandled, Scalar};
10+
use crate::ty::print::{FmtPrinter, PrettyPrinter};
911
use crate::ty::{self, Ty, TyCtxt};
1012

1113
/// This datastructure is used to represent the value of constants used in the type system.
@@ -203,3 +205,14 @@ impl<'tcx> rustc_type_ir::inherent::ValueConst<TyCtxt<'tcx>> for Value<'tcx> {
203205
self.valtree
204206
}
205207
}
208+
209+
impl<'tcx> fmt::Display for Value<'tcx> {
210+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
211+
ty::tls::with(move |tcx| {
212+
let cv = tcx.lift(*self).unwrap();
213+
let mut cx = FmtPrinter::new(tcx, Namespace::ValueNS);
214+
cx.pretty_print_const_valtree(cv, /*print_ty*/ true)?;
215+
f.write_str(&cx.into_buffer())
216+
})
217+
}
218+
}

compiler/rustc_middle/src/ty/structural_impls.rs

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ use rustc_hir::def_id::LocalDefId;
1212
use rustc_span::source_map::Spanned;
1313
use rustc_type_ir::{ConstKind, TypeFolder, VisitorResult, try_visit};
1414

15-
use super::print::PrettyPrinter;
1615
use super::{GenericArg, GenericArgKind, Pattern, Region};
1716
use crate::mir::PlaceElem;
1817
use crate::ty::print::{FmtPrinter, Printer, with_no_trimmed_paths};
@@ -168,15 +167,11 @@ impl<'tcx> fmt::Debug for ty::Const<'tcx> {
168167
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
169168
// If this is a value, we spend some effort to make it look nice.
170169
if let ConstKind::Value(cv) = self.kind() {
171-
return ty::tls::with(move |tcx| {
172-
let cv = tcx.lift(cv).unwrap();
173-
let mut cx = FmtPrinter::new(tcx, Namespace::ValueNS);
174-
cx.pretty_print_const_valtree(cv, /*print_ty*/ true)?;
175-
f.write_str(&cx.into_buffer())
176-
});
170+
write!(f, "{}", cv)
171+
} else {
172+
// Fall back to something verbose.
173+
write!(f, "{:?}", self.kind())
177174
}
178-
// Fall back to something verbose.
179-
write!(f, "{:?}", self.kind())
180175
}
181176
}
182177

compiler/rustc_mir_build/src/builder/custom/parse/instruction.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -146,9 +146,9 @@ impl<'a, 'tcx> ParseCtxt<'a, 'tcx> {
146146
for arm in rest {
147147
let arm = &self.thir[*arm];
148148
let value = match arm.pattern.kind {
149-
PatKind::Constant { value } => value,
149+
PatKind::Constant { value, .. } => value,
150150
PatKind::ExpandedConstant { ref subpattern, def_id: _ }
151-
if let PatKind::Constant { value } = subpattern.kind =>
151+
if let PatKind::Constant { value, .. } = subpattern.kind =>
152152
{
153153
value
154154
}
@@ -160,7 +160,7 @@ impl<'a, 'tcx> ParseCtxt<'a, 'tcx> {
160160
});
161161
}
162162
};
163-
values.push(value.eval_bits(self.tcx, self.typing_env));
163+
values.push(value.unwrap_leaf().to_bits_unchecked());
164164
targets.push(self.parse_block(arm.body)?);
165165
}
166166

compiler/rustc_mir_build/src/builder/matches/match_pair.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ impl<'tcx> MatchPairTree<'tcx> {
136136
}
137137
}
138138

139-
PatKind::Constant { value } => Some(TestCase::Constant { value }),
139+
PatKind::Constant { ty, value } => Some(TestCase::Constant { ty, value }),
140140

141141
PatKind::AscribeUserType {
142142
ascription: Ascription { ref annotation, variance },

compiler/rustc_mir_build/src/builder/matches/mod.rs

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use rustc_data_structures::stack::ensure_sufficient_stack;
1616
use rustc_hir::{BindingMode, ByRef, LetStmt, LocalSource, Node};
1717
use rustc_middle::bug;
1818
use rustc_middle::middle::region;
19-
use rustc_middle::mir::{self, *};
19+
use rustc_middle::mir::*;
2020
use rustc_middle::thir::{self, *};
2121
use rustc_middle::ty::{self, CanonicalUserTypeAnnotation, Ty, ValTree, ValTreeKind};
2222
use rustc_pattern_analysis::constructor::RangeEnd;
@@ -1260,7 +1260,7 @@ struct Ascription<'tcx> {
12601260
#[derive(Debug, Clone)]
12611261
enum TestCase<'tcx> {
12621262
Variant { adt_def: ty::AdtDef<'tcx>, variant_index: VariantIdx },
1263-
Constant { value: mir::Const<'tcx> },
1263+
Constant { ty: Ty<'tcx>, value: ty::ValTree<'tcx> },
12641264
Range(Arc<PatRange<'tcx>>),
12651265
Slice { len: usize, variable_length: bool },
12661266
Deref { temp: Place<'tcx>, mutability: Mutability },
@@ -1333,11 +1333,12 @@ enum TestKind<'tcx> {
13331333
/// Test for equality with value, possibly after an unsizing coercion to
13341334
/// `ty`,
13351335
Eq {
1336-
value: Const<'tcx>,
1336+
value: ty::ValTree<'tcx>,
1337+
value_ty: Ty<'tcx>,
13371338
// Integer types are handled by `SwitchInt`, and constants with ADT
13381339
// types and `&[T]` types are converted back into patterns, so this can
1339-
// only be `&str`, `f32` or `f64`.
1340-
ty: Ty<'tcx>,
1340+
// only be `&str` or `f*`.
1341+
cast_ty: Ty<'tcx>,
13411342
},
13421343

13431344
/// Test whether the value falls within an inclusive or exclusive range.
@@ -1373,16 +1374,16 @@ enum TestBranch<'tcx> {
13731374
/// Success branch, used for tests with two possible outcomes.
13741375
Success,
13751376
/// Branch corresponding to this constant.
1376-
Constant(Const<'tcx>, u128),
1377+
Constant(ty::ValTree<'tcx>, u128),
13771378
/// Branch corresponding to this variant.
13781379
Variant(VariantIdx),
13791380
/// Failure branch for tests with two possible outcomes, and "otherwise" branch for other tests.
13801381
Failure,
13811382
}
13821383

13831384
impl<'tcx> TestBranch<'tcx> {
1384-
fn as_constant(&self) -> Option<&Const<'tcx>> {
1385-
if let Self::Constant(v, _) = self { Some(v) } else { None }
1385+
fn as_constant(&self) -> Option<ty::ValTree<'tcx>> {
1386+
if let Self::Constant(v, _) = self { Some(*v) } else { None }
13861387
}
13871388
}
13881389

0 commit comments

Comments
 (0)