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

Confusing output for signature mismatch in Fn trait with GAT lifetime parameter #93342

Closed
skyzh opened this issue Jan 26, 2022 · 6 comments
Closed
Assignees
Labels
A-GATs Area: Generic associated types (GATs) C-bug Category: This is a bug. F-generic_associated_types `#![feature(generic_associated_types)]` a.k.a. GATs

Comments

@skyzh
Copy link
Contributor

skyzh commented Jan 26, 2022

I tried this code:

#![feature(generic_associated_types)]

use std::marker::PhantomData;

pub trait Scalar: 'static {
    type RefType<'a>: ScalarRef<'a>;
}

pub trait ScalarRef<'a>: 'a {}

impl Scalar for i32 {
    type RefType<'a> = i32;
}

impl Scalar for String {
    type RefType<'a> = &'a str;
}

impl Scalar for bool {
    type RefType<'a> = i32;
}

impl<'a> ScalarRef<'a> for bool {}

impl<'a> ScalarRef<'a> for i32 {}

impl<'a> ScalarRef<'a> for &'a str {}

fn str_contains(a: &str, b: &str) -> bool {
    a.contains(b)
}

pub struct BinaryExpression<A: Scalar, B: Scalar, O: Scalar, F>
where
    F: Fn(A::RefType<'_>, B::RefType<'_>) -> O,
{
    f: F,
    _phantom: PhantomData<(A, B, O)>,
}

impl<A: Scalar, B: Scalar, O: Scalar, F> BinaryExpression<A, B, O, F>
where
    F: Fn(A::RefType<'_>, B::RefType<'_>) -> O,
{
    pub fn new(f: F) -> Self {
        Self {
            f,
            _phantom: PhantomData,
        }
    }
}

fn fail_case() {
    //     error[E0631]: type mismatch in function arguments
    //   --> src/main.rs:58:54
    //    |
    // 33 | fn str_contains(a: &str, b: &str) -> bool {
    //    | ----------------------------------------- found signature of `for<'r, 's> fn(&'r str, &'s str) -> _`
    // ...
    // 58 |     BinaryExpression::<String, String, bool, _>::new(str_contains);
    //    |     ------------------------------------------------ ^^^^^^^^^^^^ expected signature of `for<'r, 's> fn(<String as Scalar>::RefType<'r>, <String as Scalar>::RefType<'s>) -> _`
    //    |     |
    //    |     required by a bound introduced by this call
    //    |
    // note: required by a bound in `BinaryExpression::<A, B, O, F>::new`
    //   --> src/main.rs:47:8
    //    |
    // 47 |     F: Fn(A::RefType<'_>, B::RefType<'_>) -> O,
    //    |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `BinaryExpression::<A, B, O, F>::new`
    // 48 | {
    // 49 |     pub fn new(f: F) -> Self {
    //    |            --- required by a bound in this

    BinaryExpression::<String, String, bool, _>::new(str_contains);
}

fn success_case() {
    BinaryExpression::<String, String, bool, _> {
        f: str_contains,
        _phantom: PhantomData,
    };
}

fn main() {
    println!("Hello, world!");
}

I expected to see this happen:

Both functions compile.

Instead, this happened:

If I directly call new,

expected signature of `for<'r, 's> fn(<String as Scalar>::RefType<'r>, <String as Scalar>::RefType<'s>) -> _`

... but if I manually construct the struct, it succeeds.

Meta

rustc --version --verbose:

rustc 1.60.0-nightly (8cdb3cd94 2022-01-25)
binary: rustc
commit-hash: 8cdb3cd94efece1e17cbd8f6edb1dc1a482779a0
commit-date: 2022-01-25
host: aarch64-apple-darwin
release: 1.60.0-nightly
LLVM version: 13.0.0

Thanks for investigating!

@skyzh skyzh added the C-bug Category: This is a bug. label Jan 26, 2022
@skyzh
Copy link
Contributor Author

skyzh commented Jan 26, 2022

... and the interesting thing is that, once I used a trait to "prove" str_contains is actually Fn(A::RefType<'_>, B::RefType<'_>) -> O, it compiles 🤣🤣🤣

(which is exactly the technique I used in https://github.com/skyzh/type-exercise-in-rust when I was implementing expression vectorization in safe, nightly Rust without any macro)

#![feature(generic_associated_types)]

use std::marker::PhantomData;

pub trait Scalar: 'static {
    type RefType<'a>: ScalarRef<'a>;
}

pub trait ScalarRef<'a>: 'a {}

impl Scalar for i32 {
    type RefType<'a> = i32;
}

impl Scalar for String {
    type RefType<'a> = &'a str;
}

impl Scalar for bool {
    type RefType<'a> = i32;
}

impl<'a> ScalarRef<'a> for bool {}

impl<'a> ScalarRef<'a> for i32 {}

impl<'a> ScalarRef<'a> for &'a str {}

fn str_contains(a: &str, b: &str) -> bool {
    a.contains(b)
}

pub struct BinaryExpression<A: Scalar, B: Scalar, O: Scalar, F>
where
    // We don't use `Fn` trait here. Instead, we use a bridge trait called `BinaryExprFunc`
    F: BinaryExprFunc<A, B, O>,
{
    f: F,
    _phantom: PhantomData<(A, B, O)>,
}

impl<A: Scalar, B: Scalar, O: Scalar, F> BinaryExpression<A, B, O, F>
where
    F: BinaryExprFunc<A, B, O>,
{
    pub fn new(f: F) -> Self {
        Self {
            f,
            _phantom: PhantomData,
        }
    }
}

pub trait BinaryExprFunc<A: Scalar, B: Scalar, O: Scalar> {
    fn eval(&self, a: A::RefType<'_>, b: B::RefType<'_>) -> O;
}

// We prove that `str_contains` is `Fn(A::RefType<'_>, B::RefType<'_>) -> O` by implementing `BinaryExprFunc`
impl<A: Scalar, B: Scalar, O: Scalar, F> BinaryExprFunc<A, B, O> for F
where
    F: Fn(A::RefType<'_>, B::RefType<'_>) -> O,
{
    fn eval(&self, a: A::RefType<'_>, b: B::RefType<'_>) -> O {
        self(a, b)
    }
}

fn success_case_1() {
    BinaryExpression::<String, String, bool, _>::new(str_contains);
}

fn success_case_2() {
    BinaryExpression::<String, String, bool, _> {
        f: str_contains,
        _phantom: PhantomData,
    };
}

fn main() {
    println!("Hello, world!");
}
   Compiling rust-bug v0.1.0 (/Users/skyzh/Work/rust-bug)
warning: function is never used: `str_contains`
  --> src/main.rs:29:4
   |
29 | fn str_contains(a: &str, b: &str) -> bool {
   |    ^^^^^^^^^^^^
   |
   = note: `#[warn(dead_code)]` on by default

warning: field is never read: `f`
  --> src/main.rs:37:5
   |
37 |     f: F,
   |     ^^^^

warning: function is never used: `success_case_1`
  --> src/main.rs:66:4
   |
66 | fn success_case_1() {
   |    ^^^^^^^^^^^^^^

warning: function is never used: `success_case_2`
  --> src/main.rs:70:4
   |
70 | fn success_case_2() {
   |    ^^^^^^^^^^^^^^

warning: `rust-bug` (bin "rust-bug") generated 4 warnings
    Finished dev [unoptimized + debuginfo] target(s) in 0.27s

@skyzh skyzh changed the title Confusing output for lifetime mismatch in Fn trait with GAT parameter Confusing output for signature mismatch in Fn trait with GAT parameter Jan 26, 2022
@skyzh skyzh changed the title Confusing output for signature mismatch in Fn trait with GAT parameter Confusing output for signature mismatch in Fn trait with GAT lifetime parameter Jan 26, 2022
@compiler-errors
Copy link
Member

I'd like to take a look at this.

@rustbot claim

@jackh726
Copy link
Member

jackh726 commented Mar 8, 2022

I think this was fixed in #90887, but needs to be confirmed.

@skyzh
Copy link
Contributor Author

skyzh commented Mar 8, 2022

Thanks a lot! I'll have a try on the latest nightly.

@compiler-errors
Copy link
Member

@skyzh you probably need to try on tomorrow's nightly (or the day after), since #90887 just landed very recently.

@compiler-errors
Copy link
Member

@jackh726, I manually checked this compiles on master.

@skyzh skyzh closed this as completed Mar 8, 2022
@fmease fmease added the A-GATs Area: Generic associated types (GATs) label Nov 2, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-GATs Area: Generic associated types (GATs) C-bug Category: This is a bug. F-generic_associated_types `#![feature(generic_associated_types)]` a.k.a. GATs
Projects
None yet
4 participants