Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Auto-completion for warp Filters #4774

Open
cynecx opened this issue Jun 6, 2020 · 17 comments
Open

Auto-completion for warp Filters #4774

cynecx opened this issue Jun 6, 2020 · 17 comments
Labels
A-ty type system / type inference / traits / method resolution S-actionable Someone could pick this issue up and work on it right now

Comments

@cynecx
Copy link
Contributor

cynecx commented Jun 6, 2020

  • rustc 1.46.0-nightly (826cb062a 2020-06-05)
  • rust-analyzer (current HEAD/master at a609336)
  • Linux 5.6.15-arch1-1 x86_64 GNU/Linux
[dependencies]
warp = "0.2.3"
use warp::Filter;

fn main() {
    let a = warp::path("hello");
    let b = a.and(warp::path::param::<String>());
    b.<|>
}

No auto-completions are shown here (at the current cursor <|>).

EDIT:

Repro:

Test code
#[test]
fn infer_complex_traits() {
    check_types(
        r#"
//- /main.rs crate:main

#[lang = "sized"]
trait Sized {}

pub struct Product<H, T: HList>(H, T);

// Converts Product (and ()) into tuples.
pub trait HList {
    type Tuple: Tuple<HList = Self>;

    fn flatten(self) -> Self::Tuple;
}

// Typeclass that tuples can be converted into a Product (or unit ()).
pub trait Tuple: Sized {
    type HList: HList<Tuple = Self>;

    fn hlist(self) -> Self::HList;

    fn combine<T>(self, other: T) -> CombinedTuples<Self, T>
    where
        T: Tuple,
        Self::HList: Combine<T::HList>,
    {
        self.hlist().combine(other.hlist()).flatten()
    }
}

pub type CombinedTuples<T, U> =
    <<<T as Tuple>::HList as Combine<<U as Tuple>::HList>>::Output as HList>::Tuple;

// Combines Product together.
pub trait Combine<T: HList> {
    type Output: HList;

    fn combine(self, other: T) -> Self::Output;
}

impl<T: HList> Combine<T> for () {
    type Output = T;

    fn combine(self, other: T) -> Self::Output {
        other
    }
}

impl<H, T: HList, U: HList> Combine<U> for Product<H, T>
where
    T: Combine<U>,
    Product<H, <T as Combine<U>>::Output>: HList,
{
    type Output = Product<H, <T as Combine<U>>::Output>;

    fn combine(self, other: U) -> Self::Output {
        Product(self.0, self.1.combine(other))
    }
}

impl HList for () {
    type Tuple = ();

    fn flatten(self) -> Self::Tuple {}
}

impl Tuple for () {
    type HList = ();

    fn hlist(self) -> Self::HList {}
}

impl<T16> HList for Product<T16, ()> {
    type Tuple = (T16,);

    fn flatten(self) -> Self::Tuple {
        (self.0,)
    }
}
impl<T16> Tuple for (T16,) {
    type HList = Product<T16, ()>;

    fn hlist(self) -> Self::HList {
        Product(self.0, ())
    }
}

pub trait FilterBase {
    type Extract: Tuple;
}

pub trait Filter: FilterBase {
}

impl<T: FilterBase> Filter for T {}

trait Mumbo {
    type Extract;
    fn mumbo(&self) -> Self::Extract;
}

impl<T, U> Mumbo for (T, U)
where
    // There trait bounds are taken from `And`
    T: Filter,
    U: Filter,
    <T::Extract as Tuple>::HList: Combine<<U::Extract as Tuple>::HList>,

{
    type Extract = CombinedTuples<T::Extract, U::Extract>;

    fn mumbo(&self) -> Self::Extract {
        unreachable!()
    }
}

pub struct Exact<P>(P);

impl<P> FilterBase for Exact<P> {
    type Extract = ();
}

struct Param;

impl FilterBase for Param {
    type Extract = (usize,);
}

// Param as impl Trait
fn impl_param() -> impl Filter<Extract = (usize,)> {
    Param
}

fn main() {
    let a = Exact("");
    let b = Param;
    let c = impl_param();

    (a, b).mumbo();
    //           ^ (usize,)

    (a, b).mumbo();
    //           ^ (usize,)
}
"#,
    );
@flodiebold flodiebold added the A-completion autocompletion label Jun 6, 2020
@matklad
Copy link
Member

matklad commented Jul 11, 2020

Triage: still an issue, a somewhat more direct example

use warp::Filter;

fn main() {
    warp::path("hello")
        .and(warp::path::param())
        .and(warp::header("user-agent")) // <- this and doesn't resolve
        .map(|param: String, agent: String| format!("Hello {}, whose agent is {}", param, agent));
}

@matklad matklad added A-ty type system / type inference / traits / method resolution and removed A-completion autocompletion labels Jul 11, 2020
@cynecx
Copy link
Contributor Author

cynecx commented Jul 13, 2020

Note: This is still an issue on HEAD/master (5ca7cd9).

@cynecx
Copy link
Contributor Author

cynecx commented Jul 21, 2020

According to @flodiebold, macro expansions in associated type positions aren't expanded yet: #5455 (comment), which is probably also the cause of this issue (Warp utilizes macros in associated type positions https://github.com/seanmonstar/warp/blob/032123c1b9636f7999d5bc6d9ceda6cd85062f77/src/generic.rs#L119).

@lnicola lnicola added S-unactionable Issue requires feedback, design decisions or is blocked on other work A-macro macro expansion labels Jan 17, 2021
@lnicola
Copy link
Member

lnicola commented Jan 27, 2021

Triage: the original test case almost works now

image

However and is missing from the completion list, and matklad's second test case fails.

@detrumi
Copy link
Member

detrumi commented Jan 30, 2021

Looks like chalk was unable to solve this Implemented goal:

(&'static mut And<[
    ?0 := Exact<[?0 := Opaque<[?0 := (&'static Str)]>]>,
    ?1 := OpaqueTyId { index: 0 }
]>): Filter

It's tricky to minimize the example, but from digging through the logs I think the opaque type is from one of these two:

fn param<?0.0>() -> impl Filter<Extract = (?1.0,), Error = Rejection> + Copy: Deref
fn header<?0.0>(&str) -> impl Filter<Extract = (?1.0,), Error = Rejection> + Copy: Deref

@cynecx
Copy link
Contributor Author

cynecx commented Apr 10, 2021

@detrumi I've reduce the issue down to this code:

pub struct Product<H, T: HList>(H, T);

// Converts Product (and ()) into tuples.
pub trait HList {
    type Tuple: Tuple<HList = Self>;
}

// Typeclass that tuples can be converted into a Product (or unit ()).
pub trait Tuple {
    type HList: HList<Tuple = Self>;
}

pub type CombinedTuples<T, U> =
    <<<T as Tuple>::HList as Combine<<U as Tuple>::HList>>::Output as HList>::Tuple;

// Combines Product together.
pub trait Combine<T: HList> {
    type Output: HList;
}

impl<T: HList> Combine<T> for () {
    type Output = T;
}

impl<H, T: HList, U: HList> Combine<U> for Product<H, T>
where
    T: Combine<U>,
    Product<H, <T as Combine<U>>::Output>: HList,
{
    type Output = Product<H, <T as Combine<U>>::Output>;
}

impl HList for () {
    type Tuple = ();
}

impl Tuple for () {
    type HList = ();
}

impl<T16> HList for Product<T16, ()> {
    type Tuple = (T16,);
}
impl<T16> Tuple for (T16,) {
    type HList = Product<T16, ()>;
}

pub trait FilterBase {
    type Extract: Tuple;
}

pub trait Filter: FilterBase {
}

impl<T: FilterBase> Filter for T {}

trait Mumbo {
    type Extract;
    fn mumbo(&self) -> Self::Extract;
}

impl<T, U> Mumbo for (T, U)
where
    // There trait bounds are taken from `And`
    T: Filter,
    U: Filter,
    // When this bound is commented out, the type of `y` will change to an unnormalized type:
    // let y: <(Exact<&str>, impl Filter<Extract = (String,)>) as Mumbo>::Extract
    <T::Extract as Tuple>::HList: Combine<<U::Extract as Tuple>::HList>,

{
    type Extract = CombinedTuples<T::Extract, U::Extract>;

    fn mumbo(&self) -> Self::Extract {
        unreachable!()
    }
}

pub struct Exact<P>(P);
impl<P> FilterBase for Exact<P> {
    type Extract = ();
}

struct Param;
impl FilterBase for Param {
    type Extract = (String,);
}

// Param as impl Trait
fn impl_param() -> impl Filter<Extract = (String,)> {
    Param
}

fn main() {
    let a = Exact(""); // Exact<&str>
    let b = Param;
    let c = impl_param();

    let x = (a, b).mumbo(); // (String,)
    let y = (a, c).mumbo(); // {unknown} but should be (String,)
}

The x example works just fine. However the impl Trait causes the trait bounds on impl<T, U> Mumbo for (T, U) to fail.

EDIT: I minimized it further.
EDIT: even further

@flodiebold
Copy link
Member

That looks like it requires inference of auto traits for opaque types, which we currently don't do.

@cynecx
Copy link
Contributor Author

cynecx commented Apr 10, 2021

@flodiebold What kind of auto-trait are you talking about?

@flodiebold
Copy link
Member

You had a Send bound in the Mumbo impl.

@cynecx
Copy link
Contributor Author

cynecx commented Apr 11, 2021

@flodiebold Ah yes, the updated example doesn’t have that bound anymore so I think there is more to it.

@flodiebold flodiebold added S-actionable Someone could pick this issue up and work on it right now and removed S-unactionable Issue requires feedback, design decisions or is blocked on other work labels Apr 11, 2021
@cynecx
Copy link
Contributor Author

cynecx commented Apr 12, 2021

@flodiebold @detrumi @jackh726? It seems like an issue with chalk:

#[test]
fn warp() {
    test! {
        program {
            struct Product<H, T>
            where
              T: HList
            {}

            trait HList {
              type Tuple: Tuple<HList=Self>;
            }

            trait Tuple {
              type HList: HList<Tuple=Self>;
            }

            trait Combine<T>
            where
              T: HList
            {
              type Output: HList;
            }

            impl<T> Combine<T> for ()
            where
                T: HList
            {
                type Output = T;
            }

            impl<H, T, U> Combine<U> for Product<H, T>
            where
                T: HList,
                T: Combine<U>,
                U: HList,
                Product<H, <T as Combine<U>>::Output>: HList,
            {
                type Output = Product<H, <T as Combine<U>>::Output>;
            }

            impl HList for () {
                type Tuple = ();
            }

            impl Tuple for () {
                type HList = ();
            }

            impl<T16> HList for Product<T16, ()> {
                type Tuple = (T16,);
            }
            impl<T16> Tuple for (T16,) {
                type HList = Product<T16, ()>;
            }

            trait FilterBase {
              type Extract: Tuple;
            }

            trait Filter
            where Self: FilterBase {
            }

            impl<T> Filter for T
            where T: FilterBase {}

            trait Mumbo {
              type Extract;
            }

            impl<T, U> Mumbo for (T, U)
            where
              T: Filter,
              U: Filter,
              <<T as FilterBase>::Extract as Tuple>::HList: Combine<<<U as FilterBase>::Extract as Tuple>::HList>
            {
              type Extract = <<<<T as FilterBase>::Extract as Tuple>::HList as Combine<<<U as FilterBase>::Extract as Tuple>::HList>>::Output as HList>::Tuple;
            }

            struct Exact<T> {}
            impl<T> FilterBase for Exact<T> {
              type Extract = ();
            }

            struct Param {}
            impl FilterBase for Param {
              type Extract = (usize,);
            }

            opaque type AnonParam: FilterBase<Extract = (usize,)> = Param;
        }

        goal {
            (Exact<usize>, Param): Mumbo
        } yields {
            "Unique; substitution [], lifetime constraints []"
        }

        goal {
            (Exact<usize>, AnonParam): Mumbo
        } yields {
            "Unique; substitution [], lifetime constraints []"
        }
    }
}

The first goal succeeds. The second one does not (No possible solution).

@flodiebold
Copy link
Member

Hm. You've got your AnonParam only implementing FilterBase, unlike the code example. Does it still fail if you make it implement Filter?

@cynecx
Copy link
Contributor Author

cynecx commented Apr 12, 2021

@flodiebold You mean something like this:

opaque type AnonParam: Filter + FilterBase<Extract = (usize,)> = Param;

?

That also gives No possible solution.

Besides should it matter? Since this impl exists:

impl<T> Filter for T
        where T: FilterBase {}

@flodiebold
Copy link
Member

Besides should it matter?

Probably not, but just making sure.

@cynecx
Copy link
Contributor Author

cynecx commented Apr 12, 2021

I'll open an upstream issue (rust-lang/chalk#700).

@jackh726
Copy link
Member

@detrumi can you look into this? Looks like something related to opaque types and I'm not really going to have time to look into it this week.

@cynecx
Copy link
Contributor Author

cynecx commented May 30, 2021

That's weird. After #8856 this seems to work. However the chalk test still fails O.o

Edit: nvm, it still doesn’t work.

@Veykril Veykril removed the A-macro macro expansion label Dec 6, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-ty type system / type inference / traits / method resolution S-actionable Someone could pick this issue up and work on it right now
Projects
None yet
Development

No branches or pull requests

7 participants