about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_codegen_llvm/src/back/archive.rs2
-rw-r--r--compiler/rustc_codegen_llvm/src/back/lto.rs2
-rw-r--r--compiler/rustc_const_eval/src/interpret/terminator.rs47
-rw-r--r--compiler/rustc_errors/src/lib.rs2
-rw-r--r--compiler/rustc_feature/src/accepted.rs2
-rw-r--r--compiler/rustc_lint/src/early.rs1
-rw-r--r--compiler/rustc_lint/src/passes.rs1
-rw-r--r--compiler/rustc_lint/src/unused.rs29
-rw-r--r--src/tools/miri/tests/pass/function_calls/abi_compat.rs37
-rw-r--r--tests/ui/lint/unused/unused-parens-issue-106413.rs32
-rw-r--r--tests/ui/lint/unused/unused-parens-issue-106413.stderr127
11 files changed, 233 insertions, 49 deletions
diff --git a/compiler/rustc_codegen_llvm/src/back/archive.rs b/compiler/rustc_codegen_llvm/src/back/archive.rs
index a82d2c5771a..f33075a8879 100644
--- a/compiler/rustc_codegen_llvm/src/back/archive.rs
+++ b/compiler/rustc_codegen_llvm/src/back/archive.rs
@@ -367,7 +367,7 @@ impl<'a> LlvmArchiveBuilder<'a> {
                 match addition {
                     Addition::File { path, name_in_archive } => {
                         let path = CString::new(path.to_str().unwrap())?;
-                        let name = CString::new(name_in_archive.clone())?;
+                        let name = CString::new(name_in_archive.as_bytes())?;
                         members.push(llvm::LLVMRustArchiveMemberNew(
                             path.as_ptr(),
                             name.as_ptr(),
diff --git a/compiler/rustc_codegen_llvm/src/back/lto.rs b/compiler/rustc_codegen_llvm/src/back/lto.rs
index b2d28cef899..c1d3392386c 100644
--- a/compiler/rustc_codegen_llvm/src/back/lto.rs
+++ b/compiler/rustc_codegen_llvm/src/back/lto.rs
@@ -441,7 +441,7 @@ fn thin_lto(
 
         for (i, (name, buffer)) in modules.into_iter().enumerate() {
             info!("local module: {} - {}", i, name);
-            let cname = CString::new(name.clone()).unwrap();
+            let cname = CString::new(name.as_bytes()).unwrap();
             thin_modules.push(llvm::ThinLTOModule {
                 identifier: cname.as_ptr(),
                 data: buffer.data().as_ptr(),
diff --git a/compiler/rustc_const_eval/src/interpret/terminator.rs b/compiler/rustc_const_eval/src/interpret/terminator.rs
index a016bfa5cf8..ca66c4cfb63 100644
--- a/compiler/rustc_const_eval/src/interpret/terminator.rs
+++ b/compiler/rustc_const_eval/src/interpret/terminator.rs
@@ -254,6 +254,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
     }
 
     /// Find the wrapped inner type of a transparent wrapper.
+    /// Must not be called on 1-ZST (as they don't have a uniquely defined "wrapped field").
     fn unfold_transparent(&self, layout: TyAndLayout<'tcx>) -> TyAndLayout<'tcx> {
         match layout.ty.kind() {
             ty::Adt(adt_def, _) if adt_def.repr().transparent() => {
@@ -263,11 +264,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                     let field = layout.field(self, idx);
                     if field.is_1zst() { None } else { Some(field) }
                 });
-                let Some(first) = non_1zst_fields.next() else {
-                    // All fields are 1-ZST, so this is basically the same as `()`.
-                    // (We still also compare the `PassMode`, so if this target does something strange with 1-ZST there, we'll know.)
-                    return self.layout_of(self.tcx.types.unit).unwrap();
-                };
+                let first = non_1zst_fields.next().expect("`unfold_transparent` called on 1-ZST");
                 assert!(
                     non_1zst_fields.next().is_none(),
                     "more than one non-1-ZST field in a transparent type"
@@ -289,17 +286,6 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         caller_layout: TyAndLayout<'tcx>,
         callee_layout: TyAndLayout<'tcx>,
     ) -> bool {
-        fn primitive_abi_compat(a1: abi::Primitive, a2: abi::Primitive) -> bool {
-            match (a1, a2) {
-                // For integers, ignore the sign.
-                (abi::Primitive::Int(int_ty1, _sign1), abi::Primitive::Int(int_ty2, _sign2)) => {
-                    int_ty1 == int_ty2
-                }
-                // For everything else we require full equality.
-                _ => a1 == a2,
-            }
-        }
-
         if caller_layout.ty == callee_layout.ty {
             // Fast path: equal types are definitely compatible.
             return true;
@@ -308,27 +294,40 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         match (caller_layout.abi, callee_layout.abi) {
             // If both sides have Scalar/Vector/ScalarPair ABI, we can easily directly compare them.
             // Different valid ranges are okay (the validity check will complain if this leads to
-            // invalid transmutes).
+            // invalid transmutes). Different signs are *not* okay on some targets (e.g. `extern
+            // "C"` on `s390x` where small integers are passed zero/sign-extended in large
+            // registers), so we generally reject them to increase portability.
+            // NOTE: this is *not* a stable guarantee! It just reflects a property of our current
+            // ABIs. It's also fragile; the same pair of types might be considered ABI-compatible
+            // when used directly by-value but not considered compatible as a struct field or array
+            // element.
             (abi::Abi::Scalar(caller), abi::Abi::Scalar(callee)) => {
-                primitive_abi_compat(caller.primitive(), callee.primitive())
+                caller.primitive() == callee.primitive()
             }
             (
                 abi::Abi::Vector { element: caller_element, count: caller_count },
                 abi::Abi::Vector { element: callee_element, count: callee_count },
             ) => {
-                primitive_abi_compat(caller_element.primitive(), callee_element.primitive())
+                caller_element.primitive() == callee_element.primitive()
                     && caller_count == callee_count
             }
             (abi::Abi::ScalarPair(caller1, caller2), abi::Abi::ScalarPair(callee1, callee2)) => {
-                primitive_abi_compat(caller1.primitive(), callee1.primitive())
-                    && primitive_abi_compat(caller2.primitive(), callee2.primitive())
+                caller1.primitive() == callee1.primitive()
+                    && caller2.primitive() == callee2.primitive()
             }
             (abi::Abi::Aggregate { .. }, abi::Abi::Aggregate { .. }) => {
-                // Aggregates are compatible only if they newtype-wrap the same type.
+                // Aggregates are compatible only if they newtype-wrap the same type, or if they are both 1-ZST.
+                // (The latter part is needed to ensure e.g. that `struct Zst` is compatible with `struct Wrap((), Zst)`.)
                 // This is conservative, but also means that our check isn't quite so heavily dependent on the `PassMode`,
                 // which means having ABI-compatibility on one target is much more likely to imply compatibility for other targets.
-                self.unfold_transparent(caller_layout).ty
-                    == self.unfold_transparent(callee_layout).ty
+                if caller_layout.is_1zst() || callee_layout.is_1zst() {
+                    // If either is a 1-ZST, both must be.
+                    caller_layout.is_1zst() && callee_layout.is_1zst()
+                } else {
+                    // Neither is a 1-ZST, so we can check what they are wrapping.
+                    self.unfold_transparent(caller_layout).ty
+                        == self.unfold_transparent(callee_layout).ty
+                }
             }
             // What remains is `Abi::Uninhabited` (which can never be passed anyway) and
             // mismatching ABIs, that should all be rejected.
diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs
index c2fe942fe37..9bb1a6a2b14 100644
--- a/compiler/rustc_errors/src/lib.rs
+++ b/compiler/rustc_errors/src/lib.rs
@@ -55,6 +55,8 @@ use std::num::NonZeroUsize;
 use std::panic;
 use std::path::{Path, PathBuf};
 
+// Used by external projects such as `rust-gpu`.
+// See https://github.com/rust-lang/rust/pull/115393.
 pub use termcolor::{Color, ColorSpec, WriteColor};
 
 pub mod annotate_snippet_emitter_writer;
diff --git a/compiler/rustc_feature/src/accepted.rs b/compiler/rustc_feature/src/accepted.rs
index b1f74643b69..afcf30d0b29 100644
--- a/compiler/rustc_feature/src/accepted.rs
+++ b/compiler/rustc_feature/src/accepted.rs
@@ -54,7 +54,7 @@ declare_features! (
     /// instead of just the platforms on which it is the C ABI.
     (accepted, abi_sysv64, "1.24.0", Some(36167), None),
     /// Allows using the `thiscall` ABI.
-    (accepted, abi_thiscall, "1.19.0", None, None),
+    (accepted, abi_thiscall, "1.73.0", None, None),
     /// Allows using ADX intrinsics from `core::arch::{x86, x86_64}`.
     (accepted, adx_target_feature, "1.61.0", Some(44839), None),
     /// Allows explicit discriminants on non-unit enum variants.
diff --git a/compiler/rustc_lint/src/early.rs b/compiler/rustc_lint/src/early.rs
index 211ea8f4347..d102e3a6c15 100644
--- a/compiler/rustc_lint/src/early.rs
+++ b/compiler/rustc_lint/src/early.rs
@@ -228,6 +228,7 @@ impl<'a, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a, T>
             }) => self.check_id(closure_id),
             _ => {}
         }
+        lint_callback!(self, check_expr_post, e);
     }
 
     fn visit_generic_arg(&mut self, arg: &'a ast::GenericArg) {
diff --git a/compiler/rustc_lint/src/passes.rs b/compiler/rustc_lint/src/passes.rs
index 16964565b01..fca0eeeecc4 100644
--- a/compiler/rustc_lint/src/passes.rs
+++ b/compiler/rustc_lint/src/passes.rs
@@ -153,6 +153,7 @@ macro_rules! early_lint_methods {
             fn check_pat(a: &ast::Pat);
             fn check_pat_post(a: &ast::Pat);
             fn check_expr(a: &ast::Expr);
+            fn check_expr_post(a: &ast::Expr);
             fn check_ty(a: &ast::Ty);
             fn check_generic_arg(a: &ast::GenericArg);
             fn check_generic_param(a: &ast::GenericParam);
diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs
index 6041f80753b..39e6fb805ae 100644
--- a/compiler/rustc_lint/src/unused.rs
+++ b/compiler/rustc_lint/src/unused.rs
@@ -955,11 +955,14 @@ declare_lint! {
 
 pub struct UnusedParens {
     with_self_ty_parens: bool,
+    /// `1 as (i32) < 2` parses to ExprKind::Lt
+    /// `1 as i32 < 2` parses to i32::<2[missing angle bracket]
+    parens_in_cast_in_lt: Vec<ast::NodeId>,
 }
 
 impl UnusedParens {
     pub fn new() -> Self {
-        Self { with_self_ty_parens: false }
+        Self { with_self_ty_parens: false, parens_in_cast_in_lt: Vec::new() }
     }
 }
 
@@ -1055,6 +1058,14 @@ impl UnusedParens {
 impl EarlyLintPass for UnusedParens {
     #[inline]
     fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) {
+        if let ExprKind::Binary(op, lhs, _rhs) = &e.kind &&
+            (op.node == ast::BinOpKind::Lt || op.node == ast::BinOpKind::Shl) &&
+            let ExprKind::Cast(_expr, ty) = &lhs.kind &&
+            let ast::TyKind::Paren(_) = &ty.kind
+        {
+            self.parens_in_cast_in_lt.push(ty.id);
+        }
+
         match e.kind {
             ExprKind::Let(ref pat, _, _) | ExprKind::ForLoop(ref pat, ..) => {
                 self.check_unused_parens_pat(cx, pat, false, false, (true, true));
@@ -1101,6 +1112,17 @@ impl EarlyLintPass for UnusedParens {
         <Self as UnusedDelimLint>::check_expr(self, cx, e)
     }
 
+    fn check_expr_post(&mut self, _cx: &EarlyContext<'_>, e: &ast::Expr) {
+        if let ExprKind::Binary(op, lhs, _rhs) = &e.kind &&
+            (op.node == ast::BinOpKind::Lt || op.node == ast::BinOpKind::Shl) &&
+            let ExprKind::Cast(_expr, ty) = &lhs.kind &&
+            let ast::TyKind::Paren(_) = &ty.kind
+        {
+            let id = self.parens_in_cast_in_lt.pop().expect("check_expr and check_expr_post must balance");
+            assert_eq!(id, ty.id, "check_expr, check_ty, and check_expr_post are called, in that order, by the visitor");
+        }
+    }
+
     fn check_pat(&mut self, cx: &EarlyContext<'_>, p: &ast::Pat) {
         use ast::{Mutability, PatKind::*};
         let keep_space = (false, false);
@@ -1141,6 +1163,11 @@ impl EarlyLintPass for UnusedParens {
     }
 
     fn check_ty(&mut self, cx: &EarlyContext<'_>, ty: &ast::Ty) {
+        if let ast::TyKind::Paren(_) = ty.kind &&
+            Some(&ty.id) == self.parens_in_cast_in_lt.last()
+        {
+            return;
+        }
         match &ty.kind {
             ast::TyKind::Array(_, len) => {
                 self.check_unused_delims_expr(
diff --git a/src/tools/miri/tests/pass/function_calls/abi_compat.rs b/src/tools/miri/tests/pass/function_calls/abi_compat.rs
index dc1e1f0ba8d..08be29115ca 100644
--- a/src/tools/miri/tests/pass/function_calls/abi_compat.rs
+++ b/src/tools/miri/tests/pass/function_calls/abi_compat.rs
@@ -1,9 +1,7 @@
-#![feature(portable_simd)]
 use std::mem;
 use std::num;
-use std::simd;
 
-#[derive(Copy, Clone)]
+#[derive(Copy, Clone, Default)]
 struct Zst;
 
 fn test_abi_compat<T: Copy, U: Copy>(t: T, u: U) {
@@ -33,7 +31,7 @@ fn test_abi_compat<T: Copy, U: Copy>(t: T, u: U) {
 }
 
 /// Ensure that `T` is compatible with various repr(transparent) wrappers around `T`.
-fn test_abi_newtype<T: Copy>(t: T) {
+fn test_abi_newtype<T: Copy + Default>() {
     #[repr(transparent)]
     #[derive(Copy, Clone)]
     struct Wrapper1<T>(T);
@@ -47,6 +45,7 @@ fn test_abi_newtype<T: Copy>(t: T) {
     #[derive(Copy, Clone)]
     struct Wrapper3<T>(Zst, T, [u8; 0]);
 
+    let t = T::default();
     test_abi_compat(t, Wrapper1(t));
     test_abi_compat(t, Wrapper2(t, ()));
     test_abi_compat(t, Wrapper2a((), t));
@@ -56,8 +55,7 @@ fn test_abi_newtype<T: Copy>(t: T) {
 
 fn main() {
     // Here we check:
-    // - unsigned vs signed integer is allowed
-    // - u32/i32 vs char is allowed
+    // - u32 vs char is allowed
     // - u32 vs NonZeroU32/Option<NonZeroU32> is allowed
     // - reference vs raw pointer is allowed
     // - references to things of the same size and alignment are allowed
@@ -65,27 +63,24 @@ fn main() {
     // these would be stably guaranteed. Code that relies on this is equivalent to code that relies
     // on the layout of `repr(Rust)` types. They are also fragile: the same mismatches in the fields
     // of a struct (even with `repr(C)`) will not always be accepted by Miri.
-    test_abi_compat(0u32, 0i32);
-    test_abi_compat(simd::u32x8::splat(1), simd::i32x8::splat(1));
+    // Note that `bool` and `u8` are *not* compatible, at least on x86-64!
+    // One of them has `arg_ext: Zext`, the other does not.
+    // Similarly, `i32` and `u32` are not compatible on s390x due to different `arg_ext`.
     test_abi_compat(0u32, 'x');
-    test_abi_compat(0i32, 'x');
     test_abi_compat(42u32, num::NonZeroU32::new(1).unwrap());
     test_abi_compat(0u32, Some(num::NonZeroU32::new(1).unwrap()));
     test_abi_compat(&0u32, &0u32 as *const u32);
     test_abi_compat(&0u32, &([true; 4], [0u32; 0]));
-    // Note that `bool` and `u8` are *not* compatible, at least on x86-64!
-    // One of them has `arg_ext: Zext`, the other does not.
 
     // These must work for *any* type, since we guarantee that `repr(transparent)` is ABI-compatible
     // with the wrapped field.
-    test_abi_newtype(());
-    // FIXME: this still fails! test_abi_newtype(Zst);
-    test_abi_newtype(0u32);
-    test_abi_newtype(0f32);
-    test_abi_newtype((0u32, 1u32, 2u32));
-    // FIXME: skipping the array tests on mips64 due to https://github.com/rust-lang/rust/issues/115404
-    if !cfg!(target_arch = "mips64") {
-        test_abi_newtype([0u32, 1u32, 2u32]);
-        test_abi_newtype([0i32; 0]);
-    }
+    test_abi_newtype::<()>();
+    test_abi_newtype::<Zst>();
+    test_abi_newtype::<u32>();
+    test_abi_newtype::<f32>();
+    test_abi_newtype::<(u8, u16, f32)>();
+    test_abi_newtype::<[u8; 0]>();
+    test_abi_newtype::<[u32; 0]>();
+    test_abi_newtype::<[u32; 2]>();
+    test_abi_newtype::<[u32; 32]>();
 }
diff --git a/tests/ui/lint/unused/unused-parens-issue-106413.rs b/tests/ui/lint/unused/unused-parens-issue-106413.rs
new file mode 100644
index 00000000000..7e76ab073b4
--- /dev/null
+++ b/tests/ui/lint/unused/unused-parens-issue-106413.rs
@@ -0,0 +1,32 @@
+// check-pass
+#![warn(unused_parens)]
+
+fn id<T>(t: T) -> T { t }
+
+fn main() {
+    // This should not warn
+    let _ = 1 as (i32) < 2;
+    let _ = id(1 as (i32) < 2);
+    let _ = id(1 as i32)
+        as (i32) < 2;
+    // These should warn
+    let _ = 1 as ((i32)) < 2; //~WARN unnecessary parentheses
+    let _ = 1 as (i32); //~WARN unnecessary parentheses
+    let _ = 1 as (i32) > 2; //~WARN unnecessary parentheses
+    let _ = id(1 as (i32)) //~WARN unnecessary parentheses
+        as (i32) < 2;
+    let _ = id(1 as (i32)) < 2; //~WARN unnecessary parentheses
+
+    // This should not warn
+    let _ = 1 as (i32) << 2;
+    let _ = id(1 as (i32) << 2);
+    let _ = id(1 as i32)
+        as (i32) << 2;
+    // These should warn
+    let _ = 1 as ((i32)) << 2; //~WARN unnecessary parentheses
+    let _ = 1 as (i32); //~WARN unnecessary parentheses
+    let _ = 1 as (i32) >> 2; //~WARN unnecessary parentheses
+    let _ = id(1 as (i32)) //~WARN unnecessary parentheses
+        as (i32) << 2;
+    let _ = id(1 as (i32)) << 2; //~WARN unnecessary parentheses
+}
diff --git a/tests/ui/lint/unused/unused-parens-issue-106413.stderr b/tests/ui/lint/unused/unused-parens-issue-106413.stderr
new file mode 100644
index 00000000000..d2d8f76d967
--- /dev/null
+++ b/tests/ui/lint/unused/unused-parens-issue-106413.stderr
@@ -0,0 +1,127 @@
+warning: unnecessary parentheses around type
+  --> $DIR/unused-parens-issue-106413.rs:13:19
+   |
+LL |     let _ = 1 as ((i32)) < 2;
+   |                   ^   ^
+   |
+note: the lint level is defined here
+  --> $DIR/unused-parens-issue-106413.rs:2:9
+   |
+LL | #![warn(unused_parens)]
+   |         ^^^^^^^^^^^^^
+help: remove these parentheses
+   |
+LL -     let _ = 1 as ((i32)) < 2;
+LL +     let _ = 1 as (i32) < 2;
+   |
+
+warning: unnecessary parentheses around type
+  --> $DIR/unused-parens-issue-106413.rs:14:18
+   |
+LL |     let _ = 1 as (i32);
+   |                  ^   ^
+   |
+help: remove these parentheses
+   |
+LL -     let _ = 1 as (i32);
+LL +     let _ = 1 as i32;
+   |
+
+warning: unnecessary parentheses around type
+  --> $DIR/unused-parens-issue-106413.rs:15:18
+   |
+LL |     let _ = 1 as (i32) > 2;
+   |                  ^   ^
+   |
+help: remove these parentheses
+   |
+LL -     let _ = 1 as (i32) > 2;
+LL +     let _ = 1 as i32 > 2;
+   |
+
+warning: unnecessary parentheses around type
+  --> $DIR/unused-parens-issue-106413.rs:16:21
+   |
+LL |     let _ = id(1 as (i32))
+   |                     ^   ^
+   |
+help: remove these parentheses
+   |
+LL -     let _ = id(1 as (i32))
+LL +     let _ = id(1 as i32)
+   |
+
+warning: unnecessary parentheses around type
+  --> $DIR/unused-parens-issue-106413.rs:18:21
+   |
+LL |     let _ = id(1 as (i32)) < 2;
+   |                     ^   ^
+   |
+help: remove these parentheses
+   |
+LL -     let _ = id(1 as (i32)) < 2;
+LL +     let _ = id(1 as i32) < 2;
+   |
+
+warning: unnecessary parentheses around type
+  --> $DIR/unused-parens-issue-106413.rs:26:19
+   |
+LL |     let _ = 1 as ((i32)) << 2;
+   |                   ^   ^
+   |
+help: remove these parentheses
+   |
+LL -     let _ = 1 as ((i32)) << 2;
+LL +     let _ = 1 as (i32) << 2;
+   |
+
+warning: unnecessary parentheses around type
+  --> $DIR/unused-parens-issue-106413.rs:27:18
+   |
+LL |     let _ = 1 as (i32);
+   |                  ^   ^
+   |
+help: remove these parentheses
+   |
+LL -     let _ = 1 as (i32);
+LL +     let _ = 1 as i32;
+   |
+
+warning: unnecessary parentheses around type
+  --> $DIR/unused-parens-issue-106413.rs:28:18
+   |
+LL |     let _ = 1 as (i32) >> 2;
+   |                  ^   ^
+   |
+help: remove these parentheses
+   |
+LL -     let _ = 1 as (i32) >> 2;
+LL +     let _ = 1 as i32 >> 2;
+   |
+
+warning: unnecessary parentheses around type
+  --> $DIR/unused-parens-issue-106413.rs:29:21
+   |
+LL |     let _ = id(1 as (i32))
+   |                     ^   ^
+   |
+help: remove these parentheses
+   |
+LL -     let _ = id(1 as (i32))
+LL +     let _ = id(1 as i32)
+   |
+
+warning: unnecessary parentheses around type
+  --> $DIR/unused-parens-issue-106413.rs:31:21
+   |
+LL |     let _ = id(1 as (i32)) << 2;
+   |                     ^   ^
+   |
+help: remove these parentheses
+   |
+LL -     let _ = id(1 as (i32)) << 2;
+LL +     let _ = id(1 as i32) << 2;
+   |
+
+warning: 10 warnings emitted
+