-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Fix #4230: Handle import in the REPL correctly #4915
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
@@ -0,0 +1,30 @@ | |||
package dotty.tools.repl |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe put it in repl.transform ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Even if that's the only one?
*/ | ||
class ReplCompiler extends Compiler { | ||
override protected def frontendPhases: List[List[Phase]] = | ||
Phases.replace(classOf[FrontEnd], _ => new REPLFrontEnd :: Nil, super.frontendPhases) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think keeping Phases.replace would be cleaner, it just needs to be passed a list of list of phases as a replacement.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That would be trickier. I need to replace FrontEnd
by ReplFrontend
, remove sbt.ExtractDependencies
and sbt.ExtractAPI
and finally add CollectTopLevelImports
which needs to run after ReplFrontEnd
but in its own group
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah indeed, not worth it then
I have a pickling issue:
I am not sure how to debug that. cc/ @smarter @nicolasstucki |
I'll have a look tomorrow |
I think I found the place where it happens in 3402a29 (5cbb512 needs to be reverted, sorry). To debug it you should try writing 3402a29#diff-c7dacf9ef97cabd025fede32386ffdecR269 in some other smaller class, then you should be able to reproduce it with |
@allanrenucci in a01be64 I added a test that reproduces the issue. You can minimize it from that test file. |
@nicolasstucki Thanks! |
40da6e6
to
4ab0a5e
Compare
With this new scheme I still cannot handle: scala> object A { def f = 1 }
// defined object A
scala> import A._; def f = 2
def f: Int
scala> f
val res0: Int = 1 // should probably be 2 The scalac REPL handles it properly, but they use a complex object nesting scheme that we don't want to replicate |
@@ -329,7 +329,7 @@ object Interactive { | |||
|
|||
def contextOfPath(path: List[Tree])(implicit ctx: Context): Context = path match { | |||
case Nil | _ :: Nil => | |||
ctx.run.runContext.fresh.setCompilationUnit(ctx.compilationUnit) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@odersky Was this intentional? If so, what's the rational for using the run context?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pretty sure that's intentional. contextOfPath
constructs a context with the correct scoping information. If you start from a random Context then you'll have too much stuff in the scope.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I could patch the rootContext
of the new run as we used to do:
class ReplCompiler extends Compiler {
def newRun(initCtx: Context, state: State) = new Run(this, initCtx) {
override protected[this] def rootContext(implicit ctx: Context) =
addMagicImports(super.rootContext)
private def addMagicImports(initCtx: Context): Context = ...
}
}
This way I wouldn't need to modify this line
class CollectTopLevelImports extends Phase { | ||
import tpd._ | ||
|
||
def phaseName = "collecttoplevelimports" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we usually use camelCase.
* custom subclass of `GenBCode`. The custom `GenBCode`, `REPLGenBCode`, works | ||
* in conjunction with a specialized class loader in order to load virtual | ||
* classfiles. | ||
* Specifically it replaces the front end with `REPLFrontEnd`. | ||
*/ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would just delete this whole comment, it doesn't really say anything interesting. (it could be replaced by an actual description of what REPLCompiler does though :))
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually #4915 (comment) needs to be addressed.
Can you open an issue for that? |
e40d390
to
2548813
Compare
@smarter comments addressed |
Will do once this is merged |
We used to collect user defined imports from the parsed tree and insert them as untyped trees at the top of the REPL wrapper object. This caused members shadowing issues. We now introduce a phase in the REPL compiler that collects imports after type checking and store them as typed tree. We can then create a context with its imports set in the correct order and use it to compile future expressions. This also fixes scala#4978 by having user defined import in the run context. Auto-completions somehow ignored them when they were part of the untyped tree.
2548813
to
52a604c
Compare
We used to collect imports from the parsed tree and insert them as
parsed trees at the top of the REPL wrapper object. This caused members
shadowing issues.
We now introduce a phase in the REPL compiler that collects imports
after type checking and store them as typed tree. We can then create a
context with its imports set in the correct order and use it to compile
future expressions.