summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorPaul Phillips <paulp@improving.org>2012-09-27 16:12:50 -0700
committerEugene Burmako <xeno.by@gmail.com>2012-09-28 14:22:05 +0200
commitb5b614444cefe84861b06a09fbaf10d100556556 (patch)
tree1b7b0e8f8410daa2cd41b76257d09e97ad4a0958 /src
parenta6b81ac12a45866e97d30133c12dee775b93ea39 (diff)
downloadscala-b5b614444cefe84861b06a09fbaf10d100556556.tar.gz
scala-b5b614444cefe84861b06a09fbaf10d100556556.tar.bz2
scala-b5b614444cefe84861b06a09fbaf10d100556556.zip
Implementations of isValueType and isNonValueType.
Restrictions regarding how non-value types can be used have generally not been enforced explicitly, depending instead on the fact that the compiler wouldn't attempt to use them in strange ways like offering a method type as a type argument. Since users can now create most types from scratch, it has become important to enforce the restrictions in a more direct fashion. This was a lot harder than it probably should have been because there are so many types which go unmentioned by the specification. Hopefully a useful exercise in any case.
Diffstat (limited to 'src')
-rw-r--r--src/compiler/scala/reflect/reify/utils/Extractors.scala13
-rw-r--r--src/reflect/scala/reflect/internal/Types.scala116
2 files changed, 117 insertions, 12 deletions
diff --git a/src/compiler/scala/reflect/reify/utils/Extractors.scala b/src/compiler/scala/reflect/reify/utils/Extractors.scala
index 523520749b..b60d15c1d4 100644
--- a/src/compiler/scala/reflect/reify/utils/Extractors.scala
+++ b/src/compiler/scala/reflect/reify/utils/Extractors.scala
@@ -92,13 +92,12 @@ trait Extractors {
Block(List(universeAlias, mirrorAlias), wrappee)
}
- private def mkTarg(tpe: Type): Tree = {
- // 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
- val guineaPig = Apply(TypeApply(Select(Select(gen.mkRuntimeUniverseRef, nme.TypeTag), nme.apply), List(TypeTree(tpe))), List(Literal(Constant(null)), Literal(Constant(null))))
- val isGoodTpe = typer.silent(_.typed(guineaPig)) match { case analyzer.SilentResultValue(_) => true; case _ => false }
- TypeTree(if (isGoodTpe) tpe else AnyTpe)
- }
+ // 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 = {
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