diff options
-rw-r--r-- | config/list/scalac.lst | 1 | ||||
-rw-r--r-- | doc/reference/ExamplesPart.tex | 14 | ||||
-rw-r--r-- | sources/scala/Console.scala | 1 | ||||
-rw-r--r-- | sources/scala/Enumeration.scala | 5 | ||||
-rw-r--r-- | sources/scala/List.scala | 6 | ||||
-rw-r--r-- | sources/scala/Ord.scala | 5 | ||||
-rw-r--r-- | sources/scala/tools/scalac/ast/parser/Parser.scala | 37 | ||||
-rw-r--r-- | sources/scala/tools/scalac/ast/parser/Scanner.scala | 117 | ||||
-rw-r--r-- | sources/scala/tools/scalac/ast/parser/TokenData.scala | 8 | ||||
-rw-r--r-- | sources/scala/tools/scalac/typechecker/Analyzer.scala | 56 | ||||
-rw-r--r-- | sources/scala/tools/scalac/typechecker/Context.scala | 42 | ||||
-rw-r--r-- | sources/scala/tools/scalac/typechecker/ImportList.scala | 21 | ||||
-rw-r--r-- | sources/scala/tools/scalac/typechecker/Infer.scala | 58 | ||||
-rw-r--r-- | sources/scalac/ast/TreeInfo.java | 25 | ||||
-rw-r--r-- | sources/scalac/util/Names.java | 1 |
15 files changed, 275 insertions, 122 deletions
diff --git a/config/list/scalac.lst b/config/list/scalac.lst index fdc3823814..131741ca79 100644 --- a/config/list/scalac.lst +++ b/config/list/scalac.lst @@ -31,6 +31,7 @@ typechecker/DeSugarize.scala typechecker/ImportList.scala typechecker/Infer.scala typechecker/Substituter.scala +typechecker/Coerce.scala util/NewArray.scala diff --git a/doc/reference/ExamplesPart.tex b/doc/reference/ExamplesPart.tex index 30ea725cfd..483a56f74b 100644 --- a/doc/reference/ExamplesPart.tex +++ b/doc/reference/ExamplesPart.tex @@ -75,10 +75,10 @@ functional style. def sort(xs: List[int]): List[int] = if (xs.length <= 1) xs else { - val pivot = a(a.length / 2); - sort(a.filter(x => x < pivot)) - ::: a.filter(x => x == pivot) - ::: sort(a.filter(x => x > pivot)) + val pivot = xs(xs.length / 2); + sort(xs.filter(x => x < pivot)) + ::: xs.filter(x => x == pivot) + ::: sort(xs.filter(x => x > pivot)) } \end{lstlisting} @@ -1605,7 +1605,7 @@ a more streamlined alternative definition of the empty set: \begin{lstlisting} object EmptySet extends IntSet { def contains(x: int): boolean = false; - def incl(x: int): IntSet = new NonEmptySet(x, empty, empty); + def incl(x: int): IntSet = new NonEmptySet(x, EmptySet, EmptySet); } \end{lstlisting} The syntax of an object definition follows the syntax of a class @@ -2761,7 +2761,7 @@ A list containing the elements \code{x}$_1$, \ldots, \code{x}$_n$ is written \begin{lstlisting} val fruit = List("apples", "oranges", "pears"); val nums = List(1, 2, 3, 4); -val diag3 = List(List(1, 0, 0), List(0, 1, 0)); +val diag3 = List(List(1, 0, 0), List(0, 1, 0), List(0, 0, 1)); val empty = List(); \end{lstlisting} Lists are similar to arrays in languages such as C or Java, but there @@ -2779,7 +2779,7 @@ list all have the same type. The type of a list with elements of type \begin{lstlisting} val fruit: List[String] = List("apples", "oranges", "pears"); val nums : List[int] = List(1, 2, 3, 4); -val diag3: List[List[int]] = List(List(1, 0, 0), List(0, 1, 0)); +val diag3: List[List[int]] = List(List(1, 0, 0), List(0, 1, 0), List(0, 0, 1)); val empty: List[int] = List(); \end{lstlisting} diff --git a/sources/scala/Console.scala b/sources/scala/Console.scala index f3017e6b11..ec01840e0f 100644 --- a/sources/scala/Console.scala +++ b/sources/scala/Console.scala @@ -120,6 +120,7 @@ object Console { * @param args the parameters used to instantiate the format. */ def printf(text: String)(args: Any*): Unit = { + // todo: Uncurry if (text == null) out.print("null"); else diff --git a/sources/scala/Enumeration.scala b/sources/scala/Enumeration.scala index 44e478d0ab..c084c717e7 100644 --- a/sources/scala/Enumeration.scala +++ b/sources/scala/Enumeration.scala @@ -86,7 +86,10 @@ abstract class Enumeration(initial: Int, names: String*) { trait Value extends Ord[Value] { def id: Int; - def < (that: Value): Boolean = id < that.id; + def < [S >: Value <: Ord[S]](that: S): Boolean = that match { + case that1: Value => id < that1.id + case _ => that > this + } } protected class Val(i: Int, name: String) extends Value { diff --git a/sources/scala/List.scala b/sources/scala/List.scala index 609f71e9d3..fc5c8a63c5 100644 --- a/sources/scala/List.scala +++ b/sources/scala/List.scala @@ -441,7 +441,11 @@ trait List[+a] extends Seq[a] { def filter(p: a => Boolean): List[a] = match { case Nil => this case head :: tail => - if (p(head)) head :: (tail filter p) else tail filter p + val tail1 = tail filter p; + if (p(head)) + if (tail eq tail1) this + else head :: tail1 + else tail1 }; /** Remove all elements of the list which satisfy the predicate diff --git a/sources/scala/Ord.scala b/sources/scala/Ord.scala index aa55504c39..ff352ac584 100644 --- a/sources/scala/Ord.scala +++ b/sources/scala/Ord.scala @@ -9,6 +9,8 @@ package scala; +/* Shall we use a nonvariant Ord? + trait Ord[t <: Ord[t]]: t { def < (that: t): Boolean; def <=(that: t): Boolean = this < that || this == that; @@ -16,7 +18,7 @@ trait Ord[t <: Ord[t]]: t { def >=(that: t): Boolean = that <= this; } -/* Shall we use a covariant Ord? +*/ trait Ord[+T <: Ord[T]] { def < [S >: T <: Ord[S]](that: S): Boolean; @@ -24,4 +26,3 @@ trait Ord[+T <: Ord[T]] { def > [S >: T <: Ord[S]](that: S): Boolean = that < this; def >=[S >: T <: Ord[S]](that: S): Boolean = that <= this; } -*/ diff --git a/sources/scala/tools/scalac/ast/parser/Parser.scala b/sources/scala/tools/scalac/ast/parser/Parser.scala index abd8e9d822..f3c8cfbc0d 100644 --- a/sources/scala/tools/scalac/ast/parser/Parser.scala +++ b/sources/scala/tools/scalac/ast/parser/Parser.scala @@ -572,18 +572,18 @@ class Parser(unit: Unit) { * | symbol [ArgumentExprs] * | null */ - def literal(isPattern: boolean): Tree = { + def literal(isPattern: boolean, isNegated: boolean): Tree = { def litToTree() = s.token match { case CHARLIT => gen.mkCharLit(s.pos, s.intVal.asInstanceOf[char]) case INTLIT => - gen.mkIntLit(s.pos, s.intVal.asInstanceOf[int]) + gen.mkIntLit(s.pos, s.intVal(isNegated).asInstanceOf[int]) case LONGLIT => - gen.mkLongLit(s.pos, s.intVal) + gen.mkLongLit(s.pos, s.intVal(isNegated)) case FLOATLIT => - gen.mkFloatLit(s.pos, s.floatVal.asInstanceOf[float]) + gen.mkFloatLit(s.pos, s.floatVal(isNegated).asInstanceOf[float]) case DOUBLELIT => - gen.mkDoubleLit(s.pos, s.floatVal) + gen.mkDoubleLit(s.pos, s.floatVal(isNegated)) case STRINGLIT | SYMBOLLIT => gen.mkStringLit(s.pos, s.name.toString()) case TRUE => @@ -921,9 +921,16 @@ class Parser(unit: Unit) { /** PrefixExpr ::= [`-' | `+' | `~' | `!'] SimpleExpr */ def prefixExpr(): Tree = - if (s.token == IDENTIFIER && - (s.name == MINUS || - s.name == PLUS || + if (s.token == IDENTIFIER && s.name == MINUS) { + val name = ident(); + s.token match { + case INTLIT | LONGLIT | FLOATLIT | DOUBLELIT => + return literal(false, true); + case _ => + make.Select(s.pos, simpleExpr(), name); + } + } else if (s.token == IDENTIFIER && + (s.name == PLUS || s.name == TILDE || s.name == BANG)) { val name = ident(); @@ -947,7 +954,7 @@ class Parser(unit: Unit) { s.token match { case CHARLIT | INTLIT | LONGLIT | FLOATLIT | DOUBLELIT | STRINGLIT | SYMBOLLIT | TRUE | FALSE | NULL => - t = literal(false); + t = literal(false, false); case IDENTIFIER | THIS | SUPER => t = if( s.xStartsXML ) { xmlp.xLiteral; @@ -1263,6 +1270,14 @@ class Parser(unit: Unit) { xmlp.xLiteralPattern } else { var t = stableId(); + s.token match { + case INTLIT | LONGLIT | FLOATLIT | DOUBLELIT => + t match { + case Tree$Ident(name) if name == Names.MINUS => + return literal(true, true); + } + case _ => + } while (s.token == LPAREN) { var ts = Tree.EMPTY_ARRAY; accept(LPAREN); @@ -1276,7 +1291,7 @@ class Parser(unit: Unit) { case USCORE => make.Ident(s.skipToken(), Names.PATTERN_WILDCARD) case CHARLIT | INTLIT | LONGLIT | FLOATLIT | DOUBLELIT | STRINGLIT | SYMBOLLIT | TRUE | FALSE | NULL => - literal(true) + literal(true, false) case LPAREN => val p = s.pos; s.nextToken(); @@ -1923,6 +1938,8 @@ class Parser(unit: Unit) { isModifier()) { stats.append(joinComment(clsDef(modifiers()))); } else if (s.token != SEMI) { + System.out.println(s.token); + System.out.println(s.name); syntaxError("illegal start of class or object definition", true); } if (s.token != RBRACE && s.token != EOF) accept(SEMI); diff --git a/sources/scala/tools/scalac/ast/parser/Scanner.scala b/sources/scala/tools/scalac/ast/parser/Scanner.scala index 7814b2d25a..ab6d307c0c 100644 --- a/sources/scala/tools/scalac/ast/parser/Scanner.scala +++ b/sources/scala/tools/scalac/ast/parser/Scanner.scala @@ -63,7 +63,7 @@ class Scanner(_unit: Unit) extends TokenData { /** the input buffer: */ var buf: Array[byte] = unit.source.bytes(); - var bp: int = -1; + private var bp: int = -1; /** the current character */ @@ -89,6 +89,7 @@ class Scanner(_unit: Unit) extends TokenData { def nextch(): unit = { bp = bp + 1; ch = buf(bp); ccol = ccol + 1; + //System.out.print(bp + "[" + (ch.asInstanceOf[char]) + "]");//DEBUG } /** read next token and return last position @@ -141,7 +142,7 @@ class Scanner(_unit: Unit) extends TokenData { this.copyFrom(prev); } } - //System.out.println("<" + token2string(token) + ">");//DEBUG + //System.out.println("<" + token2string(token) + ":" + name + ">");//DEBUG } } @@ -206,14 +207,17 @@ class Scanner(_unit: Unit) extends TokenData { nextch(); if (ch == 'x' || ch == 'X') { nextch(); - getNumber(index + 2, 16); + base = 16; + getNumber(index + 2); } else { - getNumber(index, 8); + base = 8; + getNumber(index); } return; // scala-mode: return is a keyword case '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' => - getNumber(index, 10); + base = 10; + getNumber(index); return; case '`' => //" scala-mode: need to understand literals getStringLit('`'); @@ -252,12 +256,6 @@ class Scanner(_unit: Unit) extends TokenData { if (ch == '\'') { nextch(); token = CHARLIT; - val ascii = new Array[byte](litlen * 2); - val alen = SourceRepresentation.source2ascii(lit, 0, litlen, ascii); - if (alen > 0) - intVal = SourceRepresentation.ascii2string(ascii, 0, alen).charAt(0); - else - intVal = 0; } else { syntaxError("unclosed character literal"); } @@ -534,7 +532,6 @@ class Scanner(_unit: Unit) extends TokenData { } /** read fractional part of floating point number; - * Then floatVal := buf[index..], converted to a floating point number. */ protected def getFraction(index: int) = { while (SourceRepresentation.digit2int(ch, 10) >= 0) { @@ -556,54 +553,76 @@ class Scanner(_unit: Unit) extends TokenData { nextch(); } } - var limit = Double.MAX_VALUE; if ((ch == 'd') || (ch == 'D')) { nextch(); } else if ((ch == 'f') || (ch == 'F')) { token = FLOATLIT; - limit = Float.MAX_VALUE; nextch(); } + name = Name.fromAscii(buf, index, bp - index); + } + + /** convert name, base to long value + * base = the base of the number; one of 8, 10, 16. + */ + def intVal(negated: boolean): long = { + if (token == CHARLIT && !negated) { + val ascii = new Array[byte](litlen * 2); + val alen = SourceRepresentation.source2ascii(lit, 0, litlen, ascii); + if (alen > 0) + SourceRepresentation.ascii2string(ascii, 0, alen).charAt(0) + else + 0 + } else { + var value: long = 0; + val divider = if (base == 10) 1 else 2; + val limit: long = if (token == LONGLIT) Long.MAX_VALUE else Integer.MAX_VALUE; + var i = 0; + val len = name.length(); + while (i < len) { + val d = SourceRepresentation.digit2int(name sub i, base); + if (d < 0) { + syntaxError("malformed integer number"); + return 0; + } + if (value < 0 || + limit / (base / divider) < value || + limit - (d / divider) < value * (base / divider) && + !(negated && limit == value * base - 1 + d)) { + syntaxError("integer number too large"); + return 0; + } + value = value * base + d; + i = i + 1; + } + if (negated) -value else value + } + } + + def intVal: long = intVal(false); + + /** convert name, base to double value + */ + def floatVal(negated: boolean): double = { + val limit: double = + if (token == DOUBLELIT) Double.MAX_VALUE else Float.MAX_VALUE; try { - floatVal = Double.valueOf(new String(buf, index, bp - index)).doubleValue(); - if (floatVal > limit) + val value = Double.valueOf(name.toString()).doubleValue(); + if (value > limit) syntaxError("floating point number too large"); + if (negated) -value else value } catch { case _: NumberFormatException => syntaxError("malformed floating point number"); + 0.0 } } - /** intVal := buf(index..index+len-1), converted to an integer number. - * base = the base of the number; one of 8, 10, 16. - * max = the maximal number before an overflow. - */ - protected def makeInt (index: int, len: int, base: int, max: long): unit = { - intVal = 0; - val divider = if (base == 10) 1 else 2; - var i = 0; - while (i < len) { - val d = SourceRepresentation.digit2int(buf(index + i), base); - if (d < 0) { - syntaxError("malformed integer number"); - return; - } - if (intVal < 0 || - max / (base / divider) < intVal || - max - (d / divider) < (intVal * (base / divider) - 0)) { - syntaxError("integer number too large"); - return; - } - intVal = intVal * base + d; - i = i + 1; - } - } + def floatVal: double = floatVal(false); - /** read a number, - * and convert buf[index..], setting either intVal or floatVal. - * base = the base of the number; one of 8, 10, 16. + /** read a number into name and set base */ - protected def getNumber(index: int, base: int) = { + protected def getNumber(index: int) = { while (SourceRepresentation.digit2int(ch, if (base == 8) 10 else base) >= 0) { nextch(); } @@ -630,9 +649,8 @@ class Scanner(_unit: Unit) extends TokenData { ch == '$' || ch == '_') { bp = bp - 1; ch = buf(bp); - ccol = ccol - 1; - makeInt(index, bp - index, base, Integer.MAX_VALUE); - intVal = intVal.asInstanceOf[int]; + ccol = ccol - 1; + name = Name.fromAscii(buf, index, bp - index); token = INTLIT; } else getFraction(index); @@ -643,12 +661,11 @@ class Scanner(_unit: Unit) extends TokenData { getFraction(index); } else { if (ch == 'l' || ch == 'L') { - makeInt(index, bp - index, base, Long.MAX_VALUE); + name = Name.fromAscii(buf, index, bp - index); nextch(); token = LONGLIT; } else { - makeInt(index, bp - index, base, Integer.MAX_VALUE); - intVal = intVal.asInstanceOf[int]; + name = Name.fromAscii(buf, index, bp - index); token = INTLIT; } } diff --git a/sources/scala/tools/scalac/ast/parser/TokenData.scala b/sources/scala/tools/scalac/ast/parser/TokenData.scala index ab3b0ba9f5..43f11e18de 100644 --- a/sources/scala/tools/scalac/ast/parser/TokenData.scala +++ b/sources/scala/tools/scalac/ast/parser/TokenData.scala @@ -31,17 +31,15 @@ class TokenData { */ var name: Name = null; - /** the value of a number + /** the base of a number */ - var intVal: long = 0; - var floatVal: double = 0; + var base: int = 0; def copyFrom(td: TokenData) = { this.token = td.token; this.pos = td.pos; this.name = td.name; - this.intVal = td.intVal; - this.floatVal = td.floatVal; + this.base = td.base; } } } diff --git a/sources/scala/tools/scalac/typechecker/Analyzer.scala b/sources/scala/tools/scalac/typechecker/Analyzer.scala index afaab26e79..9a8862d567 100644 --- a/sources/scala/tools/scalac/typechecker/Analyzer.scala +++ b/sources/scala/tools/scalac/typechecker/Analyzer.scala @@ -39,13 +39,6 @@ class Analyzer(global: scalac_Global, descr: AnalyzerPhase) extends Transformer( import Modifiers._; import Kinds._; - val definitions = global.definitions; - val infer = new scala.tools.scalac.typechecker.Infer(this); - val desugarize = new DeSugarize(make, copy, gen, infer, global); - val constfold = new ConstantFolder(global); - - var unit: Unit = _; - private var context: Context = _; private var pt: Type = _; private var mode: int = _; @@ -53,6 +46,15 @@ class Analyzer(global: scalac_Global, descr: AnalyzerPhase) extends Transformer( private var inAlternative: boolean = _; private var patternVars: HashMap = _; // for pattern matching; maps x to {true,false} + val definitions = global.definitions; + val infer = new scala.tools.scalac.typechecker.Infer(this) { + override def getCoerceMeths = { context.coerceMeths; } + } + val desugarize = new DeSugarize(make, copy, gen, infer, global); + val constfold = new ConstantFolder(global); + + var unit: Unit = _; + override def apply(units: Array[Unit]): unit = { var i = 0; while (i < units.length) { enterUnit(units(i)); @@ -87,6 +89,7 @@ class Analyzer(global: scalac_Global, descr: AnalyzerPhase) extends Transformer( def enter(context: Context, unit: Unit): unit = { assert(this.unit == null, "start unit non null for " + unit); + context.infer = infer; this.unit = unit; this.context = context; this.patternVars = new HashMap(); @@ -1221,14 +1224,13 @@ class Analyzer(global: scalac_Global, descr: AnalyzerPhase) extends Transformer( checkStable(expr); owntype = expr.getType(); val tp: Type = owntype.widen(); - var i = 0; while (i < selectors.length) { + { var i = 0; while (i < selectors.length) { if (selectors(i) != Names.IMPORT_WILDCARD && tp.lookup(selectors(i)) == Symbol.NONE && tp.lookup(selectors(i).toTypeName()) == Symbol.NONE) error(tree.pos, "" + NameTransformer.decode(selectors(i)) + " is not a member of " + expr); i = i + 2 - } - + }} case _ => throw new ApplicationError(); } @@ -1526,14 +1528,30 @@ class Analyzer(global: scalac_Global, descr: AnalyzerPhase) extends Transformer( if (pt.symbol() == definitions.UNIT_CLASS) { return gen.mkUnitBlock(tree); } else if (infer.isCompatible(tree.getType(), pt)) { - val coerceMeth: Symbol = tree.getType().lookup(Names.coerce); - if (coerceMeth != Symbol.NONE) { - val coerceType: Type = checkAccessible( - tree.pos, coerceMeth, tree.getType().memberType(coerceMeth), - tree, tree.getType()); - val tree1 = make.Select(tree.pos, tree, Names.coerce) - .setSymbol(coerceMeth) - .setType(coerceType); + val coerce = infer.bestCoerce(tree.getType(), pt); + if (coerce != null) { + val coerceFn = + if (coerce.qual == Tree.Empty) { + make.Ident(tree.pos, Names.view) + .setSymbol(coerce.sym).setType(coerce.symtype) + } else { + val coercetype = checkAccessible( + tree.pos, coerce.sym, coerce.symtype, coerce.qual, coerce.qual.getType()); + make.Select(tree.pos, coerce.qual, Names.view) + .setSymbol(coerce.sym).setType(coercetype) + } + val tree1 = gen.Apply(coerceFn, NewArray.Tree(tree)); + return adapt(tree1, mode, pt); + } + // todo: remove + val coerceMeth: Symbol = tree.getType().lookup(Names.coerce); + if (coerceMeth != Symbol.NONE) { + val coerceType: Type = checkAccessible( + tree.pos, coerceMeth, tree.getType().memberType(coerceMeth), + tree, tree.getType()); + val tree1 = make.Select(tree.pos, tree, Names.coerce) + .setSymbol(coerceMeth) + .setType(coerceType); return adapt(tree1, mode, pt); } } @@ -1798,7 +1816,7 @@ class Analyzer(global: scalac_Global, descr: AnalyzerPhase) extends Transformer( /** Attribute a template */ def transformTemplate(templ: Tree$Template, owner: Symbol): Tree$Template = { - if (global.debug) global.log("transforming template of " + owner);//debug + //if (global.debug) global.log("transforming template of " + owner);//DEBUG if (templ.getType() == null) defineTemplate(templ, owner, owner.members());//may happen for mixins //System.out.println(owner.info());//DEBUG diff --git a/sources/scala/tools/scalac/typechecker/Context.scala b/sources/scala/tools/scalac/typechecker/Context.scala index 56e3852a18..a245aeb437 100644 --- a/sources/scala/tools/scalac/typechecker/Context.scala +++ b/sources/scala/tools/scalac/typechecker/Context.scala @@ -7,24 +7,30 @@ \* */ import scalac.symtab._; import scalac.ast._; +import scalac.util.Names; package scala.tools.scalac.typechecker { object Context { val NONE = new Context(); + NONE.coerceCache = List(); } class Context { + import Kinds._; + var tree: Tree = _; // Tree associated with this context var owner: Symbol = _; // The current owner var scope: Scope = _; // The current scope - var imports: ImportList = _; // The current import list + var imports: ImportList = null; // The current import list var outer: Context = _; // The next outer context var enclClass: Context = this; // The next outer context whose tree // is a class template var variance: int = _; // Variance relative to enclosing class. var constructorClass: Symbol = _; // Class for auxiliary constructor + var coerceCache: List[Coerce] = null; // Coerce symbols visible in scope + var infer: Infer = null; // Type inferencer def this(tree: Tree, owner: Symbol, scope: Scope, outer: Context) = { this(); @@ -38,6 +44,7 @@ class Context { else outer.enclClass; this.variance = outer.variance; this.constructorClass = outer.constructorClass; + this.infer = outer.infer; this.outer = outer; } @@ -60,5 +67,38 @@ class Context { case _ => outer.isTopLevel() } + + def coerceMeths: List[Coerce] = { + + def addCoerce(sym: Symbol, symtype: Type, qual: Tree): unit = symtype match { + case Type$OverloadedType(alts, alttypes) => + var i = 0; + while (i < alts.length) { + addCoerce(alts(i), alttypes(i), qual); + i = i + 1; + } + case _ => + def isUnShadowed(coerce: Coerce) = + coerce.context == this || !infer.specializes(coerce.symtype, symtype); + val coerce = new Coerce(sym, symtype, qual, this); + System.out.println("COERCE " + symtype + " " + qual); + coerceCache = coerce :: coerceCache.filter(isUnShadowed); + } + + if (coerceCache == null) { + coerceCache = outer.coerceMeths; + val e = scope.lookupEntry(Names.view); + if (e.owner == scope && e.sym.kind == VAL) + addCoerce(e.sym, e.sym.getType(), Tree.Empty); + var imp = imports; + while (imp != outer.imports) { + val sym = imp.importedSymbol(Names.view); + if (sym.kind == VAL) + addCoerce(sym, imp.importType().memberType(sym), imp.importPrefix()); + imp = imp.prev; + } + } + coerceCache + } } } diff --git a/sources/scala/tools/scalac/typechecker/ImportList.scala b/sources/scala/tools/scalac/typechecker/ImportList.scala index 136aa5474f..0ff717965a 100644 --- a/sources/scala/tools/scalac/typechecker/ImportList.scala +++ b/sources/scala/tools/scalac/typechecker/ImportList.scala @@ -41,26 +41,7 @@ case class ImportList(tree: Tree, enclScope: Scope, prev: ImportList) { this.importType().isSameAs(that.importType()); def importedSymbol(name: Name): Symbol = { - val t = this.importType(); - var renamed = false; - tree match { - case Tree$Import(expr, selectors) => - var i = 0; - while (i < selectors.length) { - if (i + 1 < selectors.length && name.toTermName() == selectors(i+1)) { - if (name.isTypeName()) - return t.lookupNonPrivate(selectors(i).toTypeName()); - else - return t.lookupNonPrivate(selectors(i)); - } else if (name.toTermName() == selectors(i)) { - renamed = true; - } else if (selectors(i) == Names.IMPORT_WILDCARD && !renamed) { - return t.lookupNonPrivate(name); - } - i = i + 2 - } - Symbol.NONE - } + return TreeInfo.importedSymbol(tree, name); } } } diff --git a/sources/scala/tools/scalac/typechecker/Infer.scala b/sources/scala/tools/scalac/typechecker/Infer.scala index 5edc10d53e..74ed2d8cad 100644 --- a/sources/scala/tools/scalac/typechecker/Infer.scala +++ b/sources/scala/tools/scalac/typechecker/Infer.scala @@ -28,6 +28,10 @@ class Infer(global: scalac_Global, gen: TreeGen, make: TreeFactory) extends scal def this(trans: Transformer) = this(trans.global, trans.gen, trans.make); +// Coerce generator, overridable */ + + def getCoerceMeths: List[Coerce] = List(); + // Error messages ------------------------------------------------------------- def applyErrorMsg(msg1: String, fn: Tree, msg2: String, argtypes: Array[Type], pt: Type): String = @@ -465,8 +469,16 @@ class Infer(global: scalac_Global, gen: TreeGen, make: TreeFactory) extends scal val tp1 = normalize(tp); if (tp1.isSubType(pt)) true else { - val coerceMeth: Symbol = tp1.lookup(Names.coerce); - coerceMeth.kind != NONE && canCoerce(tp1.memberType(coerceMeth)); + val argtypes = NewArray.Type(tp1); + var coerceMeths = getCoerceMeths; + while (!coerceMeths.isEmpty && !isApplicable(coerceMeths.head.symtype, argtypes, pt, false)) + coerceMeths = coerceMeths.tail; + if (!coerceMeths.isEmpty) true + // todo: remove + else { + val coerceMeth: Symbol = tp1.lookup(Names.coerce); + coerceMeth.kind != NONE && canCoerce(tp1.memberType(coerceMeth)); + } } } @@ -787,13 +799,17 @@ class Infer(global: scalac_Global, gen: TreeGen, make: TreeFactory) extends scal /** Is function type `ftpe' applicable to `argtypes' and * does its result conform to `pt'? */ - def isApplicable(ftpe: Type, argtypes: Array[Type], pt: Type): boolean = ftpe match { + def isApplicable(ftpe: Type, argtypes: Array[Type], pt: Type): boolean = + isApplicable(ftpe, argtypes, pt, true); + + def isApplicable(ftpe: Type, argtypes: Array[Type], pt: Type, + coercible: boolean): boolean = ftpe match { case Type$MethodType(params, restpe) => // sequences ? List( a* ) val formals: Array[Type] = formalTypes(params, argtypes.length); - isCompatible(restpe, pt) && formals.length == argtypes.length && - isCompatible(argtypes, formals) + (if (coercible) isCompatible(argtypes, formals) && isCompatible(restpe, pt) + else Type.isSubType(argtypes, formals) && restpe.isSubType(pt)); case Type$PolyType(tparams, Type$MethodType(params, restpe)) => try { val targs: Array[Type] = methTypeArgs( @@ -944,6 +960,36 @@ class Infer(global: scalac_Global, gen: TreeGen, make: TreeFactory) extends scal tree.setSymbol(alts(i)).setType(alttypes(i)); } } -} + /** return coerce which best matches argument type `tp' and expected type `pt'. + */ + def bestCoerce(tp: Type, pt: Type): Coerce = { + var best: Coerce = null; + var coerceMeths = getCoerceMeths; + val argtypes = NewArray.Type(tp); + while (!coerceMeths.isEmpty) { + if (isApplicable(coerceMeths.head.symtype, argtypes, pt, false) && + (best == null || specializes(coerceMeths.head.symtype, best.symtype))) + best = coerceMeths.head; + coerceMeths = coerceMeths.tail + } + if (best != null) { + coerceMeths = getCoerceMeths; + while (!coerceMeths.isEmpty) { + if (coerceMeths.head != best && + isApplicable(coerceMeths.head.symtype, argtypes, pt, false) && + !(specializes(best.symtype, coerceMeths.head.symtype) && + !specializes(coerceMeths.head.symtype, best.symtype))) + throw new Type$Error( + "ambiguous coerce,\n" + + "both " + coerceMeths.head.sym + ": " + coerceMeths.head.symtype + coerceMeths.head.sym.locationString() + "\n" + + "and " + best.sym + ": " + best.symtype + best.sym.locationString() + "\nmap argument type " + + tp + " to expected type " + pt); + coerceMeths = coerceMeths.tail; + } + } + best + } +} } + diff --git a/sources/scalac/ast/TreeInfo.java b/sources/scalac/ast/TreeInfo.java index 2aa95f401c..b66b7d2357 100644 --- a/sources/scalac/ast/TreeInfo.java +++ b/sources/scalac/ast/TreeInfo.java @@ -191,6 +191,31 @@ public class TreeInfo { } } + /** The symbol with name `name' imported from import clause `tree'. + */ + public static Symbol importedSymbol(Tree tree, Name name) { + switch (tree) { + case Import(Tree expr, Name[] selectors): + Type pre = tree.symbol().type(); + boolean renamed = false; + for (int i = 0; i < selectors.length; i = i + 2) { + if (i + 1 < selectors.length && name.toTermName() == selectors[i + 1]) { + if (name.isTypeName()) + return pre.lookupNonPrivate(selectors[i].toTypeName()); + else + return pre.lookupNonPrivate(selectors[i]); + } else if (name.toTermName() == selectors[i]) { + renamed = true; + } else if (selectors[i] == Names.IMPORT_WILDCARD && !renamed) { + return pre.lookupNonPrivate(name); + } + } + return Symbol.NONE; + default: + throw new ApplicationError(); + } + } + /** returns true if the tree is a sequence-valued pattern. * precondition: tree is a pattern. diff --git a/sources/scalac/util/Names.java b/sources/scalac/util/Names.java index 9824c2cf9a..ac8917a752 100644 --- a/sources/scalac/util/Names.java +++ b/sources/scalac/util/Names.java @@ -213,6 +213,7 @@ public class Names { public static final Name throw_ = Name.fromString("throw"); public static final Name true_ = Name.fromString("true"); public static final Name update = Name.fromString("update"); + public static final Name view = Name.fromString("view"); public static final Name tag = Name.fromString("$tag"); |