From 264cf2e63b81c7b476f88208aed15faabfa7403a Mon Sep 17 00:00:00 2001 From: odersky Date: Sat, 6 May 2023 19:18:20 +0200 Subject: [PATCH 1/6] Make Pure a class in scala that gets erased The Pure marker trait is now a class in the scala package that gets erased to Object. --- .../dotty/tools/dotc/core/Definitions.scala | 22 +++++++++++-------- library/src/scala/Pure.scala | 9 ++++++++ library/src/scala/caps.scala | 6 ----- tests/neg-custom-args/captures/selftype.scala | 4 ++-- .../backend/jvm/BCodeHelpers.scala | 2 +- .../backend/jvm/BCodeIdiomatic.scala | 2 +- .../backend/jvm/BTypes.scala | 4 ++-- .../backend/sjs/ScopedVar.scala | 2 +- .../dotc/ast/Positioned.scala | 2 +- .../dotc/cc/CaptureSet.scala | 2 +- .../dotc/core/Annotations.scala | 2 +- .../dotc/core/CheckRealizable.scala | 2 +- .../dotc/core/Denotations.scala | 2 +- .../dotc/core/NameKinds.scala | 4 ++-- .../dotc/core/Names.scala | 2 +- .../dotc/core/Phases.scala | 2 +- .../dotc/core/Scopes.scala | 2 +- .../dotc/core/TypeErrors.scala | 2 +- .../dotc/core/Types.scala | 2 +- .../dotc/core/classfile/ClassfileParser.scala | 2 +- .../dotc/interactive/Completion.scala | 2 +- .../dotc/parsing/Scanners.scala | 2 +- .../dotc/printing/Highlighting.scala | 2 +- .../dotc/printing/Printer.scala | 2 +- .../dotc/profile/AsyncHelper.scala | 2 +- .../dotc/profile/Profiler.scala | 2 +- .../dotc/transform/CapturedVars.scala | 2 +- .../dotc/transform/CrossStageSafety.scala | 4 ++-- .../dotc/transform/ForwardDepChecks.scala | 2 +- .../dotc/transform/MacroTransform.scala | 2 +- .../dotc/transform/MegaPhase.scala | 2 +- .../dotc/transform/OverridingPairs.scala | 2 +- .../dotc/transform/PatternMatcher.scala | 2 +- .../dotc/transform/SymUtils.scala | 2 -- .../dotc/typer/Implicits.scala | 6 ++--- .../dotc/typer/ImportInfo.scala | 2 +- .../dotc/typer/ProtoTypes.scala | 8 +++---- .../dotc/typer/Synthesizer.scala | 2 +- .../dotc/util/ReadOnlyMap.scala | 2 +- .../dotc/util/ReadOnlySet.scala | 2 +- .../dotc/util/SourceFile.scala | 2 +- .../dotc/util/SourcePosition.scala | 2 +- .../run-custom-args/captures/minicheck.scala | 4 ++-- .../stdlibExperimentalDefinitions.scala | 1 + 44 files changed, 72 insertions(+), 66 deletions(-) create mode 100644 library/src/scala/Pure.scala diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 027aec16e9a3..3a1b4cad28a2 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -976,6 +976,8 @@ class Definitions { @tu lazy val Caps_unsafeBoxFunArg: Symbol = CapsUnsafeModule.requiredMethod("unsafeBoxFunArg") @tu lazy val Caps_SealedAnnot: ClassSymbol = requiredClass("scala.caps.Sealed") + @tu lazy val PureClass: Symbol = requiredClass("scala.Pure") + // Annotation base classes @tu lazy val AnnotationClass: ClassSymbol = requiredClass("scala.annotation.Annotation") @tu lazy val StaticAnnotationClass: ClassSymbol = requiredClass("scala.annotation.StaticAnnotation") @@ -2041,15 +2043,17 @@ class Definitions { def isValueSubClass(sym1: Symbol, sym2: Symbol): Boolean = valueTypeEnc(sym2.asClass.name) % valueTypeEnc(sym1.asClass.name) == 0 - @tu lazy val specialErasure: SimpleIdentityMap[Symbol, ClassSymbol] = - SimpleIdentityMap.empty[Symbol] - .updated(AnyClass, ObjectClass) - .updated(MatchableClass, ObjectClass) - .updated(AnyValClass, ObjectClass) - .updated(SingletonClass, ObjectClass) - .updated(TupleClass, ProductClass) - .updated(NonEmptyTupleClass, ProductClass) - .updated(PairClass, ObjectClass) + @tu lazy val specialErasure: collection.Map[Symbol, ClassSymbol] = + val m = mutable.Map[Symbol, ClassSymbol]() + m(AnyClass) = ObjectClass + m(MatchableClass) = ObjectClass + m(PureClass) = ObjectClass + m(AnyValClass) = ObjectClass + m(SingletonClass) = ObjectClass + m(TupleClass) = ProductClass + m(NonEmptyTupleClass) = ProductClass + m(PairClass) = ObjectClass + m // ----- Initialization --------------------------------------------------- diff --git a/library/src/scala/Pure.scala b/library/src/scala/Pure.scala new file mode 100644 index 000000000000..f3f624cd40d0 --- /dev/null +++ b/library/src/scala/Pure.scala @@ -0,0 +1,9 @@ +package scala +import annotation.experimental + +/** A marker trait that declares that all inheriting classes are "pure" in the + * sense that their values retain no capabilities including capabilities needed + * to perform effects. This has formal meaning only under capture checking. + */ +@experimental trait Pure: + this: Pure => diff --git a/library/src/scala/caps.scala b/library/src/scala/caps.scala index 866e5dbd18cd..e4520d48354c 100644 --- a/library/src/scala/caps.scala +++ b/library/src/scala/caps.scala @@ -40,9 +40,3 @@ import annotation.experimental */ @deprecated("The Sealed annotation should not be directly used in source code.\nUse the `sealed` modifier on type parameters instead.") class Sealed extends annotation.Annotation - - /** Mixing in this trait forces a trait or class to be pure, i.e. - * have no capabilities retained in its self type. - */ - trait Pure: - this: Pure => diff --git a/tests/neg-custom-args/captures/selftype.scala b/tests/neg-custom-args/captures/selftype.scala index 21148f625a7a..f5d24fd2381f 100644 --- a/tests/neg-custom-args/captures/selftype.scala +++ b/tests/neg-custom-args/captures/selftype.scala @@ -1,4 +1,4 @@ -@annotation.experimental class C(x: () => Unit) extends caps.Pure // error +@annotation.experimental class C(x: () => Unit) extends Pure // error -@annotation.experimental class D(@annotation.constructorOnly x: () => Unit) extends caps.Pure // ok +@annotation.experimental class D(@annotation.constructorOnly x: () => Unit) extends Pure // ok diff --git a/tests/pos-with-compiler-cc/backend/jvm/BCodeHelpers.scala b/tests/pos-with-compiler-cc/backend/jvm/BCodeHelpers.scala index 6f83af540bea..3ab75bda787e 100644 --- a/tests/pos-with-compiler-cc/backend/jvm/BCodeHelpers.scala +++ b/tests/pos-with-compiler-cc/backend/jvm/BCodeHelpers.scala @@ -209,7 +209,7 @@ trait BCodeHelpers extends BCodeIdiomatic with BytecodeWriters { } } // end of trait BCPickles - trait BCInnerClassGen extends caps.Pure { + trait BCInnerClassGen extends Pure { def debugLevel = 3 // 0 -> no debug info; 1-> filename; 2-> lines; 3-> varnames diff --git a/tests/pos-with-compiler-cc/backend/jvm/BCodeIdiomatic.scala b/tests/pos-with-compiler-cc/backend/jvm/BCodeIdiomatic.scala index 5eb8d7a52aa2..2218adf6e18b 100644 --- a/tests/pos-with-compiler-cc/backend/jvm/BCodeIdiomatic.scala +++ b/tests/pos-with-compiler-cc/backend/jvm/BCodeIdiomatic.scala @@ -17,7 +17,7 @@ import dotty.tools.dotc.report * @version 1.0 * */ -trait BCodeIdiomatic extends caps.Pure { +trait BCodeIdiomatic extends Pure { val int: DottyBackendInterface final lazy val bTypes = new BTypesFromSymbols[int.type](int) diff --git a/tests/pos-with-compiler-cc/backend/jvm/BTypes.scala b/tests/pos-with-compiler-cc/backend/jvm/BTypes.scala index dda85e2d5616..f9a3a3aae105 100644 --- a/tests/pos-with-compiler-cc/backend/jvm/BTypes.scala +++ b/tests/pos-with-compiler-cc/backend/jvm/BTypes.scala @@ -14,7 +14,7 @@ import scala.tools.asm * This representation is immutable and independent of the compiler data structures, hence it can * be queried by concurrent threads. */ -abstract class BTypes extends caps.Pure { +abstract class BTypes extends Pure { val int: DottyBackendInterface import int.given @@ -47,7 +47,7 @@ abstract class BTypes extends caps.Pure { * A BType is either a primitve type, a ClassBType, an ArrayBType of one of these, or a MethodType * referring to BTypes. */ - /*sealed*/ trait BType extends caps.Pure { // Not sealed for now due to SI-8546 + /*sealed*/ trait BType extends Pure { // Not sealed for now due to SI-8546 final override def toString: String = this match { case UNIT => "V" case BOOL => "Z" diff --git a/tests/pos-with-compiler-cc/backend/sjs/ScopedVar.scala b/tests/pos-with-compiler-cc/backend/sjs/ScopedVar.scala index 21462929833c..af7570a6edca 100644 --- a/tests/pos-with-compiler-cc/backend/sjs/ScopedVar.scala +++ b/tests/pos-with-compiler-cc/backend/sjs/ScopedVar.scala @@ -1,6 +1,6 @@ package dotty.tools.backend.sjs -class ScopedVar[A](init: A) extends caps.Pure { +class ScopedVar[A](init: A) extends Pure { import ScopedVar.Assignment private[ScopedVar] var value = init diff --git a/tests/pos-with-compiler-cc/dotc/ast/Positioned.scala b/tests/pos-with-compiler-cc/dotc/ast/Positioned.scala index fd30d441a6ee..7b558c65e425 100644 --- a/tests/pos-with-compiler-cc/dotc/ast/Positioned.scala +++ b/tests/pos-with-compiler-cc/dotc/ast/Positioned.scala @@ -15,7 +15,7 @@ import annotation.internal.sharable /** A base class for things that have positions (currently: modifiers and trees) */ -abstract class Positioned(implicit @constructorOnly src: SourceFile) extends SrcPos, Product, Cloneable, caps.Pure { +abstract class Positioned(implicit @constructorOnly src: SourceFile) extends SrcPos, Product, Cloneable, Pure { import Positioned.{ids, nextId, debugId} private var mySpan: Span = _ diff --git a/tests/pos-with-compiler-cc/dotc/cc/CaptureSet.scala b/tests/pos-with-compiler-cc/dotc/cc/CaptureSet.scala index c31bcb76c2c7..2072b43089fb 100644 --- a/tests/pos-with-compiler-cc/dotc/cc/CaptureSet.scala +++ b/tests/pos-with-compiler-cc/dotc/cc/CaptureSet.scala @@ -39,7 +39,7 @@ import annotation.retains * if the mapped function is either a bijection or if it is idempotent * on capture references (c.f. doc comment on `map` below). */ -sealed abstract class CaptureSet extends Showable, caps.Pure: +sealed abstract class CaptureSet extends Showable, Pure: import CaptureSet.* /** The elements of this capture set. For capture variables, diff --git a/tests/pos-with-compiler-cc/dotc/core/Annotations.scala b/tests/pos-with-compiler-cc/dotc/core/Annotations.scala index f307d4a36697..2061bddb9e8a 100644 --- a/tests/pos-with-compiler-cc/dotc/core/Annotations.scala +++ b/tests/pos-with-compiler-cc/dotc/core/Annotations.scala @@ -17,7 +17,7 @@ object Annotations { if (tree.symbol.isConstructor) tree.symbol.owner else tree.tpe.typeSymbol - abstract class Annotation extends Showable, caps.Pure { + abstract class Annotation extends Showable, Pure { def tree(using Context): Tree diff --git a/tests/pos-with-compiler-cc/dotc/core/CheckRealizable.scala b/tests/pos-with-compiler-cc/dotc/core/CheckRealizable.scala index 47fa84b467d8..d166cec11573 100644 --- a/tests/pos-with-compiler-cc/dotc/core/CheckRealizable.scala +++ b/tests/pos-with-compiler-cc/dotc/core/CheckRealizable.scala @@ -13,7 +13,7 @@ import annotation.constructorOnly /** Realizability status */ object CheckRealizable { - sealed abstract class Realizability(val msg: String) extends caps.Pure { + sealed abstract class Realizability(val msg: String) extends Pure { def andAlso(other: => Realizability): Realizability = if (this == Realizable) other else this def mapError(f: Realizability -> Context ?-> Realizability)(using Context): Realizability = diff --git a/tests/pos-with-compiler-cc/dotc/core/Denotations.scala b/tests/pos-with-compiler-cc/dotc/core/Denotations.scala index 246e359f0597..9db285975a0a 100644 --- a/tests/pos-with-compiler-cc/dotc/core/Denotations.scala +++ b/tests/pos-with-compiler-cc/dotc/core/Denotations.scala @@ -76,7 +76,7 @@ object Denotations { /** A PreDenotation represents a group of single denotations or a single multi-denotation * It is used as an optimization to avoid forming MultiDenotations too eagerly. */ - abstract class PreDenotation extends caps.Pure { + abstract class PreDenotation extends Pure { /** A denotation in the group exists */ def exists: Boolean diff --git a/tests/pos-with-compiler-cc/dotc/core/NameKinds.scala b/tests/pos-with-compiler-cc/dotc/core/NameKinds.scala index 2ed9a17b9f7e..07c69afdaf24 100644 --- a/tests/pos-with-compiler-cc/dotc/core/NameKinds.scala +++ b/tests/pos-with-compiler-cc/dotc/core/NameKinds.scala @@ -23,14 +23,14 @@ object NameKinds { @sharable private val uniqueNameKinds = util.HashMap[String, UniqueNameKind]() /** A class for the info stored in a derived name */ - abstract class NameInfo extends caps.Pure { + abstract class NameInfo extends Pure { def kind: NameKind def mkString(underlying: TermName): String def map(f: SimpleName => SimpleName): NameInfo = this } /** An abstract base class of classes that define the kind of a derived name info */ - abstract class NameKind(val tag: Int) extends caps.Pure { self => + abstract class NameKind(val tag: Int) extends Pure { self => /** The info class defined by this kind */ type ThisInfo <: Info diff --git a/tests/pos-with-compiler-cc/dotc/core/Names.scala b/tests/pos-with-compiler-cc/dotc/core/Names.scala index e6ea66f4025b..d1eba69c57a0 100644 --- a/tests/pos-with-compiler-cc/dotc/core/Names.scala +++ b/tests/pos-with-compiler-cc/dotc/core/Names.scala @@ -32,7 +32,7 @@ object Names { * in a name table. A derived term name adds a tag, and possibly a number * or a further simple name to some other name. */ - abstract class Name extends Designator, Showable, caps.Pure derives CanEqual { + abstract class Name extends Designator, Showable, Pure derives CanEqual { /** A type for names of the same kind as this name */ type ThisName <: Name diff --git a/tests/pos-with-compiler-cc/dotc/core/Phases.scala b/tests/pos-with-compiler-cc/dotc/core/Phases.scala index 3744b1f21122..809e24b1e6ef 100644 --- a/tests/pos-with-compiler-cc/dotc/core/Phases.scala +++ b/tests/pos-with-compiler-cc/dotc/core/Phases.scala @@ -285,7 +285,7 @@ object Phases { final def isTyper(phase: Phase): Boolean = phase.id == typerPhase.id } - abstract class Phase extends caps.Pure { + abstract class Phase extends Pure { /** A name given to the `Phase` that can be used to debug the compiler. For * instance, it is possible to print trees after a given phase using: diff --git a/tests/pos-with-compiler-cc/dotc/core/Scopes.scala b/tests/pos-with-compiler-cc/dotc/core/Scopes.scala index 7ab68ddf78a2..f5a108a13c19 100644 --- a/tests/pos-with-compiler-cc/dotc/core/Scopes.scala +++ b/tests/pos-with-compiler-cc/dotc/core/Scopes.scala @@ -60,7 +60,7 @@ object Scopes { * or to delete them. These methods are provided by subclass * MutableScope. */ - abstract class Scope extends printing.Showable, caps.Pure { + abstract class Scope extends printing.Showable, Pure { /** The last scope-entry from which all others are reachable via `prev` */ private[dotc] def lastEntry: ScopeEntry | Null diff --git a/tests/pos-with-compiler-cc/dotc/core/TypeErrors.scala b/tests/pos-with-compiler-cc/dotc/core/TypeErrors.scala index 24f281b36785..43eec81467d4 100644 --- a/tests/pos-with-compiler-cc/dotc/core/TypeErrors.scala +++ b/tests/pos-with-compiler-cc/dotc/core/TypeErrors.scala @@ -15,7 +15,7 @@ import ast.untpd import config.Printers.cyclicErrors import language.experimental.pureFunctions -abstract class TypeError(using creationContext: DetachedContext) extends Exception(""), caps.Pure: +abstract class TypeError(using creationContext: DetachedContext) extends Exception(""), Pure: /** Convert to message. This takes an additional Context, so that we * use the context when the message is first produced, i.e. when the TypeError diff --git a/tests/pos-with-compiler-cc/dotc/core/Types.scala b/tests/pos-with-compiler-cc/dotc/core/Types.scala index e4b30888a5dc..90f2e322ac1c 100644 --- a/tests/pos-with-compiler-cc/dotc/core/Types.scala +++ b/tests/pos-with-compiler-cc/dotc/core/Types.scala @@ -91,7 +91,7 @@ object Types { * * Note: please keep in sync with copy in `docs/docs/internals/type-system.md`. */ - abstract class Type extends Hashable, printing.Showable, caps.Pure { + abstract class Type extends Hashable, printing.Showable, Pure { // ----- Tests ----------------------------------------------------- diff --git a/tests/pos-with-compiler-cc/dotc/core/classfile/ClassfileParser.scala b/tests/pos-with-compiler-cc/dotc/core/classfile/ClassfileParser.scala index 7e369dd6d9cc..21140f437114 100644 --- a/tests/pos-with-compiler-cc/dotc/core/classfile/ClassfileParser.scala +++ b/tests/pos-with-compiler-cc/dotc/core/classfile/ClassfileParser.scala @@ -28,7 +28,7 @@ import annotation.retains object ClassfileParser { /** Marker trait for unpicklers that can be embedded in classfiles. */ - trait Embedded extends caps.Pure + trait Embedded extends Pure /** Indicate that there is nothing to unpickle and the corresponding symbols can * be invalidated. */ diff --git a/tests/pos-with-compiler-cc/dotc/interactive/Completion.scala b/tests/pos-with-compiler-cc/dotc/interactive/Completion.scala index 6a3ac439ef5f..ead3e6c413c9 100644 --- a/tests/pos-with-compiler-cc/dotc/interactive/Completion.scala +++ b/tests/pos-with-compiler-cc/dotc/interactive/Completion.scala @@ -538,7 +538,7 @@ object Completion { /** Temporary data structure representing denotations with the same name introduced in a given scope * as a member of a type, by a local definition or by an import clause */ - private case class ScopedDenotations(denots: Seq[SingleDenotation], ctx: DetachedContext) extends caps.Pure + private case class ScopedDenotations(denots: Seq[SingleDenotation], ctx: DetachedContext) extends Pure /** * The completion mode: defines what kinds of symbols should be included in the completion diff --git a/tests/pos-with-compiler-cc/dotc/parsing/Scanners.scala b/tests/pos-with-compiler-cc/dotc/parsing/Scanners.scala index 90c11e4430ec..737a37b2d4ce 100644 --- a/tests/pos-with-compiler-cc/dotc/parsing/Scanners.scala +++ b/tests/pos-with-compiler-cc/dotc/parsing/Scanners.scala @@ -1554,7 +1554,7 @@ object Scanners { * InBraces a pair of braces { ... } * Indented a pair of ... tokens */ - abstract class Region(val closedBy: Token) extends caps.Pure: + abstract class Region(val closedBy: Token) extends Pure: /** The region enclosing this one, or `null` for the outermost region */ def outer: Region | Null diff --git a/tests/pos-with-compiler-cc/dotc/printing/Highlighting.scala b/tests/pos-with-compiler-cc/dotc/printing/Highlighting.scala index b5fbbfdf1b3a..018bf65b851d 100644 --- a/tests/pos-with-compiler-cc/dotc/printing/Highlighting.scala +++ b/tests/pos-with-compiler-cc/dotc/printing/Highlighting.scala @@ -28,7 +28,7 @@ object Highlighting { else mod + super.show } - case class HighlightBuffer(hl: Highlight)(using DetachedContext) extends caps.Pure { + case class HighlightBuffer(hl: Highlight)(using DetachedContext) extends Pure { private val buffer = new mutable.ListBuffer[String] buffer += hl.show diff --git a/tests/pos-with-compiler-cc/dotc/printing/Printer.scala b/tests/pos-with-compiler-cc/dotc/printing/Printer.scala index 25429c8fc01b..b9da874cf9ae 100644 --- a/tests/pos-with-compiler-cc/dotc/printing/Printer.scala +++ b/tests/pos-with-compiler-cc/dotc/printing/Printer.scala @@ -15,7 +15,7 @@ import scala.annotation.internal.sharable /** The base class of all printers */ -abstract class Printer extends caps.Pure { +abstract class Printer extends Pure { private var prec: Precedence = GlobalPrec diff --git a/tests/pos-with-compiler-cc/dotc/profile/AsyncHelper.scala b/tests/pos-with-compiler-cc/dotc/profile/AsyncHelper.scala index deb8e172a780..1c52e47ade7e 100644 --- a/tests/pos-with-compiler-cc/dotc/profile/AsyncHelper.scala +++ b/tests/pos-with-compiler-cc/dotc/profile/AsyncHelper.scala @@ -9,7 +9,7 @@ import java.util.concurrent.atomic.AtomicInteger import dotty.tools.dotc.core.Phases.Phase import dotty.tools.dotc.core.Contexts._ -sealed trait AsyncHelper extends caps.Pure { +sealed trait AsyncHelper extends Pure { def newUnboundedQueueFixedThreadPool (nThreads: Int, diff --git a/tests/pos-with-compiler-cc/dotc/profile/Profiler.scala b/tests/pos-with-compiler-cc/dotc/profile/Profiler.scala index bb4f9ffe226b..35c98ac2a0d2 100644 --- a/tests/pos-with-compiler-cc/dotc/profile/Profiler.scala +++ b/tests/pos-with-compiler-cc/dotc/profile/Profiler.scala @@ -65,7 +65,7 @@ case class ProfileRange(start: ProfileSnap, end:ProfileSnap, phase:Phase, purpos def retainedHeapMB: Double = toMegaBytes(end.heapBytes - start.heapBytes) } -sealed trait Profiler extends caps.Pure { +sealed trait Profiler extends Pure { def finished(): Unit diff --git a/tests/pos-with-compiler-cc/dotc/transform/CapturedVars.scala b/tests/pos-with-compiler-cc/dotc/transform/CapturedVars.scala index 3aaf4a23ec1c..f3b3421b8356 100644 --- a/tests/pos-with-compiler-cc/dotc/transform/CapturedVars.scala +++ b/tests/pos-with-compiler-cc/dotc/transform/CapturedVars.scala @@ -34,7 +34,7 @@ class CapturedVars extends MiniPhase with IdentityDenotTransformer: override def initContext(ctx: FreshContext): Unit = Captured = ctx.addLocation(util.ReadOnlySet.empty) - private class RefInfo(using DetachedContext) extends caps.Pure { + private class RefInfo(using DetachedContext) extends Pure { /** The classes for which a Ref type exists. */ val refClassKeys: collection.Set[Symbol] = defn.ScalaNumericValueClasses() `union` Set(defn.BooleanClass, defn.ObjectClass) diff --git a/tests/pos-with-compiler-cc/dotc/transform/CrossStageSafety.scala b/tests/pos-with-compiler-cc/dotc/transform/CrossStageSafety.scala index ca00c87161ef..eba5e0071c01 100644 --- a/tests/pos-with-compiler-cc/dotc/transform/CrossStageSafety.scala +++ b/tests/pos-with-compiler-cc/dotc/transform/CrossStageSafety.scala @@ -48,7 +48,7 @@ import scala.annotation.constructorOnly * } * */ -class CrossStageSafety(@constructorOnly ictx: DetachedContext) extends TreeMapWithStages(ictx), Checking, caps.Pure { +class CrossStageSafety(@constructorOnly ictx: DetachedContext) extends TreeMapWithStages(ictx), Checking, Pure { import tpd._ private val InAnnotation = Property.Key[Unit]() @@ -278,7 +278,7 @@ class CrossStageSafety(@constructorOnly ictx: DetachedContext) extends TreeMapWi object CrossStageSafety { import tpd._ - class QuoteTypeTags(span: Span)(using DetachedContext) extends caps.Pure { + class QuoteTypeTags(span: Span)(using DetachedContext) extends Pure { private val tags = collection.mutable.LinkedHashMap.empty[Symbol, TypeDef] diff --git a/tests/pos-with-compiler-cc/dotc/transform/ForwardDepChecks.scala b/tests/pos-with-compiler-cc/dotc/transform/ForwardDepChecks.scala index ada633c04626..8fa0956bed89 100644 --- a/tests/pos-with-compiler-cc/dotc/transform/ForwardDepChecks.scala +++ b/tests/pos-with-compiler-cc/dotc/transform/ForwardDepChecks.scala @@ -26,7 +26,7 @@ object ForwardDepChecks: /** A class to help in forward reference checking */ class LevelInfo(val outer: OptLevelInfo, val owner: Symbol, stats: List[Tree])(using DetachedContext) - extends OptLevelInfo, caps.Pure { + extends OptLevelInfo, Pure { override val levelAndIndex: LevelAndIndex = stats.foldLeft(outer.levelAndIndex, 0) {(mi, stat) => val (m, idx) = mi diff --git a/tests/pos-with-compiler-cc/dotc/transform/MacroTransform.scala b/tests/pos-with-compiler-cc/dotc/transform/MacroTransform.scala index bff0e8340c0b..14ccdc5c6f86 100644 --- a/tests/pos-with-compiler-cc/dotc/transform/MacroTransform.scala +++ b/tests/pos-with-compiler-cc/dotc/transform/MacroTransform.scala @@ -9,7 +9,7 @@ import Contexts._ /** A base class for transforms. * A transform contains a compiler phase which applies a tree transformer. */ -abstract class MacroTransform extends Phase, caps.Pure { +abstract class MacroTransform extends Phase, Pure { import ast.tpd._ diff --git a/tests/pos-with-compiler-cc/dotc/transform/MegaPhase.scala b/tests/pos-with-compiler-cc/dotc/transform/MegaPhase.scala index 2543a89af4d7..14dd7f17d58a 100644 --- a/tests/pos-with-compiler-cc/dotc/transform/MegaPhase.scala +++ b/tests/pos-with-compiler-cc/dotc/transform/MegaPhase.scala @@ -28,7 +28,7 @@ object MegaPhase { * - Other: to prepape/transform a tree that does not have a specific prepare/transform * method pair. */ - abstract class MiniPhase extends Phase, caps.Pure { + abstract class MiniPhase extends Phase, Pure { private[MegaPhase] var superPhase: MegaPhase = _ private[MegaPhase] var idxInGroup: Int = _ diff --git a/tests/pos-with-compiler-cc/dotc/transform/OverridingPairs.scala b/tests/pos-with-compiler-cc/dotc/transform/OverridingPairs.scala index 5db2872e73e5..9e02b291b14e 100644 --- a/tests/pos-with-compiler-cc/dotc/transform/OverridingPairs.scala +++ b/tests/pos-with-compiler-cc/dotc/transform/OverridingPairs.scala @@ -22,7 +22,7 @@ object OverridingPairs: /** The cursor class * @param base the base class that contains the overriding pairs */ - class Cursor(base: Symbol)(using DetachedContext) extends caps.Pure: + class Cursor(base: Symbol)(using DetachedContext) extends Pure: private val self = base.thisType diff --git a/tests/pos-with-compiler-cc/dotc/transform/PatternMatcher.scala b/tests/pos-with-compiler-cc/dotc/transform/PatternMatcher.scala index a93545aec606..b1b268634736 100644 --- a/tests/pos-with-compiler-cc/dotc/transform/PatternMatcher.scala +++ b/tests/pos-with-compiler-cc/dotc/transform/PatternMatcher.scala @@ -145,7 +145,7 @@ object PatternMatcher { private var nxId = 0 /** The different kinds of plans */ - sealed abstract class Plan extends caps.Pure { val id: Int = nxId; nxId += 1 } + sealed abstract class Plan extends Pure { val id: Int = nxId; nxId += 1 } case class TestPlan(test: Test, var scrutinee: Tree, span: Span, var onSuccess: Plan) extends Plan { diff --git a/tests/pos-with-compiler-cc/dotc/transform/SymUtils.scala b/tests/pos-with-compiler-cc/dotc/transform/SymUtils.scala index 6010fe2a2a44..0a6fa9217303 100644 --- a/tests/pos-with-compiler-cc/dotc/transform/SymUtils.scala +++ b/tests/pos-with-compiler-cc/dotc/transform/SymUtils.scala @@ -363,8 +363,6 @@ object SymUtils: self.hasAnnotation(defn.ExperimentalAnnot) || isDefaultArgumentOfExperimentalMethod || (!self.is(Package) && self.owner.isInExperimentalScope) - || self.topLevelClass.ownersIterator.exists(p => - p.is(Package) && p.owner.isRoot && p.name == tpnme.dotty) /** The declared self type of this class, as seen from `site`, stripping * all refinements for opaque types. diff --git a/tests/pos-with-compiler-cc/dotc/typer/Implicits.scala b/tests/pos-with-compiler-cc/dotc/typer/Implicits.scala index 6034b10bf6f3..1f7286b02b5a 100644 --- a/tests/pos-with-compiler-cc/dotc/typer/Implicits.scala +++ b/tests/pos-with-compiler-cc/dotc/typer/Implicits.scala @@ -84,7 +84,7 @@ object Implicits: /** A common base class of contextual implicits and of-type implicits which * represents a set of references to implicit definitions. */ - abstract class ImplicitRefs(initctx: DetachedContext) extends caps.Pure { + abstract class ImplicitRefs(initctx: DetachedContext) extends Pure { val irefCtx: DetachedContext = if (initctx eq NoContext) initctx else initctx.retractMode(Mode.ImplicitsEnabled).detach protected given Context = irefCtx @@ -1651,7 +1651,7 @@ end Implicits * recursive references and emit a complete implicit dictionary when the outermost search * is complete. */ -abstract class SearchHistory extends caps.Pure: +abstract class SearchHistory extends Pure: val root: SearchRoot /** Does this search history contain any by name implicit arguments. */ val byname: Boolean @@ -1869,7 +1869,7 @@ final class SearchRoot extends SearchHistory: end SearchRoot /** A set of term references where equality is =:= */ -sealed class TermRefSet(using DetachedContext) extends caps.Pure: +sealed class TermRefSet(using DetachedContext) extends Pure: private val elemsMap = new util.HashMap[TermSymbol, Type | List[Type]] private val elemsBuf = new mutable.ListBuffer[TermSymbol] diff --git a/tests/pos-with-compiler-cc/dotc/typer/ImportInfo.scala b/tests/pos-with-compiler-cc/dotc/typer/ImportInfo.scala index 3c817f054ac6..8f4ab5de30cb 100644 --- a/tests/pos-with-compiler-cc/dotc/typer/ImportInfo.scala +++ b/tests/pos-with-compiler-cc/dotc/typer/ImportInfo.scala @@ -53,7 +53,7 @@ object ImportInfo { class ImportInfo(symf: Context ?-> Symbol, val selectors: List[untpd.ImportSelector], val qualifier: untpd.Tree, - val isRootImport: Boolean = false) extends Showable, caps.Pure { + val isRootImport: Boolean = false) extends Showable, Pure { private def symNameOpt = qualifier match { case ref: untpd.RefTree => Some(ref.name.asTermName) diff --git a/tests/pos-with-compiler-cc/dotc/typer/ProtoTypes.scala b/tests/pos-with-compiler-cc/dotc/typer/ProtoTypes.scala index 77fd2c1d6d66..285f9b983cb4 100644 --- a/tests/pos-with-compiler-cc/dotc/typer/ProtoTypes.scala +++ b/tests/pos-with-compiler-cc/dotc/typer/ProtoTypes.scala @@ -123,7 +123,7 @@ object ProtoTypes { } /** A trait for prototypes that match all types */ - trait MatchAlways extends ProtoType, caps.Pure { + trait MatchAlways extends ProtoType, Pure { def isMatchedBy(tp1: Type, keepConstraint: Boolean)(using Context): Boolean = true def map(tm: TypeMap @retains(caps.cap))(using Context): ProtoType = this def fold[T](x: T, ta: TypeAccumulator[T] @retains(caps.cap))(using Context): T = x @@ -131,7 +131,7 @@ object ProtoTypes { } /** A class marking ignored prototypes that can be revealed by `deepenProto` */ - abstract case class IgnoredProto(ignored: Type) extends CachedGroundType, MatchAlways, caps.Pure: + abstract case class IgnoredProto(ignored: Type) extends CachedGroundType, MatchAlways, Pure: private var myWasDeepened = false override def revealIgnored = ignored override def deepenProto(using Context): Type = @@ -165,7 +165,7 @@ object ProtoTypes { * [ ].name: proto */ abstract case class SelectionProto(name: Name, memberProto: Type, compat: Compatibility, privateOK: Boolean) - extends CachedProxyType, ProtoType, ValueTypeOrProto, caps.Pure { + extends CachedProxyType, ProtoType, ValueTypeOrProto, Pure { /** Is the set of members of this type unknown, in the sense that we * cannot compute a non-trivial upper approximation? This is the case if: @@ -575,7 +575,7 @@ object ProtoTypes { * []: argType => resultType */ abstract case class ViewProto(argType: Type, resType: Type) - extends CachedGroundType, ApplyingProto, caps.Pure { + extends CachedGroundType, ApplyingProto, Pure { override def resultType(using Context): Type = resType diff --git a/tests/pos-with-compiler-cc/dotc/typer/Synthesizer.scala b/tests/pos-with-compiler-cc/dotc/typer/Synthesizer.scala index 7bc9619922db..6ead3134235a 100644 --- a/tests/pos-with-compiler-cc/dotc/typer/Synthesizer.scala +++ b/tests/pos-with-compiler-cc/dotc/typer/Synthesizer.scala @@ -596,7 +596,7 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context): case JavaArrayType(elemTp) => defn.ArrayOf(escapeJavaArray(elemTp)) case _ => tp - private enum ManifestKind extends caps.Pure: // !cc! should all enums be Pure? + private enum ManifestKind extends Pure: // !cc! should all enums be Pure? case Full, Opt, Clss /** The kind that should be used for an array element, if we are `OptManifest` then this diff --git a/tests/pos-with-compiler-cc/dotc/util/ReadOnlyMap.scala b/tests/pos-with-compiler-cc/dotc/util/ReadOnlyMap.scala index d24a9ab3ddb2..dbef59d6dee7 100644 --- a/tests/pos-with-compiler-cc/dotc/util/ReadOnlyMap.scala +++ b/tests/pos-with-compiler-cc/dotc/util/ReadOnlyMap.scala @@ -3,7 +3,7 @@ package dotc.util /** A class for the reading part of mutable or immutable maps. */ -abstract class ReadOnlyMap[Key, Value] extends caps.Pure: +abstract class ReadOnlyMap[Key, Value] extends Pure: def lookup(x: Key): Value | Null diff --git a/tests/pos-with-compiler-cc/dotc/util/ReadOnlySet.scala b/tests/pos-with-compiler-cc/dotc/util/ReadOnlySet.scala index 318a04e846fe..2ab59c4c6cb6 100644 --- a/tests/pos-with-compiler-cc/dotc/util/ReadOnlySet.scala +++ b/tests/pos-with-compiler-cc/dotc/util/ReadOnlySet.scala @@ -2,7 +2,7 @@ package dotty.tools.dotc.util /** A class for the readonly part of mutable sets. */ -abstract class ReadOnlySet[T] extends caps.Pure: +abstract class ReadOnlySet[T] extends Pure: /** The entry in the set such that `isEqual(x, entry)`, or else `null`. */ def lookup(x: T): T | Null diff --git a/tests/pos-with-compiler-cc/dotc/util/SourceFile.scala b/tests/pos-with-compiler-cc/dotc/util/SourceFile.scala index 8a5a4828adfd..351cf4c8bf85 100644 --- a/tests/pos-with-compiler-cc/dotc/util/SourceFile.scala +++ b/tests/pos-with-compiler-cc/dotc/util/SourceFile.scala @@ -60,7 +60,7 @@ object ScriptSourceFile { } } -class SourceFile(val file: AbstractFile, computeContent: -> Array[Char]) extends interfaces.SourceFile, caps.Pure { +class SourceFile(val file: AbstractFile, computeContent: -> Array[Char]) extends interfaces.SourceFile, Pure { import SourceFile._ private var myContent: Array[Char] | Null = null diff --git a/tests/pos-with-compiler-cc/dotc/util/SourcePosition.scala b/tests/pos-with-compiler-cc/dotc/util/SourcePosition.scala index ef4350741036..7ab565d58533 100644 --- a/tests/pos-with-compiler-cc/dotc/util/SourcePosition.scala +++ b/tests/pos-with-compiler-cc/dotc/util/SourcePosition.scala @@ -12,7 +12,7 @@ import scala.annotation.internal.sharable /** A source position is comprised of a span and a source file */ case class SourcePosition(source: SourceFile, span: Span, outer: SourcePosition = NoSourcePosition) -extends SrcPos, interfaces.SourcePosition, Showable, caps.Pure { +extends SrcPos, interfaces.SourcePosition, Showable, Pure { def sourcePos(using Context) = this diff --git a/tests/run-custom-args/captures/minicheck.scala b/tests/run-custom-args/captures/minicheck.scala index bdc591580482..a6aca38ae704 100644 --- a/tests/run-custom-args/captures/minicheck.scala +++ b/tests/run-custom-args/captures/minicheck.scala @@ -5,7 +5,7 @@ import annotation.{experimental, tailrec, constructorOnly} import collection.mutable import language.`3.3` -case class Symbol(name: String, initOwner: Symbol | Null) extends caps.Pure: +case class Symbol(name: String, initOwner: Symbol | Null) extends Pure: def owner = initOwner.nn private var myInfo: Type = uninitialized def infoOrCompleter: Type = myInfo @@ -29,7 +29,7 @@ object NoSymbol extends Symbol("", null): override def exists: Boolean = false override def orElse(alt: => Symbol): Symbol = alt -abstract class Type extends caps.Pure: +abstract class Type extends Pure: def exists = true def show: String case class IntType()(using @constructorOnly c: Context) extends Type: diff --git a/tests/run-custom-args/tasty-inspector/stdlibExperimentalDefinitions.scala b/tests/run-custom-args/tasty-inspector/stdlibExperimentalDefinitions.scala index 644efb54c32e..1eb2ce3beee0 100644 --- a/tests/run-custom-args/tasty-inspector/stdlibExperimentalDefinitions.scala +++ b/tests/run-custom-args/tasty-inspector/stdlibExperimentalDefinitions.scala @@ -53,6 +53,7 @@ val experimentalDefinitionInLibrary = Set( "scala.annotation.internal.requiresCapability", "scala.annotation.retains", "scala.annotation.retainsByName", + "scala.Pure", "scala.caps", "scala.caps$", From 62b728163253f33858eb093a15f4ddd75f958dd8 Mon Sep 17 00:00:00 2001 From: odersky Date: Sat, 6 May 2023 20:38:01 +0200 Subject: [PATCH 2/6] Special handling of experimental.captureChecking import The question is, how do we introduce capture checking safely. There are two conflicting requirements: - Capture checking should not leak into standard Scala. It should be meaningful only in code that has capture checking explicitly enabled. - We need to be able to build up libraries that express capture information and that can be consumed from other code. This needs to start with the standard library itself. So far, everything related to capture checking was marked experimental. This means all code that refers to a capture checking abstraction in any way whatsoever needs to be declared experimental. That clearly does not work for the new use cases. But fortunately, capture checking has some properties that enable a different scheme. Specifically, a file compiled under capture checking looks like a completely normal component (both Tasty and binary) to any other file that uses it and that is not compiled with captureChecking. Only when the consumer is also compiled with capture checking, the capturing types of the original file will be revealed. The same holds for binaries. Capture checking has no effect at all on the binaries that get generated and all types and annotations needed for capture checking are erased. This allows the following more flexible scheme: - We can turn capture checking on with a setting or language import in any source file. The sources do not have to be @experimental. - If capture checking is turned on, a number of annotations and other symbols that are normally experimental are also made available. The important property is that capture checking in one component cannot poison other normal components. Like @experimental itself, the whole scheme is transitive. With the new scheme we do not need a special exemption for the dotty package anymore, so that part is dropped. --- compiler/src/dotty/tools/dotc/config/Feature.scala | 5 ++++- compiler/src/dotty/tools/dotc/core/Definitions.scala | 8 ++++++++ compiler/src/dotty/tools/dotc/core/TypeErasure.scala | 2 +- compiler/src/dotty/tools/dotc/typer/Checking.scala | 4 +++- .../no-experimental/cc-experimental.scala | 11 +++++++++++ 5 files changed, 27 insertions(+), 3 deletions(-) create mode 100644 tests/pos-custom-args/no-experimental/cc-experimental.scala diff --git a/compiler/src/dotty/tools/dotc/config/Feature.scala b/compiler/src/dotty/tools/dotc/config/Feature.scala index e5ab8f65f55b..2500266ba324 100644 --- a/compiler/src/dotty/tools/dotc/config/Feature.scala +++ b/compiler/src/dotty/tools/dotc/config/Feature.scala @@ -136,8 +136,11 @@ object Feature: if !isExperimentalEnabled then report.error(em"Experimental $which may only be used with a nightly or snapshot version of the compiler$note", srcPos) + private def ccException(sym: Symbol)(using Context): Boolean = + ccEnabled && defn.ccExperimental.contains(sym) + def checkExperimentalDef(sym: Symbol, srcPos: SrcPos)(using Context) = - if !isExperimentalEnabled then + if !isExperimentalEnabled && !ccException(sym) then val symMsg = if sym.hasAnnotation(defn.ExperimentalAnnot) then i"$sym is marked @experimental" diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 3a1b4cad28a2..3d11ad80733e 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -1946,6 +1946,14 @@ class Definitions { case Some(pkgs) => pkgs.contains(sym.owner) case none => false + /** Experimental definitions that can nevertheless be accessed from a stable + * compiler if capture checking is enabled. + */ + @tu lazy val ccExperimental: Set[Symbol] = Set( + CapsModule, CapsModule.moduleClass, PureClass, + CapabilityAnnot, RequiresCapabilityAnnot, + RetainsAnnot, RetainsByNameAnnot, WithPureFunsAnnot) + // ----- primitive value class machinery ------------------------------------------ class PerRun[T](generate: Context ?=> T) { diff --git a/compiler/src/dotty/tools/dotc/core/TypeErasure.scala b/compiler/src/dotty/tools/dotc/core/TypeErasure.scala index 9bcb3eca36bb..50e7e1847adf 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeErasure.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeErasure.scala @@ -410,7 +410,7 @@ object TypeErasure { val candidates = takeUntil(tp2superclasses)(!_.is(Trait)) // Candidates st "no other common superclass or trait derives from S" - // Also, drop `PairClass` since it is not valid after erasue + // Also, drop `PairClass` since it is not valid after erasure val minimums = candidates.filter { cand => cand != defn.PairClass && candidates.forall(x => !x.derivesFrom(cand) || x.eq(cand)) diff --git a/compiler/src/dotty/tools/dotc/typer/Checking.scala b/compiler/src/dotty/tools/dotc/typer/Checking.scala index b2ab5332c3b2..2869f64f33d0 100644 --- a/compiler/src/dotty/tools/dotc/typer/Checking.scala +++ b/compiler/src/dotty/tools/dotc/typer/Checking.scala @@ -784,7 +784,9 @@ object Checking { for case imp @ Import(qual, selectors) <- trees do def isAllowedImport(sel: untpd.ImportSelector) = val name = Feature.experimental(sel.name) - name == Feature.scala2macros || name == Feature.erasedDefinitions + name == Feature.scala2macros + || name == Feature.erasedDefinitions + || name == Feature.captureChecking languageImport(qual) match case Some(nme.experimental) diff --git a/tests/pos-custom-args/no-experimental/cc-experimental.scala b/tests/pos-custom-args/no-experimental/cc-experimental.scala new file mode 100644 index 000000000000..c4c1754fa21c --- /dev/null +++ b/tests/pos-custom-args/no-experimental/cc-experimental.scala @@ -0,0 +1,11 @@ +package scala.runtime + +import language.experimental.captureChecking + +object test: + type T = Pure + +class Foo extends Object, Pure: + val x: Pure = ??? + def foo() = () + From 72d2bddd2df8b588bc029066f1f4abd93082dbde Mon Sep 17 00:00:00 2001 From: odersky Date: Sat, 6 May 2023 21:54:52 +0200 Subject: [PATCH 3/6] Fix tasty bootstrap problem --- .../src/dotty/tools/dotc/transform/Erasure.scala | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/Erasure.scala b/compiler/src/dotty/tools/dotc/transform/Erasure.scala index 981dd5f60aea..f068a48cdba4 100644 --- a/compiler/src/dotty/tools/dotc/transform/Erasure.scala +++ b/compiler/src/dotty/tools/dotc/transform/Erasure.scala @@ -190,12 +190,14 @@ class Erasure extends Phase with DenotTransformer { def assertErased(tp: Type, tree: tpd.Tree = tpd.EmptyTree)(using Context): Unit = { def isAllowed(cls: Symbol, sourceName: String) = tp.typeSymbol == cls && ctx.compilationUnit.source.file.name == sourceName - assert(isErasedType(tp) || - isAllowed(defn.ArrayClass, "Array.scala") || - isAllowed(defn.TupleClass, "Tuple.scala") || - isAllowed(defn.NonEmptyTupleClass, "Tuple.scala") || - isAllowed(defn.PairClass, "Tuple.scala"), - i"The type $tp - ${tp.toString} of class ${tp.getClass} of tree $tree : ${tree.tpe} / ${tree.getClass} is illegal after erasure, phase = ${ctx.phase.prev}") + assert( + isErasedType(tp) + || isAllowed(defn.ArrayClass, "Array.scala") + || isAllowed(defn.TupleClass, "Tuple.scala") + || isAllowed(defn.NonEmptyTupleClass, "Tuple.scala") + || isAllowed(defn.PairClass, "Tuple.scala") + || isAllowed(defn.PureClass, "Pure.scala"), + i"The type $tp - ${tp.toString} of class ${tp.getClass} of tree $tree : ${tree.tpe} / ${tree.getClass} is illegal after erasure, phase = ${ctx.phase.prev}") } } From 785182d3a9bfabe8b234e9a1639b09cb43d0e767 Mon Sep 17 00:00:00 2001 From: odersky Date: Mon, 15 May 2023 12:49:17 +0200 Subject: [PATCH 4/6] Drop special handling of dotty (2) --- compiler/src/dotty/tools/dotc/transform/SymUtils.scala | 2 -- 1 file changed, 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/SymUtils.scala b/compiler/src/dotty/tools/dotc/transform/SymUtils.scala index 3fe05a45699e..d3a17334caf5 100644 --- a/compiler/src/dotty/tools/dotc/transform/SymUtils.scala +++ b/compiler/src/dotty/tools/dotc/transform/SymUtils.scala @@ -370,8 +370,6 @@ object SymUtils: self.hasAnnotation(defn.ExperimentalAnnot) || isDefaultArgumentOfExperimentalMethod || (!self.is(Package) && self.owner.isInExperimentalScope) - || self.topLevelClass.ownersIterator.exists(p => - p.is(Package) && p.owner.isRoot && p.name == tpnme.dotty) /** The declared self type of this class, as seen from `site`, stripping * all refinements for opaque types. From 82e6abb148863c1d592e6595890f280460e4ce9c Mon Sep 17 00:00:00 2001 From: odersky Date: Wed, 17 May 2023 15:06:18 +0200 Subject: [PATCH 5/6] Fix test to work under new regime Previously: test was exempted because it was in dotty package Now: test is exempted since it language imports captureChecking --- tests/pos-custom-args/no-experimental/dotty-experimental.scala | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/pos-custom-args/no-experimental/dotty-experimental.scala b/tests/pos-custom-args/no-experimental/dotty-experimental.scala index 72d16ddd9b15..d0f0c5011299 100644 --- a/tests/pos-custom-args/no-experimental/dotty-experimental.scala +++ b/tests/pos-custom-args/no-experimental/dotty-experimental.scala @@ -1,6 +1,7 @@ -package dotty.tools +import language.experimenal.captureChecking object test { val x = caps.cap } + From e6d1242ea65d3c243c1a2e3dd5fb3965c578dbb9 Mon Sep 17 00:00:00 2001 From: odersky Date: Fri, 26 May 2023 16:56:09 +0200 Subject: [PATCH 6/6] Make checkExperimentalDef work for members of experimental objects --- .../src/dotty/tools/dotc/config/Feature.scala | 22 ++++++++++--------- .../no-experimental/dotty-experimental.scala | 3 +-- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/config/Feature.scala b/compiler/src/dotty/tools/dotc/config/Feature.scala index 2500266ba324..5bcc139326f9 100644 --- a/compiler/src/dotty/tools/dotc/config/Feature.scala +++ b/compiler/src/dotty/tools/dotc/config/Feature.scala @@ -137,18 +137,20 @@ object Feature: report.error(em"Experimental $which may only be used with a nightly or snapshot version of the compiler$note", srcPos) private def ccException(sym: Symbol)(using Context): Boolean = - ccEnabled && defn.ccExperimental.contains(sym) + ccEnabled && defn.ccExperimental.contains(sym) def checkExperimentalDef(sym: Symbol, srcPos: SrcPos)(using Context) = - if !isExperimentalEnabled && !ccException(sym) then - val symMsg = - if sym.hasAnnotation(defn.ExperimentalAnnot) then - i"$sym is marked @experimental" - else if sym.owner.hasAnnotation(defn.ExperimentalAnnot) then - i"${sym.owner} is marked @experimental" - else - i"$sym inherits @experimental" - report.error(em"$symMsg and therefore may only be used in an experimental scope.", srcPos) + if !isExperimentalEnabled then + val experimentalSym = + if sym.hasAnnotation(defn.ExperimentalAnnot) then sym + else if sym.owner.hasAnnotation(defn.ExperimentalAnnot) then sym.owner + else NoSymbol + if !ccException(experimentalSym) then + val symMsg = + if experimentalSym.exists + then i"$experimentalSym is marked @experimental" + else i"$sym inherits @experimental" + report.error(em"$symMsg and therefore may only be used in an experimental scope.", srcPos) /** Check that experimental compiler options are only set for snapshot or nightly compiler versions. */ def checkExperimentalSettings(using Context): Unit = diff --git a/tests/pos-custom-args/no-experimental/dotty-experimental.scala b/tests/pos-custom-args/no-experimental/dotty-experimental.scala index d0f0c5011299..ca2fa5ca9f74 100644 --- a/tests/pos-custom-args/no-experimental/dotty-experimental.scala +++ b/tests/pos-custom-args/no-experimental/dotty-experimental.scala @@ -1,7 +1,6 @@ -import language.experimenal.captureChecking +import language.experimental.captureChecking object test { val x = caps.cap } -