Skip to content

Commit 0fea788

Browse files
authored
Merge pull request #2331 from dotty-staging/fix-strawman-compile
Bug fixes to make collection strawman compile
2 parents 9567fc4 + 1ebff50 commit 0fea788

17 files changed

+190
-146
lines changed

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

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,11 +77,18 @@ object Config {
7777
final val traceDeepSubTypeRecursions = false
7878

7979
/** When explaining subtypes and this flag is set, also show the classes of the compared types. */
80-
final val verboseExplainSubtype = true
80+
final val verboseExplainSubtype = false
8181

8282
/** If this flag is set, take the fast path when comparing same-named type-aliases and types */
8383
final val fastPathForRefinedSubtype = true
8484

85+
/** If this flag is set, `TypeOps.normalizeToClassRefs` will insert forwarders
86+
* for type parameters of base classes. This is an optimization, which avoids
87+
* long alias chains. We should not rely on the optimization, though. So changing
88+
* the flag to false can be used for checking that everything works OK without it.
89+
*/
90+
final val forwardTypeParams = true
91+
8592
/** If this flag is set, and we compute `T1 { X = S1 }` & `T2 { X = S2 }` as a new
8693
* upper bound of a constrained parameter, try to align the refinements by computing
8794
* `S1 =:= S2` (which might instantiate type parameters).

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@ class ScalaSettings extends Settings.SettingGroup {
2424
val deprecation = BooleanSetting("-deprecation", "Emit warning and location for usages of deprecated APIs.")
2525
val migration = BooleanSetting("-migration", "Emit warning and location for migration issues from Scala 2.")
2626
val encoding = StringSetting("-encoding", "encoding", "Specify character encoding used by source files.", Properties.sourceEncoding)
27-
val explaintypes = BooleanSetting("-explaintypes", "Explain type errors in more detail.")
27+
val explainTypes = BooleanSetting("-explain-types", "Explain type errors in more detail.")
28+
val explainImplicits = BooleanSetting("-explain-implicits", "Explain implicit search errors in more detail.")
2829
val explain = BooleanSetting("-explain", "Explain errors in more detail.")
2930
val feature = BooleanSetting("-feature", "Emit warning and location for usages of features that should be imported explicitly.")
3031
val help = BooleanSetting("-help", "Print a synopsis of standard options")

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ object Mode {
3939
val TypevarsMissContext = newMode(4, "TypevarsMissContext")
4040
val CheckCyclic = newMode(5, "CheckCyclic")
4141

42+
/** We are looking at the arguments of a supercall */
4243
val InSuperCall = newMode(6, "InSuperCall")
4344

4445
/** Allow GADTFlexType labelled types to have their bounds adjusted */
@@ -81,7 +82,7 @@ object Mode {
8182
val ReadPositions = newMode(16, "ReadPositions")
8283

8384
val PatternOrType = Pattern | Type
84-
85+
8586
/** We are elaborating the fully qualified name of a package clause.
8687
* In this case, identifiers should never be imported.
8788
*/

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

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1381,12 +1381,15 @@ object SymDenotations {
13811381
/** Invalidate baseTypeRefCache, baseClasses and superClassBits on new run */
13821382
private def checkBasesUpToDate()(implicit ctx: Context) =
13831383
if (baseTypeRefValid != ctx.runId) {
1384-
baseTypeRefCache = new java.util.HashMap[CachedType, Type]
1384+
invalidateBaseTypeRefCache()
13851385
myBaseClasses = null
13861386
mySuperClassBits = null
13871387
baseTypeRefValid = ctx.runId
13881388
}
13891389

1390+
def invalidateBaseTypeRefCache() =
1391+
baseTypeRefCache = new java.util.HashMap[CachedType, Type]
1392+
13901393
private def computeBases(implicit ctx: Context): (List[ClassSymbol], BitSet) = {
13911394
if (myBaseClasses eq Nil) throw CyclicReference(this)
13921395
myBaseClasses = Nil
@@ -1712,18 +1715,23 @@ object SymDenotations {
17121715
/*>|>*/ ctx.debugTraceIndented(s"$tp.baseTypeRef($this)") /*<|<*/ {
17131716
tp match {
17141717
case tp: CachedType =>
1715-
checkBasesUpToDate()
1716-
var basetp = baseTypeRefCache get tp
1717-
if (basetp == null) {
1718-
baseTypeRefCache.put(tp, NoPrefix)
1719-
basetp = computeBaseTypeRefOf(tp)
1720-
if (isCachable(tp)) baseTypeRefCache.put(tp, basetp)
1721-
else baseTypeRefCache.remove(tp)
1722-
} else if (basetp == NoPrefix) {
1723-
baseTypeRefCache.put(tp, null)
1724-
throw CyclicReference(this)
1718+
try {
1719+
checkBasesUpToDate()
1720+
var basetp = baseTypeRefCache get tp
1721+
if (basetp == null) {
1722+
baseTypeRefCache.put(tp, NoPrefix)
1723+
basetp = computeBaseTypeRefOf(tp)
1724+
if (isCachable(tp)) baseTypeRefCache.put(tp, basetp)
1725+
else baseTypeRefCache.remove(tp)
1726+
} else if (basetp == NoPrefix)
1727+
throw CyclicReference(this)
1728+
basetp
1729+
}
1730+
catch {
1731+
case ex: Throwable =>
1732+
baseTypeRefCache.put(tp, null)
1733+
throw ex
17251734
}
1726-
basetp
17271735
case _ =>
17281736
computeBaseTypeRefOf(tp)
17291737
}

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

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -574,8 +574,15 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
574574
isNewSubType(tp1.parent, tp2)
575575
case tp1 @ HKApply(tycon1, args1) =>
576576
compareHkApply1(tp1, tycon1, args1, tp2)
577-
case EtaExpansion(tycon1) =>
578-
isSubType(tycon1, tp2)
577+
case tp1: HKTypeLambda =>
578+
def compareHKLambda = tp1 match {
579+
case EtaExpansion(tycon1) => isSubType(tycon1, tp2)
580+
case _ => tp2 match {
581+
case tp2: HKTypeLambda => false // this case was covered in thirdTry
582+
case _ => tp2.isHK && isSubType(tp1.resultType, tp2.appliedTo(tp1.paramRefs))
583+
}
584+
}
585+
compareHKLambda
579586
case AndType(tp11, tp12) =>
580587
// Rewrite (T111 | T112) & T12 <: T2 to (T111 & T12) <: T2 and (T112 | T12) <: T2
581588
// and analogously for T11 & (T121 | T122) & T12 <: T2
@@ -775,7 +782,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
775782
(v < 0 || isSubType(tp1, tp2))
776783
}
777784
isSub(args1.head, args2.head)
778-
} && isSubArgs(args1.tail, args2.tail, tparams)
785+
} && isSubArgs(args1.tail, args2.tail, tparams.tail)
779786

780787
/** Test whether `tp1` has a base type of the form `B[T1, ..., Tn]` where
781788
* - `B` derives from one of the class symbols of `tp2`,
@@ -1287,8 +1294,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
12871294
tl.integrate(tparams1, tparam1.paramInfoAsSeenFrom(tp1)).bounds &
12881295
tl.integrate(tparams2, tparam2.paramInfoAsSeenFrom(tp2)).bounds),
12891296
resultTypeExp = tl =>
1290-
original(tl.integrate(tparams1, tp1).appliedTo(tl.paramRefs),
1291-
tl.integrate(tparams2, tp2).appliedTo(tl.paramRefs)))
1297+
original(tp1.appliedTo(tl.paramRefs), tp2.appliedTo(tl.paramRefs)))
12921298
}
12931299

12941300
/** Try to distribute `&` inside type, detect and handle conflicts

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

Lines changed: 61 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -321,72 +321,11 @@ trait TypeOps { this: Context => // TODO: Make standalone object.
321321
}
322322
}
323323

324-
/** If we have member definitions
325-
*
326-
* type argSym v= from
327-
* type from v= to
328-
*
329-
* where the variances of both alias are the same, then enter a new definition
330-
*
331-
* type argSym v= to
332-
*
333-
* unless a definition for `argSym` already exists in the current scope.
334-
*/
335-
def forwardRef(argSym: Symbol, from: Symbol, to: TypeBounds, cls: ClassSymbol, decls: Scope) =
336-
argSym.info match {
337-
case info @ TypeBounds(lo2 @ TypeRef(_: ThisType, name), hi2) =>
338-
if (name == from.name &&
339-
(lo2 eq hi2) &&
340-
info.variance == to.variance &&
341-
!decls.lookup(argSym.name).exists) {
342-
// println(s"short-circuit ${argSym.name} was: ${argSym.info}, now: $to")
343-
enterArgBinding(argSym, to, cls, decls)
344-
}
345-
case _ =>
346-
}
347-
348-
349324
/** Normalize a list of parent types of class `cls` that may contain refinements
350325
* to a list of typerefs referring to classes, by converting all refinements to member
351326
* definitions in scope `decls`. Can add members to `decls` as a side-effect.
352327
*/
353328
def normalizeToClassRefs(parents: List[Type], cls: ClassSymbol, decls: Scope): List[TypeRef] = {
354-
355-
/** If we just entered the type argument binding
356-
*
357-
* type From = To
358-
*
359-
* and there is a type argument binding in a parent in `prefs` of the form
360-
*
361-
* type X = From
362-
*
363-
* then also add the binding
364-
*
365-
* type X = To
366-
*
367-
* to the current scope, provided (1) variances of both aliases are the same, and
368-
* (2) X is not yet defined in current scope. This "short-circuiting" prevents
369-
* long chains of aliases which would have to be traversed in type comparers.
370-
*
371-
* Note: Test i1401.scala shows that `forwardRefs` is also necessary
372-
* for typechecking in the case where self types refer to type parameters
373-
* that are upper-bounded by subclass instances.
374-
*/
375-
def forwardRefs(from: Symbol, to: Type, prefs: List[TypeRef]) = to match {
376-
case to @ TypeBounds(lo1, hi1) if lo1 eq hi1 =>
377-
for (pref <- prefs) {
378-
def forward()(implicit ctx: Context): Unit =
379-
for (argSym <- pref.decls)
380-
if (argSym is BaseTypeArg)
381-
forwardRef(argSym, from, to, cls, decls)
382-
pref.info match {
383-
case info: TempClassInfo => info.addSuspension(implicit ctx => forward())
384-
case _ => forward()
385-
}
386-
}
387-
case _ =>
388-
}
389-
390329
// println(s"normalizing $parents of $cls in ${cls.owner}") // !!! DEBUG
391330

392331
// A map consolidating all refinements arising from parent type parameters
@@ -429,16 +368,70 @@ trait TypeOps { this: Context => // TODO: Make standalone object.
429368
s"redefinition of ${decls.lookup(name).debugString} in ${cls.showLocated}")
430369
enterArgBinding(formals(name), refinedInfo, cls, decls)
431370
}
432-
// Forward definitions in super classes that have one of the refined parameters
433-
// as aliases directly to the refined info.
434-
// Note that this cannot be fused with the previous loop because we now
435-
// assume that all arguments have been entered in `decls`.
436-
refinements foreachBinding { (name, refinedInfo) =>
437-
forwardRefs(formals(name), refinedInfo, parentRefs)
438-
}
371+
372+
if (Config.forwardTypeParams)
373+
forwardParamBindings(parentRefs, refinements, cls, decls)
374+
439375
parentRefs
440376
}
441377

378+
/** Forward parameter bindings in baseclasses to argument types of
379+
* class `cls` if possible.
380+
* If there have member definitions
381+
*
382+
* type param v= middle
383+
* type middle v= to
384+
*
385+
* where the variances of both alias are the same, then enter a new definition
386+
*
387+
* type param v= to
388+
*
389+
* If multiple forwarders would be generated, join their `to` types with an `&`.
390+
*
391+
* @param cls The class for which parameter bindings should be forwarded
392+
* @param decls Its scope
393+
* @param parentRefs The parent type references of `cls`
394+
* @param paramBindings The type parameter bindings generated for `cls`
395+
*
396+
*/
397+
def forwardParamBindings(parentRefs: List[TypeRef],
398+
paramBindings: SimpleMap[TypeName, Type],
399+
cls: ClassSymbol, decls: Scope)(implicit ctx: Context) = {
400+
401+
def forwardRef(argSym: Symbol, from: TypeName, to: TypeAlias) = argSym.info match {
402+
case info @ TypeAlias(TypeRef(_: ThisType, `from`)) if info.variance == to.variance =>
403+
val existing = decls.lookup(argSym.name)
404+
if (existing.exists) existing.info = existing.info & to
405+
else enterArgBinding(argSym, to, cls, decls)
406+
case _ =>
407+
}
408+
409+
def forwardRefs(from: TypeName, to: Type) = to match {
410+
case to: TypeAlias =>
411+
for (pref <- parentRefs) {
412+
def forward()(implicit ctx: Context): Unit =
413+
for (argSym <- pref.decls)
414+
if (argSym is BaseTypeArg) forwardRef(argSym, from, to)
415+
pref.info match {
416+
case info: TempClassInfo => info.addSuspension(implicit ctx => forward())
417+
case _ => forward()
418+
}
419+
}
420+
case _ =>
421+
}
422+
423+
paramBindings.foreachBinding(forwardRefs)
424+
}
425+
426+
/** Used only for debugging: All BaseTypeArg definitions in
427+
* `cls` and all its base classes.
428+
*/
429+
def allBaseTypeArgs(cls: ClassSymbol)(implicit ctx: Context) =
430+
for { bc <- cls.baseClasses
431+
sym <- bc.info.decls.toList
432+
if sym.is(BaseTypeArg)
433+
} yield sym
434+
442435
/** An argument bounds violation is a triple consisting of
443436
* - the argument tree
444437
* - a string "upper" or "lower" indicating which bound is violated

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3035,6 +3035,8 @@ object Types {
30353035
override def hashCode: Int = identityHash
30363036
override def equals(that: Any) = this eq that.asInstanceOf[AnyRef]
30373037

3038+
def withName(name: Name): this.type = { myRepr = name; this }
3039+
30383040
private var myRepr: Name = null
30393041
def repr(implicit ctx: Context): Name = {
30403042
if (myRepr == null) myRepr = SkolemName.fresh()

compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala

Lines changed: 18 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -119,24 +119,26 @@ class TreePickler(pickler: TastyPickler) {
119119
pickleType(c.symbolValue.termRef)
120120
}
121121

122-
def pickleType(tpe0: Type, richTypes: Boolean = false)(implicit ctx: Context): Unit = try {
122+
def pickleType(tpe0: Type, richTypes: Boolean = false)(implicit ctx: Context): Unit = {
123123
val tpe = tpe0.stripTypeVar
124-
val prev = pickledTypes.get(tpe)
125-
if (prev == null) {
126-
pickledTypes.put(tpe, currentAddr)
127-
pickleNewType(tpe, richTypes)
128-
}
129-
else {
130-
writeByte(SHARED)
131-
writeRef(prev.asInstanceOf[Addr])
124+
try {
125+
val prev = pickledTypes.get(tpe)
126+
if (prev == null) {
127+
pickledTypes.put(tpe, currentAddr)
128+
pickleNewType(tpe, richTypes)
129+
}
130+
else {
131+
writeByte(SHARED)
132+
writeRef(prev.asInstanceOf[Addr])
133+
}
134+
} catch {
135+
case ex: AssertionError =>
136+
println(i"error when pickling type $tpe")
137+
throw ex
132138
}
133-
} catch {
134-
case ex: AssertionError =>
135-
println(i"error when pickling type $tpe0")
136-
throw ex
137139
}
138140

139-
private def pickleNewType(tpe: Type, richTypes: Boolean)(implicit ctx: Context): Unit = try { tpe match {
141+
private def pickleNewType(tpe: Type, richTypes: Boolean)(implicit ctx: Context): Unit = tpe match {
140142
case AppliedType(tycon, args) =>
141143
writeByte(APPLIEDtype)
142144
withLength { pickleType(tycon); args.foreach(pickleType(_)) }
@@ -241,21 +243,10 @@ class TreePickler(pickler: TastyPickler) {
241243
pickleMethodic(POLYtype, tpe)
242244
case tpe: MethodType if richTypes =>
243245
pickleMethodic(METHODtype, tpe)
244-
case tpe: TypeParamRef =>
245-
if (!pickleParamRef(tpe))
246-
// TODO figure out why this case arises in e.g. pickling AbstractFileReader.
247-
ctx.typerState.constraint.entry(tpe) match {
248-
case TypeBounds(lo, hi) if lo eq hi => pickleNewType(lo, richTypes)
249-
case _ => assert(false, s"orphan poly parameter: $tpe")
250-
}
251-
case tpe: TermParamRef =>
252-
assert(pickleParamRef(tpe), s"orphan method parameter: $tpe")
246+
case tpe: ParamRef =>
247+
assert(pickleParamRef(tpe), s"orphan parameter reference: $tpe")
253248
case tpe: LazyRef =>
254249
pickleType(tpe.ref)
255-
}} catch {
256-
case ex: AssertionError =>
257-
println(i"error while pickling type $tpe")
258-
throw ex
259250
}
260251

261252
def picklePackageRef(pkg: Symbol)(implicit ctx: Context): Unit = {

compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ class PlainPrinter(_ctx: Context) extends Printer {
174174
def paramText(name: Name, bounds: TypeBounds): Text = name.toString ~ toText(bounds)
175175
changePrec(GlobalPrec) {
176176
"[" ~ Text((tp.paramNames, tp.paramInfos).zipped.map(paramText), ", ") ~
177-
"]" ~ (" => " provided !tp.resultType.isInstanceOf[MethodType]) ~
177+
"]" ~ lambdaHash(tp) ~ (" => " provided !tp.resultType.isInstanceOf[MethodType]) ~
178178
toTextGlobal(tp.resultType)
179179
}
180180
case tp: TypeParamRef =>

0 commit comments

Comments
 (0)