diff --git a/compiler/rustc_ast_lowering/src/delegation/generics.rs b/compiler/rustc_ast_lowering/src/delegation/generics.rs index 4d9bc09faeecb..911ec5956006d 100644 --- a/compiler/rustc_ast_lowering/src/delegation/generics.rs +++ b/compiler/rustc_ast_lowering/src/delegation/generics.rs @@ -662,10 +662,10 @@ impl<'hir> LoweringContext<'_, 'hir> { p.def_id.to_def_id(), ); - self.create_resolved_path(res, p.name.ident(), p.span) + self.create_resolved_qpath(res, p.name.ident(), p.span) } - pub(super) fn create_resolved_path( + pub(super) fn create_resolved_qpath( &mut self, res: Res, ident: Ident, diff --git a/compiler/rustc_ast_lowering/src/delegation/mod.rs b/compiler/rustc_ast_lowering/src/delegation/mod.rs index d0033ba0e472e..02fd6de314d3a 100644 --- a/compiler/rustc_ast_lowering/src/delegation/mod.rs +++ b/compiler/rustc_ast_lowering/src/delegation/mod.rs @@ -439,7 +439,7 @@ impl<'hir> LoweringContext<'_, 'hir> { }; let ident = Ident::new(kw::SelfUpper, span); - let path = self.create_resolved_path(res, ident, span); + let path = self.create_resolved_qpath(res, ident, span); // FIXME(fn_delegation): add default `..` for all other fields. let initializer = hir::ExprKind::Struct( @@ -454,7 +454,14 @@ impl<'hir> LoweringContext<'_, 'hir> { hir::StructTailExpr::None, ); - self.arena.alloc(self.mk_expr(initializer, span)) + let expr = self.mk_expr(initializer, span); + + let path = self.make_lang_item_qpath(hir::LangItem::FromFn, span, None); + let path = self.arena.alloc(self.mk_expr(hir::ExprKind::Path(path), span)); + + let call = hir::ExprKind::Call(path, self.arena.alloc_slice(&[expr])); + + self.arena.alloc(self.mk_expr(call, span)) } else { self.arena.alloc(call) }; diff --git a/compiler/rustc_ast_lowering/src/delegation/resolution.rs b/compiler/rustc_ast_lowering/src/delegation/resolution.rs index 1d9bcef7ac5a7..dd1b9518e6d7f 100644 --- a/compiler/rustc_ast_lowering/src/delegation/resolution.rs +++ b/compiler/rustc_ast_lowering/src/delegation/resolution.rs @@ -5,10 +5,10 @@ use hir::def::DefKind; use rustc_ast::{self as ast, Delegation, DelegationSource, NodeId}; use rustc_data_structures::fx::{FxHashSet, FxIndexSet}; use rustc_hir as hir; -use rustc_middle::ty::Ty; +use rustc_middle::ty::{Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor}; use rustc_middle::{span_bug, ty}; use rustc_span::def_id::{DefId, LocalDefId}; -use rustc_span::{ErrorGuaranteed, Span, kw}; +use rustc_span::{ErrorGuaranteed, Span}; use crate::delegation::generics::GenericsGenerationResults; use crate::delegation::resolution::resolver::DelegationResolver; @@ -31,7 +31,7 @@ pub(super) struct ParamInfo { pub splatted: Option, } -#[derive(Default)] +#[derive(Default, Debug)] pub(super) struct SigMapping { pub map_return: bool, pub arguments_to_map: FxIndexSet, @@ -254,17 +254,52 @@ impl<'tcx> DelegationResolver<'_, 'tcx> { } if self.can_perform_self_mapping(delegation, parent)? { - // FIXME(fn_delegation): support heuristics for mapping of complex - // return types: `Self` -> `Box>>` - mapping.map_return = sig.output().is_param(0); + /// Finds `Self` generic param only in ADT or references, so we avoid cases like + /// `Self::Item` which will return true if `output.contains(...)` will be used. + struct SelfFinder; + + impl<'tcx> TypeVisitor> for SelfFinder { + type Result = ControlFlow<()>; + + fn visit_ty(&mut self, t: Ty<'tcx>) -> Self::Result { + match t.kind() { + ty::Adt(_, args) => { + if args + .iter() + .flat_map(|arg| arg.as_type()) + .any(|type_arg| type_arg.is_self_param()) + { + return ControlFlow::Break(()); + } + + t.super_visit_with(self) + } + ty::Ref(_, ref_t, _) => { + if ref_t.is_self_param() { + return ControlFlow::Break(()); + } + + t.super_visit_with(self) + } + _ => ControlFlow::Continue(()), + } + } + } + + impl SelfFinder { + fn contains_self(t: Ty<'_>) -> bool { + t.is_self_param() || t.visit_with(&mut SelfFinder).is_break() + } + } + + mapping.map_return = SelfFinder::contains_self(sig.output()); - let self_param = Ty::new_param(self.tcx(), 0, kw::SelfUpper); let arguments_to_map = sig .inputs() .iter() .enumerate() .skip(1) // Already checked above. - .filter_map(|(idx, param)| param.contains(self_param).then_some(idx)); + .filter_map(|(idx, ¶m)| SelfFinder::contains_self(param).then_some(idx)); mapping.arguments_to_map.extend(arguments_to_map); } diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index e6e0b3726552f..fe1ca75d30cf7 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -456,6 +456,7 @@ language_item_table! { // Used to fallback `{float}` to `f32` when `f32: From<{float}>` From, sym::From, from_trait, Target::Trait, GenericRequirement::Exact(1); + FromFn, sym::from, from_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None; } /// The requirement imposed on the generics of a lang item diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index e768c75961937..0c0e3d87c9f20 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -1191,6 +1191,15 @@ impl<'tcx> Ty<'tcx> { matches!(self.kind(), Adt(..)) } + #[inline] + pub fn is_self_param(self) -> bool { + if let Param(param) = self.kind() { + param.index == 0 && param.name == kw::SelfUpper + } else { + false + } + } + #[inline] pub fn is_ref(self) -> bool { matches!(self.kind(), Ref(..)) diff --git a/library/core/src/convert/mod.rs b/library/core/src/convert/mod.rs index ae8458c199503..912623b73050e 100644 --- a/library/core/src/convert/mod.rs +++ b/library/core/src/convert/mod.rs @@ -591,6 +591,7 @@ pub const trait From: Sized { #[rustc_diagnostic_item = "from_fn"] #[must_use] #[stable(feature = "rust1", since = "1.0.0")] + #[lang = "from"] fn from(value: T) -> Self; } diff --git a/tests/pretty/delegation/self-mapping-output.pp b/tests/pretty/delegation/self-mapping-output.pp index 84e98d6e97b06..5bce43315e1d0 100644 --- a/tests/pretty/delegation/self-mapping-output.pp +++ b/tests/pretty/delegation/self-mapping-output.pp @@ -24,7 +24,7 @@ struct W(S); impl Trait for W { #[attr = Inline(Hint)] - fn method(self: _) -> _ { Self { 0: Trait::method(self.0) } } + fn method(self: _) -> _ { from(Self { 0: Trait::method(self.0) }) } #[attr = Inline(Hint)] fn r#static() -> _ { Trait::r#static() } //~^ WARN: function cannot return without recursing [unconditional_recursion] @@ -34,7 +34,7 @@ impl W { #[attr = Inline(Hint)] - fn method(self: _) -> _ { Self { 0: Trait::method(self.0) } } + fn method(self: _) -> _ { from(Self { 0: Trait::method(self.0) }) } #[attr = Inline(Hint)] fn r#static() -> _ { Trait::r#static() } #[attr = Inline(Hint)] diff --git a/tests/ui/delegation/self-mapping-output-from-wrap-errors.rs b/tests/ui/delegation/self-mapping-output-from-wrap-errors.rs new file mode 100644 index 0000000000000..6ef2a4b72559c --- /dev/null +++ b/tests/ui/delegation/self-mapping-output-from-wrap-errors.rs @@ -0,0 +1,72 @@ +#![feature(fn_delegation)] + +mod pin_box_self { + use std::pin::Pin; + + trait MyAdd { + fn add(self, other: Self) -> Pin>; + } + + impl MyAdd for usize { + fn add(self, other: usize) -> Pin> { + Pin::new(Box::new(self + other)) + } + } + + #[derive(Eq, PartialEq, Debug)] + struct W(Pin>); + + reuse impl MyAdd for W { + //~^ ERROR: the trait bound `Pin>: From` is not satisfied + *self.0 + } +} + +mod many_froms { + use std::sync::Arc; + use std::rc::Rc; + + trait MyAdd { + fn add(self, other: Self) -> Box>>>>>; + } + + impl MyAdd for usize { + fn add(self, other: usize) -> Box>>>>> { + Box::new(Box::new(Box::new(Arc::new(Box::new(Rc::new(self + other)))))) + } + } + + #[derive(Eq, PartialEq, Debug)] + struct W(Box>>>>>); + + reuse impl MyAdd for W { + //~^ ERROR: the trait bound `Box>>>>>: From` is not satisfied + ******self.0 + } +} + +mod many_froms_2 { + use std::sync::Arc; + use std::rc::Rc; + + trait MyAdd { + fn add(self, other: Self) -> Box>>>>; + } + + impl MyAdd for usize { + fn add(self, other: usize) -> Box>>>> { + Box::new(Arc::new(Rc::new(Box::new(Rc::new(self + other))))) + } + } + + #[derive(Eq, PartialEq, Debug)] + struct W(Box>>>>); + + reuse impl MyAdd for W { + //~^ ERROR: the trait bound `Box>>>>: From` is not satisfied + *****self.0 + } +} + +fn main() { +} diff --git a/tests/ui/delegation/self-mapping-output-from-wrap-errors.stderr b/tests/ui/delegation/self-mapping-output-from-wrap-errors.stderr new file mode 100644 index 0000000000000..d6290bc220966 --- /dev/null +++ b/tests/ui/delegation/self-mapping-output-from-wrap-errors.stderr @@ -0,0 +1,57 @@ +error[E0277]: the trait bound `Pin>: From` is not satisfied + --> $DIR/self-mapping-output-from-wrap-errors.rs:19:5 + | +LL | / reuse impl MyAdd for W { +LL | | +LL | | *self.0 +LL | | } + | |_____^ the trait `From` is not implemented for `Pin>` + | +help: the trait `From` is not implemented for `Pin>` + but trait `From>` is implemented for it + --> $SRC_DIR/alloc/src/boxed/convert.rs:LL:COL + = help: for that trait implementation, expected `Box`, found `pin_box_self::W` + +error[E0277]: the trait bound `Box>>>>>: From` is not satisfied + --> $DIR/self-mapping-output-from-wrap-errors.rs:42:5 + | +LL | / reuse impl MyAdd for W { +LL | | +LL | | ******self.0 +LL | | } + | |_____^ the trait `From` is not implemented for `Box>>>>>` + | + = help: the following other types implement trait `From`: + `Box` implements `From>` + `Box` implements `From<&CStr>` + `Box` implements `From<&mut CStr>` + `Box` implements `From` + `Box` implements `From>` + `Box` implements `From<&OsStr>` + `Box` implements `From<&mut OsStr>` + `Box` implements `From>` + and 25 others + +error[E0277]: the trait bound `Box>>>>: From` is not satisfied + --> $DIR/self-mapping-output-from-wrap-errors.rs:65:5 + | +LL | / reuse impl MyAdd for W { +LL | | +LL | | *****self.0 +LL | | } + | |_____^ the trait `From` is not implemented for `Box>>>>` + | + = help: the following other types implement trait `From`: + `Box` implements `From>` + `Box` implements `From<&CStr>` + `Box` implements `From<&mut CStr>` + `Box` implements `From` + `Box` implements `From>` + `Box` implements `From<&OsStr>` + `Box` implements `From<&mut OsStr>` + `Box` implements `From>` + and 25 others + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/delegation/self-mapping-output-from-wrap.rs b/tests/ui/delegation/self-mapping-output-from-wrap.rs new file mode 100644 index 0000000000000..2dfff2882fbc6 --- /dev/null +++ b/tests/ui/delegation/self-mapping-output-from-wrap.rs @@ -0,0 +1,199 @@ +//@ run-pass +//@ check-run-results + +#![feature(fn_delegation)] + +mod simple_self { + trait MyAdd { + fn add(self, other: Self) -> Self; + } + + impl MyAdd for usize { + fn add(self, other: usize) -> usize { + self + other + } + } + + #[derive(Eq, PartialEq, Debug)] + struct W(usize); + + reuse impl MyAdd for W { + println!("simple_self {self:?}"); + self.0 + } + + pub fn check() { + assert_eq!(W(1).add(W(2)), W(3)) + } +} + +mod box_self { + trait MyAdd { + fn add(self, other: Self) -> Box; + } + + impl MyAdd for usize { + fn add(self, other: usize) -> Box { + Box::new(self + other) + } + } + + #[derive(Eq, PartialEq, Debug)] + struct W(Box); + + reuse impl MyAdd for W { + println!("box_self {self:?}"); + *self.0 + } + + pub fn check() { + fn w(x: usize) -> W { + W(Box::new(x)) + } + + assert_eq!(w(1).add(w(2)), Box::new(w(3))) + } +} + +mod rc_self { + use std::rc::Rc; + + trait MyAdd { + fn add(self, other: Self) -> Rc; + } + + impl MyAdd for usize { + fn add(self, other: usize) -> Rc { + Rc::new(self + other) + } + } + + #[derive(Eq, PartialEq, Debug)] + struct W(Rc); + + reuse impl MyAdd for W { + println!("rc_self {self:?}"); + *self.0 + } + + pub fn check() { + fn w(x: usize) -> W { + W(Rc::new(x)) + } + + assert_eq!(w(1).add(w(2)), Rc::new(w(3))) + } +} + +mod arc_self { + use std::sync::Arc; + + trait MyAdd { + fn add(self, other: Self) -> Arc; + } + + impl MyAdd for usize { + fn add(self, other: usize) -> Arc { + Arc::new(self + other) + } + } + + #[derive(Eq, PartialEq, Debug)] + struct W(Arc); + + reuse impl MyAdd for W { + println!("arc_self {self:?}"); + *self.0 + } + + pub fn check() { + fn w(x: usize) -> W { + W(Arc::new(x)) + } + + assert_eq!(w(1).add(w(2)), Arc::new(w(3))) + } +} + +mod custom_froms { + #[derive(Debug)] + struct S1 { + a: A, + } + + impl From for S1 { + fn from(a: A) -> S1 { + S1 { a } + } + } + + #[derive(Debug)] + struct S2 { + t: T, + } + + impl From for S2 { + fn from(t: T) -> S2 { + S2 { t } + } + } + + #[derive(Debug)] + struct S3<'a, const C: usize, T, U, const B: bool> { + t: T, + pd: std::marker::PhantomData<&'a [(usize, U); C]> + } + + impl<'a, const C: usize, T, const B: bool> From for S3<'a, C, T, (), B> { + fn from(t: T) -> S3<'a, C, T, (), B> { + S3 { + t, + pd: std::marker::PhantomData::<&'a [(usize, ()); C]>, + } + } + } + + trait MyAdd: Sized { + fn add(self, other: Self) -> S1>>, (), true>>>; + } + + fn create_monster_struct(x: T) -> S1>>, (), true>>> { + S1::from(S1::from(S3::from(S2::from(S2::from(S1::from(x)))))) + } + + impl MyAdd for usize { + fn add(self, other: usize) -> S1>>, (), true>>> { + create_monster_struct(self + other) + } + } + + #[derive(Debug)] + struct W(S1>>, (), true>>>); + + impl From for S1>>, (), true>>> { + fn from(x: W) -> Self { + create_monster_struct(x) + } + } + + reuse impl MyAdd for W { + println!("custom_froms {self:?}"); + self.0.a.a.t.t.t.a + } + + pub fn check() { + fn w(x: usize) -> W { + W(create_monster_struct(x)) + } + + assert_eq!(w(1).add(w(2)).a.a.t.t.t.a.0.a.a.t.t.t.a, 3) + } +} + +fn main() { + simple_self::check(); + box_self::check(); + rc_self::check(); + arc_self::check(); + custom_froms::check(); +} diff --git a/tests/ui/delegation/self-mapping-output-from-wrap.run.stdout b/tests/ui/delegation/self-mapping-output-from-wrap.run.stdout new file mode 100644 index 0000000000000..ee96199c54e07 --- /dev/null +++ b/tests/ui/delegation/self-mapping-output-from-wrap.run.stdout @@ -0,0 +1,10 @@ +simple_self W(1) +simple_self W(2) +box_self W(1) +box_self W(2) +rc_self W(1) +rc_self W(2) +arc_self W(1) +arc_self W(2) +custom_froms W(S1 { a: S1 { a: S3 { t: S2 { t: S2 { t: S1 { a: 1 } } }, pd: PhantomData<&[(usize, ()); 123]> } } }) +custom_froms W(S1 { a: S1 { a: S3 { t: S2 { t: S2 { t: S1 { a: 2 } } }, pd: PhantomData<&[(usize, ()); 123]> } } })