Skip to content
This repository was archived by the owner on Sep 1, 2020. It is now read-only.

Commit 53d98e7

Browse files
committed
refined implicit resolution.
1 parent 32cac0e commit 53d98e7

File tree

9 files changed

+93
-25
lines changed

9 files changed

+93
-25
lines changed

src/compiler/scala/tools/nsc/transform/Erasure.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -609,7 +609,7 @@ abstract class Erasure extends AddInterfaces with typechecker.Analyzer with ast.
609609
* @param pt ...
610610
* @return the adapted tree
611611
*/
612-
override protected def adapt(tree: Tree, mode: Int, pt: Type): Tree =
612+
override protected def adapt(tree: Tree, mode: Int, pt: Type, original: Tree = EmptyTree): Tree =
613613
adaptToType(tree, pt)
614614

615615
/** A replacement for the standard typer's `typed1' method */

src/compiler/scala/tools/nsc/transform/UnCurry.scala

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -410,17 +410,22 @@ abstract class UnCurry extends InfoTransform with TypingTransformers {
410410
}
411411
}
412412

413+
val lastElemType = lastFormal.typeArgs.head
413414
var suffix: Tree =
414415
if (!args.isEmpty && (treeInfo isWildcardStarArg args.last)) {
415416
val Typed(tree, _) = args.last;
416-
if (isJava && !(tree.tpe.typeSymbol == ArrayClass) && (tree.tpe.typeSymbol isSubClass TraversableClass)) sequenceToArray(tree)
417-
else tree
417+
if (isJava)
418+
if (tree.tpe.typeSymbol == ArrayClass) tree
419+
else sequenceToArray(tree)
420+
else
421+
if (tree.tpe.typeSymbol isSubClass TraversableClass) tree
422+
else arrayToSequence(tree, lastElemType)
418423
} else {
419-
val lastElemType = lastFormal.typeArgs.head
420424
val tree = mkArrayValue(args drop (formals.length - 1), lastElemType)
421425
if (isJava || inPattern) tree
422426
else arrayToSequence(tree, lastElemType)
423427
}
428+
424429
atPhase(phase.next) {
425430
if (isJava &&
426431
suffix.tpe.typeSymbol == ArrayClass &&

src/compiler/scala/tools/nsc/typechecker/Infer.scala

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -451,6 +451,19 @@ trait Infer {
451451
tp.isInstanceOf[MethodType] && // can perform implicit () instantiation
452452
tp.paramTypes.length == 0 && isCompatible(tp.resultType, pt)
453453

454+
/** Like weakly compatible but don't apply any implicit conversions yet.
455+
* Used when comparing the result type of a method with its prototype.
456+
*/
457+
def isConservativelyCompatible(tp: Type, pt: Type): Boolean = {
458+
val savedImplicitsEnabled = context.implicitsEnabled
459+
context.implicitsEnabled = false
460+
try {
461+
isWeaklyCompatible(tp, pt)
462+
} finally {
463+
context.implicitsEnabled = savedImplicitsEnabled
464+
}
465+
}
466+
454467
def isCoercible(tp: Type, pt: Type): Boolean = false
455468

456469
def isCompatible(tps: List[Type], pts: List[Type]): Boolean =
@@ -556,7 +569,7 @@ trait Infer {
556569
case ex: NoInstance => WildcardType
557570
}
558571
val tvars = tparams map freshVar
559-
if (isWeaklyCompatible(restpe.instantiateTypeParams(tparams, tvars), pt))
572+
if (isConservativelyCompatible(restpe.instantiateTypeParams(tparams, tvars), pt))
560573
List.map2(tparams, tvars) ((tparam, tvar) =>
561574
instantiateToBound(tvar, varianceInTypes(formals)(tparam)))
562575
else
@@ -611,7 +624,7 @@ trait Infer {
611624
}
612625
// check first whether type variables can be fully defined from
613626
// expected result type.
614-
if (!isWeaklyCompatible(restpe.instantiateTypeParams(tparams, tvars), pt)) {
627+
if (!isConservativelyCompatible(restpe.instantiateTypeParams(tparams, tvars), pt)) {
615628
// just wait and instantiate from the arguments.
616629
// that way, we can try to apply an implicit conversion afterwards.
617630
// This case could happen if restpe is not fully defined, so that
@@ -821,9 +834,13 @@ trait Infer {
821834
case OverloadedType(pre, alts) =>
822835
alts exists (alt => isAsSpecific(pre.memberType(alt), ftpe2))
823836
case et: ExistentialType =>
824-
et.withTypeVars(isAsSpecific(_, ftpe2)) // !!! why isStrictly?
837+
et.withTypeVars(isAsSpecific(_, ftpe2))
838+
case mt: ImplicitMethodType =>
839+
isAsSpecific(ftpe1.resultType, ftpe2)
825840
case MethodType(params @ (x :: xs), _) =>
826841
isApplicable(List(), ftpe2, params map (_.tpe), WildcardType)
842+
case PolyType(tparams, mt: ImplicitMethodType) =>
843+
isAsSpecific(PolyType(tparams, mt.resultType), ftpe2)
827844
case PolyType(_, MethodType(params @ (x :: xs), _)) =>
828845
isApplicable(List(), ftpe2, params map (_.tpe), WildcardType)
829846
case ErrorType =>
@@ -834,12 +851,17 @@ trait Infer {
834851
alts forall (alt => isAsSpecific(ftpe1, pre.memberType(alt)))
835852
case et: ExistentialType =>
836853
et.withTypeVars(isAsSpecific(ftpe1, _))
854+
case mt: ImplicitMethodType =>
855+
isAsSpecific(ftpe1, mt.resultType)
856+
case PolyType(tparams, mt: ImplicitMethodType) =>
857+
isAsSpecific(ftpe1, PolyType(tparams, mt.resultType))
837858
case MethodType(_, _) | PolyType(_, MethodType(_, _)) =>
838859
true
839860
case _ =>
840861
isAsSpecificValueType(ftpe1, ftpe2, List(), List())
841862
}
842863
}
864+
843865
/*
844866
def isStrictlyMoreSpecific(ftpe1: Type, ftpe2: Type): Boolean =
845867
ftpe1.isError || isAsSpecific(ftpe1, ftpe2) &&

src/compiler/scala/tools/nsc/typechecker/Typers.scala

Lines changed: 23 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -238,13 +238,12 @@ trait Typers { self: Analyzer =>
238238
psym = to.decls enter psym
239239
psym setInfo tp
240240
try {
241-
inferView(tree, from, to, true)
241+
inferView(tree, from, to, true)
242242
} catch {
243243
case ex: AssertionError =>
244-
println("infer view "+tree+" "+name+" "+context.undetparams)
244+
println("inverView "+tree+", from = "+from+", name = "+name+" tp = "+tp)
245245
throw ex
246246
}
247-
248247
}
249248

250249
import infer._
@@ -762,25 +761,25 @@ trait Typers { self: Analyzer =>
762761
* (13) When in mode EXPRmode, apply a view
763762
* If all this fails, error
764763
*/
765-
protected def adapt(tree: Tree, mode: Int, pt: Type): Tree = tree.tpe match {
764+
protected def adapt(tree: Tree, mode: Int, pt: Type, original: Tree = EmptyTree): Tree = tree.tpe match {
766765
case atp @ AnnotatedType(_, _, _) if canAdaptAnnotations(tree, mode, pt) => // (-1)
767766
adaptAnnotations(tree, mode, pt)
768767
case ct @ ConstantType(value) if ((mode & (TYPEmode | FUNmode)) == 0 && (ct <:< pt) && !onlyPresentation) => // (0)
769768
treeCopy.Literal(tree, value)
770769
case OverloadedType(pre, alts) if ((mode & FUNmode) == 0) => // (1)
771770
inferExprAlternative(tree, pt)
772-
adapt(tree, mode, pt)
771+
adapt(tree, mode, pt, original)
773772
case PolyType(List(), restpe) => // (2)
774-
adapt(tree setType restpe, mode, pt)
773+
adapt(tree setType restpe, mode, pt, original)
775774
case TypeRef(_, sym, List(arg))
776775
if ((mode & EXPRmode) != 0 && sym == ByNameParamClass) => // (2)
777-
adapt(tree setType arg, mode, pt)
776+
adapt(tree setType arg, mode, pt, original)
778777
case tr @ TypeRef(_, sym, _)
779778
if sym.isAliasType && tr.normalize.isInstanceOf[ExistentialType] &&
780779
((mode & (EXPRmode | LHSmode)) == EXPRmode) =>
781-
adapt(tree setType tr.normalize.skolemizeExistential(context.owner, tree), mode, pt)
780+
adapt(tree setType tr.normalize.skolemizeExistential(context.owner, tree), mode, pt, original)
782781
case et @ ExistentialType(_, _) if ((mode & (EXPRmode | LHSmode)) == EXPRmode) =>
783-
adapt(tree setType et.skolemizeExistential(context.owner, tree), mode, pt)
782+
adapt(tree setType et.skolemizeExistential(context.owner, tree), mode, pt, original)
784783
case PolyType(tparams, restpe) if ((mode & (TAPPmode | PATTERNmode | HKmode)) == 0) => // (3)
785784
// assert((mode & HKmode) == 0) //@M a PolyType in HKmode represents an anonymous type function,
786785
// 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 =>
794793
else TypeApply(tree, tparams1 map (tparam =>
795794
TypeTree(tparam.tpe) setPos tree.pos.focus)) setPos tree.pos
796795
context.undetparams = context.undetparams ::: tparams1
797-
adapt(tree1 setType restpe.substSym(tparams, tparams1), mode, pt)
796+
adapt(tree1 setType restpe.substSym(tparams, tparams1), mode, pt, original)
798797
case mt: ImplicitMethodType if ((mode & (EXPRmode | FUNmode | LHSmode)) == EXPRmode) => // (4.1)
799798
if (!context.undetparams.isEmpty/* && (mode & POLYmode) == 0 disabled to make implicits in new collection work; we should revisit this. */) { // (9)
800799
context.undetparams = inferExprInstance(
@@ -805,7 +804,17 @@ trait Typers { self: Analyzer =>
805804
// so we need to instantiate to minimal type List[Nothing].
806805
}
807806
val typer1 = constrTyperIf(treeInfo.isSelfOrSuperConstrCall(tree))
808-
typer1.typed(typer1.applyImplicitArgs(tree), mode, pt)
807+
if (original != EmptyTree && pt != WildcardType)
808+
typer1.silent(tpr => tpr.typed(tpr.applyImplicitArgs(tree), mode, pt)) match {
809+
case result: Tree => result
810+
case ex: TypeError =>
811+
if (settings.debug.value) log("fallback on implicits: "+tree)
812+
val tree1 = typed(original, mode, WildcardType)
813+
tree1.tpe = addAnnotations(tree1, tree1.tpe)
814+
if (tree1.isEmpty) tree1 else adapt(tree1, mode, pt, EmptyTree)
815+
}
816+
else
817+
typer1.typed(typer1.applyImplicitArgs(tree), mode, pt)
809818
case mt: MethodType
810819
if (((mode & (EXPRmode | FUNmode | LHSmode)) == EXPRmode) &&
811820
(context.undetparams.isEmpty || (mode & POLYmode) != 0)) =>
@@ -825,7 +834,7 @@ trait Typers { self: Analyzer =>
825834
//println("eta "+tree+" ---> "+tree1+":"+tree1.tpe)
826835
typed(tree1, mode, pt)
827836
} else if (!meth.isConstructor && mt.params.isEmpty) { // (4.3)
828-
adapt(typed(Apply(tree, List()) setPos tree.pos), mode, pt)
837+
adapt(typed(Apply(tree, List()) setPos tree.pos), mode, pt, original)
829838
} else if (context.implicitsEnabled) {
830839
errorTree(tree, "missing arguments for "+meth+meth.locationString+
831840
(if (meth.isConstructor) ""
@@ -933,7 +942,7 @@ trait Typers { self: Analyzer =>
933942
return tree
934943
}
935944
val tree1 = constfold(tree, pt) // (10) (11)
936-
if (tree1.tpe <:< pt) adapt(tree1, mode, pt)
945+
if (tree1.tpe <:< pt) adapt(tree1, mode, pt, original)
937946
else {
938947
if ((mode & (EXPRmode | FUNmode)) == EXPRmode) {
939948
pt.normalize match {
@@ -2966,10 +2975,6 @@ trait Typers { self: Analyzer =>
29662975

29672976
/** Try to apply function to arguments; if it does not work try to
29682977
* insert an implicit conversion.
2969-
*
2970-
* @param fun ...
2971-
* @param args ...
2972-
* @return ...
29732978
*/
29742979
def tryTypedApply(fun: Tree, args: List[Tree]): Tree = {
29752980
val start = System.nanoTime()
@@ -3775,7 +3780,7 @@ trait Typers { self: Analyzer =>
37753780

37763781
tree1.tpe = addAnnotations(tree1, tree1.tpe)
37773782

3778-
val result = if (tree1.isEmpty) tree1 else adapt(tree1, mode, pt)
3783+
val result = if (tree1.isEmpty) tree1 else adapt(tree1, mode, pt, tree)
37793784
if (printTypings) println("adapted "+tree1+":"+tree1.tpe.widen+" to "+pt+", "+context.undetparams); //DEBUG
37803785
// for (t <- tree1.tpe) assert(t != WildcardType)
37813786
// if ((mode & TYPEmode) != 0) println("type: "+tree1+" has type "+tree1.tpe)

test/files/neg/implicits.scala

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,3 +37,22 @@ object test2 {
3737
def foo(x: Int) = 3
3838
foo(set)
3939
}
40+
41+
// #2180
42+
class Mxml {
43+
44+
private def processChildren( children:Seq[Any] ):List[Mxml] = {
45+
46+
children.toList.flatMap ( e => {
47+
48+
e match {
49+
50+
case s:scala.collection.Traversable[_] => s case a => List(a)
51+
52+
}
53+
54+
})
55+
56+
}
57+
58+
}

test/files/pos/implicits.scala

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,9 @@ object Test1625 {
3030
println("=> result: " + res)
3131
}
3232
}
33+
34+
object Test2188 {
35+
implicit def toJavaList[A: ClassManifest](t:collection.Sequence[A]):java.util.List[A] = java.util.Arrays.asList(t.toArray:_*)
36+
37+
val x: java.util.List[String] = List("foo")
38+
}

test/files/run/arrays.scala

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,12 @@ object Test {
158158
c(xs)
159159
}
160160

161+
def checkT2368() {
162+
val arr = Array(1, 2, 3)
163+
arr(0) += 1
164+
assert(arr(0) == 1)
165+
}
166+
161167
//##########################################################################
162168
// Values
163169

@@ -919,6 +925,7 @@ object Test {
919925

920926
checkZip
921927
checkConcat
928+
checkT2368()
922929

923930
//######################################################################
924931

@@ -929,3 +936,4 @@ object Test {
929936

930937
//##########################################################################
931938
}
939+

test/files/run/colltest1.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,8 @@ object Test extends Application {
127127
assert(ten.patch(0, List(1, 2, 3), 9) == List(1, 2, 3, 10))
128128
assert(empty.padTo(10, 7) == Array.fill(10)(7).toSequence)
129129
assert((ten zip ten.indices) == ten.zipWithIndex)
130+
assert(ten sortWith (_ < _) == ten)
131+
assert(ten sortWith (_ > _) == ten.reverse)
130132
}
131133

132134
def setTest(empty: => Set[String]) {

test/files/run/multi-array.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,5 @@ object Test extends Application {
1010
for (i <- 0 until 3; j <- 0 until 3)
1111
aaiComplete(i)(j) = i + j
1212
println(aaiComplete.deepToString)
13+
assert(aaiComplete.last.last == 4)
1314
}

0 commit comments

Comments
 (0)