Skip to content

Commit 38b3626

Browse files
committed
Normalize parent types so that first one always refers to a class, not a trait.
Also: forward type parameter references of newly added to class scope. This is necessary, or the pattern match in test.scala would fail. Need to find out why.
1 parent 6eece75 commit 38b3626

File tree

5 files changed

+97
-27
lines changed

5 files changed

+97
-27
lines changed

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -284,7 +284,8 @@ class Definitions(implicit ctx: Context) {
284284
object FunctionType {
285285
def apply(args: List[Type], resultType: Type) =
286286
FunctionClass(args.length).typeRef.appliedTo(args :+ resultType)
287-
def unapply(ft: Type) = {
287+
def unapply(ft: Type): Option[(List[Type], Type)] = { // Dotty deviation: Type annotation needed because inferred type
288+
// is Some[(List[Type], Type)] | None, which is not a legal unapply type.
288289
val tsym = ft.typeSymbol
289290
lazy val targs = ft.typeArgs
290291
if ((FunctionClasses contains tsym) &&

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

Lines changed: 34 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -85,18 +85,43 @@ trait TypeOps { this: Context =>
8585
}
8686
}
8787

88+
private def enterArgBinding(formal: Symbol, info: Type, cls: ClassSymbol, decls: Scope) = {
89+
val typeArgFlag = if (formal is Local) TypeArgument else EmptyFlags
90+
val sym = ctx.newSymbol(cls, formal.name, formal.flags & RetainedTypeArgFlags | typeArgFlag, info)
91+
cls.enter(sym, decls)
92+
}
93+
94+
/** If we have member definitions
95+
*
96+
* type argSym v= from
97+
* type from v= to
98+
*
99+
* where the variances of both alias are the same, then enter a new definition
100+
*
101+
* type argSym v= to
102+
*
103+
* unless a definition for `argSym` already exists in the current scope.
104+
*/
105+
def forwardRef(argSym: Symbol, from: Symbol, to: TypeBounds, cls: ClassSymbol, decls: Scope) =
106+
argSym.info match {
107+
case info @ TypeBounds(lo2 @ TypeRef(ThisType(_), name), hi2) =>
108+
if (name == from.name &&
109+
(lo2 eq hi2) &&
110+
info.variance == to.variance &&
111+
!decls.lookup(argSym.name).exists) {
112+
// println(s"short-circuit ${argSym.name} was: ${argSym.info}, now: $to")
113+
enterArgBinding(argSym, to, cls, decls)
114+
}
115+
case _ =>
116+
}
117+
118+
88119
/** Normalize a list of parent types of class `cls` that may contain refinements
89120
* to a list of typerefs referring to classes, by converting all refinements to member
90121
* definitions in scope `decls`. Can add members to `decls` as a side-effect.
91122
*/
92123
def normalizeToClassRefs(parents: List[Type], cls: ClassSymbol, decls: Scope): List[TypeRef] = {
93124

94-
def enterArgBinding(formal: Symbol, info: Type) = {
95-
val typeArgFlag = if (formal is Local) TypeArgument else EmptyFlags
96-
val sym = ctx.newSymbol(cls, formal.name, formal.flags & RetainedTypeArgFlags | typeArgFlag, info)
97-
cls.enter(sym, decls)
98-
}
99-
100125
/** If we just entered the type argument binding
101126
*
102127
* type From = To
@@ -117,19 +142,8 @@ trait TypeOps { this: Context =>
117142
case to @ TypeBounds(lo1, hi1) if lo1 eq hi1 =>
118143
for (pref <- prefs)
119144
for (argSym <- pref.decls)
120-
if (argSym is TypeArgument) {
121-
argSym.info match {
122-
case info @ TypeBounds(lo2 @ TypeRef(ThisType(_), name), hi2) =>
123-
if (name == from.name &&
124-
(lo2 eq hi2) &&
125-
info.variance == to.variance &&
126-
!decls.lookup(argSym.name).exists) {
127-
// println(s"short-circuit ${argSym.name} was: ${argSym.info}, now: $to")
128-
enterArgBinding(argSym, to)
129-
}
130-
case _ =>
131-
}
132-
}
145+
if (argSym is TypeArgument)
146+
forwardRef(argSym, from, to, cls, decls)
133147
case _ =>
134148
}
135149

@@ -155,7 +169,7 @@ trait TypeOps { this: Context =>
155169
refinements foreachBinding { (name, refinedInfo) =>
156170
assert(decls.lookup(name) == NoSymbol, // DEBUG
157171
s"redefinition of ${decls.lookup(name).debugString} in ${cls.showLocated}")
158-
enterArgBinding(formals(name), refinedInfo)
172+
enterArgBinding(formals(name), refinedInfo, cls, decls)
159173
}
160174
// These two loops cannot be fused because second loop assumes that
161175
// all arguments have been entered in `decls`.

src/dotty/tools/dotc/typer/Inferencing.scala

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import ast._
77
import Contexts._, Types._, Flags._, Denotations._, Names._, StdNames._, NameOps._, Symbols._
88
import Trees._
99
import Constants._
10+
import Scopes._
1011
import annotation.unchecked
1112
import util.Positions._
1213
import util.{Stats, SimpleMap}
@@ -397,6 +398,47 @@ object Inferencing {
397398
defn.ObjectClass.typeRef
398399
}
399400

401+
/** Ensure that first typeref in a list of parents points to a non-trait class.
402+
* If that's not already the case, add one.
403+
*/
404+
def ensureFirstIsClass(prefs: List[TypeRef])(implicit ctx: Context): List[TypeRef] = {
405+
def isRealClass(sym: Symbol) = sym.isClass && !(sym is Trait)
406+
def realClassParent(tref: TypeRef): TypeRef =
407+
if (isRealClass(tref.symbol)) tref
408+
else tref.info.parents match {
409+
case pref :: _ => if (isRealClass(pref.symbol)) pref else realClassParent(pref)
410+
case nil => defn.ObjectClass.typeRef
411+
}
412+
def improve(clsRef: TypeRef, parent: TypeRef): TypeRef = {
413+
val pclsRef = realClassParent(parent)
414+
if (pclsRef.symbol derivesFrom clsRef.symbol) pclsRef else clsRef
415+
}
416+
prefs match {
417+
case pref :: _ if isRealClass(pref.symbol) => prefs
418+
case _ => (defn.ObjectClass.typeRef /: prefs)(improve) :: prefs
419+
}
420+
}
421+
422+
/** Forward bindings of all type parameters of `pcls`. That is, if the type parameter
423+
* if instantiated in a parent class, include its type binding in the current class.
424+
*/
425+
def forwardTypeParams(pcls: ClassSymbol, cls: ClassSymbol, decls: Scope)(implicit ctx: Context): Unit = {
426+
for (tparam <- pcls.typeParams) {
427+
val argSym: Symbol = cls.thisType.member(tparam.name).symbol
428+
argSym.info match {
429+
case TypeAlias(TypeRef(ThisType(_), name)) =>
430+
val from = cls.thisType.member(name).symbol
431+
from.info match {
432+
case bounds: TypeBounds =>
433+
typr.println(s"forward ref $argSym $from $bounds")
434+
ctx.forwardRef(argSym, from, bounds, cls, decls)
435+
case _ =>
436+
}
437+
case _ =>
438+
}
439+
}
440+
}
441+
400442
/** Check that class does not define */
401443
def checkNoDoubleDefs(cls: Symbol)(implicit ctx: Context): Unit = {
402444
val seen = new mutable.HashMap[Name, List[Symbol]] {

src/dotty/tools/dotc/typer/Namer.scala

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import ast._
77
import Trees._, Constants._, StdNames._, Scopes._, Denotations._
88
import Contexts._, Symbols._, Types._, SymDenotations._, Names._, NameOps._, Flags._, Decorators._
99
import ast.desugar, ast.desugar._
10-
import Inferencing.{fullyDefinedType, AnySelectionProto, checkClassTypeWithStablePrefix}
10+
import Inferencing.{fullyDefinedType, AnySelectionProto, checkClassTypeWithStablePrefix, ensureFirstIsClass, forwardTypeParams}
1111
import util.Positions._
1212
import util.SourcePosition
1313
import collection.mutable
@@ -422,10 +422,15 @@ class Namer { typer: Typer =>
422422
val parentClsRefs =
423423
for ((parentRef, constr) <- parentRefs zip parents)
424424
yield checkClassTypeWithStablePrefix(parentRef, constr.pos)
425+
val normalizedParentClsRefs = ensureFirstIsClass(parentClsRefs)
425426

426427
index(constr)
427428
index(rest)(inClassContext(selfInfo))
428-
denot.info = ClassInfo(cls.owner.thisType, cls, parentClsRefs, decls, selfInfo)
429+
denot.info = ClassInfo(cls.owner.thisType, cls, normalizedParentClsRefs, decls, selfInfo)
430+
if (parentClsRefs ne normalizedParentClsRefs) {
431+
forwardTypeParams(normalizedParentClsRefs.head.symbol.asClass, cls, decls)
432+
typr.println(i"expanded parents of $denot: $normalizedParentClsRefs%, %")
433+
}
429434
}
430435
}
431436

tests/pos/test.scala

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,15 @@
11
object test {
2-
object foo extends scala.reflect.io.Streamable.Chars {
3-
val lns = lines()
4-
val pkgLines = lns collect { case x if x startsWith "package " => x stripPrefix "package" trim }
5-
}
2+
3+
abstract class Tree[-T0 >: Null]
4+
5+
trait TermTree[-T1 >: Null] extends Tree[T1]
6+
7+
case class Literal[-T2 >: Null](x: Tree[T2]) extends /*Tree[T2] with*/ TermTree[T2]
68

9+
def f[T >: Null](x: Tree[T]): Unit = {
10+
def g(x: Tree[T]): Unit = ()
11+
x match {
12+
case Literal(x) => g(x)
13+
}
14+
}
715
}

0 commit comments

Comments
 (0)