Skip to content

Commit

Permalink
Account for object unsafe traits
Browse files Browse the repository at this point in the history
Fix #119525.
  • Loading branch information
estebank committed Jan 3, 2024
1 parent 771966b commit 78ef946
Show file tree
Hide file tree
Showing 4 changed files with 98 additions and 25 deletions.
66 changes: 41 additions & 25 deletions compiler/rustc_hir_analysis/src/astconv/lint.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use rustc_ast::TraitObjectSyntax;
use rustc_errors::{Diagnostic, StashKey};
use rustc_hir as hir;
use rustc_hir::def::{DefKind, Res};
use rustc_lint_defs::{builtin::BARE_TRAIT_OBJECTS, Applicability};
use rustc_span::Span;
use rustc_trait_selection::traits::error_reporting::suggestions::NextTypeParamName;
Expand Down Expand Up @@ -90,15 +91,29 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
return false;
};
let impl_sugg = vec![(self_ty.span.shrink_to_lo(), "impl ".to_string())];
let is_object_safe = match self_ty.kind {
hir::TyKind::TraitObject(objects, ..) => {
objects.iter().all(|o| match o.trait_ref.path.res {
Res::Def(DefKind::Trait, id) => tcx.check_is_object_safe(id),
_ => false,
})
}
_ => false,
};
if let hir::FnRetTy::Return(ty) = sig.decl.output
&& ty.hir_id == self_ty.hir_id
{
diag.multipart_suggestion_verbose(
format!("use `impl {trait_name}` to return an opaque type, as long as you return a single underlying type"),
impl_sugg,
Applicability::MachineApplicable,
let pre = if !is_object_safe {
format!("`{trait_name}` is not object safe, ")
} else {
String::new()
};
let msg = format!(
"{pre}use `impl {trait_name}` to return an opaque type, as long as you return a \
single underlying type",
);
if tcx.check_is_object_safe(def_id) {
diag.multipart_suggestion_verbose(msg, impl_sugg, Applicability::MachineApplicable);
if is_object_safe {
diag.multipart_suggestion_verbose(
"alternatively, you can return an owned trait object",
vec![
Expand All @@ -111,25 +126,26 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
return true;
}
for ty in sig.decl.inputs {
if ty.hir_id == self_ty.hir_id {
let sugg = self.add_generic_param_suggestion(generics, self_ty.span, &trait_name);
if !sugg.is_empty() {
diag.multipart_suggestion_verbose(
format!("use a new generic type parameter, constrained by `{trait_name}`"),
sugg,
Applicability::MachineApplicable,
);
diag.multipart_suggestion_verbose(
"you can also use an opaque type, but users won't be able to specify the \
type parameter when calling the `fn`, having to rely exclusively on type \
inference",
impl_sugg,
Applicability::MachineApplicable,
);
}
if !tcx.check_is_object_safe(def_id) {
diag.note(format!("it is not object safe, so it can't be `dyn`"));
}
if ty.hir_id != self_ty.hir_id {
continue;
}
let sugg = self.add_generic_param_suggestion(generics, self_ty.span, &trait_name);
if !sugg.is_empty() {
diag.multipart_suggestion_verbose(
format!("use a new generic type parameter, constrained by `{trait_name}`"),
sugg,
Applicability::MachineApplicable,
);
diag.multipart_suggestion_verbose(
"you can also use an opaque type, but users won't be able to specify the type \
parameter when calling the `fn`, having to rely exclusively on type inference",
impl_sugg,
Applicability::MachineApplicable,
);
}
if !is_object_safe {
diag.note(format!("`{trait_name}` it is not object safe, so it can't be `dyn`"));
} else {
let sugg = if let hir::TyKind::TraitObject([_, _, ..], _, _) = self_ty.kind {
// There are more than one trait bound, we need surrounding parentheses.
vec![
Expand All @@ -147,8 +163,8 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
sugg,
Applicability::MachineApplicable,
);
return true;
}
return true;
}
false
}
Expand Down
11 changes: 11 additions & 0 deletions tests/ui/object-safety/bare-trait-dont-suggest-dyn.fixed
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// run-rustfix
#![deny(bare_trait_objects)]
fn ord_prefer_dot(s: String) -> impl Ord {
//~^ ERROR trait objects without an explicit `dyn` are deprecated
//~| ERROR the trait `Ord` cannot be made into an object
//~| WARNING this is accepted in the current edition (Rust 2015)
(s.starts_with("."), s)
}
fn main() {
let _ = ord_prefer_dot(String::new());
}
11 changes: 11 additions & 0 deletions tests/ui/object-safety/bare-trait-dont-suggest-dyn.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// run-rustfix
#![deny(bare_trait_objects)]
fn ord_prefer_dot(s: String) -> Ord {
//~^ ERROR trait objects without an explicit `dyn` are deprecated
//~| ERROR the trait `Ord` cannot be made into an object
//~| WARNING this is accepted in the current edition (Rust 2015)
(s.starts_with("."), s)
}
fn main() {
let _ = ord_prefer_dot(String::new());
}
35 changes: 35 additions & 0 deletions tests/ui/object-safety/bare-trait-dont-suggest-dyn.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
error: trait objects without an explicit `dyn` are deprecated
--> $DIR/bare-trait-dont-suggest-dyn.rs:3:33
|
LL | fn ord_prefer_dot(s: String) -> Ord {
| ^^^
|
= warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2021!
= note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/warnings-promoted-to-error.html>
note: the lint level is defined here
--> $DIR/bare-trait-dont-suggest-dyn.rs:2:9
|
LL | #![deny(bare_trait_objects)]
| ^^^^^^^^^^^^^^^^^^
help: `Ord` is not object safe, use `impl Ord` to return an opaque type, as long as you return a single underlying type
|
LL | fn ord_prefer_dot(s: String) -> impl Ord {
| ++++

error[E0038]: the trait `Ord` cannot be made into an object
--> $DIR/bare-trait-dont-suggest-dyn.rs:3:33
|
LL | fn ord_prefer_dot(s: String) -> Ord {
| ^^^ `Ord` cannot be made into an object
|
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>
--> $SRC_DIR/core/src/cmp.rs:LL:COL
|
= note: the trait cannot be made into an object because it uses `Self` as a type parameter
::: $SRC_DIR/core/src/cmp.rs:LL:COL
|
= note: the trait cannot be made into an object because it uses `Self` as a type parameter

error: aborting due to 2 previous errors

For more information about this error, try `rustc --explain E0038`.

0 comments on commit 78ef946

Please sign in to comment.