Skip to content

Commit 8800ec1

Browse files
committed
Auto merge of #144591 - RalfJung:pattern-valtrees, r=BoxyUwU
Patterns: represent constants as valtrees Const patterns are always valtrees now. Let's represent that in the types. We use `ty::Value` for this since it nicely packages value and type, and has some convenient methods. Cc `@Nadrieril` `@BoxyUwU`
2 parents 3507a74 + 2330afa commit 8800ec1

12 files changed

Lines changed: 146 additions & 175 deletions

File tree

‎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+
pubfnfrom_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
pubfnfrom_bits(
452457
tcx:TyCtxt<'tcx>,
453458
bits:u128,

‎compiler/rustc_middle/src/thir.rs‎

Lines changed: 26 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 = matchself.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 = matchself.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,17 @@ impl<'tcx> PatRange<'tcx> {
957957
}
958958

959959
#[inline]
960-
pubfncontains(
961-
&self,
962-
value: mir::Const<'tcx>,
963-
tcx:TyCtxt<'tcx>,
964-
typing_env: ty::TypingEnv<'tcx>,
965-
) -> Option<bool>{
960+
pubfncontains(&self,value: ty::Value<'tcx>,tcx:TyCtxt<'tcx>) -> Option<bool>{
966961
useOrdering::*;
967-
debug_assert_eq!(self.ty,value.ty());
962+
debug_assert_eq!(value.ty,self.ty);
968963
let ty = self.ty;
969-
let value = PatRangeBoundary::Finite(value);
964+
let value = PatRangeBoundary::Finite(value.valtree);
970965
// For performance, it's important to only do the second comparison if necessary.
971966
Some(
972-
matchself.lo.compare_with(value, ty, tcx, typing_env)? {
967+
matchself.lo.compare_with(value, ty, tcx)? {
973968
Less | Equal => true,
974969
Greater => false,
975-
} && match value.compare_with(self.hi, ty, tcx, typing_env)? {
970+
} && match value.compare_with(self.hi, ty, tcx)? {
976971
Less => true,
977972
Equal => self.end == RangeEnd::Included,
978973
Greater => false,
@@ -981,21 +976,16 @@ impl<'tcx> PatRange<'tcx> {
981976
}
982977

983978
#[inline]
984-
pubfnoverlaps(
985-
&self,
986-
other:&Self,
987-
tcx:TyCtxt<'tcx>,
988-
typing_env: ty::TypingEnv<'tcx>,
989-
) -> Option<bool>{
979+
pubfnoverlaps(&self,other:&Self,tcx:TyCtxt<'tcx>) -> Option<bool>{
990980
useOrdering::*;
991981
debug_assert_eq!(self.ty, other.ty);
992982
// For performance, it's important to only do the second comparison if necessary.
993983
Some(
994-
match other.lo.compare_with(self.hi,self.ty, tcx, typing_env)? {
984+
match other.lo.compare_with(self.hi,self.ty, tcx)? {
995985
Less => true,
996986
Equal => self.end == RangeEnd::Included,
997987
Greater => false,
998-
} && matchself.lo.compare_with(other.hi,self.ty, tcx, typing_env)? {
988+
} && matchself.lo.compare_with(other.hi,self.ty, tcx)? {
999989
Less => true,
1000990
Equal => other.end == RangeEnd::Included,
1001991
Greater => false,
@@ -1006,11 +996,13 @@ impl<'tcx> PatRange<'tcx> {
1006996

1007997
impl<'tcx> fmt::DisplayforPatRange<'tcx>{
1008998
fnfmt(&self,f:&mut fmt::Formatter<'_>) -> fmt::Result{
1009-
ifletPatRangeBoundary::Finite(value) = &self.lo{
999+
iflet&PatRangeBoundary::Finite(valtree) = &self.lo{
1000+
let value = ty::Value{ty:self.ty, valtree };
10101001
write!(f,"{value}")?;
10111002
}
1012-
ifletPatRangeBoundary::Finite(value) = &self.hi{
1003+
iflet&PatRangeBoundary::Finite(valtree) = &self.hi{
10131004
write!(f,"{}",self.end)?;
1005+
let value = ty::Value{ty:self.ty, valtree };
10141006
write!(f,"{value}")?;
10151007
}else{
10161008
// `0..` is parsed as an inclusive range, we must display it correctly.
@@ -1024,7 +1016,8 @@ impl<'tcx> fmt::Display for PatRange<'tcx> {
10241016
/// If present, the const must be of a numeric type.
10251017
#[derive(Copy,Clone,Debug,PartialEq,HashStable,TypeVisitable)]
10261018
pubenumPatRangeBoundary<'tcx>{
1027-
Finite(mir::Const<'tcx>),
1019+
/// The type of this valtree is stored in the surrounding `PatRange`.
1020+
Finite(ty::ValTree<'tcx>),
10281021
NegInfinity,
10291022
PosInfinity,
10301023
}
@@ -1035,20 +1028,15 @@ impl<'tcx> PatRangeBoundary<'tcx> {
10351028
matches!(self,Self::Finite(..))
10361029
}
10371030
#[inline]
1038-
pubfnas_finite(self) -> Option<mir::Const<'tcx>>{
1031+
pubfnas_finite(self) -> Option<ty::ValTree<'tcx>>{
10391032
matchself{
10401033
Self::Finite(value) => Some(value),
10411034
Self::NegInfinity | Self::PosInfinity => None,
10421035
}
10431036
}
1044-
pubfneval_bits(
1045-
self,
1046-
ty:Ty<'tcx>,
1047-
tcx:TyCtxt<'tcx>,
1048-
typing_env: ty::TypingEnv<'tcx>,
1049-
) -> u128{
1037+
pubfnto_bits(self,ty:Ty<'tcx>,tcx:TyCtxt<'tcx>) -> u128{
10501038
matchself{
1051-
Self::Finite(value) => value.eval_bits(tcx, typing_env),
1039+
Self::Finite(value) => value.try_to_scalar_int().unwrap().to_bits_unchecked(),
10521040
Self::NegInfinity => {
10531041
// Unwrap is ok because the type is known to be numeric.
10541042
ty.numeric_min_and_max_as_bits(tcx).unwrap().0
@@ -1060,14 +1048,8 @@ impl<'tcx> PatRangeBoundary<'tcx> {
10601048
}
10611049
}
10621050

1063-
#[instrument(skip(tcx, typing_env), level = "debug", ret)]
1064-
pubfncompare_with(
1065-
self,
1066-
other:Self,
1067-
ty:Ty<'tcx>,
1068-
tcx:TyCtxt<'tcx>,
1069-
typing_env: ty::TypingEnv<'tcx>,
1070-
) -> Option<Ordering>{
1051+
#[instrument(skip(tcx), level = "debug", ret)]
1052+
pubfncompare_with(self,other:Self,ty:Ty<'tcx>,tcx:TyCtxt<'tcx>) -> Option<Ordering>{
10711053
usePatRangeBoundary::*;
10721054
match(self, other){
10731055
// When comparing with infinities, we must remember that `0u8..` and `0u8..=255`
@@ -1095,8 +1077,8 @@ impl<'tcx> PatRangeBoundary<'tcx> {
10951077
_ => {}
10961078
}
10971079

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

11011083
match ty.kind(){
11021084
ty::Float(ty::FloatTy::F16) => {

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

Lines changed: 15 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
usesuper::ScalarInt;
89
usecrate::mir::interpret::{ErrorHandled,Scalar};
10+
usecrate::ty::print::{FmtPrinter,PrettyPrinter};
911
usecrate::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 also 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
pubstructValue<'tcx>{
@@ -203,3 +207,14 @@ impl<'tcx> rustc_type_ir::inherent::ValueConst<TyCtxt<'tcx>> for Value<'tcx> {
203207
self.valtree
204208
}
205209
}
210+
211+
impl<'tcx> fmt::DisplayforValue<'tcx>{
212+
fnfmt(&self,f:&mut fmt::Formatter<'_>) -> fmt::Result{
213+
ty::tls::with(move |tcx| {
214+
let cv = tcx.lift(*self).unwrap();
215+
letmut p = FmtPrinter::new(tcx,Namespace::ValueNS);
216+
p.pretty_print_const_valtree(cv,/*print_ty*/true)?;
217+
f.write_str(&p.into_buffer())
218+
})
219+
}
220+
}

‎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-
usesuper::print::PrettyPrinter;
1615
usesuper::{GenericArg,GenericArgKind,Pattern,Region};
1716
usecrate::mir::PlaceElem;
1817
usecrate::ty::print::{FmtPrinter,Printer, with_no_trimmed_paths};
@@ -168,15 +167,11 @@ impl<'tcx> fmt::Debug for ty::Const<'tcx> {
168167
fnfmt(&self,f:&mut fmt::Formatter<'_>) -> fmt::Result{
169168
// If this is a value, we spend some effort to make it look nice.
170169
ifletConstKind::Value(cv) = self.kind(){
171-
return ty::tls::with(move |tcx| {
172-
let cv = tcx.lift(cv).unwrap();
173-
letmut p = FmtPrinter::new(tcx,Namespace::ValueNS);
174-
p.pretty_print_const_valtree(cv,/*print_ty*/true)?;
175-
f.write_str(&p.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.valtree.unwrap_leaf().to_bits_unchecked());
164164
targets.push(self.parse_block(arm.body)?);
165165
}
166166

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

Lines changed: 10 additions & 10 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;
@@ -1245,7 +1245,7 @@ struct Ascription<'tcx> {
12451245
#[derive(Debug,Clone)]
12461246
enumTestCase<'tcx>{
12471247
Variant{adt_def: ty::AdtDef<'tcx>,variant_index:VariantIdx},
1248-
Constant{value:mir::Const<'tcx>},
1248+
Constant{value:ty::Value<'tcx>},
12491249
Range(Arc<PatRange<'tcx>>),
12501250
Slice{len:usize,variable_length:bool},
12511251
Deref{temp:Place<'tcx>,mutability:Mutability},
@@ -1316,13 +1316,13 @@ enum TestKind<'tcx> {
13161316
If,
13171317

13181318
/// Test for equality with value, possibly after an unsizing coercion to
1319-
/// `ty`,
1319+
/// `cast_ty`,
13201320
Eq{
1321-
value:Const<'tcx>,
1321+
value:ty::Value<'tcx>,
13221322
// Integer types are handled by `SwitchInt`, and constants with ADT
13231323
// types and `&[T]` types are converted back into patterns, so this can
1324-
// only be `&str`, `f32` or `f64`.
1325-
ty:Ty<'tcx>,
1324+
// only be `&str`or floats.
1325+
cast_ty:Ty<'tcx>,
13261326
},
13271327

13281328
/// Test whether the value falls within an inclusive or exclusive range.
@@ -1357,17 +1357,17 @@ pub(crate) struct Test<'tcx> {
13571357
enumTestBranch<'tcx>{
13581358
/// Success branch, used for tests with two possible outcomes.
13591359
Success,
1360-
/// Branch corresponding to this constant.
1361-
Constant(Const<'tcx>,u128),
1360+
/// Branch corresponding to this constant. Must be a scalar.
1361+
Constant(ty::Value<'tcx>),
13621362
/// Branch corresponding to this variant.
13631363
Variant(VariantIdx),
13641364
/// Failure branch for tests with two possible outcomes, and "otherwise" branch for other tests.
13651365
Failure,
13661366
}
13671367

13681368
impl<'tcx>TestBranch<'tcx>{
1369-
fnas_constant(&self) -> Option<&Const<'tcx>>{
1370-
ifletSelf::Constant(v, _) = self{Some(v)}else{None}
1369+
fnas_constant(&self) -> Option<ty::Value<'tcx>>{
1370+
ifletSelf::Constant(v) = self{Some(*v)}else{None}
13711371
}
13721372
}
13731373

0 commit comments

Comments
 (0)