Skip to content
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

Rollup of 4 pull requests #112914

Merged
merged 9 commits into from
Jun 22, 2023
2 changes: 1 addition & 1 deletion compiler/rustc_middle/src/ty/sty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1584,7 +1584,7 @@ pub struct EarlyBoundRegion {

impl fmt::Debug for EarlyBoundRegion {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}, {}", self.index, self.name)
write!(f, "{:?}, {}, {}", self.def_id, self.index, self.name)
}
}

Expand Down
8 changes: 4 additions & 4 deletions compiler/rustc_trait_selection/src/traits/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,12 +65,12 @@ pub use self::specialize::{
pub use self::structural_match::search_for_structural_match_violation;
pub use self::structural_normalize::StructurallyNormalizeExt;
pub use self::util::elaborate;
pub use self::util::{expand_trait_aliases, TraitAliasExpander};
pub use self::util::{get_vtable_index_of_object_method, impl_item_is_final, upcast_choices};
pub use self::util::{
supertrait_def_ids, supertraits, transitive_bounds, transitive_bounds_that_define_assoc_item,
SupertraitDefIds,
check_substs_compatible, supertrait_def_ids, supertraits, transitive_bounds,
transitive_bounds_that_define_assoc_item, SupertraitDefIds,
};
pub use self::util::{expand_trait_aliases, TraitAliasExpander};
pub use self::util::{get_vtable_index_of_object_method, impl_item_is_final, upcast_choices};

pub use self::chalk_fulfill::FulfillmentContext as ChalkFulfillmentContext;

Expand Down
42 changes: 1 addition & 41 deletions compiler/rustc_trait_selection/src/traits/project.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//! Code for projecting associated types out of trait references.

use super::check_substs_compatible;
use super::specialization_graph;
use super::translate_substs;
use super::util;
Expand Down Expand Up @@ -2378,47 +2379,6 @@ fn confirm_impl_candidate<'cx, 'tcx>(
}
}

// Verify that the trait item and its implementation have compatible substs lists
fn check_substs_compatible<'tcx>(
tcx: TyCtxt<'tcx>,
assoc_item: ty::AssocItem,
substs: ty::SubstsRef<'tcx>,
) -> bool {
fn check_substs_compatible_inner<'tcx>(
tcx: TyCtxt<'tcx>,
generics: &'tcx ty::Generics,
args: &'tcx [ty::GenericArg<'tcx>],
) -> bool {
if generics.count() != args.len() {
return false;
}

let (parent_args, own_args) = args.split_at(generics.parent_count);

if let Some(parent) = generics.parent
&& let parent_generics = tcx.generics_of(parent)
&& !check_substs_compatible_inner(tcx, parent_generics, parent_args) {
return false;
}

for (param, arg) in std::iter::zip(&generics.params, own_args) {
match (&param.kind, arg.unpack()) {
(ty::GenericParamDefKind::Type { .. }, ty::GenericArgKind::Type(_))
| (ty::GenericParamDefKind::Lifetime, ty::GenericArgKind::Lifetime(_))
| (ty::GenericParamDefKind::Const { .. }, ty::GenericArgKind::Const(_)) => {}
_ => return false,
}
}

true
}

let generics = tcx.generics_of(assoc_item.def_id);
// Chop off any additional substs (RPITIT) substs
let substs = &substs[0..generics.count().min(substs.len())];
check_substs_compatible_inner(tcx, generics, substs)
}

fn confirm_impl_trait_in_trait_candidate<'tcx>(
selcx: &mut SelectionContext<'_, 'tcx>,
obligation: &ProjectionTyObligation<'tcx>,
Expand Down
41 changes: 41 additions & 0 deletions compiler/rustc_trait_selection/src/traits/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -302,3 +302,44 @@ pub enum TupleArgumentsFlag {
Yes,
No,
}

// Verify that the trait item and its implementation have compatible substs lists
pub fn check_substs_compatible<'tcx>(
tcx: TyCtxt<'tcx>,
assoc_item: ty::AssocItem,
substs: ty::SubstsRef<'tcx>,
) -> bool {
fn check_substs_compatible_inner<'tcx>(
tcx: TyCtxt<'tcx>,
generics: &'tcx ty::Generics,
args: &'tcx [ty::GenericArg<'tcx>],
) -> bool {
if generics.count() != args.len() {
return false;
}

let (parent_args, own_args) = args.split_at(generics.parent_count);

if let Some(parent) = generics.parent
&& let parent_generics = tcx.generics_of(parent)
&& !check_substs_compatible_inner(tcx, parent_generics, parent_args) {
return false;
}

for (param, arg) in std::iter::zip(&generics.params, own_args) {
match (&param.kind, arg.unpack()) {
(ty::GenericParamDefKind::Type { .. }, ty::GenericArgKind::Type(_))
| (ty::GenericParamDefKind::Lifetime, ty::GenericArgKind::Lifetime(_))
| (ty::GenericParamDefKind::Const { .. }, ty::GenericArgKind::Const(_)) => {}
_ => return false,
}
}

true
}

let generics = tcx.generics_of(assoc_item.def_id);
// Chop off any additional substs (RPITIT) substs
let substs = &substs[0..generics.count().min(substs.len())];
check_substs_compatible_inner(tcx, generics, substs)
}
80 changes: 50 additions & 30 deletions compiler/rustc_ty_utils/src/opaque_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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,
Expand All @@ -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(());
}
Expand Down Expand Up @@ -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);
}
}
}
Expand Down
3 changes: 2 additions & 1 deletion src/librustdoc/html/render/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -799,10 +799,11 @@ fn assoc_type(
if !bounds.is_empty() {
write!(w, ": {}", print_generic_bounds(bounds, cx))
}
write!(w, "{}", print_where_clause(generics, cx, indent, Ending::NoNewline));
// Render the default before the where-clause which aligns with the new recommended style. See #89122.
if let Some(default) = default {
write!(w, " = {}", default.print(cx))
}
write!(w, "{}", print_where_clause(generics, cx, indent, Ending::NoNewline));
}

fn assoc_method(
Expand Down
4 changes: 2 additions & 2 deletions tests/rustdoc/generic-associated-types/gats.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@ impl LendingIterator for () {
pub struct Infinite<T>(T);

// @has foo/trait.LendingIterator.html
// @has - '//*[@id="associatedtype.Item-2"]//h4[@class="code-header"]' "type Item<'a> where Self: 'a = &'a T"
// @has - '//*[@id="associatedtype.Item-2"]//h4[@class="code-header"]' "type Item<'a> = &'a T where Self: 'a"
impl<T> LendingIterator for Infinite<T> {
type Item<'a> where Self: 'a = &'a T;
type Item<'a> = &'a T where Self: 'a;

fn next<'a>(&'a self) -> Self::Item<'a> {
&self.0
Expand Down
4 changes: 2 additions & 2 deletions tests/ui/nll/ty-outlives/impl-trait-captures.stderr
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
error[E0700]: hidden type for `Opaque(DefId(0:13 ~ impl_trait_captures[aeb9]::foo::{opaque#0}), [ReEarlyBound(0, 'a), T, ReEarlyBound(0, 'a)])` captures lifetime that does not appear in bounds
error[E0700]: hidden type for `Opaque(DefId(0:13 ~ impl_trait_captures[aeb9]::foo::{opaque#0}), [ReEarlyBound(DefId(0:9 ~ impl_trait_captures[aeb9]::foo::'a), 0, 'a), T, ReEarlyBound(DefId(0:9 ~ impl_trait_captures[aeb9]::foo::'a), 0, 'a)])` captures lifetime that does not appear in bounds
--> $DIR/impl-trait-captures.rs:11:5
|
LL | fn foo<'a, T>(x: &T) -> impl Foo<'a> {
Expand All @@ -8,7 +8,7 @@ LL | fn foo<'a, T>(x: &T) -> impl Foo<'a> {
LL | x
| ^
|
help: to declare that `Opaque(DefId(0:13 ~ impl_trait_captures[aeb9]::foo::{opaque#0}), [ReEarlyBound(0, 'a), T, ReEarlyBound(2, 'a)])` captures `ReFree(DefId(0:8 ~ impl_trait_captures[aeb9]::foo), BrNamed(DefId(0:12 ~ impl_trait_captures[aeb9]::foo::'_), '_))`, you can add an explicit `ReFree(DefId(0:8 ~ impl_trait_captures[aeb9]::foo), BrNamed(DefId(0:12 ~ impl_trait_captures[aeb9]::foo::'_), '_))` lifetime bound
help: to declare that `Opaque(DefId(0:13 ~ impl_trait_captures[aeb9]::foo::{opaque#0}), [ReEarlyBound(DefId(0:9 ~ impl_trait_captures[aeb9]::foo::'a), 0, 'a), T, ReEarlyBound(DefId(0:14 ~ impl_trait_captures[aeb9]::foo::{opaque#0}::'a), 2, 'a)])` captures `ReFree(DefId(0:8 ~ impl_trait_captures[aeb9]::foo), BrNamed(DefId(0:12 ~ impl_trait_captures[aeb9]::foo::'_), '_))`, you can add an explicit `ReFree(DefId(0:8 ~ impl_trait_captures[aeb9]::foo), BrNamed(DefId(0:12 ~ impl_trait_captures[aeb9]::foo::'_), '_))` lifetime bound
|
LL | fn foo<'a, T>(x: &T) -> impl Foo<'a> + ReFree(DefId(0:8 ~ impl_trait_captures[aeb9]::foo), BrNamed(DefId(0:12 ~ impl_trait_captures[aeb9]::foo::'_), '_)) {
| ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Expand Down
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`.