diff options
author | Adriaan Moors <adriaan.moors@epfl.ch> | 2011-01-20 09:43:18 +0000 |
---|---|---|
committer | Adriaan Moors <adriaan.moors@epfl.ch> | 2011-01-20 09:43:18 +0000 |
commit | f0bff86d31c0ce9548e595c4b4b7fda8efe038c0 (patch) | |
tree | a01a425900de3bca3202bcfca1749099cb74a235 /src/compiler/scala/tools/nsc/typechecker | |
parent | f4f1738fe79193010c328371521ed554e7e8cf6d (diff) | |
download | scala-f0bff86d31c0ce9548e595c4b4b7fda8efe038c0.tar.gz scala-f0bff86d31c0ce9548e595c4b4b7fda8efe038c0.tar.bz2 scala-f0bff86d31c0ce9548e595c4b4b7fda8efe038c0.zip |
introduce NullaryMethodType to disambiguate Pol...
introduce NullaryMethodType to disambiguate PolyType
motivation:
given `def foo[T]: (T, T)` and `type Foo[T] = (T, T)`,
`foo.info` and `TypeRef(_, Foo, Nil).normalize` are both
`PolyType(List(T), Pair[T, T])`
uncurry has been relying on an ugly hack to distinguish these
cases based on ad-hoc kind inference without this distinction,
the type alias's info (a type function) would be transformed to
`PolyType(List(T), MethodType(Nil, Pair[T, T]))`
anonymous type functions are being used more often (see #2741, #4017,
#4079, #3443, #3106), which makes a proper treatment of PolyTypes more
pressing
change to type representation:
PolyType(Nil, tp) -> NullaryMethodType(tp)
PolyType(tps, tp) -> PolyType(tps, NullaryMethodType(tp)) (if the polytype denoted a polymorphic nullary method)
PolyType(Nil, tp) is now invalid
the kind of a PolyType is * iff its resulttype is a NullaryMethodType or a MethodType (i.e., it's a polymorphic value)
in all other cases a PolyType now denotes a type constructor
NullaryMethodType is eliminated during uncurry
pickling:
for backwards compatibility, a NullaryMethodType(tp) is still pickled as a PolyType(Nil, tp),
unpickling rewrites pre-2.9-pickled PolyTypes according to the expected kind of the unpickled type (similar to what we used to do in uncurry)
a pickled PolyType(Nil, restpe) is unpickled to NullaryMethodType(restpe)
a pickled PolyType(tps, restpe) is unpickled to PolyType(tps, NullaryMethodType(restpe)) when the type is expected to have kind *
the rewrite probably isn't complete, but was validated by compiling
against the old scalacheck jar (which has plenty of polymorphic nullary
methods) nevertheless, this commit includes a new scalacheck jar
summary of the refactoring:
* PolyType(List(), tp) or PolyType(Nil, tp) or PolyType(parms, tp) if params.isEmpty ==> NullaryMethodType(tp)
* whenever there was a case PolyType(tps, tp) (irrespective of tps isEmpty), now need to consider the
case PolyType(tps, NullaryMethodType(tp)); just add a case NullaryMethodType(tp), since usually:
- there already is a PolyType case that recurses on the result type,
- the polytype case applied to empty and non-empty type parameter lists alike
* tp.resultType, where tp was assumed to be a PolyType that represents a polymorphic nullary method type
before, tp == PolyType(tps, res), now tp == PolyType(tps, NullaryMethodType(res))
* got bitten again (last time was dependent-method types refactoring)
by a TypeMap not being the identity when dropNonConstraintAnnotations
is true (despite having an identity apply method). Since asSeenFrom
is skipped when isTrivial, the annotations aren't dropped. The
cps plugin relies on asSeenFrom dropping these annotations for
trivial types though. Therefore, NullaryMethodType pretends to
never be trivial. Better fix(?) in AsSeenFromMap: `if(tp.isTrivial)
dropNonContraintAnnotations(tp) else ...`
TODO: scalap and eclipse
review by odersky, rytz
Diffstat (limited to 'src/compiler/scala/tools/nsc/typechecker')
9 files changed, 55 insertions, 48 deletions
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) => |