Skip to content
Merged
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
16 changes: 8 additions & 8 deletions compiler/rustc_middle/src/thir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -832,18 +832,18 @@ pub enum PatKind<'tcx> {
value: ty::Value<'tcx>,
},

/// Pattern obtained by converting a constant (inline or named) to its pattern
/// representation using `const_to_pat`. This is used for unsafety checking.
/// Wrapper node representing a named constant that was lowered to a pattern
/// using `const_to_pat`.
///
/// This is used by some diagnostics for non-exhaustive matches, to map
/// the pattern node back to the `DefId` of its original constant.
///
/// FIXME(#150498): Can we make this an `Option<DefId>` field on `Pat`
/// instead, so that non-diagnostic code can ignore it more easily?
ExpandedConstant {
/// [DefId] of the constant item.
def_id: DefId,
/// The pattern that the constant lowered to.
///
/// HACK: we need to keep the `DefId` of inline constants around for unsafety checking;
/// therefore when a range pattern contains inline constants, we re-wrap the range pattern
/// with the `ExpandedConstant` nodes that correspond to the range endpoints. Hence
/// `subpattern` may actually be a range pattern, and `def_id` be the constant for one of
/// its endpoints.
subpattern: Box<Pat<'tcx>>,
},

Expand Down
8 changes: 0 additions & 8 deletions compiler/rustc_mir_build/src/check_unsafety.rs
Original file line number Diff line number Diff line change
Expand Up @@ -410,14 +410,6 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
visit::walk_pat(self, pat);
self.inside_adt = old_inside_adt;
}
PatKind::ExpandedConstant { def_id, .. } => {
if let Some(def) = def_id.as_local()
&& matches!(self.tcx.def_kind(def_id), DefKind::InlineConst)
{
self.visit_inner_body(def);
}
visit::walk_pat(self, pat);
}
_ => {
visit::walk_pat(self, pat);
}
Expand Down
36 changes: 23 additions & 13 deletions compiler/rustc_mir_build/src/thir/pattern/check_match.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use rustc_data_structures::stack::ensure_sufficient_stack;
use rustc_errors::codes::*;
use rustc_errors::{Applicability, ErrorGuaranteed, MultiSpan, struct_span_code_err};
use rustc_hir::def::*;
use rustc_hir::def_id::LocalDefId;
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir::{self as hir, BindingMode, ByRef, HirId, MatchSource};
use rustc_infer::infer::TyCtxtInferExt;
use rustc_lint::Level;
Expand Down Expand Up @@ -687,12 +687,7 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> {
unpeeled_pat = subpattern;
}

if let PatKind::ExpandedConstant { def_id, .. } = unpeeled_pat.kind
&& let DefKind::Const = self.tcx.def_kind(def_id)
&& let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(pat.span)
// We filter out paths with multiple path::segments.
&& snippet.chars().all(|c| c.is_alphanumeric() || c == '_')
{
if let Some(def_id) = is_const_pat_that_looks_like_binding(self.tcx, unpeeled_pat) {
let span = self.tcx.def_span(def_id);
let variable = self.tcx.item_name(def_id).to_string();
// When we encounter a constant as the binding name, point at the `const` definition.
Expand Down Expand Up @@ -1209,6 +1204,26 @@ fn pat_is_catchall(pat: &DeconstructedPat<'_, '_>) -> bool {
}
}

/// If the given pattern is a named constant that looks like it could have been
/// intended to be a binding, returns the `DefId` of the named constant.
///
/// Diagnostics use this to give more detailed suggestions for non-exhaustive
/// matches.
fn is_const_pat_that_looks_like_binding<'tcx>(tcx: TyCtxt<'tcx>, pat: &Pat<'tcx>) -> Option<DefId> {
// The pattern must be a named constant, and the name that appears in
// the pattern's source text must resemble a plain identifier without any
// `::` namespace separators or other non-identifier characters.
if let PatKind::ExpandedConstant { def_id, .. } = pat.kind
&& matches!(tcx.def_kind(def_id), DefKind::Const)
&& let Ok(snippet) = tcx.sess.source_map().span_to_snippet(pat.span)
&& snippet.chars().all(|c| c.is_alphanumeric() || c == '_')
{
Some(def_id)
} else {
None
}
}

/// Report that a match is not exhaustive.
fn report_non_exhaustive_match<'p, 'tcx>(
cx: &PatCtxt<'p, 'tcx>,
Expand Down Expand Up @@ -1303,12 +1318,7 @@ fn report_non_exhaustive_match<'p, 'tcx>(

for &arm in arms {
let arm = &thir.arms[arm];
if let PatKind::ExpandedConstant { def_id, .. } = arm.pattern.kind
&& !matches!(cx.tcx.def_kind(def_id), DefKind::InlineConst)
&& let Ok(snippet) = cx.tcx.sess.source_map().span_to_snippet(arm.pattern.span)
// We filter out paths with multiple path::segments.
&& snippet.chars().all(|c| c.is_alphanumeric() || c == '_')
{
if let Some(def_id) = is_const_pat_that_looks_like_binding(cx.tcx, &arm.pattern) {
let const_name = cx.tcx.item_name(def_id);
err.span_label(
arm.pattern.span,
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ impl<'tcx> ConstToPat<'tcx> {
}

// Wrap the pattern in a marker node to indicate that it is the result of lowering a
// constant. This is used for diagnostics, and for unsafety checking of inline const blocks.
// constant. This is used for diagnostics.
let kind = PatKind::ExpandedConstant { subpattern: inlined_const_as_pat, def_id: uv.def };
Box::new(Pat { kind, ty, span: self.span })
}
Expand Down
24 changes: 11 additions & 13 deletions compiler/rustc_mir_build/src/thir/pattern/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ use rustc_middle::ty::adjustment::{PatAdjust, PatAdjustment};
use rustc_middle::ty::layout::IntegerExt;
use rustc_middle::ty::{self, CanonicalUserTypeAnnotation, Ty, TyCtxt};
use rustc_middle::{bug, span_bug};
use rustc_span::def_id::DefId;
use rustc_span::{ErrorGuaranteed, Span};
use tracing::{debug, instrument};

Expand Down Expand Up @@ -131,9 +130,8 @@ impl<'tcx> PatCtxt<'tcx> {
fn lower_pattern_range_endpoint(
&mut self,
expr: Option<&'tcx hir::PatExpr<'tcx>>,
// Out-parameters collecting extra data to be reapplied by the caller
// Out-parameter collecting extra data to be reapplied by the caller
ascriptions: &mut Vec<Ascription<'tcx>>,
expanded_consts: &mut Vec<DefId>,
) -> Result<Option<PatRangeBoundary<'tcx>>, ErrorGuaranteed> {
let Some(expr) = expr else { return Ok(None) };

Expand All @@ -148,8 +146,10 @@ impl<'tcx> PatCtxt<'tcx> {
ascriptions.push(ascription);
kind = subpattern.kind;
}
PatKind::ExpandedConstant { def_id, subpattern } => {
expanded_consts.push(def_id);
PatKind::ExpandedConstant { def_id: _, subpattern } => {
// Expanded-constant nodes are currently only needed by
// diagnostics that don't apply to range patterns, so we
// can just discard them here.
kind = subpattern.kind;
}
_ => break,
Expand Down Expand Up @@ -227,10 +227,7 @@ impl<'tcx> PatCtxt<'tcx> {

// Collect extra data while lowering the endpoints, to be reapplied later.
let mut ascriptions = vec![];
let mut expanded_consts = vec![];

let mut lower_endpoint =
|expr| self.lower_pattern_range_endpoint(expr, &mut ascriptions, &mut expanded_consts);
let mut lower_endpoint = |expr| self.lower_pattern_range_endpoint(expr, &mut ascriptions);

let lo = lower_endpoint(lo_expr)?.unwrap_or(PatRangeBoundary::NegInfinity);
let hi = lower_endpoint(hi_expr)?.unwrap_or(PatRangeBoundary::PosInfinity);
Expand Down Expand Up @@ -282,10 +279,11 @@ impl<'tcx> PatCtxt<'tcx> {
let subpattern = Box::new(Pat { span, ty, kind });
kind = PatKind::AscribeUserType { ascription, subpattern };
}
for def_id in expanded_consts {
let subpattern = Box::new(Pat { span, ty, kind });
kind = PatKind::ExpandedConstant { def_id, subpattern };
}
// `PatKind::ExpandedConstant` wrappers from range endpoints used to
// also be preserved here, but that was only needed for unsafeck of
// inline `const { .. }` patterns, which were removed by
// <https://github.com/rust-lang/rust/pull/138492>.

Ok(kind)
}

Expand Down
55 changes: 49 additions & 6 deletions library/core/src/char/convert.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ impl const From<char> for u32 {
/// ```
/// let c = 'c';
/// let u = u32::from(c);
///
/// assert!(4 == size_of_val(&u))
/// ```
#[inline]
Expand All @@ -63,6 +64,7 @@ impl const From<char> for u64 {
/// ```
/// let c = '👤';
/// let u = u64::from(c);
///
/// assert!(8 == size_of_val(&u))
/// ```
#[inline]
Expand All @@ -83,6 +85,7 @@ impl const From<char> for u128 {
/// ```
/// let c = '⚙';
/// let u = u128::from(c);
///
/// assert!(16 == size_of_val(&u))
/// ```
#[inline]
Expand All @@ -93,8 +96,8 @@ impl const From<char> for u128 {
}
}

/// Maps a `char` with code point in U+0000..=U+00FF to a byte in 0x00..=0xFF with same value,
/// failing if the code point is greater than U+00FF.
/// Maps a `char` with a code point from U+0000 to U+00FF (inclusive) to a byte in `0x00..=0xFF` with
/// the same value, failing if the code point is greater than U+00FF.
///
/// See [`impl From<u8> for char`](char#impl-From<u8>-for-char) for details on the encoding.
#[stable(feature = "u8_from_char", since = "1.59.0")]
Expand All @@ -109,6 +112,7 @@ impl const TryFrom<char> for u8 {
/// ```
/// let a = 'ÿ'; // U+00FF
/// let b = 'Ā'; // U+0100
///
/// assert_eq!(u8::try_from(a), Ok(0xFF_u8));
/// assert!(u8::try_from(b).is_err());
/// ```
Expand All @@ -122,8 +126,8 @@ impl const TryFrom<char> for u8 {
}
}

/// Maps a `char` with code point in U+0000..=U+FFFF to a `u16` in 0x0000..=0xFFFF with same value,
/// failing if the code point is greater than U+FFFF.
/// Maps a `char` with a code point from U+0000 to U+FFFF (inclusive) to a `u16` in `0x0000..=0xFFFF`
/// with the same value, failing if the code point is greater than U+FFFF.
///
/// This corresponds to the UCS-2 encoding, as specified in ISO/IEC 10646:2003.
#[stable(feature = "u16_from_char", since = "1.74.0")]
Expand All @@ -138,6 +142,7 @@ impl const TryFrom<char> for u16 {
/// ```
/// let trans_rights = '⚧'; // U+26A7
/// let ninjas = '🥷'; // U+1F977
///
/// assert_eq!(u16::try_from(trans_rights), Ok(0x26A7_u16));
/// assert!(u16::try_from(ninjas).is_err());
/// ```
Expand All @@ -151,7 +156,45 @@ impl const TryFrom<char> for u16 {
}
}

/// Maps a byte in 0x00..=0xFF to a `char` whose code point has the same value, in U+0000..=U+00FF.
/// Maps a `char` with a code point from U+0000 to U+10FFFF (inclusive) to a `usize` in
/// `0x0000..=0x10FFFF` with the same value, failing if the final value is unrepresentable by
/// `usize`.
///
/// Generally speaking, this conversion can be seen as obtaining the character's corresponding
/// UTF-32 code point to the extent representable by pointer addresses.
#[stable(feature = "usize_try_from_char", since = "CURRENT_RUSTC_VERSION")]
#[rustc_const_unstable(feature = "const_convert", issue = "143773")]
impl const TryFrom<char> for usize {
type Error = TryFromCharError;

/// Tries to convert a [`char`] into a [`usize`].
///
/// # Examples
///
/// ```
/// let a = '\u{FFFF}'; // Always succeeds.
/// let b = '\u{10FFFF}'; // Conditionally succeeds.
///
/// assert_eq!(usize::try_from(a), Ok(0xFFFF));
///
/// if size_of::<usize>() >= size_of::<u32>() {
/// assert_eq!(usize::try_from(b), Ok(0x10FFFF));
/// } else {
/// assert!(matches!(usize::try_from(b), Err(_)));
/// }
/// ```
#[inline]
fn try_from(c: char) -> Result<usize, Self::Error> {
// FIXME(const-hack): this should use map_err instead
match usize::try_from(u32::from(c)) {
Ok(x) => Ok(x),
Err(_) => Err(TryFromCharError(())),
}
}
}

/// Maps a byte in `0x00..=0xFF` to a `char` whose code point has the same value from U+0000 to U+00FF
/// (inclusive).
///
/// Unicode is designed such that this effectively decodes bytes
/// with the character encoding that IANA calls ISO-8859-1.
Expand Down Expand Up @@ -179,6 +222,7 @@ impl const From<u8> for char {
/// ```
/// let u = 32 as u8;
/// let c = char::from(u);
///
/// assert!(4 == size_of_val(&c))
/// ```
#[inline]
Expand Down Expand Up @@ -246,7 +290,6 @@ const fn char_try_from_u32(i: u32) -> Result<char, CharTryFromError> {
// Subtracting 0x800 causes 0x0000..0x0800 to wrap, meaning that a single
// unsigned comparison against 0x110000 - 0x800 will detect both the wrapped
// surrogate range as well as the numbers originally larger than 0x110000.
//
if (i ^ 0xD800).wrapping_sub(0x800) >= 0x110000 - 0x800 {
Err(CharTryFromError(()))
} else {
Expand Down
2 changes: 1 addition & 1 deletion library/std/src/sys/fs/unix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2166,7 +2166,7 @@ fn open_from(from: &Path) -> io::Result<(crate::fs::File, crate::fs::Metadata)>

fn set_times_impl(p: &CStr, times: FileTimes, follow_symlinks: bool) -> io::Result<()> {
cfg_select! {
any(target_os = "redox", target_os = "espidf", target_os = "horizon", target_os = "nuttx", target_os = "vita") => {
any(target_os = "redox", target_os = "espidf", target_os = "horizon", target_os = "nuttx", target_os = "vita", target_os = "rtems") => {
let _ = (p, times, follow_symlinks);
Err(io::const_error!(
io::ErrorKind::Unsupported,
Expand Down
Loading