1
Fork 0

Implement #[define_opaque] attribute for functions.

This commit is contained in:
Oli Scherer 2024-07-26 10:04:02 +00:00 committed by Oli Scherer
parent 2c6a12ec44
commit cb4751d4b8
653 changed files with 2911 additions and 2580 deletions

View file

@ -3466,6 +3466,7 @@ pub struct Fn {
pub generics: Generics,
pub sig: FnSig,
pub contract: Option<P<FnContract>>,
pub define_opaque: Option<ThinVec<(NodeId, Path)>>,
pub body: Option<P<Block>>,
}
@ -3763,7 +3764,7 @@ mod size_asserts {
static_assert_size!(Block, 32);
static_assert_size!(Expr, 72);
static_assert_size!(ExprKind, 40);
static_assert_size!(Fn, 168);
static_assert_size!(Fn, 176);
static_assert_size!(ForeignItem, 88);
static_assert_size!(ForeignItemKind, 16);
static_assert_size!(GenericArg, 24);

View file

@ -973,7 +973,14 @@ fn walk_fn<T: MutVisitor>(vis: &mut T, kind: FnKind<'_>) {
_ctxt,
_ident,
_vis,
Fn { defaultness, generics, contract, body, sig: FnSig { header, decl, span } },
Fn {
defaultness,
generics,
contract,
body,
sig: FnSig { header, decl, span },
define_opaque,
},
) => {
// Identifier and visibility are visited as a part of the item.
visit_defaultness(vis, defaultness);
@ -987,6 +994,11 @@ fn walk_fn<T: MutVisitor>(vis: &mut T, kind: FnKind<'_>) {
vis.visit_block(body);
}
vis.visit_span(span);
for (id, path) in define_opaque.iter_mut().flatten() {
vis.visit_id(id);
vis.visit_path(path)
}
}
FnKind::Closure(binder, coroutine_kind, decl, body) => {
vis.visit_closure_binder(binder);

View file

@ -892,7 +892,14 @@ pub fn walk_fn<'a, V: Visitor<'a>>(visitor: &mut V, kind: FnKind<'a>) -> V::Resu
_ctxt,
_ident,
_vis,
Fn { defaultness: _, sig: FnSig { header, decl, span: _ }, generics, contract, body },
Fn {
defaultness: _,
sig: FnSig { header, decl, span: _ },
generics,
contract,
body,
define_opaque,
},
) => {
// Identifier and visibility are visited as a part of the item.
try_visit!(visitor.visit_fn_header(header));
@ -900,6 +907,9 @@ pub fn walk_fn<'a, V: Visitor<'a>>(visitor: &mut V, kind: FnKind<'a>) -> V::Resu
try_visit!(visitor.visit_fn_decl(decl));
visit_opt!(visitor, visit_contract, contract);
visit_opt!(visitor, visit_block, body);
for (id, path) in define_opaque.iter().flatten() {
try_visit!(visitor.visit_path(path, *id))
}
}
FnKind::Closure(binder, coroutine_kind, decl, body) => {
try_visit!(visitor.visit_closure_binder(binder));
@ -1203,7 +1213,7 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) -> V
FnKind::Closure(binder, coroutine_kind, fn_decl, body),
*span,
*id
))
));
}
ExprKind::Block(block, opt_label) => {
visit_opt!(visitor, visit_label, opt_label);

View file

@ -3,10 +3,9 @@ use rustc_ast::ptr::P;
use rustc_ast::visit::AssocCtxt;
use rustc_ast::*;
use rustc_errors::ErrorGuaranteed;
use rustc_hir as hir;
use rustc_hir::PredicateOrigin;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::{CRATE_DEF_ID, LocalDefId};
use rustc_hir::{self as hir, HirId, PredicateOrigin};
use rustc_index::{IndexSlice, IndexVec};
use rustc_middle::span_bug;
use rustc_middle::ty::{ResolverAstLowering, TyCtxt};
@ -209,6 +208,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
generics,
body,
contract,
define_opaque,
..
}) => {
self.with_new_scopes(*fn_sig_span, |this| {
@ -237,6 +237,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
header: this.lower_fn_header(*header, hir::Safety::Safe, attrs),
span: this.lower_span(*fn_sig_span),
};
this.lower_define_opaque(hir_id, &define_opaque);
hir::ItemKind::Fn { sig, generics, body: body_id, has_body: body.is_some() }
})
}
@ -779,7 +780,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
);
(generics, kind, expr.is_some())
}
AssocItemKind::Fn(box Fn { sig, generics, body: None, .. }) => {
AssocItemKind::Fn(box Fn { sig, generics, body: None, define_opaque, .. }) => {
// FIXME(contracts): Deny contract here since it won't apply to
// any impl method or callees.
let names = self.lower_fn_params_to_names(&sig.decl);
@ -791,9 +792,22 @@ impl<'hir> LoweringContext<'_, 'hir> {
sig.header.coroutine_kind,
attrs,
);
if define_opaque.is_some() {
self.dcx().span_err(
i.span,
"only trait methods with default bodies can define opaque types",
);
}
(generics, hir::TraitItemKind::Fn(sig, hir::TraitFn::Required(names)), false)
}
AssocItemKind::Fn(box Fn { sig, generics, body: Some(body), contract, .. }) => {
AssocItemKind::Fn(box Fn {
sig,
generics,
body: Some(body),
contract,
define_opaque,
..
}) => {
let body_id = self.lower_maybe_coroutine_body(
sig.span,
i.span,
@ -812,6 +826,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
sig.header.coroutine_kind,
attrs,
);
self.lower_define_opaque(hir_id, &define_opaque);
(generics, hir::TraitItemKind::Fn(sig, hir::TraitFn::Provided(body_id)), true)
}
AssocItemKind::Type(box TyAlias { generics, where_clauses, bounds, ty, .. }) => {
@ -911,7 +926,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
hir::ImplItemKind::Const(ty, body)
},
),
AssocItemKind::Fn(box Fn { sig, generics, body, contract, .. }) => {
AssocItemKind::Fn(box Fn { sig, generics, body, contract, define_opaque, .. }) => {
let body_id = self.lower_maybe_coroutine_body(
sig.span,
i.span,
@ -930,6 +945,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
sig.header.coroutine_kind,
attrs,
);
self.lower_define_opaque(hir_id, &define_opaque);
(generics, hir::ImplItemKind::Fn(sig, body_id))
}
@ -1657,6 +1673,31 @@ impl<'hir> LoweringContext<'_, 'hir> {
(lowered_generics, res)
}
pub(super) fn lower_define_opaque(
&mut self,
hir_id: HirId,
define_opaque: &Option<ThinVec<(NodeId, Path)>>,
) {
assert_eq!(self.define_opaque, None);
assert!(hir_id.is_owner());
let Some(define_opaque) = define_opaque.as_ref() else {
return;
};
let define_opaque = define_opaque
.iter()
// TODO: error reporting for non-local items being mentioned and tests that go through these code paths
.map(|(id, _path)| {
self.resolver
.get_partial_res(*id)
.unwrap()
.expect_full_res()
.def_id()
.expect_local()
});
let define_opaque = self.arena.alloc_from_iter(define_opaque);
self.define_opaque = Some(define_opaque);
}
pub(super) fn lower_generic_bound_predicate(
&mut self,
ident: Ident,

View file

@ -35,6 +35,7 @@
#![doc(rust_logo)]
#![feature(assert_matches)]
#![feature(box_patterns)]
#![feature(exact_size_is_empty)]
#![feature(if_let_guard)]
#![feature(let_chains)]
#![feature(rustdoc_internals)]
@ -97,6 +98,8 @@ struct LoweringContext<'a, 'hir> {
/// Bodies inside the owner being lowered.
bodies: Vec<(hir::ItemLocalId, &'hir hir::Body<'hir>)>,
/// `#[define_opaque]` attributes
define_opaque: Option<&'hir [LocalDefId]>,
/// Attributes inside the owner being lowered.
attrs: SortedMap<hir::ItemLocalId, &'hir [hir::Attribute]>,
/// Collect items that were created by lowering the current owner.
@ -154,6 +157,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
// HirId handling.
bodies: Vec::new(),
define_opaque: None,
attrs: SortedMap::default(),
children: Vec::default(),
contract_ensures: None,
@ -546,6 +550,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
let current_attrs = std::mem::take(&mut self.attrs);
let current_bodies = std::mem::take(&mut self.bodies);
let current_define_opaque = std::mem::take(&mut self.define_opaque);
let current_ident_and_label_to_local_id =
std::mem::take(&mut self.ident_and_label_to_local_id);
@ -579,6 +584,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
self.attrs = current_attrs;
self.bodies = current_bodies;
self.define_opaque = current_define_opaque;
self.ident_and_label_to_local_id = current_ident_and_label_to_local_id;
#[cfg(debug_assertions)]
@ -598,6 +604,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
fn make_owner_info(&mut self, node: hir::OwnerNode<'hir>) -> &'hir hir::OwnerInfo<'hir> {
let attrs = std::mem::take(&mut self.attrs);
let mut bodies = std::mem::take(&mut self.bodies);
let define_opaque = std::mem::take(&mut self.define_opaque);
let trait_map = std::mem::take(&mut self.trait_map);
#[cfg(debug_assertions)]
@ -617,7 +624,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
let num_nodes = self.item_local_id_counter.as_usize();
let (nodes, parenting) = index::index_hir(self.tcx, node, &bodies, num_nodes);
let nodes = hir::OwnerNodes { opt_hash_including_bodies, nodes, bodies };
let attrs = hir::AttributeMap { map: attrs, opt_hash: attrs_hash };
let attrs = hir::AttributeMap { map: attrs, opt_hash: attrs_hash, define_opaque };
self.arena.alloc(hir::OwnerInfo { nodes, parenting, attrs, trait_map })
}

View file

@ -917,7 +917,10 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
walk_list!(self, visit_attribute, &item.attrs);
return; // Avoid visiting again.
}
ItemKind::Fn(func @ box Fn { defaultness, generics: _, sig, contract: _, body }) => {
ItemKind::Fn(
func
@ box Fn { defaultness, generics: _, sig, contract: _, body, define_opaque: _ },
) => {
self.check_defaultness(item.span, *defaultness);
let is_intrinsic =

View file

@ -24,7 +24,6 @@ use rustc_span::edition::Edition;
use rustc_span::source_map::{SourceMap, Spanned};
use rustc_span::symbol::IdentPrinter;
use rustc_span::{BytePos, CharPos, DUMMY_SP, FileName, Ident, Pos, Span, Symbol, kw, sym};
use thin_vec::ThinVec;
use crate::pp::Breaks::{Consistent, Inconsistent};
use crate::pp::{self, Breaks};
@ -1978,15 +1977,7 @@ impl<'a> State<'a> {
) {
self.ibox(INDENT_UNIT);
self.print_formal_generic_params(generic_params);
let generics = ast::Generics {
params: ThinVec::new(),
where_clause: ast::WhereClause {
has_where_token: false,
predicates: ThinVec::new(),
span: DUMMY_SP,
},
span: DUMMY_SP,
};
let generics = ast::Generics::default();
let header = ast::FnHeader { safety, ext, ..ast::FnHeader::default() };
self.print_fn(decl, header, name, &generics);
self.end();

View file

@ -650,7 +650,16 @@ impl<'a> State<'a> {
attrs: &[ast::Attribute],
func: &ast::Fn,
) {
let ast::Fn { defaultness, generics, sig, contract, body } = func;
let ast::Fn { defaultness, generics, sig, contract, body, define_opaque } = func;
if let Some(define_opaque) = define_opaque {
for (_, path) in define_opaque {
self.word("define opaques from ");
self.print_path(path, false, 0);
self.word(",");
}
}
if body.is_some() {
self.head("");
}
@ -698,7 +707,7 @@ impl<'a> State<'a> {
}
self.print_generic_params(&generics.params);
self.print_fn_params_and_ret(decl, false);
self.print_where_clause(&generics.where_clause)
self.print_where_clause(&generics.where_clause);
}
pub(crate) fn print_fn_params_and_ret(&mut self, decl: &ast::FnDecl, is_closure: bool) {

View file

@ -180,6 +180,7 @@ pub(super) fn take_opaques_and_register_member_constraints<'tcx>(
/// // Equivalent to:
/// # mod dummy { use super::*;
/// type FooReturn<'a, T> = impl Foo<'a>;
/// #[define_opaque(FooReturn)]
/// fn foo<'a, T>(x: &'a u32, y: T) -> FooReturn<'a, T> {
/// (x, y)
/// }

View file

@ -88,6 +88,7 @@ fn generate_handler(cx: &ExtCtxt<'_>, handler: Ident, span: Span, sig_span: Span
generics: Generics::default(),
contract: None,
body,
define_opaque: None,
}));
let attrs = thin_vec![cx.attr_word(sym::rustc_std_internal_symbol, span)];

View file

@ -247,6 +247,7 @@ mod llvm_enzyme {
generics: Generics::default(),
contract: None,
body: Some(d_body),
define_opaque: None,
});
let mut rustc_ad_attr =
P(ast::NormalAttr::from_ident(Ident::with_dummy_span(sym::rustc_autodiff)));

View file

@ -0,0 +1,54 @@
use rustc_ast::{DUMMY_NODE_ID, ast};
use rustc_expand::base::{Annotatable, ExtCtxt};
use rustc_span::Span;
pub(crate) fn expand(
ecx: &mut ExtCtxt<'_>,
_expand_span: Span,
meta_item: &ast::MetaItem,
mut item: Annotatable,
) -> Vec<Annotatable> {
let define_opaque = match &mut item {
Annotatable::Item(p) => match &mut p.kind {
ast::ItemKind::Fn(f) => Some(&mut f.define_opaque),
_ => None,
},
Annotatable::AssocItem(i, _assoc_ctxt) => match &mut i.kind {
ast::AssocItemKind::Fn(func) => Some(&mut func.define_opaque),
_ => None,
},
Annotatable::Stmt(s) => match &mut s.kind {
ast::StmtKind::Item(p) => match &mut p.kind {
ast::ItemKind::Fn(f) => Some(&mut f.define_opaque),
_ => None,
},
_ => None,
},
_ => None,
};
let Some(list) = meta_item.meta_item_list() else {
ecx.dcx().span_err(meta_item.span, "expected list of type aliases");
return vec![item];
};
if let Some(define_opaque) = define_opaque {
*define_opaque = Some(
list.iter()
.filter_map(|entry| match entry {
ast::MetaItemInner::MetaItem(meta_item) if meta_item.is_word() => {
Some((DUMMY_NODE_ID, meta_item.path.clone()))
}
_ => {
ecx.dcx().span_err(entry.span(), "expected path to type alias");
None
}
})
.collect(),
);
} else {
ecx.dcx().span_err(meta_item.span, "only functions and methods can define opaque types");
}
vec![item]
}

View file

@ -1040,6 +1040,7 @@ impl<'a> MethodDef<'a> {
generics: fn_generics,
contract: None,
body: Some(body_block),
define_opaque: None,
})),
tokens: None,
})

View file

@ -83,6 +83,7 @@ impl AllocFnFactory<'_, '_> {
generics: Generics::default(),
contract: None,
body,
define_opaque: None,
}));
let item = self.cx.item(
self.span,

View file

@ -38,6 +38,7 @@ mod compile_error;
mod concat;
mod concat_bytes;
mod concat_idents;
mod define_opaque;
mod derive;
mod deriving;
mod edition_panic;
@ -113,6 +114,7 @@ pub fn register_builtin_macros(resolver: &mut dyn ResolverExpand) {
bench: test::expand_bench,
cfg_accessible: cfg_accessible::Expander,
cfg_eval: cfg_eval::expand,
define_opaque: define_opaque::expand,
derive: derive::Expander { is_const: false },
derive_const: derive::Expander { is_const: true },
global_allocator: global_allocator::expand,

View file

@ -346,6 +346,7 @@ fn mk_main(cx: &mut TestCtxt<'_>) -> P<ast::Item> {
generics: ast::Generics::default(),
contract: None,
body: Some(main_body),
define_opaque: None,
}));
// Honor the reexport_test_harness_main attribute

View file

@ -2,23 +2,21 @@
#![feature(type_alias_impl_trait)]
mod helper {
pub trait T {
type Item;
}
pub type Alias<'a> = impl T<Item = &'a ()>;
struct S;
impl<'a> T for &'a S {
type Item = &'a ();
}
pub fn filter_positive<'a>() -> Alias<'a> {
&S
}
pub trait T {
type Item;
}
pub type Alias<'a> = impl T<Item = &'a ()>;
struct S;
impl<'a> T for &'a S {
type Item = &'a ();
}
#[define_opaque(Alias)]
pub fn filter_positive<'a>() -> Alias<'a> {
&S
}
use helper::*;
fn with_positive(fun: impl Fn(Alias<'_>)) {
fun(filter_positive());

View file

@ -315,6 +315,7 @@ mod helper {
use super::*;
pub(super) type ObligationTreeIdGenerator = impl Iterator<Item = ObligationTreeId>;
impl<O: ForestObligation> ObligationForest<O> {
#[cfg_attr(not(bootstrap), define_opaque(ObligationTreeIdGenerator))]
pub fn new() -> ObligationForest<O> {
ObligationForest {
nodes: vec![],

View file

@ -7,6 +7,7 @@ This means
type Foo<T> = impl std::fmt::Debug;
#[define_opaque(Foo)]
fn foo() -> Foo<u32> {
5u32
}
@ -19,6 +20,7 @@ is not accepted. If it were accepted, one could create unsound situations like
type Foo<T> = impl Default;
#[define_opaque(Foo)]
fn foo() -> Foo<u32> {
5u32
}
@ -36,6 +38,7 @@ Instead you need to make the function generic:
type Foo<T> = impl std::fmt::Debug;
#[define_opaque(Foo)]
fn foo<U>() -> Foo<U> {
5u32
}
@ -56,6 +59,7 @@ use std::fmt::Debug;
type Foo<T: Debug> = impl Debug;
#[define_opaque(Foo)]
fn foo<U: Debug>() -> Foo<U> {
Vec::<U>::new()
}

View file

@ -208,6 +208,7 @@ pub type LazyFallbackBundle = Arc<LazyLock<FluentBundle, impl FnOnce() -> Fluent
/// Return the default `FluentBundle` with standard "en-US" diagnostic messages.
#[instrument(level = "trace", skip(resources))]
#[cfg_attr(not(bootstrap), define_opaque(LazyFallbackBundle))]
pub fn fallback_fluent_bundle(
resources: Vec<&'static str>,
with_directionality_markers: bool,

View file

@ -1307,13 +1307,18 @@ impl Attribute {
#[derive(Debug)]
pub struct AttributeMap<'tcx> {
pub map: SortedMap<ItemLocalId, &'tcx [Attribute]>,
/// Preprocessed `#[define_opaque]` attribute.
pub define_opaque: Option<&'tcx [LocalDefId]>,
// Only present when the crate hash is needed.
pub opt_hash: Option<Fingerprint>,
}
impl<'tcx> AttributeMap<'tcx> {
pub const EMPTY: &'static AttributeMap<'static> =
&AttributeMap { map: SortedMap::new(), opt_hash: Some(Fingerprint::ZERO) };
pub const EMPTY: &'static AttributeMap<'static> = &AttributeMap {
map: SortedMap::new(),
opt_hash: Some(Fingerprint::ZERO),
define_opaque: None,
};
#[inline]
pub fn get(&self, id: ItemLocalId) -> &'tcx [Attribute] {

View file

@ -106,7 +106,7 @@ impl<'tcx, HirCtx: crate::HashStableContext> HashStable<HirCtx> for AttributeMap
fn hash_stable(&self, hcx: &mut HirCtx, hasher: &mut StableHasher) {
// We ignore the `map` since it refers to information included in `opt_hash` which is
// hashed in the collector and used for the crate hash.
let AttributeMap { opt_hash, map: _ } = *self;
let AttributeMap { opt_hash, define_opaque: _, map: _ } = *self;
opt_hash.unwrap().hash_stable(hcx, hasher);
}
}

View file

@ -504,12 +504,9 @@ hir_analysis_supertrait_item_shadowee = item from `{$supertrait}` is shadowed by
hir_analysis_supertrait_item_shadowing = trait item `{$item}` from `{$subtrait}` shadows identically named item from supertrait
hir_analysis_tait_forward_compat = item constrains opaque type that is not in its signature
.note = this item must mention the opaque type in its signature in order to be able to register hidden types
hir_analysis_tait_forward_compat2 = item does not constrain `{$opaque_type}`, but has it in its signature
.note = consider moving the opaque type's declaration and defining uses into a separate module
.opaque = this opaque type is in the signature
hir_analysis_tait_forward_compat2 = item does not constrain `{$opaque_type}`
.note = consider removing `#[define_opaque]` or adding an empty `#[define_opaque()]`
.opaque = this opaque type is supposed to be constrained
hir_analysis_target_feature_on_main = `main` function is not allowed to have `#[target_feature]`

View file

@ -392,6 +392,12 @@ fn best_definition_site_of_opaque<'tcx>(
return ControlFlow::Continue(());
}
let opaque_types_defined_by = self.tcx.opaque_types_defined_by(item_def_id);
// Don't try to check items that cannot possibly constrain the type.
if !opaque_types_defined_by.contains(&self.opaque_def_id) {
return ControlFlow::Continue(());
}
if let Some(hidden_ty) =
self.tcx.mir_borrowck(item_def_id).concrete_opaque_types.get(&self.opaque_def_id)
{
@ -451,19 +457,7 @@ fn best_definition_site_of_opaque<'tcx>(
None
}
hir::OpaqueTyOrigin::TyAlias { in_assoc_ty: false, .. } => {
let scope = tcx.hir_get_defining_scope(tcx.local_def_id_to_hir_id(opaque_def_id));
let found = if scope == hir::CRATE_HIR_ID {
tcx.hir_walk_toplevel_module(&mut locator)
} else {
match tcx.hir_node(scope) {
Node::Item(it) => locator.visit_item(it),
Node::ImplItem(it) => locator.visit_impl_item(it),
Node::TraitItem(it) => locator.visit_trait_item(it),
Node::ForeignItem(it) => locator.visit_foreign_item(it),
other => bug!("{:?} is not a valid scope for an opaque type item", other),
}
};
found.break_value()
tcx.hir_walk_toplevel_module(&mut locator).break_value()
}
}
}

View file

@ -1,14 +1,13 @@
use rustc_hir::def::DefKind;
use rustc_hir::def_id::LocalDefId;
use rustc_hir::intravisit::{self, Visitor};
use rustc_hir::{self as hir, Expr, ImplItem, Item, Node, TraitItem, def};
use rustc_hir::{self as hir, Expr, ImplItem, Item, Node, TraitItem, def, intravisit};
use rustc_middle::bug;
use rustc_middle::hir::nested_filter;
use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt};
use rustc_span::DUMMY_SP;
use tracing::{debug, instrument, trace};
use crate::errors::{TaitForwardCompat, TaitForwardCompat2, UnconstrainedOpaqueType};
use crate::errors::{TaitForwardCompat2, UnconstrainedOpaqueType};
/// Checks "defining uses" of opaque `impl Trait` in associated types.
/// These can only be defined by associated items of the same trait.
@ -82,38 +81,9 @@ pub(super) fn find_opaque_ty_constraints_for_impl_trait_in_assoc_type(
/// ```
#[instrument(skip(tcx), level = "debug")]
pub(super) fn find_opaque_ty_constraints_for_tait(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Ty<'_> {
let hir_id = tcx.local_def_id_to_hir_id(def_id);
let scope = tcx.hir_get_defining_scope(hir_id);
let mut locator = TaitConstraintLocator { def_id, tcx, found: None, typeck_types: vec![] };
debug!(?scope);
if scope == hir::CRATE_HIR_ID {
tcx.hir_walk_toplevel_module(&mut locator);
} else {
trace!("scope={:#?}", tcx.hir_node(scope));
match tcx.hir_node(scope) {
// We explicitly call `visit_*` methods, instead of using `intravisit::walk_*` methods
// This allows our visitor to process the defining item itself, causing
// it to pick up any 'sibling' defining uses.
//
// For example, this code:
// ```
// fn foo() {
// type Blah = impl Debug;
// let my_closure = || -> Blah { true };
// }
// ```
//
// requires us to explicitly process `foo()` in order
// to notice the defining usage of `Blah`.
Node::Item(it) => locator.visit_item(it),
Node::ImplItem(it) => locator.visit_impl_item(it),
Node::TraitItem(it) => locator.visit_trait_item(it),
Node::ForeignItem(it) => locator.visit_foreign_item(it),
other => bug!("{:?} is not a valid scope for an opaque type item", other),
}
}
tcx.hir_walk_toplevel_module(&mut locator);
if let Some(hidden) = locator.found {
// Only check against typeck if we didn't already error
@ -137,12 +107,7 @@ pub(super) fn find_opaque_ty_constraints_for_tait(tcx: TyCtxt<'_>, def_id: Local
let reported = tcx.dcx().emit_err(UnconstrainedOpaqueType {
span: tcx.def_span(def_id),
name: tcx.item_ident(parent_def_id.to_def_id()),
what: match tcx.hir_node(scope) {
_ if scope == hir::CRATE_HIR_ID => "module",
Node::Item(hir::Item { kind: hir::ItemKind::Mod(_), .. }) => "module",
Node::Item(hir::Item { kind: hir::ItemKind::Impl(_), .. }) => "impl",
_ => "item",
},
what: "crate",
});
Ty::new_error(tcx, reported)
}
@ -176,6 +141,13 @@ impl TaitConstraintLocator<'_> {
return;
}
let opaque_types_defined_by = self.tcx.opaque_types_defined_by(item_def_id);
// Don't try to check items that cannot possibly constrain the type.
if !opaque_types_defined_by.contains(&self.def_id) {
debug!("no constraint: no opaque types defined");
return;
}
// Function items with `_` in their return type already emit an error, skip any
// "non-defining use" errors for them.
// Note that we use `Node::fn_sig` instead of `Node::fn_decl` here, because the former
@ -215,8 +187,6 @@ impl TaitConstraintLocator<'_> {
return;
}
let opaque_types_defined_by = self.tcx.opaque_types_defined_by(item_def_id);
let mut constrained = false;
for (&opaque_type_key, &hidden_type) in &tables.concrete_opaque_types {
if opaque_type_key.def_id != self.def_id {
@ -224,20 +194,6 @@ impl TaitConstraintLocator<'_> {
}
constrained = true;
if !opaque_types_defined_by.contains(&self.def_id) {
let guar = self.tcx.dcx().emit_err(TaitForwardCompat {
span: hidden_type.span,
item_span: self
.tcx
.def_ident_span(item_def_id)
.unwrap_or_else(|| self.tcx.def_span(item_def_id)),
});
// Avoid "opaque type not constrained" errors on the opaque itself.
self.found = Some(ty::OpaqueHiddenType {
span: DUMMY_SP,
ty: Ty::new_error(self.tcx, guar),
});
}
let concrete_type =
self.tcx.erase_regions(hidden_type.remap_generic_params_to_declaration_params(
opaque_type_key,
@ -309,19 +265,13 @@ impl<'tcx> intravisit::Visitor<'tcx> for TaitConstraintLocator<'tcx> {
}
fn visit_item(&mut self, it: &'tcx Item<'tcx>) {
trace!(?it.owner_id);
// The opaque type itself or its children are not within its reveal scope.
if it.owner_id.def_id != self.def_id {
self.check(it.owner_id.def_id);
intravisit::walk_item(self, it);
}
self.check(it.owner_id.def_id);
intravisit::walk_item(self, it);
}
fn visit_impl_item(&mut self, it: &'tcx ImplItem<'tcx>) {
trace!(?it.owner_id);
// The opaque type itself or its children are not within its reveal scope.
if it.owner_id.def_id != self.def_id {
self.check(it.owner_id.def_id);
intravisit::walk_impl_item(self, it);
}
self.check(it.owner_id.def_id);
intravisit::walk_impl_item(self, it);
}
fn visit_trait_item(&mut self, it: &'tcx TraitItem<'tcx>) {
trace!(?it.owner_id);

View file

@ -424,16 +424,6 @@ pub(crate) struct UnconstrainedOpaqueType {
pub what: &'static str,
}
#[derive(Diagnostic)]
#[diag(hir_analysis_tait_forward_compat)]
#[note]
pub(crate) struct TaitForwardCompat {
#[primary_span]
pub span: Span,
#[note]
pub item_span: Span,
}
#[derive(Diagnostic)]
#[diag(hir_analysis_tait_forward_compat2)]
#[note]

View file

@ -43,6 +43,7 @@ declare_lint! {
///
/// type Tait = impl Sized;
///
/// #[define_opaque(Tait)]
/// fn test() -> impl Trait<Assoc = Tait> {
/// 42
/// }

View file

@ -454,6 +454,7 @@ mod helper {
/// Like [`SwitchTargets::target_for_value`], but returning the same type as
/// [`Terminator::successors`].
#[inline]
#[cfg_attr(not(bootstrap), define_opaque(Successors))]
pub fn successors_for_value(&self, value: u128) -> Successors<'_> {
let target = self.target_for_value(value);
(&[]).into_iter().copied().chain(Some(target))
@ -462,6 +463,7 @@ mod helper {
impl<'tcx> TerminatorKind<'tcx> {
#[inline]
#[cfg_attr(not(bootstrap), define_opaque(Successors))]
pub fn successors(&self) -> Successors<'_> {
use self::TerminatorKind::*;
match *self {
@ -500,6 +502,7 @@ mod helper {
}
#[inline]
#[cfg_attr(not(bootstrap), define_opaque(SuccessorsMut))]
pub fn successors_mut(&mut self) -> SuccessorsMut<'_> {
use self::TerminatorKind::*;
match *self {

View file

@ -24,6 +24,7 @@ pub trait EraseType: Copy {
pub type Erase<T: EraseType> = Erased<impl Copy>;
#[inline(always)]
#[cfg_attr(not(bootstrap), define_opaque(Erase))]
pub fn erase<T: EraseType>(src: T) -> Erase<T> {
// Ensure the sizes match
const {
@ -47,6 +48,7 @@ pub fn erase<T: EraseType>(src: T) -> Erase<T> {
/// Restores an erased value.
#[inline(always)]
#[cfg_attr(not(bootstrap), define_opaque(Erase))]
pub fn restore<T: EraseType>(value: Erase<T>) -> T {
let value: Erased<<T as EraseType>::Result> = value;
// See comment in `erase` for why we use `transmute_unchecked`.

View file

@ -217,7 +217,14 @@ impl<'a> Parser<'a> {
self.parse_fn(attrs, fn_parse_mode, lo, vis, case)?;
(
ident,
ItemKind::Fn(Box::new(Fn { defaultness: def_(), sig, generics, contract, body })),
ItemKind::Fn(Box::new(Fn {
defaultness: def_(),
sig,
generics,
contract,
body,
define_opaque: None,
})),
)
} else if self.eat_keyword(exp!(Extern)) {
if self.eat_keyword(exp!(Crate)) {

View file

@ -397,32 +397,37 @@ pub(crate) enum AliasPossibility {
#[derive(Copy, Clone, Debug)]
pub(crate) enum PathSource<'a> {
// Type paths `Path`.
/// Type paths `Path`.
Type,
// Trait paths in bounds or impls.
/// Trait paths in bounds or impls.
Trait(AliasPossibility),
// Expression paths `path`, with optional parent context.
/// Expression paths `path`, with optional parent context.
Expr(Option<&'a Expr>),
// Paths in path patterns `Path`.
/// Paths in path patterns `Path`.
Pat,
// Paths in struct expressions and patterns `Path { .. }`.
/// Paths in struct expressions and patterns `Path { .. }`.
Struct,
// Paths in tuple struct patterns `Path(..)`.
/// Paths in tuple struct patterns `Path(..)`.
TupleStruct(Span, &'a [Span]),
// `m::A::B` in `<T as m::A>::B::C`.
/// `m::A::B` in `<T as m::A>::B::C`.
TraitItem(Namespace),
// Paths in delegation item
/// Paths in delegation item
Delegation,
/// An arg in a `use<'a, N>` precise-capturing bound.
PreciseCapturingArg(Namespace),
// Paths that end with `(..)`, for return type notation.
/// Paths that end with `(..)`, for return type notation.
ReturnTypeNotation,
/// Paths from `#[define_opaque]` attributes
DefineOpaques,
}
impl<'a> PathSource<'a> {
fn namespace(self) -> Namespace {
match self {
PathSource::Type | PathSource::Trait(_) | PathSource::Struct => TypeNS,
PathSource::Type
| PathSource::Trait(_)
| PathSource::Struct
| PathSource::DefineOpaques => TypeNS,
PathSource::Expr(..)
| PathSource::Pat
| PathSource::TupleStruct(..)
@ -443,6 +448,7 @@ impl<'a> PathSource<'a> {
| PathSource::ReturnTypeNotation => true,
PathSource::Trait(_)
| PathSource::TraitItem(..)
| PathSource::DefineOpaques
| PathSource::Delegation
| PathSource::PreciseCapturingArg(..) => false,
}
@ -450,6 +456,7 @@ impl<'a> PathSource<'a> {
fn descr_expected(self) -> &'static str {
match &self {
PathSource::DefineOpaques => "type alias or associated type with opaqaue types",
PathSource::Type => "type",
PathSource::Trait(_) => "trait",
PathSource::Pat => "unit struct, unit variant or constant",
@ -493,6 +500,19 @@ impl<'a> PathSource<'a> {
pub(crate) fn is_expected(self, res: Res) -> bool {
match self {
PathSource::DefineOpaques => {
matches!(
res,
Res::Def(
DefKind::Struct
| DefKind::Union
| DefKind::Enum
| DefKind::TyAlias
| DefKind::AssocTy,
_
) | Res::SelfTyAlias { .. }
)
}
PathSource::Type => matches!(
res,
Res::Def(
@ -572,16 +592,16 @@ impl<'a> PathSource<'a> {
match (self, has_unexpected_resolution) {
(PathSource::Trait(_), true) => E0404,
(PathSource::Trait(_), false) => E0405,
(PathSource::Type, true) => E0573,
(PathSource::Type, false) => E0412,
(PathSource::Type | PathSource::DefineOpaques, true) => E0573,
(PathSource::Type | PathSource::DefineOpaques, false) => E0412,
(PathSource::Struct, true) => E0574,
(PathSource::Struct, false) => E0422,
(PathSource::Expr(..), true) | (PathSource::Delegation, true) => E0423,
(PathSource::Expr(..), false) | (PathSource::Delegation, false) => E0425,
(PathSource::Pat | PathSource::TupleStruct(..), true) => E0532,
(PathSource::Pat | PathSource::TupleStruct(..), false) => E0531,
(PathSource::TraitItem(..), true) | (PathSource::ReturnTypeNotation, true) => E0575,
(PathSource::TraitItem(..), false) | (PathSource::ReturnTypeNotation, false) => E0576,
(PathSource::TraitItem(..) | PathSource::ReturnTypeNotation, true) => E0575,
(PathSource::TraitItem(..) | PathSource::ReturnTypeNotation, false) => E0576,
(PathSource::PreciseCapturingArg(..), true) => E0799,
(PathSource::PreciseCapturingArg(..), false) => E0800,
}
@ -2006,6 +2026,7 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
| PathSource::Pat
| PathSource::Struct
| PathSource::TupleStruct(..)
| PathSource::DefineOpaques
| PathSource::Delegation => true,
};
if inferred {
@ -2619,7 +2640,7 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
);
}
ItemKind::Fn(box Fn { ref generics, .. }) => {
ItemKind::Fn(box Fn { ref generics, ref define_opaque, .. }) => {
self.with_generic_param_rib(
&generics.params,
RibKind::Item(HasGenericParams::Yes(generics.span), def_kind),
@ -2630,6 +2651,10 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
},
|this| visit::walk_item(this, item),
);
for (id, path) in define_opaque.iter().flatten() {
self.smart_resolve_path(*id, &None, path, PathSource::DefineOpaques);
}
}
ItemKind::Enum(_, ref generics)
@ -3100,8 +3125,12 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
},
);
}
AssocItemKind::Fn(box Fn { generics, .. }) => {
AssocItemKind::Fn(box Fn { generics, define_opaque, .. }) => {
walk_assoc_item(self, generics, LifetimeBinderKind::Function, item);
for (id, path) in define_opaque.iter().flatten() {
self.smart_resolve_path(*id, &None, path, PathSource::DefineOpaques);
}
}
AssocItemKind::Delegation(delegation) => {
self.with_generic_param_rib(
@ -3311,7 +3340,7 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
},
);
}
AssocItemKind::Fn(box Fn { generics, .. }) => {
AssocItemKind::Fn(box Fn { generics, define_opaque, .. }) => {
debug!("resolve_implementation AssocItemKind::Fn");
// We also need a new scope for the impl item type parameters.
self.with_generic_param_rib(
@ -3338,6 +3367,10 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
visit::walk_assoc_item(this, item, AssocCtxt::Impl)
},
);
for (id, path) in define_opaque.iter().flatten() {
self.smart_resolve_path(*id, &None, path, PathSource::DefineOpaques);
}
}
AssocItemKind::Type(box TyAlias { generics, .. }) => {
self.diag_metadata.in_non_gat_assoc_type = Some(generics.params.is_empty());

View file

@ -779,6 +779,7 @@ symbols! {
default_method_body_is_const,
default_type_parameter_fallback,
default_type_params,
define_opaque,
delayed_bug_from_inside_query,
deny,
deprecated,

View file

@ -395,11 +395,28 @@ impl<T> Trait<T> for X {
let sp = tcx
.def_ident_span(body_owner_def_id)
.unwrap_or_else(|| tcx.def_span(body_owner_def_id));
diag.span_note(
sp,
"this item must have the opaque type in its signature in order to \
be able to register hidden types",
);
let mut alias_def_id = opaque_ty.def_id;
while let DefKind::OpaqueTy = tcx.def_kind(alias_def_id) {
alias_def_id = tcx.parent(alias_def_id);
}
let opaque_path = tcx.def_path_str(alias_def_id);
// FIXME(type_alias_impl_trait): make this a structured suggestion
match tcx.opaque_ty_origin(opaque_ty.def_id) {
rustc_hir::OpaqueTyOrigin::FnReturn { .. } => {}
rustc_hir::OpaqueTyOrigin::AsyncFn { .. } => {}
rustc_hir::OpaqueTyOrigin::TyAlias {
in_assoc_ty: false, ..
} => {
diag.span_note(
sp,
format!("this item must have a `#[define_opaque({opaque_path})]` \
attribute to be able to define hidden types"),
);
}
rustc_hir::OpaqueTyOrigin::TyAlias {
in_assoc_ty: true, ..
} => {}
}
}
// If two if arms can be coerced to a trait object, provide a structured
// suggestion.

View file

@ -7,6 +7,7 @@ edition = "2024"
# tidy-alphabetical-start
itertools = "0.12"
rustc_abi = { path = "../rustc_abi" }
rustc_attr_parsing = { path = "../rustc_attr_parsing" }
rustc_data_structures = { path = "../rustc_data_structures" }
rustc_errors = { path = "../rustc_errors" }
rustc_fluent_macro = { path = "../rustc_fluent_macro" }

View file

@ -1,8 +1,8 @@
use rustc_data_structures::fx::FxHashSet;
use rustc_hir::def::DefKind;
use rustc_hir::def_id::LocalDefId;
use rustc_hir::intravisit;
use rustc_hir::intravisit::Visitor;
use rustc_hir::{CRATE_HIR_ID, intravisit};
use rustc_middle::bug;
use rustc_middle::query::Providers;
use rustc_middle::ty::util::{CheckRegions, NotUniqueParam};
@ -30,14 +30,21 @@ enum CollectionMode {
/// For impl trait in assoc types we only permit collecting them from
/// associated types of the same impl block.
ImplTraitInAssocTypes,
TypeAliasImplTraitTransition,
/// When collecting for an explicit `#[define_opaque]` attribute, find all TAITs
Taits,
/// The default case, only collect RPITs and AsyncFn return types, as these are
/// always defined by the current item.
RpitAndAsyncFnOnly,
}
impl<'tcx> OpaqueTypeCollector<'tcx> {
fn new(tcx: TyCtxt<'tcx>, item: LocalDefId) -> Self {
let mode = match tcx.def_kind(tcx.local_parent(item)) {
DefKind::Impl { of_trait: true } => CollectionMode::ImplTraitInAssocTypes,
_ => CollectionMode::TypeAliasImplTraitTransition,
let mode = match tcx.def_kind(item) {
DefKind::AssocConst | DefKind::AssocFn | DefKind::AssocTy => {
CollectionMode::ImplTraitInAssocTypes
}
DefKind::TyAlias => CollectionMode::Taits,
_ => CollectionMode::RpitAndAsyncFnOnly,
};
Self { tcx, opaques: Vec::new(), item, seen: Default::default(), span: None, mode }
}
@ -73,40 +80,6 @@ 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.local_def_id_to_hir_id(self.item);
let opaque_hir_id = self.tcx.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_owned_by(self.item).value;
@ -139,18 +112,31 @@ impl<'tcx> OpaqueTypeCollector<'tcx> {
}
// TAITs outside their defining scopes are ignored.
let origin = self.tcx.local_opaque_ty_origin(alias_ty.def_id.expect_local());
trace!(?origin);
match origin {
match self.tcx.local_opaque_ty_origin(alias_ty.def_id.expect_local()) {
rustc_hir::OpaqueTyOrigin::FnReturn { .. }
| rustc_hir::OpaqueTyOrigin::AsyncFn { .. } => {}
rustc_hir::OpaqueTyOrigin::TyAlias { in_assoc_ty, .. } => {
if !in_assoc_ty && !self.check_tait_defining_scope(alias_ty.def_id.expect_local()) {
return;
rustc_hir::OpaqueTyOrigin::TyAlias { in_assoc_ty, .. } => match self.mode {
// If we are collecting opaques in an assoc method, we are only looking at assoc types
// mentioned in the assoc method and only at opaques defined in there. We do not
// want to collect TAITs
CollectionMode::ImplTraitInAssocTypes => {
if !in_assoc_ty {
return;
}
}
}
// If we are collecting opaques referenced from a `define_opaque` attribute, we
// do not want to look at opaques defined in associated types. Those can only be
// defined by methods on the same impl.
CollectionMode::Taits => {
if in_assoc_ty {
return;
}
}
CollectionMode::RpitAndAsyncFnOnly => return,
},
}
trace!(?alias_ty, "adding");
self.opaques.push(alias_ty.def_id.expect_local());
let parent_count = self.tcx.generics_of(alias_ty.def_id).parent_count;
@ -192,6 +178,29 @@ impl<'tcx> OpaqueTypeCollector<'tcx> {
}
}
}
/// Checks the `#[define_opaque]` attributes on items and collectes opaques to define
/// from the referenced types.
#[instrument(level = "trace", skip(self))]
fn collect_taits_from_defines_attr(&mut self) {
let hir_id = self.tcx.local_def_id_to_hir_id(self.item);
if !hir_id.is_owner() {
return;
}
let Some(defines) = self.tcx.hir_attrs(hir_id.owner).define_opaque else {
return;
};
for &define in defines {
trace!(?define);
let mode = std::mem::replace(&mut self.mode, CollectionMode::Taits);
// TODO: check that opaque types were introduced and error otherwise (also add tests)
super::sig_types::walk_types(self.tcx, define, self);
self.mode = mode;
}
// Allow using `#[define_opaque]` on assoc methods and type aliases to override the default collection mode in
// case it was capturing too much.
self.mode = CollectionMode::RpitAndAsyncFnOnly;
}
}
impl<'tcx> super::sig_types::SpannedTypeVisitor<'tcx> for OpaqueTypeCollector<'tcx> {
@ -210,6 +219,7 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for OpaqueTypeCollector<'tcx> {
self.visit_opaque_ty(alias_ty);
}
// Skips type aliases, as they are meant to be transparent.
// FIXME(type_alias_impl_trait): can we require mentioning nested type aliases explicitly?
ty::Alias(ty::Weak, alias_ty) if alias_ty.def_id.is_local() => {
self.tcx
.type_of(alias_ty.def_id)
@ -283,28 +293,6 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for OpaqueTypeCollector<'tcx> {
self.visit_opaque_ty(alias_ty);
}
}
ty::Adt(def, _) if def.did().is_local() => {
if let CollectionMode::ImplTraitInAssocTypes = self.mode {
return;
}
if !self.seen.insert(def.did().expect_local()) {
return;
}
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 uninstantiated fields
// The only other situation that can occur is that after instantiating,
// some projection resolves to an opaque that we would have otherwise
// not found. While we could instantiate and walk those, that would mean we
// would have to walk all generic parameters 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()),
}
}
@ -317,7 +305,9 @@ fn opaque_types_defined_by<'tcx>(
let kind = tcx.def_kind(item);
trace!(?kind);
let mut collector = OpaqueTypeCollector::new(tcx, item);
collector.collect_taits_from_defines_attr();
super::sig_types::walk_types(tcx, item, &mut collector);
match kind {
DefKind::AssocFn
| DefKind::Fn
@ -350,8 +340,7 @@ fn opaque_types_defined_by<'tcx>(
| DefKind::GlobalAsm
| DefKind::Impl { .. }
| DefKind::SyntheticCoroutineBody => {}
// 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.
// Closures and coroutines are type checked with their parent
DefKind::Closure | DefKind::InlineConst => {
collector.opaques.extend(tcx.opaque_types_defined_by(tcx.local_parent(item)));
}