Skip to content

Commit ae0e3d0

Browse files
committed
Tweak "object unsafe" errors
Fix #77598.
1 parent 9832374 commit ae0e3d0

File tree

57 files changed

+699
-421
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

57 files changed

+699
-421
lines changed

compiler/rustc_infer/src/traits/error_reporting/mod.rs

+22-15
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@ use super::ObjectSafetyViolation;
22

33
use crate::infer::InferCtxt;
44
use rustc_data_structures::fx::FxHashSet;
5-
use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder};
5+
use rustc_errors::{struct_span_err, DiagnosticBuilder};
66
use rustc_hir as hir;
77
use rustc_hir::def_id::DefId;
88
use rustc_middle::ty::TyCtxt;
99
use rustc_span::symbol::Symbol;
10-
use rustc_span::Span;
10+
use rustc_span::{MultiSpan, Span};
1111
use std::fmt;
1212

1313
impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
@@ -57,7 +57,8 @@ pub fn report_object_safety_error(
5757
err.span_label(span, format!("the trait `{}` cannot be made into an object", trait_str));
5858

5959
let mut reported_violations = FxHashSet::default();
60-
let mut had_span_label = false;
60+
let mut multi_span = vec![];
61+
let mut messages = vec![];
6162
for violation in violations {
6263
if let ObjectSafetyViolation::SizedSelf(sp) = &violation {
6364
if !sp.is_empty() {
@@ -71,31 +72,37 @@ pub fn report_object_safety_error(
7172
let msg = if trait_span.is_none() || spans.is_empty() {
7273
format!("the trait cannot be made into an object because {}", violation.error_msg())
7374
} else {
74-
had_span_label = true;
7575
format!("...because {}", violation.error_msg())
7676
};
7777
if spans.is_empty() {
7878
err.note(&msg);
7979
} else {
8080
for span in spans {
81-
err.span_label(span, &msg);
81+
multi_span.push(span);
82+
messages.push(msg.clone());
8283
}
8384
}
84-
match (trait_span, violation.solution()) {
85-
(Some(_), Some((note, None))) => {
86-
err.help(&note);
87-
}
88-
(Some(_), Some((note, Some((sugg, span))))) => {
89-
err.span_suggestion(span, &note, sugg, Applicability::MachineApplicable);
90-
}
85+
if trait_span.is_some() {
9186
// Only provide the help if its a local trait, otherwise it's not actionable.
92-
_ => {}
87+
violation.solution(&mut err);
9388
}
9489
}
9590
}
96-
if let (Some(trait_span), true) = (trait_span, had_span_label) {
97-
err.span_label(trait_span, "this trait cannot be made into an object...");
91+
let has_multi_span = !multi_span.is_empty();
92+
let mut note_span = MultiSpan::from_spans(multi_span.clone());
93+
if let (Some(trait_span), true) = (trait_span, has_multi_span) {
94+
note_span
95+
.push_span_label(trait_span, "this trait cannot be made into an object...".to_string());
9896
}
97+
for (span, msg) in multi_span.into_iter().zip(messages.into_iter()) {
98+
note_span.push_span_label(span, msg);
99+
}
100+
err.span_note(
101+
note_span,
102+
"for a trait to be \"object safe\" it needs to allow building a vtable to allow the call \
103+
to be resolvable dynamically; for more information visit \
104+
<https://doc.rust-lang.org/reference/items/traits.html#object-safety>",
105+
);
99106

100107
if tcx.sess.trait_methods_not_found.borrow().contains(&span) {
101108
// Avoid emitting error caused by non-existing method (#58734)

compiler/rustc_middle/src/traits/mod.rs

+54-20
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ use crate::mir::interpret::ErrorHandled;
1313
use crate::ty::subst::SubstsRef;
1414
use crate::ty::{self, AdtKind, Ty, TyCtxt};
1515

16+
use rustc_errors::{Applicability, DiagnosticBuilder};
1617
use rustc_hir as hir;
1718
use rustc_hir::def_id::DefId;
1819
use rustc_span::symbol::Symbol;
@@ -652,7 +653,7 @@ impl ObjectSafetyViolation {
652653
.into()
653654
}
654655
}
655-
ObjectSafetyViolation::Method(name, MethodViolationCode::StaticMethod(_), _) => {
656+
ObjectSafetyViolation::Method(name, MethodViolationCode::StaticMethod(_, _, _), _) => {
656657
format!("associated function `{}` has no `self` parameter", name).into()
657658
}
658659
ObjectSafetyViolation::Method(
@@ -686,32 +687,65 @@ impl ObjectSafetyViolation {
686687
}
687688
}
688689

689-
pub fn solution(&self) -> Option<(String, Option<(String, Span)>)> {
690-
Some(match *self {
691-
ObjectSafetyViolation::SizedSelf(_) | ObjectSafetyViolation::SupertraitSelf(_) => {
692-
return None;
690+
pub fn solution(&self, err: &mut DiagnosticBuilder<'_>) {
691+
match *self {
692+
ObjectSafetyViolation::SizedSelf(_) | ObjectSafetyViolation::SupertraitSelf(_) => {}
693+
ObjectSafetyViolation::Method(
694+
name,
695+
MethodViolationCode::StaticMethod(sugg, self_span, has_args),
696+
_,
697+
) => {
698+
err.span_suggestion(
699+
self_span,
700+
&format!(
701+
"consider turning `{}` into a method by giving it a `&self` argument",
702+
name
703+
),
704+
format!("&self{}", if has_args { ", " } else { "" }),
705+
Applicability::MaybeIncorrect,
706+
);
707+
match sugg {
708+
Some((sugg, span)) => {
709+
err.span_suggestion(
710+
span,
711+
&format!(
712+
"alternatively, consider constraining `{}` so it does not apply to \
713+
trait objects",
714+
name
715+
),
716+
sugg.to_string(),
717+
Applicability::MaybeIncorrect,
718+
);
719+
}
720+
None => {
721+
err.help(&format!(
722+
"consider turning `{}` into a method by giving it a `&self` \
723+
argument or constraining it so it does not apply to trait objects",
724+
name
725+
));
726+
}
727+
}
693728
}
694-
ObjectSafetyViolation::Method(name, MethodViolationCode::StaticMethod(sugg), _) => (
695-
format!(
696-
"consider turning `{}` into a method by giving it a `&self` argument or \
697-
constraining it so it does not apply to trait objects",
698-
name
699-
),
700-
sugg.map(|(sugg, sp)| (sugg.to_string(), sp)),
701-
),
702729
ObjectSafetyViolation::Method(
703730
name,
704731
MethodViolationCode::UndispatchableReceiver,
705732
span,
706-
) => (
707-
format!("consider changing method `{}`'s `self` parameter to be `&self`", name),
708-
Some(("&Self".to_string(), span)),
709-
),
733+
) => {
734+
err.span_suggestion(
735+
span,
736+
&format!(
737+
"consider changing method `{}`'s `self` parameter to be `&self`",
738+
name
739+
),
740+
"&Self".to_string(),
741+
Applicability::MachineApplicable,
742+
);
743+
}
710744
ObjectSafetyViolation::AssocConst(name, _)
711745
| ObjectSafetyViolation::Method(name, ..) => {
712-
(format!("consider moving `{}` to another trait", name), None)
746+
err.help(&format!("consider moving `{}` to another trait", name));
713747
}
714-
})
748+
}
715749
}
716750

717751
pub fn spans(&self) -> SmallVec<[Span; 1]> {
@@ -735,7 +769,7 @@ impl ObjectSafetyViolation {
735769
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, HashStable)]
736770
pub enum MethodViolationCode {
737771
/// e.g., `fn foo()`
738-
StaticMethod(Option<(&'static str, Span)>),
772+
StaticMethod(Option<(&'static str, Span)>, Span, bool /* has args */),
739773

740774
/// e.g., `fn foo(&self, x: Self)`
741775
ReferencesSelfInput(usize),

compiler/rustc_trait_selection/src/traits/object_safety.rs

+42-28
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,15 @@ use super::elaborate_predicates;
1313
use crate::infer::TyCtxtInferExt;
1414
use crate::traits::query::evaluate_obligation::InferCtxtExt;
1515
use crate::traits::{self, Obligation, ObligationCause};
16-
use rustc_errors::{Applicability, FatalError};
16+
use rustc_errors::FatalError;
1717
use rustc_hir as hir;
1818
use rustc_hir::def_id::DefId;
1919
use rustc_middle::ty::subst::{GenericArg, InternalSubsts, Subst};
2020
use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, TypeVisitor, WithConstness};
2121
use rustc_middle::ty::{Predicate, ToPredicate};
2222
use rustc_session::lint::builtin::WHERE_CLAUSES_OBJECT_SAFETY;
2323
use rustc_span::symbol::Symbol;
24-
use rustc_span::Span;
24+
use rustc_span::{MultiSpan, Span};
2525
use smallvec::SmallVec;
2626

2727
use std::array;
@@ -112,33 +112,35 @@ fn object_safety_violations_for_trait(
112112
tcx.def_path_str(trait_def_id)
113113
));
114114
let node = tcx.hir().get_if_local(trait_def_id);
115-
let msg = if let Some(hir::Node::Item(item)) = node {
116-
err.span_label(
115+
let mut spans = MultiSpan::from_span(*span);
116+
if let Some(hir::Node::Item(item)) = node {
117+
spans.push_span_label(
117118
item.ident.span,
118-
"this trait cannot be made into an object...",
119+
"this trait cannot be made into an object...".into(),
120+
);
121+
spans.push_span_label(
122+
*span,
123+
format!("...because {}", violation.error_msg()),
119124
);
120-
format!("...because {}", violation.error_msg())
121125
} else {
122-
format!(
123-
"the trait cannot be made into an object because {}",
124-
violation.error_msg()
125-
)
126+
spans.push_span_label(
127+
*span,
128+
format!(
129+
"the trait cannot be made into an object because {}",
130+
violation.error_msg()
131+
),
132+
);
126133
};
127-
err.span_label(*span, &msg);
128-
match (node, violation.solution()) {
129-
(Some(_), Some((note, None))) => {
130-
err.help(&note);
131-
}
132-
(Some(_), Some((note, Some((sugg, span))))) => {
133-
err.span_suggestion(
134-
span,
135-
&note,
136-
sugg,
137-
Applicability::MachineApplicable,
138-
);
139-
}
140-
// Only provide the help if its a local trait, otherwise it's not actionable.
141-
_ => {}
134+
err.span_note(
135+
spans,
136+
"for a trait to be \"object safe\" it needs to allow building a vtable \
137+
to allow the call to be resolvable dynamically; for more information \
138+
visit <https://doc.rust-lang.org/reference/items/traits.html\
139+
#object-safety>",
140+
);
141+
if node.is_some() {
142+
// Only provide the help if its a local trait, otherwise it's not
143+
violation.solution(&mut err);
142144
}
143145
err.emit();
144146
},
@@ -385,6 +387,8 @@ fn virtual_call_violation_for_method<'tcx>(
385387
trait_def_id: DefId,
386388
method: &ty::AssocItem,
387389
) -> Option<MethodViolationCode> {
390+
let sig = tcx.fn_sig(method.def_id);
391+
388392
// The method's first parameter must be named `self`
389393
if !method.fn_has_self_parameter {
390394
// We'll attempt to provide a structured suggestion for `Self: Sized`.
@@ -395,11 +399,21 @@ fn virtual_call_violation_for_method<'tcx>(
395399
[.., pred] => (", Self: Sized", pred.span().shrink_to_hi()),
396400
},
397401
);
398-
return Some(MethodViolationCode::StaticMethod(sugg));
402+
// Get the span pointing at where the `self` receiver should be.
403+
let sm = tcx.sess.source_map();
404+
let self_span = method.ident.span.to(tcx
405+
.hir()
406+
.span_if_local(method.def_id)
407+
.unwrap_or_else(|| sm.next_point(method.ident.span))
408+
.shrink_to_hi());
409+
let self_span = sm.span_through_char(self_span, '(').shrink_to_hi();
410+
return Some(MethodViolationCode::StaticMethod(
411+
sugg,
412+
self_span,
413+
!sig.inputs().skip_binder().is_empty(),
414+
));
399415
}
400416

401-
let sig = tcx.fn_sig(method.def_id);
402-
403417
for (i, input_ty) in sig.skip_binder().inputs()[1..].iter().enumerate() {
404418
if contains_illegal_self_type_reference(tcx, trait_def_id, input_ty) {
405419
return Some(MethodViolationCode::ReferencesSelfInput(i));

src/test/ui/associated-consts/associated-const-in-trait.stderr

+7-5
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
11
error[E0038]: the trait `Trait` cannot be made into an object
22
--> $DIR/associated-const-in-trait.rs:9:6
33
|
4-
LL | trait Trait {
5-
| ----- this trait cannot be made into an object...
6-
LL | const N: usize;
7-
| - ...because it contains this associated `const`
8-
...
94
LL | impl dyn Trait {
105
| ^^^^^^^^^ the trait `Trait` cannot be made into an object
116
|
127
= help: consider moving `N` to another trait
8+
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>
9+
--> $DIR/associated-const-in-trait.rs:6:11
10+
|
11+
LL | trait Trait {
12+
| ----- this trait cannot be made into an object...
13+
LL | const N: usize;
14+
| ^ ...because it contains this associated `const`
1315

1416
error: aborting due to previous error
1517

src/test/ui/associated-item/issue-48027.stderr

+7-5
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
11
error[E0038]: the trait `Bar` cannot be made into an object
22
--> $DIR/issue-48027.rs:6:6
33
|
4-
LL | trait Bar {
5-
| --- this trait cannot be made into an object...
6-
LL | const X: usize;
7-
| - ...because it contains this associated `const`
8-
...
94
LL | impl dyn Bar {}
105
| ^^^^^^^ the trait `Bar` cannot be made into an object
116
|
127
= help: consider moving `X` to another trait
8+
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>
9+
--> $DIR/issue-48027.rs:2:11
10+
|
11+
LL | trait Bar {
12+
| --- this trait cannot be made into an object...
13+
LL | const X: usize;
14+
| ^ ...because it contains this associated `const`
1315

1416
error[E0283]: type annotations needed
1517
--> $DIR/issue-48027.rs:3:32

src/test/ui/coherence/coherence-impl-trait-for-trait-object-safe.stderr

+7-4
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11
error[E0038]: the trait `NotObjectSafe` cannot be made into an object
22
--> $DIR/coherence-impl-trait-for-trait-object-safe.rs:7:24
33
|
4-
LL | trait NotObjectSafe { fn eq(&self, other: Self); }
5-
| ------------- ---- ...because method `eq` references the `Self` type in this parameter
6-
| |
7-
| this trait cannot be made into an object...
84
LL | impl NotObjectSafe for dyn NotObjectSafe { }
95
| ^^^^^^^^^^^^^^^^^ the trait `NotObjectSafe` cannot be made into an object
106
|
117
= help: consider moving `eq` to another trait
8+
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>
9+
--> $DIR/coherence-impl-trait-for-trait-object-safe.rs:6:43
10+
|
11+
LL | trait NotObjectSafe { fn eq(&self, other: Self); }
12+
| ------------- ^^^^ ...because method `eq` references the `Self` type in this parameter
13+
| |
14+
| this trait cannot be made into an object...
1215

1316
error: aborting due to previous error
1417

src/test/ui/did_you_mean/trait-object-reference-without-parens-suggestion.stderr

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ LL | let _: &Copy + 'static;
1717
| ^^^^^ the trait `Copy` cannot be made into an object
1818
|
1919
= note: the trait cannot be made into an object because it requires `Self: Sized`
20+
= 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>
2021

2122
error: aborting due to 3 previous errors
2223

src/test/ui/error-codes/E0033-teach.stderr

+11-5
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,21 @@ LL | let trait_obj: &dyn SomeTrait = SomeTrait;
77
error[E0038]: the trait `SomeTrait` cannot be made into an object
88
--> $DIR/E0033-teach.rs:8:20
99
|
10+
LL | let trait_obj: &dyn SomeTrait = SomeTrait;
11+
| ^^^^^^^^^^^^^^ the trait `SomeTrait` cannot be made into an object
12+
|
13+
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>
14+
--> $DIR/E0033-teach.rs:4:8
15+
|
1016
LL | trait SomeTrait {
1117
| --------- this trait cannot be made into an object...
1218
LL | fn foo();
13-
| --- ...because associated function `foo` has no `self` parameter
14-
...
15-
LL | let trait_obj: &dyn SomeTrait = SomeTrait;
16-
| ^^^^^^^^^^^^^^ the trait `SomeTrait` cannot be made into an object
19+
| ^^^ ...because associated function `foo` has no `self` parameter
20+
help: consider turning `foo` into a method by giving it a `&self` argument
1721
|
18-
help: consider turning `foo` into a method by giving it a `&self` argument or constraining it so it does not apply to trait objects
22+
LL | fn foo(&self);
23+
| ^^^^^
24+
help: alternatively, consider constraining `foo` so it does not apply to trait objects
1925
|
2026
LL | fn foo() where Self: Sized;
2127
| ^^^^^^^^^^^^^^^^^

0 commit comments

Comments
 (0)