From df061de3fb2bc9a96d7bc77dc45c09f10fdb8531 Mon Sep 17 00:00:00 2001 From: Eugene Burmako Date: Tue, 28 Jan 2014 15:12:38 +0300 Subject: splits Type.normalize into dealias and etaExpand normalize is a highly overloaded name and it also conflates two distinct operators, so how about we give our users self-explaning atomic tools instead. --- src/reflect/scala/reflect/api/Types.scala | 25 ++++++++++++++- src/reflect/scala/reflect/internal/Types.scala | 42 +++++++++++++++++--------- test/files/run/reflection-idtc.check | 6 ++++ test/files/run/reflection-idtc.scala | 16 ++++++++++ 4 files changed, 74 insertions(+), 15 deletions(-) create mode 100644 test/files/run/reflection-idtc.check create mode 100644 test/files/run/reflection-idtc.scala diff --git a/src/reflect/scala/reflect/api/Types.scala b/src/reflect/scala/reflect/api/Types.scala index c45ef83a8a..e524af583b 100644 --- a/src/reflect/scala/reflect/api/Types.scala +++ b/src/reflect/scala/reflect/api/Types.scala @@ -76,6 +76,12 @@ trait Types { /** The API of types. * The main source of information about types is the [[scala.reflect.api.Types]] page. * @group API + * + * @define dealiasWidenWarning Note that type aliases can hide beneath + * singleton types and singleton types can hide inside type aliases. + * Moreover, aliases might lurk in the upper bounds of abstract types. + * Therefore careful thought has to be applied to identify and carry out + * unwrapping logic specific to your use case. */ abstract class TypeApi { /** The term symbol associated with the type, or `NoSymbol` for types @@ -123,7 +129,7 @@ trait Types { */ def typeConstructor: Type - /** + /** Reduce to beta eta-long normal form. * Expands type aliases and converts higher-kinded TypeRefs to PolyTypes. * Functions on types are also implemented as PolyTypes. * @@ -131,8 +137,18 @@ trait Types { * TypeRef(pre, , List()) is replaced by * PolyType(X, TypeRef(pre, , List(X))) */ + @deprecated("Use `dealias` or `etaExpand` instead", "2.11.0") def normalize: Type + /** Converts higher-kinded TypeRefs to PolyTypes. + * Functions on types are also implemented as PolyTypes. + * + * Example: (in the below, is the type constructor of List) + * TypeRef(pre, , List()) is replaced by + * PolyType(X, TypeRef(pre, , List(X))) + */ + def etaExpand: Type + /** Does this type conform to given type argument `that`? */ def <:< (that: Type): Boolean @@ -205,9 +221,16 @@ trait Types { * class Outer { class C ; val x: C } * val o: Outer * .widen = o.C + * + * $dealiasWidenWarning */ def widen: Type + /** Expands type aliases arising from type members. + * $dealiasWidenWarning + */ + def dealias: Type + /******************* helpers *******************/ /** Provides an alternate if type is NoType. diff --git a/src/reflect/scala/reflect/internal/Types.scala b/src/reflect/scala/reflect/internal/Types.scala index d8c7682910..ca99b1af74 100644 --- a/src/reflect/scala/reflect/internal/Types.scala +++ b/src/reflect/scala/reflect/internal/Types.scala @@ -194,6 +194,7 @@ trait Types override def instantiateTypeParams(formals: List[Symbol], actuals: List[Type]) = underlying.instantiateTypeParams(formals, actuals) override def skolemizeExistential(owner: Symbol, origin: AnyRef) = underlying.skolemizeExistential(owner, origin) override def normalize = maybeRewrap(underlying.normalize) + override def etaExpand = maybeRewrap(underlying.etaExpand) override def dealias = maybeRewrap(underlying.dealias) override def cloneInfo(owner: Symbol) = maybeRewrap(underlying.cloneInfo(owner)) override def atOwner(owner: Symbol) = maybeRewrap(underlying.atOwner(owner)) @@ -498,6 +499,8 @@ trait Types */ def normalize = this // @MAT + def etaExpand = this + /** Expands type aliases. */ def dealias = this @@ -1589,20 +1592,27 @@ trait Types } else if (flattened != parents) { refinedType(flattened, if (typeSymbol eq NoSymbol) NoSymbol else typeSymbol.owner, decls, NoPosition) } else if (isHigherKinded) { - // MO to AM: This is probably not correct - // If they are several higher-kinded parents with different bounds we need - // to take the intersection of their bounds - typeFun( - typeParams, - RefinedType( - parents map { - case TypeRef(pre, sym, List()) => TypeRef(pre, sym, dummyArgs) - case p => p - }, - decls, - typeSymbol)) + etaExpand } else super.normalize } + + final override def etaExpand: Type = { + // MO to AM: This is probably not correct + // If they are several higher-kinded parents with different bounds we need + // to take the intersection of their bounds + // !!! inconsistent with TypeRef.etaExpand that uses initializedTypeParams + if (!isHigherKinded) this + else typeFun( + typeParams, + RefinedType( + parents map { + case TypeRef(pre, sym, List()) => TypeRef(pre, sym, dummyArgs) + case p => p + }, + decls, + typeSymbol)) + } + override def kind = "RefinedType" } @@ -2119,7 +2129,7 @@ trait Types || pre.isGround && args.forall(_.isGround) ) - def etaExpand: Type = { + final override def etaExpand: Type = { // must initialise symbol, see test/files/pos/ticket0137.scala val tpars = initializedTypeParams if (tpars.isEmpty) this @@ -3119,9 +3129,13 @@ trait Types if (instValid) inst // get here when checking higher-order subtyping of the typevar by itself // TODO: check whether this ever happens? - else if (isHigherKinded) logResult("Normalizing HK $this")(typeFun(params, applyArgs(params map (_.typeConstructor)))) + else if (isHigherKinded) etaExpand else super.normalize ) + override def etaExpand: Type = ( + if (!isHigherKinded) this + else logResult("Normalizing HK $this")(typeFun(params, applyArgs(params map (_.typeConstructor)))) + ) override def typeSymbol = origin.typeSymbol private def tparamsOfSym(sym: Symbol) = sym.info match { diff --git a/test/files/run/reflection-idtc.check b/test/files/run/reflection-idtc.check new file mode 100644 index 0000000000..9cdeb02f8c --- /dev/null +++ b/test/files/run/reflection-idtc.check @@ -0,0 +1,6 @@ +[X]X +Int +=== +[X]Id[X] +Id[Int] +Int diff --git a/test/files/run/reflection-idtc.scala b/test/files/run/reflection-idtc.scala new file mode 100644 index 0000000000..bbe90f6826 --- /dev/null +++ b/test/files/run/reflection-idtc.scala @@ -0,0 +1,16 @@ +import scala.reflect.runtime.universe._ +import scala.reflect.runtime.{currentMirror => cm} +import scala.tools.reflect.ToolBox + +object Test extends App { + val tb = cm.mkToolBox() + val idsym = tb.typecheck(q"type Id[X] = X").symbol.asType + val idTC1 = idsym.typeSignature + println(idTC1) + println(appliedType(idTC1, List(typeOf[Int]))) + println("===") + val idTC2 = idsym.toType.etaExpand + println(idTC2) + println(appliedType(idTC2, List(typeOf[Int]))) + println(appliedType(idTC2, List(typeOf[Int])).dealias) +} \ No newline at end of file -- cgit v1.2.3