Skip to content
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

Stress test for add-class-deps (#18958) #19015

Closed
wants to merge 9 commits into from
Closed
32 changes: 21 additions & 11 deletions compiler/src/dotty/tools/dotc/ast/Desugar.scala
Original file line number Diff line number Diff line change
Expand Up @@ -437,13 +437,13 @@ object desugar {
private def toDefParam(tparam: TypeDef, keepAnnotations: Boolean): TypeDef = {
var mods = tparam.rawMods
if (!keepAnnotations) mods = mods.withAnnotations(Nil)
tparam.withMods(mods & (EmptyFlags | Sealed) | Param)
tparam.withMods(mods & EmptyFlags | Param)
}
private def toDefParam(vparam: ValDef, keepAnnotations: Boolean, keepDefault: Boolean): ValDef = {
var mods = vparam.rawMods
if (!keepAnnotations) mods = mods.withAnnotations(Nil)
val hasDefault = if keepDefault then HasDefault else EmptyFlags
vparam.withMods(mods & (GivenOrImplicit | Erased | hasDefault) | Param)
vparam.withMods(mods & (GivenOrImplicit | Erased | hasDefault | Tracked) | Param)
}

def mkApply(fn: Tree, paramss: List[ParamClause])(using Context): Tree =
Expand Down Expand Up @@ -529,7 +529,7 @@ object desugar {
// but not on the constructor parameters. The reverse is true for
// annotations on class _value_ parameters.
val constrTparams = impliedTparams.map(toDefParam(_, keepAnnotations = false))
val constrVparamss =
def defVparamss =
if (originalVparamss.isEmpty) { // ensure parameter list is non-empty
if (isCaseClass)
report.error(CaseClassMissingParamList(cdef), namePos)
Expand All @@ -540,6 +540,7 @@ object desugar {
ListOfNil
}
else originalVparamss.nestedMap(toDefParam(_, keepAnnotations = true, keepDefault = true))
val constrVparamss = defVparamss
val derivedTparams =
constrTparams.zipWithConserve(impliedTparams)((tparam, impliedParam) =>
derivedTypeParam(tparam).withAnnotations(impliedParam.mods.annotations))
Expand Down Expand Up @@ -614,6 +615,11 @@ object desugar {
case _ => false
}

def isRepeated(tree: Tree): Boolean = stripByNameType(tree) match {
case PostfixOp(_, Ident(tpnme.raw.STAR)) => true
case _ => false
}

def appliedRef(tycon: Tree, tparams: List[TypeDef] = constrTparams, widenHK: Boolean = false) = {
val targs = for (tparam <- tparams) yield {
val targ = refOfDef(tparam)
Expand All @@ -630,10 +636,13 @@ object desugar {
appliedTypeTree(tycon, targs)
}

def isRepeated(tree: Tree): Boolean = stripByNameType(tree) match {
case PostfixOp(_, Ident(tpnme.raw.STAR)) => true
case _ => false
}
def addParamRefinements(core: Tree, paramss: List[List[ValDef]]): Tree =
val refinements =
for params <- paramss; param <- params; if param.mods.is(Tracked) yield
ValDef(param.name, SingletonTypeTree(TermRefTree().watching(param)), EmptyTree)
.withSpan(param.span)
if refinements.isEmpty then core
else RefinedTypeTree(core, refinements).showing(i"refined result: $result", Printers.desugar)

// a reference to the class type bound by `cdef`, with type parameters coming from the constructor
val classTypeRef = appliedRef(classTycon)
Expand Down Expand Up @@ -854,18 +863,17 @@ object desugar {
Nil
}
else {
val defParamss = constrVparamss match {
val defParamss = defVparamss match
case Nil :: paramss =>
paramss // drop leading () that got inserted by class
// TODO: drop this once we do not silently insert empty class parameters anymore
case paramss => paramss
}
val finalFlag = if ctx.settings.YcompileScala2Library.value then EmptyFlags else Final
// implicit wrapper is typechecked in same scope as constructor, so
// we can reuse the constructor parameters; no derived params are needed.
DefDef(
className.toTermName, joinParams(constrTparams, defParamss),
classTypeRef, creatorExpr)
addParamRefinements(classTypeRef, defParamss), creatorExpr)
.withMods(companionMods | mods.flags.toTermFlags & (GivenOrImplicit | Inline) | finalFlag)
.withSpan(cdef.span) :: Nil
}
Expand Down Expand Up @@ -894,7 +902,9 @@ object desugar {
}
if mods.isAllOf(Given | Inline | Transparent) then
report.error("inline given instances cannot be trasparent", cdef)
val classMods = if mods.is(Given) then mods &~ (Inline | Transparent) | Synthetic else mods
var classMods = if mods.is(Given) then mods &~ (Inline | Transparent) | Synthetic else mods
if vparamAccessors.exists(_.mods.is(Tracked)) then
classMods |= Dependent
cpy.TypeDef(cdef: TypeDef)(
name = className,
rhs = cpy.Template(impl)(constr, parents1, clsDerived, self1,
Expand Down
2 changes: 2 additions & 0 deletions compiler/src/dotty/tools/dotc/ast/untpd.scala
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,8 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {

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

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

/** 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
8 changes: 5 additions & 3 deletions compiler/src/dotty/tools/dotc/core/Flags.scala
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ object Flags {
val (AccessorOrSealed @ _, Accessor @ _, Sealed @ _) = newFlags(11, "<accessor>", "sealed")

/** A mutable var, an open class */
val (MutableOrOpen @ __, Mutable @ _, Open @ _) = newFlags(12, "mutable", "open")
val (MutableOrOpen @ _, Mutable @ _, Open @ _) = newFlags(12, "mutable", "open")

/** Symbol is local to current class (i.e. private[this] or protected[this]
* pre: Private or Protected are also set
Expand Down Expand Up @@ -377,6 +377,8 @@ object Flags {
/** Symbol cannot be found as a member during typer */
val (Invisible @ _, _, _) = newFlags(45, "<invisible>")

val (Tracked @ _, _, Dependent @ _) = newFlags(46, "tracked", "dependent")

// ------------ Flags following this one are not pickled ----------------------------------

/** Symbol is not a member of its owner */
Expand Down Expand Up @@ -452,7 +454,7 @@ object Flags {
CommonSourceModifierFlags.toTypeFlags | Abstract | Sealed | Opaque | Open

val TermSourceModifierFlags: FlagSet =
CommonSourceModifierFlags.toTermFlags | Inline | AbsOverride | Lazy
CommonSourceModifierFlags.toTermFlags | Inline | AbsOverride | Lazy | Tracked

/** Flags representing modifiers that can appear in trees */
val ModifierFlags: FlagSet =
Expand All @@ -477,7 +479,7 @@ object Flags {
*/
val AfterLoadFlags: FlagSet = commonFlags(
FromStartFlags, AccessFlags, Final, AccessorOrSealed,
Abstract, LazyOrTrait, SelfName, JavaDefined, JavaAnnotation, Transparent)
Abstract, LazyOrTrait, SelfName, JavaDefined, JavaAnnotation, Transparent, Tracked)

/** A value that's unstable unless complemented with a Stable flag */
val UnstableValueFlags: FlagSet = Mutable | Method
Expand Down
54 changes: 51 additions & 3 deletions compiler/src/dotty/tools/dotc/core/NamerOps.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ package core
import Contexts.*, Symbols.*, Types.*, Flags.*, Scopes.*, Decorators.*, Names.*, NameOps.*
import SymDenotations.{LazyType, SymDenotation}, StdNames.nme
import TypeApplications.EtaExpansion
import collection.mutable

/** Operations that are shared between Namer and TreeUnpickler */
object NamerOps:
Expand All @@ -14,9 +15,56 @@ object NamerOps:
* @param ctor the constructor
*/
def effectiveResultType(ctor: Symbol, paramss: List[List[Symbol]])(using Context): Type =
paramss match
case TypeSymbols(tparams) :: _ => ctor.owner.typeRef.appliedTo(tparams.map(_.typeRef))
case _ => ctor.owner.typeRef
var resType = paramss match
case TypeSymbols(tparams) :: _ =>
ctor.owner.typeRef.appliedTo(tparams.map(_.typeRef))
case _ =>
ctor.owner.typeRef
for params <- paramss; param <- params do
if param.is(Tracked) then
resType = RefinedType(resType, param.name, param.termRef)
resType

/** Split dependent class refinements off parent type and add them to `refinements` */
extension (tp: Type)
def separateRefinements(refinements: mutable.LinkedHashMap[Name, Type])(using Context): Type =
tp match
case RefinedType(tp1, rname, rinfo) =>
try tp1.separateRefinements(refinements)
finally
refinements(rname) = refinements.get(rname) match
case Some(tp) => tp & rinfo
case None => rinfo
case tp => tp

/** Add all parent `refinements` to the result type of the info of the dependent
* class constructor `constr`. Parent refinements refer to parameter accessors
* in the current class. These have to be mapped to the paramRefs of the
* constructor info.
*/
def integrateParentRefinements(
constr: Symbol, refinements: mutable.LinkedHashMap[Name, Type])(using Context): Unit =

/** @param info the (remaining part) of the constructor info
* @param nameToParamRef the map from parameter names to paramRefs of
* previously encountered parts of `info`.
*/
def recur(info: Type, nameToParamRef: mutable.Map[Name, Type]): Type = info match
case info: MethodOrPoly =>
info.derivedLambdaType(resType =
recur(info.resType, nameToParamRef ++= info.paramNames.zip(info.paramRefs)))
case _ =>
val mapParams = new TypeMap:
def apply(t: Type) = t match
case t: TermRef if t.symbol.is(ParamAccessor) && t.symbol.owner == constr.owner =>
nameToParamRef(t.name)
case _ =>
mapOver(t)
refinements.foldLeft(info): (info, refinement) =>
val (rname, rinfo) = refinement
RefinedType(info, rname, mapParams(rinfo))
constr.info = recur(constr.info, mutable.Map())
end integrateParentRefinements

/** If isConstructor, make sure it has at least one non-implicit parameter list
* This is done by adding a () in front of a leading old style implicit parameter,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import Contexts.ctx
import dotty.tools.dotc.reporting.trace
import config.Feature.migrateTo3
import config.Printers.*
import transform.TypeUtils.stripRefinement

trait PatternTypeConstrainer { self: TypeComparer =>

Expand Down Expand Up @@ -88,11 +89,6 @@ trait PatternTypeConstrainer { self: TypeComparer =>
}
}

def stripRefinement(tp: Type): Type = tp match {
case tp: RefinedOrRecType => stripRefinement(tp.parent)
case tp => tp
}

def tryConstrainSimplePatternType(pat: Type, scrut: Type) = {
val patCls = pat.classSymbol
val scrCls = scrut.classSymbol
Expand Down
9 changes: 4 additions & 5 deletions compiler/src/dotty/tools/dotc/core/Scopes.scala
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import Denotations.*
import printing.Texts.*
import printing.Printer
import SymDenotations.NoDenotation
import util.common.alwaysFalse

import collection.mutable
import scala.compiletime.uninitialized
Expand Down Expand Up @@ -94,15 +95,13 @@ object Scopes {
def foreach[U](f: Symbol => U)(using Context): Unit = toList.foreach(f)

/** Selects all Symbols of this Scope which satisfy a predicate. */
def filter(p: Symbol => Boolean)(using Context): List[Symbol] = {
def filter(p: Symbol => Boolean, stopAt: Symbol => Boolean = alwaysFalse)(using Context): List[Symbol] = {
ensureComplete()
var syms: List[Symbol] = Nil
var e = lastEntry
while ((e != null) && e.owner == this) {
val sym = e.sym
if (p(sym)) syms = sym :: syms
while e != null && e.owner == this && !stopAt(e.sym) do
if p(e.sym) then syms = e.sym :: syms
e = e.prev
}
syms
}

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 @@ -623,6 +623,7 @@ object StdNames {
val toString_ : N = "toString"
val toTypeConstructor: N = "toTypeConstructor"
val tpe : N = "tpe"
val tracked: N = "tracked"
val transparent : N = "transparent"
val tree : N = "tree"
val true_ : N = "true"
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/core/SymDenotations.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2380,7 +2380,7 @@ object SymDenotations {
* Both getters and setters are returned in this list.
*/
def paramAccessors(using Context): List[Symbol] =
unforcedDecls.filter(_.is(ParamAccessor))
unforcedDecls.filter(_.is(ParamAccessor))//, stopAt = sym => sym.is(Method, butNot = ParamAccessor))

/** The term parameter getters of this class. */
def paramGetters(using Context): List[Symbol] =
Expand Down
1 change: 1 addition & 0 deletions compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -776,6 +776,7 @@ class TreePickler(pickler: TastyPickler) {
if (flags.is(Exported)) writeModTag(EXPORTED)
if (flags.is(Given)) writeModTag(GIVEN)
if (flags.is(Implicit)) writeModTag(IMPLICIT)
if (flags.is(Tracked)) writeModTag(TRACKED)
if (isTerm) {
if (flags.is(Lazy, butNot = Module)) writeModTag(LAZY)
if (flags.is(AbsOverride)) { writeModTag(ABSTRACT); writeModTag(OVERRIDE) }
Expand Down
28 changes: 20 additions & 8 deletions compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -733,6 +733,7 @@ class TreeUnpickler(reader: TastyReader,
case INVISIBLE => addFlag(Invisible)
case TRANSPARENT => addFlag(Transparent)
case INFIX => addFlag(Infix)
case TRACKED => addFlag(Tracked)
case PRIVATEqualified =>
readByte()
privateWithin = readWithin
Expand Down Expand Up @@ -1011,12 +1012,20 @@ class TreeUnpickler(reader: TastyReader,
* but skip constructor arguments. Return any trees that were partially
* parsed in this way as InferredTypeTrees.
*/
def readParents(withArgs: Boolean)(using Context): List[Tree] =
def readParents(cls: ClassSymbol, withArgs: Boolean)(using Context): List[Tree] =
collectWhile(nextByte != SELFDEF && nextByte != DEFDEF) {
nextUnsharedTag match
case APPLY | TYPEAPPLY | BLOCK =>
if withArgs then readTree()
else InferredTypeTree().withType(readParentType())
if withArgs then
readTree()
else if cls.is(Dependent) then
val parentReader = fork
val parentCoreType = readParentType()
if parentCoreType.dealias.typeSymbol.is(Dependent)
then parentReader.readTree() // read the whole tree since we need to see the refinement
else InferredTypeTree().withType(parentCoreType)
else
InferredTypeTree().withType(readParentType())
case _ => readTpt()
}

Expand All @@ -1042,9 +1051,10 @@ class TreeUnpickler(reader: TastyReader,
while (bodyIndexer.reader.nextByte != DEFDEF) bodyIndexer.skipTree()
bodyIndexer.indexStats(end)
}
val parentReader = fork
val parents = readParents(withArgs = false)(using parentCtx)
val parentTypes = parents.map(_.tpe.dealias)
val parentsReader = fork
val parents = readParents(cls, withArgs = false)(using parentCtx)
val parentRefinements = mutable.LinkedHashMap[Name, Type]()
val parentTypes = parents.map(_.tpe.dealias.separateRefinements(parentRefinements))
val self =
if (nextByte == SELFDEF) {
readByte()
Expand All @@ -1057,11 +1067,13 @@ class TreeUnpickler(reader: TastyReader,
selfInfo = if (self.isEmpty) NoType else self.tpt.tpe
).integrateOpaqueMembers
val constr = readIndexedDef().asInstanceOf[DefDef]
if parentRefinements.nonEmpty then
integrateParentRefinements(constr.symbol, parentRefinements)
val mappedParents: LazyTreeList =
if parents.exists(_.isInstanceOf[InferredTypeTree]) then
// parents were not read fully, will need to be read again later on demand
new LazyReader(parentReader, localDummy, ctx.mode, ctx.source,
_.readParents(withArgs = true)
new LazyReader(parentsReader, localDummy, ctx.mode, ctx.source,
_.readParents(cls, withArgs = true)
.map(_.changeOwner(localDummy, constr.symbol)))
else parents

Expand Down
Loading
Loading