Skip to content

Fix #6603: various changes to avoid cyclic references in separate compilation #6842

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 21 commits into from
Jul 15, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
16184eb
Fix classpath when running `dotc -with-compiler` from sbt
smarter Jun 26, 2019
e7e3718
Add -Ydebug-type-error to print the stack when a TypeError is caught
smarter Jun 26, 2019
4050dd2
Make JavaDefined an AfterLoadFlags
smarter Jul 2, 2019
98cd179
Better default toString for LazyType
smarter Jul 5, 2019
bdc5b6f
Move "is this a value?" check from Typer to Erasure
smarter Jun 27, 2019
85f0f4f
Consistently use markAbsent/isAbsent
smarter Jun 5, 2019
eb8e8ff
Move SymDenotation#proxy to SymbolLoader#proxy
smarter Jul 4, 2019
5c37a38
Add new SymbolLoader: NoLoader
smarter Jul 5, 2019
a92a209
Make isAbsent force less
smarter Jul 5, 2019
ccde318
accessBoundary: Avoid forcing completion when possible
smarter Jul 2, 2019
f648faa
Namer: set privateWithin at symbol creation time
smarter Jul 4, 2019
9fc9f21
Scala2Unpickler: set privateWithin at symbol creation time
smarter Jul 4, 2019
1e8860a
ClassfileParser: set privateWithin at symbol creation time for members
smarter Jul 4, 2019
7fc5deb
Fix #6603: Make privateWithin force less
smarter Jul 4, 2019
f28ed39
Remove unused suffix param of DerivedFromParamTree
smarter Jul 11, 2019
d8391c6
TempClassInfo#finalize: remove selfInfo parameter
smarter Jul 11, 2019
d9ade19
When typing a DerivedFromParamTree, only complete constructors
smarter Jul 11, 2019
b264bf0
Add explicit check against self-annotated classes
smarter Jul 7, 2019
9fcceb4
Make checkNonCyclic force less
smarter Jul 6, 2019
777fa57
Make checkNonCyclicInherited force less
smarter Jul 8, 2019
b602d83
Drop annotations from constructor parameters
smarter Jul 6, 2019
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
107 changes: 63 additions & 44 deletions compiler/src/dotty/tools/dotc/ast/Desugar.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import util.Spans._, Types._, Contexts._, Constants._, Names._, NameOps._, Flags
import Symbols._, StdNames._, Trees._
import Decorators._, transform.SymUtils._
import NameKinds.{UniqueName, EvidenceParamName, DefaultGetterName}
import typer.FrontEnd
import typer.{FrontEnd, Namer}
import util.{Property, SourceFile, SourcePosition}
import util.NameTransformer.avoidIllegalChars
import collection.mutable.ListBuffer
Expand Down Expand Up @@ -74,46 +74,45 @@ object desugar {
def derivedTree(sym: Symbol)(implicit ctx: Context): tpd.Tree = tpd.ref(sym)
}

/** A type tree that computes its type from an existing parameter.
* @param suffix String difference between existing parameter (call it `P`) and parameter owning the
* DerivedTypeTree (call it `O`). We have: `O.name == P.name + suffix`.
*/
class DerivedFromParamTree(suffix: String)(implicit @constructorOnly src: SourceFile) extends DerivedTypeTree {
/** A type tree that computes its type from an existing parameter. */
class DerivedFromParamTree()(implicit @constructorOnly src: SourceFile) extends DerivedTypeTree {

/** Make sure that for all enclosing module classes their companion classes
* are completed. Reason: We need the constructor of such companion classes to
* be completed so that OriginalSymbol attachments are pushed to DerivedTypeTrees
* in apply/unapply methods.
/** Complete the appropriate constructors so that OriginalSymbol attachments are
* pushed to DerivedTypeTrees.
*/
override def ensureCompletions(implicit ctx: Context): Unit =
override def ensureCompletions(implicit ctx: Context): Unit = {
def completeConstructor(sym: Symbol) =
sym.infoOrCompleter match {
case completer: Namer#ClassCompleter =>
completer.completeConstructor(sym)
case _ =>
}

if (!ctx.owner.is(Package))
if (ctx.owner.isClass) {
ctx.owner.ensureCompleted()
completeConstructor(ctx.owner)
if (ctx.owner.is(ModuleClass))
ctx.owner.linkedClass.ensureCompleted()
completeConstructor(ctx.owner.linkedClass)
}
else ensureCompletions(ctx.outer)
}

/** Return info of original symbol, where all references to siblings of the
* original symbol (i.e. sibling and original symbol have the same owner)
* are rewired to like-named* parameters or accessors in the scope enclosing
* are rewired to same-named parameters or accessors in the scope enclosing
* the current scope. The current scope is the scope owned by the defined symbol
* itself, that's why we have to look one scope further out. If the resulting
* type is an alias type, dealias it. This is necessary because the
* accessor of a type parameter is a private type alias that cannot be accessed
* from subclasses.
*
* (*) like-named means:
*
* parameter name == reference name ++ suffix
*/
def derivedTree(sym: Symbol)(implicit ctx: Context): tpd.TypeTree = {
val relocate = new TypeMap {
val originalOwner = sym.owner
def apply(tp: Type) = tp match {
case tp: NamedType if tp.symbol.exists && (tp.symbol.owner eq originalOwner) =>
val defctx = ctx.outersIterator.dropWhile(_.scope eq ctx.scope).next()
var local = defctx.denotNamed(tp.name ++ suffix).suchThat(_.isParamOrAccessor).symbol
var local = defctx.denotNamed(tp.name).suchThat(_.isParamOrAccessor).symbol
if (local.exists) (defctx.owner.thisType select local).dealiasKeepAnnots
else {
def msg =
Expand All @@ -129,20 +128,19 @@ object desugar {
}

/** A type definition copied from `tdef` with a rhs typetree derived from it */
def derivedTypeParam(tdef: TypeDef, suffix: String = "")(implicit ctx: Context): TypeDef =
def derivedTypeParam(tdef: TypeDef)(implicit ctx: Context): TypeDef =
cpy.TypeDef(tdef)(
name = tdef.name ++ suffix,
rhs = DerivedFromParamTree(suffix).withSpan(tdef.rhs.span).watching(tdef)
rhs = DerivedFromParamTree().withSpan(tdef.rhs.span).watching(tdef)
)

/** A derived type definition watching `sym` */
def derivedTypeParam(sym: TypeSymbol)(implicit ctx: Context): TypeDef =
TypeDef(sym.name, DerivedFromParamTree("").watching(sym)).withFlags(TypeParam)
TypeDef(sym.name, DerivedFromParamTree().watching(sym)).withFlags(TypeParam)

/** A value definition copied from `vdef` with a tpt typetree derived from it */
def derivedTermParam(vdef: ValDef)(implicit ctx: Context): ValDef =
cpy.ValDef(vdef)(
tpt = DerivedFromParamTree("").withSpan(vdef.tpt.span).watching(vdef))
tpt = DerivedFromParamTree().withSpan(vdef.tpt.span).watching(vdef))

// ----- Desugar methods -------------------------------------------------

Expand Down Expand Up @@ -269,8 +267,8 @@ object desugar {
def defaultGetter: DefDef =
DefDef(
name = DefaultGetterName(methName, n),
tparams = meth.tparams.map(tparam => dropContextBounds(toDefParam(tparam))),
vparamss = takeUpTo(normalizedVparamss.nestedMap(toDefParam), n),
tparams = meth.tparams.map(tparam => dropContextBounds(toDefParam(tparam, keepAnnotations = true))),
vparamss = takeUpTo(normalizedVparamss.nestedMap(toDefParam(_, keepAnnotations = true)), n),
tpt = TypeTree(),
rhs = vparam.rhs
)
Expand Down Expand Up @@ -374,10 +372,16 @@ object desugar {

@sharable private val synthetic = Modifiers(Synthetic)

private def toDefParam(tparam: TypeDef): TypeDef =
tparam.withMods(tparam.rawMods & EmptyFlags | Param)
private def toDefParam(vparam: ValDef): ValDef =
vparam.withMods(vparam.rawMods & (GivenOrImplicit | Erased) | Param)
private def toDefParam(tparam: TypeDef, keepAnnotations: Boolean): TypeDef = {
var mods = tparam.rawMods
if (!keepAnnotations) mods = mods.withAnnotations(Nil)
tparam.withMods(mods & EmptyFlags | Param)
}
private def toDefParam(vparam: ValDef, keepAnnotations: Boolean): ValDef = {
var mods = vparam.rawMods
if (!keepAnnotations) mods = mods.withAnnotations(Nil)
vparam.withMods(mods & (GivenOrImplicit | Erased) | Param)
}

/** The expansion of a class definition. See inline comments for what is involved */
def classDef(cdef: TypeDef)(implicit ctx: Context): Tree = {
Expand Down Expand Up @@ -439,7 +443,7 @@ object desugar {
else originalTparams
}
else originalTparams
val constrTparams = impliedTparams.map(toDefParam)
val constrTparams = impliedTparams.map(toDefParam(_, keepAnnotations = false))
val constrVparamss =
if (originalVparamss.isEmpty) { // ensure parameter list is non-empty
if (isCaseClass && originalTparams.isEmpty)
Expand All @@ -449,7 +453,7 @@ object desugar {
ctx.error("Case classes should have a non-implicit parameter list", namePos)
ListOfNil
}
else originalVparamss.nestedMap(toDefParam)
else originalVparamss.nestedMap(toDefParam(_, keepAnnotations = false))
val constr = cpy.DefDef(constr1)(tparams = constrTparams, vparamss = constrVparamss)

val (normalizedBody, enumCases, enumCompanionRef) = {
Expand All @@ -461,7 +465,7 @@ object desugar {
defDef(
addEvidenceParams(
cpy.DefDef(ddef)(tparams = constrTparams),
evidenceParams(constr1).map(toDefParam))))
evidenceParams(constr1).map(toDefParam(_, keepAnnotations = false)))))
case stat =>
stat
}
Expand All @@ -484,8 +488,19 @@ object desugar {

def anyRef = ref(defn.AnyRefAlias.typeRef)

val derivedTparams = constrTparams.map(derivedTypeParam(_))
val derivedVparamss = constrVparamss.nestedMap(derivedTermParam(_))
// Annotations are dropped from the constructor parameters but should be
// preserved in all derived parameters.
val derivedTparams = {
val impliedTparamsIt = impliedTparams.toIterator
constrTparams.map(tparam => derivedTypeParam(tparam)
.withAnnotations(impliedTparamsIt.next().mods.annotations))
}
val derivedVparamss = {
val constrVparamsIt = constrVparamss.toIterator.flatten
constrVparamss.nestedMap(vparam => derivedTermParam(vparam)
.withAnnotations(constrVparamsIt.next().mods.annotations))
}

val arity = constrVparamss.head.length

val classTycon: Tree = TypeRefTree() // watching is set at end of method
Expand Down Expand Up @@ -774,16 +789,20 @@ object desugar {
}

val cdef1 = addEnumFlags {
val originalTparamsIt = impliedTparams.toIterator
val originalVparamsIt = originalVparamss.toIterator.flatten
val tparamAccessors = derivedTparams.map(_.withMods(originalTparamsIt.next().mods))
val tparamAccessors = {
val impliedTparamsIt = impliedTparams.toIterator
derivedTparams.map(_.withMods(impliedTparamsIt.next().mods))
}
val caseAccessor = if (isCaseClass) CaseAccessor else EmptyFlags
val vparamAccessors = derivedVparamss match {
case first :: rest =>
first.map(_.withMods(originalVparamsIt.next().mods | caseAccessor)) ++
rest.flatten.map(_.withMods(originalVparamsIt.next().mods))
case _ =>
Nil
val vparamAccessors = {
val originalVparamsIt = originalVparamss.toIterator.flatten
derivedVparamss match {
case first :: rest =>
first.map(_.withMods(originalVparamsIt.next().mods | caseAccessor)) ++
rest.flatten.map(_.withMods(originalVparamsIt.next().mods))
case _ =>
Nil
}
}
cpy.TypeDef(cdef: TypeDef)(
name = className,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ class TreeMapWithImplicits extends tpd.TreeMap {
}
catch {
case ex: TypeError =>
ctx.error(ex.toMessage, tree.sourcePos)
ctx.error(ex, tree.sourcePos)
tree
}
}
Expand Down
2 changes: 2 additions & 0 deletions compiler/src/dotty/tools/dotc/ast/Trees.scala
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,8 @@ object Trees {

def rawComment: Option[Comment] = getAttachment(DocComment)

def withAnnotations(annots: List[untpd.Tree]): ThisTree[Untyped] = withMods(rawMods.withAnnotations(annots))

def withMods(mods: untpd.Modifiers): ThisTree[Untyped] = {
val tree = if (myMods == null || (myMods == mods)) this else cloneIn(source)
tree.setMods(mods)
Expand Down
1 change: 1 addition & 0 deletions compiler/src/dotty/tools/dotc/config/ScalaSettings.scala
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ class ScalaSettings extends Settings.SettingGroup {
val YdebugNames: Setting[Boolean] = BooleanSetting("-Ydebug-names", "Show internal representation of names")
val YdebugPos: Setting[Boolean] = BooleanSetting("-Ydebug-pos", "Show full source positions including spans")
val YdebugTreeWithId: Setting[Int] = IntSetting("-Ydebug-tree-with-id", "Print the stack trace when the tree with the given id is created", Int.MinValue)
val YdebugTypeError: Setting[Boolean] = BooleanSetting("-Ydebug-type-error", "Print the stack trace when a TypeError is caught", false)
val YtermConflict: Setting[String] = ChoiceSetting("-Yresolve-term-conflict", "strategy", "Resolve term conflicts", List("package", "object", "error"), "error")
val Ylog: Setting[List[String]] = PhasesSetting("-Ylog", "Log operations during")
val YemitTastyInClass: Setting[Boolean] = BooleanSetting("-Yemit-tasty-in-class", "Generate tasty in the .class file and add an empty *.hasTasty file.")
Expand Down
13 changes: 7 additions & 6 deletions compiler/src/dotty/tools/dotc/core/Definitions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ class Definitions {

private def completeClass(cls: ClassSymbol, ensureCtor: Boolean = true): ClassSymbol = {
if (ensureCtor) ensureConstructor(cls, EmptyScope)
if (cls.linkedClass.exists) cls.linkedClass.info = NoType
if (cls.linkedClass.exists) cls.linkedClass.markAbsent()
cls
}

Expand Down Expand Up @@ -296,12 +296,13 @@ class Definitions {
cls.info = ClassInfo(cls.owner.thisType, cls, AnyClass.typeRef :: Nil, newScope)
cls.setFlag(NoInits)

// The companion object doesn't really exist, `NoType` is the general
// technique to do that. Here we need to set it before completing
// attempt to load Object's classfile, which causes issue #1648.
// The companion object doesn't really exist, so it needs to be marked as
// absent. Here we need to set it before completing attempt to load Object's
// classfile, which causes issue #1648.
val companion = JavaLangPackageVal.info.decl(nme.Object).symbol
companion.moduleClass.info = NoType // to indicate that it does not really exist
companion.info = NoType // to indicate that it does not really exist
companion.moduleClass.markAbsent()
companion.markAbsent()

completeClass(cls)
}
def ObjectType: TypeRef = ObjectClass.typeRef
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/core/Flags.scala
Original file line number Diff line number Diff line change
Expand Up @@ -452,7 +452,7 @@ object Flags {
* is completed)
*/
val AfterLoadFlags: FlagSet = commonFlags(
FromStartFlags, AccessFlags, Final, AccessorOrSealed, LazyOrTrait, SelfName)
FromStartFlags, AccessFlags, Final, AccessorOrSealed, LazyOrTrait, SelfName, JavaDefined)


/** A value that's unstable unless complemented with a Stable flag */
Expand Down
Loading