Skip to content

Commit d27c6c6

Browse files
committed
Auto merge of #66037 - nikomatsakis:degenerate-object-safe-issue-57893, r=<try>
[wip] degenerate object safety check for crater The purpose of this PR is just to be able to do a crater run to assess the impact of the proposed fix for #57893. With this fix: * traits that are implemented for unsized types are not dyn safe * unless they are marked `#[rustc_dyn]` (this would eventually be `dyn trait`) r? @nikomatsakis -- this doesn't need review, won't land in this form
2 parents 30ca215 + 46cef3e commit d27c6c6

File tree

5 files changed

+93
-5
lines changed

5 files changed

+93
-5
lines changed

src/librustc/traits/object_safety.rs

Lines changed: 89 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,11 @@ use super::elaborate_predicates;
1212

1313
use crate::traits::{self, Obligation, ObligationCause};
1414
use crate::ty::subst::{InternalSubsts, Subst};
15-
use crate::ty::{self, Predicate, ToPredicate, Ty, TyCtxt, TypeFoldable};
15+
use crate::ty::{self, ParamTy, Predicate, ToPredicate, Ty, TyCtxt, TypeFoldable};
1616
use rustc_hir as hir;
1717
use rustc_hir::def_id::DefId;
1818
use rustc_session::lint::builtin::WHERE_CLAUSES_OBJECT_SAFETY;
19-
use rustc_span::symbol::Symbol;
19+
use rustc_span::symbol::{sym, Symbol};
2020
use rustc_span::{Span, DUMMY_SP};
2121
use syntax::ast;
2222

@@ -269,6 +269,66 @@ fn predicates_reference_self(tcx: TyCtxt<'_>, trait_def_id: DefId, supertraits_o
269269
})
270270
}
271271

272+
fn generics_require_sized_param(tcx: TyCtxt<'_>, def_id: DefId, param_ty: ParamTy) -> bool {
273+
debug!("generics_require_sized_param(def_id={:?}, param_ty={:?})", def_id, param_ty);
274+
275+
let sized_def_id = match tcx.lang_items().sized_trait() {
276+
Some(def_id) => def_id,
277+
None => {
278+
return false; /* No Sized trait, can't require it! */
279+
}
280+
};
281+
282+
// Search for a predicate like `Self : Sized` amongst the trait bounds.
283+
let predicates = tcx.predicates_of(def_id);
284+
let predicates = predicates.instantiate_identity(tcx).predicates;
285+
predicates.iter().any(|predicate| match predicate {
286+
ty::Predicate::Trait(ref trait_pred) => {
287+
debug!("generics_require_sized_param: trait_pred = {:?}", trait_pred);
288+
289+
trait_pred.def_id() == sized_def_id
290+
&& trait_pred.skip_binder().self_ty().is_param(param_ty.index)
291+
}
292+
ty::Predicate::Projection(..)
293+
| ty::Predicate::Subtype(..)
294+
| ty::Predicate::RegionOutlives(..)
295+
| ty::Predicate::WellFormed(..)
296+
| ty::Predicate::ObjectSafe(..)
297+
| ty::Predicate::ClosureKind(..)
298+
| ty::Predicate::TypeOutlives(..)
299+
| ty::Predicate::ConstEvaluatable(..) => false,
300+
})
301+
}
302+
303+
/// Searches for an impl that potentially overlaps `dyn Trait`
304+
/// (where `Trait` is the trait with def-id `trait_def_id`). This
305+
/// is used to distinguish between a trait being **fully**
306+
/// object-safe and being **degenerate** object-safe -- the latter
307+
/// means that we permit `dyn Foo` but we do not supply a `dyn
308+
/// Foo: Foo` impl.
309+
fn impl_potentially_overlapping_dyn_trait(tcx: TyCtxt<'_>, trait_def_id: DefId) -> bool {
310+
debug!("impl_potentially_overlapping_dyn_trait({:?})", trait_def_id);
311+
let mut found_match = false;
312+
tcx.for_each_impl(trait_def_id, |impl_def_id| {
313+
let impl_self_ty = tcx.type_of(impl_def_id);
314+
match impl_self_ty.kind {
315+
ty::Param(param_ty) => {
316+
if !generics_require_sized_param(tcx, impl_def_id, param_ty) {
317+
found_match = true;
318+
debug!("Match found = {}; for param_ty {}", found_match, param_ty.name);
319+
tcx.sess.span_warn(
320+
tcx.def_span(impl_def_id),
321+
"impl_potentially_overlapping_dyn_trait",
322+
);
323+
}
324+
}
325+
_ => (),
326+
}
327+
});
328+
329+
found_match
330+
}
331+
272332
fn trait_has_sized_self(tcx: TyCtxt<'_>, trait_def_id: DefId) -> bool {
273333
generics_require_sized_self(tcx, trait_def_id)
274334
}
@@ -417,7 +477,7 @@ fn virtual_call_violation_for_method<'tcx>(
417477
tcx.def_span(method.def_id),
418478
&format!(
419479
"receiver when `Self = {}` should have a ScalarPair ABI; \
420-
found {:?}",
480+
found {:?}",
421481
trait_object_ty, abi
422482
),
423483
);
@@ -716,13 +776,37 @@ fn contains_illegal_self_type_reference<'tcx>(
716776
}
717777
}
718778

719-
_ => true, // walk contained types, if any
779+
_ => true,
720780
}
721781
});
722782

723783
error
724784
}
725785

726786
pub(super) fn is_object_safe_provider(tcx: TyCtxt<'_>, trait_def_id: DefId) -> bool {
727-
object_safety_violations(tcx, trait_def_id).is_empty()
787+
object_safety_violations(tcx, trait_def_id).is_empty() && {
788+
if tcx.has_attr(trait_def_id, sym::rustc_dyn) {
789+
true
790+
} else {
791+
// Issue #57893. A trait cannot be considered dyn-safe if:
792+
//
793+
// (a) it has associated items that are not functions and
794+
// (b) it has a potentially dyn-overlapping impl.
795+
//
796+
// Why don't functions matter? Because we never resolve
797+
// them to their normalizd type until code generation
798+
// time, in short.
799+
let has_associated_non_fn = traits::supertrait_def_ids(tcx, trait_def_id)
800+
.flat_map(|def_id| tcx.associated_items(def_id))
801+
.any(|assoc_item| match assoc_item.kind {
802+
ty::AssocKind::Method => false,
803+
ty::AssocKind::Type | ty::AssocKind::OpaqueTy | ty::AssocKind::Const => true,
804+
});
805+
806+
let not_object_safe =
807+
has_associated_non_fn && impl_potentially_overlapping_dyn_trait(tcx, trait_def_id);
808+
809+
!not_object_safe
810+
}
811+
}
728812
}

src/librustc_data_structures/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#![feature(integer_atomics)]
2424
#![feature(test)]
2525
#![feature(associated_type_bounds)]
26+
#![feature(rustc_attrs)]
2627
#![cfg_attr(unix, feature(libc))]
2728
#![allow(rustc::default_hash_types)]
2829

src/librustc_data_structures/sync.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ pub use std::sync::atomic::Ordering::SeqCst;
2929
cfg_if! {
3030
if #[cfg(not(parallel_compiler))] {
3131
pub auto trait Send {}
32+
3233
pub auto trait Sync {}
3334

3435
impl<T: ?Sized> Send for T {}

src/librustc_feature/builtin_attrs.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -536,6 +536,7 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
536536
// Internal attributes, Testing:
537537
// ==========================================================================
538538

539+
rustc_attr!(TEST, rustc_dyn, Whitelisted, template!(Word)),
539540
rustc_attr!(TEST, rustc_outlives, Normal, template!(Word)),
540541
rustc_attr!(TEST, rustc_variance, Normal, template!(Word)),
541542
rustc_attr!(TEST, rustc_layout, Normal, template!(List: "field1, field2, ...")),

src/librustc_span/symbol.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -620,6 +620,7 @@ symbols! {
620620
rustc_dump_env_program_clauses,
621621
rustc_dump_program_clauses,
622622
rustc_dump_user_substs,
623+
rustc_dyn,
623624
rustc_error,
624625
rustc_expected_cgu_reuse,
625626
rustc_if_this_changed,

0 commit comments

Comments
 (0)