From 5a15c86d0354d9e55e062e7ef5c95e188e3edf3d Mon Sep 17 00:00:00 2001 From: odersky Date: Thu, 18 Apr 2024 19:34:41 +0200 Subject: [PATCH 1/2] Allow @retains arguments to be context functions Suppress the creation of apply methods for these arguments Fixes #20231 --- compiler/src/dotty/tools/dotc/core/Definitions.scala | 1 + compiler/src/dotty/tools/dotc/typer/Typer.scala | 5 +++++ library/src/scala/annotation/retains.scala | 12 ++++++++++-- library/src/scala/annotation/retainsByName.scala | 2 +- tests/pos-custom-args/captures/i20231.scala | 4 ++++ 5 files changed, 21 insertions(+), 3 deletions(-) create mode 100644 tests/pos-custom-args/captures/i20231.scala diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 932a7d72d33e..e813786068a5 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -1056,6 +1056,7 @@ class Definitions { @tu lazy val RetainsAnnot: ClassSymbol = requiredClass("scala.annotation.retains") @tu lazy val RetainsCapAnnot: ClassSymbol = requiredClass("scala.annotation.retainsCap") @tu lazy val RetainsByNameAnnot: ClassSymbol = requiredClass("scala.annotation.retainsByName") + @tu lazy val RetainsArgAnnot: ClassSymbol = requiredClass("scala.annotation.retainsArg") @tu lazy val PublicInBinaryAnnot: ClassSymbol = requiredClass("scala.annotation.publicInBinary") @tu lazy val JavaRepeatableAnnot: ClassSymbol = requiredClass("java.lang.annotation.Repeatable") diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 9d0150f49a1f..9150ad6be392 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -4047,10 +4047,15 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer true } + def isRetainsArg(pt: Type) = pt match + case AnnotatedType(arg, annot) => annot.symbol == defn.RetainsArgAnnot + case _ => false + if (implicitFun || caseCompanion) && !isApplyProto(pt) && pt != SingletonTypeProto && pt != LhsProto + && !isRetainsArg(pt) && !ctx.mode.is(Mode.Pattern) && !tree.isInstanceOf[SplicePattern] && !ctx.isAfterTyper diff --git a/library/src/scala/annotation/retains.scala b/library/src/scala/annotation/retains.scala index 4fa14e635136..909adc13a1c2 100644 --- a/library/src/scala/annotation/retains.scala +++ b/library/src/scala/annotation/retains.scala @@ -12,12 +12,20 @@ package scala.annotation * non-standard capturing type syntax. */ @experimental -class retains(xs: Any*) extends annotation.StaticAnnotation +class retains(xs: (Any@retainsArg)*) extends annotation.StaticAnnotation -/** Equivalent in meaning to `@retains(cap)`, but consumes less bytecode. +/** Equivalent in meaning to `@retains(cap)`, but consumes less bytecode. */ @experimental class retainsCap() extends annotation.StaticAnnotation // This special case is needed to be able to load standard library modules without // cyclic reference errors. Specifically, load sequences involving IterableOnce. +/** Internal use, only for parameters of `retains` and `retainsByName`. + */ +@experimental +class retainsArg extends annotation.StaticAnnotation + // This annotation prevents argument references to retains and retainsByName from being + // augmented with explicit arguments. That's unsound in general, but necessary + // since a captureRef could have an impure context function type, A ?=> B, but + // we still need to have the unapplied captureRef in the annotation. diff --git a/library/src/scala/annotation/retainsByName.scala b/library/src/scala/annotation/retainsByName.scala index 421e0400c4e1..e6e3dafcb752 100644 --- a/library/src/scala/annotation/retainsByName.scala +++ b/library/src/scala/annotation/retainsByName.scala @@ -2,5 +2,5 @@ package scala.annotation /** An annotation that indicates capture of an enclosing by-name type */ -@experimental class retainsByName(xs: Any*) extends annotation.StaticAnnotation +@experimental class retainsByName(xs: (Any@retainsArg)*) extends annotation.StaticAnnotation diff --git a/tests/pos-custom-args/captures/i20231.scala b/tests/pos-custom-args/captures/i20231.scala new file mode 100644 index 000000000000..5557bc9929e6 --- /dev/null +++ b/tests/pos-custom-args/captures/i20231.scala @@ -0,0 +1,4 @@ +class Async +class C(val x: Async ?=> Unit) +def foo(x: Async ?=> Unit): C^{x} = C(x) +def foo(x: Async ?=> Unit)(using Async): C^{x} = C(x) \ No newline at end of file From 829e84a9da26b28379f2797edea3d457112e90bc Mon Sep 17 00:00:00 2001 From: odersky Date: Thu, 18 Apr 2024 19:42:33 +0200 Subject: [PATCH 2/2] Update stdLibExperimentalDefinitions --- tests/run-tasty-inspector/stdlibExperimentalDefinitions.scala | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/run-tasty-inspector/stdlibExperimentalDefinitions.scala b/tests/run-tasty-inspector/stdlibExperimentalDefinitions.scala index 407f789beb58..2d0081977435 100644 --- a/tests/run-tasty-inspector/stdlibExperimentalDefinitions.scala +++ b/tests/run-tasty-inspector/stdlibExperimentalDefinitions.scala @@ -30,6 +30,7 @@ val experimentalDefinitionInLibrary = Set( "scala.annotation.retains", "scala.annotation.retainsByName", "scala.annotation.retainsCap", + "scala.annotation.retainsArg", "scala.Pure", "scala.caps", "scala.caps$",