Skip to content

Commit 8271e0a

Browse files
authored
feat(turbo-tasks): Accept ResolvedVc as a self argument (#70367)
#70269 adds support for `#[turbo_tasks::function]` to accept arguments of type `ResolvedVc<...>`, while still exposing a signature that uses `Vc<...>`. This PR allows them to also accept `ResolvedVc<...>` for `self`, using the nightly `arbitrary_self_types` feature. This re-uses the same mechanisms used for other arguments, with the added wrinkle that we can't shadow `self`, so we must rewrite the function body to replace references to `self` with `turbo_tasks_self`.
1 parent ac799c3 commit 8271e0a

File tree

2 files changed

+87
-15
lines changed

2 files changed

+87
-15
lines changed

turbopack/crates/turbo-tasks-macros-tests/tests/function/pass_resolved_vc_input.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,23 @@ impl ExampleStruct {
2121
fn constructor_vec(items: Vec<turbo_tasks::ResolvedVc<u32>>) -> Vc<Self> {
2222
ExampleStruct { items }.cell()
2323
}
24+
25+
#[turbo_tasks::function]
26+
fn method_with_resolved_vc_self(self: ResolvedVc<Self>) {
27+
// inner methods using `self` are unaffected
28+
struct InnerImpl;
29+
impl InnerImpl {
30+
fn inner_method(&self) {
31+
let _: &InnerImpl = self;
32+
}
33+
}
34+
35+
let _: ResolvedVc<Self> = self;
36+
}
37+
}
38+
39+
impl ExampleStruct {
40+
fn non_turbo_method_with_resolved_vc_self(self: ResolvedVc<Self>) {}
2441
}
2542

2643
#[turbo_tasks::value(resolved, transparent)]

turbopack/crates/turbo-tasks-macros/src/func.rs

Lines changed: 70 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use syn::{
99
punctuated::{Pair, Punctuated},
1010
spanned::Spanned,
1111
token::Paren,
12+
visit_mut::VisitMut,
1213
AngleBracketedGenericArguments, Block, Expr, ExprBlock, ExprPath, FnArg, GenericArgument,
1314
Local, Meta, Pat, PatIdent, PatType, Path, PathArguments, PathSegment, Receiver, ReturnType,
1415
Signature, Stmt, Token, Type, TypeGroup, TypePath, TypeTuple,
@@ -332,6 +333,7 @@ impl TurboFn<'_> {
332333
&self,
333334
orig_block: &'a Block,
334335
) -> (Signature, Cow<'a, Block>) {
336+
let mut shadow_self = None;
335337
let (inputs, transform_stmts): (Punctuated<_, _>, Vec<Option<_>>) = self
336338
.orig_signature
337339
.inputs
@@ -340,52 +342,74 @@ impl TurboFn<'_> {
340342
.map(|(idx, arg)| match arg {
341343
FnArg::Receiver(_) => (arg.clone(), None),
342344
FnArg::Typed(pat_type) => {
343-
// arbitrary self types aren't `FnArg::Receiver` on syn 1.x (fixed in 2.x)
344-
if let Pat::Ident(pat_id) = &*pat_type.pat {
345-
// TODO: Support `self: ResolvedVc<Self>`
346-
if pat_id.ident == "self" {
347-
return (arg.clone(), None);
348-
}
349-
}
350345
let Cow::Owned(expanded_ty) = expand_task_input_type(&pat_type.ty) else {
351346
// common-case: skip if no type conversion is needed
352347
return (arg.clone(), None);
353348
};
354-
let arg_ident = Ident::new(
355-
&format!("arg{idx}"),
356-
pat_type.span().resolved_at(Span::mixed_site()),
357-
);
349+
350+
let arg_id = if let Pat::Ident(pat_id) = &*pat_type.pat {
351+
// common case: argument is just an identifier
352+
Cow::Borrowed(&pat_id.ident)
353+
} else {
354+
// argument is a pattern, we need to rewrite it to a unique identifier
355+
Cow::Owned(Ident::new(
356+
&format!("arg{idx}"),
357+
pat_type.span().resolved_at(Span::mixed_site()),
358+
))
359+
};
360+
358361
let arg = FnArg::Typed(PatType {
359362
pat: Box::new(Pat::Ident(PatIdent {
360363
attrs: Vec::new(),
361364
by_ref: None,
362365
mutability: None,
363-
ident: arg_ident.clone(),
366+
ident: arg_id.clone().into_owned(),
364367
subpat: None,
365368
})),
366369
ty: Box::new(expanded_ty),
367370
..pat_type.clone()
368371
});
372+
373+
// We can't shadow `self` variables, so it this argument is a `self` argument,
374+
// generate a new identifier, and rewrite the body of the function later to use
375+
// that new identifier.
376+
// NOTE: arbitrary self types aren't `FnArg::Receiver` on syn 1.x (fixed in 2.x)
377+
let transform_pat = match &*pat_type.pat {
378+
Pat::Ident(pat_id) if pat_id.ident == "self" => {
379+
let shadow_self_id = Ident::new(
380+
"turbo_tasks_self",
381+
Span::mixed_site().located_at(pat_id.ident.span()),
382+
);
383+
shadow_self = Some(shadow_self_id.clone());
384+
Pat::Ident(PatIdent {
385+
ident: shadow_self_id,
386+
..pat_id.clone()
387+
})
388+
}
389+
pat => pat.clone(),
390+
};
391+
369392
// convert an argument of type `FromTaskInput<T>::TaskInput` into `T`.
370393
// essentially, replace any instances of `Vc` with `ResolvedVc`.
371394
let orig_ty = &*pat_type.ty;
372395
let transform_stmt = Some(Stmt::Local(Local {
373396
attrs: Vec::new(),
374397
let_token: Default::default(),
375-
pat: *pat_type.pat.clone(),
398+
pat: transform_pat,
376399
init: Some((
377400
Default::default(),
378401
// we know the argument implements `FromTaskInput` because
379402
// `expand_task_input_type` returned `Cow::Owned`
380403
parse_quote_spanned! {
381404
pat_type.span() =>
382405
<#orig_ty as turbo_tasks::task::FromTaskInput>::from_task_input(
383-
#arg_ident
406+
#arg_id
384407
)
385408
},
386409
)),
387410
semi_token: Default::default(),
388411
}));
412+
389413
(arg, transform_stmt)
390414
}
391415
})
@@ -399,13 +423,24 @@ impl TurboFn<'_> {
399423
};
400424

401425
let inline_block = if transform_stmts.is_empty() {
426+
// common case: No argument uses ResolvedVc, don't rewrite anything!
402427
Cow::Borrowed(orig_block)
403428
} else {
404429
let mut stmts = transform_stmts;
405430
stmts.push(Stmt::Expr(Expr::Block(ExprBlock {
406431
attrs: Vec::new(),
407432
label: None,
408-
block: orig_block.clone(),
433+
block: if let Some(shadow_self) = shadow_self {
434+
// if `self` is a `ResolvedVc<Self>`, we need to rewrite references to `self`
435+
let mut block = orig_block.clone();
436+
RewriteSelfVisitMut {
437+
self_ident: shadow_self,
438+
}
439+
.visit_block_mut(&mut block);
440+
block
441+
} else {
442+
orig_block.clone()
443+
},
409444
})));
410445
Cow::Owned(Block {
411446
brace_token: Default::default(),
@@ -876,6 +911,26 @@ fn expand_vc_return_type(orig_output: &Type) -> Type {
876911
new_output
877912
}
878913

914+
struct RewriteSelfVisitMut {
915+
self_ident: Ident,
916+
}
917+
918+
impl VisitMut for RewriteSelfVisitMut {
919+
fn visit_ident_mut(&mut self, ident: &mut Ident) {
920+
if ident == "self" {
921+
let span = self.self_ident.span().located_at(ident.span());
922+
*ident = self.self_ident.clone();
923+
ident.set_span(span);
924+
}
925+
// no children to visit
926+
}
927+
928+
fn visit_item_impl_mut(&mut self, _: &mut syn::ItemImpl) {
929+
// skip children of `impl`: the definition of "self" inside of an impl is different than the
930+
// parent scope's definition of "self"
931+
}
932+
}
933+
879934
/// The context in which the function is being defined.
880935
#[derive(Debug, Clone, Eq, PartialEq)]
881936
pub enum DefinitionContext {

0 commit comments

Comments
 (0)