Parse associated type bounds in path
diff --git a/src/path.rs b/src/path.rs
index 443bac6..693d71c 100644
--- a/src/path.rs
+++ b/src/path.rs
@@ -108,6 +108,8 @@
         /// A binding (equality constraint) on an associated type: the `Item =
         /// u8` in `Iterator<Item = u8>`.
         Binding(Binding),
+        /// An associated type bound: `Iterator<Item: Display>`.
+        Constraint(Constraint),
         /// A const expression. Must be inside of a block.
         ///
         /// NOTE: Identity expressions are represented as Type arguments, as
@@ -143,6 +145,18 @@
 }
 
 ast_struct! {
+    /// An associated type bound: `Iterator<Item: Display>`.
+    ///
+    /// *This type is available if Syn is built with the `"derive"` or `"full"`
+    /// feature.*
+    pub struct Constraint {
+        pub ident: Ident,
+        pub colon_token: Token![:],
+        pub bounds: Punctuated<TypeParamBound, Token![+]>,
+    }
+}
+
+ast_struct! {
     /// Arguments of a function path segment: the `(A, B) -> C` in `Fn(A,B) ->
     /// C`.
     ///
@@ -213,6 +227,10 @@
 
             #[cfg(feature = "full")]
             {
+                if input.peek(Ident) && input.peek2(Token![:]) && !input.peek2(Token![::]) {
+                    return Ok(GenericArgument::Constraint(input.parse()?));
+                }
+
                 if input.peek(Lit) {
                     let lit = input.call(expr::parsing::expr_lit)?;
                     return Ok(GenericArgument::Const(Expr::Lit(lit)));
@@ -307,6 +325,32 @@
         }
     }
 
+    #[cfg(feature = "full")]
+    impl Parse for Constraint {
+        fn parse(input: ParseStream) -> Result<Self> {
+            Ok(Constraint {
+                ident: input.parse()?,
+                colon_token: input.parse()?,
+                bounds: {
+                    let mut bounds = Punctuated::new();
+                    loop {
+                        if input.peek(Token![,]) || input.peek(Token![>]) {
+                            break;
+                        }
+                        let value = input.parse()?;
+                        bounds.push_value(value);
+                        if !input.peek(Token![+]) {
+                            break;
+                        }
+                        let punct = input.parse()?;
+                        bounds.push_punct(punct);
+                    }
+                    bounds
+                },
+            })
+        }
+    }
+
     impl Path {
         /// Parse a `Path` containing no path arguments on any of its segments.
         ///
@@ -498,6 +542,7 @@
                 GenericArgument::Lifetime(ref lt) => lt.to_tokens(tokens),
                 GenericArgument::Type(ref ty) => ty.to_tokens(tokens),
                 GenericArgument::Binding(ref tb) => tb.to_tokens(tokens),
+                GenericArgument::Constraint(ref tc) => tc.to_tokens(tokens),
                 GenericArgument::Const(ref e) => match *e {
                     Expr::Lit(_) => e.to_tokens(tokens),
 
@@ -529,9 +574,15 @@
             // not been settled yet. https://github.com/rust-lang/rust/issues/44580
             let mut trailing_or_empty = true;
             for param in self.args.pairs() {
-                if let GenericArgument::Lifetime(_) = **param.value() {
-                    param.to_tokens(tokens);
-                    trailing_or_empty = param.punct().is_some();
+                match **param.value() {
+                    GenericArgument::Lifetime(_) => {
+                        param.to_tokens(tokens);
+                        trailing_or_empty = param.punct().is_some();
+                    }
+                    GenericArgument::Type(_)
+                    | GenericArgument::Binding(_)
+                    | GenericArgument::Constraint(_)
+                    | GenericArgument::Const(_) => {}
                 }
             }
             for param in self.args.pairs() {
@@ -543,16 +594,23 @@
                         param.to_tokens(tokens);
                         trailing_or_empty = param.punct().is_some();
                     }
-                    GenericArgument::Lifetime(_) | GenericArgument::Binding(_) => {}
+                    GenericArgument::Lifetime(_)
+                    | GenericArgument::Binding(_)
+                    | GenericArgument::Constraint(_) => {}
                 }
             }
             for param in self.args.pairs() {
-                if let GenericArgument::Binding(_) = **param.value() {
-                    if !trailing_or_empty {
-                        <Token![,]>::default().to_tokens(tokens);
-                        trailing_or_empty = true;
+                match **param.value() {
+                    GenericArgument::Binding(_) | GenericArgument::Constraint(_) => {
+                        if !trailing_or_empty {
+                            <Token![,]>::default().to_tokens(tokens);
+                            trailing_or_empty = true;
+                        }
+                        param.to_tokens(tokens);
                     }
-                    param.to_tokens(tokens);
+                    GenericArgument::Lifetime(_)
+                    | GenericArgument::Type(_)
+                    | GenericArgument::Const(_) => {}
                 }
             }
 
@@ -568,6 +626,14 @@
         }
     }
 
+    impl ToTokens for Constraint {
+        fn to_tokens(&self, tokens: &mut TokenStream) {
+            self.ident.to_tokens(tokens);
+            self.colon_token.to_tokens(tokens);
+            self.bounds.to_tokens(tokens);
+        }
+    }
+
     impl ToTokens for ParenthesizedGenericArguments {
         fn to_tokens(&self, tokens: &mut TokenStream) {
             self.paren_token.surround(tokens, |tokens| {