diff --git a/core/src/main/scala/org/bykn/bosatsu/KindFormula.scala b/core/src/main/scala/org/bykn/bosatsu/KindFormula.scala index 49e7dbde5..17d1bf2a0 100644 --- a/core/src/main/scala/org/bykn/bosatsu/KindFormula.scala +++ b/core/src/main/scala/org/bykn/bosatsu/KindFormula.scala @@ -13,6 +13,7 @@ import org.bykn.bosatsu.rankn.{ ConstructorFn, Ref, RefSpace, + Type => RankNType, TypeEnv, DefinedType } @@ -254,13 +255,19 @@ object KindFormula { dts: List[DefinedType[Either[KnownShape, Kind.Arg]]] ): IorNec[Error, List[DefinedType[Kind.Arg]]] = dts - .foldM(List.empty[DefinedType[Kind.Arg]]) { (acc, dt) => - solveKind((imports, acc), dt) match { - case Validated.Valid(good) => Ior.Right(good :: acc) - case Validated.Invalid(errs) => Ior.Both(errs, acc) + .foldM((List.empty[DefinedType[Kind.Arg]], Set.empty[RankNType.TyConst])) { case (st @ (acc, failed), dt) => + if (dt.dependsOn.exists(failed)) { + // there was at least one failure already, just return and let that failure signal + Ior.Right(st) + } + else { + solveKind((imports, acc), dt) match { + case Validated.Valid(good) => Ior.Right((good :: acc, failed)) + case Validated.Invalid(errs) => Ior.Both(errs, (acc, failed + dt.toTypeTyConst)) + } } } - .map(_.reverse) + .map(_._1.reverse) def solveKind[Env: IsTypeEnv]( imports: Env, diff --git a/core/src/main/scala/org/bykn/bosatsu/rankn/DefinedType.scala b/core/src/main/scala/org/bykn/bosatsu/rankn/DefinedType.scala index 57718eee8..818f316f4 100644 --- a/core/src/main/scala/org/bykn/bosatsu/rankn/DefinedType.scala +++ b/core/src/main/scala/org/bykn/bosatsu/rankn/DefinedType.scala @@ -31,6 +31,13 @@ final case class DefinedType[+A]( val toTypeTyConst: Type.TyConst = Type.TyConst(toTypeConst) + lazy val dependsOn: List[Type.TyConst] = + Type.allConsts( + for { + cfn <- constructors + (_, t) <- cfn.args + } yield t + ) /** * A type with exactly one constructor is a struct */ diff --git a/core/src/main/scala/org/bykn/bosatsu/rankn/Type.scala b/core/src/main/scala/org/bykn/bosatsu/rankn/Type.scala index 49e08c837..6f6347dbb 100644 --- a/core/src/main/scala/org/bykn/bosatsu/rankn/Type.scala +++ b/core/src/main/scala/org/bykn/bosatsu/rankn/Type.scala @@ -168,7 +168,9 @@ object Type { case class TyApply(on: Rho, arg: Type) extends Rho { lazy val normalize: Rho = TyApply(on.normalize, arg.normalize) } - case class TyConst(tpe: Const) extends Leaf + case class TyConst(tpe: Const) extends Leaf { + def isPredef: Boolean = tpe.toDefined.packageName == PackageName.PredefName + } case class TyVar(toVar: Var) extends Leaf case class TyMeta(toMeta: Meta) extends Leaf @@ -382,6 +384,25 @@ object Type { case q: Quantified => rootConst(q.in) } + def allConsts(ts: List[Type]): List[TyConst] = { + @annotation.tailrec + def loop(ts: List[Type], acc: List[TyConst]): List[TyConst] = + ts match { + case (tyc@TyConst(_)) :: tail => + loop(tail, tyc :: acc) + case (TyVar(_) | TyMeta(_)) :: tail => + loop(tail, acc) + case TyApply(left, right) :: tail => + loop(left :: right :: tail, acc) + case (q: Quantified) :: tail => + loop(q.in :: tail, acc) + case Nil => + acc.reverse.distinct + } + + loop(ts, Nil) + } + object RootConst { def unapply(t: Type): Option[Type.TyConst] = rootConst(t) diff --git a/core/src/test/scala/org/bykn/bosatsu/KindFormulaTest.scala b/core/src/test/scala/org/bykn/bosatsu/KindFormulaTest.scala index 5908321e9..ccc0e3722 100644 --- a/core/src/test/scala/org/bykn/bosatsu/KindFormulaTest.scala +++ b/core/src/test/scala/org/bykn/bosatsu/KindFormulaTest.scala @@ -278,6 +278,18 @@ struct Bar(foo: Foo) test("recursion with type constructors is allowed") { allowed("""# struct Tree(root: a, children: f[Tree[a, f]]) +""") + } + + test("we don't throw when a previous kind fails") { + disallowed("""# +struct Foo[a: -*](get: a) +struct Bar[a](foo: Foo[a]) + +""") + disallowed("""# +struct Bar[a](foo: Foo[a]) + """) } }