diff options
Diffstat (limited to 'src')
36 files changed, 279 insertions, 174 deletions
diff --git a/src/compiler/scala/tools/nsc/Interpreter.scala b/src/compiler/scala/tools/nsc/Interpreter.scala index e5bc1cf732..e36877837c 100644 --- a/src/compiler/scala/tools/nsc/Interpreter.scala +++ b/src/compiler/scala/tools/nsc/Interpreter.scala @@ -156,7 +156,7 @@ class Interpreter(val settings: Settings, out: PrintWriter) { else null } - import compiler.{ Traverser, CompilationUnit, Symbol, Name, TermName, TypeName, Type, TypeRef, PolyType } + import compiler.{ Traverser, CompilationUnit, Symbol, Name, TermName, TypeName, Type, TypeRef, NullaryMethodType } import compiler.{ Tree, TermTree, ValOrDefDef, ValDef, DefDef, Assign, ClassDef, ModuleDef, Ident, BackQuotedIdent, Select, TypeDef, Import, MemberDef, DocDef, @@ -1008,7 +1008,7 @@ class Interpreter(val settings: Settings, out: PrintWriter) { def toType(name: Name): T = { // the types are all =>T; remove the => val tp1 = afterTyper(resObjSym.info.nonPrivateDecl(name).tpe match { - case PolyType(Nil, tp) => tp + case NullaryMethodType(tp) => tp case tp => tp }) // normalize non-public types so we don't see protected aliases like Self diff --git a/src/compiler/scala/tools/nsc/ast/TreeBrowsers.scala b/src/compiler/scala/tools/nsc/ast/TreeBrowsers.scala index 8fa27c04f6..969790eabc 100644 --- a/src/compiler/scala/tools/nsc/ast/TreeBrowsers.scala +++ b/src/compiler/scala/tools/nsc/ast/TreeBrowsers.scala @@ -652,6 +652,12 @@ abstract class TreeBrowsers { toDocument(result) :: ")") ) + case NullaryMethodType(result) => + Document.group( + Document.nest(4,"NullaryMethodType(" :/: + toDocument(result) :: ")") + ) + case PolyType(tparams, result) => Document.group( Document.nest(4,"PolyType(" :/: diff --git a/src/compiler/scala/tools/nsc/ast/TreeInfo.scala b/src/compiler/scala/tools/nsc/ast/TreeInfo.scala index c2623ca5cc..78c7c9e3f0 100644 --- a/src/compiler/scala/tools/nsc/ast/TreeInfo.scala +++ b/src/compiler/scala/tools/nsc/ast/TreeInfo.scala @@ -97,7 +97,7 @@ abstract class TreeInfo { } def mayBeVarGetter(sym: Symbol) = sym.info match { - case PolyType(Nil, _) => sym.owner.isClass && !sym.isStable + case NullaryMethodType(_) => sym.owner.isClass && !sym.isStable case mt @ MethodType(_, _) => mt.isImplicit && sym.owner.isClass && !sym.isStable case _ => false } diff --git a/src/compiler/scala/tools/nsc/dependencies/Changes.scala b/src/compiler/scala/tools/nsc/dependencies/Changes.scala index 9794d71db1..42ccb2de0b 100644 --- a/src/compiler/scala/tools/nsc/dependencies/Changes.scala +++ b/src/compiler/scala/tools/nsc/dependencies/Changes.scala @@ -116,6 +116,8 @@ abstract class Changes { mt1.isImplicit == mt2.isImplicit case (PolyType(tparams1, res1), PolyType(tparams2, res2)) => sameTypeParams(tparams1, tparams2) && sameType(res1, res2) + case (NullaryMethodType(res1), NullaryMethodType(res2)) => + sameType(res1, res2) case (ExistentialType(tparams1, res1), ExistentialType(tparams2, res2)) => sameTypeParams(tparams1, tparams2)(false) && sameType(res1, res2)(false) case (TypeBounds(lo1, hi1), TypeBounds(lo2, hi2)) => diff --git a/src/compiler/scala/tools/nsc/dependencies/DependencyAnalysis.scala b/src/compiler/scala/tools/nsc/dependencies/DependencyAnalysis.scala index e9cc191167..3e8c9cfea7 100644 --- a/src/compiler/scala/tools/nsc/dependencies/DependencyAnalysis.scala +++ b/src/compiler/scala/tools/nsc/dependencies/DependencyAnalysis.scala @@ -234,6 +234,10 @@ trait DependencyAnalysis extends SubComponent with Files { checkType(t.resultType) updateReferences(t.typeSymbol.fullName) + case t: NullaryMethodType => + checkType(t.resultType) + updateReferences(t.typeSymbol.fullName) + case t => updateReferences(t.typeSymbol.fullName) } diff --git a/src/compiler/scala/tools/nsc/doc/model/ModelFactory.scala b/src/compiler/scala/tools/nsc/doc/model/ModelFactory.scala index 449fdca7b9..fb6d20848f 100644 --- a/src/compiler/scala/tools/nsc/doc/model/ModelFactory.scala +++ b/src/compiler/scala/tools/nsc/doc/model/ModelFactory.scala @@ -132,6 +132,7 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { thisFactory def resultTpe(tpe: Type): Type = tpe match { // similar to finalResultType, except that it leaves singleton types alone case PolyType(_, res) => resultTpe(res) case MethodType(_, res) => resultTpe(res) + case NullaryMethodType(res) => resultTpe(res) case _ => tpe } makeType(resultTpe(sym.tpe), inTemplate, sym) @@ -577,8 +578,12 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { thisFactory if (!defs.isEmpty) { nameBuffer append " {...}" // TODO: actually print the refinement } + /* Eval-by-name types */ + case NullaryMethodType(result) => + nameBuffer append '⇒' + appendType0(result) /* Polymorphic types */ - case PolyType(tparams, result) if tparams nonEmpty => + case PolyType(tparams, result) => assert(tparams nonEmpty) // throw new Error("Polymorphic type '" + tpe + "' cannot be printed as a type") def typeParamsToString(tps: List[Symbol]): String = if(tps isEmpty) "" else tps.map{tparam => @@ -586,9 +591,6 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { thisFactory }.mkString("[", ", ", "]") nameBuffer append typeParamsToString(tparams) appendType0(result) - case PolyType(tparams, result) if (tparams.isEmpty) => - nameBuffer append '⇒' - appendType0(result) case tpen => nameBuffer append tpen.toString } diff --git a/src/compiler/scala/tools/nsc/interpreter/Completion.scala b/src/compiler/scala/tools/nsc/interpreter/Completion.scala index 971146ea14..bb177a9946 100644 --- a/src/compiler/scala/tools/nsc/interpreter/Completion.scala +++ b/src/compiler/scala/tools/nsc/interpreter/Completion.scala @@ -66,9 +66,9 @@ class Completion(val repl: Interpreter) extends CompletionOutput { trait CompilerCompletion { def tp: Type def effectiveTp = tp match { - case MethodType(Nil, resType) => resType - case PolyType(Nil, resType) => resType - case _ => tp + case MethodType(Nil, resType) => resType + case NullaryMethodType(resType) => resType + case _ => tp } // for some reason any's members don't show up in subclasses, which diff --git a/src/compiler/scala/tools/nsc/interpreter/CompletionOutput.scala b/src/compiler/scala/tools/nsc/interpreter/CompletionOutput.scala index e0d678b961..e977e28922 100644 --- a/src/compiler/scala/tools/nsc/interpreter/CompletionOutput.scala +++ b/src/compiler/scala/tools/nsc/interpreter/CompletionOutput.scala @@ -77,7 +77,7 @@ trait CompletionOutput { def methodString() = method.keyString + " " + method.nameString + (method.info.normalize match { - case PolyType(Nil, resType) => ": " + typeToString(resType) // nullary method + case NullaryMethodType(resType) => ": " + typeToString(resType) case PolyType(tparams, resType) => tparamsString(tparams) + typeToString(resType) case mt @ MethodType(_, _) => methodTypeToString(mt) case x => diff --git a/src/compiler/scala/tools/nsc/symtab/AnnotationInfos.scala b/src/compiler/scala/tools/nsc/symtab/AnnotationInfos.scala index 2429f53aa1..24672cc411 100644 --- a/src/compiler/scala/tools/nsc/symtab/AnnotationInfos.scala +++ b/src/compiler/scala/tools/nsc/symtab/AnnotationInfos.scala @@ -107,6 +107,8 @@ trait AnnotationInfos extends reflect.generic.AnnotationInfos { self: SymbolTabl this } + lazy val isTrivial: Boolean = atp.isTrivial && !(args exists (_.exists(_.isInstanceOf[This]))) // see annotationArgRewriter + override def toString: String = atp + (if (!args.isEmpty) args.mkString("(", ", ", ")") else "") + (if (!assocs.isEmpty) (assocs map { case (x, y) => x+" = "+y } mkString ("(", ", ", ")")) else "") diff --git a/src/compiler/scala/tools/nsc/symtab/BaseTypeSeqs.scala b/src/compiler/scala/tools/nsc/symtab/BaseTypeSeqs.scala index 72dc5b9e91..5c0fe74967 100644 --- a/src/compiler/scala/tools/nsc/symtab/BaseTypeSeqs.scala +++ b/src/compiler/scala/tools/nsc/symtab/BaseTypeSeqs.scala @@ -149,6 +149,8 @@ trait BaseTypeSeqs { max(maxDpth(lo), maxDpth(hi)) case MethodType(paramtypes, result) => maxDpth(result) + case NullaryMethodType(result) => + maxDpth(result) case PolyType(tparams, result) => max(maxDpth(result), maxDpth(tparams map (_.info)) + 1) case ExistentialType(tparams, result) => diff --git a/src/compiler/scala/tools/nsc/symtab/Definitions.scala b/src/compiler/scala/tools/nsc/symtab/Definitions.scala index 340ab09c81..ecda252ec6 100644 --- a/src/compiler/scala/tools/nsc/symtab/Definitions.scala +++ b/src/compiler/scala/tools/nsc/symtab/Definitions.scala @@ -28,7 +28,7 @@ trait Definitions extends reflect.generic.StandardDefinitions { lazy val RootPackage: Symbol = { val rp = NoSymbol.newValue(NoPosition, nme.ROOTPKG) .setFlag(FINAL | MODULE | PACKAGE | JAVA) - .setInfo(PolyType(List(), RootClass.tpe)) + .setInfo(NullaryMethodType(RootClass.tpe)) RootClass.sourceModule = rp rp } @@ -227,7 +227,7 @@ trait Definitions extends reflect.generic.StandardDefinitions { ) lazy val EqualsPatternClass = { val clazz = newClass(ScalaPackageClass, tpnme.EQUALS_PATTERN_NAME, Nil) - clazz setInfo PolyType(List(newTypeParam(clazz, 0)), ClassInfoType(anyparam, new Scope, clazz)) + clazz setInfo polyType(List(newTypeParam(clazz, 0)), ClassInfoType(anyparam, new Scope, clazz)) } // collections classes @@ -587,7 +587,7 @@ trait Definitions extends reflect.generic.StandardDefinitions { else*/ List(p) println("creating " + name + " with parents " + parents) */ clazz.setInfo( - PolyType( + polyType( List(tparam), ClassInfoType(List(AnyRefClass.tpe, p), new Scope, clazz))) } @@ -619,11 +619,11 @@ trait Definitions extends reflect.generic.StandardDefinitions { private def newPolyMethodCon(owner: Symbol, name: TermName, tcon: Symbol => Symbol => Type): Symbol = { val msym = newMethod(owner, name) val tparam = newTypeParam(msym, 0) - msym.setInfo(PolyType(List(tparam), tcon(tparam)(msym))) + msym.setInfo(polyType(List(tparam), tcon(tparam)(msym))) } private def newParameterlessMethod(owner: Symbol, name: TermName, restpe: Type) = - newMethod(owner, name).setInfo(PolyType(List(),restpe)) + newMethod(owner, name).setInfo(NullaryMethodType(restpe)) private def newTypeParam(owner: Symbol, index: Int): Symbol = owner.newTypeParameter(NoPosition, newTypeName("T" + index)) setInfo TypeBounds.empty @@ -889,9 +889,9 @@ trait Definitions extends reflect.generic.StandardDefinitions { Any_## = newMethod(AnyClass, nme.HASHHASH, Nil, inttype) setFlag FINAL Any_isInstanceOf = newPolyMethod( - AnyClass, nme.isInstanceOf_, tparam => booltype) setFlag FINAL + AnyClass, nme.isInstanceOf_, tparam => NullaryMethodType(booltype)) setFlag FINAL Any_asInstanceOf = newPolyMethod( - AnyClass, nme.asInstanceOf_, tparam => tparam.typeConstructor) setFlag FINAL + AnyClass, nme.asInstanceOf_, tparam => NullaryMethodType(tparam.typeConstructor)) setFlag FINAL // members of class java.lang.{ Object, String } Object_## = newMethod(ObjectClass, nme.HASHHASH, Nil, inttype) setFlag FINAL diff --git a/src/compiler/scala/tools/nsc/symtab/Symbols.scala b/src/compiler/scala/tools/nsc/symtab/Symbols.scala index c269472aa3..12f044aafe 100644 --- a/src/compiler/scala/tools/nsc/symtab/Symbols.scala +++ b/src/compiler/scala/tools/nsc/symtab/Symbols.scala @@ -510,6 +510,7 @@ trait Symbols extends reflect.generic.Symbols { self: SymbolTable => case ConstantType(_) => true case PolyType(_, ConstantType(_)) => true case MethodType(_, ConstantType(_)) => true + case NullaryMethodType(ConstantType(_)) => true case _ => false }) @@ -1604,8 +1605,10 @@ trait Symbols extends reflect.generic.Symbols { self: SymbolTable => tp match { case PolyType(tparams, res) => typeParamsString + infoString(res) + case NullaryMethodType(res) => + infoString(res) case MethodType(params, res) => - params.map(_.defString).mkString("(", ",", ")") + infoString(res) + params.map(_.defString).mkString("(", ",", ")") + infoString(res) case _ => ": " + tp } diff --git a/src/compiler/scala/tools/nsc/symtab/Types.scala b/src/compiler/scala/tools/nsc/symtab/Types.scala index f302f5c6ea..cbecd073f6 100644 --- a/src/compiler/scala/tools/nsc/symtab/Types.scala +++ b/src/compiler/scala/tools/nsc/symtab/Types.scala @@ -50,14 +50,13 @@ import scala.annotation.tailrec case MethodType(paramtypes, result) => // (paramtypes)result // For instance def m(): T is represented as MethodType(List(), T) + case NullaryMethodType(result) => // eliminated by uncurry + // an eval-by-name type + // For instance def m: T is represented as NullaryMethodType(T) case PolyType(tparams, result) => - // [tparams]result where result is a MethodType or ClassInfoType - // or - // []T for a eval-by-name type - // For instance def m: T is represented as PolyType(List(), T) + // [tparams]result where result is a (Nullary)MethodType or ClassInfoType // The remaining types are not used after phase `typer'. - case OverloadedType(pre, tparams, alts) => // all alternatives of an overloaded ident case AntiPolyType(pre, targs) => @@ -365,7 +364,7 @@ trait Types extends reflect.generic.Types { self: SymbolTable => /** For a typeref, its arguments. The empty list for all other types */ def typeArgs: List[Type] = List() - /** For a method or poly type, its direct result type, + /** For a (nullary) method or poly type, its direct result type, * the type itself for all other types. */ def resultType: Type = this @@ -379,11 +378,11 @@ trait Types extends reflect.generic.Types { self: SymbolTable => */ def remove(clazz: Symbol): Type = this - /** For a curried method or poly type its non-method result type, + /** For a curried/nullary method or poly type its non-method result type, * the type itself for all other types */ def finalResultType: Type = this - /** For a method or poly type, the number of its value parameter sections, + /** For a method type, the number of its value parameter sections, * 0 for all other types */ def paramSectionCount: Int = 0 @@ -708,7 +707,7 @@ trait Types extends reflect.generic.Types { self: SymbolTable => * - Either both types are polytypes with the same number of * type parameters and their result types match after renaming * corresponding type parameters - * - Or both types are method types with equivalent type parameter types + * - Or both types are (nullary) method types with equivalent type parameter types * and matching result types * - Or both types are equivalent * - Or phase.erasedTypes is false and both types are neither method nor @@ -1137,14 +1136,6 @@ trait Types extends reflect.generic.Types { self: SymbolTable => else sym.tpe } - // case class DeBruijnIndex(level: Int, paramId: Int) extends Type { - // override def isTrivial = true - // override def isStable = true - // override def safeToString = "<param "+level+"."+paramId+">" - // override def kind = "DeBruijnIndex" - // // todo: this should be a subtype, which forwards to underlying - // } - /** A class for singleton types of the form <prefix>.<sym.name>.type. * Cannot be created directly; one should always use * `singleType' for creation. @@ -1416,7 +1407,7 @@ trait Types extends reflect.generic.Types { self: SymbolTable => */ override def normalize = { if (isHigherKinded) { - PolyType( + typeFun( typeParams, RefinedType( parents map { @@ -1807,11 +1798,10 @@ A type's typeSymbol should never be inspected directly. transform(sym.info.resultType) } - // @M TODO: should not use PolyType, as that's the type of a polymorphic value -- we really want a type *function* // @M: initialize (by sym.info call) needed (see test/files/pos/ticket0137.scala) @inline private def etaExpand: Type = { val tpars = sym.info.typeParams // must go through sym.info for typeParams to initialise symbol - PolyType(tpars, typeRef(pre, sym, tpars map (_.tpeHK))) // todo: also beta-reduce? + typeFunAnon(tpars, typeRef(pre, sym, tpars map (_.tpeHK))) // todo: also beta-reduce? } override def dealias: Type = @@ -1963,11 +1953,10 @@ A type's typeSymbol should never be inspected directly. } /** A class representing a method type with parameters. - * Note that a parameterless method is instead encoded - * as a PolyType, as shown here: + * Note that a parameterless method is represented by a NullaryMethodType: * * def m(): Int MethodType(Nil, Int) - * def m: Int PolyType(Nil, Int) + * def m: Int NullaryMethodType(Int) */ case class MethodType(override val params: List[Symbol], override val resultType: Type) extends Type { @@ -1989,6 +1978,7 @@ A type's typeSymbol should never be inspected directly. override def boundSyms = immutable.Set[Symbol](params ++ resultType.boundSyms: _*) + // AM to TR: #dropNonContraintAnnotations // this is needed for plugins to work correctly, only TypeConstraint annotations are supposed to be carried over // TODO: this should probably be handled in a more structured way in adapt -- remove this map in resultType and watch the continuations tests fail object dropNonContraintAnnotations extends TypeMap { @@ -2040,18 +2030,47 @@ A type's typeSymbol should never be inspected directly. override def isJava = true } - /** A class representing a polymorphic type or, if typeParams.isEmpty, a parameterless method type. + case class NullaryMethodType(override val resultType: Type) extends Type { + // AM to TR: #dropNonContraintAnnotations + // change isTrivial to the commented version and watch continuations-run/t3225.scala fail + // isTrivial implies asSeenFrom is bypassed, since it's supposed to be the identity map + // it's not really the identity due to dropNonContraintAnnotations + override def isTrivial: Boolean = false //resultType.isTrivial -- `false` to make continuations plugin work (so that asSeenFromMap drops non-constrain annotations even when type doesn't change otherwise) + override def prefix: Type = resultType.prefix + override def narrow: Type = resultType.narrow + override def finalResultType: Type = resultType.finalResultType + override def termSymbol: Symbol = resultType.termSymbol + override def typeSymbol: Symbol = resultType.typeSymbol + override def parents: List[Type] = resultType.parents + override def decls: Scope = resultType.decls + override def baseTypeSeq: BaseTypeSeq = resultType.baseTypeSeq + override def baseTypeSeqDepth: Int = resultType.baseTypeSeqDepth + override def baseClasses: List[Symbol] = resultType.baseClasses + override def baseType(clazz: Symbol): Type = resultType.baseType(clazz) + override def boundSyms = resultType.boundSyms + override def isVolatile = resultType.isVolatile + override def safeToString: String = "=> "+ resultType + override def kind = "NullaryMethodType" + } + + object NullaryMethodType extends NullaryMethodTypeExtractor + + /** A type function or the type of a polymorphic value (and thus of kind *). + * + * Before the introduction of NullaryMethodType, a polymorphic nullary method (e.g, def isInstanceOf[T]: Boolean) + * used to be typed as PolyType(tps, restpe), and a monomorphic one as PolyType(Nil, restpe) + * This is now: PolyType(tps, NullaryMethodType(restpe)) and NullaryMethodType(restpe) + * by symmetry to MethodTypes: PolyType(tps, MethodType(params, restpe)) and MethodType(params, restpe) + * + * Thus, a PolyType(tps, TypeRef(...)) unambiguously indicates a type function (which results from eta-expanding a type constructor alias). + * Similarly, PolyType(tps, ClassInfoType(...)) is a type constructor. * - * (@M: note that polymorphic nullary methods have non-empty tparams, - * e.g., isInstanceOf or def makeList[T] = new List[T]. - * Ideally, there would be a NullaryMethodType, but since the only polymorphic values are methods, it's not that problematic. - * More pressingly, we should add a TypeFunction type for anonymous type constructors -- for now, PolyType is used in: - * - normalize: for eta-expansion of type aliases - * - typeDefSig ) + * A polytype is of kind * iff its resultType is a (nullary) method type. */ case class PolyType(override val typeParams: List[Symbol], override val resultType: Type) extends Type { - // assert(!(typeParams contains NoSymbol), this) + //assert(!(typeParams contains NoSymbol), this) + assert(typeParams nonEmpty, this) // used to be a marker for nullary method type, illegal now (see @NullaryMethodType) override def paramSectionCount: Int = resultType.paramSectionCount override def paramss: List[List[Symbol]] = resultType.paramss @@ -2071,19 +2090,18 @@ A type's typeSymbol should never be inspected directly. override def isVolatile = resultType.isVolatile override def finalResultType: Type = resultType.finalResultType - /** @M: typeDefSig now wraps a TypeBounds in a PolyType + /** @M: typeDefSig wraps a TypeBounds in a PolyType * to represent a higher-kinded type parameter * wrap lo&hi in polytypes to bind variables */ override def bounds: TypeBounds = - TypeBounds(PolyType(typeParams, resultType.bounds.lo), - PolyType(typeParams, resultType.bounds.hi)) + TypeBounds(typeFun(typeParams, resultType.bounds.lo), + typeFun(typeParams, resultType.bounds.hi)) override def isHigherKinded = !typeParams.isEmpty override def safeToString: String = - (if (typeParams.isEmpty) "=> " - else (typeParams map (_.defString) mkString ("[", ",", "]")))+resultType + (typeParams map (_.defString) mkString ("[", ",", "]"))+ resultType override def cloneInfo(owner: Symbol) = { val tparams = cloneSymbols(typeParams, owner) @@ -2167,7 +2185,7 @@ A type's typeSymbol should never be inspected directly. } var ustr = underlying.toString underlying match { - case MethodType(_, _) | PolyType(_, _) => ustr = "("+ustr+")" + case MethodType(_, _) | NullaryMethodType(_) | PolyType(_, _) => ustr = "("+ustr+")" case _ => } val str = @@ -2439,8 +2457,7 @@ A type's typeSymbol should never be inspected directly. override def normalize: Type = if (constr.instValid) constr.inst else if (isHigherKinded) { // get here when checking higher-order subtyping of the typevar by itself (TODO: check whether this ever happens?) - // @M TODO: should not use PolyType, as that's the type of a polymorphic value -- we really want a type *function* - PolyType(params, applyArgs(params map (_.typeConstructor))) + typeFun(params, applyArgs(params map (_.typeConstructor))) } else { super.normalize } @@ -2483,6 +2500,9 @@ A type's typeSymbol should never be inspected directly. override protected def rewrap(tp: Type) = AnnotatedType(annotations, tp, selfsym) + override def isTrivial: Boolean = isTrivial0 + private lazy val isTrivial0 = underlying.isTrivial && (annotations forall (_.isTrivial)) + override def safeToString: String = { val attString = if (annotations.isEmpty) @@ -2725,16 +2745,24 @@ A type's typeSymbol should never be inspected directly. case _ => abort(debugString(tycon)) } - /** A creator for type parameterizations - * If tparams is empty, simply returns result type + /** A creator for type parameterizations that strips empty type parameter lists. + * Use this factory method to indicate the type has kind * (it's a polymorphic value) + * until we start tracking explicit kinds equivalent to typeFun (except that the latter requires tparams nonEmpty) */ def polyType(tparams: List[Symbol], tpe: Type): Type = - if (tparams.isEmpty) tpe - else - PolyType(tparams, tpe match { - case PolyType(List(), tpe1) => tpe1 - case _ => tpe - }) + if (tparams nonEmpty) typeFun(tparams, tpe) + else tpe // it's okay to be forgiving here + + /** A creator for anonymous type functions, where the symbol for the type function still needs to be created + * TODO_NMT: + * - can we avoid this whole synthetic owner business? harden ASF instead to deal with orphan type params + * - orthogonally: try to recycle the class symbol in the common case type C[X] = Class[X] (where appliedType(this, dummyArgs) =:= appliedType(sym.info, dummyArgs)) + */ + def typeFunAnon(tps: List[Symbol], body: Type): Type = typeFun(tps, body) + + /** A creator for a type functions, assuming the type parameters tps already have the right owner + */ + def typeFun(tps: List[Symbol], body: Type): Type = PolyType(tps, body) /** A creator for existential types. This generates: * @@ -3011,8 +3039,11 @@ A type's typeSymbol should never be inspected directly. var result1 = this(result) if ((tparams1 eq tparams) && (result1 eq result)) tp else PolyType(tparams1, result1.substSym(tparams, tparams1)) + case NullaryMethodType(result) => + val result1 = this(result) + if (result1 eq result) tp + else NullaryMethodType(result1) case ConstantType(_) => tp - // case DeBruijnIndex(_, _) => tp case SuperType(thistp, supertp) => val thistp1 = this(thistp) val supertp1 = this(supertp) @@ -3251,13 +3282,13 @@ A type's typeSymbol should never be inspected directly. override def mapOver(tree: Tree, giveup: ()=>Nothing): Tree = { object annotationArgRewriter extends TypeMapTransformer { - /** Rewrite "this" trees as needed for asSeenFrom */ + /** Rewrite `This` trees in annotation argument trees */ def rewriteThis(tree: Tree): Tree = tree match { case This(_) if (tree.symbol isNonBottomSubClass clazz) && (pre.widen.typeSymbol isNonBottomSubClass tree.symbol) => - if (pre.isStable) { + if (pre.isStable) { // XXX why is this in this method? pull it out and guard the call `annotationArgRewriter.transform(tree)`? val termSym = pre.typeSymbol.owner.newValue( pre.typeSymbol.pos, @@ -3846,6 +3877,10 @@ A type's typeSymbol should never be inspected directly. val restp1 = this(restp) if (restp1 eq restp) tp else copyMethodType(tp, params, restp1) + case NullaryMethodType(restp) => + val restp1 = this(restp) + if (restp1 eq restp) tp + else NullaryMethodType(restp1) case PolyType(tparams, restp) => val restp1 = this(restp) if (restp1 eq restp) tp @@ -4235,6 +4270,14 @@ A type's typeSymbol should never be inspected directly. return isSameTypes(mt1.paramTypes, mt2.paramTypes) && mt1.resultType =:= mt2.resultType && mt1.isImplicit == mt2.isImplicit + // note: no case NullaryMethodType(restpe) => return mt1.params.isEmpty && mt1.resultType =:= restpe + case _ => + } + case NullaryMethodType(restpe1) => + tp2 match { + // note: no case mt2: MethodType => return mt2.params.isEmpty && restpe =:= mt2.resultType + case NullaryMethodType(restpe2) => + return restpe1 =:= restpe2 case _ => } case PolyType(tparams1, res1) => @@ -4584,13 +4627,14 @@ A type's typeSymbol should never be inspected directly. matchingParams(params1, params2, mt1.isJava, mt2.isJava) && (res1 <:< res2) && mt1.isImplicit == mt2.isImplicit) + // TODO: if mt1.params.isEmpty, consider NullaryMethodType? case _ => false } - case pt2 @ PolyType(List(), _) => + case pt2 @ NullaryMethodType(_) => tp1 match { - case pt1 @ PolyType(List(), _) => - // other polytypes were already checked in isHKSubType + // TODO: consider MethodType mt for which mt.params.isEmpty?? + case pt1 @ NullaryMethodType(_) => pt1.resultType <:< pt2.resultType case _ => false @@ -4706,7 +4750,7 @@ A type's typeSymbol should never be inspected directly. matchingParams(params1, params2, mt1.isJava, mt2.isJava) && matchesType(res1, res2, alwaysMatchSimple) && mt1.isImplicit == mt2.isImplicit - case PolyType(List(), res2) => + case NullaryMethodType(res2) => if (params1.isEmpty) matchesType(res1, res2, alwaysMatchSimple) else matchesType(tp1, res2, alwaysMatchSimple) case ExistentialType(_, res2) => @@ -4714,16 +4758,25 @@ A type's typeSymbol should never be inspected directly. case _ => false } + case mt1 @ NullaryMethodType(res1) => + tp2 match { + case mt2 @ MethodType(Nil, res2) => // could never match if params nonEmpty, and !mt2.isImplicit is implied by empty param list + matchesType(res1, res2, alwaysMatchSimple) + case NullaryMethodType(res2) => + matchesType(res1, res2, alwaysMatchSimple) + case ExistentialType(_, res2) => + alwaysMatchSimple && matchesType(tp1, res2, true) + case _ => + matchesType(res1, tp2, alwaysMatchSimple) + } case PolyType(tparams1, res1) => tp2 match { case PolyType(tparams2, res2) => matchesQuantified(tparams1, tparams2, res1, res2) - case MethodType(List(), res2) if (tparams1.isEmpty) => - matchesType(res1, res2, alwaysMatchSimple) case ExistentialType(_, res2) => alwaysMatchSimple && matchesType(tp1, res2, true) case _ => - tparams1.isEmpty && matchesType(res1, tp2, alwaysMatchSimple) + false // remember that tparams1.nonEmpty is now an invariant of PolyType } case ExistentialType(tparams1, res1) => tp2 match { @@ -4752,9 +4805,9 @@ A type's typeSymbol should never be inspected directly. tp1.isImplicit == tp2.isImplicit case (PolyType(tparams1, res1), PolyType(tparams2, res2)) => matchesQuantified(tparams1, tparams2, res1, res2) - case (PolyType(List(), rtp1), MethodType(List(), rtp2)) => + case (NullaryMethodType(rtp1), MethodType(List(), rtp2)) => matchesType(rtp1, rtp2, alwaysMatchSimple) - case (MethodType(List(), rtp1), PolyType(List(), rtp2)) => + case (MethodType(List(), rtp1), NullaryMethodType(rtp2)) => matchesType(rtp1, rtp2, alwaysMatchSimple) case (ExistentialType(tparams1, res1), ExistentialType(tparams2, res2)) => matchesQuantified(tparams1, tparams2, res1, res2) @@ -4762,9 +4815,9 @@ A type's typeSymbol should never be inspected directly. matchesType(res1, tp2, alwaysMatchSimple) case (_, ExistentialType(_, res2)) if alwaysMatchSimple => matchesType(tp1, res2, alwaysMatchSimple) - case (PolyType(List(), rtp1), _) => + case (NullaryMethodType(rtp1), _) => matchesType(rtp1, tp2, alwaysMatchSimple) - case (_, PolyType(List(), rtp2)) => + case (_, NullaryMethodType(rtp2)) => matchesType(tp1, rtp2, alwaysMatchSimple) case (MethodType(_, _), _) => false case (PolyType(_, _), _) => false @@ -5077,6 +5130,8 @@ A type's typeSymbol should never be inspected directly. PolyType(tparams1, lub0(matchingInstTypes(ts, tparams1))) case ts @ MethodType(params, _) :: rest => MethodType(params, lub0(matchingRestypes(ts, params map (_.tpe)))) + case ts @ NullaryMethodType(_) :: rest => + NullaryMethodType(lub0(matchingRestypes(ts, Nil))) case ts @ TypeBounds(_, _) :: rest => TypeBounds(glb(ts map (_.bounds.lo), depth), lub(ts map (_.bounds.hi), depth)) case ts => @@ -5192,6 +5247,8 @@ A type's typeSymbol should never be inspected directly. PolyType(tparams1, glb0(matchingInstTypes(ts, tparams1))) case ts @ MethodType(params, _) :: rest => MethodType(params, glb0(matchingRestypes(ts, params map (_.tpe)))) + case ts @ NullaryMethodType(_) :: rest => + NullaryMethodType(glb0(matchingRestypes(ts, Nil))) case ts @ TypeBounds(_, _) :: rest => TypeBounds(lub(ts map (_.bounds.lo), depth), glb(ts map (_.bounds.hi), depth)) case ts => @@ -5433,6 +5490,8 @@ A type's typeSymbol should never be inspected directly. tps map { case MethodType(params1, res) if (isSameTypes(params1 map (_.tpe), pts)) => res + case NullaryMethodType(res) if pts isEmpty => + res case _ => throw new NoCommonType(tps) } diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala index 253ebb708d..20f990d057 100644 --- a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala +++ b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala @@ -1261,7 +1261,7 @@ abstract class ClassfileParser { override def complete(sym: Symbol) { alias.initialize val tparams1 = cloneSymbols(alias.typeParams) - sym.setInfo(polyType(tparams1, alias.tpe.substSym(alias.typeParams, tparams1))) + sym.setInfo(typeFun(tparams1, alias.tpe.substSym(alias.typeParams, tparams1))) } } diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala b/src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala index 246bb6a88d..050b59118d 100644 --- a/src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala +++ b/src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala @@ -182,6 +182,8 @@ abstract class Pickler extends SubComponent { putSymbol(clazz); putTypes(parents); putSymbols(decls.toList) case MethodType(params, restpe) => putType(restpe); putSymbols(params) + case NullaryMethodType(restpe) => + putType(restpe) case PolyType(tparams, restpe) => /** no longer needed since all params are now local tparams foreach { tparam => @@ -575,7 +577,11 @@ abstract class Pickler extends SubComponent { writeRef(restpe); writeRefs(formals) if (mt.isImplicit) IMPLICITMETHODtpe else METHODtpe - case PolyType(tparams, restpe) => + case mt @ NullaryMethodType(restpe) => // reuse POLYtpe since those can never have an empty list of tparams -- TODO: is there any way this can come back and bite us in the bottom? + // ugliness and thrift aside, this should make this somewhat more backward compatible + // (I'm not sure how old scalac's would deal with nested PolyTypes, as these used to be folded into one) + writeRef(restpe); writeRefs(Nil); POLYtpe + case PolyType(tparams, restpe) => // invar: tparams nonEmpty writeRef(restpe); writeRefs(tparams); POLYtpe case ExistentialType(tparams, restpe) => writeRef(restpe); writeRefs(tparams); EXISTENTIALtpe diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/UnPickler.scala b/src/compiler/scala/tools/nsc/symtab/classfile/UnPickler.scala index afafce5abe..3427701899 100644 --- a/src/compiler/scala/tools/nsc/symtab/classfile/UnPickler.scala +++ b/src/compiler/scala/tools/nsc/symtab/classfile/UnPickler.scala @@ -50,7 +50,7 @@ abstract class UnPickler extends reflect.generic.UnPickler { private val definedAtRunId = currentRunId private val p = phase override def complete(sym: Symbol) : Unit = { - val tp = at(i, readType) + val tp = at(i, () => readType(sym.isTerm)) // after NMT_TRANSITION, revert `() => readType(sym.isTerm)` to `readType` if (p != phase) atPhase(p) (sym setInfo tp) else sym setInfo tp if (currentRunId != definedAtRunId) sym.setInfo(adaptToNewRunMap(tp)) diff --git a/src/compiler/scala/tools/nsc/symtab/clr/TypeParser.scala b/src/compiler/scala/tools/nsc/symtab/clr/TypeParser.scala index 3f5fc6fdcb..80092af22e 100644 --- a/src/compiler/scala/tools/nsc/symtab/clr/TypeParser.scala +++ b/src/compiler/scala/tools/nsc/symtab/clr/TypeParser.scala @@ -309,8 +309,8 @@ abstract class TypeParser { val flags = translateAttributes(getter); val owner: Symbol = if (getter.IsStatic) statics else clazz; val methodSym = owner.newMethod(NoPosition, name).setFlag(flags) - val mtype: Type = if (gparamsLength == 0) PolyType(List(), propType) // .NET properties can't be polymorphic - else methodType(getter, getter.ReturnType)(methodSym) + val mtype: Type = if (gparamsLength == 0) NullaryMethodType(propType) // .NET properties can't be polymorphic + else methodType(getter, getter.ReturnType)(methodSym) methodSym.setInfo(mtype); methodSym.setFlag(Flags.ACCESSOR); (if (getter.IsStatic) staticDefs else instanceDefs).enter(methodSym) diff --git a/src/compiler/scala/tools/nsc/transform/Erasure.scala b/src/compiler/scala/tools/nsc/transform/Erasure.scala index 998cf69f5e..46707fe445 100644 --- a/src/compiler/scala/tools/nsc/transform/Erasure.scala +++ b/src/compiler/scala/tools/nsc/transform/Erasure.scala @@ -402,7 +402,7 @@ abstract class Erasure extends AddInterfaces with typechecker.Analyzer with ast. val deconstMap = new TypeMap { def apply(tp: Type): Type = tp match { case PolyType(_, _) => mapOver(tp) - case MethodType(_, _) => mapOver(tp) + case MethodType(_, _) => mapOver(tp) // nullarymethod was eliminated during uncurry case _ => tp.deconst } } diff --git a/src/compiler/scala/tools/nsc/transform/Reifiers.scala b/src/compiler/scala/tools/nsc/transform/Reifiers.scala index 49ae43e1fe..e0ee4d3af8 100644 --- a/src/compiler/scala/tools/nsc/transform/Reifiers.scala +++ b/src/compiler/scala/tools/nsc/transform/Reifiers.scala @@ -79,6 +79,8 @@ trait Reifiers { if (_log_reify_type_) println("cannot handle ClassInfoType "+tp); reflect.NoType case MethodType(params, result) => reflect.MethodType(params.map(reify), reify(result)) + case NullaryMethodType(result) => + reflect.NullaryMethodType(reify(result)) case PolyType(tparams, result) => val boundss = for { @@ -146,6 +148,8 @@ trait Reifiers { TypeBounds(unreify(lo), unreify(hi)) case reflect.MethodType(params, restpe) => MethodType(params.map(unreify), unreify(restpe)) + case reflect.NullaryMethodType(restpe) => + NullaryMethodType(unreify(restpe)) case reflect.PolyType(typeParams, typeBounds, resultType) => PolyType(typeParams.map(unreify), unreify(resultType)) //todo: treat ExistentialType diff --git a/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala b/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala index 387f069875..d8c5370df6 100644 --- a/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala +++ b/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala @@ -327,6 +327,8 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { case PolyType(tparams, resTpe) => specializedTypeVars(tparams map (_.info)) ++ specializedTypeVars(resTpe) + case NullaryMethodType(resTpe) => // since this method may be run at phase typer (before uncurry, where NMTs are eliminated) + specializedTypeVars(resTpe) case MethodType(argSyms, resTpe) => specializedTypeVars(argSyms map (_.tpe)) ++ specializedTypeVars(resTpe) @@ -889,7 +891,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { if (settings.debug.value) log("transformInfo (poly) " + clazz + " with parents1: " + parents + " ph: " + phase) // if (clazz.name.toString == "$colon$colon") // (new Throwable).printStackTrace - PolyType(targs, ClassInfoType(parents, + polyType(targs, ClassInfoType(parents, new Scope(specializeClass(clazz, typeEnv(clazz)) ::: specialOverrides(clazz)), clazz)) diff --git a/src/compiler/scala/tools/nsc/transform/UnCurry.scala b/src/compiler/scala/tools/nsc/transform/UnCurry.scala index 1a6a36691d..3c8997657c 100644 --- a/src/compiler/scala/tools/nsc/transform/UnCurry.scala +++ b/src/compiler/scala/tools/nsc/transform/UnCurry.scala @@ -66,10 +66,8 @@ abstract class UnCurry extends InfoTransform with TypingTransformers with ast.Tr tp0 case MethodType(h :: t, restpe) if h.isImplicit => apply(MethodType(h.cloneSymbol.resetFlag(IMPLICIT) :: t, restpe)) - case PolyType(List(), restpe) => // nullary method type + case NullaryMethodType(restpe) => apply(MethodType(List(), restpe)) - case PolyType(tparams, restpe) => // polymorphic nullary method type, since it didn't occur in a higher-kinded position - PolyType(tparams, apply(MethodType(List(), restpe))) case TypeRef(pre, ByNameParamClass, List(arg)) => apply(functionType(List(), arg)) case TypeRef(pre, RepeatedParamClass, args) => @@ -81,29 +79,6 @@ abstract class UnCurry extends InfoTransform with TypingTransformers with ast.Tr expandAlias(mapOver(tp)) } } - -//@M TODO: better fix for the gross hack that conflates polymorphic nullary method types with type functions -// `[tpars] tref` (PolyType(tpars, tref)) could uncurry to either: -// - `[tpars]() tref` (PolyType(tpars, MethodType(List(), tref)) -// a nullary method types uncurry to a method with an empty argument list -// - `[tpars] tref` (PolyType(tpars, tref)) -// a proper type function -- see mapOverArgs: can only occur in args of TypeRef (right?)) -// the issue comes up when a partial type application gets normalised to a polytype, like `[A] Function1[X, A]` -// should not apply the uncurry transform to such a type -// see #2594 for an example - - // decide whether PolyType represents a nullary method type (only if type has kind *) - // for higher-kinded types, leave PolyType intact - override def mapOverArgs(args: List[Type], tparams: List[Symbol]): List[Type] = - map2Conserve(args, tparams) { (arg, tparam) => - arg match { - // is this a higher-kinded position? (TODO: confirm this is the only case) - case PolyType(tparams, restpe) if tparam.typeParams.nonEmpty => // higher-kinded type param - PolyType(tparams, apply(restpe)) // could not be a nullary method type - case _ => - this(arg) - } - } } private val uncurryType = new TypeMap { @@ -640,10 +615,10 @@ abstract class UnCurry extends InfoTransform with TypingTransformers with ast.Tr def postTransform(tree: Tree): Tree = atPhase(phase.next) { def applyUnary(): Tree = { - def needsParens = tree.symbol.isMethod && (!tree.tpe.isInstanceOf[PolyType] || tree.tpe.typeParams.isEmpty) + def needsParens = tree.symbol.isMethod && !tree.tpe.isInstanceOf[PolyType] // TODO_NMT: verify that the inner tree of a type-apply also gets parens if the whole tree is a polymorphic nullary method application def repair = { - if (!tree.tpe.isInstanceOf[MethodType]) - tree.tpe = MethodType(Nil, tree.tpe) + if (!tree.tpe.isInstanceOf[MethodType]) // i.e., it's a NullaryMethodType + tree.tpe = MethodType(Nil, tree.tpe.resultType) // TODO_NMT: I think the original `tree.tpe` was wrong, since that would set the method's resulttype to PolyType(Nil, restp) instead of restp atPos(tree.pos)(Apply(tree, Nil) setType tree.tpe.resultType) } diff --git a/src/compiler/scala/tools/nsc/typechecker/DeVirtualize.scala b/src/compiler/scala/tools/nsc/typechecker/DeVirtualize.scala index c473df5333..34ba6f6da9 100644 --- a/src/compiler/scala/tools/nsc/typechecker/DeVirtualize.scala +++ b/src/compiler/scala/tools/nsc/typechecker/DeVirtualize.scala @@ -232,7 +232,7 @@ abstract class DeVirtualize /* extends InfoTransform with TypingTransformers { val param = clazz.newMethod(clazz.pos, paramFieldName(clazz, index)) .setFlag(PROTECTED | LOCAL | DEFERRED | EXPANDEDNAME | SYNTHETIC | STABLE) atPhase(ownPhase.next) { - param.setInfo(PolyType(List(), tpe)) + param.setInfo(NullaryMethodType(tpe)) } param } @@ -294,7 +294,7 @@ abstract class DeVirtualize /* extends InfoTransform with TypingTransformers { factory setInfo new PolyTypeCompleter(factory, clazz) { private def copyType(tpe: Type): Type = tpe match { case MethodType(formals, restpe) => MethodType(formals, copyType(restpe)) - case PolyType(List(), restpe) => PolyType(List(), copyType(restpe)) + case NullaryMethodType(restpe) => NullaryMethodType(copyType(restpe)) case PolyType(_, _) => abort("bad case: "+tpe) case _ => owner.thisType.memberType(abstractType(clazz)) } @@ -394,7 +394,7 @@ abstract class DeVirtualize /* extends InfoTransform with TypingTransformers { case (pt, i) => val pfield = cclazz.newMethod(cclazz.pos, paramFieldName(clazz, i)) .setFlag(PROTECTED | LOCAL | EXPANDEDNAME | SYNTHETIC | STABLE) - .setInfo(PolyType(List(), pt)) + .setInfo(NullaryMethodType(pt)) cclazz.info.decls enter pfield atPos(factory.pos) { DefDef(pfield, Ident(fixParamName(i))) diff --git a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala index 6a708a873c..8ad6830e4c 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala @@ -114,6 +114,8 @@ trait Implicits { private def containsError(tp: Type): Boolean = tp match { case PolyType(tparams, restpe) => containsError(restpe) + case NullaryMethodType(restpe) => + containsError(restpe) case MethodType(params, restpe) => params.exists(_.tpe.isError) || containsError(restpe) case _ => diff --git a/src/compiler/scala/tools/nsc/typechecker/Infer.scala b/src/compiler/scala/tools/nsc/typechecker/Infer.scala index 84c770b553..15996923b2 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Infer.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Infer.scala @@ -175,7 +175,7 @@ trait Infer { normalize(restpe) case mt @ MethodType(params, restpe) if !restpe.isDependent => functionType(params map (_.tpe), normalize(restpe)) - case PolyType(List(), restpe) => // nullary method type + case NullaryMethodType(restpe) => normalize(restpe) case ExistentialType(tparams, qtpe) => ExistentialType(tparams, normalize(qtpe)) @@ -350,6 +350,7 @@ trait Infer { case tr: TypeRef => mtcheck(mt, tr) case _ => lastChanceCheck(tp, pt) } + case NullaryMethodType(restpe) => apply(restpe, pt) case PolyType(_, restpe) => apply(restpe, pt) case ExistentialType(_, qtpe) => apply(qtpe, pt) case _ => argumentCheck(tp, pt) @@ -367,7 +368,6 @@ trait Infer { def lastChanceCheck(tp: Type, pt: Type) = tp <:< pt override def apply(tp: Type, pt: Type): Boolean = tp match { - case PolyType(tparams, restpe) => tparams.isEmpty && normSubType(restpe, pt) case ExistentialType(_, _) => normalize(tp) <:< pt case _ => super.apply(tp, pt) } @@ -659,7 +659,7 @@ trait Infer { } private[typechecker] def followApply(tp: Type): Type = tp match { - case PolyType(List(), restp) => + case NullaryMethodType(restp) => val restp1 = followApply(restp) if (restp1 eq restp) tp else restp1 case _ => @@ -816,6 +816,8 @@ trait Infer { else tryTupleApply } + case NullaryMethodType(restpe) => // strip nullary method type, which used to be done by the polytype case below + isApplicable(undetparams, restpe, argtpes0, pt) case PolyType(tparams, restpe) => val tparams1 = cloneSymbols(tparams) isApplicable(tparams1 ::: undetparams, restpe.substSym(tparams, tparams1), argtpes0, pt) @@ -860,18 +862,24 @@ trait Infer { case et: ExistentialType => isAsSpecific(ftpe1.skolemizeExistential, ftpe2) //et.withTypeVars(isAsSpecific(_, ftpe2)) + case NullaryMethodType(res) => + isAsSpecific(res, ftpe2) case mt: MethodType if mt.isImplicit => isAsSpecific(ftpe1.resultType, ftpe2) - case MethodType(params @ (x :: xs), _) => + case MethodType(params, _) if params nonEmpty => var argtpes = params map (_.tpe) if (isVarArgsList(params) && isVarArgsList(ftpe2.params)) argtpes = argtpes map (argtpe => if (isRepeatedParamType(argtpe)) argtpe.typeArgs.head else argtpe) isApplicable(List(), ftpe2, argtpes, WildcardType) + case PolyType(tparams, NullaryMethodType(res)) => + isAsSpecific(PolyType(tparams, res), ftpe2) case PolyType(tparams, mt: MethodType) if mt.isImplicit => isAsSpecific(PolyType(tparams, mt.resultType), ftpe2) - case PolyType(_, MethodType(params @ (x :: xs), _)) => + case PolyType(_, MethodType(params, _)) if params nonEmpty => isApplicable(List(), ftpe2, params map (_.tpe), WildcardType) + // case NullaryMethodType(res) => + // isAsSpecific(res, ftpe2) case ErrorType => true case _ => @@ -882,12 +890,25 @@ trait Infer { et.withTypeVars(isAsSpecific(ftpe1, _)) case mt: MethodType => !mt.isImplicit || isAsSpecific(ftpe1, mt.resultType) + case NullaryMethodType(res) => + isAsSpecific(ftpe1, res) + case PolyType(tparams, NullaryMethodType(res)) => + isAsSpecific(ftpe1, PolyType(tparams, res)) case PolyType(tparams, mt: MethodType) => !mt.isImplicit || isAsSpecific(ftpe1, PolyType(tparams, mt.resultType)) case _ => isAsSpecificValueType(ftpe1, ftpe2, List(), List()) } } + private def isAsSpecificValueType(tpe1: Type, tpe2: Type, undef1: List[Symbol], undef2: List[Symbol]): Boolean = (tpe1, tpe2) match { + case (PolyType(tparams1, rtpe1), _) => + isAsSpecificValueType(rtpe1, tpe2, undef1 ::: tparams1, undef2) + case (_, PolyType(tparams2, rtpe2)) => + isAsSpecificValueType(tpe1, rtpe2, undef1, undef2 ::: tparams2) + case _ => + existentialAbstraction(undef1, tpe1) <:< existentialAbstraction(undef2, tpe2) + } + /* def isStrictlyMoreSpecific(ftpe1: Type, ftpe2: Type): Boolean = @@ -943,16 +964,6 @@ trait Infer { case _ => false } - - private def isAsSpecificValueType(tpe1: Type, tpe2: Type, undef1: List[Symbol], undef2: List[Symbol]): Boolean = (tpe1, tpe2) match { - case (PolyType(tparams1, rtpe1), _) => - isAsSpecificValueType(rtpe1, tpe2, undef1 ::: tparams1, undef2) - case (_, PolyType(tparams2, rtpe2)) => - isAsSpecificValueType(tpe1, rtpe2, undef1, undef2 ::: tparams2) - case _ => - existentialAbstraction(undef1, tpe1) <:< existentialAbstraction(undef2, tpe2) - } - /* /** Is type `tpe1' a strictly better expression alternative than type `tpe2'? */ @@ -1120,7 +1131,7 @@ trait Infer { if (targs eq null) { if (!tree.tpe.isErroneous && !pt.isErroneous) error(tree.pos, "polymorphic expression cannot be instantiated to expected type" + - foundReqMsg(PolyType(undetparams, skipImplicit(tree.tpe)), pt)) + foundReqMsg(polyType(undetparams, skipImplicit(tree.tpe)), pt)) } else { new TreeTypeSubstituter(undetparams, targs).traverse(tree) } diff --git a/src/compiler/scala/tools/nsc/typechecker/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala index 775731df2b..9b27b35e82 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Namers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala @@ -578,7 +578,7 @@ trait Namers { self: Analyzer => def getterTypeCompleter(vd: ValDef) = mkTypeCompleter(vd) { sym => if (settings.debug.value) log("defining " + sym) val tp = typeSig(vd) - sym.setInfo(PolyType(List(), tp)) + sym.setInfo(NullaryMethodType(tp)) if (settings.debug.value) log("defined " + sym) validate(sym) } @@ -862,7 +862,7 @@ trait Namers { self: Analyzer => polyType( tparamSyms, // deSkolemized symbols -- TODO: check that their infos don't refer to method args? - if (vparamSymss.isEmpty) PolyType(List(), restpe) // nullary method type + if (vparamSymss.isEmpty) NullaryMethodType(restpe) // vparamss refer (if they do) to skolemized tparams else (vparamSymss :\ restpe) (makeMethodType)) } @@ -907,7 +907,7 @@ trait Namers { self: Analyzer => resultPt = resultPt.resultType } resultPt match { - case PolyType(List(), rtpe) => resultPt = rtpe + case NullaryMethodType(rtpe) => resultPt = rtpe case MethodType(List(), rtpe) => resultPt = rtpe case _ => } @@ -1264,22 +1264,8 @@ trait Namers { self: Analyzer => } result match { case PolyType(tparams @ (tp :: _), _) if tp.owner.isTerm => - // || - // Adriaan: The added condition below is quite a hack. It seems that HK type parameters is relying - // on a pass that forces all infos in the type to get everything right. - // The problem is that the same pass causes cyclic reference errors in - // test pos/cyclics.scala. It turned out that deSkolemize is run way more often than necessary, - // running it only when needed fixes the cyclic reference errors. - // But correcting deSkolemize broke HK types, because we don't do the traversal anymore. - // For the moment I made a special hack to do the traversal if we have HK type parameters. - // Maybe it's not a hack, then we need to document it better. But ideally, we should find - // a way to deal with HK types that's not dependent on accidental side - // effects like this. - // tparams.exists(!_.typeParams.isEmpty)) => new DeSkolemizeMap(tparams) mapOver result case _ => -// println("not skolemizing "+result+" in "+context.owner) -// new DeSkolemizeMap(List()) mapOver result result } } diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala index adc7813b9c..df8ec9c762 100644 --- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala +++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala @@ -66,7 +66,7 @@ abstract class RefChecks extends InfoTransform { sym.setFlag(LAZY | ACCESSOR) } } - PolyType(List(), tp) + NullaryMethodType(tp) } else tp } @@ -244,12 +244,12 @@ abstract class RefChecks extends InfoTransform { } def overridesType(tp1: Type, tp2: Type): Boolean = (tp1.normalize, tp2.normalize) match { - case (MethodType(List(), rtp1), PolyType(List(), rtp2)) => + case (MethodType(List(), rtp1), NullaryMethodType(rtp2)) => rtp1 <:< rtp2 - case (PolyType(List(), rtp1), MethodType(List(), rtp2)) => + case (NullaryMethodType(rtp1), MethodType(List(), rtp2)) => rtp1 <:< rtp2 case (TypeRef(_, sym, _), _) if (sym.isModuleClass) => - overridesType(PolyType(List(), tp1), tp2) + overridesType(NullaryMethodType(tp1), tp2) case _ => tp1 <:< tp2 } @@ -753,6 +753,8 @@ abstract class RefChecks extends InfoTransform { validateVariance(hi, variance) case MethodType(formals, result) => validateVariance(result, variance) + case NullaryMethodType(result) => + validateVariance(result, variance) case PolyType(tparams, result) => // type parameters will be validated separately, because they are defined explicitly. validateVariance(result, variance) @@ -1006,7 +1008,7 @@ abstract class RefChecks extends InfoTransform { if (!sym.allOverriddenSymbols.isEmpty) { val factory = sym.owner.newMethod(sym.pos, sym.name.toTermName) .setFlag(sym.flags | STABLE).resetFlag(MODULE) - .setInfo(PolyType(List(), sym.moduleClass.tpe)) + .setInfo(NullaryMethodType(sym.moduleClass.tpe)) sym.owner.info.decls.enter(factory) val ddef = atPhase(phase.next) { @@ -1043,7 +1045,7 @@ abstract class RefChecks extends InfoTransform { sym.resetFlag(MODULE | FINAL | CASE) sym.setFlag(LAZY | ACCESSOR | SYNTHETIC) - sym.setInfo(PolyType(List(), sym.tpe)) + sym.setInfo(NullaryMethodType(sym.tpe)) sym setFlag (lateMETHOD | STABLE) } diff --git a/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala b/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala index 3e8f7bd2c7..6f3bb9e976 100644 --- a/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala +++ b/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala @@ -103,7 +103,7 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT var superAccTpe = clazz.thisType.memberType(sym) if (sym.isModule && !sym.isMethod) { // the super accessor always needs to be a method. See #231 - superAccTpe = PolyType(List(), superAccTpe) + superAccTpe = NullaryMethodType(superAccTpe) } superAcc.setInfo(superAccTpe.cloneInfo(superAcc)) //println("creating super acc "+superAcc+":"+superAcc.tpe)//DEBUG diff --git a/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala b/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala index 2d544ba1d1..03b080398f 100644 --- a/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala +++ b/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala @@ -86,12 +86,12 @@ trait SyntheticMethods extends ast.TreeDSL { import CODE._ def productPrefixMethod: Tree = typer.typed { - val method = syntheticMethod(nme.productPrefix, 0, sym => PolyType(Nil, StringClass.tpe)) + val method = syntheticMethod(nme.productPrefix, 0, sym => NullaryMethodType(StringClass.tpe)) DEF(method) === LIT(clazz.name.decode) } def productArityMethod(nargs: Int): Tree = { - val method = syntheticMethod(nme.productArity, 0, sym => PolyType(Nil, IntClass.tpe)) + val method = syntheticMethod(nme.productArity, 0, sym => NullaryMethodType(IntClass.tpe)) typer typed { DEF(method) === LIT(nargs) } } diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 3337584420..19fc981035 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -568,6 +568,7 @@ trait Typers extends Modes { case ExistentialType(_, tpe1) => isNarrowable(tpe1) case AnnotatedType(_, tpe1, _) => isNarrowable(tpe1) case PolyType(_, tpe1) => isNarrowable(tpe1) + case NullaryMethodType(tpe1) => isNarrowable(tpe1) case _ => !phase.erasedTypes } @@ -702,7 +703,7 @@ trait Typers extends Modes { case OverloadedType(pre, alts) if !inFunMode(mode) => // (1) inferExprAlternative(tree, pt) adapt(tree, mode, pt, original) - case PolyType(List(), restpe) => // (2) + case NullaryMethodType(restpe) => // (2) adapt(tree setType restpe, mode, pt, original) case TypeRef(_, ByNameParamClass, List(arg)) if ((mode & EXPRmode) != 0) => // (2) @@ -1591,6 +1592,8 @@ trait Typers extends Modes { if (meth.owner.isStructuralRefinement && meth.allOverriddenSymbols.isEmpty && !(meth.isPrivate || meth.hasAccessBoundary)) { val tp: Type = meth.tpe match { case mt: MethodType => mt + case NullaryMethodType(res) => res + // TODO_NMT: drop NullaryMethodType from resultType? case pt: PolyType => pt.resultType case _ => NoType } @@ -1624,7 +1627,7 @@ trait Typers extends Modes { case tpt: Tree => val alias = enclClass.newAliasType(useCase.pos, name.toTypeName) val tparams = cloneSymbols(tpt.tpe.typeSymbol.typeParams, alias) - alias setInfo polyType(tparams, appliedType(tpt.tpe, tparams map (_.tpe))) + alias setInfo typeFun(tparams, appliedType(tpt.tpe, tparams map (_.tpe))) context.scope.enter(alias) case _ => } @@ -2175,6 +2178,7 @@ trait Typers extends Modes { } def doTypedApply(tree: Tree, fun0: Tree, args: List[Tree], mode: Int, pt: Type): Tree = { + // TODO_NMT: check the assumption that args nonEmpty var fun = fun0 if (fun.hasSymbol && fun.symbol.isOverloaded) { // remove alternatives with wrong number of parameters without looking at types. @@ -3148,7 +3152,7 @@ trait Typers extends Modes { val expr2 = Function(List(), expr1) setPos expr1.pos new ChangeOwnerTraverser(context.owner, expr2.symbol).traverse(expr2) typed1(expr2, mode, pt) - case PolyType(List(), restpe) => + case NullaryMethodType(restpe) => val expr2 = Function(List(), expr1) setPos expr1.pos new ChangeOwnerTraverser(context.owner, expr2.symbol).traverse(expr2) typed1(expr2, mode, pt) diff --git a/src/compiler/scala/tools/nsc/typechecker/Variances.scala b/src/compiler/scala/tools/nsc/typechecker/Variances.scala index 4ad5c9057f..e29d96e663 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Variances.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Variances.scala @@ -78,6 +78,8 @@ trait Variances { varianceInTypes(parents)(tparam) & varianceInSyms(defs.toList)(tparam) case MethodType(params, restpe) => flip(varianceInSyms(params)(tparam)) & varianceInType(restpe)(tparam) + case NullaryMethodType(restpe) => + varianceInType(restpe)(tparam) case PolyType(tparams, restpe) => flip(varianceInSyms(tparams)(tparam)) & varianceInType(restpe)(tparam) case ExistentialType(tparams, restpe) => diff --git a/src/continuations/plugin/scala/tools/selectivecps/CPSAnnotationChecker.scala b/src/continuations/plugin/scala/tools/selectivecps/CPSAnnotationChecker.scala index 5151f1eeee..2df538fe85 100644 --- a/src/continuations/plugin/scala/tools/selectivecps/CPSAnnotationChecker.scala +++ b/src/continuations/plugin/scala/tools/selectivecps/CPSAnnotationChecker.scala @@ -373,9 +373,10 @@ abstract class CPSAnnotationChecker extends CPSUtils { transChildrenInOrder(tree, tpe, qual::(transArgList(fun, args).flatten), Nil) case TypeApply(fun @ Select(qual, name), args) if fun.isTyped => + def stripNullaryMethodType(tp: Type) = tp match { case NullaryMethodType(restpe) => restpe case tp => tp } vprintln("[checker] checking select apply " + tree + "/" + tpe) - transChildrenInOrder(tree, tpe, List(qual, fun), Nil) + transChildrenInOrder(tree, stripNullaryMethodType(tpe), List(qual, fun), Nil) case Apply(fun, args) if fun.isTyped => @@ -406,7 +407,7 @@ abstract class CPSAnnotationChecker extends CPSUtils { // we have to do it here so we don't lose the cps information (wouldn't trigger our // adapt and there is no Apply/TypeApply created) tpe match { - case PolyType(List(), restpe) => + case NullaryMethodType(restpe) => //println("yep: " + restpe + "," + restpe.getClass) transChildrenInOrder(tree, restpe, List(qual), Nil) case _ : PolyType => tpe diff --git a/src/continuations/plugin/scala/tools/selectivecps/SelectiveCPSTransform.scala b/src/continuations/plugin/scala/tools/selectivecps/SelectiveCPSTransform.scala index 07a9e5fed5..78cc8f7ff7 100644 --- a/src/continuations/plugin/scala/tools/selectivecps/SelectiveCPSTransform.scala +++ b/src/continuations/plugin/scala/tools/selectivecps/SelectiveCPSTransform.scala @@ -50,8 +50,8 @@ abstract class SelectiveCPSTransform extends PluginComponent with def transformCPSType(tp: Type): Type = { // TODO: use a TypeMap? need to handle more cases? tp match { case PolyType(params,res) => PolyType(params, transformCPSType(res)) - case MethodType(params,res) => - MethodType(params, transformCPSType(res)) + case NullaryMethodType(res) => NullaryMethodType(transformCPSType(res)) + case MethodType(params,res) => MethodType(params, transformCPSType(res)) case TypeRef(pre, sym, args) => TypeRef(pre, sym, args.map(transformCPSType(_))) case _ => getExternalAnswerTypeAnn(tp) match { diff --git a/src/library/scala/reflect/Print.scala b/src/library/scala/reflect/Print.scala index a84e024c36..1c51a8b2b1 100644 --- a/src/library/scala/reflect/Print.scala +++ b/src/library/scala/reflect/Print.scala @@ -101,6 +101,8 @@ object Print extends Function1[Any, String] { "[" + Print(lo) + " ... " + Print(hi) + "]" case reflect.MethodType(formals, resultType) => formals.map(Print).mkString("(", ", ", ")") + " => " + Print(resultType) + case reflect.NullaryMethodType(resultType) => + " => " + Print(resultType) case reflect.PolyType(typeParams, typeBounds, resultType) => val z = (typeParams, typeBounds).zipped map ((tp, tb) => "[" + Print(tb._1) + " :> " + Print(tp) + " :> " + Print(tb._2) + "]") z.mkString("[", ", ", "]") + " -> " + Print(resultType) diff --git a/src/library/scala/reflect/Type.scala b/src/library/scala/reflect/Type.scala index 029bb3966e..0ec0b77fad 100644 --- a/src/library/scala/reflect/Type.scala +++ b/src/library/scala/reflect/Type.scala @@ -49,6 +49,8 @@ case class TypeBounds(lo: Type, hi: Type) extends Type * <code>(formals1 ... formalsn) restpe</code> */ case class MethodType(formals: List[Symbol], restpe: Type) extends Type +/** This type is required by the compiler and <b>should not be used in client code</b>. */ +case class NullaryMethodType(resultType: Type) extends Type /** This type is required by the compiler and <b>should not be used in client code</b>. */ case class PolyType(typeParams: List[Symbol], typeBounds: List[(Type, Type)], resultType: Type) extends Type @@ -71,5 +73,6 @@ extends MethodType(formals, restpe) case reflect.AppliedType(tpe, args) => case reflect.TypeBounds(lo, hi) => case reflect.MethodType(formals, restpe) => //can also be ImplicitMethodType + case reflect.NullaryMethodType(restpe) => case reflect.PolyType(typeParams, typeBounds, resultType) => */ diff --git a/src/library/scala/reflect/generic/Types.scala b/src/library/scala/reflect/generic/Types.scala index 17e19715d7..6dcd90e66c 100755 --- a/src/library/scala/reflect/generic/Types.scala +++ b/src/library/scala/reflect/generic/Types.scala @@ -69,6 +69,9 @@ trait Types { self: Universe => type MethodType <: Type val MethodType: MethodTypeExtractor + type NullaryMethodType <: Type + val NullaryMethodType: NullaryMethodTypeExtractor + type PolyType <: Type val PolyType: PolyTypeExtractor @@ -132,6 +135,11 @@ trait Types { self: Universe => def unapply(tpe: MethodType): Option[(List[Symbol], Type)] } + abstract class NullaryMethodTypeExtractor { + def apply(resultType: Type): NullaryMethodType + def unapply(tpe: NullaryMethodType): Option[(Type)] + } + abstract class PolyTypeExtractor { def apply(typeParams: List[Symbol], resultType: Type): PolyType def unapply(tpe: PolyType): Option[(List[Symbol], Type)] diff --git a/src/library/scala/reflect/generic/UnPickler.scala b/src/library/scala/reflect/generic/UnPickler.scala index 7b7c34a767..86b73cf5fd 100755 --- a/src/library/scala/reflect/generic/UnPickler.scala +++ b/src/library/scala/reflect/generic/UnPickler.scala @@ -281,7 +281,7 @@ abstract class UnPickler { sym case MODULEsym => - val clazz = at(inforef, readType).typeSymbol + val clazz = at(inforef, () => readType()).typeSymbol // after the NMT_TRANSITION period, we can leave off the () => ... () if (isModuleRoot) moduleRoot else { val m = owner.newModule(name, clazz) @@ -298,8 +298,13 @@ abstract class UnPickler { }) } - /** Read a type */ - protected def readType(): Type = { + /** Read a type + * + * @param forceProperType is used to ease the transition to NullaryMethodTypes (commentmarker: NMT_TRANSITION) + * the flag say that a type of kind * is expected, so that PolyType(tps, restpe) can be disambiguated to PolyType(tps, NullaryMethodType(restpe)) + * (if restpe is not a ClassInfoType, a MethodType or a NullaryMethodType, which leaves TypeRef/SingletonType -- the latter would make the polytype a type constructor) + */ + protected def readType(forceProperType: Boolean = false): Type = { val tag = readByte() val end = readNat() + readIndex (tag: @switch) match { @@ -341,7 +346,19 @@ abstract class UnPickler { case POLYtpe => val restpe = readTypeRef() val typeParams = until(end, readSymbolRef) - PolyType(typeParams, restpe) + if(typeParams nonEmpty) { + // NMT_TRANSITION: old class files denoted a polymorphic nullary method as PolyType(tps, restpe), we now require PolyType(tps, NullaryMethodType(restpe)) + // when a type of kind * is expected (forceProperType is true), we know restpe should be wrapped in a NullaryMethodType (if it wasn't suitably wrapped yet) + def transitionNMT(restpe: Type) = { + val resTpeCls = restpe.getClass.toString // what's uglier than isInstanceOf? right! -- isInstanceOf does not work since the concrete types are defined in the compiler (not in scope here) + if(forceProperType /*&& pickleformat < 2.9 */ && !(resTpeCls.endsWith("MethodType"))) { assert(!resTpeCls.contains("ClassInfoType")) + NullaryMethodType(restpe) } + else restpe + } + PolyType(typeParams, transitionNMT(restpe)) + } + else + NullaryMethodType(restpe) case EXISTENTIALtpe => val restpe = readTypeRef() ExistentialType(until(end, readSymbolRef), restpe) @@ -352,7 +369,7 @@ abstract class UnPickler { typeRef = readNat() s } else NoSymbol // selfsym can go. - val tp = at(typeRef, readType) + val tp = at(typeRef, () => readType(forceProperType)) // NMT_TRANSITION val annots = until(end, readAnnotationRef) if (selfsym == NoSymbol) AnnotatedType(annots, tp, selfsym) else tp @@ -739,7 +756,7 @@ abstract class UnPickler { /* Read a reference to a pickled item */ protected def readNameRef(): Name = at(readNat(), readName) protected def readSymbolRef(): Symbol = at(readNat(), readSymbol) - protected def readTypeRef(): Type = at(readNat(), readType) + protected def readTypeRef(): Type = at(readNat(), () => readType()) // after the NMT_TRANSITION period, we can leave off the () => ... () protected def readConstantRef(): Constant = at(readNat(), readConstant) protected def readAnnotationRef(): AnnotationInfo = at(readNat(), readAnnotation) protected def readModifiersRef(): Modifiers = at(readNat(), readModifiers) |