diff options
| author | Bryanskiy <ivakin.kir@gmail.com> | 2023-11-26 15:57:31 +0300 |
|---|---|---|
| committer | Bryanskiy <ivakin.kir@gmail.com> | 2024-01-12 14:11:16 +0300 |
| commit | d69cd6473c110096bb009db0f2f21da6f67ac5a6 (patch) | |
| tree | 9c9ee290363a90b945c89bb88e734af4ac4f0840 /compiler | |
| parent | 2b1365b34f0d5ee43944c4266a625923a7b312dd (diff) | |
| download | rust-d69cd6473c110096bb009db0f2f21da6f67ac5a6.tar.gz rust-d69cd6473c110096bb009db0f2f21da6f67ac5a6.zip | |
Delegation implementation: step 1
Diffstat (limited to 'compiler')
25 files changed, 811 insertions, 80 deletions
diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index e1e4e5fc567..21077c312bd 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -2873,6 +2873,7 @@ impl Item { | ItemKind::ForeignMod(_) | ItemKind::GlobalAsm(_) | ItemKind::MacCall(_) + | ItemKind::Delegation(_) | ItemKind::MacroDef(_) => None, ItemKind::Static(_) => None, ItemKind::Const(i) => Some(&i.generics), @@ -3020,6 +3021,15 @@ pub struct Fn { } #[derive(Clone, Encodable, Decodable, Debug)] +pub struct Delegation { + /// Path resolution id. + pub id: NodeId, + pub qself: Option<P<QSelf>>, + pub path: Path, + pub body: Option<P<Block>>, +} + +#[derive(Clone, Encodable, Decodable, Debug)] pub struct StaticItem { pub ty: P<Ty>, pub mutability: Mutability, @@ -3104,6 +3114,11 @@ pub enum ItemKind { /// A macro definition. MacroDef(MacroDef), + + /// A delegation item (`reuse`). + /// + /// E.g. `reuse <Type as Trait>::name { target_expr_template }`. + Delegation(Box<Delegation>), } impl ItemKind { @@ -3111,7 +3126,8 @@ impl ItemKind { use ItemKind::*; match self { Use(..) | Static(..) | Const(..) | Fn(..) | Mod(..) | GlobalAsm(..) | TyAlias(..) - | Struct(..) | Union(..) | Trait(..) | TraitAlias(..) | MacroDef(..) => "a", + | Struct(..) | Union(..) | Trait(..) | TraitAlias(..) | MacroDef(..) + | Delegation(..) => "a", ExternCrate(..) | ForeignMod(..) | MacCall(..) | Enum(..) | Impl { .. } => "an", } } @@ -3135,6 +3151,7 @@ impl ItemKind { ItemKind::MacCall(..) => "item macro invocation", ItemKind::MacroDef(..) => "macro definition", ItemKind::Impl { .. } => "implementation", + ItemKind::Delegation(..) => "delegated function", } } @@ -3176,6 +3193,8 @@ pub enum AssocItemKind { Type(Box<TyAlias>), /// A macro expanding to associated items. MacCall(P<MacCall>), + /// An associated delegation item. + Delegation(Box<Delegation>), } impl AssocItemKind { @@ -3184,7 +3203,7 @@ impl AssocItemKind { Self::Const(box ConstItem { defaultness, .. }) | Self::Fn(box Fn { defaultness, .. }) | Self::Type(box TyAlias { defaultness, .. }) => defaultness, - Self::MacCall(..) => Defaultness::Final, + Self::MacCall(..) | Self::Delegation(..) => Defaultness::Final, } } } @@ -3196,6 +3215,7 @@ impl From<AssocItemKind> for ItemKind { AssocItemKind::Fn(fn_kind) => ItemKind::Fn(fn_kind), AssocItemKind::Type(ty_alias_kind) => ItemKind::TyAlias(ty_alias_kind), AssocItemKind::MacCall(a) => ItemKind::MacCall(a), + AssocItemKind::Delegation(delegation) => ItemKind::Delegation(delegation), } } } @@ -3209,6 +3229,7 @@ impl TryFrom<ItemKind> for AssocItemKind { ItemKind::Fn(fn_kind) => AssocItemKind::Fn(fn_kind), ItemKind::TyAlias(ty_kind) => AssocItemKind::Type(ty_kind), ItemKind::MacCall(a) => AssocItemKind::MacCall(a), + ItemKind::Delegation(d) => AssocItemKind::Delegation(d), _ => return Err(item_kind), }) } diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index 82f28143630..450555d0cb5 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -1117,6 +1117,14 @@ pub fn noop_visit_item_kind<T: MutVisitor>(kind: &mut ItemKind, vis: &mut T) { } ItemKind::MacCall(m) => vis.visit_mac_call(m), ItemKind::MacroDef(def) => vis.visit_macro_def(def), + ItemKind::Delegation(box Delegation { id, qself, path, body }) => { + vis.visit_id(id); + vis.visit_qself(qself); + vis.visit_path(path); + if let Some(body) = body { + vis.visit_block(body); + } + } } } @@ -1155,6 +1163,14 @@ pub fn noop_flat_map_assoc_item<T: MutVisitor>( visit_opt(ty, |ty| visitor.visit_ty(ty)); } AssocItemKind::MacCall(mac) => visitor.visit_mac_call(mac), + AssocItemKind::Delegation(box Delegation { id, qself, path, body }) => { + visitor.visit_id(id); + visitor.visit_qself(qself); + visitor.visit_path(path); + if let Some(body) = body { + visitor.visit_block(body); + } + } } visitor.visit_span(span); visit_lazy_tts(tokens, visitor); diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index 45261ca48fc..3617df931e2 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -375,6 +375,15 @@ pub fn walk_item<'a, V: Visitor<'a>>(visitor: &mut V, item: &'a Item) { } ItemKind::MacCall(mac) => visitor.visit_mac_call(mac), ItemKind::MacroDef(ts) => visitor.visit_mac_def(ts, item.id), + ItemKind::Delegation(box Delegation { id: _, qself, path, body }) => { + if let Some(qself) = qself { + visitor.visit_ty(&qself.ty); + } + walk_path(visitor, path); + if let Some(body) = body { + visitor.visit_block(body); + } + } } walk_list!(visitor, visit_attribute, &item.attrs); } @@ -704,6 +713,15 @@ pub fn walk_assoc_item<'a, V: Visitor<'a>>(visitor: &mut V, item: &'a AssocItem, AssocItemKind::MacCall(mac) => { visitor.visit_mac_call(mac); } + AssocItemKind::Delegation(box Delegation { id: _, qself, path, body }) => { + if let Some(qself) = qself { + visitor.visit_ty(&qself.ty); + } + walk_path(visitor, path); + if let Some(body) = body { + visitor.visit_block(body); + } + } } } diff --git a/compiler/rustc_ast_lowering/src/delegation.rs b/compiler/rustc_ast_lowering/src/delegation.rs new file mode 100644 index 00000000000..6ccf39b0cb1 --- /dev/null +++ b/compiler/rustc_ast_lowering/src/delegation.rs @@ -0,0 +1,348 @@ +//! This module implements expansion of delegation items with early resolved paths. +//! It includes a delegation to a free functions: +//! +//! ```ignore (illustrative) +//! reuse module::name { target_expr_template } +//! ``` +//! +//! And delegation to a trait methods: +//! +//! ```ignore (illustrative) +//! reuse <Type as Trait>::name { target_expr_template } +//! ``` +//! +//! After expansion for both cases we get: +//! +//! ```ignore (illustrative) +//! fn name( +//! arg0: InferDelegation(sig_id, Input(0)), +//! arg1: InferDelegation(sig_id, Input(1)), +//! ..., +//! argN: InferDelegation(sig_id, Input(N)), +//! ) -> InferDelegation(sig_id, Output) { +//! callee_path(target_expr_template(arg0), arg1, ..., argN) +//! } +//! ``` +//! +//! Where `callee_path` is a path in delegation item e.g. `<Type as Trait>::name`. +//! `sig_id` is a id of item from which the signature is inherited. It may be a delegation +//! item id (`item_id`) in case of impl trait or path resolution id (`path_id`) otherwise. +//! +//! Since we do not have a proper way to obtain function type information by path resolution +//! in AST, we mark each function parameter type as `InferDelegation` and inherit it in `AstConv`. +//! +//! Similarly generics, predicates and header are set to the "default" values. +//! In case of discrepancy with callee function the `NotSupportedDelegation` error will +//! also be emitted in `AstConv`. + +use crate::{ImplTraitPosition, ResolverAstLoweringExt}; + +use super::{ImplTraitContext, LoweringContext, ParamMode}; + +use ast::visit::Visitor; +use hir::def::{DefKind, PartialRes, Res}; +use hir::{BodyId, HirId}; +use rustc_ast as ast; +use rustc_ast::*; +use rustc_errors::ErrorGuaranteed; +use rustc_hir as hir; +use rustc_hir::def_id::DefId; +use rustc_middle::span_bug; +use rustc_middle::ty::ResolverAstLowering; +use rustc_span::{symbol::Ident, Span}; +use rustc_target::spec::abi; +use std::iter; + +pub(crate) struct DelegationResults<'hir> { + pub body_id: hir::BodyId, + pub sig: hir::FnSig<'hir>, + pub generics: &'hir hir::Generics<'hir>, +} + +impl<'hir> LoweringContext<'_, 'hir> { + pub(crate) fn delegation_has_self(&self, item_id: NodeId, path_id: NodeId, span: Span) -> bool { + let sig_id = self.get_delegation_sig_id(item_id, path_id, span); + let Ok(sig_id) = sig_id else { + return false; + }; + if let Some(local_sig_id) = sig_id.as_local() { + self.resolver.has_self.contains(&local_sig_id) + } else { + match self.tcx.def_kind(sig_id) { + DefKind::Fn => false, + DefKind::AssocFn => self.tcx.associated_item(sig_id).fn_has_self_parameter, + _ => span_bug!(span, "unexpected DefKind for delegation item"), + } + } + } + + pub(crate) fn lower_delegation( + &mut self, + delegation: &Delegation, + item_id: NodeId, + ) -> DelegationResults<'hir> { + let span = delegation.path.segments.last().unwrap().ident.span; + let sig_id = self.get_delegation_sig_id(item_id, delegation.id, span); + match sig_id { + Ok(sig_id) => { + let decl = self.lower_delegation_decl(sig_id, span); + let sig = self.lower_delegation_sig(span, decl); + let body_id = self.lower_delegation_body(sig.decl, delegation); + + let generics = self.lower_delegation_generics(span); + DelegationResults { body_id, sig, generics } + } + Err(err) => self.generate_delegation_error(err, span), + } + } + + fn get_delegation_sig_id( + &self, + item_id: NodeId, + path_id: NodeId, + span: Span, + ) -> Result<DefId, ErrorGuaranteed> { + let sig_id = if self.is_in_trait_impl { item_id } else { path_id }; + let sig_id = self + .resolver + .get_partial_res(sig_id) + .map(|r| r.expect_full_res().opt_def_id()) + .unwrap_or(None); + + sig_id.ok_or_else(|| { + self.tcx + .dcx() + .span_delayed_bug(span, "LoweringContext: couldn't resolve delegation item") + }) + } + + fn lower_delegation_generics(&mut self, span: Span) -> &'hir hir::Generics<'hir> { + self.arena.alloc(hir::Generics { + params: &[], + predicates: &[], + has_where_clause_predicates: false, + where_clause_span: span, + span: span, + }) + } + + fn lower_delegation_decl( + &mut self, + sig_id: DefId, + param_span: Span, + ) -> &'hir hir::FnDecl<'hir> { + let args_count = if let Some(local_sig_id) = sig_id.as_local() { + // Map may be filled incorrectly due to recursive delegation. + // Error will be emmited later in astconv. + self.resolver.fn_parameter_counts.get(&local_sig_id).cloned().unwrap_or_default() + } else { + self.tcx.fn_arg_names(sig_id).len() + }; + let inputs = self.arena.alloc_from_iter((0..args_count).into_iter().map(|arg| hir::Ty { + hir_id: self.next_id(), + kind: hir::TyKind::InferDelegation(sig_id, hir::InferDelegationKind::Input(arg)), + span: self.lower_span(param_span), + })); + + let output = self.arena.alloc(hir::Ty { + hir_id: self.next_id(), + kind: hir::TyKind::InferDelegation(sig_id, hir::InferDelegationKind::Output), + span: self.lower_span(param_span), + }); + + self.arena.alloc(hir::FnDecl { + inputs, + output: hir::FnRetTy::Return(output), + c_variadic: false, + lifetime_elision_allowed: true, + implicit_self: hir::ImplicitSelfKind::None, + }) + } + + fn lower_delegation_sig( + &mut self, + span: Span, + decl: &'hir hir::FnDecl<'hir>, + ) -> hir::FnSig<'hir> { + hir::FnSig { + decl, + header: hir::FnHeader { + unsafety: hir::Unsafety::Normal, + constness: hir::Constness::NotConst, + asyncness: hir::IsAsync::NotAsync, + abi: abi::Abi::Rust, + }, + span: self.lower_span(span), + } + } + + fn generate_param(&mut self, ty: &'hir hir::Ty<'hir>) -> (hir::Param<'hir>, NodeId) { + let pat_node_id = self.next_node_id(); + let pat_id = self.lower_node_id(pat_node_id); + let pat = self.arena.alloc(hir::Pat { + hir_id: pat_id, + kind: hir::PatKind::Binding(hir::BindingAnnotation::NONE, pat_id, Ident::empty(), None), + span: ty.span, + default_binding_modes: false, + }); + + (hir::Param { hir_id: self.next_id(), pat, ty_span: ty.span, span: ty.span }, pat_node_id) + } + + fn generate_arg(&mut self, ty: &'hir hir::Ty<'hir>, param_id: HirId) -> hir::Expr<'hir> { + let segments = self.arena.alloc_from_iter(iter::once(hir::PathSegment { + ident: Ident::empty(), + hir_id: self.next_id(), + res: Res::Local(param_id), + args: None, + infer_args: false, + })); + + let path = + self.arena.alloc(hir::Path { span: ty.span, res: Res::Local(param_id), segments }); + + hir::Expr { + hir_id: self.next_id(), + kind: hir::ExprKind::Path(hir::QPath::Resolved(None, path)), + span: ty.span, + } + } + + fn lower_delegation_body( + &mut self, + decl: &'hir hir::FnDecl<'hir>, + delegation: &Delegation, + ) -> BodyId { + let path = self.lower_qpath( + delegation.id, + &delegation.qself, + &delegation.path, + ParamMode::Optional, + &ImplTraitContext::Disallowed(ImplTraitPosition::Path), + None, + ); + let block = delegation.body.as_deref(); + + self.lower_body(|this| { + let mut parameters: Vec<hir::Param<'_>> = Vec::new(); + let mut args: Vec<hir::Expr<'hir>> = Vec::new(); + + for (idx, param_ty) in decl.inputs.iter().enumerate() { + let (param, pat_node_id) = this.generate_param(param_ty); + parameters.push(param); + + let arg = if let Some(block) = block + && idx == 0 + { + let mut self_resolver = SelfResolver { + resolver: this.resolver, + path_id: delegation.id, + self_param_id: pat_node_id, + }; + self_resolver.visit_block(block); + let block = this.lower_block(block, false); + hir::Expr { + hir_id: this.next_id(), + kind: hir::ExprKind::Block(block, None), + span: block.span, + } + } else { + let pat_hir_id = this.lower_node_id(pat_node_id); + this.generate_arg(param_ty, pat_hir_id) + }; + args.push(arg); + } + + let args = self.arena.alloc_from_iter(args); + let final_expr = this.generate_call(path, args); + (this.arena.alloc_from_iter(parameters), final_expr) + }) + } + + fn generate_call( + &mut self, + path: hir::QPath<'hir>, + args: &'hir [hir::Expr<'hir>], + ) -> hir::Expr<'hir> { + let callee = self.arena.alloc(hir::Expr { + hir_id: self.next_id(), + kind: hir::ExprKind::Path(path), + span: path.span(), + }); + + let expr = self.arena.alloc(hir::Expr { + hir_id: self.next_id(), + kind: hir::ExprKind::Call(callee, args), + span: path.span(), + }); + + let block = self.arena.alloc(hir::Block { + stmts: &[], + expr: Some(expr), + hir_id: self.next_id(), + rules: hir::BlockCheckMode::DefaultBlock, + span: path.span(), + targeted_by_break: false, + }); + + hir::Expr { + hir_id: self.next_id(), + kind: hir::ExprKind::Block(block, None), + span: path.span(), + } + } + + fn generate_delegation_error( + &mut self, + err: ErrorGuaranteed, + span: Span, + ) -> DelegationResults<'hir> { + let generics = self.lower_delegation_generics(span); + + let decl = self.arena.alloc(hir::FnDecl { + inputs: &[], + output: hir::FnRetTy::DefaultReturn(span), + c_variadic: false, + lifetime_elision_allowed: true, + implicit_self: hir::ImplicitSelfKind::None, + }); + + let sig = self.lower_delegation_sig(span, decl); + let body_id = self.lower_body(|this| { + let expr = + hir::Expr { hir_id: this.next_id(), kind: hir::ExprKind::Err(err), span: span }; + (&[], expr) + }); + DelegationResults { generics, body_id, sig } + } +} + +struct SelfResolver<'a> { + resolver: &'a mut ResolverAstLowering, + path_id: NodeId, + self_param_id: NodeId, +} + +impl<'a> SelfResolver<'a> { + fn try_replace_id(&mut self, id: NodeId) { + if let Some(res) = self.resolver.partial_res_map.get(&id) + && let Some(Res::Local(sig_id)) = res.full_res() + && sig_id == self.path_id + { + let new_res = PartialRes::new(Res::Local(self.self_param_id)); + self.resolver.partial_res_map.insert(id, new_res); + } + } +} + +impl<'ast, 'a> Visitor<'ast> for SelfResolver<'a> { + fn visit_path(&mut self, path: &'ast Path, id: NodeId) { + self.try_replace_id(id); + visit::walk_path(self, path); + } + + fn visit_path_segment(&mut self, seg: &'ast PathSegment) { + self.try_replace_id(seg.id); + visit::walk_path_segment(self, seg); + } +} diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index d8de447e5b4..a3ff02f5f69 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -441,6 +441,14 @@ impl<'hir> LoweringContext<'_, 'hir> { let macro_def = self.arena.alloc(ast::MacroDef { body, macro_rules: *macro_rules }); hir::ItemKind::Macro(macro_def, macro_kind) } + ItemKind::Delegation(box delegation) => { + let delegation_results = self.lower_delegation(delegation, id); + hir::ItemKind::Fn( + delegation_results.sig, + delegation_results.generics, + delegation_results.body_id, + ) + } ItemKind::MacCall(..) => { panic!("`TyMac` should have been expanded by now") } @@ -805,6 +813,14 @@ impl<'hir> LoweringContext<'_, 'hir> { ); (generics, kind, ty.is_some()) } + AssocItemKind::Delegation(box delegation) => { + let delegation_results = self.lower_delegation(delegation, i.id); + let item_kind = hir::TraitItemKind::Fn( + delegation_results.sig, + hir::TraitFn::Provided(delegation_results.body_id), + ); + (delegation_results.generics, item_kind, true) + } AssocItemKind::MacCall(..) => panic!("macro item shouldn't exist at this point"), }; @@ -826,6 +842,9 @@ impl<'hir> LoweringContext<'_, 'hir> { AssocItemKind::Fn(box Fn { sig, .. }) => { hir::AssocItemKind::Fn { has_self: sig.decl.has_self() } } + AssocItemKind::Delegation(box delegation) => hir::AssocItemKind::Fn { + has_self: self.delegation_has_self(i.id, delegation.id, i.span), + }, AssocItemKind::MacCall(..) => unimplemented!(), }; let id = hir::TraitItemId { owner_id: hir::OwnerId { def_id: self.local_def_id(i.id) } }; @@ -908,6 +927,13 @@ impl<'hir> LoweringContext<'_, 'hir> { }, ) } + AssocItemKind::Delegation(box delegation) => { + let delegation_results = self.lower_delegation(delegation, i.id); + ( + delegation_results.generics, + hir::ImplItemKind::Fn(delegation_results.sig, delegation_results.body_id), + ) + } AssocItemKind::MacCall(..) => panic!("`TyMac` should have been expanded by now"), }; @@ -924,6 +950,13 @@ impl<'hir> LoweringContext<'_, 'hir> { } fn lower_impl_item_ref(&mut self, i: &AssocItem) -> hir::ImplItemRef { + let trait_item_def_id = self + .resolver + .get_partial_res(i.id) + .map(|r| r.expect_full_res().opt_def_id()) + .unwrap_or(None); + self.is_in_trait_impl = trait_item_def_id.is_some(); + hir::ImplItemRef { id: hir::ImplItemId { owner_id: hir::OwnerId { def_id: self.local_def_id(i.id) } }, ident: self.lower_ident(i.ident), @@ -934,12 +967,12 @@ impl<'hir> LoweringContext<'_, 'hir> { AssocItemKind::Fn(box Fn { sig, .. }) => { hir::AssocItemKind::Fn { has_self: sig.decl.has_self() } } + AssocItemKind::Delegation(box delegation) => hir::AssocItemKind::Fn { + has_self: self.delegation_has_self(i.id, delegation.id, i.span), + }, AssocItemKind::MacCall(..) => unimplemented!(), }, - trait_item_def_id: self - .resolver - .get_partial_res(i.id) - .map(|r| r.expect_full_res().def_id()), + trait_item_def_id, } } diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index dc23b1dce7b..d2d42a15808 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -77,6 +77,7 @@ macro_rules! arena_vec { mod asm; mod block; +mod delegation; mod errors; mod expr; mod format; diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index 737e81eb6ec..539b520f54f 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -556,6 +556,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) { gate_all!(explicit_tail_calls, "`become` expression is experimental"); gate_all!(generic_const_items, "generic const items are experimental"); gate_all!(unnamed_fields, "unnamed fields are not yet fully implemented"); + gate_all!(fn_delegation, "functions delegation is not yet fully implemented"); if !visitor.features.never_patterns { if let Some(spans) = spans.get(&sym::never_patterns) { diff --git a/compiler/rustc_ast_pretty/src/pprust/state/item.rs b/compiler/rustc_ast_pretty/src/pprust/state/item.rs index 43561a1c020..584f01e16c2 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/item.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/item.rs @@ -376,6 +376,9 @@ impl<'a> State<'a> { state.print_visibility(&item.vis) }); } + ast::ItemKind::Delegation(box delegation) => { + self.print_delegation(delegation, &item.vis, &item.attrs) + } } self.ann.post(self, AnnNode::Item(item)) } @@ -554,10 +557,38 @@ impl<'a> State<'a> { self.word(";"); } } + ast::AssocItemKind::Delegation(box delegation) => { + self.print_delegation(delegation, vis, &item.attrs) + } } self.ann.post(self, AnnNode::SubItem(id)) } + pub(crate) fn print_delegation( + &mut self, + delegation: &ast::Delegation, + vis: &ast::Visibility, + attrs: &[ast::Attribute], + ) { + if delegation.body.is_some() { + self.head(""); + } + self.print_visibility(vis); + self.word_space("reuse"); + + if let Some(qself) = &delegation.qself { + self.print_qpath(&delegation.path, qself, false); + } else { + self.print_path(&delegation.path, false, 0); + } + if let Some(body) = &delegation.body { + self.nbsp(); + self.print_block_with_attrs(body, attrs); + } else { + self.word(";"); + } + } + fn print_fn_full( &mut self, sig: &ast::FnSig, diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 58ac9668da5..6b347f7035a 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -2571,9 +2571,17 @@ pub enum OpaqueTyOrigin { }, } +#[derive(Debug, Clone, Copy, PartialEq, Eq, HashStable_Generic)] +pub enum InferDelegationKind { + Input(usize), + Output, +} + /// The various kinds of types recognized by the compiler. #[derive(Debug, Clone, Copy, HashStable_Generic)] pub enum TyKind<'hir> { + /// Actual type should be inherited from `DefId` signature + InferDelegation(DefId, InferDelegationKind), /// A variable length slice (i.e., `[T]`). Slice(&'hir Ty<'hir>), /// A fixed length array (i.e., `[T; n]`). diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index dd3633b6b4f..adc09025809 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -856,7 +856,7 @@ pub fn walk_ty<'v, V: Visitor<'v>>(visitor: &mut V, typ: &'v Ty<'v>) { visitor.visit_lifetime(lifetime); } TyKind::Typeof(ref expression) => visitor.visit_anon_const(expression), - TyKind::Infer | TyKind::Err(_) => {} + TyKind::Infer | TyKind::InferDelegation(..) | TyKind::Err(_) => {} } } diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl index 6a17668ad17..432c9c12cbf 100644 --- a/compiler/rustc_hir_analysis/messages.ftl +++ b/compiler/rustc_hir_analysis/messages.ftl @@ -263,6 +263,10 @@ hir_analysis_must_implement_not_function_span_note = required by this annotation hir_analysis_must_implement_one_of_attribute = the `#[rustc_must_implement_one_of]` attribute must be used with at least 2 args +hir_analysis_not_supported_delegation = + {$descr} is not supported yet + .label = callee defined here + hir_analysis_only_current_traits_arbitrary = only traits defined in the current crate can be implemented for arbitrary types hir_analysis_only_current_traits_foreign = this is not defined in the current crate because this is a foreign trait diff --git a/compiler/rustc_hir_analysis/src/astconv/mod.rs b/compiler/rustc_hir_analysis/src/astconv/mod.rs index 2886ec21320..78740ac33ca 100644 --- a/compiler/rustc_hir_analysis/src/astconv/mod.rs +++ b/compiler/rustc_hir_analysis/src/astconv/mod.rs @@ -29,10 +29,9 @@ use rustc_hir::{GenericArg, GenericArgs, OpaqueTyOrigin}; use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; use rustc_infer::traits::ObligationCause; use rustc_middle::middle::stability::AllowUnstable; -use rustc_middle::ty::GenericParamDefKind; use rustc_middle::ty::{ - self, Const, GenericArgKind, GenericArgsRef, IsSuggestable, ParamEnv, Ty, TyCtxt, - TypeVisitableExt, + self, Const, GenericArgKind, GenericArgsRef, GenericParamDefKind, IsSuggestable, ParamEnv, Ty, + TyCtxt, TypeVisitableExt, }; use rustc_session::lint::builtin::AMBIGUOUS_ASSOCIATED_ITEMS; use rustc_span::edit_distance::find_best_match_for_name; @@ -2322,6 +2321,114 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { self.ast_ty_to_ty_inner(ast_ty, false, true) } + fn check_delegation_constraints(&self, sig_id: DefId, span: Span, emit: bool) -> bool { + let mut error_occured = false; + let sig_span = self.tcx().def_span(sig_id); + let mut try_emit = |descr| { + if emit { + self.tcx().dcx().emit_err(crate::errors::NotSupportedDelegation { + span, + descr, + callee_span: sig_span, + }); + } + error_occured = true; + }; + + if let Some(node) = self.tcx().hir().get_if_local(sig_id) + && let Some(decl) = node.fn_decl() + && let hir::FnRetTy::Return(ty) = decl.output + && let hir::TyKind::InferDelegation(_, _) = ty.kind + { + try_emit("recursive delegation"); + } + + let sig = self.tcx().fn_sig(sig_id).instantiate_identity(); + if sig.output().has_opaque_types() { + try_emit("delegation to a function with opaque type"); + } + + let sig_generics = self.tcx().generics_of(sig_id); + let parent = self.tcx().parent(self.item_def_id()); + let parent_generics = self.tcx().generics_of(parent); + + let parent_is_trait = (self.tcx().def_kind(parent) == DefKind::Trait) as usize; + let sig_has_self = sig_generics.has_self as usize; + + if sig_generics.count() > sig_has_self || parent_generics.count() > parent_is_trait { + try_emit("delegation with early bound generics"); + } + + if self.tcx().asyncness(sig_id) == ty::Asyncness::Yes { + try_emit("delegation to async functions"); + } + + if self.tcx().constness(sig_id) == hir::Constness::Const { + try_emit("delegation to const functions"); + } + + if sig.c_variadic() { + try_emit("delegation to variadic functions"); + // variadic functions are also `unsafe` and `extern "C"`. + // Do not emit same error multiple times. + return error_occured; + } + + if let hir::Unsafety::Unsafe = sig.unsafety() { + try_emit("delegation to unsafe functions"); + } + + if abi::Abi::Rust != sig.abi() { + try_emit("delegation to non Rust ABI functions"); + } + + error_occured + } + + fn ty_from_delegation( + &self, + sig_id: DefId, + idx: hir::InferDelegationKind, + span: Span, + ) -> Ty<'tcx> { + if self.check_delegation_constraints(sig_id, span, idx == hir::InferDelegationKind::Output) + { + let e = self.tcx().dcx().span_delayed_bug(span, "not supported delegation case"); + self.set_tainted_by_errors(e); + return Ty::new_error(self.tcx(), e); + }; + let sig = self.tcx().fn_sig(sig_id); + let sig_generics = self.tcx().generics_of(sig_id); + + let parent = self.tcx().parent(self.item_def_id()); + let parent_def_kind = self.tcx().def_kind(parent); + + let sig = if let DefKind::Impl { .. } = parent_def_kind + && sig_generics.has_self + { + // Generic params can't be here except the trait self type. + // They are not supported yet. + assert_eq!(sig_generics.count(), 1); + assert_eq!(self.tcx().generics_of(parent).count(), 0); + + let self_ty = self.tcx().type_of(parent).instantiate_identity(); + let generic_self_ty = ty::GenericArg::from(self_ty); + let substs = self.tcx().mk_args_from_iter(std::iter::once(generic_self_ty)); + sig.instantiate(self.tcx(), substs) + } else { + sig.instantiate_identity() + }; + + // Bound vars are also inherited from `sig_id`. They will be + // rebinded later in `ty_of_fn`. + let sig = sig.skip_binder(); + + match idx { + hir::InferDelegationKind::Input(id) => sig.inputs()[id], + hir::InferDelegationKind::Output => sig.output(), + } + } + /// Turns a `hir::Ty` into a `Ty`. For diagnostics' purposes we keep track of whether trait /// objects are borrowed like `&dyn Trait` to avoid emitting redundant errors. #[instrument(level = "debug", skip(self), ret)] @@ -2329,6 +2436,9 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { let tcx = self.tcx(); let result_ty = match &ast_ty.kind { + hir::TyKind::InferDelegation(sig_id, idx) => { + self.ty_from_delegation(*sig_id, *idx, ast_ty.span) + } hir::TyKind::Slice(ty) => Ty::new_slice(tcx, self.ast_ty_to_ty(ty)), hir::TyKind::Ptr(mt) => { Ty::new_ptr(tcx, ty::TypeAndMut { ty: self.ast_ty_to_ty(mt.ty), mutbl: mt.mutbl }) @@ -2520,7 +2630,13 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { hir_ty: Option<&hir::Ty<'_>>, ) -> ty::PolyFnSig<'tcx> { let tcx = self.tcx(); - let bound_vars = tcx.late_bound_vars(hir_id); + let bound_vars = if let hir::FnRetTy::Return(ret_ty) = decl.output + && let hir::TyKind::InferDelegation(sig_id, _) = ret_ty.kind + { + tcx.fn_sig(sig_id).skip_binder().bound_vars() + } else { + tcx.late_bound_vars(hir_id) + }; debug!(?bound_vars); // We proactively collect all the inferred type params to emit a single error per fn def. diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index 4f22da4ba3b..f14390b77c6 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -1501,3 +1501,13 @@ pub enum RefOfMutStaticSugg { var: String, }, } + +#[derive(Diagnostic)] +#[diag(hir_analysis_not_supported_delegation)] +pub struct NotSupportedDelegation<'a> { + #[primary_span] + pub span: Span, + pub descr: &'a str, + #[label] + pub callee_span: Span, +} diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index 1eaec997053..d36e0892d19 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -320,7 +320,7 @@ impl<'a> State<'a> { self.word("/*ERROR*/"); self.pclose(); } - hir::TyKind::Infer => { + hir::TyKind::Infer | hir::TyKind::InferDelegation(..) => { self.word("_"); } } diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 9e8d7c2ef3e..ad9296a4cc8 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -42,7 +42,7 @@ use rustc_data_structures::unord::UnordMap; use rustc_errors::{DiagnosticBuilder, ErrorGuaranteed, StashKey}; use rustc_hir as hir; use rustc_hir::def::{CtorKind, CtorOf, DefKind, DocLinkResMap, LifetimeRes, Res}; -use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LocalDefId, LocalDefIdMap}; +use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LocalDefId, LocalDefIdMap, LocalDefIdSet}; use rustc_index::IndexVec; use rustc_macros::HashStable; use rustc_query_system::ich::StableHashingContext; @@ -202,6 +202,10 @@ pub struct ResolverAstLowering { /// Lints that were emitted by the resolver and early lints. pub lint_buffer: Steal<LintBuffer>, + + /// Information about functions signatures for delegation items expansion + pub has_self: LocalDefIdSet, + pub fn_parameter_counts: LocalDefIdMap<usize>, } #[derive(Clone, Copy, Debug)] diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index bcff820da79..3bd8ae02586 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -252,6 +252,8 @@ impl<'a> Parser<'a> { { // IMPL ITEM self.parse_item_impl(attrs, def_())? + } else if self.is_reuse_path_item() { + self.parse_item_delegation()? } else if self.check_keyword(kw::Mod) || self.check_keyword(kw::Unsafe) && self.is_keyword_ahead(1, &[kw::Mod]) { @@ -349,11 +351,18 @@ impl<'a> Parser<'a> { /// When parsing a statement, would the start of a path be an item? pub(super) fn is_path_start_item(&mut self) -> bool { self.is_kw_followed_by_ident(kw::Union) // no: `union::b`, yes: `union U { .. }` + || self.is_reuse_path_item() || self.check_auto_or_unsafe_trait_item() // no: `auto::b`, yes: `auto trait X { .. }` || self.is_async_fn() // no(2015): `async::b`, yes: `async fn` || matches!(self.is_macro_rules_item(), IsMacroRulesItem::Yes{..}) // no: `macro_rules::b`, yes: `macro_rules! mac` } + fn is_reuse_path_item(&mut self) -> bool { + // no: `reuse ::path` for compatibility reasons with macro invocations + self.token.is_keyword(kw::Reuse) + && self.look_ahead(1, |t| t.is_path_start() && t.kind != token::ModSep) + } + /// Are we sure this could not possibly be a macro invocation? fn isnt_macro_invocation(&mut self) -> bool { self.check_ident() && self.look_ahead(1, |t| *t != token::Not && *t != token::ModSep) @@ -655,6 +664,33 @@ impl<'a> Parser<'a> { Ok((Ident::empty(), item_kind)) } + fn parse_item_delegation(&mut self) -> PResult<'a, ItemInfo> { + let span = self.token.span; + self.expect_keyword(kw::Reuse)?; + + let (qself, path) = if self.eat_lt() { + let (qself, path) = self.parse_qpath(PathStyle::Expr)?; + (Some(qself), path) + } else { + (None, self.parse_path(PathStyle::Expr)?) + }; + + let body = if self.check(&token::OpenDelim(Delimiter::Brace)) { + Some(self.parse_block()?) + } else { + self.expect(&token::Semi)?; + None + }; + let span = span.to(self.prev_token.span); + self.sess.gated_spans.gate(sym::fn_delegation, span); + + let ident = path.segments.last().map(|seg| seg.ident).unwrap_or(Ident::empty()); + Ok(( + ident, + ItemKind::Delegation(Box::new(Delegation { id: DUMMY_NODE_ID, qself, path, body })), + )) + } + fn parse_item_list<T>( &mut self, attrs: &mut AttrVec, diff --git a/compiler/rustc_passes/src/hir_stats.rs b/compiler/rustc_passes/src/hir_stats.rs index c2392620cb2..ff29fc5929c 100644 --- a/compiler/rustc_passes/src/hir_stats.rs +++ b/compiler/rustc_passes/src/hir_stats.rs @@ -341,6 +341,7 @@ impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> { record_variants!( (self, t, t.kind, Id::Node(t.hir_id), hir, Ty, TyKind), [ + InferDelegation, Slice, Array, Ptr, @@ -521,7 +522,8 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> { TraitAlias, Impl, MacCall, - MacroDef + MacroDef, + Delegation ] ); ast_visit::walk_item(self, i) @@ -645,7 +647,7 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> { fn visit_assoc_item(&mut self, i: &'v ast::AssocItem, ctxt: ast_visit::AssocCtxt) { record_variants!( (self, i, i.kind, Id::None, ast, AssocItem, AssocItemKind), - [Const, Fn, Type, MacCall] + [Const, Fn, Type, MacCall, Delegation] ); ast_visit::walk_assoc_item(self, i, ctxt); } diff --git a/compiler/rustc_passes/src/lang_items.rs b/compiler/rustc_passes/src/lang_items.rs index 647f4d0e084..6aeaa8945fb 100644 --- a/compiler/rustc_passes/src/lang_items.rs +++ b/compiler/rustc_passes/src/lang_items.rs @@ -271,7 +271,7 @@ impl<'ast, 'tcx> visit::Visitor<'ast> for LanguageItemCollector<'ast, 'tcx> { ast::ItemKind::Use(_) => Target::Use, ast::ItemKind::Static(_) => Target::Static, ast::ItemKind::Const(_) => Target::Const, - ast::ItemKind::Fn(_) => Target::Fn, + ast::ItemKind::Fn(_) | ast::ItemKind::Delegation(..) => Target::Fn, ast::ItemKind::Mod(_, _) => Target::Mod, ast::ItemKind::ForeignMod(_) => Target::ForeignFn, ast::ItemKind::GlobalAsm(_) => Target::GlobalAsm, @@ -315,24 +315,29 @@ impl<'ast, 'tcx> visit::Visitor<'ast> for LanguageItemCollector<'ast, 'tcx> { fn visit_assoc_item(&mut self, i: &'ast ast::AssocItem, ctxt: visit::AssocCtxt) { let (target, generics) = match &i.kind { - ast::AssocItemKind::Fn(fun) => ( - match &self.parent_item.unwrap().kind { - ast::ItemKind::Impl(i) => { - if i.of_trait.is_some() { - Target::Method(MethodKind::Trait { body: fun.body.is_some() }) - } else { - Target::Method(MethodKind::Inherent) + ast::AssocItemKind::Fn(..) | ast::AssocItemKind::Delegation(..) => { + let (body, generics) = if let ast::AssocItemKind::Fn(fun) = &i.kind { + (fun.body.is_some(), Some(&fun.generics)) + } else { + (true, None) + }; + ( + match &self.parent_item.unwrap().kind { + ast::ItemKind::Impl(i) => { + if i.of_trait.is_some() { + Target::Method(MethodKind::Trait { body }) + } else { + Target::Method(MethodKind::Inherent) + } } - } - ast::ItemKind::Trait(_) => { - Target::Method(MethodKind::Trait { body: fun.body.is_some() }) - } - _ => unreachable!(), - }, - &fun.generics, - ), - ast::AssocItemKind::Const(ct) => (Target::AssocConst, &ct.generics), - ast::AssocItemKind::Type(ty) => (Target::AssocTy, &ty.generics), + ast::ItemKind::Trait(_) => Target::Method(MethodKind::Trait { body }), + _ => unreachable!(), + }, + generics, + ) + } + ast::AssocItemKind::Const(ct) => (Target::AssocConst, Some(&ct.generics)), + ast::AssocItemKind::Type(ty) => (Target::AssocTy, Some(&ty.generics)), ast::AssocItemKind::MacCall(_) => unreachable!("macros should have been expanded"), }; @@ -341,7 +346,7 @@ impl<'ast, 'tcx> visit::Visitor<'ast> for LanguageItemCollector<'ast, 'tcx> { self.resolver.node_id_to_def_id[&i.id], &i.attrs, i.span, - Some(generics), + generics, ); visit::walk_assoc_item(self, i, ctxt); diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index 9ccfde5e3c6..a4e2f9e3ff8 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -16,7 +16,7 @@ use crate::{Resolver, ResolverArenas, Segment, ToNameBinding, VisResolutionError use rustc_ast::visit::{self, AssocCtxt, Visitor}; use rustc_ast::{self as ast, AssocItem, AssocItemKind, MetaItemKind, StmtKind}; -use rustc_ast::{Block, Fn, ForeignItem, ForeignItemKind, Impl, Item, ItemKind, NodeId}; +use rustc_ast::{Block, ForeignItem, ForeignItemKind, Impl, Item, ItemKind, NodeId}; use rustc_attr as attr; use rustc_data_structures::sync::Lrc; use rustc_errors::{struct_span_code_err, Applicability}; @@ -686,10 +686,7 @@ impl<'a, 'b, 'tcx> BuildReducedGraphVisitor<'a, 'b, 'tcx> { } // These items live in the value namespace. - ItemKind::Static(..) => { - self.r.define(parent, ident, ValueNS, (res, vis, sp, expansion)); - } - ItemKind::Const(..) => { + ItemKind::Const(..) | ItemKind::Delegation(..) | ItemKind::Static(..) => { self.r.define(parent, ident, ValueNS, (res, vis, sp, expansion)); } ItemKind::Fn(..) => { @@ -701,11 +698,11 @@ impl<'a, 'b, 'tcx> BuildReducedGraphVisitor<'a, 'b, 'tcx> { } // These items live in the type namespace. - ItemKind::TyAlias(..) => { + ItemKind::TyAlias(..) | ItemKind::TraitAlias(..) => { self.r.define(parent, ident, TypeNS, (res, vis, sp, expansion)); } - ItemKind::Enum(_, _) => { + ItemKind::Enum(_, _) | ItemKind::Trait(..) => { let module = self.r.new_module( Some(parent), ModuleKind::Def(def_kind, def_id, ident.name), @@ -717,10 +714,6 @@ impl<'a, 'b, 'tcx> BuildReducedGraphVisitor<'a, 'b, 'tcx> { self.parent_scope.module = module; } - ItemKind::TraitAlias(..) => { - self.r.define(parent, ident, TypeNS, (res, vis, sp, expansion)); - } - // These items live in both the type and value namespaces. ItemKind::Struct(ref vdata, _) => { // Define a name in the type namespace. @@ -778,19 +771,6 @@ impl<'a, 'b, 'tcx> BuildReducedGraphVisitor<'a, 'b, 'tcx> { self.insert_field_visibilities_local(def_id, vdata); } - ItemKind::Trait(..) => { - // Add all the items within to a new module. - let module = self.r.new_module( - Some(parent), - ModuleKind::Def(def_kind, def_id, ident.name), - expansion.to_expn_id(), - item.span, - parent.no_implicit_prelude, - ); - self.r.define(parent, ident, TypeNS, (module, vis, sp, expansion)); - self.parent_scope.module = module; - } - // These items do not add names to modules. ItemKind::Impl(box Impl { of_trait: Some(..), .. }) => { self.r.trait_impl_items.insert(local_def_id); @@ -1358,13 +1338,9 @@ impl<'a, 'b, 'tcx> Visitor<'b> for BuildReducedGraphVisitor<'a, 'b, 'tcx> { if ctxt == AssocCtxt::Trait { let ns = match item.kind { - AssocItemKind::Const(..) => ValueNS, - AssocItemKind::Fn(box Fn { ref sig, .. }) => { - if sig.decl.has_self() { - self.r.has_self.insert(local_def_id); - } - ValueNS - } + AssocItemKind::Const(..) + | AssocItemKind::Delegation(..) + | AssocItemKind::Fn(..) => ValueNS, AssocItemKind::Type(..) => TypeNS, AssocItemKind::MacCall(_) => bug!(), // handled above }; diff --git a/compiler/rustc_resolve/src/def_collector.rs b/compiler/rustc_resolve/src/def_collector.rs index 02553d50071..b77102c085c 100644 --- a/compiler/rustc_resolve/src/def_collector.rs +++ b/compiler/rustc_resolve/src/def_collector.rs @@ -111,7 +111,7 @@ impl<'a, 'b, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'b, 'tcx> { ItemKind::TyAlias(..) => DefKind::TyAlias, ItemKind::Static(s) => DefKind::Static(s.mutability), ItemKind::Const(..) => DefKind::Const, - ItemKind::Fn(..) => DefKind::Fn, + ItemKind::Fn(..) | ItemKind::Delegation(..) => DefKind::Fn, ItemKind::MacroDef(..) => { let macro_data = self.resolver.compile_macro(i, self.resolver.tcx.sess.edition()); let macro_kind = macro_data.ext.macro_kind(); @@ -259,7 +259,7 @@ impl<'a, 'b, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'b, 'tcx> { fn visit_assoc_item(&mut self, i: &'a AssocItem, ctxt: visit::AssocCtxt) { let def_kind = match &i.kind { - AssocItemKind::Fn(..) => DefKind::AssocFn, + AssocItemKind::Fn(..) | AssocItemKind::Delegation(..) => DefKind::AssocFn, AssocItemKind::Const(..) => DefKind::AssocConst, AssocItemKind::Type(..) => DefKind::AssocTy, AssocItemKind::MacCall(..) => return self.visit_macro_invoc(i.id), diff --git a/compiler/rustc_resolve/src/effective_visibilities.rs b/compiler/rustc_resolve/src/effective_visibilities.rs index 50352169221..3443bbe6e11 100644 --- a/compiler/rustc_resolve/src/effective_visibilities.rs +++ b/compiler/rustc_resolve/src/effective_visibilities.rs @@ -277,7 +277,8 @@ impl<'r, 'ast, 'tcx> Visitor<'ast> for EffectiveVisibilitiesVisitor<'ast, 'r, 't | ast::ItemKind::TraitAlias(..) | ast::ItemKind::MacroDef(..) | ast::ItemKind::ForeignMod(..) - | ast::ItemKind::Fn(..) => return, + | ast::ItemKind::Fn(..) + | ast::ItemKind::Delegation(..) => return, } } } diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 4a3c8dfe3d7..b9e603a4992 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -394,13 +394,18 @@ pub(crate) enum PathSource<'a> { TupleStruct(Span, &'a [Span]), // `m::A::B` in `<T as m::A>::B::C`. TraitItem(Namespace), + // Paths in delegation item + Delegation, } impl<'a> PathSource<'a> { fn namespace(self) -> Namespace { match self { PathSource::Type | PathSource::Trait(_) | PathSource::Struct => TypeNS, - PathSource::Expr(..) | PathSource::Pat | PathSource::TupleStruct(..) => ValueNS, + PathSource::Expr(..) + | PathSource::Pat + | PathSource::TupleStruct(..) + | PathSource::Delegation => ValueNS, PathSource::TraitItem(ns) => ns, } } @@ -412,7 +417,7 @@ impl<'a> PathSource<'a> { | PathSource::Pat | PathSource::Struct | PathSource::TupleStruct(..) => true, - PathSource::Trait(_) | PathSource::TraitItem(..) => false, + PathSource::Trait(_) | PathSource::TraitItem(..) | PathSource::Delegation => false, } } @@ -454,6 +459,7 @@ impl<'a> PathSource<'a> { }, _ => "value", }, + PathSource::Delegation => "function", } } @@ -521,6 +527,7 @@ impl<'a> PathSource<'a> { Res::Def(DefKind::AssocTy, _) if ns == TypeNS => true, _ => false, }, + PathSource::Delegation => matches!(res, Res::Def(DefKind::Fn | DefKind::AssocFn, _)), } } @@ -533,8 +540,8 @@ impl<'a> PathSource<'a> { (PathSource::Type, false) => error_code!(E0412), (PathSource::Struct, true) => error_code!(E0574), (PathSource::Struct, false) => error_code!(E0422), - (PathSource::Expr(..), true) => error_code!(E0423), - (PathSource::Expr(..), false) => error_code!(E0425), + (PathSource::Expr(..), true) | (PathSource::Delegation, true) => error_code!(E0423), + (PathSource::Expr(..), false) | (PathSource::Delegation, false) => error_code!(E0425), (PathSource::Pat | PathSource::TupleStruct(..), true) => error_code!(E0532), (PathSource::Pat | PathSource::TupleStruct(..), false) => error_code!(E0531), (PathSource::TraitItem(..), true) => error_code!(E0575), @@ -1805,7 +1812,8 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { PathSource::Expr(..) | PathSource::Pat | PathSource::Struct - | PathSource::TupleStruct(..) => true, + | PathSource::TupleStruct(..) + | PathSource::Delegation => true, }; if inferred { // Do not create a parameter for patterns and expressions: type checking can infer @@ -2514,6 +2522,10 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { visit::walk_item(self, item); } + ItemKind::Delegation(ref delegation) => { + self.resolve_delegation(delegation); + } + ItemKind::ExternCrate(..) => {} ItemKind::MacCall(_) => panic!("unexpanded macro in resolve!"), @@ -2790,6 +2802,9 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { AssocItemKind::Fn(box Fn { generics, .. }) => { walk_assoc_item(self, generics, LifetimeBinderKind::Function, item); } + AssocItemKind::Delegation(delegation) => { + self.resolve_delegation(delegation); + } AssocItemKind::Type(box TyAlias { generics, .. }) => self .with_lifetime_rib(LifetimeRibKind::AnonymousReportError, |this| { walk_assoc_item(this, generics, LifetimeBinderKind::Item, item) @@ -3036,6 +3051,19 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { }, ); } + AssocItemKind::Delegation(box delegation) => { + debug!("resolve_implementation AssocItemKind::Delegation"); + self.check_trait_item( + item.id, + item.ident, + &item.kind, + ValueNS, + item.span, + seen_trait_items, + |i, s, c| MethodNotMemberOfTrait(i, s, c), + ); + self.resolve_delegation(delegation); + } AssocItemKind::MacCall(_) => { panic!("unexpanded macro in resolve!") } @@ -3123,7 +3151,8 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { match (def_kind, kind) { (DefKind::AssocTy, AssocItemKind::Type(..)) | (DefKind::AssocFn, AssocItemKind::Fn(..)) - | (DefKind::AssocConst, AssocItemKind::Const(..)) => { + | (DefKind::AssocConst, AssocItemKind::Const(..)) + | (DefKind::AssocFn, AssocItemKind::Delegation(..)) => { self.r.record_partial_res(id, PartialRes::new(res)); return; } @@ -3136,6 +3165,7 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { AssocItemKind::Const(..) => (rustc_errors::error_code!(E0323), "const"), AssocItemKind::Fn(..) => (rustc_errors::error_code!(E0324), "method"), AssocItemKind::Type(..) => (rustc_errors::error_code!(E0325), "type"), + AssocItemKind::Delegation(..) => (rustc_errors::error_code!(E0324), "method"), AssocItemKind::MacCall(..) => span_bug!(span, "unexpanded macro"), }; let trait_path = path_names_to_string(path); @@ -3159,6 +3189,32 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { }) } + fn resolve_delegation(&mut self, delegation: &'ast Delegation) { + self.smart_resolve_path( + delegation.id, + &delegation.qself, + &delegation.path, + PathSource::Delegation, + ); + if let Some(qself) = &delegation.qself { + self.visit_ty(&qself.ty); + } + self.visit_path(&delegation.path, delegation.id); + if let Some(body) = &delegation.body { + // `PatBoundCtx` is not necessary in this context + let mut bindings = smallvec![(PatBoundCtx::Product, Default::default())]; + + let span = delegation.path.segments.last().unwrap().ident.span; + self.fresh_binding( + Ident::new(kw::SelfLower, span), + delegation.id, + PatternSource::FnParam, + &mut bindings, + ); + self.visit_block(body); + } + } + fn resolve_params(&mut self, params: &'ast [Param]) { let mut bindings = smallvec![(PatBoundCtx::Product, Default::default())]; self.with_lifetime_rib(LifetimeRibKind::Elided(LifetimeRes::Infer), |this| { @@ -4551,13 +4607,24 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { } } -struct LifetimeCountVisitor<'a, 'b, 'tcx> { +/// Walks the whole crate in DFS order, visiting each item, counting the declared number of +/// lifetime generic parameters and function parameters. +struct ItemInfoCollector<'a, 'b, 'tcx> { r: &'b mut Resolver<'a, 'tcx>, } -/// Walks the whole crate in DFS order, visiting each item, counting the declared number of -/// lifetime generic parameters. -impl<'ast> Visitor<'ast> for LifetimeCountVisitor<'_, '_, '_> { +impl ItemInfoCollector<'_, '_, '_> { + fn collect_fn_info(&mut self, sig: &FnSig, id: NodeId) { + let def_id = self.r.local_def_id(id); + self.r.fn_parameter_counts.insert(def_id, sig.decl.inputs.len()); + + if sig.decl.has_self() { + self.r.has_self.insert(def_id); + } + } +} + +impl<'ast> Visitor<'ast> for ItemInfoCollector<'_, '_, '_> { fn visit_item(&mut self, item: &'ast Item) { match &item.kind { ItemKind::TyAlias(box TyAlias { ref generics, .. }) @@ -4569,6 +4636,10 @@ impl<'ast> Visitor<'ast> for LifetimeCountVisitor<'_, '_, '_> { | ItemKind::Impl(box Impl { ref generics, .. }) | ItemKind::Trait(box Trait { ref generics, .. }) | ItemKind::TraitAlias(ref generics, _) => { + if let ItemKind::Fn(box Fn { ref sig, .. }) = &item.kind { + self.collect_fn_info(sig, item.id); + } + let def_id = self.r.local_def_id(item.id); let count = generics .params @@ -4586,14 +4657,27 @@ impl<'ast> Visitor<'ast> for LifetimeCountVisitor<'_, '_, '_> { | ItemKind::MacroDef(..) | ItemKind::GlobalAsm(..) | ItemKind::MacCall(..) => {} + ItemKind::Delegation(..) => { + // Delegated functions have lifetimes, their count is not necessarily zero. + // But skipping the delegation items here doesn't mean that the count will be considered zero, + // it means there will be a panic when retrieving the count, + // but for delegation items we are never actually retrieving that count in practice. + } } visit::walk_item(self, item) } + + fn visit_assoc_item(&mut self, item: &'ast AssocItem, ctxt: AssocCtxt) { + if let AssocItemKind::Fn(box Fn { ref sig, .. }) = &item.kind { + self.collect_fn_info(sig, item.id); + } + visit::walk_assoc_item(self, item, ctxt); + } } impl<'a, 'tcx> Resolver<'a, 'tcx> { pub(crate) fn late_resolve_crate(&mut self, krate: &Crate) { - visit::walk_crate(&mut LifetimeCountVisitor { r: self }, krate); + visit::walk_crate(&mut ItemInfoCollector { r: self }, krate); let mut late_resolution_visitor = LateResolutionVisitor::new(self); late_resolution_visitor.resolve_doc_links(&krate.attrs, MaybeExported::Ok(CRATE_NODE_ID)); visit::walk_crate(&mut late_resolution_visitor, krate); diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 2f476ae6cbc..58ff4d8c793 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -656,7 +656,10 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { let typo_sugg = self .lookup_typo_candidate(path, following_seg, source.namespace(), is_expected) .to_opt_suggestion(); - if path.len() == 1 && self.self_type_is_available() { + if path.len() == 1 + && !matches!(source, PathSource::Delegation) + && self.self_type_is_available() + { if let Some(candidate) = self.lookup_assoc_candidate(ident, ns, is_expected, source.is_call()) { @@ -1899,6 +1902,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { (AssocItemKind::Const(..), Res::Def(DefKind::AssocConst, _)) => true, (AssocItemKind::Fn(_), Res::Def(DefKind::AssocFn, _)) => true, (AssocItemKind::Type(..), Res::Def(DefKind::AssocTy, _)) => true, + (AssocItemKind::Delegation(_), Res::Def(DefKind::AssocFn, _)) => true, _ => false, }) .map(|(key, _)| key.ident.name) @@ -1960,6 +1964,12 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { } ast::AssocItemKind::Fn(..) => AssocSuggestion::AssocFn { called }, ast::AssocItemKind::Type(..) => AssocSuggestion::AssocType, + ast::AssocItemKind::Delegation(..) + if self.r.has_self.contains(&self.r.local_def_id(assoc_item.id)) => + { + AssocSuggestion::MethodWithSelf { called } + } + ast::AssocItemKind::Delegation(..) => AssocSuggestion::AssocFn { called }, ast::AssocItemKind::MacCall(_) => continue, }); } diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index a14f3d494fb..0adea65ee58 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -1101,6 +1101,8 @@ pub struct Resolver<'a, 'tcx> { legacy_const_generic_args: FxHashMap<DefId, Option<Vec<usize>>>, /// Amount of lifetime parameters for each item in the crate. item_generics_num_lifetimes: FxHashMap<LocalDefId, usize>, + /// Amount of parameters for each function in the crate. + fn_parameter_counts: LocalDefIdMap<usize>, main_def: Option<MainDefinition>, trait_impls: FxIndexMap<DefId, Vec<LocalDefId>>, @@ -1439,6 +1441,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { doc_link_resolutions: Default::default(), doc_link_traits_in_scope: Default::default(), all_macro_rules: Default::default(), + fn_parameter_counts: Default::default(), }; let root_parent_scope = ParentScope::module(graph_root, &resolver); @@ -1542,6 +1545,8 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { trait_map: self.trait_map, lifetime_elision_allowed: self.lifetime_elision_allowed, lint_buffer: Steal::new(self.lint_buffer), + has_self: self.has_self, + fn_parameter_counts: self.fn_parameter_counts, }; ResolverOutputs { global_ctxt, ast_lowering } } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 8ed1255c010..72b62a795fc 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -102,6 +102,7 @@ symbols! { Gen: "gen", MacroRules: "macro_rules", Raw: "raw", + Reuse: "reuse", Union: "union", Yeet: "yeet", } |
