diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index db947b6744da6..253ba74a19127 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -1647,7 +1647,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ast::LitKind::Char(_) => tcx.types.char, ast::LitKind::Int(_, ast::LitIntType::Signed(t)) => Ty::new_int(tcx, ty::int_ty(t)), ast::LitKind::Int(_, ast::LitIntType::Unsigned(t)) => Ty::new_uint(tcx, ty::uint_ty(t)), - ast::LitKind::Int(_, ast::LitIntType::Unsuffixed) => { + ast::LitKind::Int(i, ast::LitIntType::Unsuffixed) => { let opt_ty = expected.to_option(self).and_then(|ty| match ty.kind() { ty::Int(_) | ty::Uint(_) => Some(ty), // These exist to direct casts like `0x61 as char` to use @@ -1656,6 +1656,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty::Char => Some(tcx.types.u8), ty::RawPtr(..) => Some(tcx.types.usize), ty::FnDef(..) | ty::FnPtr(..) => Some(tcx.types.usize), + &ty::Pat(base, _) if base.is_integral() => { + let layout = tcx + .layout_of(self.typing_env(self.param_env).as_query_input(ty)) + .ok()?; + assert!(!layout.uninhabited); + + match layout.backend_repr { + rustc_abi::BackendRepr::Scalar(scalar) => { + scalar.valid_range(&tcx).contains(u128::from(i.get())).then_some(ty) + } + _ => unreachable!(), + } + } _ => None, }); opt_ty.unwrap_or_else(|| self.next_int_var()) diff --git a/compiler/rustc_mir_build/src/builder/expr/as_constant.rs b/compiler/rustc_mir_build/src/builder/expr/as_constant.rs index 84c9297e65898..f743ea60a456c 100644 --- a/compiler/rustc_mir_build/src/builder/expr/as_constant.rs +++ b/compiler/rustc_mir_build/src/builder/expr/as_constant.rs @@ -117,7 +117,12 @@ fn lit_to_mir_constant<'tcx>(tcx: TyCtxt<'tcx>, lit_input: LitToConstInput<'tcx> ConstValue::Scalar(Scalar::from_uint(result, width)) }; - let value = match (lit, ty.kind()) { + let lit_ty = match *ty.kind() { + ty::Pat(base, _) => base, + _ => ty, + }; + + let value = match (lit, lit_ty.kind()) { (ast::LitKind::Str(s, _), ty::Ref(_, inner_ty, _)) if inner_ty.is_str() => { let s = s.as_str(); let allocation = Allocation::from_bytes_byte_aligned_immutable(s.as_bytes()); diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs index 5a4bb2c95da6f..7334beb52c9d5 100644 --- a/compiler/rustc_ty_utils/src/layout.rs +++ b/compiler/rustc_ty_utils/src/layout.rs @@ -220,6 +220,34 @@ fn layout_of_uncached<'tcx>( .try_to_bits(tcx, cx.typing_env) .ok_or_else(|| error(cx, LayoutError::Unknown(ty)))?; + // FIXME(pattern_types): create implied bounds from pattern types in signatures + // that require that the range end is >= the range start so that we can't hit + // this error anymore without first having hit a trait solver error. + // Very fuzzy on the details here, but pattern types are an internal impl detail, + // so we can just go with this for now + if scalar.is_signed() { + let range = scalar.valid_range_mut(); + let start = layout.size.sign_extend(range.start); + let end = layout.size.sign_extend(range.end); + if end < start { + let guar = tcx.dcx().err(format!( + "pattern type ranges cannot wrap: {start}..={end}" + )); + + return Err(error(cx, LayoutError::ReferencesError(guar))); + } + } else { + let range = scalar.valid_range_mut(); + if range.end < range.start { + let guar = tcx.dcx().err(format!( + "pattern type ranges cannot wrap: {}..={}", + range.start, range.end + )); + + return Err(error(cx, LayoutError::ReferencesError(guar))); + } + }; + let niche = Niche { offset: Size::ZERO, value: scalar.primitive(), diff --git a/tests/ui/type/pattern_types/literals.rs b/tests/ui/type/pattern_types/literals.rs new file mode 100644 index 0000000000000..97a918645f3e5 --- /dev/null +++ b/tests/ui/type/pattern_types/literals.rs @@ -0,0 +1,136 @@ +//! Check where literals can be used to initialize pattern types and where not. + +#![feature(pattern_types, const_trait_impl, pattern_type_range_trait)] +#![feature(pattern_type_macro)] + +use std::pat::pattern_type; + +fn out_of_range() -> pattern_type!(u32 is 1..) { + 0 + //~^ mismatched types +} + +fn at_range_start() -> pattern_type!(u32 is 1..) { + 1 +} + +fn in_range() -> pattern_type!(u32 is 1..) { + 2 +} + +fn negative_lit_on_unsigned_ty() -> pattern_type!(u32 is 1..) { + -3 + //~^ ERROR: cannot apply unary operator `-` to type `(u32) is 1..` +} + +fn negative_lit_in_range() -> pattern_type!(i8 is -5..5) { + -2 + //~^ ERROR: cannot apply unary operator `-` to type `(i8) is -5..=4` +} + +fn positive_lit_in_range_of_signed() -> pattern_type!(i8 is -5..5) { + 2 +} + +fn negative_lit_at_range_start() -> pattern_type!(i8 is -5..5) { + -5 + //~^ mismatched types +} + +fn positive_lit_at_range_end() -> pattern_type!(i8 is -5..5) { + 4 +} + +fn lit_one_beyond_range_end() -> pattern_type!(i8 is -5..5) { + 5 + //~^ mismatched types +} + +fn wrong_lit_kind() -> pattern_type!(u32 is 1..) { + '3' + //~^ mismatched types +} + +fn char_lit_in_range() -> pattern_type!(char is 'a'..'z') { + 'b' + //~^ mismatched types +} + +fn char_lit_out_of_range() -> pattern_type!(char is 'a'..'z') { + 'A' + //~^ mismatched types +} + +fn lit_at_unsigned_range_inclusive_end() -> pattern_type!(u32 is 0..=1) { + 1 +} + +fn single_element_range() -> pattern_type!(u32 is 0..=0) { + 0 +} + +fn lit_oob_single_element_range() -> pattern_type!(u32 is 0..=0) { + 1 + //~^ mismatched types +} + +fn lit_oob_single_element_range_exclusive() -> pattern_type!(u32 is 0..1) { + 1 + //~^ mismatched types +} + +fn single_element_range_exclusive() -> pattern_type!(u32 is 0..1) { + 0 +} + +fn empty_range_at_base_type_min() -> pattern_type!(u32 is 0..0) { + //~^ evaluation of constant value failed + 0 +} + +fn empty_range_at_base_type_min2() -> pattern_type!(u32 is 0..0) { + //~^ evaluation of constant value failed + 1 +} + +fn empty_range() -> pattern_type!(u32 is 1..1) { + 0 + //~^ mismatched types +} + +fn empty_range2() -> pattern_type!(u32 is 1..1) { + 1 + //~^ mismatched types +} + +fn wraparound_range_at_base_ty_end() -> pattern_type!(u32 is 1..0) { + //~^ evaluation of constant value failed + 1 +} + +fn wraparound_range_at_base_ty_end2() -> pattern_type!(u32 is 1..0) { + //~^ evaluation of constant value failed + 0 +} + +fn wraparound_range_at_base_ty_end3() -> pattern_type!(u32 is 1..0) { + //~^ evaluation of constant value failed + 2 +} + +fn wraparound_range() -> pattern_type!(u32 is 2..1) { + 1 + //~^ mismatched types +} + +fn lit_in_wraparound_range() -> pattern_type!(u32 is 2..1) { + 0 + //~^ mismatched types +} + +fn lit_at_wraparound_range_start() -> pattern_type!(u32 is 2..1) { + 2 + //~^ mismatched types +} + +fn main() {} diff --git a/tests/ui/type/pattern_types/literals.stderr b/tests/ui/type/pattern_types/literals.stderr new file mode 100644 index 0000000000000..5c926742f3cdb --- /dev/null +++ b/tests/ui/type/pattern_types/literals.stderr @@ -0,0 +1,193 @@ +error[E0080]: evaluation of constant value failed + --> $DIR/literals.rs:86:62 + | +LL | fn empty_range_at_base_type_min() -> pattern_type!(u32 is 0..0) { + | ^ evaluation panicked: exclusive range end at minimum value of type + +error[E0080]: evaluation of constant value failed + --> $DIR/literals.rs:91:63 + | +LL | fn empty_range_at_base_type_min2() -> pattern_type!(u32 is 0..0) { + | ^ evaluation panicked: exclusive range end at minimum value of type + +error[E0080]: evaluation of constant value failed + --> $DIR/literals.rs:106:65 + | +LL | fn wraparound_range_at_base_ty_end() -> pattern_type!(u32 is 1..0) { + | ^ evaluation panicked: exclusive range end at minimum value of type + +error[E0080]: evaluation of constant value failed + --> $DIR/literals.rs:111:66 + | +LL | fn wraparound_range_at_base_ty_end2() -> pattern_type!(u32 is 1..0) { + | ^ evaluation panicked: exclusive range end at minimum value of type + +error[E0080]: evaluation of constant value failed + --> $DIR/literals.rs:116:66 + | +LL | fn wraparound_range_at_base_ty_end3() -> pattern_type!(u32 is 1..0) { + | ^ evaluation panicked: exclusive range end at minimum value of type + +error[E0308]: mismatched types + --> $DIR/literals.rs:9:5 + | +LL | fn out_of_range() -> pattern_type!(u32 is 1..) { + | ------------------------- expected `(u32) is 1..` because of return type +LL | 0 + | ^ expected `(u32) is 1..`, found integer + | + = note: expected pattern type `(u32) is 1..` + found type `{integer}` + +error[E0600]: cannot apply unary operator `-` to type `(u32) is 1..` + --> $DIR/literals.rs:22:5 + | +LL | -3 + | ^^ cannot apply unary operator `-` + +error[E0600]: cannot apply unary operator `-` to type `(i8) is -5..=4` + --> $DIR/literals.rs:27:5 + | +LL | -2 + | ^^ cannot apply unary operator `-` + +error[E0308]: mismatched types + --> $DIR/literals.rs:36:5 + | +LL | fn negative_lit_at_range_start() -> pattern_type!(i8 is -5..5) { + | -------------------------- expected `(i8) is -5..=4` because of return type +LL | -5 + | ^^ expected `(i8) is -5..=4`, found integer + | + = note: expected pattern type `(i8) is -5..=4` + found type `{integer}` + +error[E0308]: mismatched types + --> $DIR/literals.rs:45:5 + | +LL | fn lit_one_beyond_range_end() -> pattern_type!(i8 is -5..5) { + | -------------------------- expected `(i8) is -5..=4` because of return type +LL | 5 + | ^ expected `(i8) is -5..=4`, found integer + | + = note: expected pattern type `(i8) is -5..=4` + found type `{integer}` + +error[E0308]: mismatched types + --> $DIR/literals.rs:50:5 + | +LL | fn wrong_lit_kind() -> pattern_type!(u32 is 1..) { + | ------------------------- expected `(u32) is 1..` because of return type +LL | '3' + | ^^^ expected `(u32) is 1..`, found `char` + | + = note: expected pattern type `(u32) is 1..` + found type `char` + +error[E0308]: mismatched types + --> $DIR/literals.rs:55:5 + | +LL | fn char_lit_in_range() -> pattern_type!(char is 'a'..'z') { + | ------------------------------- expected `(char) is 'a'..='y'` because of return type +LL | 'b' + | ^^^ expected `(char) is 'a'..='y'`, found `char` + | + = note: expected pattern type `(char) is 'a'..='y'` + found type `char` + +error[E0308]: mismatched types + --> $DIR/literals.rs:60:5 + | +LL | fn char_lit_out_of_range() -> pattern_type!(char is 'a'..'z') { + | ------------------------------- expected `(char) is 'a'..='y'` because of return type +LL | 'A' + | ^^^ expected `(char) is 'a'..='y'`, found `char` + | + = note: expected pattern type `(char) is 'a'..='y'` + found type `char` + +error[E0308]: mismatched types + --> $DIR/literals.rs:73:5 + | +LL | fn lit_oob_single_element_range() -> pattern_type!(u32 is 0..=0) { + | --------------------------- expected `(u32) is 0..=0` because of return type +LL | 1 + | ^ expected `(u32) is 0..=0`, found integer + | + = note: expected pattern type `(u32) is 0..=0` + found type `{integer}` + +error[E0308]: mismatched types + --> $DIR/literals.rs:78:5 + | +LL | fn lit_oob_single_element_range_exclusive() -> pattern_type!(u32 is 0..1) { + | -------------------------- expected `(u32) is 0..=0` because of return type +LL | 1 + | ^ expected `(u32) is 0..=0`, found integer + | + = note: expected pattern type `(u32) is 0..=0` + found type `{integer}` + +error: pattern type ranges cannot wrap: 1..=0 + +error[E0308]: mismatched types + --> $DIR/literals.rs:97:5 + | +LL | fn empty_range() -> pattern_type!(u32 is 1..1) { + | -------------------------- expected `(u32) is 1..=0` because of return type +LL | 0 + | ^ expected `(u32) is 1..=0`, found integer + | + = note: expected pattern type `(u32) is 1..=0` + found type `{integer}` + +error[E0308]: mismatched types + --> $DIR/literals.rs:102:5 + | +LL | fn empty_range2() -> pattern_type!(u32 is 1..1) { + | -------------------------- expected `(u32) is 1..=0` because of return type +LL | 1 + | ^ expected `(u32) is 1..=0`, found integer + | + = note: expected pattern type `(u32) is 1..=0` + found type `{integer}` + +error: pattern type ranges cannot wrap: 2..=0 + +error[E0308]: mismatched types + --> $DIR/literals.rs:122:5 + | +LL | fn wraparound_range() -> pattern_type!(u32 is 2..1) { + | -------------------------- expected `(u32) is 2..=0` because of return type +LL | 1 + | ^ expected `(u32) is 2..=0`, found integer + | + = note: expected pattern type `(u32) is 2..=0` + found type `{integer}` + +error[E0308]: mismatched types + --> $DIR/literals.rs:127:5 + | +LL | fn lit_in_wraparound_range() -> pattern_type!(u32 is 2..1) { + | -------------------------- expected `(u32) is 2..=0` because of return type +LL | 0 + | ^ expected `(u32) is 2..=0`, found integer + | + = note: expected pattern type `(u32) is 2..=0` + found type `{integer}` + +error[E0308]: mismatched types + --> $DIR/literals.rs:132:5 + | +LL | fn lit_at_wraparound_range_start() -> pattern_type!(u32 is 2..1) { + | -------------------------- expected `(u32) is 2..=0` because of return type +LL | 2 + | ^ expected `(u32) is 2..=0`, found integer + | + = note: expected pattern type `(u32) is 2..=0` + found type `{integer}` + +error: aborting due to 22 previous errors + +Some errors have detailed explanations: E0080, E0308, E0600. +For more information about an error, try `rustc --explain E0080`. diff --git a/tests/ui/type/pattern_types/nested.rs b/tests/ui/type/pattern_types/nested.rs index 0d8cd22190e55..fd950d7329148 100644 --- a/tests/ui/type/pattern_types/nested.rs +++ b/tests/ui/type/pattern_types/nested.rs @@ -1,6 +1,6 @@ //! Check that pattern types can only have specific base types -#![feature(pattern_types)] +#![feature(pattern_types, const_trait_impl, pattern_type_range_trait)] #![feature(pattern_type_macro)] use std::pat::pattern_type; @@ -14,7 +14,7 @@ const BAD_NESTING: pattern_type!(pattern_type!(u32 is 1..) is 0..) = todo!(); // We want to get the most narrowest version that a pattern could be const BAD_NESTING2: pattern_type!(pattern_type!(i32 is 1..) is ..=-1) = todo!(); //~^ ERROR: not a valid base type for range patterns -//~| ERROR: mismatched types +//~| ERROR: cannot apply unary operator `-` to type `(i32) is 1..` const BAD_NESTING3: pattern_type!(pattern_type!(i32 is 1..) is ..0) = todo!(); //~^ ERROR: not a valid base type for range patterns diff --git a/tests/ui/type/pattern_types/nested.stderr b/tests/ui/type/pattern_types/nested.stderr index f79d12bc3f376..bb206d9db3db8 100644 --- a/tests/ui/type/pattern_types/nested.stderr +++ b/tests/ui/type/pattern_types/nested.stderr @@ -43,14 +43,11 @@ LL | const BAD_NESTING2: pattern_type!(pattern_type!(i32 is 1..) is ..=-1) = tod u128 and 5 others -error[E0308]: mismatched types +error[E0600]: cannot apply unary operator `-` to type `(i32) is 1..` --> $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 - | - = note: expected pattern type `(i32) is 1..` - found type `{integer}` + | ^^ cannot apply unary operator `-` error[E0277]: `(i32) is 1..` is not a valid base type for range patterns --> $DIR/nested.rs:19:35 @@ -180,5 +177,5 @@ LL | const BAD_NESTING5: pattern_type!(f32 is 1.0 .. 2.0) = todo!(); error: aborting due to 11 previous errors -Some errors have detailed explanations: E0277, E0308. +Some errors have detailed explanations: E0277, E0308, E0600. For more information about an error, try `rustc --explain E0277`. diff --git a/tests/ui/type/pattern_types/range_patterns.rs b/tests/ui/type/pattern_types/range_patterns.rs index 446a33195c8bd..dda7eb0ae4eca 100644 --- a/tests/ui/type/pattern_types/range_patterns.rs +++ b/tests/ui/type/pattern_types/range_patterns.rs @@ -1,4 +1,4 @@ -#![feature(pattern_types, rustc_attrs)] +#![feature(pattern_types, rustc_attrs, const_trait_impl, pattern_type_range_trait)] #![feature(pattern_type_macro)] #![allow(incomplete_features)] @@ -18,6 +18,25 @@ type A = Option; //~ ERROR layout_of #[rustc_layout(debug)] struct NonZeroU32New(pattern_type!(u32 is 1..)); //~ ERROR layout_of +#[rustc_layout(debug)] +type EMPTY = pattern_type!(u32 is 1..1); //~ ERROR unknown layout + +#[rustc_layout(debug)] +type WRAP = pattern_type!(u32 is 1..0); //~ ERROR unknown layout +//~^ ERROR: evaluation of constant value failed + +#[rustc_layout(debug)] +type WRAP2 = pattern_type!(u32 is 5..2); //~ ERROR unknown layout + +#[rustc_layout(debug)] +type SIGN = pattern_type!(i8 is -10..=10); //~ ERROR layout_of + +#[rustc_layout(debug)] +type MIN = pattern_type!(i8 is -128..=0); //~ ERROR layout_of + +#[rustc_layout(debug)] +type SignedWrap = pattern_type!(i8 is 120..=-120); //~ ERROR unknown layout + fn main() { let x: pattern_type!(u32 is 1..) = unsafe { std::mem::transmute(42_u32) }; } diff --git a/tests/ui/type/pattern_types/range_patterns.stderr b/tests/ui/type/pattern_types/range_patterns.stderr index cb24a303404c6..a05995a33f9ba 100644 --- a/tests/ui/type/pattern_types/range_patterns.stderr +++ b/tests/ui/type/pattern_types/range_patterns.stderr @@ -357,5 +357,120 @@ error: layout_of(NonZeroU32New) = Layout { LL | struct NonZeroU32New(pattern_type!(u32 is 1..)); | ^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 5 previous errors +error: pattern type ranges cannot wrap: 1..=0 +error: the type has an unknown layout + --> $DIR/range_patterns.rs:22:1 + | +LL | type EMPTY = pattern_type!(u32 is 1..1); + | ^^^^^^^^^^ + +error[E0080]: evaluation of constant value failed + --> $DIR/range_patterns.rs:25:37 + | +LL | type WRAP = pattern_type!(u32 is 1..0); + | ^ evaluation panicked: exclusive range end at minimum value of type + +error: the type has an unknown layout + --> $DIR/range_patterns.rs:25:1 + | +LL | type WRAP = pattern_type!(u32 is 1..0); + | ^^^^^^^^^ + +error: pattern type ranges cannot wrap: 5..=1 + +error: the type has an unknown layout + --> $DIR/range_patterns.rs:29:1 + | +LL | type WRAP2 = pattern_type!(u32 is 5..2); + | ^^^^^^^^^^ + +error: layout_of((i8) is -10..=10) = Layout { + size: Size(1 bytes), + align: AbiAndPrefAlign { + abi: Align(1 bytes), + pref: $SOME_ALIGN, + }, + backend_repr: Scalar( + Initialized { + value: Int( + I8, + true, + ), + valid_range: (..=10) | (246..), + }, + ), + fields: Primitive, + largest_niche: Some( + Niche { + offset: Size(0 bytes), + value: Int( + I8, + true, + ), + valid_range: (..=10) | (246..), + }, + ), + uninhabited: false, + variants: Single { + index: 0, + }, + max_repr_align: None, + unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, + } + --> $DIR/range_patterns.rs:32:1 + | +LL | type SIGN = pattern_type!(i8 is -10..=10); + | ^^^^^^^^^ + +error: layout_of((i8) is i8::MIN..=0) = Layout { + size: Size(1 bytes), + align: AbiAndPrefAlign { + abi: Align(1 bytes), + pref: $SOME_ALIGN, + }, + backend_repr: Scalar( + Initialized { + value: Int( + I8, + true, + ), + valid_range: (..=0) | (128..), + }, + ), + fields: Primitive, + largest_niche: Some( + Niche { + offset: Size(0 bytes), + value: Int( + I8, + true, + ), + valid_range: (..=0) | (128..), + }, + ), + uninhabited: false, + variants: Single { + index: 0, + }, + max_repr_align: None, + unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, + } + --> $DIR/range_patterns.rs:35:1 + | +LL | type MIN = pattern_type!(i8 is -128..=0); + | ^^^^^^^^ + +error: pattern type ranges cannot wrap: 120..=-120 + +error: the type has an unknown layout + --> $DIR/range_patterns.rs:38:1 + | +LL | type SignedWrap = pattern_type!(i8 is 120..=-120); + | ^^^^^^^^^^^^^^^ + +error: aborting due to 15 previous errors + +For more information about this error, try `rustc --explain E0080`.