Skip to content

Commit d117135

Browse files
committed
Auto merge of #106253 - nbdd0121:upcast, r=compiler-errors
Skip possible where_clause_object_safety lints when checking `multiple_supertrait_upcastable` Fix #106247 To achieve this, I lifted the `WhereClauseReferencesSelf` out from `object_safety_violations` and move it into `is_object_safe` (which is changed to a new query). cc `@dtolnay` r? `@compiler-errors`
2 parents a29efcc + 66f3ab9 commit d117135

File tree

26 files changed

+219
-28
lines changed

26 files changed

+219
-28
lines changed

compiler/rustc_error_messages/locales/en-US/lint.ftl

+2
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,8 @@ lint_cstring_ptr = getting the inner pointer of a temporary `CString`
100100
.note = pointers do not have a lifetime; when calling `as_ptr` the `CString` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned
101101
.help = for more information, see https://doc.rust-lang.org/reference/destructors.html
102102
103+
lint_multple_supertrait_upcastable = `{$ident}` is object-safe and has multiple supertraits
104+
103105
lint_identifier_non_ascii_char = identifier contains non-ASCII characters
104106
105107
lint_identifier_uncommon_codepoints = identifier contains uncommon Unicode codepoints

compiler/rustc_feature/src/active.rs

+2
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,8 @@ declare_features! (
160160
(active, intrinsics, "1.0.0", None, None),
161161
/// Allows using `#[lang = ".."]` attribute for linking items to special compiler logic.
162162
(active, lang_items, "1.0.0", None, None),
163+
/// Allows the `multiple_supertrait_upcastable` lint.
164+
(active, multiple_supertrait_upcastable, "CURRENT_RUSTC_VERSION", None, None),
163165
/// Allows using `#[omit_gdb_pretty_printer_section]`.
164166
(active, omit_gdb_pretty_printer_section, "1.5.0", None, None),
165167
/// Allows using `#[prelude_import]` on glob `use` items.

compiler/rustc_hir_analysis/src/check/wfcheck.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -841,7 +841,7 @@ fn check_object_unsafe_self_trait_by_name(tcx: TyCtxt<'_>, item: &hir::TraitItem
841841
_ => {}
842842
}
843843
if !trait_should_be_self.is_empty() {
844-
if tcx.object_safety_violations(trait_def_id).is_empty() {
844+
if tcx.check_is_object_safe(trait_def_id) {
845845
return;
846846
}
847847
let sugg = trait_should_be_self.iter().map(|span| (*span, "Self".to_string())).collect();

compiler/rustc_hir_analysis/src/coherence/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ fn check_object_overlap<'tcx>(
169169
});
170170

171171
for component_def_id in component_def_ids {
172-
if !tcx.is_object_safe(component_def_id) {
172+
if !tcx.check_is_object_safe(component_def_id) {
173173
// Without the 'object_safe_for_dispatch' feature this is an error
174174
// which will be reported by wfcheck. Ignore it here.
175175
// This is tested by `coherence-impl-trait-for-trait-object-safe.rs`.

compiler/rustc_hir_typeck/src/coercion.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1823,7 +1823,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
18231823
.trait_ref()
18241824
.and_then(|t| t.trait_def_id())
18251825
.map_or(false, |def_id| {
1826-
fcx.tcx.object_safety_violations(def_id).is_empty()
1826+
fcx.tcx.check_is_object_safe(def_id)
18271827
})
18281828
})
18291829
}

compiler/rustc_lint/src/lib.rs

+3
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ mod let_underscore;
6464
mod levels;
6565
mod lints;
6666
mod methods;
67+
mod multiple_supertrait_upcastable;
6768
mod non_ascii_idents;
6869
mod non_fmt_panic;
6970
mod nonstandard_style;
@@ -98,6 +99,7 @@ use hidden_unicode_codepoints::*;
9899
use internal::*;
99100
use let_underscore::*;
100101
use methods::*;
102+
use multiple_supertrait_upcastable::*;
101103
use non_ascii_idents::*;
102104
use non_fmt_panic::NonPanicFmt;
103105
use nonstandard_style::*;
@@ -232,6 +234,7 @@ late_lint_methods!(
232234
InvalidAtomicOrdering: InvalidAtomicOrdering,
233235
NamedAsmLabels: NamedAsmLabels,
234236
OpaqueHiddenInferredBound: OpaqueHiddenInferredBound,
237+
MultipleSupertraitUpcastable: MultipleSupertraitUpcastable,
235238
]
236239
]
237240
);

compiler/rustc_lint/src/lints.rs

+7
Original file line numberDiff line numberDiff line change
@@ -917,6 +917,13 @@ pub struct CStringPtr {
917917
pub unwrap: Span,
918918
}
919919

920+
// multiple_supertrait_upcastable.rs
921+
#[derive(LintDiagnostic)]
922+
#[diag(lint_multple_supertrait_upcastable)]
923+
pub struct MultipleSupertraitUpcastable {
924+
pub ident: Ident,
925+
}
926+
920927
// non_ascii_idents.rs
921928
#[derive(LintDiagnostic)]
922929
#[diag(lint_identifier_non_ascii_char)]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
use crate::{LateContext, LateLintPass, LintContext};
2+
3+
use rustc_hir as hir;
4+
use rustc_span::sym;
5+
6+
declare_lint! {
7+
/// The `multiple_supertrait_upcastable` lint detects when an object-safe trait has multiple
8+
/// supertraits.
9+
///
10+
/// ### Example
11+
///
12+
/// ```rust
13+
/// trait A {}
14+
/// trait B {}
15+
///
16+
/// #[warn(multiple_supertrait_upcastable)]
17+
/// trait C: A + B {}
18+
/// ```
19+
///
20+
/// {{produces}}
21+
///
22+
/// ### Explanation
23+
///
24+
/// To support upcasting with multiple supertraits, we need to store multiple vtables and this
25+
/// can result in extra space overhead, even if no code actually uses upcasting.
26+
/// This lint allows users to identify when such scenarios occur and to decide whether the
27+
/// additional overhead is justified.
28+
pub MULTIPLE_SUPERTRAIT_UPCASTABLE,
29+
Allow,
30+
"detect when an object-safe trait has multiple supertraits",
31+
@feature_gate = sym::multiple_supertrait_upcastable;
32+
}
33+
34+
declare_lint_pass!(MultipleSupertraitUpcastable => [MULTIPLE_SUPERTRAIT_UPCASTABLE]);
35+
36+
impl<'tcx> LateLintPass<'tcx> for MultipleSupertraitUpcastable {
37+
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) {
38+
let def_id = item.owner_id.to_def_id();
39+
// NOTE(nbdd0121): use `object_safety_violations` instead of `check_is_object_safe` because
40+
// the latter will report `where_clause_object_safety` lint.
41+
if let hir::ItemKind::Trait(_, _, _, _, _) = item.kind
42+
&& cx.tcx.object_safety_violations(def_id).is_empty()
43+
{
44+
let direct_super_traits_iter = cx.tcx
45+
.super_predicates_of(def_id)
46+
.predicates
47+
.into_iter()
48+
.filter_map(|(pred, _)| pred.to_opt_poly_trait_pred());
49+
if direct_super_traits_iter.count() > 1 {
50+
cx.emit_spanned_lint(
51+
MULTIPLE_SUPERTRAIT_UPCASTABLE,
52+
cx.tcx.def_span(def_id),
53+
crate::lints::MultipleSupertraitUpcastable {
54+
ident: item.ident
55+
},
56+
);
57+
}
58+
}
59+
}
60+
}

compiler/rustc_middle/src/query/mod.rs

+3
Original file line numberDiff line numberDiff line change
@@ -1274,6 +1274,9 @@ rustc_queries! {
12741274
query object_safety_violations(trait_id: DefId) -> &'tcx [traits::ObjectSafetyViolation] {
12751275
desc { |tcx| "determining object safety of trait `{}`", tcx.def_path_str(trait_id) }
12761276
}
1277+
query check_is_object_safe(trait_id: DefId) -> bool {
1278+
desc { |tcx| "checking if trait `{}` is object safe", tcx.def_path_str(trait_id) }
1279+
}
12771280

12781281
/// Gets the ParameterEnvironment for a given item; this environment
12791282
/// will be in "user-facing" mode, meaning that it is suitable for

compiler/rustc_middle/src/ty/mod.rs

-4
Original file line numberDiff line numberDiff line change
@@ -2458,10 +2458,6 @@ impl<'tcx> TyCtxt<'tcx> {
24582458
}
24592459
}
24602460

2461-
pub fn is_object_safe(self, key: DefId) -> bool {
2462-
self.object_safety_violations(key).is_empty()
2463-
}
2464-
24652461
#[inline]
24662462
pub fn is_const_fn_raw(self, def_id: DefId) -> bool {
24672463
matches!(

compiler/rustc_span/src/symbol.rs

+1
Original file line numberDiff line numberDiff line change
@@ -955,6 +955,7 @@ symbols! {
955955
mul,
956956
mul_assign,
957957
mul_with_overflow,
958+
multiple_supertrait_upcastable,
958959
must_not_suspend,
959960
must_use,
960961
naked,

compiler/rustc_trait_selection/src/solve/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -361,7 +361,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
361361
}
362362

363363
fn compute_object_safe_goal(&mut self, trait_def_id: DefId) -> QueryResult<'tcx> {
364-
if self.tcx().is_object_safe(trait_def_id) {
364+
if self.tcx().check_is_object_safe(trait_def_id) {
365365
self.make_canonical_response(Certainty::Yes)
366366
} else {
367367
Err(NoSolution)

compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1748,7 +1748,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
17481748
// If the `dyn Trait` is not object safe, do not suggest `Box<dyn Trait>`.
17491749
predicates
17501750
.principal_def_id()
1751-
.map_or(true, |def_id| self.tcx.object_safety_violations(def_id).is_empty())
1751+
.map_or(true, |def_id| self.tcx.check_is_object_safe(def_id))
17521752
}
17531753
// We only want to suggest `impl Trait` to `dyn Trait`s.
17541754
// For example, `fn foo() -> str` needs to be filtered out.

compiler/rustc_trait_selection/src/traits/fulfill.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -369,7 +369,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> {
369369
}
370370

371371
ty::PredicateKind::ObjectSafe(trait_def_id) => {
372-
if !self.selcx.tcx().is_object_safe(trait_def_id) {
372+
if !self.selcx.tcx().check_is_object_safe(trait_def_id) {
373373
ProcessResult::Error(CodeSelectionError(Unimplemented))
374374
} else {
375375
ProcessResult::Changed(vec![])

compiler/rustc_trait_selection/src/traits/object_safety.rs

+33-14
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,37 @@ fn object_safety_violations(tcx: TyCtxt<'_>, trait_def_id: DefId) -> &'_ [Object
6262
)
6363
}
6464

65+
fn check_is_object_safe(tcx: TyCtxt<'_>, trait_def_id: DefId) -> bool {
66+
let violations = tcx.object_safety_violations(trait_def_id);
67+
68+
if violations.is_empty() {
69+
return true;
70+
}
71+
72+
// If the trait contains any other violations, then let the error reporting path
73+
// report it instead of emitting a warning here.
74+
if violations.iter().all(|violation| {
75+
matches!(
76+
violation,
77+
ObjectSafetyViolation::Method(_, MethodViolationCode::WhereClauseReferencesSelf, _)
78+
)
79+
}) {
80+
for violation in violations {
81+
if let ObjectSafetyViolation::Method(
82+
_,
83+
MethodViolationCode::WhereClauseReferencesSelf,
84+
span,
85+
) = violation
86+
{
87+
lint_object_unsafe_trait(tcx, *span, trait_def_id, &violation);
88+
}
89+
}
90+
return true;
91+
}
92+
93+
false
94+
}
95+
6596
/// We say a method is *vtable safe* if it can be invoked on a trait
6697
/// object. Note that object-safe traits can have some
6798
/// non-vtable-safe methods, so long as they require `Self: Sized` or
@@ -93,19 +124,6 @@ fn object_safety_violations_for_trait(
93124
object_safety_violation_for_method(tcx, trait_def_id, &item)
94125
.map(|(code, span)| ObjectSafetyViolation::Method(item.name, code, span))
95126
})
96-
.filter(|violation| {
97-
if let ObjectSafetyViolation::Method(
98-
_,
99-
MethodViolationCode::WhereClauseReferencesSelf,
100-
span,
101-
) = violation
102-
{
103-
lint_object_unsafe_trait(tcx, *span, trait_def_id, &violation);
104-
false
105-
} else {
106-
true
107-
}
108-
})
109127
.collect();
110128

111129
// Check the trait itself.
@@ -866,5 +884,6 @@ pub fn contains_illegal_impl_trait_in_trait<'tcx>(
866884
}
867885

868886
pub fn provide(providers: &mut ty::query::Providers) {
869-
*providers = ty::query::Providers { object_safety_violations, ..*providers };
887+
*providers =
888+
ty::query::Providers { object_safety_violations, check_is_object_safe, ..*providers };
870889
}

compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -466,7 +466,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
466466
if let Some(principal) = data.principal() {
467467
if !self.infcx.tcx.features().object_safe_for_dispatch {
468468
principal.with_self_ty(self.tcx(), self_ty)
469-
} else if self.tcx().is_object_safe(principal.def_id()) {
469+
} else if self.tcx().check_is_object_safe(principal.def_id()) {
470470
principal.with_self_ty(self.tcx(), self_ty)
471471
} else {
472472
return;

compiler/rustc_trait_selection/src/traits/select/confirmation.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1009,7 +1009,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
10091009
// `T` -> `Trait`
10101010
(_, &ty::Dynamic(ref data, r, ty::Dyn)) => {
10111011
let mut object_dids = data.auto_traits().chain(data.principal_def_id());
1012-
if let Some(did) = object_dids.find(|did| !tcx.is_object_safe(*did)) {
1012+
if let Some(did) = object_dids.find(|did| !tcx.check_is_object_safe(*did)) {
10131013
return Err(TraitNotObjectSafe(did));
10141014
}
10151015

compiler/rustc_trait_selection/src/traits/select/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -797,7 +797,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
797797
}
798798

799799
ty::PredicateKind::ObjectSafe(trait_def_id) => {
800-
if self.tcx().is_object_safe(trait_def_id) {
800+
if self.tcx().check_is_object_safe(trait_def_id) {
801801
Ok(EvaluatedToOk)
802802
} else {
803803
Ok(EvaluatedToErr)

compiler/rustc_traits/src/chalk/db.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -580,7 +580,7 @@ impl<'tcx> chalk_solve::RustIrDatabase<RustInterner<'tcx>> for RustIrDatabase<'t
580580
}
581581

582582
fn is_object_safe(&self, trait_id: chalk_ir::TraitId<RustInterner<'tcx>>) -> bool {
583-
self.interner.tcx.is_object_safe(trait_id.0)
583+
self.interner.tcx.check_is_object_safe(trait_id.0)
584584
}
585585

586586
fn hidden_opaque_type(

library/alloc/src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@
8787
#![warn(missing_debug_implementations)]
8888
#![warn(missing_docs)]
8989
#![allow(explicit_outlives_requirements)]
90+
#![cfg_attr(not(bootstrap), warn(multiple_supertrait_upcastable))]
9091
//
9192
// Library features:
9293
#![feature(alloc_layout_extra)]
@@ -195,6 +196,7 @@
195196
#![feature(c_unwind)]
196197
#![feature(with_negative_coherence)]
197198
#![cfg_attr(test, feature(panic_update_hook))]
199+
#![cfg_attr(not(bootstrap), feature(multiple_supertrait_upcastable))]
198200
//
199201
// Rustdoc features:
200202
#![feature(doc_cfg)]

library/core/src/error.rs

+1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ use crate::fmt::{Debug, Display};
2828
#[stable(feature = "rust1", since = "1.0.0")]
2929
#[cfg_attr(not(test), rustc_diagnostic_item = "Error")]
3030
#[rustc_has_incoherent_inherent_impls]
31+
#[cfg_attr(not(bootstrap), allow(multiple_supertrait_upcastable))]
3132
pub trait Error: Debug + Display {
3233
/// The lower-level source of this error, if any.
3334
///

library/core/src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@
9595
#![warn(missing_docs)]
9696
#![allow(explicit_outlives_requirements)]
9797
#![allow(incomplete_features)]
98+
#![cfg_attr(not(bootstrap), warn(multiple_supertrait_upcastable))]
9899
//
99100
// Library features:
100101
#![feature(const_align_offset)]
@@ -235,6 +236,7 @@
235236
#![feature(unsized_fn_params)]
236237
#![feature(asm_const)]
237238
#![feature(const_transmute_copy)]
239+
#![cfg_attr(not(bootstrap), feature(multiple_supertrait_upcastable))]
238240
//
239241
// Target features:
240242
#![feature(arm_target_feature)]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// check-pass
2+
3+
#![deny(multiple_supertrait_upcastable)]
4+
//~^ WARNING unknown lint: `multiple_supertrait_upcastable`
5+
//~| WARNING unknown lint: `multiple_supertrait_upcastable`
6+
//~| WARNING unknown lint: `multiple_supertrait_upcastable`
7+
#![warn(multiple_supertrait_upcastable)]
8+
//~^ WARNING unknown lint: `multiple_supertrait_upcastable`
9+
//~| WARNING unknown lint: `multiple_supertrait_upcastable`
10+
//~| WARNING unknown lint: `multiple_supertrait_upcastable`
11+
12+
fn main() {}

0 commit comments

Comments
 (0)