about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2021-12-09 07:08:32 +0000
committerbors <bors@rust-lang.org>2021-12-09 07:08:32 +0000
commit600820da45daa50870fe711d53938fc6a52edd35 (patch)
treec5664462839bd20b23683f20ded11f594c758cdc
parente25077704164071a1ef87cdc90fe7dd1872ba3fa (diff)
parentab92eca61248f89cd1355690f49e32fcb1708d80 (diff)
downloadrust-600820da45daa50870fe711d53938fc6a52edd35.tar.gz
rust-600820da45daa50870fe711d53938fc6a52edd35.zip
Auto merge of #91692 - matthiaskrgr:rollup-u7dvh0n, r=matthiaskrgr
Rollup of 6 pull requests

Successful merges:

 - #87599 (Implement concat_bytes!)
 - #89999 (Update std::env::temp_dir to use GetTempPath2 on Windows when available.)
 - #90796 (Remove the reg_thumb register class for asm! on ARM)
 - #91042 (Use Vec extend instead of repeated pushes on several places)
 - #91634 (Do not attempt to suggest help for overly malformed struct/function call)
 - #91685 (Install llvm tools to sysroot when assembling local toolchain)

Failed merges:

r? `@ghost`
`@rustbot` modify labels: rollup
-rw-r--r--compiler/rustc_builtin_macros/src/concat_bytes.rs167
-rw-r--r--compiler/rustc_builtin_macros/src/lib.rs2
-rw-r--r--compiler/rustc_codegen_gcc/src/asm.rs7
-rw-r--r--compiler/rustc_codegen_llvm/src/asm.rs7
-rw-r--r--compiler/rustc_codegen_ssa/src/target_features.rs1
-rw-r--r--compiler/rustc_parse/src/parser/expr.rs47
-rw-r--r--compiler/rustc_span/src/symbol.rs2
-rw-r--r--compiler/rustc_target/src/asm/arm.rs37
-rw-r--r--library/core/src/macros/mod.rs28
-rw-r--r--library/core/src/prelude/v1.rs9
-rw-r--r--library/std/src/env.rs21
-rw-r--r--library/std/src/lib.rs9
-rw-r--r--library/std/src/prelude/v1.rs9
-rw-r--r--library/std/src/sys/windows/c.rs6
-rw-r--r--library/std/src/sys/windows/os.rs2
-rw-r--r--src/bootstrap/compile.rs11
-rw-r--r--src/doc/unstable-book/src/library-features/asm.md6
-rw-r--r--src/test/assembly/asm/arm-modifiers.rs6
-rw-r--r--src/test/assembly/asm/arm-types.rs30
-rw-r--r--src/test/ui/feature-gates/feature-gate-concat_bytes.rs4
-rw-r--r--src/test/ui/feature-gates/feature-gate-concat_bytes.stderr12
-rw-r--r--src/test/ui/macros/concat-bytes-error.rs42
-rw-r--r--src/test/ui/macros/concat-bytes-error.stderr131
-rw-r--r--src/test/ui/macros/concat-bytes.rs7
-rw-r--r--src/test/ui/parser/issues/issue-91461.rs6
-rw-r--r--src/test/ui/parser/issues/issue-91461.stderr31
26 files changed, 542 insertions, 98 deletions
diff --git a/compiler/rustc_builtin_macros/src/concat_bytes.rs b/compiler/rustc_builtin_macros/src/concat_bytes.rs
new file mode 100644
index 00000000000..a107f5993b5
--- /dev/null
+++ b/compiler/rustc_builtin_macros/src/concat_bytes.rs
@@ -0,0 +1,167 @@
+use rustc_ast as ast;
+use rustc_ast::{ptr::P, tokenstream::TokenStream};
+use rustc_data_structures::sync::Lrc;
+use rustc_errors::Applicability;
+use rustc_expand::base::{self, DummyResult};
+
+/// Emits errors for literal expressions that are invalid inside and outside of an array.
+fn invalid_type_err(cx: &mut base::ExtCtxt<'_>, expr: &P<rustc_ast::Expr>, is_nested: bool) {
+    let lit = if let ast::ExprKind::Lit(lit) = &expr.kind {
+        lit
+    } else {
+        unreachable!();
+    };
+    match lit.kind {
+        ast::LitKind::Char(_) => {
+            let mut err = cx.struct_span_err(expr.span, "cannot concatenate character literals");
+            if let Ok(snippet) = cx.sess.source_map().span_to_snippet(expr.span) {
+                err.span_suggestion(
+                    expr.span,
+                    "try using a byte character",
+                    format!("b{}", snippet),
+                    Applicability::MachineApplicable,
+                )
+                .emit();
+            }
+        }
+        ast::LitKind::Str(_, _) => {
+            let mut err = cx.struct_span_err(expr.span, "cannot concatenate string literals");
+            // suggestion would be invalid if we are nested
+            if !is_nested {
+                if let Ok(snippet) = cx.sess.source_map().span_to_snippet(expr.span) {
+                    err.span_suggestion(
+                        expr.span,
+                        "try using a byte string",
+                        format!("b{}", snippet),
+                        Applicability::MachineApplicable,
+                    );
+                }
+            }
+            err.emit();
+        }
+        ast::LitKind::Float(_, _) => {
+            cx.span_err(expr.span, "cannot concatenate float literals");
+        }
+        ast::LitKind::Bool(_) => {
+            cx.span_err(expr.span, "cannot concatenate boolean literals");
+        }
+        ast::LitKind::Err(_) => {}
+        ast::LitKind::Int(_, _) if !is_nested => {
+            let mut err = cx.struct_span_err(expr.span, "cannot concatenate numeric literals");
+            if let Ok(snippet) = cx.sess.source_map().span_to_snippet(expr.span) {
+                err.span_suggestion(
+                    expr.span,
+                    "try wrapping the number in an array",
+                    format!("[{}]", snippet),
+                    Applicability::MachineApplicable,
+                );
+            }
+            err.emit();
+        }
+        ast::LitKind::Int(
+            val,
+            ast::LitIntType::Unsuffixed | ast::LitIntType::Unsigned(ast::UintTy::U8),
+        ) => {
+            assert!(val > u8::MAX.into()); // must be an error
+            cx.span_err(expr.span, "numeric literal is out of bounds");
+        }
+        ast::LitKind::Int(_, _) => {
+            cx.span_err(expr.span, "numeric literal is not a `u8`");
+        }
+        _ => unreachable!(),
+    }
+}
+
+pub fn expand_concat_bytes(
+    cx: &mut base::ExtCtxt<'_>,
+    sp: rustc_span::Span,
+    tts: TokenStream,
+) -> Box<dyn base::MacResult + 'static> {
+    let es = match base::get_exprs_from_tts(cx, sp, tts) {
+        Some(e) => e,
+        None => return DummyResult::any(sp),
+    };
+    let mut accumulator = Vec::new();
+    let mut missing_literals = vec![];
+    let mut has_errors = false;
+    for e in es {
+        match e.kind {
+            ast::ExprKind::Array(ref exprs) => {
+                for expr in exprs {
+                    match expr.kind {
+                        ast::ExprKind::Array(_) => {
+                            if !has_errors {
+                                cx.span_err(expr.span, "cannot concatenate doubly nested array");
+                            }
+                            has_errors = true;
+                        }
+                        ast::ExprKind::Lit(ref lit) => match lit.kind {
+                            ast::LitKind::Int(
+                                val,
+                                ast::LitIntType::Unsuffixed
+                                | ast::LitIntType::Unsigned(ast::UintTy::U8),
+                            ) if val <= u8::MAX.into() => {
+                                accumulator.push(val as u8);
+                            }
+
+                            ast::LitKind::Byte(val) => {
+                                accumulator.push(val);
+                            }
+                            ast::LitKind::ByteStr(_) => {
+                                if !has_errors {
+                                    cx.struct_span_err(
+                                        expr.span,
+                                        "cannot concatenate doubly nested array",
+                                    )
+                                    .note("byte strings are treated as arrays of bytes")
+                                    .help("try flattening the array")
+                                    .emit();
+                                }
+                                has_errors = true;
+                            }
+                            _ => {
+                                if !has_errors {
+                                    invalid_type_err(cx, expr, true);
+                                }
+                                has_errors = true;
+                            }
+                        },
+                        _ => {
+                            missing_literals.push(expr.span);
+                        }
+                    }
+                }
+            }
+            ast::ExprKind::Lit(ref lit) => match lit.kind {
+                ast::LitKind::Byte(val) => {
+                    accumulator.push(val);
+                }
+                ast::LitKind::ByteStr(ref bytes) => {
+                    accumulator.extend_from_slice(&bytes);
+                }
+                _ => {
+                    if !has_errors {
+                        invalid_type_err(cx, &e, false);
+                    }
+                    has_errors = true;
+                }
+            },
+            ast::ExprKind::Err => {
+                has_errors = true;
+            }
+            _ => {
+                missing_literals.push(e.span);
+            }
+        }
+    }
+    if !missing_literals.is_empty() {
+        let mut err = cx.struct_span_err(missing_literals.clone(), "expected a byte literal");
+        err.note("only byte literals (like `b\"foo\"`, `b's'`, and `[3, 4, 5]`) can be passed to `concat_bytes!()`");
+        err.emit();
+        return base::MacEager::expr(DummyResult::raw_expr(sp, true));
+    } else if has_errors {
+        return base::MacEager::expr(DummyResult::raw_expr(sp, true));
+    }
+    let sp = cx.with_def_site_ctxt(sp);
+    base::MacEager::expr(cx.expr_lit(sp, ast::LitKind::ByteStr(Lrc::from(accumulator))))
+}
diff --git a/compiler/rustc_builtin_macros/src/lib.rs b/compiler/rustc_builtin_macros/src/lib.rs
index d1d276930b9..f5acf9db085 100644
--- a/compiler/rustc_builtin_macros/src/lib.rs
+++ b/compiler/rustc_builtin_macros/src/lib.rs
@@ -27,6 +27,7 @@ mod cfg_accessible;
 mod cfg_eval;
 mod compile_error;
 mod concat;
+mod concat_bytes;
 mod concat_idents;
 mod derive;
 mod deriving;
@@ -65,6 +66,7 @@ pub fn register_builtin_macros(resolver: &mut dyn ResolverExpand) {
         cfg: cfg::expand_cfg,
         column: source_util::expand_column,
         compile_error: compile_error::expand_compile_error,
+        concat_bytes: concat_bytes::expand_concat_bytes,
         concat_idents: concat_idents::expand_concat_idents,
         concat: concat::expand_concat,
         env: env::expand_env,
diff --git a/compiler/rustc_codegen_gcc/src/asm.rs b/compiler/rustc_codegen_gcc/src/asm.rs
index 7481b5db755..10edcf36955 100644
--- a/compiler/rustc_codegen_gcc/src/asm.rs
+++ b/compiler/rustc_codegen_gcc/src/asm.rs
@@ -568,7 +568,6 @@ fn reg_to_gcc(reg: InlineAsmRegOrRegClass) -> ConstraintOrRegister {
             InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::vreg) => unimplemented!(),
             InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::vreg_low16) => unimplemented!(),
             InlineAsmRegClass::Arm(ArmInlineAsmRegClass::reg) => unimplemented!(),
-            InlineAsmRegClass::Arm(ArmInlineAsmRegClass::reg_thumb) => unimplemented!(),
             InlineAsmRegClass::Arm(ArmInlineAsmRegClass::sreg)
             | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::dreg_low16)
             | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::qreg_low8) => unimplemented!(),
@@ -628,8 +627,7 @@ fn dummy_output_type<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, reg: InlineAsmRegCl
         | InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::vreg_low16) => {
             unimplemented!()
         }
-        InlineAsmRegClass::Arm(ArmInlineAsmRegClass::reg)
-        | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::reg_thumb) => cx.type_i32(),
+        InlineAsmRegClass::Arm(ArmInlineAsmRegClass::reg)=> cx.type_i32(),
         InlineAsmRegClass::Arm(ArmInlineAsmRegClass::sreg)
         | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::sreg_low16) => cx.type_f32(),
         InlineAsmRegClass::Arm(ArmInlineAsmRegClass::dreg)
@@ -737,8 +735,7 @@ fn modifier_to_gcc(arch: InlineAsmArch, reg: InlineAsmRegClass, modifier: Option
         | InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::vreg_low16) => {
             unimplemented!()
         }
-        InlineAsmRegClass::Arm(ArmInlineAsmRegClass::reg)
-        | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::reg_thumb) => unimplemented!(),
+        InlineAsmRegClass::Arm(ArmInlineAsmRegClass::reg)  => unimplemented!(),
         InlineAsmRegClass::Arm(ArmInlineAsmRegClass::sreg)
         | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::sreg_low16) => unimplemented!(),
         InlineAsmRegClass::Arm(ArmInlineAsmRegClass::dreg)
diff --git a/compiler/rustc_codegen_llvm/src/asm.rs b/compiler/rustc_codegen_llvm/src/asm.rs
index 90d3c0fb2f1..f3c3a174908 100644
--- a/compiler/rustc_codegen_llvm/src/asm.rs
+++ b/compiler/rustc_codegen_llvm/src/asm.rs
@@ -632,7 +632,6 @@ fn reg_to_llvm(reg: InlineAsmRegOrRegClass, layout: Option<&TyAndLayout<'tcx>>)
                 unreachable!("clobber-only")
             }
             InlineAsmRegClass::Arm(ArmInlineAsmRegClass::reg) => "r",
-            InlineAsmRegClass::Arm(ArmInlineAsmRegClass::reg_thumb) => "l",
             InlineAsmRegClass::Arm(ArmInlineAsmRegClass::sreg)
             | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::dreg_low16)
             | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::qreg_low8) => "t",
@@ -703,8 +702,7 @@ fn modifier_to_llvm(
         InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::preg) => {
             unreachable!("clobber-only")
         }
-        InlineAsmRegClass::Arm(ArmInlineAsmRegClass::reg)
-        | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::reg_thumb) => None,
+        InlineAsmRegClass::Arm(ArmInlineAsmRegClass::reg) => None,
         InlineAsmRegClass::Arm(ArmInlineAsmRegClass::sreg)
         | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::sreg_low16) => None,
         InlineAsmRegClass::Arm(ArmInlineAsmRegClass::dreg)
@@ -785,8 +783,7 @@ fn dummy_output_type(cx: &CodegenCx<'ll, 'tcx>, reg: InlineAsmRegClass) -> &'ll
         InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::preg) => {
             unreachable!("clobber-only")
         }
-        InlineAsmRegClass::Arm(ArmInlineAsmRegClass::reg)
-        | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::reg_thumb) => cx.type_i32(),
+        InlineAsmRegClass::Arm(ArmInlineAsmRegClass::reg) => cx.type_i32(),
         InlineAsmRegClass::Arm(ArmInlineAsmRegClass::sreg)
         | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::sreg_low16) => cx.type_f32(),
         InlineAsmRegClass::Arm(ArmInlineAsmRegClass::dreg)
diff --git a/compiler/rustc_codegen_ssa/src/target_features.rs b/compiler/rustc_codegen_ssa/src/target_features.rs
index b4420df5df4..ba72e3cfafc 100644
--- a/compiler/rustc_codegen_ssa/src/target_features.rs
+++ b/compiler/rustc_codegen_ssa/src/target_features.rs
@@ -35,6 +35,7 @@ const ARM_ALLOWED_FEATURES: &[(&str, Option<Symbol>)] = &[
     // since it should be enabled per-function using #[instruction_set], not
     // #[target_feature].
     ("thumb-mode", Some(sym::arm_target_feature)),
+    ("thumb2", Some(sym::arm_target_feature)),
 ];
 
 const AARCH64_ALLOWED_FEATURES: &[(&str, Option<Symbol>)] = &[
diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs
index 1dbd7bad0f0..0f8c0e1b8cf 100644
--- a/compiler/rustc_parse/src/parser/expr.rs
+++ b/compiler/rustc_parse/src/parser/expr.rs
@@ -1100,30 +1100,37 @@ impl<'a> Parser<'a> {
                 snapshot.bump(); // `(`
                 match snapshot.parse_struct_fields(path, false, token::Paren) {
                     Ok((fields, ..)) if snapshot.eat(&token::CloseDelim(token::Paren)) => {
-                        // We have are certain we have `Enum::Foo(a: 3, b: 4)`, suggest
+                        // We are certain we have `Enum::Foo(a: 3, b: 4)`, suggest
                         // `Enum::Foo { a: 3, b: 4 }` or `Enum::Foo(3, 4)`.
                         *self = snapshot;
                         let close_paren = self.prev_token.span;
                         let span = lo.to(self.prev_token.span);
-                        err.cancel();
-                        self.struct_span_err(
-                            span,
-                            "invalid `struct` delimiters or `fn` call arguments",
-                        )
-                        .multipart_suggestion(
-                            &format!("if `{}` is a struct, use braces as delimiters", name),
-                            vec![(open_paren, " { ".to_string()), (close_paren, " }".to_string())],
-                            Applicability::MaybeIncorrect,
-                        )
-                        .multipart_suggestion(
-                            &format!("if `{}` is a function, use the arguments directly", name),
-                            fields
-                                .into_iter()
-                                .map(|field| (field.span.until(field.expr.span), String::new()))
-                                .collect(),
-                            Applicability::MaybeIncorrect,
-                        )
-                        .emit();
+                        if !fields.is_empty() {
+                            err.cancel();
+                            let mut err = self.struct_span_err(
+                                span,
+                                "invalid `struct` delimiters or `fn` call arguments",
+                            );
+                            err.multipart_suggestion(
+                                &format!("if `{}` is a struct, use braces as delimiters", name),
+                                vec![
+                                    (open_paren, " { ".to_string()),
+                                    (close_paren, " }".to_string()),
+                                ],
+                                Applicability::MaybeIncorrect,
+                            );
+                            err.multipart_suggestion(
+                                &format!("if `{}` is a function, use the arguments directly", name),
+                                fields
+                                    .into_iter()
+                                    .map(|field| (field.span.until(field.expr.span), String::new()))
+                                    .collect(),
+                                Applicability::MaybeIncorrect,
+                            );
+                            err.emit();
+                        } else {
+                            err.emit();
+                        }
                         return Some(self.mk_expr_err(span));
                     }
                     Ok(_) => {}
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index 492cecf7d67..eb6063d7612 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -439,6 +439,7 @@ symbols! {
         compiler_builtins,
         compiler_fence,
         concat,
+        concat_bytes,
         concat_idents,
         conservative_impl_trait,
         console,
@@ -1058,7 +1059,6 @@ symbols! {
         reg_nonzero,
         reg_pair,
         reg_ptr,
-        reg_thumb,
         reg_upper,
         register_attr,
         register_tool,
diff --git a/compiler/rustc_target/src/asm/arm.rs b/compiler/rustc_target/src/asm/arm.rs
index 4c323fc35d6..4eeb7fcc71b 100644
--- a/compiler/rustc_target/src/asm/arm.rs
+++ b/compiler/rustc_target/src/asm/arm.rs
@@ -6,7 +6,6 @@ use std::fmt;
 def_reg_class! {
     Arm ArmInlineAsmRegClass {
         reg,
-        reg_thumb,
         sreg,
         sreg_low16,
         dreg,
@@ -47,7 +46,7 @@ impl ArmInlineAsmRegClass {
         _arch: InlineAsmArch,
     ) -> &'static [(InlineAsmType, Option<&'static str>)] {
         match self {
-            Self::reg | Self::reg_thumb => types! { _: I8, I16, I32, F32; },
+            Self::reg => types! { _: I8, I16, I32, F32; },
             Self::sreg | Self::sreg_low16 => types! { "vfp2": I32, F32; },
             Self::dreg | Self::dreg_low16 | Self::dreg_low8 => types! {
                 "vfp2": I64, F64, VecI8(8), VecI16(4), VecI32(2), VecI64(1), VecF32(2);
@@ -88,20 +87,32 @@ fn frame_pointer_r7(
     }
 }
 
+fn not_thumb1(
+    _arch: InlineAsmArch,
+    mut has_feature: impl FnMut(&str) -> bool,
+    _target: &Target,
+) -> Result<(), &'static str> {
+    if has_feature("thumb-mode") && !has_feature("thumb2") {
+        Err("high registers (r8+) cannot be used in Thumb-1 code")
+    } else {
+        Ok(())
+    }
+}
+
 def_regs! {
     Arm ArmInlineAsmReg ArmInlineAsmRegClass {
-        r0: reg, reg_thumb = ["r0", "a1"],
-        r1: reg, reg_thumb = ["r1", "a2"],
-        r2: reg, reg_thumb = ["r2", "a3"],
-        r3: reg, reg_thumb = ["r3", "a4"],
-        r4: reg, reg_thumb = ["r4", "v1"],
-        r5: reg, reg_thumb = ["r5", "v2"],
-        r7: reg, reg_thumb = ["r7", "v4"] % frame_pointer_r7,
-        r8: reg = ["r8", "v5"],
-        r10: reg = ["r10", "sl"],
+        r0: reg = ["r0", "a1"],
+        r1: reg = ["r1", "a2"],
+        r2: reg = ["r2", "a3"],
+        r3: reg = ["r3", "a4"],
+        r4: reg = ["r4", "v1"],
+        r5: reg = ["r5", "v2"],
+        r7: reg = ["r7", "v4"] % frame_pointer_r7,
+        r8: reg = ["r8", "v5"] % not_thumb1,
+        r10: reg = ["r10", "sl"] % not_thumb1,
         r11: reg = ["r11", "fp"] % frame_pointer_r11,
-        r12: reg = ["r12", "ip"],
-        r14: reg = ["r14", "lr"],
+        r12: reg = ["r12", "ip"] % not_thumb1,
+        r14: reg = ["r14", "lr"] % not_thumb1,
         s0: sreg, sreg_low16 = ["s0"],
         s1: sreg, sreg_low16 = ["s1"],
         s2: sreg, sreg_low16 = ["s2"],
diff --git a/library/core/src/macros/mod.rs b/library/core/src/macros/mod.rs
index 993ae723229..b18508186a6 100644
--- a/library/core/src/macros/mod.rs
+++ b/library/core/src/macros/mod.rs
@@ -967,6 +967,34 @@ pub(crate) mod builtin {
         ($($e:ident),+ $(,)?) => {{ /* compiler built-in */ }};
     }
 
+    /// Concatenates literals into a byte slice.
+    ///
+    /// This macro takes any number of comma-separated literals, and concatenates them all into
+    /// one, yielding an expression of type `&[u8, _]`, which represents all of the literals
+    /// concatenated left-to-right. The literals passed can be any combination of:
+    ///
+    /// - byte literals (`b'r'`)
+    /// - byte strings (`b"Rust"`)
+    /// - arrays of bytes/numbers (`[b'A', 66, b'C']`)
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(concat_bytes)]
+    ///
+    /// # fn main() {
+    /// let s: &[u8; 6] = concat_bytes!(b'A', b"BC", [68, b'E', 70]);
+    /// assert_eq!(s, b"ABCDEF");
+    /// # }
+    /// ```
+    #[cfg(not(bootstrap))]
+    #[unstable(feature = "concat_bytes", issue = "87555")]
+    #[rustc_builtin_macro]
+    #[macro_export]
+    macro_rules! concat_bytes {
+        ($($e:literal),+ $(,)?) => {{ /* compiler built-in */ }};
+    }
+
     /// Concatenates literals into a static string slice.
     ///
     /// This macro takes any number of comma-separated literals, yielding an
diff --git a/library/core/src/prelude/v1.rs b/library/core/src/prelude/v1.rs
index 6b51ef5b012..8705eb39468 100644
--- a/library/core/src/prelude/v1.rs
+++ b/library/core/src/prelude/v1.rs
@@ -61,6 +61,15 @@ pub use crate::{
 };
 
 #[unstable(
+    feature = "concat_bytes",
+    issue = "87555",
+    reason = "`concat_bytes` is not stable enough for use and is subject to change"
+)]
+#[cfg(not(bootstrap))]
+#[doc(no_inline)]
+pub use crate::concat_bytes;
+
+#[unstable(
     feature = "asm",
     issue = "72016",
     reason = "inline assembly is not stable enough for use and is subject to change"
diff --git a/library/std/src/env.rs b/library/std/src/env.rs
index c6af708f6cd..c06928647d3 100644
--- a/library/std/src/env.rs
+++ b/library/std/src/env.rs
@@ -583,28 +583,25 @@ pub fn home_dir() -> Option<PathBuf> {
 /// may result in "insecure temporary file" security vulnerabilities. Consider
 /// using a crate that securely creates temporary files or directories.
 ///
-/// # Unix
+/// # Platform-specific behavior
 ///
-/// Returns the value of the `TMPDIR` environment variable if it is
+/// On Unix, returns the value of the `TMPDIR` environment variable if it is
 /// set, otherwise for non-Android it returns `/tmp`. If Android, since there
 /// is no global temporary folder (it is usually allocated per-app), it returns
 /// `/data/local/tmp`.
+/// On Windows, the behavior is equivalent to that of [`GetTempPath2`][GetTempPath2] /
+/// [`GetTempPath`][GetTempPath], which this function uses internally.
+/// Note that, this [may change in the future][changes].
 ///
-/// # Windows
-///
-/// Returns the value of, in order, the `TMP`, `TEMP`,
-/// `USERPROFILE` environment variable if any are set and not the empty
-/// string. Otherwise, `temp_dir` returns the path of the Windows directory.
-/// This behavior is identical to that of [`GetTempPath`][msdn], which this
-/// function uses internally.
-///
-/// [msdn]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-gettemppatha
+/// [changes]: io#platform-specific-behavior
+/// [GetTempPath2]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-gettemppath2a
+/// [GetTempPath]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-gettemppatha
 ///
 /// ```no_run
 /// use std::env;
 ///
 /// fn main() {
-///     let mut dir = env::temp_dir();
+///     let dir = env::temp_dir();
 ///     println!("Temporary directory: {}", dir.display());
 /// }
 /// ```
diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs
index 67846e78835..367f072ffc7 100644
--- a/library/std/src/lib.rs
+++ b/library/std/src/lib.rs
@@ -250,6 +250,7 @@
 #![feature(cfg_target_thread_local)]
 #![feature(char_error_internals)]
 #![feature(char_internals)]
+#![cfg_attr(not(bootstrap), feature(concat_bytes))]
 #![feature(concat_idents)]
 #![feature(const_cstr_unchecked)]
 #![feature(const_fn_floating_point_arithmetic)]
@@ -576,6 +577,14 @@ pub use core::{
     log_syntax, module_path, option_env, stringify, trace_macros,
 };
 
+#[unstable(
+    feature = "concat_bytes",
+    issue = "87555",
+    reason = "`concat_bytes` is not stable enough for use and is subject to change"
+)]
+#[cfg(not(bootstrap))]
+pub use core::concat_bytes;
+
 #[stable(feature = "core_primitive", since = "1.43.0")]
 pub use core::primitive;
 
diff --git a/library/std/src/prelude/v1.rs b/library/std/src/prelude/v1.rs
index 772044f0149..9b23aa37e31 100644
--- a/library/std/src/prelude/v1.rs
+++ b/library/std/src/prelude/v1.rs
@@ -46,6 +46,15 @@ pub use core::prelude::v1::{
 };
 
 #[unstable(
+    feature = "concat_bytes",
+    issue = "87555",
+    reason = "`concat_bytes` is not stable enough for use and is subject to change"
+)]
+#[cfg(not(bootstrap))]
+#[doc(no_inline)]
+pub use core::prelude::v1::concat_bytes;
+
+#[unstable(
     feature = "asm",
     issue = "72016",
     reason = "inline assembly is not stable enough for use and is subject to change"
diff --git a/library/std/src/sys/windows/c.rs b/library/std/src/sys/windows/c.rs
index 50c4547de85..b87b6b5d88e 100644
--- a/library/std/src/sys/windows/c.rs
+++ b/library/std/src/sys/windows/c.rs
@@ -1110,6 +1110,12 @@ compat_fn! {
                                           -> () {
         GetSystemTimeAsFileTime(lpSystemTimeAsFileTime)
     }
+
+    // >= Win11 / Server 2022
+    // https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-gettemppath2a
+    pub fn GetTempPath2W(nBufferLength: DWORD, lpBuffer: LPCWSTR) -> DWORD {
+        GetTempPathW(nBufferLength, lpBuffer)
+    }
 }
 
 compat_fn! {
diff --git a/library/std/src/sys/windows/os.rs b/library/std/src/sys/windows/os.rs
index b5209aa690b..5f8556c3bc3 100644
--- a/library/std/src/sys/windows/os.rs
+++ b/library/std/src/sys/windows/os.rs
@@ -275,7 +275,7 @@ pub fn unsetenv(n: &OsStr) -> io::Result<()> {
 }
 
 pub fn temp_dir() -> PathBuf {
-    super::fill_utf16_buf(|buf, sz| unsafe { c::GetTempPathW(sz, buf) }, super::os2path).unwrap()
+    super::fill_utf16_buf(|buf, sz| unsafe { c::GetTempPath2W(sz, buf) }, super::os2path).unwrap()
 }
 
 #[cfg(not(target_vendor = "uwp"))]
diff --git a/src/bootstrap/compile.rs b/src/bootstrap/compile.rs
index 007ca9f7f5a..186b5e92d33 100644
--- a/src/bootstrap/compile.rs
+++ b/src/bootstrap/compile.rs
@@ -28,6 +28,7 @@ use crate::dist;
 use crate::native;
 use crate::tool::SourceType;
 use crate::util::{exe, is_debug_info, is_dylib, symlink_dir};
+use crate::LLVM_TOOLS;
 use crate::{Compiler, DependencyType, GitRepo, Mode};
 
 #[derive(Debug, PartialOrd, Ord, Copy, Clone, PartialEq, Eq, Hash)]
@@ -1164,6 +1165,16 @@ impl Step for Assemble {
                 let llvm_bin_dir = output(Command::new(llvm_config_bin).arg("--bindir"));
                 let llvm_bin_dir = Path::new(llvm_bin_dir.trim());
                 builder.copy(&llvm_bin_dir.join(&src_exe), &libdir_bin.join(&dst_exe));
+
+                // Since we've already built the LLVM tools, install them to the sysroot.
+                // This is the equivalent of installing the `llvm-tools-preview` component via
+                // rustup, and lets developers use a locally built toolchain to
+                // build projects that expect llvm tools to be present in the sysroot
+                // (e.g. the `bootimage` crate).
+                for tool in LLVM_TOOLS {
+                    let tool_exe = exe(tool, target_compiler.host);
+                    builder.copy(&llvm_bin_dir.join(&tool_exe), &libdir_bin.join(&tool_exe));
+                }
             }
         }
 
diff --git a/src/doc/unstable-book/src/library-features/asm.md b/src/doc/unstable-book/src/library-features/asm.md
index dffa3a8b80f..59987cccde6 100644
--- a/src/doc/unstable-book/src/library-features/asm.md
+++ b/src/doc/unstable-book/src/library-features/asm.md
@@ -564,12 +564,8 @@ Here is the list of currently supported register classes:
 | AArch64 | `vreg` | `v[0-31]` | `w` |
 | AArch64 | `vreg_low16` | `v[0-15]` | `x` |
 | AArch64 | `preg` | `p[0-15]`, `ffr` | Only clobbers |
-| ARM (ARM) | `reg` | `r[0-12]`, `r14` | `r` |
-| ARM (Thumb2) | `reg` | `r[0-12]`, `r14` | `r` |
+| ARM (ARM/Thumb2) | `reg` | `r[0-12]`, `r14` | `r` |
 | ARM (Thumb1) | `reg` | `r[0-7]` | `r` |
-| ARM (ARM) | `reg_thumb` | `r[0-r12]`, `r14` | `l` |
-| ARM (Thumb2) | `reg_thumb` | `r[0-7]` | `l` |
-| ARM (Thumb1) | `reg_thumb` | `r[0-7]` | `l` |
 | ARM | `sreg` | `s[0-31]` | `t` |
 | ARM | `sreg_low16` | `s[0-15]` | `x` |
 | ARM | `dreg` | `d[0-31]` | `w` |
diff --git a/src/test/assembly/asm/arm-modifiers.rs b/src/test/assembly/asm/arm-modifiers.rs
index a6985a3bf5c..88ffeaecfec 100644
--- a/src/test/assembly/asm/arm-modifiers.rs
+++ b/src/test/assembly/asm/arm-modifiers.rs
@@ -59,12 +59,6 @@ macro_rules! check {
 // CHECK: @NO_APP
 check!(reg "" reg i32 "mov");
 
-// CHECK-LABEL: reg_thumb:
-// CHECK: @APP
-// CHECK: mov r0, r0
-// CHECK: @NO_APP
-check!(reg_thumb "" reg_thumb i32 "mov");
-
 // CHECK-LABEL: sreg:
 // CHECK: @APP
 // CHECK: vmov.f32 s0, s0
diff --git a/src/test/assembly/asm/arm-types.rs b/src/test/assembly/asm/arm-types.rs
index 0c57b1fc478..5ac1af6afd6 100644
--- a/src/test/assembly/asm/arm-types.rs
+++ b/src/test/assembly/asm/arm-types.rs
@@ -163,36 +163,6 @@ check!(reg_f32 f32 reg "mov");
 // CHECK: @NO_APP
 check!(reg_ptr ptr reg "mov");
 
-// CHECK-LABEL: reg_thumb_i8:
-// CHECK: @APP
-// CHECK: mov {{[a-z0-9]+}}, {{[a-z0-9]+}}
-// CHECK: @NO_APP
-check!(reg_thumb_i8 i8 reg_thumb "mov");
-
-// CHECK-LABEL: reg_thumb_i16:
-// CHECK: @APP
-// CHECK: mov {{[a-z0-9]+}}, {{[a-z0-9]+}}
-// CHECK: @NO_APP
-check!(reg_thumb_i16 i16 reg_thumb "mov");
-
-// CHECK-LABEL: reg_thumb_i32:
-// CHECK: @APP
-// CHECK: mov {{[a-z0-9]+}}, {{[a-z0-9]+}}
-// CHECK: @NO_APP
-check!(reg_thumb_i32 i32 reg_thumb "mov");
-
-// CHECK-LABEL: reg_thumb_f32:
-// CHECK: @APP
-// CHECK: mov {{[a-z0-9]+}}, {{[a-z0-9]+}}
-// CHECK: @NO_APP
-check!(reg_thumb_f32 f32 reg_thumb "mov");
-
-// CHECK-LABEL: reg_thumb_ptr:
-// CHECK: @APP
-// CHECK: mov {{[a-z0-9]+}}, {{[a-z0-9]+}}
-// CHECK: @NO_APP
-check!(reg_thumb_ptr ptr reg_thumb "mov");
-
 // CHECK-LABEL: sreg_i32:
 // CHECK: @APP
 // CHECK: vmov.f32 s{{[0-9]+}}, s{{[0-9]+}}
diff --git a/src/test/ui/feature-gates/feature-gate-concat_bytes.rs b/src/test/ui/feature-gates/feature-gate-concat_bytes.rs
new file mode 100644
index 00000000000..07d63cb11e0
--- /dev/null
+++ b/src/test/ui/feature-gates/feature-gate-concat_bytes.rs
@@ -0,0 +1,4 @@
+fn main() {
+    let a = concat_bytes!(b'A', b"BC"); //~ ERROR use of unstable library feature 'concat_bytes'
+    assert_eq!(a, &[65, 66, 67]);
+}
diff --git a/src/test/ui/feature-gates/feature-gate-concat_bytes.stderr b/src/test/ui/feature-gates/feature-gate-concat_bytes.stderr
new file mode 100644
index 00000000000..4b3ee4c19ce
--- /dev/null
+++ b/src/test/ui/feature-gates/feature-gate-concat_bytes.stderr
@@ -0,0 +1,12 @@
+error[E0658]: use of unstable library feature 'concat_bytes'
+  --> $DIR/feature-gate-concat_bytes.rs:2:13
+   |
+LL |     let a = concat_bytes!(b'A', b"BC");
+   |             ^^^^^^^^^^^^
+   |
+   = note: see issue #87555 <https://github.com/rust-lang/rust/issues/87555> for more information
+   = help: add `#![feature(concat_bytes)]` to the crate attributes to enable
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/src/test/ui/macros/concat-bytes-error.rs b/src/test/ui/macros/concat-bytes-error.rs
new file mode 100644
index 00000000000..9b4a9c2cf81
--- /dev/null
+++ b/src/test/ui/macros/concat-bytes-error.rs
@@ -0,0 +1,42 @@
+#![feature(concat_bytes)]
+
+fn main() {
+    concat_bytes!(pie); //~ ERROR expected a byte literal
+    concat_bytes!(pie, pie); //~ ERROR expected a byte literal
+    concat_bytes!("tnrsi", "tnri"); //~ ERROR cannot concatenate string literals
+    concat_bytes!(2.8); //~ ERROR cannot concatenate float literals
+    concat_bytes!(300); //~ ERROR cannot concatenate numeric literals
+    concat_bytes!('a'); //~ ERROR cannot concatenate character literals
+    concat_bytes!(true, false); //~ ERROR cannot concatenate boolean literals
+    concat_bytes!(42, b"va", b'l'); //~ ERROR cannot concatenate numeric literals
+    concat_bytes!(42, b"va", b'l', [1, 2]); //~ ERROR cannot concatenate numeric literals
+    concat_bytes!([
+        "hi", //~ ERROR cannot concatenate string literals
+    ]);
+    concat_bytes!([
+        'a', //~ ERROR cannot concatenate character literals
+    ]);
+    concat_bytes!([
+        true, //~ ERROR cannot concatenate boolean literals
+    ]);
+    concat_bytes!([
+        false, //~ ERROR cannot concatenate boolean literals
+    ]);
+    concat_bytes!([
+        2.6, //~ ERROR cannot concatenate float literals
+    ]);
+    concat_bytes!([
+        265, //~ ERROR numeric literal is out of bounds
+    ]);
+    concat_bytes!([
+        -33, //~ ERROR expected a byte literal
+    ]);
+    concat_bytes!([
+        b"hi!", //~ ERROR cannot concatenate doubly nested array
+    ]);
+    concat_bytes!([
+        [5, 6, 7], //~ ERROR cannot concatenate doubly nested array
+    ]);
+    concat_bytes!(5u16); //~ ERROR cannot concatenate numeric literals
+    concat_bytes!([5u16]); //~ ERROR numeric literal is not a `u8`
+}
diff --git a/src/test/ui/macros/concat-bytes-error.stderr b/src/test/ui/macros/concat-bytes-error.stderr
new file mode 100644
index 00000000000..1fc2d5c4843
--- /dev/null
+++ b/src/test/ui/macros/concat-bytes-error.stderr
@@ -0,0 +1,131 @@
+error: expected a byte literal
+  --> $DIR/concat-bytes-error.rs:4:19
+   |
+LL |     concat_bytes!(pie);
+   |                   ^^^
+   |
+   = note: only byte literals (like `b"foo"`, `b's'`, and `[3, 4, 5]`) can be passed to `concat_bytes!()`
+
+error: expected a byte literal
+  --> $DIR/concat-bytes-error.rs:5:19
+   |
+LL |     concat_bytes!(pie, pie);
+   |                   ^^^  ^^^
+   |
+   = note: only byte literals (like `b"foo"`, `b's'`, and `[3, 4, 5]`) can be passed to `concat_bytes!()`
+
+error: cannot concatenate string literals
+  --> $DIR/concat-bytes-error.rs:6:19
+   |
+LL |     concat_bytes!("tnrsi", "tnri");
+   |                   ^^^^^^^ help: try using a byte string: `b"tnrsi"`
+
+error: cannot concatenate float literals
+  --> $DIR/concat-bytes-error.rs:7:19
+   |
+LL |     concat_bytes!(2.8);
+   |                   ^^^
+
+error: cannot concatenate numeric literals
+  --> $DIR/concat-bytes-error.rs:8:19
+   |
+LL |     concat_bytes!(300);
+   |                   ^^^ help: try wrapping the number in an array: `[300]`
+
+error: cannot concatenate character literals
+  --> $DIR/concat-bytes-error.rs:9:19
+   |
+LL |     concat_bytes!('a');
+   |                   ^^^ help: try using a byte character: `b'a'`
+
+error: cannot concatenate boolean literals
+  --> $DIR/concat-bytes-error.rs:10:19
+   |
+LL |     concat_bytes!(true, false);
+   |                   ^^^^
+
+error: cannot concatenate numeric literals
+  --> $DIR/concat-bytes-error.rs:11:19
+   |
+LL |     concat_bytes!(42, b"va", b'l');
+   |                   ^^ help: try wrapping the number in an array: `[42]`
+
+error: cannot concatenate numeric literals
+  --> $DIR/concat-bytes-error.rs:12:19
+   |
+LL |     concat_bytes!(42, b"va", b'l', [1, 2]);
+   |                   ^^ help: try wrapping the number in an array: `[42]`
+
+error: cannot concatenate string literals
+  --> $DIR/concat-bytes-error.rs:14:9
+   |
+LL |         "hi",
+   |         ^^^^
+
+error: cannot concatenate character literals
+  --> $DIR/concat-bytes-error.rs:17:9
+   |
+LL |         'a',
+   |         ^^^ help: try using a byte character: `b'a'`
+
+error: cannot concatenate boolean literals
+  --> $DIR/concat-bytes-error.rs:20:9
+   |
+LL |         true,
+   |         ^^^^
+
+error: cannot concatenate boolean literals
+  --> $DIR/concat-bytes-error.rs:23:9
+   |
+LL |         false,
+   |         ^^^^^
+
+error: cannot concatenate float literals
+  --> $DIR/concat-bytes-error.rs:26:9
+   |
+LL |         2.6,
+   |         ^^^
+
+error: numeric literal is out of bounds
+  --> $DIR/concat-bytes-error.rs:29:9
+   |
+LL |         265,
+   |         ^^^
+
+error: expected a byte literal
+  --> $DIR/concat-bytes-error.rs:32:9
+   |
+LL |         -33,
+   |         ^^^
+   |
+   = note: only byte literals (like `b"foo"`, `b's'`, and `[3, 4, 5]`) can be passed to `concat_bytes!()`
+
+error: cannot concatenate doubly nested array
+  --> $DIR/concat-bytes-error.rs:35:9
+   |
+LL |         b"hi!",
+   |         ^^^^^^
+   |
+   = note: byte strings are treated as arrays of bytes
+   = help: try flattening the array
+
+error: cannot concatenate doubly nested array
+  --> $DIR/concat-bytes-error.rs:38:9
+   |
+LL |         [5, 6, 7],
+   |         ^^^^^^^^^
+
+error: cannot concatenate numeric literals
+  --> $DIR/concat-bytes-error.rs:40:19
+   |
+LL |     concat_bytes!(5u16);
+   |                   ^^^^ help: try wrapping the number in an array: `[5u16]`
+
+error: numeric literal is not a `u8`
+  --> $DIR/concat-bytes-error.rs:41:20
+   |
+LL |     concat_bytes!([5u16]);
+   |                    ^^^^
+
+error: aborting due to 20 previous errors
+
diff --git a/src/test/ui/macros/concat-bytes.rs b/src/test/ui/macros/concat-bytes.rs
new file mode 100644
index 00000000000..5415cf3fe22
--- /dev/null
+++ b/src/test/ui/macros/concat-bytes.rs
@@ -0,0 +1,7 @@
+// run-pass
+#![feature(concat_bytes)]
+
+fn main() {
+    assert_eq!(concat_bytes!(), &[]);
+    assert_eq!(concat_bytes!(b'A', b"BC", [68, b'E', 70]), b"ABCDEF");
+}
diff --git a/src/test/ui/parser/issues/issue-91461.rs b/src/test/ui/parser/issues/issue-91461.rs
new file mode 100644
index 00000000000..3e3c411c478
--- /dev/null
+++ b/src/test/ui/parser/issues/issue-91461.rs
@@ -0,0 +1,6 @@
+fn main() {
+    a(_:b:,)
+    //~^ ERROR: expected identifier, found reserved identifier `_`
+    //~| ERROR: expected type, found `,`
+    //~| ERROR: expected type, found `,`
+}
diff --git a/src/test/ui/parser/issues/issue-91461.stderr b/src/test/ui/parser/issues/issue-91461.stderr
new file mode 100644
index 00000000000..94fcf1721d8
--- /dev/null
+++ b/src/test/ui/parser/issues/issue-91461.stderr
@@ -0,0 +1,31 @@
+error: expected identifier, found reserved identifier `_`
+  --> $DIR/issue-91461.rs:2:7
+   |
+LL |     a(_:b:,)
+   |       ^ expected identifier, found reserved identifier
+
+error: expected type, found `,`
+  --> $DIR/issue-91461.rs:2:11
+   |
+LL |     a(_:b:,)
+   |     -    -^ expected type
+   |     |    |
+   |     |    tried to parse a type due to this type ascription
+   |     while parsing this struct
+   |
+   = note: `#![feature(type_ascription)]` lets you annotate an expression with a type: `<expr>: <type>`
+   = note: see issue #23416 <https://github.com/rust-lang/rust/issues/23416> for more information
+
+error: expected type, found `,`
+  --> $DIR/issue-91461.rs:2:11
+   |
+LL |     a(_:b:,)
+   |          -^ expected type
+   |          |
+   |          tried to parse a type due to this type ascription
+   |
+   = note: `#![feature(type_ascription)]` lets you annotate an expression with a type: `<expr>: <type>`
+   = note: see issue #23416 <https://github.com/rust-lang/rust/issues/23416> for more information
+
+error: aborting due to 3 previous errors
+