Skip to content

Commit 0501bfa

Browse files
committed
Refactorings and comments for better clarity
1 parent 72df855 commit 0501bfa

File tree

3 files changed

+96
-79
lines changed

3 files changed

+96
-79
lines changed

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ object ErrorReporting {
157157
|| expected.isRef(defn.ObjectClass)
158158
|| defn.isBottomType(found)
159159
then postScript
160-
else ctx.typer.implicitSuggestionsFor(ViewProto(found.widen, expected))
160+
else ctx.typer.implicitSuggestionAddendum(ViewProto(found.widen, expected))
161161
TypeMismatch(found2, expected2, whyNoMatchStr(found, expected), postScript1)
162162
}
163163

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

+93-76
Original file line numberDiff line numberDiff line change
@@ -695,15 +695,10 @@ trait Implicits { self: Typer =>
695695
}
696696
}
697697

698-
/** A helper class to find imports of givens that might fix a type error.
698+
/** A list of TermRefs referring to the roots where suggestions for
699+
* imports of givens that might fix a type error are searched.
699700
*
700-
* suggestions(p).search
701-
*
702-
* returns a list of TermRefs that refer to implicits or givens
703-
* that satisfy predicate `p`.
704-
*
705-
* The search algorithm looks for givens in the smallest set of objects
706-
* and packages that includes
701+
* These roots are the smallest set of objects and packages that includes
707702
*
708703
* - any object that is a defined in an enclosing scope,
709704
* - any object that is a member of an enclosing class,
@@ -712,11 +707,23 @@ trait Implicits { self: Typer =>
712707
* - any object or package from which something is imported in an enclosing scope,
713708
* - any package that is nested in a searched package, provided
714709
* the package was accessed in some way previously.
710+
*
711+
* Excluded from the root set are:
712+
*
713+
* - Objects that contain `$`s in their name. These have to
714+
* be omitted since they might be inner Java class files which
715+
* cannot be read by the ClassfileParser without crashing.
716+
* - Any members of static parts of Java classes.
717+
* - Any members of the empty package. These should be
718+
* skipped since the empty package often contains unrelated junk files
719+
* that should not be used for suggestions.
720+
* - Any members of the java or java.lang packages. These are
721+
* skipped as an optimization, since they won't contain implicits anyway.
715722
*/
716-
private class Suggestions(pt: Type) with
717-
private val seen = mutable.Set[TermRef]()
723+
private def suggestionRoots(given Context) =
724+
val seen = mutable.Set[TermRef]()
718725

719-
private def lookInside(root: Symbol)(given Context): Boolean =
726+
def lookInside(root: Symbol)(given Context): Boolean =
720727
if root.is(Package) then root.isTerm && root.isCompleted
721728
else !root.name.is(FlatName)
722729
&& !root.name.lastPart.contains('$')
@@ -732,7 +739,7 @@ trait Implicits { self: Typer =>
732739
}
733740
}
734741

735-
private def rootsStrictlyIn(ref: Type)(given Context): List[TermRef] =
742+
def rootsStrictlyIn(ref: Type)(given Context): List[TermRef] =
736743
val site = ref.widen
737744
val refSym = site.typeSymbol
738745
val nested =
@@ -751,18 +758,18 @@ trait Implicits { self: Typer =>
751758
.flatMap(rootsIn)
752759
.toList
753760

754-
private def rootsIn(ref: TermRef)(given Context): List[TermRef] =
761+
def rootsIn(ref: TermRef)(given Context): List[TermRef] =
755762
if seen.contains(ref) then Nil
756763
else
757764
implicits.println(i"search for suggestions in ${ref.symbol.fullName}")
758765
seen += ref
759766
ref :: rootsStrictlyIn(ref)
760767

761-
private def rootsOnPath(tp: Type)(given Context): List[TermRef] = tp match
768+
def rootsOnPath(tp: Type)(given Context): List[TermRef] = tp match
762769
case ref: TermRef => rootsIn(ref) ::: rootsOnPath(ref.prefix)
763770
case _ => Nil
764771

765-
private def roots(given ctx: Context): List[TermRef] =
772+
def recur(given ctx: Context): List[TermRef] =
766773
if ctx.owner.exists then
767774
val defined =
768775
if ctx.owner.isClass then
@@ -778,76 +785,86 @@ trait Implicits { self: Typer =>
778785
else ctx.importInfo.sym.info match
779786
case ImportType(expr) => rootsOnPath(expr.tpe)
780787
case _ => Nil
781-
defined ++ imported ++ roots(given ctx.outer)
788+
defined ++ imported ++ recur(given ctx.outer)
782789
else Nil
783790

784-
def search(given ctx: Context): (List[TermRef], List[TermRef]) =
785-
val timer = new Timer()
786-
val deadLine = System.currentTimeMillis() + suggestImplicitTimeOut
791+
recur
792+
end suggestionRoots
787793

788-
/** Test whether the head of a given instance matches the expected type `pt`,
789-
* ignoring any dependent implicit arguments.
790-
*/
791-
def shallowTest(ref: TermRef)(given Context): Boolean =
792-
System.currentTimeMillis < deadLine
793-
&& (ref <:< pt)(given ctx.fresh.setExploreTyperState())
794+
/** Given an expected type `pt`, return two lists of TermRefs:
795+
*
796+
* 1. The _fully matching_ given instances that can be completed
797+
* to a full synthesized given term that matches the expected type `pt`.
798+
*
799+
* 2. The _head matching_ given instances, that conform to the
800+
* expected type `pt`, ignoring any dependent implicit arguments.
801+
*/
802+
private def importSuggestions(pt: Type)(given ctx: Context): (List[TermRef], List[TermRef]) =
803+
val timer = new Timer()
804+
val deadLine = System.currentTimeMillis() + suggestImplicitTimeOut
794805

795-
/** Test whether a full given term can be synthesized that matches
796-
* the expected type `pt`.
797-
*/
798-
def deepTest(ref: TermRef)(given Context): Boolean =
799-
System.currentTimeMillis < deadLine
800-
&& {
801-
val task = new TimerTask with
802-
def run() =
803-
println(i"Cancelling test of $ref when making suggestions for error in ${ctx.source}")
804-
ctx.run.isCancelled = true
805-
val span = ctx.owner.sourcePos.span
806-
val (expectedType, argument, kind) = pt match
807-
case ViewProto(argType, resType) =>
808-
(resType,
809-
untpd.Ident(ref.name).withSpan(span).withType(argType),
810-
if hasExtMethod(ref, resType) then Candidate.Extension
811-
else Candidate.Conversion)
812-
case _ =>
813-
(pt, EmptyTree, Candidate.Value)
814-
val candidate = Candidate(ref, kind, 0)
815-
try
816-
timer.schedule(task, testOneImplicitTimeOut)
817-
typedImplicit(candidate, expectedType, argument, span)(
818-
given ctx.fresh.setExploreTyperState()).isSuccess
819-
finally
820-
task.cancel()
821-
ctx.run.isCancelled = false
822-
}
823-
end deepTest
824-
825-
try
826-
roots
827-
.filterNot(root => defn.RootImportTypes.exists(_.symbol == root.symbol))
828-
// don't suggest things that are imported by default
829-
.flatMap(_.implicitMembers.filter(shallowTest))
830-
// filter whether the head of the implicit can match
831-
.partition(deepTest)
832-
// partition into full matches and head matches
833-
catch
834-
case ex: Throwable =>
835-
if ctx.settings.Ydebug.value then
836-
println("caught exception when searching for suggestions")
837-
ex.printStackTrace()
838-
(Nil, Nil)
839-
finally timer.cancel()
840-
end search
841-
end Suggestions
806+
/** Test whether the head of a given instance matches the expected type `pt`,
807+
* ignoring any dependent implicit arguments.
808+
*/
809+
def shallowTest(ref: TermRef)(given Context): Boolean =
810+
System.currentTimeMillis < deadLine
811+
&& (ref <:< pt)(given ctx.fresh.setExploreTyperState())
812+
813+
/** Test whether a full given term can be synthesized that matches
814+
* the expected type `pt`.
815+
*/
816+
def deepTest(ref: TermRef)(given Context): Boolean =
817+
System.currentTimeMillis < deadLine
818+
&& {
819+
val task = new TimerTask with
820+
def run() =
821+
println(i"Cancelling test of $ref when making suggestions for error in ${ctx.source}")
822+
ctx.run.isCancelled = true
823+
val span = ctx.owner.sourcePos.span
824+
val (expectedType, argument, kind) = pt match
825+
case ViewProto(argType, resType) =>
826+
(resType,
827+
untpd.Ident(ref.name).withSpan(span).withType(argType),
828+
if hasExtMethod(ref, resType) then Candidate.Extension
829+
else Candidate.Conversion)
830+
case _ =>
831+
(pt, EmptyTree, Candidate.Value)
832+
val candidate = Candidate(ref, kind, 0)
833+
try
834+
timer.schedule(task, testOneImplicitTimeOut)
835+
typedImplicit(candidate, expectedType, argument, span)(
836+
given ctx.fresh.setExploreTyperState()).isSuccess
837+
finally
838+
task.cancel()
839+
ctx.run.isCancelled = false
840+
}
841+
end deepTest
842+
843+
try
844+
suggestionRoots
845+
.filterNot(root => defn.RootImportTypes.exists(_.symbol == root.symbol))
846+
// don't suggest things that are imported by default
847+
.flatMap(_.implicitMembers.filter(shallowTest))
848+
// filter whether the head of the implicit can match
849+
.partition(deepTest)
850+
// partition into full matches and head matches
851+
catch
852+
case ex: Throwable =>
853+
if ctx.settings.Ydebug.value then
854+
println("caught exception when searching for suggestions")
855+
ex.printStackTrace()
856+
(Nil, Nil)
857+
finally timer.cancel()
858+
end importSuggestions
842859

843860
/** An addendum to an error message where the error might be fixed
844861
* by some implicit value of type `pt` that is however not found.
845862
* The addendum suggests given imports that might fix the problem.
846863
* If there's nothing to suggest, an empty string is returned.
847864
*/
848-
override def implicitSuggestionsFor(pt: Type)(given ctx: Context): String =
865+
override def implicitSuggestionAddendum(pt: Type)(given ctx: Context): String =
849866
val (fullMatches, headMatches) =
850-
Suggestions(pt).search(given ctx.fresh.setExploreTyperState())
867+
importSuggestions(pt)(given ctx.fresh.setExploreTyperState())
851868
implicits.println(i"suggestions for $pt in ${ctx.owner} = ($fullMatches%, %, $headMatches%, %)")
852869
val (suggestedRefs, help) =
853870
if fullMatches.nonEmpty then (fullMatches, "fix")
@@ -874,7 +891,7 @@ trait Implicits { self: Typer =>
874891
|
875892
|$suggestions%\n%
876893
"""
877-
end implicitSuggestionsFor
894+
end implicitSuggestionAddendum
878895

879896
/** Handlers to synthesize implicits for special types */
880897
type SpecialHandler = (Type, Span) => Context => Tree
@@ -1435,7 +1452,7 @@ trait Implicits { self: Typer =>
14351452
// example where searching for a nested type causes an infinite loop.
14361453
""
14371454

1438-
def suggestedImports = implicitSuggestionsFor(pt)
1455+
def suggestedImports = implicitSuggestionAddendum(pt)
14391456
if normalImports.isEmpty then suggestedImports else normalImports
14401457
end hiddenImplicitsAddendum
14411458

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -283,15 +283,15 @@ trait TypeAssigner {
283283
|If you do not want that, insert a `;` or empty line in front
284284
|or drop any spaces behind the operator."""
285285
else
286-
implicitSuggestionsFor(
286+
implicitSuggestionAddendum(
287287
ViewProto(qualType.widen,
288288
SelectionProto(name, WildcardType, NoViewsAllowed, privateOK = false)))
289289
}
290290
errorType(NotAMember(qualType, name, kind, addendum), tree.sourcePos)
291291
}
292292
}
293293

294-
def implicitSuggestionsFor(pt: Type)(given Context): String = ""
294+
def implicitSuggestionAddendum(pt: Type)(given Context): String = ""
295295

296296
/** The type of the selection in `tree`, where `qual1` is the typed qualifier part.
297297
* The selection type is additionally checked for accessibility.

0 commit comments

Comments
 (0)