Skip to content

Convert -Ycc to language import #16240

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Oct 25, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions compiler/src/dotty/tools/dotc/Compiler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,8 @@ class Compiler {
new PatternMatcher) :: // Compile pattern matches
List(new TestRecheck.Pre) :: // Test only: run rechecker, enabled under -Yrecheck-test
List(new TestRecheck) :: // Test only: run rechecker, enabled under -Yrecheck-test
List(new CheckCaptures.Pre) :: // Preparations for check captures phase, enabled under -Ycc
List(new CheckCaptures) :: // Check captures, enabled under -Ycc
List(new CheckCaptures.Pre) :: // Preparations for check captures phase, enabled under captureChecking
List(new CheckCaptures) :: // Check captures, enabled under captureChecking
List(new ElimOpaque, // Turn opaque into normal aliases
new sjs.ExplicitJSClasses, // Make all JS classes explicit (Scala.js only)
new ExplicitOuter, // Add accessors to outer classes from nested ones.
Expand Down
12 changes: 6 additions & 6 deletions compiler/src/dotty/tools/dotc/ast/TreeInfo.scala
Original file line number Diff line number Diff line change
Expand Up @@ -195,11 +195,11 @@ trait TreeInfo[T >: Untyped <: Type] { self: Trees.Instance[T] =>
case arg => arg.typeOpt.widen.isRepeatedParam
}

/** Is tree a type tree of the form `=> T` or (under -Ycc) `{refs}-> T`? */
/** Is tree a type tree of the form `=> T` or (under pureFunctions) `{refs}-> T`? */
def isByNameType(tree: Tree)(using Context): Boolean =
stripByNameType(tree) ne tree

/** Strip `=> T` to `T` and (under -Ycc) `{refs}-> T` to `T` */
/** Strip `=> T` to `T` and (under pureFunctions) `{refs}-> T` to `T` */
def stripByNameType(tree: Tree)(using Context): Tree = unsplice(tree) match
case ByNameTypeTree(t1) => t1
case untpd.CapturingTypeTree(_, parent) =>
Expand Down Expand Up @@ -400,18 +400,18 @@ trait UntypedTreeInfo extends TreeInfo[Untyped] { self: Trees.Instance[Untyped]
}
}

/** Under -Ycc: A builder and extractor for `=> T`, which is an alias for `{*}-> T`.
/** Under pureFunctions: A builder and extractor for `=> T`, which is an alias for `{*}-> T`.
* Only trees of the form `=> T` are matched; trees written directly as `{*}-> T`
* are ignored by the extractor.
*/
object ImpureByNameTypeTree:

def apply(tp: ByNameTypeTree)(using Context): untpd.CapturingTypeTree =
untpd.CapturingTypeTree(
Ident(nme.CAPTURE_ROOT).withSpan(tp.span.startPos) :: Nil, tp)
untpd.captureRoot.withSpan(tp.span.startPos) :: Nil, tp)

def unapply(tp: Tree)(using Context): Option[ByNameTypeTree] = tp match
case untpd.CapturingTypeTree(id @ Ident(nme.CAPTURE_ROOT) :: Nil, bntp: ByNameTypeTree)
case untpd.CapturingTypeTree(id @ Select(_, nme.CAPTURE_ROOT) :: Nil, bntp: ByNameTypeTree)
if id.span == bntp.span.startPos => Some(bntp)
case _ => None
end ImpureByNameTypeTree
Expand Down
7 changes: 5 additions & 2 deletions compiler/src/dotty/tools/dotc/ast/untpd.scala
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
case Floating
}

/** {x1, ..., xN} T (only relevant under -Ycc) */
/** {x1, ..., xN} T (only relevant under captureChecking) */
case class CapturingTypeTree(refs: List[Tree], parent: Tree)(implicit @constructorOnly src: SourceFile) extends TypTree

/** Short-lived usage in typer, does not need copy/transform/fold infrastructure */
Expand Down Expand Up @@ -217,7 +217,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {

case class Infix()(implicit @constructorOnly src: SourceFile) extends Mod(Flags.Infix)

/** Used under -Ycc to mark impure function types `A => B` in `FunctionWithMods` */
/** Used under pureFunctions to mark impure function types `A => B` in `FunctionWithMods` */
case class Impure()(implicit @constructorOnly src: SourceFile) extends Mod(Flags.Impure)
}

Expand Down Expand Up @@ -492,6 +492,9 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
def scalaAny(implicit src: SourceFile): Select = scalaDot(tpnme.Any)
def javaDotLangDot(name: Name)(implicit src: SourceFile): Select = Select(Select(Ident(nme.java), nme.lang), name)

def captureRoot(using Context): Select =
Select(scalaDot(nme.caps), nme.CAPTURE_ROOT)

def makeConstructor(tparams: List[TypeDef], vparamss: List[List[ValDef]], rhs: Tree = EmptyTree)(using Context): DefDef =
DefDef(nme.CONSTRUCTOR, joinParams(tparams, vparamss), TypeTree(), rhs)

Expand Down
7 changes: 4 additions & 3 deletions compiler/src/dotty/tools/dotc/cc/CaptureOps.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import Decorators.*, NameOps.*
import config.Printers.capt
import util.Property.Key
import tpd.*
import config.Feature

private val Captures: Key[CaptureSet] = Key()
private val BoxedType: Key[BoxedTypeCache] = Key()
Expand Down Expand Up @@ -120,11 +121,11 @@ extension (tp: Type)
case _ =>
tp

/** Under -Ycc, map regular function type to impure function type
/** Under pureFunctions, map regular function type to impure function type
*/
def adaptFunctionTypeUnderCC(using Context): Type = tp match
def adaptFunctionTypeUnderPureFuns(using Context): Type = tp match
case AppliedType(fn, args)
if ctx.settings.Ycc.value && defn.isFunctionClass(fn.typeSymbol) =>
if Feature.pureFunsEnabled && defn.isFunctionClass(fn.typeSymbol) =>
val fname = fn.typeSymbol.name
defn.FunctionType(
fname.functionArity,
Expand Down
8 changes: 4 additions & 4 deletions compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import Phases.*, DenotTransformers.*, SymDenotations.*
import Contexts.*, Names.*, Flags.*, Symbols.*, Decorators.*
import Types.*, StdNames.*, Denotations.*
import config.Printers.{capt, recheckr}
import config.Config
import config.{Config, Feature}
import ast.{tpd, untpd, Trees}
import Trees.*
import typer.RefChecks.{checkAllOverrides, checkParents}
Expand All @@ -26,7 +26,7 @@ object CheckCaptures:

class Pre extends PreRecheck, SymTransformer:

override def isEnabled(using Context) = ctx.settings.Ycc.value
override def isEnabled(using Context) = Feature.ccEnabled

/** Reset `private` flags of parameter accessors so that we can refine them
* in Setup if they have non-empty capture sets. Special handling of some
Expand Down Expand Up @@ -133,7 +133,7 @@ class CheckCaptures extends Recheck, SymTransformer:
import CheckCaptures.*

def phaseName: String = "cc"
override def isEnabled(using Context) = ctx.settings.Ycc.value
override def isEnabled(using Context) = Feature.ccEnabled

def newRechecker()(using Context) = CaptureChecker(ctx)

Expand All @@ -148,7 +148,7 @@ class CheckCaptures extends Recheck, SymTransformer:
/** Check overrides again, taking capture sets into account.
* TODO: Can we avoid doing overrides checks twice?
* We need to do them here since only at this phase CaptureTypes are relevant
* But maybe we can then elide the check during the RefChecks phase if -Ycc is set?
* But maybe we can then elide the check during the RefChecks phase under captureChecking?
*/
def checkOverrides = new TreeTraverser:
def traverse(t: Tree)(using Context) =
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/config/Config.scala
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@ object Config {
*/
inline val printCaptureSetsAsPrefix = true

/** If true, allow mappping capture set variables under -Ycc with maps that are neither
/** If true, allow mappping capture set variables under captureChecking with maps that are neither
* bijective nor idempotent. We currently do now know how to do this correctly in all
* cases, though.
*/
Expand Down
9 changes: 9 additions & 0 deletions compiler/src/dotty/tools/dotc/config/Feature.scala
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ object Feature:
val symbolLiterals = deprecated("symbolLiterals")
val fewerBraces = experimental("fewerBraces")
val saferExceptions = experimental("saferExceptions")
val pureFunctions = experimental("pureFunctions")
val captureChecking = experimental("captureChecking")

val globalOnlyImports: Set[TermName] = Set(pureFunctions, captureChecking)

/** Is `feature` enabled by by a command-line setting? The enabling setting is
*
Expand Down Expand Up @@ -75,6 +79,11 @@ object Feature:

def scala2ExperimentalMacroEnabled(using Context) = enabled(scala2macros)

def pureFunsEnabled(using Context) =
enabled(pureFunctions) || ccEnabled

def ccEnabled(using Context) = enabled(captureChecking)

def sourceVersionSetting(using Context): SourceVersion =
SourceVersion.valueOf(ctx.settings.source.value)

Expand Down
5 changes: 2 additions & 3 deletions compiler/src/dotty/tools/dotc/config/ScalaSettings.scala
Original file line number Diff line number Diff line change
Expand Up @@ -328,9 +328,8 @@ private sealed trait YSettings:
val YcheckInit: Setting[Boolean] = BooleanSetting("-Ysafe-init", "Ensure safe initialization of objects")
val YrequireTargetName: Setting[Boolean] = BooleanSetting("-Yrequire-targetName", "Warn if an operator is defined without a @targetName annotation")
val YrecheckTest: Setting[Boolean] = BooleanSetting("-Yrecheck-test", "Run basic rechecking (internal test only)")
val Ycc: Setting[Boolean] = BooleanSetting("-Ycc", "Check captured references (warning: extremely experimental and unstable)")
val YccDebug: Setting[Boolean] = BooleanSetting("-Ycc-debug", "Used in conjunction with -Ycc, debug info for captured references")
val YccNoAbbrev: Setting[Boolean] = BooleanSetting("-Ycc-no-abbrev", "Used in conjunction with -Ycc, suppress type abbreviations")
val YccDebug: Setting[Boolean] = BooleanSetting("-Ycc-debug", "Used in conjunction with captureChecking language import, debug info for captured references")
val YccNoAbbrev: Setting[Boolean] = BooleanSetting("-Ycc-no-abbrev", "Used in conjunction with captureChecking language import, suppress type abbreviations")

/** Area-specific debug output */
val YexplainLowlevel: Setting[Boolean] = BooleanSetting("-Yexplain-lowlevel", "When explaining type errors, show types at a lower level.")
Expand Down
13 changes: 6 additions & 7 deletions compiler/src/dotty/tools/dotc/core/Definitions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import typer.ImportInfo.RootRef
import Comments.CommentsContext
import Comments.Comment
import util.Spans.NoSpan
import config.Feature
import Symbols.requiredModuleRef
import cc.{CapturingType, CaptureSet, EventuallyCapturingType}

Expand Down Expand Up @@ -469,7 +470,6 @@ class Definitions {

@tu lazy val andType: TypeSymbol = enterBinaryAlias(tpnme.AND, AndType(_, _))
@tu lazy val orType: TypeSymbol = enterBinaryAlias(tpnme.OR, OrType(_, _, soft = false))
@tu lazy val captureRoot: TermSymbol = enterPermanentSymbol(nme.CAPTURE_ROOT, AnyType).asTerm

/** Method representing a throw */
@tu lazy val throwMethod: TermSymbol = enterMethod(OpsPackageClass, nme.THROWkw,
Expand Down Expand Up @@ -963,6 +963,7 @@ class Definitions {
@tu lazy val CapsModule: Symbol = requiredModule("scala.caps")
@tu lazy val Caps_unsafeBox: Symbol = CapsModule.requiredMethod("unsafeBox")
@tu lazy val Caps_unsafeUnbox: Symbol = CapsModule.requiredMethod("unsafeUnbox")
@tu lazy val captureRoot: TermSymbol = CapsModule.requiredValue("*")

// Annotation base classes
@tu lazy val AnnotationClass: ClassSymbol = requiredClass("scala.annotation.Annotation")
Expand All @@ -976,7 +977,6 @@ class Definitions {
@tu lazy val BooleanBeanPropertyAnnot: ClassSymbol = requiredClass("scala.beans.BooleanBeanProperty")
@tu lazy val BodyAnnot: ClassSymbol = requiredClass("scala.annotation.internal.Body")
@tu lazy val CapabilityAnnot: ClassSymbol = requiredClass("scala.annotation.capability")
@tu lazy val CaptureCheckedAnnot: ClassSymbol = requiredClass("scala.annotation.internal.CaptureChecked")
@tu lazy val ChildAnnot: ClassSymbol = requiredClass("scala.annotation.internal.Child")
@tu lazy val ContextResultCountAnnot: ClassSymbol = requiredClass("scala.annotation.internal.ContextResultCount")
@tu lazy val ProvisionalSuperClassAnnot: ClassSymbol = requiredClass("scala.annotation.internal.ProvisionalSuperClass")
Expand Down Expand Up @@ -1012,6 +1012,7 @@ class Definitions {
@tu lazy val UncheckedStableAnnot: ClassSymbol = requiredClass("scala.annotation.unchecked.uncheckedStable")
@tu lazy val UncheckedVarianceAnnot: ClassSymbol = requiredClass("scala.annotation.unchecked.uncheckedVariance")
@tu lazy val VolatileAnnot: ClassSymbol = requiredClass("scala.volatile")
@tu lazy val WithPureFunsAnnot: ClassSymbol = requiredClass("scala.annotation.internal.WithPureFuns")
@tu lazy val FieldMetaAnnot: ClassSymbol = requiredClass("scala.annotation.meta.field")
@tu lazy val GetterMetaAnnot: ClassSymbol = requiredClass("scala.annotation.meta.getter")
@tu lazy val ParamMetaAnnot: ClassSymbol = requiredClass("scala.annotation.meta.param")
Expand All @@ -1024,7 +1025,6 @@ class Definitions {
@tu lazy val RequiresCapabilityAnnot: ClassSymbol = requiredClass("scala.annotation.internal.requiresCapability")
@tu lazy val RetainsAnnot: ClassSymbol = requiredClass("scala.annotation.retains")
@tu lazy val RetainsByNameAnnot: ClassSymbol = requiredClass("scala.annotation.retainsByName")
@tu lazy val RetainsUniversalAnnot: ClassSymbol = requiredClass("scala.annotation.retainsUniversal")

@tu lazy val JavaRepeatableAnnot: ClassSymbol = requiredClass("java.lang.annotation.Repeatable")

Expand Down Expand Up @@ -1160,7 +1160,7 @@ class Definitions {

/** Extractor for context function types representing by-name parameters, of the form
* `() ?=> T`.
* Under -Ycc, this becomes `() ?-> T` or `{r1, ..., rN} () ?-> T`.
* Under purefunctions, this becomes `() ?-> T` or `{r1, ..., rN} () ?-> T`.
*/
object ByNameFunction:
def apply(tp: Type)(using Context): Type = tp match
Expand Down Expand Up @@ -1983,9 +1983,8 @@ class Definitions {
this.initCtx = ctx
if (!isInitialized) {
// force initialization of every symbol that is synthesized or hijacked by the compiler
val forced = syntheticCoreClasses ++ syntheticCoreMethods ++ ScalaValueClasses()
++ (JavaEnumClass :: (if ctx.settings.Ycc.value then captureRoot :: Nil else Nil))

val forced =
syntheticCoreClasses ++ syntheticCoreMethods ++ ScalaValueClasses() :+ JavaEnumClass
isInitialized = true
}
addSyntheticSymbolsComments
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/core/Mode.scala
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ object Mode {
/** Use Scala2 scheme for overloading and implicit resolution */
val OldOverloadingResolution: Mode = newMode(15, "OldOverloadingResolution")

/** Treat CapturingTypes as plain AnnotatedTypes even in phase =Ycc.
/** Treat CapturingTypes as plain AnnotatedTypes even in phase CheckCaptures.
* Reuses the value of OldOverloadingResolution to save Mode bits.
* This is OK since OldOverloadingResolution only affects implicit search, which
* is done during phases Typer and Inlinig, and IgnoreCaptures only has an
Expand Down
5 changes: 2 additions & 3 deletions compiler/src/dotty/tools/dotc/core/NameOps.scala
Original file line number Diff line number Diff line change
Expand Up @@ -208,14 +208,13 @@ object NameOps {
if str == mustHave then found = true
idx + str.length
else idx
val start = if ctx.settings.Ycc.value then skip(0, "Impure") else 0
skip(skip(start, "Erased"), "Context") == suffixStart
skip(skip(skip(0, "Impure"), "Erased"), "Context") == suffixStart
&& found
}

/** Same as `funArity`, except that it returns -1 if the prefix
* is not one of a (possibly empty) concatenation of a subset of
* "Impure" (only under -Ycc), "Erased" and "Context" (in that order).
* "Impure" (only under pureFunctions), "Erased" and "Context" (in that order).
*/
private def checkedFunArity(suffixStart: Int)(using Context): Int =
if isFunctionPrefix(suffixStart) then funArity(suffixStart) else -1
Expand Down
1 change: 1 addition & 0 deletions compiler/src/dotty/tools/dotc/core/StdNames.scala
Original file line number Diff line number Diff line change
Expand Up @@ -425,6 +425,7 @@ object StdNames {
val bytes: N = "bytes"
val canEqual_ : N = "canEqual"
val canEqualAny : N = "canEqualAny"
val caps: N = "caps"
val checkInitialized: N = "checkInitialized"
val classOf: N = "classOf"
val classType: N = "classType"
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/core/Types.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4238,7 +4238,7 @@ object Types {
final val Unknown: DependencyStatus = 0 // not yet computed
final val NoDeps: DependencyStatus = 1 // no dependent parameters found
final val FalseDeps: DependencyStatus = 2 // all dependent parameters are prefixes of non-depended alias types
final val CaptureDeps: DependencyStatus = 3 // dependencies in capture sets under -Ycc, otherwise only false dependencoes
final val CaptureDeps: DependencyStatus = 3 // dependencies in capture sets under captureChecking, otherwise only false dependencoes
final val TrueDeps: DependencyStatus = 4 // some truly dependent parameters exist
final val StatusMask: DependencyStatus = 7 // the bits indicating actual dependency status
final val Provisional: DependencyStatus = 8 // set if dependency status can still change due to type variable instantiations
Expand Down
16 changes: 8 additions & 8 deletions compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ import ast.{Trees, tpd, untpd}
import Trees._
import Decorators._
import transform.SymUtils._
import cc.adaptFunctionTypeUnderCC
import cc.adaptFunctionTypeUnderPureFuns

import dotty.tools.tasty.{TastyBuffer, TastyReader}
import TastyBuffer._
Expand Down Expand Up @@ -87,8 +87,8 @@ class TreeUnpickler(reader: TastyReader,
/** The root owner tree. See `OwnerTree` class definition. Set by `enterTopLevel`. */
private var ownerTree: OwnerTree = _

/** Was unpickled class compiled with -Ycc? */
private var wasCaptureChecked: Boolean = false
/** Was unpickled class compiled with pureFunctions? */
private var knowsPureFuns: Boolean = false

private def registerSym(addr: Addr, sym: Symbol) =
symAtAddr(addr) = sym
Expand Down Expand Up @@ -489,11 +489,11 @@ class TreeUnpickler(reader: TastyReader,
def readTermRef()(using Context): TermRef =
readType().asInstanceOf[TermRef]

/** Under -Ycc, map all function types to impure function types,
* unless the unpickled class was also compiled with -Ycc.
/** Under pureFunctions, map all function types to impure function types,
* unless the unpickled class was also compiled with pureFunctions.
*/
private def postProcessFunction(tp: Type)(using Context): Type =
if wasCaptureChecked then tp else tp.adaptFunctionTypeUnderCC
if knowsPureFuns then tp else tp.adaptFunctionTypeUnderPureFuns

// ------ Reading definitions -----------------------------------------------------

Expand Down Expand Up @@ -642,8 +642,8 @@ class TreeUnpickler(reader: TastyReader,
}
registerSym(start, sym)
if (isClass) {
if sym.owner.is(Package) && annots.exists(_.hasSymbol(defn.CaptureCheckedAnnot)) then
wasCaptureChecked = true
if sym.owner.is(Package) && annots.exists(_.hasSymbol(defn.WithPureFunsAnnot)) then
knowsPureFuns = true
sym.completer.withDecls(newScope)
forkAt(templateStart).indexTemplateParams()(using localContext(sym))
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ import scala.collection.mutable
import scala.collection.mutable.ListBuffer
import scala.annotation.switch
import reporting._
import cc.adaptFunctionTypeUnderCC
import cc.adaptFunctionTypeUnderPureFuns

object Scala2Unpickler {

Expand Down Expand Up @@ -826,7 +826,7 @@ class Scala2Unpickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClas
}
else if args.nonEmpty then
tycon.safeAppliedTo(EtaExpandIfHK(sym.typeParams, args.map(translateTempPoly)))
.adaptFunctionTypeUnderCC
.adaptFunctionTypeUnderPureFuns
else if (sym.typeParams.nonEmpty) tycon.EtaExpand(sym.typeParams)
else tycon
case TYPEBOUNDStpe =>
Expand Down
Loading