Skip to content

Commit ef10f32

Browse files
authored
Rollup merge of rust-lang#137715 - oli-obk:pattern-type-literals, r=BoxyUwU
Allow int literals for pattern types with int base types r? `@BoxyUwU` I also added an error at layout computation time for layouts that contain wrapping ranges (happens at monomorphization time). This is obviously hacky, but at least prevents such types from making it to codegen for now. It made writing the tests for int literals easier as I didn't have to think about that edge case Basically this PR allows you to stop using transmutes for creating pattern types and instead just use literals: ```rust let x: pattern_type!(u32 is 5..10) = 7; ``` works, and if the literal is out of range you get a type mismatch because it just stays at the base type and the base type can't be coerced to the pattern type. cc `@joshtriplett` `@scottmcm`
2 parents cb81162 + f87e58f commit ef10f32

File tree

9 files changed

+518
-12
lines changed

9 files changed

+518
-12
lines changed

compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs

+14-1
Original file line numberDiff line numberDiff line change
@@ -1636,7 +1636,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
16361636
ast::LitKind::Char(_) => tcx.types.char,
16371637
ast::LitKind::Int(_, ast::LitIntType::Signed(t)) => Ty::new_int(tcx, ty::int_ty(t)),
16381638
ast::LitKind::Int(_, ast::LitIntType::Unsigned(t)) => Ty::new_uint(tcx, ty::uint_ty(t)),
1639-
ast::LitKind::Int(_, ast::LitIntType::Unsuffixed) => {
1639+
ast::LitKind::Int(i, ast::LitIntType::Unsuffixed) => {
16401640
let opt_ty = expected.to_option(self).and_then(|ty| match ty.kind() {
16411641
ty::Int(_) | ty::Uint(_) => Some(ty),
16421642
// These exist to direct casts like `0x61 as char` to use
@@ -1645,6 +1645,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
16451645
ty::Char => Some(tcx.types.u8),
16461646
ty::RawPtr(..) => Some(tcx.types.usize),
16471647
ty::FnDef(..) | ty::FnPtr(..) => Some(tcx.types.usize),
1648+
&ty::Pat(base, _) if base.is_integral() => {
1649+
let layout = tcx
1650+
.layout_of(self.typing_env(self.param_env).as_query_input(ty))
1651+
.ok()?;
1652+
assert!(!layout.uninhabited);
1653+
1654+
match layout.backend_repr {
1655+
rustc_abi::BackendRepr::Scalar(scalar) => {
1656+
scalar.valid_range(&tcx).contains(u128::from(i.get())).then_some(ty)
1657+
}
1658+
_ => unreachable!(),
1659+
}
1660+
}
16481661
_ => None,
16491662
});
16501663
opt_ty.unwrap_or_else(|| self.next_int_var())

compiler/rustc_mir_build/src/builder/expr/as_constant.rs

+6-1
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,12 @@ fn lit_to_mir_constant<'tcx>(tcx: TyCtxt<'tcx>, lit_input: LitToConstInput<'tcx>
117117
ConstValue::Scalar(Scalar::from_uint(result, width))
118118
};
119119

120-
let value = match (lit, ty.kind()) {
120+
let lit_ty = match *ty.kind() {
121+
ty::Pat(base, _) => base,
122+
_ => ty,
123+
};
124+
125+
let value = match (lit, lit_ty.kind()) {
121126
(ast::LitKind::Str(s, _), ty::Ref(_, inner_ty, _)) if inner_ty.is_str() => {
122127
let s = s.as_str();
123128
let allocation = Allocation::from_bytes_byte_aligned_immutable(s.as_bytes());

compiler/rustc_ty_utils/src/layout.rs

+28
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,34 @@ fn layout_of_uncached<'tcx>(
220220
.try_to_bits(tcx, cx.typing_env)
221221
.ok_or_else(|| error(cx, LayoutError::Unknown(ty)))?;
222222

223+
// FIXME(pattern_types): create implied bounds from pattern types in signatures
224+
// that require that the range end is >= the range start so that we can't hit
225+
// this error anymore without first having hit a trait solver error.
226+
// Very fuzzy on the details here, but pattern types are an internal impl detail,
227+
// so we can just go with this for now
228+
if scalar.is_signed() {
229+
let range = scalar.valid_range_mut();
230+
let start = layout.size.sign_extend(range.start);
231+
let end = layout.size.sign_extend(range.end);
232+
if end < start {
233+
let guar = tcx.dcx().err(format!(
234+
"pattern type ranges cannot wrap: {start}..={end}"
235+
));
236+
237+
return Err(error(cx, LayoutError::ReferencesError(guar)));
238+
}
239+
} else {
240+
let range = scalar.valid_range_mut();
241+
if range.end < range.start {
242+
let guar = tcx.dcx().err(format!(
243+
"pattern type ranges cannot wrap: {}..={}",
244+
range.start, range.end
245+
));
246+
247+
return Err(error(cx, LayoutError::ReferencesError(guar)));
248+
}
249+
};
250+
223251
let niche = Niche {
224252
offset: Size::ZERO,
225253
value: scalar.primitive(),
+136
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
//! Check where literals can be used to initialize pattern types and where not.
2+
3+
#![feature(pattern_types, const_trait_impl, pattern_type_range_trait)]
4+
#![feature(pattern_type_macro)]
5+
6+
use std::pat::pattern_type;
7+
8+
fn out_of_range() -> pattern_type!(u32 is 1..) {
9+
0
10+
//~^ mismatched types
11+
}
12+
13+
fn at_range_start() -> pattern_type!(u32 is 1..) {
14+
1
15+
}
16+
17+
fn in_range() -> pattern_type!(u32 is 1..) {
18+
2
19+
}
20+
21+
fn negative_lit_on_unsigned_ty() -> pattern_type!(u32 is 1..) {
22+
-3
23+
//~^ ERROR: cannot apply unary operator `-` to type `(u32) is 1..`
24+
}
25+
26+
fn negative_lit_in_range() -> pattern_type!(i8 is -5..5) {
27+
-2
28+
//~^ ERROR: cannot apply unary operator `-` to type `(i8) is -5..=4`
29+
}
30+
31+
fn positive_lit_in_range_of_signed() -> pattern_type!(i8 is -5..5) {
32+
2
33+
}
34+
35+
fn negative_lit_at_range_start() -> pattern_type!(i8 is -5..5) {
36+
-5
37+
//~^ mismatched types
38+
}
39+
40+
fn positive_lit_at_range_end() -> pattern_type!(i8 is -5..5) {
41+
4
42+
}
43+
44+
fn lit_one_beyond_range_end() -> pattern_type!(i8 is -5..5) {
45+
5
46+
//~^ mismatched types
47+
}
48+
49+
fn wrong_lit_kind() -> pattern_type!(u32 is 1..) {
50+
'3'
51+
//~^ mismatched types
52+
}
53+
54+
fn char_lit_in_range() -> pattern_type!(char is 'a'..'z') {
55+
'b'
56+
//~^ mismatched types
57+
}
58+
59+
fn char_lit_out_of_range() -> pattern_type!(char is 'a'..'z') {
60+
'A'
61+
//~^ mismatched types
62+
}
63+
64+
fn lit_at_unsigned_range_inclusive_end() -> pattern_type!(u32 is 0..=1) {
65+
1
66+
}
67+
68+
fn single_element_range() -> pattern_type!(u32 is 0..=0) {
69+
0
70+
}
71+
72+
fn lit_oob_single_element_range() -> pattern_type!(u32 is 0..=0) {
73+
1
74+
//~^ mismatched types
75+
}
76+
77+
fn lit_oob_single_element_range_exclusive() -> pattern_type!(u32 is 0..1) {
78+
1
79+
//~^ mismatched types
80+
}
81+
82+
fn single_element_range_exclusive() -> pattern_type!(u32 is 0..1) {
83+
0
84+
}
85+
86+
fn empty_range_at_base_type_min() -> pattern_type!(u32 is 0..0) {
87+
//~^ evaluation of constant value failed
88+
0
89+
}
90+
91+
fn empty_range_at_base_type_min2() -> pattern_type!(u32 is 0..0) {
92+
//~^ evaluation of constant value failed
93+
1
94+
}
95+
96+
fn empty_range() -> pattern_type!(u32 is 1..1) {
97+
0
98+
//~^ mismatched types
99+
}
100+
101+
fn empty_range2() -> pattern_type!(u32 is 1..1) {
102+
1
103+
//~^ mismatched types
104+
}
105+
106+
fn wraparound_range_at_base_ty_end() -> pattern_type!(u32 is 1..0) {
107+
//~^ evaluation of constant value failed
108+
1
109+
}
110+
111+
fn wraparound_range_at_base_ty_end2() -> pattern_type!(u32 is 1..0) {
112+
//~^ evaluation of constant value failed
113+
0
114+
}
115+
116+
fn wraparound_range_at_base_ty_end3() -> pattern_type!(u32 is 1..0) {
117+
//~^ evaluation of constant value failed
118+
2
119+
}
120+
121+
fn wraparound_range() -> pattern_type!(u32 is 2..1) {
122+
1
123+
//~^ mismatched types
124+
}
125+
126+
fn lit_in_wraparound_range() -> pattern_type!(u32 is 2..1) {
127+
0
128+
//~^ mismatched types
129+
}
130+
131+
fn lit_at_wraparound_range_start() -> pattern_type!(u32 is 2..1) {
132+
2
133+
//~^ mismatched types
134+
}
135+
136+
fn main() {}

0 commit comments

Comments
 (0)