Skip to content

Commit 44fbf1c

Browse files
Auto merge of #144591 - RalfJung:pattern-valtrees, r=<try>
Patterns: represent constants as valtrees
2 parents 5529041 + 3ebc541 commit 44fbf1c

File tree

12 files changed

+152
-177
lines changed

12 files changed

+152
-177
lines changed

compiler/rustc_middle/src/mir/consts.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -448,6 +448,11 @@ impl<'tcx> Const<'tcx> {
448448
Self::Val(val, ty)
449449
}
450450

451+
#[inline]
452+
pub fn from_ty_value(tcx: TyCtxt<'tcx>, val: ty::Value<'tcx>) -> Self {
453+
Self::Ty(val.ty, ty::Const::new_value(tcx, val.valtree, val.ty))
454+
}
455+
451456
pub fn from_bits(
452457
tcx: TyCtxt<'tcx>,
453458
bits: u128,

compiler/rustc_middle/src/thir.rs

Lines changed: 25 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -832,17 +832,17 @@ 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
/// * raw pointers derived from integers, other raw pointers will have already resulted in an
842842
// error.
843843
/// * `String`, if `string_deref_patterns` is enabled.
844844
Constant {
845-
value: mir::Const<'tcx>,
845+
value: ty::Value<'tcx>,
846846
},
847847

848848
/// Pattern obtained by converting a constant (inline or named) to its pattern
@@ -935,7 +935,7 @@ impl<'tcx> PatRange<'tcx> {
935935
let lo_is_min = match self.lo {
936936
PatRangeBoundary::NegInfinity => true,
937937
PatRangeBoundary::Finite(value) => {
938-
let lo = value.try_to_bits(size).unwrap() ^ bias;
938+
let lo = value.try_to_scalar_int().unwrap().to_bits(size) ^ bias;
939939
lo <= min
940940
}
941941
PatRangeBoundary::PosInfinity => false,
@@ -944,7 +944,7 @@ impl<'tcx> PatRange<'tcx> {
944944
let hi_is_max = match self.hi {
945945
PatRangeBoundary::NegInfinity => false,
946946
PatRangeBoundary::Finite(value) => {
947-
let hi = value.try_to_bits(size).unwrap() ^ bias;
947+
let hi = value.try_to_scalar_int().unwrap().to_bits(size) ^ bias;
948948
hi > max || hi == max && self.end == RangeEnd::Included
949949
}
950950
PatRangeBoundary::PosInfinity => true,
@@ -957,22 +957,16 @@ impl<'tcx> PatRange<'tcx> {
957957
}
958958

959959
#[inline]
960-
pub fn contains(
961-
&self,
962-
value: mir::Const<'tcx>,
963-
tcx: TyCtxt<'tcx>,
964-
typing_env: ty::TypingEnv<'tcx>,
965-
) -> Option<bool> {
960+
pub fn contains(&self, valtree: ty::ValTree<'tcx>, tcx: TyCtxt<'tcx>) -> Option<bool> {
966961
use Ordering::*;
967-
debug_assert_eq!(self.ty, value.ty());
968962
let ty = self.ty;
969-
let value = PatRangeBoundary::Finite(value);
963+
let value = PatRangeBoundary::Finite(valtree);
970964
// For performance, it's important to only do the second comparison if necessary.
971965
Some(
972-
match self.lo.compare_with(value, ty, tcx, typing_env)? {
966+
match self.lo.compare_with(value, ty, tcx)? {
973967
Less | Equal => true,
974968
Greater => false,
975-
} && match value.compare_with(self.hi, ty, tcx, typing_env)? {
969+
} && match value.compare_with(self.hi, ty, tcx)? {
976970
Less => true,
977971
Equal => self.end == RangeEnd::Included,
978972
Greater => false,
@@ -981,21 +975,16 @@ impl<'tcx> PatRange<'tcx> {
981975
}
982976

983977
#[inline]
984-
pub fn overlaps(
985-
&self,
986-
other: &Self,
987-
tcx: TyCtxt<'tcx>,
988-
typing_env: ty::TypingEnv<'tcx>,
989-
) -> Option<bool> {
978+
pub fn overlaps(&self, other: &Self, tcx: TyCtxt<'tcx>) -> Option<bool> {
990979
use Ordering::*;
991980
debug_assert_eq!(self.ty, other.ty);
992981
// For performance, it's important to only do the second comparison if necessary.
993982
Some(
994-
match other.lo.compare_with(self.hi, self.ty, tcx, typing_env)? {
983+
match other.lo.compare_with(self.hi, self.ty, tcx)? {
995984
Less => true,
996985
Equal => self.end == RangeEnd::Included,
997986
Greater => false,
998-
} && match self.lo.compare_with(other.hi, self.ty, tcx, typing_env)? {
987+
} && match self.lo.compare_with(other.hi, self.ty, tcx)? {
999988
Less => true,
1000989
Equal => other.end == RangeEnd::Included,
1001990
Greater => false,
@@ -1006,11 +995,13 @@ impl<'tcx> PatRange<'tcx> {
1006995

1007996
impl<'tcx> fmt::Display for PatRange<'tcx> {
1008997
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1009-
if let PatRangeBoundary::Finite(value) = &self.lo {
998+
if let &PatRangeBoundary::Finite(valtree) = &self.lo {
999+
let value = ty::Value { ty: self.ty, valtree };
10101000
write!(f, "{value}")?;
10111001
}
1012-
if let PatRangeBoundary::Finite(value) = &self.hi {
1002+
if let &PatRangeBoundary::Finite(valtree) = &self.hi {
10131003
write!(f, "{}", self.end)?;
1004+
let value = ty::Value { ty: self.ty, valtree };
10141005
write!(f, "{value}")?;
10151006
} else {
10161007
// `0..` is parsed as an inclusive range, we must display it correctly.
@@ -1024,7 +1015,8 @@ impl<'tcx> fmt::Display for PatRange<'tcx> {
10241015
/// If present, the const must be of a numeric type.
10251016
#[derive(Copy, Clone, Debug, PartialEq, HashStable, TypeVisitable)]
10261017
pub enum PatRangeBoundary<'tcx> {
1027-
Finite(mir::Const<'tcx>),
1018+
/// The type if this valtree is stored in the surrounding `PatRange`.
1019+
Finite(ty::ValTree<'tcx>),
10281020
NegInfinity,
10291021
PosInfinity,
10301022
}
@@ -1035,20 +1027,15 @@ impl<'tcx> PatRangeBoundary<'tcx> {
10351027
matches!(self, Self::Finite(..))
10361028
}
10371029
#[inline]
1038-
pub fn as_finite(self) -> Option<mir::Const<'tcx>> {
1030+
pub fn as_finite(self) -> Option<ty::ValTree<'tcx>> {
10391031
match self {
10401032
Self::Finite(value) => Some(value),
10411033
Self::NegInfinity | Self::PosInfinity => None,
10421034
}
10431035
}
1044-
pub fn eval_bits(
1045-
self,
1046-
ty: Ty<'tcx>,
1047-
tcx: TyCtxt<'tcx>,
1048-
typing_env: ty::TypingEnv<'tcx>,
1049-
) -> u128 {
1036+
pub fn eval_bits(self, ty: Ty<'tcx>, tcx: TyCtxt<'tcx>) -> u128 {
10501037
match self {
1051-
Self::Finite(value) => value.eval_bits(tcx, typing_env),
1038+
Self::Finite(value) => value.try_to_scalar_int().unwrap().to_bits_unchecked(),
10521039
Self::NegInfinity => {
10531040
// Unwrap is ok because the type is known to be numeric.
10541041
ty.numeric_min_and_max_as_bits(tcx).unwrap().0
@@ -1060,14 +1047,8 @@ impl<'tcx> PatRangeBoundary<'tcx> {
10601047
}
10611048
}
10621049

1063-
#[instrument(skip(tcx, typing_env), level = "debug", ret)]
1064-
pub fn compare_with(
1065-
self,
1066-
other: Self,
1067-
ty: Ty<'tcx>,
1068-
tcx: TyCtxt<'tcx>,
1069-
typing_env: ty::TypingEnv<'tcx>,
1070-
) -> Option<Ordering> {
1050+
#[instrument(skip(tcx), level = "debug", ret)]
1051+
pub fn compare_with(self, other: Self, ty: Ty<'tcx>, tcx: TyCtxt<'tcx>) -> Option<Ordering> {
10711052
use PatRangeBoundary::*;
10721053
match (self, other) {
10731054
// When comparing with infinities, we must remember that `0u8..` and `0u8..=255`
@@ -1095,8 +1076,8 @@ impl<'tcx> PatRangeBoundary<'tcx> {
10951076
_ => {}
10961077
}
10971078

1098-
let a = self.eval_bits(ty, tcx, typing_env);
1099-
let b = other.eval_bits(ty, tcx, typing_env);
1079+
let a = self.eval_bits(ty, tcx);
1080+
let b = other.eval_bits(ty, tcx);
11001081

11011082
match ty.kind() {
11021083
ty::Float(ty::FloatTy::F16) => {

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

Lines changed: 23 additions & 4 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.
@@ -133,6 +135,8 @@ pub type ConstToValTreeResult<'tcx> = Result<Result<ValTree<'tcx>, Ty<'tcx>>, Er
133135
/// A type-level constant value.
134136
///
135137
/// Represents a typed, fully evaluated constant.
138+
/// Note that this is used by pattern elaboration to represent values which cannot occur in types,
139+
/// such as raw pointers and floats.
136140
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
137141
#[derive(HashStable, TyEncodable, TyDecodable, TypeFoldable, TypeVisitable, Lift)]
138142
pub struct Value<'tcx> {
@@ -147,15 +151,19 @@ impl<'tcx> Value<'tcx> {
147151
/// or an aggregate).
148152
#[inline]
149153
pub fn try_to_bits(self, tcx: TyCtxt<'tcx>, typing_env: ty::TypingEnv<'tcx>) -> Option<u128> {
150-
let (ty::Bool | ty::Char | ty::Uint(_) | ty::Int(_) | ty::Float(_)) = self.ty.kind() else {
151-
return None;
152-
};
153-
let scalar = self.valtree.try_to_scalar_int()?;
154+
let scalar = self.try_to_scalar_int()?;
154155
let input = typing_env.with_post_analysis_normalized(tcx).as_query_input(self.ty);
155156
let size = tcx.layout_of(input).ok()?.size;
156157
Some(scalar.to_bits(size))
157158
}
158159

160+
pub fn try_to_scalar_int(self) -> Option<ScalarInt> {
161+
let (ty::Bool | ty::Char | ty::Uint(_) | ty::Int(_) | ty::Float(_)) = self.ty.kind() else {
162+
return None;
163+
};
164+
self.valtree.try_to_scalar_int()
165+
}
166+
159167
pub fn try_to_bool(self) -> Option<bool> {
160168
if !self.ty.is_bool() {
161169
return None;
@@ -203,3 +211,14 @@ impl<'tcx> rustc_type_ir::inherent::ValueConst<TyCtxt<'tcx>> for Value<'tcx> {
203211
self.valtree
204212
}
205213
}
214+
215+
impl<'tcx> fmt::Display for Value<'tcx> {
216+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
217+
ty::tls::with(move |tcx| {
218+
let cv = tcx.lift(*self).unwrap();
219+
let mut cx = FmtPrinter::new(tcx, Namespace::ValueNS);
220+
cx.pretty_print_const_valtree(cv, /*print_ty*/ true)?;
221+
f.write_str(&cx.into_buffer())
222+
})
223+
}
224+
}

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: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -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.try_to_scalar_int().unwrap().to_bits_unchecked());
164164
targets.push(self.parse_block(arm.body)?);
165165
}
166166

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 { value: ty::Value<'tcx> },
12641264
Range(Arc<PatRange<'tcx>>),
12651265
Slice { len: usize, variable_length: bool },
12661266
Deref { temp: Place<'tcx>, mutability: Mutability },
@@ -1333,11 +1333,11 @@ 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::Value<'tcx>,
13371337
// Integer types are handled by `SwitchInt`, and constants with ADT
13381338
// types and `&[T]` types are converted back into patterns, so this can
1339-
// only be `&str`, `f32` or `f64`.
1340-
ty: Ty<'tcx>,
1339+
// only be `&str` or `f*`.
1340+
cast_ty: Ty<'tcx>,
13411341
},
13421342

13431343
/// Test whether the value falls within an inclusive or exclusive range.
@@ -1373,16 +1373,17 @@ enum TestBranch<'tcx> {
13731373
/// Success branch, used for tests with two possible outcomes.
13741374
Success,
13751375
/// Branch corresponding to this constant.
1376-
Constant(Const<'tcx>, u128),
1376+
/// The type of this valtree is the same for all branches, it must be known by the context.
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)