Skip to content

Commit d8028de

Browse files
committed
WIP: Try out some alternatives
1 parent d06d5ae commit d8028de

File tree

4 files changed

+269
-43
lines changed

4 files changed

+269
-43
lines changed

compiler/src/dotty/tools/dotc/core/TyperState.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ class TyperState(previous: TyperState /* | Null */) extends DotClass with Showab
8989
private[this] var testReporter: StoreReporter = null
9090

9191
/** Test using `op`, restoring typerState to previous state afterwards */
92-
def test(op: => Boolean): Boolean = {
92+
def test[T](op: => T): T = {
9393
val savedReporter = myReporter
9494
val savedConstraint = myConstraint
9595
val savedCommittable = myIsCommittable

compiler/src/dotty/tools/dotc/typer/Applications.scala

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1031,6 +1031,22 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
10311031
tp.member(nme.apply).hasAltWith(d => p(TermRef(tp, nme.apply, d)))
10321032
}
10331033

1034+
/** Compare owner inheritance level.
1035+
* @param sym1 The first owner
1036+
* @param sym2 The second owner
1037+
* @return 1 if `sym1` properly derives from `sym2`
1038+
* -1 if `sym2` properly derives from `sym1`
1039+
* 0 otherwise
1040+
* Module classes also inherit the relationship from their companions.
1041+
*/
1042+
def compareOwner(sym1: Symbol, sym2: Symbol)(implicit ctx: Context): Int =
1043+
if (sym1 == sym2) 0
1044+
else if (sym1 isSubClass sym2) 1
1045+
else if (sym2 isSubClass sym1) -1
1046+
else if (sym2 is Module) compareOwner(sym1, sym2.companionClass)
1047+
else if (sym1 is Module) compareOwner(sym1.companionClass, sym2)
1048+
else 0
1049+
10341050
/** In a set of overloaded applicable alternatives, is `alt1` at least as good as
10351051
* `alt2`? Also used for implicits disambiguation.
10361052
*
@@ -1050,22 +1066,6 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
10501066

10511067
assert(alt1 ne alt2)
10521068

1053-
/** Compare owner inheritance level.
1054-
* @param sym1 The first owner
1055-
* @param sym2 The second owner
1056-
* @return 1 if `sym1` properly derives from `sym2`
1057-
* -1 if `sym2` properly derives from `sym1`
1058-
* 0 otherwise
1059-
* Module classes also inherit the relationship from their companions.
1060-
*/
1061-
def compareOwner(sym1: Symbol, sym2: Symbol): Int =
1062-
if (sym1 == sym2) 0
1063-
else if (sym1 isSubClass sym2) 1
1064-
else if (sym2 isSubClass sym1) -1
1065-
else if (sym2 is Module) compareOwner(sym1, sym2.companionClass)
1066-
else if (sym1 is Module) compareOwner(sym1.companionClass, sym2)
1067-
else 0
1068-
10691069
/** Is alternative `alt1` with type `tp1` as specific as alternative
10701070
* `alt2` with type `tp2` ?
10711071
*

compiler/src/dotty/tools/dotc/typer/Implicits.scala

Lines changed: 156 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ import reporting.diagnostic.{Message, MessageContainer}
3030
import Inferencing.fullyDefinedType
3131
import Trees._
3232
import Hashable._
33-
import util.Property
33+
import util.{Property, PriorityGraph}
3434
import config.Config
3535
import config.Printers.{implicits, implicitsDetailed, typr}
3636
import collection.mutable
@@ -257,6 +257,10 @@ object Implicits {
257257
sealed abstract class SearchResult extends Showable {
258258
def tree: tpd.Tree
259259
def toText(printer: Printer): Text = printer.toText(this)
260+
def orElse(other: => SearchResult) = this match {
261+
case _: SearchSuccess => this
262+
case _ => other
263+
}
260264
}
261265

262266
/** A successful search
@@ -316,9 +320,9 @@ object Implicits {
316320
SearchFailure(NoMatchingImplicits)
317321

318322
/** An ambiguous implicits failure */
319-
class AmbiguousImplicits(val alt1: TermRef, val alt2: TermRef, val expectedType: Type, val argument: tpd.Tree) extends SearchFailureType {
323+
class AmbiguousImplicits(val alt1: SearchSuccess, val alt2: SearchSuccess, val expectedType: Type, val argument: tpd.Tree) extends SearchFailureType {
320324
def explanation(implicit ctx: Context): String =
321-
em"both ${err.refStr(alt1)} and ${err.refStr(alt2)} $qualify"
325+
em"both ${err.refStr(alt1.ref)} and ${err.refStr(alt2.ref)} $qualify"
322326
override def whyNoConversion(implicit ctx: Context) =
323327
"\nNote that implicit conversions cannot be applied because they are ambiguous;" +
324328
"\n " + explanation
@@ -750,17 +754,19 @@ trait Implicits { self: Typer =>
750754

751755
/** An implicit search; parameters as in `inferImplicit` */
752756
class ImplicitSearch(protected val pt: Type, protected val argument: Tree, pos: Position)(implicit ctx: Context) {
757+
assert(argument.isEmpty || argument.tpe.isValueType || argument.tpe.isInstanceOf[ExprType],
758+
em"found: $argument: ${argument.tpe}, expected: $pt")
753759

754-
private def nestedContext = ctx.fresh.setMode(ctx.mode &~ Mode.ImplicitsEnabled)
760+
private def nestedContext() = ctx.fresh.setMode(ctx.mode &~ Mode.ImplicitsEnabled)
755761

756762
private def implicitProto(resultType: Type, f: Type => Type) =
757763
if (argument.isEmpty) f(resultType) else ViewProto(f(argument.tpe.widen), f(resultType))
758764
// Not clear whether we need to drop the `.widen` here. All tests pass with it in place, though.
759765

760766
private def isCoherent = pt.isRef(defn.EqClass)
761767

762-
assert(argument.isEmpty || argument.tpe.isValueType || argument.tpe.isInstanceOf[ExprType],
763-
em"found: $argument: ${argument.tpe}, expected: $pt")
768+
private val cmpContext = nestedContext()
769+
private val cmpCandidates = (c1: Candidate, c2: Candidate) => compare(c1.ref, c2.ref, c1.level, c2.level)(cmpContext)
764770

765771
/** The expected type for the searched implicit */
766772
lazy val fullProto = implicitProto(pt, identity)
@@ -778,6 +784,8 @@ trait Implicits { self: Typer =>
778784
def searchImplicits(eligible: List[Candidate], contextual: Boolean): SearchResult = {
779785
val constr = ctx.typerState.constraint
780786

787+
//println(i"search implicits $pt / ${eligible.map(_.ref)}")
788+
781789
/** Try to typecheck an implicit reference */
782790
def typedImplicit(cand: Candidate)(implicit ctx: Context): SearchResult = track("typedImplicit") { trace(i"typed implicit ${cand.ref}, pt = $pt, implicitsEnabled == ${ctx.mode is ImplicitsEnabled}", implicits, show = true) {
783791
assert(constr eq ctx.typerState.constraint)
@@ -790,7 +798,7 @@ trait Implicits { self: Typer =>
790798
val generated1 = adapt(generated, pt)
791799
lazy val shadowing =
792800
typed(untpd.Ident(ref.name) withPos pos.toSynthetic, funProto)(
793-
nestedContext.addMode(Mode.ImplicitShadowing).setExploreTyperState())
801+
nestedContext().addMode(Mode.ImplicitShadowing).setExploreTyperState())
794802
def refSameAs(shadowing: Tree): Boolean =
795803
ref.symbol == closureBody(shadowing).symbol || {
796804
shadowing match {
@@ -820,6 +828,126 @@ trait Implicits { self: Typer =>
820828
SearchSuccess(generated1, ref, cand.level)(ctx.typerState)
821829
}}
822830

831+
def tryImplicit(cand: Candidate): SearchResult = {
832+
val history = ctx.searchHistory nest wildProto
833+
if (history eq ctx.searchHistory)
834+
SearchFailure(new DivergingImplicit(cand.ref, pt, argument))
835+
else
836+
typedImplicit(cand)(nestedContext().setNewTyperState().setSearchHistory(history))
837+
}
838+
839+
def tryInOrder(cand1: Candidate, cand2: Candidate) =
840+
tryImplicit(cand1) match {
841+
case success: SearchSuccess => success
842+
case _ => tryImplicit(cand2)
843+
}
844+
845+
def compareCandidate(prev: SearchSuccess, ref: TermRef, level: Int): Int =
846+
if (prev.ref eq ref) 0
847+
else ctx.typerState.test(compare(prev.ref, ref, prev.level, level)(nestedContext()))
848+
849+
def disambiguate(alt1: SearchResult, alt2: SearchSuccess) = alt1 match {
850+
case alt1: SearchSuccess =>
851+
def isNumeric(tp: Type) = tp.typeSymbol.isNumericValueClass
852+
def isProperSubType(tp1: Type, tp2: Type) =
853+
tp1.isValueSubType(tp2) && !tp2.isValueSubType(tp1)
854+
val rpt = pt.resultType
855+
val rt1 = alt1.ref.widen.resultType
856+
val rt2 = alt2.ref.widen.resultType
857+
def ambi = SearchFailure(new AmbiguousImplicits(alt1, alt2, pt, argument))
858+
val diff = compareCandidate(alt1, alt2.ref, alt2.level)
859+
assert(diff <= 0)
860+
if (diff < 0) alt2
861+
else if (isNumeric(rpt) && isNumeric(rt1) && isNumeric(rt2))
862+
if (isProperSubType(rt1, rt2)) alt2
863+
else if (isProperSubType(rt2, rt1)) alt1
864+
else ambi
865+
else ambi
866+
case _: SearchFailure => alt2
867+
}
868+
869+
def rank(pending: List[Candidate], found: SearchResult, rfailures: List[SearchFailure]): SearchResult = pending match {
870+
case cand :: pending1 =>
871+
tryImplicit(cand) match {
872+
case fail: SearchFailure =>
873+
if (fail.isAmbiguous) healAmbiguous(pending1, fail)
874+
else rank(pending1, found, fail :: rfailures)
875+
case best: SearchSuccess =>
876+
if (ctx.mode.is(Mode.ImplicitExploration) || isCoherent)
877+
best
878+
else disambiguate(found, best) match {
879+
case retained: SearchSuccess =>
880+
val newPending =
881+
if (retained eq found) pending1
882+
else pending1.filter(cand =>
883+
compareCandidate(retained, cand.ref, cand.level) <= 0)
884+
rank(newPending, retained, rfailures)
885+
case fail: SearchFailure =>
886+
healAmbiguous(pending1, fail)
887+
}
888+
}
889+
case nil =>
890+
if (rfailures.isEmpty) found
891+
else found.orElse(rfailures.reverse.maxBy(_.tree.treeSize))
892+
}
893+
894+
def healAmbiguous(pending: List[Candidate], fail: SearchFailure) = {
895+
val ambi = fail.reason.asInstanceOf[AmbiguousImplicits]
896+
val newPending = pending.filter(cand =>
897+
compareCandidate(ambi.alt1, cand.ref, cand.level) < 0 &&
898+
compareCandidate(ambi.alt2, cand.ref, cand.level) < 0)
899+
rank(newPending, fail, Nil).orElse(fail)
900+
}
901+
902+
def go: SearchResult = eligible match {
903+
case Nil =>
904+
SearchFailure(new NoMatchingImplicits(pt, argument))
905+
case cand :: Nil =>
906+
tryImplicit(cand)
907+
case cand1 :: cand2 :: Nil =>
908+
def compareTwo = cmpCandidates(cand1, cand2) match {
909+
case 1 => tryInOrder(cand1, cand2)
910+
case -1 => tryInOrder(cand2, cand1)
911+
case 0 =>
912+
val alt1 = tryImplicit(cand1)
913+
val alt2 = tryImplicit(cand2)
914+
alt2 match {
915+
case alt2: SearchSuccess => disambiguate(alt1, alt2)
916+
case _ =>
917+
alt1 match {
918+
case alt1: SearchSuccess => alt1
919+
case _ => if (alt1.tree.treeSize < alt2.tree.treeSize) alt2 else alt1
920+
}
921+
}
922+
}
923+
compareTwo
924+
case _ =>
925+
val cands = eligible.toArray
926+
val pg = new PriorityGraph(cands, cmpCandidates)
927+
928+
def loop(found: SearchResult, rfailures: List[SearchFailure]): SearchResult =
929+
if (pg.hasNextSource())
930+
tryImplicit(cands(pg.nextSource())) match {
931+
case fail: SearchFailure =>
932+
if (fail.isAmbiguous) fail
933+
else {
934+
pg.dropLastSource()
935+
loop(found, fail :: rfailures)
936+
}
937+
case best: SearchSuccess =>
938+
if (ctx.mode.is(Mode.ImplicitExploration) || isCoherent)
939+
best
940+
else disambiguate(found, best) match {
941+
case retained: SearchSuccess => loop(retained, rfailures)
942+
case ambi => ambi
943+
}
944+
}
945+
else if (found.isInstanceOf[SearchSuccess]) found
946+
else rfailures.reverse.maxBy(_.tree.treeSize)
947+
948+
loop(NoMatchingImplicitsFailure, Nil)
949+
}
950+
823951
/** Given a list of implicit references, produce a list of search results,
824952
* which is either a list of successes or a list of failures.
825953
* - if one of the references produces an ambiguity error, return it
@@ -842,7 +970,7 @@ trait Implicits { self: Typer =>
842970
if (history eq ctx.searchHistory)
843971
SearchFailure(new DivergingImplicit(cand.ref, pt, argument))
844972
else
845-
typedImplicit(cand)(nestedContext.setNewTyperState().setSearchHistory(history))
973+
typedImplicit(cand)(nestedContext().setNewTyperState().setSearchHistory(history))
846974
result match {
847975
case fail: SearchFailure =>
848976
if (fail.isAmbiguous) fail :: Nil
@@ -852,7 +980,7 @@ trait Implicits { self: Typer =>
852980
best :: Nil
853981
else {
854982
val newPending = pending1.filter(cand1 =>
855-
ctx.typerState.test(isAsGood(cand1.ref, best.ref, cand1.level, best.level)(nestedContext)))
983+
ctx.typerState.test(isAsGood(cand1.ref, best.ref, cand1.level, best.level)(nestedContext())))
856984
rankImplicits(newPending, best :: successes, rfailures)
857985
}
858986
}
@@ -901,7 +1029,7 @@ trait Implicits { self: Typer =>
9011029
case eliminated: SearchSuccess =>
9021030
condense(results.filter(_ ne eliminated))
9031031
case _ =>
904-
SearchFailure(new AmbiguousImplicits(best.ref, alt.ref, pt, argument))
1032+
SearchFailure(new AmbiguousImplicits(best, alt, pt, argument))
9051033
}
9061034
case None =>
9071035
ctx.runInfo.useCount(best.ref) += 1
@@ -920,21 +1048,20 @@ trait Implicits { self: Typer =>
9201048
* `cand1` has been selected as an implicit more often than `cand2`.
9211049
*/
9221050
def prefer(cand1: Candidate, cand2: Candidate): Boolean = {
923-
/*val sym1 = cand1.ref.symbol
1051+
val level1 = cand1.level
1052+
val level2 = cand2.level
1053+
if (level1 > level2) return true
1054+
if (level1 < level2) return false
1055+
val sym1 = cand1.ref.symbol
9241056
val sym2 = cand2.ref.symbol
925-
if (sym1.associatedFile == sym2.associatedFile) {
926-
val coord1 = sym1.coord
927-
val coord2 = sym2.coord
928-
if (coord1.isPosition && coord2.isPosition) {
929-
val pos1 = coord1.toPosition
930-
val pos2 = coord2.toPosition
931-
return pos1.exists && (!pos2.exists || pos1.start < pos2.start)
932-
}
933-
if (coord1.isIndex && coord2.isIndex)
934-
return coord1.toIndex < coord2.toIndex
935-
}
936-
*/
937-
ranking(cand1) < ranking(cand2)
1057+
val ownerScore = compareOwner(sym1.maybeOwner, sym2.maybeOwner)
1058+
if (ownerScore > 0) return true
1059+
if (ownerScore < 0) return false
1060+
val arity1 = sym1.info.firstParamTypes.length
1061+
val arity2 = sym2.info.firstParamTypes.length
1062+
if (arity1 < arity2) return true
1063+
if (arity1 > arity2) return false
1064+
false
9381065
}
9391066

9401067
/** Sort list of implicit references according to their popularity
@@ -946,10 +1073,13 @@ trait Implicits { self: Typer =>
9461073
case e1 :: e2 :: Nil =>
9471074
if (prefer(e2, e1)) e2 :: e1 :: Nil
9481075
else eligible
949-
case _ => eligible.sortWith(prefer)
1076+
case _ =>
1077+
eligible.sortWith(prefer)
9501078
}
9511079

952-
condense(rankImplicits(sort(eligible), Nil, Nil))
1080+
if (true) rank(sort(eligible), NoMatchingImplicitsFailure, Nil)
1081+
else if (true) go
1082+
else condense(rankImplicits(sort(eligible), Nil, Nil))
9531083
}
9541084

9551085
/** Find a unique best implicit reference */

0 commit comments

Comments
 (0)