diff --git a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala index d1a88406fe45..ba717f2f26e7 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala @@ -85,7 +85,10 @@ object Inlines: if (tree.symbol == defn.CompiletimeTesting_typeChecks) return Intrinsics.typeChecks(tree) if (tree.symbol == defn.CompiletimeTesting_typeCheckErrors) return Intrinsics.typeCheckErrors(tree) - CrossVersionChecks.checkExperimentalRef(tree.symbol, tree.srcPos) + if ctx.isAfterTyper then + // During typer we wait with cross version checks until PostTyper, in order + // not to provoke cyclic references. See i16116 for a test case. + CrossVersionChecks.checkExperimentalRef(tree.symbol, tree.srcPos) if tree.symbol.isConstructor then return tree // error already reported for the inline constructor definition diff --git a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala index 3db751df4145..faa201ece7e3 100644 --- a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala +++ b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala @@ -359,6 +359,7 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase } case Inlined(call, bindings, expansion) if !call.isEmpty => val pos = call.sourcePos + CrossVersionChecks.checkExperimentalRef(call.symbol, pos) val callTrace = Inlines.inlineCallTrace(call.symbol, pos)(using ctx.withSource(pos.source)) cpy.Inlined(tree)(callTrace, transformSub(bindings), transform(expansion)(using inlineContext(call))) case templ: Template => diff --git a/tests/neg-custom-args/no-experimental/experimentalInline.scala b/tests/neg-custom-args/no-experimental/experimentalInline.scala index 8827fd42e36a..eb49bf15d11a 100644 --- a/tests/neg-custom-args/no-experimental/experimentalInline.scala +++ b/tests/neg-custom-args/no-experimental/experimentalInline.scala @@ -4,5 +4,5 @@ import scala.annotation.experimental inline def g() = () def test: Unit = - g() // errors + g() // error () diff --git a/tests/neg-custom-args/no-experimental/experimentalInline2.scala b/tests/neg-custom-args/no-experimental/experimentalInline2.scala new file mode 100644 index 000000000000..c40eb050a832 --- /dev/null +++ b/tests/neg-custom-args/no-experimental/experimentalInline2.scala @@ -0,0 +1,8 @@ +import scala.annotation.experimental + +@experimental +transparent inline def g() = () + +def test: Unit = + g() // error + () diff --git a/tests/pos-custom-args/captures/i16116.scala b/tests/pos-custom-args/captures/i16116.scala new file mode 100644 index 000000000000..2f5d5304dca5 --- /dev/null +++ b/tests/pos-custom-args/captures/i16116.scala @@ -0,0 +1,39 @@ +package x + +import scala.annotation.* +import scala.concurrent.* + +trait CpsMonad[F[_]] { + type Context +} + +object CpsMonad { + type Aux[F[_],C] = CpsMonad[F] { type Context = C } + given CpsMonad[Future] with {} +} + +@experimental +object Test { + + @capability + class CpsTransform[F[_]] { + def await[T](ft: F[T]): { this } T = ??? + } + + transparent inline def cpsAsync[F[_]](using m:CpsMonad[F]) = + new Test.InfernAsyncArg + + class InfernAsyncArg[F[_],C](using am:CpsMonad.Aux[F,C]) { + def apply[A](expr: (CpsTransform[F], C) ?=> A): F[A] = ??? + } + + def asyncPlus[F[_]](a:Int, b:F[Int])(using cps: CpsTransform[F]): { cps } Int = + a + (cps.await(b).asInstanceOf[Int]) + + def testExample1Future(): Unit = + val fr = cpsAsync[Future] { + val y = asyncPlus(1,Future successful 2).asInstanceOf[Int] + y+1 + } + +}