Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow int literals for pattern types with int base types #137715

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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())
Expand Down
7 changes: 6 additions & 1 deletion compiler/rustc_mir_build/src/builder/expr/as_constant.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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());
Expand Down
28 changes: 28 additions & 0 deletions compiler/rustc_ty_utils/src/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand Down
136 changes: 136 additions & 0 deletions tests/ui/type/pattern_types/literals.rs
Original file line number Diff line number Diff line change
@@ -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`
}
Comment on lines +26 to +29
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this something you expect to start supporting with your other work about having negated literals explicit in the ast/hir?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that's why I started that work


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() {}
Loading
Loading