From 4a6f54b5f16b3179b23a44f2f1d83f080e218f72 Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Sat, 14 Apr 2012 06:16:32 +0100 Subject: New facility: TypeDestructurers. Would prefer to bake a little longer, but, scala days. More elaboration to come. --- .../scala/reflect/internal/Definitions.scala | 6 +- .../scala/reflect/internal/SymbolCreations.scala | 1 - .../scala/reflect/internal/SymbolFlags.scala | 1 - src/compiler/scala/reflect/internal/Symbols.scala | 10 +- src/compiler/scala/reflect/internal/Types.scala | 5 +- .../scala/tools/nsc/CompilationUnits.scala | 2 +- src/compiler/scala/tools/nsc/Global.scala | 7 +- .../scala/tools/nsc/ast/parser/Parsers.scala | 9 +- .../scala/tools/nsc/interpreter/CodeHandlers.scala | 12 +- .../scala/tools/nsc/interpreter/ExprTyper.scala | 117 +++++------- .../scala/tools/nsc/interpreter/ILoop.scala | 178 +++++++++++------- .../scala/tools/nsc/interpreter/IMain.scala | 81 +++++--- .../tools/nsc/interpreter/JLineCompletion.scala | 9 +- .../tools/nsc/interpreter/MemberHandlers.scala | 3 + .../scala/tools/nsc/interpreter/Power.scala | 4 +- .../scala/tools/nsc/interpreter/ReplGlobal.scala | 57 ++++++ .../scala/tools/nsc/interpreter/ReplReporter.scala | 4 + .../scala/tools/nsc/interpreter/ReplVals.scala | 7 +- .../scala/tools/nsc/interpreter/RichClass.scala | 5 +- .../scala/tools/nsc/interpreter/TypeStrings.scala | 136 ++++++++++++++ .../tools/nsc/typechecker/ContextErrors.scala | 4 +- .../tools/nsc/typechecker/DestructureTypes.scala | 206 +++++++++++++++++++++ src/library/scala/collection/SeqExtractors.scala | 3 + test/files/jvm/interpreter.check | 5 +- test/files/run/repl-colon-type.check | 173 ++++++++++++++++- test/files/run/repl-colon-type.scala | 8 + test/files/run/repl-power.check | 1 - test/files/run/repl-type-verbose.check | 186 +++++++++++++++++++ test/files/run/repl-type-verbose.scala | 20 ++ 29 files changed, 1067 insertions(+), 193 deletions(-) create mode 100644 src/compiler/scala/tools/nsc/interpreter/ReplGlobal.scala create mode 100644 src/compiler/scala/tools/nsc/typechecker/DestructureTypes.scala create mode 100644 test/files/run/repl-type-verbose.check create mode 100644 test/files/run/repl-type-verbose.scala diff --git a/src/compiler/scala/reflect/internal/Definitions.scala b/src/compiler/scala/reflect/internal/Definitions.scala index 72fca5da12..66b505c0d1 100644 --- a/src/compiler/scala/reflect/internal/Definitions.scala +++ b/src/compiler/scala/reflect/internal/Definitions.scala @@ -292,7 +292,7 @@ trait Definitions extends reflect.api.StandardDefinitions { sealed abstract class BottomClassSymbol(name: TypeName, parent: Symbol) extends ClassSymbol(ScalaPackageClass, NoPosition, name) { locally { - this initFlags ABSTRACT | TRAIT | FINAL + this initFlags ABSTRACT | FINAL this setInfoAndEnter ClassInfoType(List(parent.tpe), newScope, this) } final override def isBottomClass = true @@ -1014,10 +1014,10 @@ trait Definitions extends reflect.api.StandardDefinitions { val flatname = nme.flattenedName(owner.name, name) getMember(pkg, flatname) } else { - throw new FatalError(owner + " does not have a member " + name) - } + throw new FatalError(owner + " does not have a member " + name) } } + } def getMemberIfDefined(owner: Symbol, name: Name): Symbol = owner.info.nonPrivateMember(name) diff --git a/src/compiler/scala/reflect/internal/SymbolCreations.scala b/src/compiler/scala/reflect/internal/SymbolCreations.scala index a1163b0f57..ac82ffe62a 100644 --- a/src/compiler/scala/reflect/internal/SymbolCreations.scala +++ b/src/compiler/scala/reflect/internal/SymbolCreations.scala @@ -11,7 +11,6 @@ import scala.collection.mutable.ListBuffer import util.Statistics._ import Flags._ import api.Modifier -import scala.tools.util.StringOps.{ ojoin } trait SymbolCreations { self: SymbolTable => diff --git a/src/compiler/scala/reflect/internal/SymbolFlags.scala b/src/compiler/scala/reflect/internal/SymbolFlags.scala index febcec8c7c..7741d700b9 100644 --- a/src/compiler/scala/reflect/internal/SymbolFlags.scala +++ b/src/compiler/scala/reflect/internal/SymbolFlags.scala @@ -11,7 +11,6 @@ import scala.collection.mutable.ListBuffer import util.Statistics._ import Flags._ import api.Modifier -import scala.tools.util.StringOps.{ ojoin } trait SymbolFlags { self: SymbolTable => diff --git a/src/compiler/scala/reflect/internal/Symbols.scala b/src/compiler/scala/reflect/internal/Symbols.scala index c9947c3c09..6eaae7f1ee 100644 --- a/src/compiler/scala/reflect/internal/Symbols.scala +++ b/src/compiler/scala/reflect/internal/Symbols.scala @@ -707,7 +707,10 @@ trait Symbols extends api.Symbols { self: SymbolTable => || isAnonOrRefinementClass // has uninteresting or prefix || nme.isReplWrapperName(name) // has ugly $iw. prefix (doesn't call isInterpreterWrapper due to nesting) ) - def isFBounded = info.baseTypeSeq exists (_ contains this) + def isFBounded = info match { + case TypeBounds(_, _) => info.baseTypeSeq exists (_ contains this) + case _ => false + } /** Is symbol a monomorphic type? * assumption: if a type starts out as monomorphic, it will not acquire @@ -1205,8 +1208,9 @@ trait Symbols extends api.Symbols { self: SymbolTable => /** Set new info valid from start of this phase. */ def updateInfo(info: Type): Symbol = { - assert(phaseId(infos.validFrom) <= phase.id) - if (phaseId(infos.validFrom) == phase.id) infos = infos.prev + val pid = phaseId(infos.validFrom) + assert(pid <= phase.id, (pid, phase.id)) + if (pid == phase.id) infos = infos.prev infos = TypeHistory(currentPeriod, info, infos) _validTo = if (info.isComplete) currentPeriod else NoPeriod this diff --git a/src/compiler/scala/reflect/internal/Types.scala b/src/compiler/scala/reflect/internal/Types.scala index afb1d8061e..fc57a130d1 100644 --- a/src/compiler/scala/reflect/internal/Types.scala +++ b/src/compiler/scala/reflect/internal/Types.scala @@ -2082,7 +2082,7 @@ trait Types extends api.Types { self: SymbolTable => // TODO: is there another way a typeref's symbol can refer to a symbol defined in its pre? case _ => sym } - + override def kind = "AliasTypeRef" } trait AbstractTypeRef extends NonClassTypeRef { @@ -2135,6 +2135,7 @@ trait Types extends api.Types { self: SymbolTable => override def bounds = thisInfo.bounds // def transformInfo(tp: Type): Type = appliedType(tp.asSeenFrom(pre, sym.owner), typeArgsOrDummies) override protected[Types] def baseTypeSeqImpl: BaseTypeSeq = transform(bounds.hi).baseTypeSeq prepend this + override def kind = "AbstractTypeRef" } /** A class for named types of the form @@ -2579,7 +2580,7 @@ trait Types extends api.Types { self: SymbolTable => !(qset contains sym) && !isQuantified(pre) case _ => false - } + } } override def safeToString: String = { diff --git a/src/compiler/scala/tools/nsc/CompilationUnits.scala b/src/compiler/scala/tools/nsc/CompilationUnits.scala index 92a0efff1e..ce6a7ed6af 100644 --- a/src/compiler/scala/tools/nsc/CompilationUnits.scala +++ b/src/compiler/scala/tools/nsc/CompilationUnits.scala @@ -5,7 +5,7 @@ package scala.tools.nsc -import util.{ FreshNameCreator, Position, NoPosition, SourceFile, NoSourceFile } +import util.{ FreshNameCreator, Position, NoPosition, BatchSourceFile, SourceFile, NoSourceFile } import scala.collection.mutable import scala.collection.mutable.{ LinkedHashSet, ListBuffer } diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala index 403b5717b0..9169f80e1c 100644 --- a/src/compiler/scala/tools/nsc/Global.scala +++ b/src/compiler/scala/tools/nsc/Global.scala @@ -950,6 +950,11 @@ class Global(var currentSettings: Settings, var reporter: NscReporter) extends S reporter.warning(NoPosition, "there were %d %s warnings; re-run with %s for details".format(warnings.size, what, option.name)) } + def newUnitParser(code: String) = new syntaxAnalyzer.UnitParser(newCompilationUnit(code)) + def newUnitScanner(code: String) = new syntaxAnalyzer.UnitScanner(newCompilationUnit(code)) + def newCompilationUnit(code: String) = new CompilationUnit(newSourceFile(code)) + def newSourceFile(code: String) = new BatchSourceFile("", code) + /** A Run is a single execution of the compiler on a sets of units */ class Run { @@ -1275,7 +1280,7 @@ class Global(var currentSettings: Settings, var reporter: NscReporter) extends S // nothing to compile, but we should still report use of deprecated options if (sources.isEmpty) { - checkDeprecatedSettings(new CompilationUnit(new BatchSourceFile("", ""))) + checkDeprecatedSettings(newCompilationUnit("")) reportCompileErrors() return } diff --git a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala index eb4deeeee2..bca1cc4596 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala @@ -317,7 +317,7 @@ self => * by compilationUnit(). */ def scriptBody(): Tree = { - val stmts = templateStatSeq(false)._2 + val stmts = templateStats() accept(EOF) def mainModuleName = newTermName(settings.script.value) @@ -2869,6 +2869,13 @@ self => stats.toList } + /** Informal - for the repl and other direct parser accessors. + */ + def templateStats(): List[Tree] = templateStatSeq(false)._2 match { + case Nil => List(EmptyTree) + case stats => stats + } + /** {{{ * TemplateStatSeq ::= [id [`:' Type] `=>'] TemplateStat {semi TemplateStat} * TemplateStat ::= Import diff --git a/src/compiler/scala/tools/nsc/interpreter/CodeHandlers.scala b/src/compiler/scala/tools/nsc/interpreter/CodeHandlers.scala index 42a47896a2..453b106808 100644 --- a/src/compiler/scala/tools/nsc/interpreter/CodeHandlers.scala +++ b/src/compiler/scala/tools/nsc/interpreter/CodeHandlers.scala @@ -17,9 +17,7 @@ import scala.util.control.ControlThrowable trait CodeHandlers[T] { self => - // Expressions are composed of operators and operands. - def expr(code: String): T - +/* // A declaration introduces names and assigns them types. // It can form part of a class definition (§5.1) or of a refinement in a compound type (§3.2.7). // (Ed: aka abstract members.) @@ -34,6 +32,10 @@ trait CodeHandlers[T] { // ‘val’ PatDef | ‘var’ VarDef | ‘def’ FunDef | ‘type’ {nl} TypeDef | // [‘case’] ‘class’ ClassDef | [‘case’] ‘object’ ObjectDef | ‘trait’ TraitDef def defn(code: String): T +*/ + + // Expressions are composed of operators and operands. + def expr(code: String): T // An import clause has the form import p.I where p is a stable identifier (§3.1) and I is an import expression. def impt(code: String): T @@ -52,9 +54,9 @@ trait CodeHandlers[T] { case _: NoSuccess => Nil } + // def decl(code: String) = try Some(self.decl(code)) catch handler + // def defn(code: String) = try Some(self.defn(code)) catch handler def expr(code: String) = try Some(self.expr(code)) catch handler - def decl(code: String) = try Some(self.decl(code)) catch handler - def defn(code: String) = try Some(self.defn(code)) catch handler def impt(code: String) = try Some(self.impt(code)) catch handler def stmt(code: String) = try Some(self.stmt(code)) catch handler def stmts(code: String) = try (self.stmts(code) map (x => Some(x))) catch handlerSeq diff --git a/src/compiler/scala/tools/nsc/interpreter/ExprTyper.scala b/src/compiler/scala/tools/nsc/interpreter/ExprTyper.scala index a2ce8439de..0cd918b6a5 100644 --- a/src/compiler/scala/tools/nsc/interpreter/ExprTyper.scala +++ b/src/compiler/scala/tools/nsc/interpreter/ExprTyper.scala @@ -22,8 +22,7 @@ trait ExprTyper { object codeParser extends { val global: repl.global.type = repl.global } with CodeHandlers[Tree] { def applyRule[T](code: String, rule: UnitParser => T): T = { reporter.reset() - val unit = new CompilationUnit(new BatchSourceFile("", code)) - val scanner = new UnitParser(unit) + val scanner = newUnitParser(code) val result = rule(scanner) if (!reporter.hasErrors) @@ -33,23 +32,17 @@ trait ExprTyper { } def tokens(code: String) = { reporter.reset() - val unit = new CompilationUnit(new BatchSourceFile("", code)) - val in = new UnitScanner(unit) + val in = newUnitScanner(code) in.init() - new Tokenizer(in) tokenIterator } - def decl(code: String) = CodeHandlers.fail("todo") - def defn(code: String) = CodeHandlers.fail("todo") + def defns(code: String) = stmts(code) collect { case x: DefTree => x } def expr(code: String) = applyRule(code, _.expr()) def impt(code: String) = applyRule(code, _.importExpr()) def impts(code: String) = applyRule(code, _.importClause()) - def stmts(code: String) = applyRule(code, _.templateStatSeq(false)._2) - def stmt(code: String) = stmts(code) match { - case List(t) => t - case xs => CodeHandlers.fail("Not a single statement: " + xs.mkString(", ")) - } + def stmts(code: String) = applyRule(code, _.templateStats()) + def stmt(code: String) = stmts(code).last // guaranteed nonempty } /** Parse a line into a sequence of trees. Returns None if the input is incomplete. */ @@ -62,77 +55,69 @@ trait ExprTyper { else Some(trees) } } - def tokens(line: String) = beQuietDuring(codeParser.tokens(line)) + // def parsesAsExpr(line: String) = { + // import codeParser._ + // (opt expr line).isDefined + // } - // TODO: integrate these into a CodeHandler[Type]. + def symbolOfLine(code: String): Symbol = { + def asExpr(): Symbol = { + val name = freshInternalVarName() + // Typing it with a lazy val would give us the right type, but runs + // into compiler bugs with things like existentials, so we compile it + // behind a def and strip the NullaryMethodType which wraps the expr. + val line = "def " + name + " = {\n" + code + "\n}" + + interpretSynthetic(line) match { + case IR.Success => + val sym0 = symbolOfTerm(name) + // drop NullaryMethodType + val sym = sym0.cloneSymbol setInfo afterTyper(sym0.info.finalResultType) + if (sym.info.typeSymbol eq UnitClass) NoSymbol + else sym + case _ => NoSymbol + } + } + def asDefn(): Symbol = { + val old = repl.definedSymbolList.toSet + + interpretSynthetic(code) match { + case IR.Success => + repl.definedSymbolList filterNot old match { + case Nil => NoSymbol + case sym :: Nil => sym + case syms => NoSymbol.newOverloaded(NoPrefix, syms) + } + case _ => NoSymbol + } + } + beQuietDuring(asExpr()) orElse beQuietDuring(asDefn()) + } - // XXX literals. - // 1) Identifiers defined in the repl. - // 2) A path loadable via getModule. - // 3) Try interpreting it as an expression. private var typeOfExpressionDepth = 0 def typeOfExpression(expr: String, silent: Boolean = true): Type = { - repltrace("typeOfExpression(" + expr + ")") if (typeOfExpressionDepth > 2) { repldbg("Terminating typeOfExpression recursion for expression: " + expr) return NoType } - - def asQualifiedImport: Type = { - val name = expr.takeWhile(_ != '.') - typeOfExpression(importedTermNamed(name).fullName + expr.drop(name.length), true) - } - def asModule: Type = getModuleIfDefined(expr).tpe - def asExpr: Type = { - val lhs = freshInternalVarName() - val line = "lazy val " + lhs + " =\n" + expr - - interpret(line, true) match { - case IR.Success => typeOfExpression(lhs, true) - case _ => NoType - } - } - - def evaluate(): Type = { - typeOfExpressionDepth += 1 - try typeOfTerm(expr) orElse asModule orElse asExpr orElse asQualifiedImport - finally typeOfExpressionDepth -= 1 - } - + typeOfExpressionDepth += 1 // Don't presently have a good way to suppress undesirable success output // while letting errors through, so it is first trying it silently: if there // is an error, and errors are desired, then it re-evaluates non-silently // to induce the error message. - beSilentDuring(evaluate()) orElse beSilentDuring(typeOfDeclaration(expr)) orElse { - if (!silent) - evaluate() - - NoType + try beSilentDuring(symbolOfLine(expr).tpe) match { + case NoType if !silent => symbolOfLine(expr).tpe // generate error + case tpe => tpe } + finally typeOfExpressionDepth -= 1 } - // Since people will be giving us ":t def foo = 5" even though that is not an - // expression, we have a means of typing declarations too. - private def typeOfDeclaration(code: String): Type = { - repltrace("typeOfDeclaration(" + code + ")") - val obname = freshInternalVarName() - interpret("object " + obname + " {\n" + code + "\n}\n", true) match { - case IR.Success => - val sym = symbolOfTerm(obname) - if (sym == NoSymbol) NoType else { - // TODO: bitmap$n is not marked synthetic. - val decls = sym.tpe.decls.toList filterNot (x => x.isConstructor || x.isPrivate || (x.name.toString contains "$")) - repltrace("decls: " + decls) - if (decls.isEmpty) NoType - else cleanMemberDecl(sym, decls.last.name) - } - case _ => - NoType - } - } + def tokens(line: String) = beQuietDuring(codeParser.tokens(line)) + + // In the todo column + // // def compileAndTypeExpr(expr: String): Option[Typer] = { // class TyperRun extends Run { // override def stopPhase(name: String) = name == "superaccessors" // } - // } } diff --git a/src/compiler/scala/tools/nsc/interpreter/ILoop.scala b/src/compiler/scala/tools/nsc/interpreter/ILoop.scala index bf386d8a12..9279d37464 100644 --- a/src/compiler/scala/tools/nsc/interpreter/ILoop.scala +++ b/src/compiler/scala/tools/nsc/interpreter/ILoop.scala @@ -49,12 +49,60 @@ class ILoop(in0: Option[BufferedReader], protected val out: JPrintWriter) var settings: Settings = _ var intp: IMain = _ - override def echoCommandMessage(msg: String): Unit = - intp.reporter.printMessage(msg) + /** Having inherited the difficult "var-ness" of the repl instance, + * I'm trying to work around it by moving operations into a class from + * which it will appear a stable prefix. + */ + private def onIntp[T](f: IMain => T): T = f(intp) + + class IMainOps[T <: IMain](val intp: T) { + import intp._ + import global._ + + def printAfterTyper(msg: => String) = + intp.reporter printUntruncatedMessage afterTyper(msg) + + /** Strip NullaryMethodType artifacts. */ + private def replInfo(sym: Symbol) = { + sym.info match { + case NullaryMethodType(restpe) if sym.isAccessor => restpe + case info => info + } + } + def echoTypeStructure(sym: Symbol) = + printAfterTyper("" + deconstruct.show(replInfo(sym))) + + def echoTypeSignature(sym: Symbol, verbose: Boolean) = { + if (verbose) ILoop.this.echo("// Type signature") + printAfterTyper("" + replInfo(sym)) + + if (verbose) { + ILoop.this.echo("\n// Internal Type structure") + echoTypeStructure(sym) + } + } + } + implicit def stabilizeIMain(intp: IMain) = new IMainOps[intp.type](intp) + + /** TODO - + * -n normalize + * -l label with case class parameter names + * -c complete - leave nothing out + */ + private def typeCommandInternal(expr: String, verbose: Boolean): Result = { + onIntp { intp => + val sym = intp.symbolOfLine(expr) + if (sym.exists) intp.echoTypeSignature(sym, verbose) + else "" + } + } + + override def echoCommandMessage(msg: String) { + intp.reporter printUntruncatedMessage msg + } def isAsync = !settings.Yreplsync.value lazy val power = new Power(intp, new StdReplVals(this)) - lazy val NoType = intp.global.NoType // TODO // object opt extends AestheticSettings @@ -249,7 +297,7 @@ class ILoop(in0: Option[BufferedReader], protected val out: JPrintWriter) // nullary("reset", "reset the interpreter, forgetting session values but retaining session types", replay), shCommand, nullary("silent", "disable/enable automatic printing of results", verbosity), - cmd("type", "", "display the type of an expression without evaluating it", typeCommand), + cmd("type", "[-v] ", "display the type of an expression without evaluating it", typeCommand), nullary("warnings", "show the suppressed warnings from the most recent line which had any", warningsCommand) ) @@ -322,10 +370,9 @@ class ILoop(in0: Option[BufferedReader], protected val out: JPrintWriter) } } - private def implicitsCommand(line: String): Result = { - val intp = ILoop.this.intp + private def implicitsCommand(line: String): Result = onIntp { intp => import intp._ - import global.{ Symbol, afterTyper } + import global._ def p(x: Any) = intp.reporter.printMessage("" + x) @@ -436,14 +483,14 @@ class ILoop(in0: Option[BufferedReader], protected val out: JPrintWriter) private lazy val javap = substituteAndLog[Javap]("javap", NoJavap)(newJavap()) // Still todo: modules. - private def typeCommand(line: String): Result = { - if (line.trim == "") ":type " - else { - val tp = intp.typeOfExpression(line, false) - if (tp == NoType) "" // the error message was already printed - else intp.global.afterTyper(tp.toString) + private def typeCommand(line0: String): Result = { + line0.trim match { + case "" => ":type [-v] " + case s if s startsWith "-v " => typeCommandInternal(s stripPrefix "-v " trim, true) + case s => typeCommandInternal(s, false) } } + private def warningsCommand(): Result = { intp.lastWarnings foreach { case (pos, msg) => intp.reporter.warning(pos, msg) } } @@ -471,33 +518,34 @@ class ILoop(in0: Option[BufferedReader], protected val out: JPrintWriter) } private def wrapCommand(line: String): Result = { def failMsg = "Argument to :wrap must be the name of a method with signature [T](=> T): T" - val intp = ILoop.this.intp - val g: intp.global.type = intp.global - import g._ - - words(line) match { - case Nil => - intp.executionWrapper match { - case "" => "No execution wrapper is set." - case s => "Current execution wrapper: " + s - } - case "clear" :: Nil => - intp.executionWrapper match { - case "" => "No execution wrapper is set." - case s => intp.clearExecutionWrapper() ; "Cleared execution wrapper." - } - case wrapper :: Nil => - intp.typeOfExpression(wrapper) match { - case PolyType(List(targ), MethodType(List(arg), restpe)) => - intp setExecutionWrapper intp.pathToTerm(wrapper) - "Set wrapper to '" + wrapper + "'" - case tp => - failMsg + ( - if (tp == g.NoType) "\nFound: " - else "\nFound: " - ) - } - case _ => failMsg + onIntp { intp => + import intp._ + import global._ + + words(line) match { + case Nil => + intp.executionWrapper match { + case "" => "No execution wrapper is set." + case s => "Current execution wrapper: " + s + } + case "clear" :: Nil => + intp.executionWrapper match { + case "" => "No execution wrapper is set." + case s => intp.clearExecutionWrapper() ; "Cleared execution wrapper." + } + case wrapper :: Nil => + intp.typeOfExpression(wrapper) match { + case PolyType(List(targ), MethodType(List(arg), restpe)) => + intp setExecutionWrapper intp.pathToTerm(wrapper) + "Set wrapper to '" + wrapper + "'" + case tp => + failMsg + ( + if (tp == NoType) "\nFound: " + else "\nFound: " + ) + } + case _ => failMsg + } } } @@ -798,6 +846,10 @@ class ILoop(in0: Option[BufferedReader], protected val out: JPrintWriter) }) } + def runCompletion = + try in.completion execute code map (intp bindSyntheticValue _) + catch { case ex: Exception => None } + /** Here we place ourselves between the user and the interpreter and examine * the input they are ostensibly submitting. We intervene in several cases: * @@ -815,30 +867,28 @@ class ILoop(in0: Option[BufferedReader], protected val out: JPrintWriter) else if (Completion.looksLikeInvocation(code) && intp.mostRecentVar != "") { interpretStartingWith(intp.mostRecentVar + code) } - else { - def runCompletion = - try in.completion execute code map (intp bindValue _) - catch { case ex: Exception => None } - - /** Due to my accidentally letting file completion execution sneak ahead - * of actual parsing this now operates in such a way that the scala - * interpretation always wins. However to avoid losing useful file - * completion I let it fail and then check the others. So if you - * type /tmp it will echo a failure and then give you a Directory object. - * It's not pretty: maybe I'll implement the silence bits I need to avoid - * echoing the failure. - */ - if (intp isParseable code) { - val (code, result) = reallyInterpret - if (power != null && code == IR.Error) - runCompletion - - result - } - else runCompletion match { - case Some(_) => None // completion hit: avoid the latent error - case _ => reallyInterpret._2 // trigger the latent error - } + else if (code.trim startsWith "//") { + // line comment, do nothing + None + } + /** Due to my accidentally letting file completion execution sneak ahead + * of actual parsing this now operates in such a way that the scala + * interpretation always wins. However to avoid losing useful file + * completion I let it fail and then check the others. So if you + * type /tmp it will echo a failure and then give you a Directory object. + * It's not pretty: maybe I'll implement the silence bits I need to avoid + * echoing the failure. + */ + else if (intp isParseable code) { + val (code, result) = reallyInterpret + if (power != null && code == IR.Error) + runCompletion + + result + } + else runCompletion match { + case Some(_) => None // completion hit: avoid the latent error + case _ => reallyInterpret._2 // trigger the latent error } } diff --git a/src/compiler/scala/tools/nsc/interpreter/IMain.scala b/src/compiler/scala/tools/nsc/interpreter/IMain.scala index 43e01bebfd..13124e6afc 100644 --- a/src/compiler/scala/tools/nsc/interpreter/IMain.scala +++ b/src/compiler/scala/tools/nsc/interpreter/IMain.scala @@ -24,6 +24,7 @@ import scala.collection.{ mutable, immutable } import scala.util.control.Exception.{ ultimately } import IMain._ import java.util.concurrent.Future +import typechecker.Analyzer import language.implicitConversions /** directory to save .class files to */ @@ -140,7 +141,7 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends lazy val formatting: Formatting = new Formatting { val prompt = Properties.shellPromptString } - lazy val reporter: ConsoleReporter = new ReplReporter(this) + lazy val reporter: ReplReporter = new ReplReporter(this) import formatting._ import reporter.{ printMessage, withoutTruncating } @@ -156,6 +157,8 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends } catch AbstractOrMissingHandler() } + private def tquoted(s: String) = "\"\"\"" + s + "\"\"\"" + // argument is a thunk to execute after init is done def initialize(postInitSignal: => Unit) { synchronized { @@ -226,6 +229,10 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends } import naming._ + object deconstruct extends { + val global: imain.global.type = imain.global + } with StructuredTypeStrings + // object dossiers extends { // val intp: imain.type = imain // } with Dossiers { } @@ -275,11 +282,10 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends protected def createLineManager(classLoader: ClassLoader): Line.Manager = new Line.Manager(classLoader) /** Instantiate a compiler. Overridable. */ - protected def newCompiler(settings: Settings, reporter: NscReporter) = { + protected def newCompiler(settings: Settings, reporter: NscReporter): ReplGlobal = { settings.outputDirs setSingleOutput virtualDirectory settings.exposeEmptyPackage.value = true - - Global(settings, reporter) + new Global(settings, reporter) with ReplGlobal } /** Parent classloader. Overridable. */ @@ -521,7 +527,7 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends trees.last match { case _:Assign => // we don't want to include assignments case _:TermTree | _:Ident | _:Select => // ... but do want other unnamed terms. - val varName = if (synthetic) freshInternalVarName() else ("" + freshUserTermName()) + val varName = if (synthetic) freshInternalVarName() else freshUserVarName() val rewrittenLine = ( // In theory this would come out the same without the 1-specific test, but // it's a cushion against any more sneaky parse-tree position vs. code mismatches: @@ -587,6 +593,7 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends * e.g. that there were no parse errors. */ def interpret(line: String): IR.Result = interpret(line, false) + def interpretSynthetic(line: String): IR.Result = interpret(line, true) def interpret(line: String, synthetic: Boolean): IR.Result = { def loadAndRunReq(req: Request) = { val (result, succeeded) = req.loadAndRun @@ -679,7 +686,8 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends def quietBind(p: NamedParam): IR.Result = beQuietDuring(bind(p)) def bind(p: NamedParam): IR.Result = bind(p.name, p.tpe, p.value) def bind[T: Manifest](name: String, value: T): IR.Result = bind((name, value)) - def bindValue(x: Any): IR.Result = bindValue("" + freshUserTermName(), x) + def bindSyntheticValue(x: Any): IR.Result = bindValue(freshInternalVarName(), x) + def bindValue(x: Any): IR.Result = bindValue(freshUserVarName(), x) def bindValue(name: String, x: Any): IR.Result = bind(name, TypeStrings.fromValue(x), x) /** Reset this interpreter, forgetting all user-specified requests. */ @@ -852,6 +860,7 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends /** handlers for each tree in this request */ val handlers: List[MemberHandler] = trees map (memberHandlers chooseHandler _) + def defHandlers = handlers collect { case x: MemberDefHandler => x } /** all (public) names defined by these statements */ val definedNames = handlers flatMap (_.definedNames) @@ -863,6 +872,10 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends def termNames = handlers flatMap (_.definesTerm) def typeNames = handlers flatMap (_.definesType) def definedOrImported = handlers flatMap (_.definedOrImported) + def definedSymbolList = defHandlers flatMap (_.definedSymbols) + + def definedTypeSymbol(name: String) = definedSymbols(newTypeName(name)) + def definedTermSymbol(name: String) = definedSymbols(newTermName(name)) /** Code to import bound names from previous lines - accessPath is code to * append to objectName to access anything bound by request. @@ -915,8 +928,6 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends val generate = (m: MemberHandler) => m extraCodeToEvaluate Request.this } - def tquoted(s: String) = "\"\"\"" + s + "\"\"\"" - private object ResultObjectSourceCode extends CodeAssembler[MemberHandler] { /** We only want to generate this code when the result * is a value which can be referred to as-is. @@ -970,6 +981,16 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends typeOf typesOfDefinedTerms + // Assign symbols to the original trees + // TODO - just use the new trees. + defHandlers foreach { dh => + val name = dh.member.name + definedSymbols get name foreach { sym => + dh.member setSymbol sym + repldbg("Set symbol of " + name + " to " + sym.defString) + } + } + // compile the result-extraction object beQuietDuring { savingSettings(_.nowarn.value = true) { @@ -991,17 +1012,17 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends } /** Types of variables defined by this request. */ - lazy val compilerTypeOf = typeMap[Type](x => x) + lazy val compilerTypeOf = typeMap[Type](x => x) withDefaultValue NoType /** String representations of same. */ lazy val typeOf = typeMap[String](tp => afterTyper(tp.toString)) // lazy val definedTypes: Map[Name, Type] = { // typeNames map (x => x -> afterTyper(resultSymbol.info.nonPrivateDecl(x).tpe)) toMap // } - lazy val definedSymbols: Map[Name, Symbol] = ( + lazy val definedSymbols = ( termNames.map(x => x -> applyToResultMember(x, x => x)) ++ - typeNames.map(x => x -> compilerTypeOf.get(x).map(_.typeSymbol).getOrElse(NoSymbol)) - ).toMap + typeNames.map(x => x -> compilerTypeOf(x).typeSymbol) + ).toMap[Name, Symbol] withDefaultValue NoSymbol lazy val typesOfDefinedTerms: Map[Name, Type] = termNames map (x => x -> applyToResultMember(x, _.tpe)) toMap @@ -1075,18 +1096,21 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends } def valueOfTerm(id: String): Option[AnyRef] = - requestForIdent(id) flatMap (_.getEval) + requestForName(newTermName(id)) flatMap (_.getEval) def classOfTerm(id: String): Option[JClass] = valueOfTerm(id) map (_.getClass) def typeOfTerm(id: String): Type = newTermName(id) match { case nme.ROOTPKG => definitions.RootClass.tpe - case name => requestForName(name) flatMap (_.compilerTypeOf get name) getOrElse NoType + case name => requestForName(name).fold(NoType: Type)(_ compilerTypeOf name) } + def symbolOfType(id: String): Symbol = + requestForName(newTypeName(id)).fold(NoSymbol: Symbol)(_ definedTypeSymbol id) + def symbolOfTerm(id: String): Symbol = - requestForIdent(id) flatMap (_.definedSymbols get newTermName(id)) getOrElse NoSymbol + requestForIdent(newTermName(id)).fold(NoSymbol: Symbol)(_ definedTermSymbol id) def runtimeClassAndTypeOfTerm(id: String): Option[(JClass, Type)] = { classOfTerm(id) flatMap { clazz => @@ -1120,11 +1144,15 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends val global: imain.global.type = imain.global } with ReplTokens { } - private object exprTyper extends { + object exprTyper extends { val repl: IMain.this.type = imain } with ExprTyper { } def parse(line: String): Option[List[Tree]] = exprTyper.parse(line) + + def symbolOfLine(code: String): Symbol = + exprTyper.symbolOfLine(code) + def typeOfExpression(expr: String, silent: Boolean = true): Type = exprTyper.typeOfExpression(expr, silent) @@ -1134,14 +1162,15 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends protected def onlyTerms(xs: List[Name]) = xs collect { case x: TermName => x } protected def onlyTypes(xs: List[Name]) = xs collect { case x: TypeName => x } - def definedTerms = onlyTerms(allDefinedNames) filterNot isInternalTermName - def definedTypes = onlyTypes(allDefinedNames) - def definedSymbols = prevRequests.toSet flatMap ((x: Request) => x.definedSymbols.values) + def definedTerms = onlyTerms(allDefinedNames) filterNot isInternalTermName + def definedTypes = onlyTypes(allDefinedNames) + def definedSymbols = prevRequestList.flatMap(_.definedSymbols.values).toSet[Symbol] + def definedSymbolList = prevRequestList flatMap (_.definedSymbolList) filterNot (s => isInternalTermName(s.name)) // Terms with user-given names (i.e. not res0 and not synthetic) def namedDefinedTerms = definedTerms filterNot (x => isUserVarName("" + x) || directlyBoundNames(x)) - private def findName(name: Name) = definedSymbols find (_.name == name) + private def findName(name: Name) = definedSymbols find (_.name == name) getOrElse NoSymbol /** Translate a repl-defined identifier into a Symbol. */ @@ -1150,16 +1179,19 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends def types(name: String): Symbol = { val tpname = newTypeName(name) - findName(tpname) getOrElse getClassIfDefined(tpname) + findName(tpname) orElse getClassIfDefined(tpname) } def terms(name: String): Symbol = { val termname = newTypeName(name) - findName(termname) getOrElse getModuleIfDefined(termname) + findName(termname) orElse getModuleIfDefined(termname) } def types[T: ClassManifest] : Symbol = types(classManifest[T].erasure.getName) def terms[T: ClassManifest] : Symbol = terms(classManifest[T].erasure.getName) def apply[T: ClassManifest] : Symbol = apply(classManifest[T].erasure.getName) + def classSymbols = allDefSymbols collect { case x: ClassSymbol => x } + def methodSymbols = allDefSymbols collect { case x: MethodSymbol => x } + /** the previous requests this interpreter has processed */ private var executingRequest: Request = _ private val prevRequests = mutable.ListBuffer[Request]() @@ -1167,7 +1199,10 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends private val definedNameMap = mutable.Map[Name, Request]() private val directlyBoundNames = mutable.Set[Name]() - private def allHandlers = prevRequestList flatMap (_.handlers) + def allHandlers = prevRequestList flatMap (_.handlers) + def allDefHandlers = allHandlers collect { case x: MemberDefHandler => x } + def allDefSymbols = allDefHandlers map (_.symbol) filter (_ ne NoSymbol) + def lastRequest = if (prevRequests.isEmpty) null else prevRequests.last def prevRequestList = prevRequests.toList def allSeenTypes = prevRequestList flatMap (_.typeOf.values.toList) distinct diff --git a/src/compiler/scala/tools/nsc/interpreter/JLineCompletion.scala b/src/compiler/scala/tools/nsc/interpreter/JLineCompletion.scala index a86462ad5f..795b2e3678 100644 --- a/src/compiler/scala/tools/nsc/interpreter/JLineCompletion.scala +++ b/src/compiler/scala/tools/nsc/interpreter/JLineCompletion.scala @@ -194,14 +194,7 @@ class JLineCompletion(val intp: IMain) extends Completion with CompletionOutput // literal Ints, Strings, etc. object literals extends CompletionAware { - def simpleParse(code: String): Tree = { - val unit = new CompilationUnit(new util.BatchSourceFile("", code)) - val scanner = new syntaxAnalyzer.UnitParser(unit) - val tss = scanner.templateStatSeq(false)._2 - - if (tss.size == 1) tss.head else EmptyTree - } - + def simpleParse(code: String): Tree = newUnitParser(code).templateStats().last def completions(verbosity: Int) = Nil override def follow(id: String) = simpleParse(id) match { diff --git a/src/compiler/scala/tools/nsc/interpreter/MemberHandlers.scala b/src/compiler/scala/tools/nsc/interpreter/MemberHandlers.scala index 40993eb916..fcede04aaf 100644 --- a/src/compiler/scala/tools/nsc/interpreter/MemberHandlers.scala +++ b/src/compiler/scala/tools/nsc/interpreter/MemberHandlers.scala @@ -64,6 +64,7 @@ trait MemberHandlers { } sealed abstract class MemberDefHandler(override val member: MemberDef) extends MemberHandler(member) { + def symbol = if (member.symbol eq null) NoSymbol else member.symbol def name: Name = member.name def mods: Modifiers = member.mods def keyword = member.keyword @@ -72,6 +73,7 @@ trait MemberHandlers { override def definesImplicit = member.mods.isImplicit override def definesTerm: Option[TermName] = Some(name.toTermName) filter (_ => name.isTermName) override def definesType: Option[TypeName] = Some(name.toTypeName) filter (_ => name.isTypeName) + override def definedSymbols = if (symbol eq NoSymbol) Nil else List(symbol) } /** Class to handle one member among all the members included @@ -89,6 +91,7 @@ trait MemberHandlers { def importedNames = List[Name]() def definedNames = definesTerm.toList ++ definesType.toList def definedOrImported = definedNames ++ importedNames + def definedSymbols = List[Symbol]() def extraCodeToEvaluate(req: Request): String = "" def resultExtractionCode(req: Request): String = "" diff --git a/src/compiler/scala/tools/nsc/interpreter/Power.scala b/src/compiler/scala/tools/nsc/interpreter/Power.scala index ecf9dd219b..2cb034f7ab 100644 --- a/src/compiler/scala/tools/nsc/interpreter/Power.scala +++ b/src/compiler/scala/tools/nsc/interpreter/Power.scala @@ -410,8 +410,8 @@ class Power[ReplValsImpl <: ReplVals : Manifest](val intp: IMain, replVals: Repl lazy val phased: Phased = new { val global: intp.global.type = intp.global } with Phased { } def context(code: String) = analyzer.rootContext(unit(code)) - def source(code: String) = new BatchSourceFile("", code) - def unit(code: String) = new CompilationUnit(source(code)) + def source(code: String) = newSourceFile(code) + def unit(code: String) = newCompilationUnit(code) def trees(code: String) = parse(code) getOrElse Nil def typeOf(id: String) = intp.typeOfExpression(id) diff --git a/src/compiler/scala/tools/nsc/interpreter/ReplGlobal.scala b/src/compiler/scala/tools/nsc/interpreter/ReplGlobal.scala new file mode 100644 index 0000000000..05321dd7e6 --- /dev/null +++ b/src/compiler/scala/tools/nsc/interpreter/ReplGlobal.scala @@ -0,0 +1,57 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2011 LAMP/EPFL + * @author Paul Phillips + */ + +package scala.tools.nsc +package interpreter + +import reporters._ +import typechecker.Analyzer + +/** A layer on top of Global so I can guarantee some extra + * functionality for the repl. It doesn't do much yet. + */ +trait ReplGlobal extends Global { + // This exists mostly because using the reporter too early leads to deadlock. + private def echo(msg: String) { Console println msg } + + override def abort(msg: String): Nothing = { + echo("ReplGlobal.abort: " + msg) + super.abort(msg) + } + + override lazy val analyzer = new { + val global: ReplGlobal.this.type = ReplGlobal.this + } with Analyzer { + override def newTyper(context: Context): Typer = new Typer(context) { + override def typed(tree: Tree, mode: Int, pt: Type): Tree = { + val res = super.typed(tree, mode, pt) + tree match { + case Ident(name) if !tree.symbol.hasPackageFlag && !name.toString.startsWith("$") => + repldbg("typed %s: %s".format(name, res.tpe)) + case _ => + } + res + } + } + } + + object replPhase extends SubComponent { + val global: ReplGlobal.this.type = ReplGlobal.this + val phaseName = "repl" + val runsAfter = List[String]("typer") + val runsRightAfter = None + def newPhase(_prev: Phase): StdPhase = new StdPhase(_prev) { + def apply(unit: CompilationUnit) { + repldbg("Running replPhase on " + unit.body) + // newNamer(rootContext(unit)).enterSym(unit.body) + } + } + } + + override protected def computePhaseDescriptors: List[SubComponent] = { + addToPhasesSet(replPhase, "repl") + super.computePhaseDescriptors + } +} diff --git a/src/compiler/scala/tools/nsc/interpreter/ReplReporter.scala b/src/compiler/scala/tools/nsc/interpreter/ReplReporter.scala index 130af990ad..fb61dfb672 100644 --- a/src/compiler/scala/tools/nsc/interpreter/ReplReporter.scala +++ b/src/compiler/scala/tools/nsc/interpreter/ReplReporter.scala @@ -9,7 +9,11 @@ package interpreter import reporters._ import IMain._ +/** Like ReplGlobal, a layer for ensuring extra functionality. + */ class ReplReporter(intp: IMain) extends ConsoleReporter(intp.settings, Console.in, new ReplStrippingWriter(intp)) { + def printUntruncatedMessage(msg: String) = withoutTruncating(printMessage(msg)) + override def printMessage(msg: String) { // Avoiding deadlock if the compiler starts logging before // the lazy val is complete. diff --git a/src/compiler/scala/tools/nsc/interpreter/ReplVals.scala b/src/compiler/scala/tools/nsc/interpreter/ReplVals.scala index 8b891e0010..4efab7e260 100644 --- a/src/compiler/scala/tools/nsc/interpreter/ReplVals.scala +++ b/src/compiler/scala/tools/nsc/interpreter/ReplVals.scala @@ -27,7 +27,8 @@ class StdReplVals(final val r: ILoop) extends ReplVals { final lazy val phased = power.phased final lazy val analyzer = global.analyzer - final lazy val treedsl = new { val global: intp.global.type = intp.global } with ast.TreeDSL { } + object treedsl extends { val global: intp.global.type = intp.global } with ast.TreeDSL { } + final lazy val typer = analyzer.newTyper( analyzer.rootContext( power.unit("").asInstanceOf[analyzer.global.CompilationUnit] @@ -35,13 +36,15 @@ class StdReplVals(final val r: ILoop) extends ReplVals { ) def lastRequest = intp.lastRequest - final lazy val replImplicits = new power.Implicits2 { + class ReplImplicits extends power.Implicits2 { import intp.global._ private val manifestFn = ReplVals.mkManifestToType[intp.global.type](global) implicit def mkManifestToType(sym: Symbol) = manifestFn(sym) } + final lazy val replImplicits = new ReplImplicits + def typed[T <: analyzer.global.Tree](tree: T): T = typer.typed(tree).asInstanceOf[T] } diff --git a/src/compiler/scala/tools/nsc/interpreter/RichClass.scala b/src/compiler/scala/tools/nsc/interpreter/RichClass.scala index 59a7b9b5d2..b1bee6ce93 100644 --- a/src/compiler/scala/tools/nsc/interpreter/RichClass.scala +++ b/src/compiler/scala/tools/nsc/interpreter/RichClass.scala @@ -13,7 +13,10 @@ class RichClass[T](val clazz: Class[T]) { // Sadly isAnonymousClass does not return true for scala anonymous // classes because our naming scheme is not doing well against the // jvm's many assumptions. - def isScalaAnonymous = clazz.isAnonymousClass || (clazz.getName contains "$anon$") + def isScalaAnonymous = ( + try clazz.isAnonymousClass || (clazz.getName contains "$anon$") + catch { case _: java.lang.InternalError => false } // good ol' "Malformed class name" + ) /** It's not easy... to be... me... */ def supermans: List[Manifest[_]] = supers map (_.toManifest) diff --git a/src/compiler/scala/tools/nsc/interpreter/TypeStrings.scala b/src/compiler/scala/tools/nsc/interpreter/TypeStrings.scala index 872ac00bfd..9ba75d9166 100644 --- a/src/compiler/scala/tools/nsc/interpreter/TypeStrings.scala +++ b/src/compiler/scala/tools/nsc/interpreter/TypeStrings.scala @@ -11,6 +11,142 @@ import r.TypeVariable import scala.reflect.NameTransformer import NameTransformer._ import scala.reflect.{mirror => rm} +import typechecker.DestructureTypes +import scala.tools.util.StringOps.ojoin + +/** A more principled system for turning types into strings. + */ +trait StructuredTypeStrings extends DestructureTypes { + val global: Global + import global._ + import definitions._ + + case class LabelAndType(label: String, typeName: String) { } + object LabelAndType { + val empty = LabelAndType("", "") + } + case class Grouping(ldelim: String, mdelim: String, rdelim: String, labels: Boolean) { + def join(elems: String*): String = ( + if (elems.isEmpty) "" + else elems.mkString(ldelim, mdelim, rdelim) + ) + } + val NoGrouping = Grouping("", "", "", false) + val ListGrouping = Grouping("(", ", ", ")", false) + val ProductGrouping = Grouping("(", ", ", ")", true) + val ParamGrouping = Grouping("(", ", ", ")", true) + val BlockGrouping = Grouping(" { ", "; ", "}", false) + + private implicit def lowerName(n: Name): String = "" + n + private def str(level: Int)(body: => String): String = " " * level + body + private def block(level: Int, grouping: Grouping)(name: String, nodes: List[TypeNode]): String = { + val l1 = str(level)(name + grouping.ldelim) + val l2 = nodes.map(_ show level + 1) + val l3 = str(level)(grouping.rdelim) + + l1 +: l2 :+ l3 mkString "\n" + } + private def maybeBlock(level: Int, grouping: Grouping)(name: String, nodes: List[TypeNode]): String = { + import grouping._ + val threshold = 70 + + val try1 = str(level)(name + grouping.join(nodes map (_.show(0, grouping.labels)): _*)) + if (try1.length < threshold) try1 + else block(level, grouping)(name, nodes) + } + private def shortClass(x: Any) = { + if (opt.debug) { + val name = (x.getClass.getName split '.').last + val isAnon = name.reverse takeWhile (_ != '$') forall (_.isDigit) + val str = if (isAnon) name else (name split '$').last + + " // " + str + } + else "" + } + + sealed abstract class TypeNode { + def grouping: Grouping + def nodes: List[TypeNode] + + def show(indent: Int, showLabel: Boolean): String = maybeBlock(indent, grouping)(mkPrefix(showLabel), nodes) + def show(indent: Int): String = show(indent, true) + def show(): String = show(0) + + def withLabel(l: String): this.type = modifyNameInfo(_.copy(label = l)) + def withType(t: String): this.type = modifyNameInfo(_.copy(typeName = t)) + + def label = nameInfo.label + def typeName = nameInfo.typeName + + protected def mkPrefix(showLabel: Boolean) = { + val pre = if (showLabel && label != "") label + " = " else "" + pre + typeName + } + override def toString = show() // + "(toString)" + private var nameInfo: LabelAndType = LabelAndType.empty + private def modifyNameInfo(f: LabelAndType => LabelAndType): this.type = { + nameInfo = f(nameInfo) + this + } + } + case class TypeAtom[T](atom: T) extends TypeNode { + def grouping = NoGrouping + def nodes = Nil + override protected def mkPrefix(showLabel: Boolean) = + super.mkPrefix(showLabel) + atom + shortClass(atom) + } + case class TypeProduct(nodes: List[TypeNode]) extends TypeNode { + def grouping: Grouping = ProductGrouping + def emptyTypeName = "" + override def typeName = if (nodes.isEmpty) emptyTypeName else super.typeName + } + + /** For a NullaryMethod, in = TypeEmpty; for MethodType(Nil, _) in = TypeNil */ + class NullaryFunction(out: TypeNode) extends TypeProduct(List(out)) { + override def typeName = "NullaryMethodType" + } + class MonoFunction(in: TypeNode, out: TypeNode) extends TypeProduct(List(in, out)) { + override def typeName = "MethodType" + } + class PolyFunction(in: TypeNode, out: TypeNode) extends TypeProduct(List(in, out)) { + override def typeName = "PolyType" + } + + class TypeList(nodes: List[TypeNode]) extends TypeProduct(nodes) { + override def grouping = ListGrouping + override def emptyTypeName = "Nil" + override def typeName = "List" + } + class TypeScope(nodes: List[TypeNode]) extends TypeProduct(nodes) { + override def grouping = BlockGrouping + override def typeName = "Scope" + override def emptyTypeName = "EmptyScope" + } + + object TypeEmpty extends TypeNode { + override def grouping = NoGrouping + override def nodes = Nil + override def label = "" + override def typeName = "" + override def show(indent: Int, showLabel: Boolean) = "" + } + + object intoNodes extends DestructureType[TypeNode] { + def withLabel(node: TypeNode, label: String): TypeNode = node withLabel label + def withType(node: TypeNode, typeName: String): TypeNode = node withType typeName + + def wrapEmpty = TypeEmpty + def wrapSequence(nodes: List[TypeNode]) = new TypeList(nodes) + def wrapProduct(nodes: List[TypeNode]) = new TypeProduct(nodes) + def wrapPoly(in: TypeNode, out: TypeNode) = new PolyFunction(in, out) + def wrapMono(in: TypeNode, out: TypeNode) = if (in == wrapEmpty) new NullaryFunction(out) else new MonoFunction(in, out) + def wrapAtom[U](value: U) = new TypeAtom(value) + } + + def show(tp: Type): String = intoNodes(tp).show +} + /** Logic for turning a type into a String. The goal is to be * able to take some arbitrary object 'x' and obtain the most precise diff --git a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala index e9c3bef737..8d26f66370 100644 --- a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala +++ b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala @@ -129,11 +129,11 @@ trait ContextErrors { val retyped = typed (tree.duplicate setType null) val foundDecls = retyped.tpe.decls filter (sym => !sym.isConstructor && !sym.isSynthetic) - if (foundDecls.isEmpty) found + if (foundDecls.isEmpty || (found.typeSymbol eq NoSymbol)) found else { // The members arrive marked private, presumably because there was no // expected type and so they're considered members of an anon class. - foundDecls foreach (_ resetFlag (PRIVATE | PROTECTED)) + foundDecls foreach (_.makePublic) // TODO: if any of the found parents match up with required parents after normalization, // print the error so that they match. The major beneficiary there would be // java.lang.Object vs. AnyRef. diff --git a/src/compiler/scala/tools/nsc/typechecker/DestructureTypes.scala b/src/compiler/scala/tools/nsc/typechecker/DestructureTypes.scala new file mode 100644 index 0000000000..0b414801d6 --- /dev/null +++ b/src/compiler/scala/tools/nsc/typechecker/DestructureTypes.scala @@ -0,0 +1,206 @@ +/* NSC -- new Scala compiler +* Copyright 2005-2012 LAMP/EPFL +* @author Paul Phillips +*/ + +package scala.tools.nsc +package typechecker + +/** A generic means of breaking down types into their subcomponents. + * Types are decomposed top down, and recognizable substructure is + * dispatched via self-apparently named methods. Those methods can + * be overridden for custom behavior, but only the abstract methods + * require implementations, each of which must create some unknown + * "Node" type from its inputs. + * + * - wrapProduct create Node from a product of Nodes + * - wrapSequence create Node from a sequence of Nodes + * - wrapAtom create Node from an arbitrary value + * + * This is a work in progress. + */ +trait DestructureTypes { + val global: Global + import global._ + import definitions.{ NothingClass, AnyClass } + + trait DestructureType[Node] extends (Type => Node) { + def withLabel(node: Node, label: String): Node + def withType(node: Node, typeName: String): Node + + def wrapEmpty: Node + def wrapPoly(in: Node, out: Node): Node + def wrapMono(in: Node, out: Node): Node + def wrapProduct(nodes: List[Node]): Node + def wrapSequence(nodes: List[Node]): Node + def wrapAtom[U](value: U): Node + + private implicit def liftToTerm(name: String): TermName = newTermName(name) + + private val openSymbols = collection.mutable.Set[Symbol]() + + private def nodeList[T](elems: List[T], mkNode: T => Node): Node = + if (elems.isEmpty) wrapEmpty else list(elems map mkNode) + + private def scopeMemberList(elems: List[Symbol]): Node = nodeList(elems, wrapAtom) + private def typeList(elems: List[Type]): Node = nodeList(elems, this) + private def symbolList(elems: List[Symbol]): Node = nodeList(elems, wrapSymbolInfo) + private def treeList(elems: List[Tree]): Node = nodeList(elems, wrapTree) + private def annotationList(annots: List[AnnotationInfo]): Node = nodeList(annots, annotation) + + private def assocsNode(ann: AnnotationInfo): Node = { + val (names, args) = ann.assocs.toIndexedSeq.unzip + if (names.isEmpty) wrapEmpty + else node("assocs", nodeList(names.indices.toList, (i: Int) => atom(names(i).toString, args(i)))) + } + private def typeTypeName(tp: Type) = tp match { + case mt @ MethodType(_, _) if mt.isImplicit => "ImplicitMethodType" + case TypeRef(_, sym, _) => typeRefType(sym) + case _ => tp.kind + } + + def wrapTree(tree: Tree): Node = withType( + tree match { + case x: NameTree => atom(x.name.toString, x) + case _ => wrapAtom(tree) + }, + tree.printingPrefix + ) + def wrapSymbol(label: String, sym: Symbol): Node = { + if (sym eq NoSymbol) wrapEmpty + else atom(label, sym) + } + def wrapInfo(sym: Symbol) = sym.info match { + case TypeBounds(lo, hi) => typeBounds(lo, hi) + case PolyType(tparams, restpe) => polyFunction(tparams, restpe) + case _ => wrapEmpty + } + def wrapSymbolInfo(sym: Symbol): Node = { + if ((sym eq NoSymbol) || openSymbols(sym)) wrapEmpty + else { + openSymbols += sym + try product(symbolType(sym), wrapAtom(sym.defString)) + finally openSymbols -= sym + } + } + + def list(nodes: List[Node]): Node = wrapSequence(nodes) + def product(tp: Type, nodes: Node*): Node = product(typeTypeName(tp), nodes: _*) + def product(typeName: String, nodes: Node*): Node = ( + nodes.toList filterNot (_ == wrapEmpty) match { + case Nil => wrapEmpty + case xs => withType(wrapProduct(xs), typeName) + } + ) + + def atom[U](label: String, value: U): Node = node(label, wrapAtom(value)) + def constant(label: String, const: Constant): Node = atom(label, const) + + def scope(decls: Scope): Node = node("decls", scopeMemberList(decls.toList)) + def const[T](named: (String, T)): Node = constant(named._1, Constant(named._2)) + + def resultType(restpe: Type): Node = this("resultType", restpe) + def typeParams(tps: List[Symbol]): Node = node("typeParams", symbolList(tps)) + def valueParams(params: List[Symbol]): Node = node("params", symbolList(params)) + def typeArgs(tps: List[Type]): Node = node("args", typeList(tps)) + def parentList(tps: List[Type]): Node = node("parents", typeList(tps)) + + def polyFunction(tparams: List[Symbol], restpe: Type): Node = wrapPoly(typeParams(tparams), resultType(restpe)) + def monoFunction(params: List[Symbol], restpe: Type): Node = wrapMono(valueParams(params), resultType(restpe)) + def nullaryFunction(restpe: Type): Node = wrapMono(wrapEmpty, this(restpe)) + + def prefix(pre: Type): Node = pre match { + case NoPrefix => wrapEmpty + case _ => this("pre", pre) + } + def typeBounds(lo0: Type, hi0: Type): Node = { + val lo = if ((lo0 eq WildcardType) || (lo0.typeSymbol eq NothingClass)) wrapEmpty else this("lo", lo0) + val hi = if ((hi0 eq WildcardType) || (hi0.typeSymbol eq AnyClass)) wrapEmpty else this("hi", hi0) + + product("TypeBounds", lo, hi) + } + + def annotation(ann: AnnotationInfo): Node = product( + "AnnotationInfo", + this("atp", ann.atp), + node("args", treeList(ann.args)), + assocsNode(ann) + ) + def typeConstraint(constr: TypeConstraint): Node = product( + "TypeConstraint", + node("lo", typeList(constr.loBounds)), + node("hi", typeList(constr.hiBounds)), + this("inst", constr.inst) + ) + def annotatedType(annotations: List[AnnotationInfo], underlying: Type) = product( + "AnnotatedType", + node("annotations", annotationList(annotations)), + this("underlying", underlying) + ) + + /** This imposes additional structure beyond that which is visible in + * the case class hierarchy. In particular, (too) many different constructs + * are encoded in TypeRefs; here they are partitioned somewhat before + * being dispatched. + * + * For example, a typical type parameter is encoded as TypeRef(NoPrefix, sym, Nil) + * with its upper and lower bounds stored in the info of the symbol. Viewing the + * TypeRef naively we are treated to both too much information (useless prefix, usually + * empty args) and too little (bounds hidden behind indirection.) So drop the prefix + * and promote the bounds. + */ + def typeRef(tp: TypeRef) = { + val TypeRef(pre, sym, args) = tp + // Filtered down to elements with "interesting" content + product( + tp, + if (sym.isDefinedInPackage) wrapEmpty else prefix(pre), + wrapSymbolInfo(sym), + typeArgs(args), + if (tp ne tp.normalize) this("normalize", tp.normalize) else wrapEmpty + ) + } + + def symbolType(sym: Symbol) = ( + if (sym.isRefinementClass) "Refinement" + else if (sym.isAliasType) "Alias" + else if (sym.isTypeSkolem) "TypeSkolem" + else if (sym.isTypeParameter) "TypeParam" + else if (sym.isAbstractType) "AbstractType" + else if (sym.isType) "TypeSymbol" + else "TermSymbol" + ) + def typeRefType(sym: Symbol) = ( + if (sym.isRefinementClass) "RefinementTypeRef" + else if (sym.isAliasType) "AliasTypeRef" + else if (sym.isTypeSkolem) "SkolemTypeRef" + else if (sym.isTypeParameter) "TypeParamTypeRef" + else if (sym.isAbstractType) "AbstractTypeRef" + else "TypeRef" + ) + ( if (sym.isFBounded) "(F-Bounded)" else "" ) + + def node(label: String, node: Node): Node = withLabel(node, label) + def apply(label: String, tp: Type): Node = withLabel(this(tp), label) + + def apply(tp: Type): Node = tp match { + case AntiPolyType(pre, targs) => product(tp, prefix(pre), typeArgs(targs)) + case ClassInfoType(parents, decls, clazz) => product(tp, parentList(parents), scope(decls), wrapAtom(clazz)) + case ConstantType(const) => product(tp, constant("value", const)) + case DeBruijnIndex(level, index, args) => product(tp, const("level" -> level), const("index" -> index), typeArgs(args)) + case OverloadedType(pre, alts) => product(tp, prefix(pre), node("alts", typeList(alts map pre.memberType))) + case RefinedType(parents, decls) => product(tp, parentList(parents), scope(decls)) + case SingleType(pre, sym) => product(tp, prefix(pre), wrapAtom(sym)) + case SuperType(thistp, supertp) => product(tp, this("this", thistp), this("super", supertp)) + case ThisType(clazz) => product(tp, wrapAtom(clazz)) + case TypeVar(inst, constr) => product(tp, this("inst", inst), typeConstraint(constr)) + case AnnotatedType(annotations, underlying, _) => annotatedType(annotations, underlying) + case ExistentialType(tparams, underlying) => polyFunction(tparams, underlying) + case PolyType(tparams, restpe) => polyFunction(tparams, restpe) + case MethodType(params, restpe) => monoFunction(params, restpe) + case NullaryMethodType(restpe) => nullaryFunction(restpe) + case TypeBounds(lo, hi) => typeBounds(lo, hi) + case tr @ TypeRef(pre, sym, args) => typeRef(tr) + case _ => wrapAtom(tp) // XXX see what this is + } + } +} diff --git a/src/library/scala/collection/SeqExtractors.scala b/src/library/scala/collection/SeqExtractors.scala index cb3cb27f18..cbb09a0a90 100644 --- a/src/library/scala/collection/SeqExtractors.scala +++ b/src/library/scala/collection/SeqExtractors.scala @@ -19,3 +19,6 @@ object :+ { if(t.isEmpty) None else Some(t.init -> t.last) } + +// Dummy to fool ant +private abstract class SeqExtractors \ No newline at end of file diff --git a/test/files/jvm/interpreter.check b/test/files/jvm/interpreter.check index b9ff6afa2b..292d2fc4cb 100644 --- a/test/files/jvm/interpreter.check +++ b/test/files/jvm/interpreter.check @@ -316,9 +316,8 @@ scala> /* */ */ -scala> -scala> +You typed two blank lines. Starting a new command. scala> // multi-line string @@ -326,7 +325,7 @@ scala> """ hello there """ -res9: String = +res12: String = " hello there diff --git a/test/files/run/repl-colon-type.check b/test/files/run/repl-colon-type.check index 66c2fcc77f..cb0b9a6c8b 100644 --- a/test/files/run/repl-colon-type.check +++ b/test/files/run/repl-colon-type.check @@ -7,6 +7,12 @@ scala> :type List[1, 2, 3] :2: error: identifier expected but integer literal found. List[1, 2, 3] ^ +:3: error: ']' expected but '}' found. + } + ^ +:1: error: identifier expected but integer literal found. + List[1, 2, 3] + ^ scala> :type List(1, 2, 3) @@ -25,7 +31,7 @@ scala> :type def f[T >: Null, U <: String](x: T, y: U) = Set(x, y) [T >: Null, U <: String](x: T, y: U)scala.collection.immutable.Set[Any] scala> :type def x = 1 ; def bar[T >: Null <: AnyRef](xyz: T) = 5 -[T >: Null <: AnyRef](xyz: T)Int +=> Int [T >: Null <: AnyRef](xyz: T)Int scala> @@ -39,10 +45,19 @@ scala> :type lazy val f = 5 Int scala> :type protected lazy val f = 5 -Int +:2: error: illegal start of statement (no modifiers allowed here) + protected lazy val f = 5 + ^ +:5: error: lazy value f cannot be accessed in object $iw + Access to protected value f not permitted because + enclosing object $eval in package $line19 is not a subclass of + object $iw where target is defined + lazy val $result = `f` + ^ + scala> :type def f = 5 -Int +=> Int scala> :type def f() = 5 ()Int @@ -54,4 +69,156 @@ scala> :type def g[T](xs: Set[_ <: T]) = Some(xs.head) scala> +scala> // verbose! + +scala> :type -v List(1,2,3) filter _ +// Type signature +(Int => Boolean) => List[Int] + +// Internal Type structure +TypeRef( + TypeSymbol(abstract trait Function1[-T1, +R] extends Object) + args = List( + TypeRef( + TypeSymbol(abstract trait Function1[-T1, +R] extends Object) + args = List( + TypeRef(TypeSymbol(final class Int extends AnyVal)) + TypeRef(TypeSymbol(final class Boolean extends AnyVal)) + ) + ) + TypeRef( + TypeSymbol( + sealed abstract class List[+A] extends AbstractSeq[A] with LinearSeq[A] with Product with GenericTraversableTemplate[A,List] with LinearSeqOptimized[A,List[A]] + + ) + args = List( + TypeRef(TypeSymbol(final class Int extends AnyVal)) + ) + ) + ) +) + +scala> :type -v def f[T >: Null, U <: String](x: T, y: U) = Set(x, y) +// Type signature +[T >: Null, U <: String](x: T, y: U)scala.collection.immutable.Set[Any] + +// Internal Type structure +PolyType( + typeParams = List(TypeParam(T >: Null), TypeParam(U <: String)) + resultType = MethodType( + params = List(TermSymbol(x: T), TermSymbol(y: U)) + resultType = TypeRef( + TypeSymbol( + abstract trait Set[A] extends Iterable[A] with Set[A] with GenericSetTemplate[A,scala.collection.immutable.Set] with SetLike[A,scala.collection.immutable.Set[A]] with Parallelizable[A,scala.collection.parallel.immutable.ParSet[A]] + + ) + args = List(TypeRef(TypeSymbol(abstract class Any extends ))) + ) + ) +) + +scala> :type -v def x = 1 ; def bar[T >: Null <: AnyRef](xyz: T) = 5 +// Type signature +=> Int [T >: Null <: AnyRef](xyz: T)Int + +// Internal Type structure +OverloadedType( + alts = List( + NullaryMethodType( + TypeRef(TypeSymbol(final class Int extends AnyVal)) + ) + PolyType( + typeParams = List(TypeParam(T >: Null <: AnyRef)) + resultType = MethodType( + params = List(TermSymbol(xyz: T)) + resultType = TypeRef( + TypeSymbol(final class Int extends AnyVal) + ) + ) + ) + ) +) + +scala> :type -v Nil.combinations _ +// Type signature +Int => Iterator[List[Nothing]] + +// Internal Type structure +TypeRef( + TypeSymbol(abstract trait Function1[-T1, +R] extends Object) + args = List( + TypeRef(TypeSymbol(final class Int extends AnyVal)) + TypeRef( + TypeSymbol( + abstract trait Iterator[+A] extends TraversableOnce[A] + ) + args = List( + TypeRef( + TypeSymbol( + sealed abstract class List[+A] extends AbstractSeq[A] with LinearSeq[A] with Product with GenericTraversableTemplate[A,List] with LinearSeqOptimized[A,List[A]] + + ) + args = List( + TypeRef( + TypeSymbol(final abstract class Nothing extends Any) + ) + ) + ) + ) + ) + ) +) + +scala> :type -v def f[T <: AnyVal] = List[T]().combinations _ +// Type signature +[T <: AnyVal]=> Int => Iterator[List[T]] + +// Internal Type structure +PolyType( + typeParams = List(TypeParam(T <: AnyVal)) + resultType = NullaryMethodType( + TypeRef( + TypeSymbol(abstract trait Function1[-T1, +R] extends Object) + args = List( + TypeRef(TypeSymbol(final class Int extends AnyVal)) + TypeRef( + TypeSymbol( + abstract trait Iterator[+A] extends TraversableOnce[A] + ) + args = List( + TypeRef( + TypeSymbol( + sealed abstract class List[+A] extends AbstractSeq[A] with LinearSeq[A] with Product with GenericTraversableTemplate[A,List] with LinearSeqOptimized[A,List[A]] + + ) + args = List(TypeParamTypeRef(TypeParam(T <: AnyVal))) + ) + ) + ) + ) + ) + ) +) + +scala> :type -v def f[T, U >: T](x: T, y: List[U]) = x :: y +// Type signature +[T, U >: T](x: T, y: List[U])List[U] + +// Internal Type structure +PolyType( + typeParams = List(TypeParam(T), TypeParam(U >: T)) + resultType = MethodType( + params = List(TermSymbol(x: T), TermSymbol(y: List[U])) + resultType = TypeRef( + TypeSymbol( + sealed abstract class List[+A] extends AbstractSeq[A] with LinearSeq[A] with Product with GenericTraversableTemplate[A,List] with LinearSeqOptimized[A,List[A]] + + ) + args = List(TypeParamTypeRef(TypeParam(U >: T))) + ) + ) +) + +scala> + scala> diff --git a/test/files/run/repl-colon-type.scala b/test/files/run/repl-colon-type.scala index 39ab580d2a..c055b215c2 100644 --- a/test/files/run/repl-colon-type.scala +++ b/test/files/run/repl-colon-type.scala @@ -18,6 +18,14 @@ object Test extends ReplTest { |:type def f() = 5 | |:type def g[T](xs: Set[_ <: T]) = Some(xs.head) + | + |// verbose! + |:type -v List(1,2,3) filter _ + |:type -v def f[T >: Null, U <: String](x: T, y: U) = Set(x, y) + |:type -v def x = 1 ; def bar[T >: Null <: AnyRef](xyz: T) = 5 + |:type -v Nil.combinations _ + |:type -v def f[T <: AnyVal] = List[T]().combinations _ + |:type -v def f[T, U >: T](x: T, y: List[U]) = x :: y """.stripMargin } diff --git a/test/files/run/repl-power.check b/test/files/run/repl-power.check index e439a2a7f4..1e7b6f0cd8 100644 --- a/test/files/run/repl-power.check +++ b/test/files/run/repl-power.check @@ -14,7 +14,6 @@ scala> global.emptyValDef // "it is imported twice in the same scope by ..." res0: $r.global.emptyValDef.type = private val _ = _ scala> val tp = ArrayClass[scala.util.Random] // magic with manifests -warning: there were 2 feature warnings; re-run with -feature for details tp: $r.global.Type = Array[scala.util.Random] scala> tp.memberType(Array_apply) // evidence diff --git a/test/files/run/repl-type-verbose.check b/test/files/run/repl-type-verbose.check new file mode 100644 index 0000000000..103ac3e64d --- /dev/null +++ b/test/files/run/repl-type-verbose.check @@ -0,0 +1,186 @@ +Type in expressions to have them evaluated. +Type :help for more information. + +scala> + +scala> // verbose! + +scala> :type -v def f = 5 +// Type signature +=> Int + +// Internal Type structure +NullaryMethodType( + TypeRef(TypeSymbol(final class Int extends AnyVal)) +) + +scala> :type -v def f() = 5 +// Type signature +()Int + +// Internal Type structure +NullaryMethodType( + resultType = TypeRef(TypeSymbol(final class Int extends AnyVal)) +) + +scala> :type -v def f[T] = 5 +// Type signature +[T]=> Int + +// Internal Type structure +PolyType( + typeParams = List(TypeParam(T)) + resultType = NullaryMethodType( + TypeRef(TypeSymbol(final class Int extends AnyVal)) + ) +) + +scala> :type -v def f[T >: Null] = 5 +// Type signature +[T >: Null]=> Int + +// Internal Type structure +PolyType( + typeParams = List(TypeParam(T >: Null)) + resultType = NullaryMethodType( + TypeRef(TypeSymbol(final class Int extends AnyVal)) + ) +) + +scala> :type -v def f[T <: String] = 5 +// Type signature +[T <: String]=> Int + +// Internal Type structure +PolyType( + typeParams = List(TypeParam(T <: String)) + resultType = NullaryMethodType( + TypeRef(TypeSymbol(final class Int extends AnyVal)) + ) +) + +scala> :type -v def f[T]() = 5 +// Type signature +[T]()Int + +// Internal Type structure +PolyType( + typeParams = List(TypeParam(T)) + resultType = NullaryMethodType( + resultType = TypeRef(TypeSymbol(final class Int extends AnyVal)) + ) +) + +scala> :type -v def f[T, U]() = 5 +// Type signature +[T, U]()Int + +// Internal Type structure +PolyType( + typeParams = List(TypeParam(T), TypeParam(U)) + resultType = NullaryMethodType( + resultType = TypeRef(TypeSymbol(final class Int extends AnyVal)) + ) +) + +scala> :type -v def f[T, U]()() = 5 +// Type signature +[T, U]()()Int + +// Internal Type structure +PolyType( + typeParams = List(TypeParam(T), TypeParam(U)) + resultType = NullaryMethodType( + resultType = NullaryMethodType( + resultType = TypeRef( + TypeSymbol(final class Int extends AnyVal) + ) + ) + ) +) + +scala> :type -v def f[T, U <: T] = 5 +// Type signature +[T, U <: T]=> Int + +// Internal Type structure +PolyType( + typeParams = List(TypeParam(T), TypeParam(U <: T)) + resultType = NullaryMethodType( + TypeRef(TypeSymbol(final class Int extends AnyVal)) + ) +) + +scala> :type -v def f[T, U <: T](x: T)(y: U) = 5 +// Type signature +[T, U <: T](x: T)(y: U)Int + +// Internal Type structure +PolyType( + typeParams = List(TypeParam(T), TypeParam(U <: T)) + resultType = MethodType( + params = List(TermSymbol(x: T)) + resultType = MethodType( + params = List(TermSymbol(y: U)) + resultType = TypeRef( + TypeSymbol(final class Int extends AnyVal) + ) + ) + ) +) + +scala> :type -v def f[T: Ordering] = 5 +// Type signature +[T](implicit evidence$1: Ordering[T])Int + +// Internal Type structure +PolyType( + typeParams = List(TypeParam(T)) + resultType = MethodType( + params = List(TermSymbol(implicit evidence$1: Ordering[T])) + resultType = TypeRef(TypeSymbol(final class Int extends AnyVal)) + ) +) + +scala> :type -v def f[T: Ordering] = implicitly[Ordering[T]] +// Type signature +[T](implicit evidence$1: Ordering[T])Ordering[T] + +// Internal Type structure +PolyType( + typeParams = List(TypeParam(T)) + resultType = MethodType( + params = List(TermSymbol(implicit evidence$1: Ordering[T])) + resultType = AliasTypeRef( + Alias(type Ordering[T] = scala.math.Ordering[T]) + args = List(TypeParamTypeRef(TypeParam(T))) + normalize = TypeRef( + TypeSymbol( + abstract trait Ordering[T] extends Comparator[T] with PartialOrdering[T] with Serializable + + ) + args = List(TypeParamTypeRef(TypeParam(T))) + ) + ) + ) +) + +scala> :type -v def f[T <: { type Bippy = List[Int] ; def g(): Bippy }] = 5 +// Type signature +[T <: AnyRef{type Bippy = List[Int]; def g(): this.Bippy}]=> Int + +// Internal Type structure +PolyType( + typeParams = List( + TypeParam( + T <: AnyRef{type Bippy = List[Int]; def g(): this.Bippy} + ) + ) + resultType = NullaryMethodType( + TypeRef(TypeSymbol(final class Int extends AnyVal)) + ) +) + +scala> + +scala> diff --git a/test/files/run/repl-type-verbose.scala b/test/files/run/repl-type-verbose.scala new file mode 100644 index 0000000000..10c390550a --- /dev/null +++ b/test/files/run/repl-type-verbose.scala @@ -0,0 +1,20 @@ +import scala.tools.partest.ReplTest + +object Test extends ReplTest { + def code = """ + |// verbose! + |:type -v def f = 5 + |:type -v def f() = 5 + |:type -v def f[T] = 5 + |:type -v def f[T >: Null] = 5 + |:type -v def f[T <: String] = 5 + |:type -v def f[T]() = 5 + |:type -v def f[T, U]() = 5 + |:type -v def f[T, U]()() = 5 + |:type -v def f[T, U <: T] = 5 + |:type -v def f[T, U <: T](x: T)(y: U) = 5 + |:type -v def f[T: Ordering] = 5 + |:type -v def f[T: Ordering] = implicitly[Ordering[T]] + |:type -v def f[T <: { type Bippy = List[Int] ; def g(): Bippy }] = 5 + """.stripMargin +} -- cgit v1.2.3