diff options
author | Martin Odersky <odersky@gmail.com> | 2004-05-13 11:02:45 +0000 |
---|---|---|
committer | Martin Odersky <odersky@gmail.com> | 2004-05-13 11:02:45 +0000 |
commit | 8837d66ac4d3e487977ce47c8d63fc189e53e763 (patch) | |
tree | 74243fa124212554a8b919793b599a1f482b2320 | |
parent | bb2e5cbb9cb285bd3ecd9b387126de2659224190 (diff) | |
download | scala-8837d66ac4d3e487977ce47c8d63fc189e53e763.tar.gz scala-8837d66ac4d3e487977ce47c8d63fc189e53e763.tar.bz2 scala-8837d66ac4d3e487977ce47c8d63fc189e53e763.zip |
*** empty log message ***
-rw-r--r-- | doc/reference/ReferencePart.tex | 30 | ||||
-rw-r--r-- | sources/scala/tools/scalac/typechecker/Analyzer.scala | 33 | ||||
-rw-r--r-- | sources/scala/tools/scalac/typechecker/Context.scala | 3 | ||||
-rw-r--r-- | sources/scala/tools/scalac/typechecker/Infer.scala | 109 | ||||
-rwxr-xr-x | sources/scala/tools/scalac/typechecker/View.scala | 4 | ||||
-rw-r--r-- | sources/scalac/symtab/ClosureHistory.java | 2 | ||||
-rw-r--r-- | sources/scalac/typechecker/Infer.java | 3 | ||||
-rw-r--r-- | sources/scalac/typechecker/RefCheck.java | 7 |
8 files changed, 132 insertions, 59 deletions
diff --git a/doc/reference/ReferencePart.tex b/doc/reference/ReferencePart.tex index 57075bba18..b4c2a7c7a4 100644 --- a/doc/reference/ReferencePart.tex +++ b/doc/reference/ReferencePart.tex @@ -4057,15 +4057,35 @@ type parameters are translated to implicit \code{view} arguments. In this case, the \code{view} method over lists would receive the \code{view} method over list elements as implicit parameter. -This opens up the risk that view methods might be passed themselves as -parameters, thus leading to an infinite instantiation. For instance, -one might try to define the following ``magic'' universal converter: +This opens up the risk of infinite instantiations because view methods +are be passed to themselves as parameters. For instance, one might try +to define the following ``magic'' universal converter: \begin{lstlisting} def view[b, a <% b](x: a): b = x // illegal! \end{lstlisting} Application of this view would entail an inifinite expansion with view -parameters. To prevent this sitauation, we require that all defined -views are {\em contractive}, according to the following definition: +parameters. To prevent this situation, we impose the following +restrictions on view definitions and applications. + +Suppose a view definition +\begin{lstlisting} +def view [$tps$](x: $T$): $S$ +\end{lstlisting} +where the list of type parameters $tps$ might be empty. The view +definition is legal only if the result type $S$ is not a type variable +in $tps$. Furthermore, we say the view definition is {\em +contractive} if the parameter type $T$ is not a type variable in +$tps$. Contractive views can never lead to infinite expansions, as +the view argument type becomes strictly smaller for each nested view +argument. + +For non-contractive views we require that every such view can be used +only once on a path of implicit view parameters. That is, a +non-contractive view may not be passed to itself as implicit view +argument, either directly or indirectly. On the other hand, +contractive view methods may be passed to themselves as arguments. In the +examples above this case arises if a list of lists of strings is seen +as a \code{Comparable}. \chapter{Top-Level Definitions} \label{sec:topdefs} diff --git a/sources/scala/tools/scalac/typechecker/Analyzer.scala b/sources/scala/tools/scalac/typechecker/Analyzer.scala index abda9b26e7..bdba70d823 100644 --- a/sources/scala/tools/scalac/typechecker/Analyzer.scala +++ b/sources/scala/tools/scalac/typechecker/Analyzer.scala @@ -622,24 +622,6 @@ class Analyzer(global: scalac_Global, descr: AnalyzerPhase) extends Transformer( } else vapp } - private def checkLegalView(pos: int, tparams: Array[Symbol], vparam: Symbol, restype: Type): boolean = { - var i = 0; - while (i < tparams.length && tparams(i) != vparam.getType().symbol()) - i = i + 1; - if (i < tparams.length) { - val vb = tparams(i).vuBound(); - if (vb != Global.instance.definitions.ANY_TYPE()) { - val vb1 = vb.subst(tparams, infer.freshVars(tparams)); - if (restype.isSubType(vb1)) { - error(pos, "view is potentially self-referential since its result type " + restype + - " is a subtype of its type parameter view bound " + vb1); - return false; - } - } - } - true - } - // Contexts ------------------------------------------------------------------- /** Push new context associated with given tree, owner, and scope on stack. @@ -1173,16 +1155,16 @@ class Analyzer(global: scalac_Global, descr: AnalyzerPhase) extends Transformer( if (!sym.isFinal()) restype = restype.deconst(); } restype = checkNoEscape(tpe.pos, restype); + if (name == Names.view && + infer.containsSymbol(tparamSyms, restype.symbol())) { + error(tree.pos, "result type of view may not be a type variable"); + restype = definitions.ANY_TYPE(); + } popContext(); + owntype = makeMethodType(tparamSyms, vparamSyms, restype); //System.out.println("methtype " + name + ":" + owntype);//DEBUG - if (name == Names.view && - infer.isViewBounded(tparamSyms) && - vparamSyms.length == 2 && vparamSyms(1).length == 1 && - !checkLegalView(tree.pos, tparamSyms, vparamSyms(1)(0), restype)) - owntype = makeMethodType(tparamSyms, vparamSyms, Type.ErrorType); - case Tree$ValDef(mods, name, _tpe, _rhs) => var tpe = _tpe; var rhs = _rhs; @@ -1361,7 +1343,8 @@ class Analyzer(global: scalac_Global, descr: AnalyzerPhase) extends Transformer( } case _ => } - if ((pt != null && pt.isStable() || (mode & QUALmode) != 0) && pre.isStable()) { + if ((pt != null && pt.isStable() || (mode & QUALmode) != 0) && + (pre != null) && pre.isStable()) { var sym: Symbol = tree.symbol(); tree.getType() match { case Type$OverloadedType(alts, alttypes) => diff --git a/sources/scala/tools/scalac/typechecker/Context.scala b/sources/scala/tools/scalac/typechecker/Context.scala index 9bea4b495a..7c5569532f 100644 --- a/sources/scala/tools/scalac/typechecker/Context.scala +++ b/sources/scala/tools/scalac/typechecker/Context.scala @@ -82,8 +82,7 @@ class Context { def isUnShadowed(view: View) = view.context == this || !infer.specializes(view.symtype, symtype); */ - if (viewCache.forall(v => v.sym != sym) && - symtype.resultType() != Type.ErrorType) { + if (viewCache.forall(v => v.sym != sym)) { val v = View(sym, symtype, qual, this); //System.out.println("VIEW " + sym + ":" + symtype + " " + qual);//DEBUG viewCache = v :: viewCache;//.filter(isUnShadowed); diff --git a/sources/scala/tools/scalac/typechecker/Infer.scala b/sources/scala/tools/scalac/typechecker/Infer.scala index 6825aae1e6..36efbc8a69 100644 --- a/sources/scala/tools/scalac/typechecker/Infer.scala +++ b/sources/scala/tools/scalac/typechecker/Infer.scala @@ -263,8 +263,9 @@ class Infer(global: scalac_Global, gen: TreeGen, make: TreeFactory) extends scal val memberViewCache = new HashMap[Symbol, List[View]]; -// private def memberViews(tp: Type): List[View] = List(); - + /** Return all views which are members of one of the objects associated + * with the base types of `tp'. + */ private def memberViews(tp: Type): List[View] = { val tpsym = tp.widen().symbol(); memberViewCache.get(tpsym) match { @@ -281,6 +282,9 @@ class Infer(global: scalac_Global, gen: TreeGen, make: TreeFactory) extends scal } } + /** Return all views which are member of the companion object of + * base class `clazz' of type `tp', empty if no companion object exists. + */ private def companionObjViews(tp: Type, clazz: Symbol): List[View] = { if (clazz.kind == CLASS && !clazz.isModuleClass() && !clazz.isCaseClass()) { var obj = clazz.owner().info().lookupNonPrivate(clazz.name.toTermName()); @@ -304,15 +308,12 @@ class Infer(global: scalac_Global, gen: TreeGen, make: TreeFactory) extends scal var i = alttypes.length - 1; var vs: List[View] = List(); while (i >= 0) { - if (alttypes(i).resultType() != Type.ErrorType) - vs = View(alts(i), alttypes(i), qual, Context.NONE) :: vs; + vs = View(alts(i), alttypes(i), qual, Context.NONE) :: vs; i = i - 1 } vs case viewtype => - if (viewtype.resultType() != Type.ErrorType) - List(View(viewsym, viewtype, qual, Context.NONE)) - else List() + List(View(viewsym, viewtype, qual, Context.NONE)) } } else List() } else List() @@ -320,11 +321,34 @@ class Infer(global: scalac_Global, gen: TreeGen, make: TreeFactory) extends scal } else List() } - private def getViews(tp: Type): List[View] = { + /** Return all available views for type `tp' + */ + private def availableViews(tp: Type): List[View] = { //System.out.println("view for " + tp + " = " + (memberViews(tp) ::: getContext.viewMeths));//DEBUG memberViews(tp) ::: getContext.viewMeths } + def containsSymbol(syms: Array[Symbol], sym: Symbol): boolean = { + var i = 0; + while (i < syms.length && syms(i) != sym) i = i + 1; + i < syms.length + } + + def isContractive(tp: Type): boolean = { + tp match { + case Type$PolyType(tparams, tp1) => + skipViewParams(tparams, tp1) match { + case Type$MethodType(vparams, _) => + vparams.length != 1 || + !containsSymbol(tparams, vparams(0).getType().symbol()) + case _ => true + } + case _ => true + } + } + + /** Construct a tree referring to the view method in `v' at position `pos'. + */ def viewExpr(pos: int, v: View): Tree = { val viewType = checkAccessible( pos, v.sym, v.symtype, v.qual, v.qual.getType()); @@ -340,7 +364,9 @@ class Infer(global: scalac_Global, gen: TreeGen, make: TreeFactory) extends scal } } - private def viewObj(meth: Tree) = meth.getType() match { + /** Construct a function value tree from method reference `meth'. + */ + private def viewObj(meth: Tree): Tree = meth.getType() match { case Type$MethodType(params: Array[Symbol], _) => assert(params.length == 1); val paramsym = params(0).cloneSymbol(getContext.owner); @@ -354,6 +380,11 @@ class Infer(global: scalac_Global, gen: TreeGen, make: TreeFactory) extends scal meth } + /** Construct an implicit view argument for a formal type parameter + * with type `vtype'. `targs' is the vector of type arguments + * in the current application for which a view argument is needed. + * This is needed for error diagnostics only. + */ private def viewArg(pos: int, vtype: Type, targs: Array[Type]): Tree = { val vargs = vtype.typeArgs(); if (vargs(0).isSubType(vargs(1))) { @@ -361,17 +392,26 @@ class Infer(global: scalac_Global, gen: TreeGen, make: TreeFactory) extends scal } else { val v = bestView(vargs(0), vargs(1), Names.EMPTY); if (v != null) { - var vmeth = viewExpr(pos, v); - vmeth.getType() match { - case Type$PolyType(vtparams, vrestype) => - assert(vtparams.length != 0); - vmeth = exprInstance(vmeth, vtparams, vrestype, vtype); - case _ => + if (v.locked) { + error(pos, "recursive view instantiation of non-contractive " + + v.sym + v.sym.locationString() + " with type " + + v.sym.getType()); + gen.mkDefaultValue(pos, vtype) + } else { + v.locked = !isContractive(v.symtype); + var vmeth = viewExpr(pos, v); + vmeth.getType() match { + case Type$PolyType(vtparams, vrestype) => + assert(vtparams.length != 0); + vmeth = exprInstance(vmeth, vtparams, vrestype, vtype); + case _ => + } + val vobj = viewObj(vmeth); + if (!vobj.getType().isSubType(vtype)) + assert(false, "view argument " + vobj + ":" + vobj.getType() + " is not a subtype of view param type " + vtype); + v.locked = false; + vobj } - val vobj = viewObj(vmeth); - if (!vobj.getType().isSubType(vtype)) - assert(false, "view argument " + vobj + ":" + vobj.getType() + " is not a subtype of view param type " + vtype); - vobj } else { error(pos, "type instantiation with [" + @@ -382,6 +422,14 @@ class Infer(global: scalac_Global, gen: TreeGen, make: TreeFactory) extends scal } } + /** Augment type application tree with implicit view arguments. + * @param tree The type application + * @param tparams The formal parameters of the application. + * @param restype The result type of the type application (which + * must be a method type containing the view parameters, + * unless it is an error type. + * @param targs The actual type arguments. + */ private def passViewArgs(tree: Tree, tparams: Array[Symbol], restype: Type, targs: Array[Type]): Tree = restype match { @@ -398,6 +446,8 @@ class Infer(global: scalac_Global, gen: TreeGen, make: TreeFactory) extends scal tree } + /** Do type parameters `tparams' contain a view bounded parameter? + */ def isViewBounded(tparams: Array[Symbol]) = { var viewbounded = false; var j = 0; while (j < tparams.length) { @@ -407,6 +457,9 @@ class Infer(global: scalac_Global, gen: TreeGen, make: TreeFactory) extends scal viewbounded } + /** Skip view parameters in type `tp' in the case where `tparams' contains + * a view bound. + */ def skipViewParams(tparams: Array[Symbol], tp: Type): Type = tp match { case Type$MethodType(_, restp) if isViewBounded(tparams) => restp @@ -414,6 +467,9 @@ class Infer(global: scalac_Global, gen: TreeGen, make: TreeFactory) extends scal tp } + /** If tree is a type application, pass implicit view arguments where + * necessary. + */ def completeTypeApply(tree: Tree): Tree = { //System.out.println("complete type apply: " + tree + ":" + tree.getType());//DEBUG tree match { @@ -742,7 +798,7 @@ class Infer(global: scalac_Global, gen: TreeGen, make: TreeFactory) extends scal if (tp1.isSubType(pt)) true else if (regularValue) { val argtypes = NewArray.Type(tp1); - var viewMeths = getViews(tp1); + var viewMeths = availableViews(tp1); while (!viewMeths.isEmpty && !isApplicable(viewMeths.head.symtype, argtypes, pt, Names.EMPTY, false)) viewMeths = viewMeths.tail; @@ -1167,8 +1223,13 @@ class Infer(global: scalac_Global, gen: TreeGen, make: TreeFactory) extends scal def specializes(ftpe1: Type, ftpe2: Type): boolean = ftpe1 match { case Type$MethodType(params, _) => isApplicable(ftpe2, Symbol.getType(params), Type.AnyType) - case Type$PolyType(_, Type$MethodType(params, _)) => - isApplicable(ftpe2, Symbol.getType(params), Type.AnyType); + case Type$PolyType(tparams, restype) => + skipViewParams(tparams, restype) match { + case Type$MethodType(params, _) => + isApplicable(ftpe2, Symbol.getType(params), Type.AnyType); + case _ => + false + } case _ => false } @@ -1301,7 +1362,7 @@ class Infer(global: scalac_Global, gen: TreeGen, make: TreeFactory) extends scal */ def bestView(tp: Type, pt: Type, name: Name): View = { var best: View = null; - var viewMeths = getViews(tp); + var viewMeths = availableViews(tp); //System.out.println("best view for " + tp + "/" + pt + "/" + name + " in " + viewMeths);//DEBUG val argtypes = NewArray.Type(tp); while (!viewMeths.isEmpty) { @@ -1311,7 +1372,7 @@ class Infer(global: scalac_Global, gen: TreeGen, make: TreeFactory) extends scal viewMeths = viewMeths.tail } if (best != null) { - viewMeths = getViews(tp); + viewMeths = availableViews(tp); while (!viewMeths.isEmpty) { if (viewMeths.head != best && isApplicable(viewMeths.head.symtype, argtypes, pt, name, false) && diff --git a/sources/scala/tools/scalac/typechecker/View.scala b/sources/scala/tools/scalac/typechecker/View.scala index ae736c7bc9..a3b64636d5 100755 --- a/sources/scala/tools/scalac/typechecker/View.scala +++ b/sources/scala/tools/scalac/typechecker/View.scala @@ -10,6 +10,8 @@ import scalac.ast._; package scala.tools.scalac.typechecker { -case class View(sym: Symbol, symtype: Type, qual: Tree, context: Context); +case class View(sym: Symbol, symtype: Type, qual: Tree, context: Context) { + var locked = false; +} } diff --git a/sources/scalac/symtab/ClosureHistory.java b/sources/scalac/symtab/ClosureHistory.java index c17ad46f90..514e093f29 100644 --- a/sources/scalac/symtab/ClosureHistory.java +++ b/sources/scalac/symtab/ClosureHistory.java @@ -54,7 +54,7 @@ public class ClosureHistory extends History { /** Adds all parents of given type to given parent table. */ private static void addParents(TreeMap/*<Symbol,Type>*/ table, Type type) { switch (type) { - case ErrorType: + case ErrorType: case NoType: return; case TypeRef(_, Symbol symbol, _): Type.Map map = Type.getThisTypeMap(symbol, type); diff --git a/sources/scalac/typechecker/Infer.java b/sources/scalac/typechecker/Infer.java index 8ccca5d586..528ca7b47d 100644 --- a/sources/scalac/typechecker/Infer.java +++ b/sources/scalac/typechecker/Infer.java @@ -17,4 +17,7 @@ public abstract class Infer { */ public abstract void checkBounds(Symbol[] tparams, Type[] targs, String prefix); + + public abstract Type skipViewParams(Symbol[] tparams, Type tp); + } diff --git a/sources/scalac/typechecker/RefCheck.java b/sources/scalac/typechecker/RefCheck.java index f65485a33a..a96a21fd0a 100644 --- a/sources/scalac/typechecker/RefCheck.java +++ b/sources/scalac/typechecker/RefCheck.java @@ -648,7 +648,12 @@ public class RefCheck extends Transformer implements Modifiers, Kinds { } private Tree[] caseFields(ClassSymbol clazz) { - Symbol[] vparams = clazz.primaryConstructor().type().firstParams(); + Type ct = clazz.primaryConstructor().type(); + switch (ct) { + case Type.PolyType(Symbol[] tparams, Type restp): + ct = infer.skipViewParams(tparams, restp); + } + Symbol[] vparams = ct.firstParams(); Tree[] fields = new Tree[vparams.length]; for (int i = 0; i < fields.length; i++) { fields[i] = gen.mkRef(clazz.pos, clazz.thisType(), clazz.caseFieldAccessor(i)); |