Avoid having to handle an Option in the type system

This commit is contained in:
Oli Scherer 2025-02-05 15:22:10 +00:00
parent 4f2b108816
commit 0e7b283573
21 changed files with 146 additions and 109 deletions

View File

@ -21,8 +21,9 @@ pub mod generics;
mod lint;
use std::assert_matches::assert_matches;
use std::slice;
use std::{char, slice};
use rustc_abi::Size;
use rustc_ast::TraitObjectSyntax;
use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
use rustc_errors::codes::*;
@ -31,7 +32,7 @@ use rustc_errors::{
};
use rustc_hir::def::{CtorKind, CtorOf, DefKind, Namespace, Res};
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir::{self as hir, AnonConst, GenericArg, GenericArgs, HirId};
use rustc_hir::{self as hir, AnonConst, ConstArg, GenericArg, GenericArgs, HirId};
use rustc_infer::infer::{InferCtxt, TyCtxtInferExt};
use rustc_infer::traits::ObligationCause;
use rustc_middle::middle::stability::AllowUnstable;
@ -2693,20 +2694,22 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
let ty = self.lower_ty(ty);
let pat_ty = match pat.kind {
hir::TyPatKind::Range(start, end, include_end) => {
let ty = match ty.kind() {
ty::Int(_) | ty::Uint(_) | ty::Char => ty,
_ => Ty::new_error(
tcx,
self.dcx().emit_err(InvalidBaseType {
let (ty, start, end) = match ty.kind() {
ty::Int(_) | ty::Uint(_) | ty::Char => {
let (start, end) = self.lower_ty_pat_range(ty, start, end);
(ty, start, end)
}
_ => {
let guar = self.dcx().emit_err(InvalidBaseType {
ty,
pat: "range",
ty_span,
pat_span: pat.span,
}),
),
});
let errc = ty::Const::new_error(tcx, guar);
(Ty::new_error(tcx, guar), errc, errc)
}
};
let start = start.map(|expr| self.lower_const_arg(expr, FeedConstTy::No));
let end = end.map(|expr| self.lower_const_arg(expr, FeedConstTy::No));
let pat = tcx.mk_pat(ty::PatternKind::Range { start, end, include_end });
Ty::new_pat(tcx, ty, pat)
@ -2723,6 +2726,70 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
result_ty
}
fn lower_ty_pat_range(
&self,
base: Ty<'tcx>,
start: Option<&ConstArg<'tcx>>,
end: Option<&ConstArg<'tcx>>,
) -> (ty::Const<'tcx>, ty::Const<'tcx>) {
let tcx = self.tcx();
let size = match base.kind() {
ty::Int(i) => {
i.bit_width().map_or(tcx.data_layout.pointer_size, |bits| Size::from_bits(bits))
}
ty::Uint(ui) => {
ui.bit_width().map_or(tcx.data_layout.pointer_size, |bits| Size::from_bits(bits))
}
ty::Char => Size::from_bytes(4),
_ => unreachable!(),
};
let start =
start.map(|expr| self.lower_const_arg(expr, FeedConstTy::No)).unwrap_or_else(|| {
match base.kind() {
ty::Char | ty::Uint(_) => ty::Const::new_value(
tcx,
ty::ValTree::from_scalar_int(ty::ScalarInt::null(size)),
base,
),
ty::Int(_) => ty::Const::new_value(
tcx,
ty::ValTree::from_scalar_int(
ty::ScalarInt::truncate_from_int(size.signed_int_min(), size).0,
),
base,
),
_ => unreachable!(),
}
});
let end = end.map(|expr| self.lower_const_arg(expr, FeedConstTy::No)).unwrap_or_else(
|| match base.kind() {
ty::Char => ty::Const::new_value(
tcx,
ty::ValTree::from_scalar_int(
ty::ScalarInt::truncate_from_uint(char::MAX, size).0,
),
base,
),
ty::Uint(_) => ty::Const::new_value(
tcx,
ty::ValTree::from_scalar_int(
ty::ScalarInt::truncate_from_uint(size.unsigned_int_max(), size).0,
),
base,
),
ty::Int(_) => ty::Const::new_value(
tcx,
ty::ValTree::from_scalar_int(
ty::ScalarInt::truncate_from_int(size.signed_int_max(), size).0,
),
base,
),
_ => unreachable!(),
},
);
(start, end)
}
/// Lower an opaque type (i.e., an existential impl-Trait type) from the HIR.
#[instrument(level = "debug", skip(self), ret)]
fn lower_opaque_ty(&self, def_id: LocalDefId, in_trait: bool) -> Ty<'tcx> {

View File

@ -253,12 +253,8 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> {
ty::Pat(typ, pat) => {
match *pat {
ty::PatternKind::Range { start, end, include_end: _ } => {
if let Some(start) = start {
self.add_constraints_from_const(current, start, variance);
}
if let Some(end) = end {
self.add_constraints_from_const(current, end, variance);
}
self.add_constraints_from_const(current, start, variance);
self.add_constraints_from_const(current, end, variance);
}
}
self.add_constraints_from_ty(current, typ, variance);

View File

@ -883,22 +883,14 @@ fn ty_is_known_nonnull<'tcx>(
try {
match **pat {
ty::PatternKind::Range { start, end, include_end } => {
match (start, end) {
(Some(start), None) => {
start.try_to_value()?.try_to_bits(tcx, typing_env)? > 0
}
(Some(start), Some(end)) => {
let start =
start.try_to_value()?.try_to_bits(tcx, typing_env)?;
let end =
end.try_to_value()?.try_to_bits(tcx, typing_env)?;
let start = start.try_to_value()?.try_to_bits(tcx, typing_env)?;
let end = end.try_to_value()?.try_to_bits(tcx, typing_env)?;
match include_end {
RangeEnd::Included => start > 0 && end >= start,
RangeEnd::Excluded => start > 0 && end > start,
}
}
_ => false,
match include_end {
// This also works for negative numbers, as we just need
// to ensure we aren't wrapping over zero.
RangeEnd::Included => start > 0 && end >= start,
RangeEnd::Excluded => start > 0 && end > start,
}
}
}

View File

@ -221,12 +221,8 @@ impl FlagComputation {
self.add_ty(ty);
match *pat {
ty::PatternKind::Range { start, end, include_end: _ } => {
if let Some(start) = start {
self.add_const(start)
}
if let Some(end) = end {
self.add_const(end)
}
self.add_const(start);
self.add_const(end);
}
}
}

View File

@ -28,14 +28,7 @@ impl<'tcx> fmt::Debug for PatternKind<'tcx> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
PatternKind::Range { start, end, include_end } => {
if let Some(start) = start {
write!(f, "{start}")?;
}
write!(f, "{include_end}")?;
if let Some(end) = end {
write!(f, "{end}")?;
}
Ok(())
write!(f, "{start}{include_end}{end}")
}
}
}
@ -44,5 +37,5 @@ impl<'tcx> fmt::Debug for PatternKind<'tcx> {
#[derive(Clone, PartialEq, Eq, Hash)]
#[derive(HashStable, TyEncodable, TyDecodable, TypeVisitable, TypeFoldable)]
pub enum PatternKind<'tcx> {
Range { start: Option<ty::Const<'tcx>>, end: Option<ty::Const<'tcx>>, include_end: RangeEnd },
Range { start: ty::Const<'tcx>, end: ty::Const<'tcx>, include_end: RangeEnd },
}

View File

@ -54,19 +54,18 @@ impl<'tcx> Relate<TyCtxt<'tcx>> for ty::Pattern<'tcx> {
&ty::PatternKind::Range { start: start_a, end: end_a, include_end: inc_a },
&ty::PatternKind::Range { start: start_b, end: end_b, include_end: inc_b },
) => {
// FIXME(pattern_types): make equal patterns equal (`0..=` is the same as `..=`).
let mut relate_opt_const = |a, b| match (a, b) {
(None, None) => Ok(None),
(Some(a), Some(b)) => relation.relate(a, b).map(Some),
// FIXME(pattern_types): report a better error
_ => Err(TypeError::Mismatch),
};
let start = relate_opt_const(start_a, start_b)?;
let end = relate_opt_const(end_a, end_b)?;
if inc_a != inc_b {
todo!()
let start = relation.relate(start_a, start_b)?;
// FIXME(pattern_types): make equal patterns equal (`0..5` is the same as `0..=6`).
let end = relation.relate(end_a, end_b)?;
if inc_a == inc_b {
Ok(relation.cx().mk_pat(ty::PatternKind::Range {
start,
end,
include_end: inc_a,
}))
} else {
Err(TypeError::Mismatch)
}
Ok(relation.cx().mk_pat(ty::PatternKind::Range { start, end, include_end: inc_a }))
}
}
}

View File

@ -138,8 +138,8 @@ fn push_inner<'tcx>(stack: &mut TypeWalkerStack<'tcx>, parent: GenericArg<'tcx>)
ty::Pat(ty, pat) => {
match *pat {
ty::PatternKind::Range { start, end, include_end: _ } => {
stack.extend(end.map(Into::into));
stack.extend(start.map(Into::into));
stack.push(end.into());
stack.push(start.into());
}
}
stack.push(ty.into());

View File

@ -90,8 +90,8 @@ impl RustcInternal for Pattern {
fn internal<'tcx>(&self, tables: &mut Tables<'_>, tcx: TyCtxt<'tcx>) -> Self::T<'tcx> {
tcx.mk_pat(match self {
Pattern::Range { start, end, include_end } => rustc_ty::PatternKind::Range {
start: start.as_ref().map(|c| c.internal(tables, tcx)),
end: end.as_ref().map(|c| c.internal(tables, tcx)),
start: start.as_ref().unwrap().internal(tables, tcx),
end: end.as_ref().unwrap().internal(tables, tcx),
include_end: if *include_end { RangeEnd::Included } else { RangeEnd::Excluded },
},
})

View File

@ -406,8 +406,9 @@ impl<'tcx> Stable<'tcx> for ty::Pattern<'tcx> {
fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
match **self {
ty::PatternKind::Range { start, end, include_end } => stable_mir::ty::Pattern::Range {
start: start.stable(tables),
end: end.stable(tables),
// FIXME(SMIR): update data structures to not have an Option here anymore
start: Some(start.stable(tables)),
end: Some(end.stable(tables)),
include_end: matches!(include_end, rustc_hir::RangeEnd::Included),
},
}

View File

@ -415,8 +415,8 @@ impl<'tcx> Printer<'tcx> for SymbolMangler<'tcx> {
ty::Pat(ty, pat) => match *pat {
ty::PatternKind::Range { start, end, include_end } => {
let consts = [
start.unwrap_or(self.tcx.consts.unit),
end.unwrap_or(self.tcx.consts.unit),
start,
end,
ty::Const::from_bool(
self.tcx,
matches!(include_end, rustc_hir::RangeEnd::Included),

View File

@ -738,12 +738,8 @@ impl<'a, 'tcx> TypeVisitor<TyCtxt<'tcx>> for WfPredicates<'a, 'tcx> {
}
}
};
if let Some(start) = start {
check(start)
}
if let Some(end) = end {
check(end)
}
check(start);
check(end);
}
}
}

View File

@ -209,21 +209,18 @@ fn layout_of_uncached<'tcx>(
if let BackendRepr::Scalar(scalar) | BackendRepr::ScalarPair(scalar, _) =
&mut layout.backend_repr
{
if let Some(start) = start {
scalar.valid_range_mut().start = extract_const_value(cx, ty, start)?
.try_to_bits(tcx, cx.typing_env)
.ok_or_else(|| error(cx, LayoutError::Unknown(ty)))?;
}
if let Some(end) = end {
let mut end = extract_const_value(cx, ty, end)?
.try_to_bits(tcx, cx.typing_env)
.ok_or_else(|| error(cx, LayoutError::Unknown(ty)))?;
match include_end {
rustc_hir::RangeEnd::Included => {}
rustc_hir::RangeEnd::Excluded => end = end.wrapping_sub(1),
}
scalar.valid_range_mut().end = end;
scalar.valid_range_mut().start = extract_const_value(cx, ty, start)?
.try_to_bits(tcx, cx.typing_env)
.ok_or_else(|| error(cx, LayoutError::Unknown(ty)))?;
let mut end = extract_const_value(cx, ty, end)?
.try_to_bits(tcx, cx.typing_env)
.ok_or_else(|| error(cx, LayoutError::Unknown(ty)))?;
match include_end {
rustc_hir::RangeEnd::Included => {}
rustc_hir::RangeEnd::Excluded => end = end.wrapping_sub(1),
}
scalar.valid_range_mut().end = end;
let niche = Niche {
offset: Size::ZERO,

View File

@ -3,9 +3,9 @@
fn main() -> () {
let mut _0: ();
scope 1 {
debug x => const 2_u32 is 1..=;
debug x => const 2_u32 is 1..=u32::MAX;
scope 2 {
debug y => const {transmute(0x00000000): (u32) is 1..=};
debug y => const {transmute(0x00000000): (u32) is 1..=u32::MAX};
}
}

View File

@ -5,8 +5,8 @@ use std::pat::pattern_type;
// EMIT_MIR pattern_types.main.PreCodegen.after.mir
fn main() {
// CHECK: debug x => const 2_u32 is 1..=
// CHECK: debug x => const 2_u32 is 1..=u32::MAX
let x: pattern_type!(u32 is 1..) = unsafe { std::mem::transmute(2) };
// CHECK: debug y => const {transmute(0x00000000): (u32) is 1..=}
// CHECK: debug y => const {transmute(0x00000000): (u32) is 1..=u32::MAX}
let y: pattern_type!(u32 is 1..) = unsafe { std::mem::transmute(0) };
}

View File

@ -17,7 +17,7 @@ LL | fn hidden_niche_unsafe_cell() -> Option<UnsafeCell<NonZero<usiz
= help: consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum
= note: enum has no representation hint
warning: `extern` block uses type `Option<(usize) is 0..=>`, which is not FFI-safe
warning: `extern` block uses type `Option<(usize) is 0..=usize::MAX>`, which is not FFI-safe
--> $DIR/clashing-extern-fn.rs:502:54
|
LL | fn pt_non_zero_usize_opt_full_range() -> Option<pattern_type!(usize is 0..)>;
@ -276,7 +276,7 @@ LL | fn pt_non_null_ptr() -> pattern_type!(usize is 1..);
LL | fn pt_non_null_ptr() -> *const ();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this signature doesn't match the previous declaration
|
= note: expected `unsafe extern "C" fn() -> (usize) is 1..=`
= note: expected `unsafe extern "C" fn() -> (usize) is 1..=usize::MAX`
found `unsafe extern "C" fn() -> *const ()`
warning: 24 warnings emitted

View File

@ -1,4 +1,4 @@
error: `(u32) is 1..=` is not a valid base type for range patterns
error: `(u32) is 1..=u32::MAX` is not a valid base type for range patterns
--> $DIR/nested.rs:10:34
|
LL | const BAD_NESTING: pattern_type!(pattern_type!(u32 is 1..) is 0..) = todo!();
@ -10,7 +10,7 @@ note: range patterns only support integers
LL | const BAD_NESTING: pattern_type!(pattern_type!(u32 is 1..) is 0..) = todo!();
| ^^^
error: `(i32) is 1..=` is not a valid base type for range patterns
error: `(i32) is 1..=i32::MAX` is not a valid base type for range patterns
--> $DIR/nested.rs:15:35
|
LL | const BAD_NESTING2: pattern_type!(pattern_type!(i32 is 1..) is ..=-1) = todo!();
@ -22,7 +22,7 @@ note: range patterns only support integers
LL | const BAD_NESTING2: pattern_type!(pattern_type!(i32 is 1..) is ..=-1) = todo!();
| ^^^^^
error: `(i32) is 1..=` is not a valid base type for range patterns
error: `(i32) is 1..=i32::MAX` is not a valid base type for range patterns
--> $DIR/nested.rs:19:35
|
LL | const BAD_NESTING3: pattern_type!(pattern_type!(i32 is 1..) is ..0) = todo!();
@ -62,27 +62,27 @@ error[E0308]: mismatched types
--> $DIR/nested.rs:10:63
|
LL | const BAD_NESTING: pattern_type!(pattern_type!(u32 is 1..) is 0..) = todo!();
| ^ expected `(u32) is 1..=`, found integer
| ^ expected `(u32) is 1..=u32::MAX`, found integer
|
= note: expected pattern type `(u32) is 1..=`
= note: expected pattern type `(u32) is 1..=u32::MAX`
found type `{integer}`
error[E0308]: mismatched types
--> $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
| ^^ expected `(i32) is 1..=i32::MAX`, found integer
|
= note: expected pattern type `(i32) is 1..=`
= note: expected pattern type `(i32) is 1..=i32::MAX`
found type `{integer}`
error[E0308]: mismatched types
--> $DIR/nested.rs:19:66
|
LL | const BAD_NESTING3: pattern_type!(pattern_type!(i32 is 1..) is ..0) = todo!();
| ^ expected `(i32) is 1..=`, found integer
| ^ expected `(i32) is 1..=i32::MAX`, found integer
|
= note: expected pattern type `(i32) is 1..=`
= note: expected pattern type `(i32) is 1..=i32::MAX`
found type `{integer}`
error[E0308]: mismatched types

View File

@ -44,7 +44,7 @@ error: layout_of(NonZero<u32>) = Layout {
LL | type X = std::num::NonZeroU32;
| ^^^^^^
error: layout_of((u32) is 1..=) = Layout {
error: layout_of((u32) is 1..=u32::MAX) = Layout {
size: Size(4 bytes),
align: AbiAndPrefAlign {
abi: Align(4 bytes),
@ -83,7 +83,7 @@ error: layout_of((u32) is 1..=) = Layout {
LL | type Y = pattern_type!(u32 is 1..);
| ^^^^^^
error: layout_of(Option<(u32) is 1..=>) = Layout {
error: layout_of(Option<(u32) is 1..=u32::MAX>) = Layout {
size: Size(4 bytes),
align: AbiAndPrefAlign {
abi: Align(4 bytes),

View File

@ -4,7 +4,7 @@ error[E0117]: only traits defined in the current crate can be implemented for ar
LL | impl Eq for Y {}
| ^^^^^^^^^^^^-
| |
| `(u32) is 1..=` is not defined in the current crate
| `(u32) is 1..=u32::MAX` is not defined in the current crate
|
= note: impl doesn't have any local type before any uncovered type parameters
= note: for more information see https://doc.rust-lang.org/reference/items/implementations.html#orphan-rules

View File

@ -4,7 +4,7 @@ error[E0512]: cannot transmute between types of different sizes, or dependently-
LL | let _: Option<u32> = unsafe { std::mem::transmute(z) };
| ^^^^^^^^^^^^^^^^^^^
|
= note: source type: `Option<(u32) is 1..=>` (32 bits)
= note: source type: `Option<(u32) is 1..=u32::MAX>` (32 bits)
= note: target type: `Option<u32>` (64 bits)
error: aborting due to 1 previous error

View File

@ -11,5 +11,5 @@ type Z = Option<pattern_type!(u32 is 1..)>;
fn main() {
let x: Y = unsafe { std::mem::transmute(42_u32) };
let x = x + 1_u32; //~ ERROR cannot add `u32` to `(u32) is 1..=`
let x = x + 1_u32; //~ ERROR cannot add `u32` to `(u32) is 1..=u32::MAX`
}

View File

@ -1,10 +1,10 @@
error[E0369]: cannot add `u32` to `(u32) is 1..=`
error[E0369]: cannot add `u32` to `(u32) is 1..=u32::MAX`
--> $DIR/range_patterns_unusable_math.rs:14:15
|
LL | let x = x + 1_u32;
| - ^ ----- u32
| |
| (u32) is 1..=
| (u32) is 1..=u32::MAX
error: aborting due to 1 previous error