Skip to content

Commit a8b9b78

Browse files
authored
Merge pull request #6842 from dotty-staging/wip/avoid-cycle-6603-12
Fix #6603: various changes to avoid cyclic references in separate compilation
2 parents e4f06aa + b602d83 commit a8b9b78

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+393
-262
lines changed

compiler/src/dotty/tools/dotc/ast/Desugar.scala

Lines changed: 63 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import util.Spans._, Types._, Contexts._, Constants._, Names._, NameOps._, Flags
77
import Symbols._, StdNames._, Trees._
88
import Decorators._, transform.SymUtils._
99
import NameKinds.{UniqueName, EvidenceParamName, DefaultGetterName}
10-
import typer.FrontEnd
10+
import typer.{FrontEnd, Namer}
1111
import util.{Property, SourceFile, SourcePosition}
1212
import util.NameTransformer.avoidIllegalChars
1313
import collection.mutable.ListBuffer
@@ -74,46 +74,45 @@ object desugar {
7474
def derivedTree(sym: Symbol)(implicit ctx: Context): tpd.Tree = tpd.ref(sym)
7575
}
7676

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

83-
/** Make sure that for all enclosing module classes their companion classes
84-
* are completed. Reason: We need the constructor of such companion classes to
85-
* be completed so that OriginalSymbol attachments are pushed to DerivedTypeTrees
86-
* in apply/unapply methods.
80+
/** Complete the appropriate constructors so that OriginalSymbol attachments are
81+
* pushed to DerivedTypeTrees.
8782
*/
88-
override def ensureCompletions(implicit ctx: Context): Unit =
83+
override def ensureCompletions(implicit ctx: Context): Unit = {
84+
def completeConstructor(sym: Symbol) =
85+
sym.infoOrCompleter match {
86+
case completer: Namer#ClassCompleter =>
87+
completer.completeConstructor(sym)
88+
case _ =>
89+
}
90+
8991
if (!ctx.owner.is(Package))
9092
if (ctx.owner.isClass) {
91-
ctx.owner.ensureCompleted()
93+
completeConstructor(ctx.owner)
9294
if (ctx.owner.is(ModuleClass))
93-
ctx.owner.linkedClass.ensureCompleted()
95+
completeConstructor(ctx.owner.linkedClass)
9496
}
9597
else ensureCompletions(ctx.outer)
98+
}
9699

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

131130
/** A type definition copied from `tdef` with a rhs typetree derived from it */
132-
def derivedTypeParam(tdef: TypeDef, suffix: String = "")(implicit ctx: Context): TypeDef =
131+
def derivedTypeParam(tdef: TypeDef)(implicit ctx: Context): TypeDef =
133132
cpy.TypeDef(tdef)(
134-
name = tdef.name ++ suffix,
135-
rhs = DerivedFromParamTree(suffix).withSpan(tdef.rhs.span).watching(tdef)
133+
rhs = DerivedFromParamTree().withSpan(tdef.rhs.span).watching(tdef)
136134
)
137135

138136
/** A derived type definition watching `sym` */
139137
def derivedTypeParam(sym: TypeSymbol)(implicit ctx: Context): TypeDef =
140-
TypeDef(sym.name, DerivedFromParamTree("").watching(sym)).withFlags(TypeParam)
138+
TypeDef(sym.name, DerivedFromParamTree().watching(sym)).withFlags(TypeParam)
141139

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

147145
// ----- Desugar methods -------------------------------------------------
148146

@@ -269,8 +267,8 @@ object desugar {
269267
def defaultGetter: DefDef =
270268
DefDef(
271269
name = DefaultGetterName(methName, n),
272-
tparams = meth.tparams.map(tparam => dropContextBounds(toDefParam(tparam))),
273-
vparamss = takeUpTo(normalizedVparamss.nestedMap(toDefParam), n),
270+
tparams = meth.tparams.map(tparam => dropContextBounds(toDefParam(tparam, keepAnnotations = true))),
271+
vparamss = takeUpTo(normalizedVparamss.nestedMap(toDefParam(_, keepAnnotations = true)), n),
274272
tpt = TypeTree(),
275273
rhs = vparam.rhs
276274
)
@@ -374,10 +372,16 @@ object desugar {
374372

375373
@sharable private val synthetic = Modifiers(Synthetic)
376374

377-
private def toDefParam(tparam: TypeDef): TypeDef =
378-
tparam.withMods(tparam.rawMods & EmptyFlags | Param)
379-
private def toDefParam(vparam: ValDef): ValDef =
380-
vparam.withMods(vparam.rawMods & (GivenOrImplicit | Erased) | Param)
375+
private def toDefParam(tparam: TypeDef, keepAnnotations: Boolean): TypeDef = {
376+
var mods = tparam.rawMods
377+
if (!keepAnnotations) mods = mods.withAnnotations(Nil)
378+
tparam.withMods(mods & EmptyFlags | Param)
379+
}
380+
private def toDefParam(vparam: ValDef, keepAnnotations: Boolean): ValDef = {
381+
var mods = vparam.rawMods
382+
if (!keepAnnotations) mods = mods.withAnnotations(Nil)
383+
vparam.withMods(mods & (GivenOrImplicit | Erased) | Param)
384+
}
381385

382386
/** The expansion of a class definition. See inline comments for what is involved */
383387
def classDef(cdef: TypeDef)(implicit ctx: Context): Tree = {
@@ -439,7 +443,7 @@ object desugar {
439443
else originalTparams
440444
}
441445
else originalTparams
442-
val constrTparams = impliedTparams.map(toDefParam)
446+
val constrTparams = impliedTparams.map(toDefParam(_, keepAnnotations = false))
443447
val constrVparamss =
444448
if (originalVparamss.isEmpty) { // ensure parameter list is non-empty
445449
if (isCaseClass && originalTparams.isEmpty)
@@ -449,7 +453,7 @@ object desugar {
449453
ctx.error("Case classes should have a non-implicit parameter list", namePos)
450454
ListOfNil
451455
}
452-
else originalVparamss.nestedMap(toDefParam)
456+
else originalVparamss.nestedMap(toDefParam(_, keepAnnotations = false))
453457
val constr = cpy.DefDef(constr1)(tparams = constrTparams, vparamss = constrVparamss)
454458

455459
val (normalizedBody, enumCases, enumCompanionRef) = {
@@ -461,7 +465,7 @@ object desugar {
461465
defDef(
462466
addEvidenceParams(
463467
cpy.DefDef(ddef)(tparams = constrTparams),
464-
evidenceParams(constr1).map(toDefParam))))
468+
evidenceParams(constr1).map(toDefParam(_, keepAnnotations = false)))))
465469
case stat =>
466470
stat
467471
}
@@ -484,8 +488,19 @@ object desugar {
484488

485489
def anyRef = ref(defn.AnyRefAlias.typeRef)
486490

487-
val derivedTparams = constrTparams.map(derivedTypeParam(_))
488-
val derivedVparamss = constrVparamss.nestedMap(derivedTermParam(_))
491+
// Annotations are dropped from the constructor parameters but should be
492+
// preserved in all derived parameters.
493+
val derivedTparams = {
494+
val impliedTparamsIt = impliedTparams.toIterator
495+
constrTparams.map(tparam => derivedTypeParam(tparam)
496+
.withAnnotations(impliedTparamsIt.next().mods.annotations))
497+
}
498+
val derivedVparamss = {
499+
val constrVparamsIt = constrVparamss.toIterator.flatten
500+
constrVparamss.nestedMap(vparam => derivedTermParam(vparam)
501+
.withAnnotations(constrVparamsIt.next().mods.annotations))
502+
}
503+
489504
val arity = constrVparamss.head.length
490505

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

776791
val cdef1 = addEnumFlags {
777-
val originalTparamsIt = impliedTparams.toIterator
778-
val originalVparamsIt = originalVparamss.toIterator.flatten
779-
val tparamAccessors = derivedTparams.map(_.withMods(originalTparamsIt.next().mods))
792+
val tparamAccessors = {
793+
val impliedTparamsIt = impliedTparams.toIterator
794+
derivedTparams.map(_.withMods(impliedTparamsIt.next().mods))
795+
}
780796
val caseAccessor = if (isCaseClass) CaseAccessor else EmptyFlags
781-
val vparamAccessors = derivedVparamss match {
782-
case first :: rest =>
783-
first.map(_.withMods(originalVparamsIt.next().mods | caseAccessor)) ++
784-
rest.flatten.map(_.withMods(originalVparamsIt.next().mods))
785-
case _ =>
786-
Nil
797+
val vparamAccessors = {
798+
val originalVparamsIt = originalVparamss.toIterator.flatten
799+
derivedVparamss match {
800+
case first :: rest =>
801+
first.map(_.withMods(originalVparamsIt.next().mods | caseAccessor)) ++
802+
rest.flatten.map(_.withMods(originalVparamsIt.next().mods))
803+
case _ =>
804+
Nil
805+
}
787806
}
788807
cpy.TypeDef(cdef: TypeDef)(
789808
name = className,

compiler/src/dotty/tools/dotc/ast/TreeMapWithImplicits.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ class TreeMapWithImplicits extends tpd.TreeMap {
119119
}
120120
catch {
121121
case ex: TypeError =>
122-
ctx.error(ex.toMessage, tree.sourcePos)
122+
ctx.error(ex, tree.sourcePos)
123123
tree
124124
}
125125
}

compiler/src/dotty/tools/dotc/ast/Trees.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,8 @@ object Trees {
327327

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

330+
def withAnnotations(annots: List[untpd.Tree]): ThisTree[Untyped] = withMods(rawMods.withAnnotations(annots))
331+
330332
def withMods(mods: untpd.Modifiers): ThisTree[Untyped] = {
331333
val tree = if (myMods == null || (myMods == mods)) this else cloneIn(source)
332334
tree.setMods(mods)

compiler/src/dotty/tools/dotc/config/ScalaSettings.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ class ScalaSettings extends Settings.SettingGroup {
103103
val YdebugNames: Setting[Boolean] = BooleanSetting("-Ydebug-names", "Show internal representation of names")
104104
val YdebugPos: Setting[Boolean] = BooleanSetting("-Ydebug-pos", "Show full source positions including spans")
105105
val YdebugTreeWithId: Setting[Int] = IntSetting("-Ydebug-tree-with-id", "Print the stack trace when the tree with the given id is created", Int.MinValue)
106+
val YdebugTypeError: Setting[Boolean] = BooleanSetting("-Ydebug-type-error", "Print the stack trace when a TypeError is caught", false)
106107
val YtermConflict: Setting[String] = ChoiceSetting("-Yresolve-term-conflict", "strategy", "Resolve term conflicts", List("package", "object", "error"), "error")
107108
val Ylog: Setting[List[String]] = PhasesSetting("-Ylog", "Log operations during")
108109
val YemitTastyInClass: Setting[Boolean] = BooleanSetting("-Yemit-tasty-in-class", "Generate tasty in the .class file and add an empty *.hasTasty file.")

compiler/src/dotty/tools/dotc/core/Definitions.scala

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@ class Definitions {
189189

190190
private def completeClass(cls: ClassSymbol, ensureCtor: Boolean = true): ClassSymbol = {
191191
if (ensureCtor) ensureConstructor(cls, EmptyScope)
192-
if (cls.linkedClass.exists) cls.linkedClass.info = NoType
192+
if (cls.linkedClass.exists) cls.linkedClass.markAbsent()
193193
cls
194194
}
195195

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

299-
// The companion object doesn't really exist, `NoType` is the general
300-
// technique to do that. Here we need to set it before completing
301-
// attempt to load Object's classfile, which causes issue #1648.
299+
// The companion object doesn't really exist, so it needs to be marked as
300+
// absent. Here we need to set it before completing attempt to load Object's
301+
// classfile, which causes issue #1648.
302302
val companion = JavaLangPackageVal.info.decl(nme.Object).symbol
303-
companion.moduleClass.info = NoType // to indicate that it does not really exist
304-
companion.info = NoType // to indicate that it does not really exist
303+
companion.moduleClass.markAbsent()
304+
companion.markAbsent()
305+
305306
completeClass(cls)
306307
}
307308
def ObjectType: TypeRef = ObjectClass.typeRef

compiler/src/dotty/tools/dotc/core/Flags.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -452,7 +452,7 @@ object Flags {
452452
* is completed)
453453
*/
454454
val AfterLoadFlags: FlagSet = commonFlags(
455-
FromStartFlags, AccessFlags, Final, AccessorOrSealed, LazyOrTrait, SelfName)
455+
FromStartFlags, AccessFlags, Final, AccessorOrSealed, LazyOrTrait, SelfName, JavaDefined)
456456

457457

458458
/** A value that's unstable unless complemented with a Stable flag */

0 commit comments

Comments
 (0)