Implement #[define_opaque]
attribute for functions.
This commit is contained in:
parent
2c6a12ec44
commit
cb4751d4b8
653 changed files with 2911 additions and 2580 deletions
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 })
|
||||
}
|
||||
|
|
|
@ -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 =
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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)
|
||||
/// }
|
||||
|
|
|
@ -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)];
|
||||
|
|
|
@ -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)));
|
||||
|
|
54
compiler/rustc_builtin_macros/src/define_opaque.rs
Normal file
54
compiler/rustc_builtin_macros/src/define_opaque.rs
Normal 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]
|
||||
}
|
|
@ -1040,6 +1040,7 @@ impl<'a> MethodDef<'a> {
|
|||
generics: fn_generics,
|
||||
contract: None,
|
||||
body: Some(body_block),
|
||||
define_opaque: None,
|
||||
})),
|
||||
tokens: None,
|
||||
})
|
||||
|
|
|
@ -83,6 +83,7 @@ impl AllocFnFactory<'_, '_> {
|
|||
generics: Generics::default(),
|
||||
contract: None,
|
||||
body,
|
||||
define_opaque: None,
|
||||
}));
|
||||
let item = self.cx.item(
|
||||
self.span,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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![],
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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] {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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]`
|
||||
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -43,6 +43,7 @@ declare_lint! {
|
|||
///
|
||||
/// type Tait = impl Sized;
|
||||
///
|
||||
/// #[define_opaque(Tait)]
|
||||
/// fn test() -> impl Trait<Assoc = Tait> {
|
||||
/// 42
|
||||
/// }
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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`.
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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" }
|
||||
|
|
|
@ -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)));
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue