From 41dc7225084c735f0306e0ebb1a9da0fbd9886ff Mon Sep 17 00:00:00 2001 From: Lex Spoon Date: Mon, 18 Feb 2008 21:22:08 +0000 Subject: Generalizes the allowed inputs to the interpret... Generalizes the allowed inputs to the interpreter to be any combination of phrases that can be placed inside a template. Instead of having one "request" object try to understand the entire combination of phrases, the code now uses multiple "member handlers" for each request, one member handler for each phrase. --- src/compiler/scala/tools/nsc/Interpreter.scala | 512 ++++++++++----------- src/compiler/scala/tools/nsc/symtab/StdNames.scala | 1 + test/files/run/interpreter.check | 22 +- test/files/run/interpreter.scala | 8 + 4 files changed, 269 insertions(+), 274 deletions(-) diff --git a/src/compiler/scala/tools/nsc/Interpreter.scala b/src/compiler/scala/tools/nsc/Interpreter.scala index be954205f2..1833ff1065 100644 --- a/src/compiler/scala/tools/nsc/Interpreter.scala +++ b/src/compiler/scala/tools/nsc/Interpreter.scala @@ -10,6 +10,7 @@ import java.io.{File, PrintWriter, StringWriter, Writer} import java.lang.{Class, ClassLoader} import java.net.{URL, URLClassLoader} +import scala.collection.immutable.ListSet import scala.collection.mutable import scala.collection.mutable.{ListBuffer, HashSet, ArrayBuffer} @@ -78,6 +79,8 @@ class Interpreter(val settings: Settings, out: PrintWriter) { import compiler.CompilationUnit import compiler.{Symbol,Name,Type} import compiler.nme + import compiler.newTermName + import compiler.nme.{INTERPRETER_VAR_PREFIX, INTERPRETER_SYNTHVAR_PREFIX} import Interpreter.string2code /** construct an interpreter that reports to Console */ @@ -181,9 +184,27 @@ class Interpreter(val settings: Settings, out: PrintWriter) { private def newVarName() = { val num = nextVarNameNo nextVarNameNo += 1 - compiler.nme.INTERPRETER_VAR_PREFIX + num + INTERPRETER_VAR_PREFIX + num } + /** next internal variable number to use */ + private var nextInternalVarNo = 0 + + /** allocate a fresh internal variable name */ + private def newInternalVarName() = { + val num = nextVarNameNo + nextVarNameNo += 1 + INTERPRETER_SYNTHVAR_PREFIX + num + } + + + /** Check if a name looks like it was generated by newVarName */ + private def isGeneratedVarName(name: String): Boolean = + name.startsWith(INTERPRETER_VAR_PREFIX) && { + val suffix = name.drop(INTERPRETER_VAR_PREFIX.length) + suffix.forall(_.isDigit) + } + /** generate a string using a routine that wants to write on a stream */ private def stringFrom(writer: PrintWriter => Unit): String = { @@ -259,34 +280,31 @@ class Interpreter(val settings: Settings, out: PrintWriter) { * should be taken. Removes requests which cannot contribute * useful imports for the specified set of wanted names. */ - def reqsToUse: List[Request] = { - /** Loop through the requests in reverse and select + def reqsToUse: List[(Request,MemberHandler)] = { + /** Loop through a list of MemberHandlers and select * which ones to keep. 'wanted' is the set of * names that need to be imported, and * 'shadowed' is the list of names useless to import * because a later request will re-import it anyway. */ - def select(reqs: List[Request], wanted: Set[Name]): List[Request] = { + def select(reqs: List[(Request,MemberHandler)], wanted: Set[Name]): + List[(Request,MemberHandler)] = { reqs match { case Nil => Nil - case req::rest => - val keepit = req.definesImplicit || (req match { - case req:ImportReq => - req.importsWildcard || - req.importedNames.exists(wanted.contains) - case _ => - req.boundNames.exists(wanted.contains) - }) + case (req,handler)::rest => + val keepit = + (handler.definesImplicit || + handler.importsWildcard || + handler.importedNames.exists(wanted.contains(_)) || + handler.boundNames.exists(wanted.contains(_))) val newWanted = if (keepit) { - req match { - case req:ImportReq => - wanted -- req.importedNames ++ req.usedNames - - case _ => wanted -- req.boundNames - } + (wanted + ++ handler.usedNames + -- handler.boundNames + -- handler.importedNames) } else { wanted } @@ -294,13 +312,18 @@ class Interpreter(val settings: Settings, out: PrintWriter) { val restToKeep = select(rest, newWanted) if(keepit) - req :: restToKeep + (req,handler) :: restToKeep else restToKeep } } - select(prevRequests.toList.reverse, wanted).reverse + val rhpairs = for { + req <- prevRequests.toList.reverse + handler <- req.handlers + } yield (req, handler) + + select(rhpairs, wanted).reverse } val code = new StringBuffer @@ -321,40 +344,37 @@ class Interpreter(val settings: Settings, out: PrintWriter) { // loop through previous requests, adding imports // for each one - for (req <- reqsToUse) { - req match { - case req:ImportReq => + for ((req,handler) <- reqsToUse) { // If the user entered an import, then just use it // add an import wrapping level if the import might // conflict with some other import - if(req.importsWildcard || - currentImps.exists(req.importedNames.contains)) + if(handler.importsWildcard || + currentImps.exists(handler.importedNames.contains)) if(!currentImps.isEmpty) addWrapper() - code.append(req.line + ";\n") + if (handler.member.isInstanceOf[Import]) + code.append(handler.member.toString + ";\n") // give wildcard imports a import wrapper all to their own - if(req.importsWildcard) + if(handler.importsWildcard) addWrapper() else - currentImps ++= req.importedNames + currentImps ++= handler.importedNames - case req => // For other requests, import each bound variable. // import them explicitly instead of with _, so that // ambiguity errors will not be generated. Also, quote // the name of the variable, so that we don't need to // handle quoting keywords separately. - for (imv <- req.boundNames) { + for (imv <- handler.boundNames) { if (currentImps.contains(imv)) addWrapper() code.append("import ") code.append(req.objectName + req.accessPath + ".`" + imv + "`;\n") currentImps += imv } - } } addWrapper() // Add one extra wrapper, to prevent warnings @@ -411,26 +431,20 @@ class Interpreter(val settings: Settings, out: PrintWriter) { * after being parsed. */ private def buildRequest(trees: List[Tree], line: String, lineName: String): Request = - trees match { - /* This case for assignments is more specialized than desirable: it only - handles assignments to an identifier. It would be better to support - arbitrary paths being assigned, but that is technically difficult - because of the way objectSourceCode and resultObjectSourceCode are - implemented in class Request. */ - case List(Assign(Ident(lhs), _)) => - new AssignReq(lhs, line, lineName) - case _ if trees.forall(t => t.isInstanceOf[ValOrDefDef]) => - new DefReq(line, lineName) - case List(_:TermTree) | List(_:Ident) | List(_:Select) => - new ExprReq(line, lineName) - case List(_:ModuleDef) => new ModuleReq(line, lineName) - case List(_:ClassDef) => new ClassReq(line, lineName) - case List(t:TypeDef) if compiler.treeInfo.isAliasTypeDef(t) => - new TypeAliasReq(line, lineName) - case List(_:Import) => new ImportReq(line, lineName) - case _ => - reporter.error(null, "That kind of statement combination is not supported by the interpreter.") - null + new Request(line, lineName) + + private def chooseHandler(member: Tree): Option[MemberHandler] = + member match { + case member: DefDef => + Some(new DefHandler(member)) + case member: ValDef => + Some(new ValHandler(member)) + case member@Assign(Ident(_), _) => Some(new AssignHandler(member)) + case member: ModuleDef => Some(new ModuleHandler(member)) + case member: ClassDef => Some(new ClassHandler(member)) + case member: TypeDef => Some(new TypeAliasHandler(member)) + case member: Import => Some(new ImportHandler(member)) + case _ => None } /**

@@ -458,6 +472,19 @@ class Interpreter(val settings: Settings, out: PrintWriter) { case Some(trees) => trees } + trees match { + case List(_:Assign) => () + + case List(_:TermTree) | List(_:Ident) | List(_:Select) => + // Treat a single bare expression specially. + // This is necessary due to it being hard to modify + // code at a textual level, and it being hard to + // submit an AST to the compiler. + return interpret("val "+newVarName()+" = \n"+line) + + case _ => () + } + val lineName = newLineName // figure out what kind of request @@ -472,10 +499,6 @@ class Interpreter(val settings: Settings, out: PrintWriter) { if (printResults || !succeeded) { // print the result out.print(clean(interpreterResultString)) - - // print out types of functions; they are not printed in the - // request printout - out.print(clean(req.defTypesSummary)) } // book-keeping @@ -556,94 +579,178 @@ class Interpreter(val settings: Settings, out: PrintWriter) { } - /** One line of code submitted by the user for interpretation */ - private abstract class Request(val line: String, val lineName: String) { - val trees = parse(line) match { - case Some(ts) => ts - case None => Nil + /** Class to handle one member among all the members included + * in a single interpreter request. + */ + private sealed abstract class MemberHandler(val member: Tree) { + val usedNames: List[Name] = { + val ivt = new ImportVarsTraverser(boundNames) + ivt.traverseTrees(List(member)) + ivt.importVars.toList } + val boundNames: List[Name] = Nil + def valAndVarNames: List[Name] = Nil + def defNames: List[Name] = Nil + val importsWildcard = false + val importedNames: Seq[Name] = Nil + val definesImplicit = + member match { + case tree:MemberDef => + tree.mods.hasFlag(symtab.Flags.IMPLICIT) + case _ => false + } - /** name to use for the object that will compute "line" */ - def objectName = lineName + compiler.nme.INTERPRETER_WRAPPER_SUFFIX + def extraCodeToEvaluate(req: Request, code: PrintWriter) { } + def resultExtractionCode(req: Request, code: PrintWriter) { } + } - /** name of the object that retrieves the result from the above object */ - def resultObjectName = "RequestResult$" + objectName + private class ValHandler(member: ValDef) extends MemberHandler(member) { + override val boundNames = List(member.name) + override def valAndVarNames = boundNames - /** whether the trees need a variable name, as opposed to standing - alone */ - val needsVarName: Boolean = false + override def resultExtractionCode(req: Request, code: PrintWriter) { + val vname = member.name + if (member.mods.isPublic && + !(isGeneratedVarName(vname) && + req.typeOf(compiler.encode(vname)) == "Unit")) + { + code.print(" + \"" + vname + ": " + + string2code(req.typeOf(vname)) + + " = \" + " + + " (if(" + + req.fullPath(vname) + + ".asInstanceOf[AnyRef] != null) " + + " ((if(" + + req.fullPath(vname) + + ".toString.contains('\\n')) " + + " \"\\n\" else \"\") + " + + req.fullPath(vname) + ".toString + \"\\n\") else \"null\\n\") ") + } + } + } - /** A cache for the chosen variable name, if one has been calculated */ - var varNameCache: Option[String] = None + private class DefHandler(defDef: DefDef) extends MemberHandler(defDef) { + override val boundNames = List(defDef.name) + override def defNames = boundNames - /** A computed variable name, if one is needed */ - def varName = varNameCache match { - case None => - varNameCache = Some(newVarName) - varNameCache.get - case Some(name) => - name + override def resultExtractionCode(req: Request, code: PrintWriter) { + if (defDef.mods.isPublic) + code.print("+\""+string2code(defDef.name)+": "+ + string2code(req.typeOf(defDef.name))+"\\n\"") } + } - /** list of methods defined */ - val defNames = - for (DefDef(mods, name, _, _, _, _) <- trees if mods.isPublic) - yield name + private class AssignHandler(member: Assign) extends MemberHandler(member) { + val lhs = member. lhs.asInstanceOf[Ident] // an unfortunate limitation - /** list of val's and var's defined */ - val valAndVarNames = { - val baseNames = - for (ValDef(mods, name, _, _) <- trees if mods.isPublic) - yield name + val helperName = newTermName(newInternalVarName()) + override val valAndVarNames = List(helperName) - if (needsVarName) - compiler.encode(varName) :: baseNames // add a var name - else - baseNames + override def extraCodeToEvaluate(req: Request, code: PrintWriter) { + code.println("val "+helperName+" = "+member.lhs+";") } - /** list of modules defined */ - val moduleNames = { - val explicit = - for (ModuleDef(mods, name, _) <- trees if mods.isPublic) - yield name - val caseClasses = - for {val ClassDef(mods, name, _, _) <- trees - mods.isPublic - mods.hasFlag(Flags.CASE)} - yield name.toTermName - explicit ::: caseClasses + /** Print out lhs instead of the generated varName */ + override def resultExtractionCode(req: Request, code: PrintWriter) { + code.print(" + \"" + lhs + ": " + + string2code(req.typeOf(compiler.encode(helperName))) + + " = \" + " + + string2code(req.fullPath(helperName)) + + " + \"\\n\"") } + } + + private class ModuleHandler(module: ModuleDef) extends MemberHandler(module) { + override val boundNames = List(module.name) - /** list of classes defined */ - val classNames = - for (ClassDef(mods, name, _, _) <- trees if mods.isPublic) - yield name + override def resultExtractionCode(req: Request, code: PrintWriter) { + code.println(" + \"defined module " + + string2code(module.name) + + "\\n\"") + } + } - /** list of type aliases defined */ - val typeNames = - for (t @ TypeDef(mods, name, _, _) <- trees - if mods.isPublic && compiler.treeInfo.isAliasTypeDef(t)) - yield name + private class ClassHandler(classdef: ClassDef) + extends MemberHandler(classdef) + { + override val boundNames = + List(classdef.name) ::: + (if (classdef.mods.hasFlag(Flags.CASE)) + List(classdef.name.toTermName) + else + Nil) - /** all (public) names defined by these statements */ - val boundNames = - defNames ::: valAndVarNames ::: moduleNames ::: classNames ::: typeNames + // TODO: MemberDef.keyword does not include "trait"; + // otherwise it could be used here + def keyword: String = + if (classdef.mods.isTrait) "trait" else "class" - /** list of names used by this expression */ - val usedNames: List[Name] = { - val ivt = new ImportVarsTraverser(boundNames) - ivt.traverseTrees(trees) - ivt.importVars.toList + override def resultExtractionCode(req: Request, code: PrintWriter) { + code.print( + " + \"defined " + + keyword + + " " + + string2code(classdef.name) + + "\\n\"") } + } + + private class TypeAliasHandler(typeDef: TypeDef) + extends MemberHandler(typeDef) + { + override val boundNames = + if (typeDef.mods.isPublic && compiler.treeInfo.isAliasTypeDef(typeDef)) + List(typeDef.name) + else + Nil + + override def resultExtractionCode(req: Request, code: PrintWriter) { + code.println(" + \"defined type alias " + + string2code(typeDef.name) + "\\n\"") + } + } + + private class ImportHandler(imp: Import) extends MemberHandler(imp) { + override def resultExtractionCode(req: Request, code: PrintWriter) { + code.println("+ \"" + imp.toString + "\\n\"") + } + + /** Whether this import includes a wildcard import */ + override val importsWildcard = + imp.selectors.map(_._1).contains(nme.USCOREkw) + + /** The individual names imported by this statement */ + override val importedNames: Seq[Name] = + for { + val (_,sel) <- imp.selectors + sel != null + sel != nme.USCOREkw + val name <- List(sel.toTypeName, sel.toTermName) + } + yield name + } - /** Whether this request defines an implicit. */ - def definesImplicit = trees.exists { - case tree:MemberDef => - tree.mods.hasFlag(symtab.Flags.IMPLICIT) - case _ => false + /** One line of code submitted by the user for interpretation */ + private class Request(val line: String, val lineName: String) { + val trees = parse(line) match { + case Some(ts) => ts + case None => Nil } + /** name to use for the object that will compute "line" */ + def objectName = lineName + compiler.nme.INTERPRETER_WRAPPER_SUFFIX + + /** name of the object that retrieves the result from the above object */ + def resultObjectName = "RequestResult$" + objectName + + val handlers: List[MemberHandler] = trees.flatMap(chooseHandler(_)) + + /** all (public) names defined by these statements */ + val boundNames = (ListSet() ++ handlers.flatMap(_.boundNames)).toList + + /** list of names used by this expression */ + val usedNames: List[Name] = handlers.flatMap(_.usedNames) + def myImportsCode = importsCode(Set.empty ++ usedNames) /** Code to append to objectName to access anything that @@ -671,12 +778,10 @@ class Interpreter(val settings: Settings, out: PrintWriter) { code.print(importsPreamble) - // the variable to compute, if any - if (needsVarName) - code.println(" val " + varName + " = ") - code.println(indentCode(toCompute)) + handlers.foreach(_.extraCodeToEvaluate(this,code)) + code.println(importsTrailer) //end the wrapper object @@ -694,28 +799,13 @@ class Interpreter(val settings: Settings, out: PrintWriter) { code.println("object " + resultObjectName) code.println("{ val result: String = {") code.println(objectName + accessPath + ";") // evaluate the object, to make sure its constructor is run - code.print("\"\"") // print an initial empty string, so later code can + code.print("(\"\"") // print an initial empty string, so later code can // uniformly be: + morestuff - resultExtractionCode(code) - code.println("}") + handlers.foreach(_.resultExtractionCode(this, code)) + code.println("\n)}") code.println(";}") }) - def resultExtractionCode(code: PrintWriter) { - for (vname <- valAndVarNames) { - code.print(" + \"" + vname + ": " + - string2code(typeOf(vname)) + - " = \" + " + - " (if(" + - fullPath(vname) + - ".asInstanceOf[AnyRef] != null) " + - " ((if(" + - fullPath(vname) + - ".toString.contains('\\n')) " + - " \"\\n\" else \"\") + " + - fullPath(vname) + ".toString + \"\\n\") else \"null\\n\") ") - } - } /** Compile the object file. Returns whether the compilation succeeded. * If all goes well, the "types" map is computed. */ @@ -750,11 +840,14 @@ class Interpreter(val settings: Settings, out: PrintWriter) { * @return ... */ def findTypes(objRun: compiler.Run): Map[Name, String] = { + def valAndVarNames = handlers.flatMap(_.valAndVarNames) + def defNames = handlers.flatMap(_.defNames) + def getTypes(names: List[Name], nameMap: Name=>Name): Map[Name, String] = { /** the outermost wrapper object */ val outerResObjSym: Symbol = compiler.definitions.getMember(compiler.definitions.EmptyPackage, - compiler.newTermName(objectName)) + newTermName(objectName)) /** the innermost object inside the wrapper, found by * following accessPath into the outer one. */ @@ -762,7 +855,7 @@ class Interpreter(val settings: Settings, out: PrintWriter) { (accessPath.split("\\.")).foldLeft(outerResObjSym)((sym,name) => if(name == "") sym else compiler.atPhase(objRun.typerPhase.next) { - sym.info.member(compiler.newTermName(name)) }) + sym.info.member(newTermName(name)) }) names.foldLeft(Map.empty[Name,String])((map, name) => { val rawType = @@ -802,139 +895,14 @@ class Interpreter(val settings: Settings, out: PrintWriter) { (stringFrom(str => orig.printStackTrace(str)), false) } } - - /** return a summary of the defined methods */ - def defTypesSummary: String = - stringFrom(summ => { - for (methname <- defNames) - summ.println("" + methname + ": " + - string2code(typeOf(methname))) - }) - } - - /** A sequence of definition's. val's, var's, def's. */ - private class DefReq(line: String, lineName: String) - extends Request(line, lineName) - - /** Assignment of a single variable: lhs = exp */ - private class AssignReq(val lhs: Name, line: String, lineName: String) - extends Request(line, lineName) { - override val needsVarName = true - - /** Perform the assignment, and then return the new value */ - override def toCompute = "{\n" + line + "\n;\n" + lhs + "\n}" - - /** Print out lhs instead of the generated varName */ - override def resultExtractionCode(code: PrintWriter) { - code.print(" + \"" + lhs + ": " + - string2code(typeOf(compiler.encode(varName))) + - " = \" + " + - string2code(fullPath(varName)) - + " + \"\\n\"") -// override def resultExtractionCode(code: PrintWriter) { -// {wrapperObj; lhs} -// } - } } - /** A single expression */ - private class ExprReq(line: String, lineName: String) - extends Request(line, lineName) { - override val needsVarName = true - - /** Skip the printout if the expression has type Unit */ - override def resultExtractionCode(code: PrintWriter) { - if (typeOf(compiler.encode(varName)) != "Unit") - super.resultExtractionCode(code) - } - } - - /** A module definition */ - private class ModuleReq(line: String, lineName: String) - extends Request(line, lineName) { - def moduleName = trees match { - case List(ModuleDef(_, name, _)) => name - } - override def resultExtractionCode(code: PrintWriter) { - super.resultExtractionCode(code) - code.println(" + \"defined module " + - string2code(moduleName) - + "\\n\"") - } - } - - /** A class definition */ - private class ClassReq(line: String, lineName: String) - extends Request(line, lineName) { - def newClassName = trees match { - case List(ClassDef(_, name, _, _)) => name - } - - def classdef = trees.head.asInstanceOf[ClassDef] - - // TODO: MemberDef.keyword does not include "trait"; - // otherwise it could be used here - def keyword: String = - if (classdef.mods.isTrait) "trait" else "class" - - override def resultExtractionCode(code: PrintWriter) { - super.resultExtractionCode(code) - code.print( - " + \"defined " + - keyword + - " " + - string2code(newClassName) + - "\\n\"") - } - } - - /** a type alias */ - private class TypeAliasReq(line: String, lineName: String) - extends Request(line, lineName) { - def newTypeName = trees match { - case List(TypeDef(_, name, _, _)) => name - } - - override def resultExtractionCode(code: PrintWriter) { - super.resultExtractionCode(code) - code.println(" + \"defined type alias " + newTypeName + "\\n\"") - } - } - - /** an import */ - private class ImportReq(line: String, lineName: String) - extends Request(line, lineName) { - override val boundNames = Nil - override def resultExtractionCode(code: PrintWriter) { - code.println("+ \"" + trees.head.toString + "\\n\"") - } - - /** Whether this import includes a wildcard import */ - def importsWildcard = - trees.exists { - case Import(_, selectors) => - selectors.map(_._1).contains(nme.USCOREkw) - case _ => false - } - - /** The individual names imported by this statement */ - def importedNames: Seq[Name] = - for { - val Import(_, selectors) <- trees - val (_,sel) <- selectors - sel != null - sel != nme.USCOREkw - val name <- List(sel.toTypeName, sel.toTermName) - } - yield name - } -} - class NewLinePrintWriter(out: Writer, autoFlush: Boolean) extends PrintWriter(out, autoFlush) { def this(out: Writer) = this(out, false) override def println() { print("\n"); flush() } } +} /** Utility methods for the Interpreter. */ object Interpreter { diff --git a/src/compiler/scala/tools/nsc/symtab/StdNames.scala b/src/compiler/scala/tools/nsc/symtab/StdNames.scala index 70597eb5db..9403280384 100644 --- a/src/compiler/scala/tools/nsc/symtab/StdNames.scala +++ b/src/compiler/scala/tools/nsc/symtab/StdNames.scala @@ -78,6 +78,7 @@ trait StdNames { val INTERPRETER_LINE_PREFIX = "line" val INTERPRETER_VAR_PREFIX = "res" val INTERPRETER_IMPORT_WRAPPER = "$iw" + val INTERPRETER_SYNTHVAR_PREFIX = "synthvar$" def LOCAL(clazz: Symbol) = newTermName(LOCALDUMMY_PREFIX_STRING + clazz.name+">") def TUPLE_FIELD(index: Int) = newTermName(TUPLE_FIELD_PREFIX_STRING + index) diff --git a/test/files/run/interpreter.check b/test/files/run/interpreter.check index 7623eb2fbc..1bb6c28486 100644 --- a/test/files/run/interpreter.check +++ b/test/files/run/interpreter.check @@ -27,6 +27,11 @@ scala> :5: error: type mismatch; scala> defined trait PointlessTrait +scala> x: Int = 2 +y: Int = 3 + +scala> hello + scala> scala> scala> defined class Foo @@ -151,7 +156,7 @@ scala> :1: error: identifier expected but integer literal found. scala> scala> scala> -scala> | | | | res2: scala.xml.Elem = +scala> | | | | res3: scala.xml.Elem = @@ -161,7 +166,7 @@ scala> | | | | scala> scala> scala> -scala> | | | res3: java.lang.String = +scala> | | | res4: java.lang.String = hello there @@ -177,3 +182,16 @@ scala> x: Int = 1 scala> scala> +scala> defined class Exp +defined class Fact +defined class Term + +scala> | | :15: warning: match is not exhaustive! +missing combination Term + + def f(e: Exp) = e match { // non-exhaustive warning here + ^ +f: (Exp)Int + +scala> +scala> diff --git a/test/files/run/interpreter.scala b/test/files/run/interpreter.scala index 5ad9953857..b239b253cf 100644 --- a/test/files/run/interpreter.scala +++ b/test/files/run/interpreter.scala @@ -19,6 +19,8 @@ type anotherint = Int val four: anotherint = 4 val bogus: anotherint = "hello" trait PointlessTrait +val (x,y) = (2,3) +println("hello") // implicit conversions case class Foo(n: int) @@ -111,6 +113,12 @@ there def `match` = 1 val x = `match` +// multiple classes defined on one line +sealed class Exp; class Fact extends Exp; class Term extends Exp +def f(e: Exp) = e match {{ // non-exhaustive warning here + case _:Fact => 3 +}} + .text /** A writer that skips the first line of text. The first -- cgit v1.2.3