rust-lang / rust

Empowering everyone to build reliable and efficient software.
https://www.rust-lang.org
Other
98.57k stars 12.74k forks source link

Broken method resolution for arc-swap + diesel #127306

Open weiznich opened 4 months ago

weiznich commented 4 months ago

I tried this code:

use arc_swap::ArcSwap; // arc-swap = "1.7.1"
use diesel_async::RunQueryDsl; // diesel-async = "0.4.1"
use std::sync::Arc;

fn issue(arc: ArcSwap<Arc<String>>) {
    let v = arc.load();
}

I expected to see this happen: Code compiles without error as ArcSwap::load() exists

Instead, this happened: Code fails to compile with the following error:

error[E0277]: the trait bound `ArcSwapAny<Arc<Arc<String>>>: diesel::query_builder::Query` is not satisfied
 --> src/main.rs:7:17
  |
7 |     let v = arc.load();
  |                 ^^^^ the trait `diesel::query_builder::Query` is not implemented for `ArcSwapAny<Arc<Arc<String>>>`, which is required by `ArcSwapAny<Arc<Arc<String>>>: LoadQuery<'_, _, _>`
  |
  = help: the following other types implement trait `diesel::query_builder::Query`:
            &'a T
            diesel::expression::sql_literal::SqlLiteral<ST, T>
            diesel::expression::sql_literal::UncheckedBind<Q, Value>
            diesel::query_builder::combination_clause::CombinationClause<Combinator, Rule, Source, Rhs>
            diesel::query_builder::delete_statement::DeleteStatement<T, U, diesel::query_builder::returning_clause::ReturningClause<Ret>>
            diesel::query_builder::insert_statement::InsertStatement<T, U, Op, diesel::query_builder::returning_clause::ReturningClause<Ret>>
            diesel::query_builder::select_statement::SelectStatement<F, S, D, W, O, LOf, G, H, LC>
            diesel::query_builder::select_statement::boxed::BoxedSelectStatement<'a, ST, QS, DB, GB>
          and 6 others
  = note: required for `ArcSwapAny<Arc<Arc<String>>>` to implement `diesel::query_builder::AsQuery`
  = note: required for `ArcSwapAny<Arc<Arc<String>>>` to implement `LoadQuery<'_, _, _>`

error[E0277]: the trait bound `ArcSwapAny<Arc<Arc<String>>>: diesel::query_builder::QueryFragment<_>` is not satisfied
 --> src/main.rs:7:17
  |
7 |     let v = arc.load();
  |                 ^^^^ the trait `diesel::query_builder::QueryFragment<_>` is not implemented for `ArcSwapAny<Arc<Arc<String>>>`, which is required by `ArcSwapAny<Arc<Arc<String>>>: LoadQuery<'_, _, _>`
  |
  = help: the following other types implement trait `diesel::query_builder::QueryFragment<DB, SP>`:
            <&'a T as diesel::query_builder::QueryFragment<DB>>
            <() as diesel::query_builder::QueryFragment<DB>>
            <(T0, T1) as diesel::query_builder::QueryFragment<__DB>>
            <(T0, T1, T2) as diesel::query_builder::QueryFragment<__DB>>
            <(T0, T1, T2, T3) as diesel::query_builder::QueryFragment<__DB>>
            <(T0, T1, T2, T3, T4) as diesel::query_builder::QueryFragment<__DB>>
            <(T0, T1, T2, T3, T4, T5) as diesel::query_builder::QueryFragment<__DB>>
            <(T0, T1, T2, T3, T4, T5, T6) as diesel::query_builder::QueryFragment<__DB>>
          and 160 others
  = note: required for `ArcSwapAny<Arc<Arc<String>>>` to implement `LoadQuery<'_, _, _>`

error[E0277]: the trait bound `ArcSwapAny<Arc<Arc<String>>>: diesel::query_builder::query_id::QueryId` is not satisfied
 --> src/main.rs:7:17
  |
7 |     let v = arc.load();
  |                 ^^^^ the trait `diesel::query_builder::query_id::QueryId` is not implemented for `ArcSwapAny<Arc<Arc<String>>>`, which is required by `ArcSwapAny<Arc<Arc<String>>>: LoadQuery<'_, _, _>`
  |
  = help: the following other types implement trait `diesel::query_builder::query_id::QueryId`:
            &'a T
            ()
            (T0, T1)
            (T0, T1, T2)
            (T0, T1, T2, T3)
            (T0, T1, T2, T3, T4)
            (T0, T1, T2, T3, T4, T5)
            (T0, T1, T2, T3, T4, T5, T6)
          and 148 others
  = note: required for `ArcSwapAny<Arc<Arc<String>>>` to implement `LoadQuery<'_, _, _>`

error[E0277]: the trait bound `ArcSwapAny<Arc<Arc<String>>>: LoadQuery<'_, _, _>` is not satisfied
   --> src/main.rs:7:17
    |
7   |     let v = arc.load();
    |                 ^^^^ the trait `diesel::query_builder::QueryFragment<_>` is not implemented for `ArcSwapAny<Arc<Arc<String>>>`, which is required by `ArcSwapAny<Arc<Arc<String>>>: LoadQuery<'_, _, _>`
    |
    = note: required for `ArcSwapAny<Arc<Arc<String>>>` to implement `LoadQuery<'_, _, _>`
note: required by a bound in `diesel_async::RunQueryDsl::load`
   --> /home/weiznich/.cargo/registry/src/index.crates.io-6f17d22bba15001f/diesel-async-0.4.1/src/run_query_dsl/mod.rs:338:15
    |
331 |     fn load<'query, 'conn, U>(
    |        ---- required by a bound in this associated function
...
338 |         Self: methods::LoadQuery<'query, Conn, U> + 'query,
    |               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `RunQueryDsl::load`

error[E0061]: this method takes 1 argument but 0 arguments were supplied
   --> src/main.rs:7:17
    |
7   |     let v = arc.load();
    |                 ^^^^-- an argument of type `&mut _` is missing
    |
note: method defined here
   --> /home/weiznich/.cargo/registry/src/index.crates.io-6f17d22bba15001f/diesel-async-0.4.1/src/run_query_dsl/mod.rs:331:8
    |
331 |     fn load<'query, 'conn, U>(
    |        ^^^^
help: provide the argument
    |
7   |     let v = arc.load(/* conn */);
    |                     ~~~~~~~~~~~~

warning: unused import: `arc_swap::access::Access`
 --> src/main.rs:1:5
  |
1 | use arc_swap::access::Access;
  |     ^^^^^^^^^^^^^^^^^^^^^^^^
  |
  = note: `#[warn(unused_imports)]` on by default

The method provided by diesel does clearly not apply as:

Meta

rustc --version --verbose:

rustc 1.79.0 (129f3b996 2024-06-10)
binary: rustc
commit-hash: 129f3b9964af4d4a709d1383930ade12dfe7c081
commit-date: 2024-06-10
host: x86_64-unknown-linux-gnu
release: 1.79.0
LLVM version: 18.1.7

(Although the same behavior can be observed with the latest nightly compiler)

Backtrace

``` ```

theemathas commented 4 months ago

Minimized reproduction:

trait Requirement {}

trait Extension {
    fn foo(self, x: i32)
    where
        Self: Requirement;
}

impl<T> Extension for T {
    fn foo(self)
    where
        Self: Requirement,
    {
    }
}

struct Thing;

impl Thing {
    fn foo(&self) {} // Note: &self, not self
}

fn what(x: Thing) {
    x.foo()
}
Error output ``` Compiling playground v0.0.1 (/playground) error[E0050]: method `foo` has 1 parameter but the declaration in trait `Extension::foo` has 2 --> src/lib.rs:10:12 | 4 | fn foo(self, x: i32) | ------------ trait requires 2 parameters ... 10 | fn foo(self) | ^^^^ expected 2 parameters, found 1 error[E0277]: the trait bound `Thing: Requirement` is not satisfied --> src/lib.rs:24:7 | 24 | x.foo() | ^^^ the trait `Requirement` is not implemented for `Thing` | help: this trait has no implementations, consider adding one --> src/lib.rs:1:1 | 1 | trait Requirement {} | ^^^^^^^^^^^^^^^^^ note: required by a bound in `Extension::foo` --> src/lib.rs:6:15 | 4 | fn foo(self, x: i32) | --- required by a bound in this associated function 5 | where 6 | Self: Requirement; | ^^^^^^^^^^^ required by this bound in `Extension::foo` error[E0061]: this method takes 1 argument but 0 arguments were supplied --> src/lib.rs:24:7 | 24 | x.foo() | ^^^-- an argument of type `i32` is missing | note: method defined here --> src/lib.rs:4:8 | 4 | fn foo(self, x: i32) | ^^^ help: provide the argument | 24 | x.foo(/* i32 */) | ~~~~~~~~~~~ Some errors have detailed explanations: E0050, E0061, E0277. For more information about an error, try `rustc --explain E0050`. error: could not compile `playground` (lib) due to 3 previous errors ```

This happens because, when resolving the method call, rust checks calling the method on Thing before trying to call the method on &Thing. See the reference, which states that "This process does not take into account the mutability or lifetime of the receiver, or whether a method is unsafe." Is appears that this process also doesn't take into account the where bounds on the method.

This can be worked around by using the disambiguated function call syntax, e.g. Thing::foo(&x)