Skip to content

Commit

Permalink
Merge #190
Browse files Browse the repository at this point in the history
190: Improve handling of Self r=taiki-e a=taiki-e

Based on https://github.com/dtolnay/async-trait/blob/0.1.30/src/receiver.rs



Co-authored-by: Taiki Endo <[email protected]>
  • Loading branch information
bors[bot] and taiki-e authored Apr 17, 2020
2 parents 2707b35 + 2dc57f7 commit fc7630a
Show file tree
Hide file tree
Showing 4 changed files with 141 additions and 208 deletions.
95 changes: 70 additions & 25 deletions pin-project-internal/src/utils.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::mem;
use std::{iter::FromIterator, mem};

use proc_macro2::{Group, TokenStream, TokenTree};
use quote::{format_ident, quote_spanned};
Expand Down Expand Up @@ -191,7 +191,7 @@ impl<'a> ParseBufferExt<'a> for ParseBuffer<'a> {
// visitors

// Replace `self`/`Self` with `__self`/`self_ty`.
// Based on https://github.com/dtolnay/async-trait/blob/0.1.22/src/receiver.rs
// Based on https://github.com/dtolnay/async-trait/blob/0.1.30/src/receiver.rs

pub(crate) struct ReplaceReceiver<'a> {
self_ty: &'a Type,
Expand All @@ -202,7 +202,7 @@ impl<'a> ReplaceReceiver<'a> {
Self { self_ty }
}

fn self_to_qself(&mut self, qself: &mut Option<QSelf>, path: &mut Path) {
fn self_to_qself(&self, qself: &mut Option<QSelf>, path: &mut Path) {
if path.leading_colon.is_some() {
return;
}
Expand Down Expand Up @@ -235,15 +235,28 @@ impl<'a> ReplaceReceiver<'a> {
}

fn self_to_expr_path(&self, path: &mut Path) {
if path.leading_colon.is_some() {
return;
}

let first = &path.segments[0];
if first.ident != "Self" || !first.arguments.is_empty() {
return;
}

if let Type::Path(self_ty) = &self.self_ty {
*path = self_ty.path.clone();
let variant = mem::replace(path, self_ty.path.clone());
for segment in &mut path.segments {
if let PathArguments::AngleBracketed(bracketed) = &mut segment.arguments {
if bracketed.colon2_token.is_none() && !bracketed.args.is_empty() {
bracketed.colon2_token = Some(token::Colon2::default());
}
}
}
if variant.segments.len() > 1 {
path.segments.push_punct(token::Colon2::default());
path.segments.extend(variant.segments.into_pairs().skip(1));
}
} else {
let span = path.segments[0].ident.span();
let msg = "Self type of this impl is unsupported in expression position";
Expand Down Expand Up @@ -285,26 +298,48 @@ impl VisitMut for ReplaceReceiver<'_> {
}

fn visit_expr_struct_mut(&mut self, expr: &mut ExprStruct) {
if expr.path.is_ident("Self") {
self.self_to_expr_path(&mut expr.path);
}
self.self_to_expr_path(&mut expr.path);
visit_mut::visit_expr_struct_mut(self, expr);
}

fn visit_pat_path_mut(&mut self, pat: &mut PatPath) {
if pat.qself.is_none() {
self.self_to_qself(&mut pat.qself, &mut pat.path);
}
visit_mut::visit_pat_path_mut(self, pat);
}

fn visit_pat_struct_mut(&mut self, pat: &mut PatStruct) {
self.self_to_expr_path(&mut pat.path);
visit_mut::visit_pat_struct_mut(self, pat);
}

fn visit_pat_tuple_struct_mut(&mut self, pat: &mut PatTupleStruct) {
self.self_to_expr_path(&mut pat.path);
visit_mut::visit_pat_tuple_struct_mut(self, pat);
}

fn visit_item_mut(&mut self, node: &mut Item) {
match node {
// Visit `macro_rules!` because locally defined macros can refer to `self`.
Item::Macro(node) if node.mac.path.is_ident("macro_rules") => {
self.visit_macro_mut(&mut node.mac)
}
// Otherwise, do not recurse into nested items.
_ => {}
}
}

fn visit_macro_mut(&mut self, node: &mut Macro) {
// We can't tell in general whether `self` inside a macro invocation
// refers to the self in the argument list or a different self
// introduced within the macro. Heuristic: if the macro input contains
// `fn`, then `self` is more likely to refer to something other than the
// outer function's self argument.
if !contains_fn(node.tokens.clone()) {
node.tokens = fold_token_stream(node.tokens.clone());
visit_token_stream(&mut node.tokens);
}
}

fn visit_item_mut(&mut self, _: &mut Item) {
// Do not recurse into nested items.
}
}

fn contains_fn(tokens: TokenStream) -> bool {
Expand All @@ -315,25 +350,35 @@ fn contains_fn(tokens: TokenStream) -> bool {
})
}

fn fold_token_stream(tokens: TokenStream) -> TokenStream {
tokens
.into_iter()
.map(|tt| match tt {
fn visit_token_stream(tokens: &mut TokenStream) -> bool {
let mut out = Vec::new();
let mut modified = false;
for tt in tokens.clone() {
match tt {
TokenTree::Ident(mut ident) => {
prepend_underscore_to_self(&mut ident);
TokenTree::Ident(ident)
modified |= prepend_underscore_to_self(&mut ident);
out.push(TokenTree::Ident(ident));
}
TokenTree::Group(group) => {
let content = fold_token_stream(group.stream());
TokenTree::Group(Group::new(group.delimiter(), content))
let mut content = group.stream();
modified |= visit_token_stream(&mut content);
let mut new = Group::new(group.delimiter(), content);
new.set_span(group.span());
out.push(TokenTree::Group(new));
}
other => other,
})
.collect()
other => out.push(other),
}
}
if modified {
*tokens = TokenStream::from_iter(out);
}
modified
}

pub(crate) fn prepend_underscore_to_self(ident: &mut Ident) {
if ident == "self" {
pub(crate) fn prepend_underscore_to_self(ident: &mut Ident) -> bool {
let modified = ident == "self";
if modified {
*ident = Ident::new("__self", ident.span());
}
modified
}
61 changes: 43 additions & 18 deletions tests/pinned_drop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,6 @@ fn self_call() {
}
}

// See also `ui/pinned_drop/self.rs`.
#[test]
fn self_expr() {
#[pin_project(PinnedDrop)]
Expand All @@ -136,25 +135,26 @@ fn self_expr() {
let _: Self = Self(0);
}
}
}

#[rustversion::since(1.37)]
#[rustversion::since(1.37)]
#[test]
fn self_expr_enum() {
#[pin_project(PinnedDrop)]
pub enum Enum {
StructVariant { x: usize },
TupleVariant(usize),
}

#[rustversion::since(1.37)]
#[pinned_drop]
impl PinnedDrop for Enum {
fn drop(mut self: Pin<&mut Self>) {
// let _: Self = Self::StructVariant { x: 0 }; //~ ERROR can't use generic parameters from outer function [E0401]
let _: Self = Self::StructVariant { x: 0 };
let _: Self = Self::TupleVariant(0);
}
}
}

// See also `ui/pinned_drop/self.rs`.
#[test]
fn self_pat() {
#[pin_project(PinnedDrop)]
Expand All @@ -164,12 +164,13 @@ fn self_pat() {

#[pinned_drop]
impl PinnedDrop for Struct {
#[allow(irrefutable_let_patterns)]
fn drop(mut self: Pin<&mut Self>) {
// match *self {
// Self { x: _ } => {} //~ ERROR can't use generic parameters from outer function [E0401]
// }
// if let Self { x: _ } = *self {} //~ ERROR can't use generic parameters from outer function [E0401]
// let Self { x: _ } = *self; //~ ERROR can't use generic parameters from outer function [E0401]
match *self {
Self { x: _ } => {}
}
if let Self { x: _ } = *self {}
let Self { x: _ } = *self;
}
}

Expand All @@ -187,24 +188,48 @@ fn self_pat() {
let Self(_) = *self;
}
}
}

#[rustversion::since(1.37)]
#[rustversion::since(1.37)]
#[test]
fn self_pat_enum() {
#[pin_project(PinnedDrop)]
pub enum Enum {
StructVariant { x: usize },
TupleVariant(usize),
}

#[rustversion::since(1.37)]
#[pinned_drop]
impl PinnedDrop for Enum {
fn drop(mut self: Pin<&mut Self>) {
// match *self {
// Self::StructVariant { x: _ } => {} //~ ERROR can't use generic parameters from outer function [E0401]
// Self::TupleVariant(_) => {} //~ ERROR can't use generic parameters from outer function [E0401]
// }
// if let Self::StructVariant { x: _ } = *self {} //~ ERROR can't use generic parameters from outer function [E0401]
// if let Self::TupleVariant(_) = *self {} //~ ERROR can't use generic parameters from outer function [E0401]
match *self {
Self::StructVariant { x: _ } => {}
Self::TupleVariant(_) => {}
}
if let Self::StructVariant { x: _ } = *self {}
if let Self::TupleVariant(_) = *self {}
}
}
}

// See also `ui/pinned_drop/self.rs`.
#[rustversion::since(1.40)] // https://github.com/rust-lang/rust/pull/64690
#[test]
fn self_in_macro_def() {
#[pin_project(PinnedDrop)]
pub struct Struct {
x: usize,
}

#[pinned_drop]
impl PinnedDrop for Struct {
fn drop(self: Pin<&mut Self>) {
macro_rules! t {
() => {{
let _ = self;
}};
}
t!();
}
}
}
89 changes: 12 additions & 77 deletions tests/ui/pinned_drop/self.rs
Original file line number Diff line number Diff line change
@@ -1,91 +1,26 @@
use pin_project::{pin_project, pinned_drop};
use std::pin::Pin;

fn self_expr() {
fn self_in_macro_def() {
#[pin_project(PinnedDrop)]
pub struct Struct {
x: usize,
}

#[pinned_drop]
impl PinnedDrop for Struct {
fn drop(mut self: Pin<&mut Self>) {
let _: Self = Self { x: 0 };
}
}

#[pin_project(PinnedDrop)]
pub struct TupleStruct(usize);

#[pinned_drop]
impl PinnedDrop for TupleStruct {
fn drop(mut self: Pin<&mut Self>) {
let _: Self = Self(0);
}
}

#[pin_project(PinnedDrop)]
pub enum Enum {
StructVariant { x: usize },
TupleVariant(usize),
}

#[pinned_drop]
impl PinnedDrop for Enum {
fn drop(mut self: Pin<&mut Self>) {
let _: Self = Self::StructVariant { x: 0 }; //~ ERROR can't use generic parameters from outer function [E0401]
let _: Self = Self::TupleVariant(0);
}
}
}

fn self_pat() {
#[pin_project(PinnedDrop)]
pub struct Struct {
x: usize,
}

#[pinned_drop]
impl PinnedDrop for Struct {
fn drop(mut self: Pin<&mut Self>) {
match *self {
Self { x: _ } => {} //~ ERROR can't use generic parameters from outer function [E0401]
}
if let Self { x: _ } = *self {} //~ ERROR can't use generic parameters from outer function [E0401]
let Self { x: _ } = *self; //~ ERROR can't use generic parameters from outer function [E0401]
}
}

#[pin_project(PinnedDrop)]
pub struct TupleStruct(usize);

#[pinned_drop]
impl PinnedDrop for TupleStruct {
#[allow(irrefutable_let_patterns)]
fn drop(mut self: Pin<&mut Self>) {
match *self {
Self(_) => {}
}
if let Self(_) = *self {}
let Self(_) = *self;
}
}

#[pin_project(PinnedDrop)]
pub enum Enum {
StructVariant { x: usize },
TupleVariant(usize),
}

#[pinned_drop]
impl PinnedDrop for Enum {
fn drop(mut self: Pin<&mut Self>) {
match *self {
Self::StructVariant { x: _ } => {} //~ ERROR can't use generic parameters from outer function [E0401]
Self::TupleVariant(_) => {} //~ ERROR can't use generic parameters from outer function [E0401]
fn drop(self: Pin<&mut Self>) {
macro_rules! t {
() => {{
let _ = self; //~ ERROR can't capture dynamic environment in a fn item

fn f(self: ()) {
//~^ ERROR `self` parameter is only allowed in associated functions
let _ = self;
}
}};
}
if let Self::StructVariant { x: _ } = *self {} //~ ERROR can't use generic parameters from outer function [E0401]
if let Self::TupleVariant(_) = *self {} //~ ERROR can't use generic parameters from outer function [E0401]
t!();
}
}
}
Expand Down
Loading

0 comments on commit fc7630a

Please sign in to comment.