diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs index 69c5297bf6b..2be59deb705 100644 --- a/compiler/rustc_feature/src/active.rs +++ b/compiler/rustc_feature/src/active.rs @@ -160,6 +160,8 @@ declare_features! ( (active, intrinsics, "1.0.0", None, None), /// Allows using `#[lang = ".."]` attribute for linking items to special compiler logic. (active, lang_items, "1.0.0", None, None), + /// Allows the `multiple_supertrait_upcastable` lint. + (active, multiple_supertrait_upcastable, "CURRENT_RUSTC_VERSION", None, None), /// Allows using `#[omit_gdb_pretty_printer_section]`. (active, omit_gdb_pretty_printer_section, "1.5.0", None, None), /// Allows using `#[prelude_import]` on glob `use` items. diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 10bae36e0fd..f9c86f8b5f3 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -61,6 +61,7 @@ mod late; mod let_underscore; mod levels; mod methods; +mod multiple_supertrait_upcastable; mod non_ascii_idents; mod non_fmt_panic; mod nonstandard_style; @@ -95,6 +96,7 @@ use hidden_unicode_codepoints::*; use internal::*; use let_underscore::*; use methods::*; +use multiple_supertrait_upcastable::*; use non_ascii_idents::*; use non_fmt_panic::NonPanicFmt; use nonstandard_style::*; @@ -228,6 +230,7 @@ late_lint_methods!( InvalidAtomicOrdering: InvalidAtomicOrdering, NamedAsmLabels: NamedAsmLabels, OpaqueHiddenInferredBound: OpaqueHiddenInferredBound, + MultipleSupertraitUpcastable: MultipleSupertraitUpcastable, ] ], ['tcx] diff --git a/compiler/rustc_lint/src/multiple_supertrait_upcastable.rs b/compiler/rustc_lint/src/multiple_supertrait_upcastable.rs new file mode 100644 index 00000000000..5861b826b1c --- /dev/null +++ b/compiler/rustc_lint/src/multiple_supertrait_upcastable.rs @@ -0,0 +1,63 @@ +use crate::{LateContext, LateLintPass, LintContext}; + +use rustc_errors::DelayDm; +use rustc_hir as hir; +use rustc_span::sym; + +declare_lint! { + /// The `multiple_supertrait_upcastable` lint detects when an object-safe trait has multiple + /// supertraits. + /// + /// ### Example + /// + /// ```rust + /// trait A {} + /// trait B {} + /// + /// #[warn(multiple_supertrait_upcastable)] + /// trait C: A + B {} + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// To support upcasting with multiple supertraits, we need to store multiple vtables and this + /// can result in extra space overhead, even if no code actually uses upcasting. + /// This lint allows users to identify when such scenarios occur and to decide whether the + /// additional overhead is justified. + pub MULTIPLE_SUPERTRAIT_UPCASTABLE, + Allow, + "detect when an object-safe trait has multiple supertraits", + @feature_gate = sym::multiple_supertrait_upcastable; +} + +declare_lint_pass!(MultipleSupertraitUpcastable => [MULTIPLE_SUPERTRAIT_UPCASTABLE]); + +impl<'tcx> LateLintPass<'tcx> for MultipleSupertraitUpcastable { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) { + let def_id = item.owner_id.to_def_id(); + if let hir::ItemKind::Trait(_, _, _, _, _) = item.kind + && cx.tcx.is_object_safe(def_id) + { + let direct_super_traits_iter = cx.tcx + .super_predicates_of(def_id) + .predicates + .into_iter() + .filter_map(|(pred, _)| pred.to_opt_poly_trait_pred()); + if direct_super_traits_iter.count() > 1 { + cx.struct_span_lint( + MULTIPLE_SUPERTRAIT_UPCASTABLE, + cx.tcx.def_span(def_id), + DelayDm(|| { + format!( + "`{}` is object-safe and has multiple supertraits", + item.ident, + ) + }), + |diag| diag, + ); + } + } + } +} diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 61253845497..2e90bd71ea6 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -941,6 +941,7 @@ symbols! { mul, mul_assign, mul_with_overflow, + multiple_supertrait_upcastable, must_not_suspend, must_use, naked, diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index 96960d43f58..ad22c5b14ae 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -87,6 +87,7 @@ #![warn(missing_debug_implementations)] #![warn(missing_docs)] #![allow(explicit_outlives_requirements)] +#![cfg_attr(not(bootstrap), warn(multiple_supertrait_upcastable))] // // Library features: #![feature(alloc_layout_extra)] @@ -190,6 +191,7 @@ #![feature(unsized_fn_params)] #![feature(c_unwind)] #![feature(with_negative_coherence)] +#![cfg_attr(not(bootstrap), feature(multiple_supertrait_upcastable))] // // Rustdoc features: #![feature(doc_cfg)] diff --git a/library/core/src/error.rs b/library/core/src/error.rs index 7152300abcb..d2fac23ff18 100644 --- a/library/core/src/error.rs +++ b/library/core/src/error.rs @@ -28,6 +28,7 @@ use crate::fmt::{Debug, Display}; #[stable(feature = "rust1", since = "1.0.0")] #[cfg_attr(not(test), rustc_diagnostic_item = "Error")] #[rustc_has_incoherent_inherent_impls] +#[cfg_attr(not(bootstrap), allow(multiple_supertrait_upcastable))] pub trait Error: Debug + Display { /// The lower-level source of this error, if any. /// diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 1823fd30062..825c8541f0d 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -95,6 +95,7 @@ #![warn(missing_docs)] #![allow(explicit_outlives_requirements)] #![allow(incomplete_features)] +#![cfg_attr(not(bootstrap), warn(multiple_supertrait_upcastable))] // // Library features: #![feature(const_align_offset)] @@ -231,6 +232,7 @@ #![feature(unsized_fn_params)] #![feature(asm_const)] #![feature(const_transmute_copy)] +#![cfg_attr(not(bootstrap), feature(multiple_supertrait_upcastable))] // // Target features: #![feature(arm_target_feature)]