Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions compiler/rustc_ast_lowering/src/delegation/generics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
11 changes: 9 additions & 2 deletions compiler/rustc_ast_lowering/src/delegation/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand All @@ -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)
};
Expand Down
51 changes: 43 additions & 8 deletions compiler/rustc_ast_lowering/src/delegation/resolution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -31,7 +31,7 @@ pub(super) struct ParamInfo {
pub splatted: Option<u8>,
}

#[derive(Default)]
#[derive(Default, Debug)]
pub(super) struct SigMapping {
pub map_return: bool,
pub arguments_to_map: FxIndexSet<usize>,
Expand Down Expand Up @@ -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<Arc<Rc<Self>>>`
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;
Comment thread
petrochenkov marked this conversation as resolved.

impl<'tcx> TypeVisitor<TyCtxt<'tcx>> 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, &param)| SelfFinder::contains_self(param).then_some(idx));

mapping.arguments_to_map.extend(arguments_to_map);
}
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_hir/src/lang_items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

@petrochenkov petrochenkov Aug 5, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I expected this to already be used by AST lowering, but apparently all the operators were migrated to other more specific trait methods like Try::from_residual and similar.

View changes since the review

}

/// The requirement imposed on the generics of a lang item
Expand Down
9 changes: 9 additions & 0 deletions compiler/rustc_middle/src/ty/sty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(..))
Expand Down
1 change: 1 addition & 0 deletions library/core/src/convert/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -591,6 +591,7 @@ pub const trait From<T>: Sized {
#[rustc_diagnostic_item = "from_fn"]
#[must_use]
#[stable(feature = "rust1", since = "1.0.0")]
#[lang = "from"]
fn from(value: T) -> Self;
}

Expand Down
4 changes: 2 additions & 2 deletions tests/pretty/delegation/self-mapping-output.pp
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand All @@ -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)]
Expand Down
72 changes: 72 additions & 0 deletions tests/ui/delegation/self-mapping-output-from-wrap-errors.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
#![feature(fn_delegation)]

mod pin_box_self {
use std::pin::Pin;

trait MyAdd {
fn add(self, other: Self) -> Pin<Box<Self>>;
}

impl MyAdd for usize {
fn add(self, other: usize) -> Pin<Box<usize>> {
Pin::new(Box::new(self + other))
}
}

#[derive(Eq, PartialEq, Debug)]
struct W(Pin<Box<usize>>);

reuse impl MyAdd for W {
//~^ ERROR: the trait bound `Pin<Box<pin_box_self::W>>: From<pin_box_self::W>` is not satisfied
*self.0
}
}

mod many_froms {
use std::sync::Arc;
use std::rc::Rc;

trait MyAdd {
fn add(self, other: Self) -> Box<Box<Box<Arc<Box<Rc<Self>>>>>>;
}

impl MyAdd for usize {
fn add(self, other: usize) -> Box<Box<Box<Arc<Box<Rc<usize>>>>>> {
Box::new(Box::new(Box::new(Arc::new(Box::new(Rc::new(self + other))))))
}
}

#[derive(Eq, PartialEq, Debug)]
struct W(Box<Box<Box<Arc<Box<Rc<usize>>>>>>);

reuse impl MyAdd for W {
//~^ ERROR: the trait bound `Box<Box<Box<Arc<Box<Rc<many_froms::W>>>>>>: From<many_froms::W>` 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<Arc<Rc<Box<Rc<Self>>>>>;
}

impl MyAdd for usize {
fn add(self, other: usize) -> Box<Arc<Rc<Box<Rc<usize>>>>> {
Box::new(Arc::new(Rc::new(Box::new(Rc::new(self + other)))))
}
}

#[derive(Eq, PartialEq, Debug)]
struct W(Box<Arc<Rc<Box<Rc<usize>>>>>);

reuse impl MyAdd for W {
//~^ ERROR: the trait bound `Box<Arc<Rc<Box<Rc<many_froms_2::W>>>>>: From<many_froms_2::W>` is not satisfied
*****self.0
}
}

fn main() {
}
57 changes: 57 additions & 0 deletions tests/ui/delegation/self-mapping-output-from-wrap-errors.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
error[E0277]: the trait bound `Pin<Box<pin_box_self::W>>: From<pin_box_self::W>` 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<pin_box_self::W>` is not implemented for `Pin<Box<pin_box_self::W>>`
|
help: the trait `From<W>` is not implemented for `Pin<Box<pin_box_self::W>>`
but trait `From<Box<W>>` is implemented for it
--> $SRC_DIR/alloc/src/boxed/convert.rs:LL:COL
= help: for that trait implementation, expected `Box<pin_box_self::W>`, found `pin_box_self::W`

error[E0277]: the trait bound `Box<Box<Box<Arc<Box<Rc<many_froms::W>>>>>>: From<many_froms::W>` 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<many_froms::W>` is not implemented for `Box<Box<Box<Arc<Box<Rc<many_froms::W>>>>>>`
|
= help: the following other types implement trait `From<T>`:
`Box<ByteStr>` implements `From<Box<[u8]>>`
`Box<CStr>` implements `From<&CStr>`
`Box<CStr>` implements `From<&mut CStr>`
`Box<CStr>` implements `From<CString>`
`Box<CStr>` implements `From<Cow<'_, CStr>>`
`Box<OsStr>` implements `From<&OsStr>`
`Box<OsStr>` implements `From<&mut OsStr>`
`Box<OsStr>` implements `From<Cow<'_, OsStr>>`
and 25 others

error[E0277]: the trait bound `Box<Arc<Rc<Box<Rc<many_froms_2::W>>>>>: From<many_froms_2::W>` 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<many_froms_2::W>` is not implemented for `Box<Arc<Rc<Box<Rc<many_froms_2::W>>>>>`
|
= help: the following other types implement trait `From<T>`:
`Box<ByteStr>` implements `From<Box<[u8]>>`
`Box<CStr>` implements `From<&CStr>`
`Box<CStr>` implements `From<&mut CStr>`
`Box<CStr>` implements `From<CString>`
`Box<CStr>` implements `From<Cow<'_, CStr>>`
`Box<OsStr>` implements `From<&OsStr>`
`Box<OsStr>` implements `From<&mut OsStr>`
`Box<OsStr>` implements `From<Cow<'_, OsStr>>`
and 25 others

error: aborting due to 3 previous errors

For more information about this error, try `rustc --explain E0277`.
Loading
Loading