diff --git a/airframe-surface/src/main/scala-3/wvlet/airframe/surface/CompileTimeSurfaceFactory.scala b/airframe-surface/src/main/scala-3/wvlet/airframe/surface/CompileTimeSurfaceFactory.scala index 58831ffe9..197ae4993 100644 --- a/airframe-surface/src/main/scala-3/wvlet/airframe/surface/CompileTimeSurfaceFactory.scala +++ b/airframe-surface/src/main/scala-3/wvlet/airframe/surface/CompileTimeSurfaceFactory.scala @@ -703,9 +703,12 @@ private[surface] class CompileTimeSurfaceFactory[Q <: Quotes](using quotes: Q): .toSeq // Exclude primitive surfaces as it is already defined in Primitive object .filterNot(x => primitiveTypeFactory.isDefinedAt(x._1)) - .sortBy(_._2) + .map { (tpe, order) => + // first list all lazy vals, otherwise there is a risk of forward reference error across strict vals + (tpe, (!lazySurface.contains(tpe), order)) + }.sortBy(_._2) .reverse - .map { case (tpe, order) => + .foreach { (tpe, order) => // Update the cache so that the next call of surfaceOf method will use the local varaible reference surfaceToVar += tpe -> Symbol.newVal( Symbol.spliceOwner, diff --git a/airframe-surface/src/test/scala/wvlet/airframe/surface/i3451.scala b/airframe-surface/src/test/scala/wvlet/airframe/surface/i3451.scala new file mode 100644 index 000000000..65c15c6bc --- /dev/null +++ b/airframe-surface/src/test/scala/wvlet/airframe/surface/i3451.scala @@ -0,0 +1,40 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package wvlet.airframe.surface + +import wvlet.airspec.AirSpec + +object i3451 extends AirSpec { + + case class Cons(head: String, tail: Cons) + case class TypedCons[A](head: Int, tail: TypedCons[A]) + + trait Recursive[T <: Recursive[T]] + + case class E(x: Int) extends Recursive[E] { + def cons(c: Cons): Cons = c + def typedCons(c: TypedCons[_]): TypedCons[_] = c + def r3(r: Recursive[_]): Recursive[_] = r + } + + test("Support methods with lazy and non-lazy types mixed in any order") { + val s = Surface.of[E] + debug(s.params) + val m = Surface.methodsOf[E] + val names = m.map(_.name) + names shouldContain "cons" + names shouldContain "typedCons" + names shouldContain "r3" + } +}