1
Fork 0

Require TAITs to be mentioned in the signatures of functions that register hidden types for them

This commit is contained in:
Oli Scherer 2023-06-15 09:41:14 +00:00
parent 921f669749
commit 4c99872efe
70 changed files with 796 additions and 184 deletions

View file

@ -1,11 +1,17 @@
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_infer::infer::TyCtxtInferExt;
use rustc_middle::query::Providers;
use rustc_middle::traits::ObligationCause;
use rustc_middle::ty::util::{CheckRegions, NotUniqueParam};
use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt};
use rustc_middle::ty::{TypeSuperVisitable, TypeVisitable, TypeVisitor};
use rustc_span::def_id::CRATE_DEF_ID;
use rustc_span::Span;
use rustc_trait_selection::traits::check_substs_compatible;
use rustc_trait_selection::traits::ObligationCtxt;
use std::ops::ControlFlow;
use crate::errors::{DuplicateArg, NotParam};
@ -19,12 +25,21 @@ struct OpaqueTypeCollector<'tcx> {
/// Avoid infinite recursion due to recursive declarations.
seen: FxHashSet<LocalDefId>,
universes: Vec<Option<ty::UniverseIndex>>,
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 }
Self {
tcx,
opaques: Vec::new(),
item,
seen: Default::default(),
universes: vec![],
span: None,
}
}
fn span(&self) -> Span {
@ -51,7 +66,7 @@ impl<'tcx> OpaqueTypeCollector<'tcx> {
fn parent(&self) -> Option<LocalDefId> {
match self.tcx.def_kind(self.item) {
DefKind::Fn => None,
DefKind::AnonConst | DefKind::InlineConst | DefKind::Fn | DefKind::TyAlias => None,
DefKind::AssocFn | DefKind::AssocTy | DefKind::AssocConst => {
Some(self.tcx.local_parent(self.item))
}
@ -61,9 +76,53 @@ impl<'tcx> OpaqueTypeCollector<'tcx> {
),
}
}
/// 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
}
}
impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for OpaqueTypeCollector<'tcx> {
fn visit_binder<T: TypeVisitable<TyCtxt<'tcx>>>(
&mut self,
t: &ty::Binder<'tcx, T>,
) -> ControlFlow<!> {
self.universes.push(None);
let t = t.super_visit_with(self);
self.universes.pop();
t
}
#[instrument(skip(self), ret, level = "trace")]
fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<!> {
t.super_visit_with(self)?;
@ -73,6 +132,34 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for OpaqueTypeCollector<'tcx> {
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 {
// Only associated items can be defining for opaque types in associated types.
if let Some(parent) = self.parent() {
let mut current = alias_ty.def_id.expect_local();
while current != parent && current != CRATE_DEF_ID {
current = self.tcx.local_parent(current);
}
if current != parent {
return ControlFlow::Continue(());
}
} else {
return ControlFlow::Continue(());
}
} else {
if !self.check_tait_defining_scope(alias_ty.def_id.expect_local()) {
return ControlFlow::Continue(());
}
}
}
}
self.opaques.push(alias_ty.def_id.expect_local());
match self.tcx.uses_unique_generic_params(alias_ty.substs, CheckRegions::Bound) {
@ -159,6 +246,28 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for OpaqueTypeCollector<'tcx> {
}
}
}
// Normalize trivial projections.
let mut infcx = self.tcx.infer_ctxt();
let infcx = infcx.build();
let t = if t.has_escaping_bound_vars() {
let (t, _mapped_regions, _mapped_types, _mapped_consts) =
rustc_trait_selection::traits::project::BoundVarReplacer::replace_bound_vars(
&infcx,
&mut self.universes,
t,
);
t
} else {
t
};
let ocx = ObligationCtxt::new(&infcx);
let cause = ObligationCause::dummy_with_span(self.span());
let normalized = ocx.normalize(&cause, self.tcx.param_env(self.item), t);
trace!(?normalized);
if normalized != t {
normalized.visit_with(self)?;
}
}
ty::Adt(def, _) if def.did().is_local() => {
if !self.seen.insert(def.did().expect_local()) {
@ -188,29 +297,71 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for OpaqueTypeCollector<'tcx> {
fn opaque_types_defined_by<'tcx>(tcx: TyCtxt<'tcx>, item: LocalDefId) -> &'tcx [LocalDefId] {
let kind = tcx.def_kind(item);
trace!(?kind);
// FIXME(type_alias_impl_trait): This is definitely still wrong except for RPIT and impl trait in assoc types.
match kind {
// We're also doing this for `AssocTy` for the wf checks in `check_opaque_meets_bounds`
DefKind::Fn | DefKind::AssocFn | DefKind::AssocTy | DefKind::AssocConst => {
DefKind::Static(_)
| DefKind::Const
| DefKind::TyAlias
| DefKind::Fn
| DefKind::OpaqueTy
| DefKind::AnonConst
| DefKind::InlineConst
| DefKind::AssocFn
| DefKind::AssocTy
| DefKind::AssocConst => {
let mut collector = OpaqueTypeCollector::new(tcx, item);
match kind {
// Walk over the signature of the function-like to find the opaques.
DefKind::AssocFn | DefKind::Fn => {
let ty_sig = tcx.fn_sig(item).subst_identity();
let hir_sig = tcx.hir().get_by_def_id(item).fn_sig().unwrap();
// Walk over the inputs and outputs manually in order to get good spans for them.
collector.visit_spanned(hir_sig.decl.output.span(), ty_sig.output());
for (hir, ty) in hir_sig.decl.inputs.iter().zip(ty_sig.inputs().iter()) {
collector.visit_spanned(hir.span, ty.map_bound(|x| *x));
DefKind::Static(_)
| DefKind::Const
| DefKind::AssocConst
| DefKind::AssocFn
| DefKind::AnonConst
| DefKind::InlineConst
| DefKind::Fn => {
match kind {
// Walk over the signature of the function-like to find the opaques.
DefKind::AssocFn | DefKind::Fn => {
let ty_sig = tcx.fn_sig(item).subst_identity();
let hir_sig = tcx.hir().get_by_def_id(item).fn_sig().unwrap();
// Walk over the inputs and outputs manually in order to get good spans for them.
collector.visit_spanned(hir_sig.decl.output.span(), ty_sig.output());
for (hir, ty) in hir_sig.decl.inputs.iter().zip(ty_sig.inputs().iter())
{
collector.visit_spanned(hir.span, ty.map_bound(|x| *x));
}
}
// Walk over the type of the item to find opaques.
DefKind::Static(_)
| DefKind::Const
| DefKind::AssocConst
| DefKind::AnonConst
| DefKind::InlineConst => {
let span = match tcx.hir().get_by_def_id(item).ty() {
Some(ty) => ty.span,
_ => tcx.def_span(item),
};
collector.visit_spanned(span, tcx.type_of(item).subst_identity());
}
_ => unreachable!(),
}
// Look at all where bounds.
tcx.predicates_of(item).instantiate_identity(tcx).visit_with(&mut collector);
// An item is allowed to constrain opaques declared within its own body (but not nested within
// nested functions).
for id in find_taits_declared_in_body(tcx, item) {
if let DefKind::TyAlias = tcx.def_kind(id) {
collector.opaques.extend(tcx.opaque_types_defined_by(id))
}
}
}
// Walk over the type of the item to find opaques.
DefKind::AssocTy | DefKind::AssocConst => {
let span = match tcx.hir().get_by_def_id(item).ty() {
Some(ty) => ty.span,
_ => tcx.def_span(item),
};
collector.visit_spanned(span, tcx.type_of(item).subst_identity());
DefKind::TyAlias | DefKind::AssocTy => {
tcx.type_of(item).subst_identity().visit_with(&mut collector);
}
DefKind::OpaqueTy => {
for (pred, span) in tcx.explicit_item_bounds(item).subst_identity_iter_copied()
{
collector.visit_spanned(span, pred);
}
}
_ => unreachable!(),
}
@ -222,33 +373,46 @@ fn opaque_types_defined_by<'tcx>(tcx: TyCtxt<'tcx>, item: LocalDefId) -> &'tcx [
| DefKind::Enum
| DefKind::Variant
| DefKind::Trait
| DefKind::TyAlias
| DefKind::ForeignTy
| DefKind::TraitAlias
| DefKind::TyParam
| DefKind::Const
| DefKind::ConstParam
| DefKind::Static(_)
| DefKind::Ctor(_, _)
| DefKind::Macro(_)
| DefKind::ExternCrate
| DefKind::Use
| DefKind::ForeignMod
| DefKind::AnonConst
| DefKind::InlineConst
| DefKind::OpaqueTy
| DefKind::ImplTraitPlaceholder
| DefKind::Field
| DefKind::LifetimeParam
| DefKind::GlobalAsm
| DefKind::Impl { .. }
| DefKind::Closure
| DefKind::Generator => {
span_bug!(tcx.def_span(item), "{kind:?} is type checked as part of its parent")
| DefKind::Impl { .. } => &[],
DefKind::Closure | DefKind::Generator => {
tcx.opaque_types_defined_by(tcx.local_parent(item))
}
}
}
fn find_taits_declared_in_body(tcx: TyCtxt<'_>, item: LocalDefId) -> Vec<LocalDefId> {
let body = tcx.hir().body(tcx.hir().body_owned_by(item)).value;
#[derive(Default, Debug)]
struct TaitInBodyFinder {
/// Ids of type aliases found in the body
type_aliases: Vec<LocalDefId>,
}
impl<'v> intravisit::Visitor<'v> for TaitInBodyFinder {
#[instrument(level = "trace")]
fn visit_nested_item(&mut self, id: rustc_hir::ItemId) {
let id = id.owner_id.def_id;
self.type_aliases.push(id);
}
}
let mut visitor = TaitInBodyFinder::default();
trace!(?body);
visitor.visit_expr(body);
visitor.type_aliases
}
pub(super) fn provide(providers: &mut Providers) {
*providers = Providers { opaque_types_defined_by, ..*providers };
}