diff --git a/compile/interface/src/main/scala/xsbt/ExtractAPI.scala b/compile/interface/src/main/scala/xsbt/ExtractAPI.scala index 9179c2c87..4575dc27e 100644 --- a/compile/interface/src/main/scala/xsbt/ExtractAPI.scala +++ b/compile/interface/src/main/scala/xsbt/ExtractAPI.scala @@ -336,14 +336,19 @@ class ExtractAPI[GlobalType <: CallbackGlobal](val global: GlobalType, private def removeConstructors(ds: List[Symbol]): List[Symbol] = ds filter { !_.isConstructor } - private def mkStructure(info: Type, s: Symbol, inherit: Boolean): xsbti.api.Structure = - { - val (declared, inherited) = info.members.toList.reverse.partition(_.owner == s) - val baseTypes = info.baseClasses.tail.map(info.baseType) - val ds = if (s.isModuleClass) removeConstructors(declared) else declared - val is = if (inherit) removeConstructors(inherited) else Nil - mkStructure(s, baseTypes, ds, is) - } + private def mkStructure(info: Type, s: Symbol, inherit: Boolean): xsbti.api.Structure = { + val (declared, inherited) = info.members.reverse.partition(_.owner == s) + // Note that the ordering of classes in `baseClasses` is important. + // It would be easier to just say `val baseTypes = baseTypeSeq`, but that does not seem + // to take linearization into account. + // Also, we take info.parents when we're not interested in the full linearization, + // which side steps issues with baseType when f-bounded existential types and refined types mix + // (and we get cyclic types which cause a stack overflow in showAPI) + val baseTypes = if (inherit) info.baseClasses.tail.map(info.baseType) else info.parents + val ds = if (s.isModuleClass) removeConstructors(declared) else declared + val is = if (inherit) removeConstructors(inherited) else Nil + mkStructure(s, baseTypes, ds, is) + } // If true, this template is publicly visible and should be processed as a public inheritance dependency. // Local classes and local refinements will never be traversed by the api phase, so we don't need to check for that. diff --git a/sbt/src/sbt-test/source-dependencies/fbounded-existentials/fbounds.scala b/sbt/src/sbt-test/source-dependencies/fbounded-existentials/fbounds.scala new file mode 100644 index 000000000..60fe40879 --- /dev/null +++ b/sbt/src/sbt-test/source-dependencies/fbounded-existentials/fbounds.scala @@ -0,0 +1,10 @@ +class Dep { + // The API representation for `bla`'s result type contains a cycle + // (an existential's type variable's bound is the existential type itself) + // This results in a stack overflow while showing the API diff. + // Note that the actual result type in the compiler is not cyclic + // (the f-bounded existential for Comparable is truncated) + def bla(c: Boolean) = if (c) new Value else "bla" +} + +class Value extends java.lang.Comparable[Value] { def compareTo(that: Value): Int = 1 } \ No newline at end of file diff --git a/sbt/src/sbt-test/source-dependencies/fbounded-existentials/test b/sbt/src/sbt-test/source-dependencies/fbounded-existentials/test new file mode 100644 index 000000000..5df2af1f3 --- /dev/null +++ b/sbt/src/sbt-test/source-dependencies/fbounded-existentials/test @@ -0,0 +1 @@ +> compile