Rollup merge of #104593 - compiler-errors:rpitit-object-safety-spans, r=fee1-dead
Improve spans for RPITIT object-safety errors No reason why we can't point at the `impl Trait` that causes the object-safety violation. Also [drive-by: Add is_async fn to hir::IsAsync](https://github.com/rust-lang/rust/pull/104593/commits/c4165f3a965e258531928180195637455299c6f3), which touches clippy too.
This commit is contained in:
commit
c571b2a964
11 changed files with 86 additions and 27 deletions
|
@ -62,7 +62,7 @@ impl<'mir, 'tcx> ConstCx<'mir, 'tcx> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_async(&self) -> bool {
|
fn is_async(&self) -> bool {
|
||||||
self.tcx.asyncness(self.def_id()) == hir::IsAsync::Async
|
self.tcx.asyncness(self.def_id()).is_async()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2720,6 +2720,12 @@ pub enum IsAsync {
|
||||||
NotAsync,
|
NotAsync,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl IsAsync {
|
||||||
|
pub fn is_async(self) -> bool {
|
||||||
|
self == IsAsync::Async
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Encodable, Decodable, HashStable_Generic)]
|
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Encodable, Decodable, HashStable_Generic)]
|
||||||
pub enum Defaultness {
|
pub enum Defaultness {
|
||||||
Default { has_value: bool },
|
Default { has_value: bool },
|
||||||
|
|
|
@ -684,9 +684,7 @@ fn report_trait_method_mismatch<'tcx>(
|
||||||
// Suggestion to change output type. We do not suggest in `async` functions
|
// Suggestion to change output type. We do not suggest in `async` functions
|
||||||
// to avoid complex logic or incorrect output.
|
// to avoid complex logic or incorrect output.
|
||||||
match tcx.hir().expect_impl_item(impl_m.def_id.expect_local()).kind {
|
match tcx.hir().expect_impl_item(impl_m.def_id.expect_local()).kind {
|
||||||
ImplItemKind::Fn(ref sig, _)
|
ImplItemKind::Fn(ref sig, _) if !sig.header.asyncness.is_async() => {
|
||||||
if sig.header.asyncness == hir::IsAsync::NotAsync =>
|
|
||||||
{
|
|
||||||
let msg = "change the output type to match the trait";
|
let msg = "change the output type to match the trait";
|
||||||
let ap = Applicability::MachineApplicable;
|
let ap = Applicability::MachineApplicable;
|
||||||
match sig.decl.output {
|
match sig.decl.output {
|
||||||
|
|
|
@ -924,10 +924,13 @@ impl ObjectSafetyViolation {
|
||||||
}
|
}
|
||||||
ObjectSafetyViolation::Method(
|
ObjectSafetyViolation::Method(
|
||||||
name,
|
name,
|
||||||
MethodViolationCode::ReferencesImplTraitInTrait,
|
MethodViolationCode::ReferencesImplTraitInTrait(_),
|
||||||
_,
|
_,
|
||||||
) => format!("method `{}` references an `impl Trait` type in its return type", name)
|
) => format!("method `{}` references an `impl Trait` type in its return type", name)
|
||||||
.into(),
|
.into(),
|
||||||
|
ObjectSafetyViolation::Method(name, MethodViolationCode::AsyncFn, _) => {
|
||||||
|
format!("method `{}` is `async`", name).into()
|
||||||
|
}
|
||||||
ObjectSafetyViolation::Method(
|
ObjectSafetyViolation::Method(
|
||||||
name,
|
name,
|
||||||
MethodViolationCode::WhereClauseReferencesSelf,
|
MethodViolationCode::WhereClauseReferencesSelf,
|
||||||
|
@ -1035,7 +1038,10 @@ pub enum MethodViolationCode {
|
||||||
ReferencesSelfOutput,
|
ReferencesSelfOutput,
|
||||||
|
|
||||||
/// e.g., `fn foo(&self) -> impl Sized`
|
/// e.g., `fn foo(&self) -> impl Sized`
|
||||||
ReferencesImplTraitInTrait,
|
ReferencesImplTraitInTrait(Span),
|
||||||
|
|
||||||
|
/// e.g., `async fn foo(&self)`
|
||||||
|
AsyncFn,
|
||||||
|
|
||||||
/// e.g., `fn foo(&self) where Self: Clone`
|
/// e.g., `fn foo(&self) where Self: Clone`
|
||||||
WhereClauseReferencesSelf,
|
WhereClauseReferencesSelf,
|
||||||
|
|
|
@ -375,6 +375,7 @@ fn object_safety_violation_for_method(
|
||||||
let span = match (&v, node) {
|
let span = match (&v, node) {
|
||||||
(MethodViolationCode::ReferencesSelfInput(Some(span)), _) => *span,
|
(MethodViolationCode::ReferencesSelfInput(Some(span)), _) => *span,
|
||||||
(MethodViolationCode::UndispatchableReceiver(Some(span)), _) => *span,
|
(MethodViolationCode::UndispatchableReceiver(Some(span)), _) => *span,
|
||||||
|
(MethodViolationCode::ReferencesImplTraitInTrait(span), _) => *span,
|
||||||
(MethodViolationCode::ReferencesSelfOutput, Some(node)) => {
|
(MethodViolationCode::ReferencesSelfOutput, Some(node)) => {
|
||||||
node.fn_decl().map_or(method.ident(tcx).span, |decl| decl.output.span())
|
node.fn_decl().map_or(method.ident(tcx).span, |decl| decl.output.span())
|
||||||
}
|
}
|
||||||
|
@ -437,8 +438,8 @@ fn virtual_call_violation_for_method<'tcx>(
|
||||||
if contains_illegal_self_type_reference(tcx, trait_def_id, sig.output()) {
|
if contains_illegal_self_type_reference(tcx, trait_def_id, sig.output()) {
|
||||||
return Some(MethodViolationCode::ReferencesSelfOutput);
|
return Some(MethodViolationCode::ReferencesSelfOutput);
|
||||||
}
|
}
|
||||||
if contains_illegal_impl_trait_in_trait(tcx, sig.output()) {
|
if let Some(code) = contains_illegal_impl_trait_in_trait(tcx, method.def_id, sig.output()) {
|
||||||
return Some(MethodViolationCode::ReferencesImplTraitInTrait);
|
return Some(code);
|
||||||
}
|
}
|
||||||
|
|
||||||
// We can't monomorphize things like `fn foo<A>(...)`.
|
// We can't monomorphize things like `fn foo<A>(...)`.
|
||||||
|
@ -864,16 +865,24 @@ fn contains_illegal_self_type_reference<'tcx, T: TypeVisitable<'tcx>>(
|
||||||
|
|
||||||
pub fn contains_illegal_impl_trait_in_trait<'tcx>(
|
pub fn contains_illegal_impl_trait_in_trait<'tcx>(
|
||||||
tcx: TyCtxt<'tcx>,
|
tcx: TyCtxt<'tcx>,
|
||||||
|
fn_def_id: DefId,
|
||||||
ty: ty::Binder<'tcx, Ty<'tcx>>,
|
ty: ty::Binder<'tcx, Ty<'tcx>>,
|
||||||
) -> bool {
|
) -> Option<MethodViolationCode> {
|
||||||
|
// This would be caught below, but rendering the error as a separate
|
||||||
|
// `async-specific` message is better.
|
||||||
|
if tcx.asyncness(fn_def_id).is_async() {
|
||||||
|
return Some(MethodViolationCode::AsyncFn);
|
||||||
|
}
|
||||||
|
|
||||||
// FIXME(RPITIT): Perhaps we should use a visitor here?
|
// FIXME(RPITIT): Perhaps we should use a visitor here?
|
||||||
ty.skip_binder().walk().any(|arg| {
|
ty.skip_binder().walk().find_map(|arg| {
|
||||||
if let ty::GenericArgKind::Type(ty) = arg.unpack()
|
if let ty::GenericArgKind::Type(ty) = arg.unpack()
|
||||||
&& let ty::Projection(proj) = ty.kind()
|
&& let ty::Projection(proj) = ty.kind()
|
||||||
|
&& tcx.def_kind(proj.item_def_id) == DefKind::ImplTraitPlaceholder
|
||||||
{
|
{
|
||||||
tcx.def_kind(proj.item_def_id) == DefKind::ImplTraitPlaceholder
|
Some(MethodViolationCode::ReferencesImplTraitInTrait(tcx.def_span(proj.item_def_id)))
|
||||||
} else {
|
} else {
|
||||||
false
|
None
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
13
src/test/ui/async-await/in-trait/object-safety.rs
Normal file
13
src/test/ui/async-await/in-trait/object-safety.rs
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
// edition:2021
|
||||||
|
|
||||||
|
#![feature(async_fn_in_trait)]
|
||||||
|
//~^ WARN the feature `async_fn_in_trait` is incomplete and may not be safe to use and/or cause compiler crashes
|
||||||
|
|
||||||
|
trait Foo {
|
||||||
|
async fn foo(&self);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let x: &dyn Foo = todo!();
|
||||||
|
//~^ ERROR the trait `Foo` cannot be made into an object
|
||||||
|
}
|
27
src/test/ui/async-await/in-trait/object-safety.stderr
Normal file
27
src/test/ui/async-await/in-trait/object-safety.stderr
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
warning: the feature `async_fn_in_trait` is incomplete and may not be safe to use and/or cause compiler crashes
|
||||||
|
--> $DIR/object-safety.rs:3:12
|
||||||
|
|
|
||||||
|
LL | #![feature(async_fn_in_trait)]
|
||||||
|
| ^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: see issue #91611 <https://github.com/rust-lang/rust/issues/91611> for more information
|
||||||
|
= note: `#[warn(incomplete_features)]` on by default
|
||||||
|
|
||||||
|
error[E0038]: the trait `Foo` cannot be made into an object
|
||||||
|
--> $DIR/object-safety.rs:11:12
|
||||||
|
|
|
||||||
|
LL | let x: &dyn Foo = todo!();
|
||||||
|
| ^^^^^^^^ `Foo` 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>
|
||||||
|
--> $DIR/object-safety.rs:7:14
|
||||||
|
|
|
||||||
|
LL | trait Foo {
|
||||||
|
| --- this trait cannot be made into an object...
|
||||||
|
LL | async fn foo(&self);
|
||||||
|
| ^^^ ...because method `foo` is `async`
|
||||||
|
= help: consider moving `foo` to another trait
|
||||||
|
|
||||||
|
error: aborting due to previous error; 1 warning emitted
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0038`.
|
|
@ -5,12 +5,12 @@ LL | let i = Box::new(42_u32) as Box<dyn Foo>;
|
||||||
| ^^^^^^^^^^^^ `Foo` cannot be made into an object
|
| ^^^^^^^^^^^^ `Foo` 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>
|
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>
|
||||||
--> $DIR/object-safety.rs:7:8
|
--> $DIR/object-safety.rs:7:22
|
||||||
|
|
|
|
||||||
LL | trait Foo {
|
LL | trait Foo {
|
||||||
| --- this trait cannot be made into an object...
|
| --- this trait cannot be made into an object...
|
||||||
LL | fn baz(&self) -> impl Debug;
|
LL | fn baz(&self) -> impl Debug;
|
||||||
| ^^^ ...because method `baz` references an `impl Trait` type in its return type
|
| ^^^^^^^^^^ ...because method `baz` references an `impl Trait` type in its return type
|
||||||
= help: consider moving `baz` to another trait
|
= help: consider moving `baz` to another trait
|
||||||
|
|
||||||
error[E0038]: the trait `Foo` cannot be made into an object
|
error[E0038]: the trait `Foo` cannot be made into an object
|
||||||
|
@ -20,12 +20,12 @@ LL | let s = i.baz();
|
||||||
| ^^^^^^^ `Foo` cannot be made into an object
|
| ^^^^^^^ `Foo` 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>
|
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>
|
||||||
--> $DIR/object-safety.rs:7:8
|
--> $DIR/object-safety.rs:7:22
|
||||||
|
|
|
|
||||||
LL | trait Foo {
|
LL | trait Foo {
|
||||||
| --- this trait cannot be made into an object...
|
| --- this trait cannot be made into an object...
|
||||||
LL | fn baz(&self) -> impl Debug;
|
LL | fn baz(&self) -> impl Debug;
|
||||||
| ^^^ ...because method `baz` references an `impl Trait` type in its return type
|
| ^^^^^^^^^^ ...because method `baz` references an `impl Trait` type in its return type
|
||||||
= help: consider moving `baz` to another trait
|
= help: consider moving `baz` to another trait
|
||||||
|
|
||||||
error[E0038]: the trait `Foo` cannot be made into an object
|
error[E0038]: the trait `Foo` cannot be made into an object
|
||||||
|
@ -35,12 +35,12 @@ LL | let i = Box::new(42_u32) as Box<dyn Foo>;
|
||||||
| ^^^^^^^^^^^^^^^^ `Foo` cannot be made into an object
|
| ^^^^^^^^^^^^^^^^ `Foo` 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>
|
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>
|
||||||
--> $DIR/object-safety.rs:7:8
|
--> $DIR/object-safety.rs:7:22
|
||||||
|
|
|
|
||||||
LL | trait Foo {
|
LL | trait Foo {
|
||||||
| --- this trait cannot be made into an object...
|
| --- this trait cannot be made into an object...
|
||||||
LL | fn baz(&self) -> impl Debug;
|
LL | fn baz(&self) -> impl Debug;
|
||||||
| ^^^ ...because method `baz` references an `impl Trait` type in its return type
|
| ^^^^^^^^^^ ...because method `baz` references an `impl Trait` type in its return type
|
||||||
= help: consider moving `baz` to another trait
|
= help: consider moving `baz` to another trait
|
||||||
= note: required for `Box<u32>` to implement `CoerceUnsized<Box<dyn Foo>>`
|
= note: required for `Box<u32>` to implement `CoerceUnsized<Box<dyn Foo>>`
|
||||||
= note: required by cast to type `Box<dyn Foo>`
|
= note: required by cast to type `Box<dyn Foo>`
|
||||||
|
|
|
@ -6,7 +6,7 @@ use rustc_errors::Applicability;
|
||||||
use rustc_hir::intravisit::FnKind;
|
use rustc_hir::intravisit::FnKind;
|
||||||
use rustc_hir::{
|
use rustc_hir::{
|
||||||
AsyncGeneratorKind, Block, Body, Closure, Expr, ExprKind, FnDecl, FnRetTy, GeneratorKind, GenericArg, GenericBound,
|
AsyncGeneratorKind, Block, Body, Closure, Expr, ExprKind, FnDecl, FnRetTy, GeneratorKind, GenericArg, GenericBound,
|
||||||
HirId, IsAsync, ItemKind, LifetimeName, Term, TraitRef, Ty, TyKind, TypeBindingKind,
|
HirId, ItemKind, LifetimeName, Term, TraitRef, Ty, TyKind, TypeBindingKind,
|
||||||
};
|
};
|
||||||
use rustc_lint::{LateContext, LateLintPass};
|
use rustc_lint::{LateContext, LateLintPass};
|
||||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||||
|
@ -49,7 +49,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualAsyncFn {
|
||||||
) {
|
) {
|
||||||
if_chain! {
|
if_chain! {
|
||||||
if let Some(header) = kind.header();
|
if let Some(header) = kind.header();
|
||||||
if header.asyncness == IsAsync::NotAsync;
|
if !header.asyncness.is_async();
|
||||||
// Check that this function returns `impl Future`
|
// Check that this function returns `impl Future`
|
||||||
if let FnRetTy::Return(ret_ty) = decl.output;
|
if let FnRetTy::Return(ret_ty) = decl.output;
|
||||||
if let Some((trait_ref, output_lifetimes)) = future_trait_ref(cx, ret_ty);
|
if let Some((trait_ref, output_lifetimes)) = future_trait_ref(cx, ret_ty);
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use clippy_utils::diagnostics::span_lint_and_help;
|
use clippy_utils::diagnostics::span_lint_and_help;
|
||||||
use rustc_hir::intravisit::{walk_expr, walk_fn, FnKind, Visitor};
|
use rustc_hir::intravisit::{walk_expr, walk_fn, FnKind, Visitor};
|
||||||
use rustc_hir::{Body, Expr, ExprKind, FnDecl, HirId, IsAsync, YieldSource};
|
use rustc_hir::{Body, Expr, ExprKind, FnDecl, HirId, YieldSource};
|
||||||
use rustc_lint::{LateContext, LateLintPass};
|
use rustc_lint::{LateContext, LateLintPass};
|
||||||
use rustc_middle::hir::nested_filter;
|
use rustc_middle::hir::nested_filter;
|
||||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||||
|
@ -68,7 +68,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedAsync {
|
||||||
span: Span,
|
span: Span,
|
||||||
hir_id: HirId,
|
hir_id: HirId,
|
||||||
) {
|
) {
|
||||||
if !span.from_expansion() && fn_kind.asyncness() == IsAsync::Async {
|
if !span.from_expansion() && fn_kind.asyncness().is_async() {
|
||||||
let mut visitor = AsyncFnVisitor { cx, found_await: false };
|
let mut visitor = AsyncFnVisitor { cx, found_await: false };
|
||||||
walk_fn(&mut visitor, fn_kind, fn_decl, body.id(), hir_id);
|
walk_fn(&mut visitor, fn_kind, fn_decl, body.id(), hir_id);
|
||||||
if !visitor.found_await {
|
if !visitor.found_await {
|
||||||
|
|
|
@ -87,10 +87,10 @@ use rustc_hir::hir_id::{HirIdMap, HirIdSet};
|
||||||
use rustc_hir::intravisit::{walk_expr, FnKind, Visitor};
|
use rustc_hir::intravisit::{walk_expr, FnKind, Visitor};
|
||||||
use rustc_hir::LangItem::{OptionNone, ResultErr, ResultOk};
|
use rustc_hir::LangItem::{OptionNone, ResultErr, ResultOk};
|
||||||
use rustc_hir::{
|
use rustc_hir::{
|
||||||
def, Arm, ArrayLen, BindingAnnotation, Block, BlockCheckMode, Body, Closure, Constness, Destination, Expr,
|
def, Arm, ArrayLen, BindingAnnotation, Block, BlockCheckMode, Body, Closure, Constness,
|
||||||
ExprKind, FnDecl, HirId, Impl, ImplItem, ImplItemKind, IsAsync, Item, ItemKind, LangItem, Local, MatchSource,
|
Destination, Expr, ExprKind, FnDecl, HirId, Impl, ImplItem, ImplItemKind, Item, ItemKind,
|
||||||
Mutability, Node, Param, Pat, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, TraitItem, TraitItemKind,
|
LangItem, Local, MatchSource, Mutability, Node, Param, Pat, PatKind, Path, PathSegment, PrimTy,
|
||||||
TraitRef, TyKind, UnOp,
|
QPath, Stmt, StmtKind, TraitItem, TraitItemKind, TraitRef, TyKind, UnOp,
|
||||||
};
|
};
|
||||||
use rustc_lexer::{tokenize, TokenKind};
|
use rustc_lexer::{tokenize, TokenKind};
|
||||||
use rustc_lint::{LateContext, Level, Lint, LintContext};
|
use rustc_lint::{LateContext, Level, Lint, LintContext};
|
||||||
|
@ -1861,7 +1861,7 @@ pub fn if_sequence<'tcx>(mut expr: &'tcx Expr<'tcx>) -> (Vec<&'tcx Expr<'tcx>>,
|
||||||
|
|
||||||
/// Checks if the given function kind is an async function.
|
/// Checks if the given function kind is an async function.
|
||||||
pub fn is_async_fn(kind: FnKind<'_>) -> bool {
|
pub fn is_async_fn(kind: FnKind<'_>) -> bool {
|
||||||
matches!(kind, FnKind::ItemFn(_, _, header) if header.asyncness == IsAsync::Async)
|
matches!(kind, FnKind::ItemFn(_, _, header) if header.asyncness.is_async())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Peels away all the compiler generated code surrounding the body of an async function,
|
/// Peels away all the compiler generated code surrounding the body of an async function,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue