Skip to content

Commit 27b386a

Browse files
committedJun 22, 2023
Only walk the identity substituted version of struct fields
1 parent b323f58 commit 27b386a

File tree

5 files changed

+126
-1
lines changed

5 files changed

+126
-1
lines changed
 

‎compiler/rustc_ty_utils/src/opaque_types.rs

+22-1
Original file line numberDiff line numberDiff line change
@@ -82,10 +82,12 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for OpaqueTypeCollector<'tcx> {
8282
// start seeing the error below.
8383

8484
// Collect opaque types nested within the associated type bounds of this opaque type.
85+
// We use identity substs here, because we already know that the opaque type uses
86+
// only generic parameters, and thus substituting would not give us more information.
8587
for (pred, span) in self
8688
.tcx
8789
.explicit_item_bounds(alias_ty.def_id)
88-
.subst_iter_copied(self.tcx, alias_ty.substs)
90+
.subst_identity_iter_copied()
8991
{
9092
trace!(?pred);
9193
self.visit_spanned(span, pred);
@@ -158,6 +160,25 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for OpaqueTypeCollector<'tcx> {
158160
}
159161
}
160162
}
163+
ty::Adt(def, _) if def.did().is_local() => {
164+
if !self.seen.insert(def.did().expect_local()) {
165+
return ControlFlow::Continue(());
166+
}
167+
for variant in def.variants().iter() {
168+
for field in variant.fields.iter() {
169+
// Don't use the `ty::Adt` substs, we either
170+
// * found the opaque in the substs
171+
// * will find the opaque in the unsubstituted fields
172+
// The only other situation that can occur is that after substituting,
173+
// some projection resolves to an opaque that we would have otherwise
174+
// not found. While we could substitute and walk those, that would mean we
175+
// would have to walk all substitutions of an Adt, which can quickly
176+
// degenerate into looking at an exponential number of types.
177+
let ty = self.tcx.type_of(field.did).subst_identity();
178+
self.visit_spanned(self.tcx.def_span(field.did), ty);
179+
}
180+
}
181+
}
161182
_ => trace!(kind=?t.kind()),
162183
}
163184
ControlFlow::Continue(())
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
//! This test shows that a field type that is a projection that resolves to an opaque,
2+
//! is not a defining use. While we could substitute the struct generics, that would
3+
//! mean we would have to walk all substitutions of an `Foo`, which can quickly
4+
//! degenerate into looking at an exponential number of types depending on the complexity
5+
//! of a program.
6+
7+
#![feature(impl_trait_in_assoc_type)]
8+
9+
struct Bar;
10+
11+
trait Trait: Sized {
12+
type Assoc;
13+
fn foo() -> Foo<Self>;
14+
}
15+
16+
impl Trait for Bar {
17+
type Assoc = impl std::fmt::Debug;
18+
fn foo() -> Foo<Bar> {
19+
Foo { field: () }
20+
//~^ ERROR: mismatched types
21+
}
22+
}
23+
24+
struct Foo<T: Trait> {
25+
field: <T as Trait>::Assoc,
26+
}
27+
28+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
error[E0308]: mismatched types
2+
--> $DIR/hidden_behind_projection_behind_struct_field.rs:19:22
3+
|
4+
LL | type Assoc = impl std::fmt::Debug;
5+
| -------------------- the expected opaque type
6+
LL | fn foo() -> Foo<Bar> {
7+
LL | Foo { field: () }
8+
| ^^ expected opaque type, found `()`
9+
|
10+
= note: expected opaque type `<Bar as Trait>::Assoc`
11+
found unit type `()`
12+
note: this item must have the opaque type in its signature in order to be able to register hidden types
13+
--> $DIR/hidden_behind_projection_behind_struct_field.rs:18:8
14+
|
15+
LL | fn foo() -> Foo<Bar> {
16+
| ^^^
17+
18+
error: aborting due to previous error
19+
20+
For more information about this error, try `rustc --explain E0308`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
//! This test shows that the appearance of an opaque type
2+
//! in the substs of a struct are enough to make it count
3+
//! for making the function a defining use. It doesn't matter
4+
//! if the opaque type is actually used in the field.
5+
6+
#![feature(impl_trait_in_assoc_type)]
7+
// check-pass
8+
9+
use std::marker::PhantomData;
10+
11+
struct Bar;
12+
13+
trait Trait: Sized {
14+
type Assoc;
15+
fn foo() -> Foo<Self::Assoc>;
16+
}
17+
18+
impl Trait for Bar {
19+
type Assoc = impl std::fmt::Debug;
20+
fn foo() -> Foo<Self::Assoc> {
21+
let foo: Foo<()> = Foo { field: PhantomData };
22+
foo
23+
}
24+
}
25+
26+
struct Foo<T> {
27+
field: PhantomData<T>,
28+
}
29+
30+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
//! This test shows that we can even follow projections
2+
//! into associated types of the same impl if they are
3+
//! indirectly mentioned in a struct field.
4+
5+
#![feature(impl_trait_in_assoc_type)]
6+
// check-pass
7+
8+
struct Bar;
9+
10+
trait Trait: Sized {
11+
type Assoc;
12+
fn foo() -> Foo;
13+
}
14+
15+
impl Trait for Bar {
16+
type Assoc = impl std::fmt::Debug;
17+
fn foo() -> Foo {
18+
Foo { field: () }
19+
}
20+
}
21+
22+
struct Foo {
23+
field: <Bar as Trait>::Assoc,
24+
}
25+
26+
fn main() {}

0 commit comments

Comments
 (0)
Please sign in to comment.