Skip to content

Commit 5c0eee7

Browse files
committed
Support simplest newtype wrapping in delegation
1 parent 73100ee commit 5c0eee7

4 files changed

Lines changed: 515 additions & 6 deletions

File tree

compiler/rustc_ast_lowering/src/delegation.rs

Lines changed: 61 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,15 +42,16 @@ use std::ops::ControlFlow;
4242
use ast::visit::Visitor;
4343
use hir::def::{DefKind, Res};
4444
use hir::{BodyId, HirId};
45-
use rustc_abi::ExternAbi;
45+
use rustc_abi::{ExternAbi, VariantIdx};
4646
use rustc_ast as ast;
4747
use rustc_ast::node_id::NodeMap;
4848
use rustc_ast::*;
4949
use rustc_data_structures::fx::FxHashSet;
5050
use rustc_hir::attrs::{AttributeKind, InlineAttr};
5151
use rustc_hir::{self as hir, FnDeclFlags};
52-
use rustc_middle::span_bug;
52+
use rustc_middle::ty::print::with_no_trimmed_paths;
5353
use rustc_middle::ty::{Asyncness, PerOwnerResolverData};
54+
use rustc_middle::{span_bug, ty};
5455
use rustc_span::def_id::{DefId, LocalDefId};
5556
use rustc_span::symbol::kw;
5657
use rustc_span::{ErrorGuaranteed, Ident, Span, Symbol};
@@ -159,6 +160,15 @@ impl<'hir> LoweringContext<'_, 'hir> {
159160
&mut self,
160161
delegation: &Delegation,
161162
item_id: NodeId,
163+
) -> DelegationResults<'hir> {
164+
// Prevent some query cycles with it (`tests\ui\delegation\ice-issue-150673.rs`).
165+
with_no_trimmed_paths!(self.lower_delegation_internal(delegation, item_id))
166+
}
167+
168+
fn lower_delegation_internal(
169+
&mut self,
170+
delegation: &Delegation,
171+
item_id: NodeId,
162172
) -> DelegationResults<'hir> {
163173
let span = self.lower_span(delegation.last_segment_span());
164174

@@ -662,11 +672,31 @@ impl<'hir> LoweringContext<'_, 'hir> {
662672

663673
let callee_path = self.arena.alloc(self.mk_expr(hir::ExprKind::Path(new_path), span));
664674
let args = self.arena.alloc_from_iter(args);
665-
let call = self.arena.alloc(self.mk_expr(hir::ExprKind::Call(callee_path, args), span));
675+
let call = self.mk_expr(hir::ExprKind::Call(callee_path, args), span);
676+
677+
let expr = if let Some(new_type) = self.should_wrap_return_value(delegation) {
678+
let variant = new_type.variant(VariantIdx::ZERO);
679+
let res = Res::Def(DefKind::Fn, variant.ctor.expect("must have constructor").1);
680+
681+
let name = Ident::new(variant.name, self.tcx.def_span(new_type.did()));
682+
683+
// Do not need to propagate generics for newtypes as they have only one
684+
// field and if generics are specified for the newtype then they must be used
685+
// in a wrapped struct/enum/etc. somehow and they will be inferred from the return
686+
// type of the callee.
687+
let path = self.create_resolved_path(res, name, span);
688+
689+
let path = self.arena.alloc(self.mk_expr(hir::ExprKind::Path(path), span));
690+
let call = hir::ExprKind::Call(path, self.arena.alloc_slice(&[call]));
691+
692+
self.arena.alloc(self.mk_expr(call, span))
693+
} else {
694+
self.arena.alloc(call)
695+
};
666696

667697
let block = self.arena.alloc(hir::Block {
668698
stmts,
669-
expr: Some(call),
699+
expr: Some(expr),
670700
hir_id: self.next_id(),
671701
rules: hir::BlockCheckMode::DefaultBlock,
672702
span,
@@ -676,6 +706,33 @@ impl<'hir> LoweringContext<'_, 'hir> {
676706
(self.mk_expr(hir::ExprKind::Block(block, None), span), call.hir_id)
677707
}
678708

709+
fn should_wrap_return_value(&self, delegation: &Delegation) -> Option<ty::AdtDef<'hir>> {
710+
let tcx = self.tcx;
711+
let parent = tcx.local_parent(self.owner.def_id);
712+
if !matches!(tcx.def_kind(parent), DefKind::Impl { of_trait: true }) {
713+
return None;
714+
}
715+
716+
let ty::Adt(def, _) = tcx.type_of(parent).skip_binder().kind() else {
717+
return None;
718+
};
719+
720+
Some(*def).filter(|def| {
721+
// Check that we are newtype - constructor + single field in it.
722+
def.has_ctor()
723+
&& def.all_fields().count() == 1
724+
// Check that delegation path resolves to a trait AssocFn, not to a free method.
725+
&& self.get_resolution_id(delegation.id).is_some_and(|id| {
726+
tcx.def_kind(id) == DefKind::AssocFn
727+
&& tcx.def_kind(tcx.parent(id)) == DefKind::Trait
728+
// Check that the return type of the callee is `Self` param.
729+
// After previous check we are sure that `sig_id` and `delegation.id`
730+
// point to the same function.
731+
&& tcx.fn_sig(id).skip_binder().output().skip_binder().is_param(0)
732+
})
733+
})
734+
}
735+
679736
fn process_segment(
680737
&mut self,
681738
span: Span,

compiler/rustc_ast_lowering/src/delegation/generics.rs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -581,18 +581,27 @@ impl<'hir> LoweringContext<'_, 'hir> {
581581
p.def_id.to_def_id(),
582582
);
583583

584+
self.create_resolved_path(res, p.name.ident(), p.span)
585+
}
586+
587+
pub(super) fn create_resolved_path(
588+
&mut self,
589+
res: Res,
590+
ident: Ident,
591+
span: Span,
592+
) -> hir::QPath<'hir> {
584593
hir::QPath::Resolved(
585594
None,
586595
self.arena.alloc(hir::Path {
587596
segments: self.arena.alloc_slice(&[hir::PathSegment {
588597
args: None,
589598
hir_id: self.next_id(),
590-
ident: p.name.ident(),
599+
ident,
591600
infer_args: false,
592601
res,
593602
}]),
594603
res,
595-
span: p.span,
604+
span,
596605
}),
597606
)
598607
}

tests/ui/delegation/newtypes.rs

Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
#![feature(fn_delegation)]
2+
3+
mod success {
4+
trait Trait {
5+
fn method(&self) -> Self;
6+
fn r#static() -> Self;
7+
fn raw_S(&self) -> S { S }
8+
}
9+
10+
struct S;
11+
impl Trait for S {
12+
fn method(&self) -> S { S }
13+
fn r#static() -> S { S }
14+
}
15+
16+
struct W(S);
17+
impl Trait for W {
18+
reuse Trait::method { self.0 }
19+
reuse Trait::r#static;
20+
reuse Trait::raw_S { self.0 }
21+
}
22+
}
23+
24+
mod success_generics {
25+
trait Trait<'a, T, const N: usize> {
26+
fn method(&self) -> Self;
27+
fn r#static() -> Self;
28+
fn raw_S(&self) -> S<'static, ()> { S::<'static, ()>(std::marker::PhantomData) }
29+
}
30+
31+
struct S<'a, T>(std::marker::PhantomData<&'a T>);
32+
impl<'a, T, const N: usize> Trait<'a, T, N> for S<'a, T> {
33+
fn method(&self) -> S<'a, T> {
34+
S(std::marker::PhantomData)
35+
}
36+
37+
fn r#static() -> S<'a, T> { S(std::marker::PhantomData) }
38+
}
39+
40+
struct W<'a, T>(S<'a, T>);
41+
impl<'a, T, const N: usize> Trait<'a, T, N> for W<'a, T> {
42+
reuse Trait::<'a, T, N>::method { self.0 }
43+
reuse Trait::<'a, T, N>::r#static;
44+
reuse Trait::<'a, T, N>::raw_S { self.0 }
45+
}
46+
}
47+
48+
mod more_than_one_field {
49+
trait Trait {
50+
fn method(&self) -> Self;
51+
fn r#static() -> Self;
52+
fn raw_S(&self) -> S { S }
53+
}
54+
55+
struct S;
56+
impl Trait for S {
57+
fn method(&self) -> S { S }
58+
fn r#static() -> S { S }
59+
}
60+
61+
struct W(S, S, S);
62+
impl Trait for W {
63+
reuse Trait::method { self.0 }
64+
//~^ ERROR: mismatched types
65+
66+
reuse Trait::r#static { self.0 }
67+
//~^ ERROR: delegation's target expression is specified for function with no params
68+
//~| ERROR: method `static` has an incompatible type for trait
69+
//~| ERROR: this function takes 0 arguments but 1 argument was supplied
70+
//~| ERROR: the trait bound `(): more_than_one_field::Trait` is not satisfied
71+
72+
reuse Trait::raw_S { self.0 }
73+
}
74+
}
75+
76+
mod non_trait_path_reuse {
77+
trait Trait {
78+
fn method(&self) -> Self;
79+
fn r#static() -> Self;
80+
fn raw_S(&self) -> S { S }
81+
}
82+
83+
mod to_reuse {
84+
pub fn method(_: impl super::Trait) -> impl super::Trait {
85+
super::S
86+
}
87+
88+
pub fn r#static() -> impl super::Trait {
89+
super::S
90+
}
91+
}
92+
93+
pub struct S;
94+
impl Trait for S {
95+
fn method(&self) -> S { S }
96+
fn r#static() -> S { S }
97+
}
98+
99+
struct W(S);
100+
impl Trait for W {
101+
reuse to_reuse::method { self.0 }
102+
//~^ ERROR: mismatched types
103+
reuse to_reuse::r#static;
104+
//~^ ERROR: mismatched types
105+
}
106+
}
107+
108+
mod non_Self_return_type {
109+
trait Trait {
110+
fn method(&self) -> ();
111+
fn r#static() -> ();
112+
fn raw_S(&self) -> S { S }
113+
}
114+
115+
struct S;
116+
impl Trait for S {
117+
fn method(&self) -> () { () }
118+
fn r#static() -> () { () }
119+
fn raw_S(&self) -> S { S }
120+
}
121+
122+
struct W(());
123+
impl Trait for W {
124+
reuse Trait::method { self.0 }
125+
//~^ ERROR: mismatched types
126+
127+
reuse Trait::r#static;
128+
//~^ ERROR: type annotations needed
129+
130+
reuse Trait::raw_S { self.0 }
131+
//~^ ERROR: mismatched types
132+
}
133+
}
134+
135+
mod wrong_return_type {
136+
trait Trait {
137+
fn method(&self) -> Self;
138+
fn r#static() -> Self;
139+
fn raw_S(&self) -> S { S }
140+
}
141+
142+
struct F;
143+
impl Trait for F {
144+
fn method(&self) -> F { F }
145+
fn r#static() -> F { F }
146+
}
147+
148+
struct S;
149+
impl Trait for S {
150+
fn method(&self) -> S { S }
151+
fn r#static() -> S { S }
152+
}
153+
154+
struct W(S);
155+
impl Trait for W {
156+
reuse <F as Trait>::method { self.0 }
157+
//~^ ERROR: mismatched types
158+
//~| ERROR: mismatched types
159+
160+
reuse <F as Trait>::r#static;
161+
//~^ ERROR: mismatched types
162+
163+
reuse <F as Trait>::raw_S { self.0 }
164+
//~^ ERROR: mismatched types
165+
}
166+
}
167+
168+
mod wrong_target_expression {
169+
trait Trait {
170+
fn method(&self) -> Self;
171+
fn r#static() -> Self;
172+
fn raw_S(&self) -> S { S }
173+
}
174+
175+
struct S;
176+
impl Trait for S {
177+
fn method(&self) -> S { S }
178+
fn r#static() -> S { S }
179+
}
180+
181+
struct F;
182+
impl Trait for F {
183+
fn method(&self) -> F { F }
184+
fn r#static() -> F { F }
185+
}
186+
187+
struct W(S);
188+
impl Trait for W {
189+
reuse Trait::method { F }
190+
//~^ ERROR: mismatched types
191+
192+
reuse Trait::r#static;
193+
reuse Trait::raw_S { F }
194+
}
195+
}
196+
197+
fn main() {}

0 commit comments

Comments
 (0)