Skip to content

Commit d93a8eb

Browse files
committed
Consider implicit conversions for possible completions
1 parent 518e9fd commit d93a8eb

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
@@ -833,64 +833,63 @@ trait Implicits { self: Typer =>
833833

834834
val isNot = wildProto.classSymbol == defn.NotClass
835835

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

842-
/** Try to typecheck an implicit reference */
843-
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) {
844-
assert(constr eq ctx.typerState.constraint)
845-
val ref = cand.ref
846-
var generated: Tree = tpd.ref(ref).withPos(pos.startPos)
847-
if (!argument.isEmpty)
848-
generated = typedUnadapted(
849-
untpd.Apply(untpd.TypedSplice(generated), untpd.TypedSplice(argument) :: Nil),
850-
pt)
851-
val generated1 = adapt(generated, pt)
852-
lazy val shadowing =
853-
typed(untpd.Ident(cand.implicitRef.implicitName) withPos pos.toSynthetic, funProto)(
854-
nestedContext().addMode(Mode.ImplicitShadowing).setExploreTyperState())
855-
def refSameAs(shadowing: Tree): Boolean =
856-
ref.symbol == closureBody(shadowing).symbol || {
857-
shadowing match {
858-
case Trees.Select(qual, nme.apply) => refSameAs(qual)
859-
case Trees.Apply(fn, _) => refSameAs(fn)
860-
case Trees.TypeApply(fn, _) => refSameAs(fn)
861-
case _ => false
862-
}
838+
/** Try to typecheck an implicit reference */
839+
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) {
840+
val ref = cand.ref
841+
var generated: Tree = tpd.ref(ref).withPos(pos.startPos)
842+
if (!argument.isEmpty)
843+
generated = typedUnadapted(
844+
untpd.Apply(untpd.TypedSplice(generated), untpd.TypedSplice(argument) :: Nil),
845+
pt)
846+
val generated1 = adapt(generated, pt)
847+
lazy val shadowing =
848+
typed(untpd.Ident(cand.implicitRef.implicitName) withPos pos.toSynthetic, funProto)(
849+
nestedContext().addMode(Mode.ImplicitShadowing).setExploreTyperState())
850+
def refSameAs(shadowing: Tree): Boolean =
851+
ref.symbol == closureBody(shadowing).symbol || {
852+
shadowing match {
853+
case Trees.Select(qual, nme.apply) => refSameAs(qual)
854+
case Trees.Apply(fn, _) => refSameAs(fn)
855+
case Trees.TypeApply(fn, _) => refSameAs(fn)
856+
case _ => false
863857
}
858+
}
864859

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

895894
/** Compare previous success with reference and level to determine which one would be chosen, if
896895
* an implicit starting with the reference was found.
@@ -963,7 +962,7 @@ trait Implicits { self: Typer =>
963962
def rank(pending: List[Candidate], found: SearchResult, rfailures: List[SearchFailure]): SearchResult =
964963
pending match {
965964
case cand :: remaining =>
966-
negateIfNot(tryImplicit(cand)) match {
965+
negateIfNot(tryImplicit(cand, contextual)) match {
967966
case fail: SearchFailure =>
968967
if (fail.isAmbiguous)
969968
if (ctx.scala2Mode) {
@@ -1078,6 +1077,15 @@ trait Implicits { self: Typer =>
10781077
}
10791078

10801079
def implicitScope(tp: Type): OfTypeImplicits = ctx.run.implicitScope(tp, ctx)
1080+
1081+
/** All available implicits, without ranking */
1082+
def allImplicits: Set[TermRef] = {
1083+
val contextuals = ctx.implicits.eligible(wildProto).map(tryImplicit(_, contextual = true))
1084+
val inscope = implicitScope(wildProto).eligible.map(tryImplicit(_, contextual = false))
1085+
(contextuals.toSet ++ inscope).collect {
1086+
case success: SearchSuccess => success.ref
1087+
}
1088+
}
10811089
}
10821090
}
10831091

0 commit comments

Comments
 (0)