From c7f85003a345826b17801d473303d0c545d0e8d2 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 11 Feb 2013 16:33:31 -0800 Subject: [PATCH 1/5] Permit lifetimes to appear in type parameter lists and after `&`. Lifetimes in type parameter lists are currently ignored, but `&'a T` is equivalent to `&a/T`. --- src/libsyntax/ast.rs | 6 + src/libsyntax/parse/parser.rs | 110 ++++++++++++++++-- src/test/compile-fail/regions-addr-of-arg.rs | 4 +- .../regions-addr-of-upvar-self.rs | 2 +- src/test/compile-fail/regions-bounds.rs | 6 +- src/test/compile-fail/regions-in-enums.rs | 9 +- src/test/run-pass/regions-self-impls.rs | 4 +- 7 files changed, 120 insertions(+), 21 deletions(-) diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index 14a66a49df3b..5fcc53a407bc 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -67,6 +67,12 @@ pub impl to_bytes::IterBytes for ident { // Functions may or may not have names. pub type fn_ident = Option; +pub struct Lifetime { + id: node_id, + span: span, + ident: ident +} + #[auto_encode] #[auto_decode] #[deriving_eq] diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 15258a6bbeef..fce0948e711d 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -624,8 +624,13 @@ pub impl Parser { sigil: ast::Sigil, ctor: &fn(+v: mt) -> ty_) -> ty_ { - // @foo/fn() or @fn() are parsed directly as fn types: + // @'foo fn() or @foo/fn() or @fn() are parsed directly as fn types: match copy self.token { + token::LIFETIME(rname) => { + self.bump(); + return self.parse_ty_closure(Some(sigil), Some(rname)); + } + token::IDENT(rname, _) => { if self.look_ahead(1u) == token::BINOP(token::SLASH) && self.token_is_closure_keyword(self.look_ahead(2u)) @@ -648,8 +653,13 @@ pub impl Parser { } fn parse_borrowed_pointee() -> ty_ { - // look for `&foo/` and interpret `foo` as the region name: - let rname = match copy self.token { + // look for `&'lt` or `&foo/` and interpret `foo` as the region name: + let rname = match self.token { + token::LIFETIME(sid) => { + self.bump(); + Some(sid) + } + token::IDENT(sid, _) => { if self.look_ahead(1u) == token::BINOP(token::SLASH) { self.bump(); self.bump(); @@ -658,6 +668,7 @@ pub impl Parser { None } } + _ => { None } }; @@ -890,22 +901,95 @@ pub impl Parser { } }; - // Parse any type parameters which may appear: + // Parse any lifetime or type parameters which may appear: let tps = { - if self.token == token::LT { - self.parse_seq_lt_gt(Some(token::COMMA), - |p| p.parse_ty(false)) + if !self.eat(token::LT) { + ~[] } else { - codemap::spanned {node: ~[], span: path.span} + // First consume lifetimes. + let _lifetimes = self.parse_lifetimes(); + let result = self.parse_seq_to_gt( + Some(token::COMMA), + |p| p.parse_ty(false)); + result } }; - @ast::path { span: mk_sp(lo, tps.span.hi), + let hi = self.span.lo; + + @ast::path { span: mk_sp(lo, hi), rp: rp, - types: tps.node, + types: tps, .. *path } } + fn parse_opt_lifetime() -> Option { + /*! + * + * Parses 0 or 1 lifetime. + */ + + match self.token { + token::LIFETIME(_) => { + Some(self.parse_lifetime()) + } + _ => { + None + } + } + } + + fn parse_lifetime() -> ast::Lifetime { + /*! + * + * Parses a single lifetime. + */ + + match self.token { + token::LIFETIME(i) => { + self.bump(); + return ast::Lifetime { + id: self.get_id(), + span: self.span, + ident: i + }; + } + _ => { + self.fatal(fmt!("Expected a lifetime name")); + } + } + } + + fn parse_lifetimes() -> ~[ast::Lifetime] { + /*! + * + * Parses zero or more comma separated lifetimes. + * Expects each lifetime to be followed by either + * a comma or `>`. Used when parsing type parameter + * lists, where we expect something like `<'a, 'b, T>`. + */ + + let mut res = ~[]; + loop { + match self.token { + token::LIFETIME(_) => { + res.push(self.parse_lifetime()); + } + _ => { + return res; + } + } + + match self.token { + token::COMMA => { self.bump();} + token::GT => { return res; } + _ => { + self.fatal(~"expected `,` or `>` after lifetime name"); + } + } + } + } + fn parse_mutability() -> mutability { if self.eat_keyword(~"mut") { m_mutbl @@ -1424,6 +1508,7 @@ pub impl Parser { } token::AND => { self.bump(); + let _lt = self.parse_opt_lifetime(); let m = self.parse_mutability(); let e = self.parse_prefix_expr(); hi = e.span.hi; @@ -2574,7 +2659,10 @@ pub impl Parser { fn parse_ty_params() -> ~[ty_param] { if self.eat(token::LT) { - self.parse_seq_to_gt(Some(token::COMMA), |p| p.parse_ty_param()) + let _lifetimes = self.parse_lifetimes(); + self.parse_seq_to_gt( + Some(token::COMMA), + |p| p.parse_ty_param()) } else { ~[] } } diff --git a/src/test/compile-fail/regions-addr-of-arg.rs b/src/test/compile-fail/regions-addr-of-arg.rs index 54d7c0b4d18b..7f2140d96e16 100644 --- a/src/test/compile-fail/regions-addr-of-arg.rs +++ b/src/test/compile-fail/regions-addr-of-arg.rs @@ -9,11 +9,11 @@ // except according to those terms. fn foo(a: int) { - let _p: &static/int = &a; //~ ERROR illegal borrow + let _p: &'static int = &a; //~ ERROR illegal borrow } fn bar(a: int) { - let _q: &blk/int = &a; + let _q: &'blk int = &a; } fn main() { diff --git a/src/test/compile-fail/regions-addr-of-upvar-self.rs b/src/test/compile-fail/regions-addr-of-upvar-self.rs index 5bb82d1ee662..cf026785420d 100644 --- a/src/test/compile-fail/regions-addr-of-upvar-self.rs +++ b/src/test/compile-fail/regions-addr-of-upvar-self.rs @@ -15,7 +15,7 @@ struct dog { impl dog { fn chase_cat() { for uint::range(0u, 10u) |_i| { - let p: &static/mut uint = &mut self.food; //~ ERROR illegal borrow + let p: &'static mut uint = &mut self.food; //~ ERROR illegal borrow *p = 3u; } } diff --git a/src/test/compile-fail/regions-bounds.rs b/src/test/compile-fail/regions-bounds.rs index ba125f0b3231..47ddbf38e3de 100644 --- a/src/test/compile-fail/regions-bounds.rs +++ b/src/test/compile-fail/regions-bounds.rs @@ -13,8 +13,10 @@ // checked. enum an_enum = ∫ -trait a_trait { fn foo() -> &self/int; } -struct a_class { x:&self/int } +trait a_trait { + fn foo() -> &'self int; +} +struct a_class { x:&'self int } fn a_fn1(e: an_enum/&a) -> an_enum/&b { return e; //~ ERROR mismatched types: expected `an_enum/&b` but found `an_enum/&a` diff --git a/src/test/compile-fail/regions-in-enums.rs b/src/test/compile-fail/regions-in-enums.rs index baf072c01eea..68c61137cbf6 100644 --- a/src/test/compile-fail/regions-in-enums.rs +++ b/src/test/compile-fail/regions-in-enums.rs @@ -8,16 +8,17 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -enum yes0 { - x3(&uint) +enum yes0<'lt> { + // This will eventually be legal (and in fact the only way): + x3(&'lt uint) //~ ERROR named regions other than `self` are not allowed as part of a type declaration } enum yes1 { - x4(&self/uint) + x4(&'self uint) } enum yes2 { - x5(&foo/uint) //~ ERROR named regions other than `self` are not allowed as part of a type declaration + x5(&'foo uint) //~ ERROR named regions other than `self` are not allowed as part of a type declaration } fn main() {} diff --git a/src/test/run-pass/regions-self-impls.rs b/src/test/run-pass/regions-self-impls.rs index 7b07a8cf1af4..87aefdca537d 100644 --- a/src/test/run-pass/regions-self-impls.rs +++ b/src/test/run-pass/regions-self-impls.rs @@ -8,7 +8,9 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -struct Clam { chowder: &int } +struct Clam<'self> { + chowder: &'self int +} trait get_chowder { fn get_chowder() -> &self/int; From 1ef8c48a204ad215310f38a239a898ee38d9bf8e Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 11 Feb 2013 16:33:44 -0800 Subject: [PATCH 2/5] emacs mode: Highlight 'foo as a lifetime, not a character constant. --- src/etc/emacs/rust-mode.el | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/src/etc/emacs/rust-mode.el b/src/etc/emacs/rust-mode.el index ef98fa669e34..92b1d809008f 100644 --- a/src/etc/emacs/rust-mode.el +++ b/src/etc/emacs/rust-mode.el @@ -101,14 +101,7 @@ (rust-push-context st 'string (current-column) t) (setf (rust-state-tokenize st) 'rust-token-string) (rust-token-string st)) - (def ?\' (forward-char) - (setf rust-tcat 'atom) - (let ((is-escape (eq (char-after) ?\\)) - (start (point))) - (if (not (rust-eat-until-unescaped ?\')) - 'font-lock-warning-face - (if (or is-escape (= (point) (+ start 2))) - 'font-lock-string-face 'font-lock-warning-face)))) + (def ?\' (rust-single-quote)) (def ?/ (forward-char) (case (char-after) (?/ (end-of-line) 'font-lock-comment-face) @@ -150,6 +143,23 @@ (setf rust-tcat 'op) nil) table))) +(defun rust-single-quote () + (forward-char) + (setf rust-tcat 'atom) + ; Is this a lifetime? + (if (or (looking-at "[a-zA-Z_]$") + (looking-at "[a-zA-Z_][^']")) + ; If what we see is 'abc, use font-lock-type-face: + (progn (rust-eat-re "[a-zA-Z_][a-zA-Z_0-9]*") + 'font-lock-type-face) + ; Otherwise, handle as a character constant: + (let ((is-escape (eq (char-after) ?\\)) + (start (point))) + (if (not (rust-eat-until-unescaped ?\')) + 'font-lock-warning-face + (if (or is-escape (= (point) (+ start 2))) + 'font-lock-string-face 'font-lock-warning-face))))) + (defun rust-token-base (st) (funcall (char-table-range rust-char-table (char-after)) st)) From ad8b437adabbc11e79c3672bd5c74294f38d3bc4 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 12 Feb 2013 14:39:11 -0800 Subject: [PATCH 3/5] Make 'foo use font-lock-builtin-face, like module names, and make capitalized identifiers optionally use font-lock-type-face --- src/etc/emacs/rust-mode.el | 32 +++++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/src/etc/emacs/rust-mode.el b/src/etc/emacs/rust-mode.el index 92b1d809008f..5fbd2ab67c29 100644 --- a/src/etc/emacs/rust-mode.el +++ b/src/etc/emacs/rust-mode.el @@ -7,7 +7,6 @@ (require 'cm-mode) (require 'cc-mode) -(eval-when-compile (require 'cl)) (defun rust-electric-brace (arg) (interactive "*P") @@ -17,6 +16,12 @@ '(font-lock-comment-face font-lock-string-face)))) (cm-indent))) +(defcustom rust-capitalized-idents-are-types t + "If non-nil, capitalized identifiers will be treated as types for the purposes of font-lock mode" + :type 'boolean + :require 'rust-mode + :group 'rust-mode) + (defvar rust-indent-unit 4) (defvar rust-syntax-table (let ((table (make-syntax-table))) (c-populate-syntax-table table) @@ -115,12 +120,7 @@ ((rust-eat-re "[a-z_]+") (setf rust-tcat 'macro))) 'font-lock-preprocessor-face) (def ((?a . ?z) (?A . ?Z) ?_) - (rust-eat-re "[a-zA-Z_][a-zA-Z0-9_]*") - (setf rust-tcat 'ident) - (if (and (eq (char-after) ?:) (eq (char-after (+ (point) 1)) ?:) - (not (eq (char-after (+ (point) 2)) ?:))) - (progn (forward-char 2) 'font-lock-builtin-face) - (match-string 0))) + (rust-token-identifier)) (def ((?0 . ?9)) (rust-eat-re "0x[0-9a-fA-F_]+\\|0b[01_]+\\|[0-9_]+\\(\\.[0-9_]+\\)?\\(e[+\\-]?[0-9_]+\\)?") (setf rust-tcat 'atom) @@ -143,15 +143,23 @@ (setf rust-tcat 'op) nil) table))) +(defun rust-token-identifier () + (rust-eat-re "[a-zA-Z_][a-zA-Z0-9_]*") + (setf rust-tcat 'ident) + (if (and (eq (char-after) ?:) (eq (char-after (+ (point) 1)) ?:) + (not (eq (char-after (+ (point) 2)) ?:))) + (progn (forward-char 2) 'font-lock-builtin-face) + (match-string 0))) + (defun rust-single-quote () (forward-char) (setf rust-tcat 'atom) ; Is this a lifetime? (if (or (looking-at "[a-zA-Z_]$") (looking-at "[a-zA-Z_][^']")) - ; If what we see is 'abc, use font-lock-type-face: + ; If what we see is 'abc, use font-lock-builtin-face: (progn (rust-eat-re "[a-zA-Z_][a-zA-Z_0-9]*") - 'font-lock-type-face) + 'font-lock-builtin-face) ; Otherwise, handle as a character constant: (let ((is-escape (eq (char-after) ?\\)) (start (point))) @@ -200,6 +208,10 @@ (dolist (cx (rust-state-context st)) (when (eq (rust-context-type cx) ?\}) (return (rust-context-info cx))))) +(defun rust-is-capitalized (string) + (let ((case-fold-search nil)) + (string-match-p "[A-Z]" string))) + (defun rust-token (st) (let ((cx (car (rust-state-context st)))) (when (bolp) @@ -216,6 +228,8 @@ (setf tok (cond ((eq tok-id 'atom) 'font-lock-constant-face) (tok-id 'font-lock-keyword-face) ((equal (rust-state-last-token st) 'def) 'font-lock-function-name-face) + ((and rust-capitalized-idents-are-types + (rust-is-capitalized tok)) 'font-lock-type-face) (t nil)))) (when rust-tcat (when (eq (rust-context-align cx) 'unset) From 6c728e32c0bad69318c1c4d4413ea55ce7c5813f Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 12 Feb 2013 15:01:29 -0800 Subject: [PATCH 4/5] Parse (and discard) lifetime declarations on function types --- src/libsyntax/parse/parser.rs | 46 ++++++++++++++--------- src/test/compile-fail/regions-in-enums.rs | 6 +-- src/test/compile-fail/regions-scoping.rs | 22 +++++------ 3 files changed, 43 insertions(+), 31 deletions(-) diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index fce0948e711d..799f0d40a461 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -307,12 +307,12 @@ pub impl Parser { { /* - extern "ABI" [pure|unsafe] fn (S) -> T - ^~~~^ ^~~~~~~~~~~~^ ^~^ ^ - | | | | - | | | Return type - | | Argument types - | | + extern "ABI" [pure|unsafe] fn <'lt> (S) -> T + ^~~~^ ^~~~~~~~~~~~^ ^~~~^ ^~^ ^ + | | | | | + | | | | Return type + | | | Argument types + | | Lifetimes | | | Purity ABI @@ -333,12 +333,12 @@ pub impl Parser { { /* - (&|~|@) [r/] [pure|unsafe] [once] fn (S) -> T - ^~~~~~^ ^~~^ ^~~~~~~~~~~~^ ^~~~~^ ^~^ ^ - | | | | | | - | | | | | Return type - | | | | Argument types - | | | | + (&|~|@) [r/] [pure|unsafe] [once] fn <'lt> (S) -> T + ^~~~~~^ ^~~^ ^~~~~~~~~~~~^ ^~~~~^ ^~~~^ ^~^ ^ + | | | | | | | + | | | | | | Return type + | | | | | Argument types + | | | | Lifetimes | | | Once-ness (a.k.a., affine) | | Purity | Lifetime bound @@ -394,12 +394,24 @@ pub impl Parser { } fn parse_ty_fn_decl() -> fn_decl { - let inputs = do self.parse_unspanned_seq( - token::LPAREN, token::RPAREN, - seq_sep_trailing_disallowed(token::COMMA)) |p| { + /* - p.parse_arg_general(false) - }; + (fn) <'lt> (S) -> T + ^~~~^ ^~^ ^ + | | | + | | Return type + | Argument types + Lifetimes + + */ + if self.eat(token::LT) { + let _lifetimes = self.parse_lifetimes(); + self.expect(token::GT); + } + let inputs = self.parse_unspanned_seq( + token::LPAREN, token::RPAREN, + seq_sep_trailing_disallowed(token::COMMA), + |p| p.parse_arg_general(false)); let (ret_style, ret_ty) = self.parse_ret_ty(); ast::fn_decl { inputs: inputs, output: ret_ty, cf: ret_style } } diff --git a/src/test/compile-fail/regions-in-enums.rs b/src/test/compile-fail/regions-in-enums.rs index 68c61137cbf6..b399ef8a747f 100644 --- a/src/test/compile-fail/regions-in-enums.rs +++ b/src/test/compile-fail/regions-in-enums.rs @@ -10,15 +10,15 @@ enum yes0<'lt> { // This will eventually be legal (and in fact the only way): - x3(&'lt uint) //~ ERROR named regions other than `self` are not allowed as part of a type declaration + X3(&'lt uint) //~ ERROR named regions other than `self` are not allowed as part of a type declaration } enum yes1 { - x4(&'self uint) + X4(&'self uint) } enum yes2 { - x5(&'foo uint) //~ ERROR named regions other than `self` are not allowed as part of a type declaration + X5(&'foo uint) //~ ERROR named regions other than `self` are not allowed as part of a type declaration } fn main() {} diff --git a/src/test/compile-fail/regions-scoping.rs b/src/test/compile-fail/regions-scoping.rs index 380a9a57d01f..063bc32f7b44 100644 --- a/src/test/compile-fail/regions-scoping.rs +++ b/src/test/compile-fail/regions-scoping.rs @@ -10,13 +10,13 @@ fn with(t: T, f: fn(T)) { f(t) } -fn nested(x: &x/int) { // (1) +fn nested<'x>(x: &'x int) { // (1) do with( - fn&(x: &x/int, // Refers to the region `x` at (1) - y: &y/int, // A fresh region `y` (2) - z: fn(x: &x/int, // Refers to `x` at (1) - y: &y/int, // Refers to `y` at (2) - z: &z/int) -> &z/int) // A fresh region `z` (3) + fn&(x: &'x int, // Refers to the region `x` at (1) + y: &'y int, // A fresh region `y` (2) + z: fn<'z>(x: &'x int, // Refers to `x` at (1) + y: &'y int, // Refers to `y` at (2) + z: &'z int) -> &'z int) // A fresh region `z` (3) -> &x/int { if false { return z(x, y, x); } @@ -29,13 +29,13 @@ fn nested(x: &x/int) { // (1) } ) |foo| { - let a: &x/int = foo(x, x, |_x, _y, z| z ); - let b: &x/int = foo(x, a, |_x, _y, z| z ); - let c: &x/int = foo(a, a, |_x, _y, z| z ); + let a: &'x int = foo(x, x, |_x, _y, z| z ); + let b: &'x int = foo(x, a, |_x, _y, z| z ); + let c: &'x int = foo(a, a, |_x, _y, z| z ); let z = 3i; - let d: &x/int = foo(x, x, |_x, _y, z| z ); - let e: &x/int = foo(x, &z, |_x, _y, z| z ); + let d: &'x int = foo(x, x, |_x, _y, z| z ); + let e: &'x int = foo(x, &z, |_x, _y, z| z ); // This would result in an error, but it is not reported by typeck // anymore but rather borrowck. Therefore, it doesn't end up From d3f3d0731cb1c6050b540bc5542b575dd19844fa Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 13 Feb 2013 16:15:29 -0800 Subject: [PATCH 5/5] update this test to use the new syntax entirely --- src/test/run-pass/regions-self-impls.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/test/run-pass/regions-self-impls.rs b/src/test/run-pass/regions-self-impls.rs index 87aefdca537d..22eec65b02a0 100644 --- a/src/test/run-pass/regions-self-impls.rs +++ b/src/test/run-pass/regions-self-impls.rs @@ -12,12 +12,12 @@ struct Clam<'self> { chowder: &'self int } -trait get_chowder { - fn get_chowder() -> &self/int; +trait get_chowder<'self> { + fn get_chowder() -> &'self int; } -impl get_chowder for Clam { - fn get_chowder() -> &self/int { return self.chowder; } +impl<'self> get_chowder<'self> for Clam<'self> { + fn get_chowder() -> &'self int { return self.chowder; } } pub fn main() {