diff --git a/src/libcore/char/methods.rs b/src/libcore/char/methods.rs
index c341bb552a1ea..2b52c48cb9077 100644
--- a/src/libcore/char/methods.rs
+++ b/src/libcore/char/methods.rs
@@ -1072,8 +1072,9 @@ impl char {
     /// assert!(!esc.is_ascii_alphabetic());
     /// ```
     #[stable(feature = "ascii_ctype_on_intrinsics", since = "1.24.0")]
+    #[rustc_const_unstable(feature = "const_ascii_ctype_on_intrinsics", issue = "68983")]
     #[inline]
-    pub fn is_ascii_alphabetic(&self) -> bool {
+    pub const fn is_ascii_alphabetic(&self) -> bool {
         self.is_ascii() && (*self as u8).is_ascii_alphabetic()
     }
 
@@ -1104,8 +1105,9 @@ impl char {
     /// assert!(!esc.is_ascii_uppercase());
     /// ```
     #[stable(feature = "ascii_ctype_on_intrinsics", since = "1.24.0")]
+    #[rustc_const_unstable(feature = "const_ascii_ctype_on_intrinsics", issue = "68983")]
     #[inline]
-    pub fn is_ascii_uppercase(&self) -> bool {
+    pub const fn is_ascii_uppercase(&self) -> bool {
         self.is_ascii() && (*self as u8).is_ascii_uppercase()
     }
 
@@ -1136,8 +1138,9 @@ impl char {
     /// assert!(!esc.is_ascii_lowercase());
     /// ```
     #[stable(feature = "ascii_ctype_on_intrinsics", since = "1.24.0")]
+    #[rustc_const_unstable(feature = "const_ascii_ctype_on_intrinsics", issue = "68983")]
     #[inline]
-    pub fn is_ascii_lowercase(&self) -> bool {
+    pub const fn is_ascii_lowercase(&self) -> bool {
         self.is_ascii() && (*self as u8).is_ascii_lowercase()
     }
 
@@ -1171,8 +1174,9 @@ impl char {
     /// assert!(!esc.is_ascii_alphanumeric());
     /// ```
     #[stable(feature = "ascii_ctype_on_intrinsics", since = "1.24.0")]
+    #[rustc_const_unstable(feature = "const_ascii_ctype_on_intrinsics", issue = "68983")]
     #[inline]
-    pub fn is_ascii_alphanumeric(&self) -> bool {
+    pub const fn is_ascii_alphanumeric(&self) -> bool {
         self.is_ascii() && (*self as u8).is_ascii_alphanumeric()
     }
 
@@ -1203,8 +1207,9 @@ impl char {
     /// assert!(!esc.is_ascii_digit());
     /// ```
     #[stable(feature = "ascii_ctype_on_intrinsics", since = "1.24.0")]
+    #[rustc_const_unstable(feature = "const_ascii_ctype_on_intrinsics", issue = "68983")]
     #[inline]
-    pub fn is_ascii_digit(&self) -> bool {
+    pub const fn is_ascii_digit(&self) -> bool {
         self.is_ascii() && (*self as u8).is_ascii_digit()
     }
 
@@ -1238,8 +1243,9 @@ impl char {
     /// assert!(!esc.is_ascii_hexdigit());
     /// ```
     #[stable(feature = "ascii_ctype_on_intrinsics", since = "1.24.0")]
+    #[rustc_const_unstable(feature = "const_ascii_ctype_on_intrinsics", issue = "68983")]
     #[inline]
-    pub fn is_ascii_hexdigit(&self) -> bool {
+    pub const fn is_ascii_hexdigit(&self) -> bool {
         self.is_ascii() && (*self as u8).is_ascii_hexdigit()
     }
 
@@ -1274,8 +1280,9 @@ impl char {
     /// assert!(!esc.is_ascii_punctuation());
     /// ```
     #[stable(feature = "ascii_ctype_on_intrinsics", since = "1.24.0")]
+    #[rustc_const_unstable(feature = "const_ascii_ctype_on_intrinsics", issue = "68983")]
     #[inline]
-    pub fn is_ascii_punctuation(&self) -> bool {
+    pub const fn is_ascii_punctuation(&self) -> bool {
         self.is_ascii() && (*self as u8).is_ascii_punctuation()
     }
 
@@ -1306,8 +1313,9 @@ impl char {
     /// assert!(!esc.is_ascii_graphic());
     /// ```
     #[stable(feature = "ascii_ctype_on_intrinsics", since = "1.24.0")]
+    #[rustc_const_unstable(feature = "const_ascii_ctype_on_intrinsics", issue = "68983")]
     #[inline]
-    pub fn is_ascii_graphic(&self) -> bool {
+    pub const fn is_ascii_graphic(&self) -> bool {
         self.is_ascii() && (*self as u8).is_ascii_graphic()
     }
 
@@ -1355,8 +1363,9 @@ impl char {
     /// assert!(!esc.is_ascii_whitespace());
     /// ```
     #[stable(feature = "ascii_ctype_on_intrinsics", since = "1.24.0")]
+    #[rustc_const_unstable(feature = "const_ascii_ctype_on_intrinsics", issue = "68983")]
     #[inline]
-    pub fn is_ascii_whitespace(&self) -> bool {
+    pub const fn is_ascii_whitespace(&self) -> bool {
         self.is_ascii() && (*self as u8).is_ascii_whitespace()
     }
 
@@ -1389,8 +1398,9 @@ impl char {
     /// assert!(esc.is_ascii_control());
     /// ```
     #[stable(feature = "ascii_ctype_on_intrinsics", since = "1.24.0")]
+    #[rustc_const_unstable(feature = "const_ascii_ctype_on_intrinsics", issue = "68983")]
     #[inline]
-    pub fn is_ascii_control(&self) -> bool {
+    pub const fn is_ascii_control(&self) -> bool {
         self.is_ascii() && (*self as u8).is_ascii_control()
     }
 }
diff --git a/src/libcore/lib.rs b/src/libcore/lib.rs
index 1fd70e1a1b049..176c36664a9b1 100644
--- a/src/libcore/lib.rs
+++ b/src/libcore/lib.rs
@@ -70,6 +70,7 @@
 #![feature(bound_cloned)]
 #![feature(cfg_target_has_atomic)]
 #![feature(concat_idents)]
+#![feature(const_ascii_ctype_on_intrinsics)]
 #![feature(const_alloc_layout)]
 #![feature(const_if_match)]
 #![feature(const_checked_int_methods)]
diff --git a/src/libcore/num/mod.rs b/src/libcore/num/mod.rs
index 3ae8f0f7870c1..471ab96662249 100644
--- a/src/libcore/num/mod.rs
+++ b/src/libcore/num/mod.rs
@@ -4449,8 +4449,9 @@ impl u8 {
     /// assert!(!esc.is_ascii_alphabetic());
     /// ```
     #[stable(feature = "ascii_ctype_on_intrinsics", since = "1.24.0")]
+    #[rustc_const_unstable(feature = "const_ascii_ctype_on_intrinsics", issue = "68983")]
     #[inline]
-    pub fn is_ascii_alphabetic(&self) -> bool {
+    pub const fn is_ascii_alphabetic(&self) -> bool {
         matches!(*self, b'A'..=b'Z' | b'a'..=b'z')
     }
 
@@ -4481,8 +4482,9 @@ impl u8 {
     /// assert!(!esc.is_ascii_uppercase());
     /// ```
     #[stable(feature = "ascii_ctype_on_intrinsics", since = "1.24.0")]
+    #[rustc_const_unstable(feature = "const_ascii_ctype_on_intrinsics", issue = "68983")]
     #[inline]
-    pub fn is_ascii_uppercase(&self) -> bool {
+    pub const fn is_ascii_uppercase(&self) -> bool {
         matches!(*self, b'A'..=b'Z')
     }
 
@@ -4513,8 +4515,9 @@ impl u8 {
     /// assert!(!esc.is_ascii_lowercase());
     /// ```
     #[stable(feature = "ascii_ctype_on_intrinsics", since = "1.24.0")]
+    #[rustc_const_unstable(feature = "const_ascii_ctype_on_intrinsics", issue = "68983")]
     #[inline]
-    pub fn is_ascii_lowercase(&self) -> bool {
+    pub const fn is_ascii_lowercase(&self) -> bool {
         matches!(*self, b'a'..=b'z')
     }
 
@@ -4548,8 +4551,9 @@ impl u8 {
     /// assert!(!esc.is_ascii_alphanumeric());
     /// ```
     #[stable(feature = "ascii_ctype_on_intrinsics", since = "1.24.0")]
+    #[rustc_const_unstable(feature = "const_ascii_ctype_on_intrinsics", issue = "68983")]
     #[inline]
-    pub fn is_ascii_alphanumeric(&self) -> bool {
+    pub const fn is_ascii_alphanumeric(&self) -> bool {
         matches!(*self, b'0'..=b'9' | b'A'..=b'Z' | b'a'..=b'z')
     }
 
@@ -4580,8 +4584,9 @@ impl u8 {
     /// assert!(!esc.is_ascii_digit());
     /// ```
     #[stable(feature = "ascii_ctype_on_intrinsics", since = "1.24.0")]
+    #[rustc_const_unstable(feature = "const_ascii_ctype_on_intrinsics", issue = "68983")]
     #[inline]
-    pub fn is_ascii_digit(&self) -> bool {
+    pub const fn is_ascii_digit(&self) -> bool {
         matches!(*self, b'0'..=b'9')
     }
 
@@ -4615,8 +4620,9 @@ impl u8 {
     /// assert!(!esc.is_ascii_hexdigit());
     /// ```
     #[stable(feature = "ascii_ctype_on_intrinsics", since = "1.24.0")]
+    #[rustc_const_unstable(feature = "const_ascii_ctype_on_intrinsics", issue = "68983")]
     #[inline]
-    pub fn is_ascii_hexdigit(&self) -> bool {
+    pub const fn is_ascii_hexdigit(&self) -> bool {
         matches!(*self, b'0'..=b'9' | b'A'..=b'F' | b'a'..=b'f')
     }
 
@@ -4651,8 +4657,9 @@ impl u8 {
     /// assert!(!esc.is_ascii_punctuation());
     /// ```
     #[stable(feature = "ascii_ctype_on_intrinsics", since = "1.24.0")]
+    #[rustc_const_unstable(feature = "const_ascii_ctype_on_intrinsics", issue = "68983")]
     #[inline]
-    pub fn is_ascii_punctuation(&self) -> bool {
+    pub const fn is_ascii_punctuation(&self) -> bool {
         matches!(*self, b'!'..=b'/' | b':'..=b'@' | b'['..=b'`' | b'{'..=b'~')
     }
 
@@ -4683,8 +4690,9 @@ impl u8 {
     /// assert!(!esc.is_ascii_graphic());
     /// ```
     #[stable(feature = "ascii_ctype_on_intrinsics", since = "1.24.0")]
+    #[rustc_const_unstable(feature = "const_ascii_ctype_on_intrinsics", issue = "68983")]
     #[inline]
-    pub fn is_ascii_graphic(&self) -> bool {
+    pub const fn is_ascii_graphic(&self) -> bool {
         matches!(*self, b'!'..=b'~')
     }
 
@@ -4732,8 +4740,9 @@ impl u8 {
     /// assert!(!esc.is_ascii_whitespace());
     /// ```
     #[stable(feature = "ascii_ctype_on_intrinsics", since = "1.24.0")]
+    #[rustc_const_unstable(feature = "const_ascii_ctype_on_intrinsics", issue = "68983")]
     #[inline]
-    pub fn is_ascii_whitespace(&self) -> bool {
+    pub const fn is_ascii_whitespace(&self) -> bool {
         matches!(*self, b'\t' | b'\n' | b'\x0C' | b'\r' | b' ')
     }
 
@@ -4766,8 +4775,9 @@ impl u8 {
     /// assert!(esc.is_ascii_control());
     /// ```
     #[stable(feature = "ascii_ctype_on_intrinsics", since = "1.24.0")]
+    #[rustc_const_unstable(feature = "const_ascii_ctype_on_intrinsics", issue = "68983")]
     #[inline]
-    pub fn is_ascii_control(&self) -> bool {
+    pub const fn is_ascii_control(&self) -> bool {
         matches!(*self, b'\0'..=b'\x1F' | b'\x7F')
     }
 }
diff --git a/src/librustc/ty/query/on_disk_cache.rs b/src/librustc/ty/query/on_disk_cache.rs
index a81fe33831c96..01f684dc65c30 100644
--- a/src/librustc/ty/query/on_disk_cache.rs
+++ b/src/librustc/ty/query/on_disk_cache.rs
@@ -1053,8 +1053,10 @@ where
     Q: super::config::QueryDescription<'tcx, Value: Encodable>,
     E: 'a + TyEncoder,
 {
-    let desc = &format!("encode_query_results_for_{}", ::std::any::type_name::<Q>());
-    let _timer = tcx.sess.prof.extra_verbose_generic_activity(desc);
+    let _timer = tcx
+        .sess
+        .prof
+        .extra_verbose_generic_activity("encode_query_results_for", ::std::any::type_name::<Q>());
 
     let shards = Q::query_cache(tcx).lock_shards();
     assert!(shards.iter().all(|shard| shard.active.is_empty()));
diff --git a/src/librustc_codegen_llvm/back/lto.rs b/src/librustc_codegen_llvm/back/lto.rs
index b5895b53698dc..76a6ffbb1c5b2 100644
--- a/src/librustc_codegen_llvm/back/lto.rs
+++ b/src/librustc_codegen_llvm/back/lto.rs
@@ -110,23 +110,21 @@ fn prepare_lto(
                 symbol_white_list.extend(exported_symbols[&cnum].iter().filter_map(symbol_filter));
             }
 
-            let _timer = cgcx.prof.generic_activity("LLVM_lto_load_upstream_bitcode");
             let archive = ArchiveRO::open(&path).expect("wanted an rlib");
             let bytecodes = archive
                 .iter()
                 .filter_map(|child| child.ok().and_then(|c| c.name().map(|name| (name, c))))
                 .filter(|&(name, _)| name.ends_with(RLIB_BYTECODE_EXTENSION));
             for (name, data) in bytecodes {
+                let _timer =
+                    cgcx.prof.generic_activity_with_arg("LLVM_lto_load_upstream_bitcode", name);
                 info!("adding bytecode {}", name);
                 let bc_encoded = data.data();
 
-                let (bc, id) = cgcx
-                    .prof
-                    .extra_verbose_generic_activity(&format!("decode {}", name))
-                    .run(|| match DecodedBytecode::new(bc_encoded) {
-                        Ok(b) => Ok((b.bytecode(), b.identifier().to_string())),
-                        Err(e) => Err(diag_handler.fatal(&e)),
-                    })?;
+                let (bc, id) = match DecodedBytecode::new(bc_encoded) {
+                    Ok(b) => Ok((b.bytecode(), b.identifier().to_string())),
+                    Err(e) => Err(diag_handler.fatal(&e)),
+                }?;
                 let bc = SerializedModule::FromRlib(bc);
                 upstream_modules.push((bc, CString::new(id).unwrap()));
             }
@@ -281,14 +279,14 @@ fn fat_lto(
         // save and persist everything with the original module.
         let mut linker = Linker::new(llmod);
         for (bc_decoded, name) in serialized_modules {
-            let _timer = cgcx.prof.generic_activity("LLVM_fat_lto_link_module");
+            let _timer = cgcx
+                .prof
+                .generic_activity_with_arg("LLVM_fat_lto_link_module", format!("{:?}", name));
             info!("linking {:?}", name);
-            cgcx.prof.extra_verbose_generic_activity(&format!("ll link {:?}", name)).run(|| {
-                let data = bc_decoded.data();
-                linker.add(&data).map_err(|()| {
-                    let msg = format!("failed to load bc of {:?}", name);
-                    write::llvm_err(&diag_handler, &msg)
-                })
+            let data = bc_decoded.data();
+            linker.add(&data).map_err(|()| {
+                let msg = format!("failed to load bc of {:?}", name);
+                write::llvm_err(&diag_handler, &msg)
             })?;
             serialized_bitcode.push(bc_decoded);
         }
@@ -577,6 +575,8 @@ pub(crate) fn run_pass_manager(
     config: &ModuleConfig,
     thin: bool,
 ) {
+    let _timer = cgcx.prof.extra_verbose_generic_activity("LLVM_lto_optimize", &module.name[..]);
+
     // Now we have one massive module inside of llmod. Time to run the
     // LTO-specific optimization passes that LLVM provides.
     //
@@ -634,9 +634,7 @@ pub(crate) fn run_pass_manager(
             llvm::LLVMRustAddPass(pm, pass.unwrap());
         }
 
-        cgcx.prof
-            .extra_verbose_generic_activity("LTO_passes")
-            .run(|| llvm::LLVMRunPassManager(pm, module.module_llvm.llmod()));
+        llvm::LLVMRunPassManager(pm, module.module_llvm.llmod());
 
         llvm::LLVMDisposePassManager(pm);
     }
@@ -760,7 +758,9 @@ pub unsafe fn optimize_thin_module(
         // Like with "fat" LTO, get some better optimizations if landing pads
         // are disabled by removing all landing pads.
         if cgcx.no_landing_pads {
-            let _timer = cgcx.prof.generic_activity("LLVM_thin_lto_remove_landing_pads");
+            let _timer = cgcx
+                .prof
+                .generic_activity_with_arg("LLVM_thin_lto_remove_landing_pads", thin_module.name());
             llvm::LLVMRustMarkAllFunctionsNounwind(llmod);
             save_temp_bitcode(&cgcx, &module, "thin-lto-after-nounwind");
         }
@@ -774,7 +774,8 @@ pub unsafe fn optimize_thin_module(
         // You can find some more comments about these functions in the LLVM
         // bindings we've got (currently `PassWrapper.cpp`)
         {
-            let _timer = cgcx.prof.generic_activity("LLVM_thin_lto_rename");
+            let _timer =
+                cgcx.prof.generic_activity_with_arg("LLVM_thin_lto_rename", thin_module.name());
             if !llvm::LLVMRustPrepareThinLTORename(thin_module.shared.data.0, llmod) {
                 let msg = "failed to prepare thin LTO module";
                 return Err(write::llvm_err(&diag_handler, msg));
@@ -783,7 +784,9 @@ pub unsafe fn optimize_thin_module(
         }
 
         {
-            let _timer = cgcx.prof.generic_activity("LLVM_thin_lto_resolve_weak");
+            let _timer = cgcx
+                .prof
+                .generic_activity_with_arg("LLVM_thin_lto_resolve_weak", thin_module.name());
             if !llvm::LLVMRustPrepareThinLTOResolveWeak(thin_module.shared.data.0, llmod) {
                 let msg = "failed to prepare thin LTO module";
                 return Err(write::llvm_err(&diag_handler, msg));
@@ -792,7 +795,9 @@ pub unsafe fn optimize_thin_module(
         }
 
         {
-            let _timer = cgcx.prof.generic_activity("LLVM_thin_lto_internalize");
+            let _timer = cgcx
+                .prof
+                .generic_activity_with_arg("LLVM_thin_lto_internalize", thin_module.name());
             if !llvm::LLVMRustPrepareThinLTOInternalize(thin_module.shared.data.0, llmod) {
                 let msg = "failed to prepare thin LTO module";
                 return Err(write::llvm_err(&diag_handler, msg));
@@ -801,7 +806,8 @@ pub unsafe fn optimize_thin_module(
         }
 
         {
-            let _timer = cgcx.prof.generic_activity("LLVM_thin_lto_import");
+            let _timer =
+                cgcx.prof.generic_activity_with_arg("LLVM_thin_lto_import", thin_module.name());
             if !llvm::LLVMRustPrepareThinLTOImport(thin_module.shared.data.0, llmod) {
                 let msg = "failed to prepare thin LTO module";
                 return Err(write::llvm_err(&diag_handler, msg));
@@ -839,7 +845,9 @@ pub unsafe fn optimize_thin_module(
         // so it appears). Hopefully we can remove this once upstream bugs are
         // fixed in LLVM.
         {
-            let _timer = cgcx.prof.generic_activity("LLVM_thin_lto_patch_debuginfo");
+            let _timer = cgcx
+                .prof
+                .generic_activity_with_arg("LLVM_thin_lto_patch_debuginfo", thin_module.name());
             llvm::LLVMRustThinLTOPatchDICompileUnit(llmod, cu1);
             save_temp_bitcode(cgcx, &module, "thin-lto-after-patch");
         }
@@ -850,7 +858,6 @@ pub unsafe fn optimize_thin_module(
         // populate a thin-specific pass manager, which presumably LLVM treats a
         // little differently.
         {
-            let _timer = cgcx.prof.generic_activity("LLVM_thin_lto_optimize");
             info!("running thin lto passes over {}", module.name);
             let config = cgcx.config(module.kind);
             run_pass_manager(cgcx, &module, config, true);
diff --git a/src/librustc_codegen_llvm/back/write.rs b/src/librustc_codegen_llvm/back/write.rs
index 4be7b84660d09..7dd57da90c3d6 100644
--- a/src/librustc_codegen_llvm/back/write.rs
+++ b/src/librustc_codegen_llvm/back/write.rs
@@ -310,7 +310,7 @@ pub(crate) unsafe fn optimize(
     module: &ModuleCodegen<ModuleLlvm>,
     config: &ModuleConfig,
 ) -> Result<(), FatalError> {
-    let _timer = cgcx.prof.generic_activity("LLVM_module_optimize");
+    let _timer = cgcx.prof.generic_activity_with_arg("LLVM_module_optimize", &module.name[..]);
 
     let llmod = module.module_llvm.llmod();
     let llcx = &*module.module_llvm.llcx;
@@ -424,23 +424,17 @@ pub(crate) unsafe fn optimize(
 
         // Finally, run the actual optimization passes
         {
-            let _timer = cgcx.prof.generic_activity("LLVM_module_optimize_function_passes");
-            let desc = &format!("llvm function passes [{}]", module_name.unwrap());
-            let _timer = if config.time_module {
-                Some(cgcx.prof.extra_verbose_generic_activity(desc))
-            } else {
-                None
-            };
+            let _timer = cgcx.prof.extra_verbose_generic_activity(
+                "LLVM_module_optimize_function_passes",
+                &module.name[..],
+            );
             llvm::LLVMRustRunFunctionPassManager(fpm, llmod);
         }
         {
-            let _timer = cgcx.prof.generic_activity("LLVM_module_optimize_module_passes");
-            let desc = &format!("llvm module passes [{}]", module_name.unwrap());
-            let _timer = if config.time_module {
-                Some(cgcx.prof.extra_verbose_generic_activity(desc))
-            } else {
-                None
-            };
+            let _timer = cgcx.prof.extra_verbose_generic_activity(
+                "LLVM_module_optimize_module_passes",
+                &module.name[..],
+            );
             llvm::LLVMRunPassManager(mpm, llmod);
         }
 
@@ -480,7 +474,7 @@ pub(crate) unsafe fn codegen(
     module: ModuleCodegen<ModuleLlvm>,
     config: &ModuleConfig,
 ) -> Result<CompiledModule, FatalError> {
-    let _timer = cgcx.prof.generic_activity("LLVM_module_codegen");
+    let _timer = cgcx.prof.generic_activity_with_arg("LLVM_module_codegen", &module.name[..]);
     {
         let llmod = module.module_llvm.llmod();
         let llcx = &*module.module_llvm.llcx;
@@ -533,12 +527,17 @@ pub(crate) unsafe fn codegen(
         let obj_out = cgcx.output_filenames.temp_path(OutputType::Object, module_name);
 
         if write_bc || config.emit_bc_compressed || config.embed_bitcode {
-            let _timer = cgcx.prof.generic_activity("LLVM_module_codegen_make_bitcode");
+            let _timer = cgcx
+                .prof
+                .generic_activity_with_arg("LLVM_module_codegen_make_bitcode", &module.name[..]);
             let thin = ThinBuffer::new(llmod);
             let data = thin.data();
 
             if write_bc {
-                let _timer = cgcx.prof.generic_activity("LLVM_module_codegen_emit_bitcode");
+                let _timer = cgcx.prof.generic_activity_with_arg(
+                    "LLVM_module_codegen_emit_bitcode",
+                    &module.name[..],
+                );
                 if let Err(e) = fs::write(&bc_out, data) {
                     let msg = format!("failed to write bytecode to {}: {}", bc_out.display(), e);
                     diag_handler.err(&msg);
@@ -546,13 +545,18 @@ pub(crate) unsafe fn codegen(
             }
 
             if config.embed_bitcode {
-                let _timer = cgcx.prof.generic_activity("LLVM_module_codegen_embed_bitcode");
+                let _timer = cgcx.prof.generic_activity_with_arg(
+                    "LLVM_module_codegen_embed_bitcode",
+                    &module.name[..],
+                );
                 embed_bitcode(cgcx, llcx, llmod, Some(data));
             }
 
             if config.emit_bc_compressed {
-                let _timer =
-                    cgcx.prof.generic_activity("LLVM_module_codegen_emit_compressed_bitcode");
+                let _timer = cgcx.prof.generic_activity_with_arg(
+                    "LLVM_module_codegen_emit_compressed_bitcode",
+                    &module.name[..],
+                );
                 let dst = bc_out.with_extension(RLIB_BYTECODE_EXTENSION);
                 let data = bytecode::encode(&module.name, data);
                 if let Err(e) = fs::write(&dst, data) {
@@ -565,15 +569,10 @@ pub(crate) unsafe fn codegen(
         }
 
         {
-            let desc = &format!("codegen passes [{}]", module_name.unwrap());
-            let _timer = if config.time_module {
-                Some(cgcx.prof.extra_verbose_generic_activity(desc))
-            } else {
-                None
-            };
-
             if config.emit_ir {
-                let _timer = cgcx.prof.generic_activity("LLVM_module_codegen_emit_ir");
+                let _timer = cgcx
+                    .prof
+                    .generic_activity_with_arg("LLVM_module_codegen_emit_ir", &module.name[..]);
                 let out = cgcx.output_filenames.temp_path(OutputType::LlvmAssembly, module_name);
                 let out_c = path_to_c_string(&out);
 
@@ -618,7 +617,9 @@ pub(crate) unsafe fn codegen(
             }
 
             if config.emit_asm || asm_to_obj {
-                let _timer = cgcx.prof.generic_activity("LLVM_module_codegen_emit_asm");
+                let _timer = cgcx
+                    .prof
+                    .generic_activity_with_arg("LLVM_module_codegen_emit_asm", &module.name[..]);
                 let path = cgcx.output_filenames.temp_path(OutputType::Assembly, module_name);
 
                 // We can't use the same module for asm and binary output, because that triggers
@@ -638,7 +639,9 @@ pub(crate) unsafe fn codegen(
             }
 
             if write_obj {
-                let _timer = cgcx.prof.generic_activity("LLVM_module_codegen_emit_obj");
+                let _timer = cgcx
+                    .prof
+                    .generic_activity_with_arg("LLVM_module_codegen_emit_obj", &module.name[..]);
                 with_codegen(tm, llmod, config.no_builtins, |cpm| {
                     write_output_file(
                         diag_handler,
@@ -650,7 +653,9 @@ pub(crate) unsafe fn codegen(
                     )
                 })?;
             } else if asm_to_obj {
-                let _timer = cgcx.prof.generic_activity("LLVM_module_codegen_asm_to_obj");
+                let _timer = cgcx
+                    .prof
+                    .generic_activity_with_arg("LLVM_module_codegen_asm_to_obj", &module.name[..]);
                 let assembly = cgcx.output_filenames.temp_path(OutputType::Assembly, module_name);
                 run_assembler(cgcx, diag_handler, &assembly, &obj_out);
 
diff --git a/src/librustc_codegen_ssa/back/link.rs b/src/librustc_codegen_ssa/back/link.rs
index 4f05aac089867..d3b8f7ea33fab 100644
--- a/src/librustc_codegen_ssa/back/link.rs
+++ b/src/librustc_codegen_ssa/back/link.rs
@@ -1648,7 +1648,7 @@ fn add_upstream_rust_crates<'a, B: ArchiveBuilder<'a>>(
         let name = cratepath.file_name().unwrap().to_str().unwrap();
         let name = &name[3..name.len() - 5]; // chop off lib/.rlib
 
-        sess.prof.extra_verbose_generic_activity(&format!("altering {}.rlib", name)).run(|| {
+        sess.prof.generic_activity_with_arg("link_altering_rlib", name).run(|| {
             let mut archive = <B as ArchiveBuilder>::new(sess, &dst, Some(cratepath));
             archive.update_symbols();
 
diff --git a/src/librustc_codegen_ssa/back/write.rs b/src/librustc_codegen_ssa/back/write.rs
index 841827d15fef4..9905b3a56c0fa 100644
--- a/src/librustc_codegen_ssa/back/write.rs
+++ b/src/librustc_codegen_ssa/back/write.rs
@@ -21,6 +21,7 @@ use rustc::session::Session;
 use rustc::ty::TyCtxt;
 use rustc_data_structures::fx::FxHashMap;
 use rustc_data_structures::profiling::SelfProfilerRef;
+use rustc_data_structures::profiling::TimingGuard;
 use rustc_data_structures::profiling::VerboseTimingGuard;
 use rustc_data_structures::svh::Svh;
 use rustc_data_structures::sync::Lrc;
@@ -691,11 +692,17 @@ impl<B: WriteBackendMethods> WorkItem<B> {
         }
     }
 
-    fn profiling_event_id(&self) -> &'static str {
+    fn start_profiling<'a>(&self, cgcx: &'a CodegenContext<B>) -> TimingGuard<'a> {
         match *self {
-            WorkItem::Optimize(_) => "codegen_module_optimize",
-            WorkItem::CopyPostLtoArtifacts(_) => "codegen_copy_artifacts_from_incr_cache",
-            WorkItem::LTO(_) => "codegen_module_perform_lto",
+            WorkItem::Optimize(ref m) => {
+                cgcx.prof.generic_activity_with_arg("codegen_module_optimize", &m.name[..])
+            }
+            WorkItem::CopyPostLtoArtifacts(ref m) => cgcx
+                .prof
+                .generic_activity_with_arg("codegen_copy_artifacts_from_incr_cache", &m.name[..]),
+            WorkItem::LTO(ref m) => {
+                cgcx.prof.generic_activity_with_arg("codegen_module_perform_lto", m.name())
+            }
         }
     }
 }
@@ -1520,7 +1527,7 @@ fn start_executing_work<B: ExtraBackendMethods>(
         llvm_start_time: &mut Option<VerboseTimingGuard<'a>>,
     ) {
         if config.time_module && llvm_start_time.is_none() {
-            *llvm_start_time = Some(prof.extra_verbose_generic_activity("LLVM_passes"));
+            *llvm_start_time = Some(prof.extra_verbose_generic_activity("LLVM_passes", "crate"));
         }
     }
 }
@@ -1575,7 +1582,7 @@ fn spawn_work<B: ExtraBackendMethods>(cgcx: CodegenContext<B>, work: WorkItem<B>
         // as a diagnostic was already sent off to the main thread - just
         // surface that there was an error in this worker.
         bomb.result = {
-            let _prof_timer = cgcx.prof.generic_activity(work.profiling_event_id());
+            let _prof_timer = work.start_profiling(&cgcx);
             Some(execute_work_item(&cgcx, work))
         };
     });
diff --git a/src/librustc_data_structures/profiling.rs b/src/librustc_data_structures/profiling.rs
index 90f74328a1d51..debda9f0a0a24 100644
--- a/src/librustc_data_structures/profiling.rs
+++ b/src/librustc_data_structures/profiling.rs
@@ -83,6 +83,9 @@
 
 use crate::fx::FxHashMap;
 
+use std::borrow::Borrow;
+use std::collections::hash_map::Entry;
+use std::convert::Into;
 use std::error::Error;
 use std::fs;
 use std::path::Path;
@@ -123,11 +126,14 @@ bitflags::bitflags! {
         const INCR_CACHE_LOADS   = 1 << 4;
 
         const QUERY_KEYS         = 1 << 5;
+        const FUNCTION_ARGS      = 1 << 6;
 
         const DEFAULT = Self::GENERIC_ACTIVITIES.bits |
                         Self::QUERY_PROVIDERS.bits |
                         Self::QUERY_BLOCKED.bits |
                         Self::INCR_CACHE_LOADS.bits;
+
+        const ARGS = Self::QUERY_KEYS.bits | Self::FUNCTION_ARGS.bits;
     }
 }
 
@@ -142,6 +148,8 @@ const EVENT_FILTERS_BY_NAME: &[(&str, EventFilter)] = &[
     ("query-blocked", EventFilter::QUERY_BLOCKED),
     ("incr-cache-load", EventFilter::INCR_CACHE_LOADS),
     ("query-keys", EventFilter::QUERY_KEYS),
+    ("function-args", EventFilter::FUNCTION_ARGS),
+    ("args", EventFilter::ARGS),
 ];
 
 /// Something that uniquely identifies a query invocation.
@@ -216,43 +224,68 @@ impl SelfProfilerRef {
     /// VerboseTimingGuard returned from this call is dropped. In addition to recording
     /// a measureme event, "verbose" generic activities also print a timing entry to
     /// stdout if the compiler is invoked with -Ztime or -Ztime-passes.
-    #[inline(always)]
     pub fn verbose_generic_activity<'a>(
         &'a self,
-        event_id: &'static str,
+        event_label: &'static str,
     ) -> VerboseTimingGuard<'a> {
-        VerboseTimingGuard::start(
-            event_id,
-            self.print_verbose_generic_activities,
-            self.generic_activity(event_id),
-        )
+        let message =
+            if self.print_verbose_generic_activities { Some(event_label.to_owned()) } else { None };
+
+        VerboseTimingGuard::start(message, self.generic_activity(event_label))
     }
 
     /// Start profiling a extra verbose generic activity. Profiling continues until the
     /// VerboseTimingGuard returned from this call is dropped. In addition to recording
     /// a measureme event, "extra verbose" generic activities also print a timing entry to
     /// stdout if the compiler is invoked with -Ztime-passes.
-    #[inline(always)]
-    pub fn extra_verbose_generic_activity<'a>(
+    pub fn extra_verbose_generic_activity<'a, A>(
         &'a self,
-        event_id: &'a str,
-    ) -> VerboseTimingGuard<'a> {
-        // FIXME: This does not yet emit a measureme event
-        // because callers encode arguments into `event_id`.
-        VerboseTimingGuard::start(
-            event_id,
-            self.print_extra_verbose_generic_activities,
-            TimingGuard::none(),
-        )
+        event_label: &'static str,
+        event_arg: A,
+    ) -> VerboseTimingGuard<'a>
+    where
+        A: Borrow<str> + Into<String>,
+    {
+        let message = if self.print_extra_verbose_generic_activities {
+            Some(format!("{}({})", event_label, event_arg.borrow()))
+        } else {
+            None
+        };
+
+        VerboseTimingGuard::start(message, self.generic_activity_with_arg(event_label, event_arg))
+    }
+
+    /// Start profiling a generic activity. Profiling continues until the
+    /// TimingGuard returned from this call is dropped.
+    #[inline(always)]
+    pub fn generic_activity(&self, event_label: &'static str) -> TimingGuard<'_> {
+        self.exec(EventFilter::GENERIC_ACTIVITIES, |profiler| {
+            let event_label = profiler.get_or_alloc_cached_string(event_label);
+            let event_id = EventId::from_label(event_label);
+            TimingGuard::start(profiler, profiler.generic_activity_event_kind, event_id)
+        })
     }
 
     /// Start profiling a generic activity. Profiling continues until the
     /// TimingGuard returned from this call is dropped.
     #[inline(always)]
-    pub fn generic_activity(&self, event_id: &'static str) -> TimingGuard<'_> {
+    pub fn generic_activity_with_arg<A>(
+        &self,
+        event_label: &'static str,
+        event_arg: A,
+    ) -> TimingGuard<'_>
+    where
+        A: Borrow<str> + Into<String>,
+    {
         self.exec(EventFilter::GENERIC_ACTIVITIES, |profiler| {
-            let event_id = profiler.get_or_alloc_cached_string(event_id);
-            let event_id = EventId::from_label(event_id);
+            let builder = EventIdBuilder::new(&profiler.profiler);
+            let event_label = profiler.get_or_alloc_cached_string(event_label);
+            let event_id = if profiler.event_filter_mask.contains(EventFilter::FUNCTION_ARGS) {
+                let event_arg = profiler.get_or_alloc_cached_string(event_arg);
+                builder.from_label_and_arg(event_label, event_arg)
+            } else {
+                builder.from_label(event_label)
+            };
             TimingGuard::start(profiler, profiler.generic_activity_event_kind, event_id)
         })
     }
@@ -337,7 +370,7 @@ pub struct SelfProfiler {
     profiler: Profiler,
     event_filter_mask: EventFilter,
 
-    string_cache: RwLock<FxHashMap<&'static str, StringId>>,
+    string_cache: RwLock<FxHashMap<String, StringId>>,
 
     query_event_kind: StringId,
     generic_activity_event_kind: StringId,
@@ -419,13 +452,16 @@ impl SelfProfiler {
     /// Gets a `StringId` for the given string. This method makes sure that
     /// any strings going through it will only be allocated once in the
     /// profiling data.
-    pub fn get_or_alloc_cached_string(&self, s: &'static str) -> StringId {
+    pub fn get_or_alloc_cached_string<A>(&self, s: A) -> StringId
+    where
+        A: Borrow<str> + Into<String>,
+    {
         // Only acquire a read-lock first since we assume that the string is
         // already present in the common case.
         {
             let string_cache = self.string_cache.read();
 
-            if let Some(&id) = string_cache.get(s) {
+            if let Some(&id) = string_cache.get(s.borrow()) {
                 return id;
             }
         }
@@ -433,7 +469,13 @@ impl SelfProfiler {
         let mut string_cache = self.string_cache.write();
         // Check if the string has already been added in the small time window
         // between dropping the read lock and acquiring the write lock.
-        *string_cache.entry(s).or_insert_with(|| self.profiler.alloc_string(s))
+        match string_cache.entry(s.into()) {
+            Entry::Occupied(e) => *e.get(),
+            Entry::Vacant(e) => {
+                let string_id = self.profiler.alloc_string(&e.key()[..]);
+                *e.insert(string_id)
+            }
+        }
     }
 
     pub fn map_query_invocation_id_to_string(&self, from: QueryInvocationId, to: StringId) {
@@ -498,18 +540,13 @@ impl<'a> TimingGuard<'a> {
 
 #[must_use]
 pub struct VerboseTimingGuard<'a> {
-    event_id: &'a str,
-    start: Option<Instant>,
+    start_and_message: Option<(Instant, String)>,
     _guard: TimingGuard<'a>,
 }
 
 impl<'a> VerboseTimingGuard<'a> {
-    pub fn start(event_id: &'a str, verbose: bool, _guard: TimingGuard<'a>) -> Self {
-        VerboseTimingGuard {
-            event_id,
-            _guard,
-            start: if unlikely!(verbose) { Some(Instant::now()) } else { None },
-        }
+    pub fn start(message: Option<String>, _guard: TimingGuard<'a>) -> Self {
+        VerboseTimingGuard { _guard, start_and_message: message.map(|msg| (Instant::now(), msg)) }
     }
 
     #[inline(always)]
@@ -521,7 +558,9 @@ impl<'a> VerboseTimingGuard<'a> {
 
 impl Drop for VerboseTimingGuard<'_> {
     fn drop(&mut self) {
-        self.start.map(|start| print_time_passes_entry(true, self.event_id, start.elapsed()));
+        if let Some((start, ref message)) = self.start_and_message {
+            print_time_passes_entry(true, &message[..], start.elapsed());
+        }
     }
 }
 
diff --git a/src/librustc_error_codes/error_codes.rs b/src/librustc_error_codes/error_codes.rs
index c3d9ed088981d..ba43b29538d50 100644
--- a/src/librustc_error_codes/error_codes.rs
+++ b/src/librustc_error_codes/error_codes.rs
@@ -353,6 +353,7 @@ E0631: include_str!("./error_codes/E0631.md"),
 E0633: include_str!("./error_codes/E0633.md"),
 E0635: include_str!("./error_codes/E0635.md"),
 E0636: include_str!("./error_codes/E0636.md"),
+E0637: include_str!("./error_codes/E0637.md"),
 E0638: include_str!("./error_codes/E0638.md"),
 E0639: include_str!("./error_codes/E0639.md"),
 E0641: include_str!("./error_codes/E0641.md"),
@@ -584,7 +585,6 @@ E0746: include_str!("./error_codes/E0746.md"),
     E0632, // cannot provide explicit generic arguments when `impl Trait` is
            // used in argument position
     E0634, // type has conflicting packed representaton hints
-    E0637, // "'_" is not a valid lifetime bound
     E0640, // infer outlives requirements
 //  E0645, // trait aliases not finished
     E0657, // `impl Trait` can only capture lifetimes bound at the fn level
diff --git a/src/librustc_error_codes/error_codes/E0275.md b/src/librustc_error_codes/error_codes/E0275.md
index 836a9a95fa17b..2d12fcea4cafc 100644
--- a/src/librustc_error_codes/error_codes/E0275.md
+++ b/src/librustc_error_codes/error_codes/E0275.md
@@ -1,8 +1,6 @@
-This error occurs when there was a recursive trait requirement that overflowed
-before it could be evaluated. Often this means that there is unbounded
-recursion in resolving some type bounds.
+An evaluation of a trait requirement overflowed.
 
-For example, in the following code:
+Erroneous code example:
 
 ```compile_fail,E0275
 trait Foo {}
@@ -12,6 +10,10 @@ struct Bar<T>(T);
 impl<T> Foo for T where Bar<T>: Foo {}
 ```
 
+This error occurs when there was a recursive trait requirement that overflowed
+before it could be evaluated. This often means that there is an unbounded
+recursion in resolving some type bounds.
+
 To determine if a `T` is `Foo`, we need to check if `Bar<T>` is `Foo`. However,
 to do this check, we need to determine that `Bar<Bar<T>>` is `Foo`. To
 determine this, we check if `Bar<Bar<Bar<T>>>` is `Foo`, and so on. This is
diff --git a/src/librustc_error_codes/error_codes/E0283.md b/src/librustc_error_codes/error_codes/E0283.md
index 075f8b2e3bb80..6885f9a486d3d 100644
--- a/src/librustc_error_codes/error_codes/E0283.md
+++ b/src/librustc_error_codes/error_codes/E0283.md
@@ -1,7 +1,6 @@
-This error occurs when the compiler doesn't have enough information
-to unambiguously choose an implementation.
+An implementation cannot be chosen unambiguously because of lack of information.
 
-For example:
+Erroneous code example:
 
 ```compile_fail,E0283
 trait Generator {
@@ -27,7 +26,9 @@ fn main() {
 }
 ```
 
-To resolve this error use the concrete type:
+This error can be solved by adding type annotations that provide the missing
+information to the compiler. In this case, the solution is to use a concrete
+type:
 
 ```
 trait Generator {
diff --git a/src/librustc_error_codes/error_codes/E0637.md b/src/librustc_error_codes/error_codes/E0637.md
new file mode 100644
index 0000000000000..e114d3d0f94ae
--- /dev/null
+++ b/src/librustc_error_codes/error_codes/E0637.md
@@ -0,0 +1,32 @@
+An underscore `_` character has been used as the identifier for a lifetime.
+
+Erroneous example:
+```compile_fail,E0106,E0637
+fn longest<'_>(str1: &'_ str, str2: &'_ str) -> &'_ str {
+         //^^ `'_` is a reserved lifetime name
+    if str1.len() > str2.len() {
+        str1
+    } else {
+        str2
+    }
+}
+```
+`'_`, cannot be used as a lifetime identifier because it is a reserved for the
+anonymous lifetime. To fix this, use a lowercase letter such as 'a, or a series
+of lowercase letters such as `'foo`.  For more information, see [the
+book][bk-no].  For more information on using the anonymous lifetime in rust
+nightly, see [the nightly book][bk-al].
+
+Corrected example:
+```
+fn longest<'a>(str1: &'a str, str2: &'a str) -> &'a str {
+    if str1.len() > str2.len() {
+        str1
+    } else {
+        str2
+    }
+}
+```
+
+[bk-no]: https://doc.rust-lang.org/book/appendix-02-operators.html#non-operator-symbols
+[bk-al]: https://doc.rust-lang.org/nightly/edition-guide/rust-2018/ownership-and-lifetimes/the-anonymous-lifetime.html
diff --git a/src/librustc_lint/early.rs b/src/librustc_lint/early.rs
index 542cbea0c954a..27781eb41d28d 100644
--- a/src/librustc_lint/early.rs
+++ b/src/librustc_lint/early.rs
@@ -342,10 +342,8 @@ pub fn check_ast_crate<T: EarlyLintPass>(
         }
     } else {
         for pass in &mut passes {
-            buffered = sess
-                .prof
-                .extra_verbose_generic_activity(&format!("running lint: {}", pass.name()))
-                .run(|| {
+            buffered =
+                sess.prof.extra_verbose_generic_activity("run_lint", pass.name()).run(|| {
                     early_lint_crate(
                         sess,
                         lint_store,
diff --git a/src/librustc_lint/late.rs b/src/librustc_lint/late.rs
index 30a3788377508..b3d5cdf15c933 100644
--- a/src/librustc_lint/late.rs
+++ b/src/librustc_lint/late.rs
@@ -441,27 +441,20 @@ fn late_lint_crate<'tcx, T: for<'a> LateLintPass<'a, 'tcx>>(tcx: TyCtxt<'tcx>, b
         late_lint_pass_crate(tcx, builtin_lints);
     } else {
         for pass in &mut passes {
-            tcx.sess
-                .prof
-                .extra_verbose_generic_activity(&format!("running late lint: {}", pass.name()))
-                .run(|| {
-                    late_lint_pass_crate(tcx, LateLintPassObjects { lints: slice::from_mut(pass) });
-                });
+            tcx.sess.prof.extra_verbose_generic_activity("run_late_lint", pass.name()).run(|| {
+                late_lint_pass_crate(tcx, LateLintPassObjects { lints: slice::from_mut(pass) });
+            });
         }
 
         let mut passes: Vec<_> =
             unerased_lint_store(tcx).late_module_passes.iter().map(|pass| (pass)()).collect();
 
         for pass in &mut passes {
-            tcx.sess
-                .prof
-                .extra_verbose_generic_activity(&format!(
-                    "running late module lint: {}",
-                    pass.name()
-                ))
-                .run(|| {
+            tcx.sess.prof.extra_verbose_generic_activity("run_late_module_lint", pass.name()).run(
+                || {
                     late_lint_pass_crate(tcx, LateLintPassObjects { lints: slice::from_mut(pass) });
-                });
+                },
+            );
         }
     }
 }
diff --git a/src/librustc_parse/parser/ty.rs b/src/librustc_parse/parser/ty.rs
index 990661bf6b5b9..a573a1ee69976 100644
--- a/src/librustc_parse/parser/ty.rs
+++ b/src/librustc_parse/parser/ty.rs
@@ -214,7 +214,10 @@ impl<'a> Parser<'a> {
                     let path = match bounds.remove(0) {
                         GenericBound::Trait(pt, ..) => pt.trait_ref.path,
                         GenericBound::Outlives(..) => {
-                            self.span_bug(ty.span, "unexpected lifetime bound")
+                            return Err(self.struct_span_err(
+                                ty.span,
+                                "expected trait bound, not lifetime bound",
+                            ));
                         }
                     };
                     self.parse_remaining_bounds(Vec::new(), path, lo, true)
diff --git a/src/test/ui/const-generics/const-param-elided-lifetime.stderr b/src/test/ui/const-generics/const-param-elided-lifetime.stderr
index 93133c507fe40..6841d1fdf360b 100644
--- a/src/test/ui/const-generics/const-param-elided-lifetime.stderr
+++ b/src/test/ui/const-generics/const-param-elided-lifetime.stderr
@@ -38,3 +38,4 @@ LL | #![feature(const_generics)]
 
 error: aborting due to 5 previous errors
 
+For more information about this error, try `rustc --explain E0637`.
diff --git a/src/test/ui/consts/ascii_ctype.rs b/src/test/ui/consts/ascii_ctype.rs
new file mode 100644
index 0000000000000..25b270ee49cae
--- /dev/null
+++ b/src/test/ui/consts/ascii_ctype.rs
@@ -0,0 +1,55 @@
+// run-pass
+
+#![feature(const_ascii_ctype_on_intrinsics)]
+
+macro_rules! suite {
+    ( $( $fn:ident => [$a:ident, $A:ident, $nine:ident, $dot:ident, $space:ident]; )* ) => {
+        $(
+            mod $fn {
+                const CHAR_A_LOWER: bool = 'a'.$fn();
+                const CHAR_A_UPPER: bool = 'A'.$fn();
+                const CHAR_NINE: bool = '9'.$fn();
+                const CHAR_DOT: bool = '.'.$fn();
+                const CHAR_SPACE: bool = ' '.$fn();
+
+                const U8_A_LOWER: bool = b'a'.$fn();
+                const U8_A_UPPER: bool = b'A'.$fn();
+                const U8_NINE: bool = b'9'.$fn();
+                const U8_DOT: bool = b'.'.$fn();
+                const U8_SPACE: bool = b' '.$fn();
+
+                pub fn run() {
+                    assert_eq!(CHAR_A_LOWER, $a);
+                    assert_eq!(CHAR_A_UPPER, $A);
+                    assert_eq!(CHAR_NINE, $nine);
+                    assert_eq!(CHAR_DOT, $dot);
+                    assert_eq!(CHAR_SPACE, $space);
+
+                    assert_eq!(U8_A_LOWER, $a);
+                    assert_eq!(U8_A_UPPER, $A);
+                    assert_eq!(U8_NINE, $nine);
+                    assert_eq!(U8_DOT, $dot);
+                    assert_eq!(U8_SPACE, $space);
+                }
+            }
+        )*
+
+        fn main() {
+            $( $fn::run(); )*
+        }
+    }
+}
+
+suite! {
+    //                        'a'    'A'    '9'    '.'    ' '
+    is_ascii_alphabetic   => [true,  true,  false, false, false];
+    is_ascii_uppercase    => [false, true,  false, false, false];
+    is_ascii_lowercase    => [true,  false, false, false, false];
+    is_ascii_alphanumeric => [true,  true,  true,  false, false];
+    is_ascii_digit        => [false, false, true,  false, false];
+    is_ascii_hexdigit     => [true,  true,  true,  false, false];
+    is_ascii_punctuation  => [false, false, false, true,  false];
+    is_ascii_graphic      => [true,  true,  true,  true,  false];
+    is_ascii_whitespace   => [false, false, false, false, true];
+    is_ascii_control      => [false, false, false, false, false];
+}
diff --git a/src/test/ui/error-codes/E0637.stderr b/src/test/ui/error-codes/E0637.stderr
index 9c3ca87ed7e64..d19ebfd15a52c 100644
--- a/src/test/ui/error-codes/E0637.stderr
+++ b/src/test/ui/error-codes/E0637.stderr
@@ -18,3 +18,4 @@ LL | impl<'a: '_> Bar<'a> {
 
 error: aborting due to 3 previous errors
 
+For more information about this error, try `rustc --explain E0637`.
diff --git a/src/test/ui/generics/issue-65285-incorrect-explicit-lifetime-name-needed.stderr b/src/test/ui/generics/issue-65285-incorrect-explicit-lifetime-name-needed.stderr
index 8720288b53e58..9f410c0dbbbd2 100644
--- a/src/test/ui/generics/issue-65285-incorrect-explicit-lifetime-name-needed.stderr
+++ b/src/test/ui/generics/issue-65285-incorrect-explicit-lifetime-name-needed.stderr
@@ -18,4 +18,5 @@ LL | fn bar<'b, L: X<&'b Nested<i32>>>(){}
 
 error: aborting due to 3 previous errors
 
-For more information about this error, try `rustc --explain E0106`.
+Some errors have detailed explanations: E0106, E0637.
+For more information about an error, try `rustc --explain E0106`.
diff --git a/src/test/ui/parser/issue-68890.rs b/src/test/ui/parser/issue-68890.rs
new file mode 100644
index 0000000000000..a7c5a5e13008a
--- /dev/null
+++ b/src/test/ui/parser/issue-68890.rs
@@ -0,0 +1,4 @@
+enum e{A((?'a a+?+l))}
+//~^ ERROR `?` may only modify trait bounds, not lifetime bounds
+//~| ERROR expected one of `)`, `+`, or `,`
+//~| ERROR expected trait bound, not lifetime bound
diff --git a/src/test/ui/parser/issue-68890.stderr b/src/test/ui/parser/issue-68890.stderr
new file mode 100644
index 0000000000000..9bb8761b67b5f
--- /dev/null
+++ b/src/test/ui/parser/issue-68890.stderr
@@ -0,0 +1,20 @@
+error: `?` may only modify trait bounds, not lifetime bounds
+  --> $DIR/issue-68890.rs:1:11
+   |
+LL | enum e{A((?'a a+?+l))}
+   |           ^
+
+error: expected one of `)`, `+`, or `,`, found `a`
+  --> $DIR/issue-68890.rs:1:15
+   |
+LL | enum e{A((?'a a+?+l))}
+   |               ^ expected one of `)`, `+`, or `,`
+
+error: expected trait bound, not lifetime bound
+  --> $DIR/issue-68890.rs:1:11
+   |
+LL | enum e{A((?'a a+?+l))}
+   |           ^^^
+
+error: aborting due to 3 previous errors
+
diff --git a/src/test/ui/underscore-lifetime/in-binder.stderr b/src/test/ui/underscore-lifetime/in-binder.stderr
index 1b936dd9aec2f..fcd7eddb57605 100644
--- a/src/test/ui/underscore-lifetime/in-binder.stderr
+++ b/src/test/ui/underscore-lifetime/in-binder.stderr
@@ -36,3 +36,4 @@ LL | fn foo<'_>() {
 
 error: aborting due to 6 previous errors
 
+For more information about this error, try `rustc --explain E0637`.
diff --git a/src/test/ui/underscore-lifetime/underscore-lifetime-binders.stderr b/src/test/ui/underscore-lifetime/underscore-lifetime-binders.stderr
index c7cda38e47691..ada4551baefff 100644
--- a/src/test/ui/underscore-lifetime/underscore-lifetime-binders.stderr
+++ b/src/test/ui/underscore-lifetime/underscore-lifetime-binders.stderr
@@ -38,4 +38,5 @@ LL | fn foo2<'a>(_: &'a u8, y: &'a u8) -> &'a u8 { y }
 
 error: aborting due to 5 previous errors
 
-For more information about this error, try `rustc --explain E0106`.
+Some errors have detailed explanations: E0106, E0637.
+For more information about an error, try `rustc --explain E0106`.
diff --git a/src/test/ui/underscore-lifetime/underscore-outlives-bounds.stderr b/src/test/ui/underscore-lifetime/underscore-outlives-bounds.stderr
index 6fa74d4e31034..4b38a26f957f9 100644
--- a/src/test/ui/underscore-lifetime/underscore-outlives-bounds.stderr
+++ b/src/test/ui/underscore-lifetime/underscore-outlives-bounds.stderr
@@ -6,3 +6,4 @@ LL | impl<'b: '_> Foo<'b> for i32 {}
 
 error: aborting due to previous error
 
+For more information about this error, try `rustc --explain E0637`.
diff --git a/src/test/ui/underscore-lifetime/where-clause-inherent-impl-ampersand.rust2015.stderr b/src/test/ui/underscore-lifetime/where-clause-inherent-impl-ampersand.rust2015.stderr
index eec8e4b846886..fe726cb49c737 100644
--- a/src/test/ui/underscore-lifetime/where-clause-inherent-impl-ampersand.rust2015.stderr
+++ b/src/test/ui/underscore-lifetime/where-clause-inherent-impl-ampersand.rust2015.stderr
@@ -6,3 +6,4 @@ LL |     T: WithType<&u32>
 
 error: aborting due to previous error
 
+For more information about this error, try `rustc --explain E0637`.
diff --git a/src/test/ui/underscore-lifetime/where-clause-inherent-impl-ampersand.rust2018.stderr b/src/test/ui/underscore-lifetime/where-clause-inherent-impl-ampersand.rust2018.stderr
index eec8e4b846886..fe726cb49c737 100644
--- a/src/test/ui/underscore-lifetime/where-clause-inherent-impl-ampersand.rust2018.stderr
+++ b/src/test/ui/underscore-lifetime/where-clause-inherent-impl-ampersand.rust2018.stderr
@@ -6,3 +6,4 @@ LL |     T: WithType<&u32>
 
 error: aborting due to previous error
 
+For more information about this error, try `rustc --explain E0637`.
diff --git a/src/test/ui/underscore-lifetime/where-clause-inherent-impl-underscore.rust2015.stderr b/src/test/ui/underscore-lifetime/where-clause-inherent-impl-underscore.rust2015.stderr
index d2c3e352045b6..95939fd6b7e03 100644
--- a/src/test/ui/underscore-lifetime/where-clause-inherent-impl-underscore.rust2015.stderr
+++ b/src/test/ui/underscore-lifetime/where-clause-inherent-impl-underscore.rust2015.stderr
@@ -6,3 +6,4 @@ LL |     T: WithRegion<'_>
 
 error: aborting due to previous error
 
+For more information about this error, try `rustc --explain E0637`.
diff --git a/src/test/ui/underscore-lifetime/where-clause-inherent-impl-underscore.rust2018.stderr b/src/test/ui/underscore-lifetime/where-clause-inherent-impl-underscore.rust2018.stderr
index d2c3e352045b6..95939fd6b7e03 100644
--- a/src/test/ui/underscore-lifetime/where-clause-inherent-impl-underscore.rust2018.stderr
+++ b/src/test/ui/underscore-lifetime/where-clause-inherent-impl-underscore.rust2018.stderr
@@ -6,3 +6,4 @@ LL |     T: WithRegion<'_>
 
 error: aborting due to previous error
 
+For more information about this error, try `rustc --explain E0637`.
diff --git a/src/test/ui/underscore-lifetime/where-clause-trait-impl-region.rust2015.stderr b/src/test/ui/underscore-lifetime/where-clause-trait-impl-region.rust2015.stderr
index 586b2b6aeaf28..fbd14de21078b 100644
--- a/src/test/ui/underscore-lifetime/where-clause-trait-impl-region.rust2015.stderr
+++ b/src/test/ui/underscore-lifetime/where-clause-trait-impl-region.rust2015.stderr
@@ -6,3 +6,4 @@ LL |     T: WithType<&u32>
 
 error: aborting due to previous error
 
+For more information about this error, try `rustc --explain E0637`.
diff --git a/src/test/ui/underscore-lifetime/where-clause-trait-impl-region.rust2018.stderr b/src/test/ui/underscore-lifetime/where-clause-trait-impl-region.rust2018.stderr
index 586b2b6aeaf28..fbd14de21078b 100644
--- a/src/test/ui/underscore-lifetime/where-clause-trait-impl-region.rust2018.stderr
+++ b/src/test/ui/underscore-lifetime/where-clause-trait-impl-region.rust2018.stderr
@@ -6,3 +6,4 @@ LL |     T: WithType<&u32>
 
 error: aborting due to previous error
 
+For more information about this error, try `rustc --explain E0637`.
diff --git a/src/test/ui/underscore-lifetime/where-clause-trait-impl-underscore.rust2015.stderr b/src/test/ui/underscore-lifetime/where-clause-trait-impl-underscore.rust2015.stderr
index faabf57a7df40..92caff0dcde99 100644
--- a/src/test/ui/underscore-lifetime/where-clause-trait-impl-underscore.rust2015.stderr
+++ b/src/test/ui/underscore-lifetime/where-clause-trait-impl-underscore.rust2015.stderr
@@ -6,3 +6,4 @@ LL |     T: WithRegion<'_>
 
 error: aborting due to previous error
 
+For more information about this error, try `rustc --explain E0637`.
diff --git a/src/test/ui/underscore-lifetime/where-clause-trait-impl-underscore.rust2018.stderr b/src/test/ui/underscore-lifetime/where-clause-trait-impl-underscore.rust2018.stderr
index faabf57a7df40..92caff0dcde99 100644
--- a/src/test/ui/underscore-lifetime/where-clause-trait-impl-underscore.rust2018.stderr
+++ b/src/test/ui/underscore-lifetime/where-clause-trait-impl-underscore.rust2018.stderr
@@ -6,3 +6,4 @@ LL |     T: WithRegion<'_>
 
 error: aborting due to previous error
 
+For more information about this error, try `rustc --explain E0637`.
diff --git a/src/test/ui/underscore-lifetime/where-clauses.stderr b/src/test/ui/underscore-lifetime/where-clauses.stderr
index 8674a925c110d..1a3ea4af7e12e 100644
--- a/src/test/ui/underscore-lifetime/where-clauses.stderr
+++ b/src/test/ui/underscore-lifetime/where-clauses.stderr
@@ -12,3 +12,4 @@ LL | impl<T: '_> Foo<'static> for Vec<T> {}
 
 error: aborting due to 2 previous errors
 
+For more information about this error, try `rustc --explain E0637`.