diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 5755ae8a8bc47..a1cfa9827ebe7 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -2978,6 +2978,14 @@ pub struct Fn { pub body: Option>, } +#[derive(Clone, Encodable, Decodable, Debug)] +pub struct Delegation { + pub id: NodeId, + pub path: (Option>, Path), + pub target_expr: Option>, + pub span: Span, +} + #[derive(Clone, Encodable, Decodable, Debug)] pub struct StaticItem { pub ty: P, @@ -3063,6 +3071,11 @@ pub enum ItemKind { /// A macro definition. MacroDef(MacroDef), + + /// A delegation item (`reuse`). + /// + /// E.g. reuse ::name { target_expr_template } + Delegation(Box), } impl ItemKind { @@ -3070,7 +3083,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", } } @@ -3094,6 +3108,7 @@ impl ItemKind { ItemKind::MacCall(..) => "item macro invocation", ItemKind::MacroDef(..) => "macro definition", ItemKind::Impl { .. } => "implementation", + ItemKind::Delegation(..) => "delegation", } } @@ -3135,6 +3150,8 @@ pub enum AssocItemKind { Type(Box), /// A macro expanding to associated items. MacCall(P), + /// An associated delegation item. + Delegation(Box), } impl AssocItemKind { @@ -3143,7 +3160,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, } } } @@ -3155,6 +3172,7 @@ impl From 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), } } } @@ -3168,6 +3186,7 @@ impl TryFrom 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/ast_traits.rs b/compiler/rustc_ast/src/ast_traits.rs index 4dc9c30a2c807..2b5377c3457dd 100644 --- a/compiler/rustc_ast/src/ast_traits.rs +++ b/compiler/rustc_ast/src/ast_traits.rs @@ -5,7 +5,7 @@ use crate::ptr::P; use crate::token::Nonterminal; use crate::tokenstream::LazyAttrTokenStream; -use crate::{Arm, Crate, ExprField, FieldDef, GenericParam, Param, PatField, Variant}; +use crate::{Arm, Crate, Delegation, ExprField, FieldDef, GenericParam, Param, PatField, Variant}; use crate::{AssocItem, Expr, ForeignItem, Item, NodeId}; use crate::{AttrItem, AttrKind, Block, Pat, Path, Ty, Visibility}; use crate::{AttrVec, Attribute, Stmt, StmtKind}; @@ -80,6 +80,7 @@ impl_has_node_id!( Stmt, Ty, Variant, + Delegation ); impl> HasNodeId for T { @@ -108,7 +109,19 @@ macro_rules! impl_has_span { }; } -impl_has_span!(AssocItem, Block, Expr, ForeignItem, Item, Pat, Path, Stmt, Ty, Visibility); +impl_has_span!( + AssocItem, + Block, + Expr, + ForeignItem, + Item, + Pat, + Path, + Stmt, + Ty, + Visibility, + Delegation +); impl> HasSpan for T { fn span(&self) -> Span { @@ -159,7 +172,7 @@ macro_rules! impl_has_tokens_none { } impl_has_tokens!(AssocItem, AttrItem, Block, Expr, ForeignItem, Item, Pat, Path, Ty, Visibility); -impl_has_tokens_none!(Arm, ExprField, FieldDef, GenericParam, Param, PatField, Variant); +impl_has_tokens_none!(Arm, ExprField, FieldDef, GenericParam, Param, PatField, Variant, Delegation); impl> HasTokens for T { fn tokens(&self) -> Option<&LazyAttrTokenStream> { diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index 10b2025f93783..d16c2ee7aa1bf 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(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) => { + vis.visit_id(&mut delegation.id); + vis.visit_qself(&mut delegation.path.0); + vis.visit_path(&mut delegation.path.1); + if let Some(target_expr) = &mut delegation.target_expr { + vis.visit_expr(target_expr); + } + } } } @@ -1155,6 +1163,14 @@ pub fn noop_flat_map_assoc_item( visit_opt(ty, |ty| visitor.visit_ty(ty)); } AssocItemKind::MacCall(mac) => visitor.visit_mac_call(mac), + AssocItemKind::Delegation(box delegation) => { + visitor.visit_id(&mut delegation.id); + visitor.visit_qself(&mut delegation.path.0); + visitor.visit_path(&mut delegation.path.1); + if let Some(target_expr) = &mut delegation.target_expr { + visitor.visit_expr(target_expr); + } + } } 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 27f1b84f37251..2fca9724bbac3 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) => { + if let Some(qself) = &delegation.path.0 { + visitor.visit_ty(&qself.ty); + } + walk_path(visitor, &delegation.path.1); + if let Some(target_expr) = &delegation.target_expr { + visitor.visit_expr(target_expr); + } + } } 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) => { + if let Some(qself) = &delegation.path.0 { + visitor.visit_ty(&qself.ty); + } + walk_path(visitor, &delegation.path.1); + if let Some(target_expr) = &delegation.target_expr { + visitor.visit_expr(target_expr); + } + } } } diff --git a/compiler/rustc_ast_lowering/src/delegation.rs b/compiler/rustc_ast_lowering/src/delegation.rs new file mode 100644 index 0000000000000..97b0792e9e529 --- /dev/null +++ b/compiler/rustc_ast_lowering/src/delegation.rs @@ -0,0 +1,277 @@ +use crate::{ImplTraitPosition, ResolverAstLoweringExt}; + +use super::{ImplTraitContext, LoweringContext, ParamMode}; + +use ast::visit::Visitor; +use hir::def::DefKind; +use hir::def::PartialRes; +use hir::{BodyId, HirId}; +use rustc_ast as ast; +use rustc_ast::*; +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::kw; +use rustc_span::{symbol::Ident, Span}; +use rustc_target::spec::abi; +use std::iter; + +pub struct DelegationResults<'hir> { + pub body_id: hir::BodyId, + pub sig: hir::FnSig<'hir>, + pub generics: &'hir hir::Generics<'hir>, +} + +impl<'hir> LoweringContext<'_, 'hir> { + pub fn delegation_has_self(&self, delegation: &Delegation) -> bool { + let res_id = self.get_delegation_res_id(delegation.id); + let Some(res_id) = res_id else { + self.tcx.sess.span_delayed_bug( + delegation.span, + "LoweringContext: couldn't resolve delegation item", + ); + return false; + }; + + if let Some(local_res_id) = res_id.as_local() { + self.resolver.has_self.contains(&local_res_id) + } else { + match self.tcx.def_kind(res_id) { + DefKind::Fn => false, + DefKind::AssocFn => self.tcx.associated_item(res_id).fn_has_self_parameter, + _ => span_bug!(delegation.span, "unexpected DefKind for delegation item"), + } + } + } + + pub fn lower_delegation(&mut self, delegation: &Delegation) -> DelegationResults<'hir> { + let res_id = self.get_delegation_res_id(delegation.id); + let Some(res_id) = res_id else { + self.tcx.sess.span_delayed_bug( + delegation.span, + "LoweringContext: couldn't resolve delegation item", + ); + return self.generate_delegation_error(delegation); + }; + + let decl = self.lower_delegation_decl(res_id, delegation.span); + let sig = self.lower_delegation_sig(delegation.span, decl); + let body_id = self.lower_delegation_body(sig.decl, delegation); + + let generics = self.lower_delegation_generics(delegation.span); + DelegationResults { body_id, sig, generics } + } + + fn get_delegation_res_id(&self, node_id: NodeId) -> Option { + self.resolver.get_partial_res(node_id).map(|r| r.base_res().opt_def_id()).unwrap_or(None) + } + + fn lower_delegation_generics(&mut self, span: Span) -> &'hir hir::Generics<'hir> { + self.arena.alloc(hir::Generics { + params: self.arena.alloc_from_iter([]), + predicates: self.arena.alloc_from_iter([]), + has_where_clause_predicates: false, + where_clause_span: span, + span, + }) + } + + fn lower_delegation_decl(&mut self, res_id: DefId, span: Span) -> &'hir hir::FnDecl<'hir> { + let args_count = if res_id.is_local() { + self.resolver.fns_arguments_count[&res_id] + } else { + self.tcx.fn_arg_names(res_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(res_id, hir::InferDelegationVar::Input(arg)), + span, + })); + + let output = self.arena.alloc(hir::Ty { + hir_id: self.next_id(), + kind: hir::TyKind::InferDelegation(res_id, hir::InferDelegationVar::Output), + 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: hir::def::Res::Local(param_id), + args: None, + infer_args: false, + })); + + let path = self.arena.alloc(hir::Path { + span: ty.span, + res: hir::def::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 (qself, path) = &delegation.path; + let path = self.lower_qpath( + delegation.id, + qself, + path, + ParamMode::Optional, + &ImplTraitContext::Disallowed(ImplTraitPosition::Path), + None, + ); + let expr = delegation.target_expr.as_deref(); + + self.lower_body(|this| { + let mut parameters: Vec> = Vec::new(); + let mut args: Vec> = Vec::new(); + + for (idx, param_ty) in decl.inputs.iter().enumerate() { + let (param, pat_node_id) = this.generate_param(param_ty); + parameters.push(param); + + if idx != 0 || expr.is_none() { + let pat_hir_id = this.lower_node_id(pat_node_id); + let arg = this.generate_arg(param_ty, pat_hir_id); + args.push(arg); + } else { + let expr = expr.unwrap(); + let mut self_resolver = + SelfResolver { res_id: pat_node_id, resolver: this.resolver }; + self_resolver.visit_expr(expr); + let target_expr = this.lower_expr_mut(expr); + args.push(target_expr); + }; + } + + 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: self.arena.alloc_from_iter([]), + 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, delegation: &Delegation) -> DelegationResults<'hir> { + let generics = self.lower_delegation_generics(delegation.span); + + let decl = self.arena.alloc(hir::FnDecl { + inputs: self.arena.alloc_from_iter([]), + output: hir::FnRetTy::DefaultReturn(delegation.span), + c_variadic: false, + lifetime_elision_allowed: true, + implicit_self: hir::ImplicitSelfKind::None, + }); + + let sig = self.lower_delegation_sig(delegation.span, decl); + let body_id = self.lower_body(|this| { + let err = this.tcx.sess.span_delayed_bug( + delegation.span, + "LoweringContext: couldn't resolve delegation item", + ); + let expr = hir::Expr { + hir_id: this.next_id(), + kind: hir::ExprKind::Err(err), + span: delegation.span, + }; + (this.arena.alloc_from_iter([]), expr) + }); + DelegationResults { generics, body_id, sig } + } +} + +struct SelfResolver<'a> { + resolver: &'a mut ResolverAstLowering, + res_id: NodeId, +} + +impl<'ast, 'a> ast::visit::Visitor<'ast> for SelfResolver<'a> { + fn visit_path(&mut self, path: &'ast Path, id: NodeId) { + if path.segments.len() == 1 && path.segments[0].ident.name == kw::SelfLower { + let res = PartialRes::new(hir::def::Res::Local(self.res_id)); + self.resolver.partial_res_map.insert(id, res); + self.resolver.partial_res_map.insert(path.segments[0].id, res); + } + } +} diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index 7c05724f64c95..3cba35da7d9c8 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -427,6 +427,16 @@ 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) => self.with_new_scopes(ident.span, |this| { + this.current_item = Some(ident.span); + + let delegation_results = this.lower_delegation(delegation); + hir::ItemKind::Fn( + delegation_results.sig, + delegation_results.generics, + delegation_results.body_id, + ) + }), ItemKind::MacCall(..) => { panic!("`TyMac` should have been expanded by now") } @@ -787,6 +797,17 @@ impl<'hir> LoweringContext<'_, 'hir> { ); (generics, kind, ty.is_some()) } + AssocItemKind::Delegation(box delegation) => { + let delegation_results = self.lower_delegation(delegation); + ( + delegation_results.generics, + hir::TraitItemKind::Fn( + delegation_results.sig, + hir::TraitFn::Provided(delegation_results.body_id), + ), + true, + ) + } AssocItemKind::MacCall(..) => panic!("macro item shouldn't exist at this point"), }; @@ -808,6 +829,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(delegation) } + } AssocItemKind::MacCall(..) => unimplemented!(), }; let id = hir::TraitItemId { owner_id: hir::OwnerId { def_id: self.local_def_id(i.id) } }; @@ -890,6 +914,13 @@ impl<'hir> LoweringContext<'_, 'hir> { }, ) } + AssocItemKind::Delegation(box delegation) => { + let delegation_results = self.lower_delegation(delegation); + ( + delegation_results.generics, + hir::ImplItemKind::Fn(delegation_results.sig, delegation_results.body_id), + ) + } AssocItemKind::MacCall(..) => panic!("`TyMac` should have been expanded by now"), }; @@ -916,6 +947,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(delegation) } + } AssocItemKind::MacCall(..) => unimplemented!(), }, trait_item_def_id: self diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index d9663d50c595c..1db967a73705d 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -78,6 +78,7 @@ macro_rules! arena_vec { mod asm; mod block; +mod delegation; mod errors; mod expr; mod format; diff --git a/compiler/rustc_ast_pretty/src/pprust/state/item.rs b/compiler/rustc_ast_pretty/src/pprust/state/item.rs index ea5d22a344877..e22759ae31364 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/item.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/item.rs @@ -2,7 +2,7 @@ use crate::pp::Breaks::Inconsistent; use crate::pprust::state::expr::FixupContext; use crate::pprust::state::{AnnNode, PrintState, State, INDENT_UNIT}; -use ast::StaticItem; +use ast::{AstDeref, StaticItem}; use itertools::{Itertools, Position}; use rustc_ast as ast; use rustc_ast::GenericBound; @@ -387,6 +387,7 @@ impl<'a> State<'a> { state.print_visibility(&item.vis) }); } + ast::ItemKind::Delegation(box delegation) => self.print_delegation(delegation), } self.ann.post(self, AnnNode::Item(item)) } @@ -565,10 +566,33 @@ impl<'a> State<'a> { self.word(";"); } } + ast::AssocItemKind::Delegation(box delegation) => self.print_delegation(delegation), } self.ann.post(self, AnnNode::SubItem(id)) } + pub(crate) fn print_delegation(&mut self, delegation: &ast::Delegation) { + self.ibox(0); + self.word_space("reuse"); + let (qself, path, expr) = (&delegation.path.0, &delegation.path.1, &delegation.target_expr); + + if let Some(qself) = qself { + self.print_qpath(path, qself.ast_deref(), false); + } else { + self.print_path(path, false, 0); + } + self.space(); + if let Some(target_expr) = expr { + self.word_space("{"); + self.print_expr(target_expr.ast_deref(), FixupContext::default()); + self.word(" }"); + } else { + self.word(";"); + } + + self.end(); + } + 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 01508375b1ace..c42615e08bdfa 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -2526,9 +2526,16 @@ pub enum OpaqueTyOrigin { }, } +#[derive(Debug, Clone, Copy, PartialEq, Eq, HashStable_Generic)] +pub enum InferDelegationVar { + Input(usize), + Output, +} + /// The various kinds of types recognized by the compiler. #[derive(Debug, Clone, Copy, HashStable_Generic)] pub enum TyKind<'hir> { + InferDelegation(DefId, InferDelegationVar), /// 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 9cf1db166a581..ab5b0c44c5ca8 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -861,7 +861,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 139e1c0ac5fdc..6536cf19197cd 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 = + delegation to {$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 caae6fa4f065e..7d7fafbe5f1ef 100644 --- a/compiler/rustc_hir_analysis/src/astconv/mod.rs +++ b/compiler/rustc_hir_analysis/src/astconv/mod.rs @@ -2405,6 +2405,109 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { self.ast_ty_to_ty_inner(ast_ty, false, true) } + fn check_delegation_constraints(&self, res_id: DefId, span: Span, emit: bool) -> bool { + let mut error_occured = false; + let mut try_emit = |error| { + if emit { + self.tcx().sess.emit_err(error); + } + error_occured = true; + }; + let res_span = self.tcx().def_span(res_id); + + let res_generics = self.tcx().generics_of(res_id); + let parent = self.tcx().parent(self.item_def_id()); + let parent_generics = self.tcx().generics_of(parent); + let self_ty_bias = (self.tcx().def_kind(parent) == DefKind::Trait) as usize; + if parent_generics.count() + res_generics.own_counts().count() > self_ty_bias { + try_emit(super::errors::NotSupportedDelegation { + span, + descr: "generic function", + res_span, + }); + } + if self.tcx().asyncness(res_id) == ty::Asyncness::Yes { + try_emit(super::errors::NotSupportedDelegation { + span, + descr: "async function", + res_span, + }); + } + + if self.tcx().constness(res_id) == hir::Constness::Const { + try_emit(super::errors::NotSupportedDelegation { + span, + descr: "const function", + res_span, + }); + } + + let res_sig = self.tcx().fn_sig(res_id).instantiate_identity(); + if res_sig.c_variadic() { + try_emit(super::errors::NotSupportedDelegation { + span, + descr: "variadic function", + res_span, + }); + return error_occured; + } + + if let hir::Unsafety::Unsafe = res_sig.unsafety() { + try_emit(super::errors::NotSupportedDelegation { + span, + descr: "unsafe function", + res_span, + }); + } + + if abi::Abi::Rust != res_sig.abi() { + try_emit(super::errors::NotSupportedDelegation { + span, + descr: "function with non Rust ABI", + res_span, + }); + } + + error_occured + } + + fn ty_from_delegation( + &self, + res_id: DefId, + idx: hir::InferDelegationVar, + span: Span, + ) -> Ty<'tcx> { + if self.check_delegation_constraints(res_id, span, idx == hir::InferDelegationVar::Output) { + let e = self.tcx().sess.span_delayed_bug(span, "not supported delegation case"); + self.set_tainted_by_errors(e); + return Ty::new_error(self.tcx(), e); + }; + let res_sig = self.tcx().fn_sig(res_id); + + let parent = self.tcx().parent(self.item_def_id()); + let item_def_kind = self.tcx().def_kind(self.item_def_id()); + let parent_def_kind = self.tcx().def_kind(parent); + + let res_sig = match (item_def_kind, parent_def_kind) { + (DefKind::AssocFn, DefKind::Impl { of_trait: true }) => { + 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)); + res_sig.instantiate(self.tcx(), substs).skip_binder() + } + (DefKind::Fn, _) | (DefKind::AssocFn, _) => { + res_sig.instantiate_identity().skip_binder() + } + _ => span_bug!(span, "ty_from_delegation: couldn't calculate resolution signature"), + }; + + if let hir::InferDelegationVar::Input(id) = idx { + res_sig.inputs()[id] + } else { + res_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)] @@ -2412,6 +2515,9 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { let tcx = self.tcx(); let result_ty = match &ast_ty.kind { + hir::TyKind::InferDelegation(res_id, idx) => { + self.ty_from_delegation(*res_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 }) diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index a47722936975e..8dbe3c3e4f931 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -1411,3 +1411,13 @@ pub struct OnlyCurrentTraitsPointerSugg<'a> { pub mut_key: &'a str, pub ptr_ty: Ty<'a>, } + +#[derive(Diagnostic)] +#[diag(hir_analysis_not_supported_delegation)] +pub struct NotSupportedDelegation<'a> { + #[primary_span] + pub span: Span, + pub descr: &'a str, + #[label] + pub res_span: Span, +} diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index 8701626058d43..0068461d32590 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/generics.rs b/compiler/rustc_middle/src/ty/generics.rs index c3699b114c411..61110e85fce03 100644 --- a/compiler/rustc_middle/src/ty/generics.rs +++ b/compiler/rustc_middle/src/ty/generics.rs @@ -122,6 +122,12 @@ pub struct GenericParamCount { pub consts: usize, } +impl GenericParamCount { + pub fn count(&self) -> usize { + self.lifetimes + self.types + self.consts + } +} + /// Information about the formal type/lifetime parameters associated /// with an item or method. Analogous to `hir::Generics`. /// diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 71ff7021ca5de..126dc58f30782 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -41,7 +41,7 @@ use rustc_data_structures::tagged_ptr::CopyTaggedPtr; 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; @@ -201,6 +201,10 @@ pub struct ResolverAstLowering { /// Lints that were emitted by the resolver and early lints. pub lint_buffer: Steal, + + /// Information about functions signatures for delegation items expansion + pub has_self: LocalDefIdSet, + pub fns_arguments_count: FxHashMap, } #[derive(Clone, Copy, Debug)] diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 89919247e8228..339bfdbad67fd 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -255,6 +255,8 @@ impl<'a> Parser<'a> { { // IMPL ITEM self.parse_item_impl(attrs, def_())? + } else if self.check_keyword(kw::Reuse) { + self.parse_item_delegation()? } else if self.check_keyword(kw::Mod) || self.check_keyword(kw::Unsafe) && self.is_keyword_ahead(1, &[kw::Mod]) { @@ -352,11 +354,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 { + self.is_kw_followed_by_ident(kw::Reuse) // no: `reuse::b`, yes: `reuse U::` + // no: `reuse::b`, yes: reuse :: + || (self.token.is_keyword(kw::Reuse) && self.look_ahead(1, |t| *t == token::Lt)) + } + /// 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) @@ -658,6 +667,39 @@ impl<'a> Parser<'a> { Ok((Ident::empty(), item_kind)) } + fn parse_item_delegation(&mut self) -> PResult<'a, ItemInfo> { + self.expect_keyword(kw::Reuse)?; + let span = self.prev_token.span; + + 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 expr = if self.eat(&token::OpenDelim(Delimiter::Brace)) { + let expr = self.parse_expr()?; + self.expect(&token::CloseDelim(Delimiter::Brace))?; + Some(expr) + } else { + self.expect(&token::Semi)?; + None + }; + + let ident = + path.segments.last().and_then(|segment| Some(segment.ident)).unwrap_or(Ident::empty()); + Ok(( + ident, + ItemKind::Delegation(Box::new(Delegation { + id: DUMMY_NODE_ID, + path: (qself, path), + target_expr: expr, + span: span.to(self.prev_token.span), + })), + )) + } + fn parse_item_list( &mut self, attrs: &mut AttrVec, diff --git a/compiler/rustc_passes/src/hir_stats.rs b/compiler/rustc_passes/src/hir_stats.rs index 85969b72d2397..a6bb675d08fcd 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_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index 213b320ed1a66..65440f3e51a78 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -788,6 +788,11 @@ impl<'a, 'b, 'tcx> BuildReducedGraphVisitor<'a, 'b, 'tcx> { ItemKind::Impl(box Impl { of_trait: Some(..), .. }) => { self.r.trait_impl_items.insert(local_def_id); } + + ItemKind::Delegation(..) => { + let res = Res::Def(DefKind::Fn, def_id); + self.r.define(parent, ident, ValueNS, (res, vis, sp, expansion)); + } ItemKind::Impl { .. } | ItemKind::ForeignMod(..) | ItemKind::GlobalAsm(..) => {} ItemKind::MacroDef(..) | ItemKind::MacCall(_) => unreachable!(), @@ -1367,6 +1372,7 @@ impl<'a, 'b, 'tcx> Visitor<'b> for BuildReducedGraphVisitor<'a, 'b, 'tcx> { ValueNS } AssocItemKind::Type(..) => TypeNS, + AssocItemKind::Delegation(..) => ValueNS, AssocItemKind::MacCall(_) => bug!(), // handled above }; diff --git a/compiler/rustc_resolve/src/def_collector.rs b/compiler/rustc_resolve/src/def_collector.rs index 02553d5007155..b7f90c93a5758 100644 --- a/compiler/rustc_resolve/src/def_collector.rs +++ b/compiler/rustc_resolve/src/def_collector.rs @@ -112,6 +112,7 @@ impl<'a, 'b, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'b, 'tcx> { ItemKind::Static(s) => DefKind::Static(s.mutability), ItemKind::Const(..) => DefKind::Const, ItemKind::Fn(..) => DefKind::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(); @@ -154,8 +155,12 @@ impl<'a, 'b, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'b, 'tcx> { }); } - fn visit_fn(&mut self, fn_kind: FnKind<'a>, span: Span, _: NodeId) { + fn visit_fn(&mut self, fn_kind: FnKind<'a>, span: Span, fn_id: NodeId) { if let FnKind::Fn(_, _, sig, _, generics, body) = fn_kind { + self.resolver + .fns_arguments_count + .insert(self.resolver.node_id_to_def_id[&fn_id].to_def_id(), sig.decl.inputs.len()); + match sig.header.coroutine_kind { Some(coroutine_kind) => { self.visit_generics(generics); @@ -262,6 +267,7 @@ impl<'a, 'b, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'b, 'tcx> { AssocItemKind::Fn(..) => DefKind::AssocFn, AssocItemKind::Const(..) => DefKind::AssocConst, AssocItemKind::Type(..) => DefKind::AssocTy, + AssocItemKind::Delegation(..) => DefKind::AssocFn, 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 a71c50dd82fdd..8cc6ef30dd2a1 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 9c96e9a9bd728..a07a69da60463 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 `::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, _)), } } @@ -539,6 +546,7 @@ impl<'a> PathSource<'a> { (PathSource::Pat | PathSource::TupleStruct(..), false) => error_code!(E0531), (PathSource::TraitItem(..), true) => error_code!(E0575), (PathSource::TraitItem(..), false) => error_code!(E0576), + (PathSource::Delegation, _) => error_code!(E0795), } } } @@ -673,6 +681,8 @@ struct LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { /// If it is `true`, then it will be updated when entering a nested function or trait body. in_func_body: bool, + in_delegation_item: bool, + /// Count the number of places a lifetime is used. lifetime_uses: FxHashMap, } @@ -896,6 +906,7 @@ impl<'a: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast, } fn visit_fn(&mut self, fn_kind: FnKind<'ast>, sp: Span, fn_id: NodeId) { let previous_value = self.diagnostic_metadata.current_function; + match fn_kind { // Bail if the function is foreign, and thus cannot validly have // a body, or if there's no body for some other reason. @@ -1289,6 +1300,7 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { diagnostic_metadata: Default::default(), // errors at module scope should always be reported in_func_body: false, + in_delegation_item: false, lifetime_uses: Default::default(), } } @@ -1331,6 +1343,10 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { opt_ns: Option, // `None` indicates a module path in import finalize: Option, ) -> PathResult<'a> { + if self.in_delegation_item && path.len() == 1 && path[0].ident.name == kw::SelfLower { + return PathResult::NonModule(def::PartialRes::new(def::Res::Err)); + } + self.r.resolve_path_with_ribs( path, opt_ns, @@ -1805,7 +1821,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 @@ -2518,9 +2535,15 @@ 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!"), + ItemKind::MacCall(_) => { + panic!("unexpanded item in resolve!") + } } } @@ -2794,13 +2817,14 @@ 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) }), - AssocItemKind::MacCall(_) => { - panic!("unexpanded macro in resolve!") - } + AssocItemKind::MacCall(_) => panic!("unexpanded assoc item in resolve!"), }; } @@ -3040,6 +3064,23 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { }, ); } + AssocItemKind::Delegation(box delegation) => { + debug!("resolve_implementation AssocItemKind::Delegation"); + let emitted = self.check_trait_item( + item.id, + item.ident, + &item.kind, + ValueNS, + item.span, + seen_trait_items, + |i, s, c| MethodNotMemberOfTrait(i, s, c), + ); + + // do not emit same errors twice + if !emitted { + self.resolve_delegation(delegation); + } + } AssocItemKind::MacCall(_) => { panic!("unexpanded macro in resolve!") } @@ -3055,12 +3096,13 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { span: Span, seen_trait_items: &mut FxHashMap, err: F, - ) where + ) -> bool + where F: FnOnce(Ident, String, Option) -> ResolutionError<'a>, { // If there is a TraitRef in scope for an impl, then the method must be in the trait. let Some((module, _)) = self.current_trait_ref else { - return; + return false; }; ident.span.normalize_to_macros_2_0_and_adjust(module.expansion); let key = BindingKey::new(ident, ns); @@ -3084,7 +3126,7 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { let path = &self.current_trait_ref.as_ref().unwrap().1.path; let path_names = path_names_to_string(path); self.report_error(span, err(ident, path_names, candidate)); - return; + return true; }; let res = binding.res(); @@ -3100,7 +3142,7 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { trait_item_span: binding.span, }, ); - return; + return false; } Entry::Vacant(entry) => { entry.insert(span); @@ -3110,9 +3152,10 @@ 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; + return false; } _ => {} } @@ -3123,6 +3166,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); @@ -3136,6 +3180,7 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { trait_item_span: binding.span, }, ); + true } fn resolve_const_body(&mut self, expr: &'ast Expr, item: Option<(Ident, ConstantItemKind)>) { @@ -3146,6 +3191,21 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { }) } + fn resolve_delegation(&mut self, delegation: &'ast Delegation) { + self.in_delegation_item = true; + + let (qself, path) = &delegation.path; + if let Some(qself) = &delegation.path.0 { + self.visit_ty(&qself.ty); + } + self.smart_resolve_path(delegation.id, qself, path, PathSource::Delegation); + + if let Some(target_expr) = &delegation.target_expr { + self.visit_expr(target_expr); + } + self.in_delegation_item = false; + } + 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| { @@ -4572,7 +4632,8 @@ impl<'ast> Visitor<'ast> for LifetimeCountVisitor<'_, '_, '_> { | ItemKind::ExternCrate(..) | ItemKind::MacroDef(..) | ItemKind::GlobalAsm(..) - | ItemKind::MacCall(..) => {} + | ItemKind::MacCall(..) + | ItemKind::Delegation(..) => {} } visit::walk_item(self, item) } diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index d767ed74139ba..13cd260cd0382 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -221,6 +221,18 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { let mut span_label = None; let item_ident = path.last().unwrap().ident; let item_span = item_ident.span; + if let PathSource::Delegation = source { + return BaseError { + msg: format!("couldn't resolve `{item_str}` in delegation item"), + fallback_label: "delegation is only supported yet for trait methods with explicit paths and free functions".to_string(), + span: item_span, + span_label, + could_be_expr: false, + suggestion: None, + module: None, + }; + } + let (mod_prefix, mod_str, module, suggestion) = if path.len() == 1 { debug!(?self.diagnostic_metadata.current_impl_items); debug!(?self.diagnostic_metadata.current_function); @@ -435,7 +447,13 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { code, ); + // FIXME: suggestions do not supported yet for delegation item + if let PathSource::Delegation = source { + err.span_label(base_error.span, base_error.fallback_label); + return (err, Vec::new()); + } self.suggest_at_operator_in_slice_pat_with_range(&mut err, path); + self.suggest_swapping_misplaced_self_ty_and_trait(&mut err, source, res, base_error.span); if let Some((span, label)) = base_error.span_label { @@ -1905,6 +1923,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) @@ -1966,6 +1985,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 70e0eb12c01da..d8ee285017462 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -1119,6 +1119,8 @@ pub struct Resolver<'a, 'tcx> { doc_link_resolutions: FxHashMap, doc_link_traits_in_scope: FxHashMap>, all_macro_rules: FxHashMap, + + fns_arguments_count: FxHashMap, } /// Nothing really interesting here; it just provides memory for the rest of the crate. @@ -1446,6 +1448,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { doc_link_resolutions: Default::default(), doc_link_traits_in_scope: Default::default(), all_macro_rules: Default::default(), + fns_arguments_count: Default::default(), }; let root_parent_scope = ParentScope::module(graph_root, &resolver); @@ -1544,6 +1547,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, + fns_arguments_count: self.fns_arguments_count, }; ResolverOutputs { global_ctxt, ast_lowering } } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 0333b5f04c3bc..9ffeacd5bb91f 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -101,6 +101,7 @@ symbols! { Gen: "gen", MacroRules: "macro_rules", Raw: "raw", + Reuse: "reuse", Union: "union", Yeet: "yeet", } diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 28ea662eab378..35f0f55895b04 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -1881,7 +1881,9 @@ pub(crate) fn clean_ty<'tcx>(ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> T } TyKind::BareFn(barefn) => BareFunction(Box::new(clean_bare_fn_ty(barefn, cx))), // Rustdoc handles `TyKind::Err`s by turning them into `Type::Infer`s. - TyKind::Infer | TyKind::Err(_) | TyKind::Typeof(..) => Infer, + TyKind::Infer | TyKind::Err(_) | TyKind::Typeof(..) | TyKind::DelegationPlaceholder(..) => { + Infer + } } } diff --git a/tests/ui/delegation/bad-resolve.rs b/tests/ui/delegation/bad-resolve.rs new file mode 100644 index 0000000000000..a1bc9534b27f9 --- /dev/null +++ b/tests/ui/delegation/bad-resolve.rs @@ -0,0 +1,44 @@ +trait Trait { + const C: u32 = 0; + type Type; + fn bar() {} + fn foo(&self, x: i32) -> i32 { x } +} + +struct F; +impl Trait for F { + type Type = i32; +} + +impl F { + fn foo(&self, x: i32) -> i32 { x } +} + +struct S(F); + +impl Trait for S { +//~^ not all trait items implemented, missing: `Type` + reuse ::C; + //~^ ERROR item `C` is an associated method, which doesn't match its trait `Trait` + reuse ::Type; + //~^ ERROR item `Type` is an associated method, which doesn't match its trait `Trait` + reuse ::baz; + //~^ ERROR method `baz` is not a member of trait `Trait` + reuse ::bar; + + reuse foo { &self.0 } + //~^ ERROR couldn't resolve `foo` in delegation item + //~| method `foo` has a `&self` declaration in the trait, but not in the impl + reuse F::foo { &self.0 } + //~^ ERROR couldn't resolve `foo` in delegation item + //~| ERROR duplicate definitions with name `foo` +} + +impl S { + reuse F::foo { &self.0 } + //~^ ERROR couldn't resolve `foo` in delegation item +} + +fn main() { + +} diff --git a/tests/ui/delegation/bad-resolve.stderr b/tests/ui/delegation/bad-resolve.stderr new file mode 100644 index 0000000000000..f6dd9f2282fe9 --- /dev/null +++ b/tests/ui/delegation/bad-resolve.stderr @@ -0,0 +1,79 @@ +error[E0324]: item `C` is an associated method, which doesn't match its trait `Trait` + --> $DIR/bad-resolve.rs:21:5 + | +LL | const C: u32 = 0; + | ----------------- item in trait +... +LL | reuse ::C; + | ^^^^^^^^^^^^^^^^^^^^^^ does not match trait + +error[E0324]: item `Type` is an associated method, which doesn't match its trait `Trait` + --> $DIR/bad-resolve.rs:23:5 + | +LL | type Type; + | ---------- item in trait +... +LL | reuse ::Type; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ does not match trait + +error[E0407]: method `baz` is not a member of trait `Trait` + --> $DIR/bad-resolve.rs:25:5 + | +LL | reuse ::baz; + | ^^^^^^^^^^^^^^^^^^^^---^ + | | | + | | help: there is an associated function with a similar name: `bar` + | not a member of trait `Trait` + +error[E0201]: duplicate definitions with name `foo`: + --> $DIR/bad-resolve.rs:32:5 + | +LL | fn foo(&self, x: i32) -> i32 { x } + | ---------------------------------- item in trait +... +LL | reuse foo { &self.0 } + | --------------------- previous definition here +... +LL | reuse F::foo { &self.0 } + | ^^^^^^^^^^^^^^^^^^^^^^^^ duplicate definition + +error[E0795]: couldn't resolve `foo` in delegation item + --> $DIR/bad-resolve.rs:29:11 + | +LL | reuse foo { &self.0 } + | ^^^ delegation is only supported yet for trait methods with explicit paths and free functions + +error[E0795]: couldn't resolve `foo` in delegation item + --> $DIR/bad-resolve.rs:32:14 + | +LL | reuse F::foo { &self.0 } + | ^^^ delegation is only supported yet for trait methods with explicit paths and free functions + +error[E0795]: couldn't resolve `foo` in delegation item + --> $DIR/bad-resolve.rs:38:14 + | +LL | reuse F::foo { &self.0 } + | ^^^ delegation is only supported yet for trait methods with explicit paths and free functions + +error[E0186]: method `foo` has a `&self` declaration in the trait, but not in the impl + --> $DIR/bad-resolve.rs:29:5 + | +LL | fn foo(&self, x: i32) -> i32 { x } + | ---------------------------- `&self` used in trait +... +LL | reuse foo { &self.0 } + | ^^^^^^^^^^^^^^^^^^^^^ expected `&self` in impl + +error[E0046]: not all trait items implemented, missing: `Type` + --> $DIR/bad-resolve.rs:19:1 + | +LL | type Type; + | --------- `Type` from trait +... +LL | impl Trait for S { + | ^^^^^^^^^^^^^^^^ missing `Type` in implementation + +error: aborting due to 9 previous errors + +Some errors have detailed explanations: E0046, E0186, E0201, E0324, E0407, E0795. +For more information about an error, try `rustc --explain E0046`. diff --git a/tests/ui/delegation/explicit-paths-pass.rs b/tests/ui/delegation/explicit-paths-pass.rs new file mode 100644 index 0000000000000..3cacba3f984e2 --- /dev/null +++ b/tests/ui/delegation/explicit-paths-pass.rs @@ -0,0 +1,75 @@ +// run-pass + +fn foo(x: i32) -> i32 { x * 2 } + +trait Trait { + fn bar(&self, x: i32) -> i32 { x } + fn description(&self) -> &str { + "hello world!" + } + fn static_method(x: i32) -> i32 { x } + fn static_method2(x: i32, y: i32) -> i32 { x + y } + reuse foo; + fn baz<'a>(&self, x: &'a i32) -> &'a i32 { x } +} + +struct F; +impl Trait for F {} + +mod reuse { + pub fn foo(x: i32) -> i32 { x + 1 } + + #[allow(unused, non_camel_case_types)] + struct reuse { + a: i32, + b: i32, + c: i32, + } + + #[allow(unused)] + fn baz() { + let (a, b, c) = (0, 0, 0); + let val = reuse {a, b, c}; + assert_eq!(val.a, 0); + } +} + +struct S(F); +impl Trait for S { + reuse ::bar { &self.0 } + reuse ::description { &self.0 } + reuse ::static_method; + reuse ::static_method2 { S::static_method(self) } + reuse ::baz { &self.0 } +} + +impl S { + reuse ::static_method { reuse::foo(self) } +} + +impl std::fmt::Display for S { + reuse ::fmt { self.description() } +} + +fn main() { + let s = S(F); + assert_eq!(42, s.bar(42)); + assert_eq!("hello world!", format!("{s}")); + assert_eq!(43, S::static_method(42)); + assert_eq!(42, ::static_method(42)); + assert_eq!(21, S::static_method2(10, 10)); + + assert_eq!(20, S::foo(10)); + assert_eq!(30, F::foo(15)); + + reuse ::static_method; + reuse ::static_method2 { static_method(self) } + #[inline] + reuse reuse::foo; + assert_eq!(42, static_method(42)); + assert_eq!(21, static_method2(10, 10)); + assert_eq!(43, foo(42)); + + let x: i32 = 15; + assert_eq!(&x, s.baz(&x)); +} diff --git a/tests/ui/delegation/explicit-paths.rs b/tests/ui/delegation/explicit-paths.rs new file mode 100644 index 0000000000000..efc14213ad27f --- /dev/null +++ b/tests/ui/delegation/explicit-paths.rs @@ -0,0 +1,22 @@ +trait Trait { + fn bar(&self) -> i32 { 42 } +} + +struct F; +impl Trait for F {} + +struct S(F); + +impl Trait for S { + reuse ::bar; + //~^ ERROR mismatched types +} + +struct S2(F); + +impl Trait for S2 { + reuse ::bar { &self.0 } + //~^ ERROR mismatched types +} + +fn main() {} diff --git a/tests/ui/delegation/explicit-paths.stderr b/tests/ui/delegation/explicit-paths.stderr new file mode 100644 index 0000000000000..4032f5954cd60 --- /dev/null +++ b/tests/ui/delegation/explicit-paths.stderr @@ -0,0 +1,36 @@ +error[E0308]: mismatched types + --> $DIR/explicit-paths.rs:11:5 + | +LL | reuse ::bar; + | ^^^^^^-----------------^ + | | | + | | arguments to this function are incorrect + | expected `&F`, found `&S` + | + = note: expected reference `&F` + found reference `&S` +note: method defined here + --> $DIR/explicit-paths.rs:2:8 + | +LL | fn bar(&self) -> i32 { 42 } + | ^^^ ----- + +error[E0308]: mismatched types + --> $DIR/explicit-paths.rs:18:32 + | +LL | reuse ::bar { &self.0 } + | ------------------ ^^^^^^^ expected `&S2`, found `&F` + | | + | arguments to this function are incorrect + | + = note: expected reference `&S2` + found reference `&F` +note: method defined here + --> $DIR/explicit-paths.rs:2:8 + | +LL | fn bar(&self) -> i32 { 42 } + | ^^^ ----- + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/delegation/not-supported.rs b/tests/ui/delegation/not-supported.rs new file mode 100644 index 0000000000000..36a6a3c1cbc0a --- /dev/null +++ b/tests/ui/delegation/not-supported.rs @@ -0,0 +1,67 @@ +#![feature(c_variadic)] + +mod generics { + trait GenericTrait { + fn foo(&self, x: T) -> T { x } + fn bar() {} + } + trait Trait { + fn foo(&self, x: i32) -> i32 { x } + fn bar<'a>(&self, x: &'a i32) -> &'a i32 { x } + fn baz(&self, x: T) -> T { x } + } + + struct F; + impl Trait for F {} + impl GenericTrait for F {} + + struct S(F); + + impl GenericTrait for S { + reuse ::foo { &self.0 } + //~^ ERROR delegation to generic function is not supported yet + reuse ::bar; + //~^ ERROR delegation to generic function is not supported yet + } + + impl Trait for S { + reuse ::bar { &self.0 } + reuse ::baz { &self.0 } + //~^ ERROR delegation to generic function is not supported yet + } + + struct GenericS { + i32: T + } + + impl Trait for GenericS { + reuse ::foo { &self.0 } + //~^ ERROR delegation to generic function is not supported yet + } + + mod to_reuse { + pub fn opaque(_: impl super::Trait) -> i32 { 0 } + } + reuse to_reuse::opaque; + //~^ delegation to generic function is not supported yet +} + +mod fn_header { + mod to_reuse { + pub unsafe fn unsafe_fn() {} + pub extern "C" fn extern_fn() {} + pub unsafe extern "C" fn variadic_fn(n: usize, mut args: ...) {} + pub const fn const_fn() {} + } + + reuse to_reuse::unsafe_fn; + //~^ delegation to unsafe function is not supported yet + reuse to_reuse::extern_fn; + //~^ delegation to function with non Rust ABI is not supported yet + reuse to_reuse::variadic_fn; + //~^ delegation to variadic function is not supported yet + reuse to_reuse::const_fn; + //~^ delegation to const function is not supported yet +} + +fn main() {} diff --git a/tests/ui/delegation/not-supported.stderr b/tests/ui/delegation/not-supported.stderr new file mode 100644 index 0000000000000..4fcee00e169b9 --- /dev/null +++ b/tests/ui/delegation/not-supported.stderr @@ -0,0 +1,83 @@ +error: delegation to generic function is not supported yet + --> $DIR/not-supported.rs:45:5 + | +LL | pub fn opaque(_: impl super::Trait) -> i32 { 0 } + | ------------------------------------------ callee defined here +LL | } +LL | reuse to_reuse::opaque; + | ^^^^^^^^^^^^^^^^^^^^^^^ + +error: delegation to generic function is not supported yet + --> $DIR/not-supported.rs:21:9 + | +LL | fn foo(&self, x: T) -> T { x } + | ------------------------ callee defined here +... +LL | reuse ::foo { &self.0 } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: delegation to generic function is not supported yet + --> $DIR/not-supported.rs:23:9 + | +LL | fn bar() {} + | -------- callee defined here +... +LL | reuse ::bar; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: delegation to generic function is not supported yet + --> $DIR/not-supported.rs:29:9 + | +LL | fn baz(&self, x: T) -> T { x } + | --------------------------- callee defined here +... +LL | reuse ::baz { &self.0 } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: delegation to generic function is not supported yet + --> $DIR/not-supported.rs:38:9 + | +LL | fn foo(&self, x: i32) -> i32 { x } + | ---------------------------- callee defined here +... +LL | reuse ::foo { &self.0 } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: delegation to unsafe function is not supported yet + --> $DIR/not-supported.rs:57:5 + | +LL | pub unsafe fn unsafe_fn() {} + | ------------------------- callee defined here +... +LL | reuse to_reuse::unsafe_fn; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: delegation to function with non Rust ABI is not supported yet + --> $DIR/not-supported.rs:59:5 + | +LL | pub extern "C" fn extern_fn() {} + | ----------------------------- callee defined here +... +LL | reuse to_reuse::extern_fn; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: delegation to variadic function is not supported yet + --> $DIR/not-supported.rs:61:5 + | +LL | pub unsafe extern "C" fn variadic_fn(n: usize, mut args: ...) {} + | ------------------------------------------------------------- callee defined here +... +LL | reuse to_reuse::variadic_fn; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: delegation to const function is not supported yet + --> $DIR/not-supported.rs:63:5 + | +LL | pub const fn const_fn() {} + | ----------------------- callee defined here +... +LL | reuse to_reuse::const_fn; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 9 previous errors + diff --git a/tests/ui/delegation/target-expr-pass.rs b/tests/ui/delegation/target-expr-pass.rs new file mode 100644 index 0000000000000..6edbc855ca12f --- /dev/null +++ b/tests/ui/delegation/target-expr-pass.rs @@ -0,0 +1,34 @@ +// run-pass + +mod to_reuse { + pub fn foo(x: i32) -> i32 { x } + pub mod inner {} +} + +reuse to_reuse::foo {{ + use self::to_reuse::foo; + let x = foo(12); + x + self +}} + +trait Trait { + fn bar(&self, x: i32) -> i32 { x } +} + +struct F; +impl Trait for F {} + +struct S(F); +impl Trait for S { + reuse ::bar {{ + #[allow(unused_imports)] + use self::to_reuse::{foo, inner::self}; + let x = foo(12); + assert_eq!(x, 12); + &self.0 + }} +} + +fn main() { + assert_eq!(foo(12), 24); +}