diff options
Diffstat (limited to 'src/dotty/tools/dotc')
-rw-r--r-- | src/dotty/tools/dotc/core/Contexts.scala | 23 | ||||
-rw-r--r-- | src/dotty/tools/dotc/core/Printers.scala | 406 |
2 files changed, 374 insertions, 55 deletions
diff --git a/src/dotty/tools/dotc/core/Contexts.scala b/src/dotty/tools/dotc/core/Contexts.scala index a5cc8d493..c4c9e7764 100644 --- a/src/dotty/tools/dotc/core/Contexts.scala +++ b/src/dotty/tools/dotc/core/Contexts.scala @@ -16,6 +16,25 @@ import config.{Settings, Platform, JavaPlatform} object Contexts { + /** A context is passed basically everywhere in dotc. + * This is convenient but carries the risk of captured contexts in + * objects that turn into space leaks. To combat this risk, here are some + * conventions to follow: + * + * - Never let an implicit context be an argument of a class whose instances + * live longer than the context. + * - Classes that need contexts for their initialization take an explicit parameter + * named `initctx`. They pass initctx to all positions where it is needed + * (and these positions should all be part of the intialization sequence of the class). + * - Classes that need contexts that survive initialization are passed + * a "condensed context", typically named `cctx` instead. Consensed contexts + * just add some basic information to the context base without the + * risk of capturing complete trees. + * - To make sure these rules are kept, it would be good to do a sanity + * check using bytecode inspection with javap or scalap: Keep track + * of all class fields of type context; allow them only in whitelisted + * classes (which should be short-lived). + */ abstract class Context extends Periods with Substituters with TypeOps @@ -182,6 +201,10 @@ object Contexts { private[core] val pendingVolatiles = new mutable.HashSet[Type] } + object Context { + implicit def toPrinter(ctx: Context) = ctx.printer(ctx) + } + implicit def ctxToBase(ctx: Context): ContextBase = ctx.base /** Initial size of superId table */ diff --git a/src/dotty/tools/dotc/core/Printers.scala b/src/dotty/tools/dotc/core/Printers.scala index e956a3a53..44a647f50 100644 --- a/src/dotty/tools/dotc/core/Printers.scala +++ b/src/dotty/tools/dotc/core/Printers.scala @@ -2,16 +2,15 @@ package dotty.tools.dotc package core import Types._, Symbols._, Contexts._, Scopes._, Names._, NameOps._, Flags._ +import Constants._, Annotations._, StdNames._ +import java.lang.Integer.toOctalString +import scala.annotation.switch trait Printers { this: Context => - def show(tp: Type): String = printer(this).show(tp) - def show(sym: Symbol): String = printer(this).show(sym) - def showLocated(sym: Symbol): String = printer(this).showLocated(sym) - def showDef(sym: Symbol): String = printer(this).showDef(sym) - def show(sc: Scope): String = printer(this).show(sc) - def show(syms: List[Symbol], sep: String): String = printer(this).show(syms, sep) - def showNameDetailed(name: Name) = printer(this).showNameDetailed(name) + import Printers._ + + def show(tp: Type): String = printer(this).show(tp, GlobalPrec) private var _diagnostics: Option[StringBuilder] = _ @@ -25,34 +24,79 @@ trait Printers { this: Context => } } - object Printers { + class Precedence(val value: Int) extends AnyVal { + def parenthesize(nested: Precedence)(str: String) = + if (nested.value < value) "(" + str + ")" else str + } + + val DotPrec = new Precedence(4) + val AndPrec = new Precedence(3) + val OrPrec = new Precedence(2) + val WithPrec = new Precedence(1) + val LeftArrowPrec = new Precedence(1) + val GlobalPrec = new Precedence(0) + trait PrinterBase { self: ContextBase => private[core] var showRecursions = 0 } abstract class Printer { - def show(tp: Type): String - def show(sym: Symbol): String - def showLocated(sym: Symbol): String - def showDef(sym: Symbol): String - def show(sc: Scope): String - def show(syms: List[Symbol], sep: String): String - def showNameDetailed(name: Name): String - def showFullName(sym: Symbol): String - /** String representation of symbol's simple name. - * If !settings.debug translates expansions of operators back to operator symbol. + /** Show name, same as name.toString */ + def show(name: Name): String + + /** Show name with "type" or "term" prefix */ + def showDetailed(name: Name): String + + /** Show type in context with given precedence */ + def show(tp: Type, precedence: Precedence): String + + /** Show name of symbol. + * If !settings.debug shows original name and + * translates expansions of operators back to operator symbol. * E.g. $eq => =. * If settings.uniqid, adds id. - * If settings.Yshowsymkinds, adds abbreviated symbol kind. */ def showName(sym: Symbol): String + + /** Show fully qualified name of symbol */ + def showFullName(sym: Symbol): String + + /** Show kind of symbol */ + def showKind(sym: Symbol): String + + /** String representation, including symbol's kind e.g., "class Foo", "method Bar". + * If hasMeaninglessName is true, uses the owner's name to disambiguate identity. + */ + def show(sym: Symbol): String + + /** Show symbol's declaration */ + def showDcl(sym: Symbol): String + + /** If symbol's owner is printable class C, the string "in C", otherwise "" */ + def showLocation(sym: Symbol): String + + /** Show symbol and its location */ + def showLocated(sym: Symbol): String + + /** Show constant */ + def show(const: Constant) + + /** Show annotation */ + def show(annot: Annotation): String + + /** Show all symbols in given list separated by `sep`, using `showDcl` for each */ + def show(syms: List[Symbol], sep: String): String + + /** Show all definitions in a scope usng `showDcl` for each */ + def show(sc: Scope): String } class PlainPrinter(_ctx: Context) extends Printer { protected[this] implicit val ctx = _ctx + def controlled(op: => String): String = if (ctx.showRecursions < maxShowRecursions) try { @@ -71,10 +115,114 @@ object Printers { (new Throwable).printStackTrace } - protected def showSimpleName(sym: Symbol) = sym.originalName.decode + /** Concatenate strings separated by spaces */ + protected def compose(ss: String*) = ss filter (_.nonEmpty) mkString " " + + /** If the name of the symbol's owner should be used when you care about + * seeing an interesting name: in such cases this symbol is e.g. a method + * parameter with a synthetic name, a constructor named "this", an object + * "package", etc. The kind string, if non-empty, will be phrased relative + * to the name of the owner. + */ + protected def hasMeaninglessName(sym: Symbol) = ( + (sym is Param) && sym.owner.isSetter // x$1 + || sym.isClassConstructor // this + || (sym.name == nme.PACKAGE) // package + ) + + def show(name: Name): String = name.toString + + def showDetailed(name: Name): String = + (if (name.isTypeName) "type " else "term ") + name + + /** String representation of a name used in a refinement + * In refined printing this undoes type parameter expansion + */ + protected def showRefinementName(tp: RefinedType) = show(tp.name) - def showName(sym: Symbol): String = - showSimpleName(sym) + (if (ctx.settings.uniqid.value) "#" + sym.id else "") + /** String representation of a refinement */ + protected def showRefinement(rt: RefinedType) = + showRefinementName(rt) + showRHS(rt.info) + + /** The longest sequence refinement types, starting at given type + * and following parents. + */ + private def refinementChain(tp: Type): List[Type] = + tp :: (tp match { + case RefinedType(parent, _) => refinementChain(parent) + case _ => Nil + }) + + def show(tp: Type, prec: Precedence): String = controlled { + tp match { + case tp: TypeType => + showRHS(tp) + case tp: SingletonType => + val str = showPrefix(tp) + if (str.endsWith(".")) str + "type" + else showFullName(tp.underlying.typeSymbol.skipPackageObject) + ".type" + case TypeRef(pre, name) => + showPrefix(pre) + showName(tp.typeSymbol) + case tp: RefinedType => + val parent :: refined = refinementChain(tp).reverse + showLocal(parent) + + refined.asInstanceOf[List[RefinedType]].map(showRefinement).mkString("{", "; ", "}") + case AndType(tp1, tp2) => + (prec parenthesize AndPrec) { + show(tp1, AndPrec) + "&" + show(tp2, AndPrec) + } + case OrType(tp1, tp2) => + (prec parenthesize OrPrec) { + show(tp1, OrPrec) + "|" + show(tp2, OrPrec) + } + case ErrorType => + "<error>" + case WildcardType => + "?" + case NoType => + "<notype>" + case NoPrefix => + "<noprefix>" + case tp: MethodType => + (prec parenthesize GlobalPrec) { + val openStr = if (tp.isImplicit) "(implicit " else "(" + (tp.paramNames, tp.paramTypes).zipped + .map((name, tp) => show(name) + ": " + showGlobal(tp)) + .mkString(openStr, ", ", ")" + showGlobal(tp.resultType)) + } + case tp: ExprType => + (prec parenthesize GlobalPrec) { + "=> " + showGlobal(tp.resultType) + } + case tp: PolyType => + (prec parenthesize GlobalPrec) { + (tp.paramNames, tp.paramBounds).zipped + .map((name, bounds) => show(name) + showGlobal(bounds)) + .mkString("[", ", ", "]" + showGlobal(tp.resultType)) + } + case PolyParam(pt, n) => + show(pt.paramNames(n)) + case AnnotatedType(annots, tpe) => + showLocal(tpe) + " " + annots.map(show).mkString(" ") + } + } + + /** Show type within highest precedence */ + protected def showLocal(tp: Type) = show(tp, DotPrec) + + /** Show type within lowest precedence */ + protected def showGlobal(tp: Type) = show(tp, GlobalPrec) + + /** Show name of symbol without unique id. Under refined printing, + * shows decoded original name. + */ + protected def showSimpleName(sym: Symbol) = show(sym.name) + + /** Show unique id of symbol, after a # */ + protected def showId(sym: Symbol) = + if (ctx.settings.uniqid.value) "#" + sym.id else "" + + def showName(sym: Symbol): String = showSimpleName(sym) + showId(sym) def showFullName(sym: Symbol): String = if (sym.isRoot || sym == NoSymbol || sym.owner.isEffectiveRoot) @@ -91,68 +239,165 @@ object Printers { protected def isOmittablePrefix(sym: Symbol) = (defn.UnqualifiedOwners contains sym) || isEmptyPrefix(sym) - protected def isEmptyPrefix(sym: Symbol) = + protected def isEmptyPrefix(sym: Symbol) = sym.isEffectiveRoot || sym.isAnonymousClass || sym.name.isReplWrapperName - def showPrefix(tp: Type): String = controlled { + @switch private def escapedChar(ch: Char): String = ch match { + case '\b' => "\\b" + case '\t' => "\\t" + case '\n' => "\\n" + case '\f' => "\\f" + case '\r' => "\\r" + case '"' => "\\\"" + case '\'' => "\\\'" + case '\\' => "\\\\" + case _ => if (ch.isControl) "\\0" + toOctalString(ch) else String.valueOf(ch) + } + + /** The string representation of this type used as a prefix */ + protected def showPrefix(tp: Type): String = controlled { tp match { - case RefinedThis(_) => - "this." + case tp @ TermRef(pre, name) => + showPrefix(pre) + showName(tp.symbol) + "." case ThisType(cls) => showName(cls) + ".this." case SuperType(thistpe, _) => showPrefix(thistpe).replaceAll("""\bthis\.$""", "super.") - case tp @ TermRef(pre, name) => - val sym = tp.symbol - if (!sym.exists) showPrefix(pre) + name + "." - else showPrefix(pre) + showName(sym) + "." + case tp @ ConstantType(value) => + showLocal(tp.underlying) + "(" + show(value) + ")." case MethodParam(mt, idx) => - mt.paramNames(idx) + "." + show(mt.paramNames(idx)) + "." + case RefinedThis(_) => + "this." case NoPrefix => "" - case ConstantType(value) => - "(" + value + ")." case _ => - trimPrefix(show(tp)) + "#" + trimPrefix(showLocal(tp)) + "#" } } - def show(tp: Type): String = controlled { + /** String representation of a definition's type following its name */ + protected def showRHS(tp: Type): String = controlled { tp match { - case tp: SingletonType => - val str = showPrefix(tp) - if (str.endsWith(".")) str + "type" - else showFullName(tp.underlying.typeSymbol.skipPackageObject) + ".type" - case - TermRef(pre, name) => - ??? // showPrefix(pre) + show(name) + case TypeBounds(lo, hi) => + if (lo eq hi) + " = " + lo + else + (if (lo.typeSymbol == defn.NothingClass) "" else ">: " + lo) + + (if (hi.typeSymbol == defn.AnyClass) "" else "<: " + hi) + case ClassInfo(pre, cdenot) => + val preStr = showLocal(pre) + val selfStr = + if (cdenot.selfType == cdenot.typeConstructor) "" + else s"this: ${show(cdenot.selfType, LeftArrowPrec)} =>" + val parentsStr = cdenot.parents.map(show(_, WithPrec)).mkString(" with ") + val declsStr = + if (cdenot.decls.isEmpty) "" + else "\n " + show(cdenot.decls.toList, "\n ") + s"""$parentsStr { $selfStr$declsStr + |} at $preStr""".stripMargin + case _ => ": " + showGlobal(tp) + } + } + + /** Show kind of symbol */ + def showKind(sym: Symbol) = + if (sym.isPackageClass) "package class" + else if (sym.isPackageVal) "package" + else if (sym is PackageObjectClass) "package object class" + else if (sym is PackageObjectVal) "package object" + else if (sym.isAnonymousClass) "anonymous class" + else if (sym.isModuleClass) "module class" + else if (sym.isModuleVal) "module" + else if (sym is ImplClass) "implementation class" + else if (sym is Trait) "trait" + else if (sym.isClass) "class" + else if (sym.isType) "type" + else if (sym.isGetter) "getter" + else if (sym.isSetter) "setter" + else if (sym is Lazy) "lazy value" + else if (sym is Mutable) "variable" + else if (sym.isClassConstructor && sym.isPrimaryConstructor) "primary constructor" + else if (sym.isClassConstructor) "constructor" + else if (sym.isSourceMethod) "method" + else if (sym.isTerm) "value" + else "" + + /** String representation of symbol's definition key word */ + protected def showKey(sym: Symbol): String = + if (sym is JavaInterface) "interface" + else if (sym is (Trait, butNot = ImplClass)) "trait" + else if (sym.isClass) "class" + else if (sym.isType && !(sym is ExpandedTypeParam)) "type" + else if (sym is Mutable) "var" + else if (sym.isPackage) "package" + else if (sym.isModule) "object" + else if (sym.isSourceMethod) "def" + else if (sym.isTerm && (!(sym is Param))) "val" + else "" + + /** String representation of symbol's flags */ + protected def showFlags(sym: Symbol) = sym.flags.toString + + /** String representation of symbol's variance or "" if not applicable */ + protected def showVariance(sym: Symbol) = sym.variance match { + case -1 => "-" + case 1 => "+" + case _ => "" + } + def showDcl(sym: Symbol): String = compose( + showFlags(sym), + showKey(sym), + showVariance(sym) + showName(sym) + showRHS(sym.info)) + def show(sym: Symbol): String = compose( + showKind(sym), + if (hasMeaninglessName(sym)) showSimpleName(sym.owner) + showId(sym) + else showName(sym) + ) - } + def showLocation(sym: Symbol): String = { + val owns = sym.effectiveOwner + if (owns.isClass && !isEmptyPrefix(owns)) "in "+show(owns) else "" } + def showLocated(sym: Symbol): String = + show(sym) + showLocation(sym) + + def show(const: Constant) = { + ??? /* + def escape(text: String): String = text flatMap escapedChar + tag match { + case NullTag => "null" + case StringTag => "\"" + escape(stringValue) + "\"" + case ClazzTag => + def show(tpe: Type) = "classOf[" + signature(tpe) + "]" + typeValue match { + case ErasedValueType(orig) => show(orig) + case _ => show(typeValue) + } + case CharTag => "'" + escapedChar(charValue) + "'" + case LongTag => longValue.toString() + "L" + case EnumTag => symbolValue.name.toString() + case _ => String.valueOf(value) + }*/ + } - def show(sym: Symbol): String = controlled { + def show(annot: Annotation): String = ??? + + def show(syms: List[Symbol], sep: String): String = + syms map (_.showDcl) mkString sep - ??? - } - def showLocated(sym: Symbol): String = ??? - def showDef(sym: Symbol): String = ??? def show(sc: Scope): String = "Scope{\n" + show(sc.toList, ";\n ") + "\n}" - def show(syms: List[Symbol], sep: String): String = - syms map (_.showDef) mkString sep - - def showNameDetailed(name: Name): String = - (if (name.isTypeName) "type " else "term ") + name } class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { override protected def recursionLimitExceeeded() = {} - override protected def showSimpleName(sym: Symbol) = sym.name.toString + override protected def showSimpleName(sym: Symbol) = sym.originalName.decode override def showPrefix(tp: Type): String = controlled { tp match { @@ -168,8 +413,59 @@ object Printers { } super.showPrefix(tp) } + + override protected def showRefinementName(tp: RefinedType): String = { + val tsym = tp.member(tp.name).symbol + val name = tsym.originalName + show(if (tsym is ExpandedTypeParam) name.asTypeName.unexpandedName() else name) + } + + override def show(tp: Type, prec: Precedence): String = controlled { + def showFunction(args: List[Type]): String = + (prec parenthesize GlobalPrec) { + val argStr = + if (args.length == 2 && + !(defn.TupleClasses contains args.head.typeSymbol)) show(args.head, LeftArrowPrec) + else args.init.map(showGlobal(_)).mkString("(", ", ", ")") + argStr + " => " + showGlobal(args.last) + } + def showTuple(args: List[Type]): String = + args.map(showGlobal(_)).mkString("(", ", ", ")") + try { + tp match { + case tp: RefinedType => + val (tycon, args) = tp.splitArgs + if (args.nonEmpty) { + if (tycon.typeParams.length == args.length) { + val cls = tycon.typeSymbol + if (cls == defn.RepeatedParamClass) return showLocal(args.head) + "*" + if (cls == defn.ByNameParamClass) return "=> " + showGlobal(args.head) + if (defn.FunctionClasses contains cls) return showFunction(args) + if (defn.TupleClasses contains cls) return showTuple(args) + } + return showLocal(tycon) + args.map(showGlobal(_)).mkString("[", ", ", "]") + } + case _ => + } + } catch { + case ex: CyclicReference => + "<cylic reference during display>" + super.show(tp, prec) + } + super.show(tp, prec) + } + + override def showKind(sym: Symbol) = + if (sym.isPackage) "package" + else if (sym is PackageObject) "package object" + else if (sym is Module) "object" + else if (sym is ImplClass) "class" + else if (sym.isClassConstructor) "constructor" + else super.showKind(sym) + + override def showFlags(sym: Symbol) = + sym.flags.flagStrings.filterNot(_.startsWith("<")).mkString(" ") } final val maxShowRecursions = 100 -}
\ No newline at end of file +} |