Skip to content

Commit 981b182

Browse files
committed
Add support for wrapping of the return value of delegation
1 parent 22057b8 commit 981b182

11 files changed

Lines changed: 438 additions & 22 deletions

File tree

compiler/rustc_ast_lowering/src/delegation/generics.rs

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -662,10 +662,10 @@ impl<'hir> LoweringContext<'_, 'hir> {
662662
p.def_id.to_def_id(),
663663
);
664664

665-
self.create_resolved_path(res, p.name.ident(), p.span)
665+
self.create_resolved_qpath(res, p.name.ident(), p.span)
666666
}
667667

668-
pub(super) fn create_resolved_path(
668+
pub(super) fn create_resolved_qpath(
669669
&mut self,
670670
res: Res,
671671
ident: Ident,
@@ -674,17 +674,26 @@ impl<'hir> LoweringContext<'_, 'hir> {
674674
hir::QPath::Resolved(
675675
None,
676676
self.arena.alloc(hir::Path {
677-
segments: self.arena.alloc_slice(&[hir::PathSegment {
678-
args: None,
679-
hir_id: self.next_id(),
680-
ident,
681-
infer_args: false,
682-
res,
683-
delegation_child_segment: false,
684-
}]),
677+
segments: self.arena.alloc_slice(&[self.create_path_segment(res, ident, false)]),
685678
res,
686679
span,
687680
}),
688681
)
689682
}
683+
684+
pub(super) fn create_path_segment(
685+
&mut self,
686+
res: Res,
687+
ident: Ident,
688+
infer_args: bool,
689+
) -> hir::PathSegment<'hir> {
690+
hir::PathSegment {
691+
args: None,
692+
hir_id: self.next_id(),
693+
ident,
694+
infer_args,
695+
res,
696+
delegation_child_segment: false,
697+
}
698+
}
690699
}

compiler/rustc_ast_lowering/src/delegation/mod.rs

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -439,7 +439,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
439439
};
440440

441441
let ident = Ident::new(kw::SelfUpper, span);
442-
let path = self.create_resolved_path(res, ident, span);
442+
let path = self.create_resolved_qpath(res, ident, span);
443443

444444
// FIXME(fn_delegation): add default `..` for all other fields.
445445
let initializer = hir::ExprKind::Struct(
@@ -454,7 +454,9 @@ impl<'hir> LoweringContext<'_, 'hir> {
454454
hir::StructTailExpr::None,
455455
);
456456

457-
self.arena.alloc(self.mk_expr(initializer, span))
457+
let initializer = self.mk_expr(initializer, span);
458+
let wrapped_initializer = self.generate_from_wrapper(initializer, span);
459+
self.arena.alloc(wrapped_initializer)
458460
} else {
459461
self.arena.alloc(call)
460462
};
@@ -471,6 +473,25 @@ impl<'hir> LoweringContext<'_, 'hir> {
471473
(self.mk_expr(hir::ExprKind::Block(block, None), span), call.hir_id)
472474
}
473475

476+
fn generate_from_wrapper(&mut self, expr: hir::Expr<'hir>, span: Span) -> hir::Expr<'hir> {
477+
let items = self.tcx.lang_items();
478+
let from_res = Res::Def(DefKind::Trait, items.from_trait().expect("lang item"));
479+
let from_fn_res = Res::Def(DefKind::AssocFn, items.from_fn().expect("lang item"));
480+
481+
let ident = Ident::new(sym::From, span);
482+
let from_segment = self.create_path_segment(from_res, ident, true);
483+
484+
let ident = Ident::new(sym::from, span);
485+
let from_fn_segment = self.create_path_segment(from_fn_res, ident, false);
486+
487+
let segments = self.arena.alloc_slice(&[from_segment, from_fn_segment]);
488+
let path = self.arena.alloc(hir::Path { res: from_fn_res, segments, span });
489+
let path = self.mk_expr(hir::ExprKind::Path(hir::QPath::Resolved(None, path)), span);
490+
let path = self.arena.alloc(path);
491+
492+
self.mk_expr(hir::ExprKind::Call(path, self.arena.alloc_slice(&[expr])), span)
493+
}
494+
474495
fn process_segment(
475496
&mut self,
476497
span: Span,

compiler/rustc_ast_lowering/src/delegation/resolution.rs

Lines changed: 45 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@ use hir::def::DefKind;
55
use rustc_ast::{self as ast, Delegation, DelegationSource, NodeId};
66
use rustc_data_structures::fx::{FxHashSet, FxIndexSet};
77
use rustc_hir as hir;
8-
use rustc_middle::ty::Ty;
8+
use rustc_middle::ty::{Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor};
99
use rustc_middle::{span_bug, ty};
1010
use rustc_span::def_id::{DefId, LocalDefId};
11-
use rustc_span::{ErrorGuaranteed, Span, kw};
11+
use rustc_span::{ErrorGuaranteed, Span};
1212

1313
use crate::delegation::generics::GenericsGenerationResults;
1414
use crate::delegation::resolution::resolver::DelegationResolver;
@@ -31,7 +31,7 @@ pub(super) struct ParamInfo {
3131
pub splatted: Option<u8>,
3232
}
3333

34-
#[derive(Default)]
34+
#[derive(Default, Debug)]
3535
pub(super) struct SigMapping {
3636
pub map_return: bool,
3737
pub arguments_to_map: FxIndexSet<usize>,
@@ -254,17 +254,54 @@ impl<'tcx> DelegationResolver<'_, 'tcx> {
254254
}
255255

256256
if self.can_perform_self_mapping(delegation, parent)? {
257-
// FIXME(fn_delegation): support heuristics for mapping of complex
258-
// return types: `Self` -> `Box<Arc<Rc<Self>>>`
259-
mapping.map_return = sig.output().is_param(0);
257+
/// Finds `Self` generic param only in ADT or references, so we avoid cases like
258+
/// `Self::Item` which will return true if `output.contains(...)` will be used.
259+
struct SelfFinder;
260+
261+
impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for SelfFinder {
262+
type Result = ControlFlow<()>;
263+
264+
fn visit_ty(&mut self, t: Ty<'tcx>) -> Self::Result {
265+
match t.kind() {
266+
ty::Adt(_, args) => {
267+
if args
268+
.iter()
269+
.flat_map(|arg| arg.as_type())
270+
.any(|type_arg| type_arg.is_self_param())
271+
{
272+
return ControlFlow::Break(());
273+
}
274+
275+
t.super_visit_with(self)
276+
}
277+
ty::Ref(_, ref_t, _) => {
278+
if ref_t.is_self_param() {
279+
return ControlFlow::Break(());
280+
}
281+
282+
t.super_visit_with(self)
283+
}
284+
_ => ControlFlow::Continue(()),
285+
}
286+
}
287+
}
288+
289+
impl SelfFinder {
290+
fn contains_self(t: Ty<'_>) -> bool {
291+
t.is_self_param() || t.visit_with(&mut SelfFinder).is_break()
292+
}
293+
}
294+
295+
let output = sig.output();
296+
297+
mapping.map_return = SelfFinder::contains_self(output);
260298

261-
let self_param = Ty::new_param(self.tcx(), 0, kw::SelfUpper);
262299
let arguments_to_map = sig
263300
.inputs()
264301
.iter()
265302
.enumerate()
266303
.skip(1) // Already checked above.
267-
.filter_map(|(idx, param)| param.contains(self_param).then_some(idx));
304+
.filter_map(|(idx, &param)| SelfFinder::contains_self(param).then_some(idx));
268305

269306
mapping.arguments_to_map.extend(arguments_to_map);
270307
}

compiler/rustc_hir/src/lang_items.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -456,6 +456,7 @@ language_item_table! {
456456

457457
// Used to fallback `{float}` to `f32` when `f32: From<{float}>`
458458
From, sym::From, from_trait, Target::Trait, GenericRequirement::Exact(1);
459+
FromFn, sym::from, from_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
459460
}
460461

461462
/// The requirement imposed on the generics of a lang item

compiler/rustc_middle/src/ty/sty.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1191,6 +1191,15 @@ impl<'tcx> Ty<'tcx> {
11911191
matches!(self.kind(), Adt(..))
11921192
}
11931193

1194+
#[inline]
1195+
pub fn is_self_param(self) -> bool {
1196+
if let Param(param) = self.kind() {
1197+
param.index == 0 && param.name == kw::SelfUpper
1198+
} else {
1199+
false
1200+
}
1201+
}
1202+
11941203
#[inline]
11951204
pub fn is_ref(self) -> bool {
11961205
matches!(self.kind(), Ref(..))

library/core/src/convert/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -591,6 +591,7 @@ pub const trait From<T>: Sized {
591591
#[rustc_diagnostic_item = "from_fn"]
592592
#[must_use]
593593
#[stable(feature = "rust1", since = "1.0.0")]
594+
#[lang = "from"]
594595
fn from(value: T) -> Self;
595596
}
596597

tests/pretty/delegation/self-mapping-output.pp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
struct W(S);
2525
impl Trait for W {
2626
#[attr = Inline(Hint)]
27-
fn method(self: _) -> _ { Self { 0: Trait::method(self.0) } }
27+
fn method(self: _) -> _ { From::from(Self { 0: Trait::method(self.0) }) }
2828
#[attr = Inline(Hint)]
2929
fn r#static() -> _ { Trait::r#static() }
3030
//~^ WARN: function cannot return without recursing [unconditional_recursion]
@@ -34,7 +34,7 @@
3434

3535
impl W {
3636
#[attr = Inline(Hint)]
37-
fn method(self: _) -> _ { Self { 0: Trait::method(self.0) } }
37+
fn method(self: _) -> _ { From::from(Self { 0: Trait::method(self.0) }) }
3838
#[attr = Inline(Hint)]
3939
fn r#static() -> _ { Trait::r#static() }
4040
#[attr = Inline(Hint)]
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
#![feature(fn_delegation)]
2+
3+
mod pin_box_self {
4+
use std::pin::Pin;
5+
6+
trait MyAdd {
7+
fn add(self, other: Self) -> Pin<Box<Self>>;
8+
}
9+
10+
impl MyAdd for usize {
11+
fn add(self, other: usize) -> Pin<Box<usize>> {
12+
Pin::new(Box::new(self + other))
13+
}
14+
}
15+
16+
#[derive(Eq, PartialEq, Debug)]
17+
struct W(Pin<Box<usize>>);
18+
19+
reuse impl MyAdd for W {
20+
//~^ ERROR: the trait bound `Pin<Box<pin_box_self::W>>: From<pin_box_self::W>` is not satisfied
21+
*self.0
22+
}
23+
}
24+
25+
mod many_froms {
26+
use std::sync::Arc;
27+
use std::rc::Rc;
28+
29+
trait MyAdd {
30+
fn add(self, other: Self) -> Box<Box<Box<Arc<Box<Rc<Self>>>>>>;
31+
}
32+
33+
impl MyAdd for usize {
34+
fn add(self, other: usize) -> Box<Box<Box<Arc<Box<Rc<usize>>>>>> {
35+
Box::new(Box::new(Box::new(Arc::new(Box::new(Rc::new(self + other))))))
36+
}
37+
}
38+
39+
#[derive(Eq, PartialEq, Debug)]
40+
struct W(Box<Box<Box<Arc<Box<Rc<usize>>>>>>);
41+
42+
reuse impl MyAdd for W {
43+
//~^ ERROR: the trait bound `Box<Box<Box<Arc<Box<Rc<many_froms::W>>>>>>: From<many_froms::W>` is not satisfied
44+
******self.0
45+
}
46+
}
47+
48+
mod many_froms_2 {
49+
use std::sync::Arc;
50+
use std::rc::Rc;
51+
52+
trait MyAdd {
53+
fn add(self, other: Self) -> Box<Arc<Rc<Box<Rc<Self>>>>>;
54+
}
55+
56+
impl MyAdd for usize {
57+
fn add(self, other: usize) -> Box<Arc<Rc<Box<Rc<usize>>>>> {
58+
Box::new(Arc::new(Rc::new(Box::new(Rc::new(self + other)))))
59+
}
60+
}
61+
62+
#[derive(Eq, PartialEq, Debug)]
63+
struct W(Box<Arc<Rc<Box<Rc<usize>>>>>);
64+
65+
reuse impl MyAdd for W {
66+
//~^ ERROR: the trait bound `Box<Arc<Rc<Box<Rc<many_froms_2::W>>>>>: From<many_froms_2::W>` is not satisfied
67+
*****self.0
68+
}
69+
}
70+
71+
fn main() {
72+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
error[E0277]: the trait bound `Pin<Box<pin_box_self::W>>: From<pin_box_self::W>` is not satisfied
2+
--> $DIR/self-mapping-output-from-wrap-errors.rs:19:5
3+
|
4+
LL | / reuse impl MyAdd for W {
5+
LL | |
6+
LL | | *self.0
7+
LL | | }
8+
| |_____^ the trait `From<pin_box_self::W>` is not implemented for `Pin<Box<pin_box_self::W>>`
9+
|
10+
help: the trait `From<W>` is not implemented for `Pin<Box<pin_box_self::W>>`
11+
but trait `From<Box<W>>` is implemented for it
12+
--> $SRC_DIR/alloc/src/boxed/convert.rs:LL:COL
13+
= help: for that trait implementation, expected `Box<pin_box_self::W>`, found `pin_box_self::W`
14+
15+
error[E0277]: the trait bound `Box<Box<Box<Arc<Box<Rc<many_froms::W>>>>>>: From<many_froms::W>` is not satisfied
16+
--> $DIR/self-mapping-output-from-wrap-errors.rs:42:5
17+
|
18+
LL | / reuse impl MyAdd for W {
19+
LL | |
20+
LL | | ******self.0
21+
LL | | }
22+
| |_____^ the trait `From<many_froms::W>` is not implemented for `Box<Box<Box<Arc<Box<Rc<many_froms::W>>>>>>`
23+
|
24+
= help: the following other types implement trait `From<T>`:
25+
`Box<ByteStr>` implements `From<Box<[u8]>>`
26+
`Box<CStr>` implements `From<&CStr>`
27+
`Box<CStr>` implements `From<&mut CStr>`
28+
`Box<CStr>` implements `From<CString>`
29+
`Box<CStr>` implements `From<Cow<'_, CStr>>`
30+
`Box<OsStr>` implements `From<&OsStr>`
31+
`Box<OsStr>` implements `From<&mut OsStr>`
32+
`Box<OsStr>` implements `From<Cow<'_, OsStr>>`
33+
and 25 others
34+
35+
error[E0277]: the trait bound `Box<Arc<Rc<Box<Rc<many_froms_2::W>>>>>: From<many_froms_2::W>` is not satisfied
36+
--> $DIR/self-mapping-output-from-wrap-errors.rs:65:5
37+
|
38+
LL | / reuse impl MyAdd for W {
39+
LL | |
40+
LL | | *****self.0
41+
LL | | }
42+
| |_____^ the trait `From<many_froms_2::W>` is not implemented for `Box<Arc<Rc<Box<Rc<many_froms_2::W>>>>>`
43+
|
44+
= help: the following other types implement trait `From<T>`:
45+
`Box<ByteStr>` implements `From<Box<[u8]>>`
46+
`Box<CStr>` implements `From<&CStr>`
47+
`Box<CStr>` implements `From<&mut CStr>`
48+
`Box<CStr>` implements `From<CString>`
49+
`Box<CStr>` implements `From<Cow<'_, CStr>>`
50+
`Box<OsStr>` implements `From<&OsStr>`
51+
`Box<OsStr>` implements `From<&mut OsStr>`
52+
`Box<OsStr>` implements `From<Cow<'_, OsStr>>`
53+
and 25 others
54+
55+
error: aborting due to 3 previous errors
56+
57+
For more information about this error, try `rustc --explain E0277`.

0 commit comments

Comments
 (0)