about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock10
-rw-r--r--Cargo.toml2
-rw-r--r--compiler/rustc_borrowck/src/def_use.rs2
-rw-r--r--compiler/rustc_borrowck/src/renumber.rs8
-rw-r--r--compiler/rustc_borrowck/src/type_check/mod.rs15
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/analyze.rs4
-rw-r--r--compiler/rustc_hir_typeck/src/expr.rs6
-rw-r--r--compiler/rustc_middle/src/mir/visit.rs30
-rw-r--r--compiler/rustc_mir_dataflow/src/impls/liveness.rs1
-rw-r--r--compiler/rustc_mir_transform/src/const_prop.rs1
-rw-r--r--compiler/rustc_resolve/src/late.rs4
-rw-r--r--library/alloc/src/lib.rs1
-rw-r--r--library/alloc/src/string.rs9
-rw-r--r--library/core/src/array/ascii.rs34
-rw-r--r--library/core/src/array/mod.rs1
-rw-r--r--library/core/src/ascii.rs4
-rw-r--r--library/core/src/ascii/ascii_char.rs565
-rw-r--r--library/core/src/char/methods.rs19
-rw-r--r--library/core/src/num/mod.rs11
-rw-r--r--library/core/src/num/nonzero.rs26
-rw-r--r--library/core/src/slice/ascii.rs30
-rw-r--r--library/core/src/str/mod.rs11
-rw-r--r--library/std/src/ascii.rs3
-rw-r--r--library/sysroot/Cargo.toml27
-rw-r--r--library/sysroot/src/lib.rs1
-rw-r--r--library/test/Cargo.toml20
-rw-r--r--library/test/src/formatters/junit.rs40
-rw-r--r--src/bootstrap/check.rs2
-rw-r--r--src/bootstrap/clean.rs2
-rw-r--r--src/bootstrap/compile.rs4
-rw-r--r--src/bootstrap/dist.rs1
-rw-r--r--src/bootstrap/doc.rs2
-rw-r--r--src/bootstrap/test.rs2
-rw-r--r--tests/codegen/ascii-char.rs37
-rw-r--r--tests/mir-opt/dead-store-elimination/place_mention.main.DeadStoreElimination.diff25
-rw-r--r--tests/mir-opt/dead-store-elimination/place_mention.rs9
-rw-r--r--tests/run-make/libtest-junit/Makefile19
-rw-r--r--tests/run-make/libtest-junit/f.rs23
-rw-r--r--tests/run-make/libtest-junit/output-default.xml1
-rw-r--r--tests/run-make/libtest-junit/output-stdout-success.xml1
-rwxr-xr-xtests/run-make/libtest-junit/validate_junit.py12
-rw-r--r--tests/ui/const-generics/sneaky-array-repeat-expr.rs2
-rw-r--r--tests/ui/const-generics/sneaky-array-repeat-expr.stderr20
-rw-r--r--tests/ui/consts/issue-50439.rs4
-rw-r--r--tests/ui/consts/issue-50439.stderr10
-rw-r--r--tests/ui/self/elision/nested-item.rs13
-rw-r--r--tests/ui/self/elision/nested-item.stderr38
-rw-r--r--tests/ui/typeck/repeat-expr-checks-wf.rs10
-rw-r--r--tests/ui/typeck/repeat-expr-checks-wf.stderr12
49 files changed, 1084 insertions, 50 deletions
diff --git a/Cargo.lock b/Cargo.lock
index c368b4a79d3..e69b36cb983 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -4740,6 +4740,15 @@ dependencies = [
 ]
 
 [[package]]
+name = "sysroot"
+version = "0.0.0"
+dependencies = [
+ "proc_macro",
+ "std",
+ "test",
+]
+
+[[package]]
 name = "tar"
 version = "0.4.38"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -4823,7 +4832,6 @@ dependencies = [
  "getopts",
  "panic_abort",
  "panic_unwind",
- "proc_macro",
  "std",
 ]
 
diff --git a/Cargo.toml b/Cargo.toml
index a497d7321e0..7aaa34a68e6 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -2,7 +2,7 @@
 members = [
   "compiler/rustc",
   "library/std",
-  "library/test",
+  "library/sysroot",
   "src/rustdoc-json-types",
   "src/tools/build_helper",
   "src/tools/cargotest",
diff --git a/compiler/rustc_borrowck/src/def_use.rs b/compiler/rustc_borrowck/src/def_use.rs
index 6259722b694..74e6ce37e97 100644
--- a/compiler/rustc_borrowck/src/def_use.rs
+++ b/compiler/rustc_borrowck/src/def_use.rs
@@ -54,7 +54,7 @@ pub fn categorize(context: PlaceContext) -> Option<DefUse> {
 
         // `PlaceMention` and `AscribeUserType` both evaluate the place, which must not
         // contain dangling references.
-        PlaceContext::NonUse(NonUseContext::PlaceMention) |
+        PlaceContext::NonMutatingUse(NonMutatingUseContext::PlaceMention) |
         PlaceContext::NonUse(NonUseContext::AscribeUserTy) |
 
         PlaceContext::MutatingUse(MutatingUseContext::AddressOf) |
diff --git a/compiler/rustc_borrowck/src/renumber.rs b/compiler/rustc_borrowck/src/renumber.rs
index 22de7549e94..4389d2b60bc 100644
--- a/compiler/rustc_borrowck/src/renumber.rs
+++ b/compiler/rustc_borrowck/src/renumber.rs
@@ -109,6 +109,14 @@ impl<'a, 'tcx> MutVisitor<'tcx> for RegionRenumberer<'a, 'tcx> {
     }
 
     #[instrument(skip(self), level = "debug")]
+    fn visit_ty_const(&mut self, ct: &mut ty::Const<'tcx>, location: Location) {
+        let old_ct = *ct;
+        *ct = self.renumber_regions(old_ct, || RegionCtxt::Location(location));
+
+        debug!(?ct);
+    }
+
+    #[instrument(skip(self), level = "debug")]
     fn visit_constant(&mut self, constant: &mut Constant<'tcx>, location: Location) {
         let literal = constant.literal;
         constant.literal = self.renumber_regions(literal, || RegionCtxt::Location(location));
diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs
index 601589480d1..dcabeb792be 100644
--- a/compiler/rustc_borrowck/src/type_check/mod.rs
+++ b/compiler/rustc_borrowck/src/type_check/mod.rs
@@ -772,12 +772,10 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> {
 
         match context {
             PlaceContext::MutatingUse(_) => ty::Invariant,
-            PlaceContext::NonUse(StorageDead | StorageLive | PlaceMention | VarDebugInfo) => {
-                ty::Invariant
-            }
+            PlaceContext::NonUse(StorageDead | StorageLive | VarDebugInfo) => ty::Invariant,
             PlaceContext::NonMutatingUse(
-                Inspect | Copy | Move | SharedBorrow | ShallowBorrow | UniqueBorrow | AddressOf
-                | Projection,
+                Inspect | Copy | Move | PlaceMention | SharedBorrow | ShallowBorrow | UniqueBorrow
+                | AddressOf | Projection,
             ) => ty::Covariant,
             PlaceContext::NonUse(AscribeUserTy) => ty::Covariant,
         }
@@ -1803,6 +1801,13 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
             Rvalue::Repeat(operand, len) => {
                 self.check_operand(operand, location);
 
+                let array_ty = rvalue.ty(body.local_decls(), tcx);
+                self.prove_predicate(
+                    ty::PredicateKind::WellFormed(array_ty.into()),
+                    Locations::Single(location),
+                    ConstraintCategory::Boring,
+                );
+
                 // If the length cannot be evaluated we must assume that the length can be larger
                 // than 1.
                 // If the length is larger than 1, the repeat expression will need to copy the
diff --git a/compiler/rustc_codegen_ssa/src/mir/analyze.rs b/compiler/rustc_codegen_ssa/src/mir/analyze.rs
index 0334c7ff132..569599faa36 100644
--- a/compiler/rustc_codegen_ssa/src/mir/analyze.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/analyze.rs
@@ -203,7 +203,9 @@ impl<'mir, 'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> Visitor<'tcx>
                 self.assign(local, DefLocation::Body(location));
             }
 
-            PlaceContext::NonUse(_) | PlaceContext::MutatingUse(MutatingUseContext::Retag) => {}
+            PlaceContext::NonUse(_)
+            | PlaceContext::NonMutatingUse(NonMutatingUseContext::PlaceMention)
+            | PlaceContext::MutatingUse(MutatingUseContext::Retag) => {}
 
             PlaceContext::NonMutatingUse(
                 NonMutatingUseContext::Copy | NonMutatingUseContext::Move,
diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs
index 047d8a82bfc..bba049c3819 100644
--- a/compiler/rustc_hir_typeck/src/expr.rs
+++ b/compiler/rustc_hir_typeck/src/expr.rs
@@ -1426,6 +1426,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
         self.check_repeat_element_needs_copy_bound(element, count, element_ty);
 
+        self.register_wf_obligation(
+            tcx.mk_array_with_const_len(t, count).into(),
+            expr.span,
+            traits::WellFormed(None),
+        );
+
         tcx.mk_array_with_const_len(t, count)
     }
 
diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs
index 0a9fcd898b9..6718605ed0b 100644
--- a/compiler/rustc_middle/src/mir/visit.rs
+++ b/compiler/rustc_middle/src/mir/visit.rs
@@ -192,6 +192,14 @@ macro_rules! make_mir_visitor {
                 self.super_constant(constant, location);
             }
 
+            fn visit_ty_const(
+                &mut self,
+                ct: $( & $mutability)? ty::Const<'tcx>,
+                location: Location,
+            ) {
+                self.super_ty_const(ct, location);
+            }
+
             fn visit_span(
                 &mut self,
                 span: $(& $mutability)? Span,
@@ -410,7 +418,7 @@ macro_rules! make_mir_visitor {
                     StatementKind::PlaceMention(place) => {
                         self.visit_place(
                             place,
-                            PlaceContext::NonUse(NonUseContext::PlaceMention),
+                            PlaceContext::NonMutatingUse(NonMutatingUseContext::PlaceMention),
                             location
                         );
                     }
@@ -625,8 +633,9 @@ macro_rules! make_mir_visitor {
                         self.visit_operand(operand, location);
                     }
 
-                    Rvalue::Repeat(value, _) => {
+                    Rvalue::Repeat(value, ct) => {
                         self.visit_operand(value, location);
+                        self.visit_ty_const($(&$mutability)? *ct, location);
                     }
 
                     Rvalue::ThreadLocalRef(_) => {}
@@ -878,12 +887,20 @@ macro_rules! make_mir_visitor {
                 self.visit_span($(& $mutability)? *span);
                 drop(user_ty); // no visit method for this
                 match literal {
-                    ConstantKind::Ty(_) => {}
+                    ConstantKind::Ty(ct) => self.visit_ty_const($(&$mutability)? *ct, location),
                     ConstantKind::Val(_, ty) => self.visit_ty($(& $mutability)? *ty, TyContext::Location(location)),
                     ConstantKind::Unevaluated(_, ty) => self.visit_ty($(& $mutability)? *ty, TyContext::Location(location)),
                 }
             }
 
+            fn super_ty_const(
+                &mut self,
+                _ct: $(& $mutability)? ty::Const<'tcx>,
+                _location: Location,
+            ) {
+
+            }
+
             fn super_span(&mut self, _span: $(& $mutability)? Span) {
             }
 
@@ -1251,6 +1268,11 @@ pub enum NonMutatingUseContext {
     UniqueBorrow,
     /// AddressOf for *const pointer.
     AddressOf,
+    /// PlaceMention statement.
+    ///
+    /// This statement is executed as a check that the `Place` is live without reading from it,
+    /// so it must be considered as a non-mutating use.
+    PlaceMention,
     /// Used as base for another place, e.g., `x` in `x.y`. Will not mutate the place.
     /// For example, the projection `x.y` is not marked as a mutation in these cases:
     /// ```ignore (illustrative)
@@ -1301,8 +1323,6 @@ pub enum NonUseContext {
     AscribeUserTy,
     /// The data of a user variable, for debug info.
     VarDebugInfo,
-    /// PlaceMention statement.
-    PlaceMention,
 }
 
 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
diff --git a/compiler/rustc_mir_dataflow/src/impls/liveness.rs b/compiler/rustc_mir_dataflow/src/impls/liveness.rs
index bc67aa476f1..aeca0073304 100644
--- a/compiler/rustc_mir_dataflow/src/impls/liveness.rs
+++ b/compiler/rustc_mir_dataflow/src/impls/liveness.rs
@@ -197,6 +197,7 @@ impl DefUse {
                 | NonMutatingUseContext::Copy
                 | NonMutatingUseContext::Inspect
                 | NonMutatingUseContext::Move
+                | NonMutatingUseContext::PlaceMention
                 | NonMutatingUseContext::ShallowBorrow
                 | NonMutatingUseContext::SharedBorrow
                 | NonMutatingUseContext::UniqueBorrow,
diff --git a/compiler/rustc_mir_transform/src/const_prop.rs b/compiler/rustc_mir_transform/src/const_prop.rs
index deec66bbaf3..b3a3a25ebe8 100644
--- a/compiler/rustc_mir_transform/src/const_prop.rs
+++ b/compiler/rustc_mir_transform/src/const_prop.rs
@@ -752,6 +752,7 @@ impl Visitor<'_> for CanConstProp {
             | NonMutatingUse(NonMutatingUseContext::Move)
             | NonMutatingUse(NonMutatingUseContext::Inspect)
             | NonMutatingUse(NonMutatingUseContext::Projection)
+            | NonMutatingUse(NonMutatingUseContext::PlaceMention)
             | NonUse(_) => {}
 
             // These could be propagated with a smarter analysis or just some careful thinking about
diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs
index f876b8c7ae0..6f5d54bcf87 100644
--- a/compiler/rustc_resolve/src/late.rs
+++ b/compiler/rustc_resolve/src/late.rs
@@ -2070,6 +2070,10 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
                 }
                 visit::walk_ty(self, ty)
             }
+
+            // A type may have an expression as a const generic argument.
+            // We do not want to recurse into those.
+            fn visit_expr(&mut self, _: &'a Expr) {}
         }
 
         let impl_self = self
diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs
index a002421aeef..18f25aec5fe 100644
--- a/library/alloc/src/lib.rs
+++ b/library/alloc/src/lib.rs
@@ -101,6 +101,7 @@
 #![feature(array_into_iter_constructors)]
 #![feature(array_methods)]
 #![feature(array_windows)]
+#![feature(ascii_char)]
 #![feature(assert_matches)]
 #![feature(async_iterator)]
 #![feature(coerce_unsized)]
diff --git a/library/alloc/src/string.rs b/library/alloc/src/string.rs
index cf16a3424a0..b9ef76c109a 100644
--- a/library/alloc/src/string.rs
+++ b/library/alloc/src/string.rs
@@ -2527,6 +2527,15 @@ impl<T: fmt::Display + ?Sized> ToString for T {
 }
 
 #[cfg(not(no_global_oom_handling))]
+#[unstable(feature = "ascii_char", issue = "110998")]
+impl ToString for core::ascii::Char {
+    #[inline]
+    fn to_string(&self) -> String {
+        self.as_str().to_owned()
+    }
+}
+
+#[cfg(not(no_global_oom_handling))]
 #[stable(feature = "char_to_string_specialization", since = "1.46.0")]
 impl ToString for char {
     #[inline]
diff --git a/library/core/src/array/ascii.rs b/library/core/src/array/ascii.rs
new file mode 100644
index 00000000000..6750d7c0711
--- /dev/null
+++ b/library/core/src/array/ascii.rs
@@ -0,0 +1,34 @@
+use crate::ascii;
+
+#[cfg(not(test))]
+impl<const N: usize> [u8; N] {
+    /// Converts this array of bytes into a array of ASCII characters,
+    /// or returns `None` if any of the characters is non-ASCII.
+    #[unstable(feature = "ascii_char", issue = "110998")]
+    #[must_use]
+    #[inline]
+    pub fn as_ascii(&self) -> Option<&[ascii::Char; N]> {
+        if self.is_ascii() {
+            // SAFETY: Just checked that it's ASCII
+            Some(unsafe { self.as_ascii_unchecked() })
+        } else {
+            None
+        }
+    }
+
+    /// Converts this array of bytes into a array of ASCII characters,
+    /// without checking whether they're valid.
+    ///
+    /// # Safety
+    ///
+    /// Every byte in the array must be in `0..=127`, or else this is UB.
+    #[unstable(feature = "ascii_char", issue = "110998")]
+    #[must_use]
+    #[inline]
+    pub const unsafe fn as_ascii_unchecked(&self) -> &[ascii::Char; N] {
+        let byte_ptr: *const [u8; N] = self;
+        let ascii_ptr = byte_ptr as *const [ascii::Char; N];
+        // SAFETY: The caller promised all the bytes are ASCII
+        unsafe { &*ascii_ptr }
+    }
+}
diff --git a/library/core/src/array/mod.rs b/library/core/src/array/mod.rs
index 940558974e6..bdb4c975909 100644
--- a/library/core/src/array/mod.rs
+++ b/library/core/src/array/mod.rs
@@ -17,6 +17,7 @@ use crate::ops::{
 };
 use crate::slice::{Iter, IterMut};
 
+mod ascii;
 mod drain;
 mod equality;
 mod iter;
diff --git a/library/core/src/ascii.rs b/library/core/src/ascii.rs
index 065f1b3e70e..7fd14a7e1ea 100644
--- a/library/core/src/ascii.rs
+++ b/library/core/src/ascii.rs
@@ -14,6 +14,10 @@ use crate::fmt;
 use crate::iter::FusedIterator;
 use crate::num::NonZeroUsize;
 
+mod ascii_char;
+#[unstable(feature = "ascii_char", issue = "110998")]
+pub use ascii_char::AsciiChar as Char;
+
 /// An iterator over the escaped version of a byte.
 ///
 /// This `struct` is created by the [`escape_default`] function. See its
diff --git a/library/core/src/ascii/ascii_char.rs b/library/core/src/ascii/ascii_char.rs
new file mode 100644
index 00000000000..f093a0990d1
--- /dev/null
+++ b/library/core/src/ascii/ascii_char.rs
@@ -0,0 +1,565 @@
+//! This uses the name `AsciiChar`, even though it's not exposed that way right now,
+//! because it avoids a whole bunch of "are you sure you didn't mean `char`?"
+//! suggestions from rustc if you get anything slightly wrong in here, and overall
+//! helps with clarity as we're also referring to `char` intentionally in here.
+
+use crate::fmt;
+use crate::mem::transmute;
+
+/// One of the 128 Unicode characters from U+0000 through U+007F,
+/// often known as the [ASCII] subset.
+///
+/// Officially, this is the first [block] in Unicode, _Basic Latin_.
+/// For details, see the [*C0 Controls and Basic Latin*][chart] code chart.
+///
+/// This block was based on older 7-bit character code standards such as
+/// ANSI X3.4-1977, ISO 646-1973, and [NIST FIPS 1-2].
+///
+/// # When to use this
+///
+/// The main advantage of this subset is that it's always valid UTF-8.  As such,
+/// the `&[ascii::Char]` -> `&str` conversion function (as well as other related
+/// ones) are O(1): *no* runtime checks are needed.
+///
+/// If you're consuming strings, you should usually handle Unicode and thus
+/// accept `str`s, not limit yourself to `ascii::Char`s.
+///
+/// However, certain formats are intentionally designed to produce ASCII-only
+/// output in order to be 8-bit-clean.  In those cases, it can be simpler and
+/// faster to generate `ascii::Char`s instead of dealing with the variable width
+/// properties of general UTF-8 encoded strings, while still allowing the result
+/// to be used freely with other Rust things that deal in general `str`s.
+///
+/// For example, a UUID library might offer a way to produce the string
+/// representation of a UUID as an `[ascii::Char; 36]` to avoid memory
+/// allocation yet still allow it to be used as UTF-8 via `as_str` without
+/// paying for validation (or needing `unsafe` code) the way it would if it
+/// were provided as a `[u8; 36]`.
+///
+/// # Layout
+///
+/// This type is guaranteed to have a size and alignment of 1 byte.
+///
+/// # Names
+///
+/// The variants on this type are [Unicode names][NamesList] of the characters
+/// in upper camel case, with a few tweaks:
+/// - For `<control>` characters, the primary alias name is used.
+/// - `LATIN` is dropped, as this block has no non-latin letters.
+/// - `LETTER` is dropped, as `CAPITAL`/`SMALL` suffices in this block.
+/// - `DIGIT`s use a single digit rather than writing out `ZERO`, `ONE`, etc.
+///
+/// [ASCII]: https://www.unicode.org/glossary/index.html#ASCII
+/// [block]: https://www.unicode.org/glossary/index.html#block
+/// [chart]: https://www.unicode.org/charts/PDF/U0000.pdf
+/// [NIST FIPS 1-2]: https://nvlpubs.nist.gov/nistpubs/Legacy/FIPS/fipspub1-2-1977.pdf
+/// [NamesList]: https://www.unicode.org/Public/15.0.0/ucd/NamesList.txt
+#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
+#[unstable(feature = "ascii_char", issue = "110998")]
+#[repr(u8)]
+pub enum AsciiChar {
+    /// U+0000
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    Null = 0,
+    /// U+0001
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    StartOfHeading = 1,
+    /// U+0002
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    StartOfText = 2,
+    /// U+0003
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    EndOfText = 3,
+    /// U+0004
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    EndOfTransmission = 4,
+    /// U+0005
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    Enquiry = 5,
+    /// U+0006
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    Acknowledge = 6,
+    /// U+0007
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    Bell = 7,
+    /// U+0008
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    Backspace = 8,
+    /// U+0009
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    CharacterTabulation = 9,
+    /// U+000A
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    LineFeed = 10,
+    /// U+000B
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    LineTabulation = 11,
+    /// U+000C
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    FormFeed = 12,
+    /// U+000D
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    CarriageReturn = 13,
+    /// U+000E
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    ShiftOut = 14,
+    /// U+000F
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    ShiftIn = 15,
+    /// U+0010
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    DataLinkEscape = 16,
+    /// U+0011
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    DeviceControlOne = 17,
+    /// U+0012
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    DeviceControlTwo = 18,
+    /// U+0013
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    DeviceControlThree = 19,
+    /// U+0014
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    DeviceControlFour = 20,
+    /// U+0015
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    NegativeAcknowledge = 21,
+    /// U+0016
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    SynchronousIdle = 22,
+    /// U+0017
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    EndOfTransmissionBlock = 23,
+    /// U+0018
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    Cancel = 24,
+    /// U+0019
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    EndOfMedium = 25,
+    /// U+001A
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    Substitute = 26,
+    /// U+001B
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    Escape = 27,
+    /// U+001C
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    InformationSeparatorFour = 28,
+    /// U+001D
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    InformationSeparatorThree = 29,
+    /// U+001E
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    InformationSeparatorTwo = 30,
+    /// U+001F
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    InformationSeparatorOne = 31,
+    /// U+0020
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    Space = 32,
+    /// U+0021
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    ExclamationMark = 33,
+    /// U+0022
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    QuotationMark = 34,
+    /// U+0023
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    NumberSign = 35,
+    /// U+0024
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    DollarSign = 36,
+    /// U+0025
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    PercentSign = 37,
+    /// U+0026
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    Ampersand = 38,
+    /// U+0027
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    Apostrophe = 39,
+    /// U+0028
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    LeftParenthesis = 40,
+    /// U+0029
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    RightParenthesis = 41,
+    /// U+002A
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    Asterisk = 42,
+    /// U+002B
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    PlusSign = 43,
+    /// U+002C
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    Comma = 44,
+    /// U+002D
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    HyphenMinus = 45,
+    /// U+002E
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    FullStop = 46,
+    /// U+002F
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    Solidus = 47,
+    /// U+0030
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    Digit0 = 48,
+    /// U+0031
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    Digit1 = 49,
+    /// U+0032
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    Digit2 = 50,
+    /// U+0033
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    Digit3 = 51,
+    /// U+0034
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    Digit4 = 52,
+    /// U+0035
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    Digit5 = 53,
+    /// U+0036
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    Digit6 = 54,
+    /// U+0037
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    Digit7 = 55,
+    /// U+0038
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    Digit8 = 56,
+    /// U+0039
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    Digit9 = 57,
+    /// U+003A
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    Colon = 58,
+    /// U+003B
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    Semicolon = 59,
+    /// U+003C
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    LessThanSign = 60,
+    /// U+003D
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    EqualsSign = 61,
+    /// U+003E
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    GreaterThanSign = 62,
+    /// U+003F
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    QuestionMark = 63,
+    /// U+0040
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    CommercialAt = 64,
+    /// U+0041
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    CapitalA = 65,
+    /// U+0042
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    CapitalB = 66,
+    /// U+0043
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    CapitalC = 67,
+    /// U+0044
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    CapitalD = 68,
+    /// U+0045
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    CapitalE = 69,
+    /// U+0046
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    CapitalF = 70,
+    /// U+0047
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    CapitalG = 71,
+    /// U+0048
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    CapitalH = 72,
+    /// U+0049
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    CapitalI = 73,
+    /// U+004A
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    CapitalJ = 74,
+    /// U+004B
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    CapitalK = 75,
+    /// U+004C
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    CapitalL = 76,
+    /// U+004D
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    CapitalM = 77,
+    /// U+004E
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    CapitalN = 78,
+    /// U+004F
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    CapitalO = 79,
+    /// U+0050
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    CapitalP = 80,
+    /// U+0051
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    CapitalQ = 81,
+    /// U+0052
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    CapitalR = 82,
+    /// U+0053
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    CapitalS = 83,
+    /// U+0054
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    CapitalT = 84,
+    /// U+0055
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    CapitalU = 85,
+    /// U+0056
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    CapitalV = 86,
+    /// U+0057
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    CapitalW = 87,
+    /// U+0058
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    CapitalX = 88,
+    /// U+0059
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    CapitalY = 89,
+    /// U+005A
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    CapitalZ = 90,
+    /// U+005B
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    LeftSquareBracket = 91,
+    /// U+005C
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    ReverseSolidus = 92,
+    /// U+005D
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    RightSquareBracket = 93,
+    /// U+005E
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    CircumflexAccent = 94,
+    /// U+005F
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    LowLine = 95,
+    /// U+0060
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    GraveAccent = 96,
+    /// U+0061
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    SmallA = 97,
+    /// U+0062
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    SmallB = 98,
+    /// U+0063
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    SmallC = 99,
+    /// U+0064
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    SmallD = 100,
+    /// U+0065
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    SmallE = 101,
+    /// U+0066
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    SmallF = 102,
+    /// U+0067
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    SmallG = 103,
+    /// U+0068
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    SmallH = 104,
+    /// U+0069
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    SmallI = 105,
+    /// U+006A
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    SmallJ = 106,
+    /// U+006B
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    SmallK = 107,
+    /// U+006C
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    SmallL = 108,
+    /// U+006D
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    SmallM = 109,
+    /// U+006E
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    SmallN = 110,
+    /// U+006F
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    SmallO = 111,
+    /// U+0070
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    SmallP = 112,
+    /// U+0071
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    SmallQ = 113,
+    /// U+0072
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    SmallR = 114,
+    /// U+0073
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    SmallS = 115,
+    /// U+0074
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    SmallT = 116,
+    /// U+0075
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    SmallU = 117,
+    /// U+0076
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    SmallV = 118,
+    /// U+0077
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    SmallW = 119,
+    /// U+0078
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    SmallX = 120,
+    /// U+0079
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    SmallY = 121,
+    /// U+007A
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    SmallZ = 122,
+    /// U+007B
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    LeftCurlyBracket = 123,
+    /// U+007C
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    VerticalLine = 124,
+    /// U+007D
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    RightCurlyBracket = 125,
+    /// U+007E
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    Tilde = 126,
+    /// U+007F
+    #[unstable(feature = "ascii_char_variants", issue = "110998")]
+    Delete = 127,
+}
+
+impl AsciiChar {
+    /// Creates an ascii character from the byte `b`,
+    /// or returns `None` if it's too large.
+    #[unstable(feature = "ascii_char", issue = "110998")]
+    #[inline]
+    pub const fn from_u8(b: u8) -> Option<Self> {
+        if b <= 127 {
+            // SAFETY: Just checked that `b` is in-range
+            Some(unsafe { Self::from_u8_unchecked(b) })
+        } else {
+            None
+        }
+    }
+
+    /// Creates an ASCII character from the byte `b`,
+    /// without checking whether it's valid.
+    ///
+    /// # Safety
+    ///
+    /// `b` must be in `0..=127`, or else this is UB.
+    #[unstable(feature = "ascii_char", issue = "110998")]
+    #[inline]
+    pub const unsafe fn from_u8_unchecked(b: u8) -> Self {
+        // SAFETY: Our safety precondition is that `b` is in-range.
+        unsafe { transmute(b) }
+    }
+
+    /// When passed the *number* `0`, `1`, …, `9`, returns the *character*
+    /// `'0'`, `'1'`, …, `'9'` respectively.
+    ///
+    /// If `d >= 10`, returns `None`.
+    #[unstable(feature = "ascii_char", issue = "110998")]
+    #[inline]
+    pub const fn digit(d: u8) -> Option<Self> {
+        if d < 10 {
+            // SAFETY: Just checked it's in-range.
+            Some(unsafe { Self::digit_unchecked(d) })
+        } else {
+            None
+        }
+    }
+
+    /// When passed the *number* `0`, `1`, …, `9`, returns the *character*
+    /// `'0'`, `'1'`, …, `'9'` respectively, without checking that it's in-range.
+    ///
+    /// # Safety
+    ///
+    /// This is immediate UB if called with `d > 64`.
+    ///
+    /// If `d >= 10` and `d <= 64`, this is allowed to return any value or panic.
+    /// Notably, it should not be expected to return hex digits, or any other
+    /// reasonable extension of the decimal digits.
+    ///
+    /// (This lose safety condition is intended to simplify soundness proofs
+    /// when writing code using this method, since the implementation doesn't
+    /// need something really specific, not to make those other arguments do
+    /// something useful. It might be tightened before stabilization.)
+    #[unstable(feature = "ascii_char", issue = "110998")]
+    #[inline]
+    pub const unsafe fn digit_unchecked(d: u8) -> Self {
+        debug_assert!(d < 10);
+
+        // SAFETY: `'0'` through `'9'` are U+00030 through U+0039,
+        // so because `d` must be 64 or less the addition can return at most
+        // 112 (0x70), which doesn't overflow and is within the ASCII range.
+        unsafe {
+            let byte = b'0'.unchecked_add(d);
+            Self::from_u8_unchecked(byte)
+        }
+    }
+
+    /// Gets this ASCII character as a byte.
+    #[unstable(feature = "ascii_char", issue = "110998")]
+    #[inline]
+    pub const fn as_u8(self) -> u8 {
+        self as u8
+    }
+
+    /// Gets this ASCII character as a `char` Unicode Scalar Value.
+    #[unstable(feature = "ascii_char", issue = "110998")]
+    #[inline]
+    pub const fn as_char(self) -> char {
+        self as u8 as char
+    }
+
+    /// Views this ASCII character as a one-code-unit UTF-8 `str`.
+    #[unstable(feature = "ascii_char", issue = "110998")]
+    #[inline]
+    pub const fn as_str(&self) -> &str {
+        crate::slice::from_ref(self).as_str()
+    }
+}
+
+impl [AsciiChar] {
+    /// Views this slice of ASCII characters as a UTF-8 `str`.
+    #[unstable(feature = "ascii_char", issue = "110998")]
+    #[inline]
+    pub const fn as_str(&self) -> &str {
+        let ascii_ptr: *const Self = self;
+        let str_ptr = ascii_ptr as *const str;
+        // SAFETY: Each ASCII codepoint in UTF-8 is encoded as one single-byte
+        // code unit having the same value as the ASCII byte.
+        unsafe { &*str_ptr }
+    }
+
+    /// Views this slice of ASCII characters as a slice of `u8` bytes.
+    #[unstable(feature = "ascii_char", issue = "110998")]
+    #[inline]
+    pub const fn as_bytes(&self) -> &[u8] {
+        self.as_str().as_bytes()
+    }
+}
+
+#[unstable(feature = "ascii_char", issue = "110998")]
+impl fmt::Display for AsciiChar {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        <str as fmt::Display>::fmt(self.as_str(), f)
+    }
+}
diff --git a/library/core/src/char/methods.rs b/library/core/src/char/methods.rs
index 2408f178075..1dfa9c34db1 100644
--- a/library/core/src/char/methods.rs
+++ b/library/core/src/char/methods.rs
@@ -1,5 +1,6 @@
 //! impl char {}
 
+use crate::ascii;
 use crate::slice;
 use crate::str::from_utf8_unchecked_mut;
 use crate::unicode::printable::is_printable;
@@ -1101,6 +1102,24 @@ impl char {
         *self as u32 <= 0x7F
     }
 
+    /// Returns `Some` if the value is within the ASCII range,
+    /// or `None` if it's not.
+    ///
+    /// This is preferred to [`Self::is_ascii`] when you're passing the value
+    /// along to something else that can take [`ascii::Char`] rather than
+    /// needing to check again for itself whether the value is in ASCII.
+    #[must_use]
+    #[unstable(feature = "ascii_char", issue = "110998")]
+    #[inline]
+    pub const fn as_ascii(&self) -> Option<ascii::Char> {
+        if self.is_ascii() {
+            // SAFETY: Just checked that this is ASCII.
+            Some(unsafe { ascii::Char::from_u8_unchecked(*self as u8) })
+        } else {
+            None
+        }
+    }
+
     /// Makes a copy of the value in its ASCII upper case equivalent.
     ///
     /// ASCII letters 'a' to 'z' are mapped to 'A' to 'Z',
diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs
index fdd7be625ed..08444421dca 100644
--- a/library/core/src/num/mod.rs
+++ b/library/core/src/num/mod.rs
@@ -472,7 +472,16 @@ impl u8 {
     #[rustc_const_stable(feature = "const_u8_is_ascii", since = "1.43.0")]
     #[inline]
     pub const fn is_ascii(&self) -> bool {
-        *self & 128 == 0
+        *self <= 127
+    }
+
+    /// If the value of this byte is within the ASCII range, returns it as an
+    /// [ASCII character](ascii::Char).  Otherwise, returns `None`.
+    #[must_use]
+    #[unstable(feature = "ascii_char", issue = "110998")]
+    #[inline]
+    pub const fn as_ascii(&self) -> Option<ascii::Char> {
+        ascii::Char::from_u8(*self)
     }
 
     /// Makes a copy of the value in its ASCII upper case equivalent.
diff --git a/library/core/src/num/nonzero.rs b/library/core/src/num/nonzero.rs
index b80bfe1c92d..74a325b89d4 100644
--- a/library/core/src/num/nonzero.rs
+++ b/library/core/src/num/nonzero.rs
@@ -713,6 +713,32 @@ macro_rules! nonzero_signed_operations {
                     unsafe { $Uty::new_unchecked(self.get().unsigned_abs()) }
                 }
 
+                /// Returns `true` if `self` is positive and `false` if the
+                /// number is negative.
+                ///
+                /// # Example
+                ///
+                /// ```
+                /// #![feature(nonzero_negation_ops)]
+                ///
+                #[doc = concat!("# use std::num::", stringify!($Ty), ";")]
+                /// # fn main() { test().unwrap(); }
+                /// # fn test() -> Option<()> {
+                #[doc = concat!("let pos_five = ", stringify!($Ty), "::new(5)?;")]
+                #[doc = concat!("let neg_five = ", stringify!($Ty), "::new(-5)?;")]
+                ///
+                /// assert!(pos_five.is_positive());
+                /// assert!(!neg_five.is_positive());
+                /// # Some(())
+                /// # }
+                /// ```
+                #[must_use]
+                #[inline]
+                #[unstable(feature = "nonzero_negation_ops", issue = "102443")]
+                pub const fn is_positive(self) -> bool {
+                    self.get().is_positive()
+                }
+
                 /// Returns `true` if `self` is negative and `false` if the
                 /// number is positive.
                 ///
diff --git a/library/core/src/slice/ascii.rs b/library/core/src/slice/ascii.rs
index 5e5399acc1b..7bae6692ad4 100644
--- a/library/core/src/slice/ascii.rs
+++ b/library/core/src/slice/ascii.rs
@@ -16,6 +16,36 @@ impl [u8] {
         is_ascii(self)
     }
 
+    /// If this slice [`is_ascii`](Self::is_ascii), returns it as a slice of
+    /// [ASCII characters](`ascii::Char`), otherwise returns `None`.
+    #[unstable(feature = "ascii_char", issue = "110998")]
+    #[must_use]
+    #[inline]
+    pub fn as_ascii(&self) -> Option<&[ascii::Char]> {
+        if self.is_ascii() {
+            // SAFETY: Just checked that it's ASCII
+            Some(unsafe { self.as_ascii_unchecked() })
+        } else {
+            None
+        }
+    }
+
+    /// Converts this slice of bytes into a slice of ASCII characters,
+    /// without checking whether they're valid.
+    ///
+    /// # Safety
+    ///
+    /// Every byte in the slice must be in `0..=127`, or else this is UB.
+    #[unstable(feature = "ascii_char", issue = "110998")]
+    #[must_use]
+    #[inline]
+    pub const unsafe fn as_ascii_unchecked(&self) -> &[ascii::Char] {
+        let byte_ptr: *const [u8] = self;
+        let ascii_ptr = byte_ptr as *const [ascii::Char];
+        // SAFETY: The caller promised all the bytes are ASCII
+        unsafe { &*ascii_ptr }
+    }
+
     /// Checks that two slices are an ASCII case-insensitive match.
     ///
     /// Same as `to_ascii_lowercase(a) == to_ascii_lowercase(b)`,
diff --git a/library/core/src/str/mod.rs b/library/core/src/str/mod.rs
index a13107fd0de..66fa9cf6f64 100644
--- a/library/core/src/str/mod.rs
+++ b/library/core/src/str/mod.rs
@@ -16,6 +16,7 @@ mod validations;
 use self::pattern::Pattern;
 use self::pattern::{DoubleEndedSearcher, ReverseSearcher, Searcher};
 
+use crate::ascii;
 use crate::char::{self, EscapeDebugExtArgs};
 use crate::mem;
 use crate::slice::{self, SliceIndex};
@@ -2366,6 +2367,16 @@ impl str {
         self.as_bytes().is_ascii()
     }
 
+    /// If this string slice [`is_ascii`](Self::is_ascii), returns it as a slice
+    /// of [ASCII characters](`ascii::Char`), otherwise returns `None`.
+    #[unstable(feature = "ascii_char", issue = "110998")]
+    #[must_use]
+    #[inline]
+    pub fn as_ascii(&self) -> Option<&[ascii::Char]> {
+        // Like in `is_ascii`, we can work on the bytes directly.
+        self.as_bytes().as_ascii()
+    }
+
     /// Checks that two strings are an ASCII case-insensitive match.
     ///
     /// Same as `to_ascii_lowercase(a) == to_ascii_lowercase(b)`,
diff --git a/library/std/src/ascii.rs b/library/std/src/ascii.rs
index c29f015777f..b18ab50de12 100644
--- a/library/std/src/ascii.rs
+++ b/library/std/src/ascii.rs
@@ -16,6 +16,9 @@
 #[stable(feature = "rust1", since = "1.0.0")]
 pub use core::ascii::{escape_default, EscapeDefault};
 
+#[unstable(feature = "ascii_char", issue = "110998")]
+pub use core::ascii::Char;
+
 /// Extension methods for ASCII-subset only operations.
 ///
 /// Be aware that operations on seemingly non-ASCII characters can sometimes
diff --git a/library/sysroot/Cargo.toml b/library/sysroot/Cargo.toml
new file mode 100644
index 00000000000..5356ee277cc
--- /dev/null
+++ b/library/sysroot/Cargo.toml
@@ -0,0 +1,27 @@
+[package]
+name = "sysroot"
+version = "0.0.0"
+edition = "2021"
+
+# this is a dummy crate to ensure that all required crates appear in the sysroot
+[dependencies]
+proc_macro = { path = "../proc_macro" }
+std = { path = "../std" }
+test = { path = "../test" }
+
+# Forward features to the `std` crate as necessary
+[features]
+default = ["std_detect_file_io", "std_detect_dlsym_getauxval", "panic-unwind"]
+backtrace = ["std/backtrace"]
+compiler-builtins-c = ["std/compiler-builtins-c"]
+compiler-builtins-mem = ["std/compiler-builtins-mem"]
+compiler-builtins-no-asm = ["std/compiler-builtins-no-asm"]
+compiler-builtins-mangled-names = ["std/compiler-builtins-mangled-names"]
+llvm-libunwind = ["std/llvm-libunwind"]
+system-llvm-libunwind = ["std/system-llvm-libunwind"]
+panic-unwind = ["std/panic_unwind"]
+panic_immediate_abort = ["std/panic_immediate_abort"]
+profiler = ["std/profiler"]
+std_detect_file_io = ["std/std_detect_file_io"]
+std_detect_dlsym_getauxval = ["std/std_detect_dlsym_getauxval"]
+std_detect_env_override = ["std/std_detect_env_override"]
diff --git a/library/sysroot/src/lib.rs b/library/sysroot/src/lib.rs
new file mode 100644
index 00000000000..71ceb580a40
--- /dev/null
+++ b/library/sysroot/src/lib.rs
@@ -0,0 +1 @@
+// This is intentionally empty since this crate is only used to depend on other library crates.
diff --git a/library/test/Cargo.toml b/library/test/Cargo.toml
index 18cb023d274..91a1abde059 100644
--- a/library/test/Cargo.toml
+++ b/library/test/Cargo.toml
@@ -12,23 +12,3 @@ std = { path = "../std" }
 core = { path = "../core" }
 panic_unwind = { path = "../panic_unwind" }
 panic_abort = { path = "../panic_abort" }
-
-# not actually used but needed to always have proc_macro in the sysroot
-proc_macro = { path = "../proc_macro" }
-
-# Forward features to the `std` crate as necessary
-[features]
-default = ["std_detect_file_io", "std_detect_dlsym_getauxval", "panic-unwind"]
-backtrace = ["std/backtrace"]
-compiler-builtins-c = ["std/compiler-builtins-c"]
-compiler-builtins-mem = ["std/compiler-builtins-mem"]
-compiler-builtins-no-asm = ["std/compiler-builtins-no-asm"]
-compiler-builtins-mangled-names = ["std/compiler-builtins-mangled-names"]
-llvm-libunwind = ["std/llvm-libunwind"]
-system-llvm-libunwind = ["std/system-llvm-libunwind"]
-panic-unwind = ["std/panic_unwind"]
-panic_immediate_abort = ["std/panic_immediate_abort"]
-profiler = ["std/profiler"]
-std_detect_file_io = ["std/std_detect_file_io"]
-std_detect_dlsym_getauxval = ["std/std_detect_dlsym_getauxval"]
-std_detect_env_override = ["std/std_detect_env_override"]
diff --git a/library/test/src/formatters/junit.rs b/library/test/src/formatters/junit.rs
index 2e07ce3c099..9f5bf24367e 100644
--- a/library/test/src/formatters/junit.rs
+++ b/library/test/src/formatters/junit.rs
@@ -11,7 +11,7 @@ use crate::{
 
 pub struct JunitFormatter<T> {
     out: OutputLocation<T>,
-    results: Vec<(TestDesc, TestResult, Duration)>,
+    results: Vec<(TestDesc, TestResult, Duration, Vec<u8>)>,
 }
 
 impl<T: Write> JunitFormatter<T> {
@@ -26,6 +26,18 @@ impl<T: Write> JunitFormatter<T> {
     }
 }
 
+fn str_to_cdata(s: &str) -> String {
+    // Drop the stdout in a cdata. Unfortunately, you can't put either of `]]>` or
+    // `<?'` in a CDATA block, so the escaping gets a little weird.
+    let escaped_output = s.replace("]]>", "]]]]><![CDATA[>");
+    let escaped_output = escaped_output.replace("<?", "<]]><![CDATA[?");
+    // We also smuggle newlines as &#xa so as to keep all the output on one line
+    let escaped_output = escaped_output.replace("\n", "]]>&#xA;<![CDATA[");
+    // Prune empty CDATA blocks resulting from any escaping
+    let escaped_output = escaped_output.replace("<![CDATA[]]>", "");
+    format!("<![CDATA[{}]]>", escaped_output)
+}
+
 impl<T: Write> OutputFormatter for JunitFormatter<T> {
     fn write_discovery_start(&mut self) -> io::Result<()> {
         Err(io::Error::new(io::ErrorKind::NotFound, "Not yet implemented!"))
@@ -63,14 +75,14 @@ impl<T: Write> OutputFormatter for JunitFormatter<T> {
         desc: &TestDesc,
         result: &TestResult,
         exec_time: Option<&time::TestExecTime>,
-        _stdout: &[u8],
+        stdout: &[u8],
         _state: &ConsoleTestState,
     ) -> io::Result<()> {
         // Because the testsuite node holds some of the information as attributes, we can't write it
         // until all of the tests have finished. Instead of writing every result as they come in, we add
         // them to a Vec and write them all at once when run is complete.
         let duration = exec_time.map(|t| t.0).unwrap_or_default();
-        self.results.push((desc.clone(), result.clone(), duration));
+        self.results.push((desc.clone(), result.clone(), duration, stdout.to_vec()));
         Ok(())
     }
     fn write_run_finish(&mut self, state: &ConsoleTestState) -> io::Result<bool> {
@@ -85,7 +97,7 @@ impl<T: Write> OutputFormatter for JunitFormatter<T> {
              >",
             state.failed, state.total, state.ignored
         ))?;
-        for (desc, result, duration) in std::mem::take(&mut self.results) {
+        for (desc, result, duration, stdout) in std::mem::take(&mut self.results) {
             let (class_name, test_name) = parse_class_name(&desc);
             match result {
                 TestResult::TrIgnored => { /* no-op */ }
@@ -98,6 +110,11 @@ impl<T: Write> OutputFormatter for JunitFormatter<T> {
                         duration.as_secs_f64()
                     ))?;
                     self.write_message("<failure type=\"assert\"/>")?;
+                    if !stdout.is_empty() {
+                        self.write_message("<system-out>")?;
+                        self.write_message(&str_to_cdata(&String::from_utf8_lossy(&stdout)))?;
+                        self.write_message("</system-out>")?;
+                    }
                     self.write_message("</testcase>")?;
                 }
 
@@ -110,6 +127,11 @@ impl<T: Write> OutputFormatter for JunitFormatter<T> {
                         duration.as_secs_f64()
                     ))?;
                     self.write_message(&format!("<failure message=\"{m}\" type=\"assert\"/>"))?;
+                    if !stdout.is_empty() {
+                        self.write_message("<system-out>")?;
+                        self.write_message(&str_to_cdata(&String::from_utf8_lossy(&stdout)))?;
+                        self.write_message("</system-out>")?;
+                    }
                     self.write_message("</testcase>")?;
                 }
 
@@ -136,11 +158,19 @@ impl<T: Write> OutputFormatter for JunitFormatter<T> {
                 TestResult::TrOk => {
                     self.write_message(&format!(
                         "<testcase classname=\"{}\" \
-                         name=\"{}\" time=\"{}\"/>",
+                         name=\"{}\" time=\"{}\"",
                         class_name,
                         test_name,
                         duration.as_secs_f64()
                     ))?;
+                    if stdout.is_empty() || !state.options.display_output {
+                        self.write_message("/>")?;
+                    } else {
+                        self.write_message("><system-out>")?;
+                        self.write_message(&str_to_cdata(&String::from_utf8_lossy(&stdout)))?;
+                        self.write_message("</system-out>")?;
+                        self.write_message("</testcase>")?;
+                    }
                 }
             }
         }
diff --git a/src/bootstrap/check.rs b/src/bootstrap/check.rs
index 60de46ce64c..956b82385f6 100644
--- a/src/bootstrap/check.rs
+++ b/src/bootstrap/check.rs
@@ -79,7 +79,7 @@ impl Step for Std {
     const DEFAULT: bool = true;
 
     fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
-        run.all_krates("test").path("library")
+        run.all_krates("sysroot").path("library")
     }
 
     fn make_run(run: RunConfig<'_>) {
diff --git a/src/bootstrap/clean.rs b/src/bootstrap/clean.rs
index 7ebd0a8f270..0d9fd56b038 100644
--- a/src/bootstrap/clean.rs
+++ b/src/bootstrap/clean.rs
@@ -81,7 +81,7 @@ macro_rules! clean_crate_tree {
 
 clean_crate_tree! {
     Rustc, Mode::Rustc, "rustc-main";
-    Std, Mode::Std, "test";
+    Std, Mode::Std, "sysroot";
 }
 
 fn clean_default(build: &Build, all: bool) {
diff --git a/src/bootstrap/compile.rs b/src/bootstrap/compile.rs
index 7d2a6862500..966ae00fa1d 100644
--- a/src/bootstrap/compile.rs
+++ b/src/bootstrap/compile.rs
@@ -56,7 +56,7 @@ impl Step for Std {
         // When downloading stage1, the standard library has already been copied to the sysroot, so
         // there's no need to rebuild it.
         let builder = run.builder;
-        run.crate_or_deps("test")
+        run.crate_or_deps("sysroot")
             .path("library")
             .lazy_default_condition(Box::new(|| !builder.download_rustc()))
     }
@@ -364,7 +364,7 @@ pub fn std_cargo(builder: &Builder<'_>, target: TargetSelection, stage: u32, car
             .arg("--features")
             .arg(features)
             .arg("--manifest-path")
-            .arg(builder.src.join("library/test/Cargo.toml"));
+            .arg(builder.src.join("library/sysroot/Cargo.toml"));
 
         // Help the libc crate compile by assisting it in finding various
         // sysroot native libraries.
diff --git a/src/bootstrap/dist.rs b/src/bootstrap/dist.rs
index 3b35ca1d15d..ab7f7350d5f 100644
--- a/src/bootstrap/dist.rs
+++ b/src/bootstrap/dist.rs
@@ -976,6 +976,7 @@ impl Step for PlainSourceTarball {
             "config.example.toml",
             "Cargo.toml",
             "Cargo.lock",
+            ".gitmodules",
         ];
         let src_dirs = ["src", "compiler", "library", "tests"];
 
diff --git a/src/bootstrap/doc.rs b/src/bootstrap/doc.rs
index 9ad98eb5702..8f5d9bb66e1 100644
--- a/src/bootstrap/doc.rs
+++ b/src/bootstrap/doc.rs
@@ -438,7 +438,7 @@ impl Step for Std {
 
     fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
         let builder = run.builder;
-        run.all_krates("test").path("library").default_condition(builder.config.docs)
+        run.all_krates("sysroot").path("library").default_condition(builder.config.docs)
     }
 
     fn make_run(run: RunConfig<'_>) {
diff --git a/src/bootstrap/test.rs b/src/bootstrap/test.rs
index aee84e9c9be..cfccb516627 100644
--- a/src/bootstrap/test.rs
+++ b/src/bootstrap/test.rs
@@ -2145,7 +2145,7 @@ impl Step for Crate {
     const DEFAULT: bool = true;
 
     fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
-        run.crate_or_deps("test")
+        run.crate_or_deps("sysroot")
     }
 
     fn make_run(run: RunConfig<'_>) {
diff --git a/tests/codegen/ascii-char.rs b/tests/codegen/ascii-char.rs
new file mode 100644
index 00000000000..4167becf5e9
--- /dev/null
+++ b/tests/codegen/ascii-char.rs
@@ -0,0 +1,37 @@
+// compile-flags: -C opt-level=1
+// ignore-debug (the extra assertions get in the way)
+
+#![crate_type = "lib"]
+#![feature(ascii_char)]
+
+use std::ascii::Char as AsciiChar;
+
+// CHECK-LABEL: i8 @unwrap_digit_from_remainder(i32
+#[no_mangle]
+pub fn unwrap_digit_from_remainder(v: u32) -> AsciiChar {
+    // CHECK-NOT: icmp
+    // CHECK-NOT: panic
+
+    // CHECK: %[[R:.+]] = urem i32 %v, 10
+    // CHECK-NEXT: %[[T:.+]] = trunc i32 %[[R]] to i8
+    // CHECK-NEXT: %[[D:.+]] = or i8 %[[T]], 48
+    // CHECK-NEXT: ret i8 %[[D]]
+
+    // CHECK-NOT: icmp
+    // CHECK-NOT: panic
+    AsciiChar::digit((v % 10) as u8).unwrap()
+}
+
+// CHECK-LABEL: i8 @unwrap_from_masked(i8
+#[no_mangle]
+pub fn unwrap_from_masked(b: u8) -> AsciiChar {
+    // CHECK-NOT: icmp
+    // CHECK-NOT: panic
+
+    // CHECK: %[[M:.+]] = and i8 %b, 127
+    // CHECK-NEXT: ret i8 %[[M]]
+
+    // CHECK-NOT: icmp
+    // CHECK-NOT: panic
+    AsciiChar::from_u8(b & 0x7f).unwrap()
+}
diff --git a/tests/mir-opt/dead-store-elimination/place_mention.main.DeadStoreElimination.diff b/tests/mir-opt/dead-store-elimination/place_mention.main.DeadStoreElimination.diff
new file mode 100644
index 00000000000..761c074ed94
--- /dev/null
+++ b/tests/mir-opt/dead-store-elimination/place_mention.main.DeadStoreElimination.diff
@@ -0,0 +1,25 @@
+- // MIR for `main` before DeadStoreElimination
++ // MIR for `main` after DeadStoreElimination
+  
+  fn main() -> () {
+      let mut _0: ();                      // return place in scope 0 at $DIR/place_mention.rs:+0:11: +0:11
+      let mut _1: (&str, &str);            // in scope 0 at $DIR/place_mention.rs:+3:18: +3:36
+      scope 1 {
+      }
+  
+      bb0: {
+          StorageLive(_1);                 // scope 0 at $DIR/place_mention.rs:+3:18: +3:36
+          _1 = (const "Hello", const "World"); // scope 0 at $DIR/place_mention.rs:+3:18: +3:36
+                                           // mir::Constant
+                                           // + span: $DIR/place_mention.rs:8:19: 8:26
+                                           // + literal: Const { ty: &str, val: Value(Slice(..)) }
+                                           // mir::Constant
+                                           // + span: $DIR/place_mention.rs:8:28: 8:35
+                                           // + literal: Const { ty: &str, val: Value(Slice(..)) }
+          PlaceMention(_1);                // scope 0 at $DIR/place_mention.rs:+3:18: +3:36
+          StorageDead(_1);                 // scope 0 at $DIR/place_mention.rs:+3:36: +3:37
+          _0 = const ();                   // scope 0 at $DIR/place_mention.rs:+0:11: +4:2
+          return;                          // scope 0 at $DIR/place_mention.rs:+4:2: +4:2
+      }
+  }
+  
diff --git a/tests/mir-opt/dead-store-elimination/place_mention.rs b/tests/mir-opt/dead-store-elimination/place_mention.rs
new file mode 100644
index 00000000000..59dc74454a4
--- /dev/null
+++ b/tests/mir-opt/dead-store-elimination/place_mention.rs
@@ -0,0 +1,9 @@
+// unit-test: DeadStoreElimination
+// compile-flags: -Zmir-keep-place-mention
+
+// EMIT_MIR place_mention.main.DeadStoreElimination.diff
+fn main() {
+    // Verify that we account for the `PlaceMention` statement as a use of the tuple,
+    // and don't remove it as a dead store.
+    let (_, _) = ("Hello", "World");
+}
diff --git a/tests/run-make/libtest-junit/Makefile b/tests/run-make/libtest-junit/Makefile
new file mode 100644
index 00000000000..d97cafccf1f
--- /dev/null
+++ b/tests/run-make/libtest-junit/Makefile
@@ -0,0 +1,19 @@
+# ignore-cross-compile
+include ../tools.mk
+
+# Test expected libtest's junit output
+
+OUTPUT_FILE_DEFAULT := $(TMPDIR)/libtest-junit-output-default.xml
+OUTPUT_FILE_STDOUT_SUCCESS := $(TMPDIR)/libtest-junit-output-stdout-success.xml
+
+all: f.rs validate_junit.py output-default.xml output-stdout-success.xml
+	$(RUSTC) --test f.rs
+	RUST_BACKTRACE=0 $(call RUN,f) -Z unstable-options --test-threads=1 --format=junit > $(OUTPUT_FILE_DEFAULT) || true
+	RUST_BACKTRACE=0 $(call RUN,f) -Z unstable-options --test-threads=1 --format=junit --show-output > $(OUTPUT_FILE_STDOUT_SUCCESS) || true
+
+	cat $(OUTPUT_FILE_DEFAULT) | "$(PYTHON)" validate_junit.py
+	cat $(OUTPUT_FILE_STDOUT_SUCCESS) | "$(PYTHON)" validate_junit.py
+
+	# Normalize the actual output and compare to expected output file
+	cat $(OUTPUT_FILE_DEFAULT) | sed 's/time="[0-9.]*"/time="$$TIME"/g' | diff output-default.xml -
+	cat $(OUTPUT_FILE_STDOUT_SUCCESS) | sed 's/time="[0-9.]*"/time="$$TIME"/g' | diff output-stdout-success.xml -
diff --git a/tests/run-make/libtest-junit/f.rs b/tests/run-make/libtest-junit/f.rs
new file mode 100644
index 00000000000..d360d77317d
--- /dev/null
+++ b/tests/run-make/libtest-junit/f.rs
@@ -0,0 +1,23 @@
+#[test]
+fn a() {
+    println!("print from successful test");
+    // Should pass
+}
+
+#[test]
+fn b() {
+    println!("print from failing test");
+    assert!(false);
+}
+
+#[test]
+#[should_panic]
+fn c() {
+    assert!(false);
+}
+
+#[test]
+#[ignore = "msg"]
+fn d() {
+    assert!(false);
+}
diff --git a/tests/run-make/libtest-junit/output-default.xml b/tests/run-make/libtest-junit/output-default.xml
new file mode 100644
index 00000000000..d59e07b8ad8
--- /dev/null
+++ b/tests/run-make/libtest-junit/output-default.xml
@@ -0,0 +1 @@
+<?xml version="1.0" encoding="UTF-8"?><testsuites><testsuite name="test" package="test" id="0" errors="0" failures="1" tests="4" skipped="1" ><testcase classname="unknown" name="a" time="$TIME"/><testcase classname="unknown" name="b" time="$TIME"><failure type="assert"/><system-out><![CDATA[print from failing test]]>&#xA;<![CDATA[thread 'b' panicked at 'assertion failed: false', f.rs:10:5]]>&#xA;<![CDATA[note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace]]>&#xA;<![CDATA[]]></system-out></testcase><testcase classname="unknown" name="c" time="$TIME"/><system-out/><system-err/></testsuite></testsuites>
diff --git a/tests/run-make/libtest-junit/output-stdout-success.xml b/tests/run-make/libtest-junit/output-stdout-success.xml
new file mode 100644
index 00000000000..0c300611e1f
--- /dev/null
+++ b/tests/run-make/libtest-junit/output-stdout-success.xml
@@ -0,0 +1 @@
+<?xml version="1.0" encoding="UTF-8"?><testsuites><testsuite name="test" package="test" id="0" errors="0" failures="1" tests="4" skipped="1" ><testcase classname="unknown" name="a" time="$TIME"><system-out><![CDATA[print from successful test]]>&#xA;<![CDATA[]]></system-out></testcase><testcase classname="unknown" name="b" time="$TIME"><failure type="assert"/><system-out><![CDATA[print from failing test]]>&#xA;<![CDATA[thread 'b' panicked at 'assertion failed: false', f.rs:10:5]]>&#xA;<![CDATA[note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace]]>&#xA;<![CDATA[]]></system-out></testcase><testcase classname="unknown" name="c" time="$TIME"><system-out><![CDATA[thread 'c' panicked at 'assertion failed: false', f.rs:16:5]]>&#xA;<![CDATA[]]></system-out></testcase><system-out/><system-err/></testsuite></testsuites>
diff --git a/tests/run-make/libtest-junit/validate_junit.py b/tests/run-make/libtest-junit/validate_junit.py
new file mode 100755
index 00000000000..47a8e70ccc3
--- /dev/null
+++ b/tests/run-make/libtest-junit/validate_junit.py
@@ -0,0 +1,12 @@
+#!/usr/bin/env python
+
+import sys
+import xml.etree.ElementTree as ET
+
+# Try to decode line in order to ensure it is a valid XML document
+for line in sys.stdin:
+    try:
+        ET.fromstring(line)
+    except ET.ParseError as pe:
+        print("Invalid xml: %r" % line)
+        raise
diff --git a/tests/ui/const-generics/sneaky-array-repeat-expr.rs b/tests/ui/const-generics/sneaky-array-repeat-expr.rs
index b147c246bda..cd1607608a6 100644
--- a/tests/ui/const-generics/sneaky-array-repeat-expr.rs
+++ b/tests/ui/const-generics/sneaky-array-repeat-expr.rs
@@ -10,6 +10,7 @@ impl<const N: usize> Trait<N> for () {
 pub const fn foo<const N: usize>() where (): Trait<N> {
     let bar = [(); <()>::Assoc];
     //~^ error: constant expression depends on a generic parameter
+    //~| error: constant expression depends on a generic parameter
 }
 
 trait Trait2<const N: usize> {
@@ -24,6 +25,7 @@ impl<const N: usize> Trait2<N> for () {
 pub const fn foo2<const N: usize>() where (): Trait2<N> {
     let bar2 = [(); <()>::Assoc2];
     //~^ error: constant expression depends on a generic parameter
+    //~| error: constant expression depends on a generic parameter
 }
 
 fn main() {
diff --git a/tests/ui/const-generics/sneaky-array-repeat-expr.stderr b/tests/ui/const-generics/sneaky-array-repeat-expr.stderr
index 5c77375d399..e532f27a10d 100644
--- a/tests/ui/const-generics/sneaky-array-repeat-expr.stderr
+++ b/tests/ui/const-generics/sneaky-array-repeat-expr.stderr
@@ -7,12 +7,28 @@ LL |     let bar = [(); <()>::Assoc];
    = note: this may fail depending on what value the parameter takes
 
 error: constant expression depends on a generic parameter
-  --> $DIR/sneaky-array-repeat-expr.rs:25:21
+  --> $DIR/sneaky-array-repeat-expr.rs:11:15
+   |
+LL |     let bar = [(); <()>::Assoc];
+   |               ^^^^^^^^^^^^^^^^^
+   |
+   = note: this may fail depending on what value the parameter takes
+
+error: constant expression depends on a generic parameter
+  --> $DIR/sneaky-array-repeat-expr.rs:26:21
    |
 LL |     let bar2 = [(); <()>::Assoc2];
    |                     ^^^^^^^^^^^^
    |
    = note: this may fail depending on what value the parameter takes
 
-error: aborting due to 2 previous errors
+error: constant expression depends on a generic parameter
+  --> $DIR/sneaky-array-repeat-expr.rs:26:16
+   |
+LL |     let bar2 = [(); <()>::Assoc2];
+   |                ^^^^^^^^^^^^^^^^^^
+   |
+   = note: this may fail depending on what value the parameter takes
+
+error: aborting due to 4 previous errors
 
diff --git a/tests/ui/consts/issue-50439.rs b/tests/ui/consts/issue-50439.rs
index 0be7c405473..d42347e136e 100644
--- a/tests/ui/consts/issue-50439.rs
+++ b/tests/ui/consts/issue-50439.rs
@@ -22,7 +22,9 @@ impl<T: Sized> PinDropInternal for Bears<T> {
     where
         Self: ReflectDrop,
     {
-        let _ = [(); 0 - !!(<Bears<T> as ReflectDrop>::REFLECT_DROP) as usize]; //~ ERROR constant expression depends on a generic parameter
+        let _ = [(); 0 - !!(<Bears<T> as ReflectDrop>::REFLECT_DROP) as usize];
+        //~^ ERROR constant expression depends on a generic parameter
+        //~| ERROR constant expression depends on a generic parameter
     }
 }
 
diff --git a/tests/ui/consts/issue-50439.stderr b/tests/ui/consts/issue-50439.stderr
index 3fbdf33b2d8..7a8cd45ecc7 100644
--- a/tests/ui/consts/issue-50439.stderr
+++ b/tests/ui/consts/issue-50439.stderr
@@ -6,5 +6,13 @@ LL |         let _ = [(); 0 - !!(<Bears<T> as ReflectDrop>::REFLECT_DROP) as usi
    |
    = note: this may fail depending on what value the parameter takes
 
-error: aborting due to previous error
+error: constant expression depends on a generic parameter
+  --> $DIR/issue-50439.rs:25:17
+   |
+LL |         let _ = [(); 0 - !!(<Bears<T> as ReflectDrop>::REFLECT_DROP) as usize];
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: this may fail depending on what value the parameter takes
+
+error: aborting due to 2 previous errors
 
diff --git a/tests/ui/self/elision/nested-item.rs b/tests/ui/self/elision/nested-item.rs
new file mode 100644
index 00000000000..4bcb645c60e
--- /dev/null
+++ b/tests/ui/self/elision/nested-item.rs
@@ -0,0 +1,13 @@
+// Regression test for #110899.
+// When looking for the elided lifetime for `wrap`,
+// we must not consider the lifetimes in `bar` as candidates.
+
+fn wrap(self: Wrap<{ fn bar(&self) {} }>) -> &() {
+    //~^ ERROR `self` parameter is only allowed in associated functions
+    //~| ERROR `self` parameter is only allowed in associated functions
+    //~| ERROR missing lifetime specifier
+    //~| ERROR cannot find type `Wrap` in this scope
+    &()
+}
+
+fn main() {}
diff --git a/tests/ui/self/elision/nested-item.stderr b/tests/ui/self/elision/nested-item.stderr
new file mode 100644
index 00000000000..752fd82332c
--- /dev/null
+++ b/tests/ui/self/elision/nested-item.stderr
@@ -0,0 +1,38 @@
+error: `self` parameter is only allowed in associated functions
+  --> $DIR/nested-item.rs:5:9
+   |
+LL | fn wrap(self: Wrap<{ fn bar(&self) {} }>) -> &() {
+   |         ^^^^ not semantically valid as function parameter
+   |
+   = note: associated functions are those in `impl` or `trait` definitions
+
+error: `self` parameter is only allowed in associated functions
+  --> $DIR/nested-item.rs:5:29
+   |
+LL | fn wrap(self: Wrap<{ fn bar(&self) {} }>) -> &() {
+   |                             ^^^^^ not semantically valid as function parameter
+   |
+   = note: associated functions are those in `impl` or `trait` definitions
+
+error[E0106]: missing lifetime specifier
+  --> $DIR/nested-item.rs:5:46
+   |
+LL | fn wrap(self: Wrap<{ fn bar(&self) {} }>) -> &() {
+   |                                              ^ expected named lifetime parameter
+   |
+   = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from
+help: consider using the `'static` lifetime
+   |
+LL | fn wrap(self: Wrap<{ fn bar(&self) {} }>) -> &'static () {
+   |                                               +++++++
+
+error[E0412]: cannot find type `Wrap` in this scope
+  --> $DIR/nested-item.rs:5:15
+   |
+LL | fn wrap(self: Wrap<{ fn bar(&self) {} }>) -> &() {
+   |               ^^^^ not found in this scope
+
+error: aborting due to 4 previous errors
+
+Some errors have detailed explanations: E0106, E0412.
+For more information about an error, try `rustc --explain E0106`.
diff --git a/tests/ui/typeck/repeat-expr-checks-wf.rs b/tests/ui/typeck/repeat-expr-checks-wf.rs
new file mode 100644
index 00000000000..b8a2a0ceb58
--- /dev/null
+++ b/tests/ui/typeck/repeat-expr-checks-wf.rs
@@ -0,0 +1,10 @@
+trait Foo {
+    const ASSOC: [u8];
+}
+
+fn bar<T: Foo>() {
+    let a = [T::ASSOC; 2];
+    //~^ ERROR: the size for values of type `[u8]` cannot be known at compilation time
+}
+
+fn main() {}
diff --git a/tests/ui/typeck/repeat-expr-checks-wf.stderr b/tests/ui/typeck/repeat-expr-checks-wf.stderr
new file mode 100644
index 00000000000..a821088a4b3
--- /dev/null
+++ b/tests/ui/typeck/repeat-expr-checks-wf.stderr
@@ -0,0 +1,12 @@
+error[E0277]: the size for values of type `[u8]` cannot be known at compilation time
+  --> $DIR/repeat-expr-checks-wf.rs:6:13
+   |
+LL |     let a = [T::ASSOC; 2];
+   |             ^^^^^^^^^^^^^ doesn't have a size known at compile-time
+   |
+   = help: the trait `Sized` is not implemented for `[u8]`
+   = note: slice and array elements must have `Sized` type
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0277`.