1
Fork 0

Auto merge of #128440 - oli-obk:defines, r=lcnr

Add `#[define_opaques]` attribute and require it for all type-alias-impl-trait sites that register a hidden type

Instead of relying on the signature of items to decide whether they are constraining an opaque type, the opaque types that the item constrains must be explicitly listed.

A previous version of this PR used an actual attribute, but had to keep the resolved `DefId`s in a side table.

Now we just lower to fields in the AST that have no surface syntax, instead a builtin attribute macro fills in those fields where applicable.

Note that for convenience referencing opaque types in associated types from associated methods on the same impl will not require an attribute. If that causes problems `#[defines()]` can be used to overwrite the default of searching for opaques in the signature.

One wart of this design is that closures and static items do not have generics. So since I stored the opaques in the generics of functions, consts and methods, I would need to add a custom field to closures and statics to track this information. During a T-types discussion we decided to just not do this for now.

fixes #131298
This commit is contained in:
bors 2025-03-11 18:13:31 +00:00
commit 6650252439
666 changed files with 3025 additions and 2582 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,35 @@ 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().filter_map(|(id, path)| {
let res = self.resolver.get_partial_res(*id).unwrap();
let Some(did) = res.expect_full_res().opt_def_id() else {
self.dcx().span_delayed_bug(path.span, "should have errored in resolve");
return None;
};
let Some(did) = did.as_local() else {
self.dcx().span_err(
path.span,
"only opaque types defined in the local crate can be defined",
);
return None;
};
Some((self.lower_span(path.span), did))
});
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)]
@ -98,6 +99,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 [(Span, LocalDefId)]>,
/// Attributes inside the owner being lowered.
attrs: SortedMap<hir::ItemLocalId, &'hir [hir::Attribute]>,
/// Collect items that were created by lowering the current owner.
@ -155,6 +158,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
// HirId handling.
bodies: Vec::new(),
define_opaque: None,
attrs: SortedMap::default(),
children: Vec::default(),
contract_ensures: None,
@ -547,6 +551,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);
@ -580,6 +585,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)]
@ -599,6 +605,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)]
@ -618,7 +625,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

@ -39,6 +39,7 @@ mod compile_error;
mod concat;
mod concat_bytes;
mod concat_idents;
mod define_opaque;
mod derive;
mod deriving;
mod edition_panic;
@ -114,6 +115,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 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

@ -209,6 +209,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

@ -780,8 +780,8 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
}
}
Err(err) => {
let guar = err.emit();
fragment_kind.dummy(span, guar)
let _guar = err.emit();
fragment_kind.expect_from_annotatables(iter::once(item))
}
}
}

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 [(Span, 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,32 @@ 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 &(span, define) in defines {
trace!(?define);
let mode = std::mem::replace(&mut self.mode, CollectionMode::Taits);
let n = self.opaques.len();
super::sig_types::walk_types(self.tcx, define, self);
if n == self.opaques.len() {
self.tcx.dcx().span_err(span, "item does not contain any opaque types");
}
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 +222,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 +296,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 +308,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 +343,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)));
}