blob: 9242a1a751bc776cb417394dbbefb878d0eba418 [file] [log] [blame]
use rustc_data_structures::fx::FxHashSet;
use rustc_hir::intravisit::Visitor;
use rustc_hir::{def::DefKind, def_id::LocalDefId};
use rustc_hir::{intravisit, CRATE_HIR_ID};
use rustc_middle::query::Providers;
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_trait_selection::traits::check_args_compatible;
use std::ops::ControlFlow;
use crate::errors::{DuplicateArg, NotParam};
struct OpaqueTypeCollector<'tcx> {
tcx: TyCtxt<'tcx>,
opaques: Vec<LocalDefId>,
/// The `DefId` of the item which we are collecting opaque types for.
item: LocalDefId,
/// Avoid infinite recursion due to recursive declarations.
seen: FxHashSet<LocalDefId>,
span: Option<Span>,
}
impl<'tcx> OpaqueTypeCollector<'tcx> {
fn new(tcx: TyCtxt<'tcx>, item: LocalDefId) -> Self {
Self { tcx, opaques: Vec::new(), item, seen: Default::default(), span: None }
}
fn span(&self) -> Span {
self.span.unwrap_or_else(|| {
self.tcx.def_ident_span(self.item).unwrap_or_else(|| self.tcx.def_span(self.item))
})
}
fn visit_spanned(&mut self, span: Span, value: impl TypeVisitable<TyCtxt<'tcx>>) {
let old = self.span;
self.span = Some(span);
value.visit_with(self);
self.span = old;
}
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)?.instantiate_identity())
} else {
None
}
}
fn parent(&self) -> Option<LocalDefId> {
match self.tcx.def_kind(self.item) {
DefKind::AssocFn | DefKind::AssocTy | DefKind::AssocConst => {
Some(self.tcx.local_parent(self.item))
}
_ => None,
}
}
/// Returns `true` if `opaque_hir_id` is a sibling or a child of a sibling of `self.item`.
///
/// Example:
/// ```ignore UNSOLVED (is this a bug?)
/// # #![feature(type_alias_impl_trait)]
/// pub mod foo {
/// pub mod bar {
/// pub trait Bar { /* ... */ }
/// pub type Baz = impl Bar;
///
/// # impl Bar for () {}
/// fn f1() -> Baz { /* ... */ }
/// }
/// fn f2() -> bar::Baz { /* ... */ }
/// }
/// ```
///
/// and `opaque_def_id` is the `DefId` of the definition of the opaque type `Baz`.
/// For the above example, this function returns `true` for `f1` and `false` for `f2`.
#[instrument(level = "trace", skip(self), ret)]
fn check_tait_defining_scope(&self, opaque_def_id: LocalDefId) -> bool {
let mut hir_id = self.tcx.hir().local_def_id_to_hir_id(self.item);
let opaque_hir_id = self.tcx.hir().local_def_id_to_hir_id(opaque_def_id);
// Named opaque types can be defined by any siblings or children of siblings.
let scope = self.tcx.hir().get_defining_scope(opaque_hir_id);
// We walk up the node tree until we hit the root or the scope of the opaque type.
while hir_id != scope && hir_id != CRATE_HIR_ID {
hir_id = self.tcx.hir().get_parent_item(hir_id).into();
}
// Syntactically, we are allowed to define the concrete type if:
hir_id == scope
}
#[instrument(level = "trace", skip(self))]
fn collect_taits_declared_in_body(&mut self) {
let body = self.tcx.hir().body(self.tcx.hir().body_owned_by(self.item)).value;
struct TaitInBodyFinder<'a, 'tcx> {
collector: &'a mut OpaqueTypeCollector<'tcx>,
}
impl<'v> intravisit::Visitor<'v> for TaitInBodyFinder<'_, '_> {
#[instrument(level = "trace", skip(self))]
fn visit_nested_item(&mut self, id: rustc_hir::ItemId) {
let id = id.owner_id.def_id;
if let DefKind::TyAlias = self.collector.tcx.def_kind(id) {
let items = self.collector.tcx.opaque_types_defined_by(id);
self.collector.opaques.extend(items);
}
}
#[instrument(level = "trace", skip(self))]
// Recurse into these, as they are type checked with their parent
fn visit_nested_body(&mut self, id: rustc_hir::BodyId) {
let body = self.collector.tcx.hir().body(id);
self.visit_body(body);
}
}
TaitInBodyFinder { collector: self }.visit_expr(body);
}
}
impl<'tcx> super::sig_types::SpannedTypeVisitor<'tcx> for OpaqueTypeCollector<'tcx> {
#[instrument(skip(self), ret, level = "trace")]
fn visit(&mut self, span: Span, value: impl TypeVisitable<TyCtxt<'tcx>>) -> ControlFlow<!> {
self.visit_spanned(span, value);
ControlFlow::Continue(())
}
}
impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for OpaqueTypeCollector<'tcx> {
#[instrument(skip(self), ret, level = "trace")]
fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<!> {
t.super_visit_with(self)?;
match t.kind() {
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(());
}
// TAITs outside their defining scopes are ignored.
let origin = self.tcx.opaque_type_origin(alias_ty.def_id.expect_local());
trace!(?origin);
match origin {
rustc_hir::OpaqueTyOrigin::FnReturn(_)
| rustc_hir::OpaqueTyOrigin::AsyncFn(_) => {}
rustc_hir::OpaqueTyOrigin::TyAlias { in_assoc_ty } => {
if !in_assoc_ty {
if !self.check_tait_defining_scope(alias_ty.def_id.expect_local()) {
return ControlFlow::Continue(());
}
}
}
}
self.opaques.push(alias_ty.def_id.expect_local());
let parent_count = self.tcx.generics_of(alias_ty.def_id).parent_count;
// Only check that the parent generics of the TAIT/RPIT are unique.
// the args owned by the opaque are going to always be duplicate
// lifetime params for RPITs, and empty for TAITs.
match self
.tcx
.uses_unique_generic_params(&alias_ty.args[..parent_count], CheckRegions::Bound)
{
Ok(()) => {
// FIXME: implement higher kinded lifetime bounds on nested opaque types. They are not
// supported at all, so this is sound to do, but once we want to support them, you'll
// start seeing the error below.
// Collect opaque types nested within the associated type bounds of this opaque type.
// We use identity args 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
.tcx
.explicit_item_bounds(alias_ty.def_id)
.instantiate_identity_iter_copied()
{
trace!(?pred);
self.visit_spanned(span, pred);
}
}
Err(NotUniqueParam::NotParam(arg)) => {
self.tcx.sess.emit_err(NotParam {
arg,
span: self.span(),
opaque_span: self.tcx.def_span(alias_ty.def_id),
});
}
Err(NotUniqueParam::DuplicateParam(arg)) => {
self.tcx.sess.emit_err(DuplicateArg {
arg,
span: self.span(),
opaque_span: self.tcx.def_span(alias_ty.def_id),
});
}
}
}
ty::Alias(ty::Weak, alias_ty) if alias_ty.def_id.is_local() => {
self.tcx
.type_of(alias_ty.def_id)
.instantiate(self.tcx, alias_ty.args)
.visit_with(self)?;
}
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) {
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_args = alias_ty.args.rebase_onto(
self.tcx,
parent_trait_ref.def_id,
ty::GenericArgs::identity_for_item(self.tcx, parent),
);
if check_args_compatible(self.tcx, assoc, impl_args) {
return self
.tcx
.type_of(assoc.def_id)
.instantiate(self.tcx, impl_args)
.visit_with(self);
} else {
self.tcx.sess.delay_span_bug(
self.tcx.def_span(assoc.def_id),
"item had incorrect args",
);
}
}
}
}
}
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` args, we either
// * found the opaque in the args
// * 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).instantiate_identity();
self.visit_spanned(self.tcx.def_span(field.did), ty);
}
}
}
_ => trace!(kind=?t.kind()),
}
ControlFlow::Continue(())
}
}
fn opaque_types_defined_by<'tcx>(
tcx: TyCtxt<'tcx>,
item: LocalDefId,
) -> &'tcx ty::List<LocalDefId> {
let kind = tcx.def_kind(item);
trace!(?kind);
let mut collector = OpaqueTypeCollector::new(tcx, item);
super::sig_types::walk_types(tcx, item, &mut collector);
match kind {
DefKind::AssocFn
| DefKind::Fn
| DefKind::Static(_)
| DefKind::Const
| DefKind::AssocConst
| DefKind::AnonConst => {
collector.collect_taits_declared_in_body();
}
DefKind::OpaqueTy
| DefKind::TyAlias
| DefKind::AssocTy
| DefKind::Mod
| DefKind::Struct
| DefKind::Union
| DefKind::Enum
| DefKind::Variant
| DefKind::Trait
| DefKind::ForeignTy
| DefKind::TraitAlias
| DefKind::TyParam
| DefKind::ConstParam
| DefKind::Ctor(_, _)
| DefKind::Macro(_)
| DefKind::ExternCrate
| DefKind::Use
| DefKind::ForeignMod
| DefKind::Field
| DefKind::LifetimeParam
| DefKind::GlobalAsm
| DefKind::Impl { .. } => {}
// Closures and coroutines are type checked with their parent, so we need to allow all
// opaques from the closure signature *and* from the parent body.
DefKind::Closure | DefKind::Coroutine | DefKind::InlineConst => {
collector.opaques.extend(tcx.opaque_types_defined_by(tcx.local_parent(item)));
}
}
tcx.mk_local_def_ids(&collector.opaques)
}
pub(super) fn provide(providers: &mut Providers) {
*providers = Providers { opaque_types_defined_by, ..*providers };
}