diff options
author | Eugene Burmako <xeno.by@gmail.com> | 2012-09-28 13:04:38 -0700 |
---|---|---|
committer | Eugene Burmako <xeno.by@gmail.com> | 2012-09-28 13:04:38 -0700 |
commit | e46ceca8a0166a459cb336da83ae71afe69dc025 (patch) | |
tree | 51a52930865c6981210d2c92389fe80357d833b6 | |
parent | 8815637c0f0dddb4a044cd243e31890d535591e2 (diff) | |
parent | c395c926216b4356c82c606b76784a184a7f1d9b (diff) | |
download | scala-e46ceca8a0166a459cb336da83ae71afe69dc025.tar.gz scala-e46ceca8a0166a459cb336da83ae71afe69dc025.tar.bz2 scala-e46ceca8a0166a459cb336da83ae71afe69dc025.zip |
Merge pull request #1415 from scalamacros/is-value-type
a fork of isValueType and isNonValueType
-rw-r--r-- | src/compiler/scala/reflect/reify/utils/Extractors.scala | 13 | ||||
-rw-r--r-- | src/reflect/scala/reflect/internal/Printers.scala | 3 | ||||
-rw-r--r-- | src/reflect/scala/reflect/internal/Symbols.scala | 4 | ||||
-rw-r--r-- | src/reflect/scala/reflect/internal/Types.scala | 116 | ||||
-rw-r--r-- | test/files/run/fail-non-value-types.check | 3 | ||||
-rw-r--r-- | test/files/run/fail-non-value-types.scala | 40 | ||||
-rw-r--r-- | test/files/run/macro-reify-type.check | 1 | ||||
-rw-r--r-- | test/files/run/macro-reify-type.flags | 1 | ||||
-rw-r--r-- | test/files/run/macro-reify-type/Macros_1.scala | 27 | ||||
-rw-r--r-- | test/files/run/macro-reify-type/Test_2.scala | 21 | ||||
-rw-r--r-- | test/files/run/showraw_nosymbol.check | 1 | ||||
-rw-r--r-- | test/files/run/showraw_nosymbol.scala | 5 |
12 files changed, 224 insertions, 11 deletions
diff --git a/src/compiler/scala/reflect/reify/utils/Extractors.scala b/src/compiler/scala/reflect/reify/utils/Extractors.scala index b7206eda0e..b60d15c1d4 100644 --- a/src/compiler/scala/reflect/reify/utils/Extractors.scala +++ b/src/compiler/scala/reflect/reify/utils/Extractors.scala @@ -92,11 +92,18 @@ trait Extractors { Block(List(universeAlias, mirrorAlias), wrappee) } + // if we're reifying a MethodType, we can't use it as a type argument for TypeTag ctor + // http://groups.google.com/group/scala-internals/browse_thread/thread/2d7bb85bfcdb2e2 + private def mkTarg(tpe: Type): Tree = ( + if ((tpe eq null) || !isUseableAsTypeArg(tpe)) TypeTree(AnyTpe) + else TypeTree(tpe) + ) + object ReifiedTree { def apply(universe: Tree, mirror: Tree, symtab: SymbolTable, rtree: Tree, tpe: Type, rtpe: Tree, concrete: Boolean): Tree = { val tagFactory = if (concrete) nme.TypeTag else nme.WeakTypeTag - val tagCtor = TypeApply(Select(Select(Ident(nme.UNIVERSE_SHORT), tagFactory), nme.apply), List(TypeTree(tpe))) - val exprCtor = TypeApply(Select(Select(Ident(nme.UNIVERSE_SHORT), nme.Expr), nme.apply), List(TypeTree(tpe))) + val tagCtor = TypeApply(Select(Select(Ident(nme.UNIVERSE_SHORT), tagFactory), nme.apply), List(mkTarg(tpe))) + val exprCtor = TypeApply(Select(Select(Ident(nme.UNIVERSE_SHORT), nme.Expr), nme.apply), List(mkTarg(tpe))) val tagArgs = List(Ident(nme.MIRROR_SHORT), mkCreator(tpnme.REIFY_TYPECREATOR_PREFIX, symtab, rtpe)) val unwrapped = Apply(Apply(exprCtor, List(Ident(nme.MIRROR_SHORT), mkCreator(tpnme.REIFY_TREECREATOR_PREFIX, symtab, rtree))), List(Apply(tagCtor, tagArgs))) mkWrapper(universe, mirror, unwrapped) @@ -123,7 +130,7 @@ trait Extractors { object ReifiedType { def apply(universe: Tree, mirror: Tree, symtab: SymbolTable, tpe: Type, rtpe: Tree, concrete: Boolean) = { val tagFactory = if (concrete) nme.TypeTag else nme.WeakTypeTag - val ctor = TypeApply(Select(Select(Ident(nme.UNIVERSE_SHORT), tagFactory), nme.apply), List(TypeTree(tpe))) + val ctor = TypeApply(Select(Select(Ident(nme.UNIVERSE_SHORT), tagFactory), nme.apply), List(mkTarg(tpe))) val args = List(Ident(nme.MIRROR_SHORT), mkCreator(tpnme.REIFY_TYPECREATOR_PREFIX, symtab, rtpe)) val unwrapped = Apply(ctor, args) mkWrapper(universe, mirror, unwrapped) diff --git a/src/reflect/scala/reflect/internal/Printers.scala b/src/reflect/scala/reflect/internal/Printers.scala index cb8dc4b197..fb165ab50f 100644 --- a/src/reflect/scala/reflect/internal/Printers.scala +++ b/src/reflect/scala/reflect/internal/Printers.scala @@ -576,7 +576,8 @@ trait Printers extends api.Printers { self: SymbolTable => case _ => // do nothing }) case sym: Symbol => - if (sym.isStatic && (sym.isClass || sym.isModule)) print(sym.fullName) + if (sym == NoSymbol) print("NoSymbol") + else if (sym.isStatic && (sym.isClass || sym.isModule)) print(sym.fullName) else print(sym.name) if (printIds) print("#", sym.id) if (printKinds) print("#", sym.abbreviatedKindString) diff --git a/src/reflect/scala/reflect/internal/Symbols.scala b/src/reflect/scala/reflect/internal/Symbols.scala index e420b448a6..b132b91946 100644 --- a/src/reflect/scala/reflect/internal/Symbols.scala +++ b/src/reflect/scala/reflect/internal/Symbols.scala @@ -91,8 +91,8 @@ trait Symbols extends api.Symbols { self: SymbolTable => def module = sourceModule def thisPrefix: Type = thisType def selfType: Type = typeOfThis - def typeSignature: Type = info - def typeSignatureIn(site: Type): Type = site memberInfo this + def typeSignature: Type = { fullyInitializeSymbol(this); info } + def typeSignatureIn(site: Type): Type = { fullyInitializeSymbol(this); site memberInfo this } def toType: Type = tpe def toTypeIn(site: Type): Type = site.memberType(this) diff --git a/src/reflect/scala/reflect/internal/Types.scala b/src/reflect/scala/reflect/internal/Types.scala index 6b274467fc..0e8665ee84 100644 --- a/src/reflect/scala/reflect/internal/Types.scala +++ b/src/reflect/scala/reflect/internal/Types.scala @@ -66,9 +66,9 @@ import util.ThreeValues._ // a type variable // Replace occurrences of type parameters with type vars, where // inst is the instantiation and constr is a list of bounds. - case DeBruijnIndex(level, index) + case DeBruijnIndex(level, index, args) // for dependent method types: a type referring to a method parameter. - case ErasedValueType(clazz, underlying) + case ErasedValueType(tref) // only used during erasure of derived value classes. */ @@ -3623,9 +3623,20 @@ trait Types extends api.Types { self: SymbolTable => */ /** A creator for type applications */ - def appliedType(tycon: Type, args: List[Type]): Type = - if (args.isEmpty) tycon //@M! `if (args.isEmpty) tycon' is crucial (otherwise we create new types in phases after typer and then they don't get adapted (??)) - else tycon match { + def appliedType(tycon: Type, args: List[Type]): Type = { + if (args.isEmpty) + return tycon //@M! `if (args.isEmpty) tycon' is crucial (otherwise we create new types in phases after typer and then they don't get adapted (??)) + + /** Disabled - causes cycles in tcpoly tests. */ + if (false && isDefinitionsInitialized) { + assert(isUseableAsTypeArgs(args), { + val tapp_s = s"""$tycon[${args mkString ", "}]""" + val arg_s = args filterNot isUseableAsTypeArg map (t => t + "/" + t.getClass) mkString ", " + s"$tapp_s includes illegal type argument $arg_s" + }) + } + + tycon match { case TypeRef(pre, sym @ (NothingClass|AnyClass), _) => copyTypeRef(tycon, pre, sym, Nil) //@M drop type args to Any/Nothing case TypeRef(pre, sym, _) => copyTypeRef(tycon, pre, sym, args) case PolyType(tparams, restpe) => restpe.instantiateTypeParams(tparams, args) @@ -3639,6 +3650,7 @@ trait Types extends api.Types { self: SymbolTable => case WildcardType => tycon // needed for neg/t0226 case _ => abort(debugString(tycon)) } + } /** Very convenient. */ def appliedType(tyconSym: Symbol, args: Type*): Type = @@ -5732,6 +5744,100 @@ trait Types extends api.Types { self: SymbolTable => case _ => false } + /** This is defined and named as it is because the goal is to exclude source + * level types which are not value types (e.g. MethodType) without excluding + * necessary internal types such as WildcardType. There are also non-value + * types which can be used as type arguments (e.g. type constructors.) + */ + def isUseableAsTypeArg(tp: Type) = ( + isInternalTypeUsedAsTypeArg(tp) // the subset of internal types which can be type args + || isHKTypeRef(tp) // not a value type, but ok as a type arg + || isValueElseNonValue(tp) // otherwise only value types + ) + + private def isHKTypeRef(tp: Type) = tp match { + case TypeRef(_, sym, Nil) => tp.isHigherKinded + case _ => false + } + @tailrec final def isUseableAsTypeArgs(tps: List[Type]): Boolean = tps match { + case Nil => true + case x :: xs => isUseableAsTypeArg(x) && isUseableAsTypeArgs(xs) + } + + /** The "third way", types which are neither value types nor + * non-value types as defined in the SLS, further divided into + * types which are used internally in type applications and + * types which are not. + */ + private def isInternalTypeNotUsedAsTypeArg(tp: Type): Boolean = tp match { + case AntiPolyType(pre, targs) => true + case ClassInfoType(parents, defs, clazz) => true + case DeBruijnIndex(level, index, args) => true + case ErasedValueType(tref) => true + case NoPrefix => true + case NoType => true + case SuperType(thistpe, supertpe) => true + case TypeBounds(lo, hi) => true + case _ => false + } + private def isInternalTypeUsedAsTypeArg(tp: Type): Boolean = tp match { + case WildcardType => true + case BoundedWildcardType(_) => true + case ErrorType => true + case _: TypeVar => true + case _ => false + } + private def isAlwaysValueType(tp: Type) = tp match { + case RefinedType(_, _) => true + case ExistentialType(_, _) => true + case ConstantType(_) => true + case _ => false + } + private def isAlwaysNonValueType(tp: Type) = tp match { + case OverloadedType(_, _) => true + case NullaryMethodType(_) => true + case MethodType(_, _) => true + case PolyType(_, MethodType(_, _)) => true + case _ => false + } + /** Should be called only with types for which a clear true/false answer + * can be given: true == value type, false == non-value type. Otherwise, + * an exception is thrown. + */ + private def isValueElseNonValue(tp: Type): Boolean = tp match { + case tp if isAlwaysValueType(tp) => true + case tp if isAlwaysNonValueType(tp) => false + case AnnotatedType(_, underlying, _) => isValueElseNonValue(underlying) + case SingleType(_, sym) => sym.isValue // excludes packages and statics + case TypeRef(_, _, _) if tp.isHigherKinded => false // excludes type constructors + case ThisType(sym) => !sym.isPackageClass // excludes packages + case TypeRef(_, sym, _) => !sym.isPackageClass // excludes packages + case PolyType(_, _) => true // poly-methods excluded earlier + case tp => sys.error("isValueElseNonValue called with third-way type " + tp) + } + + /** SLS 3.2, Value Types + * Is the given type definitely a value type? A true result means + * it verifiably is, but a false result does not mean it is not, + * only that it cannot be assured. To avoid false positives, this + * defaults to false, but since Type is not sealed, one should take + * a false answer with a grain of salt. This method may be primarily + * useful as documentation; it is likely that !isNonValueType(tp) + * will serve better than isValueType(tp). + */ + def isValueType(tp: Type) = isValueElseNonValue(tp) + + /** SLS 3.3, Non-Value Types + * Is the given type definitely a non-value type, as defined in SLS 3.3? + * The specification-enumerated non-value types are method types, polymorphic + * method types, and type constructors. Supplements to the specified set of + * non-value types include: types which wrap non-value symbols (packages + * abd statics), overloaded types. Varargs and by-name types T* and (=>T) are + * not designated non-value types because there is code which depends on using + * them as type arguments, but their precise status is unclear. + */ + def isNonValueType(tp: Type) = !isValueElseNonValue(tp) + def isNonRefinementClassType(tpe: Type) = tpe match { case SingleType(_, sym) => sym.isModuleClass case TypeRef(_, sym, _) => sym.isClass && !sym.isRefinementClass diff --git a/test/files/run/fail-non-value-types.check b/test/files/run/fail-non-value-types.check new file mode 100644 index 0000000000..714dce2c50 --- /dev/null +++ b/test/files/run/fail-non-value-types.check @@ -0,0 +1,3 @@ +[B, That](f: A => B)(implicit cbf: ImaginaryCanBuildFrom[CompletelyIndependentList.this.Repr,B,That])That +[B, That](f: Int => B)(implicit cbf: ImaginaryCanBuildFrom[CompletelyIndependentList[Int]#Repr,B,That])That +()CompletelyIndependentList[A] diff --git a/test/files/run/fail-non-value-types.scala b/test/files/run/fail-non-value-types.scala new file mode 100644 index 0000000000..51198a5f31 --- /dev/null +++ b/test/files/run/fail-non-value-types.scala @@ -0,0 +1,40 @@ +import scala.reflect.runtime.universe._ + +class ImaginaryCanBuildFrom[-From, -Elem, +To] +class CompletelyIndependentList[+A] { + type Repr <: CompletelyIndependentList[A] + def map[B, That](f: A => B)(implicit cbf: ImaginaryCanBuildFrom[Repr, B, That]): That = ??? + def distinct(): CompletelyIndependentList[A] = ??? +} + +object Test { + var failed = false + def expectFailure[T](body: => T): Boolean = { + try { val res = body ; failed = true ; println(res + " failed to fail.") ; false } + catch { case _: AssertionError => true } + } + + /** Attempt to use a method type as a type argument - expect failure. */ + def tcon[T: TypeTag](args: Type*) = appliedType(typeOf[T].typeConstructor, args.toList) + + def cil = typeOf[CompletelyIndependentList[Int]] + def map = cil.member("map": TermName).asMethod + def distinct = cil.member("distinct": TermName).asMethod + + def main(args: Array[String]): Unit = { + // Need the assert in there to fail. + // expectFailure(println(tcon[CompletelyIndependentList[Int]](map))) + // expectFailure(tcon[CompletelyIndependentList[Int]](distinct)) + + // Why is the first map signature printing showing an + // uninitialized symbol? + // + // [B <: <?>, That <: <?>](f: <?>)(implicit cbf: <?>)That + // + + println(map.typeSignature) + println(map.typeSignatureIn(cil)) + println(distinct.typeSignature) + if (failed) sys.exit(1) + } +} diff --git a/test/files/run/macro-reify-type.check b/test/files/run/macro-reify-type.check new file mode 100644 index 0000000000..ea5e70e10d --- /dev/null +++ b/test/files/run/macro-reify-type.check @@ -0,0 +1 @@ +[B, That](f: Int => B)(implicit bf: scala.collection.generic.CanBuildFrom[List[Int],B,That])That
\ No newline at end of file diff --git a/test/files/run/macro-reify-type.flags b/test/files/run/macro-reify-type.flags new file mode 100644 index 0000000000..cd66464f2f --- /dev/null +++ b/test/files/run/macro-reify-type.flags @@ -0,0 +1 @@ +-language:experimental.macros
\ No newline at end of file diff --git a/test/files/run/macro-reify-type/Macros_1.scala b/test/files/run/macro-reify-type/Macros_1.scala new file mode 100644 index 0000000000..06de05735d --- /dev/null +++ b/test/files/run/macro-reify-type/Macros_1.scala @@ -0,0 +1,27 @@ +import scala.reflect.macros.Context +import scala.reflect.runtime.{universe => ru} + +object StaticReflect { + def method[A](name: String): ru.Type = macro methodImpl[A] + + def methodImpl[A: c.WeakTypeTag](c: Context)(name: c.Expr[String]): c.Expr[ru.Type] = { + import c.universe._ + + val nameName: TermName = name.tree match { + case Literal(Constant(str: String)) => newTermName(str) + case _ => c.error(c.enclosingPosition, s"Method name not constant.") ; return reify(ru.NoType) + } + val clazz = weakTypeOf[A] + + clazz member nameName match { + case NoSymbol => c.error(c.enclosingPosition, s"No member called $nameName in $clazz.") ; reify(ru.NoType) + case member => + val mtpe = member typeSignatureIn clazz + val mtag = c.reifyType(treeBuild.mkRuntimeUniverseRef, Select(treeBuild.mkRuntimeUniverseRef, newTermName("rootMirror")), mtpe) + val mtree = Select(mtag, newTermName("tpe")) + + c.Expr[ru.Type](mtree) + } + } + +} diff --git a/test/files/run/macro-reify-type/Test_2.scala b/test/files/run/macro-reify-type/Test_2.scala new file mode 100644 index 0000000000..9beaf98681 --- /dev/null +++ b/test/files/run/macro-reify-type/Test_2.scala @@ -0,0 +1,21 @@ +import StaticReflect._ + +object Test extends App { + //println(method[List[Int]]("distinct")) + println(method[List[Int]]("map")) + //val $u: scala.reflect.runtime.universe.type = scala.reflect.runtime.universe; + //val $m: $u.Mirror = scala.reflect.runtime.universe.rootMirror; + //import $u._, $m._, Flag._ + //val tpe = { + // val symdef$B2 = build.newNestedSymbol(build.selectTerm(staticClass("scala.collection.TraversableLike"), "map"), newTypeName("B"), NoPosition, DEFERRED | PARAM, false); + // val symdef$That2 = build.newNestedSymbol(build.selectTerm(staticClass("scala.collection.TraversableLike"), "map"), newTypeName("That"), NoPosition, DEFERRED | PARAM, false); + // val symdef$f2 = build.newNestedSymbol(build.selectTerm(staticClass("scala.collection.TraversableLike"), "map"), newTermName("f"), NoPosition, PARAM, false); + // val symdef$bf2 = build.newNestedSymbol(build.selectTerm(staticClass("scala.collection.TraversableLike"), "map"), newTermName("bf"), NoPosition, IMPLICIT | PARAM, false); + // build.setTypeSignature(symdef$B2, TypeBounds(staticClass("scala.Nothing").asType.toTypeConstructor, staticClass("scala.Any").asType.toTypeConstructor)); + // build.setTypeSignature(symdef$That2, TypeBounds(staticClass("scala.Nothing").asType.toTypeConstructor, staticClass("scala.Any").asType.toTypeConstructor)); + // build.setTypeSignature(symdef$f2, TypeRef(ThisType(staticPackage("scala").asModule.moduleClass), staticClass("scala.Function1"), List(staticClass("scala.Int").asType.toTypeConstructor, TypeRef(NoPrefix, symdef$B2, List())))); + // build.setTypeSignature(symdef$bf2, TypeRef(ThisType(staticPackage("scala.collection.generic").asModule.moduleClass), staticClass("scala.collection.generic.CanBuildFrom"), List(TypeRef(ThisType(staticPackage("scala.collection.immutable").asModule.moduleClass), staticClass("scala.collection.immutable.List"), List(staticClass("scala.Int").asType.toTypeConstructor)), TypeRef(NoPrefix, symdef$B2, List()), TypeRef(NoPrefix, symdef$That2, List())))); + // PolyType(List(symdef$B2, symdef$That2), MethodType(List(symdef$f2), MethodType(List(symdef$bf2), TypeRef(NoPrefix, symdef$That2, List())))) + //} + //println(tpe) +}
\ No newline at end of file diff --git a/test/files/run/showraw_nosymbol.check b/test/files/run/showraw_nosymbol.check new file mode 100644 index 0000000000..c54fe74717 --- /dev/null +++ b/test/files/run/showraw_nosymbol.check @@ -0,0 +1 @@ +NoSymbol diff --git a/test/files/run/showraw_nosymbol.scala b/test/files/run/showraw_nosymbol.scala new file mode 100644 index 0000000000..fbdc1591c9 --- /dev/null +++ b/test/files/run/showraw_nosymbol.scala @@ -0,0 +1,5 @@ +import scala.reflect.runtime.universe._ + +object Test extends App { + println(showRaw(NoSymbol)) +}
\ No newline at end of file |