Skip to content

Fix covariance checks for SDL type extensions - #4420

Merged
andimarek merged 1 commit into
masterfrom
codex/fix-extension-covariance
Jul 23, 2026
Merged

Fix covariance checks for SDL type extensions#4420
andimarek merged 1 commit into
masterfrom
codex/fix-extension-covariance

Conversation

@andimarek

@andimarek andimarek commented Jul 23, 2026

Copy link
Copy Markdown
Member

Problem

GraphQL Java incorrectly rejected valid covariant field return types when the subtype relationship was introduced by an SDL extension rather than declared on the base type.

Object extension example

In this schema, Dog implements Pet through an extension. Returning Dog for a field declared as Pet is valid covariance:

type Query {
  base: Base
}

interface Pet {
  id: ID
}

type Dog {
  id: ID
}

extend type Dog implements Pet

type Base {
  foo: String
}

interface PetContainer {
  pet: Pet
}

extend type Base implements PetContainer {
  pet: Dog
}

The schema was incorrectly rejected because the covariance check only inspected the base definition of Dog and did not see extend type Dog implements Pet.

Interface extension example

The same problem affected interfaces inheriting from other interfaces through extensions:

type Query {
  base: Base
}

interface Pet {
  id: ID
}

interface WorkingPet {
  id: ID
}

extend interface WorkingPet implements Pet

interface PetContainer {
  pet: Pet
}

type Base implements PetContainer {
  pet: WorkingPet
}

WorkingPet is a valid covariant return type for Pet, but the extension-based relationship was previously ignored.

Union extension example

Union members introduced through extensions were also missed:

type Query {
  base: Base
}

type Cat {
  id: ID
}

type Dog {
  id: ID
}

union Pets = Cat

extend union Pets = Dog

interface PetContainer {
  pet: Pets
}

type Base implements PetContainer {
  pet: Dog
}

Dog is a valid subtype of Pets, but the previous check only inspected members declared on the base union.

Summary

TypeDefinitionRegistry stores base SDL definitions and extension definitions separately. This change makes the registry use the logical base-plus-extension relationships when:

  • checking whether an object or interface is a possible type of an interface
  • checking whether an object is a possible type of a union
  • checking covariant field return types, including nested list and non-null wrappers
  • finding object and interface implementations of an interface

This matches graphql-js behavior. graphql-js materializes extensions into its schema types before subtype checks; GraphQL Java retains separate AST definitions and now combines them when answering the equivalent registry queries.

Wrapped covariance

The same relationship is preserved while recursively checking list and non-null wrappers:

type Query {
  base: Base
}

interface Pet {
  id: ID
}

type Dog {
  id: ID
}

extend type Dog implements Pet

interface PetContainer {
  pets: [Pet]!
}

type Base implements PetContainer {
  pets: [Dog!]!
}

[Dog!]! remains a valid subtype of [Pet]!.

Unrelated types remain invalid

Extension relationships are matched by their declared interface and do not make unrelated types compatible:

type Query {
  base: Base
}

interface Pet {
  id: ID
}

interface Vehicle {
  id: ID
}

type Car {
  id: ID
}

extend type Car implements Vehicle

interface PetContainer {
  pet: Pet
}

type Base implements PetContainer {
  pet: Car
}

This schema is still rejected because Car implements Vehicle, not Pet.

@github-actions

Copy link
Copy Markdown
Contributor

Test Report

Test Results

Java Version Total Passed Failed Errors Skipped
Java 11 5903 (+13 🟢) 5847 (+13 🟢) 0 (±0) 0 (±0) 56 (±0)
Java 17 5903 (+13 🟢) 5846 (+13 🟢) 0 (±0) 0 (±0) 57 (±0)
Java 21 5903 (+13 🟢) 5846 (+13 🟢) 0 (±0) 0 (±0) 57 (±0)
Java 25 5903 (+13 🟢) 5846 (+13 🟢) 0 (±0) 0 (±0) 57 (±0)
jcstress 32 (±0) 32 (±0) 0 (±0) 0 (±0) 0 (±0)
Total 23644 (+52 🟢) 23417 (+52 🟢) 0 (±0) 0 (±0) 227 (±0)

Code Coverage (Java 25)

Metric Covered Missed Coverage vs Master
Lines 29703 3128 90.5% ±0.0%
Branches 8720 1530 85.1% ±0.0%
Methods 7940 1208 86.8% ±0.0%

Changed Class Coverage (1 class)

Class Line Branch Method
g.s.i.TypeDefinitionRegistry ±0.0% ±0.0% +0.4% 🟢

Full HTML report: build artifact jacoco-html-report

Updated: 2026-07-23 22:55:28 UTC

@andimarek
andimarek merged commit 80e8294 into master Jul 23, 2026
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant