Only walk the identity substituted version of struct fields
This commit is contained in:
parent
b323f587fc
commit
27b386ad17
@ -82,10 +82,12 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for OpaqueTypeCollector<'tcx> {
|
|||||||
// start seeing the error below.
|
// start seeing the error below.
|
||||||
|
|
||||||
// Collect opaque types nested within the associated type bounds of this opaque type.
|
// Collect opaque types nested within the associated type bounds of this opaque type.
|
||||||
|
// We use identity substs here, because we already know that the opaque type uses
|
||||||
|
// only generic parameters, and thus substituting would not give us more information.
|
||||||
for (pred, span) in self
|
for (pred, span) in self
|
||||||
.tcx
|
.tcx
|
||||||
.explicit_item_bounds(alias_ty.def_id)
|
.explicit_item_bounds(alias_ty.def_id)
|
||||||
.subst_iter_copied(self.tcx, alias_ty.substs)
|
.subst_identity_iter_copied()
|
||||||
{
|
{
|
||||||
trace!(?pred);
|
trace!(?pred);
|
||||||
self.visit_spanned(span, pred);
|
self.visit_spanned(span, pred);
|
||||||
@ -158,6 +160,25 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for OpaqueTypeCollector<'tcx> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ty::Adt(def, _) if def.did().is_local() => {
|
||||||
|
if !self.seen.insert(def.did().expect_local()) {
|
||||||
|
return ControlFlow::Continue(());
|
||||||
|
}
|
||||||
|
for variant in def.variants().iter() {
|
||||||
|
for field in variant.fields.iter() {
|
||||||
|
// Don't use the `ty::Adt` substs, we either
|
||||||
|
// * found the opaque in the substs
|
||||||
|
// * will find the opaque in the unsubstituted fields
|
||||||
|
// The only other situation that can occur is that after substituting,
|
||||||
|
// some projection resolves to an opaque that we would have otherwise
|
||||||
|
// not found. While we could substitute and walk those, that would mean we
|
||||||
|
// would have to walk all substitutions of an Adt, which can quickly
|
||||||
|
// degenerate into looking at an exponential number of types.
|
||||||
|
let ty = self.tcx.type_of(field.did).subst_identity();
|
||||||
|
self.visit_spanned(self.tcx.def_span(field.did), ty);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => trace!(kind=?t.kind()),
|
_ => trace!(kind=?t.kind()),
|
||||||
}
|
}
|
||||||
ControlFlow::Continue(())
|
ControlFlow::Continue(())
|
||||||
|
@ -0,0 +1,28 @@
|
|||||||
|
//! This test shows that a field type that is a projection that resolves to an opaque,
|
||||||
|
//! is not a defining use. While we could substitute the struct generics, that would
|
||||||
|
//! mean we would have to walk all substitutions of an `Foo`, which can quickly
|
||||||
|
//! degenerate into looking at an exponential number of types depending on the complexity
|
||||||
|
//! of a program.
|
||||||
|
|
||||||
|
#![feature(impl_trait_in_assoc_type)]
|
||||||
|
|
||||||
|
struct Bar;
|
||||||
|
|
||||||
|
trait Trait: Sized {
|
||||||
|
type Assoc;
|
||||||
|
fn foo() -> Foo<Self>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Trait for Bar {
|
||||||
|
type Assoc = impl std::fmt::Debug;
|
||||||
|
fn foo() -> Foo<Bar> {
|
||||||
|
Foo { field: () }
|
||||||
|
//~^ ERROR: mismatched types
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Foo<T: Trait> {
|
||||||
|
field: <T as Trait>::Assoc,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
@ -0,0 +1,20 @@
|
|||||||
|
error[E0308]: mismatched types
|
||||||
|
--> $DIR/hidden_behind_projection_behind_struct_field.rs:19:22
|
||||||
|
|
|
||||||
|
LL | type Assoc = impl std::fmt::Debug;
|
||||||
|
| -------------------- the expected opaque type
|
||||||
|
LL | fn foo() -> Foo<Bar> {
|
||||||
|
LL | Foo { field: () }
|
||||||
|
| ^^ expected opaque type, found `()`
|
||||||
|
|
|
||||||
|
= note: expected opaque type `<Bar as Trait>::Assoc`
|
||||||
|
found unit type `()`
|
||||||
|
note: this item must have the opaque type in its signature in order to be able to register hidden types
|
||||||
|
--> $DIR/hidden_behind_projection_behind_struct_field.rs:18:8
|
||||||
|
|
|
||||||
|
LL | fn foo() -> Foo<Bar> {
|
||||||
|
| ^^^
|
||||||
|
|
||||||
|
error: aborting due to previous error
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0308`.
|
30
tests/ui/type-alias-impl-trait/hidden_behind_struct_field.rs
Normal file
30
tests/ui/type-alias-impl-trait/hidden_behind_struct_field.rs
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
//! This test shows that the appearance of an opaque type
|
||||||
|
//! in the substs of a struct are enough to make it count
|
||||||
|
//! for making the function a defining use. It doesn't matter
|
||||||
|
//! if the opaque type is actually used in the field.
|
||||||
|
|
||||||
|
#![feature(impl_trait_in_assoc_type)]
|
||||||
|
// check-pass
|
||||||
|
|
||||||
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
|
struct Bar;
|
||||||
|
|
||||||
|
trait Trait: Sized {
|
||||||
|
type Assoc;
|
||||||
|
fn foo() -> Foo<Self::Assoc>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Trait for Bar {
|
||||||
|
type Assoc = impl std::fmt::Debug;
|
||||||
|
fn foo() -> Foo<Self::Assoc> {
|
||||||
|
let foo: Foo<()> = Foo { field: PhantomData };
|
||||||
|
foo
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Foo<T> {
|
||||||
|
field: PhantomData<T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
@ -0,0 +1,26 @@
|
|||||||
|
//! This test shows that we can even follow projections
|
||||||
|
//! into associated types of the same impl if they are
|
||||||
|
//! indirectly mentioned in a struct field.
|
||||||
|
|
||||||
|
#![feature(impl_trait_in_assoc_type)]
|
||||||
|
// check-pass
|
||||||
|
|
||||||
|
struct Bar;
|
||||||
|
|
||||||
|
trait Trait: Sized {
|
||||||
|
type Assoc;
|
||||||
|
fn foo() -> Foo;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Trait for Bar {
|
||||||
|
type Assoc = impl std::fmt::Debug;
|
||||||
|
fn foo() -> Foo {
|
||||||
|
Foo { field: () }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Foo {
|
||||||
|
field: <Bar as Trait>::Assoc,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
Loading…
x
Reference in New Issue
Block a user