Repository: graphql-rust/juniper (language: rust)

Issue:
Automatically implement interface fields on implementor graphql objects?
Ideally I'd like to be able to do

```rust
pub trait Node {
    fn id(&self) -> &Uuid;
}

pub struct Queue {
    pub id: Uuid,
    pub name: String,
    ...
}
impl Node for Queue {
    fn id(&self) -> &Uuid {
        &self.id
    }
}

graphql_interface!(<'a> &'a Node: Context as "Node" |&self| {
    field id() -> &Uuid {
        self.id()
    }

    instance_resolvers: |&context| { ... }
});

graphql_object!(Queue: Context |&self| {
    interfaces: [&Node]
    field name() -> &str {
        &self.name
    }
});
```

And get

```graphql
scalar Uuid

interface Node {
  id: Uuid
}

type Queue implements Node {
  id: Uuid
  name: String
}
```

Currently you can get an invalid schema if you forget to implement the `id` field on the `Queue` object.

Python's Graphene works in this way http://docs.graphene-python.org/en/latest/types/interfaces/

I realise this wouldn't be a very backwards-compatible change

Thoughts?
graphql_interface and graphql_object does not mix well
**Describe the bug**

A `struct` using `#[graphql_object]` (for complexe fields resolvers) and `#[graphql_interface]` does not generate a valid schema (`as_schema_language`)

**To Reproduce**
```rust
use juniper::{graphql_interface, graphql_object, graphql_interface};

#[graphql_interface(for = User)]
pub trait Namespace {
    fn id(&self) -> Option<uuid::Uuid>;
    fn created_at(&self) -> chrono::DateTime<chrono::Utc>;
    fn name(&self) -> String;
}


#[derive(Debug, Clone)]
pub struct User {
    id: uuid::Uuid,
    created_at: chrono::DateTime<chrono::Utc>,
    name: String,
    other: String,
}

#[graphql_object]
#[graphql(impl = NamespaceValue)]
impl User {
    pub fn username() -> String {
        panic!("not implemented"); // TODO
    }
}

#[graphql_interface]
impl Namespace for User {
    fn id(&self) -> Option<uuid::Uuid> {
        Some(self.id)
    }

    fn created_at(&self) -> chrono::DateTime<chrono::Utc> {
        self.created_at
    }

    fn name(&self) -> String {
        self.name.clone()
    }
}
```
generates

```graphql
interface Namespace {
  id: Uuid
  createdAt: DateTimeUtc!
  name: String!
}


type User {
  username: String!
}

"DateTime"
scalar DateTimeUtc
"Uuid"
scalar Uuid
```


**Expected behavior**
The expected generated schema is as follow
```graphql
type User implements Namespace {
  id: Uuid!
  createdAt: DateTimeUtc!
  name: String!
  username: String!
}
```

**Additional context**
branch: `master`
rev: 4ffd276a5b33ae4423f295675f977ed6cc7fb88a



Discussion / hints:
Interesting finds!

The fact that you can create an inconsistent schema is very problematic. I _guess_ another way to cause an inconsistency is to have a mismatch between `instance_resolvers` and `interfaces`, too. These kinds of issues need to be guarded against; either by designing the API so they are impossible to create, or making sure that creating an invalid schema panics/returns errors.

The other part, automatically implementing interface fields on object types seems convenient. I'm not sure _how_ to do this in the, though. Juniper can not "upcast" an object into an interface in the general case or vice versa - this is why we need the awkward `instance_resolvers`. If anyone has any ideas here, let me know :)
Also,
When I try as follow:
```rust
#[graphql_object(
    impl = NamespaceValue,
)]
impl User {
    pub fn username() -> String {
        panic!("not implemented"); // TODO
    }
}

#[graphql_interface]
impl Namespace for User {
    fn id(&self) -> Option<uuid::Uuid> {
        Some(self.id)
    }

    fn created_at(&self) -> chrono::DateTime<chrono::Utc> {
        self.created_at
    }

    fn name(&self) -> String {
        self.name.clone()
    }
}

```

it generates the following code:
```graphql
type User implements Namespace {
  username: String!
}
```

while it should have generated:

```graphql
type User implements Namespace {
  id: Uuid!
  createdAt: DateTimeUtc!
  name: String!
  username: String!
}
```
Thanks for the high-quality report! This does indeed look like a bug. I might be able to look at it over the weekend, but feel free to take a crack at it...the schema generation code is pretty straightforward and contained.
After exploring the tests (https://github.com/graphql-rust/juniper/blob/a4871887bb6b30029bd4671af716c0649a0cc60c/juniper/src/tests/fixtures/starwars/schema.rs / https://github.com/graphql-rust/juniper/blob/a4871887bb6b30029bd4671af716c0649a0cc60c/juniper/src/tests/fixtures/starwars/starwars.graphql) I also noticed that the interface methods are duplicated in both in the `impl Human` and `impl Character for Human` so I'm not if it's finally a bug, or if the macro is working as expected, but is just not intuitive to work with.


For example, this code:
```rust

#[derive(Debug, Clone)]
pub struct User {
    id: uuid::Uuid,
    created_at: chrono::DateTime<chrono::Utc>,
    name: String,
}

#[graphql_object(
    impl = NamespaceInterface,
)]
impl User {
    pub fn id(&self) -> Option<ID> {
        Some(self.id.into())
    }

    pub fn created_at(&self) -> Time {
        self.created_at.into()
    }

    pub fn name(&self) -> String {
        self.name.clone()
    }

    pub fn username() -> String {
        panic!("not implemented"); // TODO
    }
}

#[graphql_interface]
impl Namespace for User {
    fn id(&self) -> Option<ID> {
        User::id(self)
    }

    fn created_at(&self) -> Time {
        User::created_at(self)
    }

    fn name(&self) -> String {
        User::name(&self)
    }
}
```
generates the good output, but is 'weird'

Not being intuitive is a bug :-)
@LegNeato @skerkour 

The problem is that `#[graphql_object]` macro expansion cannot work with multiple attributes, like `#[graphql_interface]` does. That's why:
```rust
#[graphql_object]
#[graphql(impl = NamespaceValue)]
impl User {
```
is invalid definition at the moment, where `#[graphql(impl = NamespaceValue)]` attribute is fully omitted.

Even if `#[graphql_object]` would have supported that, it should have been defined like that:
```rust
#[graphql_object]
#[graphql_object(impl = NamespaceValue)]
impl User {
```
like the `#[graphql_interface]`/`#[graphql_union]` allows at the moment.

This is for the reasons [explained here](https://github.com/graphql-rust/juniper/pull/666#issuecomment-634568358). But I think that point needs re-checking.

Also, need to think about some compile-time check to prevent the situation described above, so it would be complain about `User` not implementing `Namespace` in schema.

Fix patch:
diff --git a/docs/book/content/types/interfaces.md b/docs/book/content/types/interfaces.md
index 414d2f299..aa9c543e7 100644
--- a/docs/book/content/types/interfaces.md
+++ b/docs/book/content/types/interfaces.md
@@ -45,32 +45,14 @@ trait Character {
 struct Human {
     id: String,
 }
-#[graphql_interface] // implementing requires macro attribute too, (°ｏ°)!
-impl Character for Human {
-    fn id(&self) -> &str {
-        &self.id
-    }
-}
 
 #[derive(GraphQLObject)]
 #[graphql(impl = CharacterValue)]
 struct Droid {
     id: String,
 }
-#[graphql_interface]
-impl Character for Droid {
-    fn id(&self) -> &str {
-        &self.id
-    }
-}
-
-# fn main() {
-let human = Human { id: "human-32".to_owned() };
-// Values type for interface has `From` implementations for all its implementers,
-// so we don't need to bother with enum variant names.
-let character: CharacterValue = human.into();
-assert_eq!(character.id(), "human-32");
-# }
+#
+# fn main() {}
 ```
 
 Also, enum name can be specified explicitly, if desired.
@@ -90,71 +72,11 @@ struct Human {
     id: String,
     home_planet: String,
 }
-#[graphql_interface]
-impl Character for Human {
-    fn id(&self) -> &str {
-        &self.id
-    }
-}
 #
 # fn main() {}
 ```
 
 
-### Trait object values
-
-If, for some reason, we would like to use [trait objects][2] for representing [interface][1] values incorporating dynamic dispatch, then it should be specified explicitly in the trait definition.
-
-Downcasting [trait objects][2] in Rust is not that trivial, that's why macro transforms the trait definition slightly, imposing some additional type parameters under-the-hood.
-
-> __NOTICE__:  
-> A __trait has to be [object safe](https://doc.rust-lang.org/stable/reference/items/traits.html#object-safety)__, because schema resolvers will need to return a [trait object][2] to specify a [GraphQL interface][1] behind it.
-
-```rust
-# extern crate juniper;
-# extern crate tokio;
-use juniper::{graphql_interface, GraphQLObject};
-
-// `dyn` argument accepts the name of type alias for the required trait object,
-// and macro generates this alias automatically.
-#[graphql_interface(dyn = DynCharacter, for = Human)] 
-trait Character {
-    async fn id(&self) -> &str; // async fields are supported natively
-}
-
-#[derive(GraphQLObject)]
-#[graphql(impl = DynCharacter<__S>)] // macro adds `ScalarValue` type parameter to trait,
-struct Human {                       // so it may be specified explicitly when required 
-    id: String,
-}
-#[graphql_interface(dyn)] // implementing requires to know about dynamic dispatch too
-impl Character for Human {
-    async fn id(&self) -> &str {
-        &self.id
-    }
-}
-
-#[derive(GraphQLObject)]
-#[graphql(impl = DynCharacter<__S>)]
-struct Droid {
-    id: String,
-}
-#[graphql_interface]
-impl Character for Droid {
-    async fn id(&self) -> &str {
-        &self.id
-    }
-}
-
-# #[tokio::main]
-# async fn main() {
-let human = Human { id: "human-32".to_owned() };
-let character: Box<DynCharacter> = Box::new(human);
-assert_eq!(character.id().await, "human-32");
-# }
-``` 
-
-
 ### Ignoring trait methods
 
 We may want to omit some trait methods to be assumed as [GraphQL interface][1] fields and ignore them.
@@ -176,12 +98,6 @@ trait Character {
 struct Human {
     id: String,
 }
-#[graphql_interface]
-impl Character for Human {
-    fn id(&self) -> &str {
-        &self.id
-    }
-}
 #
 # fn main() {}
 ```
@@ -278,24 +194,6 @@ struct Human {
     id: String,
     name: String,
 }
-#[graphql_interface]
-impl Character for Human {
-    fn id(&self, db: &Database) -> Option<&str> {
-        if db.humans.contains_key(&self.id) {
-            Some(&self.id)
-        } else {
-            None
-        }
-    }
-
-    fn name(&self, db: &Database) -> Option<&str> {
-        if db.humans.contains_key(&self.id) {
-            Some(&self.name)
-        } else {
-            None
-        }
-    }
-}
 #
 # fn main() {}
 ```
@@ -309,119 +207,50 @@ This requires to explicitly parametrize over [`ScalarValue`][3], as [`Executor`]
 
 ```rust
 # extern crate juniper;
-use juniper::{graphql_interface, Executor, GraphQLObject, LookAheadMethods as _, ScalarValue};
+use juniper::{graphql_interface, graphql_object, Executor, LookAheadMethods as _, ScalarValue};
 
 #[graphql_interface(for = Human, Scalar = S)] // notice specifying `ScalarValue` as existing type parameter
 trait Character<S: ScalarValue> {             
     // If a field argument is named `executor`, it's automatically assumed
     // as an executor argument.
-    async fn id<'a>(&self, executor: &'a Executor<'_, '_, (), S>) -> &'a str
-    where
-        S: Send + Sync; // required by `#[async_trait]` transformation ¯\_(ツ)_/¯
+    fn id<'a>(&self, executor: &'a Executor<'_, '_, (), S>) -> &'a str;
 
     // Otherwise, you may mark it explicitly as an executor argument.
-    async fn name<'b>(
+    fn name<'b>(
         &'b self,
         #[graphql(executor)] another: &Executor<'_, '_, (), S>,
-    ) -> &'b str
-    where
-        S: Send + Sync;
+    ) -> &'b str;
+    
+    fn home_planet(&self) -> &str;
 }
 
-#[derive(GraphQLObject)]
-#[graphql(impl = CharacterValue<__S>)]
 struct Human {
     id: String,
     name: String,
+    home_planet: String,
 }
-#[graphql_interface(scalar = S)]
-impl<S: ScalarValue> Character<S> for Human {
-    async fn id<'a>(&self, executor: &'a Executor<'_, '_, (), S>) -> &'a str
+#[graphql_object(scalar = S: ScalarValue, impl = CharacterValue<S>)]
+impl Human {
+    async fn id<'a, S>(&self, executor: &'a Executor<'_, '_, (), S>) -> &'a str 
     where
-        S: Send + Sync,
+        S: ScalarValue,
     {
         executor.look_ahead().field_name()
     }
 
-    async fn name<'b>(&'b self, _: &Executor<'_, '_, (), S>) -> &'b str
-    where
-        S: Send + Sync,
-    {
+    async fn name<'b, S>(&'b self, #[graphql(executor)] _: &Executor<'_, '_, (), S>) -> &'b str {
         &self.name
     }
-}
-#
-# fn main() {}
-```
-
-
-### Downcasting
-
-By default, the [GraphQL interface][1] value is downcast to one of its implementer types via matching the enum variant or downcasting the trait object (if `dyn` macro argument is used).
-
-However, if some custom logic is needed to downcast a [GraphQL interface][1] implementer, you may specify either an external function or a trait method to do so.
-
-```rust
-# extern crate juniper;
-# use std::collections::HashMap;
-use juniper::{graphql_interface, GraphQLObject};
-
-struct Database {
-    droids: HashMap<String, Droid>,
-}
-impl juniper::Context for Database {}
-
-#[graphql_interface(for = [Human, Droid], context = Database)]
-#[graphql_interface(on Droid = get_droid)] // enables downcasting `Droid` via `get_droid()` function
-trait Character {
-    fn id(&self) -> &str;
-
-    #[graphql(downcast)] // makes method a downcast to `Human`, not a field 
-    // NOTICE: The method signature may optionally contain `&Database` context argument.
-    fn as_human(&self) -> Option<&Human> {
-        None
-    }
-}
-
-#[derive(GraphQLObject)]
-#[graphql(impl = CharacterValue, Context = Database)]
-struct Human {
-    id: String,
-}
-#[graphql_interface]
-impl Character for Human {
-    fn id(&self) -> &str {
-        &self.id
-    }
-
-    fn as_human(&self) -> Option<&Self> {
-        Some(self)
-    }   
-}
-
-#[derive(GraphQLObject)]
-#[graphql(impl = CharacterValue, Context = Database)]
-struct Droid {
-    id: String,
-}
-#[graphql_interface]
-impl Character for Droid {
-    fn id(&self) -> &str {
-        &self.id
+    
+    fn home_planet<'c, S>(&'c self, #[graphql(executor)] _: &Executor<'_, '_, (), S>) -> &'c str {
+        // Executor may not be present on the trait method  ^^^^^^^^^^^^^^^^^^^^^^^^
+        &self.home_planet
     }
 }
-
-// External downcast function doesn't have to be a method of a type.
-// It's only a matter of the function signature to match the requirements.
-fn get_droid<'db>(ch: &CharacterValue, db: &'db Database) -> Option<&'db Droid> {
-    db.droids.get(ch.id())
-}
 #
 # fn main() {}
 ```
 
-The attribute syntax `#[graphql_interface(on ImplementerType = resolver_fn)]` follows the [GraphQL syntax for downcasting interface implementer](https://spec.graphql.org/June2018/#example-5cc55).
-
 
 
 
@@ -445,12 +274,6 @@ struct Human {
     id: String,
     home_planet: String,
 }
-#[graphql_interface(scalar = DefaultScalarValue)]
-impl Character for Human {
-    fn id(&self) -> &str {
-        &self.id
-    }   
-}
 
 #[derive(GraphQLObject)]
 #[graphql(impl = CharacterValue, Scalar = DefaultScalarValue)]
@@ -458,12 +281,6 @@ struct Droid {
     id: String,
     primary_function: String,
 }
-#[graphql_interface(scalar = DefaultScalarValue)]
-impl Character for Droid {
-    fn id(&self) -> &str {
-        &self.id
-    }   
-}
 #
 # fn main() {}
 ```
diff --git a/examples/actix_subscriptions/src/main.rs b/examples/actix_subscriptions/src/main.rs
index 6da5ad562..d36c34d5a 100644
--- a/examples/actix_subscriptions/src/main.rs
+++ b/examples/actix_subscriptions/src/main.rs
@@ -10,7 +10,7 @@ use actix_web::{
 
 use juniper::{
     graphql_object, graphql_subscription, graphql_value,
-    tests::fixtures::starwars::schema::{Character as _, Database, Query},
+    tests::fixtures::starwars::schema::{Database, Query},
     EmptyMutation, FieldError, RootNode,
 };
 use juniper_actix::{graphql_handler, playground_handler, subscriptions::subscriptions_handler};
diff --git a/juniper/CHANGELOG.md b/juniper/CHANGELOG.md
index 836472fb0..411413396 100644
--- a/juniper/CHANGELOG.md
+++ b/juniper/CHANGELOG.md
@@ -10,6 +10,13 @@
 - Make `FromInputValue` methods fallible to allow post-validation. ([#987](https://github.com/graphql-rust/juniper/pull/987))
 - Change `Option` to `Result` in `from_input_value()` return type of `#[graphql_scalar]` macro. ([#987](https://github.com/graphql-rust/juniper/pull/987))
 - Forbid `__typename` field on `subscription` operations [accordingly to October 2021 spec](https://spec.graphql.org/October2021/#note-bc213). ([#1001](https://github.com/graphql-rust/juniper/pull/1001), [#1000](https://github.com/graphql-rust/juniper/pull/1000))
+- Redesign `#[graphql_interface]` macro: ([#1009](https://github.com/graphql-rust/juniper/pull/1009))
+  - Remove support for `#[graphql_interface(dyn)]` (interface values as trait objects).
+  - Remove support for `downcast` (custom resolution into implementer types).
+  - Remove support for `async` trait methods (not required anymore).
+  - Remove necessity of writing `impl Trait for Type` blocks (interfaces are implemented just by matching its fields).
+  - Forbid default impls on non-ignored trait methods.
+  - Support coercion of additional nullable arguments and return sub-typing on implementer.
 
 ## Features
 
diff --git a/juniper/src/ast.rs b/juniper/src/ast.rs
index f7805eeb3..ea2c0fec2 100644
--- a/juniper/src/ast.rs
+++ b/juniper/src/ast.rs
@@ -296,6 +296,7 @@ impl<S> InputValue<S> {
     }
 
     /// Resolve all variables to their values.
+    #[must_use]
     pub fn into_const(self, vars: &Variables<S>) -> Self
     where
         S: Clone,
diff --git a/juniper/src/executor/mod.rs b/juniper/src/executor/mod.rs
index 553af48ea..9b9de9497 100644
--- a/juniper/src/executor/mod.rs
+++ b/juniper/src/executor/mod.rs
@@ -701,7 +701,7 @@ where
             FieldPath::Root(_) => unreachable!(),
         };
         self.parent_selection_set
-            .map(|p| {
+            .and_then(|p| {
                 // Search the parent's fields to find this field within the set
                 let found_field = p.iter().find(|&x| {
                     match *x {
@@ -721,7 +721,6 @@ where
                     None
                 }
             })
-            .flatten()
             .unwrap_or_else(|| {
                 // We didn't find a field in the parent's selection matching
                 // this field, which means we're inside a FragmentSpread
diff --git a/juniper/src/executor/owned_executor.rs b/juniper/src/executor/owned_executor.rs
index 28b03c3a7..cce1a208e 100644
--- a/juniper/src/executor/owned_executor.rs
+++ b/juniper/src/executor/owned_executor.rs
@@ -49,6 +49,7 @@ where
     S: Clone,
 {
     #[doc(hidden)]
+    #[must_use]
     pub fn type_sub_executor(
         &self,
         type_name: Option<&str>,
@@ -76,6 +77,7 @@ where
     }
 
     #[doc(hidden)]
+    #[must_use]
     pub fn field_sub_executor(
         &self,
         field_alias: &'a str,
diff --git a/juniper/src/macros/mod.rs b/juniper/src/macros/mod.rs
index cecfee388..4bbcfb5f6 100644
--- a/juniper/src/macros/mod.rs
+++ b/juniper/src/macros/mod.rs
@@ -2,6 +2,9 @@
 
 #[doc(hidden)]
 pub mod helper;
+#[doc(hidden)]
+#[macro_use]
+pub mod reflect;
 
 #[macro_use]
 mod graphql_input_value;
diff --git a/juniper/src/macros/reflect.rs b/juniper/src/macros/reflect.rs
new file mode 100644
index 000000000..64b04c2c2
--- /dev/null
+++ b/juniper/src/macros/reflect.rs
@@ -0,0 +1,985 @@
+//! Compile-time reflection of Rust types into GraphQL types.
+
+use std::{rc::Rc, sync::Arc};
+
+use futures::future::BoxFuture;
+
+use crate::{
+    Arguments as FieldArguments, ExecutionResult, Executor, GraphQLValue, Nullable, ScalarValue,
+};
+
+/// Alias for a [GraphQL object][1], [scalar][2] or [interface][3] type's name
+/// in a GraphQL schema.
+///
+/// See [`BaseType`] for more info.
+///
+/// [1]: https://spec.graphql.org/October2021#sec-Objects
+/// [2]: https://spec.graphql.org/October2021#sec-Scalars
+/// [3]: https://spec.graphql.org/October2021#sec-Interfaces
+pub type Type = &'static str;
+
+/// Alias for a slice of [`Type`]s.
+///
+/// See [`BaseSubTypes`] for more info.
+pub type Types = &'static [Type];
+
+/// Naming of a [GraphQL object][1], [scalar][2] or [interface][3] [`Type`].
+///
+/// This trait is transparent to [`Option`], [`Vec`] and other containers, so to
+/// fully represent a [GraphQL object][1] we additionally use [`WrappedType`].
+///
+/// Different Rust types may have the same [`NAME`]. For example, [`String`] and
+/// `&`[`str`](prim@str) share `String!` GraphQL type.
+///
+/// [`NAME`]: Self::NAME
+/// [1]: https://spec.graphql.org/October2021#sec-Objects
+/// [2]: https://spec.graphql.org/October2021#sec-Scalars
+/// [3]: https://spec.graphql.org/October2021#sec-Interfaces
+pub trait BaseType<S> {
+    /// [`Type`] of the [GraphQL object][1], [scalar][2] or [interface][3].
+    ///
+    /// [1]: https://spec.graphql.org/October2021#sec-Objects
+    /// [2]: https://spec.graphql.org/October2021#sec-Scalars
+    /// [3]: https://spec.graphql.org/October2021#sec-Interfaces
+    const NAME: Type;
+}
+
+impl<'a, S, T: BaseType<S> + ?Sized> BaseType<S> for &'a T {
+    const NAME: Type = T::NAME;
+}
+
+impl<'ctx, S, T> BaseType<S> for (&'ctx T::Context, T)
+where
+    S: ScalarValue,
+    T: BaseType<S> + GraphQLValue<S>,
+{
+    const NAME: Type = T::NAME;
+}
+
+impl<S, T: BaseType<S>> BaseType<S> for Option<T> {
+    const NAME: Type = T::NAME;
+}
+
+impl<S, T: BaseType<S>> BaseType<S> for Nullable<T> {
+    const NAME: Type = T::NAME;
+}
+
+impl<S, T: BaseType<S>, E> BaseType<S> for Result<T, E> {
+    const NAME: Type = T::NAME;
+}
+
+impl<S, T: BaseType<S>> BaseType<S> for Vec<T> {
+    const NAME: Type = T::NAME;
+}
+
+impl<S, T: BaseType<S>> BaseType<S> for [T] {
+    const NAME: Type = T::NAME;
+}
+
+impl<S, T: BaseType<S>, const N: usize> BaseType<S> for [T; N] {
+    const NAME: Type = T::NAME;
+}
+
+impl<S, T: BaseType<S> + ?Sized> BaseType<S> for Box<T> {
+    const NAME: Type = T::NAME;
+}
+
+impl<S, T: BaseType<S> + ?Sized> BaseType<S> for Arc<T> {
+    const NAME: Type = T::NAME;
+}
+
+impl<S, T: BaseType<S> + ?Sized> BaseType<S> for Rc<T> {
+    const NAME: Type = T::NAME;
+}
+
+/// [Sub-types][2] of a [GraphQL object][1].
+///
+/// This trait is transparent to [`Option`], [`Vec`] and other containers.
+///
+/// [1]: https://spec.graphql.org/October2021#sec-Objects
+/// [2]: https://spec.graphql.org/October2021#sel-JAHZhCHCDEJDAAAEEFDBtzC
+pub trait BaseSubTypes<S> {
+    /// Sub-[`Types`] of the [GraphQL object][1].
+    ///
+    /// [1]: https://spec.graphql.org/October2021#sec-Objects
+    const NAMES: Types;
+}
+
+impl<'a, S, T: BaseSubTypes<S> + ?Sized> BaseSubTypes<S> for &'a T {
+    const NAMES: Types = T::NAMES;
+}
+
+impl<'ctx, S, T> BaseSubTypes<S> for (&'ctx T::Context, T)
+where
+    S: ScalarValue,
+    T: BaseSubTypes<S> + GraphQLValue<S>,
+{
+    const NAMES: Types = T::NAMES;
+}
+
+impl<S, T: BaseSubTypes<S>> BaseSubTypes<S> for Option<T> {
+    const NAMES: Types = T::NAMES;
+}
+
+impl<S, T: BaseSubTypes<S>> BaseSubTypes<S> for Nullable<T> {
+    const NAMES: Types = T::NAMES;
+}
+
+impl<S, T: BaseSubTypes<S>, E> BaseSubTypes<S> for Result<T, E> {
+    const NAMES: Types = T::NAMES;
+}
+
+impl<S, T: BaseSubTypes<S>> BaseSubTypes<S> for Vec<T> {
+    const NAMES: Types = T::NAMES;
+}
+
+impl<S, T: BaseSubTypes<S>> BaseSubTypes<S> for [T] {
+    const NAMES: Types = T::NAMES;
+}
+
+impl<S, T: BaseSubTypes<S>, const N: usize> BaseSubTypes<S> for [T; N] {
+    const NAMES: Types = T::NAMES;
+}
+
+impl<S, T: BaseSubTypes<S> + ?Sized> BaseSubTypes<S> for Box<T> {
+    const NAMES: Types = T::NAMES;
+}
+
+impl<S, T: BaseSubTypes<S> + ?Sized> BaseSubTypes<S> for Arc<T> {
+    const NAMES: Types = T::NAMES;
+}
+
+impl<S, T: BaseSubTypes<S> + ?Sized> BaseSubTypes<S> for Rc<T> {
+    const NAMES: Types = T::NAMES;
+}
+
+/// Alias for a value of a [`WrappedType`] (composed GraphQL type).
+pub type WrappedValue = u128;
+
+// TODO: Just use `&str`s once they're allowed in `const` generics.
+/// Encoding of a composed GraphQL type in numbers.
+///
+/// To fully represent a [GraphQL object][1] it's not enough to use [`Type`],
+/// because of the [wrapping types][2]. To work around this we use a
+/// [`WrappedValue`] which is represented via [`u128`] number in the following
+/// encoding:
+/// - In base case of non-nullable [object][1] [`VALUE`] is `1`.
+/// - To represent nullability we "append" `2` to the [`VALUE`], so
+///   [`Option`]`<`[object][1]`>` has [`VALUE`] of `12`.
+/// - To represent list we "append" `3` to the [`VALUE`], so
+///   [`Vec`]`<`[object][1]`>` has [`VALUE`] of `13`.
+///
+/// This approach allows us to uniquely represent any [GraphQL object][1] with a
+/// combination of [`Type`] and [`WrappedValue`] and even format it via
+/// [`format_type!`] macro in a `const` context.
+///
+/// # Examples
+///
+/// ```rust
+/// # use juniper::{
+/// #     format_type,
+/// #     macros::reflect::{WrappedType, BaseType, WrappedValue, Type},
+/// #     DefaultScalarValue,
+/// # };
+/// #
+/// assert_eq!(<Option<i32> as WrappedType<DefaultScalarValue>>::VALUE, 12);
+/// assert_eq!(<Vec<i32> as WrappedType<DefaultScalarValue>>::VALUE, 13);
+/// assert_eq!(<Vec<Option<i32>> as WrappedType<DefaultScalarValue>>::VALUE, 123);
+/// assert_eq!(<Option<Vec<i32>> as WrappedType<DefaultScalarValue>>::VALUE, 132);
+/// assert_eq!(<Option<Vec<Option<i32>>> as WrappedType<DefaultScalarValue>>::VALUE, 1232);
+///
+/// const TYPE_STRING: Type = <Option<Vec<Option<String>>> as BaseType<DefaultScalarValue>>::NAME;
+/// const WRAP_VAL_STRING: WrappedValue = <Option<Vec<Option<String>>> as WrappedType<DefaultScalarValue>>::VALUE;
+/// assert_eq!(format_type!(TYPE_STRING, WRAP_VAL_STRING), "[String]");
+///
+/// const TYPE_STR: Type = <Option<Vec<Option<&str>>> as BaseType<DefaultScalarValue>>::NAME;
+/// const WRAP_VAL_STR: WrappedValue = <Option<Vec<Option<&str>>> as WrappedType<DefaultScalarValue>>::VALUE;
+/// assert_eq!(format_type!(TYPE_STRING, WRAP_VAL_STRING), "[String]");
+/// ```
+///
+/// [`VALUE`]: Self::VALUE
+/// [1]: https://spec.graphql.org/October2021#sec-Objects
+/// [2]: https://spec.graphql.org/October2021#sec-Wrapping-Types
+pub trait WrappedType<S> {
+    /// [`WrappedValue`] of this type.
+    const VALUE: WrappedValue;
+}
+
+impl<'ctx, S, T: WrappedType<S>> WrappedType<S> for (&'ctx T::Context, T)
+where
+    S: ScalarValue,
+    T: GraphQLValue<S>,
+{
+    const VALUE: u128 = T::VALUE;
+}
+
+impl<S, T: WrappedType<S>> WrappedType<S> for Option<T> {
+    const VALUE: u128 = T::VALUE * 10 + 2;
+}
+
+impl<S, T: WrappedType<S>> WrappedType<S> for Nullable<T> {
+    const VALUE: u128 = T::VALUE * 10 + 2;
+}
+
+impl<S, T: WrappedType<S>, E> WrappedType<S> for Result<T, E> {
+    const VALUE: u128 = T::VALUE;
+}
+
+impl<S, T: WrappedType<S>> WrappedType<S> for Vec<T> {
+    const VALUE: u128 = T::VALUE * 10 + 3;
+}
+
+impl<S, T: WrappedType<S>> WrappedType<S> for [T] {
+    const VALUE: u128 = T::VALUE * 10 + 3;
+}
+
+impl<S, T: WrappedType<S>, const N: usize> WrappedType<S> for [T; N] {
+    const VALUE: u128 = T::VALUE * 10 + 3;
+}
+
+impl<'a, S, T: WrappedType<S> + ?Sized> WrappedType<S> for &'a T {
+    const VALUE: u128 = T::VALUE;
+}
+
+impl<S, T: WrappedType<S> + ?Sized> WrappedType<S> for Box<T> {
+    const VALUE: u128 = T::VALUE;
+}
+
+impl<S, T: WrappedType<S> + ?Sized> WrappedType<S> for Arc<T> {
+    const VALUE: u128 = T::VALUE;
+}
+
+impl<S, T: WrappedType<S> + ?Sized> WrappedType<S> for Rc<T> {
+    const VALUE: u128 = T::VALUE;
+}
+
+/// Alias for a [GraphQL object][1] or [interface][2] [field argument][3] name.
+///
+/// See [`Fields`] for more info.
+///
+/// [1]: https://spec.graphql.org/October2021#sec-Objects
+/// [2]: https://spec.graphql.org/October2021#sec-Interfaces
+/// [3]: https://spec.graphql.org/October2021#sec-Language.Arguments
+pub type Name = &'static str;
+
+/// Alias for a slice of [`Name`]s.
+///
+/// See [`Fields`] for more info.
+pub type Names = &'static [Name];
+
+/// Alias for [field argument][1]s [`Name`], [`Type`] and [`WrappedValue`].
+///
+/// [1]: https://spec.graphql.org/October2021#sec-Language.Arguments
+pub type Argument = (Name, Type, WrappedValue);
+
+/// Alias for a slice of [field argument][1]s [`Name`], [`Type`] and
+/// [`WrappedValue`].
+///
+/// [1]: https://spec.graphql.org/October2021#sec-Language.Arguments
+pub type Arguments = &'static [(Name, Type, WrappedValue)];
+
+/// Alias for a `const`-hashed [`Name`] used in a `const` context.
+pub type FieldName = u128;
+
+/// [GraphQL object][1] or [interface][2] [field arguments][3] [`Names`].
+///
+/// [1]: https://spec.graphql.org/October2021#sec-Objects
+/// [2]: https://spec.graphql.org/October2021#sec-Interfaces
+/// [3]: https://spec.graphql.org/October2021#sec-Language.Arguments
+pub trait Fields<S> {
+    /// [`Names`] of the [GraphQL object][1] or [interface][2]
+    /// [field arguments][3].
+    ///
+    /// [1]: https://spec.graphql.org/October2021#sec-Objects
+    /// [2]: https://spec.graphql.org/October2021#sec-Interfaces
+    /// [3]: https://spec.graphql.org/October2021#sec-Language.Arguments
+    const NAMES: Names;
+}
+
+/// [`Types`] of the [GraphQL interfaces][1] implemented by this type.
+///
+/// [1]: https://spec.graphql.org/October2021#sec-Interfaces
+pub trait Implements<S> {
+    /// [`Types`] of the [GraphQL interfaces][1] implemented by this type.
+    ///
+    /// [1]: https://spec.graphql.org/October2021#sec-Interfaces
+    const NAMES: Types;
+}
+
+/// Stores meta information of a [GraphQL field][1]:
+/// - [`Context`] and [`TypeInfo`].
+/// - Return type's [`TYPE`], [`SUB_TYPES`] and [`WRAPPED_VALUE`].
+/// - [`ARGUMENTS`].
+///
+/// [`ARGUMENTS`]: Self::ARGUMENTS
+/// [`Context`]: Self::Context
+/// [`SUB_TYPES`]: Self::SUB_TYPES
+/// [`TYPE`]: Self::TYPE
+/// [`TypeInfo`]: Self::TypeInfo
+/// [`WRAPPED_VALUE`]: Self::WRAPPED_VALUE
+/// [1]: https://spec.graphql.org/October2021#sec-Language.Fields
+pub trait FieldMeta<S, const N: FieldName> {
+    /// [`GraphQLValue::Context`] of this [field][1].
+    ///
+    /// [1]: https://spec.graphql.org/October2021#sec-Language.Fields
+    type Context;
+
+    /// [`GraphQLValue::TypeInfo`] of this [GraphQL field][1].
+    ///
+    /// [1]: https://spec.graphql.org/October2021#sec-Language.Fields
+    type TypeInfo;
+
+    /// [`Types`] of [GraphQL field's][1] return type.
+    ///
+    /// [1]: https://spec.graphql.org/October2021#sec-Language.Fields
+    const TYPE: Type;
+
+    /// [Sub-types][1] of [GraphQL field's][2] return type.
+    ///
+    /// [1]: BaseSubTypes
+    /// [2]: https://spec.graphql.org/October2021#sec-Language.Fields
+    const SUB_TYPES: Types;
+
+    /// [`WrappedValue`] of [GraphQL field's][1] return type.
+    ///
+    /// [1]: https://spec.graphql.org/October2021#sec-Language.Fields
+    const WRAPPED_VALUE: WrappedValue;
+
+    /// [GraphQL field's][1] [`Arguments`].
+    ///
+    /// [1]: https://spec.graphql.org/October2021#sec-Language.Fields
+    const ARGUMENTS: Arguments;
+}
+
+/// Synchronous field of a [GraphQL object][1] or [interface][2].
+///
+/// [1]: https://spec.graphql.org/October2021#sec-Objects
+/// [2]: https://spec.graphql.org/October2021#sec-Interfaces
+pub trait Field<S, const N: FieldName>: FieldMeta<S, N> {
+    /// Resolves the [`Value`] of this synchronous [`Field`].
+    ///
+    /// The `arguments` object contains all the specified arguments, with the
+    /// default values being substituted for the ones not provided by the query.
+    ///
+    /// The `executor` can be used to drive selections into sub-[objects][1].
+    ///
+    /// [`Value`]: crate::Value
+    /// [1]: https://spec.graphql.org/October2021#sec-Objects
+    fn call(
+        &self,
+        info: &Self::TypeInfo,
+        args: &FieldArguments<S>,
+        executor: &Executor<Self::Context, S>,
+    ) -> ExecutionResult<S>;
+}
+
+/// Asynchronous field of a GraphQL [object][1] or [interface][2].
+///
+/// [1]: https://spec.graphql.org/October2021#sec-Objects
+/// [2]: https://spec.graphql.org/October2021#sec-Interfaces
+pub trait AsyncField<S, const N: FieldName>: FieldMeta<S, N> {
+    /// Resolves the [`Value`] of this asynchronous [`AsyncField`].
+    ///
+    /// The `arguments` object contains all the specified arguments, with the
+    /// default values being substituted for the ones not provided by the query.
+    ///
+    /// The `executor` can be used to drive selections into sub-[objects][1].
+    ///
+    /// [1]: https://spec.graphql.org/October2021#sec-Objects
+    fn call<'b>(
+        &'b self,
+        info: &'b Self::TypeInfo,
+        args: &'b FieldArguments<S>,
+        executor: &'b Executor<Self::Context, S>,
+    ) -> BoxFuture<'b, ExecutionResult<S>>;
+}
+
+/// Non-cryptographic hash with good dispersion to use as a [`str`](prim@str) in
+/// `const` generics. See [spec] for more info.
+///
+/// [spec]: https://datatracker.ietf.org/doc/html/draft-eastlake-fnv-17.html
+#[must_use]
+pub const fn fnv1a128(str: Name) -> u128 {
+    const FNV_OFFSET_BASIS: u128 = 0x6c62272e07bb014262b821756295c58d;
+    const FNV_PRIME: u128 = 0x0000000001000000000000000000013b;
+
+    let bytes = str.as_bytes();
+    let mut hash = FNV_OFFSET_BASIS;
+    let mut i = 0;
+    while i < bytes.len() {
+        hash ^= bytes[i] as u128;
+        hash = hash.wrapping_mul(FNV_PRIME);
+        i += 1;
+    }
+    hash
+}
+
+/// Length __in bytes__ of the [`format_type!`] macro result.
+#[must_use]
+pub const fn type_len_with_wrapped_val(ty: Type, val: WrappedValue) -> usize {
+    let mut len = ty.as_bytes().len() + "!".as_bytes().len(); // Type!
+
+    let mut curr = val;
+    while curr % 10 != 0 {
+        match curr % 10 {
+            2 => len -= "!".as_bytes().len(),   // remove !
+            3 => len += "[]!".as_bytes().len(), // [Type]!
+            _ => {}
+        }
+        curr /= 10;
+    }
+
+    len
+}
+
+/// Checks whether the given GraphQL [object][1] represents a `subtype` of the
+/// given GraphQL `ty`pe, basing on the [`WrappedType`] encoding.
+///
+/// To fully determine the sub-typing relation the [`Type`] should be one of the
+/// [`BaseSubTypes::NAMES`].
+///
+/// [1]: https://spec.graphql.org/October2021#sec-Objects
+#[must_use]
+pub const fn can_be_subtype(ty: WrappedValue, subtype: WrappedValue) -> bool {
+    let ty_curr = ty % 10;
+    let sub_curr = subtype % 10;
+
+    if ty_curr == sub_curr {
+        if ty_curr == 1 {
+            true
+        } else {
+            can_be_subtype(ty / 10, subtype / 10)
+        }
+    } else if ty_curr == 2 {
+        can_be_subtype(ty / 10, subtype)
+    } else {
+        false
+    }
+}
+
+/// Checks whether the given `val` exists in the given `arr`.
+#[must_use]
+pub const fn str_exists_in_arr(val: &str, arr: &[&str]) -> bool {
+    let mut i = 0;
+    while i < arr.len() {
+        if str_eq(val, arr[i]) {
+            return true;
+        }
+        i += 1;
+    }
+    false
+}
+
+/// Compares strings in a `const` context.
+///
+/// As there is no `const impl Trait` and `l == r` calls [`Eq`], we have to
+/// write custom comparison function.
+///
+/// [`Eq`]: std::cmp::Eq
+// TODO: Remove once `Eq` trait is allowed in `const` context.
+pub const fn str_eq(l: &str, r: &str) -> bool {
+    let (l, r) = (l.as_bytes(), r.as_bytes());
+
+    if l.len() != r.len() {
+        return false;
+    }
+
+    let mut i = 0;
+    while i < l.len() {
+        if l[i] != r[i] {
+            return false;
+        }
+        i += 1;
+    }
+
+    true
+}
+
+/// Asserts that `#[graphql_interface(for = ...)]` has all the types referencing
+/// this interface in the `impl = ...` attribute argument.
+///
+/// Symmetrical to [`assert_interfaces_impls!`].
+#[macro_export]
+macro_rules! assert_implemented_for {
+    ($scalar: ty, $implementor: ty $(, $interfaces: ty)* $(,)?) => {
+        const _: () = {
+            $({
+                let is_present = $crate::macros::reflect::str_exists_in_arr(
+                    <$implementor as ::juniper::macros::reflect::BaseType<$scalar>>::NAME,
+                    <$interfaces as ::juniper::macros::reflect::BaseSubTypes<$scalar>>::NAMES,
+                );
+                if !is_present {
+                    const MSG: &str = $crate::const_concat!(
+                        "Failed to implement interface `",
+                        <$interfaces as $crate::macros::reflect::BaseType<$scalar>>::NAME,
+                        "` on `",
+                        <$implementor as $crate::macros::reflect::BaseType<$scalar>>::NAME,
+                        "`: missing implementer reference in interface's `for` attribute.",
+                    );
+                    ::std::panic!("{}", MSG);
+                }
+            })*
+        };
+    };
+}
+
+/// Asserts that `impl = ...` attribute argument has all the types referencing
+/// this GraphQL type in `#[graphql_interface(for = ...)]`.
+///
+/// Symmetrical to [`assert_implemented_for!`].
+#[macro_export]
+macro_rules! assert_interfaces_impls {
+    ($scalar: ty, $interface: ty $(, $implementers: ty)* $(,)?) => {
+        const _: () = {
+            $({
+                let is_present = $crate::macros::reflect::str_exists_in_arr(
+                    <$interface as ::juniper::macros::reflect::BaseType<$scalar>>::NAME,
+                    <$implementers as ::juniper::macros::reflect::Implements<$scalar>>::NAMES,
+                );
+                if !is_present {
+                    const MSG: &str = $crate::const_concat!(
+                        "Failed to implement interface `",
+                        <$interface as $crate::macros::reflect::BaseType<$scalar>>::NAME,
+                        "` on `",
+                        <$implementers as $crate::macros::reflect::BaseType<$scalar>>::NAME,
+                        "`: missing interface reference in implementer's `impl` attribute.",
+                    );
+                    ::std::panic!("{}", MSG);
+                }
+            })*
+        };
+    };
+}
+
+/// Asserts validness of [`Field`] [`Arguments`] and returned [`Type`].
+///
+/// This assertion is a combination of [`assert_subtype`] and
+/// [`assert_field_args`].
+///
+/// See [spec][1] for more info.
+///
+/// [1]: https://spec.graphql.org/October2021#IsValidImplementation()
+#[macro_export]
+macro_rules! assert_field {
+    (
+        $base_ty: ty,
+        $impl_ty: ty,
+        $scalar: ty,
+        $field_name: expr $(,)?
+    ) => {
+        $crate::assert_field_args!($base_ty, $impl_ty, $scalar, $field_name);
+        $crate::assert_subtype!($base_ty, $impl_ty, $scalar, $field_name);
+    };
+}
+
+/// Asserts validness of a [`Field`] return type.
+///
+/// See [spec][1] for more info.
+///
+/// [1]: https://spec.graphql.org/October2021#IsValidImplementationFieldType()
+#[macro_export]
+macro_rules! assert_subtype {
+    (
+        $base_ty: ty,
+        $impl_ty: ty,
+        $scalar: ty,
+        $field_name: expr $(,)?
+    ) => {
+        const _: () = {
+            const BASE_TY: $crate::macros::reflect::Type =
+                <$base_ty as $crate::macros::reflect::BaseType<$scalar>>::NAME;
+            const IMPL_TY: $crate::macros::reflect::Type =
+                <$impl_ty as $crate::macros::reflect::BaseType<$scalar>>::NAME;
+            const ERR_PREFIX: &str = $crate::const_concat!(
+                "Failed to implement interface `",
+                BASE_TY,
+                "` on `",
+                IMPL_TY,
+                "`: ",
+            );
+
+            const FIELD_NAME: $crate::macros::reflect::Name =
+                $field_name;
+
+            const BASE_RETURN_WRAPPED_VAL: $crate::macros::reflect::WrappedValue =
+                <$base_ty as $crate::macros::reflect::FieldMeta<
+                    $scalar,
+                    { $crate::checked_hash!(FIELD_NAME, $base_ty, $scalar, ERR_PREFIX) },
+                >>::WRAPPED_VALUE;
+            const IMPL_RETURN_WRAPPED_VAL: $crate::macros::reflect::WrappedValue =
+                <$impl_ty as $crate::macros::reflect::FieldMeta<
+                    $scalar,
+                    { $crate::checked_hash!(FIELD_NAME, $impl_ty, $scalar, ERR_PREFIX) },
+                >>::WRAPPED_VALUE;
+
+            const BASE_RETURN_TY: $crate::macros::reflect::Type =
+                <$base_ty as $crate::macros::reflect::FieldMeta<
+                    $scalar,
+                    { $crate::checked_hash!(FIELD_NAME, $base_ty, $scalar, ERR_PREFIX) },
+                >>::TYPE;
+            const IMPL_RETURN_TY: $crate::macros::reflect::Type =
+                <$impl_ty as $crate::macros::reflect::FieldMeta<
+                    $scalar,
+                    { $crate::checked_hash!(FIELD_NAME, $impl_ty, $scalar, ERR_PREFIX) },
+                >>::TYPE;
+
+            const BASE_RETURN_SUB_TYPES: $crate::macros::reflect::Types =
+                <$base_ty as $crate::macros::reflect::FieldMeta<
+                    $scalar,
+                    { $crate::checked_hash!(FIELD_NAME, $base_ty, $scalar, ERR_PREFIX) },
+                >>::SUB_TYPES;
+
+            let is_subtype = $crate::macros::reflect::str_exists_in_arr(IMPL_RETURN_TY, BASE_RETURN_SUB_TYPES)
+                && $crate::macros::reflect::can_be_subtype(BASE_RETURN_WRAPPED_VAL, IMPL_RETURN_WRAPPED_VAL);
+            if !is_subtype {
+                const MSG: &str = $crate::const_concat!(
+                    ERR_PREFIX,
+                    "Field `",
+                    FIELD_NAME,
+                    "`: implementor is expected to return a subtype of interface's return object: `",
+                    $crate::format_type!(IMPL_RETURN_TY, IMPL_RETURN_WRAPPED_VAL),
+                    "` is not a subtype of `",
+                    $crate::format_type!(BASE_RETURN_TY, BASE_RETURN_WRAPPED_VAL),
+                    "`.",
+                );
+                ::std::panic!("{}", MSG);
+            }
+        };
+    };
+}
+
+/// Asserts validness of the [`Field`]s arguments. See [spec][1] for more
+/// info.
+///
+/// [1]: https://spec.graphql.org/October2021#sel-IAHZhCHCDEEFAAADHD8Cxob
+#[macro_export]
+macro_rules! assert_field_args {
+    (
+        $base_ty: ty,
+        $impl_ty: ty,
+        $scalar: ty,
+        $field_name: expr $(,)?
+    ) => {
+        const _: () = {
+            const BASE_NAME: &str = <$base_ty as $crate::macros::reflect::BaseType<$scalar>>::NAME;
+            const IMPL_NAME: &str = <$impl_ty as $crate::macros::reflect::BaseType<$scalar>>::NAME;
+            const ERR_PREFIX: &str = $crate::const_concat!(
+                "Failed to implement interface `",
+                BASE_NAME,
+                "` on `",
+                IMPL_NAME,
+                "`: ",
+            );
+
+            const FIELD_NAME: &str = $field_name;
+
+            const BASE_ARGS: ::juniper::macros::reflect::Arguments =
+                <$base_ty as $crate::macros::reflect::FieldMeta<
+                    $scalar,
+                    { $crate::checked_hash!(FIELD_NAME, $base_ty, $scalar, ERR_PREFIX) },
+                >>::ARGUMENTS;
+            const IMPL_ARGS: ::juniper::macros::reflect::Arguments =
+                <$impl_ty as $crate::macros::reflect::FieldMeta<
+                    $scalar,
+                    { $crate::checked_hash!(FIELD_NAME, $impl_ty, $scalar, ERR_PREFIX) },
+                >>::ARGUMENTS;
+
+            struct Error {
+                cause: Cause,
+                base: ::juniper::macros::reflect::Argument,
+                implementation: ::juniper::macros::reflect::Argument,
+            }
+
+            enum Cause {
+                RequiredField,
+                AdditionalNonNullableField,
+                TypeMismatch,
+            }
+
+            const fn unwrap_error(v: ::std::result::Result<(), Error>) -> Error {
+                match v {
+                    // Unfortunately we can't use `unreachable!()` here, as this
+                    // branch will be executed either way.
+                    Ok(()) => Error {
+                        cause: Cause::RequiredField,
+                        base: ("unreachable", "unreachable", 1),
+                        implementation: ("unreachable", "unreachable", 1),
+                    },
+                    Err(err) => err,
+                }
+            }
+
+            const fn check() -> Result<(), Error> {
+                let mut base_i = 0;
+                while base_i < BASE_ARGS.len() {
+                    let (base_name, base_type, base_wrap_val) = BASE_ARGS[base_i];
+
+                    let mut impl_i = 0;
+                    let mut was_found = false;
+                    while impl_i < IMPL_ARGS.len() {
+                        let (impl_name, impl_type, impl_wrap_val) = IMPL_ARGS[impl_i];
+
+                        if $crate::macros::reflect::str_eq(base_name, impl_name) {
+                            if $crate::macros::reflect::str_eq(base_type, impl_type)
+                                && base_wrap_val == impl_wrap_val
+                            {
+                                was_found = true;
+                                break;
+                            } else {
+                                return Err(Error {
+                                    cause: Cause::TypeMismatch,
+                                    base: (base_name, base_type, base_wrap_val),
+                                    implementation: (impl_name, impl_type, impl_wrap_val),
+                                });
+                            }
+                        }
+
+                        impl_i += 1;
+                    }
+
+                    if !was_found {
+                        return Err(Error {
+                            cause: Cause::RequiredField,
+                            base: (base_name, base_type, base_wrap_val),
+                            implementation: (base_name, base_type, base_wrap_val),
+                        });
+                    }
+
+                    base_i += 1;
+                }
+
+                let mut impl_i = 0;
+                while impl_i < IMPL_ARGS.len() {
+                    let (impl_name, impl_type, impl_wrapped_val) = IMPL_ARGS[impl_i];
+                    impl_i += 1;
+
+                    if impl_wrapped_val % 10 == 2 {
+                        continue;
+                    }
+
+                    let mut base_i = 0;
+                    let mut was_found = false;
+                    while base_i < BASE_ARGS.len() {
+                        let (base_name, _, _) = BASE_ARGS[base_i];
+                        if $crate::macros::reflect::str_eq(base_name, impl_name) {
+                            was_found = true;
+                            break;
+                        }
+                        base_i += 1;
+                    }
+                    if !was_found {
+                        return Err(Error {
+                            cause: Cause::AdditionalNonNullableField,
+                            base: (impl_name, impl_type, impl_wrapped_val),
+                            implementation: (impl_name, impl_type, impl_wrapped_val),
+                        });
+                    }
+                }
+
+                Ok(())
+            }
+
+            const RES: ::std::result::Result<(), Error> = check();
+            if RES.is_err() {
+                const ERROR: Error = unwrap_error(RES);
+
+                const BASE_ARG_NAME: &str = ERROR.base.0;
+                const IMPL_ARG_NAME: &str = ERROR.implementation.0;
+
+                const BASE_TYPE_FORMATTED: &str = $crate::format_type!(ERROR.base.1, ERROR.base.2);
+                const IMPL_TYPE_FORMATTED: &str =
+                    $crate::format_type!(ERROR.implementation.1, ERROR.implementation.2);
+
+                const MSG: &str = match ERROR.cause {
+                    Cause::TypeMismatch => {
+                        $crate::const_concat!(
+                            "Argument `",
+                            BASE_ARG_NAME,
+                            "`: expected type `",
+                            BASE_TYPE_FORMATTED,
+                            "`, found: `",
+                            IMPL_TYPE_FORMATTED,
+                            "`.",
+                        )
+                    }
+                    Cause::RequiredField => {
+                        $crate::const_concat!(
+                            "Argument `",
+                            BASE_ARG_NAME,
+                            "` of type `",
+                            BASE_TYPE_FORMATTED,
+                            "` was expected, but not found."
+                        )
+                    }
+                    Cause::AdditionalNonNullableField => {
+                        $crate::const_concat!(
+                            "Argument `",
+                            IMPL_ARG_NAME,
+                            "` of type `",
+                            IMPL_TYPE_FORMATTED,
+                            "` isn't present on the interface and so has to be nullable."
+                        )
+                    }
+                };
+                const ERROR_MSG: &str =
+                    $crate::const_concat!(ERR_PREFIX, "Field `", FIELD_NAME, "`: ", MSG);
+                ::std::panic!("{}", ERROR_MSG);
+            }
+        };
+    };
+}
+
+/// Concatenates `const` [`str`](prim@str)s in a `const` context.
+#[macro_export]
+macro_rules! const_concat {
+    ($($s:expr),* $(,)?) => {{
+        const LEN: usize = 0 $(+ $s.as_bytes().len())*;
+        const CNT: usize = [$($s),*].len();
+        const fn concat(input: [&str; CNT]) -> [u8; LEN] {
+            let mut bytes = [0; LEN];
+            let (mut i, mut byte) = (0, 0);
+            while i < CNT {
+                let mut b = 0;
+                while b < input[i].len() {
+                    bytes[byte] = input[i].as_bytes()[b];
+                    byte += 1;
+                    b += 1;
+                }
+                i += 1;
+            }
+            bytes
+        }
+        const CON: [u8; LEN] = concat([$($s),*]);
+
+        // TODO: Use `str::from_utf8()` once it becomes `const`.
+        // SAFETY: This is safe, as we concatenate multiple UTF-8 strings one
+        //         after another byte-by-byte.
+        #[allow(unsafe_code)]
+        unsafe { ::std::str::from_utf8_unchecked(&CON) }
+    }};
+}
+
+/// Ensures that the given `$impl_ty` implements [`Field`] and returns a
+/// [`fnv1a128`] hash for it, otherwise panics with understandable message.
+#[macro_export]
+macro_rules! checked_hash {
+    ($field_name: expr, $impl_ty: ty, $scalar: ty $(, $prefix: expr)? $(,)?) => {{
+        let exists = $crate::macros::reflect::str_exists_in_arr(
+            $field_name,
+            <$impl_ty as $crate::macros::reflect::Fields<$scalar>>::NAMES,
+        );
+        if exists {
+            $crate::macros::reflect::fnv1a128(FIELD_NAME)
+        } else {
+            const MSG: &str = $crate::const_concat!(
+                $($prefix,)?
+                "Field `",
+                $field_name,
+                "` isn't implemented on `",
+                <$impl_ty as $crate::macros::reflect::BaseType<$scalar>>::NAME,
+                "`."
+            );
+            ::std::panic!("{}", MSG)
+        }
+    }};
+}
+
+/// Formats the given [`Type`] and [`WrappedValue`] into a readable GraphQL type
+/// name.
+///
+/// # Examples
+///
+/// ```rust
+/// # use juniper::format_type;
+/// #
+/// assert_eq!(format_type!("String", 123), "[String]!");
+/// assert_eq!(format_type!("🦀", 123), "[🦀]!");
+/// ```
+#[macro_export]
+macro_rules! format_type {
+    ($ty: expr, $wrapped_value: expr $(,)?) => {{
+        const TYPE: (
+            $crate::macros::reflect::Type,
+            $crate::macros::reflect::WrappedValue,
+        ) = ($ty, $wrapped_value);
+        const RES_LEN: usize = $crate::macros::reflect::type_len_with_wrapped_val(TYPE.0, TYPE.1);
+
+        const OPENING_BRACKET: &str = "[";
+        const CLOSING_BRACKET: &str = "]";
+        const BANG: &str = "!";
+
+        const fn format_type_arr() -> [u8; RES_LEN] {
+            let (ty, wrap_val) = TYPE;
+            let mut type_arr: [u8; RES_LEN] = [0; RES_LEN];
+
+            let mut current_start = 0;
+            let mut current_end = RES_LEN - 1;
+            let mut current_wrap_val = wrap_val;
+            let mut is_null = false;
+            while current_wrap_val % 10 != 0 {
+                match current_wrap_val % 10 {
+                    2 => is_null = true, // Skips writing `BANG` later.
+                    3 => {
+                        // Write `OPENING_BRACKET` at `current_start`.
+                        let mut i = 0;
+                        while i < OPENING_BRACKET.as_bytes().len() {
+                            type_arr[current_start + i] = OPENING_BRACKET.as_bytes()[i];
+                            i += 1;
+                        }
+                        current_start += i;
+                        if !is_null {
+                            // Write `BANG` at `current_end`.
+                            i = 0;
+                            while i < BANG.as_bytes().len() {
+                                type_arr[current_end - BANG.as_bytes().len() + i + 1] =
+                                    BANG.as_bytes()[i];
+                                i += 1;
+                            }
+                            current_end -= i;
+                        }
+                        // Write `CLOSING_BRACKET` at `current_end`.
+                        i = 0;
+                        while i < CLOSING_BRACKET.as_bytes().len() {
+                            type_arr[current_end - CLOSING_BRACKET.as_bytes().len() + i + 1] =
+                                CLOSING_BRACKET.as_bytes()[i];
+                            i += 1;
+                        }
+                        current_end -= i;
+                        is_null = false;
+                    }
+                    _ => {}
+                }
+
+                current_wrap_val /= 10;
+            }
+
+            // Writes `Type` at `current_start`.
+            let mut i = 0;
+            while i < ty.as_bytes().len() {
+                type_arr[current_start + i] = ty.as_bytes()[i];
+                i += 1;
+            }
+            i = 0;
+            if !is_null {
+                // Writes `BANG` at `current_end`.
+                while i < BANG.as_bytes().len() {
+                    type_arr[current_end - BANG.as_bytes().len() + i + 1] = BANG.as_bytes()[i];
+                    i += 1;
+                }
+            }
+
+            type_arr
+        }
+
+        const TYPE_ARR: [u8; RES_LEN] = format_type_arr();
+
+        // TODO: Use `str::from_utf8()` once it becomes `const`.
+        // SAFETY: This is safe, as we concatenate multiple UTF-8 strings one
+        //         after another byte-by-byte.
+        #[allow(unsafe_code)]
+        const TYPE_FORMATTED: &str =
+            unsafe { ::std::str::from_utf8_unchecked(TYPE_ARR.as_slice()) };
+
+        TYPE_FORMATTED
+    }};
+}
diff --git a/juniper/src/parser/value.rs b/juniper/src/parser/value.rs
index 8d66661c9..3ff6d5efc 100644
--- a/juniper/src/parser/value.rs
+++ b/juniper/src/parser/value.rs
@@ -112,9 +112,7 @@ where
                 ..
             },
             _,
-        ) => Ok(parser
-            .next_token()?
-            .map(|_| InputValue::enum_value(name.to_owned()))),
+        ) => Ok(parser.next_token()?.map(|_| InputValue::enum_value(name))),
         _ => Err(parser.next_token()?.map(ParseError::UnexpectedToken)),
     }
 }
diff --git a/juniper/src/schema/meta.rs b/juniper/src/schema/meta.rs
index d4f350842..ccd43a99a 100644
--- a/juniper/src/schema/meta.rs
+++ b/juniper/src/schema/meta.rs
@@ -447,6 +447,7 @@ impl<'a, S> ScalarMeta<'a, S> {
     /// Sets the `description` of this [`ScalarMeta`] type.
     ///
     /// Overwrites any previously set description.
+    #[must_use]
     pub fn description(mut self, description: &str) -> Self {
         self.description = Some(description.to_owned());
         self
@@ -457,6 +458,7 @@ impl<'a, S> ScalarMeta<'a, S> {
     /// Overwrites any previously set [specification URL][0].
     ///
     /// [0]: https://spec.graphql.org/October2021#sec--specifiedBy
+    #[must_use]
     pub fn specified_by_url(mut self, url: impl Into<Cow<'a, str>>) -> Self {
         self.specified_by_url = Some(url.into());
         self
@@ -515,6 +517,7 @@ impl<'a, S> ObjectMeta<'a, S> {
     /// Sets the `description` of this [`ObjectMeta`] type.
     ///
     /// Overwrites any previously set description.
+    #[must_use]
     pub fn description(mut self, description: &str) -> Self {
         self.description = Some(description.to_owned());
         self
@@ -523,6 +526,7 @@ impl<'a, S> ObjectMeta<'a, S> {
     /// Set the `interfaces` this [`ObjectMeta`] type implements.
     ///
     /// Overwrites any previously set list of interfaces.
+    #[must_use]
     pub fn interfaces(mut self, interfaces: &[Type<'a>]) -> Self {
         self.interface_names = interfaces
             .iter()
@@ -556,6 +560,7 @@ impl<'a, S> EnumMeta<'a, S> {
     /// Sets the `description` of this [`EnumMeta`] type.
     ///
     /// Overwrites any previously set description.
+    #[must_use]
     pub fn description(mut self, description: &str) -> Self {
         self.description = Some(description.to_owned());
         self
@@ -584,6 +589,7 @@ impl<'a, S> InterfaceMeta<'a, S> {
     /// Sets the `description` of this [`InterfaceMeta`] type.
     ///
     /// Overwrites any previously set description.
+    #[must_use]
     pub fn description(mut self, description: &str) -> Self {
         self.description = Some(description.to_owned());
         self
@@ -612,6 +618,7 @@ impl<'a> UnionMeta<'a> {
     /// Sets the `description` of this [`UnionMeta`] type.
     ///
     /// Overwrites any previously set description.
+    #[must_use]
     pub fn description(mut self, description: &str) -> Self {
         self.description = Some(description.to_owned());
         self
@@ -643,6 +650,7 @@ impl<'a, S> InputObjectMeta<'a, S> {
     /// Set the `description` of this [`InputObjectMeta`] type.
     ///
     /// Overwrites any previously set description.
+    #[must_use]
     pub fn description(mut self, description: &str) -> Self {
         self.description = Some(description.to_owned());
         self
@@ -658,6 +666,7 @@ impl<'a, S> Field<'a, S> {
     /// Set the `description` of this [`Field`].
     ///
     /// Overwrites any previously set description.
+    #[must_use]
     pub fn description(mut self, description: &str) -> Self {
         self.description = Some(description.to_owned());
         self
@@ -666,6 +675,7 @@ impl<'a, S> Field<'a, S> {
     /// Adds an `argument` to this [`Field`].
     ///
     /// Arguments are unordered and can't contain duplicates by name.
+    #[must_use]
     pub fn argument(mut self, argument: Argument<'a, S>) -> Self {
         match self.arguments {
             None => {
@@ -681,6 +691,7 @@ impl<'a, S> Field<'a, S> {
     /// Sets this [`Field`] as deprecated with an optional `reason`.
     ///
     /// Overwrites any previously set deprecation reason.
+    #[must_use]
     pub fn deprecated(mut self, reason: Option<&str>) -> Self {
         self.deprecation_status = DeprecationStatus::Deprecated(reason.map(ToOwned::to_owned));
         self
@@ -701,6 +712,7 @@ impl<'a, S> Argument<'a, S> {
     /// Sets the `description` of this [`Argument`].
     ///
     /// Overwrites any previously set description.
+    #[must_use]
     pub fn description(mut self, description: &str) -> Self {
         self.description = Some(description.to_owned());
         self
@@ -709,6 +721,7 @@ impl<'a, S> Argument<'a, S> {
     /// Set the default value of this [`Argument`].
     ///
     /// Overwrites any previously set default value.
+    #[must_use]
     pub fn default_value(mut self, val: InputValue<S>) -> Self {
         self.default_value = Some(val);
         self
@@ -728,6 +741,7 @@ impl EnumValue {
     /// Sets the `description` of this [`EnumValue`].
     ///
     /// Overwrites any previously set description.
+    #[must_use]
     pub fn description(mut self, description: &str) -> Self {
         self.description = Some(description.to_owned());
         self
@@ -736,6 +750,7 @@ impl EnumValue {
     /// Sets this [`EnumValue`] as deprecated with an optional `reason`.
     ///
     /// Overwrites any previously set deprecation reason.
+    #[must_use]
     pub fn deprecated(mut self, reason: Option<&str>) -> Self {
         self.deprecation_status = DeprecationStatus::Deprecated(reason.map(ToOwned::to_owned));
         self
diff --git a/juniper/src/types/nullable.rs b/juniper/src/types/nullable.rs
index 81341c597..7e8d01cc1 100644
--- a/juniper/src/types/nullable.rs
+++ b/juniper/src/types/nullable.rs
@@ -141,6 +141,7 @@ impl<T> Nullable<T> {
 
     /// Returns the nullable if it contains a value, otherwise returns `b`.
     #[inline]
+    #[must_use]
     pub fn or(self, b: Self) -> Self {
         match self {
             Self::Some(_) => self,
@@ -151,6 +152,7 @@ impl<T> Nullable<T> {
     /// Returns the nullable if it contains a value, otherwise calls `f` and
     /// returns the result.
     #[inline]
+    #[must_use]
     pub fn or_else<F: FnOnce() -> Nullable<T>>(self, f: F) -> Nullable<T> {
         match self {
             Self::Some(_) => self,
@@ -161,6 +163,7 @@ impl<T> Nullable<T> {
     /// Replaces the actual value in the nullable by the value given in parameter, returning the
     /// old value if present, leaving a `Some` in its place without deinitializing either one.
     #[inline]
+    #[must_use]
     pub fn replace(&mut self, value: T) -> Self {
         std::mem::replace(self, Self::Some(value))
     }
diff --git a/juniper/src/types/scalars.rs b/juniper/src/types/scalars.rs
index d55c6ac3d..72b559398 100644
--- a/juniper/src/types/scalars.rs
+++ b/juniper/src/types/scalars.rs
@@ -7,6 +7,7 @@ use serde::{Deserialize, Serialize};
 use crate::{
     ast::{InputValue, Selection, ToInputValue},
     executor::{ExecutionResult, Executor, Registry},
+    macros::reflect,
     parser::{LexerError, ParseError, ScalarToken, Token},
     schema::meta::MetaType,
     types::{
@@ -202,6 +203,18 @@ where
     })
 }
 
+impl<S> reflect::WrappedType<S> for str {
+    const VALUE: reflect::WrappedValue = 1;
+}
+
+impl<S> reflect::BaseType<S> for str {
+    const NAME: reflect::Type = "String";
+}
+
+impl<S> reflect::BaseSubTypes<S> for str {
+    const NAMES: reflect::Types = &[<Self as reflect::BaseType<S>>::NAME];
+}
+
 impl<S> GraphQLType<S> for str
 where
     S: ScalarValue,
diff --git a/juniper_codegen/Cargo.toml b/juniper_codegen/Cargo.toml
index acd1a33ee..746c54944 100644
--- a/juniper_codegen/Cargo.toml
+++ b/juniper_codegen/Cargo.toml
@@ -21,7 +21,7 @@ proc-macro = true
 proc-macro-error = "1.0.2"
 proc-macro2 = "1.0.1"
 quote = "1.0.3"
-syn = { version = "1.0.60", features = ["extra-traits", "full", "parsing"], default-features = false }
+syn = { version = "1.0.60", features = ["extra-traits", "full", "parsing", "visit", "visit-mut"], default-features = false }
 url = "2.0"
 
 [dev-dependencies]
diff --git a/juniper_codegen/src/common/field/mod.rs b/juniper_codegen/src/common/field/mod.rs
index 0437f97f3..bf299b458 100644
--- a/juniper_codegen/src/common/field/mod.rs
+++ b/juniper_codegen/src/common/field/mod.rs
@@ -16,7 +16,6 @@ use syn::{
 
 use crate::{
     common::{
-        gen,
         parse::{
             attr::{err, OptionExt as _},
             ParseBufferExt as _,
@@ -65,20 +64,6 @@ pub(crate) struct Attr {
     ///
     /// [1]: https://spec.graphql.org/June2018/#sec-Language.Fields
     pub(crate) ignore: Option<SpanContainer<syn::Ident>>,
-
-    /// Explicitly specified marker indicating that this trait method doesn't
-    /// represent a [GraphQL field][1], but is a downcasting function into the
-    /// [GraphQL object][2] implementer type returned by this trait method.
-    ///
-    /// Once this marker is specified, the [GraphQL object][2] implementer type
-    /// cannot be downcast via another trait method or external downcasting
-    /// function.
-    ///
-    /// Omit using this field if you're generating code for [GraphQL object][2].
-    ///
-    /// [1]: https://spec.graphql.org/June2018/#sec-Language.Fields
-    /// [2]: https://spec.graphql.org/June2018/#sec-Objects
-    pub(crate) downcast: Option<SpanContainer<syn::Ident>>,
 }
 
 impl Parse for Attr {
@@ -119,10 +104,6 @@ impl Parse for Attr {
                     .ignore
                     .replace(SpanContainer::new(ident.span(), None, ident.clone()))
                     .none_or_else(|_| err::dup_arg(&ident))?,
-                "downcast" => out
-                    .downcast
-                    .replace(SpanContainer::new(ident.span(), None, ident.clone()))
-                    .none_or_else(|_| err::dup_arg(&ident))?,
                 name => {
                     return Err(err::unknown_arg(&ident, name));
                 }
@@ -142,7 +123,6 @@ impl Attr {
             description: try_merge_opt!(description: self, another),
             deprecated: try_merge_opt!(deprecated: self, another),
             ignore: try_merge_opt!(ignore: self, another),
-            downcast: try_merge_opt!(downcast: self, another),
         })
     }
 
@@ -156,11 +136,7 @@ impl Attr {
             .try_fold(Self::default(), |prev, curr| prev.try_merge(curr?))?;
 
         if let Some(ignore) = &attr.ignore {
-            if attr.name.is_some()
-                || attr.description.is_some()
-                || attr.deprecated.is_some()
-                || attr.downcast.is_some()
-            {
+            if attr.name.is_some() || attr.description.is_some() || attr.deprecated.is_some() {
                 return Err(syn::Error::new(
                     ignore.span(),
                     "`ignore` attribute argument is not composable with any other arguments",
@@ -168,19 +144,6 @@ impl Attr {
             }
         }
 
-        if let Some(downcast) = &attr.downcast {
-            if attr.name.is_some()
-                || attr.description.is_some()
-                || attr.deprecated.is_some()
-                || attr.ignore.is_some()
-            {
-                return Err(syn::Error::new(
-                    downcast.span(),
-                    "`downcast` attribute argument is not composable with any other arguments",
-                ));
-            }
-        }
-
         if attr.description.is_none() {
             attr.description = get_doc_comment(attrs).map(|sc| {
                 let span = sc.span_ident();
@@ -288,28 +251,6 @@ impl Definition {
         }
     }
 
-    /// Returns generated code that errors about [GraphQL fields][1] tried to be
-    /// resolved asynchronously in the [`GraphQLValue::resolve_field`] method
-    /// (which is synchronous itself).
-    ///
-    /// [`GraphQLValue::resolve_field`]: juniper::GraphQLValue::resolve_field
-    /// [1]: https://spec.graphql.org/June2018/#sec-Language.Fields
-    #[must_use]
-    pub(crate) fn method_resolve_field_err_async_field_tokens(
-        field_names: &[&str],
-        scalar: &scalar::Type,
-        ty_name: &str,
-    ) -> TokenStream {
-        quote! {
-            #( #field_names )|* => return Err(::juniper::FieldError::from(format!(
-                "Tried to resolve async field `{}` on type `{}` with a sync resolver",
-                field,
-                <Self as ::juniper::GraphQLType<#scalar>>::name(info)
-                    .ok_or_else(|| ::juniper::macros::helper::err_unnamed_type(#ty_name))?,
-            ))),
-        }
-    }
-
     /// Returns generated code for the [`marker::IsOutputType::mark`] method,
     /// which performs static checks for this [GraphQL field][1].
     ///
@@ -390,106 +331,6 @@ impl Definition {
         }
     }
 
-    /// Returns generated code for the [`GraphQLValue::resolve_field`][0]
-    /// method, which resolves this [GraphQL field][1] synchronously.
-    ///
-    /// Returns [`None`] if this [`Definition::is_async`].
-    ///
-    /// [0]: juniper::GraphQLValue::resolve_field
-    /// [1]: https://spec.graphql.org/June2018/#sec-Language.Fields
-    #[must_use]
-    pub(crate) fn method_resolve_field_tokens(
-        &self,
-        scalar: &scalar::Type,
-        trait_ty: Option<&syn::Type>,
-    ) -> Option<TokenStream> {
-        if self.is_async {
-            return None;
-        }
-
-        let (name, mut ty, ident) = (&self.name, self.ty.clone(), &self.ident);
-
-        let res = if self.is_method() {
-            let args = self
-                .arguments
-                .as_ref()
-                .unwrap()
-                .iter()
-                .map(|arg| arg.method_resolve_field_tokens(scalar, false));
-
-            let rcv = self.has_receiver.then(|| {
-                quote! { self, }
-            });
-
-            if trait_ty.is_some() {
-                quote! { <Self as #trait_ty>::#ident(#rcv #( #args ),*) }
-            } else {
-                quote! { Self::#ident(#rcv #( #args ),*) }
-            }
-        } else {
-            ty = parse_quote! { _ };
-            quote! { &self.#ident }
-        };
-
-        let resolving_code = gen::sync_resolving_code();
-
-        Some(quote! {
-            #name => {
-                let res: #ty = #res;
-                #resolving_code
-            }
-        })
-    }
-
-    /// Returns generated code for the
-    /// [`GraphQLValueAsync::resolve_field_async`][0] method, which resolves
-    /// this [GraphQL field][1] asynchronously.
-    ///
-    /// [0]: juniper::GraphQLValueAsync::resolve_field_async
-    /// [1]: https://spec.graphql.org/June2018/#sec-Language.Fields
-    #[must_use]
-    pub(crate) fn method_resolve_field_async_tokens(
-        &self,
-        scalar: &scalar::Type,
-        trait_ty: Option<&syn::Type>,
-    ) -> TokenStream {
-        let (name, mut ty, ident) = (&self.name, self.ty.clone(), &self.ident);
-
-        let mut fut = if self.is_method() {
-            let args = self
-                .arguments
-                .as_ref()
-                .unwrap()
-                .iter()
-                .map(|arg| arg.method_resolve_field_tokens(scalar, true));
-
-            let rcv = self.has_receiver.then(|| {
-                quote! { self, }
-            });
-
-            if trait_ty.is_some() {
-                quote! { <Self as #trait_ty>::#ident(#rcv #( #args ),*) }
-            } else {
-                quote! { Self::#ident(#rcv #( #args ),*) }
-            }
-        } else {
-            ty = parse_quote! { _ };
-            quote! { &self.#ident }
-        };
-        if !self.is_async {
-            fut = quote! { ::juniper::futures::future::ready(#fut) };
-        }
-
-        let resolving_code = gen::async_resolving_code(Some(&ty));
-
-        quote! {
-            #name => {
-                let fut = #fut;
-                #resolving_code
-            }
-        }
-    }
-
     /// Returns generated code for the
     /// [`GraphQLSubscriptionValue::resolve_field_into_stream`][0] method, which
     /// resolves this [GraphQL field][1] as [subscription][2].
diff --git a/juniper_codegen/src/common/parse/mod.rs b/juniper_codegen/src/common/parse/mod.rs
index cae19a015..5f541eaa3 100644
--- a/juniper_codegen/src/common/parse/mod.rs
+++ b/juniper_codegen/src/common/parse/mod.rs
@@ -11,12 +11,14 @@ use std::{
 };
 
 use proc_macro2::Span;
+use quote::quote;
 use syn::{
     ext::IdentExt as _,
     parse::{Parse, ParseBuffer},
     parse_quote,
     punctuated::Punctuated,
     token::{self, Token},
+    visit_mut::VisitMut,
 };
 
 /// Extension of [`ParseBuffer`] providing common function widely used by this crate for parsing.
@@ -250,6 +252,10 @@ pub(crate) trait GenericsExt {
 
     /// Moves all trait and lifetime bounds of these [`syn::Generics`] to its [`syn::WhereClause`].
     fn move_bounds_to_where_clause(&mut self);
+
+    /// Replaces generic parameters in the given [`syn::Type`] with default
+    /// ones, provided by these [`syn::Generics`].
+    fn replace_type_with_defaults(&self, ty: &mut syn::Type);
 }
 
 impl GenericsExt for syn::Generics {
@@ -299,4 +305,42 @@ impl GenericsExt for syn::Generics {
             }
         }
     }
+
+    fn replace_type_with_defaults(&self, ty: &mut syn::Type) {
+        struct Replace<'a>(&'a syn::Generics);
+
+        impl<'a> VisitMut for Replace<'a> {
+            fn visit_generic_argument_mut(&mut self, arg: &mut syn::GenericArgument) {
+                match arg {
+                    syn::GenericArgument::Lifetime(lf) => {
+                        *lf = parse_quote! { 'static };
+                    }
+                    syn::GenericArgument::Type(ty) => {
+                        let is_generic = self
+                            .0
+                            .params
+                            .iter()
+                            .filter_map(|par| match par {
+                                syn::GenericParam::Type(ty) => Some(&ty.ident),
+                                _ => None,
+                            })
+                            .any(|par| {
+                                let par = quote! { #par }.to_string();
+                                let ty = quote! { #ty }.to_string();
+                                par == ty
+                            });
+
+                        if is_generic {
+                            // Replace with `DefaultScalarValue` instead of `()`
+                            // because generic parameter may be scalar.
+                            *ty = parse_quote!(::juniper::DefaultScalarValue);
+                        }
+                    }
+                    _ => {}
+                }
+            }
+        }
+
+        Replace(self).visit_type_mut(ty)
+    }
 }
diff --git a/juniper_codegen/src/common/scalar.rs b/juniper_codegen/src/common/scalar.rs
index 1569a5391..8f11833de 100644
--- a/juniper_codegen/src/common/scalar.rs
+++ b/juniper_codegen/src/common/scalar.rs
@@ -90,12 +90,6 @@ impl Type {
         matches!(self, Self::ExplicitGeneric(_) | Self::ImplicitGeneric(_))
     }
 
-    /// Indicates whether this [`Type`] is [`Type::ExplicitGeneric`].
-    #[must_use]
-    pub(crate) fn is_explicit_generic(&self) -> bool {
-        matches!(self, Self::ExplicitGeneric(_))
-    }
-
     /// Indicates whether this [`Type`] is [`Type::ImplicitGeneric`].
     #[must_use]
     pub(crate) fn is_implicit_generic(&self) -> bool {
@@ -123,16 +117,6 @@ impl Type {
         }
     }
 
-    /// Returns a type parameter identifier that suits this [`Type`].
-    #[must_use]
-    pub(crate) fn generic_ty(&self) -> syn::Type {
-        match self {
-            Self::ExplicitGeneric(ty_param) => parse_quote! { #ty_param },
-            Self::ImplicitGeneric(Some(pred)) => pred.bounded_ty.clone(),
-            Self::ImplicitGeneric(None) | Self::Concrete(_) => parse_quote! { __S },
-        }
-    }
-
     /// Returns a default [`ScalarValue`] type that is compatible with this [`Type`].
     ///
     /// [`ScalarValue`]: juniper::ScalarValue
diff --git a/juniper_codegen/src/derive_scalar_value.rs b/juniper_codegen/src/derive_scalar_value.rs
index 3e7582247..3888d757b 100644
--- a/juniper_codegen/src/derive_scalar_value.rs
+++ b/juniper_codegen/src/derive_scalar_value.rs
@@ -237,6 +237,25 @@ fn impl_scalar_struct(
         impl#impl_generics ::juniper::marker::IsInputType<#scalar> for #ident
             where #scalar: ::juniper::ScalarValue,
         { }
+
+        impl#impl_generics ::juniper::macros::reflect::BaseType<#scalar> for #ident
+            where #scalar: ::juniper::ScalarValue,
+        {
+            const NAME: ::juniper::macros::reflect::Type = #name;
+        }
+
+        impl#impl_generics ::juniper::macros::reflect::BaseSubTypes<#scalar> for #ident
+            where #scalar: ::juniper::ScalarValue,
+        {
+            const NAMES: ::juniper::macros::reflect::Types =
+                &[<Self as ::juniper::macros::reflect::BaseType<#scalar>>::NAME];
+        }
+
+        impl#impl_generics ::juniper::macros::reflect::WrappedType<#scalar> for #ident
+            where #scalar: ::juniper::ScalarValue,
+        {
+            const VALUE: ::juniper::macros::reflect::WrappedValue = 1;
+        }
     );
 
     Ok(content)
diff --git a/juniper_codegen/src/graphql_interface/attr.rs b/juniper_codegen/src/graphql_interface/attr.rs
index 2aedbb2ad..88f8b0d61 100644
--- a/juniper_codegen/src/graphql_interface/attr.rs
+++ b/juniper_codegen/src/graphql_interface/attr.rs
@@ -3,7 +3,7 @@
 use std::mem;
 
 use proc_macro2::{Span, TokenStream};
-use quote::{quote, ToTokens as _};
+use quote::{format_ident, quote};
 use syn::{ext::IdentExt as _, parse_quote, spanned::Spanned};
 
 use crate::{
@@ -16,32 +16,22 @@ use crate::{
     util::{path_eq_single, span_container::SpanContainer, RenameRule},
 };
 
-use super::{
-    inject_async_trait, Definition, EnumType, ImplAttr, Implementer, ImplementerDowncast,
-    TraitAttr, TraitObjectType, Type,
-};
+use super::{Definition, TraitAttr};
 
 /// [`GraphQLScope`] of errors for `#[graphql_interface]` macro.
 const ERR: GraphQLScope = GraphQLScope::InterfaceAttr;
 
 /// Expands `#[graphql_interface]` macro into generated code.
 pub fn expand(attr_args: TokenStream, body: TokenStream) -> syn::Result<TokenStream> {
-    if let Ok(mut ast) = syn::parse2::<syn::ItemTrait>(body.clone()) {
+    if let Ok(mut ast) = syn::parse2::<syn::ItemTrait>(body) {
         let trait_attrs = parse::attr::unite(("graphql_interface", &attr_args), &ast.attrs);
         ast.attrs = parse::attr::strip("graphql_interface", ast.attrs);
         return expand_on_trait(trait_attrs, ast);
-    } else if let Ok(mut ast) = syn::parse2::<syn::ItemImpl>(body) {
-        if ast.trait_.is_some() {
-            let impl_attrs = parse::attr::unite(("graphql_interface", &attr_args), &ast.attrs);
-            ast.attrs = parse::attr::strip("graphql_interface", ast.attrs);
-            return expand_on_impl(impl_attrs, ast);
-        }
     }
 
     Err(syn::Error::new(
         Span::call_site(),
-        "#[graphql_interface] attribute is applicable to trait definitions and trait \
-         implementations only",
+        "#[graphql_interface] attribute is applicable to trait definitions only",
     ))
 }
 
@@ -71,27 +61,6 @@ fn expand_on_trait(
 
     let scalar = scalar::Type::parse(attr.scalar.as_deref(), &ast.generics);
 
-    let mut implementers: Vec<_> = attr
-        .implementers
-        .iter()
-        .map(|ty| Implementer {
-            ty: ty.as_ref().clone(),
-            downcast: None,
-            context: None,
-            scalar: scalar.clone(),
-        })
-        .collect();
-    for (ty, downcast) in &attr.external_downcasts {
-        match implementers.iter_mut().find(|i| &i.ty == ty) {
-            Some(impler) => {
-                impler.downcast = Some(ImplementerDowncast::External {
-                    path: downcast.inner().clone(),
-                });
-            }
-            None => err_only_implementer_downcast(&downcast.span_joined()),
-        }
-    }
-
     proc_macro_error::abort_if_dirty();
 
     let renaming = attr
@@ -103,22 +72,8 @@ fn expand_on_trait(
     let mut fields = vec![];
     for item in &mut ast.items {
         if let syn::TraitItem::Method(m) = item {
-            match TraitMethod::parse(m, &renaming) {
-                Some(TraitMethod::Field(f)) => fields.push(f),
-                Some(TraitMethod::Downcast(d)) => {
-                    match implementers.iter_mut().find(|i| i.ty == d.ty) {
-                        Some(impler) => {
-                            if let Some(external) = &impler.downcast {
-                                err_duplicate_downcast(m, external, &impler.ty);
-                            } else {
-                                impler.downcast = d.downcast;
-                                impler.context = d.context;
-                            }
-                        }
-                        None => err_only_implementer_downcast(&m.sig),
-                    }
-                }
-                _ => {}
+            if let Some(f) = parse_field(m, &renaming) {
+                fields.push(f)
             }
         }
     }
@@ -147,336 +102,149 @@ fn expand_on_trait(
                 })
             })
         })
-        .or_else(|| {
-            implementers
-                .iter()
-                .find_map(|impler| impler.context.as_ref())
-                .cloned()
-        })
         .unwrap_or_else(|| parse_quote! { () });
 
-    let is_trait_object = attr.r#dyn.is_some();
-
-    let is_async_trait = attr.asyncness.is_some()
-        || ast
-            .items
-            .iter()
-            .find_map(|item| match item {
-                syn::TraitItem::Method(m) => m.sig.asyncness,
-                _ => None,
-            })
-            .is_some();
-    let has_default_async_methods = ast.items.iter().any(|item| match item {
-        syn::TraitItem::Method(m) => m.sig.asyncness.and(m.default.as_ref()).is_some(),
-        _ => false,
-    });
-
-    let ty = if is_trait_object {
-        Type::TraitObject(Box::new(TraitObjectType::new(
-            &ast,
-            &attr,
-            scalar.clone(),
-            context.clone(),
-        )))
-    } else {
-        Type::Enum(Box::new(EnumType::new(
-            &ast,
-            &attr,
-            &implementers,
-            scalar.clone(),
-        )))
-    };
+    let enum_alias_ident = attr
+        .r#enum
+        .as_deref()
+        .cloned()
+        .unwrap_or_else(|| format_ident!("{}Value", trait_ident.to_string()));
+    let enum_ident = attr.r#enum.as_ref().map_or_else(
+        || format_ident!("{}ValueEnum", trait_ident.to_string()),
+        |c| format_ident!("{}Enum", c.inner().to_string()),
+    );
 
     let generated_code = Definition {
-        ty,
-
+        trait_generics: ast.generics.clone(),
+        vis: ast.vis.clone(),
+        enum_ident,
+        enum_alias_ident,
         name,
-        description: attr.description.map(SpanContainer::into_inner),
-
+        description: attr.description.as_deref().cloned(),
         context,
-        scalar: scalar.clone(),
-
+        scalar,
         fields,
-        implementers,
+        implementers: attr
+            .implementers
+            .iter()
+            .map(|c| c.inner().clone())
+            .collect(),
     };
 
-    // Attach the `juniper::AsDynGraphQLValue` on top of the trait if dynamic dispatch is used.
-    if is_trait_object {
-        ast.attrs.push(parse_quote! {
-            #[allow(unused_qualifications, clippy::type_repetition_in_bounds)]
-        });
-
-        let scalar_ty = scalar.generic_ty();
-        if !scalar.is_explicit_generic() {
-            let default_ty = scalar.default_ty();
-            ast.generics
-                .params
-                .push(parse_quote! { #scalar_ty = #default_ty });
-        }
-        ast.generics
-            .make_where_clause()
-            .predicates
-            .push(parse_quote! { #scalar_ty: ::juniper::ScalarValue });
-        ast.supertraits
-            .push(parse_quote! { ::juniper::AsDynGraphQLValue<#scalar_ty> });
-    }
-
-    if is_async_trait {
-        if has_default_async_methods {
-            // Hack for object safety. See details: https://docs.rs/async-trait/#dyn-traits
-            ast.supertraits.push(parse_quote! { Sync });
-        }
-        inject_async_trait(
-            &mut ast.attrs,
-            ast.items.iter_mut().filter_map(|i| {
-                if let syn::TraitItem::Method(m) = i {
-                    Some(&mut m.sig)
-                } else {
-                    None
-                }
-            }),
-            &ast.generics,
-        );
-    }
-
     Ok(quote! {
         #ast
         #generated_code
     })
 }
 
-/// Expands `#[graphql_interface]` macro placed on a trait implementation block.
-fn expand_on_impl(attrs: Vec<syn::Attribute>, mut ast: syn::ItemImpl) -> syn::Result<TokenStream> {
-    let attr = ImplAttr::from_attrs("graphql_interface", &attrs)?;
-
-    let is_async_trait = attr.asyncness.is_some()
-        || ast
-            .items
-            .iter()
-            .find_map(|item| match item {
-                syn::ImplItem::Method(m) => m.sig.asyncness,
-                _ => None,
-            })
-            .is_some();
-
-    let is_trait_object = attr.r#dyn.is_some();
-
-    if is_trait_object {
-        let scalar = scalar::Type::parse(attr.scalar.as_deref(), &ast.generics);
+/// Parses a [`field::Definition`] from the given trait method definition.
+///
+/// Returns [`None`] if parsing fails, or the method field is ignored.
+#[must_use]
+fn parse_field(
+    method: &mut syn::TraitItemMethod,
+    renaming: &RenameRule,
+) -> Option<field::Definition> {
+    let method_ident = &method.sig.ident;
+    let method_attrs = method.attrs.clone();
+
+    // Remove repeated attributes from the method, to omit incorrect expansion.
+    method.attrs = mem::take(&mut method.attrs)
+        .into_iter()
+        .filter(|attr| !path_eq_single(&attr.path, "graphql"))
+        .collect();
 
-        ast.attrs.push(parse_quote! {
-            #[allow(unused_qualifications, clippy::type_repetition_in_bounds)]
-        });
+    let attr = field::Attr::from_attrs("graphql", &method_attrs)
+        .map_err(|e| proc_macro_error::emit_error!(e))
+        .ok()?;
 
-        if scalar.is_implicit_generic() {
-            ast.generics.params.push(parse_quote! { #scalar });
-        }
-        if scalar.is_generic() {
-            ast.generics
-                .make_where_clause()
-                .predicates
-                .push(parse_quote! { #scalar: ::juniper::ScalarValue + Send + Sync });
-        }
-
-        if !scalar.is_explicit_generic() {
-            let (_, trait_path, _) = ast.trait_.as_mut().unwrap();
-            let trait_params = &mut trait_path.segments.last_mut().unwrap().arguments;
-            if let syn::PathArguments::None = trait_params {
-                *trait_params = syn::PathArguments::AngleBracketed(parse_quote! { <> });
-            }
-            if let syn::PathArguments::AngleBracketed(a) = trait_params {
-                a.args.push(parse_quote! { #scalar });
-            }
-        }
+    if attr.ignore.is_some() {
+        return None;
     }
 
-    if is_async_trait {
-        inject_async_trait(
-            &mut ast.attrs,
-            ast.items.iter_mut().filter_map(|i| {
-                if let syn::ImplItem::Method(m) = i {
-                    Some(&mut m.sig)
-                } else {
-                    None
-                }
-            }),
-            &ast.generics,
-        );
+    if method.default.is_some() {
+        return err_default_impl_block(&method.default);
     }
 
-    Ok(quote! { #ast })
-}
-
-/// Representation of parsed Rust trait method for `#[graphql_interface]` macro code generation.
-enum TraitMethod {
-    /// Method represents a [`Field`] of [GraphQL interface][1].
-    ///
-    /// [1]: https://spec.graphql.org/June2018/#sec-Interfaces
-    Field(field::Definition),
-
-    /// Method represents a custom downcasting function into the [`Implementer`] of
-    /// [GraphQL interface][1].
-    ///
-    /// [1]: https://spec.graphql.org/June2018/#sec-Interfaces
-    Downcast(Box<Implementer>),
-}
-
-impl TraitMethod {
-    /// Parses this [`TraitMethod`] from the given trait method definition.
-    ///
-    /// Returns [`None`] if the trait method marked with `#[graphql(ignore)]` attribute,
-    /// or parsing fails.
-    #[must_use]
-    fn parse(method: &mut syn::TraitItemMethod, renaming: &RenameRule) -> Option<Self> {
-        let method_attrs = method.attrs.clone();
-
-        // Remove repeated attributes from the method, to omit incorrect expansion.
-        method.attrs = mem::take(&mut method.attrs)
-            .into_iter()
-            .filter(|attr| !path_eq_single(&attr.path, "graphql"))
-            .collect();
-
-        let attr = field::Attr::from_attrs("graphql", &method_attrs)
-            .map_err(|e| proc_macro_error::emit_error!(e))
-            .ok()?;
-
-        if attr.ignore.is_some() {
-            return None;
-        }
-
-        if attr.downcast.is_some() {
-            return Some(Self::Downcast(Box::new(Self::parse_downcast(method)?)));
-        }
-
-        Some(Self::Field(Self::parse_field(method, attr, renaming)?))
-    }
-
-    /// Parses [`TraitMethod::Downcast`] from the given trait method definition.
-    ///
-    /// Returns [`None`] if parsing fails.
-    #[must_use]
-    fn parse_downcast(method: &mut syn::TraitItemMethod) -> Option<Implementer> {
-        let method_ident = &method.sig.ident;
-
-        let ty = parse::downcaster::output_type(&method.sig.output)
-            .map_err(|span| {
-                ERR.emit_custom(
-                    span,
-                    "expects trait method return type to be `Option<&ImplementerType>` only",
-                )
-            })
-            .ok()?;
-        let context_ty = parse::downcaster::context_ty(&method.sig)
-            .map_err(|span| {
-                ERR.emit_custom(
-                    span,
-                    "expects trait method to accept `&self` only and, optionally, `&Context`",
-                )
-            })
-            .ok()?;
-        if let Some(is_async) = &method.sig.asyncness {
-            ERR.emit_custom(
-                is_async.span(),
-                "async downcast to interface implementer is not supported",
-            );
-            return None;
-        }
-
-        let downcast = ImplementerDowncast::Method {
-            name: method_ident.clone(),
-            with_context: context_ty.is_some(),
-        };
-
-        Some(Implementer {
-            ty,
-            downcast: Some(downcast),
-            context: context_ty,
-            scalar: scalar::Type::ImplicitGeneric(None),
-        })
+    let name = attr
+        .name
+        .as_ref()
+        .map(|m| m.as_ref().value())
+        .unwrap_or_else(|| renaming.apply(&method_ident.unraw().to_string()));
+    if name.starts_with("__") {
+        ERR.no_double_underscore(
+            attr.name
+                .as_ref()
+                .map(SpanContainer::span_ident)
+                .unwrap_or_else(|| method_ident.span()),
+        );
+        return None;
     }
 
-    /// Parses [`TraitMethod::Field`] from the given trait method definition.
-    ///
-    /// Returns [`None`] if parsing fails.
-    #[must_use]
-    fn parse_field(
-        method: &mut syn::TraitItemMethod,
-        attr: field::Attr,
-        renaming: &RenameRule,
-    ) -> Option<field::Definition> {
-        let method_ident = &method.sig.ident;
-
-        let name = attr
-            .name
-            .as_ref()
-            .map(|m| m.as_ref().value())
-            .unwrap_or_else(|| renaming.apply(&method_ident.unraw().to_string()));
-        if name.starts_with("__") {
-            ERR.no_double_underscore(
-                attr.name
-                    .as_ref()
-                    .map(SpanContainer::span_ident)
-                    .unwrap_or_else(|| method_ident.span()),
-            );
-            return None;
+    let arguments = {
+        if method.sig.inputs.is_empty() {
+            return err_no_method_receiver(&method.sig.inputs);
         }
-
-        let arguments = {
-            if method.sig.inputs.is_empty() {
-                return err_no_method_receiver(&method.sig.inputs);
-            }
-            let mut args_iter = method.sig.inputs.iter_mut();
-            match args_iter.next().unwrap() {
-                syn::FnArg::Receiver(rcv) => {
-                    if rcv.reference.is_none() || rcv.mutability.is_some() {
-                        return err_invalid_method_receiver(rcv);
-                    }
+        let mut args_iter = method.sig.inputs.iter_mut();
+        match args_iter.next().unwrap() {
+            syn::FnArg::Receiver(rcv) => {
+                if rcv.reference.is_none() || rcv.mutability.is_some() {
+                    return err_invalid_method_receiver(rcv);
                 }
-                syn::FnArg::Typed(arg) => {
-                    if let syn::Pat::Ident(a) = &*arg.pat {
-                        if a.ident.to_string().as_str() != "self" {
-                            return err_invalid_method_receiver(arg);
-                        }
+            }
+            syn::FnArg::Typed(arg) => {
+                if let syn::Pat::Ident(a) = &*arg.pat {
+                    if a.ident.to_string().as_str() != "self" {
+                        return err_invalid_method_receiver(arg);
                     }
-                    return err_no_method_receiver(arg);
                 }
-            };
-            args_iter
-                .filter_map(|arg| match arg {
-                    syn::FnArg::Receiver(_) => None,
-                    syn::FnArg::Typed(arg) => field::MethodArgument::parse(arg, renaming, &ERR),
-                })
-                .collect()
+                return err_no_method_receiver(arg);
+            }
         };
+        args_iter
+            .filter_map(|arg| match arg {
+                syn::FnArg::Receiver(_) => None,
+                syn::FnArg::Typed(arg) => field::MethodArgument::parse(arg, renaming, &ERR),
+            })
+            .collect()
+    };
 
-        let mut ty = match &method.sig.output {
-            syn::ReturnType::Default => parse_quote! { () },
-            syn::ReturnType::Type(_, ty) => ty.unparenthesized().clone(),
-        };
-        ty.lifetimes_anonymized();
-
-        let description = attr.description.as_ref().map(|d| d.as_ref().value());
-        let deprecated = attr
-            .deprecated
-            .as_deref()
-            .map(|d| d.as_ref().map(syn::LitStr::value));
-
-        Some(field::Definition {
-            name,
-            ty,
-            description,
-            deprecated,
-            ident: method_ident.clone(),
-            arguments: Some(arguments),
-            has_receiver: method.sig.receiver().is_some(),
-            is_async: method.sig.asyncness.is_some(),
-        })
-    }
+    let mut ty = match &method.sig.output {
+        syn::ReturnType::Default => parse_quote! { () },
+        syn::ReturnType::Type(_, ty) => ty.unparenthesized().clone(),
+    };
+    ty.lifetimes_anonymized();
+
+    let description = attr.description.as_ref().map(|d| d.as_ref().value());
+    let deprecated = attr
+        .deprecated
+        .as_deref()
+        .map(|d| d.as_ref().map(syn::LitStr::value));
+
+    Some(field::Definition {
+        name,
+        ty,
+        description,
+        deprecated,
+        ident: method_ident.clone(),
+        arguments: Some(arguments),
+        has_receiver: method.sig.receiver().is_some(),
+        is_async: method.sig.asyncness.is_some(),
+    })
+}
+
+/// Emits "trait method can't have default implementation" [`syn::Error`]
+/// pointing to the given `span`.
+fn err_default_impl_block<T, S: Spanned>(span: &S) -> Option<T> {
+    ERR.emit_custom(
+        span.span(),
+        "trait method can't have default implementation",
+    );
+    None
 }
 
 /// Emits "invalid trait method receiver" [`syn::Error`] pointing to the given
 /// `span`.
-#[must_use]
 fn err_invalid_method_receiver<T, S: Spanned>(span: &S) -> Option<T> {
     ERR.emit_custom(
         span.span(),
@@ -487,7 +255,6 @@ fn err_invalid_method_receiver<T, S: Spanned>(span: &S) -> Option<T> {
 
 /// Emits "no trait method receiver" [`syn::Error`] pointing to the given
 /// `span`.
-#[must_use]
 fn err_no_method_receiver<T, S: Spanned>(span: &S) -> Option<T> {
     ERR.emit_custom(
         span.span(),
@@ -495,42 +262,3 @@ fn err_no_method_receiver<T, S: Spanned>(span: &S) -> Option<T> {
     );
     None
 }
-
-/// Emits "non-implementer downcast target" [`syn::Error`] pointing to the given
-/// `span`.
-fn err_only_implementer_downcast<S: Spanned>(span: &S) {
-    ERR.emit_custom(
-        span.span(),
-        "downcasting is possible only to interface implementers",
-    );
-}
-
-/// Emits "duplicate downcast" [`syn::Error`] for the given `method` and
-/// `external` [`ImplementerDowncast`] function.
-fn err_duplicate_downcast(
-    method: &syn::TraitItemMethod,
-    external: &ImplementerDowncast,
-    impler_ty: &syn::Type,
-) {
-    let external = match external {
-        ImplementerDowncast::External { path } => path,
-        _ => unreachable!(),
-    };
-
-    ERR.custom(
-        method.span(),
-        format!(
-            "trait method `{}` conflicts with the external downcast function \
-             `{}` declared on the trait to downcast into the implementer type \
-             `{}`",
-            method.sig.ident,
-            external.to_token_stream(),
-            impler_ty.to_token_stream(),
-        ),
-    )
-    .note(String::from(
-        "use `#[graphql(ignore)]` attribute argument to ignore this trait \
-         method for interface implementers downcasting",
-    ))
-    .emit()
-}
diff --git a/juniper_codegen/src/graphql_interface/mod.rs b/juniper_codegen/src/graphql_interface/mod.rs
index 0e4ff0665..263d1c5f7 100644
--- a/juniper_codegen/src/graphql_interface/mod.rs
+++ b/juniper_codegen/src/graphql_interface/mod.rs
@@ -4,19 +4,18 @@
 
 pub mod attr;
 
-use std::{
-    collections::{HashMap, HashSet},
-    convert::TryInto as _,
-};
+use std::{collections::HashSet, convert::TryInto as _};
 
 use proc_macro2::TokenStream;
-use quote::{format_ident, quote, ToTokens, TokenStreamExt as _};
+use quote::{format_ident, quote, ToTokens};
 use syn::{
     ext::IdentExt as _,
     parse::{Parse, ParseStream},
     parse_quote,
+    punctuated::Punctuated,
     spanned::Spanned as _,
     token,
+    visit::Visit,
 };
 
 use crate::{
@@ -52,32 +51,21 @@ struct TraitAttr {
     /// [2]: https://spec.graphql.org/June2018/#sec-Descriptions
     description: Option<SpanContainer<String>>,
 
-    /// Explicitly specified identifier of the enum Rust type behind the trait,
-    /// being an actual implementation of a [GraphQL interface][1] type.
+    /// Explicitly specified identifier of the type alias of Rust enum type
+    /// behind the trait, being an actual implementation of a
+    /// [GraphQL interface][1] type.
     ///
     /// If [`None`], then `{trait_name}Value` identifier will be used.
     ///
     /// [1]: https://spec.graphql.org/June2018/#sec-Interfaces
     r#enum: Option<SpanContainer<syn::Ident>>,
 
-    /// Explicitly specified identifier of the Rust type alias of the
-    /// [trait object][2], being an actual implementation of a
-    /// [GraphQL interface][1] type.
-    ///
-    /// Effectively makes code generation to use a [trait object][2] as a
-    /// [GraphQL interface][1] type rather than an enum. If [`None`], then enum
-    /// is used by default.
-    ///
-    /// [1]: https://spec.graphql.org/June2018/#sec-Interfaces
-    /// [2]: https://doc.rust-lang.org/reference/types/trait-object.html
-    r#dyn: Option<SpanContainer<syn::Ident>>,
-
     /// Explicitly specified Rust types of [GraphQL objects][2] implementing
     /// this [GraphQL interface][1] type.
     ///
     /// [1]: https://spec.graphql.org/June2018/#sec-Interfaces
     /// [2]: https://spec.graphql.org/June2018/#sec-Objects
-    implementers: HashSet<SpanContainer<syn::Type>>,
+    implementers: HashSet<SpanContainer<syn::TypePath>>,
 
     /// Explicitly specified type of [`Context`] to use for resolving this
     /// [GraphQL interface][1] type with.
@@ -109,22 +97,6 @@ struct TraitAttr {
     /// it contains async methods.
     asyncness: Option<SpanContainer<syn::Ident>>,
 
-    /// Explicitly specified external downcasting functions for
-    /// [GraphQL interface][1] implementers.
-    ///
-    /// If [`None`], then macro will downcast to the implementers via enum
-    /// dispatch or dynamic dispatch (if the one is chosen). That's why
-    /// specifying an external resolver function has sense, when some custom
-    /// [interface][1] implementer resolving logic is involved.
-    ///
-    /// Once the downcasting function is specified for some [GraphQL object][2]
-    /// implementer type, it cannot be downcast another such function or trait
-    /// method marked with a [`MethodMeta::downcast`] marker.
-    ///
-    /// [1]: https://spec.graphql.org/June2018/#sec-Interfaces
-    /// [2]: https://spec.graphql.org/June2018/#sec-Objects
-    external_downcasts: HashMap<syn::Type, SpanContainer<syn::ExprPath>>,
-
     /// Explicitly specified [`RenameRule`] for all fields of this
     /// [GraphQL interface][1] type.
     ///
@@ -183,7 +155,7 @@ impl Parse for TraitAttr {
                 "for" | "implementers" => {
                     input.parse::<token::Eq>()?;
                     for impler in input.parse_maybe_wrapped_and_punctuated::<
-                        syn::Type, token::Bracket, token::Comma,
+                        syn::TypePath, token::Bracket, token::Comma,
                     >()? {
                         let impler_span = impler.span();
                         out
@@ -192,13 +164,6 @@ impl Parse for TraitAttr {
                             .none_or_else(|_| err::dup_arg(impler_span))?;
                     }
                 }
-                "dyn" => {
-                    input.parse::<token::Eq>()?;
-                    let alias = input.parse::<syn::Ident>()?;
-                    out.r#dyn
-                        .replace(SpanContainer::new(ident.span(), Some(alias.span()), alias))
-                        .none_or_else(|_| err::dup_arg(&ident))?
-                }
                 "enum" => {
                     input.parse::<token::Eq>()?;
                     let alias = input.parse::<syn::Ident>()?;
@@ -212,16 +177,6 @@ impl Parse for TraitAttr {
                         .replace(SpanContainer::new(span, Some(span), ident))
                         .none_or_else(|_| err::dup_arg(span))?;
                 }
-                "on" => {
-                    let ty = input.parse::<syn::Type>()?;
-                    input.parse::<token::Eq>()?;
-                    let dwncst = input.parse::<syn::ExprPath>()?;
-                    let dwncst_spanned = SpanContainer::new(ident.span(), Some(ty.span()), dwncst);
-                    let dwncst_span = dwncst_spanned.span_joined();
-                    out.external_downcasts
-                        .insert(ty, dwncst_spanned)
-                        .none_or_else(|_| err::dup_arg(dwncst_span))?
-                }
                 "rename_all" => {
                     input.parse::<token::Eq>()?;
                     let val = input.parse::<syn::LitStr>()?;
@@ -256,12 +211,8 @@ impl TraitAttr {
             context: try_merge_opt!(context: self, another),
             scalar: try_merge_opt!(scalar: self, another),
             implementers: try_merge_hashset!(implementers: self, another => span_joined),
-            r#dyn: try_merge_opt!(r#dyn: self, another),
             r#enum: try_merge_opt!(r#enum: self, another),
             asyncness: try_merge_opt!(asyncness: self, another),
-            external_downcasts: try_merge_hashmap!(
-                external_downcasts: self, another => span_joined
-            ),
             rename_fields: try_merge_opt!(rename_fields: self, another),
             is_internal: self.is_internal || another.is_internal,
         })
@@ -274,15 +225,6 @@ impl TraitAttr {
             .map(|attr| attr.parse_args())
             .try_fold(Self::default(), |prev, curr| prev.try_merge(curr?))?;
 
-        if let Some(as_dyn) = &attr.r#dyn {
-            if attr.r#enum.is_some() {
-                return Err(syn::Error::new(
-                    as_dyn.span(),
-                    "`dyn` attribute argument is not composable with `enum` attribute argument",
-                ));
-            }
-        }
-
         if attr.description.is_none() {
             attr.description = get_doc_comment(attrs);
         }
@@ -291,109 +233,32 @@ impl TraitAttr {
     }
 }
 
-/// Available arguments behind `#[graphql_interface]` attribute placed on a
-/// trait implementation block, when generating code for [GraphQL interface][1]
-/// type.
+/// Definition of [GraphQL interface][1] for code generation.
 ///
 /// [1]: https://spec.graphql.org/June2018/#sec-Interfaces
-#[derive(Debug, Default)]
-struct ImplAttr {
-    /// Explicitly specified type (or type parameter with its bounds) of
-    /// [`ScalarValue`] to implementing the [GraphQL interface][1] type with.
-    ///
-    /// If absent, then generated code will be generic over any [`ScalarValue`]
-    /// type, which, in turn, requires all [interface][1] implementers to be
-    /// generic over any [`ScalarValue`] type too. That's why this type should
-    /// be specified only if the implementer itself implements [`GraphQLType`]
-    /// in a non-generic way over [`ScalarValue`] type.
+struct Definition {
+    /// [`syn::Generics`] of the trait describing the [GraphQL interface][1].
     ///
-    /// [`GraphQLType`]: juniper::GraphQLType
-    /// [`ScalarValue`]: juniper::ScalarValue
     /// [1]: https://spec.graphql.org/June2018/#sec-Interfaces
-    scalar: Option<SpanContainer<scalar::AttrValue>>,
+    trait_generics: syn::Generics,
 
-    /// Explicitly specified marker indicating that the trait implementation
-    /// block should be transformed with applying [`async_trait`].
-    ///
-    /// If absent, then trait will be transformed with applying [`async_trait`]
-    /// only if it contains async methods.
+    /// [`syn::Visibility`] of the trait describing the [GraphQL interface][1].
     ///
-    /// This marker is especially useful when Rust trait contains async default
-    /// methods, while the implementation block doesn't.
-    asyncness: Option<SpanContainer<syn::Ident>>,
+    /// [1]: https://spec.graphql.org/June2018/#sec-Interfaces
+    vis: syn::Visibility,
 
-    /// Explicitly specified marker indicating that the implemented
-    /// [GraphQL interface][1] type is represented as a [trait object][2] in
-    /// Rust type system rather then an enum (default mode, when the marker is
-    /// absent).
+    /// Name of the generic enum describing all [`implementers`]. It's generic
+    /// to derive [`Clone`], [`Copy`] and [`Debug`] on it.
     ///
-    /// [2]: https://doc.rust-lang.org/reference/types/trait-object.html
-    r#dyn: Option<SpanContainer<syn::Ident>>,
-}
-
-impl Parse for ImplAttr {
-    fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
-        let mut out = Self::default();
-        while !input.is_empty() {
-            let ident = input.parse_any_ident()?;
-            match ident.to_string().as_str() {
-                "scalar" | "Scalar" | "ScalarValue" => {
-                    input.parse::<token::Eq>()?;
-                    let scl = input.parse::<scalar::AttrValue>()?;
-                    out.scalar
-                        .replace(SpanContainer::new(ident.span(), Some(scl.span()), scl))
-                        .none_or_else(|_| err::dup_arg(&ident))?
-                }
-                "dyn" => {
-                    let span = ident.span();
-                    out.r#dyn
-                        .replace(SpanContainer::new(span, Some(span), ident))
-                        .none_or_else(|_| err::dup_arg(span))?;
-                }
-                "async" => {
-                    let span = ident.span();
-                    out.asyncness
-                        .replace(SpanContainer::new(span, Some(span), ident))
-                        .none_or_else(|_| err::dup_arg(span))?;
-                }
-                name => {
-                    return Err(err::unknown_arg(&ident, name));
-                }
-            }
-            input.try_parse::<token::Comma>()?;
-        }
-        Ok(out)
-    }
-}
-
-impl ImplAttr {
-    /// Tries to merge two [`ImplAttr`]s into a single one, reporting about
-    /// duplicates, if any.
-    fn try_merge(self, mut another: Self) -> syn::Result<Self> {
-        Ok(Self {
-            scalar: try_merge_opt!(scalar: self, another),
-            r#dyn: try_merge_opt!(r#dyn: self, another),
-            asyncness: try_merge_opt!(asyncness: self, another),
-        })
-    }
-
-    /// Parses [`ImplAttr`] from the given multiple `name`d [`syn::Attribute`]s
-    /// placed on a trait implementation block.
-    pub fn from_attrs(name: &str, attrs: &[syn::Attribute]) -> syn::Result<Self> {
-        filter_attrs(name, attrs)
-            .map(|attr| attr.parse_args())
-            .try_fold(Self::default(), |prev, curr| prev.try_merge(curr?))
-    }
-}
+    /// [`implementers`]: Self::implementers
+    /// [`Debug`]: std::fmt::Debug
+    enum_ident: syn::Ident,
 
-/// Definition of [GraphQL interface][1] for code generation.
-///
-/// [1]: https://spec.graphql.org/June2018/#sec-Interfaces
-struct Definition {
-    /// Rust type that this [GraphQL interface][1] is represented with.
+    /// Name of the type alias for [`enum_ident`] with [`implementers`].
     ///
-    /// [1]: https://spec.graphql.org/June2018/#sec-Interfaces
-    ty: Type,
+    /// [`enum_ident`]: Self::enum_ident
+    /// [`implementers`]: Self::implementers
+    enum_alias_ident: syn::Ident,
 
     /// Name of this [GraphQL interface][1] in GraphQL schema.
     ///
@@ -430,21 +295,148 @@ struct Definition {
     /// Defined [`Implementer`]s of this [GraphQL interface][1].
     ///
     /// [1]: https://spec.graphql.org/June2018/#sec-Interfaces
-    implementers: Vec<Implementer>,
+    implementers: Vec<syn::TypePath>,
 }
 
 impl ToTokens for Definition {
     fn to_tokens(&self, into: &mut TokenStream) {
-        self.ty.to_token_stream().to_tokens(into);
+        self.generate_enum_tokens().to_tokens(into);
         self.impl_graphql_interface_tokens().to_tokens(into);
         self.impl_output_type_tokens().to_tokens(into);
         self.impl_graphql_type_tokens().to_tokens(into);
         self.impl_graphql_value_tokens().to_tokens(into);
         self.impl_graphql_value_async_tokens().to_tokens(into);
+        self.impl_reflection_traits_tokens().to_tokens(into);
+        self.impl_field_meta_tokens().to_tokens(into);
+        self.impl_field_tokens().to_tokens(into);
+        self.impl_async_field_tokens().to_tokens(into);
     }
 }
 
 impl Definition {
+    /// Generates enum describing all the [`implementers`].
+    ///
+    /// [`implementers`]: Self::implementers
+    #[must_use]
+    fn generate_enum_tokens(&self) -> TokenStream {
+        let vis = &self.vis;
+        let enum_ident = &self.enum_ident;
+        let alias_ident = &self.enum_alias_ident;
+
+        let variant_gens_pars = self
+            .implementers
+            .iter()
+            .enumerate()
+            .map::<syn::GenericParam, _>(|(id, _)| {
+                let par = format_ident!("__I{}", id);
+                parse_quote! { #par }
+            });
+
+        let variants_idents = self
+            .implementers
+            .iter()
+            .filter_map(|ty| ty.path.segments.last().map(|seg| &seg.ident));
+
+        let trait_gens = &self.trait_generics;
+        let (trait_impl_gens, trait_ty_gens, trait_where_clause) =
+            self.trait_generics.split_for_impl();
+
+        let (trait_gens_lifetimes, trait_gens_tys) = trait_gens
+            .params
+            .clone()
+            .into_iter()
+            .partition::<Punctuated<_, _>, _>(|par| {
+            matches!(par, syn::GenericParam::Lifetime(_))
+        });
+
+        let enum_gens = {
+            let mut enum_gens = trait_gens.clone();
+            enum_gens.params = trait_gens_lifetimes.clone();
+            enum_gens.params.extend(variant_gens_pars.clone());
+            enum_gens.params.extend(trait_gens_tys.clone());
+            enum_gens
+        };
+
+        let enum_alias_gens = {
+            let mut enum_alias_gens = trait_gens.clone();
+            enum_alias_gens.move_bounds_to_where_clause();
+            enum_alias_gens
+        };
+
+        let enum_to_alias_gens = {
+            trait_gens_lifetimes
+                .into_iter()
+                .map(|par| match par {
+                    syn::GenericParam::Lifetime(def) => {
+                        let lifetime = &def.lifetime;
+                        quote! { #lifetime }
+                    }
+                    rest => quote! { #rest },
+                })
+                .chain(self.implementers.iter().map(ToTokens::to_token_stream))
+                .chain(trait_gens_tys.into_iter().map(|par| match par {
+                    syn::GenericParam::Type(ty) => {
+                        let par_ident = &ty.ident;
+                        quote! { #par_ident }
+                    }
+                    rest => quote! { #rest },
+                }))
+        };
+
+        let phantom_variant = self.has_phantom_variant().then(|| {
+            let phantom_params = trait_gens.params.iter().filter_map(|p| {
+                let ty = match p {
+                    syn::GenericParam::Type(ty) => {
+                        let ident = &ty.ident;
+                        quote! { #ident }
+                    }
+                    syn::GenericParam::Lifetime(lt) => {
+                        let lifetime = &lt.lifetime;
+                        quote! { &#lifetime () }
+                    }
+                    syn::GenericParam::Const(_) => return None,
+                };
+                Some(quote! {
+                    ::std::marker::PhantomData<::std::sync::atomic::AtomicPtr<Box<#ty>>>
+                })
+            });
+            quote! { __Phantom(#(#phantom_params),*) }
+        });
+
+        let from_impls =
+            self.implementers
+                .iter()
+                .zip(variants_idents.clone())
+                .map(|(ty, ident)| {
+                    quote! {
+                        #[automatically_derived]
+                        impl#trait_impl_gens ::std::convert::From<#ty>
+                            for #alias_ident#trait_ty_gens
+                            #trait_where_clause
+                        {
+                            fn from(v: #ty) -> Self {
+                                Self::#ident(v)
+                            }
+                        }
+                    }
+                });
+
+        quote! {
+            #[automatically_derived]
+            #[derive(Clone, Copy, Debug)]
+            #vis enum #enum_ident#enum_gens {
+                #(#variants_idents(#variant_gens_pars),)*
+                #phantom_variant
+            }
+
+            #[automatically_derived]
+            #vis type #alias_ident#enum_alias_gens =
+                #enum_ident<#(#enum_to_alias_gens),*>;
+
+            #(#from_impls)*
+        }
+    }
+
     /// Returns generated code implementing [`GraphQLInterface`] trait for this
     /// [GraphQL interface][1].
     ///
@@ -452,19 +444,23 @@ impl Definition {
     /// [1]: https://spec.graphql.org/June2018/#sec-Interfaces
     #[must_use]
     fn impl_graphql_interface_tokens(&self) -> TokenStream {
+        let ty = &self.enum_alias_ident;
         let scalar = &self.scalar;
 
-        let (impl_generics, where_clause) = self.ty.impl_generics(false);
-        let ty = self.ty.ty_tokens();
+        let gens = self.impl_generics(false);
+        let (impl_generics, _, where_clause) = gens.split_for_impl();
+        let (_, ty_generics, _) = self.trait_generics.split_for_impl();
 
-        let impler_tys: Vec<_> = self.implementers.iter().map(|impler| &impler.ty).collect();
+        let impler_tys = &self.implementers;
         let all_implers_unique = (impler_tys.len() > 1).then(|| {
             quote! { ::juniper::sa::assert_type_ne_all!(#( #impler_tys ),*); }
         });
 
         quote! {
             #[automatically_derived]
-            impl#impl_generics ::juniper::marker::GraphQLInterface<#scalar> for #ty #where_clause
+            impl#impl_generics ::juniper::marker::GraphQLInterface<#scalar>
+                for #ty#ty_generics
+                #where_clause
             {
                 fn mark() {
                     #all_implers_unique
@@ -481,25 +477,34 @@ impl Definition {
     /// [1]: https://spec.graphql.org/June2018/#sec-Interfaces
     #[must_use]
     fn impl_output_type_tokens(&self) -> TokenStream {
+        let ty = &self.enum_alias_ident;
         let scalar = &self.scalar;
+        let const_scalar = &self.scalar.default_ty();
 
-        let (impl_generics, where_clause) = self.ty.impl_generics(false);
-        let ty = self.ty.ty_tokens();
+        let generics = self.impl_generics(false);
+        let (impl_generics, _, where_clause) = generics.split_for_impl();
+        let (_, ty_generics, _) = self.trait_generics.split_for_impl();
+        let ty_const_generics = self.const_trait_generics();
 
         let fields_marks = self
             .fields
             .iter()
             .map(|f| f.method_mark_tokens(false, scalar));
 
-        let impler_tys = self.implementers.iter().map(|impler| &impler.ty);
+        let impler_tys = self.implementers.iter().collect::<Vec<_>>();
 
         quote! {
             #[automatically_derived]
-            impl#impl_generics ::juniper::marker::IsOutputType<#scalar> for #ty #where_clause
+            impl#impl_generics ::juniper::marker::IsOutputType<#scalar>
+                for #ty#ty_generics
+                #where_clause
             {
                 fn mark() {
                     #( #fields_marks )*
                     #( <#impler_tys as ::juniper::marker::IsOutputType<#scalar>>::mark(); )*
+                    ::juniper::assert_interfaces_impls!(
+                        #const_scalar, #ty#ty_const_generics, #(#impler_tys),*
+                    );
                 }
             }
         }
@@ -512,10 +517,12 @@ impl Definition {
     /// [1]: https://spec.graphql.org/June2018/#sec-Interfaces
     #[must_use]
     fn impl_graphql_type_tokens(&self) -> TokenStream {
+        let ty = &self.enum_alias_ident;
         let scalar = &self.scalar;
 
-        let (impl_generics, where_clause) = self.ty.impl_generics(false);
-        let ty = self.ty.ty_tokens();
+        let generics = self.impl_generics(false);
+        let (impl_generics, _, where_clause) = generics.split_for_impl();
+        let (_, ty_generics, _) = self.trait_generics.split_for_impl();
 
         let name = &self.name;
         let description = self
@@ -524,7 +531,7 @@ impl Definition {
             .map(|desc| quote! { .description(#desc) });
 
         // Sorting is required to preserve/guarantee the order of implementers registered in schema.
-        let mut impler_tys: Vec<_> = self.implementers.iter().map(|impler| &impler.ty).collect();
+        let mut impler_tys = self.implementers.clone();
         impler_tys.sort_unstable_by(|a, b| {
             let (a, b) = (quote!(#a).to_string(), quote!(#b).to_string());
             a.cmp(&b)
@@ -534,7 +541,9 @@ impl Definition {
 
         quote! {
             #[automatically_derived]
-            impl#impl_generics ::juniper::GraphQLType<#scalar> for #ty #where_clause
+            impl#impl_generics ::juniper::GraphQLType<#scalar>
+                for #ty#ty_generics
+                #where_clause
             {
                 fn name(_ : &Self::TypeInfo) -> Option<&'static str> {
                     Some(#name)
@@ -552,7 +561,7 @@ impl Definition {
                     let fields = [
                         #( #fields_meta, )*
                     ];
-                    registry.build_interface_type::<#ty>(info, &fields)
+                    registry.build_interface_type::<#ty#ty_generics>(info, &fields)
                         #description
                         .into_meta()
                 }
@@ -567,49 +576,39 @@ impl Definition {
     /// [1]: https://spec.graphql.org/June2018/#sec-Interfaces
     #[must_use]
     fn impl_graphql_value_tokens(&self) -> TokenStream {
+        let ty = &self.enum_alias_ident;
+        let trait_name = &self.name;
         let scalar = &self.scalar;
         let context = &self.context;
 
-        let (impl_generics, where_clause) = self.ty.impl_generics(false);
-        let ty = self.ty.ty_tokens();
-        let ty_name = ty.to_string();
-        let trait_ty = self.ty.trait_ty();
+        let generics = self.impl_generics(false);
+        let (impl_generics, _, where_clause) = generics.split_for_impl();
+        let (_, ty_generics, _) = self.trait_generics.split_for_impl();
 
-        let fields_resolvers = self
-            .fields
-            .iter()
-            .filter_map(|f| f.method_resolve_field_tokens(scalar, Some(&trait_ty)));
-        let async_fields_err = {
-            let names = self
-                .fields
-                .iter()
-                .filter_map(|f| f.is_async.then(|| f.name.as_str()))
-                .collect::<Vec<_>>();
-            (!names.is_empty()).then(|| {
-                field::Definition::method_resolve_field_err_async_field_tokens(
-                    &names, scalar, &ty_name,
-                )
+        let fields_resolvers = self.fields.iter().map(|f| {
+            let name = &f.name;
+            Some(quote! {
+                #name => {
+                    ::juniper::macros::reflect::Field::<
+                        #scalar,
+                        { ::juniper::macros::reflect::fnv1a128(#name) }
+                    >::call(self, info, args, executor)
+                }
             })
-        };
+        });
+
         let no_field_err =
-            field::Definition::method_resolve_field_err_no_field_tokens(scalar, &ty_name);
+            field::Definition::method_resolve_field_err_no_field_tokens(scalar, trait_name);
 
-        let custom_downcast_checks = self
-            .implementers
-            .iter()
-            .filter_map(|i| i.method_concrete_type_name_tokens(&trait_ty));
-        let regular_downcast_check = self.ty.method_concrete_type_name_tokens();
+        let downcast_check = self.method_concrete_type_name_tokens();
 
-        let custom_downcasts = self
-            .implementers
-            .iter()
-            .filter_map(|i| i.method_resolve_into_type_tokens(&trait_ty));
-        let regular_downcast = self.ty.method_resolve_into_type_tokens();
+        let downcast = self.method_resolve_into_type_tokens();
 
         quote! {
             #[allow(deprecated)]
             #[automatically_derived]
-            impl#impl_generics ::juniper::GraphQLValue<#scalar> for #ty #where_clause
+            impl#impl_generics ::juniper::GraphQLValue<#scalar> for #ty#ty_generics
+                #where_clause
             {
                 type Context = #context;
                 type TypeInfo = ();
@@ -627,7 +626,6 @@ impl Definition {
                 ) -> ::juniper::ExecutionResult<#scalar> {
                     match field {
                         #( #fields_resolvers )*
-                        #async_fields_err
                         _ => #no_field_err,
                     }
                 }
@@ -637,8 +635,7 @@ impl Definition {
                     context: &Self::Context,
                     info: &Self::TypeInfo,
                 ) -> String {
-                    #( #custom_downcast_checks )*
-                    #regular_downcast_check
+                    #downcast_check
                 }
 
                 fn resolve_into_type(
@@ -648,8 +645,7 @@ impl Definition {
                     _: Option<&[::juniper::Selection<#scalar>]>,
                     executor: &::juniper::Executor<Self::Context, #scalar>,
                 ) -> ::juniper::ExecutionResult<#scalar> {
-                    #( #custom_downcasts )*
-                    #regular_downcast
+                    #downcast
                 }
             }
         }
@@ -662,30 +658,35 @@ impl Definition {
     /// [1]: https://spec.graphql.org/June2018/#sec-Interfaces
     #[must_use]
     fn impl_graphql_value_async_tokens(&self) -> TokenStream {
+        let ty = &self.enum_alias_ident;
+        let trait_name = &self.name;
         let scalar = &self.scalar;
 
-        let (impl_generics, where_clause) = self.ty.impl_generics(true);
-        let ty = self.ty.ty_tokens();
-        let ty_name = ty.to_string();
-        let trait_ty = self.ty.trait_ty();
+        let generics = self.impl_generics(true);
+        let (impl_generics, _, where_clause) = generics.split_for_impl();
+        let (_, ty_generics, _) = self.trait_generics.split_for_impl();
 
-        let fields_resolvers = self
-            .fields
-            .iter()
-            .map(|f| f.method_resolve_field_async_tokens(scalar, Some(&trait_ty)));
+        let fields_resolvers = self.fields.iter().map(|f| {
+            let name = &f.name;
+            quote! {
+                #name => {
+                    ::juniper::macros::reflect::AsyncField::<
+                        #scalar,
+                        { ::juniper::macros::reflect::fnv1a128(#name) }
+                    >::call(self, info, args, executor)
+                }
+            }
+        });
         let no_field_err =
-            field::Definition::method_resolve_field_err_no_field_tokens(scalar, &ty_name);
+            field::Definition::method_resolve_field_err_no_field_tokens(scalar, trait_name);
 
-        let custom_downcasts = self
-            .implementers
-            .iter()
-            .filter_map(|i| i.method_resolve_into_type_async_tokens(&trait_ty));
-        let regular_downcast = self.ty.method_resolve_into_type_async_tokens();
+        let downcast = self.method_resolve_into_type_async_tokens();
 
         quote! {
             #[allow(deprecated, non_snake_case)]
             #[automatically_derived]
-            impl#impl_generics ::juniper::GraphQLValueAsync<#scalar> for #ty #where_clause
+            impl#impl_generics ::juniper::GraphQLValueAsync<#scalar> for #ty#ty_generics
+                #where_clause
             {
                 fn resolve_field_async<'b>(
                     &'b self,
@@ -707,633 +708,335 @@ impl Definition {
                     _: Option<&'b [::juniper::Selection<'b, #scalar>]>,
                     executor: &'b ::juniper::Executor<'b, 'b, Self::Context, #scalar>
                 ) -> ::juniper::BoxFuture<'b, ::juniper::ExecutionResult<#scalar>> {
-                    #( #custom_downcasts )*
-                    #regular_downcast
+                    #downcast
                 }
             }
         }
     }
-}
-
-/// Representation of custom downcast into an [`Implementer`] from a
-/// [GraphQL interface][1] type for code generation.
-///
-/// [1]: https://spec.graphql.org/June2018/#sec-Interfaces
-#[derive(Clone, Debug)]
-enum ImplementerDowncast {
-    /// Downcast is performed via a method of trait describing a
-    /// [GraphQL interface][1].
-    ///
-    /// [1]: https://spec.graphql.org/June2018/#sec-Interfaces
-    Method {
-        /// Name of trait method which performs this [`ImplementerDowncast`].
-        name: syn::Ident,
-
-        /// Indicator whether the trait method accepts a [`Context`] as its
-        /// second argument.
-        ///
-        /// [`Context`]: juniper::Context
-        with_context: bool,
-    },
 
-    /// Downcast is performed via some external function.
-    External {
-        /// Path of the external function to be called with.
-        path: syn::ExprPath,
-    },
-}
-
-/// Representation of [GraphQL interface][1] implementer for code generation.
-///
-/// [1]: https://spec.graphql.org/June2018/#sec-Interfaces
-#[derive(Clone, Debug)]
-struct Implementer {
-    /// Rust type that this [GraphQL interface][1] [`Implementer`] is
-    /// represented by.
+    /// Returns generated code implementing [`BaseType`], [`BaseSubTypes`],
+    /// [`WrappedType`] and [`Fields`] traits for this [GraphQL interface][1].
     ///
+    /// [`BaseSubTypes`]: juniper::macros::reflect::BaseSubTypes
+    /// [`BaseType`]: juniper::macros::reflect::BaseType
+    /// [`Fields`]: juniper::macros::reflect::Fields
+    /// [`WrappedType`]: juniper::macros::reflect::WrappedType
     /// [1]: https://spec.graphql.org/June2018/#sec-Interfaces
-    ty: syn::Type,
-
-    /// Custom [`ImplementerDowncast`] for this [`Implementer`].
-    ///
-    /// If absent, then [`Implementer`] is downcast from an enum variant or a
-    /// trait object.
-    downcast: Option<ImplementerDowncast>,
+    #[must_use]
+    fn impl_reflection_traits_tokens(&self) -> TokenStream {
+        let ty = &self.enum_alias_ident;
+        let implementers = &self.implementers;
+        let scalar = &self.scalar;
+        let name = &self.name;
+        let fields = self.fields.iter().map(|f| &f.name);
 
-    /// Rust type of [`Context`] that this [GraphQL interface][1]
-    /// [`Implementer`] requires for downcasting.
-    ///
-    /// It's available only when code generation happens for Rust traits and a
-    /// trait method contains context argument.
-    ///
-    /// [`Context`]: juniper::Context
-    /// [1]: https://spec.graphql.org/June2018/#sec-Interfaces
-    context: Option<syn::Type>,
+        let generics = self.impl_generics(false);
+        let (impl_generics, _, where_clause) = generics.split_for_impl();
+        let (_, ty_generics, _) = self.trait_generics.split_for_impl();
 
-    /// [`ScalarValue`] parametrization of this [`Implementer`].
-    ///
-    /// [`ScalarValue`]: juniper::ScalarValue
-    scalar: scalar::Type,
-}
+        quote! {
+            #[automatically_derived]
+            impl#impl_generics ::juniper::macros::reflect::BaseType<#scalar>
+                for #ty#ty_generics
+                #where_clause
+            {
+                const NAME: ::juniper::macros::reflect::Type = #name;
+            }
 
-impl Implementer {
-    /// Returns generated code of downcasting this [`Implementer`] via custom
-    /// [`ImplementerDowncast`].
-    ///
-    /// Returns [`None`] if there is no custom [`Implementer::downcast`].
-    #[must_use]
-    fn downcast_call_tokens(
-        &self,
-        trait_ty: &syn::Type,
-        ctx: Option<syn::Expr>,
-    ) -> Option<TokenStream> {
-        let ctx = ctx.unwrap_or_else(|| parse_quote! { executor.context() });
-        let mut ctx_arg = Some(quote! { , ::juniper::FromContext::from(#ctx) });
-
-        let fn_path = match self.downcast.as_ref()? {
-            ImplementerDowncast::Method { name, with_context } => {
-                if !with_context {
-                    ctx_arg = None;
-                }
-                quote! { <Self as #trait_ty>::#name }
+            #[automatically_derived]
+            impl#impl_generics ::juniper::macros::reflect::BaseSubTypes<#scalar>
+                for #ty#ty_generics
+                #where_clause
+            {
+                const NAMES: ::juniper::macros::reflect::Types = &[
+                    <Self as ::juniper::macros::reflect::BaseType<#scalar>>::NAME,
+                    #(<#implementers as ::juniper::macros::reflect::BaseType<#scalar>>::NAME),*
+                ];
             }
-            ImplementerDowncast::External { path } => {
-                quote! { #path }
+
+            #[automatically_derived]
+            impl#impl_generics ::juniper::macros::reflect::WrappedType<#scalar>
+                for #ty#ty_generics
+                #where_clause
+            {
+                const VALUE: ::juniper::macros::reflect::WrappedValue = 1;
             }
-        };
 
-        Some(quote! {
-            #fn_path(self #ctx_arg)
-        })
+            #[automatically_derived]
+            impl#impl_generics ::juniper::macros::reflect::Fields<#scalar>
+                for #ty#ty_generics
+                #where_clause
+            {
+                const NAMES: ::juniper::macros::reflect::Names = &[#(#fields),*];
+            }
+        }
     }
 
-    /// Returns generated code for the [`GraphQLValue::concrete_type_name`]
-    /// method, which returns name of the GraphQL type represented by this
-    /// [`Implementer`].
-    ///
-    /// Returns [`None`] if there is no custom [`Implementer::downcast`].
+    /// Returns generated code implementing [`FieldMeta`] for each field of this
+    /// [GraphQL interface][1].
     ///
-    /// [`GraphQLValue::concrete_type_name`]: juniper::GraphQLValue::concrete_type_name
-    #[must_use]
-    fn method_concrete_type_name_tokens(&self, trait_ty: &syn::Type) -> Option<TokenStream> {
-        self.downcast.as_ref()?;
-
-        let ty = &self.ty;
+    /// [`FieldMeta`]: juniper::macros::reflect::FieldMeta
+    /// [1]: https://spec.graphql.org/June2018/#sec-Interfaces
+    fn impl_field_meta_tokens(&self) -> TokenStream {
+        let ty = &self.enum_alias_ident;
+        let context = &self.context;
         let scalar = &self.scalar;
 
-        let downcast = self.downcast_call_tokens(trait_ty, Some(parse_quote! { context }));
+        let generics = self.impl_generics(false);
+        let (impl_generics, _, where_clause) = generics.split_for_impl();
+        let (_, ty_generics, _) = self.trait_generics.split_for_impl();
+
+        self.fields
+            .iter()
+            .map(|field| {
+                let field_name = &field.name;
+                let mut return_ty = field.ty.clone();
+                generics.replace_type_with_defaults(&mut return_ty);
+
+                let (args_tys, args_names): (Vec<_>, Vec<_>) = field
+                    .arguments
+                    .iter()
+                    .flat_map(|vec| vec.iter())
+                    .filter_map(|arg| match arg {
+                        field::MethodArgument::Regular(arg) => Some((&arg.ty, &arg.name)),
+                        _ => None,
+                    })
+                    .unzip();
 
-        // Doing this may be quite an expensive, because resolving may contain some heavy
-        // computation, so we're preforming it twice. Unfortunately, we have no other options here,
-        // until the `juniper::GraphQLType` itself will allow to do it in some cleverer way.
-        Some(quote! {
-            if (#downcast as ::std::option::Option<&#ty>).is_some() {
-                return <#ty as ::juniper::GraphQLType<#scalar>>::name(info).unwrap().to_string();
-            }
-        })
+                quote! {
+                    #[allow(non_snake_case)]
+                    #[automatically_derived]
+                    impl#impl_generics ::juniper::macros::reflect::FieldMeta<
+                        #scalar,
+                        { ::juniper::macros::reflect::fnv1a128(#field_name) }
+                    > for #ty#ty_generics #where_clause {
+                        type Context = #context;
+                        type TypeInfo = ();
+                        const TYPE: ::juniper::macros::reflect::Type =
+                            <#return_ty as ::juniper::macros::reflect::BaseType<#scalar>>::NAME;
+                        const SUB_TYPES: ::juniper::macros::reflect::Types =
+                            <#return_ty as ::juniper::macros::reflect::BaseSubTypes<#scalar>>::NAMES;
+                        const WRAPPED_VALUE: ::juniper::macros::reflect::WrappedValue =
+                            <#return_ty as ::juniper::macros::reflect::WrappedType<#scalar>>::VALUE;
+                        const ARGUMENTS: &'static [(
+                            ::juniper::macros::reflect::Name,
+                            ::juniper::macros::reflect::Type,
+                            ::juniper::macros::reflect::WrappedValue,
+                        )] = &[#((
+                            #args_names,
+                            <#args_tys as ::juniper::macros::reflect::BaseType<#scalar>>::NAME,
+                            <#args_tys as ::juniper::macros::reflect::WrappedType<#scalar>>::VALUE,
+                        )),*];
+                    }
+                }
+            })
+            .collect()
     }
 
-    /// Returns generated code for the [`GraphQLValue::resolve_into_type`][0]
-    /// method, which downcasts the [GraphQL interface][1] type into this
-    /// [`Implementer`] synchronously.
-    ///
-    /// Returns [`None`] if there is no custom [`Implementer::downcast`].
+    /// Returns generated code implementing [`Field`] trait for each field of
+    /// this [GraphQL interface][1].
     ///
-    /// [0]: juniper::GraphQLValue::resolve_into_type
+    /// [`Field`]: juniper::macros::reflect::Field
     /// [1]: https://spec.graphql.org/June2018/#sec-Interfaces
-    #[must_use]
-    fn method_resolve_into_type_tokens(&self, trait_ty: &syn::Type) -> Option<TokenStream> {
-        self.downcast.as_ref()?;
-
-        let ty = &self.ty;
-        let ty_name = ty.to_token_stream().to_string();
+    fn impl_field_tokens(&self) -> TokenStream {
+        let ty = &self.enum_alias_ident;
         let scalar = &self.scalar;
+        let const_scalar = self.scalar.default_ty();
 
-        let downcast = self.downcast_call_tokens(trait_ty, None);
+        let impl_tys = self.implementers.iter().collect::<Vec<_>>();
+        let impl_idents = self
+            .implementers
+            .iter()
+            .filter_map(|ty| ty.path.segments.last().map(|seg| &seg.ident))
+            .collect::<Vec<_>>();
 
-        let resolving_code = gen::sync_resolving_code();
+        let generics = self.impl_generics(false);
+        let (impl_generics, _, where_clause) = generics.split_for_impl();
+        let (_, ty_generics, _) = self.trait_generics.split_for_impl();
 
-        Some(quote! {
-            if type_name == <#ty as ::juniper::GraphQLType<#scalar>>::name(info)
-                .ok_or_else(|| ::juniper::macros::helper::err_unnamed_type(#ty_name))?
-            {
-                let res = #downcast;
-                return #resolving_code;
-            }
-        })
+        self.fields
+            .iter()
+            .map(|field| {
+                let field_name = &field.name;
+                let mut return_ty = field.ty.clone();
+                generics.replace_type_with_defaults(&mut return_ty);
+
+                let const_ty_generics = self.const_trait_generics();
+
+                let unreachable_arm = (self.implementers.is_empty()
+                    || !self.trait_generics.params.is_empty())
+                .then(|| {
+                    quote! { _ => unreachable!() }
+                });
+
+                quote! {
+                    #[allow(non_snake_case)]
+                    #[automatically_derived]
+                    impl#impl_generics ::juniper::macros::reflect::Field<
+                        #scalar,
+                        { ::juniper::macros::reflect::fnv1a128(#field_name) }
+                    > for #ty#ty_generics #where_clause {
+                        fn call(
+                            &self,
+                            info: &Self::TypeInfo,
+                            args: &::juniper::Arguments<#scalar>,
+                            executor: &::juniper::Executor<Self::Context, #scalar>,
+                        ) -> ::juniper::ExecutionResult<#scalar> {
+                            match self {
+                                #(#ty::#impl_idents(v) => {
+                                    ::juniper::assert_field!(
+                                        #ty#const_ty_generics,
+                                        #impl_tys,
+                                        #const_scalar,
+                                        #field_name,
+                                    );
+
+                                    <_ as ::juniper::macros::reflect::Field::<
+                                        #scalar,
+                                        { ::juniper::macros::reflect::fnv1a128(#field_name) },
+                                    >>::call(v, info, args, executor)
+                                })*
+                                #unreachable_arm
+                            }
+                        }
+                    }
+                }
+            })
+            .collect()
     }
 
-    /// Returns generated code for the
-    /// [`GraphQLValueAsync::resolve_into_type_async`][0] method, which
-    /// downcasts the [GraphQL interface][1] type into this [`Implementer`]
-    /// asynchronously.
-    ///
-    /// Returns [`None`] if there is no custom [`Implementer::downcast`].
+    /// Returns generated code implementing [`AsyncField`] trait for each field
+    /// of this [GraphQL interface][1].
     ///
-    /// [0]: juniper::GraphQLValueAsync::resolve_into_type_async
+    /// [`AsyncField`]: juniper::macros::reflect::AsyncField
     /// [1]: https://spec.graphql.org/June2018/#sec-Interfaces
-    #[must_use]
-    fn method_resolve_into_type_async_tokens(&self, trait_ty: &syn::Type) -> Option<TokenStream> {
-        self.downcast.as_ref()?;
-
-        let ty = &self.ty;
-        let ty_name = ty.to_token_stream().to_string();
+    fn impl_async_field_tokens(&self) -> TokenStream {
+        let ty = &self.enum_alias_ident;
         let scalar = &self.scalar;
+        let const_scalar = self.scalar.default_ty();
 
-        let downcast = self.downcast_call_tokens(trait_ty, None);
+        let impl_tys = self.implementers.iter().collect::<Vec<_>>();
+        let impl_idents = self
+            .implementers
+            .iter()
+            .filter_map(|ty| ty.path.segments.last().map(|seg| &seg.ident))
+            .collect::<Vec<_>>();
 
-        let resolving_code = gen::async_resolving_code(None);
+        let generics = self.impl_generics(true);
+        let (impl_generics, _, where_clause) = generics.split_for_impl();
+        let (_, ty_generics, _) = self.trait_generics.split_for_impl();
 
-        Some(quote! {
-            match <#ty as ::juniper::GraphQLType<#scalar>>::name(info) {
-                Some(name) => {
-                    if type_name == name {
-                        let fut = ::juniper::futures::future::ready(#downcast);
-                        return #resolving_code;
-                    }
-                }
-                None => return ::juniper::macros::helper::err_unnamed_type_fut(#ty_name),
-            }
-        })
-    }
-}
-
-/// Representation of Rust enum implementing [GraphQL interface][1] type for
-/// code generation.
-///
-/// [1]: https://spec.graphql.org/June2018/#sec-Interfaces
-struct EnumType {
-    /// Name of this [`EnumType`] to generate it with.
-    ident: syn::Ident,
-
-    /// [`syn::Visibility`] of this [`EnumType`] to generate it with.
-    visibility: syn::Visibility,
-
-    /// Rust types of all [GraphQL interface][1] implements to represent
-    /// variants of this [`EnumType`].
-    ///
-    /// [1]: https://spec.graphql.org/June2018/#sec-Interfaces
-    variants: Vec<syn::Type>,
-
-    /// Name of the trait describing the [GraphQL interface][1] represented by
-    /// this [`EnumType`].
-    ///
-    /// [1]: https://spec.graphql.org/June2018/#sec-Interfaces
-    trait_ident: syn::Ident,
-
-    /// [`syn::Generics`] of the trait describing the [GraphQL interface][1]
-    /// represented by this [`EnumType`].
-    ///
-    /// [1]: https://spec.graphql.org/June2018/#sec-Interfaces
-    trait_generics: syn::Generics,
-
-    /// Associated types of the trait describing the [GraphQL interface][1]
-    /// represented by this [`EnumType`].
-    trait_types: Vec<(syn::Ident, syn::Generics)>,
+        self.fields
+            .iter()
+            .map(|field| {
+                let field_name = &field.name;
+                let mut return_ty = field.ty.clone();
+                generics.replace_type_with_defaults(&mut return_ty);
 
-    /// Associated constants of the trait describing the [GraphQL interface][1]
-    /// represented by this [`EnumType`].
-    ///
-    /// [1]: https://spec.graphql.org/June2018/#sec-Interfaces
-    trait_consts: Vec<(syn::Ident, syn::Type)>,
+                let const_ty_generics = self.const_trait_generics();
 
-    /// Methods of the trait describing the [GraphQL interface][1] represented
-    /// by this [`EnumType`].
-    ///
-    /// [1]: https://spec.graphql.org/June2018/#sec-Interfaces
-    trait_methods: Vec<syn::Signature>,
+                let unreachable_arm = (self.implementers.is_empty()
+                    || !self.trait_generics.params.is_empty())
+                .then(|| {
+                    quote! { _ => unreachable!() }
+                });
 
-    /// [`ScalarValue`] parametrization to generate [`GraphQLType`]
-    /// implementation with for this [`EnumType`].
-    ///
-    /// [`GraphQLType`]: juniper::GraphQLType
-    /// [`ScalarValue`]: juniper::ScalarValue
-    scalar: scalar::Type,
-}
-
-impl ToTokens for EnumType {
-    fn to_tokens(&self, into: &mut TokenStream) {
-        self.type_definition_tokens().to_tokens(into);
-        into.append_all(self.impl_from_tokens());
-        self.impl_trait_tokens().to_tokens(into);
-    }
-}
-
-impl EnumType {
-    /// Constructs a new [`EnumType`] out of the given parameters.
-    #[must_use]
-    fn new(
-        r#trait: &syn::ItemTrait,
-        meta: &TraitAttr,
-        implers: &[Implementer],
-        scalar: scalar::Type,
-    ) -> Self {
-        Self {
-            ident: meta
-                .r#enum
-                .as_ref()
-                .map(SpanContainer::as_ref)
-                .cloned()
-                .unwrap_or_else(|| format_ident!("{}Value", r#trait.ident)),
-            visibility: r#trait.vis.clone(),
-            variants: implers.iter().map(|impler| impler.ty.clone()).collect(),
-            trait_ident: r#trait.ident.clone(),
-            trait_generics: r#trait.generics.clone(),
-            trait_types: r#trait
-                .items
-                .iter()
-                .filter_map(|i| {
-                    if let syn::TraitItem::Type(ty) = i {
-                        Some((ty.ident.clone(), ty.generics.clone()))
-                    } else {
-                        None
-                    }
-                })
-                .collect(),
-            trait_consts: r#trait
-                .items
-                .iter()
-                .filter_map(|i| {
-                    if let syn::TraitItem::Const(cnst) = i {
-                        Some((cnst.ident.clone(), cnst.ty.clone()))
-                    } else {
-                        None
-                    }
-                })
-                .collect(),
-            trait_methods: r#trait
-                .items
-                .iter()
-                .filter_map(|i| {
-                    if let syn::TraitItem::Method(m) = i {
-                        Some(m.sig.clone())
-                    } else {
-                        None
+                quote! {
+                    #[allow(non_snake_case)]
+                    #[automatically_derived]
+                    impl#impl_generics ::juniper::macros::reflect::AsyncField<
+                        #scalar,
+                        { ::juniper::macros::reflect::fnv1a128(#field_name) }
+                    > for #ty#ty_generics #where_clause {
+                        fn call<'b>(
+                            &'b self,
+                            info: &'b Self::TypeInfo,
+                            args: &'b ::juniper::Arguments<#scalar>,
+                            executor: &'b ::juniper::Executor<Self::Context, #scalar>,
+                        ) -> ::juniper::BoxFuture<'b, ::juniper::ExecutionResult<#scalar>> {
+                            match self {
+                                #(#ty::#impl_idents(v) => {
+                                    ::juniper::assert_field!(
+                                        #ty#const_ty_generics,
+                                        #impl_tys,
+                                        #const_scalar,
+                                        #field_name,
+                                    );
+
+                                    <_ as ::juniper::macros::reflect::AsyncField<
+                                        #scalar,
+                                        { ::juniper::macros::reflect::fnv1a128(#field_name) },
+                                    >>::call(v, info, args, executor)
+                                })*
+                                #unreachable_arm
+                            }
+                        }
                     }
-                })
-                .collect(),
-            scalar,
-        }
-    }
-
-    /// Returns name of a single variant of this [`EnumType`] by the given
-    /// underlying [`syn::Type`] of the variant.
-    #[must_use]
-    fn variant_ident(ty: &syn::Type) -> &syn::Ident {
-        if let syn::Type::Path(p) = ty {
-            &p.path.segments.last().unwrap().ident
-        } else {
-            unreachable!("GraphQL object has unexpected type `{}`", quote! { #ty })
-        }
-    }
-
-    /// Indicates whether this [`EnumType`] has non-exhaustive phantom variant
-    /// to hold type parameters.
-    #[must_use]
-    fn has_phantom_variant(&self) -> bool {
-        !self.trait_generics.params.is_empty()
-    }
-
-    /// Returns generate code for dispatching non-exhaustive phantom variant of
-    /// this [`EnumType`] in `match` expressions.
-    ///
-    /// Returns [`None`] if this [`EnumType`] is exhaustive.
-    #[must_use]
-    fn non_exhaustive_match_arm_tokens(&self) -> Option<TokenStream> {
-        if self.has_phantom_variant() || self.variants.is_empty() {
-            Some(quote! { _ => unreachable!(), })
-        } else {
-            None
-        }
-    }
-
-    /// Returns prepared [`syn::Generics`] for [`GraphQLType`] trait (and
-    /// similar) implementation of this [`EnumType`].
-    ///
-    /// If `for_async` is `true`, then additional predicates are added to suit
-    /// the [`GraphQLAsyncValue`] trait (and similar) requirements.
-    ///
-    /// [`GraphQLAsyncValue`]: juniper::GraphQLAsyncValue
-    /// [`GraphQLType`]: juniper::GraphQLType
-    #[must_use]
-    fn impl_generics(&self, for_async: bool) -> syn::Generics {
-        let mut generics = self.trait_generics.clone();
-
-        let scalar = &self.scalar;
-        if scalar.is_implicit_generic() {
-            generics.params.push(parse_quote! { #scalar });
-        }
-        if scalar.is_generic() {
-            generics
-                .make_where_clause()
-                .predicates
-                .push(parse_quote! { #scalar: ::juniper::ScalarValue });
-        }
-        if let Some(bound) = scalar.bounds() {
-            generics.make_where_clause().predicates.push(bound);
-        }
-
-        if for_async {
-            let self_ty = if self.trait_generics.lifetimes().next().is_some() {
-                // Modify lifetime names to omit "lifetime name `'a` shadows a
-                // lifetime name that is already in scope" error.
-                let mut generics = self.trait_generics.clone();
-                for lt in generics.lifetimes_mut() {
-                    let ident = lt.lifetime.ident.unraw();
-                    lt.lifetime.ident = format_ident!("__fa__{}", ident);
                 }
-
-                let lifetimes = generics.lifetimes().map(|lt| &lt.lifetime);
-                let ty = &self.ident;
-                let (_, ty_generics, _) = generics.split_for_impl();
-
-                quote! { for<#( #lifetimes ),*> #ty#ty_generics }
-            } else {
-                quote! { Self }
-            };
-            generics
-                .make_where_clause()
-                .predicates
-                .push(parse_quote! { #self_ty: Sync });
-
-            if scalar.is_generic() {
-                generics
-                    .make_where_clause()
-                    .predicates
-                    .push(parse_quote! { #scalar: Send + Sync });
-            }
-        }
-
-        generics
-    }
-
-    /// Returns full type signature of the original trait describing the
-    /// [GraphQL interface][1] for this [`EnumType`].
-    ///
-    /// [1]: https://spec.graphql.org/June2018/#sec-Interfaces
-    #[must_use]
-    fn trait_ty(&self) -> syn::Type {
-        let ty = &self.trait_ident;
-        let (_, generics, _) = self.trait_generics.split_for_impl();
-
-        parse_quote! { #ty#generics }
-    }
-
-    /// Returns generated code of the full type signature of this [`EnumType`].
-    #[must_use]
-    fn ty_tokens(&self) -> TokenStream {
-        let ty = &self.ident;
-        let (_, generics, _) = self.trait_generics.split_for_impl();
-
-        quote! { #ty#generics }
+            })
+            .collect()
     }
 
-    /// Returns generate code of the Rust type definitions of this [`EnumType`].
+    /// Returns generated code for the [`GraphQLValue::concrete_type_name`][0]
+    /// method, which returns name of the underlying [`implementers`][1] GraphQL
+    /// type contained in this enum.
     ///
-    /// If the [`EnumType::trait_generics`] are not empty, then they are
-    /// contained in the generated enum too.
+    /// [0]: juniper::GraphQLValue::concrete_type_name
+    /// [1]: Self::implementers
     #[must_use]
-    fn type_definition_tokens(&self) -> TokenStream {
-        let enum_ty = &self.ident;
-        let generics = &self.trait_generics;
-        let vis = &self.visibility;
-
-        let doc = format!(
-            "Type implementing [GraphQL interface][1] represented by `{}` trait.\
-             \n\n\
-             [1]: https://spec.graphql.org/June2018/#sec-Interfaces",
-            self.trait_ident,
-        );
-
-        let variants = self.variants.iter().map(|ty| {
-            let variant = Self::variant_ident(ty);
-            let doc = format!(
-                "`{}` implementer of this GraphQL interface.",
-                quote! { #ty },
-            );
-
-            quote! {
-                #[doc = #doc]
-                #variant(#ty),
-            }
-        });
+    fn method_concrete_type_name_tokens(&self) -> TokenStream {
+        let scalar = &self.scalar;
 
-        let phantom_variant = if self.has_phantom_variant() {
-            let ty_params = generics.params.iter().map(|p| {
-                let ty = match p {
-                    syn::GenericParam::Type(ty) => {
-                        let ident = &ty.ident;
-                        quote! { #ident }
-                    }
-                    syn::GenericParam::Lifetime(lt) => {
-                        let lifetime = &lt.lifetime;
-                        quote! { &#lifetime () }
-                    }
-                    syn::GenericParam::Const(_) => unimplemented!(),
-                };
+        let match_arms = self
+            .implementers
+            .iter()
+            .filter_map(|ty| ty.path.segments.last().map(|seg| (&seg.ident, ty)))
+            .map(|(ident, ty)| {
                 quote! {
-                    ::std::marker::PhantomData<::std::sync::atomic::AtomicPtr<Box<#ty>>>
+                    Self::#ident(v) => <
+                        #ty as ::juniper::GraphQLValue<#scalar>
+                    >::concrete_type_name(v, context, info),
                 }
             });
 
-            Some(quote! {
-                #[doc(hidden)]
-                __Phantom(#( #ty_params ),*),
-            })
-        } else {
-            None
-        };
+        let non_exhaustive_match_arm =
+            (!self.trait_generics.params.is_empty() || self.implementers.is_empty()).then(|| {
+                quote! { _ => unreachable!(), }
+            });
 
         quote! {
-            #[automatically_derived]
-            #[doc = #doc]
-            #vis enum #enum_ty#generics {
-                #( #variants )*
-                #phantom_variant
+            match self {
+                #( #match_arms )*
+                #non_exhaustive_match_arm
             }
         }
     }
 
-    /// Returns generated code implementing [`From`] trait for this [`EnumType`]
-    /// from its [`EnumType::variants`].
-    fn impl_from_tokens(&self) -> impl Iterator<Item = TokenStream> + '_ {
-        let enum_ty = &self.ident;
-        let (impl_generics, generics, where_clause) = self.trait_generics.split_for_impl();
-
-        self.variants.iter().map(move |ty| {
-            let variant = Self::variant_ident(ty);
-
-            quote! {
-                #[automatically_derived]
-                impl#impl_generics From<#ty> for #enum_ty#generics #where_clause {
-                    fn from(v: #ty) -> Self {
-                        Self::#variant(v)
-                    }
-                }
-            }
-        })
-    }
-
-    /// Returns generated code implementing the original trait describing the
-    /// [GraphQL interface][1] for this [`EnumType`].
+    /// Returns generated code for the
+    /// [`GraphQLValueAsync::resolve_into_type_async`][0] method, which
+    /// downcasts this enum into its underlying [`implementers`][1] type
+    /// asynchronously.
     ///
-    /// [1]: https://spec.graphql.org/June2018/#sec-Interfaces
+    /// [0]: juniper::GraphQLValueAsync::resolve_into_type_async
+    /// [1]: Self::implementers
     #[must_use]
-    fn impl_trait_tokens(&self) -> TokenStream {
-        let enum_ty = &self.ident;
-
-        let trait_ident = &self.trait_ident;
-        let (impl_generics, generics, where_clause) = self.trait_generics.split_for_impl();
-
-        let var_ty = self.variants.first();
-
-        let assoc_types = self.trait_types.iter().map(|(ty, ty_gen)| {
-            quote! {
-                type #ty#ty_gen = <#var_ty as #trait_ident#generics>::#ty#ty_gen;
-            }
-        });
-
-        let assoc_consts = self.trait_consts.iter().map(|(ident, ty)| {
-            quote! {
-                const #ident: #ty = <#var_ty as #trait_ident#generics>::#ident;
-            }
-        });
-
-        let methods = self.trait_methods.iter().map(|sig| {
-            let method = &sig.ident;
-
-            let mut sig = sig.clone();
-            let mut args = vec![];
-            for (n, arg) in sig.inputs.iter_mut().enumerate() {
-                match arg {
-                    syn::FnArg::Receiver(_) => {}
-                    syn::FnArg::Typed(a) => {
-                        if !matches!(&*a.pat, syn::Pat::Ident(_)) {
-                            let ident = format_ident!("__arg{}", n);
-                            a.pat = parse_quote! { #ident };
-                        }
-                        args.push(a.pat.clone());
-                    }
-                }
-            }
-
-            let and_await = if sig.asyncness.is_some() {
-                Some(quote! { .await })
-            } else {
-                None
-            };
-
-            let match_arms = self.variants.iter().map(|ty| {
-                let variant = Self::variant_ident(ty);
-                let args = args.clone();
+    fn method_resolve_into_type_async_tokens(&self) -> TokenStream {
+        let resolving_code = gen::async_resolving_code(None);
 
+        let match_arms = self.implementers.iter().filter_map(|ty| {
+            ty.path.segments.last().map(|ident| {
                 quote! {
-                    Self::#variant(v) =>
-                        <#ty as #trait_ident#generics>::#method(v #( , #args )* )#and_await,
-                }
-            });
-            let non_exhaustive_match_arm = self.non_exhaustive_match_arm_tokens();
-
-            quote! {
-                #sig {
-                    match self {
-                        #( #match_arms )*
-                        #non_exhaustive_match_arm
+                    Self::#ident(v) => {
+                        let fut = ::juniper::futures::future::ready(v);
+                        #resolving_code
                     }
                 }
-            }
-        });
-
-        let mut impl_tokens = quote! {
-            #[allow(deprecated)]
-            #[automatically_derived]
-            impl#impl_generics #trait_ident#generics for #enum_ty#generics #where_clause {
-                #( #assoc_types )*
-
-                #( #assoc_consts )*
-
-                #( #methods )*
-            }
-        };
-
-        if self.trait_methods.iter().any(|sig| sig.asyncness.is_some()) {
-            let mut ast: syn::ItemImpl = parse_quote! { #impl_tokens };
-            inject_async_trait(
-                &mut ast.attrs,
-                ast.items.iter_mut().filter_map(|i| {
-                    if let syn::ImplItem::Method(m) = i {
-                        Some(&mut m.sig)
-                    } else {
-                        None
-                    }
-                }),
-                &ast.generics,
-            );
-            impl_tokens = quote! { #ast };
-        }
-
-        impl_tokens
-    }
-
-    /// Returns generated code for the [`GraphQLValue::concrete_type_name`][0]
-    /// method, which returns name of the underlying [`Implementer`] GraphQL
-    /// type contained in this [`EnumType`].
-    ///
-    /// [0]: juniper::GraphQLValue::concrete_type_name
-    #[must_use]
-    fn method_concrete_type_name_tokens(&self) -> TokenStream {
-        let scalar = &self.scalar;
-
-        let match_arms = self.variants.iter().map(|ty| {
-            let variant = Self::variant_ident(ty);
-
-            quote! {
-                Self::#variant(v) => <
-                    #ty as ::juniper::GraphQLValue<#scalar>
-                >::concrete_type_name(v, context, info),
-            }
+            })
         });
-        let non_exhaustive_match_arm = self.non_exhaustive_match_arm_tokens();
+        let non_exhaustive_match_arm =
+            (!self.trait_generics.params.is_empty() || self.implementers.is_empty()).then(|| {
+                quote! { _ => unreachable!(), }
+            });
 
         quote! {
             match self {
@@ -1344,22 +1047,27 @@ impl EnumType {
     }
 
     /// Returns generated code for the [`GraphQLValue::resolve_into_type`][0]
-    /// method, which downcasts this [`EnumType`] into its underlying
-    /// [`Implementer`] type synchronously.
+    /// method, which resolves this enum into its underlying
+    /// [`implementers`][1] type synchronously.
     ///
     /// [0]: juniper::GraphQLValue::resolve_into_type
+    /// [1]: Self::implementers
     #[must_use]
     fn method_resolve_into_type_tokens(&self) -> TokenStream {
         let resolving_code = gen::sync_resolving_code();
 
-        let match_arms = self.variants.iter().map(|ty| {
-            let variant = Self::variant_ident(ty);
-
-            quote! {
-                Self::#variant(res) => #resolving_code,
-            }
+        let match_arms = self.implementers.iter().filter_map(|ty| {
+            ty.path.segments.last().map(|ident| {
+                quote! {
+                    Self::#ident(res) => #resolving_code,
+                }
+            })
         });
-        let non_exhaustive_match_arm = self.non_exhaustive_match_arm_tokens();
+
+        let non_exhaustive_match_arm =
+            (!self.trait_generics.params.is_empty() || self.implementers.is_empty()).then(|| {
+                quote! { _ => unreachable!(), }
+            });
 
         quote! {
             match self {
@@ -1369,94 +1077,47 @@ impl EnumType {
         }
     }
 
-    /// Returns generated code for the
-    /// [`GraphQLValueAsync::resolve_into_type_async`][0] method, which
-    /// downcasts this [`EnumType`] into its underlying [`Implementer`] type
-    /// asynchronously.
-    ///
-    /// [0]: juniper::GraphQLValueAsync::resolve_into_type_async
+    /// Returns trait generics replaced with the default values for usage in a
+    /// `const` context.
     #[must_use]
-    fn method_resolve_into_type_async_tokens(&self) -> TokenStream {
-        let resolving_code = gen::async_resolving_code(None);
-
-        let match_arms = self.variants.iter().map(|ty| {
-            let variant = Self::variant_ident(ty);
-
-            quote! {
-                Self::#variant(v) => {
-                    let fut = ::juniper::futures::future::ready(v);
-                    #resolving_code
-                }
-            }
-        });
-        let non_exhaustive_match_arm = self.non_exhaustive_match_arm_tokens();
+    fn const_trait_generics(&self) -> syn::PathArguments {
+        struct GenericsForConst(syn::AngleBracketedGenericArguments);
 
-        quote! {
-            match self {
-                #( #match_arms )*
-                #non_exhaustive_match_arm
+        impl Visit<'_> for GenericsForConst {
+            fn visit_generic_param(&mut self, param: &syn::GenericParam) {
+                let arg = match param {
+                    syn::GenericParam::Lifetime(_) => parse_quote! { 'static },
+                    syn::GenericParam::Type(ty) => {
+                        if ty.default.is_none() {
+                            parse_quote! { ::juniper::DefaultScalarValue }
+                        } else {
+                            return;
+                        }
+                    }
+                    syn::GenericParam::Const(c) => {
+                        if c.default.is_none() {
+                            // This hack works because only `min_const_generics`
+                            // are enabled for now.
+                            // TODO: Replace this once full `const_generics` are
+                            //       available.
+                            //       Maybe with `<_ as Default>::default()`?
+                            parse_quote!({ 0_u8 as _ })
+                        } else {
+                            return;
+                        }
+                    }
+                };
+                self.0.args.push(arg)
             }
         }
-    }
-}
-
-/// Representation of Rust [trait object][2] implementing [GraphQL interface][1]
-/// type for code generation.
-///
-/// [1]: https://spec.graphql.org/June2018/#sec-Interfaces
-/// [2]: https://doc.rust-lang.org/reference/types/trait-object.html
-struct TraitObjectType {
-    /// Name of this [`TraitObjectType`] to generate it with.
-    ident: syn::Ident,
-
-    /// [`syn::Visibility`] of this [`TraitObjectType`] to generate it with.
-    visibility: syn::Visibility,
-
-    /// Name of the trait describing the [GraphQL interface][1] represented by
-    /// this [`TraitObjectType`].
-    ///
-    /// [1]: https://spec.graphql.org/June2018/#sec-Interfaces
-    trait_ident: syn::Ident,
-
-    /// [`syn::Generics`] of the trait describing the [GraphQL interface][1]
-    /// represented by this [`TraitObjectType`].
-    ///
-    /// [1]: https://spec.graphql.org/June2018/#sec-Interfaces
-    trait_generics: syn::Generics,
-
-    /// [`ScalarValue`] parametrization of this [`TraitObjectType`] to generate
-    /// it with.
-    ///
-    /// [`ScalarValue`]: juniper::ScalarValue
-    scalar: scalar::Type,
-
-    /// Rust type of [`Context`] to generate this [`TraitObjectType`] with.
-    ///
-    /// [`Context`]: juniper::Context
-    context: syn::Type,
-}
 
-impl TraitObjectType {
-    /// Constructs a new [`TraitObjectType`] out of the given parameters.
-    #[must_use]
-    fn new(
-        r#trait: &syn::ItemTrait,
-        meta: &TraitAttr,
-        scalar: scalar::Type,
-        context: syn::Type,
-    ) -> Self {
-        Self {
-            ident: meta.r#dyn.as_ref().unwrap().as_ref().clone(),
-            visibility: r#trait.vis.clone(),
-            trait_ident: r#trait.ident.clone(),
-            trait_generics: r#trait.generics.clone(),
-            scalar,
-            context,
-        }
+        let mut visitor = GenericsForConst(parse_quote!( <> ));
+        visitor.visit_generics(&self.trait_generics);
+        syn::PathArguments::AngleBracketed(visitor.0)
     }
 
     /// Returns prepared [`syn::Generics`] for [`GraphQLType`] trait (and
-    /// similar) implementation of this [`TraitObjectType`].
+    /// similar) implementation of this enum.
     ///
     /// If `for_async` is `true`, then additional predicates are added to suit
     /// the [`GraphQLAsyncValue`] trait (and similar) requirements.
@@ -1467,8 +1128,6 @@ impl TraitObjectType {
     fn impl_generics(&self, for_async: bool) -> syn::Generics {
         let mut generics = self.trait_generics.clone();
 
-        generics.params.push(parse_quote! { '__obj });
-
         let scalar = &self.scalar;
         if scalar.is_implicit_generic() {
             generics.params.push(parse_quote! { #scalar });
@@ -1484,10 +1143,28 @@ impl TraitObjectType {
         }
 
         if for_async {
+            let self_ty = if self.trait_generics.lifetimes().next().is_some() {
+                // Modify lifetime names to omit "lifetime name `'a` shadows a
+                // lifetime name that is already in scope" error.
+                let mut generics = self.trait_generics.clone();
+                for lt in generics.lifetimes_mut() {
+                    let ident = lt.lifetime.ident.unraw();
+                    lt.lifetime.ident = format_ident!("__fa__{}", ident);
+                }
+
+                let lifetimes = generics.lifetimes().map(|lt| &lt.lifetime);
+                let ty = &self.enum_alias_ident;
+                let (_, ty_generics, _) = generics.split_for_impl();
+
+                quote! { for<#( #lifetimes ),*> #ty#ty_generics }
+            } else {
+                quote! { Self }
+            };
             generics
                 .make_where_clause()
                 .predicates
-                .push(parse_quote! { Self: Sync });
+                .push(parse_quote! { #self_ty: Sync });
+
             if scalar.is_generic() {
                 generics
                     .make_where_clause()
@@ -1499,272 +1176,10 @@ impl TraitObjectType {
         generics
     }
 
-    /// Returns full type signature of the original trait describing the
-    /// [GraphQL interface][1] for this [`TraitObjectType`].
-    ///
-    /// [1]: https://spec.graphql.org/June2018/#sec-Interfaces
-    #[must_use]
-    fn trait_ty(&self) -> syn::Type {
-        let ty = &self.trait_ident;
-
-        let mut generics = self.trait_generics.clone();
-        if !self.scalar.is_explicit_generic() {
-            let scalar = &self.scalar;
-            generics.params.push(parse_quote! { #scalar });
-        }
-        let (_, generics, _) = generics.split_for_impl();
-
-        parse_quote! { #ty#generics }
-    }
-
-    /// Returns generated code of the full type signature of this
-    /// [`TraitObjectType`].
-    #[must_use]
-    fn ty_tokens(&self) -> TokenStream {
-        let ty = &self.trait_ident;
-
-        let mut generics = self.trait_generics.clone();
-        generics.remove_defaults();
-        generics.move_bounds_to_where_clause();
-        if !self.scalar.is_explicit_generic() {
-            let scalar = &self.scalar;
-            generics.params.push(parse_quote! { #scalar });
-        }
-        let ty_params = &generics.params;
-
-        let context = &self.context;
-
-        quote! {
-            dyn #ty<#ty_params, Context = #context, TypeInfo = ()> + '__obj + Send + Sync
-        }
-    }
-
-    /// Returns generated code for the [`GraphQLValue::concrete_type_name`][0]
-    /// method, which returns name of the underlying [`Implementer`] GraphQL
-    /// type contained in this [`TraitObjectType`].
-    ///
-    /// [0]: juniper::GraphQLValue::concrete_type_name
-    #[must_use]
-    fn method_concrete_type_name_tokens(&self) -> TokenStream {
-        quote! {
-            self.as_dyn_graphql_value().concrete_type_name(context, info)
-        }
-    }
-
-    /// Returns generated code for the [`GraphQLValue::resolve_into_type`][0]
-    /// method, which downcasts this [`TraitObjectType`] into its underlying
-    /// [`Implementer`] type synchronously.
-    ///
-    /// [0]: juniper::GraphQLValue::resolve_into_type
-    #[must_use]
-    fn method_resolve_into_type_tokens(&self) -> TokenStream {
-        let resolving_code = gen::sync_resolving_code();
-
-        quote! {
-            let res = self.as_dyn_graphql_value();
-            #resolving_code
-        }
-    }
-
-    /// Returns generated code for the
-    /// [`GraphQLValueAsync::resolve_into_type_async`][0] method, which
-    /// downcasts this [`TraitObjectType`] into its underlying [`Implementer`]
-    /// type asynchronously.
-    ///
-    /// [0]: juniper::GraphQLValueAsync::resolve_into_type_async
-    #[must_use]
-    fn method_resolve_into_type_async_tokens(&self) -> TokenStream {
-        let resolving_code = gen::async_resolving_code(None);
-
-        quote! {
-            let fut = ::juniper::futures::future::ready(self.as_dyn_graphql_value_async());
-            #resolving_code
-        }
-    }
-}
-
-impl ToTokens for TraitObjectType {
-    fn to_tokens(&self, into: &mut TokenStream) {
-        let dyn_ty = &self.ident;
-        let vis = &self.visibility;
-
-        let doc = format!(
-            "Helper alias for the `{}` [trait object][2] implementing [GraphQL interface][1].\
-             \n\n\
-             [1]: https://spec.graphql.org/June2018/#sec-Interfaces\n\
-             [2]: https://doc.rust-lang.org/reference/types/trait-object.html",
-            self.trait_ident,
-        );
-
-        let trait_ident = &self.trait_ident;
-
-        let mut generics = self.trait_generics.clone();
-        if !self.scalar.is_explicit_generic() {
-            let scalar_ty = self.scalar.generic_ty();
-            let default_ty = self.scalar.default_ty();
-            generics
-                .params
-                .push(parse_quote! { #scalar_ty = #default_ty });
-        }
-
-        let (mut ty_params_left, mut ty_params_right) = (None, None);
-        if !generics.params.is_empty() {
-            // We should preserve defaults for left side.
-            generics.move_bounds_to_where_clause();
-            let params = &generics.params;
-            ty_params_left = Some(quote! { , #params });
-
-            generics.remove_defaults();
-            let params = &generics.params;
-            ty_params_right = Some(quote! { #params, });
-        };
-
-        let context = &self.context;
-
-        let dyn_alias = quote! {
-            #[automatically_derived]
-            #[doc = #doc]
-            #vis type #dyn_ty<'a #ty_params_left> =
-                dyn #trait_ident<#ty_params_right Context = #context, TypeInfo = ()> +
-                    'a + Send + Sync;
-        };
-
-        into.append_all(&[dyn_alias]);
-    }
-}
-
-/// Representation of possible Rust types implementing [GraphQL interface][1]
-/// type for code generation.
-///
-/// [1]: https://spec.graphql.org/June2018/#sec-Interfaces
-enum Type {
-    /// [GraphQL interface][1] type implementation as Rust enum.
-    ///
-    /// [1]: https://spec.graphql.org/June2018/#sec-Interfaces
-    Enum(Box<EnumType>),
-
-    /// [GraphQL interface][1] type implementation as Rust [trait object][2].
-    ///
-    /// [1]: https://spec.graphql.org/June2018/#sec-Interfaces
-    /// [2]: https://doc.rust-lang.org/reference/types/trait-object.html
-    TraitObject(Box<TraitObjectType>),
-}
-
-impl ToTokens for Type {
-    fn to_tokens(&self, into: &mut TokenStream) {
-        match self {
-            Self::Enum(e) => e.to_tokens(into),
-            Self::TraitObject(o) => o.to_tokens(into),
-        }
-    }
-}
-
-impl Type {
-    /// Returns prepared [`syn::Generics`] for [`GraphQLType`] trait (and
-    /// similar) implementation of this [`Type`].
-    ///
-    /// If `for_async` is `true`, then additional predicates are added to suit
-    /// the [`GraphQLAsyncValue`] trait (and similar) requirements.
-    ///
-    /// [`GraphQLAsyncValue`]: juniper::GraphQLAsyncValue
-    /// [`GraphQLType`]: juniper::GraphQLType
-    #[must_use]
-    fn impl_generics(&self, for_async: bool) -> (TokenStream, Option<syn::WhereClause>) {
-        let generics = match self {
-            Self::Enum(e) => e.impl_generics(for_async),
-            Self::TraitObject(o) => o.impl_generics(for_async),
-        };
-        let (impl_generics, _, where_clause) = generics.split_for_impl();
-        (quote! { #impl_generics }, where_clause.cloned())
-    }
-
-    /// Returns full type signature of the original trait describing the
-    /// [GraphQL interface][1] for this [`Type`].
-    ///
-    /// [1]: https://spec.graphql.org/June2018/#sec-Interfaces
-    #[must_use]
-    fn trait_ty(&self) -> syn::Type {
-        match self {
-            Self::Enum(e) => e.trait_ty(),
-            Self::TraitObject(o) => o.trait_ty(),
-        }
-    }
-
-    /// Returns generated code of the full type signature of this [`Type`].
-    #[must_use]
-    fn ty_tokens(&self) -> TokenStream {
-        match self {
-            Self::Enum(e) => e.ty_tokens(),
-            Self::TraitObject(o) => o.ty_tokens(),
-        }
-    }
-
-    /// Returns generated code for the [`GraphQLValue::concrete_type_name`][0]
-    /// method, which returns name of the underlying [`Implementer`] GraphQL
-    /// type contained in this [`Type`].
-    ///
-    /// [0]: juniper::GraphQLValue::concrete_type_name
+    /// Indicates whether this enum has non-exhaustive phantom variant to hold
+    /// type parameters.
     #[must_use]
-    fn method_concrete_type_name_tokens(&self) -> TokenStream {
-        match self {
-            Self::Enum(e) => e.method_concrete_type_name_tokens(),
-            Self::TraitObject(o) => o.method_concrete_type_name_tokens(),
-        }
-    }
-
-    /// Returns generated code for the [`GraphQLValue::resolve_into_type`][0]
-    /// method, which downcasts this [`Type`] into its underlying
-    /// [`Implementer`] type synchronously.
-    ///
-    /// [0]: juniper::GraphQLValue::resolve_into_type
-    #[must_use]
-    fn method_resolve_into_type_tokens(&self) -> TokenStream {
-        match self {
-            Self::Enum(e) => e.method_resolve_into_type_tokens(),
-            Self::TraitObject(o) => o.method_resolve_into_type_tokens(),
-        }
-    }
-
-    /// Returns generated code for the
-    /// [`GraphQLValueAsync::resolve_into_type_async`][0] method, which
-    /// downcasts this [`Type`] into its underlying [`Implementer`] type
-    /// asynchronously.
-    ///
-    /// [0]: juniper::GraphQLValueAsync::resolve_into_type_async
-    fn method_resolve_into_type_async_tokens(&self) -> TokenStream {
-        match self {
-            Self::Enum(e) => e.method_resolve_into_type_async_tokens(),
-            Self::TraitObject(o) => o.method_resolve_into_type_async_tokens(),
-        }
-    }
-}
-
-/// Injects [`async_trait`] implementation into the given trait definition or
-/// trait implementation block, correctly restricting type and lifetime
-/// parameters with `'async_trait` lifetime, if required.
-fn inject_async_trait<'m, M>(attrs: &mut Vec<syn::Attribute>, methods: M, generics: &syn::Generics)
-where
-    M: IntoIterator<Item = &'m mut syn::Signature>,
-{
-    attrs.push(parse_quote! { #[::juniper::async_trait] });
-
-    for method in methods.into_iter() {
-        if method.asyncness.is_some() {
-            let where_clause = &mut method.generics.make_where_clause().predicates;
-            for p in &generics.params {
-                let ty_param = match p {
-                    syn::GenericParam::Type(t) => {
-                        let ty_param = &t.ident;
-                        quote! { #ty_param }
-                    }
-                    syn::GenericParam::Lifetime(l) => {
-                        let ty_param = &l.lifetime;
-                        quote! { #ty_param }
-                    }
-                    syn::GenericParam::Const(_) => continue,
-                };
-                where_clause.push(parse_quote! { #ty_param: 'async_trait });
-            }
-        }
+    fn has_phantom_variant(&self) -> bool {
+        !self.trait_generics.params.is_empty()
     }
 }
diff --git a/juniper_codegen/src/graphql_object/mod.rs b/juniper_codegen/src/graphql_object/mod.rs
index 171e00a3d..49459737b 100644
--- a/juniper_codegen/src/graphql_object/mod.rs
+++ b/juniper_codegen/src/graphql_object/mod.rs
@@ -18,10 +18,10 @@ use syn::{
 
 use crate::{
     common::{
-        field,
+        field, gen,
         parse::{
             attr::{err, OptionExt as _},
-            ParseBufferExt as _, TypeExt,
+            GenericsExt as _, ParseBufferExt as _, TypeExt,
         },
         scalar,
     },
@@ -361,6 +361,68 @@ impl<Operation: ?Sized + 'static> Definition<Operation> {
         }
     }
 
+    /// Returns generated code implementing [`BaseType`], [`BaseSubTypes`],
+    /// [`WrappedType`] and [`Fields`] traits for this [GraphQL object][1].
+    ///
+    /// [`BaseSubTypes`]: juniper::macros::reflect::BaseSubTypes
+    /// [`BaseType`]: juniper::macros::reflect::BaseType
+    /// [`Fields`]: juniper::macros::reflect::Fields
+    /// [`WrappedType`]: juniper::macros::reflect::WrappedType
+    /// [1]: https://spec.graphql.org/June2018/#sec-Objects
+    #[must_use]
+    pub(crate) fn impl_reflection_traits_tokens(&self) -> TokenStream {
+        let scalar = &self.scalar;
+        let name = &self.name;
+        let (impl_generics, where_clause) = self.impl_generics(false);
+        let ty = &self.ty;
+        let fields = self.fields.iter().map(|f| &f.name);
+        let interfaces = self.interfaces.iter();
+
+        quote! {
+            #[automatically_derived]
+            impl#impl_generics ::juniper::macros::reflect::BaseType<#scalar>
+                for #ty
+                #where_clause
+            {
+                const NAME: ::juniper::macros::reflect::Type = #name;
+            }
+
+            #[automatically_derived]
+            impl#impl_generics ::juniper::macros::reflect::BaseSubTypes<#scalar>
+                for #ty
+                #where_clause
+            {
+                const NAMES: ::juniper::macros::reflect::Types =
+                    &[<Self as ::juniper::macros::reflect::BaseType<#scalar>>::NAME];
+            }
+
+            #[automatically_derived]
+            impl#impl_generics ::juniper::macros::reflect::Implements<#scalar>
+                for #ty
+                #where_clause
+            {
+                const NAMES: ::juniper::macros::reflect::Types =
+                    &[#(<#interfaces as ::juniper::macros::reflect::BaseType<#scalar>>::NAME),*];
+            }
+
+            #[automatically_derived]
+            impl#impl_generics ::juniper::macros::reflect::WrappedType<#scalar>
+                for #ty
+                #where_clause
+            {
+                const VALUE: ::juniper::macros::reflect::WrappedValue = 1;
+            }
+
+            #[automatically_derived]
+            impl#impl_generics ::juniper::macros::reflect::Fields<#scalar>
+                for #ty
+                #where_clause
+            {
+                const NAMES: ::juniper::macros::reflect::Names = &[#(#fields),*];
+            }
+        }
+    }
+
     /// Returns generated code implementing [`GraphQLType`] trait for this
     /// [GraphQL object][1].
     ///
@@ -439,6 +501,10 @@ impl ToTokens for Definition<Query> {
         self.impl_graphql_value_tokens().to_tokens(into);
         self.impl_graphql_value_async_tokens().to_tokens(into);
         self.impl_as_dyn_graphql_value_tokens().to_tokens(into);
+        self.impl_reflection_traits_tokens().to_tokens(into);
+        self.impl_field_meta_tokens().to_tokens(into);
+        self.impl_field_tokens().to_tokens(into);
+        self.impl_async_field_tokens().to_tokens(into);
     }
 }
 
@@ -451,11 +517,25 @@ impl Definition<Query> {
     #[must_use]
     fn impl_graphql_object_tokens(&self) -> TokenStream {
         let scalar = &self.scalar;
+        let const_scalar = self.scalar.default_ty();
 
         let (impl_generics, where_clause) = self.impl_generics(false);
         let ty = &self.ty;
 
         let interface_tys = self.interfaces.iter();
+
+        let generics = {
+            let mut generics = self.generics.clone();
+            if scalar.is_implicit_generic() {
+                generics.params.push(parse_quote! { #scalar })
+            }
+            generics
+        };
+        let const_interface_tys = interface_tys.clone().cloned().map(|mut ty| {
+            generics.replace_type_with_defaults(&mut ty);
+            ty
+        });
+
         // TODO: Make it work by repeating `sa::assert_type_ne_all!` expansion,
         //       but considering generics.
         //let interface_tys: Vec<_> = self.interfaces.iter().collect();
@@ -469,11 +549,210 @@ impl Definition<Query> {
             {
                 fn mark() {
                     #( <#interface_tys as ::juniper::marker::GraphQLInterface<#scalar>>::mark(); )*
+                    ::juniper::assert_implemented_for!(
+                        #const_scalar, #ty, #(#const_interface_tys),*
+                    );
                 }
             }
         }
     }
 
+    /// Returns generated code implementing [`FieldMeta`] traits for each field
+    /// of this [GraphQL object][1].
+    ///
+    /// [`FieldMeta`]: juniper::FieldMeta
+    /// [1]: https://spec.graphql.org/June2018/#sec-Objects
+    #[must_use]
+    fn impl_field_meta_tokens(&self) -> TokenStream {
+        let impl_ty = &self.ty;
+        let scalar = &self.scalar;
+        let context = &self.context;
+        let (impl_generics, where_clause) = self.impl_generics(false);
+
+        self.fields
+            .iter()
+            .map(|field| {
+                let (name, ty) = (&field.name, field.ty.clone());
+
+                let arguments = field
+                    .arguments
+                    .as_ref()
+                    .iter()
+                    .flat_map(|vec| vec.iter())
+                    .filter_map(|arg| match arg {
+                        field::MethodArgument::Regular(arg) => {
+                            let (name, ty) = (&arg.name, &arg.ty);
+                            Some(quote! {(
+                                #name,
+                                <#ty as ::juniper::macros::reflect::BaseType<#scalar>>::NAME,
+                                <#ty as ::juniper::macros::reflect::WrappedType<#scalar>>::VALUE,
+                            )})
+                        }
+                        field::MethodArgument::Executor | field::MethodArgument::Context(_) => None,
+                    })
+                    .collect::<Vec<_>>();
+
+                quote! {
+                    #[allow(deprecated, non_snake_case)]
+                    #[automatically_derived]
+                    impl #impl_generics ::juniper::macros::reflect::FieldMeta<
+                        #scalar,
+                        { ::juniper::macros::reflect::fnv1a128(#name) }
+                    > for #impl_ty #where_clause {
+                        type Context = #context;
+                        type TypeInfo = ();
+                        const TYPE: ::juniper::macros::reflect::Type =
+                            <#ty as ::juniper::macros::reflect::BaseType<#scalar>>::NAME;
+                        const SUB_TYPES: ::juniper::macros::reflect::Types =
+                            <#ty as ::juniper::macros::reflect::BaseSubTypes<#scalar>>::NAMES;
+                        const WRAPPED_VALUE: juniper::macros::reflect::WrappedValue =
+                            <#ty as ::juniper::macros::reflect::WrappedType<#scalar>>::VALUE;
+                        const ARGUMENTS: &'static [(
+                            ::juniper::macros::reflect::Name,
+                            ::juniper::macros::reflect::Type,
+                            ::juniper::macros::reflect::WrappedValue,
+                        )] = &[#(#arguments,)*];
+                    }
+                }
+            })
+            .collect()
+    }
+
+    /// Returns generated code implementing [`Field`] trait for each field of
+    /// this [GraphQL object][1].
+    ///
+    /// [`Field`]: juniper::Field
+    /// [1]: https://spec.graphql.org/June2018/#sec-Objects
+    #[must_use]
+    fn impl_field_tokens(&self) -> TokenStream {
+        let (impl_ty, scalar) = (&self.ty, &self.scalar);
+        let (impl_generics, where_clause) = self.impl_generics(false);
+
+        self.fields
+            .iter()
+            .map(|field| {
+                let (name, mut res_ty, ident) = (&field.name, field.ty.clone(), &field.ident);
+
+                let resolve = if field.is_async {
+                    quote! {
+                        ::std::panic!(
+                             "Tried to resolve async field `{}` on type `{}` with a sync resolver",
+                             #name,
+                             <Self as ::juniper::macros::reflect::BaseType<#scalar>>::NAME,
+                         );
+                    }
+                } else {
+                    let res = if field.is_method() {
+                        let args = field
+                            .arguments
+                            .as_ref()
+                            .unwrap()
+                            .iter()
+                            .map(|arg| arg.method_resolve_field_tokens(scalar, false));
+
+                        let rcv = field.has_receiver.then(|| {
+                            quote! { self, }
+                        });
+
+                        quote! { Self::#ident(#rcv #( #args ),*) }
+                    } else {
+                        res_ty = parse_quote! { _ };
+                        quote! { &self.#ident }
+                    };
+
+                    let resolving_code = gen::sync_resolving_code();
+
+                    quote! {
+                        let res: #res_ty = #res;
+                        #resolving_code
+                    }
+                };
+
+                quote! {
+                    #[allow(deprecated, non_snake_case)]
+                    #[automatically_derived]
+                    impl #impl_generics ::juniper::macros::reflect::Field<
+                        #scalar,
+                        { ::juniper::macros::reflect::fnv1a128(#name) }
+                    > for #impl_ty
+                        #where_clause
+                    {
+                        fn call(
+                            &self,
+                            info: &Self::TypeInfo,
+                            args: &::juniper::Arguments<#scalar>,
+                            executor: &::juniper::Executor<Self::Context, #scalar>,
+                        ) -> ::juniper::ExecutionResult<#scalar> {
+                            #resolve
+                        }
+                    }
+                }
+            })
+            .collect()
+    }
+
+    /// Returns generated code implementing [`AsyncField`] trait for each field
+    /// of this [GraphQL object][1].
+    ///
+    /// [`AsyncField`]: juniper::AsyncField
+    /// [1]: https://spec.graphql.org/June2018/#sec-Objects
+    #[must_use]
+    fn impl_async_field_tokens(&self) -> TokenStream {
+        let (impl_ty, scalar) = (&self.ty, &self.scalar);
+        let (impl_generics, where_clause) = self.impl_generics(true);
+
+        self.fields
+            .iter()
+            .map(|field| {
+                let (name, mut res_ty, ident) = (&field.name, field.ty.clone(), &field.ident);
+
+                let mut res = if field.is_method() {
+                    let args = field
+                        .arguments
+                        .as_ref()
+                        .unwrap()
+                        .iter()
+                        .map(|arg| arg.method_resolve_field_tokens(scalar, true));
+
+                    let rcv = field.has_receiver.then(|| {
+                        quote! { self, }
+                    });
+
+                    quote! { Self::#ident(#rcv #( #args ),*) }
+                } else {
+                    res_ty = parse_quote! { _ };
+                    quote! { &self.#ident }
+                };
+                if !field.is_async {
+                    res = quote! { ::juniper::futures::future::ready(#res) };
+                }
+
+                let resolving_code = gen::async_resolving_code(Some(&res_ty));
+
+                quote! {
+                    #[allow(deprecated, non_snake_case)]
+                    #[automatically_derived]
+                    impl #impl_generics ::juniper::macros::reflect::AsyncField<
+                        #scalar,
+                        { ::juniper::macros::reflect::fnv1a128(#name) }
+                    > for #impl_ty
+                        #where_clause
+                    {
+                        fn call<'b>(
+                            &'b self,
+                            info: &'b Self::TypeInfo,
+                            args: &'b ::juniper::Arguments<#scalar>,
+                            executor: &'b ::juniper::Executor<Self::Context, #scalar>,
+                        ) -> ::juniper::BoxFuture<'b, ::juniper::ExecutionResult<#scalar>> {
+                            let fut = #res;
+                            #resolving_code
+                        }
+                    }
+                }
+            })
+            .collect()
+    }
+
     /// Returns generated code implementing [`GraphQLValue`] trait for this
     /// [GraphQL object][1].
     ///
@@ -490,22 +769,18 @@ impl Definition<Query> {
 
         let name = &self.name;
 
-        let fields_resolvers = self
-            .fields
-            .iter()
-            .filter_map(|f| f.method_resolve_field_tokens(scalar, None));
-        let async_fields_err = {
-            let names = self
-                .fields
-                .iter()
-                .filter_map(|f| f.is_async.then(|| f.name.as_str()))
-                .collect::<Vec<_>>();
-            (!names.is_empty()).then(|| {
-                field::Definition::method_resolve_field_err_async_field_tokens(
-                    &names, scalar, &ty_name,
-                )
-            })
-        };
+        let fields_resolvers = self.fields.iter().map(|f| {
+            let name = &f.name;
+            quote! {
+                #name => {
+                    ::juniper::macros::reflect::Field::<
+                        #scalar,
+                        { ::juniper::macros::reflect::fnv1a128(#name) }
+                    >::call(self, info, args, executor)
+                }
+            }
+        });
+
         let no_field_err =
             field::Definition::method_resolve_field_err_no_field_tokens(scalar, &ty_name);
 
@@ -530,7 +805,6 @@ impl Definition<Query> {
                 ) -> ::juniper::ExecutionResult<#scalar> {
                     match field {
                         #( #fields_resolvers )*
-                        #async_fields_err
                         _ => #no_field_err,
                     }
                 }
@@ -559,10 +833,18 @@ impl Definition<Query> {
         let ty = &self.ty;
         let ty_name = ty.to_token_stream().to_string();
 
-        let fields_resolvers = self
-            .fields
-            .iter()
-            .map(|f| f.method_resolve_field_async_tokens(scalar, None));
+        let fields_resolvers = self.fields.iter().map(|f| {
+            let name = &f.name;
+            quote! {
+                #name => {
+                    ::juniper::macros::reflect::AsyncField::<
+                        #scalar,
+                        { ::juniper::macros::reflect::fnv1a128(#name) }
+                    >::call(self, info, args, executor)
+                }
+            }
+        });
+
         let no_field_err =
             field::Definition::method_resolve_field_err_no_field_tokens(scalar, &ty_name);
 
diff --git a/juniper_codegen/src/graphql_union/mod.rs b/juniper_codegen/src/graphql_union/mod.rs
index 00ba528e5..4c3bafca5 100644
--- a/juniper_codegen/src/graphql_union/mod.rs
+++ b/juniper_codegen/src/graphql_union/mod.rs
@@ -320,6 +320,7 @@ impl ToTokens for Definition {
         self.impl_graphql_type_tokens().to_tokens(into);
         self.impl_graphql_value_tokens().to_tokens(into);
         self.impl_graphql_value_async_tokens().to_tokens(into);
+        self.impl_reflection_traits_tokens().to_tokens(into);
     }
 }
 
@@ -609,6 +610,50 @@ impl Definition {
             }
         }
     }
+
+    /// Returns generated code implementing [`BaseType`], [`BaseSubTypes`] and
+    /// [`WrappedType`] traits for this [GraphQL union][1].
+    ///
+    /// [`BaseSubTypes`]: juniper::macros::reflect::BaseSubTypes
+    /// [`BaseType`]: juniper::macros::reflect::BaseType
+    /// [`WrappedType`]: juniper::macros::reflect::WrappedType
+    /// [1]: https://spec.graphql.org/June2018/#sec-Unions
+    #[must_use]
+    pub(crate) fn impl_reflection_traits_tokens(&self) -> TokenStream {
+        let scalar = &self.scalar;
+        let name = &self.name;
+        let variants = self.variants.iter().map(|var| &var.ty);
+        let (impl_generics, ty, where_clause) = self.impl_generics(false);
+
+        quote! {
+            #[automatically_derived]
+            impl#impl_generics ::juniper::macros::reflect::BaseType<#scalar>
+                for #ty
+                #where_clause
+            {
+                const NAME: ::juniper::macros::reflect::Type = #name;
+            }
+
+            #[automatically_derived]
+            impl#impl_generics ::juniper::macros::reflect::BaseSubTypes<#scalar>
+                for #ty
+                #where_clause
+            {
+                const NAMES: ::juniper::macros::reflect::Types = &[
+                    <Self as ::juniper::macros::reflect::BaseType<#scalar>>::NAME,
+                    #(<#variants as ::juniper::macros::reflect::BaseType<#scalar>>::NAME),*
+                ];
+            }
+
+            #[automatically_derived]
+            impl#impl_generics ::juniper::macros::reflect::WrappedType<#scalar>
+                for #ty
+                #where_clause
+            {
+                const VALUE: ::juniper::macros::reflect::WrappedValue = 1;
+            }
+        }
+    }
 }
 
 /// Definition of [GraphQL union][1] variant for code generation.
diff --git a/juniper_codegen/src/impl_scalar.rs b/juniper_codegen/src/impl_scalar.rs
index a790abac5..e40aa41de 100644
--- a/juniper_codegen/src/impl_scalar.rs
+++ b/juniper_codegen/src/impl_scalar.rs
@@ -327,6 +327,25 @@ pub fn build_scalar(
                 #from_str_body
             }
         }
+
+        impl#generic_type_decl ::juniper::macros::reflect::BaseType<#generic_type> for #impl_for_type
+            #generic_type_bound
+        {
+            const NAME: ::juniper::macros::reflect::Type = #name;
+        }
+
+        impl#generic_type_decl ::juniper::macros::reflect::BaseSubTypes<#generic_type> for #impl_for_type
+            #generic_type_bound
+        {
+            const NAMES: ::juniper::macros::reflect::Types =
+                &[<Self as ::juniper::macros::reflect::BaseType<#generic_type>>::NAME];
+        }
+
+        impl#generic_type_decl ::juniper::macros::reflect::WrappedType<#generic_type> for #impl_for_type
+            #generic_type_bound
+        {
+            const VALUE: ::juniper::macros::reflect::WrappedValue = 1;
+        }
     );
 
     Ok(content)
diff --git a/juniper_codegen/src/lib.rs b/juniper_codegen/src/lib.rs
index 1e426af3d..4aff89d1e 100644
--- a/juniper_codegen/src/lib.rs
+++ b/juniper_codegen/src/lib.rs
@@ -271,70 +271,36 @@ pub fn graphql_scalar(args: TokenStream, input: TokenStream) -> TokenStream {
 /// Specifying multiple `#[graphql_interface]` attributes on the same definition
 /// is totally okay. They all will be treated as a single attribute.
 ///
-/// The main difference between [GraphQL interface][1] type and Rust trait is
+/// [GraphQL interfaces][1] are more like structurally-typed interfaces, while
+/// Rust's traits are more like type classes. Using `impl Trait` isn't an
+/// option, so you have to cover all trait's methods with type's fields or
+/// impl block. But no one is stopping you from additionally implementing trait
+/// manually.
+///
+/// Another difference between [GraphQL interface][1] type and Rust trait is
 /// that the former serves both as an _abstraction_ and a _value downcastable to
 /// concrete implementers_, while in Rust, a trait is an _abstraction only_ and
 /// you need a separate type to downcast into a concrete implementer, like enum
 /// or [trait object][3], because trait doesn't represent a type itself.
-/// Macro uses Rust enum to represent a value type of [GraphQL interface][1] by
-/// default, however [trait object][3] may be used too (use `dyn` attribute
-/// argument for that).
-///
-/// A __trait has to be [object safe][2]__ if its values are represented by
-/// [trait object][3], because schema resolvers will need to return that
-/// [trait object][3]. The [trait object][3] has to be [`Send`] and [`Sync`],
-/// and the macro automatically generate a convenien type alias for such
-/// [trait object][3].
+/// Macro uses Rust enums only to represent a value type of a
+/// [GraphQL interface][1].
 ///
 /// ```
 /// use juniper::{graphql_interface, GraphQLObject};
 ///
 /// // NOTICE: By default a `CharacterValue` enum is generated by macro to represent values of this
 /// //         GraphQL interface.
-/// #[graphql_interface(for = [Human, Droid])] // enumerating all implementers is mandatory
+/// #[graphql_interface(for = Human)] // enumerating all implementers is mandatory
 /// trait Character {
 ///     fn id(&self) -> &str;
 /// }
 ///
-/// // NOTICE: `dyn` attribute argument enables trait object usage to represent values of this
-/// //         GraphQL interface. Also, for trait objects a trait is slightly modified
-/// //         under-the-hood by adding a `ScalarValue` type parameter.
-/// #[graphql_interface(dyn = DynSerial, for = Droid)]
-/// trait Serial {
-///     fn number(&self) -> i32;
-/// }
-///
 /// #[derive(GraphQLObject)]
 /// #[graphql(impl = CharacterValue)] // notice the enum type name, not trait name
 /// struct Human {
-///     id: String,
+///     id: String, // this field is used to resolve Character::id
 ///     home_planet: String,
 /// }
-/// #[graphql_interface]
-/// impl Character for Human {
-///     fn id(&self) -> &str {
-///         &self.id
-///     }
-/// }
-///
-/// #[derive(GraphQLObject)]
-/// #[graphql(impl = [CharacterValue, DynSerial<__S>])] // notice the trait object referred by alias
-/// struct Droid {                                      // and its parametrization by generic
-///     id: String,                                     // `ScalarValue`
-///     primary_function: String,
-/// }
-/// #[graphql_interface]
-/// impl Character for Droid {
-///     fn id(&self) -> &str {
-///         &self.id
-///     }
-/// }
-/// #[graphql_interface(dyn)] // implementing requires to know about dynamic dispatch too
-/// impl Serial for Droid {
-///     fn number(&self) -> i32 {
-///         78953
-///     }
-/// }
 /// ```
 ///
 /// # Custom name, description, deprecation and argument defaults
@@ -390,7 +356,7 @@ pub fn graphql_scalar(args: TokenStream, input: TokenStream) -> TokenStream {
 /// `none` (disables any renaming).
 ///
 /// ```
-/// # use juniper::{graphql_interface, GraphQLObject};
+/// # use juniper::{graphql_interface, graphql_object};
 /// #
 /// #[graphql_interface(for = Human, rename_all = "none")] // disables renaming
 /// trait Character {
@@ -399,19 +365,26 @@ pub fn graphql_scalar(args: TokenStream, input: TokenStream) -> TokenStream {
 ///     fn detailed_info(&self, info_kind: String) -> String;
 /// }
 ///
-/// #[derive(GraphQLObject)]
-/// #[graphql(impl = CharacterValue)]
 /// struct Human {
 ///     id: String,
 ///     home_planet: String,
 /// }
-/// #[graphql_interface]
-/// impl Character for Human {
-///     fn detailed_info(&self, info_kind: String) -> String {
+///
+/// #[graphql_object(impl = CharacterValue, rename_all = "none")]
+/// impl Human {
+///     fn id(&self) -> &str {
+///         &self.id
+///     }
+///
+///     fn home_planet(&self) -> &str {
+///         &self.home_planet
+///     }
+///
+///     // You can return `&str` even if trait definition returns `String`.
+///     fn detailed_info(&self, info_kind: String) -> &str {
 ///         (info_kind == "planet")
 ///             .then(|| &self.home_planet)
 ///             .unwrap_or(&self.id)
-///             .clone()
 ///     }
 /// }
 /// ```
@@ -447,7 +420,7 @@ pub fn graphql_scalar(args: TokenStream, input: TokenStream) -> TokenStream {
 ///
 /// ```
 /// # use std::collections::HashMap;
-/// # use juniper::{graphql_interface, GraphQLObject};
+/// # use juniper::{graphql_interface, graphql_object};
 /// #
 /// struct Database {
 ///     humans: HashMap<String, Human>,
@@ -461,36 +434,38 @@ pub fn graphql_scalar(args: TokenStream, input: TokenStream) -> TokenStream {
 ///     fn info<'db>(&self, #[graphql(context)] db: &'db Database) -> Option<&'db str>;
 /// }
 ///
-/// #[derive(GraphQLObject)]
-/// #[graphql(impl = CharacterValue, Context = Database)]
 /// struct Human {
 ///     id: String,
 ///     home_planet: String,
 /// }
-/// #[graphql_interface]
-/// impl Character for Human {
-///     fn id<'db>(&self, db: &'db Database) -> Option<&'db str> {
-///         db.humans.get(&self.id).map(|h| h.id.as_str())
+/// #[graphql_object(impl = CharacterValue, Context = Database)]
+/// impl Human {
+///     fn id<'db>(&self, context: &'db Database) -> Option<&'db str> {
+///         context.humans.get(&self.id).map(|h| h.id.as_str())
 ///     }
-///     fn info<'db>(&self, db: &'db Database) -> Option<&'db str> {
+///     fn info<'db>(&self, #[graphql(context)] db: &'db Database) -> Option<&'db str> {
 ///         db.humans.get(&self.id).map(|h| h.home_planet.as_str())
 ///     }
+///     fn home_planet(&self) -> &str {
+///         &self.home_planet
+///     }
 /// }
 ///
-/// #[derive(GraphQLObject)]
-/// #[graphql(impl = CharacterValue, Context = Database)]
 /// struct Droid {
 ///     id: String,
 ///     primary_function: String,
 /// }
-/// #[graphql_interface]
-/// impl Character for Droid {
-///     fn id<'db>(&self, db: &'db Database) -> Option<&'db str> {
-///         db.droids.get(&self.id).map(|h| h.id.as_str())
+/// #[graphql_object(impl = CharacterValue, Context = Database)]
+/// impl Droid {
+///     fn id<'db>(&self, ctx: &'db Database) -> Option<&'db str> {
+///         ctx.droids.get(&self.id).map(|h| h.id.as_str())
 ///     }
-///     fn info<'db>(&self, db: &'db Database) -> Option<&'db str> {
+///     fn info<'db>(&self, #[graphql(context)] db: &'db Database) -> Option<&'db str> {
 ///         db.droids.get(&self.id).map(|h| h.primary_function.as_str())
 ///     }
+///     fn primary_function(&self) -> &str {
+///         &self.primary_function
+///     }
 /// }
 /// ```
 ///
@@ -503,42 +478,33 @@ pub fn graphql_scalar(args: TokenStream, input: TokenStream) -> TokenStream {
 /// However, this requires to explicitly parametrize over [`ScalarValue`], as [`Executor`] does so.
 ///
 /// ```
-/// # use juniper::{graphql_interface, Executor, GraphQLObject, LookAheadMethods as _, ScalarValue};
+/// # use juniper::{graphql_interface, graphql_object, Executor, LookAheadMethods as _, ScalarValue};
 /// #
 /// // NOTICE: Specifying `ScalarValue` as existing type parameter.
 /// #[graphql_interface(for = Human, scalar = S)]
 /// trait Character<S: ScalarValue> {
-///     async fn id<'a>(&self, executor: &'a Executor<'_, '_, (), S>) -> &'a str
-///     where
-///         S: Send + Sync; // required by `#[async_trait]` transformation ¯\_(ツ)_/¯
+///     fn id<'a>(&self, executor: &'a Executor<'_, '_, (), S>) -> &'a str;
 ///
-///     async fn name<'b>(
+///     fn name<'b>(
 ///         &'b self,
 ///         #[graphql(executor)] another: &Executor<'_, '_, (), S>,
-///     ) -> &'b str
-///     where
-///         S: Send + Sync;
+///     ) -> &'b str;
 /// }
 ///
-/// #[derive(GraphQLObject)]
-/// #[graphql(impl = CharacterValue<__S>)]
 /// struct Human {
 ///     id: String,
 ///     name: String,
 /// }
-/// #[graphql_interface(scalar = S)]
-/// impl<S: ScalarValue> Character<S> for Human {
-///     async fn id<'a>(&self, executor: &'a Executor<'_, '_, (), S>) -> &'a str
+/// #[graphql_object(scalar = S: ScalarValue, impl = CharacterValue<S>)]
+/// impl Human {
+///     async fn id<'a, S>(&self, executor: &'a Executor<'_, '_, (), S>) -> &'a str
 ///     where
-///         S: Send + Sync,
+///         S: ScalarValue,
 ///     {
 ///         executor.look_ahead().field_name()
 ///     }
 ///
-///     async fn name<'b>(&'b self, _: &Executor<'_, '_, (), S>) -> &'b str
-///     where
-///         S: Send + Sync,
-///     {
+///     async fn name<'b, S>(&'b self, _executor: &Executor<'_, '_, (), S>) -> &'b str {
 ///         &self.name
 ///     }
 /// }
@@ -557,7 +523,7 @@ pub fn graphql_scalar(args: TokenStream, input: TokenStream) -> TokenStream {
 /// # use juniper::{graphql_interface, DefaultScalarValue, GraphQLObject};
 /// #
 /// // NOTICE: Removing `Scalar` argument will fail compilation.
-/// #[graphql_interface(for = [Human, Droid], scalar = DefaultScalarValue)]
+/// #[graphql_interface(for = Human, scalar = DefaultScalarValue)]
 /// trait Character {
 ///     fn id(&self) -> &str;
 /// }
@@ -568,93 +534,6 @@ pub fn graphql_scalar(args: TokenStream, input: TokenStream) -> TokenStream {
 ///     id: String,
 ///     home_planet: String,
 /// }
-/// #[graphql_interface(scalar = DefaultScalarValue)]
-/// impl Character for Human {
-///     fn id(&self) -> &str{
-///         &self.id
-///     }
-/// }
-///
-/// #[derive(GraphQLObject)]
-/// #[graphql(impl = CharacterValue, scalar = DefaultScalarValue)]
-/// struct Droid {
-///     id: String,
-///     primary_function: String,
-/// }
-/// #[graphql_interface(scalar = DefaultScalarValue)]
-/// impl Character for Droid {
-///     fn id(&self) -> &str {
-///         &self.id
-///     }
-/// }
-/// ```
-///
-/// # Downcasting
-///
-/// By default, the [GraphQL interface][1] value is downcast to one of its implementer types via
-/// matching the enum variant or downcasting the trait object (if `dyn` attribute's argument is
-/// used).
-///
-/// To use a custom logic for downcasting a [GraphQL interface][1] into its implementer, there may
-/// be specified:
-/// - either a `downcast` attribute's argument directly on a trait method;
-/// - or an `on` attribute's argument on aa trait definition referring an exteranl function.
-///
-/// ```
-/// # use std::collections::HashMap;
-/// # use juniper::{graphql_interface, GraphQLObject};
-/// #
-/// struct Database {
-///     humans: HashMap<String, Human>,
-///     droids: HashMap<String, Droid>,
-/// }
-/// impl juniper::Context for Database {}
-///
-/// #[graphql_interface(for = [Human, Droid], Context = Database)]
-/// #[graphql_interface(on Droid = get_droid)] // enables downcasting `Droid` via `get_droid()`
-/// trait Character {
-///     fn id(&self) -> &str;
-///
-///     #[graphql(downcast)] // makes method a downcast to `Human`, not a field
-///     // NOTICE: The method signature may optionally contain `&Database` context argument.
-///     fn as_human(&self) -> Option<&Human> {
-///         None
-///     }
-/// }
-///
-/// #[derive(GraphQLObject)]
-/// #[graphql(impl = CharacterValue, Context = Database)]
-/// struct Human {
-///     id: String,
-/// }
-/// #[graphql_interface]
-/// impl Character for Human {
-///     fn id(&self) -> &str {
-///         &self.id
-///     }
-///
-///     fn as_human(&self) -> Option<&Self> {
-///         Some(self)
-///     }
-/// }
-///
-/// #[derive(GraphQLObject)]
-/// #[graphql(impl = CharacterValue, Context = Database)]
-/// struct Droid {
-///     id: String,
-/// }
-/// #[graphql_interface]
-/// impl Character for Droid {
-///     fn id(&self) -> &str {
-///         &self.id
-///     }
-/// }
-///
-/// // External downcast function doesn't have to be a method of a type.
-/// // It's only a matter of the function signature to match the requirements.
-/// fn get_droid<'db>(ch: &CharacterValue, db: &'db Database) -> Option<&'db Droid> {
-///     db.droids.get(ch.id())
-/// }
 /// ```
 ///
 /// [`Context`]: juniper::Context
diff --git a/juniper_codegen/src/util/mod.rs b/juniper_codegen/src/util/mod.rs
index 4109ce7e3..726fce44c 100644
--- a/juniper_codegen/src/util/mod.rs
+++ b/juniper_codegen/src/util/mod.rs
@@ -906,6 +906,25 @@ impl GraphQLTypeDefiniton {
                     }
                 }
             }
+
+            impl#impl_generics ::juniper::macros::reflect::BaseType<#scalar> for #ty
+                #where_clause
+            {
+                const NAME: ::juniper::macros::reflect::Type = #name;
+            }
+
+            impl#impl_generics ::juniper::macros::reflect::BaseSubTypes<#scalar> for #ty
+                #where_clause
+            {
+                const NAMES: ::juniper::macros::reflect::Types =
+                    &[<Self as ::juniper::macros::reflect::BaseType<#scalar>>::NAME];
+            }
+
+            impl#impl_generics ::juniper::macros::reflect::WrappedType<#scalar> for #ty
+                #where_clause
+            {
+                const VALUE: ::juniper::macros::reflect::WrappedValue = 1;
+            }
         );
 
         if !self.no_async {
@@ -1153,6 +1172,28 @@ impl GraphQLTypeDefiniton {
                     ].into_iter().collect())
                 }
             }
+
+            impl#impl_generics ::juniper::macros::reflect::BaseType<#scalar>
+                for #ty #type_generics_tokens
+                #where_clause
+            {
+                const NAME: ::juniper::macros::reflect::Type = #name;
+            }
+
+            impl#impl_generics ::juniper::macros::reflect::BaseSubTypes<#scalar>
+                for #ty #type_generics_tokens
+                #where_clause
+            {
+                const NAMES: ::juniper::macros::reflect::Types =
+                    &[<Self as ::juniper::macros::reflect::BaseType<#scalar>>::NAME];
+            }
+
+            impl#impl_generics ::juniper::macros::reflect::WrappedType<#scalar>
+                for #ty #type_generics_tokens
+                #where_clause
+            {
+                const VALUE: ::juniper::macros::reflect::WrappedValue = 1;
+            }
         );
 
         if !self.no_async {
diff --git a/juniper_graphql_ws/src/lib.rs b/juniper_graphql_ws/src/lib.rs
index f1fe9bd3b..51c2e914f 100644
--- a/juniper_graphql_ws/src/lib.rs
+++ b/juniper_graphql_ws/src/lib.rs
@@ -68,6 +68,7 @@ impl<CtxT> ConnectionConfig<CtxT> {
     /// Specifies the maximum number of in-flight operations that a connection can have. If this
     /// number is exceeded, attempting to start more will result in an error. By default, there is
     /// no limit to in-flight operations.
+    #[must_use]
     pub fn with_max_in_flight_operations(mut self, max: usize) -> Self {
         self.max_in_flight_operations = max;
         self
@@ -75,6 +76,7 @@ impl<CtxT> ConnectionConfig<CtxT> {
 
     /// Specifies the interval at which to send keep-alives. Specifying a zero duration will
     /// disable keep-alives. By default, keep-alives are sent every 15 seconds.
+    #[must_use]
     pub fn with_keep_alive_interval(mut self, interval: Duration) -> Self {
         self.keep_alive_interval = interval;
         self
diff --git a/juniper_hyper/src/lib.rs b/juniper_hyper/src/lib.rs
index 52e67a929..d46df4527 100644
--- a/juniper_hyper/src/lib.rs
+++ b/juniper_hyper/src/lib.rs
@@ -286,24 +286,24 @@ enum GraphQLRequestError {
 }
 
 impl fmt::Display for GraphQLRequestError {
-    fn fmt(&self, mut f: &mut fmt::Formatter) -> fmt::Result {
-        match *self {
-            GraphQLRequestError::BodyHyper(ref err) => fmt::Display::fmt(err, &mut f),
-            GraphQLRequestError::BodyUtf8(ref err) => fmt::Display::fmt(err, &mut f),
-            GraphQLRequestError::BodyJSONError(ref err) => fmt::Display::fmt(err, &mut f),
-            GraphQLRequestError::Variables(ref err) => fmt::Display::fmt(err, &mut f),
-            GraphQLRequestError::Invalid(ref err) => fmt::Display::fmt(err, &mut f),
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        match self {
+            GraphQLRequestError::BodyHyper(err) => fmt::Display::fmt(err, f),
+            GraphQLRequestError::BodyUtf8(err) => fmt::Display::fmt(err, f),
+            GraphQLRequestError::BodyJSONError(err) => fmt::Display::fmt(err, f),
+            GraphQLRequestError::Variables(err) => fmt::Display::fmt(err, f),
+            GraphQLRequestError::Invalid(err) => fmt::Display::fmt(err, f),
         }
     }
 }
 
 impl Error for GraphQLRequestError {
     fn source(&self) -> Option<&(dyn Error + 'static)> {
-        match *self {
-            GraphQLRequestError::BodyHyper(ref err) => Some(err),
-            GraphQLRequestError::BodyUtf8(ref err) => Some(err),
-            GraphQLRequestError::BodyJSONError(ref err) => Some(err),
-            GraphQLRequestError::Variables(ref err) => Some(err),
+        match self {
+            GraphQLRequestError::BodyHyper(err) => Some(err),
+            GraphQLRequestError::BodyUtf8(err) => Some(err),
+            GraphQLRequestError::BodyJSONError(err) => Some(err),
+            GraphQLRequestError::Variables(err) => Some(err),
             GraphQLRequestError::Invalid(_) => None,
         }
     }
diff --git a/juniper_iron/src/lib.rs b/juniper_iron/src/lib.rs
index 5a6651878..cbef5ace0 100644
--- a/juniper_iron/src/lib.rs
+++ b/juniper_iron/src/lib.rs
@@ -311,15 +311,15 @@ where
     Subscription: GraphQLType<S, Context = CtxT, TypeInfo = ()> + Send + Sync + 'static,
     'a: 'static,
 {
-    fn handle(&self, mut req: &mut Request) -> IronResult<Response> {
+    fn handle(&self, req: &mut Request) -> IronResult<Response> {
         let context = (self.context_factory)(req)?;
 
         let graphql_request = match req.method {
-            method::Get => self.handle_get(&mut req)?,
+            method::Get => self.handle_get(req)?,
             method::Post => match req.headers.get::<ContentType>().map(ContentType::deref) {
                 Some(Mime(TopLevel::Application, sub_lvl, _)) => match sub_lvl.as_str() {
-                    "json" => self.handle_post_json(&mut req)?,
-                    "graphql" => self.handle_post_graphql(&mut req)?,
+                    "json" => self.handle_post_json(req)?,
+                    "graphql" => self.handle_post_graphql(req)?,
                     _ => return Ok(Response::with(status::BadRequest)),
                 },
                 _ => return Ok(Response::with(status::BadRequest)),
@@ -369,11 +369,11 @@ enum GraphQLIronError {
 }
 
 impl fmt::Display for GraphQLIronError {
-    fn fmt(&self, mut f: &mut fmt::Formatter) -> fmt::Result {
-        match *self {
-            GraphQLIronError::Serde(ref err) => fmt::Display::fmt(err, &mut f),
-            GraphQLIronError::Url(ref err) => fmt::Display::fmt(err, &mut f),
-            GraphQLIronError::InvalidData(err) => fmt::Display::fmt(err, &mut f),
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        match self {
+            GraphQLIronError::Serde(err) => fmt::Display::fmt(err, f),
+            GraphQLIronError::Url(err) => fmt::Display::fmt(err, f),
+            GraphQLIronError::InvalidData(err) => fmt::Display::fmt(err, f),
         }
     }
 }
diff --git a/juniper_subscriptions/src/lib.rs b/juniper_subscriptions/src/lib.rs
index 5e890b630..e09e11279 100644
--- a/juniper_subscriptions/src/lib.rs
+++ b/juniper_subscriptions/src/lib.rs
@@ -183,7 +183,7 @@ where
                 ready_vec.push(None);
             }
 
-            let stream = stream::poll_fn(move |mut ctx| -> Poll<Option<ExecutionOutput<S>>> {
+            let stream = stream::poll_fn(move |ctx| -> Poll<Option<ExecutionOutput<S>>> {
                 let mut obj_iterator = object.iter_mut();
 
                 // Due to having to modify `ready_vec` contents (by-move pattern)
@@ -204,7 +204,7 @@ where
 
                     match val {
                         Value::Scalar(stream) => {
-                            match Pin::new(stream).poll_next(&mut ctx) {
+                            match Pin::new(stream).poll_next(ctx) {
                                 Poll::Ready(None) => return Poll::Ready(None),
                                 Poll::Ready(Some(value)) => {
                                     *ready = Some((field_name.clone(), value));


Test patch:
diff --git a/integration_tests/codegen_fail/fail/interface/additional_non_nullable_argument.rs b/integration_tests/codegen_fail/fail/interface/additional_non_nullable_argument.rs
new file mode 100644
index 000000000..c69f0f664
--- /dev/null
+++ b/integration_tests/codegen_fail/fail/interface/additional_non_nullable_argument.rs
@@ -0,0 +1,19 @@
+use juniper::{graphql_interface, graphql_object};
+
+pub struct ObjA {
+    id: String,
+}
+
+#[graphql_object(impl = CharacterValue)]
+impl ObjA {
+    fn id(&self, is_present: bool) -> &str {
+        is_present.then(|| self.id.as_str()).unwrap_or("missing")
+    }
+}
+
+#[graphql_interface(for = ObjA)]
+trait Character {
+    fn id(&self) -> &str;
+}
+
+fn main() {}
diff --git a/integration_tests/codegen_fail/fail/interface/additional_non_nullable_argument.stderr b/integration_tests/codegen_fail/fail/interface/additional_non_nullable_argument.stderr
new file mode 100644
index 000000000..589ed21b5
--- /dev/null
+++ b/integration_tests/codegen_fail/fail/interface/additional_non_nullable_argument.stderr
@@ -0,0 +1,7 @@
+error[E0080]: evaluation of constant value failed
+  --> fail/interface/additional_non_nullable_argument.rs:14:1
+   |
+14 | #[graphql_interface(for = ObjA)]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'Failed to implement interface `Character` on `ObjA`: Field `id`: Argument `isPresent` of type `Boolean!` isn't present on the interface and so has to be nullable.', $DIR/fail/interface/additional_non_nullable_argument.rs:14:1
+   |
+   = note: this error originates in the macro `$crate::panic::panic_2015` (in Nightly builds, run with -Z macro-backtrace for more info)
diff --git a/integration_tests/codegen_fail/fail/interface/argument_double_underscored.rs b/integration_tests/codegen_fail/fail/interface/argument_double_underscored.rs
index d7227c381..449d67485 100644
--- a/integration_tests/codegen_fail/fail/interface/argument_double_underscored.rs
+++ b/integration_tests/codegen_fail/fail/interface/argument_double_underscored.rs
@@ -1,19 +1,8 @@
-use juniper::{graphql_interface, GraphQLObject};
-
-#[derive(GraphQLObject)]
-#[graphql(impl = CharacterValue)]
-pub struct ObjA {
-    test: String,
-}
+use juniper::graphql_interface;
 
 #[graphql_interface]
-impl Character for ObjA {}
-
-#[graphql_interface(for = ObjA)]
 trait Character {
-    fn id(&self, __num: i32) -> &str {
-        "funA"
-    }
+    fn id(&self, __num: i32) -> &str;
 }
 
 fn main() {}
diff --git a/integration_tests/codegen_fail/fail/interface/argument_double_underscored.stderr b/integration_tests/codegen_fail/fail/interface/argument_double_underscored.stderr
index 0c47e6158..0a6e79eba 100644
--- a/integration_tests/codegen_fail/fail/interface/argument_double_underscored.stderr
+++ b/integration_tests/codegen_fail/fail/interface/argument_double_underscored.stderr
@@ -1,19 +1,7 @@
 error: All types and directives defined within a schema must not have a name which begins with `__` (two underscores), as this is used exclusively by GraphQL’s introspection system.
-  --> fail/interface/argument_double_underscored.rs:14:18
-   |
-14 |     fn id(&self, __num: i32) -> &str {
-   |                  ^^^^^
-   |
-   = note: https://spec.graphql.org/June2018/#sec-Schema
-
-error[E0412]: cannot find type `CharacterValue` in this scope
- --> fail/interface/argument_double_underscored.rs:4:18
+ --> fail/interface/argument_double_underscored.rs:5:18
   |
-4 | #[graphql(impl = CharacterValue)]
-  |                  ^^^^^^^^^^^^^^ not found in this scope
-
-error[E0405]: cannot find trait `Character` in this scope
-  --> fail/interface/argument_double_underscored.rs:10:6
-   |
-10 | impl Character for ObjA {}
-   |      ^^^^^^^^^ not found in this scope
+5 |     fn id(&self, __num: i32) -> &str;
+  |                  ^^^^^
+  |
+  = note: https://spec.graphql.org/June2018/#sec-Schema
diff --git a/integration_tests/codegen_fail/fail/interface/argument_non_input_type.rs b/integration_tests/codegen_fail/fail/interface/argument_non_input_type.rs
index db6ad3002..abca82751 100644
--- a/integration_tests/codegen_fail/fail/interface/argument_non_input_type.rs
+++ b/integration_tests/codegen_fail/fail/interface/argument_non_input_type.rs
@@ -1,19 +1,11 @@
 use juniper::{graphql_interface, GraphQLObject};
 
 #[derive(GraphQLObject)]
-#[graphql(impl = CharacterValue)]
 pub struct ObjA {
     test: String,
 }
 
 #[graphql_interface]
-impl Character for ObjA {
-    fn id(&self, obj: Self) -> &str {
-        "funA"
-    }
-}
-
-#[graphql_interface(for = ObjA)]
 trait Character {
     fn id(&self, obj: ObjA) -> &str;
 }
diff --git a/integration_tests/codegen_fail/fail/interface/argument_non_input_type.stderr b/integration_tests/codegen_fail/fail/interface/argument_non_input_type.stderr
index 8b87b4023..e20698e5b 100644
--- a/integration_tests/codegen_fail/fail/interface/argument_non_input_type.stderr
+++ b/integration_tests/codegen_fail/fail/interface/argument_non_input_type.stderr
@@ -1,16 +1,16 @@
 error[E0277]: the trait bound `ObjA: IsInputType<__S>` is not satisfied
-  --> fail/interface/argument_non_input_type.rs:16:1
-   |
-16 | #[graphql_interface(for = ObjA)]
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `IsInputType<__S>` is not implemented for `ObjA`
-   |
-   = note: this error originates in the attribute macro `graphql_interface` (in Nightly builds, run with -Z macro-backtrace for more info)
+ --> fail/interface/argument_non_input_type.rs:8:1
+  |
+8 | #[graphql_interface]
+  | ^^^^^^^^^^^^^^^^^^^^ the trait `IsInputType<__S>` is not implemented for `ObjA`
+  |
+  = note: this error originates in the attribute macro `graphql_interface` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error[E0277]: the trait bound `ObjA: FromInputValue<__S>` is not satisfied
-    --> fail/interface/argument_non_input_type.rs:16:1
+    --> fail/interface/argument_non_input_type.rs:8:1
      |
-16   | #[graphql_interface(for = ObjA)]
-     | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `FromInputValue<__S>` is not implemented for `ObjA`
+8    | #[graphql_interface]
+     | ^^^^^^^^^^^^^^^^^^^^ the trait `FromInputValue<__S>` is not implemented for `ObjA`
      |
 note: required by a bound in `Registry::<'r, S>::arg`
     --> $WORKSPACE/juniper/src/executor/mod.rs
@@ -18,11 +18,3 @@ note: required by a bound in `Registry::<'r, S>::arg`
      |         T: GraphQLType<S> + FromInputValue<S>,
      |                             ^^^^^^^^^^^^^^^^^ required by this bound in `Registry::<'r, S>::arg`
      = note: this error originates in the attribute macro `graphql_interface` (in Nightly builds, run with -Z macro-backtrace for more info)
-
-error[E0277]: the trait bound `ObjA: FromInputValue<__S>` is not satisfied
-  --> fail/interface/argument_non_input_type.rs:16:1
-   |
-16 | #[graphql_interface(for = ObjA)]
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `FromInputValue<__S>` is not implemented for `ObjA`
-   |
-   = note: this error originates in the attribute macro `graphql_interface` (in Nightly builds, run with -Z macro-backtrace for more info)
diff --git a/integration_tests/codegen_fail/fail/interface/argument_wrong_default_array.rs b/integration_tests/codegen_fail/fail/interface/argument_wrong_default_array.rs
index 85156a3eb..00bec5f4c 100644
--- a/integration_tests/codegen_fail/fail/interface/argument_wrong_default_array.rs
+++ b/integration_tests/codegen_fail/fail/interface/argument_wrong_default_array.rs
@@ -1,23 +1,8 @@
-use juniper::{graphql_interface, GraphQLObject};
-
-#[derive(GraphQLObject)]
-#[graphql(impl = CharacterValue)]
-pub struct ObjA {
-    test: String,
-}
+use juniper::graphql_interface;
 
 #[graphql_interface]
-impl Character for ObjA {}
-
-#[graphql_interface(for = ObjA)]
 trait Character {
-    fn wrong(
-        &self,
-        #[graphql(default = [true, false, false])]
-        input: [bool; 2],
-    ) -> bool {
-        input[0]
-    }
+    fn wrong(&self, #[graphql(default = [true, false, false])] input: [bool; 2]) -> bool;
 }
 
 fn main() {}
diff --git a/integration_tests/codegen_fail/fail/interface/argument_wrong_default_array.stderr b/integration_tests/codegen_fail/fail/interface/argument_wrong_default_array.stderr
index 0b149ee54..b745a82d0 100644
--- a/integration_tests/codegen_fail/fail/interface/argument_wrong_default_array.stderr
+++ b/integration_tests/codegen_fail/fail/interface/argument_wrong_default_array.stderr
@@ -1,11 +1,11 @@
 error[E0277]: the trait bound `[bool; 2]: From<[bool; 3]>` is not satisfied
-  --> fail/interface/argument_wrong_default_array.rs:12:1
-   |
-12 | #[graphql_interface(for = ObjA)]
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `From<[bool; 3]>` is not implemented for `[bool; 2]`
-   |
-   = help: the following implementations were found:
-             <[T; LANES] as From<Simd<T, LANES>>>
-             <[bool; LANES] as From<Mask<T, LANES>>>
-   = note: required because of the requirements on the impl of `Into<[bool; 2]>` for `[bool; 3]`
-   = note: this error originates in the attribute macro `graphql_interface` (in Nightly builds, run with -Z macro-backtrace for more info)
+ --> fail/interface/argument_wrong_default_array.rs:3:1
+  |
+3 | #[graphql_interface]
+  | ^^^^^^^^^^^^^^^^^^^^ the trait `From<[bool; 3]>` is not implemented for `[bool; 2]`
+  |
+  = help: the following implementations were found:
+            <[T; LANES] as From<Simd<T, LANES>>>
+            <[bool; LANES] as From<Mask<T, LANES>>>
+  = note: required because of the requirements on the impl of `Into<[bool; 2]>` for `[bool; 3]`
+  = note: this error originates in the attribute macro `graphql_interface` (in Nightly builds, run with -Z macro-backtrace for more info)
diff --git a/integration_tests/codegen_fail/fail/interface/downcast_method_conflicts_with_external_downcast_fn.rs b/integration_tests/codegen_fail/fail/interface/downcast_method_conflicts_with_external_downcast_fn.rs
deleted file mode 100644
index fdc9dfb08..000000000
--- a/integration_tests/codegen_fail/fail/interface/downcast_method_conflicts_with_external_downcast_fn.rs
+++ /dev/null
@@ -1,29 +0,0 @@
-use juniper::{graphql_interface, GraphQLObject};
-
-#[derive(GraphQLObject)]
-#[graphql(impl = CharacterValue)]
-pub struct ObjA {
-    test: String,
-}
-
-#[graphql_interface]
-impl Character for ObjA {
-    fn id(&self, _: i32) -> &str {
-        "funA"
-    }
-
-    fn as_obja(&self) -> Option<&ObjA> {
-        Some(self)
-    }
-}
-
-#[graphql_interface(for = ObjA)]
-#[graphql_interface(on ObjA = downcast_obja)]
-trait Character {
-    fn id(&self, num: i32) -> &str;
-
-    #[graphql(downcast)]
-    fn as_obja(&self) -> Option<&ObjA>;
-}
-
-fn main() {}
diff --git a/integration_tests/codegen_fail/fail/interface/downcast_method_conflicts_with_external_downcast_fn.stderr b/integration_tests/codegen_fail/fail/interface/downcast_method_conflicts_with_external_downcast_fn.stderr
deleted file mode 100644
index 1aede59e0..000000000
--- a/integration_tests/codegen_fail/fail/interface/downcast_method_conflicts_with_external_downcast_fn.stderr
+++ /dev/null
@@ -1,20 +0,0 @@
-error: GraphQL interface trait method `as_obja` conflicts with the external downcast function `downcast_obja` declared on the trait to downcast into the implementer type `ObjA`
-  --> fail/interface/downcast_method_conflicts_with_external_downcast_fn.rs:26:5
-   |
-26 |     fn as_obja(&self) -> Option<&ObjA>;
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = note: https://spec.graphql.org/June2018/#sec-Interfaces
-   = note: use `#[graphql(ignore)]` attribute argument to ignore this trait method for interface implementers downcasting
-
-error[E0412]: cannot find type `CharacterValue` in this scope
- --> fail/interface/downcast_method_conflicts_with_external_downcast_fn.rs:4:18
-  |
-4 | #[graphql(impl = CharacterValue)]
-  |                  ^^^^^^^^^^^^^^ not found in this scope
-
-error[E0405]: cannot find trait `Character` in this scope
-  --> fail/interface/downcast_method_conflicts_with_external_downcast_fn.rs:10:6
-   |
-10 | impl Character for ObjA {
-   |      ^^^^^^^^^ not found in this scope
diff --git a/integration_tests/codegen_fail/fail/interface/downcast_method_wrong_input_args.rs b/integration_tests/codegen_fail/fail/interface/downcast_method_wrong_input_args.rs
deleted file mode 100644
index 29cfc225a..000000000
--- a/integration_tests/codegen_fail/fail/interface/downcast_method_wrong_input_args.rs
+++ /dev/null
@@ -1,24 +0,0 @@
-use juniper::{graphql_interface, GraphQLObject};
-
-#[graphql_interface(for = Human)]
-trait Character {
-    fn id(&self) -> i32 {
-        0
-    }
-
-    #[graphql(downcast)]
-    fn a(&self, ctx: &(), rand: u8) -> Option<&Human> {
-        None
-    }
-}
-
-#[derive(GraphQLObject)]
-#[graphql(impl = CharacterValue)]
-pub struct Human {
-    id: String,
-}
-
-#[graphql_interface]
-impl Character for Human {}
-
-fn main() {}
diff --git a/integration_tests/codegen_fail/fail/interface/downcast_method_wrong_input_args.stderr b/integration_tests/codegen_fail/fail/interface/downcast_method_wrong_input_args.stderr
deleted file mode 100644
index ee9e48343..000000000
--- a/integration_tests/codegen_fail/fail/interface/downcast_method_wrong_input_args.stderr
+++ /dev/null
@@ -1,19 +0,0 @@
-error: GraphQL interface expects trait method to accept `&self` only and, optionally, `&Context`
-  --> fail/interface/downcast_method_wrong_input_args.rs:10:10
-   |
-10 |     fn a(&self, ctx: &(), rand: u8) -> Option<&Human> {
-   |          ^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = note: https://spec.graphql.org/June2018/#sec-Interfaces
-
-error[E0412]: cannot find type `CharacterValue` in this scope
-  --> fail/interface/downcast_method_wrong_input_args.rs:16:18
-   |
-16 | #[graphql(impl = CharacterValue)]
-   |                  ^^^^^^^^^^^^^^ not found in this scope
-
-error[E0405]: cannot find trait `Character` in this scope
-  --> fail/interface/downcast_method_wrong_input_args.rs:22:6
-   |
-22 | impl Character for Human {}
-   |      ^^^^^^^^^ not found in this scope
diff --git a/integration_tests/codegen_fail/fail/interface/downcast_method_wrong_return_type.rs b/integration_tests/codegen_fail/fail/interface/downcast_method_wrong_return_type.rs
deleted file mode 100644
index 2fd26790e..000000000
--- a/integration_tests/codegen_fail/fail/interface/downcast_method_wrong_return_type.rs
+++ /dev/null
@@ -1,24 +0,0 @@
-use juniper::{graphql_interface, GraphQLObject};
-
-#[graphql_interface(for = Human)]
-trait Character {
-    fn id(&self) -> i32 {
-        0
-    }
-
-    #[graphql(downcast)]
-    fn a(&self, ctx: &(), rand: u8) -> &Human {
-        unimplemented!()
-    }
-}
-
-#[derive(GraphQLObject)]
-#[graphql(impl = CharacterValue)]
-pub struct Human {
-    id: String,
-}
-
-#[graphql_interface]
-impl Character for Human {}
-
-fn main() {}
diff --git a/integration_tests/codegen_fail/fail/interface/downcast_method_wrong_return_type.stderr b/integration_tests/codegen_fail/fail/interface/downcast_method_wrong_return_type.stderr
deleted file mode 100644
index 22aa7aba2..000000000
--- a/integration_tests/codegen_fail/fail/interface/downcast_method_wrong_return_type.stderr
+++ /dev/null
@@ -1,19 +0,0 @@
-error: GraphQL interface expects trait method return type to be `Option<&ImplementerType>` only
-  --> fail/interface/downcast_method_wrong_return_type.rs:10:40
-   |
-10 |     fn a(&self, ctx: &(), rand: u8) -> &Human {
-   |                                        ^^^^^^
-   |
-   = note: https://spec.graphql.org/June2018/#sec-Interfaces
-
-error[E0412]: cannot find type `CharacterValue` in this scope
-  --> fail/interface/downcast_method_wrong_return_type.rs:16:18
-   |
-16 | #[graphql(impl = CharacterValue)]
-   |                  ^^^^^^^^^^^^^^ not found in this scope
-
-error[E0405]: cannot find trait `Character` in this scope
-  --> fail/interface/downcast_method_wrong_return_type.rs:22:6
-   |
-22 | impl Character for Human {}
-   |      ^^^^^^^^^ not found in this scope
diff --git a/integration_tests/codegen_fail/fail/interface/field_double_underscored.rs b/integration_tests/codegen_fail/fail/interface/field_double_underscored.rs
index 43115d327..10d001198 100644
--- a/integration_tests/codegen_fail/fail/interface/field_double_underscored.rs
+++ b/integration_tests/codegen_fail/fail/interface/field_double_underscored.rs
@@ -1,19 +1,8 @@
-use juniper::{graphql_interface, GraphQLObject};
-
-#[derive(GraphQLObject)]
-#[graphql(impl = CharacterValue)]
-pub struct ObjA {
-    test: String,
-}
+use juniper::graphql_interface;
 
 #[graphql_interface]
-impl Character for ObjA {}
-
-#[graphql_interface(for = ObjA)]
 trait Character {
-    fn __id(&self) -> &str {
-        "funA"
-    }
+    fn __id(&self) -> &str;
 }
 
 fn main() {}
diff --git a/integration_tests/codegen_fail/fail/interface/field_double_underscored.stderr b/integration_tests/codegen_fail/fail/interface/field_double_underscored.stderr
index 2cdfafa6d..6c683ed51 100644
--- a/integration_tests/codegen_fail/fail/interface/field_double_underscored.stderr
+++ b/integration_tests/codegen_fail/fail/interface/field_double_underscored.stderr
@@ -1,19 +1,7 @@
 error: All types and directives defined within a schema must not have a name which begins with `__` (two underscores), as this is used exclusively by GraphQL’s introspection system.
-  --> fail/interface/field_double_underscored.rs:14:8
-   |
-14 |     fn __id(&self) -> &str {
-   |        ^^^^
-   |
-   = note: https://spec.graphql.org/June2018/#sec-Schema
-
-error[E0412]: cannot find type `CharacterValue` in this scope
- --> fail/interface/field_double_underscored.rs:4:18
+ --> fail/interface/field_double_underscored.rs:5:8
   |
-4 | #[graphql(impl = CharacterValue)]
-  |                  ^^^^^^^^^^^^^^ not found in this scope
-
-error[E0405]: cannot find trait `Character` in this scope
-  --> fail/interface/field_double_underscored.rs:10:6
-   |
-10 | impl Character for ObjA {}
-   |      ^^^^^^^^^ not found in this scope
+5 |     fn __id(&self) -> &str;
+  |        ^^^^
+  |
+  = note: https://spec.graphql.org/June2018/#sec-Schema
diff --git a/integration_tests/codegen_fail/fail/interface/field_non_output_return_type.rs b/integration_tests/codegen_fail/fail/interface/field_non_output_return_type.rs
index a860ace35..ad802cf59 100644
--- a/integration_tests/codegen_fail/fail/interface/field_non_output_return_type.rs
+++ b/integration_tests/codegen_fail/fail/interface/field_non_output_return_type.rs
@@ -1,24 +1,13 @@
-use juniper::{graphql_interface, GraphQLInputObject, GraphQLObject};
+use juniper::{graphql_interface, GraphQLInputObject};
 
 #[derive(GraphQLInputObject)]
 pub struct ObjB {
     id: i32,
 }
 
-#[derive(GraphQLObject)]
-#[graphql(impl = CharacterValue)]
-pub struct ObjA {
-    test: String,
-}
-
 #[graphql_interface]
-impl Character for ObjA {}
-
-#[graphql_interface(for = ObjA)]
 trait Character {
-    fn id(&self) -> ObjB {
-        ObjB { id: 34 }
-    }
+    fn id(&self) -> ObjB;
 }
 
 fn main() {}
diff --git a/integration_tests/codegen_fail/fail/interface/field_non_output_return_type.stderr b/integration_tests/codegen_fail/fail/interface/field_non_output_return_type.stderr
index 1491540eb..5bd7c8c9c 100644
--- a/integration_tests/codegen_fail/fail/interface/field_non_output_return_type.stderr
+++ b/integration_tests/codegen_fail/fail/interface/field_non_output_return_type.stderr
@@ -1,7 +1,7 @@
 error[E0277]: the trait bound `ObjB: IsOutputType<__S>` is not satisfied
-  --> fail/interface/field_non_output_return_type.rs:17:1
-   |
-17 | #[graphql_interface(for = ObjA)]
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `IsOutputType<__S>` is not implemented for `ObjB`
-   |
-   = note: this error originates in the attribute macro `graphql_interface` (in Nightly builds, run with -Z macro-backtrace for more info)
+ --> fail/interface/field_non_output_return_type.rs:8:1
+  |
+8 | #[graphql_interface]
+  | ^^^^^^^^^^^^^^^^^^^^ the trait `IsOutputType<__S>` is not implemented for `ObjB`
+  |
+  = note: this error originates in the attribute macro `graphql_interface` (in Nightly builds, run with -Z macro-backtrace for more info)
diff --git a/integration_tests/codegen_fail/fail/interface/fields_duplicate.rs b/integration_tests/codegen_fail/fail/interface/fields_duplicate.rs
index aba568b36..b69704d27 100644
--- a/integration_tests/codegen_fail/fail/interface/fields_duplicate.rs
+++ b/integration_tests/codegen_fail/fail/interface/fields_duplicate.rs
@@ -1,24 +1,11 @@
-use juniper::{graphql_interface, GraphQLObject};
-
-#[derive(GraphQLObject)]
-#[graphql(impl = CharacterValue)]
-pub struct ObjA {
-    test: String,
-}
+use juniper::graphql_interface;
 
 #[graphql_interface]
-impl Character for ObjA {}
-
-#[graphql_interface(for = ObjA)]
 trait Character {
-    fn id(&self) -> &str {
-        "funA"
-    }
+    fn id(&self) -> &str;
 
     #[graphql(name = "id")]
-    fn id2(&self) -> &str {
-        "funB"
-    }
+    fn id2(&self) -> &str;
 }
 
 fn main() {}
diff --git a/integration_tests/codegen_fail/fail/interface/fields_duplicate.stderr b/integration_tests/codegen_fail/fail/interface/fields_duplicate.stderr
index 284a3e1d5..143f85dab 100644
--- a/integration_tests/codegen_fail/fail/interface/fields_duplicate.stderr
+++ b/integration_tests/codegen_fail/fail/interface/fields_duplicate.stderr
@@ -1,25 +1,12 @@
 error: GraphQL interface must have a different name for each field
-  --> fail/interface/fields_duplicate.rs:13:1
-   |
-13 | / trait Character {
-14 | |     fn id(&self) -> &str {
-15 | |         "funA"
-16 | |     }
-...  |
-21 | |     }
-22 | | }
-   | |_^
-   |
-   = note: https://spec.graphql.org/June2018/#sec-Interfaces
-
-error[E0412]: cannot find type `CharacterValue` in this scope
- --> fail/interface/fields_duplicate.rs:4:18
+ --> fail/interface/fields_duplicate.rs:4:1
   |
-4 | #[graphql(impl = CharacterValue)]
-  |                  ^^^^^^^^^^^^^^ not found in this scope
-
-error[E0405]: cannot find trait `Character` in this scope
-  --> fail/interface/fields_duplicate.rs:10:6
-   |
-10 | impl Character for ObjA {}
-   |      ^^^^^^^^^ not found in this scope
+4 | / trait Character {
+5 | |     fn id(&self) -> &str;
+6 | |
+7 | |     #[graphql(name = "id")]
+8 | |     fn id2(&self) -> &str;
+9 | | }
+  | |_^
+  |
+  = note: https://spec.graphql.org/June2018/#sec-Interfaces
diff --git a/integration_tests/codegen_fail/fail/interface/implementer_non_object_type.rs b/integration_tests/codegen_fail/fail/interface/implementer_non_object_type.rs
deleted file mode 100644
index 0aba2508d..000000000
--- a/integration_tests/codegen_fail/fail/interface/implementer_non_object_type.rs
+++ /dev/null
@@ -1,20 +0,0 @@
-use juniper::{graphql_interface, GraphQLInputObject};
-
-#[derive(GraphQLInputObject)]
-pub struct ObjA {
-    test: String,
-}
-
-#[graphql_interface]
-impl Character for ObjA {
-    fn id(&self) -> &str {
-        "funA"
-    }
-}
-
-#[graphql_interface(for = ObjA)]
-trait Character {
-    fn id(&self) -> &str;
-}
-
-fn main() {}
diff --git a/integration_tests/codegen_fail/fail/interface/implementer_non_object_type.stderr b/integration_tests/codegen_fail/fail/interface/implementer_non_object_type.stderr
deleted file mode 100644
index 35c2dd0ca..000000000
--- a/integration_tests/codegen_fail/fail/interface/implementer_non_object_type.stderr
+++ /dev/null
@@ -1,15 +0,0 @@
-error[E0277]: the trait bound `ObjA: GraphQLObject<__S>` is not satisfied
-  --> fail/interface/implementer_non_object_type.rs:15:1
-   |
-15 | #[graphql_interface(for = ObjA)]
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `GraphQLObject<__S>` is not implemented for `ObjA`
-   |
-   = note: this error originates in the attribute macro `graphql_interface` (in Nightly builds, run with -Z macro-backtrace for more info)
-
-error[E0277]: the trait bound `ObjA: IsOutputType<__S>` is not satisfied
-  --> fail/interface/implementer_non_object_type.rs:15:1
-   |
-15 | #[graphql_interface(for = ObjA)]
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `IsOutputType<__S>` is not implemented for `ObjA`
-   |
-   = note: this error originates in the attribute macro `graphql_interface` (in Nightly builds, run with -Z macro-backtrace for more info)
diff --git a/integration_tests/codegen_fail/fail/interface/implementers_duplicate_pretty.rs b/integration_tests/codegen_fail/fail/interface/implementers_duplicate_pretty.rs
index fbdc8f12e..889fbd016 100644
--- a/integration_tests/codegen_fail/fail/interface/implementers_duplicate_pretty.rs
+++ b/integration_tests/codegen_fail/fail/interface/implementers_duplicate_pretty.rs
@@ -3,14 +3,7 @@ use juniper::{graphql_interface, GraphQLObject};
 #[derive(GraphQLObject)]
 #[graphql(impl = CharacterValue)]
 pub struct ObjA {
-    test: String,
-}
-
-#[graphql_interface]
-impl Character for ObjA {
-    fn id(&self) -> &str {
-        "funA"
-    }
+    id: String,
 }
 
 #[graphql_interface(for = [ObjA, ObjA])]
diff --git a/integration_tests/codegen_fail/fail/interface/implementers_duplicate_pretty.stderr b/integration_tests/codegen_fail/fail/interface/implementers_duplicate_pretty.stderr
index 40db273e9..ce72fa2d7 100644
--- a/integration_tests/codegen_fail/fail/interface/implementers_duplicate_pretty.stderr
+++ b/integration_tests/codegen_fail/fail/interface/implementers_duplicate_pretty.stderr
@@ -1,17 +1,11 @@
 error: duplicated attribute argument found
-  --> $DIR/implementers_duplicate_pretty.rs:16:34
-   |
-16 | #[graphql_interface(for = [ObjA, ObjA])]
-   |                                  ^^^^
+ --> fail/interface/implementers_duplicate_pretty.rs:9:34
+  |
+9 | #[graphql_interface(for = [ObjA, ObjA])]
+  |                                  ^^^^
 
 error[E0412]: cannot find type `CharacterValue` in this scope
- --> $DIR/implementers_duplicate_pretty.rs:4:18
+ --> fail/interface/implementers_duplicate_pretty.rs:4:18
   |
 4 | #[graphql(impl = CharacterValue)]
   |                  ^^^^^^^^^^^^^^ not found in this scope
-
-error[E0405]: cannot find trait `Character` in this scope
-  --> $DIR/implementers_duplicate_pretty.rs:10:6
-   |
-10 | impl Character for ObjA {
-   |      ^^^^^^^^^ not found in this scope
diff --git a/integration_tests/codegen_fail/fail/interface/implementers_duplicate_ugly.rs b/integration_tests/codegen_fail/fail/interface/implementers_duplicate_ugly.rs
index 1acb56c01..a0b7c7ed0 100644
--- a/integration_tests/codegen_fail/fail/interface/implementers_duplicate_ugly.rs
+++ b/integration_tests/codegen_fail/fail/interface/implementers_duplicate_ugly.rs
@@ -3,18 +3,11 @@ use juniper::{graphql_interface, GraphQLObject};
 #[derive(GraphQLObject)]
 #[graphql(impl = CharacterValue)]
 pub struct ObjA {
-    test: String,
+    id: String,
 }
 
 type ObjAlias = ObjA;
 
-#[graphql_interface]
-impl Character for ObjA {
-    fn id(&self) -> &str {
-        "funA"
-    }
-}
-
 #[graphql_interface(for = [ObjA, ObjAlias])]
 trait Character {
     fn id(&self) -> &str;
diff --git a/integration_tests/codegen_fail/fail/interface/implementers_duplicate_ugly.stderr b/integration_tests/codegen_fail/fail/interface/implementers_duplicate_ugly.stderr
index d06dda364..61340f732 100644
--- a/integration_tests/codegen_fail/fail/interface/implementers_duplicate_ugly.stderr
+++ b/integration_tests/codegen_fail/fail/interface/implementers_duplicate_ugly.stderr
@@ -1,7 +1,7 @@
-error[E0119]: conflicting implementations of trait `<CharacterValue as juniper::GraphQLInterface<__S>>::mark::_::{closure#0}::MutuallyExclusive` for type `ObjA`
-  --> $DIR/implementers_duplicate_ugly.rs:18:1
+error[E0119]: conflicting implementations of trait `<CharacterValueEnum<ObjA, ObjA> as juniper::GraphQLInterface<__S>>::mark::_::{closure#0}::MutuallyExclusive` for type `ObjA`
+  --> fail/interface/implementers_duplicate_ugly.rs:11:1
    |
-18 | #[graphql_interface(for = [ObjA, ObjAlias])]
+11 | #[graphql_interface(for = [ObjA, ObjAlias])]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    | |
    | first implementation here
@@ -9,13 +9,13 @@ error[E0119]: conflicting implementations of trait `<CharacterValue as juniper::
    |
    = note: this error originates in the macro `::juniper::sa::assert_type_ne_all` (in Nightly builds, run with -Z macro-backtrace for more info)
 
-error[E0119]: conflicting implementations of trait `std::convert::From<ObjA>` for type `CharacterValue`
-  --> $DIR/implementers_duplicate_ugly.rs:18:1
+error[E0119]: conflicting implementations of trait `std::convert::From<ObjA>` for type `CharacterValueEnum<ObjA, ObjA>`
+  --> fail/interface/implementers_duplicate_ugly.rs:11:1
    |
-18 | #[graphql_interface(for = [ObjA, ObjAlias])]
+11 | #[graphql_interface(for = [ObjA, ObjAlias])]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    | |
    | first implementation here
-   | conflicting implementation for `CharacterValue`
+   | conflicting implementation for `CharacterValueEnum<ObjA, ObjA>`
    |
    = note: this error originates in the attribute macro `graphql_interface` (in Nightly builds, run with -Z macro-backtrace for more info)
diff --git a/integration_tests/codegen_fail/fail/interface/method_default_impl.rs b/integration_tests/codegen_fail/fail/interface/method_default_impl.rs
new file mode 100644
index 000000000..e7ac7832e
--- /dev/null
+++ b/integration_tests/codegen_fail/fail/interface/method_default_impl.rs
@@ -0,0 +1,10 @@
+use juniper::graphql_interface;
+
+#[graphql_interface]
+trait Character {
+    fn id(&self) -> &str {
+        "default"
+    }
+}
+
+fn main() {}
diff --git a/integration_tests/codegen_fail/fail/interface/method_default_impl.stderr b/integration_tests/codegen_fail/fail/interface/method_default_impl.stderr
new file mode 100644
index 000000000..887107a68
--- /dev/null
+++ b/integration_tests/codegen_fail/fail/interface/method_default_impl.stderr
@@ -0,0 +1,10 @@
+error: GraphQL interface trait method can't have default implementation
+ --> fail/interface/method_default_impl.rs:5:26
+  |
+5 |       fn id(&self) -> &str {
+  |  __________________________^
+6 | |         "default"
+7 | |     }
+  | |_____^
+  |
+  = note: https://spec.graphql.org/June2018/#sec-Interfaces
diff --git a/integration_tests/codegen_fail/fail/interface/missing_field.rs b/integration_tests/codegen_fail/fail/interface/missing_field.rs
new file mode 100644
index 000000000..641556939
--- /dev/null
+++ b/integration_tests/codegen_fail/fail/interface/missing_field.rs
@@ -0,0 +1,14 @@
+use juniper::{graphql_interface, GraphQLObject};
+
+#[derive(GraphQLObject)]
+#[graphql(impl = CharacterValue)]
+pub struct ObjA {
+    test: String,
+}
+
+#[graphql_interface(for = ObjA)]
+trait Character {
+    fn id(&self) -> &str;
+}
+
+fn main() {}
diff --git a/integration_tests/codegen_fail/fail/interface/missing_field.stderr b/integration_tests/codegen_fail/fail/interface/missing_field.stderr
new file mode 100644
index 000000000..d0efa3518
--- /dev/null
+++ b/integration_tests/codegen_fail/fail/interface/missing_field.stderr
@@ -0,0 +1,7 @@
+error[E0080]: evaluation of constant value failed
+ --> fail/interface/missing_field.rs:9:1
+  |
+9 | #[graphql_interface(for = ObjA)]
+  | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'Failed to implement interface `Character` on `ObjA`: Field `id` isn't implemented on `ObjA`.', $DIR/fail/interface/missing_field.rs:9:1
+  |
+  = note: this error originates in the macro `$crate::panic::panic_2015` (in Nightly builds, run with -Z macro-backtrace for more info)
diff --git a/integration_tests/codegen_fail/fail/interface/missing_field_argument.rs b/integration_tests/codegen_fail/fail/interface/missing_field_argument.rs
new file mode 100644
index 000000000..b71a38d6b
--- /dev/null
+++ b/integration_tests/codegen_fail/fail/interface/missing_field_argument.rs
@@ -0,0 +1,19 @@
+use juniper::{graphql_interface, graphql_object};
+
+pub struct ObjA {
+    id: String,
+}
+
+#[graphql_object(impl = CharacterValue)]
+impl ObjA {
+    fn id(&self) -> &String {
+        &self.id
+    }
+}
+
+#[graphql_interface(for = ObjA)]
+trait Character {
+    fn id(&self, is_present: bool) -> &str;
+}
+
+fn main() {}
diff --git a/integration_tests/codegen_fail/fail/interface/missing_field_argument.stderr b/integration_tests/codegen_fail/fail/interface/missing_field_argument.stderr
new file mode 100644
index 000000000..719cfd1a8
--- /dev/null
+++ b/integration_tests/codegen_fail/fail/interface/missing_field_argument.stderr
@@ -0,0 +1,7 @@
+error[E0080]: evaluation of constant value failed
+  --> fail/interface/missing_field_argument.rs:14:1
+   |
+14 | #[graphql_interface(for = ObjA)]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'Failed to implement interface `Character` on `ObjA`: Field `id`: Argument `isPresent` of type `Boolean!` was expected, but not found.', $DIR/fail/interface/missing_field_argument.rs:14:1
+   |
+   = note: this error originates in the macro `$crate::panic::panic_2015` (in Nightly builds, run with -Z macro-backtrace for more info)
diff --git a/integration_tests/codegen_fail/fail/interface/missing_for_attr.rs b/integration_tests/codegen_fail/fail/interface/missing_for_attr.rs
new file mode 100644
index 000000000..77a3043af
--- /dev/null
+++ b/integration_tests/codegen_fail/fail/interface/missing_for_attr.rs
@@ -0,0 +1,14 @@
+use juniper::{graphql_interface, GraphQLObject};
+
+#[derive(GraphQLObject)]
+#[graphql(impl = CharacterValue)]
+pub struct ObjA {
+    id: String,
+}
+
+#[graphql_interface]
+trait Character {
+    fn id(&self) -> &str;
+}
+
+fn main() {}
diff --git a/integration_tests/codegen_fail/fail/interface/missing_for_attr.stderr b/integration_tests/codegen_fail/fail/interface/missing_for_attr.stderr
new file mode 100644
index 000000000..84072d87d
--- /dev/null
+++ b/integration_tests/codegen_fail/fail/interface/missing_for_attr.stderr
@@ -0,0 +1,7 @@
+error[E0080]: evaluation of constant value failed
+ --> fail/interface/missing_for_attr.rs:3:10
+  |
+3 | #[derive(GraphQLObject)]
+  |          ^^^^^^^^^^^^^ the evaluated program panicked at 'Failed to implement interface `Character` on `ObjA`: missing implementer reference in interface's `for` attribute.', $DIR/fail/interface/missing_for_attr.rs:3:10
+  |
+  = note: this error originates in the macro `$crate::panic::panic_2015` (in Nightly builds, run with -Z macro-backtrace for more info)
diff --git a/integration_tests/codegen_fail/fail/interface/missing_impl_attr.rs b/integration_tests/codegen_fail/fail/interface/missing_impl_attr.rs
new file mode 100644
index 000000000..c35b23b33
--- /dev/null
+++ b/integration_tests/codegen_fail/fail/interface/missing_impl_attr.rs
@@ -0,0 +1,13 @@
+use juniper::{graphql_interface, GraphQLObject};
+
+#[derive(GraphQLObject)]
+pub struct ObjA {
+    id: String,
+}
+
+#[graphql_interface(for = ObjA)]
+trait Character {
+    fn id(&self) -> &str;
+}
+
+fn main() {}
diff --git a/integration_tests/codegen_fail/fail/interface/missing_impl_attr.stderr b/integration_tests/codegen_fail/fail/interface/missing_impl_attr.stderr
new file mode 100644
index 000000000..767a53203
--- /dev/null
+++ b/integration_tests/codegen_fail/fail/interface/missing_impl_attr.stderr
@@ -0,0 +1,7 @@
+error[E0080]: evaluation of constant value failed
+ --> fail/interface/missing_impl_attr.rs:8:1
+  |
+8 | #[graphql_interface(for = ObjA)]
+  | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'Failed to implement interface `Character` on `ObjA`: missing interface reference in implementer's `impl` attribute.', $DIR/fail/interface/missing_impl_attr.rs:8:1
+  |
+  = note: this error originates in the macro `$crate::panic::panic_2015` (in Nightly builds, run with -Z macro-backtrace for more info)
diff --git a/integration_tests/codegen_fail/fail/interface/name_double_underscored.rs b/integration_tests/codegen_fail/fail/interface/name_double_underscored.rs
index b233119f1..77343a973 100644
--- a/integration_tests/codegen_fail/fail/interface/name_double_underscored.rs
+++ b/integration_tests/codegen_fail/fail/interface/name_double_underscored.rs
@@ -2,9 +2,7 @@ use juniper::graphql_interface;
 
 #[graphql_interface]
 trait __Character {
-    fn id(&self) -> &str {
-        "funA"
-    }
+    fn id(&self) -> &str;
 }
 
 fn main() {}
diff --git a/integration_tests/codegen_fail/fail/interface/no_fields.rs b/integration_tests/codegen_fail/fail/interface/no_fields.rs
index 115960359..1308cdde8 100644
--- a/integration_tests/codegen_fail/fail/interface/no_fields.rs
+++ b/integration_tests/codegen_fail/fail/interface/no_fields.rs
@@ -1,15 +1,6 @@
-use juniper::{graphql_interface, GraphQLObject};
-
-#[derive(GraphQLObject)]
-#[graphql(impl = CharacterValue)]
-pub struct ObjA {
-    test: String,
-}
+use juniper::graphql_interface;
 
 #[graphql_interface]
-impl Character for ObjA {}
-
-#[graphql_interface(for = ObjA)]
 trait Character {}
 
-fn main() {}
\ No newline at end of file
+fn main() {}
diff --git a/integration_tests/codegen_fail/fail/interface/no_fields.stderr b/integration_tests/codegen_fail/fail/interface/no_fields.stderr
index ddab0d4cc..70ab57452 100644
--- a/integration_tests/codegen_fail/fail/interface/no_fields.stderr
+++ b/integration_tests/codegen_fail/fail/interface/no_fields.stderr
@@ -1,19 +1,7 @@
 error: GraphQL interface must have at least one field
-  --> fail/interface/no_fields.rs:13:1
-   |
-13 | trait Character {}
-   | ^^^^^^^^^^^^^^^^^^
-   |
-   = note: https://spec.graphql.org/June2018/#sec-Interfaces
-
-error[E0412]: cannot find type `CharacterValue` in this scope
- --> fail/interface/no_fields.rs:4:18
+ --> fail/interface/no_fields.rs:4:1
   |
-4 | #[graphql(impl = CharacterValue)]
-  |                  ^^^^^^^^^^^^^^ not found in this scope
-
-error[E0405]: cannot find trait `Character` in this scope
-  --> fail/interface/no_fields.rs:10:6
-   |
-10 | impl Character for ObjA {}
-   |      ^^^^^^^^^ not found in this scope
+4 | trait Character {}
+  | ^^^^^^^^^^^^^^^^^^
+  |
+  = note: https://spec.graphql.org/June2018/#sec-Interfaces
diff --git a/integration_tests/codegen_fail/fail/interface/non_subtype_return.rs b/integration_tests/codegen_fail/fail/interface/non_subtype_return.rs
new file mode 100644
index 000000000..8eab508d3
--- /dev/null
+++ b/integration_tests/codegen_fail/fail/interface/non_subtype_return.rs
@@ -0,0 +1,14 @@
+use juniper::{graphql_interface, GraphQLObject};
+
+#[derive(GraphQLObject)]
+#[graphql(impl = CharacterValue)]
+pub struct ObjA {
+    id: Vec<String>,
+}
+
+#[graphql_interface(for = ObjA)]
+trait Character {
+    fn id(&self) -> &str;
+}
+
+fn main() {}
diff --git a/integration_tests/codegen_fail/fail/interface/non_subtype_return.stderr b/integration_tests/codegen_fail/fail/interface/non_subtype_return.stderr
new file mode 100644
index 000000000..ca48f8464
--- /dev/null
+++ b/integration_tests/codegen_fail/fail/interface/non_subtype_return.stderr
@@ -0,0 +1,7 @@
+error[E0080]: evaluation of constant value failed
+ --> fail/interface/non_subtype_return.rs:9:1
+  |
+9 | #[graphql_interface(for = ObjA)]
+  | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'Failed to implement interface `Character` on `ObjA`: Field `id`: implementor is expected to return a subtype of interface's return object: `[String!]!` is not a subtype of `String!`.', $DIR/fail/interface/non_subtype_return.rs:9:1
+  |
+  = note: this error originates in the macro `$crate::panic::panic_2015` (in Nightly builds, run with -Z macro-backtrace for more info)
diff --git a/integration_tests/codegen_fail/fail/interface/wrong_argument_type.rs b/integration_tests/codegen_fail/fail/interface/wrong_argument_type.rs
new file mode 100644
index 000000000..388aea1ed
--- /dev/null
+++ b/integration_tests/codegen_fail/fail/interface/wrong_argument_type.rs
@@ -0,0 +1,19 @@
+use juniper::{graphql_interface, graphql_object};
+
+pub struct ObjA {
+    id: String,
+}
+
+#[graphql_object(impl = CharacterValue)]
+impl ObjA {
+    fn id(&self, _is_present: i32) -> &str {
+        &self.id
+    }
+}
+
+#[graphql_interface(for = ObjA)]
+trait Character {
+    fn id(&self, is_present: bool) -> &str;
+}
+
+fn main() {}
diff --git a/integration_tests/codegen_fail/fail/interface/wrong_argument_type.stderr b/integration_tests/codegen_fail/fail/interface/wrong_argument_type.stderr
new file mode 100644
index 000000000..204bd1234
--- /dev/null
+++ b/integration_tests/codegen_fail/fail/interface/wrong_argument_type.stderr
@@ -0,0 +1,7 @@
+error[E0080]: evaluation of constant value failed
+  --> fail/interface/wrong_argument_type.rs:14:1
+   |
+14 | #[graphql_interface(for = ObjA)]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'Failed to implement interface `Character` on `ObjA`: Field `id`: Argument `isPresent`: expected type `Boolean!`, found: `Int!`.', $DIR/fail/interface/wrong_argument_type.rs:14:1
+   |
+   = note: this error originates in the macro `$crate::panic::panic_2015` (in Nightly builds, run with -Z macro-backtrace for more info)
diff --git a/integration_tests/codegen_fail/fail/interface/wrong_item.rs b/integration_tests/codegen_fail/fail/interface/wrong_item_enum.rs
similarity index 100%
rename from integration_tests/codegen_fail/fail/interface/wrong_item.rs
rename to integration_tests/codegen_fail/fail/interface/wrong_item_enum.rs
diff --git a/integration_tests/codegen_fail/fail/interface/wrong_item.stderr b/integration_tests/codegen_fail/fail/interface/wrong_item_enum.stderr
similarity index 80%
rename from integration_tests/codegen_fail/fail/interface/wrong_item.stderr
rename to integration_tests/codegen_fail/fail/interface/wrong_item_enum.stderr
index a86b15c20..9c2607dd4 100644
--- a/integration_tests/codegen_fail/fail/interface/wrong_item.stderr
+++ b/integration_tests/codegen_fail/fail/interface/wrong_item_enum.stderr
@@ -1,5 +1,5 @@
-error: #[graphql_interface] attribute is applicable to trait definitions and trait implementations only
- --> $DIR/wrong_item.rs:8:1
+error: #[graphql_interface] attribute is applicable to trait definitions only
+ --> fail/interface/wrong_item_enum.rs:8:1
   |
 8 | #[graphql_interface(for = ObjA)]
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/integration_tests/codegen_fail/fail/interface/wrong_item_impl_block.rs b/integration_tests/codegen_fail/fail/interface/wrong_item_impl_block.rs
new file mode 100644
index 000000000..61e142410
--- /dev/null
+++ b/integration_tests/codegen_fail/fail/interface/wrong_item_impl_block.rs
@@ -0,0 +1,11 @@
+use juniper::{graphql_interface, GraphQLObject};
+
+#[derive(GraphQLObject)]
+pub struct ObjA {
+    test: String,
+}
+
+#[graphql_interface(for = ObjA)]
+impl ObjA {}
+
+fn main() {}
diff --git a/integration_tests/codegen_fail/fail/interface/wrong_item_impl_block.stderr b/integration_tests/codegen_fail/fail/interface/wrong_item_impl_block.stderr
new file mode 100644
index 000000000..712206c0b
--- /dev/null
+++ b/integration_tests/codegen_fail/fail/interface/wrong_item_impl_block.stderr
@@ -0,0 +1,7 @@
+error: #[graphql_interface] attribute is applicable to trait definitions only
+ --> fail/interface/wrong_item_impl_block.rs:8:1
+  |
+8 | #[graphql_interface(for = ObjA)]
+  | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+  |
+  = note: this error originates in the attribute macro `graphql_interface` (in Nightly builds, run with -Z macro-backtrace for more info)
diff --git a/integration_tests/juniper_tests/src/codegen/interface_attr.rs b/integration_tests/juniper_tests/src/codegen/interface_attr.rs
index d8869a04a..7750a1501 100644
--- a/integration_tests/juniper_tests/src/codegen/interface_attr.rs
+++ b/integration_tests/juniper_tests/src/codegen/interface_attr.rs
@@ -3,7 +3,7 @@
 use juniper::{
     execute, graphql_interface, graphql_object, graphql_value, graphql_vars, DefaultScalarValue,
     EmptyMutation, EmptySubscription, Executor, FieldError, FieldResult, GraphQLInputObject,
-    GraphQLObject, GraphQLType, IntoFieldError, RootNode, ScalarValue,
+    GraphQLObject, GraphQLType, GraphQLUnion, IntoFieldError, RootNode, ScalarValue,
 };
 
 fn schema<'q, C, Q>(query_root: Q) -> RootNode<'q, Q, EmptyMutation<C>, EmptySubscription<C>>
@@ -39,86 +39,61 @@ mod no_implers {
         fn id(&self) -> &str;
     }
 
-    #[graphql_interface(dyn = DynHero)]
-    trait Hero {
-        fn info(&self) -> &str;
-    }
-
     struct QueryRoot;
 
-    #[graphql_object(scalar = S: ScalarValue + Send + Sync)]
+    #[graphql_object]
     impl QueryRoot {
         fn character(&self) -> CharacterValue {
             unimplemented!()
         }
-
-        fn hero<S: ScalarValue + Send + Sync>(&self) -> Box<DynHero<'_, S>> {
-            unimplemented!()
-        }
     }
 
     #[tokio::test]
     async fn is_graphql_interface() {
-        let schema = schema(QueryRoot);
+        const DOC: &str = r#"{
+            __type(name: "Character") {
+                kind
+            }
+        }"#;
 
-        for interface in &["Character", "Hero"] {
-            let doc = format!(
-                r#"{{
-                    __type(name: "{}") {{
-                        kind
-                    }}
-                }}"#,
-                interface,
-            );
+        let schema = schema(QueryRoot);
 
-            assert_eq!(
-                execute(&doc, None, &schema, &graphql_vars! {}, &()).await,
-                Ok((graphql_value!({"__type": {"kind": "INTERFACE"}}), vec![])),
-            );
-        }
+        assert_eq!(
+            execute(DOC, None, &schema, &graphql_vars! {}, &()).await,
+            Ok((graphql_value!({"__type": {"kind": "INTERFACE"}}), vec![])),
+        );
     }
 
     #[tokio::test]
     async fn uses_trait_name() {
-        let schema = schema(QueryRoot);
+        const DOC: &str = r#"{
+            __type(name: "Character") {
+                name
+            }
+        }"#;
 
-        for interface in &["Character", "Hero"] {
-            let doc = format!(
-                r#"{{
-                    __type(name: "{}") {{
-                        name
-                    }}
-                }}"#,
-                interface,
-            );
+        let schema = schema(QueryRoot);
 
-            let expected_name: &str = *interface;
-            assert_eq!(
-                execute(&doc, None, &schema, &graphql_vars! {}, &()).await,
-                Ok((graphql_value!({"__type": {"name": expected_name}}), vec![])),
-            );
-        }
+        assert_eq!(
+            execute(DOC, None, &schema, &graphql_vars! {}, &()).await,
+            Ok((graphql_value!({"__type": {"name": "Character"}}), vec![])),
+        );
     }
 
     #[tokio::test]
     async fn has_no_description() {
-        let schema = schema(QueryRoot);
+        const DOC: &str = r#"{
+            __type(name: "Character") {
+                description
+            }
+        }"#;
 
-        for interface in &["Character", "Hero"] {
-            let doc = format!(
-                r#"{{
-                    __type(name: "{}") {{
-                        description
-                    }}
-                }}"#,
-                interface,
-            );
+        let schema = schema(QueryRoot);
 
-            assert_eq!(
-                execute(&doc, None, &schema, &graphql_vars! {}, &()).await,
-                Ok((graphql_value!({"__type": {"description": null}}), vec![])),
-            );
-        }
+        assert_eq!(
+            execute(DOC, None, &schema, &graphql_vars! {}, &()).await,
+            Ok((graphql_value!({"__type": {"description": null}}), vec![])),
+        );
     }
 }
 
@@ -130,49 +105,25 @@ mod trivial {
         fn id(&self) -> &str;
     }
 
-    #[graphql_interface(dyn = DynHero, for = [Human, Droid])]
-    trait Hero {
-        fn info(&self) -> &str;
-    }
-
     #[derive(GraphQLObject)]
-    #[graphql(impl = [CharacterValue, DynHero<__S>])]
+    #[graphql(impl = CharacterValue)]
     struct Human {
         id: String,
         home_planet: String,
     }
 
-    #[graphql_interface]
-    impl Character for Human {
-        fn id(&self) -> &str {
-            &self.id
-        }
-    }
-
-    #[graphql_interface(dyn)]
-    impl Hero for Human {
-        fn info(&self) -> &str {
-            &self.home_planet
-        }
-    }
-
-    #[derive(GraphQLObject)]
-    #[graphql(impl = [CharacterValue, DynHero<__S>])]
     struct Droid {
         id: String,
         primary_function: String,
     }
 
-    #[graphql_interface]
-    impl Character for Droid {
-        fn id(&self) -> &str {
+    #[graphql_object(impl = CharacterValue)]
+    impl Droid {
+        async fn id(&self) -> &str {
             &self.id
         }
-    }
 
-    #[graphql_interface(dyn)]
-    impl Hero for Droid {
-        fn info(&self) -> &str {
+        fn primary_function(&self) -> &str {
             &self.primary_function
         }
     }
@@ -183,7 +134,7 @@ mod trivial {
         Droid,
     }
 
-    #[graphql_object(scalar = S: ScalarValue + Send + Sync)]
+    #[graphql_object]
     impl QueryRoot {
         fn character(&self) -> CharacterValue {
             match self {
@@ -199,24 +150,10 @@ mod trivial {
                 .into(),
             }
         }
-
-        fn hero<S: ScalarValue + Send + Sync>(&self) -> Box<DynHero<'_, S>> {
-            let ch: Box<DynHero<'_, _>> = match self {
-                Self::Human => Box::new(Human {
-                    id: "human-32".to_string(),
-                    home_planet: "earth".to_string(),
-                }),
-                Self::Droid => Box::new(Droid {
-                    id: "droid-99".to_string(),
-                    primary_function: "run".to_string(),
-                }),
-            };
-            ch
-        }
     }
 
     #[tokio::test]
-    async fn enum_resolves_human() {
+    async fn resolves_human() {
         const DOC: &str = r#"{
             character {
                 ... on Human {
@@ -231,14 +168,17 @@ mod trivial {
         assert_eq!(
             execute(DOC, None, &schema, &graphql_vars! {}, &()).await,
             Ok((
-                graphql_value!({"character": {"humanId": "human-32", "homePlanet": "earth"}}),
+                graphql_value!({"character": {
+                    "humanId": "human-32",
+                    "homePlanet": "earth",
+                }}),
                 vec![],
             )),
         );
     }
 
     #[tokio::test]
-    async fn enum_resolves_droid() {
+    async fn resolves_droid() {
         const DOC: &str = r#"{
             character {
                 ... on Droid {
@@ -253,58 +193,17 @@ mod trivial {
         assert_eq!(
             execute(DOC, None, &schema, &graphql_vars! {}, &()).await,
             Ok((
-                graphql_value!({"character": {"droidId": "droid-99", "primaryFunction": "run"}}),
-                vec![],
-            )),
-        );
-    }
-
-    #[tokio::test]
-    async fn dyn_resolves_human() {
-        const DOC: &str = r#"{
-            hero {
-                ... on Human {
-                    humanId: id
-                    homePlanet
-                }
-            }
-        }"#;
-
-        let schema = schema(QueryRoot::Human);
-
-        assert_eq!(
-            execute(DOC, None, &schema, &graphql_vars! {}, &()).await,
-            Ok((
-                graphql_value!({"hero": {"humanId": "human-32", "homePlanet": "earth"}}),
-                vec![],
-            )),
-        );
-    }
-
-    #[tokio::test]
-    async fn dyn_resolves_droid() {
-        const DOC: &str = r#"{
-            hero {
-                ... on Droid {
-                    droidId: id
-                    primaryFunction
-                }
-            }
-        }"#;
-
-        let schema = schema(QueryRoot::Droid);
-
-        assert_eq!(
-            execute(DOC, None, &schema, &graphql_vars! {}, &()).await,
-            Ok((
-                graphql_value!({"hero": {"droidId": "droid-99", "primaryFunction": "run"}}),
+                graphql_value!({"character": {
+                    "droidId": "droid-99",
+                    "primaryFunction": "run",
+                }}),
                 vec![],
             )),
         );
     }
 
     #[tokio::test]
-    async fn enum_resolves_id_field() {
+    async fn resolves_id_field() {
         const DOC: &str = r#"{
             character {
                 id
@@ -326,73 +225,44 @@ mod trivial {
     }
 
     #[tokio::test]
-    async fn dyn_resolves_info_field() {
+    async fn is_graphql_interface() {
         const DOC: &str = r#"{
-            hero {
-                info
+            __type(name: "Character") {
+                kind
             }
         }"#;
 
-        for (root, expected_info) in &[(QueryRoot::Human, "earth"), (QueryRoot::Droid, "run")] {
-            let schema = schema(*root);
-
-            let expected_info: &str = *expected_info;
-            assert_eq!(
-                execute(DOC, None, &schema, &graphql_vars! {}, &()).await,
-                Ok((graphql_value!({"hero": {"info": expected_info}}), vec![])),
-            );
-        }
-    }
-
-    #[tokio::test]
-    async fn is_graphql_interface() {
         let schema = schema(QueryRoot::Human);
 
-        for interface in &["Character", "Hero"] {
-            let doc = format!(
-                r#"{{
-                    __type(name: "{}") {{
-                        kind
-                    }}
-                }}"#,
-                interface,
-            );
-
-            assert_eq!(
-                execute(&doc, None, &schema, &graphql_vars! {}, &()).await,
-                Ok((graphql_value!({"__type": {"kind": "INTERFACE"}}), vec![])),
-            );
-        }
+        assert_eq!(
+            execute(DOC, None, &schema, &graphql_vars! {}, &()).await,
+            Ok((graphql_value!({"__type": {"kind": "INTERFACE"}}), vec![])),
+        );
     }
 
     #[tokio::test]
     async fn registers_all_implementers() {
-        let schema = schema(QueryRoot::Human);
+        const DOC: &str = r#"{
+            __type(name: "Character") {
+                possibleTypes {
+                    kind
+                    name
+                }
+            }
+        }"#;
 
-        for interface in &["Character", "Hero"] {
-            let doc = format!(
-                r#"{{
-                    __type(name: "{}") {{
-                        possibleTypes {{
-                            kind
-                            name
-                        }}
-                    }}
-                }}"#,
-                interface,
-            );
+        let schema = schema(QueryRoot::Human);
 
-            assert_eq!(
-                execute(&doc, None, &schema, &graphql_vars! {}, &()).await,
-                Ok((
-                    graphql_value!({"__type": {"possibleTypes": [
-                        {"kind": "OBJECT", "name": "Droid"},
-                        {"kind": "OBJECT", "name": "Human"},
-                    ]}}),
-                    vec![],
-                )),
-            );
-        }
+        assert_eq!(
+            execute(DOC, None, &schema, &graphql_vars! {}, &()).await,
+            Ok((
+                graphql_value!({"__type": {"possibleTypes": [
+                    {"kind": "OBJECT", "name": "Droid"},
+                    {"kind": "OBJECT", "name": "Human"},
+                ]}}),
+                vec![],
+            )),
+        );
     }
 
     #[tokio::test]
@@ -417,7 +287,6 @@ mod trivial {
                 Ok((
                     graphql_value!({"__type": {"interfaces": [
                         {"kind": "INTERFACE", "name": "Character"},
-                        {"kind": "INTERFACE", "name": "Hero"},
                     ]}}),
                     vec![],
                 )),
@@ -427,45 +296,34 @@ mod trivial {
 
     #[tokio::test]
     async fn uses_trait_name() {
-        let schema = schema(QueryRoot::Human);
+        const DOC: &str = r#"{
+            __type(name: "Character") {
+                name
+            }
+        }"#;
 
-        for interface in &["Character", "Hero"] {
-            let doc = format!(
-                r#"{{
-                    __type(name: "{}") {{
-                        name
-                    }}
-                }}"#,
-                interface,
-            );
+        let schema = schema(QueryRoot::Human);
 
-            let expected_name: &str = *interface;
-            assert_eq!(
-                execute(&doc, None, &schema, &graphql_vars! {}, &()).await,
-                Ok((graphql_value!({"__type": {"name": expected_name}}), vec![])),
-            );
-        }
+        assert_eq!(
+            execute(DOC, None, &schema, &graphql_vars! {}, &()).await,
+            Ok((graphql_value!({"__type": {"name": "Character"}}), vec![])),
+        );
     }
 
     #[tokio::test]
     async fn has_no_description() {
-        let schema = schema(QueryRoot::Human);
+        const DOC: &str = r#"{
+            __type(name: "Character") {
+                description
+            }
+        }"#;
 
-        for interface in &["Character", "Hero"] {
-            let doc = format!(
-                r#"{{
-                    __type(name: "{}") {{
-                        description
-                    }}
-                }}"#,
-                interface,
-            );
+        let schema = schema(QueryRoot::Human);
 
-            assert_eq!(
-                execute(&doc, None, &schema, &graphql_vars! {}, &()).await,
-                Ok((graphql_value!({"__type": {"description": null}}), vec![])),
-            );
-        }
+        assert_eq!(
+            execute(DOC, None, &schema, &graphql_vars! {}, &()).await,
+            Ok((graphql_value!({"__type": {"description": null}}), vec![])),
+        );
     }
 }
 
@@ -484,25 +342,20 @@ mod explicit_alias {
         home_planet: String,
     }
 
-    #[graphql_interface]
-    impl Character for Human {
-        fn id(&self) -> &str {
-            &self.id
-        }
-    }
-
-    #[derive(GraphQLObject)]
-    #[graphql(impl = CharacterEnum)]
     struct Droid {
         id: String,
         primary_function: String,
     }
 
-    #[graphql_interface]
-    impl Character for Droid {
+    #[graphql_object(impl = CharacterEnum)]
+    impl Droid {
         fn id(&self) -> &str {
             &self.id
         }
+
+        fn primary_function(&self) -> &str {
+            &self.primary_function
+        }
     }
 
     #[derive(Clone, Copy)]
@@ -545,7 +398,10 @@ mod explicit_alias {
         assert_eq!(
             execute(DOC, None, &schema, &graphql_vars! {}, &()).await,
             Ok((
-                graphql_value!({"character": {"humanId": "human-32", "homePlanet": "earth"}}),
+                graphql_value!({"character": {
+                    "humanId": "human-32",
+                    "homePlanet": "earth",
+                }}),
                 vec![],
             )),
         );
@@ -567,7 +423,10 @@ mod explicit_alias {
         assert_eq!(
             execute(DOC, None, &schema, &graphql_vars! {}, &()).await,
             Ok((
-                graphql_value!({"character": {"droidId": "droid-99", "primaryFunction": "run"}}),
+                graphql_value!({"character": {
+                    "droidId": "droid-99",
+                    "primaryFunction": "run",
+                }}),
                 vec![],
             )),
         );
@@ -649,52 +508,28 @@ mod trivial_async {
 
     #[graphql_interface(for = [Human, Droid])]
     trait Character {
-        async fn id(&self) -> &str;
-    }
-
-    #[graphql_interface(dyn = DynHero, for = [Human, Droid])]
-    trait Hero {
-        async fn info(&self) -> &str;
+        fn id(&self) -> &str;
     }
 
     #[derive(GraphQLObject)]
-    #[graphql(impl = [CharacterValue, DynHero<__S>])]
+    #[graphql(impl = CharacterValue)]
     struct Human {
         id: String,
         home_planet: String,
     }
 
-    #[graphql_interface]
-    impl Character for Human {
-        async fn id(&self) -> &str {
-            &self.id
-        }
-    }
-
-    #[graphql_interface(dyn)]
-    impl Hero for Human {
-        async fn info(&self) -> &str {
-            &self.home_planet
-        }
-    }
-
-    #[derive(GraphQLObject)]
-    #[graphql(impl = [CharacterValue, DynHero<__S>])]
     struct Droid {
         id: String,
         primary_function: String,
     }
 
-    #[graphql_interface]
-    impl Character for Droid {
+    #[graphql_object(impl = CharacterValue)]
+    impl Droid {
         async fn id(&self) -> &str {
             &self.id
         }
-    }
 
-    #[graphql_interface(dyn)]
-    impl Hero for Droid {
-        async fn info(&self) -> &str {
+        fn primary_function(&self) -> &str {
             &self.primary_function
         }
     }
@@ -705,7 +540,7 @@ mod trivial_async {
         Droid,
     }
 
-    #[graphql_object(scalar = S: ScalarValue + Send + Sync)]
+    #[graphql_object]
     impl QueryRoot {
         fn character(&self) -> CharacterValue {
             match self {
@@ -721,24 +556,10 @@ mod trivial_async {
                 .into(),
             }
         }
-
-        fn hero<S: ScalarValue + Send + Sync>(&self) -> Box<DynHero<'_, S>> {
-            let ch: Box<DynHero<'_, _>> = match self {
-                Self::Human => Box::new(Human {
-                    id: "human-32".to_string(),
-                    home_planet: "earth".to_string(),
-                }),
-                Self::Droid => Box::new(Droid {
-                    id: "droid-99".to_string(),
-                    primary_function: "run".to_string(),
-                }),
-            };
-            ch
-        }
     }
 
     #[tokio::test]
-    async fn enum_resolves_human() {
+    async fn resolves_human() {
         const DOC: &str = r#"{
             character {
                 ... on Human {
@@ -760,7 +581,7 @@ mod trivial_async {
     }
 
     #[tokio::test]
-    async fn enum_resolves_droid() {
+    async fn resolves_droid() {
         const DOC: &str = r#"{
             character {
                 ... on Droid {
@@ -782,51 +603,7 @@ mod trivial_async {
     }
 
     #[tokio::test]
-    async fn dyn_resolves_human() {
-        const DOC: &str = r#"{
-            hero {
-                ... on Human {
-                    humanId: id
-                    homePlanet
-                }
-            }
-        }"#;
-
-        let schema = schema(QueryRoot::Human);
-
-        assert_eq!(
-            execute(DOC, None, &schema, &graphql_vars! {}, &()).await,
-            Ok((
-                graphql_value!({"hero": {"humanId": "human-32", "homePlanet": "earth"}}),
-                vec![],
-            )),
-        );
-    }
-
-    #[tokio::test]
-    async fn dyn_resolves_droid() {
-        const DOC: &str = r#"{
-            hero {
-                ... on Droid {
-                    droidId: id
-                    primaryFunction
-                }
-            }
-        }"#;
-
-        let schema = schema(QueryRoot::Droid);
-
-        assert_eq!(
-            execute(DOC, None, &schema, &graphql_vars! {}, &()).await,
-            Ok((
-                graphql_value!({"hero": {"droidId": "droid-99", "primaryFunction": "run"}}),
-                vec![],
-            )),
-        );
-    }
-
-    #[tokio::test]
-    async fn enum_resolves_id_field() {
+    async fn resolves_id_field() {
         const DOC: &str = r#"{
             character {
                 id
@@ -848,73 +625,44 @@ mod trivial_async {
     }
 
     #[tokio::test]
-    async fn dyn_resolves_info_field() {
+    async fn is_graphql_interface() {
         const DOC: &str = r#"{
-            hero {
-                info
+            __type(name: "Character") {
+                kind
             }
         }"#;
 
-        for (root, expected_info) in &[(QueryRoot::Human, "earth"), (QueryRoot::Droid, "run")] {
-            let schema = schema(*root);
-
-            let expected_info: &str = *expected_info;
-            assert_eq!(
-                execute(DOC, None, &schema, &graphql_vars! {}, &()).await,
-                Ok((graphql_value!({"hero": {"info": expected_info}}), vec![])),
-            );
-        }
-    }
-
-    #[tokio::test]
-    async fn is_graphql_interface() {
         let schema = schema(QueryRoot::Human);
 
-        for interface in &["Character", "Hero"] {
-            let doc = format!(
-                r#"{{
-                    __type(name: "{}") {{
-                        kind
-                    }}
-                }}"#,
-                interface,
-            );
-
-            assert_eq!(
-                execute(&doc, None, &schema, &graphql_vars! {}, &()).await,
-                Ok((graphql_value!({"__type": {"kind": "INTERFACE"}}), vec![])),
-            );
-        }
+        assert_eq!(
+            execute(DOC, None, &schema, &graphql_vars! {}, &()).await,
+            Ok((graphql_value!({"__type": {"kind": "INTERFACE"}}), vec![])),
+        );
     }
 
     #[tokio::test]
     async fn registers_all_implementers() {
-        let schema = schema(QueryRoot::Human);
+        const DOC: &str = r#"{
+            __type(name: "Character") {
+                possibleTypes {
+                    kind
+                    name
+                }
+            }
+        }"#;
 
-        for interface in &["Character", "Hero"] {
-            let doc = format!(
-                r#"{{
-                    __type(name: "{}") {{
-                        possibleTypes {{
-                            kind
-                            name
-                        }}
-                    }}
-                }}"#,
-                interface,
-            );
+        let schema = schema(QueryRoot::Human);
 
-            assert_eq!(
-                execute(&doc, None, &schema, &graphql_vars! {}, &()).await,
-                Ok((
-                    graphql_value!({"__type": {"possibleTypes": [
-                        {"kind": "OBJECT", "name": "Droid"},
-                        {"kind": "OBJECT", "name": "Human"},
-                    ]}}),
-                    vec![],
-                )),
-            );
-        }
+        assert_eq!(
+            execute(DOC, None, &schema, &graphql_vars! {}, &()).await,
+            Ok((
+                graphql_value!({"__type": {"possibleTypes": [
+                    {"kind": "OBJECT", "name": "Droid"},
+                    {"kind": "OBJECT", "name": "Human"},
+                ]}}),
+                vec![],
+            )),
+        );
     }
 
     #[tokio::test]
@@ -939,7 +687,6 @@ mod trivial_async {
                 Ok((
                     graphql_value!({"__type": {"interfaces": [
                         {"kind": "INTERFACE", "name": "Character"},
-                        {"kind": "INTERFACE", "name": "Hero"},
                     ]}}),
                     vec![],
                 )),
@@ -949,115 +696,72 @@ mod trivial_async {
 
     #[tokio::test]
     async fn uses_trait_name() {
-        let schema = schema(QueryRoot::Human);
+        const DOC: &str = r#"{
+            __type(name: "Character") {
+                name
+            }
+        }"#;
 
-        for interface in &["Character", "Hero"] {
-            let doc = format!(
-                r#"{{
-                    __type(name: "{}") {{
-                        name
-                    }}
-                }}"#,
-                interface,
-            );
+        let schema = schema(QueryRoot::Human);
 
-            let expected_name: &str = *interface;
-            assert_eq!(
-                execute(&doc, None, &schema, &graphql_vars! {}, &()).await,
-                Ok((graphql_value!({"__type": {"name": expected_name}}), vec![])),
-            );
-        }
+        assert_eq!(
+            execute(DOC, None, &schema, &graphql_vars! {}, &()).await,
+            Ok((graphql_value!({"__type": {"name": "Character"}}), vec![])),
+        );
     }
 
     #[tokio::test]
     async fn has_no_description() {
-        let schema = schema(QueryRoot::Human);
+        const DOC: &str = r#"{
+            __type(name: "Character") {
+                description
+            }
+        }"#;
 
-        for interface in &["Character", "Hero"] {
-            let doc = format!(
-                r#"{{
-                    __type(name: "{}") {{
-                        description
-                    }}
-                }}"#,
-                interface,
-            );
+        let schema = schema(QueryRoot::Human);
 
-            assert_eq!(
-                execute(&doc, None, &schema, &graphql_vars! {}, &()).await,
-                Ok((graphql_value!({"__type": {"description": null}}), vec![])),
-            );
-        }
+        assert_eq!(
+            execute(DOC, None, &schema, &graphql_vars! {}, &()).await,
+            Ok((graphql_value!({"__type": {"description": null}}), vec![])),
+        );
     }
 }
 
-mod explicit_async {
+mod fallible_field {
     use super::*;
 
-    #[graphql_interface(for = [Human, Droid])]
-    trait Character {
-        fn id(&self) -> &str;
+    struct CustomError;
 
-        async fn info(&self) -> String {
-            "None available".to_owned()
+    impl<S: ScalarValue> IntoFieldError<S> for CustomError {
+        fn into_field_error(self) -> FieldError<S> {
+            juniper::FieldError::new("Whatever", graphql_value!({"code": "some"}))
         }
     }
 
-    #[graphql_interface(dyn = DynHero, for = [Human, Droid])]
-    trait Hero {
-        async fn id(&self) -> &str {
-            "Non-identified"
-        }
-
-        fn info(&self) -> &str;
+    #[graphql_interface(for = [Human, Droid])]
+    trait Character {
+        fn id(&self) -> Result<&str, CustomError>;
     }
 
     #[derive(GraphQLObject)]
-    #[graphql(impl = [CharacterValue, DynHero<__S>])]
+    #[graphql(impl = CharacterValue)]
     struct Human {
         id: String,
         home_planet: String,
     }
 
-    #[graphql_interface]
-    impl Character for Human {
-        fn id(&self) -> &str {
-            &self.id
-        }
-
-        async fn info(&self) -> String {
-            format!("Home planet is {}", &self.home_planet)
-        }
-    }
-
-    #[graphql_interface(async, dyn)]
-    impl Hero for Human {
-        fn info(&self) -> &str {
-            &self.home_planet
-        }
-    }
-
-    #[derive(GraphQLObject)]
-    #[graphql(impl = [CharacterValue, DynHero<__S>])]
     struct Droid {
         id: String,
         primary_function: String,
     }
 
-    #[graphql_interface(async)]
-    impl Character for Droid {
-        fn id(&self) -> &str {
-            &self.id
-        }
-    }
-
-    #[graphql_interface(dyn)]
-    impl Hero for Droid {
-        async fn id(&self) -> &str {
-            &self.id
+    #[graphql_object(impl = CharacterValue)]
+    impl Droid {
+        fn id(&self) -> Result<String, CustomError> {
+            Ok(self.id.clone())
         }
 
-        fn info(&self) -> &str {
+        fn primary_function(&self) -> &str {
             &self.primary_function
         }
     }
@@ -1068,7 +772,7 @@ mod explicit_async {
         Droid,
     }
 
-    #[graphql_object(scalar = S: ScalarValue + Send + Sync)]
+    #[graphql_object]
     impl QueryRoot {
         fn character(&self) -> CharacterValue {
             match self {
@@ -1084,24 +788,10 @@ mod explicit_async {
                 .into(),
             }
         }
-
-        fn hero<S: ScalarValue + Send + Sync>(&self) -> Box<DynHero<'_, S>> {
-            let ch: Box<DynHero<'_, _>> = match self {
-                Self::Human => Box::new(Human {
-                    id: "human-32".to_string(),
-                    home_planet: "earth".to_string(),
-                }),
-                Self::Droid => Box::new(Droid {
-                    id: "droid-99".to_string(),
-                    primary_function: "run".to_string(),
-                }),
-            };
-            ch
-        }
     }
 
     #[tokio::test]
-    async fn enum_resolves_human() {
+    async fn resolves_human() {
         const DOC: &str = r#"{
             character {
                 ... on Human {
@@ -1123,7 +813,7 @@ mod explicit_async {
     }
 
     #[tokio::test]
-    async fn enum_resolves_droid() {
+    async fn resolves_droid() {
         const DOC: &str = r#"{
             character {
                 ... on Droid {
@@ -1145,925 +835,91 @@ mod explicit_async {
     }
 
     #[tokio::test]
-    async fn dyn_resolves_human() {
+    async fn resolves_id_field() {
         const DOC: &str = r#"{
-            hero {
-                ... on Human {
-                    humanId: id
-                    homePlanet
-                }
+            character {
+                id
             }
         }"#;
 
-        let schema = schema(QueryRoot::Human);
+        for (root, expected_id) in &[
+            (QueryRoot::Human, "human-32"),
+            (QueryRoot::Droid, "droid-99"),
+        ] {
+            let schema = schema(*root);
 
-        assert_eq!(
-            execute(DOC, None, &schema, &graphql_vars! {}, &()).await,
-            Ok((
-                graphql_value!({"hero": {"humanId": "human-32", "homePlanet": "earth"}}),
-                vec![],
-            )),
-        );
+            let expected_id: &str = *expected_id;
+            assert_eq!(
+                execute(DOC, None, &schema, &graphql_vars! {}, &()).await,
+                Ok((graphql_value!({"character": {"id": expected_id}}), vec![])),
+            );
+        }
     }
 
     #[tokio::test]
-    async fn dyn_resolves_droid() {
+    async fn has_correct_graphql_type() {
         const DOC: &str = r#"{
-            hero {
-                ... on Droid {
-                    droidId: id
-                    primaryFunction
+            __type(name: "Character") {
+                name
+                kind
+                fields {
+                    name
+                    type {
+                        kind
+                        ofType {
+                            name
+                        }
+                    }
                 }
             }
         }"#;
 
-        let schema = schema(QueryRoot::Droid);
+        let schema = schema(QueryRoot::Human);
 
         assert_eq!(
             execute(DOC, None, &schema, &graphql_vars! {}, &()).await,
             Ok((
-                graphql_value!({"hero": {"droidId": "droid-99", "primaryFunction": "run"}}),
+                graphql_value!({"__type": {
+                    "name": "Character",
+                    "kind": "INTERFACE",
+                    "fields": [{
+                        "name": "id",
+                        "type": {"kind": "NON_NULL", "ofType": {"name": "String"}},
+                    }],
+                }}),
                 vec![],
             )),
         );
     }
+}
 
-    #[tokio::test]
-    async fn enum_resolves_fields() {
-        const DOC: &str = r#"{
-            character {
-                id
-                info
-            }
-        }"#;
-
-        for (root, expected_id, expected_info) in &[
-            (QueryRoot::Human, "human-32", "Home planet is earth"),
-            (QueryRoot::Droid, "droid-99", "None available"),
-        ] {
-            let schema = schema(*root);
-
-            let expected_id: &str = *expected_id;
-            let expected_info: &str = *expected_info;
-            assert_eq!(
-                execute(DOC, None, &schema, &graphql_vars! {}, &()).await,
-                Ok((
-                    graphql_value!({"character": {
-                        "id": expected_id,
-                        "info": expected_info,
-                    }}),
-                    vec![],
-                )),
-            );
-        }
-    }
-
-    #[tokio::test]
-    async fn dyn_resolves_fields() {
-        const DOC: &str = r#"{
-            hero {
-                id
-                info
-            }
-        }"#;
-
-        for (root, expected_id, expected_info) in &[
-            (QueryRoot::Human, "Non-identified", "earth"),
-            (QueryRoot::Droid, "droid-99", "run"),
-        ] {
-            let schema = schema(*root);
-
-            let expected_id: &str = *expected_id;
-            let expected_info: &str = *expected_info;
-            assert_eq!(
-                execute(DOC, None, &schema, &graphql_vars! {}, &()).await,
-                Ok((
-                    graphql_value!({"hero": {
-                        "id": expected_id,
-                        "info": expected_info,
-                    }}),
-                    vec![],
-                )),
-            );
-        }
-    }
-}
-
-mod fallible_field {
-    use super::*;
-
-    struct CustomError;
-
-    impl<S: ScalarValue> IntoFieldError<S> for CustomError {
-        fn into_field_error(self) -> FieldError<S> {
-            juniper::FieldError::new("Whatever", graphql_value!({"code": "some"}))
-        }
-    }
-
-    #[graphql_interface(for = [Human, Droid])]
-    trait Character {
-        fn id(&self) -> Result<&str, CustomError>;
-    }
-
-    #[graphql_interface(dyn = DynHero, for = [Human, Droid])]
-    trait Hero {
-        fn info(&self) -> Result<&str, CustomError>;
-    }
-
-    #[derive(GraphQLObject)]
-    #[graphql(impl = [CharacterValue, DynHero<__S>])]
-    struct Human {
-        id: String,
-        home_planet: String,
-    }
-
-    #[graphql_interface]
-    impl Character for Human {
-        fn id(&self) -> Result<&str, CustomError> {
-            Ok(&self.id)
-        }
-    }
-
-    #[graphql_interface(dyn)]
-    impl Hero for Human {
-        fn info(&self) -> Result<&str, CustomError> {
-            Ok(&self.home_planet)
-        }
-    }
-
-    #[derive(GraphQLObject)]
-    #[graphql(impl = [CharacterValue, DynHero<__S>])]
-    struct Droid {
-        id: String,
-        primary_function: String,
-    }
-
-    #[graphql_interface]
-    impl Character for Droid {
-        fn id(&self) -> Result<&str, CustomError> {
-            Ok(&self.id)
-        }
-    }
-
-    #[graphql_interface(dyn)]
-    impl Hero for Droid {
-        fn info(&self) -> Result<&str, CustomError> {
-            Ok(&self.primary_function)
-        }
-    }
-
-    #[derive(Clone, Copy)]
-    enum QueryRoot {
-        Human,
-        Droid,
-    }
-
-    #[graphql_object(scalar = S: ScalarValue + Send + Sync)]
-    impl QueryRoot {
-        fn character(&self) -> CharacterValue {
-            match self {
-                Self::Human => Human {
-                    id: "human-32".to_string(),
-                    home_planet: "earth".to_string(),
-                }
-                .into(),
-                Self::Droid => Droid {
-                    id: "droid-99".to_string(),
-                    primary_function: "run".to_string(),
-                }
-                .into(),
-            }
-        }
-
-        fn hero<S: ScalarValue + Send + Sync>(&self) -> Box<DynHero<'_, S>> {
-            let ch: Box<DynHero<'_, _>> = match self {
-                Self::Human => Box::new(Human {
-                    id: "human-32".to_string(),
-                    home_planet: "earth".to_string(),
-                }),
-                Self::Droid => Box::new(Droid {
-                    id: "droid-99".to_string(),
-                    primary_function: "run".to_string(),
-                }),
-            };
-            ch
-        }
-    }
-
-    #[tokio::test]
-    async fn enum_resolves_human() {
-        const DOC: &str = r#"{
-            character {
-                ... on Human {
-                    humanId: id
-                    homePlanet
-                }
-            }
-        }"#;
-
-        let schema = schema(QueryRoot::Human);
-
-        assert_eq!(
-            execute(DOC, None, &schema, &graphql_vars! {}, &()).await,
-            Ok((
-                graphql_value!({"character": {"humanId": "human-32", "homePlanet": "earth"}}),
-                vec![],
-            )),
-        );
-    }
-
-    #[tokio::test]
-    async fn enum_resolves_droid() {
-        const DOC: &str = r#"{
-            character {
-                ... on Droid {
-                    droidId: id
-                    primaryFunction
-                }
-            }
-        }"#;
-
-        let schema = schema(QueryRoot::Droid);
-
-        assert_eq!(
-            execute(DOC, None, &schema, &graphql_vars! {}, &()).await,
-            Ok((
-                graphql_value!({"character": {"droidId": "droid-99", "primaryFunction": "run"}}),
-                vec![],
-            )),
-        );
-    }
-
-    #[tokio::test]
-    async fn dyn_resolves_human() {
-        const DOC: &str = r#"{
-            hero {
-                ... on Human {
-                    humanId: id
-                    homePlanet
-                }
-            }
-        }"#;
-
-        let schema = schema(QueryRoot::Human);
-
-        assert_eq!(
-            execute(DOC, None, &schema, &graphql_vars! {}, &()).await,
-            Ok((
-                graphql_value!({"hero": {"humanId": "human-32", "homePlanet": "earth"}}),
-                vec![],
-            )),
-        );
-    }
-
-    #[tokio::test]
-    async fn dyn_resolves_droid() {
-        const DOC: &str = r#"{
-            hero {
-                ... on Droid {
-                    droidId: id
-                    primaryFunction
-                }
-            }
-        }"#;
-
-        let schema = schema(QueryRoot::Droid);
-
-        assert_eq!(
-            execute(DOC, None, &schema, &graphql_vars! {}, &()).await,
-            Ok((
-                graphql_value!({"hero": {"droidId": "droid-99", "primaryFunction": "run"}}),
-                vec![],
-            )),
-        );
-    }
-
-    #[tokio::test]
-    async fn enum_resolves_id_field() {
-        const DOC: &str = r#"{
-            character {
-                id
-            }
-        }"#;
-
-        for (root, expected_id) in &[
-            (QueryRoot::Human, "human-32"),
-            (QueryRoot::Droid, "droid-99"),
-        ] {
-            let schema = schema(*root);
-
-            let expected_id: &str = *expected_id;
-            assert_eq!(
-                execute(DOC, None, &schema, &graphql_vars! {}, &()).await,
-                Ok((graphql_value!({"character": {"id": expected_id}}), vec![])),
-            );
-        }
-    }
-
-    #[tokio::test]
-    async fn dyn_resolves_info_field() {
-        const DOC: &str = r#"{
-            hero {
-                info
-            }
-        }"#;
-
-        for (root, expected_info) in &[(QueryRoot::Human, "earth"), (QueryRoot::Droid, "run")] {
-            let schema = schema(*root);
-
-            let expected_info: &str = *expected_info;
-            assert_eq!(
-                execute(DOC, None, &schema, &graphql_vars! {}, &()).await,
-                Ok((graphql_value!({"hero": {"info": expected_info}}), vec![])),
-            );
-        }
-    }
-
-    #[tokio::test]
-    async fn has_correct_graphql_type() {
-        let schema = schema(QueryRoot::Human);
-
-        for (interface, field) in &[("Character", "id"), ("Hero", "info")] {
-            let doc = format!(
-                r#"{{
-                    __type(name: "{}") {{
-                        name
-                        kind
-                        fields {{
-                            name
-                            type {{
-                                kind
-                                ofType {{
-                                    name
-                                }}
-                            }}
-                        }}
-                    }}
-                }}"#,
-                interface,
-            );
-
-            let expected_name: &str = *interface;
-            let expected_field_name: &str = *field;
-            assert_eq!(
-                execute(&doc, None, &schema, &graphql_vars! {}, &()).await,
-                Ok((
-                    graphql_value!({"__type": {
-                        "name": expected_name,
-                        "kind": "INTERFACE",
-                        "fields": [{
-                            "name": expected_field_name,
-                            "type": {"kind": "NON_NULL", "ofType": {"name": "String"}},
-                        }]
-                    }}),
-                    vec![],
-                )),
-            );
-        }
-    }
-}
-
-mod generic {
-    use super::*;
-
-    #[graphql_interface(for = [Human, Droid])]
-    trait Character<A = (), B: ?Sized = ()> {
-        fn id(&self) -> &str;
-    }
-
-    #[graphql_interface(dyn = DynHero, for = [Human, Droid])]
-    trait Hero<A, B: ?Sized> {
-        fn info(&self) -> &str;
-    }
-
-    #[derive(GraphQLObject)]
-    #[graphql(impl = [CharacterValue, DynHero<u8, (), __S>])]
-    struct Human {
-        id: String,
-        home_planet: String,
-    }
-
-    #[graphql_interface]
-    impl<A, B: ?Sized> Character<A, B> for Human {
-        fn id(&self) -> &str {
-            &self.id
-        }
-    }
-
-    #[graphql_interface(dyn)]
-    impl<A, B: ?Sized> Hero<A, B> for Human {
-        fn info(&self) -> &str {
-            &self.home_planet
-        }
-    }
-
-    #[derive(GraphQLObject)]
-    #[graphql(impl = [CharacterValue<(), u8>, DynHero<u8, (), __S>])]
-    struct Droid {
-        id: String,
-        primary_function: String,
-    }
-
-    #[graphql_interface]
-    impl<A, B: ?Sized> Character<A, B> for Droid {
-        fn id(&self) -> &str {
-            &self.id
-        }
-    }
-
-    #[graphql_interface(dyn)]
-    impl<A, B: ?Sized> Hero<A, B> for Droid {
-        fn info(&self) -> &str {
-            &self.primary_function
-        }
-    }
-
-    #[derive(Clone, Copy)]
-    enum QueryRoot {
-        Human,
-        Droid,
-    }
-
-    #[graphql_object(scalar = S: ScalarValue + Send + Sync)]
-    impl QueryRoot {
-        fn character(&self) -> CharacterValue {
-            match self {
-                Self::Human => Human {
-                    id: "human-32".to_string(),
-                    home_planet: "earth".to_string(),
-                }
-                .into(),
-                Self::Droid => Droid {
-                    id: "droid-99".to_string(),
-                    primary_function: "run".to_string(),
-                }
-                .into(),
-            }
-        }
-
-        fn hero<S: ScalarValue + Send + Sync>(&self) -> Box<DynHero<'_, u8, (), S>> {
-            let ch: Box<DynHero<'_, u8, (), _>> = match self {
-                Self::Human => Box::new(Human {
-                    id: "human-32".to_string(),
-                    home_planet: "earth".to_string(),
-                }),
-                Self::Droid => Box::new(Droid {
-                    id: "droid-99".to_string(),
-                    primary_function: "run".to_string(),
-                }),
-            };
-            ch
-        }
-    }
-
-    #[tokio::test]
-    async fn enum_resolves_human() {
-        const DOC: &str = r#"{
-            character {
-                ... on Human {
-                    humanId: id
-                    homePlanet
-                }
-            }
-        }"#;
-
-        let schema = schema(QueryRoot::Human);
-
-        assert_eq!(
-            execute(DOC, None, &schema, &graphql_vars! {}, &()).await,
-            Ok((
-                graphql_value!({"character": {"humanId": "human-32", "homePlanet": "earth"}}),
-                vec![],
-            )),
-        );
-    }
-
-    #[tokio::test]
-    async fn enum_resolves_droid() {
-        const DOC: &str = r#"{
-            character {
-                ... on Droid {
-                    droidId: id
-                    primaryFunction
-                }
-            }
-        }"#;
-
-        let schema = schema(QueryRoot::Droid);
-
-        assert_eq!(
-            execute(DOC, None, &schema, &graphql_vars! {}, &()).await,
-            Ok((
-                graphql_value!({"character": {"droidId": "droid-99", "primaryFunction": "run"}}),
-                vec![],
-            )),
-        );
-    }
-
-    #[tokio::test]
-    async fn dyn_resolves_human() {
-        const DOC: &str = r#"{
-            hero {
-                ... on Human {
-                    humanId: id
-                    homePlanet
-                }
-            }
-        }"#;
-
-        let schema = schema(QueryRoot::Human);
-
-        assert_eq!(
-            execute(DOC, None, &schema, &graphql_vars! {}, &()).await,
-            Ok((
-                graphql_value!({"hero": {"humanId": "human-32", "homePlanet": "earth"}}),
-                vec![],
-            )),
-        );
-    }
-
-    #[tokio::test]
-    async fn dyn_resolves_droid() {
-        const DOC: &str = r#"{
-            hero {
-                ... on Droid {
-                    droidId: id
-                    primaryFunction
-                }
-            }
-        }"#;
-
-        let schema = schema(QueryRoot::Droid);
-
-        assert_eq!(
-            execute(DOC, None, &schema, &graphql_vars! {}, &()).await,
-            Ok((
-                graphql_value!({"hero": {"droidId": "droid-99", "primaryFunction": "run"}}),
-                vec![],
-            )),
-        );
-    }
-
-    #[tokio::test]
-    async fn enum_resolves_id_field() {
-        const DOC: &str = r#"{
-            character {
-                id
-            }
-        }"#;
-
-        for (root, expected_id) in &[
-            (QueryRoot::Human, "human-32"),
-            (QueryRoot::Droid, "droid-99"),
-        ] {
-            let schema = schema(*root);
-
-            let expected_id: &str = *expected_id;
-            assert_eq!(
-                execute(DOC, None, &schema, &graphql_vars! {}, &()).await,
-                Ok((graphql_value!({"character": {"id": expected_id}}), vec![])),
-            );
-        }
-    }
-
-    #[tokio::test]
-    async fn dyn_resolves_info_field() {
-        const DOC: &str = r#"{
-            hero {
-                info
-            }
-        }"#;
-
-        for (root, expected_info) in &[(QueryRoot::Human, "earth"), (QueryRoot::Droid, "run")] {
-            let schema = schema(*root);
-
-            let expected_info: &str = *expected_info;
-            assert_eq!(
-                execute(DOC, None, &schema, &graphql_vars! {}, &()).await,
-                Ok((graphql_value!({"hero": {"info": expected_info}}), vec![])),
-            );
-        }
-    }
-
-    #[tokio::test]
-    async fn uses_trait_name_without_type_params() {
-        for interface in &["Character", "Hero"] {
-            let doc = format!(
-                r#"{{
-                    __type(name: "{}") {{
-                        name
-                    }}
-                }}"#,
-                interface,
-            );
-
-            let schema = schema(QueryRoot::Human);
-
-            let expected_name: &str = *interface;
-            assert_eq!(
-                execute(&doc, None, &schema, &graphql_vars! {}, &()).await,
-                Ok((graphql_value!({"__type": {"name": expected_name}}), vec![])),
-            );
-        }
-    }
-}
-
-mod generic_async {
-    use super::*;
-
-    #[graphql_interface(for = [Human, Droid])]
-    trait Character<A = (), B: ?Sized = ()> {
-        async fn id(&self) -> &str;
-    }
-
-    #[graphql_interface(dyn = DynHero, for = [Human, Droid])]
-    trait Hero<A, B: ?Sized> {
-        async fn info(&self) -> &str;
-    }
-
-    #[derive(GraphQLObject)]
-    #[graphql(impl = [CharacterValue, DynHero<u8, (), __S>])]
-    struct Human {
-        id: String,
-        home_planet: String,
-    }
-
-    #[graphql_interface]
-    impl<A, B: ?Sized> Character<A, B> for Human {
-        async fn id(&self) -> &str {
-            &self.id
-        }
-    }
-
-    #[graphql_interface(dyn)]
-    impl<A, B: ?Sized> Hero<A, B> for Human {
-        async fn info(&self) -> &str {
-            &self.home_planet
-        }
-    }
-
-    #[derive(GraphQLObject)]
-    #[graphql(impl = [CharacterValue<(), u8>, DynHero<u8, (), __S>])]
-    struct Droid {
-        id: String,
-        primary_function: String,
-    }
-
-    #[graphql_interface]
-    impl<A, B: ?Sized> Character<A, B> for Droid {
-        async fn id(&self) -> &str {
-            &self.id
-        }
-    }
-
-    #[graphql_interface(dyn)]
-    impl<A, B: ?Sized> Hero<A, B> for Droid {
-        async fn info(&self) -> &str {
-            &self.primary_function
-        }
-    }
-
-    #[derive(Clone, Copy)]
-    enum QueryRoot {
-        Human,
-        Droid,
-    }
-
-    #[graphql_object(scalar = S: ScalarValue + Send + Sync)]
-    impl QueryRoot {
-        fn character(&self) -> CharacterValue {
-            match self {
-                Self::Human => Human {
-                    id: "human-32".to_string(),
-                    home_planet: "earth".to_string(),
-                }
-                .into(),
-                Self::Droid => Droid {
-                    id: "droid-99".to_string(),
-                    primary_function: "run".to_string(),
-                }
-                .into(),
-            }
-        }
-
-        fn hero<S: ScalarValue + Send + Sync>(&self) -> Box<DynHero<'_, u8, (), S>> {
-            let ch: Box<DynHero<'_, u8, (), _>> = match self {
-                Self::Human => Box::new(Human {
-                    id: "human-32".to_string(),
-                    home_planet: "earth".to_string(),
-                }),
-                Self::Droid => Box::new(Droid {
-                    id: "droid-99".to_string(),
-                    primary_function: "run".to_string(),
-                }),
-            };
-            ch
-        }
-    }
-
-    #[tokio::test]
-    async fn enum_resolves_human() {
-        const DOC: &str = r#"{
-            character {
-                ... on Human {
-                    humanId: id
-                    homePlanet
-                }
-            }
-        }"#;
-
-        let schema = schema(QueryRoot::Human);
-
-        assert_eq!(
-            execute(DOC, None, &schema, &graphql_vars! {}, &()).await,
-            Ok((
-                graphql_value!({"character": {"humanId": "human-32", "homePlanet": "earth"}}),
-                vec![],
-            )),
-        );
-    }
-
-    #[tokio::test]
-    async fn enum_resolves_droid() {
-        const DOC: &str = r#"{
-            character {
-                ... on Droid {
-                    droidId: id
-                    primaryFunction
-                }
-            }
-        }"#;
-
-        let schema = schema(QueryRoot::Droid);
-
-        assert_eq!(
-            execute(DOC, None, &schema, &graphql_vars! {}, &()).await,
-            Ok((
-                graphql_value!({"character": {"droidId": "droid-99", "primaryFunction": "run"}}),
-                vec![],
-            )),
-        );
-    }
-
-    #[tokio::test]
-    async fn dyn_resolves_human() {
-        const DOC: &str = r#"{
-            hero {
-                ... on Human {
-                    humanId: id
-                    homePlanet
-                }
-            }
-        }"#;
-
-        let schema = schema(QueryRoot::Human);
-
-        assert_eq!(
-            execute(DOC, None, &schema, &graphql_vars! {}, &()).await,
-            Ok((
-                graphql_value!({"hero": {"humanId": "human-32", "homePlanet": "earth"}}),
-                vec![],
-            )),
-        );
-    }
-
-    #[tokio::test]
-    async fn dyn_resolves_droid() {
-        const DOC: &str = r#"{
-            hero {
-                ... on Droid {
-                    droidId: id
-                    primaryFunction
-                }
-            }
-        }"#;
-
-        let schema = schema(QueryRoot::Droid);
-
-        assert_eq!(
-            execute(DOC, None, &schema, &graphql_vars! {}, &()).await,
-            Ok((
-                graphql_value!({"hero": {"droidId": "droid-99", "primaryFunction": "run"}}),
-                vec![],
-            )),
-        );
-    }
-
-    #[tokio::test]
-    async fn enum_resolves_id_field() {
-        const DOC: &str = r#"{
-            character {
-                id
-            }
-        }"#;
-
-        for (root, expected_id) in &[
-            (QueryRoot::Human, "human-32"),
-            (QueryRoot::Droid, "droid-99"),
-        ] {
-            let schema = schema(*root);
-
-            let expected_id: &str = *expected_id;
-            assert_eq!(
-                execute(DOC, None, &schema, &graphql_vars! {}, &()).await,
-                Ok((graphql_value!({"character": {"id": expected_id}}), vec![])),
-            );
-        }
-    }
-
-    #[tokio::test]
-    async fn dyn_resolves_info_field() {
-        const DOC: &str = r#"{
-            hero {
-                info
-            }
-        }"#;
-
-        for (root, expected_info) in &[(QueryRoot::Human, "earth"), (QueryRoot::Droid, "run")] {
-            let schema = schema(*root);
-
-            let expected_info: &str = *expected_info;
-            assert_eq!(
-                execute(DOC, None, &schema, &graphql_vars! {}, &()).await,
-                Ok((graphql_value!({"hero": {"info": expected_info}}), vec![])),
-            );
-        }
-    }
-
-    #[tokio::test]
-    async fn uses_trait_name_without_type_params() {
-        for interface in &["Character", "Hero"] {
-            let doc = format!(
-                r#"{{
-                    __type(name: "{}") {{
-                        name
-                    }}
-                }}"#,
-                interface,
-            );
-
-            let schema = schema(QueryRoot::Human);
-
-            let expected_name: &str = *interface;
-            assert_eq!(
-                execute(&doc, None, &schema, &graphql_vars! {}, &()).await,
-                Ok((graphql_value!({"__type": {"name": expected_name}}), vec![])),
-            );
-        }
-    }
-}
-
-mod generic_lifetime_async {
+mod generic {
     use super::*;
 
-    #[graphql_interface(for = [Human, Droid])]
-    trait Character<'me, A> {
-        async fn id<'a>(&'a self) -> &'a str;
-    }
-
-    #[graphql_interface(dyn = DynHero, for = [Human, Droid])]
-    trait Hero<'me, A> {
-        async fn info(&self) -> &str;
+    #[graphql_interface(for = [Human, Droid])]
+    trait Character<A = (), B: ?Sized = ()> {
+        fn id(&self) -> &str;
     }
 
     #[derive(GraphQLObject)]
-    #[graphql(impl = [CharacterValue<()>, DynHero<(), __S>])]
+    #[graphql(impl = CharacterValue)]
     struct Human {
         id: String,
         home_planet: String,
     }
 
-    #[graphql_interface]
-    impl<'me, A> Character<'me, A> for Human {
-        async fn id<'a>(&'a self) -> &'a str {
-            &self.id
-        }
-    }
-
-    #[graphql_interface(dyn)]
-    impl<'me, A> Hero<'me, A> for Human {
-        async fn info(&self) -> &str {
-            &self.home_planet
-        }
-    }
-
-    #[derive(GraphQLObject)]
-    #[graphql(impl = [CharacterValue<()>, DynHero<(), __S>])]
     struct Droid {
         id: String,
         primary_function: String,
     }
 
-    #[graphql_interface]
-    impl<'me, A> Character<'me, A> for Droid {
-        async fn id<'a>(&'a self) -> &'a str {
+    #[graphql_object(impl = CharacterValue<(), u8>)]
+    impl Droid {
+        fn id(&self) -> &str {
             &self.id
         }
-    }
 
-    #[graphql_interface(dyn)]
-    impl<'me, A> Hero<'me, A> for Droid {
-        async fn info(&self) -> &str {
+        fn primary_function(&self) -> &str {
             &self.primary_function
         }
     }
@@ -2074,9 +930,9 @@ mod generic_lifetime_async {
         Droid,
     }
 
-    #[graphql_object(scalar = S: ScalarValue + Send + Sync)]
+    #[graphql_object]
     impl QueryRoot {
-        fn character(&self) -> CharacterValue<'_, ()> {
+        fn character(&self) -> CharacterValue {
             match self {
                 Self::Human => Human {
                     id: "human-32".to_string(),
@@ -2090,24 +946,10 @@ mod generic_lifetime_async {
                 .into(),
             }
         }
-
-        fn hero<S: ScalarValue + Send + Sync>(&self) -> Box<DynHero<'_, '_, (), S>> {
-            let ch: Box<DynHero<'_, '_, (), _>> = match self {
-                Self::Human => Box::new(Human {
-                    id: "human-32".to_string(),
-                    home_planet: "earth".to_string(),
-                }),
-                Self::Droid => Box::new(Droid {
-                    id: "droid-99".to_string(),
-                    primary_function: "run".to_string(),
-                }),
-            };
-            ch
-        }
     }
 
     #[tokio::test]
-    async fn enum_resolves_human() {
+    async fn resolves_human() {
         const DOC: &str = r#"{
             character {
                 ... on Human {
@@ -2129,7 +971,7 @@ mod generic_lifetime_async {
     }
 
     #[tokio::test]
-    async fn enum_resolves_droid() {
+    async fn resolves_droid() {
         const DOC: &str = r#"{
             character {
                 ... on Droid {
@@ -2151,51 +993,7 @@ mod generic_lifetime_async {
     }
 
     #[tokio::test]
-    async fn dyn_resolves_human() {
-        const DOC: &str = r#"{
-            hero {
-                ... on Human {
-                    humanId: id
-                    homePlanet
-                }
-            }
-        }"#;
-
-        let schema = schema(QueryRoot::Human);
-
-        assert_eq!(
-            execute(DOC, None, &schema, &graphql_vars! {}, &()).await,
-            Ok((
-                graphql_value!({"hero": {"humanId": "human-32", "homePlanet": "earth"}}),
-                vec![],
-            )),
-        );
-    }
-
-    #[tokio::test]
-    async fn dyn_resolves_droid() {
-        const DOC: &str = r#"{
-            hero {
-                ... on Droid {
-                    droidId: id
-                    primaryFunction
-                }
-            }
-        }"#;
-
-        let schema = schema(QueryRoot::Droid);
-
-        assert_eq!(
-            execute(DOC, None, &schema, &graphql_vars! {}, &()).await,
-            Ok((
-                graphql_value!({"hero": {"droidId": "droid-99", "primaryFunction": "run"}}),
-                vec![],
-            )),
-        );
-    }
-
-    #[tokio::test]
-    async fn enum_resolves_id_field() {
+    async fn resolves_id_field() {
         const DOC: &str = r#"{
             character {
                 id
@@ -2217,44 +1015,19 @@ mod generic_lifetime_async {
     }
 
     #[tokio::test]
-    async fn dyn_resolves_info_field() {
+    async fn uses_trait_name_without_type_params() {
         const DOC: &str = r#"{
-            hero {
-                info
+            __type(name: "Character") {
+                name
             }
         }"#;
 
-        for (root, expected_info) in &[(QueryRoot::Human, "earth"), (QueryRoot::Droid, "run")] {
-            let schema = schema(*root);
-
-            let expected_info: &str = *expected_info;
-            assert_eq!(
-                execute(DOC, None, &schema, &graphql_vars! {}, &()).await,
-                Ok((graphql_value!({"hero": {"info": expected_info}}), vec![])),
-            );
-        }
-    }
-
-    #[tokio::test]
-    async fn uses_trait_name_without_type_params() {
-        for interface in &["Character", "Hero"] {
-            let doc = format!(
-                r#"{{
-                    __type(name: "{}") {{
-                        name
-                    }}
-                }}"#,
-                interface,
-            );
-
-            let schema = schema(QueryRoot::Human);
+        let schema = schema(QueryRoot::Human);
 
-            let expected_name: &str = *interface;
-            assert_eq!(
-                execute(&doc, None, &schema, &graphql_vars! {}, &()).await,
-                Ok((graphql_value!({"__type": {"name": expected_name}}), vec![])),
-            );
-        }
+        assert_eq!(
+            execute(DOC, None, &schema, &graphql_vars! {}, &()).await,
+            Ok((graphql_value!({"__type": {"name": "Character"}}), vec![])),
+        );
     }
 }
 
@@ -2265,64 +1038,44 @@ mod argument {
     trait Character {
         fn id_wide(&self, is_number: bool) -> &str;
 
-        async fn id_wide2(&self, is_number: bool, r#async: Option<i32>) -> &str;
-    }
-
-    #[graphql_interface(dyn = DynHero, for = Human)]
-    trait Hero {
-        fn info_wide(&self, is_planet: bool) -> &str;
-
-        async fn info_wide2(&self, is_planet: bool, r#async: Option<i32>) -> &str;
+        fn id_wide2(&self, is_number: bool, r#async: Option<i32>) -> &str;
     }
 
-    #[derive(GraphQLObject)]
-    #[graphql(impl = [CharacterValue, DynHero<__S>])]
     struct Human {
         id: String,
         home_planet: String,
     }
 
-    #[graphql_interface]
-    impl Character for Human {
-        fn id_wide(&self, is_number: bool) -> &str {
-            if is_number {
-                &self.id
-            } else {
-                "none"
-            }
+    #[graphql_object(impl = CharacterValue)]
+    impl Human {
+        fn id(&self) -> &str {
+            &self.id
         }
 
-        async fn id_wide2(&self, is_number: bool, _: Option<i32>) -> &str {
+        fn home_planet(&self) -> &str {
+            &self.home_planet
+        }
+
+        async fn id_wide(&self, is_number: bool) -> &str {
             if is_number {
                 &self.id
             } else {
                 "none"
             }
         }
-    }
 
-    #[graphql_interface(dyn)]
-    impl Hero for Human {
-        fn info_wide(&self, is_planet: bool) -> &str {
-            if is_planet {
-                &self.home_planet
-            } else {
+        async fn id_wide2(&self, is_number: bool, _async: Option<i32>) -> &str {
+            if is_number {
                 &self.id
-            }
-        }
-
-        async fn info_wide2(&self, is_planet: bool, _: Option<i32>) -> &str {
-            if is_planet {
-                &self.home_planet
             } else {
-                &self.id
+                "none"
             }
         }
     }
 
     struct QueryRoot;
 
-    #[graphql_object(scalar = S: ScalarValue + Send + Sync)]
+    #[graphql_object]
     impl QueryRoot {
         fn character(&self) -> CharacterValue {
             Human {
@@ -2331,17 +1084,10 @@ mod argument {
             }
             .into()
         }
-
-        fn hero<S: ScalarValue + Send + Sync>(&self) -> Box<DynHero<'_, S>> {
-            Box::new(Human {
-                id: "human-32".to_string(),
-                home_planet: "earth".to_string(),
-            })
-        }
     }
 
     #[tokio::test]
-    async fn enum_resolves_id_field() {
+    async fn resolves_id_field() {
         let schema = schema(QueryRoot);
 
         for (input, expected) in &[
@@ -2369,141 +1115,91 @@ mod argument {
         }
     }
 
-    #[tokio::test]
-    async fn dyn_resolves_info_field() {
-        let schema = schema(QueryRoot);
-
-        for (input, expected) in &[
-            (
-                "{ hero { infoWide(isPlanet: true), infoWide2(isPlanet: true) } }",
-                "earth",
-            ),
-            (
-                "{ hero { infoWide(isPlanet: false), infoWide2(isPlanet: false, async: 3) } }",
-                "human-32",
-            ),
-        ] {
-            let expected: &str = *expected;
-
-            assert_eq!(
-                execute(*input, None, &schema, &graphql_vars! {}, &()).await,
-                Ok((
-                    graphql_value!({"hero": {
-                        "infoWide": expected,
-                        "infoWide2": expected,
-                    }}),
-                    vec![],
-                )),
-            );
-        }
-    }
-
     #[tokio::test]
     async fn camelcases_name() {
-        let schema = schema(QueryRoot);
+        const DOC: &str = r#"{
+            __type(name: "Character") {
+                fields {
+                    name
+                    args {
+                        name
+                    }
+                }
+            }
+        }"#;
 
-        for (interface, field, arg) in &[
-            ("Character", "idWide", "isNumber"),
-            ("Hero", "infoWide", "isPlanet"),
-        ] {
-            let doc = format!(
-                r#"{{
-                    __type(name: "{}") {{
-                        fields {{
-                            name
-                            args {{
-                                name
-                            }}
-                        }}
-                    }}
-                }}"#,
-                interface,
-            );
+        let schema = schema(QueryRoot);
 
-            let expected_field_name: &str = *field;
-            let expected_field_name2: &str = &format!("{}2", field);
-            let expected_arg_name: &str = *arg;
-            assert_eq!(
-                execute(&doc, None, &schema, &graphql_vars! {}, &()).await,
-                Ok((
-                    graphql_value!({"__type": {"fields": [{
-                        "name": expected_field_name,
-                        "args": [
-                            {"name": expected_arg_name},
-                        ],
-                    }, {
-                        "name": expected_field_name2,
-                        "args": [
-                            {"name": expected_arg_name},
-                            {"name": "async"},
-                        ],
-                    }]}}),
-                    vec![],
-                )),
-            );
-        }
+        assert_eq!(
+            execute(DOC, None, &schema, &graphql_vars! {}, &()).await,
+            Ok((
+                graphql_value!({"__type": {"fields": [{
+                    "name": "idWide",
+                    "args": [
+                        {"name": "isNumber"},
+                    ],
+                }, {
+                    "name": "idWide2",
+                    "args": [
+                        {"name": "isNumber"},
+                        {"name": "async"},
+                    ],
+                }]}}),
+                vec![],
+            )),
+        );
     }
 
     #[tokio::test]
     async fn has_no_description() {
-        let schema = schema(QueryRoot);
+        const DOC: &str = r#"{
+            __type(name: "Character") {
+                fields {
+                    args {
+                        description
+                    }
+                }
+            }
+        }"#;
 
-        for interface in &["Character", "Hero"] {
-            let doc = format!(
-                r#"{{
-                    __type(name: "{}") {{
-                        fields {{
-                            args {{
-                                description
-                            }}
-                        }}
-                    }}
-                }}"#,
-                interface,
-            );
+        let schema = schema(QueryRoot);
 
-            assert_eq!(
-                execute(&doc, None, &schema, &graphql_vars! {}, &()).await,
-                Ok((
-                    graphql_value!({"__type": {"fields": [
-                        {"args": [{"description": null}]},
-                        {"args": [{"description": null}, {"description": null}]},
-                    ]}}),
-                    vec![],
-                )),
-            );
-        }
+        assert_eq!(
+            execute(DOC, None, &schema, &graphql_vars! {}, &()).await,
+            Ok((
+                graphql_value!({"__type": {"fields": [
+                    {"args": [{"description": null}]},
+                    {"args": [{"description": null}, {"description": null}]},
+                ]}}),
+                vec![],
+            )),
+        );
     }
 
     #[tokio::test]
     async fn has_no_defaults() {
-        let schema = schema(QueryRoot);
+        const DOC: &str = r#"{
+            __type(name: "Character") {
+                fields {
+                    args {
+                        defaultValue
+                    }
+                }
+            }
+        }"#;
 
-        for interface in &["Character", "Hero"] {
-            let doc = format!(
-                r#"{{
-                    __type(name: "{}") {{
-                        fields {{
-                            args {{
-                                defaultValue
-                            }}
-                        }}
-                    }}
-                }}"#,
-                interface,
-            );
+        let schema = schema(QueryRoot);
 
-            assert_eq!(
-                execute(&doc, None, &schema, &graphql_vars! {}, &()).await,
-                Ok((
-                    graphql_value!({"__type": {"fields": [
-                        {"args": [{"defaultValue": null}]},
-                        {"args": [{"defaultValue": null}, {"defaultValue": null}]},
-                    ]}}),
-                    vec![],
-                )),
-            );
-        }
+        assert_eq!(
+            execute(DOC, None, &schema, &graphql_vars! {}, &()).await,
+            Ok((
+                graphql_value!({"__type": {"fields": [
+                    {"args": [{"defaultValue": null}]},
+                    {"args": [{"defaultValue": null}, {"defaultValue": null}]},
+                ]}}),
+                vec![],
+            )),
+        );
     }
 }
 
@@ -2517,27 +1213,24 @@ mod default_argument {
 
     #[graphql_interface(for = Human)]
     trait Character {
-        async fn id(
+        fn id(
             &self,
             #[graphql(default)] first: String,
             #[graphql(default = "second".to_string())] second: String,
             #[graphql(default = "t")] third: String,
         ) -> String;
 
-        fn info(&self, #[graphql(default = Point { x: 1 })] coord: Point) -> i32 {
+        fn info(&self, #[graphql(default = Point { x: 1 })] coord: Point) -> i32;
+    }
+
+    struct Human;
+
+    #[graphql_object(impl = CharacterValue)]
+    impl Human {
+        async fn info(&self, coord: Point) -> i32 {
             coord.x
         }
-    }
-
-    #[derive(GraphQLObject)]
-    #[graphql(impl = CharacterValue)]
-    struct Human {
-        id: String,
-        info: i32,
-    }
 
-    #[graphql_interface]
-    impl Character for Human {
         async fn id(&self, first: String, second: String, third: String) -> String {
             format!("{}|{}&{}", first, second, third)
         }
@@ -2548,11 +1241,7 @@ mod default_argument {
     #[graphql_object]
     impl QueryRoot {
         fn character(&self) -> CharacterValue {
-            Human {
-                id: "human-32".to_string(),
-                info: 0,
-            }
-            .into()
+            Human.into()
         }
     }
 
@@ -2668,13 +1357,6 @@ mod description_from_doc_comment {
         home_planet: String,
     }
 
-    #[graphql_interface]
-    impl Character for Human {
-        fn id(&self) -> &str {
-            &self.id
-        }
-    }
-
     struct QueryRoot;
 
     #[graphql_object]
@@ -2722,28 +1404,34 @@ mod deprecation_from_attr {
         fn id(&self) -> &str;
 
         #[deprecated]
-        fn a(&self) -> &str {
-            "a"
-        }
+        fn a(&self) -> &str;
 
         #[deprecated(note = "Use `id`.")]
-        fn b(&self) -> &str {
-            "b"
-        }
+        fn b(&self) -> &str;
     }
 
-    #[derive(GraphQLObject)]
-    #[graphql(impl = CharacterValue)]
     struct Human {
         id: String,
         home_planet: String,
     }
 
-    #[graphql_interface]
-    impl Character for Human {
+    #[graphql_object(impl = CharacterValue)]
+    impl Human {
         fn id(&self) -> &str {
             &self.id
         }
+
+        fn human_planet(&self) -> &str {
+            &self.home_planet
+        }
+
+        fn a() -> &'static str {
+            "a"
+        }
+
+        fn b() -> String {
+            "b".to_owned()
+        }
     }
 
     struct QueryRoot;
@@ -2858,27 +1546,33 @@ mod explicit_name_description_and_deprecation {
 
         #[graphql(deprecated)]
         #[deprecated(note = "Should be omitted.")]
-        fn a(&self) -> &str {
-            "a"
-        }
+        fn a(&self) -> &str;
 
-        fn b(&self) -> &str {
-            "b"
-        }
+        fn b(&self) -> &str;
     }
 
-    #[derive(GraphQLObject)]
-    #[graphql(impl = CharacterValue)]
     struct Human {
         id: String,
         home_planet: String,
     }
 
-    #[graphql_interface]
-    impl Character for Human {
-        fn id(&self, _: Option<String>) -> &str {
+    #[graphql_object(impl = CharacterValue)]
+    impl Human {
+        fn my_id(&self, #[graphql(name = "myName")] _: Option<String>) -> &str {
             &self.id
         }
+
+        fn home_planet(&self) -> &str {
+            &self.home_planet
+        }
+
+        fn a() -> String {
+            "a".to_owned()
+        }
+
+        fn b() -> &'static str {
+            "b"
+        }
     }
 
     struct QueryRoot;
@@ -3031,17 +1725,11 @@ mod renamed_all_fields_and_args {
 
     #[graphql_interface(rename_all = "none", for = Human)]
     trait Character {
-        fn id(&self) -> &str {
-            "human-32"
-        }
+        fn id(&self) -> &str;
 
-        async fn home_planet(&self, planet_name: String) -> String {
-            planet_name
-        }
+        fn home_planet(&self, planet_name: String) -> String;
 
-        async fn r#async_info(&self, r#my_num: i32) -> i32 {
-            r#my_num
-        }
+        fn r#async_info(&self, r#my_num: i32) -> i32;
     }
 
     struct Human;
@@ -3052,18 +1740,15 @@ mod renamed_all_fields_and_args {
             "human-32"
         }
 
-        fn home_planet(planet_name: String) -> String {
+        async fn home_planet(planet_name: String) -> String {
             planet_name
         }
 
-        fn r#async_info(r#my_num: i32) -> i32 {
+        async fn r#async_info(r#my_num: i32) -> i32 {
             r#my_num
         }
     }
 
-    #[graphql_interface]
-    impl Character for Human {}
-
     struct QueryRoot;
 
     #[graphql_object]
@@ -3127,285 +1812,34 @@ mod renamed_all_fields_and_args {
     }
 }
 
-mod explicit_scalar {
-    use super::*;
-
-    #[graphql_interface(for = [Human, Droid])]
-    #[graphql_interface(scalar = DefaultScalarValue)]
-    trait Character {
-        fn id(&self) -> &str;
-    }
-
-    #[graphql_interface(dyn = DynHero, for = [Human, Droid])]
-    #[graphql_interface(scalar = DefaultScalarValue)]
-    trait Hero {
-        async fn info(&self) -> &str;
-    }
-
-    #[derive(GraphQLObject)]
-    #[graphql(impl = [CharacterValue, DynHero], scalar = DefaultScalarValue)]
-    struct Human {
-        id: String,
-        home_planet: String,
-    }
-
-    #[graphql_interface(scalar = DefaultScalarValue)]
-    impl Character for Human {
-        fn id(&self) -> &str {
-            &self.id
-        }
-    }
-
-    #[graphql_interface(dyn, scalar = DefaultScalarValue)]
-    impl Hero for Human {
-        async fn info(&self) -> &str {
-            &self.home_planet
-        }
-    }
-
-    #[derive(GraphQLObject)]
-    #[graphql(impl = [CharacterValue, DynHero], scalar = DefaultScalarValue)]
-    struct Droid {
-        id: String,
-        primary_function: String,
-    }
-
-    #[graphql_interface(scalar = DefaultScalarValue)]
-    impl Character for Droid {
-        fn id(&self) -> &str {
-            &self.id
-        }
-    }
-
-    #[graphql_interface(dyn, scalar = DefaultScalarValue)]
-    impl Hero for Droid {
-        async fn info(&self) -> &str {
-            &self.primary_function
-        }
-    }
-
-    #[derive(Clone, Copy)]
-    enum QueryRoot {
-        Human,
-        Droid,
-    }
-
-    #[graphql_object(scalar = DefaultScalarValue)]
-    impl QueryRoot {
-        fn character(&self) -> CharacterValue {
-            match self {
-                Self::Human => Human {
-                    id: "human-32".to_string(),
-                    home_planet: "earth".to_string(),
-                }
-                .into(),
-                Self::Droid => Droid {
-                    id: "droid-99".to_string(),
-                    primary_function: "run".to_string(),
-                }
-                .into(),
-            }
-        }
-
-        fn hero(&self) -> Box<DynHero<'_>> {
-            let ch: Box<DynHero<'_>> = match self {
-                Self::Human => Box::new(Human {
-                    id: "human-32".to_string(),
-                    home_planet: "earth".to_string(),
-                }),
-                Self::Droid => Box::new(Droid {
-                    id: "droid-99".to_string(),
-                    primary_function: "run".to_string(),
-                }),
-            };
-            ch
-        }
-    }
-
-    #[tokio::test]
-    async fn enum_resolves_human() {
-        const DOC: &str = r#"{
-            character {
-                ... on Human {
-                    humanId: id
-                    homePlanet
-                }
-            }
-        }"#;
-
-        let schema = schema(QueryRoot::Human);
-
-        assert_eq!(
-            execute(DOC, None, &schema, &graphql_vars! {}, &()).await,
-            Ok((
-                graphql_value!({"character": {"humanId": "human-32", "homePlanet": "earth"}}),
-                vec![],
-            )),
-        );
-    }
-
-    #[tokio::test]
-    async fn enum_resolves_droid() {
-        const DOC: &str = r#"{
-            character {
-                ... on Droid {
-                    droidId: id
-                    primaryFunction
-                }
-            }
-        }"#;
-
-        let schema = schema(QueryRoot::Droid);
-
-        assert_eq!(
-            execute(DOC, None, &schema, &graphql_vars! {}, &()).await,
-            Ok((
-                graphql_value!({"character": {"droidId": "droid-99", "primaryFunction": "run"}}),
-                vec![],
-            )),
-        );
-    }
-
-    #[tokio::test]
-    async fn dyn_resolves_human() {
-        const DOC: &str = r#"{
-            hero {
-                ... on Human {
-                    humanId: id
-                    homePlanet
-                }
-            }
-        }"#;
-
-        let schema = schema(QueryRoot::Human);
-
-        assert_eq!(
-            execute(DOC, None, &schema, &graphql_vars! {}, &()).await,
-            Ok((
-                graphql_value!({"hero": {"humanId": "human-32", "homePlanet": "earth"}}),
-                vec![],
-            )),
-        );
-    }
-
-    #[tokio::test]
-    async fn dyn_resolves_droid() {
-        const DOC: &str = r#"{
-            hero {
-                ... on Droid {
-                    droidId: id
-                    primaryFunction
-                }
-            }
-        }"#;
-
-        let schema = schema(QueryRoot::Droid);
-
-        assert_eq!(
-            execute(DOC, None, &schema, &graphql_vars! {}, &()).await,
-            Ok((
-                graphql_value!({"hero": {"droidId": "droid-99", "primaryFunction": "run"}}),
-                vec![],
-            )),
-        );
-    }
-
-    #[tokio::test]
-    async fn enum_resolves_id_field() {
-        const DOC: &str = r#"{
-            character {
-                id
-            }
-        }"#;
-
-        for (root, expected_id) in &[
-            (QueryRoot::Human, "human-32"),
-            (QueryRoot::Droid, "droid-99"),
-        ] {
-            let schema = schema(*root);
-
-            let expected_id: &str = *expected_id;
-            assert_eq!(
-                execute(DOC, None, &schema, &graphql_vars! {}, &()).await,
-                Ok((graphql_value!({"character": {"id": expected_id}}), vec![])),
-            );
-        }
-    }
-
-    #[tokio::test]
-    async fn dyn_resolves_info_field() {
-        const DOC: &str = r#"{
-            hero {
-                info
-            }
-        }"#;
-
-        for (root, expected_info) in &[(QueryRoot::Human, "earth"), (QueryRoot::Droid, "run")] {
-            let schema = schema(*root);
-
-            let expected_info: &str = *expected_info;
-            assert_eq!(
-                execute(DOC, None, &schema, &graphql_vars! {}, &()).await,
-                Ok((graphql_value!({"hero": {"info": expected_info}}), vec![])),
-            );
-        }
-    }
-}
-
-mod custom_scalar {
-    use crate::custom_scalar::MyScalarValue;
-
+mod explicit_scalar {
     use super::*;
 
-    #[graphql_interface(for = [Human, Droid], scalar = MyScalarValue)]
+    #[graphql_interface(for = [Human, Droid])]
+    #[graphql_interface(scalar = DefaultScalarValue)]
     trait Character {
-        async fn id(&self) -> &str;
-    }
-
-    #[graphql_interface(dyn = DynHero, for = [Human, Droid])]
-    #[graphql_interface(scalar = MyScalarValue)]
-    trait Hero {
-        async fn info(&self) -> &str;
+        fn id(&self) -> &str;
     }
 
     #[derive(GraphQLObject)]
-    #[graphql(impl = [CharacterValue, DynHero], scalar = MyScalarValue)]
+    #[graphql(impl = CharacterValue, scalar = DefaultScalarValue)]
     struct Human {
         id: String,
         home_planet: String,
     }
 
-    #[graphql_interface(scalar = MyScalarValue)]
-    impl Character for Human {
-        async fn id(&self) -> &str {
-            &self.id
-        }
-    }
-
-    #[graphql_interface(dyn, scalar = MyScalarValue)]
-    impl Hero for Human {
-        async fn info(&self) -> &str {
-            &self.home_planet
-        }
-    }
-
-    #[derive(GraphQLObject)]
-    #[graphql(impl = [CharacterValue, DynHero], scalar = MyScalarValue)]
     struct Droid {
         id: String,
         primary_function: String,
     }
 
-    #[graphql_interface(scalar = MyScalarValue)]
-    impl Character for Droid {
-        async fn id(&self) -> &str {
+    #[graphql_object(impl = CharacterValue, scalar = DefaultScalarValue)]
+    impl Droid {
+        fn id(&self) -> &str {
             &self.id
         }
-    }
 
-    #[graphql_interface(dyn, scalar = MyScalarValue)]
-    impl Hero for Droid {
-        async fn info(&self) -> &str {
+        fn primary_function(&self) -> &str {
             &self.primary_function
         }
     }
@@ -3416,7 +1850,7 @@ mod custom_scalar {
         Droid,
     }
 
-    #[graphql_object(scalar = MyScalarValue)]
+    #[graphql_object(scalar = DefaultScalarValue)]
     impl QueryRoot {
         fn character(&self) -> CharacterValue {
             match self {
@@ -3432,24 +1866,10 @@ mod custom_scalar {
                 .into(),
             }
         }
-
-        fn hero(&self) -> Box<DynHero<'_>> {
-            let ch: Box<DynHero<'_>> = match self {
-                Self::Human => Box::new(Human {
-                    id: "human-32".to_string(),
-                    home_planet: "earth".to_string(),
-                }),
-                Self::Droid => Box::new(Droid {
-                    id: "droid-99".to_string(),
-                    primary_function: "run".to_string(),
-                }),
-            };
-            ch
-        }
     }
 
     #[tokio::test]
-    async fn enum_resolves_human() {
+    async fn resolves_human() {
         const DOC: &str = r#"{
             character {
                 ... on Human {
@@ -3459,7 +1879,7 @@ mod custom_scalar {
             }
         }"#;
 
-        let schema = schema_with_scalar::<MyScalarValue, _, _>(QueryRoot::Human);
+        let schema = schema(QueryRoot::Human);
 
         assert_eq!(
             execute(DOC, None, &schema, &graphql_vars! {}, &()).await,
@@ -3471,7 +1891,7 @@ mod custom_scalar {
     }
 
     #[tokio::test]
-    async fn enum_resolves_droid() {
+    async fn resolves_droid() {
         const DOC: &str = r#"{
             character {
                 ... on Droid {
@@ -3481,7 +1901,7 @@ mod custom_scalar {
             }
         }"#;
 
-        let schema = schema_with_scalar::<MyScalarValue, _, _>(QueryRoot::Droid);
+        let schema = schema(QueryRoot::Droid);
 
         assert_eq!(
             execute(DOC, None, &schema, &graphql_vars! {}, &()).await,
@@ -3493,51 +1913,7 @@ mod custom_scalar {
     }
 
     #[tokio::test]
-    async fn dyn_resolves_human() {
-        const DOC: &str = r#"{
-            hero {
-                ... on Human {
-                    humanId: id
-                    homePlanet
-                }
-            }
-        }"#;
-
-        let schema = schema_with_scalar::<MyScalarValue, _, _>(QueryRoot::Human);
-
-        assert_eq!(
-            execute(DOC, None, &schema, &graphql_vars! {}, &()).await,
-            Ok((
-                graphql_value!({"hero": {"humanId": "human-32", "homePlanet": "earth"}}),
-                vec![],
-            )),
-        );
-    }
-
-    #[tokio::test]
-    async fn dyn_resolves_droid() {
-        const DOC: &str = r#"{
-            hero {
-                ... on Droid {
-                    droidId: id
-                    primaryFunction
-                }
-            }
-        }"#;
-
-        let schema = schema_with_scalar::<MyScalarValue, _, _>(QueryRoot::Droid);
-
-        assert_eq!(
-            execute(DOC, None, &schema, &graphql_vars! {}, &()).await,
-            Ok((
-                graphql_value!({"hero": {"droidId": "droid-99", "primaryFunction": "run"}}),
-                vec![],
-            )),
-        );
-    }
-
-    #[tokio::test]
-    async fn enum_resolves_id_field() {
+    async fn resolves_id_field() {
         const DOC: &str = r#"{
             character {
                 id
@@ -3548,7 +1924,7 @@ mod custom_scalar {
             (QueryRoot::Human, "human-32"),
             (QueryRoot::Droid, "droid-99"),
         ] {
-            let schema = schema_with_scalar::<MyScalarValue, _, _>(*root);
+            let schema = schema(*root);
 
             let expected_id: &str = *expected_id;
             assert_eq!(
@@ -3557,79 +1933,38 @@ mod custom_scalar {
             );
         }
     }
-
-    #[tokio::test]
-    async fn dyn_resolves_info_field() {
-        const DOC: &str = r#"{
-            hero {
-                info
-            }
-        }"#;
-
-        for (root, expected_info) in &[(QueryRoot::Human, "earth"), (QueryRoot::Droid, "run")] {
-            let schema = schema_with_scalar::<MyScalarValue, _, _>(*root);
-
-            let expected_info: &str = *expected_info;
-            assert_eq!(
-                execute(DOC, None, &schema, &graphql_vars! {}, &()).await,
-                Ok((graphql_value!({"hero": {"info": expected_info}}), vec![])),
-            );
-        }
-    }
 }
 
-mod explicit_generic_scalar {
-    use super::*;
+mod custom_scalar {
+    use crate::custom_scalar::MyScalarValue;
 
-    #[graphql_interface(for = [Human, Droid], scalar = S)]
-    trait Character<S: ScalarValue = DefaultScalarValue> {
-        fn id(&self) -> FieldResult<&str, S>;
-    }
+    use super::*;
 
-    #[graphql_interface(dyn = DynHero, for = [Human, Droid], scalar = S)]
-    trait Hero<S: ScalarValue = DefaultScalarValue> {
-        async fn info(&self) -> FieldResult<&str, S>;
+    #[graphql_interface(for = [Human, Droid], scalar = MyScalarValue)]
+    trait Character {
+        fn id(&self) -> &str;
     }
 
     #[derive(GraphQLObject)]
-    #[graphql(impl = [CharacterValue<__S>, DynHero<__S>])]
+    #[graphql(impl = CharacterValue, scalar = MyScalarValue)]
     struct Human {
         id: String,
         home_planet: String,
     }
 
-    #[graphql_interface(scalar = S)]
-    impl<S: ScalarValue> Character<S> for Human {
-        fn id(&self) -> FieldResult<&str, S> {
-            Ok(&self.id)
-        }
-    }
-
-    #[graphql_interface(dyn, scalar = S)]
-    impl<S: ScalarValue> Hero<S> for Human {
-        async fn info(&self) -> FieldResult<&str, S> {
-            Ok(&self.home_planet)
-        }
-    }
-
-    #[derive(GraphQLObject)]
-    #[graphql(impl = [CharacterValue<__S>, DynHero<__S>])]
     struct Droid {
         id: String,
         primary_function: String,
     }
 
-    #[graphql_interface(scalar = S)]
-    impl<S: ScalarValue> Character<S> for Droid {
-        fn id(&self) -> FieldResult<&str, S> {
-            Ok(&self.id)
+    #[graphql_object(impl = CharacterValue, scalar = MyScalarValue)]
+    impl Droid {
+        fn id(&self) -> &str {
+            &self.id
         }
-    }
 
-    #[graphql_interface(dyn, scalar = S)]
-    impl<S: ScalarValue> Hero<S> for Droid {
-        async fn info(&self) -> FieldResult<&str, S> {
-            Ok(&self.primary_function)
+        fn primary_function(&self) -> &str {
+            &self.primary_function
         }
     }
 
@@ -3639,9 +1974,9 @@ mod explicit_generic_scalar {
         Droid,
     }
 
-    #[graphql_object(scalar = S: ScalarValue + Send + Sync)]
+    #[graphql_object(scalar = MyScalarValue)]
     impl QueryRoot {
-        fn character<S: ScalarValue>(&self) -> CharacterValue<S> {
+        fn character(&self) -> CharacterValue {
             match self {
                 Self::Human => Human {
                     id: "human-32".to_string(),
@@ -3655,24 +1990,10 @@ mod explicit_generic_scalar {
                 .into(),
             }
         }
-
-        fn hero<S: ScalarValue + Send + Sync>(&self) -> Box<DynHero<'_, S>> {
-            let ch: Box<DynHero<'_, _>> = match self {
-                Self::Human => Box::new(Human {
-                    id: "human-32".to_string(),
-                    home_planet: "earth".to_string(),
-                }),
-                Self::Droid => Box::new(Droid {
-                    id: "droid-99".to_string(),
-                    primary_function: "run".to_string(),
-                }),
-            };
-            ch
-        }
     }
 
     #[tokio::test]
-    async fn enum_resolves_human() {
+    async fn resolves_human() {
         const DOC: &str = r#"{
             character {
                 ... on Human {
@@ -3682,7 +2003,7 @@ mod explicit_generic_scalar {
             }
         }"#;
 
-        let schema = schema(QueryRoot::Human);
+        let schema = schema_with_scalar::<MyScalarValue, _, _>(QueryRoot::Human);
 
         assert_eq!(
             execute(DOC, None, &schema, &graphql_vars! {}, &()).await,
@@ -3694,7 +2015,7 @@ mod explicit_generic_scalar {
     }
 
     #[tokio::test]
-    async fn enum_resolves_droid() {
+    async fn resolves_droid() {
         const DOC: &str = r#"{
             character {
                 ... on Droid {
@@ -3704,7 +2025,7 @@ mod explicit_generic_scalar {
             }
         }"#;
 
-        let schema = schema(QueryRoot::Droid);
+        let schema = schema_with_scalar::<MyScalarValue, _, _>(QueryRoot::Droid);
 
         assert_eq!(
             execute(DOC, None, &schema, &graphql_vars! {}, &()).await,
@@ -3716,51 +2037,7 @@ mod explicit_generic_scalar {
     }
 
     #[tokio::test]
-    async fn dyn_resolves_human() {
-        const DOC: &str = r#"{
-            hero {
-                ... on Human {
-                    humanId: id
-                    homePlanet
-                }
-            }
-        }"#;
-
-        let schema = schema(QueryRoot::Human);
-
-        assert_eq!(
-            execute(DOC, None, &schema, &graphql_vars! {}, &()).await,
-            Ok((
-                graphql_value!({"hero": {"humanId": "human-32", "homePlanet": "earth"}}),
-                vec![],
-            )),
-        );
-    }
-
-    #[tokio::test]
-    async fn dyn_resolves_droid() {
-        const DOC: &str = r#"{
-            hero {
-                ... on Droid {
-                    droidId: id
-                    primaryFunction
-                }
-            }
-        }"#;
-
-        let schema = schema(QueryRoot::Droid);
-
-        assert_eq!(
-            execute(DOC, None, &schema, &graphql_vars! {}, &()).await,
-            Ok((
-                graphql_value!({"hero": {"droidId": "droid-99", "primaryFunction": "run"}}),
-                vec![],
-            )),
-        );
-    }
-
-    #[tokio::test]
-    async fn enum_resolves_id_field() {
+    async fn resolves_id_field() {
         const DOC: &str = r#"{
             character {
                 id
@@ -3771,7 +2048,7 @@ mod explicit_generic_scalar {
             (QueryRoot::Human, "human-32"),
             (QueryRoot::Droid, "droid-99"),
         ] {
-            let schema = schema(*root);
+            let schema = schema_with_scalar::<MyScalarValue, _, _>(*root);
 
             let expected_id: &str = *expected_id;
             assert_eq!(
@@ -3780,78 +2057,35 @@ mod explicit_generic_scalar {
             );
         }
     }
-
-    #[tokio::test]
-    async fn dyn_resolves_info_field() {
-        const DOC: &str = r#"{
-            hero {
-                info
-            }
-        }"#;
-
-        for (root, expected_info) in &[(QueryRoot::Human, "earth"), (QueryRoot::Droid, "run")] {
-            let schema = schema(*root);
-
-            let expected_info: &str = *expected_info;
-            assert_eq!(
-                execute(DOC, None, &schema, &graphql_vars! {}, &()).await,
-                Ok((graphql_value!({"hero": {"info": expected_info}}), vec![])),
-            );
-        }
-    }
 }
 
-mod bounded_generic_scalar {
+mod explicit_generic_scalar {
     use super::*;
 
-    #[graphql_interface(for = [Human, Droid], scalar = S: ScalarValue + Clone)]
-    trait Character {
-        fn id(&self) -> &str;
-    }
-
-    #[graphql_interface(dyn = DynHero, for = [Human, Droid], scalar = S: ScalarValue + Clone)]
-    trait Hero {
-        async fn info(&self) -> &str;
+    #[graphql_interface(for = [Human, Droid], scalar = S)]
+    trait Character<S: ScalarValue = DefaultScalarValue> {
+        fn id(&self) -> FieldResult<&str, S>;
     }
 
     #[derive(GraphQLObject)]
-    #[graphql(impl = [CharacterValue, DynHero<S>], scalar = S: ScalarValue + Clone)]
+    #[graphql(scalar = S: ScalarValue, impl = CharacterValue<S>)]
     struct Human {
         id: String,
         home_planet: String,
     }
 
-    #[graphql_interface(scalar = S: ScalarValue + Clone)]
-    impl Character for Human {
-        fn id(&self) -> &str {
-            &self.id
-        }
-    }
-
-    #[graphql_interface(dyn, scalar = S: ScalarValue + Clone)]
-    impl Hero for Human {
-        async fn info(&self) -> &str {
-            &self.home_planet
-        }
-    }
-
-    #[derive(GraphQLObject)]
-    #[graphql(impl = [CharacterValue, DynHero<__S>])]
     struct Droid {
         id: String,
         primary_function: String,
     }
 
-    #[graphql_interface(scalar = S: ScalarValue + Clone)]
-    impl Character for Droid {
+    #[graphql_object(impl = CharacterValue<__S>)]
+    impl Droid {
         fn id(&self) -> &str {
             &self.id
         }
-    }
 
-    #[graphql_interface(dyn, scalar = S: ScalarValue + Clone)]
-    impl Hero for Droid {
-        async fn info(&self) -> &str {
+        fn primary_function(&self) -> &str {
             &self.primary_function
         }
     }
@@ -3862,86 +2096,28 @@ mod bounded_generic_scalar {
         Droid,
     }
 
-    #[graphql_object(scalar = S: ScalarValue + Clone + Send + Sync)]
+    #[graphql_object(scalar = S: ScalarValue)]
     impl QueryRoot {
-        fn character(&self) -> CharacterValue {
+        fn character<S: ScalarValue>(&self) -> CharacterValue<S> {
             match self {
-                Self::Human => Human {
-                    id: "human-32".to_string(),
-                    home_planet: "earth".to_string(),
-                }
-                .into(),
-                Self::Droid => Droid {
-                    id: "droid-99".to_string(),
-                    primary_function: "run".to_string(),
-                }
-                .into(),
-            }
-        }
-
-        fn hero<S: ScalarValue + Clone + Send + Sync>(&self) -> Box<DynHero<'_, S>> {
-            let ch: Box<DynHero<'_, _>> = match self {
-                Self::Human => Box::new(Human {
-                    id: "human-32".to_string(),
-                    home_planet: "earth".to_string(),
-                }),
-                Self::Droid => Box::new(Droid {
-                    id: "droid-99".to_string(),
-                    primary_function: "run".to_string(),
-                }),
-            };
-            ch
-        }
-    }
-
-    #[tokio::test]
-    async fn enum_resolves_human() {
-        const DOC: &str = r#"{
-            character {
-                ... on Human {
-                    humanId: id
-                    homePlanet
-                }
-            }
-        }"#;
-
-        let schema = schema(QueryRoot::Human);
-
-        assert_eq!(
-            execute(DOC, None, &schema, &graphql_vars! {}, &()).await,
-            Ok((
-                graphql_value!({"character": {"humanId": "human-32", "homePlanet": "earth"}}),
-                vec![],
-            )),
-        );
-    }
-
-    #[tokio::test]
-    async fn enum_resolves_droid() {
-        const DOC: &str = r#"{
-            character {
-                ... on Droid {
-                    droidId: id
-                    primaryFunction
+                Self::Human => Human {
+                    id: "human-32".to_string(),
+                    home_planet: "earth".to_string(),
+                }
+                .into(),
+                Self::Droid => Droid {
+                    id: "droid-99".to_string(),
+                    primary_function: "run".to_string(),
                 }
+                .into(),
             }
-        }"#;
-
-        let schema = schema(QueryRoot::Droid);
-
-        assert_eq!(
-            execute(DOC, None, &schema, &graphql_vars! {}, &()).await,
-            Ok((
-                graphql_value!({"character": {"droidId": "droid-99", "primaryFunction": "run"}}),
-                vec![],
-            )),
-        );
+        }
     }
 
     #[tokio::test]
-    async fn dyn_resolves_human() {
+    async fn resolves_human() {
         const DOC: &str = r#"{
-            hero {
+            character {
                 ... on Human {
                     humanId: id
                     homePlanet
@@ -3954,16 +2130,16 @@ mod bounded_generic_scalar {
         assert_eq!(
             execute(DOC, None, &schema, &graphql_vars! {}, &()).await,
             Ok((
-                graphql_value!({"hero": {"humanId": "human-32", "homePlanet": "earth"}}),
+                graphql_value!({"character": {"humanId": "human-32", "homePlanet": "earth"}}),
                 vec![],
             )),
         );
     }
 
     #[tokio::test]
-    async fn dyn_resolves_droid() {
+    async fn resolves_droid() {
         const DOC: &str = r#"{
-            hero {
+            character {
                 ... on Droid {
                     droidId: id
                     primaryFunction
@@ -3976,14 +2152,14 @@ mod bounded_generic_scalar {
         assert_eq!(
             execute(DOC, None, &schema, &graphql_vars! {}, &()).await,
             Ok((
-                graphql_value!({"hero": {"droidId": "droid-99", "primaryFunction": "run"}}),
+                graphql_value!({"character": {"droidId": "droid-99", "primaryFunction": "run"}}),
                 vec![],
             )),
         );
     }
 
     #[tokio::test]
-    async fn enum_resolves_id_field() {
+    async fn resolves_id_field() {
         const DOC: &str = r#"{
             character {
                 id
@@ -4003,125 +2179,37 @@ mod bounded_generic_scalar {
             );
         }
     }
-
-    #[tokio::test]
-    async fn dyn_resolves_info_field() {
-        const DOC: &str = r#"{
-            hero {
-                info
-            }
-        }"#;
-
-        for (root, expected_info) in &[(QueryRoot::Human, "earth"), (QueryRoot::Droid, "run")] {
-            let schema = schema(*root);
-
-            let expected_info: &str = *expected_info;
-            assert_eq!(
-                execute(DOC, None, &schema, &graphql_vars! {}, &()).await,
-                Ok((graphql_value!({"hero": {"info": expected_info}}), vec![])),
-            );
-        }
-    }
 }
 
-mod explicit_custom_context {
+mod bounded_generic_scalar {
     use super::*;
 
-    struct CustomContext;
-
-    impl juniper::Context for CustomContext {}
-
-    #[graphql_interface(for = [Human, Droid], context = CustomContext)]
+    #[graphql_interface(for = [Human, Droid], scalar = S: ScalarValue + Clone)]
     trait Character {
-        async fn id<'a>(&'a self, context: &CustomContext) -> &'a str;
-
-        async fn info<'b>(&'b self, ctx: &()) -> &'b str;
-
-        fn more<'c>(&'c self, #[graphql(context)] custom: &CustomContext) -> &'c str;
-    }
-
-    #[graphql_interface(dyn = DynHero, for = [Human, Droid])]
-    #[graphql_interface(context = CustomContext)]
-    trait Hero {
-        async fn id<'a>(&'a self, context: &CustomContext) -> &'a str;
-
-        async fn info<'b>(&'b self, ctx: &()) -> &'b str;
-
-        fn more<'c>(&'c self, #[graphql(context)] custom: &CustomContext) -> &'c str;
+        fn id(&self) -> &str;
     }
 
     #[derive(GraphQLObject)]
-    #[graphql(impl = [CharacterValue, DynHero<__S>], context = CustomContext)]
+    #[graphql(impl = CharacterValue, scalar = S: ScalarValue + Clone)]
     struct Human {
         id: String,
         home_planet: String,
     }
 
-    #[graphql_interface]
-    impl Character for Human {
-        async fn id<'a>(&'a self, _: &CustomContext) -> &'a str {
-            &self.id
-        }
-
-        async fn info<'b>(&'b self, _: &()) -> &'b str {
-            &self.home_planet
-        }
-
-        fn more(&self, _: &CustomContext) -> &'static str {
-            "human"
-        }
-    }
-
-    #[graphql_interface(dyn)]
-    impl Hero for Human {
-        async fn id<'a>(&'a self, _: &CustomContext) -> &'a str {
-            &self.id
-        }
-
-        async fn info<'b>(&'b self, _: &()) -> &'b str {
-            &self.home_planet
-        }
-
-        fn more(&self, _: &CustomContext) -> &'static str {
-            "human"
-        }
-    }
-
-    #[derive(GraphQLObject)]
-    #[graphql(impl = [CharacterValue, DynHero<__S>], context = CustomContext)]
     struct Droid {
         id: String,
         primary_function: String,
     }
 
-    #[graphql_interface]
-    impl Character for Droid {
-        async fn id<'a>(&'a self, _: &CustomContext) -> &'a str {
-            &self.id
-        }
-
-        async fn info<'b>(&'b self, _: &()) -> &'b str {
-            &self.primary_function
-        }
-
-        fn more(&self, _: &CustomContext) -> &'static str {
-            "droid"
-        }
-    }
-
-    #[graphql_interface(dyn)]
-    impl Hero for Droid {
-        async fn id<'a>(&'a self, _: &CustomContext) -> &'a str {
+    #[graphql_object(impl = CharacterValue, scalar = S: ScalarValue + Clone)]
+    impl Droid {
+        fn id(&self) -> &str {
             &self.id
         }
 
-        async fn info<'b>(&'b self, _: &()) -> &'b str {
+        fn primary_function(&self) -> &str {
             &self.primary_function
         }
-
-        fn more(&self, _: &CustomContext) -> &'static str {
-            "droid"
-        }
     }
 
     #[derive(Clone, Copy)]
@@ -4130,7 +2218,7 @@ mod explicit_custom_context {
         Droid,
     }
 
-    #[graphql_object(context = CustomContext, scalar = S: ScalarValue + Send + Sync)]
+    #[graphql_object]
     impl QueryRoot {
         fn character(&self) -> CharacterValue {
             match self {
@@ -4146,24 +2234,10 @@ mod explicit_custom_context {
                 .into(),
             }
         }
-
-        fn hero<S: ScalarValue + Send + Sync>(&self) -> Box<DynHero<'_, S>> {
-            let ch: Box<DynHero<'_, _>> = match self {
-                Self::Human => Box::new(Human {
-                    id: "human-32".to_string(),
-                    home_planet: "earth".to_string(),
-                }),
-                Self::Droid => Box::new(Droid {
-                    id: "droid-99".to_string(),
-                    primary_function: "run".to_string(),
-                }),
-            };
-            ch
-        }
     }
 
     #[tokio::test]
-    async fn enum_resolves_human() {
+    async fn resolves_human() {
         const DOC: &str = r#"{
             character {
                 ... on Human {
@@ -4176,7 +2250,7 @@ mod explicit_custom_context {
         let schema = schema(QueryRoot::Human);
 
         assert_eq!(
-            execute(DOC, None, &schema, &graphql_vars! {}, &CustomContext).await,
+            execute(DOC, None, &schema, &graphql_vars! {}, &()).await,
             Ok((
                 graphql_value!({"character": {"humanId": "human-32", "homePlanet": "earth"}}),
                 vec![],
@@ -4185,7 +2259,7 @@ mod explicit_custom_context {
     }
 
     #[tokio::test]
-    async fn enum_resolves_droid() {
+    async fn resolves_droid() {
         const DOC: &str = r#"{
             character {
                 ... on Droid {
@@ -4198,7 +2272,7 @@ mod explicit_custom_context {
         let schema = schema(QueryRoot::Droid);
 
         assert_eq!(
-            execute(DOC, None, &schema, &graphql_vars! {}, &CustomContext).await,
+            execute(DOC, None, &schema, &graphql_vars! {}, &()).await,
             Ok((
                 graphql_value!({"character": {"droidId": "droid-99", "primaryFunction": "run"}}),
                 vec![],
@@ -4207,166 +2281,89 @@ mod explicit_custom_context {
     }
 
     #[tokio::test]
-    async fn dyn_resolves_human() {
-        const DOC: &str = r#"{
-            hero {
-                ... on Human {
-                    humanId: id
-                    homePlanet
-                }
-            }
-        }"#;
-
-        let schema = schema(QueryRoot::Human);
-
-        assert_eq!(
-            execute(DOC, None, &schema, &graphql_vars! {}, &CustomContext).await,
-            Ok((
-                graphql_value!({"hero": {"humanId": "human-32", "homePlanet": "earth"}}),
-                vec![],
-            )),
-        );
-    }
-
-    #[tokio::test]
-    async fn dyn_resolves_droid() {
+    async fn resolves_id_field() {
         const DOC: &str = r#"{
-            hero {
-                ... on Droid {
-                    droidId: id
-                    primaryFunction
-                }
+            character {
+                id
             }
         }"#;
 
-        let schema = schema(QueryRoot::Droid);
-
-        assert_eq!(
-            execute(DOC, None, &schema, &graphql_vars! {}, &CustomContext).await,
-            Ok((
-                graphql_value!({"hero": {"droidId": "droid-99", "primaryFunction": "run"}}),
-                vec![],
-            )),
-        );
-    }
+        for (root, expected_id) in &[
+            (QueryRoot::Human, "human-32"),
+            (QueryRoot::Droid, "droid-99"),
+        ] {
+            let schema = schema(*root);
 
-    #[tokio::test]
-    async fn resolves_fields() {
-        for interface in &["character", "hero"] {
-            let doc = format!(
-                r#"{{
-                    {} {{
-                        id
-                        info
-                        more
-                    }}
-                }}"#,
-                interface,
+            let expected_id: &str = *expected_id;
+            assert_eq!(
+                execute(DOC, None, &schema, &graphql_vars! {}, &()).await,
+                Ok((graphql_value!({"character": {"id": expected_id}}), vec![])),
             );
-
-            let expected_interface: &str = *interface;
-
-            for (root, expected_id, expected_info, expexted_more) in &[
-                (QueryRoot::Human, "human-32", "earth", "human"),
-                (QueryRoot::Droid, "droid-99", "run", "droid"),
-            ] {
-                let schema = schema(*root);
-
-                let expected_id: &str = *expected_id;
-                let expected_info: &str = *expected_info;
-                let expexted_more: &str = *expexted_more;
-                assert_eq!(
-                    execute(&doc, None, &schema, &graphql_vars! {}, &CustomContext).await,
-                    Ok((
-                        graphql_value!({expected_interface: {
-                            "id": expected_id,
-                            "info": expected_info,
-                            "more": expexted_more,
-                        }}),
-                        vec![],
-                    )),
-                );
-            }
         }
     }
 }
 
-mod inferred_custom_context_from_field {
+mod explicit_custom_context {
     use super::*;
 
-    struct CustomContext(String);
+    struct CustomContext;
 
     impl juniper::Context for CustomContext {}
 
-    #[graphql_interface(for = [Human, Droid])]
+    #[graphql_interface(for = [Human, Droid], context = CustomContext)]
     trait Character {
-        fn id<'a>(&self, context: &'a CustomContext) -> &'a str;
-
-        fn info<'b>(&'b self, context: &()) -> &'b str;
-    }
+        fn id<'a>(&'a self, context: &CustomContext) -> &'a str;
 
-    #[graphql_interface(dyn = DynHero, for = [Human, Droid])]
-    trait Hero {
-        async fn id<'a>(&self, context: &'a CustomContext) -> &'a str;
+        fn info<'b>(&'b self, ctx: &()) -> &'b str;
 
-        async fn info<'b>(&'b self, context: &()) -> &'b str;
+        fn more<'c>(&'c self, #[graphql(context)] custom: &CustomContext) -> &'c str;
     }
 
-    #[derive(GraphQLObject)]
-    #[graphql(impl = [CharacterValue, DynHero<__S>], context = CustomContext)]
     struct Human {
         id: String,
         home_planet: String,
     }
 
-    #[graphql_interface]
-    impl Character for Human {
-        fn id<'a>(&self, ctx: &'a CustomContext) -> &'a str {
-            &ctx.0
+    #[graphql_object(impl = CharacterValue, context = CustomContext)]
+    impl Human {
+        async fn id<'a>(&'a self, _context: &CustomContext) -> &'a str {
+            &self.id
         }
 
-        fn info<'b>(&'b self, _: &()) -> &'b str {
+        async fn home_planet(&self) -> &str {
             &self.home_planet
         }
-    }
 
-    #[graphql_interface(dyn)]
-    impl Hero for Human {
-        async fn id<'a>(&self, ctx: &'a CustomContext) -> &'a str {
-            &ctx.0
+        fn info<'b>(&'b self, _ctx: &()) -> &'b str {
+            &self.home_planet
         }
 
-        async fn info<'b>(&'b self, _: &()) -> &'b str {
-            &self.home_planet
+        fn more(&self, #[graphql(context)] _: &CustomContext) -> &'static str {
+            "human"
         }
     }
 
-    #[derive(GraphQLObject)]
-    #[graphql(impl = [CharacterValue, DynHero<__S>], context = CustomContext)]
     struct Droid {
         id: String,
         primary_function: String,
     }
 
-    #[graphql_interface]
-    impl Character for Droid {
-        fn id<'a>(&self, ctx: &'a CustomContext) -> &'a str {
-            &ctx.0
+    #[graphql_object(impl = CharacterValue, context = CustomContext)]
+    impl Droid {
+        async fn id<'a>(&'a self) -> &'a str {
+            &self.id
         }
 
-        fn info<'b>(&'b self, _: &()) -> &'b str {
+        fn primary_function(&self) -> &str {
             &self.primary_function
         }
-    }
 
-    #[graphql_interface(dyn)]
-    impl Hero for Droid {
-        async fn id<'a>(&self, ctx: &'a CustomContext) -> &'a str {
-            &ctx.0
+        async fn info<'b>(&'b self) -> &'b str {
+            &self.primary_function
         }
 
-        async fn info<'b>(&'b self, _: &()) -> &'b str {
-            &self.primary_function
+        fn more(&self) -> &'static str {
+            "droid"
         }
     }
 
@@ -4376,7 +2373,7 @@ mod inferred_custom_context_from_field {
         Droid,
     }
 
-    #[graphql_object(context = CustomContext, scalar = S: ScalarValue + Send + Sync)]
+    #[graphql_object(context = CustomContext)]
     impl QueryRoot {
         fn character(&self) -> CharacterValue {
             match self {
@@ -4392,24 +2389,10 @@ mod inferred_custom_context_from_field {
                 .into(),
             }
         }
-
-        fn hero<S: ScalarValue + Send + Sync>(&self) -> Box<DynHero<'_, S>> {
-            let ch: Box<DynHero<'_, _>> = match self {
-                Self::Human => Box::new(Human {
-                    id: "human-32".to_string(),
-                    home_planet: "earth".to_string(),
-                }),
-                Self::Droid => Box::new(Droid {
-                    id: "droid-99".to_string(),
-                    primary_function: "run".to_string(),
-                }),
-            };
-            ch
-        }
     }
 
     #[tokio::test]
-    async fn enum_resolves_human() {
+    async fn resolves_human() {
         const DOC: &str = r#"{
             character {
                 ... on Human {
@@ -4420,10 +2403,9 @@ mod inferred_custom_context_from_field {
         }"#;
 
         let schema = schema(QueryRoot::Human);
-        let ctx = CustomContext("in-ctx".into());
 
         assert_eq!(
-            execute(DOC, None, &schema, &graphql_vars! {}, &ctx).await,
+            execute(DOC, None, &schema, &graphql_vars! {}, &CustomContext).await,
             Ok((
                 graphql_value!({"character": {"humanId": "human-32", "homePlanet": "earth"}}),
                 vec![],
@@ -4432,7 +2414,7 @@ mod inferred_custom_context_from_field {
     }
 
     #[tokio::test]
-    async fn enum_resolves_droid() {
+    async fn resolves_droid() {
         const DOC: &str = r#"{
             character {
                 ... on Droid {
@@ -4443,10 +2425,9 @@ mod inferred_custom_context_from_field {
         }"#;
 
         let schema = schema(QueryRoot::Droid);
-        let ctx = CustomContext("in-droid".into());
 
         assert_eq!(
-            execute(DOC, None, &schema, &graphql_vars! {}, &ctx).await,
+            execute(DOC, None, &schema, &graphql_vars! {}, &CustomContext).await,
             Ok((
                 graphql_value!({"character": {"droidId": "droid-99", "primaryFunction": "run"}}),
                 vec![],
@@ -4454,214 +2435,116 @@ mod inferred_custom_context_from_field {
         );
     }
 
-    #[tokio::test]
-    async fn dyn_resolves_human() {
-        const DOC: &str = r#"{
-            hero {
-                ... on Human {
-                    humanId: id
-                    homePlanet
-                }
-            }
-        }"#;
-
-        let schema = schema(QueryRoot::Human);
-        let ctx = CustomContext("in-ctx".into());
-
-        assert_eq!(
-            execute(DOC, None, &schema, &graphql_vars! {}, &ctx).await,
-            Ok((
-                graphql_value!({"hero": {"humanId": "human-32", "homePlanet": "earth"}}),
-                vec![],
-            )),
-        );
-    }
-
-    #[tokio::test]
-    async fn dyn_resolves_droid() {
-        const DOC: &str = r#"{
-            hero {
-                ... on Droid {
-                    droidId: id
-                    primaryFunction
-                }
-            }
-        }"#;
-
-        let schema = schema(QueryRoot::Droid);
-        let ctx = CustomContext("in-droid".into());
-
-        assert_eq!(
-            execute(DOC, None, &schema, &graphql_vars! {}, &ctx).await,
-            Ok((
-                graphql_value!({"hero": {"droidId": "droid-99", "primaryFunction": "run"}}),
-                vec![],
-            )),
-        );
-    }
-
     #[tokio::test]
     async fn resolves_fields() {
-        for interface in &["character", "hero"] {
-            let doc = format!(
-                r#"{{
-                    {} {{
-                        id
-                        info
-                    }}
-                }}"#,
-                interface,
-            );
-
-            let expected_interface: &str = *interface;
-
-            for (root, expected_id, expected_info) in &[
-                (QueryRoot::Human, "human-ctx", "earth"),
-                (QueryRoot::Droid, "droid-ctx", "run"),
-            ] {
-                let schema = schema(*root);
-                let ctx = CustomContext(expected_id.to_string());
-
-                let expected_id: &str = *expected_id;
-                let expected_info: &str = *expected_info;
-                assert_eq!(
-                    execute(&doc, None, &schema, &graphql_vars! {}, &ctx).await,
-                    Ok((
-                        graphql_value!({expected_interface: {
-                            "id": expected_id,
-                            "info": expected_info,
-                        }}),
-                        vec![],
-                    )),
-                );
+        const DOC: &str = r#"{
+           character {
+                id
+                info
+                more
             }
+        }"#;
+
+        for (root, expected_id, expected_info, expexted_more) in &[
+            (QueryRoot::Human, "human-32", "earth", "human"),
+            (QueryRoot::Droid, "droid-99", "run", "droid"),
+        ] {
+            let schema = schema(*root);
+
+            let expected_id: &str = *expected_id;
+            let expected_info: &str = *expected_info;
+            let expexted_more: &str = *expexted_more;
+            assert_eq!(
+                execute(DOC, None, &schema, &graphql_vars! {}, &CustomContext).await,
+                Ok((
+                    graphql_value!({"character": {
+                        "id": expected_id,
+                        "info": expected_info,
+                        "more": expexted_more,
+                    }}),
+                    vec![],
+                )),
+            );
         }
     }
 }
 
-mod inferred_custom_context_from_downcast {
+mod inferred_custom_context_from_field {
     use super::*;
 
-    struct Database {
-        droid: Option<Droid>,
-    }
+    struct CustomContext(String);
 
-    impl juniper::Context for Database {}
+    impl juniper::Context for CustomContext {}
 
     #[graphql_interface(for = [Human, Droid])]
     trait Character {
-        #[graphql(downcast)]
-        fn as_human<'s>(&'s self, _: &Database) -> Option<&'s Human>;
-
-        async fn id(&self) -> &str;
-    }
-
-    #[graphql_interface(dyn = DynHero, for = [Human, Droid])]
-    trait Hero {
-        #[graphql(downcast)]
-        fn as_droid<'db>(&self, db: &'db Database) -> Option<&'db Droid>;
+        fn id<'a>(&self, context: &'a CustomContext) -> &'a str;
 
-        async fn info(&self) -> &str;
+        fn info(&self, context: &()) -> &str;
     }
 
-    #[derive(GraphQLObject)]
-    #[graphql(impl = [CharacterValue, DynHero<__S>], context = Database)]
     struct Human {
-        id: String,
         home_planet: String,
     }
 
-    #[graphql_interface]
-    impl Character for Human {
-        fn as_human<'s>(&'s self, _: &Database) -> Option<&'s Human> {
-            Some(self)
-        }
-
-        async fn id(&self) -> &str {
-            &self.id
+    #[graphql_object(impl = CharacterValue, context = CustomContext)]
+    impl Human {
+        fn id<'a>(&self, ctx: &'a CustomContext) -> &'a str {
+            &ctx.0
         }
-    }
 
-    #[graphql_interface(dyn)]
-    impl Hero for Human {
-        fn as_droid<'db>(&self, _: &'db Database) -> Option<&'db Droid> {
-            None
+        fn home_planet(&self) -> &str {
+            &self.home_planet
         }
 
-        async fn info(&self) -> &str {
+        fn info<'b>(&'b self, _context: &()) -> &'b str {
             &self.home_planet
         }
     }
 
-    #[derive(GraphQLObject)]
-    #[graphql(impl = [CharacterValue, DynHero<__S>], context = Database)]
     struct Droid {
-        id: String,
         primary_function: String,
     }
 
-    #[graphql_interface]
-    impl Character for Droid {
-        fn as_human<'s>(&'s self, _: &Database) -> Option<&'s Human> {
-            None
-        }
-
-        async fn id(&self) -> &str {
-            &self.id
+    #[graphql_object(impl = CharacterValue, context = CustomContext)]
+    impl Droid {
+        fn id<'a>(&self, ctx: &'a CustomContext) -> &'a str {
+            &ctx.0
         }
-    }
 
-    #[graphql_interface(dyn)]
-    impl Hero for Droid {
-        fn as_droid<'db>(&self, db: &'db Database) -> Option<&'db Droid> {
-            db.droid.as_ref()
+        fn primary_function(&self) -> &str {
+            &self.primary_function
         }
 
-        async fn info(&self) -> &str {
+        fn info<'b>(&'b self) -> &'b str {
             &self.primary_function
         }
     }
 
-    #[derive(Clone)]
+    #[derive(Clone, Copy)]
     enum QueryRoot {
         Human,
         Droid,
     }
 
-    #[graphql_object(context = Database, scalar = S: ScalarValue + Send + Sync)]
+    #[graphql_object(context = CustomContext)]
     impl QueryRoot {
         fn character(&self) -> CharacterValue {
             match self {
                 Self::Human => Human {
-                    id: "human-32".to_string(),
                     home_planet: "earth".to_string(),
                 }
                 .into(),
                 Self::Droid => Droid {
-                    id: "droid-99".to_string(),
                     primary_function: "run".to_string(),
                 }
                 .into(),
             }
         }
-
-        fn hero<S: ScalarValue + Send + Sync>(&self) -> Box<DynHero<'_, S>> {
-            let ch: Box<DynHero<'_, _>> = match self {
-                Self::Human => Box::new(Human {
-                    id: "human-32".to_string(),
-                    home_planet: "earth".to_string(),
-                }),
-                Self::Droid => Box::new(Droid {
-                    id: "droid-99".to_string(),
-                    primary_function: "run".to_string(),
-                }),
-            };
-            ch
-        }
     }
 
     #[tokio::test]
-    async fn enum_resolves_human() {
+    async fn resolves_human() {
         const DOC: &str = r#"{
             character {
                 ... on Human {
@@ -4672,19 +2555,19 @@ mod inferred_custom_context_from_downcast {
         }"#;
 
         let schema = schema(QueryRoot::Human);
-        let db = Database { droid: None };
+        let ctx = CustomContext("in-ctx".into());
 
         assert_eq!(
-            execute(DOC, None, &schema, &graphql_vars! {}, &db).await,
+            execute(DOC, None, &schema, &graphql_vars! {}, &ctx).await,
             Ok((
-                graphql_value!({"character": {"humanId": "human-32", "homePlanet": "earth"}}),
+                graphql_value!({"character": {"humanId": "in-ctx", "homePlanet": "earth"}}),
                 vec![],
             )),
         );
     }
 
     #[tokio::test]
-    async fn enum_resolves_droid() {
+    async fn resolves_droid() {
         const DOC: &str = r#"{
             character {
                 ... on Droid {
@@ -4695,112 +2578,44 @@ mod inferred_custom_context_from_downcast {
         }"#;
 
         let schema = schema(QueryRoot::Droid);
-        let db = Database {
-            droid: Some(Droid {
-                id: "droid-88".to_string(),
-                primary_function: "sit".to_string(),
-            }),
-        };
-
-        assert_eq!(
-            execute(DOC, None, &schema, &graphql_vars! {}, &db).await,
-            Ok((
-                graphql_value!({"character": {"droidId": "droid-99", "primaryFunction": "run"}}),
-                vec![],
-            )),
-        );
-    }
-
-    #[tokio::test]
-    async fn dyn_resolves_human() {
-        const DOC: &str = r#"{
-            hero {
-                ... on Human {
-                    humanId: id
-                    homePlanet
-                }
-            }
-        }"#;
-
-        let schema = schema(QueryRoot::Human);
-        let db = Database { droid: None };
-
-        assert_eq!(
-            execute(DOC, None, &schema, &graphql_vars! {}, &db).await,
-            Ok((
-                graphql_value!({"hero": {"humanId": "human-32", "homePlanet": "earth"}}),
-                vec![],
-            )),
-        );
-    }
-
-    #[tokio::test]
-    async fn dyn_resolves_droid() {
-        const DOC: &str = r#"{
-            hero {
-                ... on Droid {
-                    droidId: id
-                    primaryFunction
-                }
-            }
-        }"#;
-
-        let schema = schema(QueryRoot::Droid);
-        let db = Database {
-            droid: Some(Droid {
-                id: "droid-88".to_string(),
-                primary_function: "sit".to_string(),
-            }),
-        };
+        let ctx = CustomContext("in-droid".into());
 
         assert_eq!(
-            execute(DOC, None, &schema, &graphql_vars! {}, &db).await,
+            execute(DOC, None, &schema, &graphql_vars! {}, &ctx).await,
             Ok((
-                graphql_value!({"hero": {"droidId": "droid-88", "primaryFunction": "sit"}}),
+                graphql_value!({"character": {"droidId": "in-droid", "primaryFunction": "run"}}),
                 vec![],
             )),
         );
     }
 
     #[tokio::test]
-    async fn enum_resolves_id_field() {
+    async fn resolves_fields() {
         const DOC: &str = r#"{
             character {
                 id
+                info
             }
         }"#;
 
-        for (root, expected_id) in &[
-            (QueryRoot::Human, "human-32"),
-            (QueryRoot::Droid, "droid-99"),
+        for (root, expected_id, expected_info) in &[
+            (QueryRoot::Human, "human-ctx", "earth"),
+            (QueryRoot::Droid, "droid-ctx", "run"),
         ] {
-            let schema = schema(root.clone());
-            let db = Database { droid: None };
+            let schema = schema(*root);
+            let ctx = CustomContext(expected_id.to_string());
 
             let expected_id: &str = *expected_id;
-            assert_eq!(
-                execute(DOC, None, &schema, &graphql_vars! {}, &db).await,
-                Ok((graphql_value!({"character": {"id": expected_id}}), vec![])),
-            );
-        }
-    }
-
-    #[tokio::test]
-    async fn dyn_resolves_info_field() {
-        const DOC: &str = r#"{
-            hero {
-                info
-            }
-        }"#;
-
-        for (root, expected_info) in &[(QueryRoot::Human, "earth"), (QueryRoot::Droid, "run")] {
-            let schema = schema(root.clone());
-            let db = Database { droid: None };
-
             let expected_info: &str = *expected_info;
             assert_eq!(
-                execute(DOC, None, &schema, &graphql_vars! {}, &db).await,
-                Ok((graphql_value!({"hero": {"info": expected_info}}), vec![])),
+                execute(DOC, None, &schema, &graphql_vars! {}, &ctx).await,
+                Ok((
+                    graphql_value!({"character": {
+                        "id": expected_id,
+                        "info": expected_info,
+                    }}),
+                    vec![],
+                )),
             );
         }
     }
@@ -4813,90 +2628,53 @@ mod executor {
 
     #[graphql_interface(for = [Human, Droid], scalar = S)]
     trait Character<S: ScalarValue> {
-        async fn id<'a>(&self, executor: &'a Executor<'_, '_, (), S>) -> &'a str
-        where
-            S: Send + Sync,
-        {
-            executor.look_ahead().field_name()
-        }
-
-        async fn info<'b>(
-            &'b self,
-            arg: Option<i32>,
-            #[graphql(executor)] another: &Executor<'_, '_, (), S>,
-        ) -> &'b str
-        where
-            S: Send + Sync;
-    }
-
-    #[graphql_interface(dyn = DynHero, for = [Human, Droid], scalar = S)]
-    trait Hero<S: ScalarValue> {
-        async fn id<'a>(&self, executor: &'a Executor<'_, '_, (), S>) -> &'a str
-        where
-            S: Send + Sync,
-        {
-            executor.look_ahead().field_name()
-        }
+        fn id<'a>(&self, executor: &'a Executor<'_, '_, (), S>) -> &'a str;
 
-        async fn info<'b>(
+        fn info<'b>(
             &'b self,
             arg: Option<i32>,
             #[graphql(executor)] another: &Executor<'_, '_, (), S>,
-        ) -> &'b str
-        where
-            S: Send + Sync;
+        ) -> &'b str;
     }
 
-    #[derive(GraphQLObject)]
-    #[graphql(impl = [CharacterValue<__S>, DynHero<__S>])]
     struct Human {
-        id: String,
         home_planet: String,
     }
 
-    #[graphql_interface(scalar = S)]
-    impl<S: ScalarValue> Character<S> for Human {
-        async fn info<'b>(&'b self, _: Option<i32>, _: &Executor<'_, '_, (), S>) -> &'b str
-        where
-            S: Send + Sync,
-        {
+    #[graphql_object(scalar = S: ScalarValue, impl = CharacterValue<S>)]
+    impl Human {
+        async fn id<'a, S: ScalarValue>(&self, executor: &'a Executor<'_, '_, (), S>) -> &'a str {
+            executor.look_ahead().field_name()
+        }
+
+        fn home_planet(&self) -> &str {
             &self.home_planet
         }
-    }
 
-    #[graphql_interface(dyn, scalar = S)]
-    impl<S: ScalarValue> Hero<S> for Human {
-        async fn info<'b>(&'b self, _: Option<i32>, _: &Executor<'_, '_, (), S>) -> &'b str
-        where
-            S: Send + Sync,
-        {
+        async fn info<'b>(&'b self, _arg: Option<i32>) -> &'b str {
             &self.home_planet
         }
     }
 
-    #[derive(GraphQLObject)]
-    #[graphql(impl = [CharacterValue<__S>, DynHero<__S>])]
     struct Droid {
-        id: String,
         primary_function: String,
     }
 
-    #[graphql_interface(scalar = S)]
-    impl<S: ScalarValue> Character<S> for Droid {
-        async fn info<'b>(&'b self, _: Option<i32>, _: &Executor<'_, '_, (), S>) -> &'b str
-        where
-            S: Send + Sync,
-        {
+    #[graphql_object(impl = CharacterValue<__S>)]
+    impl Droid {
+        fn id<'a, S: ScalarValue>(&self, executor: &'a Executor<'_, '_, (), S>) -> &'a str {
+            executor.look_ahead().field_name()
+        }
+
+        fn primary_function(&self) -> &str {
             &self.primary_function
         }
-    }
 
-    #[graphql_interface(dyn, scalar = S)]
-    impl<S: ScalarValue> Hero<S> for Droid {
-        async fn info<'b>(&'b self, _: Option<i32>, _: &Executor<'_, '_, (), S>) -> &'b str
-        where
-            S: Send + Sync,
-        {
+        async fn info<'b, S: ScalarValue>(
+            &'b self,
+            _arg: Option<i32>,
+            _executor: &Executor<'_, '_, (), S>,
+        ) -> &'b str {
             &self.primary_function
         }
     }
@@ -4912,35 +2690,19 @@ mod executor {
         fn character(&self) -> CharacterValue<DefaultScalarValue> {
             match self {
                 Self::Human => Human {
-                    id: "human-32".to_string(),
                     home_planet: "earth".to_string(),
                 }
                 .into(),
                 Self::Droid => Droid {
-                    id: "droid-99".to_string(),
                     primary_function: "run".to_string(),
                 }
                 .into(),
             }
         }
-
-        fn hero(&self) -> Box<DynHero<'_, DefaultScalarValue>> {
-            let ch: Box<DynHero<'_, _>> = match self {
-                Self::Human => Box::new(Human {
-                    id: "human-32".to_string(),
-                    home_planet: "earth".to_string(),
-                }),
-                Self::Droid => Box::new(Droid {
-                    id: "droid-99".to_string(),
-                    primary_function: "run".to_string(),
-                }),
-            };
-            ch
-        }
     }
 
     #[tokio::test]
-    async fn enum_resolves_human() {
+    async fn resolves_human() {
         const DOC: &str = r#"{
             character {
                 ... on Human {
@@ -4955,14 +2717,14 @@ mod executor {
         assert_eq!(
             execute(DOC, None, &schema, &graphql_vars! {}, &()).await,
             Ok((
-                graphql_value!({"character": {"humanId": "human-32", "homePlanet": "earth"}}),
+                graphql_value!({"character": {"humanId": "humanId", "homePlanet": "earth"}}),
                 vec![],
             )),
         );
     }
 
     #[tokio::test]
-    async fn enum_resolves_droid() {
+    async fn resolves_droid() {
         const DOC: &str = r#"{
             character {
                 ... on Droid {
@@ -4977,117 +2739,61 @@ mod executor {
         assert_eq!(
             execute(DOC, None, &schema, &graphql_vars! {}, &()).await,
             Ok((
-                graphql_value!({"character": {"droidId": "droid-99", "primaryFunction": "run"}}),
+                graphql_value!({"character": {"droidId": "droidId", "primaryFunction": "run"}}),
                 vec![],
             )),
         );
     }
 
     #[tokio::test]
-    async fn dyn_resolves_human() {
+    async fn resolves_fields() {
         const DOC: &str = r#"{
-            hero {
-                ... on Human {
-                    humanId: id
-                    homePlanet
-                }
+            character {
+                id
+                info
             }
         }"#;
 
-        let schema = schema(QueryRoot::Human);
+        for (root, expected_info) in &[(QueryRoot::Human, "earth"), (QueryRoot::Droid, "run")] {
+            let schema = schema(*root);
 
-        assert_eq!(
-            execute(DOC, None, &schema, &graphql_vars! {}, &()).await,
-            Ok((
-                graphql_value!({"hero": {"humanId": "human-32", "homePlanet": "earth"}}),
-                vec![],
-            )),
-        );
+            let expected_info: &str = *expected_info;
+            assert_eq!(
+                execute(DOC, None, &schema, &graphql_vars! {}, &()).await,
+                Ok((
+                    graphql_value!({"character": {"id": "id", "info": expected_info}}),
+                    vec![],
+                )),
+            );
+        }
     }
 
     #[tokio::test]
-    async fn dyn_resolves_droid() {
+    async fn not_arg() {
         const DOC: &str = r#"{
-            hero {
-                ... on Droid {
-                    droidId: id
-                    primaryFunction
+            __type(name: "Character") {
+                fields {
+                    name
+                    args {
+                        name
+                    }
                 }
             }
         }"#;
 
-        let schema = schema(QueryRoot::Droid);
+        let schema = schema(QueryRoot::Human);
 
         assert_eq!(
             execute(DOC, None, &schema, &graphql_vars! {}, &()).await,
             Ok((
-                graphql_value!({"hero": {"droidId": "droid-99", "primaryFunction": "run"}}),
+                graphql_value!({"__type": {"fields": [
+                    {"name": "id", "args": []},
+                    {"name": "info", "args": [{"name": "arg"}]},
+                ]}}),
                 vec![],
             )),
         );
     }
-
-    #[tokio::test]
-    async fn resolves_fields() {
-        for interface in &["character", "hero"] {
-            let doc = format!(
-                r#"{{
-                    {} {{
-                        id
-                        info
-                    }}
-                }}"#,
-                interface,
-            );
-
-            let expected_interface: &str = *interface;
-
-            for (root, expected_info) in &[(QueryRoot::Human, "earth"), (QueryRoot::Droid, "run")] {
-                let schema = schema(*root);
-
-                let expected_info: &str = *expected_info;
-                assert_eq!(
-                    execute(&doc, None, &schema, &graphql_vars! {}, &()).await,
-                    Ok((
-                        graphql_value!({expected_interface: {"id": "id", "info": expected_info}}),
-                        vec![],
-                    )),
-                );
-            }
-        }
-    }
-
-    #[tokio::test]
-    async fn not_arg() {
-        for interface in &["Character", "Hero"] {
-            let doc = format!(
-                r#"{{
-                    __type(name: "{}") {{
-                        fields {{
-                            name
-                            args {{
-                                name
-                            }}
-                        }}
-                    }}
-                }}"#,
-                interface,
-            );
-
-            let schema = schema(QueryRoot::Human);
-
-            assert_eq!(
-                execute(&doc, None, &schema, &graphql_vars! {}, &()).await,
-                Ok((
-                    graphql_value!({"__type": {"fields": [
-                        {"name": "id", "args": []},
-                        {"name": "info", "args": [{"name": "arg"}]},
-                    ]}}),
-                    vec![],
-                )),
-            );
-        }
-    }
 }
 
 mod ignored_method {
@@ -5113,13 +2819,6 @@ mod ignored_method {
         home_planet: String,
     }
 
-    #[graphql_interface]
-    impl Character for Human {
-        fn id(&self) -> &str {
-            &self.id
-        }
-    }
-
     struct QueryRoot;
 
     #[graphql_object]
@@ -5191,88 +2890,46 @@ mod ignored_method {
             )),
         );
     }
-}
-
-mod downcast_method {
-    use super::*;
-
-    #[graphql_interface(for = [Human, Droid])]
-    trait Character {
-        fn id(&self) -> &str;
-
-        #[graphql(downcast)]
-        fn as_human(&self) -> Option<&Human> {
-            None
-        }
-    }
+}
 
-    #[graphql_interface(dyn = DynHero, for = [Human, Droid])]
-    trait Hero {
-        fn info(&self) -> &str;
+mod field_return_subtyping {
+    use super::*;
 
-        #[graphql(downcast)]
-        fn as_droid(&self) -> Option<&Droid> {
-            None
-        }
+    #[graphql_interface(for = [Human, Droid])]
+    trait Character {
+        fn id(&self) -> Option<String>;
     }
 
     #[derive(GraphQLObject)]
-    #[graphql(impl = [CharacterValue, DynHero<__S>])]
+    #[graphql(impl = CharacterValue)]
     struct Human {
         id: String,
         home_planet: String,
     }
 
-    #[graphql_interface]
-    impl Character for Human {
-        fn id(&self) -> &str {
-            &self.id
-        }
-
-        fn as_human(&self) -> Option<&Human> {
-            Some(self)
-        }
-    }
-
-    #[graphql_interface(dyn)]
-    impl Hero for Human {
-        fn info(&self) -> &str {
-            &self.home_planet
-        }
-    }
-
-    #[derive(GraphQLObject)]
-    #[graphql(impl = [CharacterValue, DynHero<__S>])]
     struct Droid {
         id: String,
         primary_function: String,
     }
 
-    #[graphql_interface]
-    impl Character for Droid {
+    #[graphql_object(impl = CharacterValue)]
+    impl Droid {
         fn id(&self) -> &str {
             &self.id
         }
-    }
 
-    #[graphql_interface(dyn)]
-    impl Hero for Droid {
-        fn info(&self) -> &str {
+        fn primary_function(&self) -> &str {
             &self.primary_function
         }
-
-        fn as_droid(&self) -> Option<&Droid> {
-            Some(self)
-        }
     }
 
-    #[derive(Clone)]
+    #[derive(Clone, Copy)]
     enum QueryRoot {
         Human,
         Droid,
     }
 
-    #[graphql_object(scalar = S: ScalarValue + Send + Sync)]
+    #[graphql_object]
     impl QueryRoot {
         fn character(&self) -> CharacterValue {
             match self {
@@ -5288,24 +2945,10 @@ mod downcast_method {
                 .into(),
             }
         }
-
-        fn hero<S: ScalarValue + Send + Sync>(&self) -> Box<DynHero<'_, S>> {
-            let ch: Box<DynHero<'_, _>> = match self {
-                Self::Human => Box::new(Human {
-                    id: "human-32".to_string(),
-                    home_planet: "earth".to_string(),
-                }),
-                Self::Droid => Box::new(Droid {
-                    id: "droid-99".to_string(),
-                    primary_function: "run".to_string(),
-                }),
-            };
-            ch
-        }
     }
 
     #[tokio::test]
-    async fn enum_resolves_human() {
+    async fn resolves_human() {
         const DOC: &str = r#"{
             character {
                 ... on Human {
@@ -5327,7 +2970,7 @@ mod downcast_method {
     }
 
     #[tokio::test]
-    async fn enum_resolves_droid() {
+    async fn resolves_droid() {
         const DOC: &str = r#"{
             character {
                 ... on Droid {
@@ -5349,62 +2992,18 @@ mod downcast_method {
     }
 
     #[tokio::test]
-    async fn dyn_resolves_human() {
-        const DOC: &str = r#"{
-            hero {
-                ... on Human {
-                    humanId: id
-                    homePlanet
-                }
-            }
-        }"#;
-
-        let schema = schema(QueryRoot::Human);
-
-        assert_eq!(
-            execute(DOC, None, &schema, &graphql_vars! {}, &()).await,
-            Ok((
-                graphql_value!({"hero": {"humanId": "human-32", "homePlanet": "earth"}}),
-                vec![],
-            )),
-        );
-    }
-
-    #[tokio::test]
-    async fn dyn_resolves_droid() {
-        const DOC: &str = r#"{
-            hero {
-                ... on Droid {
-                    droidId: id
-                    primaryFunction
-                }
-            }
-        }"#;
-
-        let schema = schema(QueryRoot::Droid);
-
-        assert_eq!(
-            execute(DOC, None, &schema, &graphql_vars! {}, &()).await,
-            Ok((
-                graphql_value!({"hero": {"droidId": "droid-99", "primaryFunction": "run"}}),
-                vec![],
-            )),
-        );
-    }
-
-    #[tokio::test]
-    async fn enum_resolves_id_field() {
+    async fn resolves_id_field() {
         const DOC: &str = r#"{
             character {
                 id
             }
-       }"#;
+        }"#;
 
         for (root, expected_id) in &[
             (QueryRoot::Human, "human-32"),
             (QueryRoot::Droid, "droid-99"),
         ] {
-            let schema = schema(root.clone());
+            let schema = schema(*root);
 
             let expected_id: &str = *expected_id;
             assert_eq!(
@@ -5413,221 +3012,250 @@ mod downcast_method {
             );
         }
     }
-
-    #[tokio::test]
-    async fn dyn_resolves_info_field() {
-        const DOC: &str = r#"{
-            hero {
-                info
-            }
-        }"#;
-
-        for (root, expected_info) in &[(QueryRoot::Human, "earth"), (QueryRoot::Droid, "run")] {
-            let schema = schema(root.clone());
-
-            let expected_info: &str = *expected_info;
-            assert_eq!(
-                execute(DOC, None, &schema, &graphql_vars! {}, &()).await,
-                Ok((graphql_value!({"hero": {"info": expected_info}}), vec![])),
-            );
-        }
-    }
-
-    #[tokio::test]
-    async fn is_not_field() {
-        let schema = schema(QueryRoot::Human);
-
-        for (doc, field) in &[
-            (r#"{__type(name: "Character") { fields { name } } }"#, "id"),
-            (r#"{__type(name: "Hero") { fields { name } } }"#, "info"),
-        ] {
-            let expected_field: &str = *field;
-
-            assert_eq!(
-                execute(*doc, None, &schema, &graphql_vars! {}, &()).await,
-                Ok((
-                    graphql_value!({"__type": {"fields": [{"name": expected_field}]}}),
-                    vec![],
-                )),
-            );
-        }
-    }
 }
 
-mod external_downcast {
+mod field_return_union_subtyping {
     use super::*;
 
-    struct Database {
-        human: Option<Human>,
-        droid: Option<Droid>,
+    #[derive(GraphQLObject)]
+    struct Strength {
+        value: i32,
     }
 
-    impl juniper::Context for Database {}
-
-    #[graphql_interface(for = [Human, Droid])]
-    #[graphql_interface(context = Database, on Human = CharacterValue::as_human)]
-    trait Character {
-        fn id(&self) -> &str;
+    #[derive(GraphQLObject)]
+    struct Knowledge {
+        value: i32,
     }
 
-    impl CharacterValue {
-        fn as_human<'db>(&self, db: &'db Database) -> Option<&'db Human> {
-            db.human.as_ref()
-        }
+    #[allow(dead_code)]
+    #[derive(GraphQLUnion)]
+    enum KeyFeature {
+        Strength(Strength),
+        Knowledge(Knowledge),
     }
 
-    #[graphql_interface(dyn = DynHero, for = [Human, Droid])]
-    #[graphql_interface(context = Database)]
-    #[graphql_interface(on Droid = DynHero::as_droid)]
-    trait Hero {
-        fn info(&self) -> &str;
-    }
+    #[graphql_interface(for = [Human, Droid])]
+    trait Character {
+        fn id(&self) -> Option<String>;
 
-    impl<'a, S: ScalarValue> DynHero<'a, S> {
-        fn as_droid<'db>(&self, db: &'db Database) -> Option<&'db Droid> {
-            db.droid.as_ref()
-        }
+        fn key_feature(&self) -> KeyFeature;
     }
 
     #[derive(GraphQLObject)]
-    #[graphql(impl = [CharacterValue, DynHero<__S>], context = Database)]
+    #[graphql(impl = CharacterValue)]
     struct Human {
         id: String,
         home_planet: String,
+        key_feature: Knowledge,
     }
 
-    #[graphql_interface]
-    impl Character for Human {
-        fn id(&self) -> &str {
-            &self.id
-        }
-    }
-
-    #[graphql_interface(dyn)]
-    impl Hero for Human {
-        fn info(&self) -> &str {
-            &self.home_planet
-        }
-    }
-
-    #[derive(GraphQLObject)]
-    #[graphql(impl = [CharacterValue, DynHero<__S>], context = Database)]
     struct Droid {
         id: String,
         primary_function: String,
+        strength: i32,
     }
 
-    #[graphql_interface]
-    impl Character for Droid {
+    #[graphql_object(impl = CharacterValue)]
+    impl Droid {
         fn id(&self) -> &str {
             &self.id
         }
-    }
 
-    #[graphql_interface(dyn)]
-    impl Hero for Droid {
-        fn info(&self) -> &str {
+        fn primary_function(&self) -> &str {
             &self.primary_function
         }
+
+        fn key_feature(&self) -> Strength {
+            Strength {
+                value: self.strength,
+            }
+        }
     }
 
-    #[derive(Clone)]
+    #[derive(Clone, Copy)]
     enum QueryRoot {
         Human,
         Droid,
     }
 
-    #[graphql_object(context = Database, scalar = S: ScalarValue + Send + Sync)]
+    #[graphql_object]
     impl QueryRoot {
         fn character(&self) -> CharacterValue {
             match self {
                 Self::Human => Human {
                     id: "human-32".to_string(),
                     home_planet: "earth".to_string(),
+                    key_feature: Knowledge { value: 10 },
                 }
                 .into(),
                 Self::Droid => Droid {
                     id: "droid-99".to_string(),
                     primary_function: "run".to_string(),
+                    strength: 42,
                 }
                 .into(),
             }
         }
-
-        fn hero<S: ScalarValue + Send + Sync>(&self) -> Box<DynHero<'_, S>> {
-            let ch: Box<DynHero<'_, _>> = match self {
-                Self::Human => Box::new(Human {
-                    id: "human-32".to_string(),
-                    home_planet: "earth".to_string(),
-                }),
-                Self::Droid => Box::new(Droid {
-                    id: "droid-99".to_string(),
-                    primary_function: "run".to_string(),
-                }),
-            };
-            ch
-        }
     }
 
     #[tokio::test]
-    async fn enum_resolves_human() {
+    async fn resolves_human() {
         const DOC: &str = r#"{
             character {
                 ... on Human {
                     humanId: id
                     homePlanet
+                    keyFeature {
+                        value
+                    }
                 }
             }
         }"#;
 
         let schema = schema(QueryRoot::Human);
-        let db = Database {
-            human: Some(Human {
-                id: "human-64".to_string(),
-                home_planet: "mars".to_string(),
-            }),
-            droid: None,
-        };
 
         assert_eq!(
-            execute(DOC, None, &schema, &graphql_vars! {}, &db).await,
+            execute(DOC, None, &schema, &graphql_vars! {}, &()).await,
             Ok((
-                graphql_value!({"character": {"humanId": "human-64", "homePlanet": "mars"}}),
+                graphql_value!({"character": {
+                    "humanId": "human-32",
+                    "homePlanet": "earth",
+                    "keyFeature": {"value": 10},
+                }}),
                 vec![],
             )),
         );
     }
 
     #[tokio::test]
-    async fn enum_resolves_droid() {
+    async fn resolves_droid() {
         const DOC: &str = r#"{
             character {
                 ... on Droid {
                     droidId: id
                     primaryFunction
+                    keyFeature {
+                        value
+                    }
                 }
             }
         }"#;
 
         let schema = schema(QueryRoot::Droid);
-        let db = Database {
-            human: None,
-            droid: None,
-        };
 
         assert_eq!(
-            execute(DOC, None, &schema, &graphql_vars! {}, &db).await,
+            execute(DOC, None, &schema, &graphql_vars! {}, &()).await,
             Ok((
-                graphql_value!({"character": {"droidId": "droid-99", "primaryFunction": "run"}}),
+                graphql_value!({"character": {
+                    "droidId": "droid-99",
+                    "primaryFunction": "run",
+                    "keyFeature": {"value": 42},
+                }}),
                 vec![],
             )),
         );
     }
 
     #[tokio::test]
-    async fn dyn_resolves_human() {
+    async fn resolves_fields() {
+        const DOC: &str = r#"{
+            character {
+                id
+                keyFeature {
+                    ...on Strength {
+                        value
+                    }
+                    ... on Knowledge {
+                        value
+                    }
+                }
+            }
+        }"#;
+
+        for (root, expected_id, expected_val) in &[
+            (QueryRoot::Human, "human-32", 10),
+            (QueryRoot::Droid, "droid-99", 42),
+        ] {
+            let schema = schema(*root);
+
+            let expected_id: &str = *expected_id;
+            let expected_val = *expected_val;
+            assert_eq!(
+                execute(DOC, None, &schema, &graphql_vars! {}, &()).await,
+                Ok((
+                    graphql_value!({"character": {
+                        "id": expected_id,
+                        "keyFeature": {"value": expected_val},
+                    }}),
+                    vec![],
+                )),
+            );
+        }
+    }
+}
+
+mod nullable_argument_subtyping {
+    use super::*;
+
+    #[graphql_interface(for = [Human, Droid])]
+    trait Character {
+        fn id(&self) -> Option<String>;
+    }
+
+    #[derive(GraphQLObject)]
+    #[graphql(impl = CharacterValue)]
+    struct Human {
+        id: String,
+        home_planet: String,
+    }
+
+    struct Droid {
+        id: String,
+        primary_function: String,
+    }
+
+    #[graphql_object(impl = CharacterValue)]
+    impl Droid {
+        fn id(&self, is_present: Option<bool>) -> &str {
+            is_present
+                .unwrap_or_default()
+                .then(|| self.id.as_str())
+                .unwrap_or("missing")
+        }
+
+        fn primary_function(&self) -> &str {
+            &self.primary_function
+        }
+    }
+
+    #[derive(Clone, Copy)]
+    enum QueryRoot {
+        Human,
+        Droid,
+    }
+
+    #[graphql_object]
+    impl QueryRoot {
+        fn character(&self) -> CharacterValue {
+            match self {
+                Self::Human => Human {
+                    id: "human-32".to_string(),
+                    home_planet: "earth".to_string(),
+                }
+                .into(),
+                Self::Droid => Droid {
+                    id: "droid-99".to_string(),
+                    primary_function: "run".to_string(),
+                }
+                .into(),
+            }
+        }
+    }
+
+    #[tokio::test]
+    async fn resolves_human() {
         const DOC: &str = r#"{
-            hero {
+            character {
                 ... on Human {
                     humanId: id
                     homePlanet
@@ -5636,103 +3264,57 @@ mod external_downcast {
         }"#;
 
         let schema = schema(QueryRoot::Human);
-        let db = Database {
-            human: None,
-            droid: None,
-        };
 
         assert_eq!(
-            execute(DOC, None, &schema, &graphql_vars! {}, &db).await,
+            execute(DOC, None, &schema, &graphql_vars! {}, &()).await,
             Ok((
-                graphql_value!({"hero": {"humanId": "human-32", "homePlanet": "earth"}}),
+                graphql_value!({"character": {"humanId": "human-32", "homePlanet": "earth"}}),
                 vec![],
             )),
         );
     }
 
     #[tokio::test]
-    async fn dyn_resolves_droid() {
+    async fn resolves_droid() {
         const DOC: &str = r#"{
-            hero {
+            character {
                 ... on Droid {
-                    droidId: id
+                    droidId: id(isPresent: true)
                     primaryFunction
                 }
             }
         }"#;
 
         let schema = schema(QueryRoot::Droid);
-        let db = Database {
-            human: None,
-            droid: Some(Droid {
-                id: "droid-01".to_string(),
-                primary_function: "swim".to_string(),
-            }),
-        };
 
         assert_eq!(
-            execute(DOC, None, &schema, &graphql_vars! {}, &db).await,
+            execute(DOC, None, &schema, &graphql_vars! {}, &()).await,
             Ok((
-                graphql_value!({"hero": {"droidId": "droid-01", "primaryFunction": "swim"}}),
+                graphql_value!({"character": {"droidId": "droid-99", "primaryFunction": "run"}}),
                 vec![],
             )),
         );
     }
 
     #[tokio::test]
-    async fn enum_resolves_id_field() {
+    async fn resolves_id_field() {
         const DOC: &str = r#"{
             character {
                 id
             }
         }"#;
 
-        let db = Database {
-            human: Some(Human {
-                id: "human-64".to_string(),
-                home_planet: "mars".to_string(),
-            }),
-            droid: None,
-        };
-
         for (root, expected_id) in &[
             (QueryRoot::Human, "human-32"),
-            (QueryRoot::Droid, "droid-99"),
+            (QueryRoot::Droid, "missing"),
         ] {
-            let schema = schema(root.clone());
+            let schema = schema(*root);
 
             let expected_id: &str = *expected_id;
             assert_eq!(
-                execute(DOC, None, &schema, &graphql_vars! {}, &db).await,
+                execute(DOC, None, &schema, &graphql_vars! {}, &()).await,
                 Ok((graphql_value!({"character": {"id": expected_id}}), vec![])),
             );
         }
     }
-
-    #[tokio::test]
-    async fn dyn_resolves_info_field() {
-        const DOC: &str = r#"{
-            hero {
-                info
-            }
-        }"#;
-
-        let db = Database {
-            human: None,
-            droid: Some(Droid {
-                id: "droid-01".to_string(),
-                primary_function: "swim".to_string(),
-            }),
-        };
-
-        for (root, expected_info) in &[(QueryRoot::Human, "earth"), (QueryRoot::Droid, "run")] {
-            let schema = schema(root.clone());
-
-            let expected_info: &str = *expected_info;
-            assert_eq!(
-                execute(DOC, None, &schema, &graphql_vars! {}, &db).await,
-                Ok((graphql_value!({"hero": {"info": expected_info}}), vec![])),
-            );
-        }
-    }
 }
diff --git a/integration_tests/juniper_tests/src/issue_407.rs b/integration_tests/juniper_tests/src/issue_407.rs
index 51bf4211a..245398c36 100644
--- a/integration_tests/juniper_tests/src/issue_407.rs
+++ b/integration_tests/juniper_tests/src/issue_407.rs
@@ -20,13 +20,6 @@ struct Human {
     name: String,
 }
 
-#[graphql_interface]
-impl Character for Human {
-    fn id(&self) -> &str {
-        &self.id
-    }
-}
-
 #[derive(GraphQLObject)]
 #[graphql(impl = CharacterValue)]
 struct Droid {
@@ -34,13 +27,6 @@ struct Droid {
     serial_number: String,
 }
 
-#[graphql_interface]
-impl Character for Droid {
-    fn id(&self) -> &str {
-        &self.id
-    }
-}
-
 #[graphql_object]
 impl Query {
     fn characters() -> Vec<CharacterValue> {
diff --git a/integration_tests/juniper_tests/src/issue_798.rs b/integration_tests/juniper_tests/src/issue_798.rs
index fc23bbd6e..fed322fc3 100644
--- a/integration_tests/juniper_tests/src/issue_798.rs
+++ b/integration_tests/juniper_tests/src/issue_798.rs
@@ -18,13 +18,6 @@ struct Human {
     home_planet: String,
 }
 
-#[graphql_interface]
-impl Character for Human {
-    fn id(&self) -> &str {
-        &self.id
-    }
-}
-
 #[derive(GraphQLObject)]
 #[graphql(impl = CharacterValue)]
 struct Droid {
@@ -32,13 +25,6 @@ struct Droid {
     primary_function: String,
 }
 
-#[graphql_interface]
-impl Character for Droid {
-    fn id(&self) -> &str {
-        &self.id
-    }
-}
-
 #[derive(GraphQLUnion)]
 enum FieldResult {
     Human(Human),
diff --git a/integration_tests/juniper_tests/src/issue_922.rs b/integration_tests/juniper_tests/src/issue_922.rs
index 60418de4a..fd28b45f9 100644
--- a/integration_tests/juniper_tests/src/issue_922.rs
+++ b/integration_tests/juniper_tests/src/issue_922.rs
@@ -38,17 +38,6 @@ struct Human {
     pub name: String,
 }
 
-#[graphql_interface]
-impl Character for Human {
-    fn id(&self) -> i32 {
-        self.id
-    }
-
-    fn name(&self) -> String {
-        self.name.clone()
-    }
-}
-
 #[derive(GraphQLObject)]
 #[graphql(impl = CharacterValue)]
 struct Droid {
@@ -56,17 +45,6 @@ struct Droid {
     pub name: String,
 }
 
-#[graphql_interface]
-impl Character for Droid {
-    fn id(&self) -> i32 {
-        self.id
-    }
-
-    fn name(&self) -> String {
-        self.name.clone()
-    }
-}
-
 type Schema = juniper::RootNode<'static, Query, EmptyMutation, EmptySubscription>;
 
 #[tokio::test]
diff --git a/juniper/src/executor_tests/interfaces_unions.rs b/juniper/src/executor_tests/interfaces_unions.rs
index 02787ce58..1044baafd 100644
--- a/juniper/src/executor_tests/interfaces_unions.rs
+++ b/juniper/src/executor_tests/interfaces_unions.rs
@@ -3,72 +3,28 @@ mod interface {
         graphql_interface, graphql_object,
         schema::model::RootNode,
         types::scalars::{EmptyMutation, EmptySubscription},
+        GraphQLObject,
     };
 
     #[graphql_interface(for = [Cat, Dog])]
     trait Pet {
         fn name(&self) -> &str;
-
-        #[graphql(downcast)]
-        fn as_dog(&self) -> Option<&Dog> {
-            None
-        }
-        #[graphql(downcast)]
-        fn as_cat(&self) -> Option<&Cat> {
-            None
-        }
     }
 
+    #[derive(GraphQLObject)]
+    #[graphql(impl = PetValue)]
     struct Dog {
         name: String,
         woofs: bool,
     }
 
-    #[graphql_interface]
-    impl Pet for Dog {
-        fn name(&self) -> &str {
-            &self.name
-        }
-        fn as_dog(&self) -> Option<&Dog> {
-            Some(self)
-        }
-    }
-
-    #[graphql_object(impl = PetValue)]
-    impl Dog {
-        fn name(&self) -> &str {
-            &self.name
-        }
-        fn woofs(&self) -> bool {
-            self.woofs
-        }
-    }
-
+    #[derive(GraphQLObject)]
+    #[graphql(impl = PetValue)]
     struct Cat {
         name: String,
         meows: bool,
     }
 
-    #[graphql_interface]
-    impl Pet for Cat {
-        fn name(&self) -> &str {
-            &self.name
-        }
-        fn as_cat(&self) -> Option<&Cat> {
-            Some(self)
-        }
-    }
-
-    #[graphql_object(impl = PetValue)]
-    impl Cat {
-        fn name(&self) -> &str {
-            &self.name
-        }
-        fn meows(&self) -> bool {
-            self.meows
-        }
-    }
-
     struct Schema {
         pets: Vec<PetValue>,
     }
diff --git a/juniper/src/executor_tests/introspection/mod.rs b/juniper/src/executor_tests/introspection/mod.rs
index a14a0f294..a5ec22cbd 100644
--- a/juniper/src/executor_tests/introspection/mod.rs
+++ b/juniper/src/executor_tests/introspection/mod.rs
@@ -43,9 +43,7 @@ impl<S: ScalarValue> GraphQLScalar for Scalar {
 #[graphql_interface(name = "SampleInterface", for = Root)]
 trait Interface {
     /// A sample field in the interface
-    fn sample_enum(&self) -> Sample {
-        Sample::One
-    }
+    fn sample_enum(&self) -> Sample;
 }
 
 struct Root;
@@ -66,9 +64,6 @@ impl Root {
     }
 }
 
-#[graphql_interface(scalar = DefaultScalarValue)]
-impl Interface for Root {}
-
 #[tokio::test]
 async fn test_execution() {
     let doc = r#"
diff --git a/juniper/src/tests/fixtures/starwars/schema.rs b/juniper/src/tests/fixtures/starwars/schema.rs
index d5eebbe5c..d9c795de6 100644
--- a/juniper/src/tests/fixtures/starwars/schema.rs
+++ b/juniper/src/tests/fixtures/starwars/schema.rs
@@ -95,11 +95,7 @@ impl Human {
         Self {
             id: id.to_owned(),
             name: name.to_owned(),
-            friend_ids: friend_ids
-                .to_owned()
-                .into_iter()
-                .map(ToOwned::to_owned)
-                .collect(),
+            friend_ids: friend_ids.iter().copied().map(ToOwned::to_owned).collect(),
             appears_in: appears_in.to_vec(),
             secret_backstory: secret_backstory.map(ToOwned::to_owned),
             home_planet: home_planet.map(|p| p.to_owned()),
@@ -111,54 +107,31 @@ impl Human {
 #[graphql_object(context = Database, impl = CharacterValue)]
 impl Human {
     /// The id of the human
-    fn id(&self) -> &str {
+    pub fn id(&self) -> &str {
         &self.id
     }
 
     /// The name of the human
-    fn name(&self) -> Option<&str> {
+    pub fn name(&self) -> Option<&str> {
         Some(self.name.as_str())
     }
 
     /// The friends of the human
-    fn friends(&self, ctx: &Database) -> Vec<CharacterValue> {
-        ctx.get_friends(self)
+    pub fn friends(&self, ctx: &Database) -> Vec<CharacterValue> {
+        ctx.get_friends(&self.friend_ids)
     }
 
     /// Which movies they appear in
-    fn appears_in(&self) -> &[Episode] {
+    pub fn appears_in(&self) -> &[Episode] {
         &self.appears_in
     }
 
     /// The home planet of the human
-    fn home_planet(&self) -> &Option<String> {
+    pub fn home_planet(&self) -> &Option<String> {
         &self.home_planet
     }
 }
 
-#[graphql_interface]
-impl Character for Human {
-    fn id(&self) -> &str {
-        &self.id
-    }
-
-    fn name(&self) -> Option<&str> {
-        Some(&self.name)
-    }
-
-    fn friends(&self, ctx: &Database) -> Vec<CharacterValue> {
-        ctx.get_friends(self)
-    }
-
-    fn appears_in(&self) -> &[Episode] {
-        &self.appears_in
-    }
-
-    fn friends_ids(&self) -> &[String] {
-        &self.friend_ids
-    }
-}
-
 #[derive(Clone)]
 pub struct Droid {
     id: String,
@@ -182,11 +155,7 @@ impl Droid {
         Self {
             id: id.to_owned(),
             name: name.to_owned(),
-            friend_ids: friend_ids
-                .to_owned()
-                .into_iter()
-                .map(ToOwned::to_owned)
-                .collect(),
+            friend_ids: friend_ids.iter().copied().map(ToOwned::to_owned).collect(),
             appears_in: appears_in.to_vec(),
             secret_backstory: secret_backstory.map(ToOwned::to_owned),
             primary_function: primary_function.map(ToOwned::to_owned),
@@ -198,54 +167,31 @@ impl Droid {
 #[graphql_object(context = Database, impl = CharacterValue)]
 impl Droid {
     /// The id of the droid
-    fn id(&self) -> &str {
+    pub fn id(&self) -> &str {
         &self.id
     }
 
     /// The name of the droid
-    fn name(&self) -> Option<&str> {
+    pub fn name(&self) -> Option<&str> {
         Some(self.name.as_str())
     }
 
     /// The friends of the droid
-    fn friends(&self, ctx: &Database) -> Vec<CharacterValue> {
-        ctx.get_friends(self)
+    pub fn friends(&self, ctx: &Database) -> Vec<CharacterValue> {
+        ctx.get_friends(&self.friend_ids)
     }
 
     /// Which movies they appear in
-    fn appears_in(&self) -> &[Episode] {
+    pub fn appears_in(&self) -> &[Episode] {
         &self.appears_in
     }
 
     /// The primary function of the droid
-    fn primary_function(&self) -> &Option<String> {
+    pub fn primary_function(&self) -> &Option<String> {
         &self.primary_function
     }
 }
 
-#[graphql_interface]
-impl Character for Droid {
-    fn id(&self) -> &str {
-        &self.id
-    }
-
-    fn name(&self) -> Option<&str> {
-        Some(&self.name)
-    }
-
-    fn friends(&self, ctx: &Database) -> Vec<CharacterValue> {
-        ctx.get_friends(self)
-    }
-
-    fn appears_in(&self) -> &[Episode] {
-        &self.appears_in
-    }
-
-    fn friends_ids(&self) -> &[String] {
-        &self.friend_ids
-    }
-}
-
 #[derive(Default, Clone)]
 pub struct Database {
     humans: HashMap<String, Human>,
@@ -373,10 +319,7 @@ impl Database {
         }
     }
 
-    pub fn get_friends(&self, c: &dyn Character) -> Vec<CharacterValue> {
-        c.friends_ids()
-            .iter()
-            .flat_map(|id| self.get_character(id))
-            .collect()
+    pub fn get_friends(&self, ids: &[String]) -> Vec<CharacterValue> {
+        ids.iter().flat_map(|id| self.get_character(id)).collect()
     }
 }