Skip to content

Commit 0fa1700

Browse files
committed
Add self argument destructuring. Closes rust-lang#7613.
1 parent f203fc7 commit 0fa1700

File tree

10 files changed

+193
-26
lines changed

10 files changed

+193
-26
lines changed

src/doc/rust.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3422,6 +3422,17 @@ impl Printable for ~str {
34223422
`self` refers to the value of type `~str` that is the receiver for a
34233423
call to the method `make_string`.
34243424

3425+
The `self` argument may be destructured using `self @ pattern` syntax.
3426+
For example:
3427+
3428+
~~~~
3429+
struct Foo(uint);
3430+
3431+
impl Foo {
3432+
fn b(&self@&Foo(ref u)) { ... }
3433+
}
3434+
~~~~
3435+
34253436
## Type kinds
34263437

34273438
Types in Rust are categorized into kinds, based on various properties of the components of the type.

src/libsyntax/ast.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -893,7 +893,7 @@ pub struct Arg {
893893
}
894894

895895
impl Arg {
896-
pub fn new_self(span: Span, mutability: Mutability) -> Arg {
896+
pub fn new_self(span: Span, mutability: Mutability, pat: Option<P<Pat>>) -> Arg {
897897
let path = ast_util::ident_to_path(span, special_idents::self_);
898898
Arg {
899899
// HACK(eddyb) fake type for the self argument.
@@ -904,7 +904,7 @@ impl Arg {
904904
}),
905905
pat: @Pat {
906906
id: DUMMY_NODE_ID,
907-
node: PatIdent(BindByValue(mutability), path, None),
907+
node: PatIdent(BindByValue(mutability), path, pat),
908908
span: span
909909
},
910910
id: DUMMY_NODE_ID

src/libsyntax/ext/deriving/generic.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -581,7 +581,7 @@ impl<'a> MethodDef<'a> {
581581

582582
let self_arg = match explicit_self.node {
583583
ast::SelfStatic => None,
584-
_ => Some(ast::Arg::new_self(trait_.span, ast::MutImmutable))
584+
_ => Some(ast::Arg::new_self(trait_.span, ast::MutImmutable, None))
585585
};
586586
let args = {
587587
let args = arg_types.move_iter().map(|(name, ty)| {

src/libsyntax/parse/parser.rs

Lines changed: 36 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1058,6 +1058,17 @@ impl Parser {
10581058
p.parse_arg_general(false)
10591059
});
10601060

1061+
match explicit_self.node {
1062+
SelfStatic => (),
1063+
_ => match d.inputs[0].pat.node {
1064+
PatIdent(_, _, Some(_)) => {
1065+
p.span_err(d.inputs[0].pat.span,
1066+
"cannot destructure self in trait methods");
1067+
}
1068+
_ => (),
1069+
}
1070+
}
1071+
10611072
let hi = p.last_span.hi;
10621073
match p.token {
10631074
token::SEMI => {
@@ -3598,8 +3609,17 @@ impl Parser {
35983609
// that may have a self type.
35993610
fn parse_fn_decl_with_self(&mut self, parse_arg_fn: |&mut Parser| -> Arg)
36003611
-> (ExplicitSelf, P<FnDecl>) {
3612+
fn parse_self_pat(p: &mut Parser) -> Option<P<Pat>> {
3613+
if p.token == token::AT {
3614+
p.bump();
3615+
Some(p.parse_pat())
3616+
} else {
3617+
None
3618+
}
3619+
}
3620+
36013621
fn maybe_parse_borrowed_explicit_self(this: &mut Parser)
3602-
-> ast::ExplicitSelf_ {
3622+
-> (ast::ExplicitSelf_,Option<P<Pat>>) {
36033623
// The following things are possible to see here:
36043624
//
36053625
// fn(&mut self)
@@ -3612,23 +3632,23 @@ impl Parser {
36123632
if this.look_ahead(1, |t| token::is_keyword(keywords::Self, t)) {
36133633
this.bump();
36143634
this.expect_self_ident();
3615-
SelfRegion(None, MutImmutable)
3635+
(SelfRegion(None, MutImmutable), parse_self_pat(this))
36163636
} else if this.look_ahead(1, |t| Parser::token_is_mutability(t)) &&
36173637
this.look_ahead(2,
36183638
|t| token::is_keyword(keywords::Self,
36193639
t)) {
36203640
this.bump();
36213641
let mutability = this.parse_mutability();
36223642
this.expect_self_ident();
3623-
SelfRegion(None, mutability)
3643+
(SelfRegion(None, mutability), parse_self_pat(this))
36243644
} else if this.look_ahead(1, |t| Parser::token_is_lifetime(t)) &&
36253645
this.look_ahead(2,
36263646
|t| token::is_keyword(keywords::Self,
36273647
t)) {
36283648
this.bump();
36293649
let lifetime = this.parse_lifetime();
36303650
this.expect_self_ident();
3631-
SelfRegion(Some(lifetime), MutImmutable)
3651+
(SelfRegion(Some(lifetime), MutImmutable), parse_self_pat(this))
36323652
} else if this.look_ahead(1, |t| Parser::token_is_lifetime(t)) &&
36333653
this.look_ahead(2, |t| {
36343654
Parser::token_is_mutability(t)
@@ -3639,9 +3659,9 @@ impl Parser {
36393659
let lifetime = this.parse_lifetime();
36403660
let mutability = this.parse_mutability();
36413661
this.expect_self_ident();
3642-
SelfRegion(Some(lifetime), mutability)
3662+
(SelfRegion(Some(lifetime), mutability), parse_self_pat(this))
36433663
} else {
3644-
SelfStatic
3664+
(SelfStatic,None)
36453665
}
36463666
}
36473667

@@ -3651,7 +3671,7 @@ impl Parser {
36513671
// backwards compatible.
36523672
let lo = self.span.lo;
36533673
let mut mutbl_self = MutImmutable;
3654-
let explicit_self = match self.token {
3674+
let (explicit_self,self_pat) = match self.token {
36553675
token::BINOP(token::AND) => {
36563676
maybe_parse_borrowed_explicit_self(self)
36573677
}
@@ -3660,14 +3680,14 @@ impl Parser {
36603680
if self.look_ahead(1, |t| token::is_keyword(keywords::Self, t)) {
36613681
self.bump();
36623682
self.expect_self_ident();
3663-
SelfUniq
3683+
(SelfUniq,parse_self_pat(self))
36643684
} else {
3665-
SelfStatic
3685+
(SelfStatic,None)
36663686
}
36673687
}
36683688
token::IDENT(..) if self.is_self_ident() => {
36693689
self.bump();
3670-
SelfValue
3690+
(SelfValue,parse_self_pat(self))
36713691
}
36723692
token::BINOP(token::STAR) => {
36733693
// Possibly "*self" or "*mut self" -- not supported. Try to avoid
@@ -3680,23 +3700,23 @@ impl Parser {
36803700
self.span_err(self.span, "cannot pass self by unsafe pointer");
36813701
self.bump();
36823702
}
3683-
SelfValue
3703+
(SelfValue,parse_self_pat(self))
36843704
}
36853705
_ if Parser::token_is_mutability(&self.token) &&
36863706
self.look_ahead(1, |t| token::is_keyword(keywords::Self, t)) => {
36873707
mutbl_self = self.parse_mutability();
36883708
self.expect_self_ident();
3689-
SelfValue
3709+
(SelfValue,parse_self_pat(self))
36903710
}
36913711
_ if Parser::token_is_mutability(&self.token) &&
36923712
self.look_ahead(1, |t| *t == token::TILDE) &&
36933713
self.look_ahead(2, |t| token::is_keyword(keywords::Self, t)) => {
36943714
mutbl_self = self.parse_mutability();
36953715
self.bump();
36963716
self.expect_self_ident();
3697-
SelfUniq
3717+
(SelfUniq,parse_self_pat(self))
36983718
}
3699-
_ => SelfStatic
3719+
_ => (SelfStatic,None)
37003720
};
37013721

37023722
let explicit_self_sp = mk_sp(lo, self.span.hi);
@@ -3712,11 +3732,11 @@ impl Parser {
37123732
sep,
37133733
parse_arg_fn
37143734
);
3715-
fn_inputs.unshift(Arg::new_self(explicit_self_sp, mutbl_self));
3735+
fn_inputs.unshift(Arg::new_self(explicit_self_sp, mutbl_self, self_pat));
37163736
fn_inputs
37173737
}
37183738
token::RPAREN => {
3719-
~[Arg::new_self(explicit_self_sp, mutbl_self)]
3739+
~[Arg::new_self(explicit_self_sp, mutbl_self, self_pat)]
37203740
}
37213741
_ => {
37223742
let token_str = self.this_token_to_str();

src/libsyntax/print/pprust.rs

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1786,14 +1786,15 @@ pub fn print_pat(s: &mut State, pat: &ast::Pat) -> io::IoResult<()> {
17861786

17871787
pub fn explicit_self_to_str(explicit_self: &ast::ExplicitSelf_) -> ~str {
17881788
to_str(explicit_self, |a, &b| {
1789-
print_explicit_self(a, b, ast::MutImmutable).map(|_| ())
1789+
print_explicit_self(a, b, ast::MutImmutable, None).map(|_| ())
17901790
})
17911791
}
17921792

17931793
// Returns whether it printed anything
17941794
fn print_explicit_self(s: &mut State,
17951795
explicit_self: ast::ExplicitSelf_,
1796-
mutbl: ast::Mutability) -> io::IoResult<bool> {
1796+
mutbl: ast::Mutability,
1797+
self_pat: Option<P<ast::Pat>>) -> io::IoResult<bool> {
17971798
try!(print_mutability(s, mutbl));
17981799
match explicit_self {
17991800
ast::SelfStatic => { return Ok(false); }
@@ -1810,6 +1811,13 @@ fn print_explicit_self(s: &mut State,
18101811
try!(word(&mut s.s, "self"));
18111812
}
18121813
}
1814+
match self_pat {
1815+
Some(pat) => {
1816+
try!(word(&mut s.s, "@"));
1817+
try!(print_pat(s, pat));
1818+
}
1819+
None => ()
1820+
}
18131821
return Ok(true);
18141822
}
18151823

@@ -1840,14 +1848,14 @@ pub fn print_fn_args(s: &mut State, decl: &ast::FnDecl,
18401848
try!(rbox(s, 0u, Inconsistent));
18411849
let mut first = true;
18421850
for &explicit_self in opt_explicit_self.iter() {
1843-
let m = match explicit_self {
1844-
ast::SelfStatic => ast::MutImmutable,
1851+
let (m,self_pat) = match explicit_self {
1852+
ast::SelfStatic => (ast::MutImmutable,None),
18451853
_ => match decl.inputs[0].pat.node {
1846-
ast::PatIdent(ast::BindByValue(m), _, _) => m,
1847-
_ => ast::MutImmutable
1854+
ast::PatIdent(ast::BindByValue(m), _, pat) => (m,pat),
1855+
_ => (ast::MutImmutable,None),
18481856
}
18491857
};
1850-
first = !try!(print_explicit_self(s, explicit_self, m));
1858+
first = !try!(print_explicit_self(s, explicit_self, m, self_pat));
18511859
}
18521860

18531861
// HACK(eddyb) ignore the separately printed self argument.

src/test/compile-fail/liveness-unused.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,5 +77,13 @@ fn f4b() -> int {
7777
}
7878
}
7979

80+
struct Foo(int);
81+
82+
impl Foo {
83+
fn foo(&self@&Foo(i)) {
84+
//~^ ERROR: unused variable: `i`
85+
}
86+
}
87+
8088
fn main() {
8189
}

src/test/compile-fail/self-pat-2.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
struct A(~uint);
12+
13+
trait T {
14+
fn t(&self@&A(ref u));
15+
//~^ ERROR: cannot destructure self in trait methods
16+
}
17+
18+
fn main() {}

src/test/compile-fail/self-pat-3.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
enum E { E1(int), E2(int) }
12+
13+
impl E {
14+
fn t(&self@&E1(ref i)) {}
15+
//~^ ERROR: refutable pattern in function argument
16+
}
17+
18+
fn main() {}

src/test/compile-fail/self-pat.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
struct A(~uint);
12+
13+
impl A {
14+
fn a(&self@&A(ref mut u), i: uint) {
15+
//~^ ERROR: cannot borrow immutable anonymous field as mutable
16+
**u = i;
17+
}
18+
19+
fn b(self@A(u)) -> ~uint {
20+
let A(u) = self; // FIXME: Remove this line when #12534 is fixed
21+
//~^ NOTE: `self#0` moved here
22+
let _ = self;
23+
//~^ ERROR: use of partially moved value: `self`
24+
u
25+
}
26+
}
27+
28+
fn main() {}

src/test/run-pass/self-pat.rs

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
struct A(uint);
12+
13+
impl A {
14+
fn a(self@A(u)) -> uint {
15+
u
16+
}
17+
18+
fn b(&mut self@&A(ref mut u), i: uint) {
19+
*u = i;
20+
}
21+
22+
fn c(~self@~A(u)) -> uint {
23+
u
24+
}
25+
}
26+
27+
struct B<T> {
28+
t: T,
29+
}
30+
31+
impl<T> B<T> {
32+
fn a(self@B { t: t }) -> T {
33+
t
34+
}
35+
36+
fn b(&mut self@&B { t: ref mut t }, i: T) {
37+
*t = i;
38+
}
39+
}
40+
41+
fn main() {
42+
let a = A(1);
43+
assert_eq!(a.a(), 1);
44+
let mut a = A(2);
45+
a.b(3);
46+
assert_eq!(a.a(), 3);
47+
// FIXME A::c crashes (probably related to #12534)
48+
// let a = ~A(1);
49+
// assert_eq!(a.c(), 1);
50+
51+
let b = B { t: false };
52+
assert!(!b.a());
53+
let mut b = B { t: 0 as uint };
54+
b.b(3);
55+
assert_eq!(b.a(), 3);
56+
}

0 commit comments

Comments
 (0)