From f0d5662e3f8668ed33bc7442951558d861e56073 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 3 Mar 2014 17:45:13 +0100 Subject: [PATCH 1/2] Add test case for #34 Right now this one fails. --- test/dotc/tests.scala | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/test/dotc/tests.scala b/test/dotc/tests.scala index 9132ce9c4d72..4e7d277ddaf8 100644 --- a/test/dotc/tests.scala +++ b/test/dotc/tests.scala @@ -71,5 +71,11 @@ class tests extends CompilerTest { "-Ylog:frontend", "-Xprompt")) + @Test def testIssue_34 = compileArgs(Array( + dotcDir + "tools/dotc/config/Properties.scala", + dotcDir + "tools/dotc/config/PathResolver.scala", + "-Ylog:frontend", + "-Xprompt")) + //@Test def dotc_compilercommand = compileFile(dotcDir + "tools/dotc/config/", "CompilerCommand") } From f3dacf968bc28f5f47612f75562a1a600e511815 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 3 Mar 2014 17:50:26 +0100 Subject: [PATCH 2/2] Fix of #34 The root cause of #34 was that we took a type argument which was an existential type. These are returned as type bounds, which make no sense in the calling context. To avoid that problem in the future, `typeArgs` got renamed to `argInfos`, so it is clear we get an info, not necessarily a value type. There are also added method `argTypes`, `argTypesLo`, `argTypesHi`, which return a type, but either throw an exception or return a lower/upper approximation of the argument is an existential type. There's another issue that the existential type only arose when compiling the same couple fo files the seciond time. We need to chase that one down separately. --- src/dotty/tools/dotc/ast/CheckTrees.scala | 6 +- src/dotty/tools/dotc/ast/tpd.scala | 2 +- src/dotty/tools/dotc/ast/untpd.scala | 2 +- src/dotty/tools/dotc/core/Definitions.scala | 6 +- .../tools/dotc/core/TypeApplications.scala | 80 +++++++++++++++---- src/dotty/tools/dotc/core/TypeComparer.scala | 5 +- src/dotty/tools/dotc/core/Types.scala | 2 +- .../tools/dotc/core/pickling/UnPickler.scala | 2 +- .../tools/dotc/printing/RefinedPrinter.scala | 2 +- src/dotty/tools/dotc/typer/Applications.scala | 8 +- src/dotty/tools/dotc/typer/Typer.scala | 6 +- 11 files changed, 88 insertions(+), 33 deletions(-) diff --git a/src/dotty/tools/dotc/ast/CheckTrees.scala b/src/dotty/tools/dotc/ast/CheckTrees.scala index 6152a2880321..ad376445089b 100644 --- a/src/dotty/tools/dotc/ast/CheckTrees.scala +++ b/src/dotty/tools/dotc/ast/CheckTrees.scala @@ -135,7 +135,7 @@ object CheckTrees { check(finalizer.isTerm) check(handler.isTerm) check(handler.tpe derivesFrom defn.FunctionClass(1)) - check(handler.tpe.baseTypeArgs(defn.FunctionClass(1)).head <:< defn.ThrowableType) + check(handler.tpe.baseArgInfos(defn.FunctionClass(1)).head <:< defn.ThrowableType) case Throw(expr) => check(expr.isValue) check(expr.tpe.derivesFrom(defn.ThrowableClass)) @@ -210,9 +210,9 @@ object CheckTrees { check(args.isEmpty) else { check(rtp isRef defn.OptionClass) - val normArgs = rtp.typeArgs match { + val normArgs = rtp.argTypesHi match { case optionArg :: Nil => - optionArg.typeArgs match { + optionArg.argTypesHi match { case Nil => optionArg :: Nil case tupleArgs if defn.isTupleType(optionArg) => diff --git a/src/dotty/tools/dotc/ast/tpd.scala b/src/dotty/tools/dotc/ast/tpd.scala index 8b9e0c12e95a..03c0f83a9ba7 100644 --- a/src/dotty/tools/dotc/ast/tpd.scala +++ b/src/dotty/tools/dotc/ast/tpd.scala @@ -336,7 +336,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { /** new C(args) */ def New(tp: Type, args: List[Tree])(implicit ctx: Context): Apply = { - val targs = tp.typeArgs + val targs = tp.argTypes Apply( Select( New(tp withoutArgs targs), diff --git a/src/dotty/tools/dotc/ast/untpd.scala b/src/dotty/tools/dotc/ast/untpd.scala index 4aff9da5d65a..fc2b07b02b90 100644 --- a/src/dotty/tools/dotc/ast/untpd.scala +++ b/src/dotty/tools/dotc/ast/untpd.scala @@ -124,7 +124,7 @@ object untpd extends Trees.Instance[Untyped] with TreeInfo[Untyped] { case TypedSplice(AppliedTypeTree(tycon, targs)) => (TypedSplice(tycon), targs map TypedSplice) case TypedSplice(tpt1: Tree) => - val argTypes = tpt1.tpe.typeArgs + val argTypes = tpt1.tpe.argTypes val tycon = tpt1.tpe.withoutArgs(argTypes) def wrap(tpe: Type) = TypeTree(tpe) withPos tpt.pos (wrap(tycon), argTypes map wrap) diff --git a/src/dotty/tools/dotc/core/Definitions.scala b/src/dotty/tools/dotc/core/Definitions.scala index 0ee0ea1e291c..ef16a970dfa4 100644 --- a/src/dotty/tools/dotc/core/Definitions.scala +++ b/src/dotty/tools/dotc/core/Definitions.scala @@ -286,7 +286,7 @@ class Definitions { 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 + lazy val targs = ft.argInfos if ((FunctionClasses contains tsym) && (targs.length - 1 <= MaxFunctionArity) && (FunctionClass(targs.length - 1) == tsym)) Some((targs.init, targs.last)) // Dotty deviation: no auto-tupling @@ -317,7 +317,7 @@ class Definitions { lazy val RootImports = List[Symbol](JavaLangPackageVal, ScalaPackageVal, ScalaPredefModule, DottyPredefModule) def isTupleType(tp: Type) = { - val arity = tp.dealias.typeArgs.length + val arity = tp.dealias.argInfos.length arity <= MaxTupleArity && (tp isRef TupleClass(arity)) } @@ -329,7 +329,7 @@ class Definitions { 0 <= arity && arity <= MaxFunctionArity && (tp isRef FunctionClass(arity)) } - def functionArity(tp: Type) = tp.dealias.typeArgs.length - 1 + def functionArity(tp: Type) = tp.dealias.argInfos.length - 1 // ----- Higher kinds machinery ------------------------------------------ diff --git a/src/dotty/tools/dotc/core/TypeApplications.scala b/src/dotty/tools/dotc/core/TypeApplications.scala index e854e672f05d..b4c30d902976 100644 --- a/src/dotty/tools/dotc/core/TypeApplications.scala +++ b/src/dotty/tools/dotc/core/TypeApplications.scala @@ -12,6 +12,29 @@ import Flags._ import util.Positions.Position import collection.mutable +object TypeApplications { + + /** Assert type is not a TypeBounds instance and return it unchanged */ + val noBounds = (tp: Type) => tp match { + case tp: TypeBounds => throw new AssertionError("no TypeBounds allowed") + case _ => tp + } + + /** If `tp` is a TypeBounds instance return its lower bound else return `tp` */ + val boundsToLo = (tp: Type) => tp match { + case tp: TypeBounds => tp.lo + case _ => tp + } + + /** If `tp` is a TypeBounds instance return its upper bound else return `tp` */ + val boundsToHi = (tp: Type) => tp match { + case tp: TypeBounds => tp.hi + case _ => tp + } +} + +import TypeApplications._ + /** A decorator that provides methods for modeling type application */ class TypeApplications(val self: Type) extends AnyVal { @@ -135,24 +158,43 @@ class TypeApplications(val self: Type) extends AnyVal { else TypeAlias(self, v) } - /** The type arguments of the base type instance wrt `base` of this type */ - final def baseTypeArgs(base: Symbol)(implicit ctx: Context): List[Type] = + /** The type arguments of this type's base type instance wrt.`base`. + * Existential types in arguments are returned as TypeBounds instances. + */ + final def baseArgInfos(base: Symbol)(implicit ctx: Context): List[Type] = if (self derivesFrom base) - base.typeParams map (param => self.member(param.name).info.argType(param)) + base.typeParams map (param => self.member(param.name).info.argInfo(param)) else Nil + /** The type arguments of this type's base type instance wrt.`base`. + * Existential types in arguments are disallowed. + */ + final def baseArgTypes(base: Symbol)(implicit ctx: Context): List[Type] = baseArgInfos(base) mapConserve noBounds + + /** The type arguments of this type's base type instance wrt.`base`. + * Existential types in arguments are approximanted by their lower bound. + */ + final def baseArgTypesLo(base: Symbol)(implicit ctx: Context): List[Type] = baseArgInfos(base) mapConserve boundsToLo + + /** The type arguments of this type's base type instance wrt.`base`. + * Existential types in arguments are approximanted by their upper bound. + */ + final def baseArgTypesHi(base: Symbol)(implicit ctx: Context): List[Type] = baseArgInfos(base) mapConserve boundsToHi + /** The first type argument of the base type instance wrt `base` of this type */ - final def firstBaseTypeArg(base: Symbol)(implicit ctx: Context): Type = base.typeParams match { + final def firstBaseArgInfo(base: Symbol)(implicit ctx: Context): Type = base.typeParams match { case param :: _ if self derivesFrom base => - self.member(param.name).info.argType(param) + self.member(param.name).info.argInfo(param) case _ => NoType } - /** The base type including all type arguments of this type */ + /** The base type including all type arguments of this type. + * Existential types in arguments are returned as TypeBounds instances. + */ final def baseTypeWithArgs(base: Symbol)(implicit ctx: Context): Type = - self.baseTypeRef(base).appliedTo(baseTypeArgs(base)) + self.baseTypeRef(base).appliedTo(baseArgInfos(base)) /** Translate a type of the form From[T] to To[T], keep other types as they are. * `from` and `to` must be static classes, both with one type parameter, and the same variance. @@ -163,9 +205,10 @@ class TypeApplications(val self: Type) extends AnyVal { else self /** If this is an encoding of a (partially) applied type, return its arguments, - * otherwise return Nil + * otherwise return Nil. + * Existential types in arguments are returned as TypeBounds instances. */ - final def typeArgs(implicit ctx: Context): List[Type] = { + final def argInfos(implicit ctx: Context): List[Type] = { var tparams: List[TypeSymbol] = null def recur(tp: Type, refineCount: Int): mutable.ListBuffer[Type] = tp.stripTypeVar match { case tp @ RefinedType(tycon, name) => @@ -175,7 +218,7 @@ class TypeApplications(val self: Type) extends AnyVal { if (tparams == null) tparams = tycon.typeParams if (buf.size < tparams.length) { val tparam = tparams(buf.size) - if (name == tparam.name) buf += tp.refinedInfo.argType(tparam) + if (name == tparam.name) buf += tp.refinedInfo.argInfo(tparam) else null } else null } @@ -187,6 +230,15 @@ class TypeApplications(val self: Type) extends AnyVal { if (buf == null) Nil else buf.toList } + /** Argument types where existential types in arguments are disallowed */ + def argTypes(implicit ctx: Context) = argInfos mapConserve noBounds + + /** Argument types where existential types in arguments are approximated by their lower bound */ + def argTypesLo(implicit ctx: Context) = argInfos mapConserve boundsToLo + + /** Argument types where existential types in arguments are approximated by their upper bound */ + def argTypesHi(implicit ctx: Context) = argInfos mapConserve boundsToHi + /** The core type without any type arguments. * @param `typeArgs` must be the type arguments of this type. */ @@ -201,7 +253,7 @@ class TypeApplications(val self: Type) extends AnyVal { /** If this is the image of a type argument to type parameter `tparam`, * recover the type argument, otherwise NoType. */ - final def argType(tparam: Symbol)(implicit ctx: Context): Type = self match { + final def argInfo(tparam: Symbol)(implicit ctx: Context): Type = self match { case TypeBounds(lo, hi) => if (lo eq hi) hi else { @@ -216,7 +268,7 @@ class TypeApplications(val self: Type) extends AnyVal { /** The element type of a sequence or array */ def elemType(implicit ctx: Context): Type = - firstBaseTypeArg(defn.SeqClass) orElse firstBaseTypeArg(defn.ArrayClass) + firstBaseArgInfo(defn.SeqClass) orElse firstBaseArgInfo(defn.ArrayClass) /** If this type is of the normalized form Array[...[Array[T]...] * return the number of Array wrappers and T. @@ -225,7 +277,7 @@ class TypeApplications(val self: Type) extends AnyVal { final def splitArray(implicit ctx: Context): (Int, Type) = { def recur(n: Int, tp: Type): (Int, Type) = tp.stripTypeVar match { case RefinedType(tycon, _) if tycon isRef defn.ArrayClass => - tp.typeArgs match { + tp.argInfos match { case arg :: Nil => recur(n + 1, arg) case _ => (n, tp) } @@ -274,7 +326,7 @@ class TypeApplications(val self: Type) extends AnyVal { val correspondingParamName: Map[Symbol, TypeName] = { for { - (tparam, targ: TypeRef) <- cls.typeParams zip typeArgs + (tparam, targ: TypeRef) <- cls.typeParams zip argInfos if boundSyms contains targ.symbol } yield targ.symbol -> tparam.name }.toMap diff --git a/src/dotty/tools/dotc/core/TypeComparer.scala b/src/dotty/tools/dotc/core/TypeComparer.scala index 0af6ebf975ea..d46a8387fdf0 100644 --- a/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/src/dotty/tools/dotc/core/TypeComparer.scala @@ -616,7 +616,7 @@ class TypeComparer(initctx: Context) extends DotClass { */ def isSubTypeHK(tp1: Type, tp2: Type): Boolean = { val tparams = tp1.typeParams - val hkArgs = tp2.typeArgs + val hkArgs = tp2.argInfos (hkArgs.length == tparams.length) && { val base = tp1.narrow (tparams, hkArgs).zipped.forall { (tparam, hkArg) => @@ -752,7 +752,7 @@ class TypeComparer(initctx: Context) extends DotClass { /** The least upper bound of two types * @note We do not admit singleton types in or-types as lubs. */ - def lub(tp1: Type, tp2: Type): Type = + def lub(tp1: Type, tp2: Type): Type = /*>|>*/ ctx.traceIndented(s"lub(${tp1.show}, ${tp2.show})", typr, show = true) /*<|<*/ { if (tp1 eq tp2) tp1 else if (!tp1.exists) tp1 else if (!tp2.exists) tp2 @@ -772,6 +772,7 @@ class TypeComparer(initctx: Context) extends DotClass { } } } + } /** The least upper bound of a list of types */ final def lub(tps: List[Type]): Type = diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala index bc8d7dfd28d2..4d26a6735d98 100644 --- a/src/dotty/tools/dotc/core/Types.scala +++ b/src/dotty/tools/dotc/core/Types.scala @@ -96,7 +96,7 @@ object Types { else thissym eq sym case this1: RefinedType => // make sure all refinements are type arguments - this1.parent.isRef(sym) && this.typeArgs.nonEmpty + this1.parent.isRef(sym) && this.argInfos.nonEmpty case _ => false } diff --git a/src/dotty/tools/dotc/core/pickling/UnPickler.scala b/src/dotty/tools/dotc/core/pickling/UnPickler.scala index c48e71052a17..c69f60758518 100644 --- a/src/dotty/tools/dotc/core/pickling/UnPickler.scala +++ b/src/dotty/tools/dotc/core/pickling/UnPickler.scala @@ -76,7 +76,7 @@ object UnPickler { case tp @ MethodType(paramNames, paramTypes) => val lastArg = paramTypes.last assert(lastArg isRef defn.ArrayClass) - val elemtp0 :: Nil = lastArg.baseTypeArgs(defn.ArrayClass) + val elemtp0 :: Nil = lastArg.baseArgInfos(defn.ArrayClass) val elemtp = elemtp0 match { case AndType(t1, t2) if t1.typeSymbol.isAbstractType && (t2 isRef defn.ObjectClass) => t1 // drop intersection with Object for abstract types in varargs. UnCurry can handle them. diff --git a/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/src/dotty/tools/dotc/printing/RefinedPrinter.scala index 6fad6738e7af..cb0780ae36b4 100644 --- a/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -96,7 +96,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { } tp match { case tp: RefinedType => - val args = tp.typeArgs + val args = tp.argInfos if (args.nonEmpty) { val tycon = tp.unrefine val cls = tycon.typeSymbol diff --git a/src/dotty/tools/dotc/typer/Applications.scala b/src/dotty/tools/dotc/typer/Applications.scala index 4b43aa8b7d66..4d7bc8976a6b 100644 --- a/src/dotty/tools/dotc/typer/Applications.scala +++ b/src/dotty/tools/dotc/typer/Applications.scala @@ -24,6 +24,7 @@ import EtaExpansion._ import collection.mutable import reflect.ClassTag import config.Printers._ +import TypeApplications._ import language.implicitConversions object Applications { @@ -266,7 +267,7 @@ trait Applications extends Compatibility { self: Typer => case arg :: Nil if isVarArg(arg) => addTyped(arg, formal) case _ => - val elemFormal = formal.typeArgs.head + val elemFormal = formal.argTypesLo.head args foreach (addTyped(_, elemFormal)) makeVarArg(args.length, elemFormal) } @@ -609,7 +610,7 @@ trait Applications extends Compatibility { self: Typer => if (extractorMemberType(unapplyResult, nme.isDefined) isRef defn.BooleanClass) { if (getTp.exists) if (unapply.symbol.name == nme.unapplySeq) { - val seqArg = getTp.firstBaseTypeArg(defn.SeqClass) + val seqArg = boundsToHi(getTp.firstBaseArgInfo(defn.SeqClass)) if (seqArg.exists) return args map Function.const(seqArg) } else return getSelectors(getTp) @@ -683,6 +684,7 @@ trait Applications extends Compatibility { self: Typer => } var argTypes = unapplyArgs(unapplyApp.tpe) + for (argType <- argTypes) assert(!argType.isInstanceOf[TypeBounds], unapplyApp.tpe.show) val bunchedArgs = argTypes match { case argType :: Nil if argType.isRepeatedParam => untpd.SeqLiteral(args) :: Nil case _ => args @@ -770,7 +772,7 @@ trait Applications extends Compatibility { self: Typer => val tparams = ctx.newTypeParams(alt1.symbol.owner, tp1.paramNames, EmptyFlags, bounds) isAsSpecific(alt1, tp1.instantiate(tparams map (_.typeRef)), alt2, tp2) case tp1: MethodType => - def repeatedToSingle(tp: Type) = if (tp.isRepeatedParam) tp.typeArgs.head else tp + def repeatedToSingle(tp: Type) = if (tp.isRepeatedParam) tp.argTypesHi.head else tp isApplicable(alt2, tp1.paramTypes map repeatedToSingle, WildcardType) || tp1.paramTypes.isEmpty && tp2.isInstanceOf[MethodOrPoly] case _ => diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala index 400a1407a87e..96fe73e7d42e 100644 --- a/src/dotty/tools/dotc/typer/Typer.scala +++ b/src/dotty/tools/dotc/typer/Typer.scala @@ -438,7 +438,7 @@ class Typer extends Namer with Applications with Implicits { } def typedPair(tree: untpd.Pair, pt: Type)(implicit ctx: Context) = track("typedPair") { - val (leftProto, rightProto) = pt.typeArgs match { + val (leftProto, rightProto) = pt.argTypesLo match { case l :: r :: Nil if pt isRef defn.PairClass => (l, r) case _ => (WildcardType, WildcardType) } @@ -561,7 +561,7 @@ class Typer extends Namer with Applications with Implicits { val params = args.asInstanceOf[List[untpd.ValDef]] val (protoFormals, protoResult): (List[Type], Type) = pt match { case _ if defn.isFunctionType(pt) => - (pt.dealias.typeArgs.init, pt.dealias.typeArgs.last) + (pt.dealias.argInfos.init, pt.dealias.argInfos.last) case SAMType(meth) => val mt @ MethodType(_, paramTypes) = meth.info (paramTypes, mt.resultType) @@ -750,7 +750,7 @@ class Typer extends Namer with Applications with Implicits { val expr1 = typed(tree.expr, pt) val handler1 = typed(tree.handler, defn.FunctionType(defn.ThrowableType :: Nil, pt)) val finalizer1 = typed(tree.finalizer, defn.UnitType) - val handlerTypeArgs = handler1.tpe.baseTypeArgs(defn.FunctionClass(1)) + val handlerTypeArgs = handler1.tpe.baseArgTypesHi(defn.FunctionClass(1)) val ownType = if (handlerTypeArgs.nonEmpty) expr1.tpe | handlerTypeArgs(1) else expr1.tpe cpy.Try(tree, expr1, handler1, finalizer1) withType ownType }