Skip to content

Commit d3fb29a

Browse files
authored
Rollup merge of #116401 - WaffleLapkin:vtablin''', r=oli-obk
Return multiple object-safety violation errors and code improvements to the object-safety check See individual commits for more information. Split off of #114260, since it turned out that the main intent of that PR was wrong. r? oli-obk
2 parents ab5c841 + ecdbefa commit d3fb29a

File tree

2 files changed

+52
-39
lines changed

2 files changed

+52
-39
lines changed

compiler/rustc_trait_selection/src/traits/object_safety.rs

+47-37
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,10 @@ fn check_is_object_safe(tcx: TyCtxt<'_>, trait_def_id: DefId) -> bool {
9797
/// object. Note that object-safe traits can have some
9898
/// non-vtable-safe methods, so long as they require `Self: Sized` or
9999
/// otherwise ensure that they cannot be used when `Self = Trait`.
100+
///
101+
/// [`MethodViolationCode::WhereClauseReferencesSelf`] is considered object safe due to backwards
102+
/// compatibility, see <https://github.com/rust-lang/rust/issues/51443> and
103+
/// [`WHERE_CLAUSES_OBJECT_SAFETY`].
100104
pub fn is_vtable_safe_method(tcx: TyCtxt<'_>, trait_def_id: DefId, method: ty::AssocItem) -> bool {
101105
debug_assert!(tcx.generics_of(trait_def_id).has_self);
102106
debug!("is_vtable_safe_method({:?}, {:?})", trait_def_id, method);
@@ -105,10 +109,9 @@ pub fn is_vtable_safe_method(tcx: TyCtxt<'_>, trait_def_id: DefId, method: ty::A
105109
return false;
106110
}
107111

108-
match virtual_call_violation_for_method(tcx, trait_def_id, method) {
109-
None | Some(MethodViolationCode::WhereClauseReferencesSelf) => true,
110-
Some(_) => false,
111-
}
112+
virtual_call_violations_for_method(tcx, trait_def_id, method)
113+
.iter()
114+
.all(|v| matches!(v, MethodViolationCode::WhereClauseReferencesSelf))
112115
}
113116

114117
fn object_safety_violations_for_trait(
@@ -119,7 +122,7 @@ fn object_safety_violations_for_trait(
119122
let mut violations: Vec<_> = tcx
120123
.associated_items(trait_def_id)
121124
.in_definition_order()
122-
.filter_map(|&item| object_safety_violation_for_assoc_item(tcx, trait_def_id, item))
125+
.flat_map(|&item| object_safety_violations_for_assoc_item(tcx, trait_def_id, item))
123126
.collect();
124127

125128
// Check the trait itself.
@@ -357,49 +360,52 @@ fn generics_require_sized_self(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
357360

358361
/// Returns `Some(_)` if this item makes the containing trait not object safe.
359362
#[instrument(level = "debug", skip(tcx), ret)]
360-
fn object_safety_violation_for_assoc_item(
363+
fn object_safety_violations_for_assoc_item(
361364
tcx: TyCtxt<'_>,
362365
trait_def_id: DefId,
363366
item: ty::AssocItem,
364-
) -> Option<ObjectSafetyViolation> {
367+
) -> Vec<ObjectSafetyViolation> {
365368
// Any item that has a `Self : Sized` requisite is otherwise
366369
// exempt from the regulations.
367370
if tcx.generics_require_sized_self(item.def_id) {
368-
return None;
371+
return Vec::new();
369372
}
370373

371374
match item.kind {
372375
// Associated consts are never object safe, as they can't have `where` bounds yet at all,
373376
// and associated const bounds in trait objects aren't a thing yet either.
374377
ty::AssocKind::Const => {
375-
Some(ObjectSafetyViolation::AssocConst(item.name, item.ident(tcx).span))
378+
vec![ObjectSafetyViolation::AssocConst(item.name, item.ident(tcx).span)]
376379
}
377-
ty::AssocKind::Fn => virtual_call_violation_for_method(tcx, trait_def_id, item).map(|v| {
378-
let node = tcx.hir().get_if_local(item.def_id);
379-
// Get an accurate span depending on the violation.
380-
let span = match (&v, node) {
381-
(MethodViolationCode::ReferencesSelfInput(Some(span)), _) => *span,
382-
(MethodViolationCode::UndispatchableReceiver(Some(span)), _) => *span,
383-
(MethodViolationCode::ReferencesImplTraitInTrait(span), _) => *span,
384-
(MethodViolationCode::ReferencesSelfOutput, Some(node)) => {
385-
node.fn_decl().map_or(item.ident(tcx).span, |decl| decl.output.span())
386-
}
387-
_ => item.ident(tcx).span,
388-
};
380+
ty::AssocKind::Fn => virtual_call_violations_for_method(tcx, trait_def_id, item)
381+
.into_iter()
382+
.map(|v| {
383+
let node = tcx.hir().get_if_local(item.def_id);
384+
// Get an accurate span depending on the violation.
385+
let span = match (&v, node) {
386+
(MethodViolationCode::ReferencesSelfInput(Some(span)), _) => *span,
387+
(MethodViolationCode::UndispatchableReceiver(Some(span)), _) => *span,
388+
(MethodViolationCode::ReferencesImplTraitInTrait(span), _) => *span,
389+
(MethodViolationCode::ReferencesSelfOutput, Some(node)) => {
390+
node.fn_decl().map_or(item.ident(tcx).span, |decl| decl.output.span())
391+
}
392+
_ => item.ident(tcx).span,
393+
};
389394

390-
ObjectSafetyViolation::Method(item.name, v, span)
391-
}),
395+
ObjectSafetyViolation::Method(item.name, v, span)
396+
})
397+
.collect(),
392398
// Associated types can only be object safe if they have `Self: Sized` bounds.
393399
ty::AssocKind::Type => {
394400
if !tcx.features().generic_associated_types_extended
395401
&& !tcx.generics_of(item.def_id).params.is_empty()
396402
&& !item.is_impl_trait_in_trait()
397403
{
398-
Some(ObjectSafetyViolation::GAT(item.name, item.ident(tcx).span))
404+
vec![ObjectSafetyViolation::GAT(item.name, item.ident(tcx).span)]
399405
} else {
400406
// We will permit associated types if they are explicitly mentioned in the trait object.
401407
// We can't check this here, as here we only check if it is guaranteed to not be possible.
402-
None
408+
Vec::new()
403409
}
404410
}
405411
}
@@ -409,11 +415,11 @@ fn object_safety_violation_for_assoc_item(
409415
/// object; this does not necessarily imply that the enclosing trait
410416
/// is not object safe, because the method might have a where clause
411417
/// `Self:Sized`.
412-
fn virtual_call_violation_for_method<'tcx>(
418+
fn virtual_call_violations_for_method<'tcx>(
413419
tcx: TyCtxt<'tcx>,
414420
trait_def_id: DefId,
415421
method: ty::AssocItem,
416-
) -> Option<MethodViolationCode> {
422+
) -> Vec<MethodViolationCode> {
417423
let sig = tcx.fn_sig(method.def_id).instantiate_identity();
418424

419425
// The method's first parameter must be named `self`
@@ -438,9 +444,14 @@ fn virtual_call_violation_for_method<'tcx>(
438444
} else {
439445
None
440446
};
441-
return Some(MethodViolationCode::StaticMethod(sugg));
447+
448+
// Not having `self` parameter messes up the later checks,
449+
// so we need to return instead of pushing
450+
return vec![MethodViolationCode::StaticMethod(sugg)];
442451
}
443452

453+
let mut errors = Vec::new();
454+
444455
for (i, &input_ty) in sig.skip_binder().inputs().iter().enumerate().skip(1) {
445456
if contains_illegal_self_type_reference(tcx, trait_def_id, sig.rebind(input_ty)) {
446457
let span = if let Some(hir::Node::TraitItem(hir::TraitItem {
@@ -452,20 +463,20 @@ fn virtual_call_violation_for_method<'tcx>(
452463
} else {
453464
None
454465
};
455-
return Some(MethodViolationCode::ReferencesSelfInput(span));
466+
errors.push(MethodViolationCode::ReferencesSelfInput(span));
456467
}
457468
}
458469
if contains_illegal_self_type_reference(tcx, trait_def_id, sig.output()) {
459-
return Some(MethodViolationCode::ReferencesSelfOutput);
470+
errors.push(MethodViolationCode::ReferencesSelfOutput);
460471
}
461472
if let Some(code) = contains_illegal_impl_trait_in_trait(tcx, method.def_id, sig.output()) {
462-
return Some(code);
473+
errors.push(code);
463474
}
464475

465476
// We can't monomorphize things like `fn foo<A>(...)`.
466477
let own_counts = tcx.generics_of(method.def_id).own_counts();
467-
if own_counts.types + own_counts.consts != 0 {
468-
return Some(MethodViolationCode::Generic);
478+
if own_counts.types > 0 || own_counts.consts > 0 {
479+
errors.push(MethodViolationCode::Generic);
469480
}
470481

471482
let receiver_ty = tcx.liberate_late_bound_regions(method.def_id, sig.input(0));
@@ -485,7 +496,7 @@ fn virtual_call_violation_for_method<'tcx>(
485496
} else {
486497
None
487498
};
488-
return Some(MethodViolationCode::UndispatchableReceiver(span));
499+
errors.push(MethodViolationCode::UndispatchableReceiver(span));
489500
} else {
490501
// Do sanity check to make sure the receiver actually has the layout of a pointer.
491502

@@ -593,10 +604,10 @@ fn virtual_call_violation_for_method<'tcx>(
593604

594605
contains_illegal_self_type_reference(tcx, trait_def_id, pred)
595606
}) {
596-
return Some(MethodViolationCode::WhereClauseReferencesSelf);
607+
errors.push(MethodViolationCode::WhereClauseReferencesSelf);
597608
}
598609

599-
None
610+
errors
600611
}
601612

602613
/// Performs a type substitution to produce the version of `receiver_ty` when `Self = self_ty`.
@@ -709,7 +720,6 @@ fn object_ty_for_trait<'tcx>(
709720
// FIXME(mikeyhew) when unsized receivers are implemented as part of unsized rvalues, add this
710721
// fallback query: `Receiver: Unsize<Receiver[Self => U]>` to support receivers like
711722
// `self: Wrapper<Self>`.
712-
#[allow(dead_code)]
713723
fn receiver_is_dispatchable<'tcx>(
714724
tcx: TyCtxt<'tcx>,
715725
method: ty::AssocItem,

tests/ui/const-generics/generic_const_exprs/object-safety-err-ret.stderr

+5-2
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,15 @@ LL | fn use_dyn(v: &dyn Foo) {
55
| ^^^^^^^ `Foo` cannot be made into an object
66
|
77
note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>
8-
--> $DIR/object-safety-err-ret.rs:8:23
8+
--> $DIR/object-safety-err-ret.rs:8:8
99
|
1010
LL | trait Foo {
1111
| --- this trait cannot be made into an object...
1212
LL | fn test(&self) -> [u8; bar::<Self>()];
13-
| ^^^^^^^^^^^^^^^^^^^ ...because method `test` references the `Self` type in its return type
13+
| ^^^^ ^^^^^^^^^^^^^^^^^^^ ...because method `test` references the `Self` type in its return type
14+
| |
15+
| ...because method `test` references the `Self` type in its `where` clause
16+
= help: consider moving `test` to another trait
1417
= help: consider moving `test` to another trait
1518

1619
error: aborting due to previous error

0 commit comments

Comments
 (0)