diff options
| author | bors <bors@rust-lang.org> | 2024-12-08 13:32:19 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2024-12-08 13:32:19 +0000 |
| commit | f33a8c6426074b7ce8d08740e9805fdca96ee150 (patch) | |
| tree | b69e06be18e5280eb3c586a77ef64005f2ff1eb8 /compiler | |
| parent | f415c07494b98e4559e4b13a9c5f867b0e6b2444 (diff) | |
| parent | d4b5345248dd5ba96eb20bc5241921533692fc15 (diff) | |
| download | rust-f33a8c6426074b7ce8d08740e9805fdca96ee150.tar.gz rust-f33a8c6426074b7ce8d08740e9805fdca96ee150.zip | |
Auto merge of #134033 - matthiaskrgr:rollup-69fswyh, r=matthiaskrgr
Rollup of 7 pull requests Successful merges: - #131669 (lint: change help for pointers to dyn types in FFI) - #133104 (crashes: add test for #131451) - #133767 (Add more info on type/trait mismatches for different crate versions) - #133861 (Add allocate_bytes and refactor allocate_str in InterpCx for raw byte…) - #133976 (Removed Unnecessary Spaces From RELEASES.md) - #133987 (Define acronym for thread local storage) - #133992 (Actually walk into lifetimes and attrs in `EarlyContextAndPass`) r? `@ghost` `@rustbot` modify labels: rollup
Diffstat (limited to 'compiler')
| -rw-r--r-- | compiler/rustc_const_eval/src/interpret/place.rs | 45 | ||||
| -rw-r--r-- | compiler/rustc_lint/messages.ftl | 13 | ||||
| -rw-r--r-- | compiler/rustc_lint/src/early.rs | 2 | ||||
| -rw-r--r-- | compiler/rustc_lint/src/hidden_unicode_codepoints.rs | 1 | ||||
| -rw-r--r-- | compiler/rustc_lint/src/lints.rs | 45 | ||||
| -rw-r--r-- | compiler/rustc_lint/src/types.rs | 372 | ||||
| -rw-r--r-- | compiler/rustc_lint_defs/src/builtin.rs | 1 | ||||
| -rw-r--r-- | compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs | 134 | ||||
| -rw-r--r-- | compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs | 4 |
9 files changed, 516 insertions, 101 deletions
diff --git a/compiler/rustc_const_eval/src/interpret/place.rs b/compiler/rustc_const_eval/src/interpret/place.rs index 2beec544fad..f54a932e1b6 100644 --- a/compiler/rustc_const_eval/src/interpret/place.rs +++ b/compiler/rustc_const_eval/src/interpret/place.rs @@ -1018,29 +1018,48 @@ where self.allocate_dyn(layout, kind, MemPlaceMeta::None) } - /// Returns a wide MPlace of type `str` to a new 1-aligned allocation. - /// Immutable strings are deduplicated and stored in global memory. - pub fn allocate_str( + /// Allocates a sequence of bytes in the interpreter's memory. + /// For immutable allocations, uses deduplication to reuse existing memory. + /// For mutable allocations, creates a new unique allocation. + pub fn allocate_bytes( &mut self, - str: &str, + bytes: &[u8], + align: Align, kind: MemoryKind<M::MemoryKind>, mutbl: Mutability, - ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> { - let tcx = self.tcx.tcx; - + ) -> InterpResult<'tcx, Pointer<M::Provenance>> { // Use cache for immutable strings. - let ptr = if mutbl.is_not() { + if mutbl.is_not() { // Use dedup'd allocation function. let salt = M::get_global_alloc_salt(self, None); - let id = tcx.allocate_bytes_dedup(str.as_bytes(), salt); + let id = self.tcx.allocate_bytes_dedup(bytes, salt); // Turn untagged "global" pointers (obtained via `tcx`) into the machine pointer to the allocation. - M::adjust_alloc_root_pointer(&self, Pointer::from(id), Some(kind))? + M::adjust_alloc_root_pointer(&self, Pointer::from(id), Some(kind)) } else { - self.allocate_bytes_ptr(str.as_bytes(), Align::ONE, kind, mutbl)? - }; - let meta = Scalar::from_target_usize(u64::try_from(str.len()).unwrap(), self); + // Allocate new memory for mutable data. + self.allocate_bytes_ptr(bytes, align, kind, mutbl) + } + } + + /// Allocates a string in the interpreter's memory with metadata for length. + /// Uses `allocate_bytes` internally but adds string-specific metadata handling. + pub fn allocate_str( + &mut self, + str: &str, + kind: MemoryKind<M::MemoryKind>, + mutbl: Mutability, + ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> { + let bytes = str.as_bytes(); + let ptr = self.allocate_bytes(bytes, Align::ONE, kind, mutbl)?; + + // Create length metadata for the string. + let meta = Scalar::from_target_usize(u64::try_from(bytes.len()).unwrap(), self); + + // Get layout for Rust's str type. let layout = self.layout_of(self.tcx.types.str_).unwrap(); + + // Combine pointer and metadata into a wide pointer. interp_ok(self.ptr_with_meta_to_mplace( ptr.into(), MemPlaceMeta::Meta(meta), diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index 49e6b763590..422629cd11d 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -359,7 +359,6 @@ lint_improper_ctypes_128bit = 128-bit integers don't currently have a known stab lint_improper_ctypes_array_help = consider passing a pointer to the array lint_improper_ctypes_array_reason = passing raw arrays by value is not FFI-safe -lint_improper_ctypes_box = box cannot be represented as a single pointer lint_improper_ctypes_char_help = consider using `u32` or `libc::wchar_t` instead @@ -377,7 +376,9 @@ lint_improper_ctypes_enum_repr_help = lint_improper_ctypes_enum_repr_reason = enum has no representation hint lint_improper_ctypes_fnptr_help = consider using an `extern fn(...) -> ...` function pointer instead +lint_improper_ctypes_fnptr_indirect_reason = the function pointer to `{$ty}` is FFI-unsafe due to `{$inner_ty}` lint_improper_ctypes_fnptr_reason = this function pointer has Rust-specific calling convention + lint_improper_ctypes_non_exhaustive = this enum is non-exhaustive lint_improper_ctypes_non_exhaustive_variant = this enum has non-exhaustive variants @@ -388,7 +389,11 @@ lint_improper_ctypes_opaque = opaque types have no C equivalent lint_improper_ctypes_pat_help = consider using the base type instead lint_improper_ctypes_pat_reason = pattern types have no C equivalent -lint_improper_ctypes_slice_help = consider using a raw pointer instead + +lint_improper_ctypes_sized_ptr_to_unsafe_type = + this reference (`{$ty}`) is ABI-compatible with a C pointer, but `{$inner_ty}` itself does not have a C layout + +lint_improper_ctypes_slice_help = consider using a raw pointer to the slice's first element (and a length) instead lint_improper_ctypes_slice_reason = slices have no C equivalent lint_improper_ctypes_str_help = consider using `*const u8` and a length instead @@ -414,6 +419,10 @@ lint_improper_ctypes_union_layout_help = consider adding a `#[repr(C)]` or `#[re lint_improper_ctypes_union_layout_reason = this union has unspecified layout lint_improper_ctypes_union_non_exhaustive = this union is non-exhaustive +lint_improper_ctypes_unsized_box = this box for an unsized type contains metadata, which makes it incompatible with a C pointer +lint_improper_ctypes_unsized_ptr = this pointer to an unsized type contains metadata, which makes it incompatible with a C pointer +lint_improper_ctypes_unsized_ref = this reference to an unsized type contains metadata, which makes it incompatible with a C pointer + lint_incomplete_include = include macro expected single expression in source diff --git a/compiler/rustc_lint/src/early.rs b/compiler/rustc_lint/src/early.rs index 4f3184f1d7c..a68a2a7f983 100644 --- a/compiler/rustc_lint/src/early.rs +++ b/compiler/rustc_lint/src/early.rs @@ -245,6 +245,7 @@ impl<'a, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a, T> fn visit_lifetime(&mut self, lt: &'a ast::Lifetime, _: ast_visit::LifetimeCtxt) { self.check_id(lt.id); + ast_visit::walk_lifetime(self, lt); } fn visit_path(&mut self, p: &'a ast::Path, id: ast::NodeId) { @@ -259,6 +260,7 @@ impl<'a, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a, T> fn visit_attribute(&mut self, attr: &'a ast::Attribute) { lint_callback!(self, check_attribute, attr); + ast_visit::walk_attribute(self, attr); } fn visit_mac_def(&mut self, mac: &'a ast::MacroDef, id: ast::NodeId) { diff --git a/compiler/rustc_lint/src/hidden_unicode_codepoints.rs b/compiler/rustc_lint/src/hidden_unicode_codepoints.rs index 025fd452040..28368e1ab46 100644 --- a/compiler/rustc_lint/src/hidden_unicode_codepoints.rs +++ b/compiler/rustc_lint/src/hidden_unicode_codepoints.rs @@ -9,6 +9,7 @@ use crate::lints::{ use crate::{EarlyContext, EarlyLintPass, LintContext}; declare_lint! { + #[allow(text_direction_codepoint_in_literal)] /// The `text_direction_codepoint_in_literal` lint detects Unicode codepoints that change the /// visual representation of text on screen in a way that does not correspond to their on /// memory representation. diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 20822f23bf1..9fa263799eb 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -1851,13 +1851,44 @@ pub(crate) struct UnpredictableFunctionPointerComparisonsSuggestion<'a> { pub right: Span, } +pub(crate) struct ImproperCTypesLayer<'a> { + pub ty: Ty<'a>, + pub inner_ty: Option<Ty<'a>>, + pub note: DiagMessage, + pub span_note: Option<Span>, + pub help: Option<DiagMessage>, +} + +impl<'a> Subdiagnostic for ImproperCTypesLayer<'a> { + fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>( + self, + diag: &mut Diag<'_, G>, + f: &F, + ) { + diag.arg("ty", self.ty); + if let Some(ty) = self.inner_ty { + diag.arg("inner_ty", ty); + } + + if let Some(help) = self.help { + let msg = f(diag, help.into()); + diag.help(msg); + } + + let msg = f(diag, self.note.into()); + diag.note(msg); + if let Some(note) = self.span_note { + let msg = f(diag, fluent::lint_note.into()); + diag.span_note(note, msg); + }; + } +} + pub(crate) struct ImproperCTypes<'a> { pub ty: Ty<'a>, pub desc: &'a str, pub label: Span, - pub help: Option<DiagMessage>, - pub note: DiagMessage, - pub span_note: Option<Span>, + pub reasons: Vec<ImproperCTypesLayer<'a>>, } // Used because of the complexity of Option<DiagMessage>, DiagMessage, and Option<Span> @@ -1867,12 +1898,8 @@ impl<'a> LintDiagnostic<'a, ()> for ImproperCTypes<'_> { diag.arg("ty", self.ty); diag.arg("desc", self.desc); diag.span_label(self.label, fluent::lint_label); - if let Some(help) = self.help { - diag.help(help); - } - diag.note(self.note); - if let Some(note) = self.span_note { - diag.span_note(note, fluent::lint_note); + for reason in self.reasons.into_iter() { + diag.subdiagnostic(reason); } } } diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index 33650be056d..90d44371ab5 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -22,10 +22,10 @@ mod improper_ctypes; use crate::lints::{ AmbiguousWidePointerComparisons, AmbiguousWidePointerComparisonsAddrMetadataSuggestion, AmbiguousWidePointerComparisonsAddrSuggestion, AtomicOrderingFence, AtomicOrderingLoad, - AtomicOrderingStore, ImproperCTypes, InvalidAtomicOrderingDiag, InvalidNanComparisons, - InvalidNanComparisonsSuggestion, UnpredictableFunctionPointerComparisons, - UnpredictableFunctionPointerComparisonsSuggestion, UnusedComparisons, - VariantSizeDifferencesDiag, + AtomicOrderingStore, ImproperCTypes, ImproperCTypesLayer, InvalidAtomicOrderingDiag, + InvalidNanComparisons, InvalidNanComparisonsSuggestion, + UnpredictableFunctionPointerComparisons, UnpredictableFunctionPointerComparisonsSuggestion, + UnusedComparisons, VariantSizeDifferencesDiag, }; use crate::{LateContext, LateLintPass, LintContext, fluent_generated as fluent}; @@ -727,7 +727,109 @@ struct CTypesVisitorState<'tcx> { enum FfiResult<'tcx> { FfiSafe, FfiPhantom(Ty<'tcx>), - FfiUnsafe { ty: Ty<'tcx>, reason: DiagMessage, help: Option<DiagMessage> }, + FfiUnsafe { + ty: Ty<'tcx>, + reason: DiagMessage, + help: Option<DiagMessage>, + }, + FfiUnsafeWrapper { + ty: Ty<'tcx>, + reason: DiagMessage, + help: Option<DiagMessage>, + wrapped: Box<FfiResult<'tcx>>, + }, +} + +/// Determine if a type is sized or not, and wether it affects references/pointers/boxes to it +#[derive(Clone, Copy)] +enum TypeSizedness { + /// type of definite size (pointers are C-compatible) + Definite, + /// unsized type because it includes an opaque/foreign type (pointers are C-compatible) + UnsizedWithExternType, + /// unsized type for other reasons (slice, string, dyn Trait, closure, ...) (pointers are not C-compatible) + UnsizedWithMetadata, +} + +/// Is this type unsized because it contains (or is) a foreign type? +/// (Returns Err if the type happens to be sized after all) +fn get_type_sizedness<'tcx, 'a>(cx: &'a LateContext<'tcx>, ty: Ty<'tcx>) -> TypeSizedness { + let tcx = cx.tcx; + + if ty.is_sized(tcx, cx.typing_env()) { + TypeSizedness::Definite + } else { + match ty.kind() { + ty::Slice(_) => TypeSizedness::UnsizedWithMetadata, + ty::Str => TypeSizedness::UnsizedWithMetadata, + ty::Dynamic(..) => TypeSizedness::UnsizedWithMetadata, + ty::Foreign(..) => TypeSizedness::UnsizedWithExternType, + // While opaque types are checked for earlier, if a projection in a struct field + // normalizes to an opaque type, then it will reach this branch. + ty::Alias(ty::Opaque, ..) => todo!("We... don't know enough about this type yet?"), + ty::Adt(def, args) => { + // for now assume: boxes and phantoms don't mess with this + match def.adt_kind() { + AdtKind::Union | AdtKind::Enum => { + bug!("unions and enums are necessarily sized") + } + AdtKind::Struct => { + if let Some(sym::cstring_type | sym::cstr_type) = + tcx.get_diagnostic_name(def.did()) + { + return TypeSizedness::UnsizedWithMetadata; + } + // FIXME: how do we deal with non-exhaustive unsized structs/unions? + + if def.non_enum_variant().fields.is_empty() { + bug!("an empty struct is necessarily sized"); + } + + let variant = def.non_enum_variant(); + + // only the last field may be unsized + let n_fields = variant.fields.len(); + let last_field = &variant.fields[(n_fields - 1).into()]; + let field_ty = last_field.ty(cx.tcx, args); + let field_ty = cx + .tcx + .try_normalize_erasing_regions(cx.typing_env(), field_ty) + .unwrap_or(field_ty); + match get_type_sizedness(cx, field_ty) { + s @ (TypeSizedness::UnsizedWithMetadata + | TypeSizedness::UnsizedWithExternType) => s, + TypeSizedness::Definite => { + bug!("failed to find the reason why struct `{:?}` is unsized", ty) + } + } + } + } + } + ty::Tuple(tuple) => { + // only the last field may be unsized + let n_fields = tuple.len(); + let field_ty: Ty<'tcx> = tuple[n_fields - 1]; + //let field_ty = last_field.ty(cx.tcx, args); + let field_ty = cx + .tcx + .try_normalize_erasing_regions(cx.typing_env(), field_ty) + .unwrap_or(field_ty); + match get_type_sizedness(cx, field_ty) { + s @ (TypeSizedness::UnsizedWithMetadata + | TypeSizedness::UnsizedWithExternType) => s, + TypeSizedness::Definite => { + bug!("failed to find the reason why tuple `{:?}` is unsized", ty) + } + } + } + ty => { + bug!( + "we shouldn't be trying to determine if this is unsized for a reason or another: `{:?}`", + ty + ) + } + } + } } pub(crate) fn nonnull_optimization_guaranteed<'tcx>( @@ -764,7 +866,7 @@ fn ty_is_known_nonnull<'tcx>( match ty.kind() { ty::FnPtr(..) => true, ty::Ref(..) => true, - ty::Adt(def, _) if def.is_box() && matches!(mode, CItemKind::Definition) => true, + ty::Adt(def, _) if def.is_box() => true, ty::Adt(def, args) if def.repr().transparent() && !def.is_union() => { let marked_non_null = nonnull_optimization_guaranteed(tcx, *def); @@ -933,12 +1035,13 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { /// 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() { - self.emit_ffi_unsafe_type_lint( + self.emit_ffi_unsafe_type_lint(ty.clone(), sp, vec![ImproperCTypesLayer { ty, - sp, - fluent::lint_improper_ctypes_array_reason, - Some(fluent::lint_improper_ctypes_array_help), - ); + note: fluent::lint_improper_ctypes_array_reason, + help: Some(fluent::lint_improper_ctypes_array_help), + inner_ty: None, + span_note: None, + }]); true } else { false @@ -995,9 +1098,9 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { all_phantom &= match self.check_field_type_for_ffi(acc, field, args) { FfiSafe => false, // `()` fields are FFI-safe! - FfiUnsafe { ty, .. } if ty.is_unit() => false, + FfiUnsafe { ty, .. } | FfiUnsafeWrapper { ty, .. } if ty.is_unit() => false, FfiPhantom(..) => true, - r @ FfiUnsafe { .. } => return r, + r @ (FfiUnsafe { .. } | FfiUnsafeWrapper { .. }) => return r, } } @@ -1031,16 +1134,47 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { match *ty.kind() { ty::Adt(def, args) => { - if let Some(boxed) = ty.boxed_ty() - && matches!(self.mode, CItemKind::Definition) - { - if boxed.is_sized(tcx, self.cx.typing_env()) { - return FfiSafe; + if let Some(inner_ty) = ty.boxed_ty() { + if let TypeSizedness::UnsizedWithExternType | TypeSizedness::Definite = + get_type_sizedness(self.cx, inner_ty) + { + // discussion on declaration vs definition: + // see the `ty::RawPtr(inner_ty, _) | ty::Ref(_, inner_ty, _)` arm + // of this `match *ty.kind()` block + if matches!(self.mode, CItemKind::Definition) { + return FfiSafe; + } else { + let inner_res = self.check_type_for_ffi(acc, inner_ty); + return match inner_res { + FfiUnsafe { .. } | FfiUnsafeWrapper { .. } => FfiUnsafeWrapper { + ty, + reason: fluent::lint_improper_ctypes_sized_ptr_to_unsafe_type, + wrapped: Box::new(inner_res), + help: None, + }, + _ => inner_res, + }; + } } else { + let help = match inner_ty.kind() { + ty::Str => Some(fluent::lint_improper_ctypes_str_help), + ty::Slice(_) => Some(fluent::lint_improper_ctypes_slice_help), + ty::Adt(def, _) + if matches!(def.adt_kind(), AdtKind::Struct | AdtKind::Union) + && matches!( + tcx.get_diagnostic_name(def.did()), + Some(sym::cstring_type | sym::cstr_type) + ) + && !acc.base_ty.is_mutable_ptr() => + { + Some(fluent::lint_improper_ctypes_cstr_help) + } + _ => None, + }; return FfiUnsafe { ty, - reason: fluent::lint_improper_ctypes_box, - help: None, + reason: fluent::lint_improper_ctypes_unsized_box, + help, }; } } @@ -1196,15 +1330,6 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { help: Some(fluent::lint_improper_ctypes_tuple_help), }, - ty::RawPtr(ty, _) | ty::Ref(_, ty, _) - if { - matches!(self.mode, CItemKind::Definition) - && ty.is_sized(self.cx.tcx, self.cx.typing_env()) - } => - { - FfiSafe - } - ty::RawPtr(ty, _) if match ty.kind() { ty::Tuple(tuple) => tuple.is_empty(), @@ -1214,7 +1339,70 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { FfiSafe } - ty::RawPtr(ty, _) | ty::Ref(_, ty, _) => self.check_type_for_ffi(acc, ty), + ty::RawPtr(inner_ty, _) | ty::Ref(_, inner_ty, _) => { + if let TypeSizedness::UnsizedWithExternType | TypeSizedness::Definite = + get_type_sizedness(self.cx, inner_ty) + { + // there's a nuance on what this lint should do for + // function definitions (`extern "C" fn fn_name(...) {...}`) + // versus declarations (`unsafe extern "C" {fn fn_name(...);}`). + // This is touched upon in https://github.com/rust-lang/rust/issues/66220 + // and https://github.com/rust-lang/rust/pull/72700 + // + // The big question is: what does "ABI safety" mean? if you have something translated to a C pointer + // (which has a stable layout) but points to FFI-unsafe type, is it safe? + // On one hand, the function's ABI will match that of a similar C-declared function API, + // on the other, dereferencing the pointer on the other side of the FFI boundary will be painful. + // In this code, the opinion on is split between function declarations and function definitions, + // with the idea that at least one side of the FFI boundary needs to treat the pointee as an opaque type. + // For declarations, we see this as unsafe, but for definitions, we see this as safe. + // + // For extern function declarations, the actual definition of the function is written somewhere else, + // meaning the declaration is free to express this opaqueness with an extern type (opaque caller-side) or a std::ffi::c_void (opaque callee-side) + // For extern function definitions, however, in the case where the type is opaque caller-side, it is not opaque callee-side, + // and having the full type information is necessary to compile the function. + if matches!(self.mode, CItemKind::Definition) { + return FfiSafe; + } else if matches!(ty.kind(), ty::RawPtr(..)) + && matches!(inner_ty.kind(), ty::Tuple(tuple) if tuple.is_empty()) + { + FfiSafe + } else { + let inner_res = self.check_type_for_ffi(acc, inner_ty); + return match inner_res { + FfiSafe => inner_res, + _ => FfiUnsafeWrapper { + ty, + reason: fluent::lint_improper_ctypes_sized_ptr_to_unsafe_type, + wrapped: Box::new(inner_res), + help: None, + }, + }; + } + } else { + let help = match inner_ty.kind() { + ty::Str => Some(fluent::lint_improper_ctypes_str_help), + ty::Slice(_) => Some(fluent::lint_improper_ctypes_slice_help), + ty::Adt(def, _) + if matches!(def.adt_kind(), AdtKind::Struct | AdtKind::Union) + && matches!( + tcx.get_diagnostic_name(def.did()), + Some(sym::cstring_type | sym::cstr_type) + ) + && !acc.base_ty.is_mutable_ptr() => + { + Some(fluent::lint_improper_ctypes_cstr_help) + } + _ => None, + }; + let reason = match ty.kind() { + ty::RawPtr(..) => fluent::lint_improper_ctypes_unsized_ptr, + ty::Ref(..) => fluent::lint_improper_ctypes_unsized_ref, + _ => unreachable!(), + }; + FfiUnsafe { ty, reason, help } + } + } ty::Array(inner_ty, _) => self.check_type_for_ffi(acc, inner_ty), @@ -1232,7 +1420,14 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { for arg in sig.inputs() { match self.check_type_for_ffi(acc, *arg) { FfiSafe => {} - r => return r, + r => { + return FfiUnsafeWrapper { + ty, + reason: fluent::lint_improper_ctypes_fnptr_indirect_reason, + help: None, + wrapped: Box::new(r), + }; + } } } @@ -1241,7 +1436,15 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { return FfiSafe; } - self.check_type_for_ffi(acc, ret_ty) + match self.check_type_for_ffi(acc, ret_ty) { + r @ (FfiSafe | FfiPhantom(_)) => r, + r => FfiUnsafeWrapper { + ty: ty.clone(), + reason: fluent::lint_improper_ctypes_fnptr_indirect_reason, + help: None, + wrapped: Box::new(r), + }, + } } ty::Foreign(..) => FfiSafe, @@ -1278,8 +1481,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { &mut self, ty: Ty<'tcx>, sp: Span, - note: DiagMessage, - help: Option<DiagMessage>, + mut reasons: Vec<ImproperCTypesLayer<'tcx>>, ) { let lint = match self.mode { CItemKind::Declaration => IMPROPER_CTYPES, @@ -1289,21 +1491,17 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { CItemKind::Declaration => "block", CItemKind::Definition => "fn", }; - let span_note = if let ty::Adt(def, _) = ty.kind() - && let Some(sp) = self.cx.tcx.hir().span_if_local(def.did()) - { - Some(sp) - } else { - None - }; - self.cx.emit_span_lint(lint, sp, ImproperCTypes { - ty, - desc, - label: sp, - help, - note, - span_note, - }); + for reason in reasons.iter_mut() { + reason.span_note = if let ty::Adt(def, _) = reason.ty.kind() + && let Some(sp) = self.cx.tcx.hir().span_if_local(def.did()) + { + Some(sp) + } else { + None + }; + } + + self.cx.emit_span_lint(lint, sp, ImproperCTypes { ty, desc, label: sp, reasons }); } fn check_for_opaque_ty(&mut self, sp: Span, ty: Ty<'tcx>) -> bool { @@ -1332,7 +1530,13 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { .visit_with(&mut ProhibitOpaqueTypes) .break_value() { - self.emit_ffi_unsafe_type_lint(ty, sp, fluent::lint_improper_ctypes_opaque, None); + self.emit_ffi_unsafe_type_lint(ty.clone(), sp, vec![ImproperCTypesLayer { + ty, + note: fluent::lint_improper_ctypes_opaque, + span_note: Some(sp), + help: None, + inner_ty: None, + }]); true } else { false @@ -1371,15 +1575,71 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { match self.check_type_for_ffi(&mut acc, ty) { FfiResult::FfiSafe => {} FfiResult::FfiPhantom(ty) => { - self.emit_ffi_unsafe_type_lint( + self.emit_ffi_unsafe_type_lint(ty.clone(), sp, vec![ImproperCTypesLayer { ty, - sp, - fluent::lint_improper_ctypes_only_phantomdata, - None, - ); + note: fluent::lint_improper_ctypes_only_phantomdata, + span_note: None, // filled later + help: None, + inner_ty: None, + }]); } FfiResult::FfiUnsafe { ty, reason, help } => { - self.emit_ffi_unsafe_type_lint(ty, sp, reason, help); + self.emit_ffi_unsafe_type_lint(ty.clone(), sp, vec![ImproperCTypesLayer { + ty, + help, + note: reason, + span_note: None, // filled later + inner_ty: None, + }]); + } + ffir @ FfiResult::FfiUnsafeWrapper { .. } => { + let mut ffiresult_recursor = ControlFlow::Continue(&ffir); + let mut cimproper_layers: Vec<ImproperCTypesLayer<'tcx>> = vec![]; + + // this whole while block converts the arbitrarily-deep + // FfiResult stack to an ImproperCTypesLayer Vec + while let ControlFlow::Continue(ref ffir_rec) = ffiresult_recursor { + match ffir_rec { + FfiResult::FfiPhantom(ty) => { + if let Some(layer) = cimproper_layers.last_mut() { + layer.inner_ty = Some(ty.clone()); + } + cimproper_layers.push(ImproperCTypesLayer { + ty: ty.clone(), + inner_ty: None, + help: None, + note: fluent::lint_improper_ctypes_only_phantomdata, + span_note: None, // filled later + }); + ffiresult_recursor = ControlFlow::Break(()); + } + FfiResult::FfiUnsafe { ty, reason, help } + | FfiResult::FfiUnsafeWrapper { ty, reason, help, .. } => { + if let Some(layer) = cimproper_layers.last_mut() { + layer.inner_ty = Some(ty.clone()); + } + cimproper_layers.push(ImproperCTypesLayer { + ty: ty.clone(), + inner_ty: None, + help: help.clone(), + note: reason.clone(), + span_note: None, // filled later + }); + + if let FfiResult::FfiUnsafeWrapper { wrapped, .. } = ffir_rec { + ffiresult_recursor = ControlFlow::Continue(wrapped.as_ref()); + } else { + ffiresult_recursor = ControlFlow::Break(()); + } + } + FfiResult::FfiSafe => { + bug!("malformed FfiResult stack: it should be unsafe all the way down") + } + }; + } + // should always have at least one type + let last_ty = cimproper_layers.last().unwrap().ty.clone(); + self.emit_ffi_unsafe_type_lint(last_ty, sp, cimproper_layers); } } } diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index d2b7ae620e2..54e927df3c4 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -3929,6 +3929,7 @@ declare_lint! { } declare_lint! { + #[allow(text_direction_codepoint_in_literal)] /// The `text_direction_codepoint_in_comment` lint detects Unicode codepoints in comments that /// change the visual representation of text on screen in a way that does not correspond to /// their on memory representation. diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs index 8ba7969207e..f856a8d7abb 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs @@ -52,7 +52,9 @@ use std::{cmp, fmt, iter}; use rustc_abi::ExternAbi; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; -use rustc_errors::{Applicability, Diag, DiagStyledString, IntoDiagArg, StringPart, pluralize}; +use rustc_errors::{ + Applicability, Diag, DiagStyledString, IntoDiagArg, MultiSpan, StringPart, pluralize, +}; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::Visitor; @@ -67,6 +69,7 @@ use rustc_middle::ty::{ self, List, Region, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, }; +use rustc_span::def_id::LOCAL_CRATE; use rustc_span::{BytePos, DesugaringKind, Pos, Span, sym}; use tracing::{debug, instrument}; @@ -211,7 +214,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } /// Adds a note if the types come from similarly named crates - fn check_and_note_conflicting_crates(&self, err: &mut Diag<'_>, terr: TypeError<'tcx>) { + fn check_and_note_conflicting_crates(&self, err: &mut Diag<'_>, terr: TypeError<'tcx>) -> bool { + // FIXME(estebank): unify with `report_similar_impl_candidates`. The message is similar, + // even if the logic needed to detect the case is very different. use hir::def_id::CrateNum; use rustc_hir::definitions::DisambiguatedDefPathData; use ty::GenericArg; @@ -285,7 +290,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } } - let report_path_match = |err: &mut Diag<'_>, did1: DefId, did2: DefId| { + let report_path_match = |err: &mut Diag<'_>, did1: DefId, did2: DefId, ty: &str| -> bool { // Only report definitions from different crates. If both definitions // are from a local module we could have false positives, e.g. // let _ = [{struct Foo; Foo}, {struct Foo; Foo}]; @@ -297,24 +302,112 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // We compare strings because DefPath can be different // for imported and non-imported crates + let expected_str = self.tcx.def_path_str(did1); + let found_str = self.tcx.def_path_str(did2); + let Ok(expected_abs) = abs_path(did1) else { return false }; + let Ok(found_abs) = abs_path(did2) else { return false }; let same_path = || -> Result<_, PrintError> { - Ok(self.tcx.def_path_str(did1) == self.tcx.def_path_str(did2) - || abs_path(did1)? == abs_path(did2)?) + Ok(expected_str == found_str || expected_abs == found_abs) + }; + // We want to use as unique a type path as possible. If both types are "locally + // known" by the same name, we use the "absolute path" which uses the original + // crate name instead. + let (expected, found) = if expected_str == found_str { + (expected_abs.join("::"), found_abs.join("::")) + } else { + (expected_str.clone(), found_str.clone()) }; if same_path().unwrap_or(false) { - let crate_name = self.tcx.crate_name(did1.krate); - let msg = if did1.is_local() || did2.is_local() { + // We've displayed "expected `a::b`, found `a::b`". We add context to + // differentiate the different cases where that might happen. + let expected_crate_name = self.tcx.crate_name(did1.krate); + let found_crate_name = self.tcx.crate_name(did2.krate); + let same_crate = expected_crate_name == found_crate_name; + let expected_sp = self.tcx.def_span(did1); + let found_sp = self.tcx.def_span(did2); + + let both_direct_dependencies = if !did1.is_local() + && !did2.is_local() + && let Some(data1) = self.tcx.extern_crate(did1.krate) + && let Some(data2) = self.tcx.extern_crate(did2.krate) + && data1.dependency_of == LOCAL_CRATE + && data2.dependency_of == LOCAL_CRATE + { + // If both crates are directly depended on, we don't want to mention that + // in the final message, as it is redundant wording. + // We skip the case of semver trick, where one version of the local crate + // depends on another version of itself by checking that both crates at play + // are not the current one. + true + } else { + false + }; + + let mut span: MultiSpan = vec![expected_sp, found_sp].into(); + span.push_span_label( + self.tcx.def_span(did1), + format!("this is the expected {ty} `{expected}`"), + ); + span.push_span_label( + self.tcx.def_span(did2), + format!("this is the found {ty} `{found}`"), + ); + for def_id in [did1, did2] { + let crate_name = self.tcx.crate_name(def_id.krate); + if !def_id.is_local() + && let Some(data) = self.tcx.extern_crate(def_id.krate) + { + let descr = if same_crate { + "one version of".to_string() + } else { + format!("one {ty} comes from") + }; + let dependency = if both_direct_dependencies { + if let rustc_session::cstore::ExternCrateSource::Extern(def_id) = + data.src + && let Some(name) = self.tcx.opt_item_name(def_id) + { + format!(", which is renamed locally to `{name}`") + } else { + String::new() + } + } else if data.dependency_of == LOCAL_CRATE { + ", as a direct dependency of the current crate".to_string() + } else { + let dep = self.tcx.crate_name(data.dependency_of); + format!(", as a dependency of crate `{dep}`") + }; + span.push_span_label( + data.span, + format!("{descr} crate `{crate_name}` used here{dependency}"), + ); + } + } + let msg = if (did1.is_local() || did2.is_local()) && same_crate { + format!( + "the crate `{expected_crate_name}` is compiled multiple times, \ + possibly with different configurations", + ) + } else if same_crate { format!( - "the crate `{crate_name}` is compiled multiple times, possibly with different configurations" + "two different versions of crate `{expected_crate_name}` are being \ + used; two types coming from two different versions of the same crate \ + are different types even if they look the same", ) } else { format!( - "perhaps two different versions of crate `{crate_name}` are being used?" + "two types coming from two different crates are different types even \ + if they look the same", ) }; - err.note(msg); + err.span_note(span, msg); + if same_crate { + err.help("you can use `cargo tree` to explore your dependency tree"); + } + return true; } } + false }; match terr { TypeError::Sorts(ref exp_found) => { @@ -323,14 +416,15 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { if let (&ty::Adt(exp_adt, _), &ty::Adt(found_adt, _)) = (exp_found.expected.kind(), exp_found.found.kind()) { - report_path_match(err, exp_adt.did(), found_adt.did()); + return report_path_match(err, exp_adt.did(), found_adt.did(), "type"); } } TypeError::Traits(ref exp_found) => { - report_path_match(err, exp_found.expected, exp_found.found); + return report_path_match(err, exp_found.expected, exp_found.found, "trait"); } _ => (), // FIXME(#22750) handle traits and stuff } + false } fn note_error_origin( @@ -1409,6 +1503,10 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { label_or_note(span, terr.to_string(self.tcx)); } + if self.check_and_note_conflicting_crates(diag, terr) { + return; + } + if let Some((expected, found, path)) = expected_found { let (expected_label, found_label, exp_found) = match exp_found { Mismatch::Variable(ef) => ( @@ -1470,15 +1568,17 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { |prim: Ty<'tcx>, shadow: Ty<'tcx>, defid: DefId, diag: &mut Diag<'_>| { let name = shadow.sort_string(self.tcx); diag.note(format!( - "{prim} and {name} have similar names, but are actually distinct types" + "`{prim}` and {name} have similar names, but are actually distinct types" + )); + diag.note(format!( + "one `{prim}` is a primitive defined by the language", )); - diag.note(format!("{prim} is a primitive defined by the language")); let def_span = self.tcx.def_span(defid); let msg = if defid.is_local() { - format!("{name} is defined in the current crate") + format!("the other {name} is defined in the current crate") } else { let crate_name = self.tcx.crate_name(defid.krate); - format!("{name} is defined in crate `{crate_name}`") + format!("the other {name} is defined in crate `{crate_name}`") }; diag.span_note(def_span, msg); }; @@ -1666,8 +1766,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } } - self.check_and_note_conflicting_crates(diag, terr); - self.note_and_explain_type_err(diag, terr, cause, span, cause.body_id.to_def_id()); if let Some(exp_found) = exp_found && let exp_found = TypeError::Sorts(exp_found) diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index 90b18253629..4fb02f60943 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -1745,9 +1745,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { }; ( data.span, - format!( - "one version of crate `{crate_name}` is used here, as a {dependency}" - ), + format!("one version of crate `{crate_name}` used here, as a {dependency}"), ) }) { |
