diff --git a/modules/core/src/main/scala/compiler.scala b/modules/core/src/main/scala/compiler.scala index 80241477..57433c2f 100644 --- a/modules/core/src/main/scala/compiler.scala +++ b/modules/core/src/main/scala/compiler.scala @@ -977,7 +977,7 @@ object QueryCompiler { f <- Elab.fragment(nme) ctpe = c.tpe.underlyingNamed subtpe <- Elab.liftR(s.definition(f.tpnme).toResult(s"Unknown type '${f.tpnme}' in type condition of fragment '$nme'")) - _ <- Elab.failure(s"Fragment '$nme' is not compatible with type '${c.tpe}'").whenA(!fragmentApplies(subtpe, ctpe)) + _ <- Elab.failure(s"Fragment '$nme' is not compatible with type '${c.tpe}'").whenA(!fragmentApplies(s, subtpe, ctpe)) _ <- Elab.push(c.asType(subtpe), f.child) ec <- transform(f.child) _ <- Elab.pop @@ -999,7 +999,7 @@ object QueryCompiler { case Some(tpnme) => Elab.liftR(s.definition(tpnme).toResult(s"Unknown type '$tpnme' in type condition inline fragment")) } - _ <- Elab.failure(s"Inline fragment with type condition '$subtpe' is not compatible with type '$ctpe'").whenA(!fragmentApplies(subtpe, ctpe)) + _ <- Elab.failure(s"Inline fragment with type condition '$subtpe' is not compatible with type '$ctpe'").whenA(!fragmentApplies(s, subtpe, ctpe)) _ <- Elab.push(c.asType(subtpe), child) ec <- transform(child) _ <- Elab.pop @@ -1014,15 +1014,11 @@ object QueryCompiler { /** * Tests the supplied type condition is satisfied by the supplied context type */ - def fragmentApplies(typeCond: NamedType, ctpe: NamedType): Boolean = - (typeCond.dealias, ctpe.dealias) match { - case (u: UnionType, _) => - u.members.exists(m => fragmentApplies(m, ctpe)) - case (_, u: UnionType) => - u.members.exists(m => fragmentApplies(typeCond, m)) - case _ => - typeCond <:< ctpe || ctpe <:< typeCond - } + def fragmentApplies(schema: Schema, typeCond: NamedType, ctpe: NamedType): Boolean = { + val typeCondPoss = schema.getPossibleTypes(typeCond) + val ctpePoss = schema.getPossibleTypes(ctpe.dealias) + typeCondPoss.exists(x => ctpePoss.exists(y => x =:= y)) + } def elaborateBinding(b: Binding, vars: Vars): Elab[Binding] = Elab.liftR(Value.elaborateValue(b.value, vars).map(ev => b.copy(value = ev))) diff --git a/modules/core/src/main/scala/schema.scala b/modules/core/src/main/scala/schema.scala index 2b619bc6..8556f68e 100644 --- a/modules/core/src/main/scala/schema.scala +++ b/modules/core/src/main/scala/schema.scala @@ -257,6 +257,17 @@ trait Schema { case _ => schemaType } } + + def getPossibleTypes: NamedType => Set[NamedType] = { + case x: ObjectType => Set(x) + case x: InterfaceType => getSubtypingObjectTypes(x) + case x: UnionType => x.members.toSet + case _ => Set.empty + } + + def getSubtypingObjectTypes(interface: InterfaceType): Set[NamedType] = + types.filter(_.isObject).filter(_ <:< interface).toSet + } object Schema { @@ -575,6 +586,8 @@ sealed trait Type extends Product { def isUnion: Boolean = false + def isObject: Boolean = false + def /(pathElement: String): Path = Path.from(this) / pathElement @@ -784,7 +797,9 @@ case class ObjectType( fields: List[Field], interfaces: List[NamedType], directives: List[Directive] -) extends TypeWithFields +) extends TypeWithFields { + override def isObject: Boolean = true +} /** * Object extensions allow additional fields to be added to a pre-existing object type