Skip to content

Commit 00e2626

Browse files
committed
Account for object safety when suggesting Box<dyn Trait>
1 parent d7a6212 commit 00e2626

10 files changed

+202
-19
lines changed

src/librustc/traits/error_reporting/suggestions.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -696,11 +696,13 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
696696
format!("Box<{}{}>", if has_dyn { "" } else { "dyn " }, snippet),
697697
));
698698
err.multipart_suggestion(
699-
"return a trait object instead",
699+
"return a boxed trait object instead",
700700
suggestions,
701701
Applicability::MaybeIncorrect,
702702
);
703703
} else {
704+
// This is currently not possible to trigger because E0038 takes precedence, but
705+
// leave it in for completeness in case anything changes in an earlier stage.
704706
err.note(&format!(
705707
"if trait `{}` was object safe, you could return a trait object",
706708
trait_obj,

src/librustc_hir/hir.rs

+7
Original file line numberDiff line numberDiff line change
@@ -377,6 +377,13 @@ pub enum GenericBound<'hir> {
377377
}
378378

379379
impl GenericBound<'_> {
380+
pub fn trait_def_id(&self) -> Option<DefId> {
381+
match self {
382+
GenericBound::Trait(data, _) => Some(data.trait_ref.trait_def_id()),
383+
_ => None,
384+
}
385+
}
386+
380387
pub fn span(&self) -> Span {
381388
match self {
382389
&GenericBound::Trait(ref t, ..) => t.span,

src/librustc_typeck/check/coercion.rs

+43-10
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ use crate::check::{FnCtxt, Needs};
5555
use rustc::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
5656
use rustc::infer::{Coercion, InferOk, InferResult};
5757
use rustc::session::parse::feature_err;
58+
use rustc::traits::object_safety_violations;
5859
use rustc::traits::{self, ObligationCause, ObligationCauseCode};
5960
use rustc::ty::adjustment::{
6061
Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, PointerCast,
@@ -68,8 +69,8 @@ use rustc_error_codes::*;
6869
use rustc_errors::{struct_span_err, DiagnosticBuilder};
6970
use rustc_hir as hir;
7071
use rustc_hir::def_id::DefId;
71-
use rustc_span::{self, Span};
7272
use rustc_span::symbol::sym;
73+
use rustc_span::{self, Span};
7374
use rustc_target::spec::abi::Abi;
7475
use smallvec::{smallvec, SmallVec};
7576
use std::ops::Deref;
@@ -1311,7 +1312,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
13111312
let mut err = fcx.report_mismatched_types(cause, expected, found, ty_err);
13121313

13131314
let mut pointing_at_return_type = false;
1314-
let mut return_sp = None;
1315+
let mut fn_output = None;
13151316

13161317
// Verify that this is a tail expression of a function, otherwise the
13171318
// label pointing out the cause for the type coercion will be wrong
@@ -1348,11 +1349,11 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
13481349
);
13491350
}
13501351
if !pointing_at_return_type {
1351-
return_sp = Some(fn_decl.output.span()); // `impl Trait` return type
1352+
fn_output = Some(&fn_decl.output); // `impl Trait` return type
13521353
}
13531354
}
1354-
if let (Some(sp), Some(return_sp)) = (fcx.ret_coercion_span.borrow().as_ref(), return_sp) {
1355-
self.add_impl_trait_explanation(&mut err, fcx, expected, *sp, return_sp);
1355+
if let (Some(sp), Some(fn_output)) = (fcx.ret_coercion_span.borrow().as_ref(), fn_output) {
1356+
self.add_impl_trait_explanation(&mut err, fcx, expected, *sp, fn_output);
13561357
}
13571358
err
13581359
}
@@ -1363,8 +1364,9 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
13631364
fcx: &FnCtxt<'a, 'tcx>,
13641365
expected: Ty<'tcx>,
13651366
sp: Span,
1366-
return_sp: Span,
1367+
fn_output: &hir::FunctionRetTy<'_>,
13671368
) {
1369+
let return_sp = fn_output.span();
13681370
err.span_label(return_sp, "expected because this return type...");
13691371
err.span_label(
13701372
sp,
@@ -1386,11 +1388,42 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
13861388
.unwrap_or_else(|_| "dyn Trait".to_string());
13871389
let mut snippet_iter = snippet.split_whitespace();
13881390
let has_impl = snippet_iter.next().map_or(false, |s| s == "impl");
1391+
// Only suggest `Box<dyn Trait>` if `Trait` in `impl Trait` is object safe.
1392+
let mut is_object_safe = false;
1393+
if let hir::FunctionRetTy::Return(ty) = fn_output {
1394+
// Get the return type.
1395+
if let hir::TyKind::Def(..) = ty.kind {
1396+
let ty = AstConv::ast_ty_to_ty(fcx, ty);
1397+
// Get the `impl Trait`'s `DefId`.
1398+
if let ty::Opaque(def_id, _) = ty.kind {
1399+
let hir_id = fcx.tcx.hir().as_local_hir_id(def_id).unwrap();
1400+
// Get the `impl Trait`'s `Item` so that we can get its trait bounds and
1401+
// get the `Trait`'s `DefId`.
1402+
if let hir::ItemKind::OpaqueTy(hir::OpaqueTy { bounds, .. }) =
1403+
fcx.tcx.hir().expect_item(hir_id).kind
1404+
{
1405+
// Are of this `impl Trait`'s traits object safe?
1406+
is_object_safe = bounds.iter().all(|bound| {
1407+
bound.trait_def_id().map_or(false, |def_id| {
1408+
object_safety_violations(fcx.tcx, def_id).is_empty()
1409+
})
1410+
})
1411+
}
1412+
}
1413+
}
1414+
};
13891415
if has_impl {
1390-
err.help(&format!(
1391-
"you can instead return a trait object using `Box<dyn {}>`",
1392-
&snippet[5..]
1393-
));
1416+
if is_object_safe {
1417+
err.help(&format!(
1418+
"you can instead return a boxed trait object using `Box<dyn {}>`",
1419+
&snippet[5..]
1420+
));
1421+
} else {
1422+
err.help(&format!(
1423+
"if the trait `{}` were object safe, you could return a boxed trait object",
1424+
&snippet[5..]
1425+
));
1426+
}
13941427
err.note(trait_obj_msg);
13951428
}
13961429
err.help("alternatively, create a new `enum` with a variant for each returned type");

src/test/ui/impl-trait/dyn-trait-return-should-be-impl-trait.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ LL | fn bal() -> dyn Trait {
8686
= note: if all the returned values were of the same type you could use `impl Trait` as the return type
8787
= note: for information on `impl Trait`, see <https://doc.rust-lang.org/book/ch10-02-traits.html#returning-types-that-implement-traits>
8888
= note: you can create a new `enum` with a variant for each returned type
89-
help: return a trait object instead
89+
help: return a boxed trait object instead
9090
|
9191
LL | fn bal() -> Box<dyn Trait> {
9292
LL | if true {

src/test/ui/impl-trait/equality.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ LL | 0_u32
1212
|
1313
= note: to return `impl Trait`, all returned values must be of the same type
1414
= note: for information on `impl Trait`, see <https://doc.rust-lang.org/book/ch10-02-traits.html#returning-types-that-implement-traits>
15-
= help: you can instead return a trait object using `Box<dyn Foo>`
15+
= help: if the trait `Foo` were object safe, you could return a boxed trait object
1616
= note: for information on trait objects, see <https://doc.rust-lang.org/book/ch17-02-trait-objects.html#using-trait-objects-that-allow-for-values-of-different-types>
1717
= help: alternatively, create a new `enum` with a variant for each returned type
1818

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
#![allow(bare_trait_objects)]
2+
trait NotObjectSafe {
3+
fn foo() -> Self;
4+
}
5+
6+
struct A;
7+
struct B;
8+
9+
impl NotObjectSafe for A {
10+
fn foo() -> Self {
11+
A
12+
}
13+
}
14+
15+
impl NotObjectSafe for B {
16+
fn foo() -> Self {
17+
B
18+
}
19+
}
20+
21+
fn car() -> dyn NotObjectSafe { //~ ERROR the trait `NotObjectSafe` cannot be made into an object
22+
if true {
23+
return A;
24+
}
25+
B
26+
}
27+
28+
fn cat() -> Box<dyn NotObjectSafe> { //~ ERROR the trait `NotObjectSafe` cannot be made into an
29+
if true {
30+
return Box::new(A);
31+
}
32+
Box::new(B)
33+
}
34+
35+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
error[E0038]: the trait `NotObjectSafe` cannot be made into an object
2+
--> $DIR/object-unsafe-trait-in-return-position-dyn-trait.rs:21:1
3+
|
4+
LL | fn foo() -> Self;
5+
| --- associated function `foo` has no `self` parameter
6+
...
7+
LL | fn car() -> dyn NotObjectSafe {
8+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `NotObjectSafe` cannot be made into an object
9+
10+
error[E0038]: the trait `NotObjectSafe` cannot be made into an object
11+
--> $DIR/object-unsafe-trait-in-return-position-dyn-trait.rs:28:1
12+
|
13+
LL | fn foo() -> Self;
14+
| --- associated function `foo` has no `self` parameter
15+
...
16+
LL | fn cat() -> Box<dyn NotObjectSafe> {
17+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `NotObjectSafe` cannot be made into an object
18+
19+
error: aborting due to 2 previous errors
20+
21+
For more information about this error, try `rustc --explain E0038`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
trait NotObjectSafe {
2+
fn foo() -> Self;
3+
}
4+
5+
trait ObjectSafe {
6+
fn bar(&self);
7+
}
8+
9+
struct A;
10+
struct B;
11+
12+
impl NotObjectSafe for A {
13+
fn foo() -> Self {
14+
A
15+
}
16+
}
17+
18+
impl NotObjectSafe for B {
19+
fn foo() -> Self {
20+
B
21+
}
22+
}
23+
24+
impl ObjectSafe for A {
25+
fn bar(&self) {}
26+
}
27+
28+
impl ObjectSafe for B {
29+
fn bar(&self) {}
30+
}
31+
32+
fn can() -> impl NotObjectSafe {
33+
if true {
34+
return A;
35+
}
36+
B //~ ERROR mismatched types
37+
}
38+
39+
fn cat() -> impl ObjectSafe {
40+
if true {
41+
return A;
42+
}
43+
B //~ ERROR mismatched types
44+
}
45+
46+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
error[E0308]: mismatched types
2+
--> $DIR/object-unsafe-trait-in-return-position-impl-trait.rs:36:5
3+
|
4+
LL | fn can() -> impl NotObjectSafe {
5+
| ------------------ expected because this return type...
6+
LL | if true {
7+
LL | return A;
8+
| - ...is found to be `A` here
9+
LL | }
10+
LL | B
11+
| ^ expected struct `A`, found struct `B`
12+
|
13+
= note: to return `impl Trait`, all returned values must be of the same type
14+
= note: for information on `impl Trait`, see <https://doc.rust-lang.org/book/ch10-02-traits.html#returning-types-that-implement-traits>
15+
= help: if the trait `NotObjectSafe` were object safe, you could return a boxed trait object
16+
= note: for information on trait objects, see <https://doc.rust-lang.org/book/ch17-02-trait-objects.html#using-trait-objects-that-allow-for-values-of-different-types>
17+
= help: alternatively, create a new `enum` with a variant for each returned type
18+
19+
error[E0308]: mismatched types
20+
--> $DIR/object-unsafe-trait-in-return-position-impl-trait.rs:43:5
21+
|
22+
LL | fn cat() -> impl ObjectSafe {
23+
| --------------- expected because this return type...
24+
LL | if true {
25+
LL | return A;
26+
| - ...is found to be `A` here
27+
LL | }
28+
LL | B
29+
| ^ expected struct `A`, found struct `B`
30+
|
31+
= note: to return `impl Trait`, all returned values must be of the same type
32+
= note: for information on `impl Trait`, see <https://doc.rust-lang.org/book/ch10-02-traits.html#returning-types-that-implement-traits>
33+
= help: you can instead return a boxed trait object using `Box<dyn ObjectSafe>`
34+
= note: for information on trait objects, see <https://doc.rust-lang.org/book/ch17-02-trait-objects.html#using-trait-objects-that-allow-for-values-of-different-types>
35+
= help: alternatively, create a new `enum` with a variant for each returned type
36+
37+
error: aborting due to 2 previous errors
38+
39+
For more information about this error, try `rustc --explain E0308`.

src/test/ui/point-to-type-err-cause-on-impl-trait-return.stderr

+6-6
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ LL | 1u32
1212
|
1313
= note: to return `impl Trait`, all returned values must be of the same type
1414
= note: for information on `impl Trait`, see <https://doc.rust-lang.org/book/ch10-02-traits.html#returning-types-that-implement-traits>
15-
= help: you can instead return a trait object using `Box<dyn std::fmt::Display>`
15+
= help: you can instead return a boxed trait object using `Box<dyn std::fmt::Display>`
1616
= note: for information on trait objects, see <https://doc.rust-lang.org/book/ch17-02-trait-objects.html#using-trait-objects-that-allow-for-values-of-different-types>
1717
= help: alternatively, create a new `enum` with a variant for each returned type
1818

@@ -30,7 +30,7 @@ LL | return 1u32;
3030
|
3131
= note: to return `impl Trait`, all returned values must be of the same type
3232
= note: for information on `impl Trait`, see <https://doc.rust-lang.org/book/ch10-02-traits.html#returning-types-that-implement-traits>
33-
= help: you can instead return a trait object using `Box<dyn std::fmt::Display>`
33+
= help: you can instead return a boxed trait object using `Box<dyn std::fmt::Display>`
3434
= note: for information on trait objects, see <https://doc.rust-lang.org/book/ch17-02-trait-objects.html#using-trait-objects-that-allow-for-values-of-different-types>
3535
= help: alternatively, create a new `enum` with a variant for each returned type
3636

@@ -48,7 +48,7 @@ LL | 1u32
4848
|
4949
= note: to return `impl Trait`, all returned values must be of the same type
5050
= note: for information on `impl Trait`, see <https://doc.rust-lang.org/book/ch10-02-traits.html#returning-types-that-implement-traits>
51-
= help: you can instead return a trait object using `Box<dyn std::fmt::Display>`
51+
= help: you can instead return a boxed trait object using `Box<dyn std::fmt::Display>`
5252
= note: for information on trait objects, see <https://doc.rust-lang.org/book/ch17-02-trait-objects.html#using-trait-objects-that-allow-for-values-of-different-types>
5353
= help: alternatively, create a new `enum` with a variant for each returned type
5454

@@ -78,7 +78,7 @@ LL | _ => 1u32,
7878
|
7979
= note: to return `impl Trait`, all returned values must be of the same type
8080
= note: for information on `impl Trait`, see <https://doc.rust-lang.org/book/ch10-02-traits.html#returning-types-that-implement-traits>
81-
= help: you can instead return a trait object using `Box<dyn std::fmt::Display>`
81+
= help: you can instead return a boxed trait object using `Box<dyn std::fmt::Display>`
8282
= note: for information on trait objects, see <https://doc.rust-lang.org/book/ch17-02-trait-objects.html#using-trait-objects-that-allow-for-values-of-different-types>
8383
= help: alternatively, create a new `enum` with a variant for each returned type
8484

@@ -98,7 +98,7 @@ LL | | }
9898
|
9999
= note: to return `impl Trait`, all returned values must be of the same type
100100
= note: for information on `impl Trait`, see <https://doc.rust-lang.org/book/ch10-02-traits.html#returning-types-that-implement-traits>
101-
= help: you can instead return a trait object using `Box<dyn std::fmt::Display>`
101+
= help: you can instead return a boxed trait object using `Box<dyn std::fmt::Display>`
102102
= note: for information on trait objects, see <https://doc.rust-lang.org/book/ch17-02-trait-objects.html#using-trait-objects-that-allow-for-values-of-different-types>
103103
= help: alternatively, create a new `enum` with a variant for each returned type
104104

@@ -116,7 +116,7 @@ LL | 1u32
116116
|
117117
= note: to return `impl Trait`, all returned values must be of the same type
118118
= note: for information on `impl Trait`, see <https://doc.rust-lang.org/book/ch10-02-traits.html#returning-types-that-implement-traits>
119-
= help: you can instead return a trait object using `Box<dyn std::fmt::Display>`
119+
= help: you can instead return a boxed trait object using `Box<dyn std::fmt::Display>`
120120
= note: for information on trait objects, see <https://doc.rust-lang.org/book/ch17-02-trait-objects.html#using-trait-objects-that-allow-for-values-of-different-types>
121121
= help: alternatively, create a new `enum` with a variant for each returned type
122122

0 commit comments

Comments
 (0)