diff options
Diffstat (limited to 'src/dotty/tools/dotc/core')
20 files changed, 311 insertions, 204 deletions
diff --git a/src/dotty/tools/dotc/core/Annotations.scala b/src/dotty/tools/dotc/core/Annotations.scala index 5f96a60e6..0e8e5a1f0 100644 --- a/src/dotty/tools/dotc/core/Annotations.scala +++ b/src/dotty/tools/dotc/core/Annotations.scala @@ -26,6 +26,8 @@ object Annotations { } def argumentConstant(i: Int)(implicit ctx: Context): Option[Constant] = for (ConstantType(c) <- argument(i) map (_.tpe)) yield c + + def ensureCompleted(implicit ctx: Context): Unit = tree } case class ConcreteAnnotation(t: Tree) extends Annotation { @@ -42,6 +44,36 @@ object Annotations { override def symbol(implicit ctx: Context): Symbol = sym } + /** An annotation indicating the body of a right-hand side, + * typically of an inline method. Treated specially in + * pickling/unpickling and TypeTreeMaps + */ + abstract class BodyAnnotation extends Annotation { + override def symbol(implicit ctx: Context) = defn.BodyAnnot + override def derivedAnnotation(tree: Tree)(implicit ctx: Context) = + if (tree eq this.tree) this else ConcreteBodyAnnotation(tree) + override def arguments(implicit ctx: Context) = Nil + override def ensureCompleted(implicit ctx: Context) = () + } + + case class ConcreteBodyAnnotation(body: Tree) extends BodyAnnotation { + def tree(implicit ctx: Context) = body + } + + case class LazyBodyAnnotation(bodyExpr: Context => Tree) extends BodyAnnotation { + private var evaluated = false + private var myBody: Tree = _ + def tree(implicit ctx: Context) = { + if (evaluated) assert(myBody != null) + else { + evaluated = true + myBody = bodyExpr(ctx) + } + myBody + } + def isEvaluated = evaluated + } + object Annotation { def apply(tree: Tree) = ConcreteAnnotation(tree) diff --git a/src/dotty/tools/dotc/core/Contexts.scala b/src/dotty/tools/dotc/core/Contexts.scala index cd76fe88b..313ea3124 100644 --- a/src/dotty/tools/dotc/core/Contexts.scala +++ b/src/dotty/tools/dotc/core/Contexts.scala @@ -18,7 +18,7 @@ import util.Positions._ import ast.Trees._ import ast.untpd import util.{FreshNameCreator, SimpleMap, SourceFile, NoSource} -import typer.{Implicits, ImplicitRunInfo, ImportInfo, NamerContextOps, SearchHistory, TypeAssigner, Typer} +import typer.{Implicits, ImplicitRunInfo, ImportInfo, Inliner, NamerContextOps, SearchHistory, TypeAssigner, Typer} import Implicits.ContextualImplicits import config.Settings._ import config.Config @@ -30,6 +30,7 @@ import config.{Settings, ScalaSettings, Platform, JavaPlatform, SJSPlatform} import language.implicitConversions import DenotTransformers.DenotTransformer import parsing.Scanners.Comment +import util.Property.Key import xsbti.AnalysisCallback object Contexts { @@ -177,9 +178,12 @@ object Contexts { def freshName(prefix: Name): String = freshName(prefix.toString) /** A map in which more contextual properties can be stored */ - private var _moreProperties: Map[String, Any] = _ - protected def moreProperties_=(moreProperties: Map[String, Any]) = _moreProperties = moreProperties - def moreProperties: Map[String, Any] = _moreProperties + private var _moreProperties: Map[Key[Any], Any] = _ + protected def moreProperties_=(moreProperties: Map[Key[Any], Any]) = _moreProperties = moreProperties + def moreProperties: Map[Key[Any], Any] = _moreProperties + + def property[T](key: Key[T]): Option[T] = + moreProperties.get(key).asInstanceOf[Option[T]] private var _typeComparer: TypeComparer = _ protected def typeComparer_=(typeComparer: TypeComparer) = _typeComparer = typeComparer @@ -459,9 +463,10 @@ object Contexts { def setTypeComparerFn(tcfn: Context => TypeComparer): this.type = { this.typeComparer = tcfn(this); this } def setSearchHistory(searchHistory: SearchHistory): this.type = { this.searchHistory = searchHistory; this } def setFreshNames(freshNames: FreshNameCreator): this.type = { this.freshNames = freshNames; this } - def setMoreProperties(moreProperties: Map[String, Any]): this.type = { this.moreProperties = moreProperties; this } + def setMoreProperties(moreProperties: Map[Key[Any], Any]): this.type = { this.moreProperties = moreProperties; this } - def setProperty(prop: (String, Any)): this.type = setMoreProperties(moreProperties + prop) + def setProperty[T](key: Key[T], value: T): this.type = + setMoreProperties(moreProperties.updated(key, value)) def setPhase(pid: PhaseId): this.type = setPeriod(Period(runId, pid)) def setPhase(phase: Phase): this.type = setPeriod(Period(runId, phase.start, phase.end)) diff --git a/src/dotty/tools/dotc/core/Decorators.scala b/src/dotty/tools/dotc/core/Decorators.scala index 387e7e466..3bf17730a 100644 --- a/src/dotty/tools/dotc/core/Decorators.scala +++ b/src/dotty/tools/dotc/core/Decorators.scala @@ -7,6 +7,7 @@ import Contexts._, Names._, Phases._, printing.Texts._, printing.Printer, printi import util.Positions.Position, util.SourcePosition import collection.mutable.ListBuffer import dotty.tools.dotc.transform.TreeTransforms._ +import ast.tpd._ import scala.language.implicitConversions import printing.Formatting._ @@ -40,7 +41,7 @@ object Decorators { */ implicit class ListDecorator[T](val xs: List[T]) extends AnyVal { - @inline final def mapconserve[U](f: T => U): List[U] = { + final def mapconserve[U](f: T => U): List[U] = { @tailrec def loop(mapped: ListBuffer[U], unchanged: List[U], pending: List[T]): List[U] = if (pending.isEmpty) { @@ -148,8 +149,15 @@ object Decorators { } } - implicit def sourcePos(pos: Position)(implicit ctx: Context): SourcePosition = - ctx.source.atPos(pos) + implicit def sourcePos(pos: Position)(implicit ctx: Context): SourcePosition = { + def recur(inlinedCalls: List[Tree], pos: Position): SourcePosition = inlinedCalls match { + case inlinedCall :: rest => + sourceFile(inlinedCall).atPos(pos).withOuter(recur(rest, inlinedCall.pos)) + case empty => + ctx.source.atPos(pos) + } + recur(enclosingInlineds, pos) + } implicit class StringInterpolators(val sc: StringContext) extends AnyVal { diff --git a/src/dotty/tools/dotc/core/Definitions.scala b/src/dotty/tools/dotc/core/Definitions.scala index cb83fda04..75b75d3d5 100644 --- a/src/dotty/tools/dotc/core/Definitions.scala +++ b/src/dotty/tools/dotc/core/Definitions.scala @@ -456,6 +456,8 @@ class Definitions { def AliasAnnot(implicit ctx: Context) = AliasAnnotType.symbol.asClass lazy val AnnotationDefaultAnnotType = ctx.requiredClassRef("dotty.annotation.internal.AnnotationDefault") def AnnotationDefaultAnnot(implicit ctx: Context) = AnnotationDefaultAnnotType.symbol.asClass + lazy val BodyAnnotType = ctx.requiredClassRef("dotty.annotation.internal.Body") + def BodyAnnot(implicit ctx: Context) = BodyAnnotType.symbol.asClass lazy val ChildAnnotType = ctx.requiredClassRef("dotty.annotation.internal.Child") def ChildAnnot(implicit ctx: Context) = ChildAnnotType.symbol.asClass lazy val CovariantBetweenAnnotType = ctx.requiredClassRef("dotty.annotation.internal.CovariantBetween") @@ -466,6 +468,10 @@ class Definitions { def DeprecatedAnnot(implicit ctx: Context) = DeprecatedAnnotType.symbol.asClass lazy val ImplicitNotFoundAnnotType = ctx.requiredClassRef("scala.annotation.implicitNotFound") def ImplicitNotFoundAnnot(implicit ctx: Context) = ImplicitNotFoundAnnotType.symbol.asClass + lazy val InlineAnnotType = ctx.requiredClassRef("scala.inline") + def InlineAnnot(implicit ctx: Context) = InlineAnnotType.symbol.asClass + lazy val InlineParamAnnotType = ctx.requiredClassRef("dotty.annotation.internal.InlineParam") + def InlineParamAnnot(implicit ctx: Context) = InlineParamAnnotType.symbol.asClass lazy val InvariantBetweenAnnotType = ctx.requiredClassRef("dotty.annotation.internal.InvariantBetween") def InvariantBetweenAnnot(implicit ctx: Context) = InvariantBetweenAnnotType.symbol.asClass lazy val MigrationAnnotType = ctx.requiredClassRef("scala.annotation.migration") diff --git a/src/dotty/tools/dotc/core/Flags.scala b/src/dotty/tools/dotc/core/Flags.scala index 0cdae6b98..3f4433708 100644 --- a/src/dotty/tools/dotc/core/Flags.scala +++ b/src/dotty/tools/dotc/core/Flags.scala @@ -431,7 +431,7 @@ object Flags { /** Flags representing source modifiers */ final val SourceModifierFlags = - commonFlags(Private, Protected, Abstract, Final, + commonFlags(Private, Protected, Abstract, Final, Inline, Sealed, Case, Implicit, Override, AbsOverride, Lazy, JavaStatic) /** Flags representing modifiers that can appear in trees */ @@ -450,7 +450,7 @@ object Flags { AccessFlags | Module | Package | Deferred | Final | MethodOrHKCommon | Param | ParamAccessor | Scala2ExistentialCommon | Mutable.toCommonFlags | InSuperCall | Touched | JavaStatic | CovariantOrOuter | ContravariantOrLabel | ExpandedName | AccessorOrSealed | CaseAccessorOrBaseTypeArg | Fresh | Frozen | Erroneous | ImplicitCommon | Permanent | Synthetic | - LazyOrTrait | SuperAccessorOrScala2x | SelfNameOrImplClass + Inline | LazyOrTrait | SuperAccessorOrScala2x | SelfNameOrImplClass assert(FromStartFlags.isTermFlags && FromStartFlags.isTypeFlags) // TODO: Should check that FromStartFlags do not change in completion @@ -529,8 +529,8 @@ object Flags { /** Either method or lazy or deferred */ final val MethodOrLazyOrDeferred = Method | Lazy | Deferred - /** Labeled `private` or `final` */ - final val PrivateOrFinal = Private | Final + /** Labeled `private`, `final`, or `inline` */ + final val PrivateOrFinalOrInline = Private | Final | Inline /** A private method */ final val PrivateMethod = allOf(Private, Method) @@ -541,6 +541,9 @@ object Flags { /** A type parameter with synthesized name */ final val ExpandedTypeParam = allOf(ExpandedName, TypeParam) + /** An inline method */ + final val InlineMethod = allOf(Inline, Method) + /** A parameter or parameter accessor */ final val ParamOrAccessor = Param | ParamAccessor @@ -553,6 +556,12 @@ object Flags { /** A type parameter or type parameter accessor */ final val TypeParamOrAccessor = TypeParam | TypeParamAccessor + /** A deferred member or a parameter accessor (these don't have right hand sides) */ + final val DeferredOrParamAccessor = Deferred | ParamAccessor + + /** value that's final or inline */ + final val FinalOrInline = Final | Inline + /** If symbol of a type alias has these flags, prefer the alias */ final val AliasPreferred = TypeParam | BaseTypeArg | ExpandedName diff --git a/src/dotty/tools/dotc/core/Mode.scala b/src/dotty/tools/dotc/core/Mode.scala index 3e9b7effe..7a9bb0572 100644 --- a/src/dotty/tools/dotc/core/Mode.scala +++ b/src/dotty/tools/dotc/core/Mode.scala @@ -89,5 +89,8 @@ object Mode { */ val AllowLambdaWildcardApply = newMode(15, "AllowHKApplyToWildcards") + /** Read original positions when unpickling from TASTY */ + val ReadPositions = newMode(16, "ReadPositions") + val PatternOrType = Pattern | Type } diff --git a/src/dotty/tools/dotc/core/NameOps.scala b/src/dotty/tools/dotc/core/NameOps.scala index f5e0eb8cd..48e823e81 100644 --- a/src/dotty/tools/dotc/core/NameOps.scala +++ b/src/dotty/tools/dotc/core/NameOps.scala @@ -84,6 +84,8 @@ object NameOps { name.stripAnonNumberSuffix endsWith MODULE_VAR_SUFFIX def isSelectorName = name.startsWith(" ") && name.tail.forall(_.isDigit) def isLazyLocal = name.endsWith(nme.LAZY_LOCAL) + def isOuterSelect = name.endsWith(nme.OUTER_SELECT) + def isInlineAccessor = name.startsWith(nme.INLINE_ACCESSOR_PREFIX) /** Is name a variable name? */ def isVariableName: Boolean = name.length > 0 && { @@ -166,7 +168,7 @@ object NameOps { // Hack to make super accessors from traits work. They would otherwise fail because of #765 // TODO: drop this once we have more robust name handling - if (name.slice(idx - FalseSuperLength, idx) == FalseSuper) + if (idx > FalseSuperLength && name.slice(idx - FalseSuperLength, idx) == FalseSuper) idx -= FalseSuper.length if (idx < 0) name else (name drop (idx + nme.EXPAND_SEPARATOR.length)).asInstanceOf[N] @@ -419,6 +421,8 @@ object NameOps { assert(name.isLazyLocal) name.dropRight(nme.LAZY_LOCAL.length) } + + def inlineAccessorName = nme.INLINE_ACCESSOR_PREFIX ++ name ++ "$" } private final val FalseSuper = "$$super".toTermName diff --git a/src/dotty/tools/dotc/core/StdNames.scala b/src/dotty/tools/dotc/core/StdNames.scala index f47ab1744..c52264637 100644 --- a/src/dotty/tools/dotc/core/StdNames.scala +++ b/src/dotty/tools/dotc/core/StdNames.scala @@ -46,6 +46,7 @@ object StdNames { final val IFkw: N = kw("if") final val IMPLICITkw: N = kw("implicit") final val IMPORTkw: N = kw("import") + final val INLINEkw: N = kw("inline") final val LAZYkw: N = kw("lazy") final val MACROkw: N = kw("macro") final val MATCHkw: N = kw("match") @@ -100,6 +101,7 @@ object StdNames { val EXPAND_SEPARATOR: N = "$$" val IMPL_CLASS_SUFFIX: N = "$class" val IMPORT: N = "<import>" + val INLINE_ACCESSOR_PREFIX = "$inlineAccessor$" val INTERPRETER_IMPORT_WRAPPER: N = "$iw" val INTERPRETER_LINE_PREFIX: N = "line" val INTERPRETER_VAR_PREFIX: N = "res" @@ -252,7 +254,7 @@ object StdNames { val MODULE_INSTANCE_FIELD: N = NameTransformer.MODULE_INSTANCE_NAME // "MODULE$" val OUTER: N = "$outer" val OUTER_LOCAL: N = "$outer " - val OUTER_SYNTH: N = "<outer>" // emitted by virtual pattern matcher, replaced by outer accessor in explicitouter + val OUTER_SELECT: N = "_<outer>" // emitted by inliner, replaced by outer path in explicitouter val REFINE_CLASS: N = "<refinement>" val ROOTPKG: N = "_root_" val SELECTOR_DUMMY: N = "<unapply-selector>" diff --git a/src/dotty/tools/dotc/core/SymDenotations.scala b/src/dotty/tools/dotc/core/SymDenotations.scala index ab45550a4..969d09c3e 100644 --- a/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/src/dotty/tools/dotc/core/SymDenotations.scala @@ -281,6 +281,15 @@ object SymDenotations { case nil => None } + /** The same as getAnnotation, but without ensuring + * that the symbol carrying the annotation is completed + */ + final def unforcedAnnotation(cls: Symbol)(implicit ctx: Context): Option[Annotation] = + dropOtherAnnotations(myAnnotations, cls) match { + case annot :: _ => Some(annot) + case nil => None + } + /** Add given annotation to the annotations of this denotation */ final def addAnnotation(annot: Annotation): Unit = annotations = annot :: myAnnotations @@ -289,6 +298,12 @@ object SymDenotations { final def removeAnnotation(cls: Symbol)(implicit ctx: Context): Unit = annotations = myAnnotations.filterNot(_ matches cls) + /** Remove any annotations with same class as `annot`, and add `annot` */ + final def updateAnnotation(annot: Annotation)(implicit ctx: Context): Unit = { + removeAnnotation(annot.symbol) + addAnnotation(annot) + } + /** Add all given annotations to this symbol */ final def addAnnotations(annots: TraversableOnce[Annotation])(implicit ctx: Context): Unit = annots.foreach(addAnnotation) @@ -670,9 +685,9 @@ object SymDenotations { val cls = owner.enclosingSubClass if (!cls.exists) fail( - i""" Access to protected $this not permitted because - | enclosing ${ctx.owner.enclosingClass.showLocated} is not a subclass of - | ${owner.showLocated} where target is defined""") + i""" + | Access to protected $this not permitted because enclosing ${ctx.owner.enclosingClass.showLocated} + | is not a subclass of ${owner.showLocated} where target is defined""") else if ( !( isType // allow accesses to types from arbitrary subclasses fixes #4737 || pre.baseTypeRef(cls).exists // ??? why not use derivesFrom ??? @@ -680,9 +695,9 @@ object SymDenotations { || (owner is ModuleClass) // don't perform this check for static members )) fail( - i""" Access to protected ${symbol.show} not permitted because - | prefix type ${pre.widen.show} does not conform to - | ${cls.showLocated} where the access takes place""") + i""" + | Access to protected ${symbol.show} not permitted because prefix type ${pre.widen.show} + | does not conform to ${cls.showLocated} where the access takes place""") else true } @@ -744,6 +759,11 @@ object SymDenotations { // def isOverridable: Boolean = !!! need to enforce that classes cannot be redefined def isSkolem: Boolean = name == nme.SKOLEM + def isInlineMethod(implicit ctx: Context): Boolean = + is(Method, butNot = Accessor) && + !isCompleting && // don't force method type; recursive inlines are ignored anyway. + hasAnnotation(defn.InlineAnnot) + // ------ access to related symbols --------------------------------- /* Modules and module classes are represented as follows: @@ -851,7 +871,7 @@ object SymDenotations { /** A symbol is effectively final if it cannot be overridden in a subclass */ final def isEffectivelyFinal(implicit ctx: Context): Boolean = - is(PrivateOrFinal) || !owner.isClass || owner.is(ModuleOrFinal) || owner.isAnonymousClass + is(PrivateOrFinalOrInline) || !owner.isClass || owner.is(ModuleOrFinal) || owner.isAnonymousClass /** The class containing this denotation which has the given effective name. */ final def enclosingClassNamed(name: Name)(implicit ctx: Context): Symbol = { @@ -1521,7 +1541,13 @@ object SymDenotations { /** Enter a symbol in given `scope` without potentially replacing the old copy. */ def enterNoReplace(sym: Symbol, scope: MutableScope)(implicit ctx: Context): Unit = { - require((sym.denot.flagsUNSAFE is Private) || !(this is Frozen) || (scope ne this.unforcedDecls) || sym.hasAnnotation(defn.ScalaStaticAnnot)) + + require( + (sym.denot.flagsUNSAFE is Private) || + !(this is Frozen) || + (scope ne this.unforcedDecls) || + sym.hasAnnotation(defn.ScalaStaticAnnot) || + sym.name.isInlineAccessor) scope.enter(sym) if (myMemberFingerPrint != FingerPrint.unknown) diff --git a/src/dotty/tools/dotc/core/SymbolLoaders.scala b/src/dotty/tools/dotc/core/SymbolLoaders.scala index 3f801bda5..4ae28c10b 100644 --- a/src/dotty/tools/dotc/core/SymbolLoaders.scala +++ b/src/dotty/tools/dotc/core/SymbolLoaders.scala @@ -198,7 +198,7 @@ abstract class SymbolLoader extends LazyType { try { val start = currentTime if (ctx.settings.debugTrace.value) - ctx.traceIndented(s">>>> loading ${root.debugString}", _ => s"<<<< loaded ${root.debugString}") { + ctx.doTraceIndented(s">>>> loading ${root.debugString}", _ => s"<<<< loaded ${root.debugString}") { doComplete(root) } else diff --git a/src/dotty/tools/dotc/core/TyperState.scala b/src/dotty/tools/dotc/core/TyperState.scala index 7b8867ccc..7e332b412 100644 --- a/src/dotty/tools/dotc/core/TyperState.scala +++ b/src/dotty/tools/dotc/core/TyperState.scala @@ -122,6 +122,18 @@ extends TyperState(r) { * type variables changes from this typer state to the current one. (2) Variables * that were temporarily instantiated in the current typer state are permanently * instantiated instead. + * + * A note on merging: An interesting test case is isApplicableSafe.scala. It turns out that this + * requires a context merge using the new `&' operator. Sequence of actions: + * 1) Typecheck argument in typerstate 1. + * 2) Cache argument. + * 3) Evolve same typer state (to typecheck other arguments, say) + * leading to a different constraint. + * 4) Take typechecked argument in same state. + * + * It turns out that the merge is needed not just for + * isApplicableSafe but also for (e.g. erased-lubs.scala) as well as + * many parts of dotty itself. */ override def commit()(implicit ctx: Context) = { val targetState = ctx.typerState diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala index 30d1c0136..2f1b6b829 100644 --- a/src/dotty/tools/dotc/core/Types.scala +++ b/src/dotty/tools/dotc/core/Types.scala @@ -228,8 +228,8 @@ object Types { !existsPart(!p(_)) /** Performs operation on all parts of this type */ - final def foreachPart(p: Type => Unit)(implicit ctx: Context): Unit = - new ForeachAccumulator(p).apply((), this) + final def foreachPart(p: Type => Unit, stopAtStatic: Boolean = false)(implicit ctx: Context): Unit = + new ForeachAccumulator(p, stopAtStatic).apply((), this) /** The parts of this type which are type or term refs */ final def namedParts(implicit ctx: Context): collection.Set[NamedType] = @@ -848,30 +848,42 @@ object Types { case tp => tp } - /** Follow aliases and dereferences LazyRefs and instantiated TypeVars until type - * is no longer alias type, LazyRef, or instantiated type variable. - */ - final def dealias(implicit ctx: Context): Type = this match { + private def dealias(keepAnnots: Boolean)(implicit ctx: Context): Type = this match { case tp: TypeRef => if (tp.symbol.isClass) tp else tp.info match { - case TypeAlias(tp) => tp.dealias + case TypeAlias(tp) => tp.dealias(keepAnnots) case _ => tp } case tp: TypeVar => val tp1 = tp.instanceOpt - if (tp1.exists) tp1.dealias else tp + if (tp1.exists) tp1.dealias(keepAnnots) else tp case tp: AnnotatedType => - tp.derivedAnnotatedType(tp.tpe.dealias, tp.annot) + val tp1 = tp.tpe.dealias(keepAnnots) + if (keepAnnots) tp.derivedAnnotatedType(tp1, tp.annot) else tp1 case tp: LazyRef => - tp.ref.dealias + tp.ref.dealias(keepAnnots) case app @ HKApply(tycon, args) => - val tycon1 = tycon.dealias - if (tycon1 ne tycon) app.superType.dealias + val tycon1 = tycon.dealias(keepAnnots) + if (tycon1 ne tycon) app.superType.dealias(keepAnnots) else this case _ => this } + /** Follow aliases and dereferences LazyRefs and instantiated TypeVars until type + * is no longer alias type, LazyRef, or instantiated type variable. + * Goes through annotated types and rewraps annotations on the result. + */ + final def dealiasKeepAnnots(implicit ctx: Context): Type = + dealias(keepAnnots = true) + + /** Follow aliases and dereferences LazyRefs, annotated types and instantiated + * TypeVars until type is no longer alias type, annotated type, LazyRef, + * or instantiated type variable. + */ + final def dealias(implicit ctx: Context): Type = + dealias(keepAnnots = false) + /** Perform successive widenings and dealiasings until none can be applied anymore */ final def widenDealias(implicit ctx: Context): Type = { val res = this.widen.dealias @@ -1788,6 +1800,7 @@ object Types { false } override def computeHash = doHash((name, sig), prefix) + override def toString = super.toString ++ s"/withSig($sig)" } trait WithFixedSym extends NamedType { @@ -2418,7 +2431,12 @@ object Types { apply(nme.syntheticParamNames(paramTypes.length), paramTypes)(resultTypeExp) def apply(paramTypes: List[Type], resultType: Type)(implicit ctx: Context): MethodType = apply(nme.syntheticParamNames(paramTypes.length), paramTypes, resultType) + + /** Produce method type from parameter symbols, with special mappings for repeated + * and inline parameters. + */ def fromSymbols(params: List[Symbol], resultType: Type)(implicit ctx: Context) = { + /** Replace @repeated annotations on Seq or Array types by <repeated> types */ def translateRepeated(tp: Type): Type = tp match { case tp @ ExprType(tp1) => tp.derivedExprType(translateRepeated(tp1)) case AnnotatedType(tp, annot) if annot matches defn.RepeatedAnnot => @@ -2428,7 +2446,15 @@ object Types { case tp => tp } - def paramInfo(param: Symbol): Type = translateRepeated(param.info) + /** Add @inlineParam to inline call-by-value parameters */ + def translateInline(tp: Type): Type = tp match { + case _: ExprType => tp + case _ => AnnotatedType(tp, Annotation(defn.InlineParamAnnot)) + } + def paramInfo(param: Symbol): Type = { + val paramType = translateRepeated(param.info) + if (param.is(Inline)) translateInline(paramType) else paramType + } def transformResult(mt: MethodType) = resultType.subst(params, (0 until params.length).toList map (MethodParam(mt, _))) apply(params map (_.name.asTermName), params map paramInfo)(transformResult _) @@ -2554,6 +2580,24 @@ object Types { x => paramBounds mapConserve (_.subst(this, x).bounds), x => resType.subst(this, x)) + /** Merge nested polytypes into one polytype. nested polytypes are normally not supported + * but can arise as temporary data structures. + */ + def flatten(implicit ctx: Context): PolyType = resType match { + case that: PolyType => + val shift = new TypeMap { + def apply(t: Type) = t match { + case PolyParam(`that`, n) => PolyParam(that, n + paramNames.length) + case t => mapOver(t) + } + } + PolyType(paramNames ++ that.paramNames)( + x => this.paramBounds.mapConserve(_.subst(this, x).bounds) ++ + that.paramBounds.mapConserve(shift(_).subst(that, x).bounds), + x => shift(that.resultType).subst(that, x).subst(this, x)) + case _ => this + } + override def toString = s"PolyType($paramNames, $paramBounds, $resType)" override def computeHash = doHash(paramNames, resType, paramBounds) @@ -3704,8 +3748,7 @@ object Types { x || p(tp) || (forceLazy || !tp.isInstanceOf[LazyRef]) && foldOver(x, tp) } - class ForeachAccumulator(p: Type => Unit)(implicit ctx: Context) extends TypeAccumulator[Unit] { - override def stopAtStatic = false + class ForeachAccumulator(p: Type => Unit, override val stopAtStatic: Boolean)(implicit ctx: Context) extends TypeAccumulator[Unit] { def apply(x: Unit, tp: Type): Unit = foldOver(p(tp), tp) } diff --git a/src/dotty/tools/dotc/core/tasty/DottyUnpickler.scala b/src/dotty/tools/dotc/core/tasty/DottyUnpickler.scala index 0ad5d6966..2c93819d5 100644 --- a/src/dotty/tools/dotc/core/tasty/DottyUnpickler.scala +++ b/src/dotty/tools/dotc/core/tasty/DottyUnpickler.scala @@ -8,8 +8,8 @@ import dotty.tools.dotc.ast.tpd import TastyUnpickler._, TastyBuffer._ import util.Positions._ import util.{SourceFile, NoSource} -import PositionUnpickler._ import Annotations.Annotation +import core.Mode import classfile.ClassfileParser object DottyUnpickler { @@ -17,14 +17,15 @@ object DottyUnpickler { /** Exception thrown if classfile is corrupted */ class BadSignature(msg: String) extends RuntimeException(msg) - class TreeSectionUnpickler extends SectionUnpickler[TreeUnpickler]("ASTs") { + class TreeSectionUnpickler(posUnpickler: Option[PositionUnpickler]) + extends SectionUnpickler[TreeUnpickler]("ASTs") { def unpickle(reader: TastyReader, tastyName: TastyName.Table) = - new TreeUnpickler(reader, tastyName) + new TreeUnpickler(reader, tastyName, posUnpickler) } - class PositionsSectionUnpickler extends SectionUnpickler[(Position, AddrToPosition)]("Positions") { + class PositionsSectionUnpickler extends SectionUnpickler[PositionUnpickler]("Positions") { def unpickle(reader: TastyReader, tastyName: TastyName.Table) = - new PositionUnpickler(reader).unpickle() + new PositionUnpickler(reader) } } @@ -36,7 +37,8 @@ class DottyUnpickler(bytes: Array[Byte]) extends ClassfileParser.Embedded { import DottyUnpickler._ val unpickler = new TastyUnpickler(bytes) - private val treeUnpickler = unpickler.unpickle(new TreeSectionUnpickler).get + private val posUnpicklerOpt = unpickler.unpickle(new PositionsSectionUnpickler) + private val treeUnpickler = unpickler.unpickle(new TreeSectionUnpickler(posUnpicklerOpt)).get /** Enter all toplevel classes and objects into their scopes * @param roots a set of SymDenotations that should be overwritten by unpickling @@ -44,13 +46,8 @@ class DottyUnpickler(bytes: Array[Byte]) extends ClassfileParser.Embedded { def enter(roots: Set[SymDenotation])(implicit ctx: Context): Unit = treeUnpickler.enterTopLevel(roots) - /** The unpickled trees, and the source file they come from - * @param readPositions if true, trees get decorated with position information. - */ - def body(readPositions: Boolean = false)(implicit ctx: Context): List[Tree] = { - if (readPositions) - for ((totalRange, positions) <- unpickler.unpickle(new PositionsSectionUnpickler)) - treeUnpickler.usePositions(totalRange, positions) + /** The unpickled trees, and the source file they come from. */ + def body(implicit ctx: Context): List[Tree] = { treeUnpickler.unpickle() } } diff --git a/src/dotty/tools/dotc/core/tasty/PositionPickler.scala b/src/dotty/tools/dotc/core/tasty/PositionPickler.scala index b0550b70a..63bb00a71 100644 --- a/src/dotty/tools/dotc/core/tasty/PositionPickler.scala +++ b/src/dotty/tools/dotc/core/tasty/PositionPickler.scala @@ -3,7 +3,8 @@ package dotc package core package tasty -import ast.tpd._ +import ast._ +import ast.Trees._ import ast.Trees.WithLazyField import TastyFormat._ import core._ @@ -12,64 +13,47 @@ import collection.mutable import TastyBuffer._ import util.Positions._ -object PositionPickler { - - trait DeferredPosition { - var parentPos: Position = NoPosition - } - - def traverse(x: Any, parentPos: Position, op: (Tree, Position) => Unit)(implicit ctx: Context): Unit = - if (parentPos.exists) - x match { - case x: Tree @unchecked => - op(x, parentPos) - x match { - case x: MemberDef @unchecked => traverse(x.symbol.annotations, x.pos, op) - case _ => - } - traverse(x.productIterator, x.pos, op) - case x: DeferredPosition => - x.parentPos = parentPos - case xs: TraversableOnce[_] => - xs.foreach(traverse(_, parentPos, op)) - case _ => - } -} -import PositionPickler._ - -class PositionPickler(pickler: TastyPickler, addrOfTree: Tree => Option[Addr]) { +class PositionPickler(pickler: TastyPickler, addrOfTree: tpd.Tree => Option[Addr]) { val buf = new TastyBuffer(5000) pickler.newSection("Positions", buf) import buf._ + import ast.tpd._ + + def header(addrDelta: Int, hasStartDelta: Boolean, hasEndDelta: Boolean) = { + def toInt(b: Boolean) = if (b) 1 else 0 + (addrDelta << 2) | (toInt(hasStartDelta) << 1) | toInt(hasEndDelta) + } - def picklePositions(roots: List[Tree], totalRange: Position)(implicit ctx: Context) = { + def picklePositions(roots: List[Tree])(implicit ctx: Context) = { var lastIndex = 0 - def record(tree: Tree, parentPos: Position): Unit = - if (tree.pos.exists) { - def msg = s"failure to pickle $tree at ${tree.pos}, parent = $parentPos" - val endPos = tree.pos.end min parentPos.end - // end positions can be larger than their parents - // e.g. in the case of synthetic empty ranges, which are placed at the next token after - // the current construct. - val endDelta = endPos - parentPos.end - val startPos = - if (endDelta == 0) tree.pos.start max parentPos.start else tree.pos.start min endPos - // Since end positions are corrected above, start positions have to follow suit. - val startDelta = startPos - parentPos.start - if (startDelta != 0 || endDelta != 0) - for (addr <- addrOfTree(tree)) { - buf.writeInt(addr.index - lastIndex) - lastIndex = addr.index - if (startDelta != 0) buf.writeInt(startDelta) - if (endDelta != 0) { - assert(endDelta < 0, msg) - buf.writeInt(endDelta) - } else - assert(startDelta >= 0, msg) + var lastPos = Position(0, 0) + def pickleDeltas(index: Int, pos: Position) = { + val addrDelta = index - lastIndex + val startDelta = pos.start - lastPos.start + val endDelta = pos.end - lastPos.end + buf.writeInt(header(addrDelta, startDelta != 0, endDelta != 0)) + if (startDelta != 0) buf.writeInt(startDelta) + if (endDelta != 0) buf.writeInt(endDelta) + lastIndex = index + lastPos = pos + } + def traverse(x: Any, parentPos: Position): Unit = x match { + case x: Tree @unchecked => + if (x.pos.exists && x.pos.toSynthetic != parentPos.toSynthetic) { + addrOfTree(x) match { + case Some(addr) => pickleDeltas(addr.index, x.pos) + case _ => } - } - - buf.writeNat(totalRange.end) - traverse(roots, totalRange, record) + } + x match { + case x: MemberDef @unchecked => traverse(x.symbol.annotations, x.pos) + case _ => + } + traverse(x.productIterator, x.pos) + case xs: TraversableOnce[_] => + xs.foreach(traverse(_, parentPos)) + case _ => + } + traverse(roots, NoPosition) } } diff --git a/src/dotty/tools/dotc/core/tasty/PositionUnpickler.scala b/src/dotty/tools/dotc/core/tasty/PositionUnpickler.scala index fa80a2769..c29aeba70 100644 --- a/src/dotty/tools/dotc/core/tasty/PositionUnpickler.scala +++ b/src/dotty/tools/dotc/core/tasty/PositionUnpickler.scala @@ -6,33 +6,31 @@ package tasty import util.Positions._ import collection.mutable -import TastyBuffer.Addr - -object PositionUnpickler { - type AddrToPosition = mutable.HashMap[Addr, Position] -} +import TastyBuffer.{Addr, NoAddr} /** Unpickler for tree positions */ class PositionUnpickler(reader: TastyReader) { - import PositionUnpickler._ import reader._ - def unpickle(): (Position, AddrToPosition) = { - val positions = new mutable.HashMap[Addr, Position] // Dotty deviation: Can't use new AddrToPosition here. TODO: fix this! - val sourceLength = readNat() - def readDelta() = if (isAtEnd) 0 else readInt() - var curIndex: Addr = Addr(readDelta()) + private[tasty] lazy val positions = { + val positions = new mutable.HashMap[Addr, Position] + var curIndex = 0 + var curStart = 0 + var curEnd = 0 while (!isAtEnd) { - val delta1 = readDelta() - val delta2 = readDelta() - val (startDelta, endDelta, indexDelta) = - if (delta2 <= 0) (delta1, -delta2, readDelta()) - else if (delta1 < 0) (0, -delta1, delta2) - else (delta1, 0, delta2) - positions(curIndex) = Position(startDelta, endDelta, startDelta) - // make non-synthetic position; will be made synthetic by normalization. - curIndex += indexDelta + val header = readInt() + val addrDelta = header >> 2 + val hasStart = (header & 2) != 0 + val hasEnd = (header & 1) != 0 + curIndex += addrDelta + assert(curIndex >= 0) + if (hasStart) curStart += readInt() + if (hasEnd) curEnd += readInt() + positions(Addr(curIndex)) = Position(curStart, curEnd) } - (Position(0, sourceLength), positions) + positions } + + def posAt(addr: Addr) = positions.getOrElse(addr, NoPosition) } + diff --git a/src/dotty/tools/dotc/core/tasty/TastyFormat.scala b/src/dotty/tools/dotc/core/tasty/TastyFormat.scala index e9de68e7f..8e8d58b47 100644 --- a/src/dotty/tools/dotc/core/tasty/TastyFormat.scala +++ b/src/dotty/tools/dotc/core/tasty/TastyFormat.scala @@ -185,21 +185,16 @@ Note: Tree tags are grouped into 5 categories that determine what follows, and t Category 4 (tags 112-127): tag Nat AST Category 5 (tags 128-255): tag Length <payload> -Standard Section: "Positions" sourceLength_Nat Assoc* - - Assoc = addr_Delta offset_Delta offset_Delta? - // addr_Delta : - // Difference of address to last recorded node. - // All but the first addr_Deltas are > 0, the first is >= 0. - // 2nd offset_Delta: - // Difference of end offset of addressed node vs parent node. Always <= 0 - // 1st offset Delta, if delta >= 0 or 2nd offset delta exists - // Difference of start offset of addressed node vs parent node. - // 1st offset Delta, if delta < 0 and 2nd offset delta does not exist: - // Difference of end offset of addressed node vs parent node. - // Offsets and addresses are difference encoded. +Standard Section: "Positions" Assoc* + + Assoc = Header offset_Delta? offset_Delta? + Header = addr_Delta + // in one Nat: difference of address to last recorded node << 2 + + hasStartDiff + // one bit indicating whether there follows a start address delta << 1 + hasEndDiff // one bit indicating whether there follows an end address delta // Nodes which have the same positions as their parents are omitted. - Delta = Int // Difference between consecutive offsets / tree addresses, + // offset_Deltas give difference of start/end offset wrt to the + // same offset in the previously recorded node (or 0 for the first recorded node) + Delta = Int // Difference between consecutive offsets, **************************************************************************************/ diff --git a/src/dotty/tools/dotc/core/tasty/TastyPickler.scala b/src/dotty/tools/dotc/core/tasty/TastyPickler.scala index 83e6020d5..98b0dc7c6 100644 --- a/src/dotty/tools/dotc/core/tasty/TastyPickler.scala +++ b/src/dotty/tools/dotc/core/tasty/TastyPickler.scala @@ -31,7 +31,6 @@ class TastyPickler { sections += ((nameBuffer.nameIndex(name), buf)) def assembleParts(): Array[Byte] = { - treePkl.compactify() def lengthWithLength(buf: TastyBuffer) = { buf.assemble() buf.length + natSize(buf.length) diff --git a/src/dotty/tools/dotc/core/tasty/TastyPrinter.scala b/src/dotty/tools/dotc/core/tasty/TastyPrinter.scala index 915ae3f21..7fcd7c29e 100644 --- a/src/dotty/tools/dotc/core/tasty/TastyPrinter.scala +++ b/src/dotty/tools/dotc/core/tasty/TastyPrinter.scala @@ -113,8 +113,8 @@ class TastyPrinter(bytes: Array[Byte])(implicit ctx: Context) { class PositionSectionUnpickler extends SectionUnpickler[Unit]("Positions") { def unpickle(reader: TastyReader, tastyName: TastyName.Table): Unit = { print(s"${reader.endAddr.index - reader.currentAddr.index}") - val (totalRange, positions) = new PositionUnpickler(reader).unpickle() - println(s" position bytes in $totalRange:") + val positions = new PositionUnpickler(reader).positions + println(s" position bytes:") val sorted = positions.toSeq.sortBy(_._1.index) for ((addr, pos) <- sorted) println(s"${addr.index}: ${offsetToInt(pos.start)} .. ${pos.end}") } diff --git a/src/dotty/tools/dotc/core/tasty/TreePickler.scala b/src/dotty/tools/dotc/core/tasty/TreePickler.scala index e5cacfc00..b6f52c0ec 100644 --- a/src/dotty/tools/dotc/core/tasty/TreePickler.scala +++ b/src/dotty/tools/dotc/core/tasty/TreePickler.scala @@ -7,6 +7,7 @@ import ast.Trees._ import TastyFormat._ import Contexts._, Symbols._, Types._, Names._, Constants._, Decorators._, Annotations._, StdNames.tpnme, NameOps._ import collection.mutable +import typer.Inliner import NameOps._ import TastyBuffer._ import TypeApplications._ @@ -307,7 +308,7 @@ class TreePickler(pickler: TastyPickler) { if (!tree.isEmpty) pickleTree(tree) def pickleDef(tag: Int, sym: Symbol, tpt: Tree, rhs: Tree = EmptyTree, pickleParams: => Unit = ())(implicit ctx: Context) = { - assert(symRefs(sym) == NoAddr) + assert(symRefs(sym) == NoAddr, sym) registerDef(sym) writeByte(tag) withLength { @@ -430,6 +431,15 @@ class TreePickler(pickler: TastyPickler) { case SeqLiteral(elems, elemtpt) => writeByte(REPEATED) withLength { pickleTree(elemtpt); elems.foreach(pickleTree) } + case tree: Inlined => + // Why drop Inlined info when pickling? + // Since we never inline inside an inlined method, we know that + // any code that continas an Inlined tree is not inlined itself. + // So position information for inline expansion is no longer needed. + // The only reason to keep the inline info around would be to have fine-grained + // position information in the linker. We should come back to this + // point once we know more what we would do with such information. + pickleTree(Inliner.dropInlined(tree)) case TypeTree(original) => pickleTpt(tree) case Bind(name, body) => @@ -555,19 +565,19 @@ class TreePickler(pickler: TastyPickler) { sym.annotations.foreach(pickleAnnotation) } - def pickleAnnotation(ann: Annotation)(implicit ctx: Context) = { - writeByte(ANNOTATION) - withLength { pickleType(ann.symbol.typeRef); pickleTree(ann.tree) } - } + def pickleAnnotation(ann: Annotation)(implicit ctx: Context) = + if (ann.symbol != defn.BodyAnnot) { // inline bodies are reconstituted automatically when unpickling + writeByte(ANNOTATION) + withLength { pickleType(ann.symbol.typeRef); pickleTree(ann.tree) } + } def pickle(trees: List[Tree])(implicit ctx: Context) = { trees.foreach(tree => if (!tree.isEmpty) pickleTree(tree)) - assert(forwardSymRefs.isEmpty, i"unresolved symbols: ${forwardSymRefs.keySet.toList}%, %") + assert(forwardSymRefs.isEmpty, i"unresolved symbols: ${forwardSymRefs.keySet.toList}%, % when pickling ${ctx.source}") } def compactify() = { buf.compactify() - assert(forwardSymRefs.isEmpty, s"unresolved symbols: ${forwardSymRefs.keySet.toList}%, %") def updateMapWithDeltas[T](mp: collection.mutable.Map[T, Addr]) = for (key <- mp.keysIterator.toBuffer[T]) mp(key) = adjusted(mp(key)) diff --git a/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index 56bb8498a..09f2c0d1f 100644 --- a/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -9,38 +9,23 @@ import util.Positions._ import ast.{tpd, Trees, untpd} import Trees._ import Decorators._ -import TastyUnpickler._, TastyBuffer._, PositionPickler._ +import TastyUnpickler._, TastyBuffer._ import scala.annotation.{tailrec, switch} import scala.collection.mutable.ListBuffer import scala.collection.{ mutable, immutable } import config.Printers.pickling /** Unpickler for typed trees - * @param reader the reader from which to unpickle - * @param tastyName the nametable + * @param reader the reader from which to unpickle + * @param tastyName the nametable + * @param posUNpicklerOpt the unpickler for positions, if it exists */ -class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table) { +class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table, posUnpicklerOpt: Option[PositionUnpickler]) { import TastyFormat._ import TastyName._ import TreeUnpickler._ import tpd._ - private var readPositions = false - private var totalRange = NoPosition - private var positions: collection.Map[Addr, Position] = _ - - /** Make a subsequent call to `unpickle` return trees with positions - * @param totalRange the range position enclosing all returned trees, - * or NoPosition if positions should not be unpickled - * @param positions a map from tree addresses to their positions relative - * to positions of parent nodes. - */ - def usePositions(totalRange: Position, positions: collection.Map[Addr, Position]): Unit = { - readPositions = true - this.totalRange = totalRange - this.positions = positions - } - /** A map from addresses of definition entries to the symbols they define */ private val symAtAddr = new mutable.HashMap[Addr, Symbol] @@ -85,10 +70,7 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table) { /** The unpickled trees */ def unpickle()(implicit ctx: Context): List[Tree] = { assert(roots != null, "unpickle without previous enterTopLevel") - val stats = new TreeReader(reader) - .readTopLevel()(ctx.addMode(Mode.AllowDependentFunctions)) - normalizePos(stats, totalRange) - stats + new TreeReader(reader).readTopLevel()(ctx.addMode(Mode.AllowDependentFunctions)) } def toTermName(tname: TastyName): TermName = tname match { @@ -468,6 +450,7 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table) { val isClass = ttag == TEMPLATE val templateStart = currentAddr skipTree() // tpt + val rhsStart = currentAddr val rhsIsEmpty = noRhs(end) if (!rhsIsEmpty) skipTree() val (givenFlags, annots, privateWithin) = readModifiers(end) @@ -504,6 +487,12 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table) { sym.completer.withDecls(newScope) forkAt(templateStart).indexTemplateParams()(localContext(sym)) } + else if (annots.exists(_.symbol == defn.InlineAnnot)) + sym.addAnnotation(LazyBodyAnnotation { ctx0 => + implicit val ctx: Context = localContext(sym)(ctx0).addMode(Mode.ReadPositions) + // avoids space leaks by not capturing the current context + forkAt(rhsStart).readTerm() + }) goto(start) sym } @@ -1010,44 +999,29 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table) { new LazyReader(localReader, op) } -// ------ Hooks for positions ------------------------------------------------ +// ------ Setting positions ------------------------------------------------ - /** Record address from which tree was created as a temporary position in the tree. - * The temporary position contains deltas relative to the position of the (as yet unknown) - * parent node. It is marked as a non-synthetic source position. - */ - def setPos[T <: Tree](addr: Addr, tree: T): T = { - if (readPositions) - tree.setPosUnchecked(positions.getOrElse(addr, Position(0, 0, 0))) - tree - } + /** Set position of `tree` at given `addr`. */ + def setPos[T <: Tree](addr: Addr, tree: T)(implicit ctx: Context): tree.type = + if (ctx.mode.is(Mode.ReadPositions)) { + posUnpicklerOpt match { + case Some(posUnpickler) => tree.withPos(posUnpickler.posAt(addr)) + case _ => tree + } + } + else tree } - private def setNormalized(tree: Tree, parentPos: Position): Unit = - tree.setPosUnchecked( - if (tree.pos.exists) - Position(parentPos.start + offsetToInt(tree.pos.start), parentPos.end - tree.pos.end) - else - parentPos) - - def normalizePos(x: Any, parentPos: Position)(implicit ctx: Context): Unit = - traverse(x, parentPos, setNormalized) - - class LazyReader[T <: AnyRef](reader: TreeReader, op: TreeReader => Context => T) extends Trees.Lazy[T] with DeferredPosition { + class LazyReader[T <: AnyRef](reader: TreeReader, op: TreeReader => Context => T) extends Trees.Lazy[T] { def complete(implicit ctx: Context): T = { pickling.println(i"starting to read at ${reader.reader.currentAddr}") - val res = op(reader)(ctx.addMode(Mode.AllowDependentFunctions).withPhaseNoLater(ctx.picklerPhase)) - normalizePos(res, parentPos) - res + op(reader)(ctx.addMode(Mode.AllowDependentFunctions).withPhaseNoLater(ctx.picklerPhase)) } } - class LazyAnnotationReader(sym: Symbol, reader: TreeReader) - extends LazyAnnotation(sym) with DeferredPosition { + class LazyAnnotationReader(sym: Symbol, reader: TreeReader) extends LazyAnnotation(sym) { def complete(implicit ctx: Context) = { - val res = reader.readTerm()(ctx.withPhaseNoLater(ctx.picklerPhase)) - normalizePos(res, parentPos) - res + reader.readTerm()(ctx.withPhaseNoLater(ctx.picklerPhase)) } } |