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

SimpleObject: error: lifetime may not live long enough #900

Closed
bbigras opened this issue Apr 20, 2022 · 28 comments
Closed

SimpleObject: error: lifetime may not live long enough #900

bbigras opened this issue Apr 20, 2022 · 28 comments
Labels
bug Something isn't working Stale

Comments

@bbigras
Copy link
Contributor

bbigras commented Apr 20, 2022

I got some weird error after using #[graphql(complex)] and #[ComplexObject] on other struct.

So the affected struct doesn't have the "complex" thing.

I'm probably using nested ComplexObject that uses some dataloaders.

I'll try to make a minimal, reproducible example.

Expected Behavior

Actual Behavior

13 | #[derive(Debug, PartialEq, SimpleObject)]
   |                            -^^^^^^^^^^^
   |                            |
   |                            let's call the lifetime of this reference `'2`
   |                            let's call the lifetime of this reference `'1`
   |                            associated function was supposed to return data with lifetime `'1` but it is returning data with lifetime `'2`
   |                            in this derive macro expansion
   |
  ::: /home/bbigras/.cargo/registry/src/github.com-1ecc6299db9ec823/async-graphql-derive-3.0.38/src/lib.rs:47:1

Steps to Reproduce the Problem

Specifications

  • Version: 3.0.38
  • Platform: NixOS 22.05 (Quokka) x86_64
  • Subsystem:
@bbigras bbigras added the bug Something isn't working label Apr 20, 2022
@sunli829
Copy link
Collaborator

sunli829 commented May 1, 2022

Please provide an example that reproduces this error.

@kennetpostigo
Copy link

I get the same thing locally since i've updated async-graphql, it happens sporadically

@kennetpostigo
Copy link

After a clean build it goes away but comes back after a while

@bbigras
Copy link
Contributor Author

bbigras commented May 1, 2022

After a clean build it goes away but comes back after a while

I had something similar too.

@sunli829
Copy link
Collaborator

sunli829 commented May 2, 2022

Could you please provide an example that can reproduce this problem?😁

@bbigras
Copy link
Contributor Author

bbigras commented May 2, 2022

Yes sorry. I'll be back home tomorrow. (I should have still made one earlier as promised when I opened the issue though)

@bbigras
Copy link
Contributor Author

bbigras commented May 2, 2022

I think I'm able to reproduce. There's a compiler bug, but I'm not sure yet if both problems are related.

error: lifetime may not live long enough
  --> src/query.rs:53:26
   |
53 | #[derive(Clone, Default, SimpleObject)]
   |                          -^^^^^^^^^^^
   |                          |
   |                          let's call the lifetime of this reference `'1`
   |                          let's call the lifetime of this reference `'2`
   |                          associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1`
   |
   = note: this error originates in the derive macro `SimpleObject` (in Nightly builds, run with -Z macro-backtrace for more info)

error: internal compiler error: encountered incremental compilation error with mir_borrowck(async_graphql_900[a851]::query::{impl#16}::name)
  |
  = help: This is a known issue with the compiler. Run `cargo clean -p async_graphql_900` or `cargo clean` to allow your project to compile
  = note: Please follow the instructions below to create a bug report with the provided information
  = note: See <https://github.com/rust-lang/rust/issues/84970> for more information

@bbigras
Copy link
Contributor Author

bbigras commented May 2, 2022

Now I can't reproduce it. I made a backup when it failed, but excluding my target folder.

So I never had the This is a known issue with the compiler. Run cargo clean -p async_graphql_900 or message before, but if I try cargo clean -p async_graphql_900 with my real project, the next build is fine.

Should we just assume that it's a bug with the compiler?

Here's my "minimal test case" https://github.com/bbigras/async-graphql-900 , but I can't reproduce the error at will.

EDIT: another weird thing is that I'm using the same compiler for my real project and the test case.

@rbozan
Copy link

rbozan commented May 9, 2022

Same on NixOS 22.05

@al8n
Copy link
Contributor

al8n commented May 23, 2022

I have met this, run cargo clean first, then this should work.

@rbozan
Copy link

rbozan commented May 23, 2022

I have a simple script now for this until there is some workaround as doing cargo clean just takes way too long for me. The issue happens quite often for me, when the compiler complains about other things beforehand for some reason this issue pops up even though the other issues have been fixed. If your workflow consists of cargo watch -x "run" and having an autosave in your editor just makes this issue very common.

cargo clean -p async-graphql -p *your crate name*

@emwalker
Copy link

emwalker commented Jun 1, 2022

I'm seeing this as well. I'm wondering whether it's a bug in Rust itself. Not sure exactly how to repro the issue, although I see it quite frequently.

@vaikzs
Copy link

vaikzs commented Jun 5, 2022

cargo clean and cargo build works for me.

To reproduce --
I had async-graphql=3.0.38 and I upgraded to async-graphql=4.0.1 which immediately showed the error in my schema.

@adzialocha
Copy link

Can confirm that it is happening frequently with both async-graphql 3.0.38 and 4.0.1 on both Rust v1.61 and v1.62

@vaikzs
Copy link

vaikzs commented Jul 2, 2022

@adzialocha It stopped happening. Not able to reproduce now either.

@tbillington
Copy link

tbillington commented Jul 3, 2022

Just started getting this, can't reliably trigger or reliably stop it from happening 😂 cargo clean temporarily fixes it as others said, perhaps something related to compilation order, or something not being recompiled?

async-graphql and async-graphql-axum 4.0.4 - stable-aarch64-apple-darwin

@Bberky
Copy link

Bberky commented Jul 3, 2022

I've been dealing with this for the past two days to no avail. cargo build -r works fine, but cargo check and cargo build (no release mode) fails. I've also been wondering whether it's got something to do with incremental compilation, but I'm really not sure, since I only get this when using the SimpleObject macro. I'm running the latest stable release of rustc and latest version of async-graphql.

@emwalker
Copy link

emwalker commented Jul 3, 2022

I was having this problem as well, and then it went away for good. Not sure what changed.

@jwangnz
Copy link

jwangnz commented Jul 4, 2022

I am having this problem too, in Rust 1.62.0. But it works fine if I switch back to Rust 1.61.0.

@danielhenrymantilla
Copy link

danielhenrymantilla commented Jul 4, 2022

Click to see how I reached that situation

Ok, I have investigated a bit the @Bberky's situation. They managed to reduce the issue to:

  #[derive(::async_graphql::SimpleObject)]
  pub struct A {
      field: String,
  }
  
+ pub struct B { field: String }

with a cargo c before and after that diff, on 1.62.0 , triggering a lifetime error message as reported by other people in this repo. By using an intermediary expansion trick, I got the expansion of SimpleObject on A to be:

Click to see the expansion
#[allow(clippy::all, clippy::pedantic)]
impl A {
    #[inline]
    #[allow(missing_docs)]
    async fn field(&self, ctx: &async_graphql::Context<'_>) -> async_graphql::Result<&String> {
        ::std::result::Result::Ok(&self.field)
    }
}
#[allow(clippy::all, clippy::pedantic)]
#[async_graphql::async_trait::async_trait]
impl async_graphql::resolver_utils::ContainerType for A {
    async fn resolve_field(
        &self,
        ctx: &async_graphql::Context<'_>,
    ) -> async_graphql::ServerResult<::std::option::Option<async_graphql::Value>> {
        if ctx.item.node.name.node == "field" {
            let f = async move {
                self.field(ctx)
                    .await
                    .map_err(|err| err.into_server_error(ctx.item.pos))
            };
            let obj = f.await.map_err(|err| ctx.set_error_path(err))?;
            let ctx_obj = ctx.with_selection_set(&ctx.item.node.selection_set);
            return async_graphql::OutputType::resolve(&obj, &ctx_obj, ctx.item)
                .await
                .map(::std::option::Option::Some);
        }
        ::std::result::Result::Ok(::std::option::Option::None)
    }
}
#[allow(clippy::all, clippy::pedantic)]
#[async_graphql::async_trait::async_trait]
impl async_graphql::OutputType for A {
    fn type_name() -> ::std::borrow::Cow<'static, ::std::primitive::str> {
        ::std::borrow::Cow::Borrowed("A")
    }
    fn create_type_info(registry: &mut async_graphql::registry::Registry) -> ::std::string::String {
        registry.create_output_type::<Self, _>(
            async_graphql::registry::MetaTypeId::Object,
            |registry| async_graphql::registry::MetaType::Object {
                name: ::std::borrow::Cow::into_owned(::std::borrow::Cow::Borrowed("A")),
                description: ::std::option::Option::None,
                fields: {
                    let mut fields = async_graphql::indexmap::IndexMap::new();
                    fields.insert(
                        ::std::borrow::ToOwned::to_owned("field"),
                        async_graphql::registry::MetaField {
                            name: ::std::borrow::ToOwned::to_owned("field"),
                            description: ::std::option::Option::None,
                            args: ::std::default::Default::default(),
                            ty: <String as async_graphql::OutputType>::create_type_info(registry),
                            deprecation: async_graphql::registry::Deprecation::NoDeprecated,
                            cache_control: async_graphql::CacheControl {
                                public: true,
                                max_age: 0usize,
                            },
                            external: false,
                            provides: ::std::option::Option::None,
                            requires: ::std::option::Option::None,
                            visible: ::std::option::Option::None,
                            compute_complexity: ::std::option::Option::None,
                        },
                    );
                    fields
                },
                cache_control: async_graphql::CacheControl {
                    public: true,
                    max_age: 0usize,
                },
                extends: false,
                keys: ::std::option::Option::None,
                visible: ::std::option::Option::None,
                is_subscription: false,
                rust_typename: ::std::any::type_name::<Self>(),
            },
        )
    }
    async fn resolve(
        &self,
        ctx: &async_graphql::ContextSelectionSet<'_>,
        _field: &async_graphql::Positioned<async_graphql::parser::types::Field>,
    ) -> async_graphql::ServerResult<async_graphql::Value> {
        async_graphql::resolver_utils::resolve_container(ctx, self).await
    }
}
impl async_graphql::ObjectType for A {}

From there, once that diff is applied in between cargo checks, I get:

error: lifetime may not live long enough
 --> /private/var/folders/m_/347zzk7j6nz08tt6z3tnd3z00000gn/T/tmp.qeHHjilha7/target/debug-proc-macros/test-f4020daedf6df8e8.rs:5:95
  |
5 |       async fn field(&self, ctx: &async_graphql::Context<'_>) -> async_graphql::Result<&String> {
  |  ____________________-___________________________________--_____________________________________^
  | |                    |                                   |
  | |                    |                                   let's call the lifetime of this reference `'2`
  | |                    let's call the lifetime of this reference `'1`
6 | |         ::std::result::Result::Ok(&self.field)
7 | |     }
  | |_____^ associated function was supposed to return data with lifetime `'1` but it is returning data with lifetime `'2`

error: lifetime may not live long enough
 --> /private/var/folders/m_/347zzk7j6nz08tt6z3tnd3z00000gn/T/tmp.qeHHjilha7/target/debug-proc-macros/test-f4020daedf6df8e8.rs:6:9
  |
5 |     async fn field(&self, ctx: &async_graphql::Context<'_>) -> async_graphql::Result<&String> {
  |                    -                                   -- let's call the lifetime of this reference `'2`
  |                    |
  |                    let's call the lifetime of this reference `'1`
6 |         ::std::result::Result::Ok(&self.field)
  |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1`

So the interesting part is that this does not involve async_trait whatsoever (I was afraid it would), and so the following part of the expansion is the one hitting the incremental compilation bug:

#[allow(clippy::all, clippy::pedantic)]
impl A {
    #[inline]
    #[allow(missing_docs)]
    async fn field(&self, ctx: &async_graphql::Context<'_>) -> async_graphql::Result<&String> {
        ::std::result::Result::Ok(&self.field)
    }
}

Finally, removing SimpleObject altogether from the equation, we end up with the following diff in between cargo checks running into the incremental compilation issue:

  pub struct A {
      field: String,
  }
  
  #[allow(clippy::all, clippy::pedantic)]
  impl A {
      #[inline]
      #[allow(missing_docs)]
      async fn field(&self, ctx: &async_graphql::Context<'_>) -> async_graphql::Result<&String> {
          ::std::result::Result::Ok(&self.field)
      }
  }
  
+ pub struct B { field: String, }

which ends up yielding the following stand-alone repro which I'll report to rust [EDIT: done]:

fn main(){let _=std::process::Command::new("/bin/bash").args(&["-c", concat!(r##"{
set -euo pipefail
cd $(mktemp -d)


cargo init -q --name example --lib

cat> src/lib.rs <<'EOF'
    #![allow(unused)]
    struct Foo;
    
    impl Foo {
        async fn f(&self, _: &&()) -> &() {
            &()
        }
    }
EOF

(set -x
    cargo c -q
    { set +x; echo 'enum Bar {}'; } 2>/dev/null | tee -a src/lib.rs
    cargo c -q
)


echo ✅
} 2>&1"##)]).status();}

In the meantime, @sunli829 (or other maintainers of this crate), you may be able to dodge the compiler limitation by using https://docs.rs/fix-hidden-lifetime-bug on the non-async_trait-annotated async fns 🙂

@danielhenrymantilla
Copy link

danielhenrymantilla commented Jul 4, 2022

Workaround

I've submitted #972 which implements the usage of fix_hidden_lifetime_bug as a palliative workaround for that compiler regression. To use it, follow the instructions described in that PR.

danielhenrymantilla added a commit to danielhenrymantilla/async-graphql that referenced this issue Jul 4, 2022
@ControlCplusControlV
Copy link

I was able to fix this issue by simple running export CARGO_INCREMENTAL=0, requires a better workstation to remain productive but it does work

@danielhenrymantilla
Copy link

danielhenrymantilla commented Jul 5, 2022

@ControlCplusControlV you should be able to save in compile times by using that PR through a patch rather than fully disabling incremental altogether 🙂:

[patch.crates-io.async-graphql]
git = "https://github.com/danielhenrymantilla/async-graphql.git"
branch = "workaround-for-1-62-bug"

@ivan
Copy link
Contributor

ivan commented Jul 6, 2022

Here are some hopefully complete but overspecific repro steps:

rustup install nightly-2022-06-30 --force
rustup default nightly-2022-06-30
git clone https://github.com/bkonkle/rust-example-caster-api
cd rust-example-caster-api
cargo build

Edit ./libs/shows/src/episodes_service.rs:

diff --git a/libs/shows/src/episodes_service.rs b/libs/shows/src/episodes_service.rs
index a1b4c9f..e7dfddd 100644
--- a/libs/shows/src/episodes_service.rs
+++ b/libs/shows/src/episodes_service.rs
@@ -70,20 +70,7 @@ impl DefaultEpisodesService {
 #[async_trait]
 impl EpisodesService for DefaultEpisodesService {
     async fn get(&self, id: &str, with_show: &bool) -> Result<Option<Episode>> {
-        let query = episode_model::Entity::find_by_id(id.to_owned());
-
-        let episode = if *with_show {
-            query
-                .find_also_related(show_model::Entity)
-                .one(&*self.db)
-                .await?
-        } else {
-            query.one(&*self.db).await?.map(|u| (u, None))
-        };
-
-        let episode: EpisodeOption = episode.into();
-
-        Ok(episode.into())
+        Ok(None)
     }

     async fn get_by_ids(&self, ids: Vec<String>) -> Result<Vec<Episode>> {
cargo build # (assuming you didn't wrap cargo or turn off incremental builds)

and you'll see

error: lifetime may not live long enough
 --> libs/shows/src/show_model.rs:9:77
  |
9 |     Clone, Debug, Eq, PartialEq, DeriveEntityModel, Deserialize, Serialize, SimpleObject, PolarClass,
  |                                                                             -^^^^^^^^^^^
  |                                                                             |
  |                                                                             let's call the lifetime of this reference `'2`
  |                                                                             let's call the lifetime of this reference `'1`
  |                                                                             associated function was supposed to return data with lifetime `'1` but it is returning data with lifetime `'2`
  |
  = note: this error originates in the derive macro `SimpleObject` (in Nightly builds, run with -Z macro-backtrace for more info)
help: consider introducing a named lifetime parameter and update trait if needed
  |
9 ~     Clone, Debug, Eq, PartialEq, DeriveEntityModel, Deserialize, Serialize, 'a'aimpleObject, PolarClass,
10| )]
...
16|     #[polar(attribute)]
17~     pub id<'a>: String,
  |

If you rm -rf target, it will then build fine.

@adzialocha
Copy link

This is probably fixed now in Rust 1.62.1: rust-lang/rust#98890

@ivan
Copy link
Contributor

ivan commented Jul 23, 2022

I tested my #900 (comment) to confirm nightly-2022-07-23 (rustc 1.64.0-nightly (848090dcd 2022-07-22)) is fixed.

@github-actions
Copy link

This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 5 days.

@github-actions github-actions bot added the Stale label Aug 27, 2022
@github-actions
Copy link

github-actions bot commented Sep 1, 2022

This issue was closed because it has been stalled for 5 days with no activity.

@github-actions github-actions bot closed this as completed Sep 1, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working Stale
Projects
None yet
Development

No branches or pull requests