-
Notifications
You must be signed in to change notification settings - Fork 13.8k
Don't substitute a GAT that has mismatched generics in OpaqueTypeCollector
#112876
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,7 +6,7 @@ use rustc_middle::ty::util::{CheckRegions, NotUniqueParam}; | |
use rustc_middle::ty::{self, Ty, TyCtxt}; | ||
use rustc_middle::ty::{TypeSuperVisitable, TypeVisitable, TypeVisitor}; | ||
use rustc_span::Span; | ||
use rustc_type_ir::AliasKind; | ||
use rustc_trait_selection::traits::check_substs_compatible; | ||
use std::ops::ControlFlow; | ||
|
||
use crate::errors::{DuplicateArg, NotParam}; | ||
|
@@ -36,6 +36,15 @@ impl<'tcx> OpaqueTypeCollector<'tcx> { | |
self.tcx.def_span(self.item) | ||
} | ||
|
||
fn parent_trait_ref(&self) -> Option<ty::TraitRef<'tcx>> { | ||
let parent = self.parent()?; | ||
if matches!(self.tcx.def_kind(parent), DefKind::Impl { .. }) { | ||
Some(self.tcx.impl_trait_ref(parent)?.subst_identity()) | ||
} else { | ||
None | ||
} | ||
} | ||
|
||
fn parent(&self) -> Option<LocalDefId> { | ||
match self.tcx.def_kind(self.item) { | ||
DefKind::Fn => None, | ||
|
@@ -56,7 +65,7 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for OpaqueTypeCollector<'tcx> { | |
#[instrument(skip(self), ret, level = "trace")] | ||
fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<ErrorGuaranteed> { | ||
match t.kind() { | ||
ty::Alias(AliasKind::Opaque, alias_ty) if alias_ty.def_id.is_local() => { | ||
ty::Alias(ty::Opaque, alias_ty) if alias_ty.def_id.is_local() => { | ||
if !self.seen.insert(alias_ty.def_id.expect_local()) { | ||
return ControlFlow::Continue(()); | ||
} | ||
|
@@ -98,37 +107,48 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for OpaqueTypeCollector<'tcx> { | |
} | ||
} | ||
} | ||
ty::Alias(AliasKind::Projection, alias_ty) => { | ||
if let Some(parent) = self.parent() { | ||
trace!(?alias_ty); | ||
let (trait_ref, own_substs) = alias_ty.trait_ref_and_own_substs(self.tcx); | ||
|
||
trace!(?trait_ref, ?own_substs); | ||
// This avoids having to do normalization of `Self::AssocTy` by only | ||
// supporting the case of a method defining opaque types from assoc types | ||
// in the same impl block. | ||
if trait_ref.self_ty() == self.tcx.type_of(parent).subst_identity() { | ||
for assoc in self.tcx.associated_items(parent).in_definition_order() { | ||
ty::Alias(ty::Projection, alias_ty) => { | ||
// This avoids having to do normalization of `Self::AssocTy` by only | ||
// supporting the case of a method defining opaque types from assoc types | ||
// in the same impl block. | ||
if let Some(parent_trait_ref) = self.parent_trait_ref() { | ||
// If the trait ref of the associated item and the impl differs, | ||
// then we can't use the impl's identity substitutions below, so | ||
// just skip. | ||
if alias_ty.trait_ref(self.tcx) == parent_trait_ref { | ||
|
||
let parent = self.parent().expect("we should have a parent here"); | ||
|
||
for &assoc in self.tcx.associated_items(parent).in_definition_order() { | ||
trace!(?assoc); | ||
if assoc.trait_item_def_id == Some(alias_ty.def_id) { | ||
// We reconstruct the generic args of the associated type within the impl | ||
// from the impl's generics and the generic args passed to the type via the | ||
// projection. | ||
let substs = ty::InternalSubsts::identity_for_item( | ||
self.tcx, | ||
parent.to_def_id(), | ||
if assoc.trait_item_def_id != Some(alias_ty.def_id) { | ||
continue; | ||
} | ||
|
||
// If the type is further specializable, then the type_of | ||
// is not actually correct below. | ||
if !assoc.defaultness(self.tcx).is_final() { | ||
continue; | ||
} | ||
|
||
|
||
let impl_substs = alias_ty.substs.rebase_onto( | ||
self.tcx, | ||
parent_trait_ref.def_id, | ||
ty::InternalSubsts::identity_for_item(self.tcx, parent), | ||
); | ||
|
||
if !check_substs_compatible(self.tcx, assoc, impl_substs) { | ||
self.tcx.sess.delay_span_bug( | ||
self.tcx.def_span(assoc.def_id), | ||
"item had incorrect substs", | ||
); | ||
trace!(?substs); | ||
let substs: Vec<_> = | ||
substs.iter().chain(own_substs.iter().copied()).collect(); | ||
trace!(?substs); | ||
// Find opaque types in this associated type. | ||
return self | ||
.tcx | ||
.type_of(assoc.def_id) | ||
.subst(self.tcx, &substs) | ||
.visit_with(self); | ||
return ControlFlow::Continue(()); | ||
} | ||
|
||
return self | ||
.tcx | ||
.type_of(assoc.def_id) | ||
.subst(self.tcx, impl_substs) | ||
.visit_with(self); | ||
} | ||
} | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
#![feature(impl_trait_in_assoc_type)] | ||
|
||
// We weren't checking that the trait and impl generics line up in the | ||
// normalization-shortcut code in `OpaqueTypeCollector`. | ||
|
||
use std::ops::Deref; | ||
|
||
trait Foo { | ||
type Bar<'a>; | ||
|
||
type Baz<'a>; | ||
|
||
fn test<'a>() -> Self::Bar<'a>; | ||
} | ||
|
||
impl Foo for () { | ||
type Bar<'a> = impl Deref<Target = Self::Baz<'a>>; | ||
|
||
type Baz<T> = impl Sized; | ||
//~^ ERROR type `Baz` has 1 type parameter but its trait declaration has 0 type parameters | ||
//~| ERROR unconstrained opaque type | ||
|
||
fn test<'a>() -> Self::Bar<'a> { | ||
&() | ||
} | ||
} | ||
|
||
fn main() {} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
error[E0049]: type `Baz` has 1 type parameter but its trait declaration has 0 type parameters | ||
--> $DIR/impl-trait-in-type-alias-with-bad-substs.rs:19:14 | ||
| | ||
LL | type Baz<'a>; | ||
| -- expected 0 type parameters | ||
... | ||
LL | type Baz<T> = impl Sized; | ||
| ^ found 1 type parameter | ||
|
||
error: unconstrained opaque type | ||
--> $DIR/impl-trait-in-type-alias-with-bad-substs.rs:19:19 | ||
| | ||
LL | type Baz<T> = impl Sized; | ||
| ^^^^^^^^^^ | ||
| | ||
= note: `Baz` must be used in combination with a concrete type within the same impl | ||
|
||
error: aborting due to 2 previous errors | ||
|
||
For more information about this error, try `rustc --explain E0049`. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
#![feature(impl_trait_in_assoc_type)] | ||
|
||
trait Foo<T> { | ||
type Assoc; | ||
|
||
fn test() -> u32; | ||
} | ||
|
||
struct DefinesOpaque; | ||
impl Foo<DefinesOpaque> for () { | ||
type Assoc = impl Sized; | ||
|
||
// This test's return type is `u32`, *not* the opaque that is defined above. | ||
// Previously we were only checking that the self type of the assoc matched, | ||
// but this doesn't account for other impls with different trait substs. | ||
fn test() -> <() as Foo<NoOpaques>>::Assoc { | ||
let _: <Self as Foo<DefinesOpaque>>::Assoc = ""; | ||
//~^ ERROR mismatched types | ||
|
||
1 | ||
} | ||
} | ||
|
||
struct NoOpaques; | ||
impl Foo<NoOpaques> for () { | ||
type Assoc = u32; | ||
|
||
fn test() -> u32 { | ||
1 | ||
} | ||
} | ||
|
||
fn main() {} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
error[E0308]: mismatched types | ||
--> $DIR/not-matching-trait-refs-isnt-defining.rs:17:54 | ||
| | ||
LL | type Assoc = impl Sized; | ||
| ---------- the expected opaque type | ||
... | ||
LL | let _: <Self as Foo<DefinesOpaque>>::Assoc = ""; | ||
| ----------------------------------- ^^ expected opaque type, found `&str` | ||
| | | ||
| expected due to this | ||
| | ||
= note: expected opaque type `<() as Foo<DefinesOpaque>>::Assoc` | ||
found reference `&'static str` | ||
note: this item must have the opaque type in its signature in order to be able to register hidden types | ||
--> $DIR/not-matching-trait-refs-isnt-defining.rs:16:5 | ||
| | ||
LL | fn test() -> <() as Foo<NoOpaques>>::Assoc { | ||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ||
|
||
error: aborting due to previous error | ||
|
||
For more information about this error, try `rustc --explain E0308`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Future nit: please do such moves in separate commits