summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartin Odersky <odersky@gmail.com>2009-09-28 11:09:14 +0000
committerMartin Odersky <odersky@gmail.com>2009-09-28 11:09:14 +0000
commit53d98e7d421d55054fb0bcb606539fc36364bebf (patch)
treebe6b68360458b97ab92a0dc3e963f365ae5acd95
parent32cac0e3fde4a512d7322cd60146bdac7d1898d4 (diff)
downloadscala-53d98e7d421d55054fb0bcb606539fc36364bebf.tar.gz
scala-53d98e7d421d55054fb0bcb606539fc36364bebf.tar.bz2
scala-53d98e7d421d55054fb0bcb606539fc36364bebf.zip
refined implicit resolution.
-rw-r--r--src/compiler/scala/tools/nsc/transform/Erasure.scala2
-rw-r--r--src/compiler/scala/tools/nsc/transform/UnCurry.scala11
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Infer.scala28
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Typers.scala41
-rw-r--r--test/files/neg/implicits.scala19
-rw-r--r--test/files/pos/implicits.scala6
-rw-r--r--test/files/run/arrays.scala8
-rw-r--r--test/files/run/colltest1.scala2
-rw-r--r--test/files/run/multi-array.scala1
9 files changed, 93 insertions, 25 deletions
diff --git a/src/compiler/scala/tools/nsc/transform/Erasure.scala b/src/compiler/scala/tools/nsc/transform/Erasure.scala
index e447921beb..769174aead 100644
--- a/src/compiler/scala/tools/nsc/transform/Erasure.scala
+++ b/src/compiler/scala/tools/nsc/transform/Erasure.scala
@@ -609,7 +609,7 @@ abstract class Erasure extends AddInterfaces with typechecker.Analyzer with ast.
* @param pt ...
* @return the adapted tree
*/
- override protected def adapt(tree: Tree, mode: Int, pt: Type): Tree =
+ override protected def adapt(tree: Tree, mode: Int, pt: Type, original: Tree = EmptyTree): Tree =
adaptToType(tree, pt)
/** A replacement for the standard typer's `typed1' method */
diff --git a/src/compiler/scala/tools/nsc/transform/UnCurry.scala b/src/compiler/scala/tools/nsc/transform/UnCurry.scala
index f03b7d5096..12711a36c6 100644
--- a/src/compiler/scala/tools/nsc/transform/UnCurry.scala
+++ b/src/compiler/scala/tools/nsc/transform/UnCurry.scala
@@ -410,17 +410,22 @@ abstract class UnCurry extends InfoTransform with TypingTransformers {
}
}
+ val lastElemType = lastFormal.typeArgs.head
var suffix: Tree =
if (!args.isEmpty && (treeInfo isWildcardStarArg args.last)) {
val Typed(tree, _) = args.last;
- if (isJava && !(tree.tpe.typeSymbol == ArrayClass) && (tree.tpe.typeSymbol isSubClass TraversableClass)) sequenceToArray(tree)
- else tree
+ if (isJava)
+ if (tree.tpe.typeSymbol == ArrayClass) tree
+ else sequenceToArray(tree)
+ else
+ if (tree.tpe.typeSymbol isSubClass TraversableClass) tree
+ else arrayToSequence(tree, lastElemType)
} else {
- val lastElemType = lastFormal.typeArgs.head
val tree = mkArrayValue(args drop (formals.length - 1), lastElemType)
if (isJava || inPattern) tree
else arrayToSequence(tree, lastElemType)
}
+
atPhase(phase.next) {
if (isJava &&
suffix.tpe.typeSymbol == ArrayClass &&
diff --git a/src/compiler/scala/tools/nsc/typechecker/Infer.scala b/src/compiler/scala/tools/nsc/typechecker/Infer.scala
index 737af2d36e..d8f54760a1 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Infer.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Infer.scala
@@ -451,6 +451,19 @@ trait Infer {
tp.isInstanceOf[MethodType] && // can perform implicit () instantiation
tp.paramTypes.length == 0 && isCompatible(tp.resultType, pt)
+ /** Like weakly compatible but don't apply any implicit conversions yet.
+ * Used when comparing the result type of a method with its prototype.
+ */
+ def isConservativelyCompatible(tp: Type, pt: Type): Boolean = {
+ val savedImplicitsEnabled = context.implicitsEnabled
+ context.implicitsEnabled = false
+ try {
+ isWeaklyCompatible(tp, pt)
+ } finally {
+ context.implicitsEnabled = savedImplicitsEnabled
+ }
+ }
+
def isCoercible(tp: Type, pt: Type): Boolean = false
def isCompatible(tps: List[Type], pts: List[Type]): Boolean =
@@ -556,7 +569,7 @@ trait Infer {
case ex: NoInstance => WildcardType
}
val tvars = tparams map freshVar
- if (isWeaklyCompatible(restpe.instantiateTypeParams(tparams, tvars), pt))
+ if (isConservativelyCompatible(restpe.instantiateTypeParams(tparams, tvars), pt))
List.map2(tparams, tvars) ((tparam, tvar) =>
instantiateToBound(tvar, varianceInTypes(formals)(tparam)))
else
@@ -611,7 +624,7 @@ trait Infer {
}
// check first whether type variables can be fully defined from
// expected result type.
- if (!isWeaklyCompatible(restpe.instantiateTypeParams(tparams, tvars), pt)) {
+ if (!isConservativelyCompatible(restpe.instantiateTypeParams(tparams, tvars), pt)) {
// just wait and instantiate from the arguments.
// that way, we can try to apply an implicit conversion afterwards.
// This case could happen if restpe is not fully defined, so that
@@ -821,9 +834,13 @@ trait Infer {
case OverloadedType(pre, alts) =>
alts exists (alt => isAsSpecific(pre.memberType(alt), ftpe2))
case et: ExistentialType =>
- et.withTypeVars(isAsSpecific(_, ftpe2)) // !!! why isStrictly?
+ et.withTypeVars(isAsSpecific(_, ftpe2))
+ case mt: ImplicitMethodType =>
+ isAsSpecific(ftpe1.resultType, ftpe2)
case MethodType(params @ (x :: xs), _) =>
isApplicable(List(), ftpe2, params map (_.tpe), WildcardType)
+ case PolyType(tparams, mt: ImplicitMethodType) =>
+ isAsSpecific(PolyType(tparams, mt.resultType), ftpe2)
case PolyType(_, MethodType(params @ (x :: xs), _)) =>
isApplicable(List(), ftpe2, params map (_.tpe), WildcardType)
case ErrorType =>
@@ -834,12 +851,17 @@ trait Infer {
alts forall (alt => isAsSpecific(ftpe1, pre.memberType(alt)))
case et: ExistentialType =>
et.withTypeVars(isAsSpecific(ftpe1, _))
+ case mt: ImplicitMethodType =>
+ isAsSpecific(ftpe1, mt.resultType)
+ case PolyType(tparams, mt: ImplicitMethodType) =>
+ isAsSpecific(ftpe1, PolyType(tparams, mt.resultType))
case MethodType(_, _) | PolyType(_, MethodType(_, _)) =>
true
case _ =>
isAsSpecificValueType(ftpe1, ftpe2, List(), List())
}
}
+
/*
def isStrictlyMoreSpecific(ftpe1: Type, ftpe2: Type): Boolean =
ftpe1.isError || isAsSpecific(ftpe1, ftpe2) &&
diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
index 1943fcc1a4..d9d9bda13f 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
@@ -238,13 +238,12 @@ trait Typers { self: Analyzer =>
psym = to.decls enter psym
psym setInfo tp
try {
- inferView(tree, from, to, true)
+ inferView(tree, from, to, true)
} catch {
case ex: AssertionError =>
- println("infer view "+tree+" "+name+" "+context.undetparams)
+ println("inverView "+tree+", from = "+from+", name = "+name+" tp = "+tp)
throw ex
}
-
}
import infer._
@@ -762,25 +761,25 @@ trait Typers { self: Analyzer =>
* (13) When in mode EXPRmode, apply a view
* If all this fails, error
*/
- protected def adapt(tree: Tree, mode: Int, pt: Type): Tree = tree.tpe match {
+ protected def adapt(tree: Tree, mode: Int, pt: Type, original: Tree = EmptyTree): Tree = tree.tpe match {
case atp @ AnnotatedType(_, _, _) if canAdaptAnnotations(tree, mode, pt) => // (-1)
adaptAnnotations(tree, mode, pt)
case ct @ ConstantType(value) if ((mode & (TYPEmode | FUNmode)) == 0 && (ct <:< pt) && !onlyPresentation) => // (0)
treeCopy.Literal(tree, value)
case OverloadedType(pre, alts) if ((mode & FUNmode) == 0) => // (1)
inferExprAlternative(tree, pt)
- adapt(tree, mode, pt)
+ adapt(tree, mode, pt, original)
case PolyType(List(), restpe) => // (2)
- adapt(tree setType restpe, mode, pt)
+ adapt(tree setType restpe, mode, pt, original)
case TypeRef(_, sym, List(arg))
if ((mode & EXPRmode) != 0 && sym == ByNameParamClass) => // (2)
- adapt(tree setType arg, mode, pt)
+ adapt(tree setType arg, mode, pt, original)
case tr @ TypeRef(_, sym, _)
if sym.isAliasType && tr.normalize.isInstanceOf[ExistentialType] &&
((mode & (EXPRmode | LHSmode)) == EXPRmode) =>
- adapt(tree setType tr.normalize.skolemizeExistential(context.owner, tree), mode, pt)
+ adapt(tree setType tr.normalize.skolemizeExistential(context.owner, tree), mode, pt, original)
case et @ ExistentialType(_, _) if ((mode & (EXPRmode | LHSmode)) == EXPRmode) =>
- adapt(tree setType et.skolemizeExistential(context.owner, tree), mode, pt)
+ adapt(tree setType et.skolemizeExistential(context.owner, tree), mode, pt, original)
case PolyType(tparams, restpe) if ((mode & (TAPPmode | PATTERNmode | HKmode)) == 0) => // (3)
// assert((mode & HKmode) == 0) //@M a PolyType in HKmode represents an anonymous type function,
// we're in HKmode since a higher-kinded type is expected --> hence, don't implicitly apply it to type params!
@@ -794,7 +793,7 @@ trait Typers { self: Analyzer =>
else TypeApply(tree, tparams1 map (tparam =>
TypeTree(tparam.tpe) setPos tree.pos.focus)) setPos tree.pos
context.undetparams = context.undetparams ::: tparams1
- adapt(tree1 setType restpe.substSym(tparams, tparams1), mode, pt)
+ adapt(tree1 setType restpe.substSym(tparams, tparams1), mode, pt, original)
case mt: ImplicitMethodType if ((mode & (EXPRmode | FUNmode | LHSmode)) == EXPRmode) => // (4.1)
if (!context.undetparams.isEmpty/* && (mode & POLYmode) == 0 disabled to make implicits in new collection work; we should revisit this. */) { // (9)
context.undetparams = inferExprInstance(
@@ -805,7 +804,17 @@ trait Typers { self: Analyzer =>
// so we need to instantiate to minimal type List[Nothing].
}
val typer1 = constrTyperIf(treeInfo.isSelfOrSuperConstrCall(tree))
- typer1.typed(typer1.applyImplicitArgs(tree), mode, pt)
+ if (original != EmptyTree && pt != WildcardType)
+ typer1.silent(tpr => tpr.typed(tpr.applyImplicitArgs(tree), mode, pt)) match {
+ case result: Tree => result
+ case ex: TypeError =>
+ if (settings.debug.value) log("fallback on implicits: "+tree)
+ val tree1 = typed(original, mode, WildcardType)
+ tree1.tpe = addAnnotations(tree1, tree1.tpe)
+ if (tree1.isEmpty) tree1 else adapt(tree1, mode, pt, EmptyTree)
+ }
+ else
+ typer1.typed(typer1.applyImplicitArgs(tree), mode, pt)
case mt: MethodType
if (((mode & (EXPRmode | FUNmode | LHSmode)) == EXPRmode) &&
(context.undetparams.isEmpty || (mode & POLYmode) != 0)) =>
@@ -825,7 +834,7 @@ trait Typers { self: Analyzer =>
//println("eta "+tree+" ---> "+tree1+":"+tree1.tpe)
typed(tree1, mode, pt)
} else if (!meth.isConstructor && mt.params.isEmpty) { // (4.3)
- adapt(typed(Apply(tree, List()) setPos tree.pos), mode, pt)
+ adapt(typed(Apply(tree, List()) setPos tree.pos), mode, pt, original)
} else if (context.implicitsEnabled) {
errorTree(tree, "missing arguments for "+meth+meth.locationString+
(if (meth.isConstructor) ""
@@ -933,7 +942,7 @@ trait Typers { self: Analyzer =>
return tree
}
val tree1 = constfold(tree, pt) // (10) (11)
- if (tree1.tpe <:< pt) adapt(tree1, mode, pt)
+ if (tree1.tpe <:< pt) adapt(tree1, mode, pt, original)
else {
if ((mode & (EXPRmode | FUNmode)) == EXPRmode) {
pt.normalize match {
@@ -2966,10 +2975,6 @@ trait Typers { self: Analyzer =>
/** Try to apply function to arguments; if it does not work try to
* insert an implicit conversion.
- *
- * @param fun ...
- * @param args ...
- * @return ...
*/
def tryTypedApply(fun: Tree, args: List[Tree]): Tree = {
val start = System.nanoTime()
@@ -3775,7 +3780,7 @@ trait Typers { self: Analyzer =>
tree1.tpe = addAnnotations(tree1, tree1.tpe)
- val result = if (tree1.isEmpty) tree1 else adapt(tree1, mode, pt)
+ val result = if (tree1.isEmpty) tree1 else adapt(tree1, mode, pt, tree)
if (printTypings) println("adapted "+tree1+":"+tree1.tpe.widen+" to "+pt+", "+context.undetparams); //DEBUG
// for (t <- tree1.tpe) assert(t != WildcardType)
// if ((mode & TYPEmode) != 0) println("type: "+tree1+" has type "+tree1.tpe)
diff --git a/test/files/neg/implicits.scala b/test/files/neg/implicits.scala
index 056e0be6ca..be85029660 100644
--- a/test/files/neg/implicits.scala
+++ b/test/files/neg/implicits.scala
@@ -37,3 +37,22 @@ object test2 {
def foo(x: Int) = 3
foo(set)
}
+
+// #2180
+class Mxml {
+
+ private def processChildren( children:Seq[Any] ):List[Mxml] = {
+
+ children.toList.flatMap ( e => {
+
+ e match {
+
+ case s:scala.collection.Traversable[_] => s case a => List(a)
+
+ }
+
+ })
+
+ }
+
+}
diff --git a/test/files/pos/implicits.scala b/test/files/pos/implicits.scala
index 88632850fa..a8d362d0a7 100644
--- a/test/files/pos/implicits.scala
+++ b/test/files/pos/implicits.scala
@@ -30,3 +30,9 @@ object Test1625 {
println("=> result: " + res)
}
}
+
+object Test2188 {
+ implicit def toJavaList[A: ClassManifest](t:collection.Sequence[A]):java.util.List[A] = java.util.Arrays.asList(t.toArray:_*)
+
+ val x: java.util.List[String] = List("foo")
+}
diff --git a/test/files/run/arrays.scala b/test/files/run/arrays.scala
index 8a2d96a2a7..e8a984dc1b 100644
--- a/test/files/run/arrays.scala
+++ b/test/files/run/arrays.scala
@@ -158,6 +158,12 @@ object Test {
c(xs)
}
+ def checkT2368() {
+ val arr = Array(1, 2, 3)
+ arr(0) += 1
+ assert(arr(0) == 1)
+ }
+
//##########################################################################
// Values
@@ -919,6 +925,7 @@ object Test {
checkZip
checkConcat
+ checkT2368()
//######################################################################
@@ -929,3 +936,4 @@ object Test {
//##########################################################################
}
+
diff --git a/test/files/run/colltest1.scala b/test/files/run/colltest1.scala
index 2fc8af2390..7069c052a5 100644
--- a/test/files/run/colltest1.scala
+++ b/test/files/run/colltest1.scala
@@ -127,6 +127,8 @@ object Test extends Application {
assert(ten.patch(0, List(1, 2, 3), 9) == List(1, 2, 3, 10))
assert(empty.padTo(10, 7) == Array.fill(10)(7).toSequence)
assert((ten zip ten.indices) == ten.zipWithIndex)
+ assert(ten sortWith (_ < _) == ten)
+ assert(ten sortWith (_ > _) == ten.reverse)
}
def setTest(empty: => Set[String]) {
diff --git a/test/files/run/multi-array.scala b/test/files/run/multi-array.scala
index 04538402f8..42f64751fd 100644
--- a/test/files/run/multi-array.scala
+++ b/test/files/run/multi-array.scala
@@ -10,4 +10,5 @@ object Test extends Application {
for (i <- 0 until 3; j <- 0 until 3)
aaiComplete(i)(j) = i + j
println(aaiComplete.deepToString)
+ assert(aaiComplete.last.last == 4)
}