From 53d98e7d421d55054fb0bcb606539fc36364bebf Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 28 Sep 2009 11:09:14 +0000 Subject: refined implicit resolution. --- .../scala/tools/nsc/transform/Erasure.scala | 2 +- .../scala/tools/nsc/transform/UnCurry.scala | 11 ++++-- .../scala/tools/nsc/typechecker/Infer.scala | 28 +++++++++++++-- .../scala/tools/nsc/typechecker/Typers.scala | 41 ++++++++++++---------- test/files/neg/implicits.scala | 19 ++++++++++ test/files/pos/implicits.scala | 6 ++++ test/files/run/arrays.scala | 8 +++++ test/files/run/colltest1.scala | 2 ++ test/files/run/multi-array.scala | 1 + 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) } -- cgit v1.2.3