Rollup merge of #69048 - estebank:hrlt-assoc, r=nagisa
Suggestion when encountering assoc types from hrtb When encountering E0212, detect whether this is a representable case or not, i.e. if it's happening on an `fn` or on an ADT. If the former, provide a structured suggestion, otherwise note that this can't be represented in Rust. Fix #69000.
This commit is contained in:
commit
8d00adf289
9 changed files with 226 additions and 20 deletions
|
|
@ -278,6 +278,17 @@ impl ItemCtxt<'tcx> {
|
|||
pub fn to_ty(&self, ast_ty: &'tcx hir::Ty<'tcx>) -> Ty<'tcx> {
|
||||
AstConv::ast_ty_to_ty(self, ast_ty)
|
||||
}
|
||||
|
||||
pub fn hir_id(&self) -> hir::HirId {
|
||||
self.tcx
|
||||
.hir()
|
||||
.as_local_hir_id(self.item_def_id)
|
||||
.expect("Non-local call to local provider is_const_fn")
|
||||
}
|
||||
|
||||
pub fn node(&self) -> hir::Node<'tcx> {
|
||||
self.tcx.hir().get(self.hir_id())
|
||||
}
|
||||
}
|
||||
|
||||
impl AstConv<'tcx> for ItemCtxt<'tcx> {
|
||||
|
|
@ -290,15 +301,7 @@ impl AstConv<'tcx> for ItemCtxt<'tcx> {
|
|||
}
|
||||
|
||||
fn default_constness_for_trait_bounds(&self) -> ast::Constness {
|
||||
// FIXME: refactor this into a method
|
||||
let hir_id = self
|
||||
.tcx
|
||||
.hir()
|
||||
.as_local_hir_id(self.item_def_id)
|
||||
.expect("Non-local call to local provider is_const_fn");
|
||||
|
||||
let node = self.tcx.hir().get(hir_id);
|
||||
if let Some(fn_like) = FnLikeNode::from_node(node) {
|
||||
if let Some(fn_like) = FnLikeNode::from_node(self.node()) {
|
||||
fn_like.constness()
|
||||
} else {
|
||||
ast::Constness::NotConst
|
||||
|
|
@ -352,14 +355,80 @@ impl AstConv<'tcx> for ItemCtxt<'tcx> {
|
|||
self.tcx().mk_projection(item_def_id, item_substs)
|
||||
} else {
|
||||
// There are no late-bound regions; we can just ignore the binder.
|
||||
struct_span_err!(
|
||||
let mut err = struct_span_err!(
|
||||
self.tcx().sess,
|
||||
span,
|
||||
E0212,
|
||||
"cannot extract an associated type from a higher-ranked trait bound \
|
||||
in this context"
|
||||
)
|
||||
.emit();
|
||||
);
|
||||
|
||||
match self.node() {
|
||||
hir::Node::Field(_) | hir::Node::Ctor(_) | hir::Node::Variant(_) => {
|
||||
let item =
|
||||
self.tcx.hir().expect_item(self.tcx.hir().get_parent_item(self.hir_id()));
|
||||
match &item.kind {
|
||||
hir::ItemKind::Enum(_, generics)
|
||||
| hir::ItemKind::Struct(_, generics)
|
||||
| hir::ItemKind::Union(_, generics) => {
|
||||
// FIXME: look for an appropriate lt name if `'a` is already used
|
||||
let (lt_sp, sugg) = match &generics.params[..] {
|
||||
[] => (generics.span, "<'a>".to_string()),
|
||||
[bound, ..] => (bound.span.shrink_to_lo(), "'a, ".to_string()),
|
||||
};
|
||||
let suggestions = vec![
|
||||
(lt_sp, sugg),
|
||||
(
|
||||
span,
|
||||
format!(
|
||||
"{}::{}",
|
||||
// Replace the existing lifetimes with a new named lifetime.
|
||||
self.tcx
|
||||
.replace_late_bound_regions(&poly_trait_ref, |_| {
|
||||
self.tcx.mk_region(ty::ReEarlyBound(
|
||||
ty::EarlyBoundRegion {
|
||||
def_id: item_def_id,
|
||||
index: 0,
|
||||
name: Symbol::intern("'a"),
|
||||
},
|
||||
))
|
||||
})
|
||||
.0,
|
||||
item_segment.ident
|
||||
),
|
||||
),
|
||||
];
|
||||
err.multipart_suggestion(
|
||||
"use a fully qualified path with explicit lifetimes",
|
||||
suggestions,
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
hir::Node::Item(hir::Item { kind: hir::ItemKind::Struct(..), .. })
|
||||
| hir::Node::Item(hir::Item { kind: hir::ItemKind::Enum(..), .. })
|
||||
| hir::Node::Item(hir::Item { kind: hir::ItemKind::Union(..), .. }) => {}
|
||||
hir::Node::Item(_)
|
||||
| hir::Node::ForeignItem(_)
|
||||
| hir::Node::TraitItem(_)
|
||||
| hir::Node::ImplItem(_) => {
|
||||
err.span_suggestion(
|
||||
span,
|
||||
"use a fully qualified path with inferred lifetimes",
|
||||
format!(
|
||||
"{}::{}",
|
||||
// Erase named lt, we want `<A as B<'_>::C`, not `<A as B<'a>::C`.
|
||||
self.tcx.anonymize_late_bound_regions(&poly_trait_ref).skip_binder(),
|
||||
item_segment.ident
|
||||
),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
err.emit();
|
||||
self.tcx().types.err
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,37 @@
|
|||
#![allow(dead_code, unused_variables)]
|
||||
// run-rustfix
|
||||
// Check projection of an associated type out of a higher-ranked trait-bound
|
||||
// in the context of a function signature.
|
||||
|
||||
pub trait Foo<T> {
|
||||
type A;
|
||||
|
||||
fn get(&self, t: T) -> Self::A;
|
||||
}
|
||||
|
||||
fn foo2<I : for<'x> Foo<&'x isize>>(
|
||||
x: <I as Foo<&isize>>::A)
|
||||
//~^ ERROR cannot extract an associated type from a higher-ranked trait bound in this context
|
||||
{
|
||||
// This case is illegal because we have to instantiate `'x`, and
|
||||
// we don't know what region to instantiate it with.
|
||||
//
|
||||
// This could perhaps be made equivalent to the examples below,
|
||||
// specifically for fn signatures.
|
||||
}
|
||||
|
||||
fn foo3<I : for<'x> Foo<&'x isize>>(
|
||||
x: <I as Foo<&isize>>::A)
|
||||
{
|
||||
// OK, in this case we spelled out the precise regions involved, though we left one of
|
||||
// them anonymous.
|
||||
}
|
||||
|
||||
fn foo4<'a, I : for<'x> Foo<&'x isize>>(
|
||||
x: <I as Foo<&'a isize>>::A)
|
||||
{
|
||||
// OK, in this case we spelled out the precise regions involved.
|
||||
}
|
||||
|
||||
|
||||
pub fn main() {}
|
||||
|
|
@ -1,3 +1,5 @@
|
|||
#![allow(dead_code, unused_variables)]
|
||||
// run-rustfix
|
||||
// Check projection of an associated type out of a higher-ranked trait-bound
|
||||
// in the context of a function signature.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
error[E0212]: cannot extract an associated type from a higher-ranked trait bound in this context
|
||||
--> $DIR/associated-types-project-from-hrtb-in-fn.rs:11:8
|
||||
--> $DIR/associated-types-project-from-hrtb-in-fn.rs:13:8
|
||||
|
|
||||
LL | x: I::A)
|
||||
| ^^^^
|
||||
| ^^^^ help: use a fully qualified path with inferred lifetimes: `<I as Foo<&isize>>::A`
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
|
|
|
|||
|
|
@ -7,18 +7,25 @@ pub trait Foo<T> {
|
|||
fn get(&self, t: T) -> Self::A;
|
||||
}
|
||||
|
||||
struct SomeStruct<I : for<'x> Foo<&'x isize>> {
|
||||
struct SomeStruct<I: for<'x> Foo<&'x isize>> {
|
||||
field: I::A
|
||||
//~^ ERROR cannot extract an associated type from a higher-ranked trait bound in this context
|
||||
}
|
||||
|
||||
enum SomeEnum<I: for<'x> Foo<&'x isize>> {
|
||||
TupleVariant(I::A),
|
||||
//~^ ERROR cannot extract an associated type from a higher-ranked trait bound in this context
|
||||
StructVariant { field: I::A },
|
||||
//~^ ERROR cannot extract an associated type from a higher-ranked trait bound in this context
|
||||
}
|
||||
|
||||
// FIXME(eddyb) This one doesn't even compile because of the unsupported syntax.
|
||||
|
||||
// struct AnotherStruct<I : for<'x> Foo<&'x isize>> {
|
||||
// field: <I as for<'y> Foo<&'y isize>>::A
|
||||
// }
|
||||
|
||||
struct YetAnotherStruct<'a, I : for<'x> Foo<&'x isize>> {
|
||||
struct YetAnotherStruct<'a, I: for<'x> Foo<&'x isize>> {
|
||||
field: <I as Foo<&'a isize>>::A
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,38 @@ error[E0212]: cannot extract an associated type from a higher-ranked trait bound
|
|||
|
|
||||
LL | field: I::A
|
||||
| ^^^^
|
||||
|
|
||||
help: use a fully qualified path with explicit lifetimes
|
||||
|
|
||||
LL | struct SomeStruct<'a, I: for<'x> Foo<&'x isize>> {
|
||||
LL | field: <I as Foo<&'a isize>>::A
|
||||
|
|
||||
|
||||
error: aborting due to previous error
|
||||
error[E0212]: cannot extract an associated type from a higher-ranked trait bound in this context
|
||||
--> $DIR/associated-types-project-from-hrtb-in-struct.rs:16:18
|
||||
|
|
||||
LL | TupleVariant(I::A),
|
||||
| ^^^^
|
||||
|
|
||||
help: use a fully qualified path with explicit lifetimes
|
||||
|
|
||||
LL | enum SomeEnum<'a, I: for<'x> Foo<&'x isize>> {
|
||||
LL | TupleVariant(<I as Foo<&'a isize>>::A),
|
||||
|
|
||||
|
||||
error[E0212]: cannot extract an associated type from a higher-ranked trait bound in this context
|
||||
--> $DIR/associated-types-project-from-hrtb-in-struct.rs:18:28
|
||||
|
|
||||
LL | StructVariant { field: I::A },
|
||||
| ^^^^
|
||||
|
|
||||
help: use a fully qualified path with explicit lifetimes
|
||||
|
|
||||
LL | enum SomeEnum<'a, I: for<'x> Foo<&'x isize>> {
|
||||
LL | TupleVariant(I::A),
|
||||
LL |
|
||||
LL | StructVariant { field: <I as Foo<&'a isize>>::A },
|
||||
|
|
||||
|
||||
error: aborting due to 3 previous errors
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,38 @@
|
|||
#![allow(dead_code)]
|
||||
// run-rustfix
|
||||
// Check projection of an associated type out of a higher-ranked trait-bound
|
||||
// in the context of a method definition in a trait.
|
||||
|
||||
pub trait Foo<T> {
|
||||
type A;
|
||||
|
||||
fn get(&self, t: T) -> Self::A;
|
||||
}
|
||||
|
||||
trait SomeTrait<I : for<'x> Foo<&'x isize>> {
|
||||
fn some_method(&self, arg: <I as Foo<&isize>>::A);
|
||||
//~^ ERROR cannot extract an associated type from a higher-ranked trait bound in this context
|
||||
}
|
||||
|
||||
trait AnotherTrait<I : for<'x> Foo<&'x isize>> {
|
||||
fn some_method(&self, arg: <I as Foo<&isize>>::A);
|
||||
}
|
||||
|
||||
trait YetAnotherTrait<I : for<'x> Foo<&'x isize>> {
|
||||
fn some_method<'a>(&self, arg: <I as Foo<&'a isize>>::A);
|
||||
}
|
||||
|
||||
trait Banana<'a> {
|
||||
type Assoc: Default;
|
||||
}
|
||||
|
||||
struct Peach<X>(std::marker::PhantomData<X>);
|
||||
|
||||
impl<X: for<'a> Banana<'a>> Peach<X> {
|
||||
fn mango(&self) -> <X as Banana<'_>>::Assoc {
|
||||
//~^ ERROR cannot extract an associated type from a higher-ranked trait bound in this context
|
||||
Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn main() {}
|
||||
|
|
@ -1,3 +1,5 @@
|
|||
#![allow(dead_code)]
|
||||
// run-rustfix
|
||||
// Check projection of an associated type out of a higher-ranked trait-bound
|
||||
// in the context of a method definition in a trait.
|
||||
|
||||
|
|
@ -20,4 +22,17 @@ trait YetAnotherTrait<I : for<'x> Foo<&'x isize>> {
|
|||
fn some_method<'a>(&self, arg: <I as Foo<&'a isize>>::A);
|
||||
}
|
||||
|
||||
trait Banana<'a> {
|
||||
type Assoc: Default;
|
||||
}
|
||||
|
||||
struct Peach<X>(std::marker::PhantomData<X>);
|
||||
|
||||
impl<X: for<'a> Banana<'a>> Peach<X> {
|
||||
fn mango(&self) -> X::Assoc {
|
||||
//~^ ERROR cannot extract an associated type from a higher-ranked trait bound in this context
|
||||
Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn main() {}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,14 @@
|
|||
error[E0212]: cannot extract an associated type from a higher-ranked trait bound in this context
|
||||
--> $DIR/associated-types-project-from-hrtb-in-trait-method.rs:11:32
|
||||
--> $DIR/associated-types-project-from-hrtb-in-trait-method.rs:13:32
|
||||
|
|
||||
LL | fn some_method(&self, arg: I::A);
|
||||
| ^^^^
|
||||
| ^^^^ help: use a fully qualified path with inferred lifetimes: `<I as Foo<&isize>>::A`
|
||||
|
||||
error: aborting due to previous error
|
||||
error[E0212]: cannot extract an associated type from a higher-ranked trait bound in this context
|
||||
--> $DIR/associated-types-project-from-hrtb-in-trait-method.rs:32:24
|
||||
|
|
||||
LL | fn mango(&self) -> X::Assoc {
|
||||
| ^^^^^^^^ help: use a fully qualified path with inferred lifetimes: `<X as Banana<'_>>::Assoc`
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue