Repository: diesel-rs/diesel (language: rust)

Issue:
`#[derive(AsExpression, FromSqlRow)]` won't compile when generic bounds have a default implementation.
## Setup

### Versions

- **Rust:** 1.75
- **Diesel:** 2.1.4 (diesel_derives 2.1.2)
- **Database:** postgres
- **Operating System** macos

### Feature Flags

- **diesel:** ` default-features = false, features = ["postgres"]`

## Problem Description

`#[derive(AsExpression, FromSqlRow)]` won't compile when generic bounds have a default implementation.

### What are you trying to accomplish?

Here's a simplified version of the relevant code, but you can see the full thing in [this PR]( https://github.com/georust/geozero/pull/188). We modified a struct field to be generic, and for backwards compatibility, we wanted to add the previous field type as the default trait implementation.

```
#[derive(AsExpression, FromSqlRow)]
#[diesel(sql_type = Geometry)]
#[diesel(sql_type = Geography)]
pub struct Ewkb<B: AsRef<[u8]> = Vec<u8>>(pub B);
```

### What is the expected output?

I expected it to compile. 🙃 

More specifically, I expected the macro to expand to something like:
```diff
- impl<'expr, B: AsRef<[u8]> = Vec<u8>> AsExpression<Geometry> for &'expr Ewkb<B> {
+ impl<'expr, B: AsRef<[u8]>> AsExpression<Geometry> for &'expr Ewkb<B> {
   ...
}
```

### What is the actual output?

Two errors are produced by the current output:
```
error: generic parameters with a default must be trailing
  --> geozero/src/wkb/wkb_reader.rs:28:17
   |
28 | pub struct Ewkb<B: AsRef<[u8]> = Vec<u8>>(pub B);
   |                 ^
```
```
error: defaults for type parameters are only allowed in `struct`, `enum`, `type`, or `trait` definitions
  --> geozero/src/wkb/wkb_reader.rs:28:17
   |
28 | pub struct Ewkb<B: AsRef<[u8]> = Vec<u8>>(pub B);
```

Both of these errors make sense only if you look at the expanded macro output. You see code like:

```
impl<'expr, B: AsRef<[u8]> = Vec<u8>> AsExpression<Geometry> for &'expr Ewkb<B> {
                          ^^^^^^^^^^- THIS IS A PROBLEM!!! ( my own annotation 😉 )
    type Expression = Bound<Geometry, Self>;
    fn as_expression(self) -> Self::Expression {
        Bound::new(self)
    }
}
```

### Are you seeing any additional errors?
 

### Steps to reproduce

let me know if the above isn't clear

## Checklist

- [x] I have already looked over the [issue tracker](https://github.com/diesel-rs/diesel/issues) and the [discussion forum](https://github.com/diesel-rs/diesel/discussions) for similar possible closed issues.
<!--
If you are unsure if your issue is a duplicate of an existing issue please link the issue in question here
--> 
- [x] This issue can be reproduced on Rust's stable channel. (Your issue will be
  closed if this is not the case)
- [x] This issue can be reproduced without requiring a third party crate

<!--
Thank you for your submission!  You're helping make Diesel more robust 🎉

We'll try to respond as quickly as possible.
-->



Discussion / hints:

Fix patch:
diff --git a/diesel_derives/src/as_expression.rs b/diesel_derives/src/as_expression.rs
index d6f820e3cfa1..19f133416dab 100644
--- a/diesel_derives/src/as_expression.rs
+++ b/diesel_derives/src/as_expression.rs
@@ -1,5 +1,6 @@
 use proc_macro2::TokenStream;
 use quote::quote;
+use syn::parse_quote;
 use syn::DeriveInput;
 use syn::Result;
 
@@ -18,19 +19,29 @@ pub fn derive(item: DeriveInput) -> Result<TokenStream> {
 
     let struct_ty = ty_for_foreign_derive(&item, &model)?;
 
-    let (impl_generics, ..) = item.generics.split_for_impl();
-    let lifetimes = item.generics.lifetimes().collect::<Vec<_>>();
-    let ty_params = item.generics.type_params().collect::<Vec<_>>();
-    let const_params = item.generics.const_params().collect::<Vec<_>>();
+    // type generics are already handled by `ty_for_foreign_derive`
+    let (impl_generics_plain, _, where_clause_plain) = item.generics.split_for_impl();
+
+    let mut generics = item.generics.clone();
+    generics.params.push(parse_quote!('__expr));
+
+    let (impl_generics, _, where_clause) = generics.split_for_impl();
+
+    let mut generics2 = generics.clone();
+    generics2.params.push(parse_quote!('__expr2));
+    let (impl_generics2, _, where_clause2) = generics2.split_for_impl();
 
     let tokens = model.sql_types.iter().map(|sql_type| {
-        let lifetimes = &lifetimes;
-        let ty_params = &ty_params;
-        let const_params = &const_params;
+
+        let mut to_sql_generics = item.generics.clone();
+        to_sql_generics.params.push(parse_quote!(__DB));
+        to_sql_generics.make_where_clause().predicates.push(parse_quote!(__DB: diesel::backend::Backend));
+        to_sql_generics.make_where_clause().predicates.push(parse_quote!(Self: ToSql<#sql_type, __DB>));
+        let (to_sql_impl_generics, _, to_sql_where_clause) = to_sql_generics.split_for_impl();
 
         let tokens = quote!(
-            impl<'expr, #(#lifetimes,)* #(#ty_params,)* #(#const_params,)*> AsExpression<#sql_type>
-                for &'expr #struct_ty
+            impl #impl_generics AsExpression<#sql_type>
+                for &'__expr #struct_ty #where_clause
             {
                 type Expression = Bound<#sql_type, Self>;
 
@@ -39,8 +50,8 @@ pub fn derive(item: DeriveInput) -> Result<TokenStream> {
                 }
             }
 
-            impl<'expr, #(#lifetimes,)* #(#ty_params,)* #(#const_params,)*> AsExpression<Nullable<#sql_type>>
-                for &'expr #struct_ty
+            impl #impl_generics AsExpression<Nullable<#sql_type>>
+                for &'__expr #struct_ty #where_clause
             {
                 type Expression = Bound<Nullable<#sql_type>, Self>;
 
@@ -49,8 +60,8 @@ pub fn derive(item: DeriveInput) -> Result<TokenStream> {
                 }
             }
 
-            impl<'expr2, 'expr, #(#lifetimes,)* #(#ty_params,)* #(#const_params,)*> AsExpression<#sql_type>
-                for &'expr2 &'expr #struct_ty
+            impl #impl_generics2 AsExpression<#sql_type>
+                for &'__expr2 &'__expr #struct_ty #where_clause2
             {
                 type Expression = Bound<#sql_type, Self>;
 
@@ -59,8 +70,8 @@ pub fn derive(item: DeriveInput) -> Result<TokenStream> {
                 }
             }
 
-            impl<'expr2, 'expr, #(#lifetimes,)* #(#ty_params,)* #(#const_params,)*> AsExpression<Nullable<#sql_type>>
-                for &'expr2 &'expr #struct_ty
+            impl #impl_generics2 AsExpression<Nullable<#sql_type>>
+                for &'__expr2 &'__expr #struct_ty #where_clause2
             {
                 type Expression = Bound<Nullable<#sql_type>, Self>;
 
@@ -69,11 +80,8 @@ pub fn derive(item: DeriveInput) -> Result<TokenStream> {
                 }
             }
 
-            impl<#(#lifetimes,)* #(#ty_params,)* __DB,  #(#const_params,)*> diesel::serialize::ToSql<Nullable<#sql_type>, __DB>
-                for #struct_ty
-            where
-                __DB: diesel::backend::Backend,
-                Self: ToSql<#sql_type, __DB>,
+            impl #to_sql_impl_generics diesel::serialize::ToSql<Nullable<#sql_type>, __DB>
+                for #struct_ty #to_sql_where_clause
             {
                 fn to_sql<'__b>(&'__b self, out: &mut Output<'__b, '_, __DB>) -> serialize::Result
                 {
@@ -88,7 +96,7 @@ pub fn derive(item: DeriveInput) -> Result<TokenStream> {
             quote!(
                 #tokens
 
-                impl #impl_generics AsExpression<#sql_type> for #struct_ty {
+                impl #impl_generics_plain AsExpression<#sql_type> for #struct_ty #where_clause_plain {
                     type Expression = Bound<#sql_type, Self>;
 
                     fn as_expression(self) -> Self::Expression {
@@ -96,7 +104,7 @@ pub fn derive(item: DeriveInput) -> Result<TokenStream> {
                     }
                 }
 
-                impl #impl_generics AsExpression<Nullable<#sql_type>> for #struct_ty {
+                impl #impl_generics_plain AsExpression<Nullable<#sql_type>> for #struct_ty #where_clause_plain {
                     type Expression = Bound<Nullable<#sql_type>, Self>;
 
                     fn as_expression(self) -> Self::Expression {
diff --git a/diesel_derives/src/from_sql_row.rs b/diesel_derives/src/from_sql_row.rs
index d9cce837b650..3753b41cb88c 100644
--- a/diesel_derives/src/from_sql_row.rs
+++ b/diesel_derives/src/from_sql_row.rs
@@ -12,10 +12,9 @@ pub fn derive(mut item: DeriveInput) -> Result<TokenStream> {
     let struct_ty = ty_for_foreign_derive(&item, &model)?;
 
     {
-        let where_clause = item
-            .generics
-            .where_clause
-            .get_or_insert(parse_quote!(where));
+        item.generics.params.push(parse_quote!(__DB));
+        item.generics.params.push(parse_quote!(__ST));
+        let where_clause = item.generics.make_where_clause();
         where_clause
             .predicates
             .push(parse_quote!(__DB: diesel::backend::Backend));
@@ -26,17 +25,13 @@ pub fn derive(mut item: DeriveInput) -> Result<TokenStream> {
             .predicates
             .push(parse_quote!(Self: FromSql<__ST, __DB>));
     }
-    let (_, _, where_clause) = item.generics.split_for_impl();
-
-    let lifetimes = item.generics.lifetimes().collect::<Vec<_>>();
-    let ty_params = item.generics.type_params().collect::<Vec<_>>();
-    let const_params = item.generics.const_params().collect::<Vec<_>>();
+    let (impl_generics, _, where_clause) = item.generics.split_for_impl();
 
     Ok(wrap_in_dummy_mod(quote! {
         use diesel::deserialize::{self, FromSql, Queryable};
 
         // Need to put __ST and __DB after lifetimes but before const params
-        impl<#(#lifetimes,)* __ST, __DB, #(#ty_params,)* #(#const_params,)*> Queryable<__ST, __DB> for #struct_ty
+        impl #impl_generics Queryable<__ST, __DB> for #struct_ty
         #where_clause
         {
             type Row = Self;


Test patch:
diff --git a/diesel_derives/tests/as_expression.rs b/diesel_derives/tests/as_expression.rs
index cf8f739979cf..31fe80e827d3 100644
--- a/diesel_derives/tests/as_expression.rs
+++ b/diesel_derives/tests/as_expression.rs
@@ -2,6 +2,7 @@ use diesel::backend::Backend;
 use diesel::deserialize::{FromSql, FromSqlRow};
 use diesel::expression::AsExpression;
 use diesel::serialize::{Output, ToSql};
+use diesel::sql_types::Binary;
 use diesel::sql_types::Text;
 use diesel::*;
 use std::convert::TryInto;
@@ -58,3 +59,9 @@ fn struct_with_sql_type() {
         .get_result(conn);
     assert!(data.is_err());
 }
+
+// check that defaulted type parameters compile correctly
+// This is a regression test for https://github.com/diesel-rs/diesel/issues/3902
+#[derive(AsExpression, FromSqlRow)]
+#[diesel(sql_type = Binary)]
+pub struct Ewkb<B: AsRef<[u8]> = Vec<u8>>(pub B);