diff options
author | Jason Zaugg <jzaugg@gmail.com> | 2015-02-17 15:21:03 +1000 |
---|---|---|
committer | Jason Zaugg <jzaugg@gmail.com> | 2015-02-17 15:53:20 +1000 |
commit | a8ec6c97ea7edbf25ffffe2e798b2deedcd5955f (patch) | |
tree | e5070955dd45536a1804606af9d6d380de4c2036 /src/compiler | |
parent | 5a89475526fc91f677d7002b6063bef9e549f0cc (diff) | |
download | scala-a8ec6c97ea7edbf25ffffe2e798b2deedcd5955f.tar.gz scala-a8ec6c97ea7edbf25ffffe2e798b2deedcd5955f.tar.bz2 scala-a8ec6c97ea7edbf25ffffe2e798b2deedcd5955f.zip |
SI-9153 More complete and stable results for completions
Three items of background are needed to understand this bug.
1. When typechecking an application like `qual.m({stats; expr})`, the
argument is typechecked using the formal parameter type of `m` as the
expected type. If this fails with a type error located within in
`expr`, the typer instead re-typechecks under `ContextMode.ReTyping`
without an expected type, and then searches for an implicit adaptation
to enable `view(qual).m(args)`. Under this mode, `Typer#typed1`
clears the type of incoming trees.
2. The presentation compiler performs targetted operations like
type completions by:
- typechecking the enclosing tree, registering all typechecker
`Context`s created in the process (`registerContext`)
- finding the smallest enclosing `Context` around the target
position (`doLocateContext`)
- Using this context to perform implicit search, which can
contribute members to the completion. (`applicableViews` within
`interactive.Global#typeMembers`)
3. When verifiying whether or not a candidate implicit is applicable
as a view from `F => T`, implicit search typechecks a dummy call
of the form `q"candiate(${Ident("<argument>").setType(typeOf[F])})".
Now, picture yourself at the nexus of these three storms.
In the enclosed test case, we search for completions at:
x + 1.<caret>
1. Because the code is incomplete, the application of `Int#+`
doesn't typecheck, and the typer also tries to adapt `x` to a
method applicable to the re-typechecked argument.
2. This process registers a context with `retypechecking` set to
true. (If multiple contexts at the same position are registered,
the last one wins.)
3. Implicit search uses this context to typecheck
`Predef.Ensuring(<argument>.setType(Int))`, but the argument
is promptly stripped of its type and retypechecking fails
as there is no definition named `<argument>` in scope.
As such, we missed out on extension methods, like `ensuring` in the
list of completions.
This commit changes the presentation compiler to turn off retyping
mode in the context before starting to work with it. (Are the other
modes that might cause similar bugs?)
Once I made that change, I noticed that the results the enclosed test
was not stable. I tracked this down to the use of a `HashMap` to
carry the applicable implicit views, together with the way that
the presentation compiler removes duplicates. This commit
switched to a `LinkedHashMap`.
Diffstat (limited to 'src/compiler')
-rw-r--r-- | src/compiler/scala/tools/nsc/typechecker/Implicits.scala | 4 |
1 files changed, 2 insertions, 2 deletions
diff --git a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala index 0861d58240..d3cd26f256 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala @@ -918,7 +918,7 @@ trait Implicits { /** Returns all eligible ImplicitInfos and their SearchResults in a map. */ - def findAll() = mapFrom(eligible)(typedImplicit(_, ptChecked = false, isLocalToCallsite)) + def findAll() = linkedMapFrom(eligible)(typedImplicit(_, ptChecked = false, isLocalToCallsite)) /** Returns the SearchResult of the best match. */ @@ -963,7 +963,7 @@ trait Implicits { * symbols of the same name in succeeding lists. * @return map from infos to search results */ - def applicableInfos(iss: Infoss, isLocalToCallsite: Boolean): Map[ImplicitInfo, SearchResult] = { + def applicableInfos(iss: Infoss, isLocalToCallsite: Boolean): mutable.LinkedHashMap[ImplicitInfo, SearchResult] = { val start = if (Statistics.canEnable) Statistics.startCounter(subtypeAppInfos) else null val computation = new ImplicitComputation(iss, isLocalToCallsite) { } val applicable = computation.findAll() |