Auto merge of #143621 - matthiaskrgr:rollup-p1ce8l7, r=matthiaskrgr

Rollup of 7 pull requests

Successful merges:

 - rust-lang/rust#142098 (Implement `int_format_into` feature)
 - rust-lang/rust#143567 (Point to correct argument in Func Call when Self type fails trait bound)
 - rust-lang/rust#143570 (consider nested cases for duplicate RPITIT)
 - rust-lang/rust#143571 (remove `has_nested` from builtin candidates)
 - rust-lang/rust#143586 (Fix wrong cache event query key)
 - rust-lang/rust#143589 (const-block-as-pattern: do not refer to no-longer-existing nightly feature)
 - rust-lang/rust#143608 (Fix in std::String docs)

r? `@ghost`
`@rustbot` modify labels: rollup
This commit is contained in:
bors 2025-07-08 03:30:58 +00:00
commit 2783fc43fd
28 changed files with 600 additions and 178 deletions

View file

@ -142,7 +142,7 @@ const EVENT_FILTERS_BY_NAME: &[(&str, EventFilter)] = &[
("generic-activity", EventFilter::GENERIC_ACTIVITIES),
("query-provider", EventFilter::QUERY_PROVIDERS),
("query-cache-hit", EventFilter::QUERY_CACHE_HITS),
("query-cache-hit-count", EventFilter::QUERY_CACHE_HITS),
("query-cache-hit-count", EventFilter::QUERY_CACHE_HIT_COUNTS),
("query-blocked", EventFilter::QUERY_BLOCKED),
("incr-cache-load", EventFilter::INCR_CACHE_LOADS),
("query-keys", EventFilter::QUERY_KEYS),

View file

@ -184,9 +184,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
return true;
}
for param in [param_to_point_at, fallback_param_to_point_at, self_param_to_point_at]
.into_iter()
.flatten()
for param in [
predicate_self_type_to_point_at,
param_to_point_at,
fallback_param_to_point_at,
self_param_to_point_at,
]
.into_iter()
.flatten()
{
if self.blame_specific_arg_if_possible(
error,

View file

@ -97,9 +97,7 @@ pub type EvaluationCache<'tcx, ENV> = Cache<(ENV, ty::PolyTraitPredicate<'tcx>),
pub enum SelectionCandidate<'tcx> {
/// A built-in implementation for the `Sized` trait. This is preferred
/// over all other candidates.
SizedCandidate {
has_nested: bool,
},
SizedCandidate,
/// A builtin implementation for some specific traits, used in cases
/// where we cannot rely an ordinary library implementations.
@ -107,10 +105,7 @@ pub enum SelectionCandidate<'tcx> {
/// The most notable examples are `Copy` and `Clone`. This is also
/// used for the `DiscriminantKind` and `Pointee` trait, both of which have
/// an associated type.
BuiltinCandidate {
/// `false` if there are no *further* obligations.
has_nested: bool,
},
BuiltinCandidate,
/// Implementation of transmutability trait.
TransmutabilityCandidate,

View file

@ -1293,8 +1293,10 @@ impl<'a> Parser<'a> {
let kind = if pat {
let guar = self
.dcx()
.struct_span_err(blk_span, "`inline_const_pat` has been removed")
.with_help("use a named `const`-item or an `if`-guard instead")
.struct_span_err(blk_span, "const blocks cannot be used as patterns")
.with_help(
"use a named `const`-item or an `if`-guard (`x if x == const { ... }`) instead",
)
.emit();
ExprKind::Err(guar)
} else {

View file

@ -80,11 +80,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
}
Some(LangItem::DiscriminantKind) => {
// `DiscriminantKind` is automatically implemented for every type.
candidates.vec.push(BuiltinCandidate { has_nested: false });
candidates.vec.push(BuiltinCandidate);
}
Some(LangItem::PointeeTrait) => {
// `Pointee` is automatically implemented for every type.
candidates.vec.push(BuiltinCandidate { has_nested: false });
candidates.vec.push(BuiltinCandidate);
}
Some(LangItem::Sized) => {
self.assemble_builtin_sized_candidate(
@ -365,7 +365,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
{
debug!(?self_ty, ?obligation, "assemble_fused_iterator_candidates",);
candidates.vec.push(BuiltinCandidate { has_nested: false });
candidates.vec.push(BuiltinCandidate);
}
}
@ -810,7 +810,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
hir::Movability::Movable => {
// Movable coroutines are always `Unpin`, so add an
// unconditional builtin candidate.
candidates.vec.push(BuiltinCandidate { has_nested: false });
candidates.vec.push(BuiltinCandidate);
}
}
}
@ -1122,10 +1122,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
sizedness: SizedTraitKind,
) {
match self.sizedness_conditions(obligation, sizedness) {
BuiltinImplConditions::Where(nested) => {
candidates
.vec
.push(SizedCandidate { has_nested: !nested.skip_binder().is_empty() });
BuiltinImplConditions::Where(_nested) => {
candidates.vec.push(SizedCandidate);
}
BuiltinImplConditions::None => {}
BuiltinImplConditions::Ambiguous => {
@ -1143,10 +1141,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
candidates: &mut SelectionCandidateSet<'tcx>,
) {
match conditions {
BuiltinImplConditions::Where(nested) => {
candidates
.vec
.push(BuiltinCandidate { has_nested: !nested.skip_binder().is_empty() });
BuiltinImplConditions::Where(_) => {
candidates.vec.push(BuiltinCandidate);
}
BuiltinImplConditions::None => {}
BuiltinImplConditions::Ambiguous => {
@ -1160,7 +1156,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
_obligation: &PolyTraitObligation<'tcx>,
candidates: &mut SelectionCandidateSet<'tcx>,
) {
candidates.vec.push(BuiltinCandidate { has_nested: false });
candidates.vec.push(BuiltinCandidate);
}
fn assemble_candidate_for_tuple(
@ -1171,7 +1167,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
let self_ty = self.infcx.shallow_resolve(obligation.self_ty().skip_binder());
match self_ty.kind() {
ty::Tuple(_) => {
candidates.vec.push(BuiltinCandidate { has_nested: false });
candidates.vec.push(BuiltinCandidate);
}
ty::Infer(ty::TyVar(_)) => {
candidates.ambiguous = true;
@ -1215,7 +1211,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
let self_ty = self.infcx.resolve_vars_if_possible(obligation.self_ty());
match self_ty.skip_binder().kind() {
ty::FnPtr(..) => candidates.vec.push(BuiltinCandidate { has_nested: false }),
ty::FnPtr(..) => candidates.vec.push(BuiltinCandidate),
ty::Bool
| ty::Char
| ty::Int(_)

View file

@ -37,13 +37,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
candidate: SelectionCandidate<'tcx>,
) -> Result<Selection<'tcx>, SelectionError<'tcx>> {
Ok(match candidate {
SizedCandidate { has_nested } => {
let data = self.confirm_builtin_candidate(obligation, has_nested);
SizedCandidate => {
let data = self.confirm_builtin_candidate(obligation);
ImplSource::Builtin(BuiltinImplSource::Misc, data)
}
BuiltinCandidate { has_nested } => {
let data = self.confirm_builtin_candidate(obligation, has_nested);
BuiltinCandidate => {
let data = self.confirm_builtin_candidate(obligation);
ImplSource::Builtin(BuiltinImplSource::Misc, data)
}
@ -249,50 +249,47 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
}
}
#[instrument(level = "debug", skip(self), ret)]
fn confirm_builtin_candidate(
&mut self,
obligation: &PolyTraitObligation<'tcx>,
has_nested: bool,
) -> PredicateObligations<'tcx> {
debug!(?obligation, ?has_nested, "confirm_builtin_candidate");
debug!(?obligation, "confirm_builtin_candidate");
let tcx = self.tcx();
let obligations = if has_nested {
let trait_def = obligation.predicate.def_id();
let conditions = match tcx.as_lang_item(trait_def) {
Some(LangItem::Sized) => {
self.sizedness_conditions(obligation, SizedTraitKind::Sized)
}
Some(LangItem::MetaSized) => {
self.sizedness_conditions(obligation, SizedTraitKind::MetaSized)
}
Some(LangItem::PointeeSized) => {
bug!("`PointeeSized` is removing during lowering");
}
Some(LangItem::Copy | LangItem::Clone) => self.copy_clone_conditions(obligation),
Some(LangItem::FusedIterator) => self.fused_iterator_conditions(obligation),
other => bug!("unexpected builtin trait {trait_def:?} ({other:?})"),
};
let BuiltinImplConditions::Where(types) = conditions else {
bug!("obligation {:?} had matched a builtin impl but now doesn't", obligation);
};
let types = self.infcx.enter_forall_and_leak_universe(types);
let cause = obligation.derived_cause(ObligationCauseCode::BuiltinDerived);
self.collect_predicates_for_types(
obligation.param_env,
cause,
obligation.recursion_depth + 1,
trait_def,
types,
)
} else {
PredicateObligations::new()
let trait_def = obligation.predicate.def_id();
let conditions = match tcx.as_lang_item(trait_def) {
Some(LangItem::Sized) => self.sizedness_conditions(obligation, SizedTraitKind::Sized),
Some(LangItem::MetaSized) => {
self.sizedness_conditions(obligation, SizedTraitKind::MetaSized)
}
Some(LangItem::PointeeSized) => {
bug!("`PointeeSized` is removing during lowering");
}
Some(LangItem::Copy | LangItem::Clone) => self.copy_clone_conditions(obligation),
Some(LangItem::FusedIterator) => self.fused_iterator_conditions(obligation),
Some(
LangItem::Destruct
| LangItem::DiscriminantKind
| LangItem::FnPtrTrait
| LangItem::PointeeTrait
| LangItem::Tuple
| LangItem::Unpin,
) => BuiltinImplConditions::Where(ty::Binder::dummy(vec![])),
other => bug!("unexpected builtin trait {trait_def:?} ({other:?})"),
};
let BuiltinImplConditions::Where(types) = conditions else {
bug!("obligation {:?} had matched a builtin impl but now doesn't", obligation);
};
let types = self.infcx.enter_forall_and_leak_universe(types);
debug!(?obligations);
obligations
let cause = obligation.derived_cause(ObligationCauseCode::BuiltinDerived);
self.collect_predicates_for_types(
obligation.param_env,
cause,
obligation.recursion_depth + 1,
trait_def,
types,
)
}
#[instrument(level = "debug", skip(self))]

View file

@ -1834,7 +1834,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
// We prefer `Sized` candidates over everything.
let mut sized_candidates =
candidates.iter().filter(|c| matches!(c.candidate, SizedCandidate { has_nested: _ }));
candidates.iter().filter(|c| matches!(c.candidate, SizedCandidate));
if let Some(sized_candidate) = sized_candidates.next() {
// There should only ever be a single sized candidate
// as they would otherwise overlap.
@ -1986,8 +1986,8 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
// Don't use impl candidates which overlap with other candidates.
// This should pretty much only ever happen with malformed impls.
if candidates.iter().all(|c| match c.candidate {
SizedCandidate { has_nested: _ }
| BuiltinCandidate { has_nested: _ }
SizedCandidate
| BuiltinCandidate
| TransmutabilityCandidate
| AutoImplCandidate
| ClosureCandidate { .. }

View file

@ -177,6 +177,17 @@ impl<'tcx> Visitor<'tcx> for RPITVisitor<'tcx> {
}
}
struct DisambiguatorIdxVisitor {
depth: u32,
}
impl<'tcx> Visitor<'tcx> for DisambiguatorIdxVisitor {
fn visit_opaque_ty(&mut self, opaque: &'tcx hir::OpaqueTy<'tcx>) -> Self::Result {
self.depth += 1;
intravisit::walk_opaque_ty(self, opaque)
}
}
/// Given an `fn_def_id` of a trait or a trait implementation:
///
/// if `fn_def_id` is a function defined inside a trait, then it synthesizes
@ -211,16 +222,19 @@ fn associated_types_for_impl_traits_in_associated_fn(
let disambiguator_idx = trait_item_refs
.iter()
.take_while(|item| item.id.owner_id.def_id != fn_def_id)
.fold(0, |acc, item| {
if !matches!(item.kind, hir::AssocItemKind::Fn { .. }) {
acc
} else if def_path_id(item.id.owner_id.def_id) == def_path_data {
tcx.def_key(item.id.owner_id.def_id).disambiguated_data.disambiguator
+ 1
} else {
acc
}
});
.filter(|item| {
matches!(item.kind, hir::AssocItemKind::Fn { .. })
&& def_path_id(item.id.owner_id.def_id) == def_path_data
})
.last()
.map(|item| {
let output = tcx.hir_get_fn_output(item.id.owner_id.def_id).unwrap();
let mut visitor = DisambiguatorIdxVisitor { depth: 0 };
visitor.visit_fn_ret_ty(output);
tcx.def_key(item.id.owner_id.def_id).disambiguated_data.disambiguator
+ visitor.depth
})
.unwrap_or_default();
let data = DefPathData::AnonAssocTy(def_path_data);
let mut visitor = RPITVisitor {

View file

@ -156,7 +156,7 @@ use crate::vec::{self, Vec};
/// ```
///
/// Next, what should `s[i]` return? Because indexing returns a reference
/// to underlying data it could be `&u8`, `&[u8]`, or something else similar.
/// to underlying data it could be `&u8`, `&[u8]`, or something similar.
/// Since we're only providing one index, `&u8` makes the most sense but that
/// might not be what the user expects and can be explicitly achieved with
/// [`as_bytes()`]:
@ -2875,7 +2875,8 @@ macro_rules! impl_to_string {
out = String::with_capacity(SIZE);
}
out.push_str(self.unsigned_abs()._fmt(&mut buf));
// SAFETY: `buf` is always big enough to contain all the digits.
unsafe { out.push_str(self.unsigned_abs()._fmt(&mut buf)); }
out
}
}
@ -2887,7 +2888,8 @@ macro_rules! impl_to_string {
const SIZE: usize = $unsigned::MAX.ilog10() as usize + 1;
let mut buf = [core::mem::MaybeUninit::<u8>::uninit(); SIZE];
self._fmt(&mut buf).to_string()
// SAFETY: `buf` is always big enough to contain all the digits.
unsafe { self._fmt(&mut buf).to_string() }
}
}
)*

View file

@ -9,6 +9,7 @@
#![feature(downcast_unchecked)]
#![feature(exact_size_is_empty)]
#![feature(hashmap_internals)]
#![feature(int_format_into)]
#![feature(linked_list_cursors)]
#![feature(map_try_insert)]
#![feature(pattern)]

View file

@ -1,15 +1,21 @@
use std::fmt::{Debug, Display};
use core::fmt::NumBuffer;
use std::str::FromStr;
fn assert_nb<Int: ToString + FromStr + Debug + Display + Eq>(value: Int) {
let s = value.to_string();
let s2 = format!("s: {}.", value);
macro_rules! assert_nb {
($int:ident, $value:expr) => {
let value: $int = $value;
let s = value.to_string();
let s2 = format!("s: {}.", value);
assert_eq!(format!("s: {s}."), s2);
let Ok(ret) = Int::from_str(&s) else {
panic!("failed to convert into to string");
assert_eq!(format!("s: {s}."), s2);
let Ok(ret) = $int::from_str(&s) else {
panic!("failed to convert into to string");
};
assert_eq!(ret, value);
let mut buffer = NumBuffer::<$int>::new();
assert_eq!(value.format_into(&mut buffer), s.as_str());
};
assert_eq!(ret, value);
}
macro_rules! uint_to_s {
@ -17,11 +23,11 @@ macro_rules! uint_to_s {
$(
#[test]
fn $fn_name() {
assert_nb::<$int>($int::MIN);
assert_nb::<$int>($int::MAX);
assert_nb::<$int>(1);
assert_nb::<$int>($int::MIN / 2);
assert_nb::<$int>($int::MAX / 2);
assert_nb!($int, $int::MIN);
assert_nb!($int, $int::MAX);
assert_nb!($int, 1);
assert_nb!($int, $int::MIN / 2);
assert_nb!($int, $int::MAX / 2);
}
)+
}
@ -31,13 +37,13 @@ macro_rules! int_to_s {
$(
#[test]
fn $fn_name() {
assert_nb::<$int>($int::MIN);
assert_nb::<$int>($int::MAX);
assert_nb::<$int>(1);
assert_nb::<$int>(0);
assert_nb::<$int>(-1);
assert_nb::<$int>($int::MIN / 2);
assert_nb::<$int>($int::MAX / 2);
assert_nb!($int, $int::MIN);
assert_nb!($int, $int::MAX);
assert_nb!($int, 1);
assert_nb!($int, 0);
assert_nb!($int, -1);
assert_nb!($int, $int::MIN / 2);
assert_nb!($int, $int::MAX / 2);
}
)+
}

View file

@ -15,6 +15,7 @@ mod float;
#[cfg(no_fp_fmt_parse)]
mod nofloat;
mod num;
mod num_buffer;
mod rt;
#[stable(feature = "fmt_flags_align", since = "1.28.0")]
@ -33,6 +34,9 @@ pub enum Alignment {
Center,
}
#[unstable(feature = "int_format_into", issue = "138215")]
pub use num_buffer::{NumBuffer, NumBufferTrait};
#[stable(feature = "debug_builders", since = "1.2.0")]
pub use self::builders::{DebugList, DebugMap, DebugSet, DebugStruct, DebugTuple};
#[unstable(feature = "debug_closure_helpers", issue = "117729")]

View file

@ -1,5 +1,6 @@
//! Integer and floating-point number formatting
use crate::fmt::NumBuffer;
use crate::mem::MaybeUninit;
use crate::num::fmt as numfmt;
use crate::ops::{Div, Rem, Sub};
@ -60,7 +61,7 @@ unsafe trait GenericRadix: Sized {
let zero = T::zero();
let is_nonnegative = x >= zero;
let mut buf = [MaybeUninit::<u8>::uninit(); 128];
let mut curr = buf.len();
let mut offset = buf.len();
let base = T::from_u8(Self::BASE);
if is_nonnegative {
// Accumulate each digit of the number from the least significant
@ -68,8 +69,8 @@ unsafe trait GenericRadix: Sized {
loop {
let n = x % base; // Get the current place value.
x = x / base; // Deaccumulate the number.
curr -= 1;
buf[curr].write(Self::digit(n.to_u8())); // Store the digit in the buffer.
offset -= 1;
buf[offset].write(Self::digit(n.to_u8())); // Store the digit in the buffer.
if x == zero {
// No more digits left to accumulate.
break;
@ -80,27 +81,17 @@ unsafe trait GenericRadix: Sized {
loop {
let n = zero - (x % base); // Get the current place value.
x = x / base; // Deaccumulate the number.
curr -= 1;
buf[curr].write(Self::digit(n.to_u8())); // Store the digit in the buffer.
offset -= 1;
buf[offset].write(Self::digit(n.to_u8())); // Store the digit in the buffer.
if x == zero {
// No more digits left to accumulate.
break;
};
}
}
// SAFETY: `curr` is initialized to `buf.len()` and is only decremented, so it can't overflow. It is
// decremented exactly once for each digit. Since u128 is the widest fixed width integer format supported,
// the maximum number of digits (bits) is 128 for base-2, so `curr` won't underflow as well.
let buf = unsafe { buf.get_unchecked(curr..) };
// SAFETY: The only chars in `buf` are created by `Self::digit` which are assumed to be
// valid UTF-8
let buf = unsafe {
str::from_utf8_unchecked(slice::from_raw_parts(
MaybeUninit::slice_as_ptr(buf),
buf.len(),
))
};
f.pad_integral(is_nonnegative, Self::PREFIX, buf)
// SAFETY: Starting from `offset`, all elements of the slice have been set.
let buf_slice = unsafe { slice_buffer_to_str(&buf, offset) };
f.pad_integral(is_nonnegative, Self::PREFIX, buf_slice)
}
}
@ -199,6 +190,20 @@ static DEC_DIGITS_LUT: &[u8; 200] = b"\
6061626364656667686970717273747576777879\
8081828384858687888990919293949596979899";
/// This function converts a slice of ascii characters into a `&str` starting from `offset`.
///
/// # Safety
///
/// `buf` content starting from `offset` index MUST BE initialized and MUST BE ascii
/// characters.
unsafe fn slice_buffer_to_str(buf: &[MaybeUninit<u8>], offset: usize) -> &str {
// SAFETY: `offset` is always included between 0 and `buf`'s length.
let written = unsafe { buf.get_unchecked(offset..) };
// SAFETY: (`assume_init_ref`) All buf content since offset is set.
// SAFETY: (`from_utf8_unchecked`) Writes use ASCII from the lookup table exclusively.
unsafe { str::from_utf8_unchecked(written.assume_init_ref()) }
}
macro_rules! impl_Display {
($($signed:ident, $unsigned:ident,)* ; as $u:ident via $conv_fn:ident named $gen_name:ident) => {
@ -212,7 +217,8 @@ macro_rules! impl_Display {
// Buffer decimals for $unsigned with right alignment.
let mut buf = [MaybeUninit::<u8>::uninit(); MAX_DEC_N];
f.pad_integral(true, "", self._fmt(&mut buf))
// SAFETY: `buf` is always big enough to contain all the digits.
unsafe { f.pad_integral(true, "", self._fmt(&mut buf)) }
}
#[cfg(feature = "optimize_for_size")]
{
@ -230,7 +236,8 @@ macro_rules! impl_Display {
// Buffer decimals for $unsigned with right alignment.
let mut buf = [MaybeUninit::<u8>::uninit(); MAX_DEC_N];
f.pad_integral(*self >= 0, "", self.unsigned_abs()._fmt(&mut buf))
// SAFETY: `buf` is always big enough to contain all the digits.
unsafe { f.pad_integral(*self >= 0, "", self.unsigned_abs()._fmt(&mut buf)) }
}
#[cfg(feature = "optimize_for_size")]
{
@ -247,7 +254,14 @@ macro_rules! impl_Display {
reason = "specialized method meant to only be used by `SpecToString` implementation",
issue = "none"
)]
pub fn _fmt<'a>(self, buf: &'a mut [MaybeUninit::<u8>]) -> &'a str {
pub unsafe fn _fmt<'a>(self, buf: &'a mut [MaybeUninit::<u8>]) -> &'a str {
// SAFETY: `buf` will always be big enough to contain all digits.
let offset = unsafe { self._fmt_inner(buf) };
// SAFETY: Starting from `offset`, all elements of the slice have been set.
unsafe { slice_buffer_to_str(buf, offset) }
}
unsafe fn _fmt_inner(self, buf: &mut [MaybeUninit::<u8>]) -> usize {
// Count the number of bytes in buf that are not initialized.
let mut offset = buf.len();
// Consume the least-significant decimals from a working copy.
@ -309,47 +323,123 @@ macro_rules! impl_Display {
// not used: remain = 0;
}
// SAFETY: All buf content since offset is set.
let written = unsafe { buf.get_unchecked(offset..) };
// SAFETY: Writes use ASCII from the lookup table exclusively.
unsafe {
str::from_utf8_unchecked(slice::from_raw_parts(
MaybeUninit::slice_as_ptr(written),
written.len(),
))
}
offset
}
})*
}
impl $signed {
/// Allows users to write an integer (in signed decimal format) into a variable `buf` of
/// type [`NumBuffer`] that is passed by the caller by mutable reference.
///
/// # Examples
///
/// ```
/// #![feature(int_format_into)]
/// use core::fmt::NumBuffer;
///
#[doc = concat!("let n = 0", stringify!($signed), ";")]
/// let mut buf = NumBuffer::new();
/// assert_eq!(n.format_into(&mut buf), "0");
///
#[doc = concat!("let n1 = 32", stringify!($signed), ";")]
/// assert_eq!(n1.format_into(&mut buf), "32");
///
#[doc = concat!("let n2 = ", stringify!($signed::MAX), ";")]
#[doc = concat!("assert_eq!(n2.format_into(&mut buf), ", stringify!($signed::MAX), ".to_string());")]
/// ```
#[unstable(feature = "int_format_into", issue = "138215")]
pub fn format_into(self, buf: &mut NumBuffer<Self>) -> &str {
let mut offset;
#[cfg(not(feature = "optimize_for_size"))]
// SAFETY: `buf` will always be big enough to contain all digits.
unsafe {
offset = self.unsigned_abs()._fmt_inner(&mut buf.buf);
}
#[cfg(feature = "optimize_for_size")]
{
offset = _inner_slow_integer_to_str(self.unsigned_abs().$conv_fn(), &mut buf.buf);
}
// Only difference between signed and unsigned are these 4 lines.
if self < 0 {
offset -= 1;
buf.buf[offset].write(b'-');
}
// SAFETY: Starting from `offset`, all elements of the slice have been set.
unsafe { slice_buffer_to_str(&buf.buf, offset) }
}
}
impl $unsigned {
/// Allows users to write an integer (in signed decimal format) into a variable `buf` of
/// type [`NumBuffer`] that is passed by the caller by mutable reference.
///
/// # Examples
///
/// ```
/// #![feature(int_format_into)]
/// use core::fmt::NumBuffer;
///
#[doc = concat!("let n = 0", stringify!($unsigned), ";")]
/// let mut buf = NumBuffer::new();
/// assert_eq!(n.format_into(&mut buf), "0");
///
#[doc = concat!("let n1 = 32", stringify!($unsigned), ";")]
/// assert_eq!(n1.format_into(&mut buf), "32");
///
#[doc = concat!("let n2 = ", stringify!($unsigned::MAX), ";")]
#[doc = concat!("assert_eq!(n2.format_into(&mut buf), ", stringify!($unsigned::MAX), ".to_string());")]
/// ```
#[unstable(feature = "int_format_into", issue = "138215")]
pub fn format_into(self, buf: &mut NumBuffer<Self>) -> &str {
let offset;
#[cfg(not(feature = "optimize_for_size"))]
// SAFETY: `buf` will always be big enough to contain all digits.
unsafe {
offset = self._fmt_inner(&mut buf.buf);
}
#[cfg(feature = "optimize_for_size")]
{
offset = _inner_slow_integer_to_str(self.$conv_fn(), &mut buf.buf);
}
// SAFETY: Starting from `offset`, all elements of the slice have been set.
unsafe { slice_buffer_to_str(&buf.buf, offset) }
}
}
)*
#[cfg(feature = "optimize_for_size")]
fn $gen_name(mut n: $u, is_nonnegative: bool, f: &mut fmt::Formatter<'_>) -> fmt::Result {
const MAX_DEC_N: usize = $u::MAX.ilog10() as usize + 1;
let mut buf = [MaybeUninit::<u8>::uninit(); MAX_DEC_N];
let mut curr = MAX_DEC_N;
let buf_ptr = MaybeUninit::slice_as_mut_ptr(&mut buf);
fn _inner_slow_integer_to_str(mut n: $u, buf: &mut [MaybeUninit::<u8>]) -> usize {
let mut curr = buf.len();
// SAFETY: To show that it's OK to copy into `buf_ptr`, notice that at the beginning
// `curr == buf.len() == 39 > log(n)` since `n < 2^128 < 10^39`, and at
// each step this is kept the same as `n` is divided. Since `n` is always
// non-negative, this means that `curr > 0` so `buf_ptr[curr..curr + 1]`
// is safe to access.
unsafe {
loop {
curr -= 1;
buf_ptr.add(curr).write((n % 10) as u8 + b'0');
n /= 10;
loop {
curr -= 1;
buf[curr].write((n % 10) as u8 + b'0');
n /= 10;
if n == 0 {
break;
}
if n == 0 {
break;
}
}
curr
}
// SAFETY: `curr` > 0 (since we made `buf` large enough), and all the chars are valid UTF-8
let buf_slice = unsafe {
str::from_utf8_unchecked(
slice::from_raw_parts(buf_ptr.add(curr), buf.len() - curr))
};
#[cfg(feature = "optimize_for_size")]
fn $gen_name(n: $u, is_nonnegative: bool, f: &mut fmt::Formatter<'_>) -> fmt::Result {
const MAX_DEC_N: usize = $u::MAX.ilog(10) as usize + 1;
let mut buf = [MaybeUninit::<u8>::uninit(); MAX_DEC_N];
let offset = _inner_slow_integer_to_str(n, &mut buf);
// SAFETY: Starting from `offset`, all elements of the slice have been set.
let buf_slice = unsafe { slice_buffer_to_str(&buf, offset) };
f.pad_integral(is_nonnegative, "", buf_slice)
}
};
@ -572,7 +662,8 @@ impl fmt::Display for u128 {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut buf = [MaybeUninit::<u8>::uninit(); U128_MAX_DEC_N];
f.pad_integral(true, "", self._fmt(&mut buf))
// SAFETY: `buf` is always big enough to contain all the digits.
unsafe { f.pad_integral(true, "", self._fmt(&mut buf)) }
}
}
@ -584,7 +675,8 @@ impl fmt::Display for i128 {
let mut buf = [MaybeUninit::<u8>::uninit(); U128_MAX_DEC_N];
let is_nonnegative = *self >= 0;
f.pad_integral(is_nonnegative, "", self.unsigned_abs()._fmt(&mut buf))
// SAFETY: `buf` is always big enough to contain all the digits.
unsafe { f.pad_integral(is_nonnegative, "", self.unsigned_abs()._fmt(&mut buf)) }
}
}
@ -597,13 +689,21 @@ impl u128 {
reason = "specialized method meant to only be used by `SpecToString` implementation",
issue = "none"
)]
pub fn _fmt<'a>(self, buf: &'a mut [MaybeUninit<u8>]) -> &'a str {
pub unsafe fn _fmt<'a>(self, buf: &'a mut [MaybeUninit<u8>]) -> &'a str {
// SAFETY: `buf` will always be big enough to contain all digits.
let offset = unsafe { self._fmt_inner(buf) };
// SAFETY: Starting from `offset`, all elements of the slice have been set.
unsafe { slice_buffer_to_str(buf, offset) }
}
unsafe fn _fmt_inner(self, buf: &mut [MaybeUninit<u8>]) -> usize {
// Optimize common-case zero, which would also need special treatment due to
// its "leading" zero.
if self == 0 {
return "0";
let offset = buf.len() - 1;
buf[offset].write(b'0');
return offset;
}
// Take the 16 least-significant decimals.
let (quot_1e16, mod_1e16) = div_rem_1e16(self);
let (mut remain, mut offset) = if quot_1e16 == 0 {
@ -677,16 +777,86 @@ impl u128 {
buf[offset].write(DEC_DIGITS_LUT[last * 2 + 1]);
// not used: remain = 0;
}
offset
}
// SAFETY: All buf content since offset is set.
let written = unsafe { buf.get_unchecked(offset..) };
// SAFETY: Writes use ASCII from the lookup table exclusively.
unsafe {
str::from_utf8_unchecked(slice::from_raw_parts(
MaybeUninit::slice_as_ptr(written),
written.len(),
))
/// Allows users to write an integer (in signed decimal format) into a variable `buf` of
/// type [`NumBuffer`] that is passed by the caller by mutable reference.
///
/// # Examples
///
/// ```
/// #![feature(int_format_into)]
/// use core::fmt::NumBuffer;
///
/// let n = 0u128;
/// let mut buf = NumBuffer::new();
/// assert_eq!(n.format_into(&mut buf), "0");
///
/// let n1 = 32u128;
/// let mut buf1 = NumBuffer::new();
/// assert_eq!(n1.format_into(&mut buf1), "32");
///
/// let n2 = u128::MAX;
/// let mut buf2 = NumBuffer::new();
/// assert_eq!(n2.format_into(&mut buf2), u128::MAX.to_string());
/// ```
#[unstable(feature = "int_format_into", issue = "138215")]
pub fn format_into(self, buf: &mut NumBuffer<Self>) -> &str {
let diff = buf.capacity() - U128_MAX_DEC_N;
// FIXME: Once const generics are better, use `NumberBufferTrait::BUF_SIZE` as generic const
// for `fmt_u128_inner`.
//
// In the meantime, we have to use a slice starting at index 1 and add 1 to the returned
// offset to ensure the number is correctly generated at the end of the buffer.
// SAFETY: `diff` will always be between 0 and its initial value.
unsafe { self._fmt(buf.buf.get_unchecked_mut(diff..)) }
}
}
impl i128 {
/// Allows users to write an integer (in signed decimal format) into a variable `buf` of
/// type [`NumBuffer`] that is passed by the caller by mutable reference.
///
/// # Examples
///
/// ```
/// #![feature(int_format_into)]
/// use core::fmt::NumBuffer;
///
/// let n = 0i128;
/// let mut buf = NumBuffer::new();
/// assert_eq!(n.format_into(&mut buf), "0");
///
/// let n1 = i128::MIN;
/// assert_eq!(n1.format_into(&mut buf), i128::MIN.to_string());
///
/// let n2 = i128::MAX;
/// assert_eq!(n2.format_into(&mut buf), i128::MAX.to_string());
/// ```
#[unstable(feature = "int_format_into", issue = "138215")]
pub fn format_into(self, buf: &mut NumBuffer<Self>) -> &str {
let diff = buf.capacity() - U128_MAX_DEC_N;
// FIXME: Once const generics are better, use `NumberBufferTrait::BUF_SIZE` as generic const
// for `fmt_u128_inner`.
//
// In the meantime, we have to use a slice starting at index 1 and add 1 to the returned
// offset to ensure the number is correctly generated at the end of the buffer.
let mut offset =
// SAFETY: `buf` will always be big enough to contain all digits.
unsafe { self.unsigned_abs()._fmt_inner(buf.buf.get_unchecked_mut(diff..)) };
// We put back the offset at the right position.
offset += diff;
// Only difference between signed and unsigned are these 4 lines.
if self < 0 {
offset -= 1;
// SAFETY: `buf` will always be big enough to contain all digits plus the minus sign.
unsafe {
buf.buf.get_unchecked_mut(offset).write(b'-');
}
}
// SAFETY: Starting from `offset`, all elements of the slice have been set.
unsafe { slice_buffer_to_str(&buf.buf, offset) }
}
}

View file

@ -0,0 +1,60 @@
use crate::mem::MaybeUninit;
/// Trait used to describe the maximum number of digits in decimal base of the implemented integer.
#[unstable(feature = "int_format_into", issue = "138215")]
pub trait NumBufferTrait {
/// Maximum number of digits in decimal base of the implemented integer.
const BUF_SIZE: usize;
}
macro_rules! impl_NumBufferTrait {
($($signed:ident, $unsigned:ident,)*) => {
$(
#[unstable(feature = "int_format_into", issue = "138215")]
impl NumBufferTrait for $signed {
// `+ 2` and not `+ 1` to include the `-` character.
const BUF_SIZE: usize = $signed::MAX.ilog(10) as usize + 2;
}
#[unstable(feature = "int_format_into", issue = "138215")]
impl NumBufferTrait for $unsigned {
const BUF_SIZE: usize = $unsigned::MAX.ilog(10) as usize + 1;
}
)*
}
}
impl_NumBufferTrait! {
i8, u8,
i16, u16,
i32, u32,
i64, u64,
isize, usize,
i128, u128,
}
/// A buffer wrapper of which the internal size is based on the maximum
/// number of digits the associated integer can have.
#[unstable(feature = "int_format_into", issue = "138215")]
#[derive(Debug)]
pub struct NumBuffer<T: NumBufferTrait> {
// FIXME: Once const generics feature is working, use `T::BUF_SIZE` instead of 40.
pub(crate) buf: [MaybeUninit<u8>; 40],
// FIXME: Remove this field once we can actually use `T`.
phantom: core::marker::PhantomData<T>,
}
#[unstable(feature = "int_format_into", issue = "138215")]
impl<T: NumBufferTrait> NumBuffer<T> {
/// Initializes internal buffer.
#[unstable(feature = "int_format_into", issue = "138215")]
pub const fn new() -> Self {
// FIXME: Once const generics feature is working, use `T::BUF_SIZE` instead of 40.
NumBuffer { buf: [MaybeUninit::<u8>::uninit(); 40], phantom: core::marker::PhantomData }
}
/// Returns the length of the internal buffer.
#[unstable(feature = "int_format_into", issue = "138215")]
pub const fn capacity(&self) -> usize {
self.buf.len()
}
}

View file

@ -2,7 +2,9 @@ error[E0283]: type annotations needed
--> $DIR/dedup-normalized-2-higher-ranked.rs:28:5
|
LL | impls(rigid);
| ^^^^^ cannot infer type of the type parameter `U` declared on the function `impls`
| ^^^^^ ----- type must be known at this point
| |
| cannot infer type of the type parameter `U` declared on the function `impls`
|
= note: cannot satisfy `for<'b> <P as Trait>::Rigid: Bound<'b, _>`
note: required by a bound in `impls`

View file

@ -20,7 +20,7 @@ error[E0284]: type annotations needed
LL | println!("{:?}", take_array_from_mut(&mut arr, i));
| ---- ^^^^^^^^^^^^^^^^^^^ cannot infer the value of the const parameter `N` declared on the function `take_array_from_mut`
| |
| type must be known at this point
| required by this formatting parameter
|
= note: required for `[i32; _]` to implement `Debug`
= note: 1 redundant requirement hidden

View file

@ -2,7 +2,9 @@ error[E0283]: type annotations needed
--> $DIR/auto-trait-selection-freeze.rs:19:16
|
LL | if false { is_trait(foo()) } else { Default::default() }
| ^^^^^^^^ cannot infer type of the type parameter `U` declared on the function `is_trait`
| ^^^^^^^^ ----- type must be known at this point
| |
| cannot infer type of the type parameter `U` declared on the function `is_trait`
|
note: multiple `impl`s satisfying `impl Sized: Trait<_>` found
--> $DIR/auto-trait-selection-freeze.rs:16:1

View file

@ -2,7 +2,9 @@ error[E0283]: type annotations needed
--> $DIR/auto-trait-selection.rs:15:16
|
LL | if false { is_trait(foo()) } else { Default::default() }
| ^^^^^^^^ cannot infer type of the type parameter `U` declared on the function `is_trait`
| ^^^^^^^^ ----- type must be known at this point
| |
| cannot infer type of the type parameter `U` declared on the function `is_trait`
|
note: multiple `impl`s satisfying `impl Sized: Trait<_>` found
--> $DIR/auto-trait-selection.rs:12:1

View file

@ -0,0 +1,46 @@
// issue#143560
trait T {
type Target;
}
trait Foo {
fn foo() -> impl T<Target = impl T<Target = impl Sized>>;
fn foo() -> impl Sized;
//~^ ERROR: the name `foo` is defined multiple times
}
trait Bar {
fn foo() -> impl T<Target = impl T<Target = impl Sized>>;
fn foo() -> impl T<Target = impl T<Target = impl Sized>>;
//~^ ERROR: the name `foo` is defined multiple times
}
struct S<T> {
a: T
}
trait Baz {
fn foo() -> S<impl T<Target = S<S<impl Sized>>>>;
fn foo() -> S<impl T<Target = S<S<impl Sized>>>>;
//~^ ERROR: the name `foo` is defined multiple times
}
struct S1<T1, T2> {
a: T1,
b: T2
}
trait Qux {
fn foo() -> S1<
impl T<Target = impl T<Target = impl Sized>>,
impl T<Target = impl T<Target = S<impl Sized>>>
>;
fn foo() -> S1<
impl T<Target = impl T<Target = impl Sized>>,
impl T<Target = impl T<Target = S<impl Sized>>>
>;
//~^^^^ ERROR: the name `foo` is defined multiple times
}
fn main() {}

View file

@ -0,0 +1,49 @@
error[E0428]: the name `foo` is defined multiple times
--> $DIR/rpitit-duplicate-associated-fn-with-nested.rs:9:5
|
LL | fn foo() -> impl T<Target = impl T<Target = impl Sized>>;
| --------------------------------------------------------- previous definition of the value `foo` here
LL | fn foo() -> impl Sized;
| ^^^^^^^^^^^^^^^^^^^^^^^ `foo` redefined here
|
= note: `foo` must be defined only once in the value namespace of this trait
error[E0428]: the name `foo` is defined multiple times
--> $DIR/rpitit-duplicate-associated-fn-with-nested.rs:15:5
|
LL | fn foo() -> impl T<Target = impl T<Target = impl Sized>>;
| --------------------------------------------------------- previous definition of the value `foo` here
LL | fn foo() -> impl T<Target = impl T<Target = impl Sized>>;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `foo` redefined here
|
= note: `foo` must be defined only once in the value namespace of this trait
error[E0428]: the name `foo` is defined multiple times
--> $DIR/rpitit-duplicate-associated-fn-with-nested.rs:25:5
|
LL | fn foo() -> S<impl T<Target = S<S<impl Sized>>>>;
| ------------------------------------------------- previous definition of the value `foo` here
LL | fn foo() -> S<impl T<Target = S<S<impl Sized>>>>;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `foo` redefined here
|
= note: `foo` must be defined only once in the value namespace of this trait
error[E0428]: the name `foo` is defined multiple times
--> $DIR/rpitit-duplicate-associated-fn-with-nested.rs:39:5
|
LL | / fn foo() -> S1<
LL | | impl T<Target = impl T<Target = impl Sized>>,
LL | | impl T<Target = impl T<Target = S<impl Sized>>>
LL | | >;
| |__________- previous definition of the value `foo` here
LL | / fn foo() -> S1<
LL | | impl T<Target = impl T<Target = impl Sized>>,
LL | | impl T<Target = impl T<Target = S<impl Sized>>>
LL | | >;
| |__________^ `foo` redefined here
|
= note: `foo` must be defined only once in the value namespace of this trait
error: aborting due to 4 previous errors
For more information about this error, try `rustc --explain E0428`.

View file

@ -27,4 +27,15 @@ impl T for () {
}
}
trait Baz {
fn foo();
fn foo() -> impl Sized; //~ ERROR: the name `foo` is defined multiple times
}
trait Foo {
fn foo() -> impl Sized;
fn foo(); //~ ERROR: the name `foo` is defined multiple times
fn foo() -> impl Sized; //~ ERROR: the name `foo` is defined multiple times
}
fn main() {}

View file

@ -8,6 +8,37 @@ LL | fn method() -> impl Sized;
|
= note: `method` must be defined only once in the value namespace of this trait
error[E0428]: the name `foo` is defined multiple times
--> $DIR/rpitit-duplicate-associated-fn.rs:32:5
|
LL | fn foo();
| --------- previous definition of the value `foo` here
LL | fn foo() -> impl Sized;
| ^^^^^^^^^^^^^^^^^^^^^^^ `foo` redefined here
|
= note: `foo` must be defined only once in the value namespace of this trait
error[E0428]: the name `foo` is defined multiple times
--> $DIR/rpitit-duplicate-associated-fn.rs:37:5
|
LL | fn foo() -> impl Sized;
| ----------------------- previous definition of the value `foo` here
LL | fn foo();
| ^^^^^^^^^ `foo` redefined here
|
= note: `foo` must be defined only once in the value namespace of this trait
error[E0428]: the name `foo` is defined multiple times
--> $DIR/rpitit-duplicate-associated-fn.rs:38:5
|
LL | fn foo() -> impl Sized;
| ----------------------- previous definition of the value `foo` here
LL | fn foo();
LL | fn foo() -> impl Sized;
| ^^^^^^^^^^^^^^^^^^^^^^^ `foo` redefined here
|
= note: `foo` must be defined only once in the value namespace of this trait
error[E0201]: duplicate definitions with name `method`:
--> $DIR/rpitit-duplicate-associated-fn.rs:12:5
|
@ -47,7 +78,7 @@ LL | fn method() -> impl Sized;
LL | impl Bar for () {
| ^^^^^^^^^^^^^^^ missing `method` in implementation
error: aborting due to 4 previous errors
error: aborting due to 7 previous errors
Some errors have detailed explanations: E0046, E0201, E0428.
For more information about an error, try `rustc --explain E0046`.

View file

@ -4,7 +4,7 @@
fn main() {
match 1 {
const { 1 + 7 } => {}
//~^ ERROR `inline_const_pat` has been removed
//~^ ERROR const blocks cannot be used as patterns
2 => {}
_ => {}
}

View file

@ -1,10 +1,10 @@
error: `inline_const_pat` has been removed
error: const blocks cannot be used as patterns
--> $DIR/in-pat-recovery.rs:6:15
|
LL | const { 1 + 7 } => {}
| ^^^^^^^^^
|
= help: use a named `const`-item or an `if`-guard instead
= help: use a named `const`-item or an `if`-guard (`x if x == const { ... }`) instead
error: aborting due to 1 previous error

View file

@ -0,0 +1,10 @@
// In this test, the span of the trait bound label should point to `1`, not `""`.
// See issue #143336
trait A<T> {
fn f(self, x: T);
}
fn main() {
A::f(1, ""); //~ ERROR the trait bound `{integer}: A<_>` is not satisfied [E0277]
}

View file

@ -0,0 +1,17 @@
error[E0277]: the trait bound `{integer}: A<_>` is not satisfied
--> $DIR/false-span-in-trait-bound-label.rs:9:10
|
LL | A::f(1, "");
| ---- ^ the trait `A<_>` is not implemented for `{integer}`
| |
| required by a bound introduced by this call
|
help: this trait has no implementations, consider adding one
--> $DIR/false-span-in-trait-bound-label.rs:4:1
|
LL | trait A<T> {
| ^^^^^^^^^^
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0277`.

View file

@ -34,10 +34,10 @@ LL | <dyn CompareToInts>::same_as(c, 22)
`i64` implements `CompareTo<u64>`
error[E0277]: the trait bound `C: CompareTo<i32>` is not satisfied
--> $DIR/repeated-supertrait-ambig.rs:38:27
--> $DIR/repeated-supertrait-ambig.rs:38:24
|
LL | CompareTo::same_as(c, 22)
| ------------------ ^^ the trait `CompareTo<i32>` is not implemented for `C`
| ------------------ ^ the trait `CompareTo<i32>` is not implemented for `C`
| |
| required by a bound introduced by this call
|

View file

@ -2,7 +2,7 @@ error[E0283]: type annotations needed
--> $DIR/multidispatch-convert-ambig-dest.rs:26:5
|
LL | test(22, std::default::Default::default());
| ^^^^ -------------------------------- type must be known at this point
| ^^^^ -- type must be known at this point
| |
| cannot infer type of the type parameter `U` declared on the function `test`
|