Parse trait bounded extern types
diff --git a/gen/src/nested.rs b/gen/src/nested.rs
index fe1664b..1f4fc5d 100644
--- a/gen/src/nested.rs
+++ b/gen/src/nested.rs
@@ -131,6 +131,8 @@
             derives: Vec::new(),
             type_token: Token![type](Span::call_site()),
             name: Pair::new(ns, Ident::new(ident, Span::call_site())),
+            colon_token: None,
+            bounds: Vec::new(),
             semi_token: Token![;](Span::call_site()),
             trusted: false,
         })
diff --git a/syntax/check.rs b/syntax/check.rs
index a60332d..15e19b8 100644
--- a/syntax/check.rs
+++ b/syntax/check.rs
@@ -3,7 +3,7 @@
 use crate::syntax::types::TrivialReason;
 use crate::syntax::{
     error, ident, Api, Array, Enum, ExternFn, ExternType, Impl, Lang, Receiver, Ref, Signature,
-    SliceRef, Struct, Trait, Ty1, Type, Types,
+    SliceRef, Struct, Trait, Ty1, Type, TypeAlias, Types,
 };
 use proc_macro2::{Delimiter, Group, Ident, TokenStream};
 use quote::{quote, ToTokens};
@@ -43,12 +43,13 @@
 
     for api in cx.apis {
         match api {
+            Api::Include(_) => {}
             Api::Struct(strct) => check_api_struct(cx, strct),
             Api::Enum(enm) => check_api_enum(cx, enm),
             Api::CxxType(ety) | Api::RustType(ety) => check_api_type(cx, ety),
             Api::CxxFunction(efn) | Api::RustFunction(efn) => check_api_fn(cx, efn),
+            Api::TypeAlias(alias) => check_api_type_alias(cx, alias),
             Api::Impl(imp) => check_api_impl(cx, imp),
-            Api::Include(_) | Api::TypeAlias(_) => {}
         }
     }
 }
@@ -290,6 +291,12 @@
         cx.error(derive, msg);
     }
 
+    if !ety.bounds.is_empty() {
+        let bounds = &ety.bounds;
+        let span = quote!(#(#bounds)*);
+        cx.error(span, "extern type bounds are not implemented yet");
+    }
+
     if let Some(reason) = cx.types.required_trivial.get(&ety.name.rust) {
         let what = match reason {
             TrivialReason::StructField(strct) => format!("a field of `{}`", strct.name.rust),
@@ -387,6 +394,13 @@
     check_multiple_arg_lifetimes(cx, efn);
 }
 
+fn check_api_type_alias(cx: &mut Check, alias: &TypeAlias) {
+    for derive in &alias.derives {
+        let msg = format!("derive({}) on extern type alias is not supported", derive);
+        cx.error(derive, msg);
+    }
+}
+
 fn check_api_impl(cx: &mut Check, imp: &Impl) {
     if let Type::UniquePtr(ty) | Type::CxxVector(ty) = &imp.ty {
         if let Type::Ident(inner) = &ty.inner {
diff --git a/syntax/mod.rs b/syntax/mod.rs
index e8288a9..c688f55 100644
--- a/syntax/mod.rs
+++ b/syntax/mod.rs
@@ -72,6 +72,8 @@
     pub derives: Vec<Derive>,
     pub type_token: Token![type],
     pub name: Pair,
+    pub colon_token: Option<Token![:]>,
+    pub bounds: Vec<Derive>,
     pub semi_token: Token![;],
     pub trusted: bool,
 }
@@ -108,6 +110,7 @@
 
 pub struct TypeAlias {
     pub doc: Doc,
+    pub derives: Vec<Derive>,
     pub type_token: Token![type],
     pub name: Pair,
     pub eq_token: Token![=],
diff --git a/syntax/parse.rs b/syntax/parse.rs
index e46cdb4..aa86a9f 100644
--- a/syntax/parse.rs
+++ b/syntax/parse.rs
@@ -3,8 +3,8 @@
 use crate::syntax::report::Errors;
 use crate::syntax::Atom::*;
 use crate::syntax::{
-    attrs, error, Api, Array, Doc, Enum, ExternFn, ExternType, Impl, Include, IncludeKind, Lang,
-    Namespace, Pair, Receiver, Ref, ResolvableName, Signature, SliceRef, Struct, Ty1, Type,
+    attrs, error, Api, Array, Derive, Doc, Enum, ExternFn, ExternType, Impl, Include, IncludeKind,
+    Lang, Namespace, Pair, Receiver, Ref, ResolvableName, Signature, SliceRef, Struct, Ty1, Type,
     TypeAlias, Var, Variant,
 };
 use proc_macro2::{Delimiter, Group, Span, TokenStream, TokenTree};
@@ -14,8 +14,8 @@
 use syn::{
     Abi, Attribute, Error, Expr, Fields, FnArg, ForeignItem, ForeignItemFn, ForeignItemType,
     GenericArgument, GenericParam, Generics, Ident, ItemEnum, ItemImpl, ItemStruct, Lit, LitStr,
-    Pat, PathArguments, Result, ReturnType, Token, Type as RustType, TypeArray, TypeBareFn,
-    TypePath, TypeReference,
+    Pat, PathArguments, Result, ReturnType, Token, TraitBound, TraitBoundModifier,
+    Type as RustType, TypeArray, TypeBareFn, TypeParamBound, TypePath, TypeReference,
 };
 
 pub mod kw {
@@ -257,7 +257,7 @@
                 }
             }
             ForeignItem::Verbatim(tokens) => {
-                match parse_extern_verbatim(cx, tokens, lang, &namespace) {
+                match parse_extern_verbatim(cx, tokens, lang, trusted, &namespace) {
                     Ok(api) => items.push(api),
                     Err(err) => cx.push(err),
                 }
@@ -352,6 +352,8 @@
         derives,
         type_token,
         name: Pair::new(namespace, ident),
+        colon_token: None,
+        bounds: Vec::new(),
         semi_token,
         trusted,
     })
@@ -518,10 +520,10 @@
     cx: &mut Errors,
     tokens: &TokenStream,
     lang: Lang,
+    trusted: bool,
     namespace: &Namespace,
 ) -> Result<Api> {
-    // type Alias = crate::path::to::Type;
-    let parse = |input: ParseStream| -> Result<TypeAlias> {
+    |input: ParseStream| -> Result<Api> {
         let attrs = input.call(Attribute::parse_outer)?;
         let type_token: Token![type] = match input.parse()? {
             Some(type_token) => type_token,
@@ -531,41 +533,137 @@
             }
         };
         let ident: Ident = input.parse()?;
-        let eq_token: Token![=] = input.parse()?;
-        let ty: RustType = input.parse()?;
-        let semi_token: Token![;] = input.parse()?;
-        let mut doc = Doc::new();
-        let mut namespace = namespace.clone();
-        attrs::parse(
-            cx,
-            &attrs,
-            attrs::Parser {
-                doc: Some(&mut doc),
-                namespace: Some(&mut namespace),
-                ..Default::default()
-            },
-        );
-
-        Ok(TypeAlias {
-            doc,
-            type_token,
-            name: Pair::new(namespace, ident),
-            eq_token,
-            ty,
-            semi_token,
-        })
-    };
-
-    let type_alias = parse.parse2(tokens.clone())?;
-    match lang {
-        Lang::Cxx => Ok(Api::TypeAlias(type_alias)),
-        Lang::Rust => {
-            let (type_token, semi_token) = (type_alias.type_token, type_alias.semi_token);
-            let span = quote!(#type_token #semi_token);
-            let msg = "type alias in extern \"Rust\" block is not supported";
-            Err(Error::new_spanned(span, msg))
+        let lookahead = input.lookahead1();
+        if lookahead.peek(Token![=]) {
+            // type Alias = crate::path::to::Type;
+            parse_type_alias(cx, attrs, type_token, ident, input, lang, namespace)
+        } else if lookahead.peek(Token![:]) {
+            // type Opaque: Bound2 + Bound2;
+            parse_extern_type_bounded(
+                cx, attrs, type_token, ident, input, lang, trusted, namespace,
+            )
+        } else {
+            Err(lookahead.error())
         }
     }
+    .parse2(tokens.clone())
+}
+
+fn parse_type_alias(
+    cx: &mut Errors,
+    attrs: Vec<Attribute>,
+    type_token: Token![type],
+    ident: Ident,
+    input: ParseStream,
+    lang: Lang,
+    namespace: &Namespace,
+) -> Result<Api> {
+    let eq_token: Token![=] = input.parse()?;
+    let ty: RustType = input.parse()?;
+    let semi_token: Token![;] = input.parse()?;
+
+    let mut doc = Doc::new();
+    let mut derives = Vec::new();
+    let mut namespace = namespace.clone();
+    attrs::parse(
+        cx,
+        &attrs,
+        attrs::Parser {
+            doc: Some(&mut doc),
+            derives: Some(&mut derives),
+            namespace: Some(&mut namespace),
+            ..Default::default()
+        },
+    );
+
+    if lang == Lang::Rust {
+        let span = quote!(#type_token #semi_token);
+        let msg = "type alias in extern \"Rust\" block is not supported";
+        return Err(Error::new_spanned(span, msg));
+    }
+
+    Ok(Api::TypeAlias(TypeAlias {
+        doc,
+        derives,
+        type_token,
+        name: Pair::new(namespace, ident),
+        eq_token,
+        ty,
+        semi_token,
+    }))
+}
+
+fn parse_extern_type_bounded(
+    cx: &mut Errors,
+    attrs: Vec<Attribute>,
+    type_token: Token![type],
+    ident: Ident,
+    input: ParseStream,
+    lang: Lang,
+    trusted: bool,
+    namespace: &Namespace,
+) -> Result<Api> {
+    let colon_token: Token![:] = input.parse()?;
+    let mut bounds = Vec::new();
+    loop {
+        match input.parse()? {
+            TypeParamBound::Trait(TraitBound {
+                paren_token: None,
+                modifier: TraitBoundModifier::None,
+                lifetimes: None,
+                path,
+            }) if if let Some(derive) = path.get_ident().and_then(Derive::from) {
+                bounds.push(derive);
+                true
+            } else {
+                false
+            } => {}
+            bound @ TypeParamBound::Trait(_) | bound @ TypeParamBound::Lifetime(_) => {
+                cx.error(bound, "unsupported trait");
+            }
+        }
+
+        let lookahead = input.lookahead1();
+        if lookahead.peek(Token![+]) {
+            input.parse::<Token![+]>()?;
+        } else if lookahead.peek(Token![;]) {
+            break;
+        } else {
+            return Err(lookahead.error());
+        }
+    }
+    let semi_token: Token![;] = input.parse()?;
+
+    let mut doc = Doc::new();
+    let mut derives = Vec::new();
+    let mut namespace = namespace.clone();
+    attrs::parse(
+        cx,
+        &attrs,
+        attrs::Parser {
+            doc: Some(&mut doc),
+            derives: Some(&mut derives),
+            namespace: Some(&mut namespace),
+            ..Default::default()
+        },
+    );
+
+    let api_type = match lang {
+        Lang::Cxx => Api::CxxType,
+        Lang::Rust => Api::RustType,
+    };
+
+    Ok(api_type(ExternType {
+        lang,
+        doc,
+        derives,
+        type_token,
+        name: Pair::new(namespace, ident),
+        colon_token: Some(colon_token),
+        bounds,
+        semi_token,
+        trusted,
+    }))
 }
 
 fn parse_impl(imp: ItemImpl) -> Result<Api> {
diff --git a/tests/ui/extern_type_bound.rs b/tests/ui/extern_type_bound.rs
new file mode 100644
index 0000000..958accd
--- /dev/null
+++ b/tests/ui/extern_type_bound.rs
@@ -0,0 +1,15 @@
+#[cxx::bridge]
+mod ffi {
+    extern "C++" {
+        type Opaque: PartialEq + PartialOrd;
+    }
+}
+
+#[cxx::bridge]
+mod ffi {
+    extern "C++" {
+        type Opaque: for<'de> Deserialize<'de>;
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/extern_type_bound.stderr b/tests/ui/extern_type_bound.stderr
new file mode 100644
index 0000000..ca07ef7
--- /dev/null
+++ b/tests/ui/extern_type_bound.stderr
@@ -0,0 +1,11 @@
+error: extern type bounds are not implemented yet
+ --> $DIR/extern_type_bound.rs:4:22
+  |
+4 |         type Opaque: PartialEq + PartialOrd;
+  |                      ^^^^^^^^^^^^^^^^^^^^^^
+
+error: unsupported trait
+  --> $DIR/extern_type_bound.rs:11:22
+   |
+11 |         type Opaque: for<'de> Deserialize<'de>;
+   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^