From 38b36268d5d29d25cb430c1e275c06ebb1c8f7dd Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 19 Jan 2014 22:26:23 +0100 Subject: 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. --- src/dotty/tools/dotc/core/Definitions.scala | 3 +- src/dotty/tools/dotc/core/TypeOps.scala | 54 +++++++++++++++++----------- src/dotty/tools/dotc/typer/Inferencing.scala | 42 ++++++++++++++++++++++ src/dotty/tools/dotc/typer/Namer.scala | 9 +++-- 4 files changed, 85 insertions(+), 23 deletions(-) (limited to 'src/dotty/tools/dotc') diff --git a/src/dotty/tools/dotc/core/Definitions.scala b/src/dotty/tools/dotc/core/Definitions.scala index 1cf875280..7f5145ba3 100644 --- a/src/dotty/tools/dotc/core/Definitions.scala +++ b/src/dotty/tools/dotc/core/Definitions.scala @@ -284,7 +284,8 @@ class Definitions(implicit ctx: Context) { object FunctionType { def apply(args: List[Type], resultType: Type) = FunctionClass(args.length).typeRef.appliedTo(args :+ resultType) - def unapply(ft: Type) = { + def unapply(ft: Type): Option[(List[Type], Type)] = { // Dotty deviation: Type annotation needed because inferred type + // is Some[(List[Type], Type)] | None, which is not a legal unapply type. val tsym = ft.typeSymbol lazy val targs = ft.typeArgs if ((FunctionClasses contains tsym) && diff --git a/src/dotty/tools/dotc/core/TypeOps.scala b/src/dotty/tools/dotc/core/TypeOps.scala index c42f0bb36..0411cc613 100644 --- a/src/dotty/tools/dotc/core/TypeOps.scala +++ b/src/dotty/tools/dotc/core/TypeOps.scala @@ -85,18 +85,43 @@ trait TypeOps { this: Context => } } + private def enterArgBinding(formal: Symbol, info: Type, cls: ClassSymbol, decls: Scope) = { + val typeArgFlag = if (formal is Local) TypeArgument else EmptyFlags + val sym = ctx.newSymbol(cls, formal.name, formal.flags & RetainedTypeArgFlags | typeArgFlag, info) + cls.enter(sym, decls) + } + + /** If we have member definitions + * + * type argSym v= from + * type from v= to + * + * where the variances of both alias are the same, then enter a new definition + * + * type argSym v= to + * + * unless a definition for `argSym` already exists in the current scope. + */ + def forwardRef(argSym: Symbol, from: Symbol, to: TypeBounds, cls: ClassSymbol, decls: Scope) = + argSym.info match { + case info @ TypeBounds(lo2 @ TypeRef(ThisType(_), name), hi2) => + if (name == from.name && + (lo2 eq hi2) && + info.variance == to.variance && + !decls.lookup(argSym.name).exists) { + // println(s"short-circuit ${argSym.name} was: ${argSym.info}, now: $to") + enterArgBinding(argSym, to, cls, decls) + } + case _ => + } + + /** Normalize a list of parent types of class `cls` that may contain refinements * to a list of typerefs referring to classes, by converting all refinements to member * definitions in scope `decls`. Can add members to `decls` as a side-effect. */ def normalizeToClassRefs(parents: List[Type], cls: ClassSymbol, decls: Scope): List[TypeRef] = { - def enterArgBinding(formal: Symbol, info: Type) = { - val typeArgFlag = if (formal is Local) TypeArgument else EmptyFlags - val sym = ctx.newSymbol(cls, formal.name, formal.flags & RetainedTypeArgFlags | typeArgFlag, info) - cls.enter(sym, decls) - } - /** If we just entered the type argument binding * * type From = To @@ -117,19 +142,8 @@ trait TypeOps { this: Context => case to @ TypeBounds(lo1, hi1) if lo1 eq hi1 => for (pref <- prefs) for (argSym <- pref.decls) - if (argSym is TypeArgument) { - argSym.info match { - case info @ TypeBounds(lo2 @ TypeRef(ThisType(_), name), hi2) => - if (name == from.name && - (lo2 eq hi2) && - info.variance == to.variance && - !decls.lookup(argSym.name).exists) { -// println(s"short-circuit ${argSym.name} was: ${argSym.info}, now: $to") - enterArgBinding(argSym, to) - } - case _ => - } - } + if (argSym is TypeArgument) + forwardRef(argSym, from, to, cls, decls) case _ => } @@ -155,7 +169,7 @@ trait TypeOps { this: Context => refinements foreachBinding { (name, refinedInfo) => assert(decls.lookup(name) == NoSymbol, // DEBUG s"redefinition of ${decls.lookup(name).debugString} in ${cls.showLocated}") - enterArgBinding(formals(name), refinedInfo) + enterArgBinding(formals(name), refinedInfo, cls, decls) } // These two loops cannot be fused because second loop assumes that // all arguments have been entered in `decls`. diff --git a/src/dotty/tools/dotc/typer/Inferencing.scala b/src/dotty/tools/dotc/typer/Inferencing.scala index 9da57ea53..fba2201bf 100644 --- a/src/dotty/tools/dotc/typer/Inferencing.scala +++ b/src/dotty/tools/dotc/typer/Inferencing.scala @@ -7,6 +7,7 @@ import ast._ import Contexts._, Types._, Flags._, Denotations._, Names._, StdNames._, NameOps._, Symbols._ import Trees._ import Constants._ +import Scopes._ import annotation.unchecked import util.Positions._ import util.{Stats, SimpleMap} @@ -397,6 +398,47 @@ object Inferencing { defn.ObjectClass.typeRef } + /** Ensure that first typeref in a list of parents points to a non-trait class. + * If that's not already the case, add one. + */ + def ensureFirstIsClass(prefs: List[TypeRef])(implicit ctx: Context): List[TypeRef] = { + def isRealClass(sym: Symbol) = sym.isClass && !(sym is Trait) + def realClassParent(tref: TypeRef): TypeRef = + if (isRealClass(tref.symbol)) tref + else tref.info.parents match { + case pref :: _ => if (isRealClass(pref.symbol)) pref else realClassParent(pref) + case nil => defn.ObjectClass.typeRef + } + def improve(clsRef: TypeRef, parent: TypeRef): TypeRef = { + val pclsRef = realClassParent(parent) + if (pclsRef.symbol derivesFrom clsRef.symbol) pclsRef else clsRef + } + prefs match { + case pref :: _ if isRealClass(pref.symbol) => prefs + case _ => (defn.ObjectClass.typeRef /: prefs)(improve) :: prefs + } + } + + /** Forward bindings of all type parameters of `pcls`. That is, if the type parameter + * if instantiated in a parent class, include its type binding in the current class. + */ + def forwardTypeParams(pcls: ClassSymbol, cls: ClassSymbol, decls: Scope)(implicit ctx: Context): Unit = { + for (tparam <- pcls.typeParams) { + val argSym: Symbol = cls.thisType.member(tparam.name).symbol + argSym.info match { + case TypeAlias(TypeRef(ThisType(_), name)) => + val from = cls.thisType.member(name).symbol + from.info match { + case bounds: TypeBounds => + typr.println(s"forward ref $argSym $from $bounds") + ctx.forwardRef(argSym, from, bounds, cls, decls) + case _ => + } + case _ => + } + } + } + /** Check that class does not define */ def checkNoDoubleDefs(cls: Symbol)(implicit ctx: Context): Unit = { val seen = new mutable.HashMap[Name, List[Symbol]] { diff --git a/src/dotty/tools/dotc/typer/Namer.scala b/src/dotty/tools/dotc/typer/Namer.scala index ece9f51a7..92441d899 100644 --- a/src/dotty/tools/dotc/typer/Namer.scala +++ b/src/dotty/tools/dotc/typer/Namer.scala @@ -7,7 +7,7 @@ import ast._ import Trees._, Constants._, StdNames._, Scopes._, Denotations._ import Contexts._, Symbols._, Types._, SymDenotations._, Names._, NameOps._, Flags._, Decorators._ import ast.desugar, ast.desugar._ -import Inferencing.{fullyDefinedType, AnySelectionProto, checkClassTypeWithStablePrefix} +import Inferencing.{fullyDefinedType, AnySelectionProto, checkClassTypeWithStablePrefix, ensureFirstIsClass, forwardTypeParams} import util.Positions._ import util.SourcePosition import collection.mutable @@ -422,10 +422,15 @@ class Namer { typer: Typer => val parentClsRefs = for ((parentRef, constr) <- parentRefs zip parents) yield checkClassTypeWithStablePrefix(parentRef, constr.pos) + val normalizedParentClsRefs = ensureFirstIsClass(parentClsRefs) index(constr) index(rest)(inClassContext(selfInfo)) - denot.info = ClassInfo(cls.owner.thisType, cls, parentClsRefs, decls, selfInfo) + denot.info = ClassInfo(cls.owner.thisType, cls, normalizedParentClsRefs, decls, selfInfo) + if (parentClsRefs ne normalizedParentClsRefs) { + forwardTypeParams(normalizedParentClsRefs.head.symbol.asClass, cls, decls) + typr.println(i"expanded parents of $denot: $normalizedParentClsRefs%, %") + } } } -- cgit v1.2.3