From f39eb7e658a5b52175e75d065f2e0720f1460d6f Mon Sep 17 00:00:00 2001 From: Urgau Date: Thu, 6 Jul 2023 21:45:24 +0200 Subject: Rename cast_ref_to_mut lint to invalid_reference_casting (cherry picked from commit 3dbbf23e29516218863bda29d2983bd503e6b7fd) --- compiler/rustc_lint/messages.ftl | 4 +- compiler/rustc_lint/src/cast_ref_to_mut.rs | 72 ---------------------------- compiler/rustc_lint/src/lib.rs | 6 +-- compiler/rustc_lint/src/lints.rs | 6 +-- compiler/rustc_lint/src/reference_casting.rs | 72 ++++++++++++++++++++++++++++ 5 files changed, 80 insertions(+), 80 deletions(-) delete mode 100644 compiler/rustc_lint/src/cast_ref_to_mut.rs create mode 100644 compiler/rustc_lint/src/reference_casting.rs (limited to 'compiler') diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index 34b7e09576a..a3453cbc026 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -155,8 +155,6 @@ lint_builtin_unused_doc_comment = unused doc comment lint_builtin_while_true = denote infinite loops with `loop {"{"} ... {"}"}` .suggestion = use `loop` -lint_cast_ref_to_mut = casting `&T` to `&mut T` is undefined behavior, even if the reference is unused, consider instead using an `UnsafeCell` - lint_check_name_deprecated = lint name `{$lint_name}` is deprecated and does not have an effect anymore. Use: {$new_name} lint_check_name_unknown = unknown lint: `{$lint_name}` @@ -319,6 +317,8 @@ lint_invalid_nan_comparisons_eq_ne = incorrect NaN comparison, NaN cannot be dir lint_invalid_nan_comparisons_lt_le_gt_ge = incorrect NaN comparison, NaN is not orderable +lint_invalid_reference_casting = casting `&T` to `&mut T` is undefined behavior, even if the reference is unused, consider instead using an `UnsafeCell` + lint_lintpass_by_hand = implementing `LintPass` by hand .help = try using `declare_lint_pass!` or `impl_lint_pass!` instead diff --git a/compiler/rustc_lint/src/cast_ref_to_mut.rs b/compiler/rustc_lint/src/cast_ref_to_mut.rs deleted file mode 100644 index 84308d48c10..00000000000 --- a/compiler/rustc_lint/src/cast_ref_to_mut.rs +++ /dev/null @@ -1,72 +0,0 @@ -use rustc_ast::Mutability; -use rustc_hir::{Expr, ExprKind, MutTy, TyKind, UnOp}; -use rustc_middle::ty; -use rustc_span::sym; - -use crate::{lints::CastRefToMutDiag, LateContext, LateLintPass, LintContext}; - -declare_lint! { - /// The `cast_ref_to_mut` lint checks for casts of `&T` to `&mut T` - /// without using interior mutability. - /// - /// ### Example - /// - /// ```rust,compile_fail - /// fn x(r: &i32) { - /// unsafe { - /// *(r as *const i32 as *mut i32) += 1; - /// } - /// } - /// ``` - /// - /// {{produces}} - /// - /// ### Explanation - /// - /// Casting `&T` to `&mut T` without using interior mutability is undefined behavior, - /// as it's a violation of Rust reference aliasing requirements. - /// - /// `UnsafeCell` is the only way to obtain aliasable data that is considered - /// mutable. - CAST_REF_TO_MUT, - Deny, - "casts of `&T` to `&mut T` without interior mutability" -} - -declare_lint_pass!(CastRefToMut => [CAST_REF_TO_MUT]); - -impl<'tcx> LateLintPass<'tcx> for CastRefToMut { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { - let ExprKind::Unary(UnOp::Deref, e) = &expr.kind else { return; }; - - let e = e.peel_blocks(); - let e = if let ExprKind::Cast(e, t) = e.kind - && let TyKind::Ptr(MutTy { mutbl: Mutability::Mut, .. }) = t.kind { - e - } else if let ExprKind::MethodCall(_, expr, [], _) = e.kind - && let Some(def_id) = cx.typeck_results().type_dependent_def_id(e.hir_id) - && cx.tcx.is_diagnostic_item(sym::ptr_cast_mut, def_id) { - expr - } else { - return; - }; - - let e = e.peel_blocks(); - let e = if let ExprKind::Cast(e, t) = e.kind - && let TyKind::Ptr(MutTy { mutbl: Mutability::Not, .. }) = t.kind { - e - } else if let ExprKind::Call(path, [arg]) = e.kind - && let ExprKind::Path(ref qpath) = path.kind - && let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id() - && cx.tcx.is_diagnostic_item(sym::ptr_from_ref, def_id) { - arg - } else { - return; - }; - - let e = e.peel_blocks(); - if let ty::Ref(..) = cx.typeck_results().node_type(e.hir_id).kind() { - cx.emit_spanned_lint(CAST_REF_TO_MUT, expr.span, CastRefToMutDiag); - } - } -} diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 5e3f057d428..602071a55e5 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -50,7 +50,6 @@ extern crate tracing; mod array_into_iter; pub mod builtin; -mod cast_ref_to_mut; mod context; mod deref_into_dyn_supertrait; mod drop_forget_useless; @@ -77,6 +76,7 @@ mod opaque_hidden_inferred_bound; mod pass_by_value; mod passes; mod redundant_semicolon; +mod reference_casting; mod traits; mod types; mod unused; @@ -98,7 +98,6 @@ use rustc_span::Span; use array_into_iter::ArrayIntoIter; use builtin::*; -use cast_ref_to_mut::*; use deref_into_dyn_supertrait::*; use drop_forget_useless::*; use enum_intrinsics_non_enums::EnumIntrinsicsNonEnums; @@ -117,6 +116,7 @@ use noop_method_call::*; use opaque_hidden_inferred_bound::*; use pass_by_value::*; use redundant_semicolon::*; +use reference_casting::*; use traits::*; use types::*; use unused::*; @@ -216,7 +216,7 @@ late_lint_methods!( BoxPointers: BoxPointers, PathStatements: PathStatements, LetUnderscore: LetUnderscore, - CastRefToMut: CastRefToMut, + InvalidReferenceCasting: InvalidReferenceCasting, // Depends on referenced function signatures in expressions UnusedResults: UnusedResults, NonUpperCaseGlobals: NonUpperCaseGlobals, diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 0613ef2c5e9..9260237fbee 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -737,10 +737,10 @@ pub enum InvalidFromUtf8Diag { }, } -// cast_ref_to_mut.rs +// reference_casting.rs #[derive(LintDiagnostic)] -#[diag(lint_cast_ref_to_mut)] -pub struct CastRefToMutDiag; +#[diag(lint_invalid_reference_casting)] +pub struct InvalidReferenceCastingDiag; // hidden_unicode_codepoints.rs #[derive(LintDiagnostic)] diff --git a/compiler/rustc_lint/src/reference_casting.rs b/compiler/rustc_lint/src/reference_casting.rs new file mode 100644 index 00000000000..34b916eef90 --- /dev/null +++ b/compiler/rustc_lint/src/reference_casting.rs @@ -0,0 +1,72 @@ +use rustc_ast::Mutability; +use rustc_hir::{Expr, ExprKind, MutTy, TyKind, UnOp}; +use rustc_middle::ty; +use rustc_span::sym; + +use crate::{lints::InvalidReferenceCastingDiag, LateContext, LateLintPass, LintContext}; + +declare_lint! { + /// The `invalid_reference_casting` lint checks for casts of `&T` to `&mut T` + /// without using interior mutability. + /// + /// ### Example + /// + /// ```rust,compile_fail + /// fn x(r: &i32) { + /// unsafe { + /// *(r as *const i32 as *mut i32) += 1; + /// } + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Casting `&T` to `&mut T` without using interior mutability is undefined behavior, + /// as it's a violation of Rust reference aliasing requirements. + /// + /// `UnsafeCell` is the only way to obtain aliasable data that is considered + /// mutable. + INVALID_REFERENCE_CASTING, + Deny, + "casts of `&T` to `&mut T` without interior mutability" +} + +declare_lint_pass!(InvalidReferenceCasting => [INVALID_REFERENCE_CASTING]); + +impl<'tcx> LateLintPass<'tcx> for InvalidReferenceCasting { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { + let ExprKind::Unary(UnOp::Deref, e) = &expr.kind else { return; }; + + let e = e.peel_blocks(); + let e = if let ExprKind::Cast(e, t) = e.kind + && let TyKind::Ptr(MutTy { mutbl: Mutability::Mut, .. }) = t.kind { + e + } else if let ExprKind::MethodCall(_, expr, [], _) = e.kind + && let Some(def_id) = cx.typeck_results().type_dependent_def_id(e.hir_id) + && cx.tcx.is_diagnostic_item(sym::ptr_cast_mut, def_id) { + expr + } else { + return; + }; + + let e = e.peel_blocks(); + let e = if let ExprKind::Cast(e, t) = e.kind + && let TyKind::Ptr(MutTy { mutbl: Mutability::Not, .. }) = t.kind { + e + } else if let ExprKind::Call(path, [arg]) = e.kind + && let ExprKind::Path(ref qpath) = path.kind + && let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id() + && cx.tcx.is_diagnostic_item(sym::ptr_from_ref, def_id) { + arg + } else { + return; + }; + + let e = e.peel_blocks(); + if let ty::Ref(..) = cx.typeck_results().node_type(e.hir_id).kind() { + cx.emit_spanned_lint(INVALID_REFERENCE_CASTING, expr.span, InvalidReferenceCastingDiag); + } + } +} -- cgit 1.4.1-3-g733a5 From e20288d2afa4be259454eae32bcf492330c7c393 Mon Sep 17 00:00:00 2001 From: Urgau Date: Thu, 6 Jul 2023 21:50:34 +0200 Subject: Temporarily switch invalid_reference_casting lint to allow-by-default (cherry picked from commit f25ad54a4d0febbcb2b7e951835228b7b2320b49) --- compiler/rustc_lint/src/reference_casting.rs | 3 ++- tests/ui/const-generics/issues/issue-100313.rs | 1 - tests/ui/const-generics/issues/issue-100313.stderr | 12 ++-------- tests/ui/lint/reference_casting.rs | 1 + tests/ui/lint/reference_casting.stderr | 26 +++++++++++++--------- 5 files changed, 20 insertions(+), 23 deletions(-) (limited to 'compiler') diff --git a/compiler/rustc_lint/src/reference_casting.rs b/compiler/rustc_lint/src/reference_casting.rs index 34b916eef90..db8d7302a25 100644 --- a/compiler/rustc_lint/src/reference_casting.rs +++ b/compiler/rustc_lint/src/reference_casting.rs @@ -12,6 +12,7 @@ declare_lint! { /// ### Example /// /// ```rust,compile_fail + /// # #![deny(invalid_reference_casting)] /// fn x(r: &i32) { /// unsafe { /// *(r as *const i32 as *mut i32) += 1; @@ -29,7 +30,7 @@ declare_lint! { /// `UnsafeCell` is the only way to obtain aliasable data that is considered /// mutable. INVALID_REFERENCE_CASTING, - Deny, + Allow, "casts of `&T` to `&mut T` without interior mutability" } diff --git a/tests/ui/const-generics/issues/issue-100313.rs b/tests/ui/const-generics/issues/issue-100313.rs index 9a9d4721c84..4e9d3626aa8 100644 --- a/tests/ui/const-generics/issues/issue-100313.rs +++ b/tests/ui/const-generics/issues/issue-100313.rs @@ -9,7 +9,6 @@ impl T { unsafe { *(B as *const bool as *mut bool) = false; //~^ ERROR evaluation of constant value failed [E0080] - //~| ERROR casting `&T` to `&mut T` is undefined behavior } } } diff --git a/tests/ui/const-generics/issues/issue-100313.stderr b/tests/ui/const-generics/issues/issue-100313.stderr index 17e4850bd12..d4b486376ca 100644 --- a/tests/ui/const-generics/issues/issue-100313.stderr +++ b/tests/ui/const-generics/issues/issue-100313.stderr @@ -1,11 +1,3 @@ -error: casting `&T` to `&mut T` is undefined behavior, even if the reference is unused, consider instead using an `UnsafeCell` - --> $DIR/issue-100313.rs:10:13 - | -LL | *(B as *const bool as *mut bool) = false; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: `#[deny(invalid_reference_casting)]` on by default - error[E0080]: evaluation of constant value failed --> $DIR/issue-100313.rs:10:13 | @@ -18,11 +10,11 @@ note: inside `T::<&true>::set_false` LL | *(B as *const bool as *mut bool) = false; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: inside `_` - --> $DIR/issue-100313.rs:19:5 + --> $DIR/issue-100313.rs:18:5 | LL | x.set_false(); | ^^^^^^^^^^^^^ -error: aborting due to 2 previous errors +error: aborting due to previous error For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/lint/reference_casting.rs b/tests/ui/lint/reference_casting.rs index 745d7070143..9963820499e 100644 --- a/tests/ui/lint/reference_casting.rs +++ b/tests/ui/lint/reference_casting.rs @@ -1,6 +1,7 @@ // check-fail #![feature(ptr_from_ref)] +#![deny(invalid_reference_casting)] extern "C" { // N.B., mutability can be easily incorrect in FFI calls -- as diff --git a/tests/ui/lint/reference_casting.stderr b/tests/ui/lint/reference_casting.stderr index d1dd1b32ff4..d5b9bbef643 100644 --- a/tests/ui/lint/reference_casting.stderr +++ b/tests/ui/lint/reference_casting.stderr @@ -1,61 +1,65 @@ error: casting `&T` to `&mut T` is undefined behavior, even if the reference is unused, consider instead using an `UnsafeCell` - --> $DIR/reference_casting.rs:19:9 + --> $DIR/reference_casting.rs:20:9 | LL | (*(a as *const _ as *mut String)).push_str(" world"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: `#[deny(invalid_reference_casting)]` on by default +note: the lint level is defined here + --> $DIR/reference_casting.rs:4:9 + | +LL | #![deny(invalid_reference_casting)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: casting `&T` to `&mut T` is undefined behavior, even if the reference is unused, consider instead using an `UnsafeCell` - --> $DIR/reference_casting.rs:21:9 + --> $DIR/reference_casting.rs:22:9 | LL | *(a as *const _ as *mut _) = String::from("Replaced"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: casting `&T` to `&mut T` is undefined behavior, even if the reference is unused, consider instead using an `UnsafeCell` - --> $DIR/reference_casting.rs:23:9 + --> $DIR/reference_casting.rs:24:9 | LL | *(a as *const _ as *mut String) += " world"; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: casting `&T` to `&mut T` is undefined behavior, even if the reference is unused, consider instead using an `UnsafeCell` - --> $DIR/reference_casting.rs:25:25 + --> $DIR/reference_casting.rs:26:25 | LL | let _num = &mut *(num as *const i32 as *mut i32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: casting `&T` to `&mut T` is undefined behavior, even if the reference is unused, consider instead using an `UnsafeCell` - --> $DIR/reference_casting.rs:27:25 + --> $DIR/reference_casting.rs:28:25 | LL | let _num = &mut *(num as *const i32).cast_mut(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: casting `&T` to `&mut T` is undefined behavior, even if the reference is unused, consider instead using an `UnsafeCell` - --> $DIR/reference_casting.rs:29:20 + --> $DIR/reference_casting.rs:30:20 | LL | let _num = *{ num as *const i32 }.cast_mut(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: casting `&T` to `&mut T` is undefined behavior, even if the reference is unused, consider instead using an `UnsafeCell` - --> $DIR/reference_casting.rs:31:9 + --> $DIR/reference_casting.rs:32:9 | LL | *std::ptr::from_ref(num).cast_mut() += 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: casting `&T` to `&mut T` is undefined behavior, even if the reference is unused, consider instead using an `UnsafeCell` - --> $DIR/reference_casting.rs:33:9 + --> $DIR/reference_casting.rs:34:9 | LL | *std::ptr::from_ref({ num }).cast_mut() += 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: casting `&T` to `&mut T` is undefined behavior, even if the reference is unused, consider instead using an `UnsafeCell` - --> $DIR/reference_casting.rs:35:9 + --> $DIR/reference_casting.rs:36:9 | LL | *{ std::ptr::from_ref(num) }.cast_mut() += 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: casting `&T` to `&mut T` is undefined behavior, even if the reference is unused, consider instead using an `UnsafeCell` - --> $DIR/reference_casting.rs:37:9 + --> $DIR/reference_casting.rs:38:9 | LL | *(std::ptr::from_ref({ num }) as *mut i32) += 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -- cgit 1.4.1-3-g733a5 From 7744a86963216f9e5b4171997563a8fe293fc2ca Mon Sep 17 00:00:00 2001 From: David Wood Date: Fri, 7 Jul 2023 18:27:16 +0100 Subject: lint/ctypes: stricter `()` return type checks `()` is normally FFI-unsafe, but is FFI-safe when used as a return type. It is also desirable that a transparent newtype for `()` is FFI-safe when used as a return type. In order to support this, when an type was deemed FFI-unsafe, because of a `()` type, and was used in return type - then the type was considered FFI-safe. However, this was the wrong approach - it didn't check that the `()` was part of a transparent newtype! The consequence of this is that the presence of a `()` type in a more complex return type would make it the entire type be considered safe (as long as the `()` type was the first that the lint found) - which is obviously incorrect. Instead, this logic is removed, and a unit return type or a transparent wrapper around a unit is checked for directly for functions and fn-ptrs. Signed-off-by: David Wood (cherry picked from commit f53cef31f5ea41c9a5ab8e5335637a6873b9f0b5) --- compiler/rustc_lint/src/types.rs | 55 +++++++++++++++++++++------------ tests/ui/lint/lint-ctypes-113436.rs | 37 ++++++++++++++++++++++ tests/ui/lint/lint-ctypes-113436.stderr | 43 ++++++++++++++++++++++++++ 3 files changed, 115 insertions(+), 20 deletions(-) create mode 100644 tests/ui/lint/lint-ctypes-113436.rs create mode 100644 tests/ui/lint/lint-ctypes-113436.stderr (limited to 'compiler') diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index 2509d493a4c..cabd7f4ad41 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -940,6 +940,30 @@ pub(crate) fn repr_nullable_ptr<'tcx>( } impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { + // Returns `true` if `ty` is a `()`, or a `repr(transparent)` type whose only non-ZST field + // is a generic substituted for `()` - in either case, the type is FFI-safe when used as a + // return type. + pub fn is_unit_or_equivalent(&self, ty: Ty<'tcx>) -> bool { + if ty.is_unit() { + return true; + } + + if let ty::Adt(def, substs) = ty.kind() && def.repr().transparent() { + return def.variants() + .iter() + .filter_map(|variant| transparent_newtype_field(self.cx.tcx, variant)) + .all(|field| { + let field_ty = field.ty(self.cx.tcx, substs); + !field_ty.has_opaque_types() && { + let field_ty = self.cx.tcx.normalize_erasing_regions(self.cx.param_env, field_ty); + self.is_unit_or_equivalent(field_ty) + } + }); + } + + false + } + /// Check if the type is array and emit an unsafe type lint. fn check_for_array_ty(&mut self, sp: Span, ty: Ty<'tcx>) -> bool { if let ty::Array(..) = ty.kind() { @@ -1217,25 +1241,19 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { } let sig = tcx.erase_late_bound_regions(sig); - if !sig.output().is_unit() { - let r = self.check_type_for_ffi(cache, sig.output()); - match r { - FfiSafe => {} - _ => { - return r; - } - } - } for arg in sig.inputs() { - let r = self.check_type_for_ffi(cache, *arg); - match r { + match self.check_type_for_ffi(cache, *arg) { FfiSafe => {} - _ => { - return r; - } + r => return r, } } - FfiSafe + + let ret_ty = sig.output(); + if self.is_unit_or_equivalent(ret_ty) { + return FfiSafe; + } + + self.check_type_for_ffi(cache, ret_ty) } ty::Foreign(..) => FfiSafe, @@ -1354,9 +1372,9 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { } // Don't report FFI errors for unit return types. This check exists here, and not in - // `check_foreign_fn` (where it would make more sense) so that normalization has definitely + // the caller (where it would make more sense) so that normalization has definitely // happened. - if is_return_type && ty.is_unit() { + if is_return_type && self.is_unit_or_equivalent(ty) { return; } @@ -1370,9 +1388,6 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { None, ); } - // If `ty` is a `repr(transparent)` newtype, and the non-zero-sized type is a generic - // argument, which after substitution, is `()`, then this branch can be hit. - FfiResult::FfiUnsafe { ty, .. } if is_return_type && ty.is_unit() => {} FfiResult::FfiUnsafe { ty, reason, help } => { self.emit_ffi_unsafe_type_lint(ty, sp, reason, help); } diff --git a/tests/ui/lint/lint-ctypes-113436.rs b/tests/ui/lint/lint-ctypes-113436.rs new file mode 100644 index 00000000000..d1328af9924 --- /dev/null +++ b/tests/ui/lint/lint-ctypes-113436.rs @@ -0,0 +1,37 @@ +#![deny(improper_ctypes_definitions)] + +#[repr(C)] +pub struct Wrap(T); + +#[repr(transparent)] +pub struct TransparentWrap(T); + +pub extern "C" fn f() -> Wrap<()> { + //~^ ERROR `extern` fn uses type `()`, which is not FFI-safe + todo!() +} + +const _: extern "C" fn() -> Wrap<()> = f; +//~^ ERROR `extern` fn uses type `()`, which is not FFI-safe + +pub extern "C" fn ff() -> Wrap> { + //~^ ERROR `extern` fn uses type `()`, which is not FFI-safe + todo!() +} + +const _: extern "C" fn() -> Wrap> = ff; +//~^ ERROR `extern` fn uses type `()`, which is not FFI-safe + +pub extern "C" fn g() -> TransparentWrap<()> { + todo!() +} + +const _: extern "C" fn() -> TransparentWrap<()> = g; + +pub extern "C" fn gg() -> TransparentWrap> { + todo!() +} + +const _: extern "C" fn() -> TransparentWrap> = gg; + +fn main() {} diff --git a/tests/ui/lint/lint-ctypes-113436.stderr b/tests/ui/lint/lint-ctypes-113436.stderr new file mode 100644 index 00000000000..72f92c1a49c --- /dev/null +++ b/tests/ui/lint/lint-ctypes-113436.stderr @@ -0,0 +1,43 @@ +error: `extern` fn uses type `()`, which is not FFI-safe + --> $DIR/lint-ctypes-113436.rs:9:26 + | +LL | pub extern "C" fn f() -> Wrap<()> { + | ^^^^^^^^ not FFI-safe + | + = help: consider using a struct instead + = note: tuples have unspecified layout +note: the lint level is defined here + --> $DIR/lint-ctypes-113436.rs:1:9 + | +LL | #![deny(improper_ctypes_definitions)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: `extern` fn uses type `()`, which is not FFI-safe + --> $DIR/lint-ctypes-113436.rs:14:10 + | +LL | const _: extern "C" fn() -> Wrap<()> = f; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe + | + = help: consider using a struct instead + = note: tuples have unspecified layout + +error: `extern` fn uses type `()`, which is not FFI-safe + --> $DIR/lint-ctypes-113436.rs:17:27 + | +LL | pub extern "C" fn ff() -> Wrap> { + | ^^^^^^^^^^^^^^ not FFI-safe + | + = help: consider using a struct instead + = note: tuples have unspecified layout + +error: `extern` fn uses type `()`, which is not FFI-safe + --> $DIR/lint-ctypes-113436.rs:22:10 + | +LL | const _: extern "C" fn() -> Wrap> = ff; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe + | + = help: consider using a struct instead + = note: tuples have unspecified layout + +error: aborting due to 4 previous errors + -- cgit 1.4.1-3-g733a5 From e9cd65033115298611f92982859d88a28ac3419d Mon Sep 17 00:00:00 2001 From: David Wood Date: Fri, 7 Jul 2023 19:18:28 +0100 Subject: lint: refactor `check_variant_for_ffi` Simplify this function a bit, it was quite hard to reason about. Signed-off-by: David Wood (cherry picked from commit 99b1897cf640d5f6dac74416761c9b3c75e1ef7a) --- compiler/rustc_lint/messages.ftl | 2 - compiler/rustc_lint/src/types.rs | 47 +++++++++++------------ tests/ui/repr/repr-transparent-issue-87496.rs | 2 +- tests/ui/repr/repr-transparent-issue-87496.stderr | 10 ++--- 4 files changed, 26 insertions(+), 35 deletions(-) (limited to 'compiler') diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index a3453cbc026..22e22c833ac 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -262,8 +262,6 @@ lint_improper_ctypes_char_help = consider using `u32` or `libc::wchar_t` instead lint_improper_ctypes_char_reason = the `char` type has no C equivalent lint_improper_ctypes_dyn = trait objects have no C equivalent -lint_improper_ctypes_enum_phantomdata = this enum contains a PhantomData field - lint_improper_ctypes_enum_repr_help = consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index cabd7f4ad41..327e226a9f1 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -1006,39 +1006,36 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { ) -> FfiResult<'tcx> { use FfiResult::*; - let transparent_safety = def.repr().transparent().then(|| { - // Can assume that at most one field is not a ZST, so only check - // that field's type for FFI-safety. + let transparent_with_all_zst_fields = if def.repr().transparent() { + // Transparent newtypes have at most one non-ZST field which needs to be checked.. if let Some(field) = transparent_newtype_field(self.cx.tcx, variant) { return self.check_field_type_for_ffi(cache, field, substs); - } else { - // All fields are ZSTs; this means that the type should behave - // like (), which is FFI-unsafe... except if all fields are PhantomData, - // which is tested for below - FfiUnsafe { ty, reason: fluent::lint_improper_ctypes_struct_zst, help: None } } - }); - // We can't completely trust repr(C) markings; make sure the fields are - // actually safe. + + // ..or have only ZST fields, which is FFI-unsafe (unless those fields are all + // `PhantomData`). + true + } else { + false + }; + + // We can't completely trust `repr(C)` markings, so make sure the fields are actually safe. let mut all_phantom = !variant.fields.is_empty(); for field in &variant.fields { - match self.check_field_type_for_ffi(cache, &field, substs) { - FfiSafe => { - all_phantom = false; - } - FfiPhantom(..) if !def.repr().transparent() && def.is_enum() => { - return FfiUnsafe { - ty, - reason: fluent::lint_improper_ctypes_enum_phantomdata, - help: None, - }; - } - FfiPhantom(..) => {} - r => return transparent_safety.unwrap_or(r), + all_phantom &= match self.check_field_type_for_ffi(cache, &field, substs) { + FfiSafe => false, + FfiPhantom(..) => true, + r @ FfiUnsafe { .. } => return r, } } - if all_phantom { FfiPhantom(ty) } else { transparent_safety.unwrap_or(FfiSafe) } + if all_phantom { + FfiPhantom(ty) + } else if transparent_with_all_zst_fields { + FfiUnsafe { ty, reason: fluent::lint_improper_ctypes_struct_zst, help: None } + } else { + FfiSafe + } } /// Checks if the given type is "ffi-safe" (has a stable, well-defined diff --git a/tests/ui/repr/repr-transparent-issue-87496.rs b/tests/ui/repr/repr-transparent-issue-87496.rs index a4dd45c63f5..0ce6fb2c19f 100644 --- a/tests/ui/repr/repr-transparent-issue-87496.rs +++ b/tests/ui/repr/repr-transparent-issue-87496.rs @@ -6,7 +6,7 @@ struct TransparentCustomZst(()); extern "C" { fn good17(p: TransparentCustomZst); - //~^ WARNING: `extern` block uses type `TransparentCustomZst`, which is not FFI-safe + //~^ WARNING: `extern` block uses type `()`, which is not FFI-safe } fn main() {} diff --git a/tests/ui/repr/repr-transparent-issue-87496.stderr b/tests/ui/repr/repr-transparent-issue-87496.stderr index aee31212b4e..03c62f8514e 100644 --- a/tests/ui/repr/repr-transparent-issue-87496.stderr +++ b/tests/ui/repr/repr-transparent-issue-87496.stderr @@ -1,15 +1,11 @@ -warning: `extern` block uses type `TransparentCustomZst`, which is not FFI-safe +warning: `extern` block uses type `()`, which is not FFI-safe --> $DIR/repr-transparent-issue-87496.rs:8:18 | LL | fn good17(p: TransparentCustomZst); | ^^^^^^^^^^^^^^^^^^^^ not FFI-safe | - = note: this struct contains only zero-sized fields -note: the type is defined here - --> $DIR/repr-transparent-issue-87496.rs:6:1 - | -LL | struct TransparentCustomZst(()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = help: consider using a struct instead + = note: tuples have unspecified layout = note: `#[warn(improper_ctypes)]` on by default warning: 1 warning emitted -- cgit 1.4.1-3-g733a5 From 91e377c286933d561211a347d504c9c14c8ca8d2 Mon Sep 17 00:00:00 2001 From: David Wood Date: Wed, 19 Jul 2023 09:42:11 +0100 Subject: lint/ctypes: allow `()` within types Consider `()` within types to be FFI-safe, and `()` to be FFI-safe as a return type (incl. when in a transparent newtype). Signed-off-by: David Wood (cherry picked from commit 24f90fdd2654e9c5437a684d3a72a4e70826a985) --- compiler/rustc_lint/src/types.rs | 47 ++++++++--------------- tests/ui/lint/lint-ctypes-113436-1.rs | 28 ++++++++++++++ tests/ui/lint/lint-ctypes-113436-1.stderr | 35 +++++++++++++++++ tests/ui/lint/lint-ctypes-113436.rs | 5 +-- tests/ui/lint/lint-ctypes-113436.stderr | 43 --------------------- tests/ui/repr/repr-transparent-issue-87496.rs | 2 +- tests/ui/repr/repr-transparent-issue-87496.stderr | 10 +++-- 7 files changed, 87 insertions(+), 83 deletions(-) create mode 100644 tests/ui/lint/lint-ctypes-113436-1.rs create mode 100644 tests/ui/lint/lint-ctypes-113436-1.stderr delete mode 100644 tests/ui/lint/lint-ctypes-113436.stderr (limited to 'compiler') diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index 327e226a9f1..99430794668 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -940,30 +940,6 @@ pub(crate) fn repr_nullable_ptr<'tcx>( } impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { - // Returns `true` if `ty` is a `()`, or a `repr(transparent)` type whose only non-ZST field - // is a generic substituted for `()` - in either case, the type is FFI-safe when used as a - // return type. - pub fn is_unit_or_equivalent(&self, ty: Ty<'tcx>) -> bool { - if ty.is_unit() { - return true; - } - - if let ty::Adt(def, substs) = ty.kind() && def.repr().transparent() { - return def.variants() - .iter() - .filter_map(|variant| transparent_newtype_field(self.cx.tcx, variant)) - .all(|field| { - let field_ty = field.ty(self.cx.tcx, substs); - !field_ty.has_opaque_types() && { - let field_ty = self.cx.tcx.normalize_erasing_regions(self.cx.param_env, field_ty); - self.is_unit_or_equivalent(field_ty) - } - }); - } - - false - } - /// Check if the type is array and emit an unsafe type lint. fn check_for_array_ty(&mut self, sp: Span, ty: Ty<'tcx>) -> bool { if let ty::Array(..) = ty.kind() { @@ -1007,14 +983,19 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { use FfiResult::*; let transparent_with_all_zst_fields = if def.repr().transparent() { - // Transparent newtypes have at most one non-ZST field which needs to be checked.. if let Some(field) = transparent_newtype_field(self.cx.tcx, variant) { - return self.check_field_type_for_ffi(cache, field, substs); - } + // Transparent newtypes have at most one non-ZST field which needs to be checked.. + match self.check_field_type_for_ffi(cache, field, substs) { + FfiUnsafe { ty, .. } if ty.is_unit() => (), + r => return r, + } - // ..or have only ZST fields, which is FFI-unsafe (unless those fields are all - // `PhantomData`). - true + false + } else { + // ..or have only ZST fields, which is FFI-unsafe (unless those fields are all + // `PhantomData`). + true + } } else { false }; @@ -1024,6 +1005,8 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { for field in &variant.fields { all_phantom &= match self.check_field_type_for_ffi(cache, &field, substs) { FfiSafe => false, + // `()` fields are FFI-safe! + FfiUnsafe { ty, .. } if ty.is_unit() => false, FfiPhantom(..) => true, r @ FfiUnsafe { .. } => return r, } @@ -1246,7 +1229,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { } let ret_ty = sig.output(); - if self.is_unit_or_equivalent(ret_ty) { + if ret_ty.is_unit() { return FfiSafe; } @@ -1371,7 +1354,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { // Don't report FFI errors for unit return types. This check exists here, and not in // the caller (where it would make more sense) so that normalization has definitely // happened. - if is_return_type && self.is_unit_or_equivalent(ty) { + if is_return_type && ty.is_unit() { return; } diff --git a/tests/ui/lint/lint-ctypes-113436-1.rs b/tests/ui/lint/lint-ctypes-113436-1.rs new file mode 100644 index 00000000000..1ca59c6868d --- /dev/null +++ b/tests/ui/lint/lint-ctypes-113436-1.rs @@ -0,0 +1,28 @@ +#![deny(improper_ctypes_definitions)] + +#[repr(C)] +pub struct Foo { + a: u8, + b: (), +} + +extern "C" fn foo(x: Foo) -> Foo { + todo!() +} + +struct NotSafe(u32); + +#[repr(C)] +pub struct Bar { + a: u8, + b: (), + c: NotSafe, +} + +extern "C" fn bar(x: Bar) -> Bar { + //~^ ERROR `extern` fn uses type `NotSafe`, which is not FFI-safe + //~^^ ERROR `extern` fn uses type `NotSafe`, which is not FFI-safe + todo!() +} + +fn main() {} diff --git a/tests/ui/lint/lint-ctypes-113436-1.stderr b/tests/ui/lint/lint-ctypes-113436-1.stderr new file mode 100644 index 00000000000..7b63043f057 --- /dev/null +++ b/tests/ui/lint/lint-ctypes-113436-1.stderr @@ -0,0 +1,35 @@ +error: `extern` fn uses type `NotSafe`, which is not FFI-safe + --> $DIR/lint-ctypes-113436-1.rs:22:22 + | +LL | extern "C" fn bar(x: Bar) -> Bar { + | ^^^ not FFI-safe + | + = help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct + = note: this struct has unspecified layout +note: the type is defined here + --> $DIR/lint-ctypes-113436-1.rs:13:1 + | +LL | struct NotSafe(u32); + | ^^^^^^^^^^^^^^ +note: the lint level is defined here + --> $DIR/lint-ctypes-113436-1.rs:1:9 + | +LL | #![deny(improper_ctypes_definitions)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: `extern` fn uses type `NotSafe`, which is not FFI-safe + --> $DIR/lint-ctypes-113436-1.rs:22:30 + | +LL | extern "C" fn bar(x: Bar) -> Bar { + | ^^^ not FFI-safe + | + = help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct + = note: this struct has unspecified layout +note: the type is defined here + --> $DIR/lint-ctypes-113436-1.rs:13:1 + | +LL | struct NotSafe(u32); + | ^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/tests/ui/lint/lint-ctypes-113436.rs b/tests/ui/lint/lint-ctypes-113436.rs index d1328af9924..4f733b5bb16 100644 --- a/tests/ui/lint/lint-ctypes-113436.rs +++ b/tests/ui/lint/lint-ctypes-113436.rs @@ -1,3 +1,4 @@ +// check-pass #![deny(improper_ctypes_definitions)] #[repr(C)] @@ -7,20 +8,16 @@ pub struct Wrap(T); pub struct TransparentWrap(T); pub extern "C" fn f() -> Wrap<()> { - //~^ ERROR `extern` fn uses type `()`, which is not FFI-safe todo!() } const _: extern "C" fn() -> Wrap<()> = f; -//~^ ERROR `extern` fn uses type `()`, which is not FFI-safe pub extern "C" fn ff() -> Wrap> { - //~^ ERROR `extern` fn uses type `()`, which is not FFI-safe todo!() } const _: extern "C" fn() -> Wrap> = ff; -//~^ ERROR `extern` fn uses type `()`, which is not FFI-safe pub extern "C" fn g() -> TransparentWrap<()> { todo!() diff --git a/tests/ui/lint/lint-ctypes-113436.stderr b/tests/ui/lint/lint-ctypes-113436.stderr deleted file mode 100644 index 72f92c1a49c..00000000000 --- a/tests/ui/lint/lint-ctypes-113436.stderr +++ /dev/null @@ -1,43 +0,0 @@ -error: `extern` fn uses type `()`, which is not FFI-safe - --> $DIR/lint-ctypes-113436.rs:9:26 - | -LL | pub extern "C" fn f() -> Wrap<()> { - | ^^^^^^^^ not FFI-safe - | - = help: consider using a struct instead - = note: tuples have unspecified layout -note: the lint level is defined here - --> $DIR/lint-ctypes-113436.rs:1:9 - | -LL | #![deny(improper_ctypes_definitions)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: `extern` fn uses type `()`, which is not FFI-safe - --> $DIR/lint-ctypes-113436.rs:14:10 - | -LL | const _: extern "C" fn() -> Wrap<()> = f; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe - | - = help: consider using a struct instead - = note: tuples have unspecified layout - -error: `extern` fn uses type `()`, which is not FFI-safe - --> $DIR/lint-ctypes-113436.rs:17:27 - | -LL | pub extern "C" fn ff() -> Wrap> { - | ^^^^^^^^^^^^^^ not FFI-safe - | - = help: consider using a struct instead - = note: tuples have unspecified layout - -error: `extern` fn uses type `()`, which is not FFI-safe - --> $DIR/lint-ctypes-113436.rs:22:10 - | -LL | const _: extern "C" fn() -> Wrap> = ff; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe - | - = help: consider using a struct instead - = note: tuples have unspecified layout - -error: aborting due to 4 previous errors - diff --git a/tests/ui/repr/repr-transparent-issue-87496.rs b/tests/ui/repr/repr-transparent-issue-87496.rs index 0ce6fb2c19f..a4dd45c63f5 100644 --- a/tests/ui/repr/repr-transparent-issue-87496.rs +++ b/tests/ui/repr/repr-transparent-issue-87496.rs @@ -6,7 +6,7 @@ struct TransparentCustomZst(()); extern "C" { fn good17(p: TransparentCustomZst); - //~^ WARNING: `extern` block uses type `()`, which is not FFI-safe + //~^ WARNING: `extern` block uses type `TransparentCustomZst`, which is not FFI-safe } fn main() {} diff --git a/tests/ui/repr/repr-transparent-issue-87496.stderr b/tests/ui/repr/repr-transparent-issue-87496.stderr index 03c62f8514e..aee31212b4e 100644 --- a/tests/ui/repr/repr-transparent-issue-87496.stderr +++ b/tests/ui/repr/repr-transparent-issue-87496.stderr @@ -1,11 +1,15 @@ -warning: `extern` block uses type `()`, which is not FFI-safe +warning: `extern` block uses type `TransparentCustomZst`, which is not FFI-safe --> $DIR/repr-transparent-issue-87496.rs:8:18 | LL | fn good17(p: TransparentCustomZst); | ^^^^^^^^^^^^^^^^^^^^ not FFI-safe | - = help: consider using a struct instead - = note: tuples have unspecified layout + = note: this struct contains only zero-sized fields +note: the type is defined here + --> $DIR/repr-transparent-issue-87496.rs:6:1 + | +LL | struct TransparentCustomZst(()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ = note: `#[warn(improper_ctypes)]` on by default warning: 1 warning emitted -- cgit 1.4.1-3-g733a5 From 33322e06da679835f48a01e775a4329a1843453f Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Mon, 17 Jul 2023 18:55:07 +0000 Subject: Substitute types before checking compatibility. (cherry picked from commit 45ffe41d144e3e5bc8993abcbcdd54d870a2ff53) --- compiler/rustc_mir_transform/src/inline.rs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) (limited to 'compiler') diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs index 240beabfc1a..b6578cb259a 100644 --- a/compiler/rustc_mir_transform/src/inline.rs +++ b/compiler/rustc_mir_transform/src/inline.rs @@ -440,6 +440,10 @@ impl<'tcx> Inliner<'tcx> { validation: Ok(()), }; + for var_debug_info in callee_body.var_debug_info.iter() { + checker.visit_var_debug_info(var_debug_info); + } + // Traverse the MIR manually so we can account for the effects of inlining on the CFG. let mut work_list = vec![START_BLOCK]; let mut visited = BitSet::new_empty(callee_body.basic_blocks.len()); @@ -847,7 +851,16 @@ impl<'tcx> Visitor<'tcx> for CostChecker<'_, 'tcx> { if let ProjectionElem::Field(f, ty) = elem { let parent_ty = place_ref.ty(&self.callee_body.local_decls, self.tcx); let check_equal = |this: &mut Self, f_ty| { - if !util::is_equal_up_to_subtyping(this.tcx, this.param_env, ty, f_ty) { + // Fast path if there is nothing to substitute. + if ty == f_ty { + return; + } + let ty = this.instance.subst_mir(this.tcx, ty::EarlyBinder::bind(&ty)); + let f_ty = this.instance.subst_mir(this.tcx, ty::EarlyBinder::bind(&f_ty)); + if ty == f_ty { + return; + } + if !util::is_subtype(this.tcx, this.param_env, ty, f_ty) { trace!(?ty, ?f_ty); this.validation = Err("failed to normalize projection type"); return; -- cgit 1.4.1-3-g733a5 From 0426c1f1d5cc7ed7e9fb4b2918b157def17f8477 Mon Sep 17 00:00:00 2001 From: David Wood Date: Fri, 21 Jul 2023 15:42:25 +0100 Subject: lint/ctypes: only try normalize Now that this lint runs on any external-ABI fn-ptr, normalization won't always succeed, so use `try_normalize_erasing_regions` instead. Signed-off-by: David Wood (cherry picked from commit 09434a2575cee12b241c516ad91f21d4b4f9c3fd) --- compiler/rustc_lint/src/types.rs | 21 +++++++++------------ tests/ui/lint/lint-ctypes-113900.rs | 12 ++++++++++++ 2 files changed, 21 insertions(+), 12 deletions(-) create mode 100644 tests/ui/lint/lint-ctypes-113900.rs (limited to 'compiler') diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index 99430794668..ed4fb98605c 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -963,12 +963,12 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { substs: SubstsRef<'tcx>, ) -> FfiResult<'tcx> { let field_ty = field.ty(self.cx.tcx, substs); - if field_ty.has_opaque_types() { - self.check_type_for_ffi(cache, field_ty) - } else { - let field_ty = self.cx.tcx.normalize_erasing_regions(self.cx.param_env, field_ty); - self.check_type_for_ffi(cache, field_ty) - } + let field_ty = self + .cx + .tcx + .try_normalize_erasing_regions(self.cx.param_env, field_ty) + .unwrap_or(field_ty); + self.check_type_for_ffi(cache, field_ty) } /// Checks if the given `VariantDef`'s field types are "ffi-safe". @@ -1315,7 +1315,8 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { if let Some(ty) = self .cx .tcx - .normalize_erasing_regions(self.cx.param_env, ty) + .try_normalize_erasing_regions(self.cx.param_env, ty) + .unwrap_or(ty) .visit_with(&mut ProhibitOpaqueTypes) .break_value() { @@ -1333,16 +1334,12 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { is_static: bool, is_return_type: bool, ) { - // We have to check for opaque types before `normalize_erasing_regions`, - // which will replace opaque types with their underlying concrete type. if self.check_for_opaque_ty(sp, ty) { // We've already emitted an error due to an opaque type. return; } - // it is only OK to use this function because extern fns cannot have - // any generic types right now: - let ty = self.cx.tcx.normalize_erasing_regions(self.cx.param_env, ty); + let ty = self.cx.tcx.try_normalize_erasing_regions(self.cx.param_env, ty).unwrap_or(ty); // C doesn't really support passing arrays by value - the only way to pass an array by value // is through a struct. So, first test that the top level isn't an array, and then diff --git a/tests/ui/lint/lint-ctypes-113900.rs b/tests/ui/lint/lint-ctypes-113900.rs new file mode 100644 index 00000000000..ac4ff1ae2df --- /dev/null +++ b/tests/ui/lint/lint-ctypes-113900.rs @@ -0,0 +1,12 @@ +// check-pass + +// Extending `improper_ctypes` to check external-ABI fn-ptrs means that it can encounter +// projections which cannot be normalized - unsurprisingly, this shouldn't crash the compiler. + +trait Bar { + type Assoc; +} + +type Foo = extern "C" fn() -> ::Assoc; + +fn main() {} -- cgit 1.4.1-3-g733a5 From 8f4613ea7715cd1646d1632cc748fa6015b4f29c Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Fri, 21 Jul 2023 09:41:07 -0400 Subject: special-case proc-macro crates in rustc_codegen_ssa::back::linker::exported_symbols to only export the two symbols that proc-macros need. (cherry picked from commit 3000c24afc37fe09c65772a71331002a7505dc11) --- compiler/rustc_codegen_ssa/src/back/linker.rs | 32 ++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) (limited to 'compiler') diff --git a/compiler/rustc_codegen_ssa/src/back/linker.rs b/compiler/rustc_codegen_ssa/src/back/linker.rs index cd56f85cccd..f622ed32c38 100644 --- a/compiler/rustc_codegen_ssa/src/back/linker.rs +++ b/compiler/rustc_codegen_ssa/src/back/linker.rs @@ -13,6 +13,7 @@ use std::{env, mem, str}; use rustc_hir::def_id::{CrateNum, LOCAL_CRATE}; use rustc_metadata::find_native_static_library; use rustc_middle::middle::dependency_format::Linkage; +use rustc_middle::middle::exported_symbols; use rustc_middle::middle::exported_symbols::{ExportedSymbol, SymbolExportInfo, SymbolExportKind}; use rustc_middle::ty::TyCtxt; use rustc_session::config::{self, CrateType, DebugInfo, LinkerPluginLto, Lto, OptLevel, Strip}; @@ -659,8 +660,6 @@ impl<'a> Linker for GccLinker<'a> { return; } - // FIXME(#99978) hide #[no_mangle] symbols for proc-macros - let is_windows = self.sess.target.is_like_windows; let path = tmpdir.join(if is_windows { "list.def" } else { "list" }); @@ -1679,8 +1678,15 @@ pub(crate) fn exported_symbols(tcx: TyCtxt<'_>, crate_type: CrateType) -> Vec, crate_type: CrateType) -> Vec { + let mut symbols = Vec::new(); let export_threshold = symbol_export::crates_export_threshold(&[crate_type]); for_each_exported_symbols_include_dep(tcx, crate_type, |symbol, info, cnum| { if info.level.is_below_threshold(export_threshold) { @@ -1691,6 +1697,26 @@ pub(crate) fn exported_symbols(tcx: TyCtxt<'_>, crate_type: CrateType) -> Vec) -> Vec { + let mut symbols = Vec::new(); + + let stable_crate_id = tcx.sess.local_stable_crate_id(); + let proc_macro_decls_name = tcx.sess.generate_proc_macro_decls_symbol(stable_crate_id); + let metadata_symbol_name = exported_symbols::metadata_symbol_name(tcx); + + // You would think that both the two names would always be there, but in + // pnkfelix's local experiments that was not case. So instead we walk the + // list and only add them if they *are* there. + for_each_exported_symbols_include_dep(tcx, CrateType::ProcMacro, |symbol, _info, cnum| { + let name = symbol_export::symbol_name_for_instance_in_crate(tcx, symbol, cnum); + if name == proc_macro_decls_name || name == metadata_symbol_name { + symbols.push(name); + } + }); + + return symbols; +} + pub(crate) fn linked_symbols( tcx: TyCtxt<'_>, crate_type: CrateType, -- cgit 1.4.1-3-g733a5 From a94c607997d4b4389c53b5198e4fe5f3f0a093ed Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Mon, 7 Aug 2023 13:31:14 -0400 Subject: Review feedback: return empty iff !should_codegen, and use simpler unconditional logic otherwise. (cherry picked from commit a2058ddbed82adda55ce39f0e0915996706cb2b9) --- compiler/rustc_codegen_ssa/src/back/linker.rs | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) (limited to 'compiler') diff --git a/compiler/rustc_codegen_ssa/src/back/linker.rs b/compiler/rustc_codegen_ssa/src/back/linker.rs index f622ed32c38..8ac86fa4b26 100644 --- a/compiler/rustc_codegen_ssa/src/back/linker.rs +++ b/compiler/rustc_codegen_ssa/src/back/linker.rs @@ -1698,23 +1698,16 @@ fn exported_symbols_for_non_proc_macro(tcx: TyCtxt<'_>, crate_type: CrateType) - } fn exported_symbols_for_proc_macro_crate(tcx: TyCtxt<'_>) -> Vec { - let mut symbols = Vec::new(); + // `exported_symbols` will be empty when !should_codegen. + if !tcx.sess.opts.output_types.should_codegen() { + return Vec::new(); + } let stable_crate_id = tcx.sess.local_stable_crate_id(); let proc_macro_decls_name = tcx.sess.generate_proc_macro_decls_symbol(stable_crate_id); let metadata_symbol_name = exported_symbols::metadata_symbol_name(tcx); - // You would think that both the two names would always be there, but in - // pnkfelix's local experiments that was not case. So instead we walk the - // list and only add them if they *are* there. - for_each_exported_symbols_include_dep(tcx, CrateType::ProcMacro, |symbol, _info, cnum| { - let name = symbol_export::symbol_name_for_instance_in_crate(tcx, symbol, cnum); - if name == proc_macro_decls_name || name == metadata_symbol_name { - symbols.push(name); - } - }); - - return symbols; + vec![proc_macro_decls_name, metadata_symbol_name] } pub(crate) fn linked_symbols( -- cgit 1.4.1-3-g733a5