Skip to content

Commit d70b8f4

Browse files
authored
Merge pull request #2991 from dotty-staging/topic/new-repl
Add native Dotty REPL
2 parents bb2f409 + 331892e commit d70b8f4

File tree

106 files changed

+2382
-2255
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

106 files changed

+2382
-2255
lines changed

build.sbt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ val `dotty-bench-bootstrapped` = Build.`dotty-bench-bootstrapped`
1818
val `scala-library` = Build.`scala-library`
1919
val `scala-compiler` = Build.`scala-compiler`
2020
val `scala-reflect` = Build.`scala-reflect`
21+
val `dotty-repl` = Build.`dotty-repl`
2122
val scalap = Build.scalap
2223
val dist = Build.dist
2324
val `dist-bootstrapped` = Build.`dist-bootstrapped`

compiler/src/dotty/tools/dotc/Bench.scala

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,6 @@ object Bench extends Driver {
1616

1717
@sharable private var numRuns = 1
1818

19-
def newCompiler(implicit ctx: Context): Compiler = new Compiler
20-
2119
private def ntimes(n: Int)(op: => Reporter): Reporter =
2220
(emptyReporter /: (0 until n)) ((_, _) => op)
2321

compiler/src/dotty/tools/dotc/Compiler.scala

Lines changed: 1 addition & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -116,41 +116,13 @@ class Compiler {
116116
runId += 1; runId
117117
}
118118

119-
/** Produces the following contexts, from outermost to innermost
120-
*
121-
* bootStrap: A context with next available runId and a scope consisting of
122-
* the RootPackage _root_
123-
* start A context with RootClass as owner and the necessary initializations
124-
* for type checking.
125-
* imports For each element of RootImports, an import context
126-
*/
127-
def rootContext(implicit ctx: Context): Context = {
128-
ctx.initialize()(ctx)
129-
ctx.setPhasePlan(phases)
130-
val rootScope = new MutableScope
131-
val bootstrap = ctx.fresh
132-
.setPeriod(Period(nextRunId, FirstPhaseId))
133-
.setScope(rootScope)
134-
rootScope.enter(ctx.definitions.RootPackage)(bootstrap)
135-
val start = bootstrap.fresh
136-
.setOwner(defn.RootClass)
137-
.setTyper(new Typer)
138-
.addMode(Mode.ImplicitsEnabled)
139-
.setTyperState(new MutableTyperState(ctx.typerState, ctx.typerState.reporter, isCommittable = true))
140-
.setFreshNames(new FreshNameCreator.Default)
141-
ctx.initialize()(start) // re-initialize the base context with start
142-
def addImport(ctx: Context, refFn: () => TermRef) =
143-
ctx.fresh.setImportInfo(ImportInfo.rootImport(refFn)(ctx))
144-
(start.setRunInfo(new RunInfo(start)) /: defn.RootImportFns)(addImport)
145-
}
146-
147119
def reset()(implicit ctx: Context): Unit = {
148120
ctx.base.reset()
149121
ctx.runInfo.clear()
150122
}
151123

152124
def newRun(implicit ctx: Context): Run = {
153125
reset()
154-
new Run(this)(rootContext)
126+
new Run(this, ctx)
155127
}
156128
}

compiler/src/dotty/tools/dotc/Driver.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@ import scala.util.control.NonFatal
1313
* process, but in most cases you only need to call [[process]] on the
1414
* existing object [[Main]].
1515
*/
16-
abstract class Driver extends DotClass {
16+
class Driver extends DotClass {
1717

18-
protected def newCompiler(implicit ctx: Context): Compiler
18+
protected def newCompiler(implicit ctx: Context): Compiler = new Compiler
1919

2020
protected def emptyReporter: Reporter = new StoreReporter(null)
2121

compiler/src/dotty/tools/dotc/FromTasty.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,11 +45,11 @@ object FromTasty extends Driver {
4545

4646
override def newRun(implicit ctx: Context): Run = {
4747
reset()
48-
new TASTYRun(this)(rootContext)
48+
new TASTYRun(this, ctx)
4949
}
5050
}
5151

52-
class TASTYRun(comp: Compiler)(implicit ctx: Context) extends Run(comp) {
52+
class TASTYRun(comp: Compiler, ictx: Context) extends Run(comp, ictx) {
5353
override def compile(classNames: List[String]) = {
5454
units = classNames.map(new TASTYCompilationUnit(_))
5555
compileUnits()

compiler/src/dotty/tools/dotc/Main.scala

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,4 @@ package dotc
44
import core.Contexts.Context
55

66
/** Main class of the `dotc` batch compiler. */
7-
object Main extends Driver {
8-
override def newCompiler(implicit ctx: Context): Compiler = new Compiler
9-
}
7+
object Main extends Driver

compiler/src/dotty/tools/dotc/Resident.scala

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,6 @@ class Resident extends Driver {
2727

2828
object residentCompiler extends Compiler
2929

30-
override def newCompiler(implicit ctx: Context): Compiler = ???
31-
3230
override def sourcesRequired = false
3331

3432
private val quit = ":q"

compiler/src/dotty/tools/dotc/Run.scala

Lines changed: 47 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ import Contexts._
66
import Periods._
77
import Symbols._
88
import Phases._
9+
import Types._
10+
import Scopes._
11+
import typer.{FrontEnd, Typer, ImportInfo, RefChecks}
912
import Decorators._
1013
import dotty.tools.dotc.transform.TreeTransforms.TreeTransformer
1114
import io.PlainFile
@@ -22,12 +25,41 @@ import dotty.tools.io.VirtualFile
2225
import scala.util.control.NonFatal
2326

2427
/** A compiler run. Exports various methods to compile source files */
25-
class Run(comp: Compiler)(implicit ctx: Context) {
26-
27-
assert(ctx.runId <= Periods.MaxPossibleRunId)
28+
class Run(comp: Compiler, ictx: Context) {
2829

2930
var units: List[CompilationUnit] = _
3031

32+
/** Produces the following contexts, from outermost to innermost
33+
*
34+
* bootStrap: A context with next available runId and a scope consisting of
35+
* the RootPackage _root_
36+
* start A context with RootClass as owner and the necessary initializations
37+
* for type checking.
38+
* imports For each element of RootImports, an import context
39+
*/
40+
protected[this] def rootContext(implicit ctx: Context): Context = {
41+
ctx.initialize()(ctx)
42+
ctx.setPhasePlan(comp.phases)
43+
val rootScope = new MutableScope
44+
val bootstrap = ctx.fresh
45+
.setPeriod(Period(comp.nextRunId, FirstPhaseId))
46+
.setScope(rootScope)
47+
rootScope.enter(ctx.definitions.RootPackage)(bootstrap)
48+
val start = bootstrap.fresh
49+
.setOwner(defn.RootClass)
50+
.setTyper(new Typer)
51+
.addMode(Mode.ImplicitsEnabled)
52+
.setTyperState(new MutableTyperState(ctx.typerState, ctx.typerState.reporter, isCommittable = true))
53+
.setFreshNames(new FreshNameCreator.Default)
54+
ctx.initialize()(start) // re-initialize the base context with start
55+
def addImport(ctx: Context, refFn: () => TermRef) =
56+
ctx.fresh.setImportInfo(ImportInfo.rootImport(refFn)(ctx))
57+
(start.setRunInfo(new RunInfo(start)) /: defn.RootImportFns)(addImport)
58+
}
59+
60+
protected[this] implicit val ctx: Context = rootContext(ictx)
61+
assert(ctx.runId <= Periods.MaxPossibleRunId)
62+
3163
def getSource(fileName: String): SourceFile = {
3264
val f = new PlainFile(fileName)
3365
if (f.isDirectory) {
@@ -63,7 +95,17 @@ class Run(comp: Compiler)(implicit ctx: Context) {
6395
compileUnits()
6496
}
6597

66-
protected def compileUnits() = Stats.monitorHeartBeat {
98+
def compileUnits(us: List[CompilationUnit]): Unit = {
99+
units = us
100+
compileUnits()
101+
}
102+
103+
def compileUnits(us: List[CompilationUnit], ctx: Context): Unit = {
104+
units = us
105+
compileUnits()(ctx)
106+
}
107+
108+
protected def compileUnits()(implicit ctx: Context) = Stats.monitorHeartBeat {
67109
ctx.checkSingleThreaded()
68110

69111
// If testing pickler, make sure to stop after pickling phase:
@@ -76,7 +118,7 @@ class Run(comp: Compiler)(implicit ctx: Context) {
76118
ctx.usePhases(phases)
77119
var lastPrintedTree: PrintedTree = NoPrintedTree
78120
for (phase <- ctx.allPhases)
79-
if (!ctx.reporter.hasErrors) {
121+
if (phase.isRunnable) {
80122
val start = System.currentTimeMillis
81123
units = phase.runOn(units)
82124
if (ctx.settings.Xprint.value.containsPhase(phase)) {

compiler/src/dotty/tools/dotc/core/Definitions.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,7 @@ class Definitions {
251251
lazy val Any_getClass = enterMethod(AnyClass, nme.getClass_, MethodType(Nil, ClassClass.typeRef.appliedTo(TypeBounds.empty)), Final)
252252
lazy val Any_isInstanceOf = enterT1ParameterlessMethod(AnyClass, nme.isInstanceOf_, _ => BooleanType, Final)
253253
lazy val Any_asInstanceOf = enterT1ParameterlessMethod(AnyClass, nme.asInstanceOf_, TypeParamRef(_, 0), Final)
254-
lazy val Any_typeTest = enterT1ParameterlessMethod(AnyClass, nme.isInstanceOfPM, _ => BooleanType, Final)
254+
lazy val Any_typeTest = enterT1ParameterlessMethod(AnyClass, nme.isInstanceOfPM, _ => BooleanType, Final | Synthetic)
255255
// generated by pattern matcher, eliminated by erasure
256256

257257
def AnyMethods = List(Any_==, Any_!=, Any_equals, Any_hashCode,
@@ -863,8 +863,8 @@ class Definitions {
863863
)
864864

865865
val PredefImportFns = List[() => TermRef](
866-
() => ScalaPredefModuleRef,
867-
() => DottyPredefModuleRef
866+
() => ScalaPredefModuleRef,
867+
() => DottyPredefModuleRef
868868
)
869869

870870
lazy val RootImportFns =

compiler/src/dotty/tools/dotc/core/NameOps.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,8 @@ object NameOps {
6464
def isConstructorName = name == CONSTRUCTOR || name == TRAIT_CONSTRUCTOR
6565
def isStaticConstructorName = name == STATIC_CONSTRUCTOR
6666
def isLocalDummyName = name startsWith str.LOCALDUMMY_PREFIX
67-
def isReplWrapperName = name.toString contains str.INTERPRETER_IMPORT_WRAPPER
67+
def isReplWrapperName = name.toString contains str.REPL_SESSION_LINE
68+
def isReplAssignName = name.toString contains str.REPL_ASSIGN_SUFFIX
6869
def isSetterName = name endsWith str.SETTER_SUFFIX
6970
def isScala2LocalSuffix = testSimple(_.endsWith(" "))
7071
def isSelectorName = testSimple(n => n.startsWith("_") && n.drop(1).forall(_.isDigit))

compiler/src/dotty/tools/dotc/core/Phases.scala

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,11 +272,16 @@ object Phases {
272272
*/
273273
def phaseName: String
274274

275+
def isRunnable(implicit ctx: Context): Boolean =
276+
!ctx.reporter.hasErrors
277+
275278
/** List of names of phases that should precede this phase */
276279
def runsAfter: Set[Class[_ <: Phase]] = Set.empty
277280

281+
/** @pre `isRunnable` returns true */
278282
def run(implicit ctx: Context): Unit
279283

284+
/** @pre `isRunnable` returns true */
280285
def runOn(units: List[CompilationUnit])(implicit ctx: Context): List[CompilationUnit] =
281286
units.map { unit =>
282287
val unitCtx = ctx.fresh.setPhase(this.start).setCompilationUnit(unit)

compiler/src/dotty/tools/dotc/core/StdNames.scala

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,10 @@ object StdNames {
3030
final val ANON_CLASS = "$anon"
3131
final val ANON_FUN = "$anonfun"
3232

33-
final val INTERPRETER_IMPORT_WRAPPER = "$iw"
34-
final val INTERPRETER_LINE_PREFIX = "line"
35-
final val INTERPRETER_VAR_PREFIX = "res"
36-
final val INTERPRETER_WRAPPER_SUFFIX = "$object"
33+
final val REPL_SESSION_LINE = "rs$line$"
34+
final val REPL_ASSIGN_SUFFIX = "$assign"
35+
final val REPL_RES_PREFIX = "res"
36+
3737
final val MODULE_INSTANCE_FIELD = "MODULE$"
3838

3939
final val Function = "Function"

compiler/src/dotty/tools/dotc/core/SymDenotations.scala

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1994,7 +1994,9 @@ object SymDenotations {
19941994
}
19951995

19961996
def isValidAt(phase: Phase)(implicit ctx: Context) =
1997-
createdAt.runId == ctx.runId && sameGroup(ctx.phases(createdAt.phaseId), phase)
1997+
createdAt.runId == ctx.runId &&
1998+
createdAt.phaseId < ctx.phases.length &&
1999+
sameGroup(ctx.phases(createdAt.phaseId), phase)
19982000
}
19992001

20002002
private class InvalidCache extends InheritedCache {

compiler/src/dotty/tools/dotc/core/Types.scala

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,14 @@ object Types {
154154
false
155155
}
156156

157+
def isInfixType(implicit ctx: Context): Boolean = this match {
158+
case TypeApplications.AppliedType(tycon, args) =>
159+
args.length == 2 &&
160+
!Character.isUnicodeIdentifierStart(tycon.typeSymbol.name.toString.head)
161+
// TODO: Once we use the 2.12 stdlib, also check the @showAsInfix annotation
162+
case _ => false
163+
}
164+
157165
/** Does this type refer exactly to class symbol `sym`, instead of to a subclass of `sym`?
158166
* Implemented like `isRef`, but follows more types: all type proxies as well as and- and or-types
159167
*/

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

Lines changed: 34 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -65,35 +65,51 @@ object Interactive {
6565
private def safely[T](op: => List[T]): List[T] =
6666
try op catch { case ex: TypeError => Nil }
6767

68-
/** Possible completions at position `pos` */
69-
def completions(trees: List[SourceTree], pos: SourcePosition)(implicit ctx: Context): List[Symbol] = {
68+
/** Get possible completions from tree at `pos`
69+
*
70+
* @return offset and list of symbols for possible completions
71+
*/
72+
def completions(trees: List[SourceTree], pos: SourcePosition)(implicit ctx: Context): (Int, List[Symbol]) = {
7073
val path = pathTo(trees, pos)
7174
val boundary = enclosingDefinitionInPath(path).symbol
7275

73-
path.take(1).flatMap {
74-
case Select(qual, _) =>
76+
// FIXME: Get all declarations available in the current scope, not just
77+
// those from the enclosing class
78+
def scopeCompletions: List[Symbol] =
79+
boundary.enclosingClass match {
80+
case csym: ClassSymbol =>
81+
val classRef = csym.classInfo.typeRef
82+
completions(classRef, boundary)
83+
case _ =>
84+
Nil
85+
}
86+
87+
path.headOption.map {
88+
case sel @ Select(qual, name) =>
7589
// When completing "`a.foo`, return the members of `a`
76-
completions(qual.tpe, boundary)
90+
(sel.pos.point, completions(qual.tpe, boundary))
91+
case id @ Ident(name) =>
92+
(id.pos.point, scopeCompletions)
7793
case _ =>
78-
// FIXME: Get all declarations available in the current scope, not just
79-
// those from the enclosing class
80-
boundary.enclosingClass match {
81-
case csym: ClassSymbol =>
82-
val classRef = csym.classInfo.typeRef
83-
completions(classRef, boundary)
84-
case _ =>
85-
Nil
86-
}
94+
(0, scopeCompletions)
8795
}
96+
.getOrElse((0, Nil))
8897
}
8998

9099
/** Possible completions of members of `prefix` which are accessible when called inside `boundary` */
91100
def completions(prefix: Type, boundary: Symbol)(implicit ctx: Context): List[Symbol] =
92101
safely {
93-
val boundaryCtx = ctx.withOwner(boundary)
94-
prefix.memberDenots(completionsFilter, (name, buf) =>
95-
buf ++= prefix.member(name).altsWith(d => !d.isAbsent && d.symbol.isAccessibleFrom(prefix)(boundaryCtx))
96-
).map(_.symbol).toList
102+
if (boundary != NoSymbol) {
103+
val boundaryCtx = ctx.withOwner(boundary)
104+
prefix.memberDenots(completionsFilter, (name, buf) =>
105+
buf ++= prefix.member(name).altsWith{ d =>
106+
!d.isAbsent &&
107+
!d.is(Synthetic) && !d.is(Artifact) &&
108+
d.symbol.isAccessibleFrom(prefix)(boundaryCtx)
109+
}
110+
).map(_.symbol).toList
111+
}
112+
else Nil
97113
}
98114

99115
/** Filter for names that should appear when looking for completions. */

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

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,6 @@ class InteractiveDriver(settings: List[String]) extends Driver {
2828
import tpd._
2929
import InteractiveDriver._
3030

31-
// FIXME: Change the Driver API to not require implementing this method
32-
override protected def newCompiler(implicit ctx: Context): Compiler = ???
3331
override def sourcesRequired = false
3432

3533
private val myInitCtx: Context = {

compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -125,23 +125,15 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
125125
("implicit " provided isImplicit) ~ argStr ~ " => " ~ argText(args.last)
126126
}
127127

128-
def isInfixType(tp: Type): Boolean = tp match {
129-
case AppliedType(tycon, args) =>
130-
args.length == 2 &&
131-
!Character.isUnicodeIdentifierStart(tycon.typeSymbol.name.toString.head)
132-
// TODO: Once we use the 2.12 stdlib, also check the @showAsInfix annotation
133-
case _ =>
134-
false
135-
}
136128
def toTextInfixType(op: Type, args: List[Type]): Text = {
137129
/* SLS 3.2.8: all infix types have the same precedence.
138130
* In A op B op' C, op and op' need the same associativity.
139131
* Therefore, if op is left associative, anything on its right
140132
* needs to be parenthesized if it's an infix type, and vice versa. */
141133
val l :: r :: Nil = args
142134
val isRightAssoc = op.typeSymbol.name.endsWith(":")
143-
val leftArg = if (isRightAssoc && isInfixType(l)) "(" ~ toText(l) ~ ")" else toText(l)
144-
val rightArg = if (!isRightAssoc && isInfixType(r)) "(" ~ toText(r) ~ ")" else toText(r)
135+
val leftArg = if (isRightAssoc && l.isInfixType) "(" ~ toText(l) ~ ")" else toText(l)
136+
val rightArg = if (!isRightAssoc && r.isInfixType) "(" ~ toText(r) ~ ")" else toText(r)
145137

146138
leftArg ~ " " ~ toTextLocal(op) ~ " " ~ rightArg
147139
}
@@ -154,7 +146,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
154146
if (tycon.isRepeatedParam) return toTextLocal(args.head) ~ "*"
155147
if (defn.isFunctionClass(cls)) return toTextFunction(args, cls.name.isImplicitFunction)
156148
if (defn.isTupleClass(cls)) return toTextTuple(args)
157-
if (isInfixType(tp)) return toTextInfixType(tycon, args)
149+
if (tp.isInfixType) return toTextInfixType(tycon, args)
158150
case EtaExpansion(tycon) =>
159151
return toText(tycon)
160152
case tp: TypeRef =>

0 commit comments

Comments
 (0)