Do not use Ident to represent Lifetime
diff --git a/src/expr.rs b/src/expr.rs
index f64bc31..96a593d 100644
--- a/src/expr.rs
+++ b/src/expr.rs
@@ -139,7 +139,7 @@
         pub While(ExprWhile {
             pub cond: Box<Expr>,
             pub body: Block,
-            pub label: Option<Ident>,
+            pub label: Option<Lifetime>,
             pub colon_token: Option<tokens::Colon>,
             pub while_token: tokens::While,
         }),
@@ -153,7 +153,7 @@
             pub pat: Box<Pat>,
             pub expr: Box<Expr>,
             pub body: Block,
-            pub label: Option<Ident>,
+            pub label: Option<Lifetime>,
             pub colon_token: Option<tokens::Colon>,
             pub while_token: tokens::While,
             pub let_token: tokens::Let,
@@ -169,7 +169,7 @@
             pub pat: Box<Pat>,
             pub expr: Box<Expr>,
             pub body: Block,
-            pub label: Option<Ident>,
+            pub label: Option<Lifetime>,
             pub for_token: tokens::For,
             pub colon_token: Option<tokens::Colon>,
             pub in_token: tokens::In,
@@ -180,7 +180,7 @@
         /// E.g. `'label: loop { block }`
         pub Loop(ExprLoop {
             pub body: Block,
-            pub label: Option<Ident>,
+            pub label: Option<Lifetime>,
             pub loop_token: tokens::Loop,
             pub colon_token: Option<tokens::Colon>,
         }),
@@ -273,14 +273,14 @@
 
         /// A `break`, with an optional label to break, and an optional expression
         pub Break(ExprBreak {
-            pub label: Option<Ident>,
+            pub label: Option<Lifetime>,
             pub expr: Option<Box<Expr>>,
             pub break_token: tokens::Break,
         }),
 
         /// A `continue`, with an optional label
         pub Continue(ExprContinue {
-            pub label: Option<Ident>,
+            pub label: Option<Lifetime>,
             pub continue_token: tokens::Continue,
         }),
 
@@ -978,7 +978,7 @@
 
     impl Synom for ExprForLoop {
         named!(parse -> Self, do_parse!(
-            lbl: option!(tuple!(label, syn!(Colon))) >>
+            lbl: option!(tuple!(syn!(Lifetime), syn!(Colon))) >>
             for_: syn!(For) >>
             pat: syn!(Pat) >>
             in_: syn!(In) >>
@@ -998,7 +998,7 @@
 
     impl Synom for ExprLoop {
         named!(parse -> Self, do_parse!(
-            lbl: option!(tuple!(label, syn!(Colon))) >>
+            lbl: option!(tuple!(syn!(Lifetime), syn!(Colon))) >>
             loop_: syn!(Loop) >>
             loop_block: syn!(Block) >>
             (ExprLoop {
@@ -1150,7 +1150,7 @@
 
     impl Synom for ExprWhile {
         named!(parse -> Self, do_parse!(
-            lbl: option!(tuple!(label, syn!(Colon))) >>
+            lbl: option!(tuple!(syn!(Lifetime), syn!(Colon))) >>
             while_: syn!(While) >>
             cond: expr_no_struct >>
             while_block: syn!(Block) >>
@@ -1166,7 +1166,7 @@
 
     impl Synom for ExprWhileLet {
         named!(parse -> Self, do_parse!(
-            lbl: option!(tuple!(label, syn!(Colon))) >>
+            lbl: option!(tuple!(syn!(Lifetime), syn!(Colon))) >>
             while_: syn!(While) >>
             let_: syn!(Let) >>
             pat: syn!(Pat) >>
@@ -1189,7 +1189,7 @@
     impl Synom for ExprContinue {
         named!(parse -> Self, do_parse!(
             cont: syn!(Continue) >>
-            lbl: option!(label) >>
+            lbl: option!(syn!(Lifetime)) >>
             (ExprContinue {
                 continue_token: cont,
                 label: lbl,
@@ -1199,7 +1199,7 @@
 
     named_ambiguous_expr!(expr_break -> ExprKind, allow_struct, do_parse!(
         break_: syn!(Break) >>
-        lbl: option!(label) >>
+        lbl: option!(syn!(Lifetime)) >>
         val: option!(call!(ambiguous_expr, allow_struct, false)) >>
         (ExprBreak {
             label: lbl,
@@ -1800,8 +1800,6 @@
             epsilon!() => { |_| CaptureBy::Ref }
         ));
     }
-
-    named!(label -> Ident, map!(syn!(Lifetime), |lt: Lifetime| lt.ident));
 }
 
 #[cfg(feature = "printing")]
diff --git a/src/generics.rs b/src/generics.rs
index 2c30a5f..60a94a6 100644
--- a/src/generics.rs
+++ b/src/generics.rs
@@ -67,24 +67,6 @@
 }
 
 ast_struct! {
-    pub struct Lifetime {
-        pub ident: Ident,
-    }
-}
-
-impl Lifetime {
-    pub fn new<T: Into<Ident>>(t: T) -> Self {
-        let id = t.into();
-        if !id.as_ref().starts_with('\'') {
-            panic!("lifetime name must start with apostrophe as in \"'a\", \
-                   got {:?}",
-                   id.as_ref());
-        }
-        Lifetime { ident: id }
-    }
-}
-
-ast_struct! {
     /// A set of bound lifetimes, e.g. `for<'a, 'b, 'c>`
     #[derive(Default)]
     pub struct BoundLifetimes {
@@ -106,10 +88,10 @@
 }
 
 impl LifetimeDef {
-    pub fn new<T: Into<Ident>>(t: T) -> Self {
+    pub fn new(lifetime: Lifetime) -> Self {
         LifetimeDef {
             attrs: Vec::new(),
-            lifetime: Lifetime::new(t),
+            lifetime: lifetime,
             colon_token: None,
             bounds: Delimited::new(),
         }
@@ -211,7 +193,7 @@
 pub mod parsing {
     use super::*;
 
-    use synom::{PResult, Cursor, Synom, parse_error};
+    use synom::Synom;
     use synom::tokens::*;
 
     impl Synom for Generics {
@@ -240,25 +222,6 @@
         ));
     }
 
-    impl Synom for Lifetime {
-        fn parse(input: Cursor) -> PResult<Self> {
-            match input.word() {
-                Some((rest, span, sym)) => {
-                    if sym.as_str().starts_with('\'') {
-                        return Ok((rest, Lifetime {
-                            ident: Ident {
-                                span: Span(span),
-                                sym: sym
-                            }
-                        }));
-                    }
-                }
-                _ => {}
-            }
-            parse_error()
-        }
-    }
-
     impl Synom for LifetimeDef {
         named!(parse -> Self, do_parse!(
             attrs: many0!(call!(Attribute::parse_outer)) >>
@@ -448,12 +411,6 @@
         }
     }
 
-    impl ToTokens for Lifetime {
-        fn to_tokens(&self, tokens: &mut Tokens) {
-            self.ident.to_tokens(tokens);
-        }
-    }
-
     impl ToTokens for BoundLifetimes {
         fn to_tokens(&self, tokens: &mut Tokens) {
             self.for_token.to_tokens(tokens);
diff --git a/src/lib.rs b/src/lib.rs
index 8e63ac4..8c55d64 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -41,7 +41,7 @@
                PatBox, PatRef, PatLit, PatRange, PatSlice};
 
 mod generics;
-pub use generics::{Generics, Lifetime, LifetimeDef, TraitBoundModifier, TyParam, TyParamBound,
+pub use generics::{Generics, LifetimeDef, TraitBoundModifier, TyParam, TyParamBound,
                    WhereBoundPredicate, WhereClause, WhereEqPredicate, WherePredicate,
                    WhereRegionPredicate, BoundLifetimes};
 #[cfg(feature = "printing")]
@@ -68,6 +68,9 @@
 #[cfg(feature = "full")]
 pub use krate::Crate;
 
+mod lifetime;
+pub use lifetime::Lifetime;
+
 mod lit;
 pub use lit::{Lit, LitKind};
 
diff --git a/src/lifetime.rs b/src/lifetime.rs
new file mode 100644
index 0000000..56582d2
--- /dev/null
+++ b/src/lifetime.rs
@@ -0,0 +1,129 @@
+use std::cmp::Ordering;
+use std::fmt::{self, Display};
+use std::hash::{Hash, Hasher};
+
+use proc_macro2::Symbol;
+use unicode_xid::UnicodeXID;
+
+use Span;
+
+#[cfg_attr(feature = "extra-traits", derive(Debug))]
+#[cfg_attr(feature = "clone-impls", derive(Clone))]
+pub struct Lifetime {
+    pub sym: Symbol,
+    pub span: Span,
+}
+
+impl Lifetime {
+    pub fn new(sym: Symbol, span: Span) -> Self {
+        let s = sym.as_str();
+
+        if !s.starts_with('\'') {
+            panic!("lifetime name must start with apostrophe as in \"'a\", \
+                   got {:?}",
+                   s);
+        }
+
+        if s == "'" {
+            panic!("lifetime name must not be empty");
+        }
+
+        if s == "'_" {
+            panic!("\"'_\" is not a valid lifetime name");
+        }
+
+        fn xid_ok(s: &str) -> bool {
+            let mut chars = s.chars();
+            let first = chars.next().unwrap();
+            if !(UnicodeXID::is_xid_start(first) || first == '_') {
+                return false;
+            }
+            for ch in chars {
+                if !UnicodeXID::is_xid_continue(ch) {
+                    return false;
+                }
+            }
+            true
+        }
+
+        if !xid_ok(&s[1..]) {
+            panic!("{:?} is not a valid lifetime name");
+        }
+
+        Lifetime {
+            sym: sym,
+            span: span,
+        }
+    }
+}
+
+impl Display for Lifetime {
+    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+        self.sym.as_str().fmt(formatter)
+    }
+}
+
+impl PartialEq for Lifetime {
+    fn eq(&self, other: &Lifetime) -> bool {
+        self.sym.as_str() == other.sym.as_str()
+    }
+}
+
+impl Eq for Lifetime {}
+
+impl PartialOrd for Lifetime {
+    fn partial_cmp(&self, other: &Lifetime) -> Option<Ordering> {
+        Some(self.cmp(other))
+    }
+}
+
+impl Ord for Lifetime {
+    fn cmp(&self, other: &Lifetime) -> Ordering {
+        self.sym.as_str().cmp(other.sym.as_str())
+    }
+}
+
+impl Hash for Lifetime {
+    fn hash<H: Hasher>(&self, h: &mut H) {
+        self.sym.as_str().hash(h)
+    }
+}
+
+#[cfg(feature = "parsing")]
+pub mod parsing {
+    use super::*;
+    use synom::{Synom, PResult, Cursor, parse_error};
+
+    impl Synom for Lifetime {
+        fn parse(input: Cursor) -> PResult<Self> {
+            let (rest, span, sym) = match input.word() {
+                Some(word) => word,
+                _ => return parse_error(),
+            };
+            if !sym.as_str().starts_with('\'') {
+                return parse_error();
+            }
+
+            Ok((rest, Lifetime {
+                sym: sym,
+                span: Span(span),
+            }))
+        }
+    }
+}
+
+#[cfg(feature = "printing")]
+mod printing {
+    use super::*;
+    use quote::{Tokens, ToTokens};
+    use proc_macro2::{TokenTree, TokenKind};
+
+    impl ToTokens for Lifetime {
+        fn to_tokens(&self, tokens: &mut Tokens) {
+            tokens.append(TokenTree {
+                span: self.span.0,
+                kind: TokenKind::Word(self.sym),
+            })
+        }
+    }
+}
diff --git a/tests/test_expr.rs b/tests/test_expr.rs
index 749d50f..0e4b574 100644
--- a/tests/test_expr.rs
+++ b/tests/test_expr.rs
@@ -88,9 +88,9 @@
 
     let actual = raw.parse::<Crate>().unwrap();
 
-    assert_eq!(&actual.items[0].ident, "catch");
-
-    assert_let!(ItemKind::Struct(..) = actual.items[0].node);
+    assert_let!(ItemKind::Struct(ItemStruct { ref ident, .. }) = actual.items[0].node; {
+        assert_eq!(ident, "catch");
+    });
 
     assert_let!(Item { node: ItemKind::Fn(ItemFn { ref block, .. }), .. } = actual.items[1]; {
         assert_let!(Stmt::Local(ref local) = block.stmts[0]; {
diff --git a/tests/test_generics.rs b/tests/test_generics.rs
index 6c37035..8bb6171 100644
--- a/tests/test_generics.rs
+++ b/tests/test_generics.rs
@@ -15,14 +15,14 @@
         lifetimes: vec![
             LifetimeDef {
                 attrs: Default::default(),
-                lifetime: Lifetime::new("'a"),
+                lifetime: Lifetime::new("'a".into(), Span::default()),
                 bounds: Default::default(),
                 colon_token: None,
             },
             LifetimeDef {
                 attrs: Default::default(),
-                lifetime: Lifetime::new("'b"),
-                bounds: vec![Lifetime::new("'a")].into(),
+                lifetime: Lifetime::new("'b".into(), Span::default()),
+                bounds: vec![Lifetime::new("'a".into(), Span::default())].into(),
                 colon_token: Some(tokens::Colon::default()),
             },
         ].into(),
@@ -37,7 +37,7 @@
                     is_sugared_doc: false,
                 }],
                 ident: "T".into(),
-                bounds: vec![TyParamBound::Region(Lifetime::new("'a"))].into(),
+                bounds: vec![TyParamBound::Region(Lifetime::new("'a".into(), Span::default()))].into(),
                 default: Some(TyTup {
                     tys: Default::default(),
                     lone_comma: None,
@@ -92,7 +92,7 @@
 #[test]
 fn test_ty_param_bound() {
     let tokens = quote!('a);
-    let expected = TyParamBound::Region(Lifetime::new("'a"));
+    let expected = TyParamBound::Region(Lifetime::new("'a".into(), Span::default()));
     assert_eq!(expected, tokens.to_string().parse().unwrap());
 
     let tokens = quote!(Debug);
diff --git a/tests/test_macro_input.rs b/tests/test_macro_input.rs
index bddf52e..5c2715c 100644
--- a/tests/test_macro_input.rs
+++ b/tests/test_macro_input.rs
@@ -115,20 +115,20 @@
                         qself: None,
                         path: Path {
                             leading_colon: None,
-                            global: false,
                             segments: vec![
                                 PathSegment {
                                     ident: "Vec".into(),
                                     parameters: PathParameters::AngleBracketed(
                                         AngleBracketedParameterData {
-                                            gt_token: Some(Default::default()),
-                                            lt_token: Some(Default::default()),
+                                            turbofish: None,
+                                            lt_token: Default::default(),
                                             lifetimes: Default::default(),
                                             types: vec![Ty::from(TyPath {
                                                 qself: None,
                                                 path: "Attribute".into(),
                                             })].into(),
                                             bindings: Default::default(),
+                                            gt_token: Default::default(),
                                         },
                                     ),
                                 }
@@ -347,7 +347,6 @@
             pound_token: Default::default(),
             style: AttrStyle::Outer,
             path: Path {
-                global: true,
                 leading_colon: Some(Default::default()),
                 segments: vec![
                     PathSegment::from("attr_args"),
@@ -402,7 +401,6 @@
             pound_token: Default::default(),
             style: AttrStyle::Outer,
             path: Path {
-                global: false,
                 leading_colon: None,
                 segments: vec![
                     PathSegment::from("inert"),
@@ -445,7 +443,6 @@
             pound_token: Default::default(),
             style: AttrStyle::Outer,
             path: Path {
-                global: false,
                 leading_colon: None,
                 segments: vec![
                     PathSegment::from("foo"),
@@ -492,7 +489,6 @@
                 ident: None,
                 vis: Visibility::Restricted(VisRestricted {
                     path: Box::new(Path {
-                        global: false,
                         leading_colon: None,
                         segments: vec![
                             PathSegment::from("m"),