Skip to content

Commit f7639cb

Browse files
committed
Consider implicit conversions for possible completions
1 parent 45c3ffe commit f7639cb

File tree

3 files changed

+77
-58
lines changed

3 files changed

+77
-58
lines changed

compiler/src/dotty/tools/dotc/interactive/Interactive.scala

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ object Interactive {
156156
* - do not contain '$' except in prefix where it is explicitly written by user
157157
* - have same term/type kind as name prefix given so far
158158
*/
159-
def include(sym: Symbol) =
159+
def include(sym: Symbol) =
160160
sym.name.startsWith(prefix) &&
161161
!sym.name.toString.drop(prefix.length).contains('$') &&
162162
(!termOnly || sym.isTerm) &&
@@ -182,6 +182,9 @@ object Interactive {
182182
}.filter(_.exists)
183183
}
184184

185+
def addAccessibleMembers(site: Type, superAccess: Boolean = true): Unit =
186+
for (mbr <- accessibleMembers(site)) addMember(site, mbr.name)
187+
185188
def getImportCompletions(ictx: Context): Unit = {
186189
implicit val ctx = ictx
187190
val imp = ctx.importInfo
@@ -221,12 +224,22 @@ object Interactive {
221224
if (outer `ne` NoContext) getScopeCompletions(outer)
222225
}
223226

224-
def getMemberCompletions(site: Type): Unit = {
225-
for (mbr <- accessibleMembers(site)) addMember(site, mbr.name)
227+
def implicitConversionTargets(qual: Tree)(implicit ctx: Context): Set[Type] = {
228+
val typer = ctx.typer
229+
val conversions = new typer.ImplicitSearch(defn.AnyType, qual, pos.pos).allImplicits
230+
val targets = conversions.map(_.widen.finalResultType)
231+
interactiv.println(i"implicit conversion targets considered: ${targets.toList}%, %")
232+
targets
233+
}
234+
235+
def getMemberCompletions(qual: Tree): Unit = {
236+
addAccessibleMembers(qual.tpe)
237+
implicitConversionTargets(qual)(ctx.fresh.setExploreTyperState())
238+
.foreach(addAccessibleMembers(_))
226239
}
227240

228241
path match {
229-
case (sel @ Select(qual, name)) :: _ => getMemberCompletions(qual.tpe)
242+
case (sel @ Select(qual, _)) :: _ => getMemberCompletions(qual)
230243
case _ => getScopeCompletions(ctx)
231244
}
232245
interactiv.println(i"completion with pos = $pos, prefix = $prefix, termOnly = $termOnly, typeOnly = $typeOnly = ${completions.toList}%, %")

compiler/src/dotty/tools/dotc/parsing/Parsers.scala

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,6 @@ object Parsers {
106106
def sourcePos(off: Int = in.offset): SourcePosition =
107107
source atPos Position(off)
108108

109-
110109
/* ------------- ERROR HANDLING ------------------------------------------- */
111110
/** The offset where the last syntax error was reported, or if a skip to a
112111
* safepoint occurred afterwards, the offset of the safe point.
@@ -128,7 +127,6 @@ object Parsers {
128127
*/
129128
def syntaxError(msg: => Message, pos: Position): Unit =
130129
ctx.error(msg, source atPos pos)
131-
132130
}
133131

134132
class Parser(source: SourceFile)(implicit ctx: Context) extends ParserCommon(source) {

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

Lines changed: 60 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -836,64 +836,63 @@ trait Implicits { self: Typer =>
836836

837837
val isNot = wildProto.classSymbol == defn.NotClass
838838

839-
/** Search a list of eligible implicit references */
840-
def searchImplicits(eligible: List[Candidate], contextual: Boolean): SearchResult = {
841-
val constr = ctx.typerState.constraint
842-
843839
//println(i"search implicits $pt / ${eligible.map(_.ref)}")
844840

845-
/** Try to typecheck an implicit reference */
846-
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) {
847-
assert(constr eq ctx.typerState.constraint)
848-
val ref = cand.ref
849-
var generated: Tree = tpd.ref(ref).withPos(pos.startPos)
850-
if (!argument.isEmpty)
851-
generated = typedUnadapted(
852-
untpd.Apply(untpd.TypedSplice(generated), untpd.TypedSplice(argument) :: Nil),
853-
pt)
854-
val generated1 = adapt(generated, pt)
855-
lazy val shadowing =
856-
typed(untpd.Ident(cand.implicitRef.implicitName) withPos pos.toSynthetic, funProto)(
857-
nestedContext().addMode(Mode.ImplicitShadowing).setExploreTyperState())
858-
def refSameAs(shadowing: Tree): Boolean =
859-
ref.symbol == closureBody(shadowing).symbol || {
860-
shadowing match {
861-
case Trees.Select(qual, nme.apply) => refSameAs(qual)
862-
case Trees.Apply(fn, _) => refSameAs(fn)
863-
case Trees.TypeApply(fn, _) => refSameAs(fn)
864-
case _ => false
865-
}
841+
/** Try to typecheck an implicit reference */
842+
def typedImplicit(cand: Candidate, contextual: Boolean)(implicit ctx: Context): SearchResult = track("typedImplicit") { trace(i"typed implicit ${cand.ref}, pt = $pt, implicitsEnabled == ${ctx.mode is ImplicitsEnabled}", implicits, show = true) {
843+
val ref = cand.ref
844+
var generated: Tree = tpd.ref(ref).withPos(pos.startPos)
845+
if (!argument.isEmpty)
846+
generated = typedUnadapted(
847+
untpd.Apply(untpd.TypedSplice(generated), untpd.TypedSplice(argument) :: Nil),
848+
pt)
849+
val generated1 = adapt(generated, pt)
850+
lazy val shadowing =
851+
typed(untpd.Ident(cand.implicitRef.implicitName) withPos pos.toSynthetic, funProto)(
852+
nestedContext().addMode(Mode.ImplicitShadowing).setExploreTyperState())
853+
def refSameAs(shadowing: Tree): Boolean =
854+
ref.symbol == closureBody(shadowing).symbol || {
855+
shadowing match {
856+
case Trees.Select(qual, nme.apply) => refSameAs(qual)
857+
case Trees.Apply(fn, _) => refSameAs(fn)
858+
case Trees.TypeApply(fn, _) => refSameAs(fn)
859+
case _ => false
866860
}
861+
}
867862

868-
if (ctx.reporter.hasErrors) {
869-
ctx.reporter.removeBufferedMessages
870-
SearchFailure {
871-
generated1.tpe match {
872-
case _: SearchFailureType => generated1
873-
case _ => generated1.withType(new MismatchedImplicit(ref, pt, argument))
874-
}
863+
if (ctx.reporter.hasErrors) {
864+
ctx.reporter.removeBufferedMessages
865+
SearchFailure {
866+
generated1.tpe match {
867+
case _: SearchFailureType => generated1
868+
case _ => generated1.withType(new MismatchedImplicit(ref, pt, argument))
875869
}
876870
}
877-
else if (contextual && !ctx.mode.is(Mode.ImplicitShadowing) &&
878-
!shadowing.tpe.isError && !refSameAs(shadowing)) {
879-
implicits.println(i"SHADOWING $ref in ${ref.termSymbol.maybeOwner} is shadowed by $shadowing in ${shadowing.symbol.maybeOwner}")
880-
SearchFailure(generated1.withTypeUnchecked(
881-
new ShadowedImplicit(ref, methPart(shadowing).tpe, pt, argument)))
882-
}
883-
else
884-
SearchSuccess(generated1, ref, cand.level)(ctx.typerState)
885-
}}
886-
887-
/** Try to type-check implicit reference, after checking that this is not
888-
* a diverging search
889-
*/
890-
def tryImplicit(cand: Candidate): SearchResult = {
891-
val history = ctx.searchHistory nest wildProto
892-
if (history eq ctx.searchHistory)
893-
SearchFailure(new DivergingImplicit(cand.ref, pt, argument))
894-
else
895-
typedImplicit(cand)(nestedContext().setNewTyperState().setSearchHistory(history))
896871
}
872+
else if (contextual && !ctx.mode.is(Mode.ImplicitShadowing) &&
873+
!shadowing.tpe.isError && !refSameAs(shadowing)) {
874+
implicits.println(i"SHADOWING $ref in ${ref.termSymbol.maybeOwner} is shadowed by $shadowing in ${shadowing.symbol.maybeOwner}")
875+
SearchFailure(generated1.withTypeUnchecked(
876+
new ShadowedImplicit(ref, methPart(shadowing).tpe, pt, argument)))
877+
}
878+
else
879+
SearchSuccess(generated1, ref, cand.level)(ctx.typerState)
880+
}}
881+
882+
/** Try to type-check implicit reference, after checking that this is not
883+
* a diverging search
884+
*/
885+
def tryImplicit(cand: Candidate, contextual: Boolean): SearchResult = {
886+
val history = ctx.searchHistory nest wildProto
887+
if (history eq ctx.searchHistory)
888+
SearchFailure(new DivergingImplicit(cand.ref, pt, argument))
889+
else
890+
typedImplicit(cand, contextual)(nestedContext().setNewTyperState().setSearchHistory(history))
891+
}
892+
893+
/** Search a list of eligible implicit references */
894+
def searchImplicits(eligible: List[Candidate], contextual: Boolean): SearchResult = {
895+
val constr = ctx.typerState.constraint
897896

898897
/** Compare previous success with reference and level to determine which one would be chosen, if
899898
* an implicit starting with the reference was found.
@@ -966,7 +965,7 @@ trait Implicits { self: Typer =>
966965
def rank(pending: List[Candidate], found: SearchResult, rfailures: List[SearchFailure]): SearchResult =
967966
pending match {
968967
case cand :: remaining =>
969-
negateIfNot(tryImplicit(cand)) match {
968+
negateIfNot(tryImplicit(cand, contextual)) match {
970969
case fail: SearchFailure =>
971970
if (fail.isAmbiguous)
972971
if (ctx.scala2Mode) {
@@ -1081,6 +1080,15 @@ trait Implicits { self: Typer =>
10811080
}
10821081

10831082
def implicitScope(tp: Type): OfTypeImplicits = ctx.run.implicitScope(tp, ctx)
1083+
1084+
/** All available implicits, without ranking */
1085+
def allImplicits: Set[TermRef] = {
1086+
val contextuals = ctx.implicits.eligible(wildProto).map(tryImplicit(_, contextual = true))
1087+
val inscope = implicitScope(wildProto).eligible.map(tryImplicit(_, contextual = false))
1088+
(contextuals.toSet ++ inscope).collect {
1089+
case success: SearchSuccess => success.ref
1090+
}
1091+
}
10841092
}
10851093
}
10861094

0 commit comments

Comments
 (0)