From 78e5e051ee6c0f7457a9df467d48aea9faa8d5f1 Mon Sep 17 00:00:00 2001 From: Jamie Thompson Date: Tue, 16 Feb 2021 14:10:05 +0100 Subject: [PATCH] refactor --- .../scala/tools/nsc/tasty/TreeUnpickler.scala | 230 ++++++++++-------- .../tools/nsc/tasty/bridge/SymbolOps.scala | 27 +- .../neg/src-2/TestDependentExtension.check | 2 +- test/tasty/neg/src-2/TestRealFunctor.check | 2 +- 4 files changed, 138 insertions(+), 123 deletions(-) diff --git a/src/compiler/scala/tools/nsc/tasty/TreeUnpickler.scala b/src/compiler/scala/tools/nsc/tasty/TreeUnpickler.scala index 680949260515..8a10f400b61a 100644 --- a/src/compiler/scala/tools/nsc/tasty/TreeUnpickler.scala +++ b/src/compiler/scala/tools/nsc/tasty/TreeUnpickler.scala @@ -686,38 +686,26 @@ class TreeUnpickler[Tasty <: TastyUniverse]( case _ => val start = currentAddr cycleAtAddr(start) = Tombstone - val noCycle = readNewMember() + val noCycle = initializeMember() cycleAtAddr.remove(start) noCycle } - private def readNewMember()(implicit ctx: Context): NoCycle = { - val symAddr = currentAddr - val tag = readByte() - val end = readEnd() - val tname = readTastyName() - val sym = symAtAddr(symAddr) - val repr = sym.repr + private def initializeMember()(implicit ctx: Context): NoCycle = { + val symAddr = currentAddr + val tag = readByte() + val end = readEnd() + val tname = readTastyName() + val sym = symAtAddr(symAddr) - ctx.log(s"$symAddr completing ${showSym(sym)} in scope ${showSym(ctx.owner)}") - - def readParamss(skipTypeParams: Boolean)(implicit ctx: Context): List[List[NoCycle]] = { + def readParamss()(implicit ctx: Context): List[List[NoCycle]] = { def readRest() = { if (nextByte == SPLITCLAUSE) readByte() - readParamss(skipTypeParams) - } - - def emptyTypeParams() = { - while (nextByte === TYPEPARAM) skipTree() - Nil + readParamss() } - nextByte match { case PARAM => readParams[NoCycle](PARAM) :: readRest() - case TYPEPARAM => - val typarams = - if (skipTypeParams) emptyTypeParams() else readParams[NoCycle](TYPEPARAM) - typarams :: readRest() + case TYPEPARAM => readParams[NoCycle](TYPEPARAM) :: readRest() case EMPTYCLAUSE => readByte(); Nil :: readRest() case _ => Nil } @@ -727,97 +715,123 @@ class TreeUnpickler[Tasty <: TastyUniverse]( unsupportedWhen(unsupported.hasFlags, s"${showTasty(unsupported)} ${sym.kindString} $tname") } - try { - val localCtx = ctx.withOwner(sym) - tag match { - case DEFDEF => - val isMacro = repr.originalFlagSet.is(Erased | Macro) - checkUnsupportedFlags(repr.tastyOnlyFlags &~ (Extension | Exported | Infix | optFlag(isMacro)(Erased))) - val isCtor = sym.isConstructor - val paramDefss = readParamss(skipTypeParams=isCtor)(localCtx) - val typeParams: List[Symbol] = { - if (isCtor) { - sym.owner.typeParams - } - else { - val first = paramDefss.take(1).flatten.map(symFromNoCycle) - if (first.headOption.exists(_.isType)) first else Nil - } - } - val vparamss: List[List[NoCycle]] = { - val valueClauses = paramDefss.drop(if (typeParams.isEmpty) 0 else 1) - val hasTypeParams = valueClauses.exists(_.exists(nc => symFromNoCycle(nc).isType)) - unsupportedWhen(hasTypeParams, s"extension method with secondary type parameter list: $tname") - valueClauses - } - val tpt = readTpt()(localCtx) - if (isMacro) { - val impl = tpd.Macro(readTerm()(ctx.addMode(ReadMacro))) - val annot = symbolTable.AnnotationInfo( - atp = symbolTable.definitions.MacroImplLocationAnnotation.tpe, - args = List(impl), - assocs = Nil - ) - sym.addAnnotation(annot) - } - val valueParamss = normalizeIfConstructor(vparamss.map(_.map(symFromNoCycle)), isCtor) - val resType = effectiveResultType(sym, typeParams, tpt.tpe) - ctx.setInfo(sym, defn.DefDefType(if (isCtor) Nil else typeParams, valueParamss, resType)) - case VALDEF => // valdef in TASTy is either a singleton object or a method forwarder to a local value. - checkUnsupportedFlags(repr.tastyOnlyFlags &~ (Enum | Extension | Exported)) - val tpe = readTpt()(localCtx).tpe - ctx.setInfo(sym, - if (repr.originalFlagSet.is(SingletonEnumFlags)) { - val enumClass = sym.objectImplementation - val selfTpe = defn.SingleType(sym.owner.thisPrefix, sym) - val ctor = ctx.unsafeNewSymbol( - owner = enumClass, - name = TastyName.Constructor, - flags = Method, - info = defn.DefDefType(Nil, Nil :: Nil, selfTpe) - ) - enumClass.typeOfThis = selfTpe - ctx.setInfo(enumClass, defn.ClassInfoType(intersectionParts(tpe), ctor :: Nil, enumClass)) - prefixedRef(sym.owner.thisPrefix, enumClass) - } - else if (sym.isFinal && isConstantType(tpe)) defn.InlineExprType(tpe) - else if (sym.isMethod) defn.ExprType(tpe) - else tpe + def DefDef(repr: TastyRepr, localCtx: Context)(implicit ctx: Context): Unit = { + val isMacro = repr.originalFlagSet.is(Erased | Macro) + checkUnsupportedFlags(repr.tastyOnlyFlags &~ (Extension | Exported | Infix | optFlag(isMacro)(Erased))) + val isCtor = sym.isConstructor + val paramDefss = readParamss()(localCtx).map(_.map(symFromNoCycle)) + val typeParams = { + // A type parameter list must be non-empty and with type symbols + val first = paramDefss.take(1) + if (first.exists(_.exists(_.isType))) first.head else Nil + } + val vparamss = { + // A value parameter list may be empty, or filled with term symbols + val valueClauses = paramDefss.drop(if (typeParams.isEmpty) 0 else 1) + val hasTypeParams = valueClauses.exists(_.exists(_.isType)) + unsupportedWhen(hasTypeParams, { + val noun = ( + if (isCtor) "constructor" + else if (repr.tastyOnlyFlags.is(Extension)) "extension method" + else "method" ) - case TYPEDEF | TYPEPARAM => - val allowedShared = Enum | Opaque | Infix - val allowedTypeFlags = allowedShared | Exported - val allowedClassFlags = allowedShared | Open | Transparent - if (sym.isClass) { - checkUnsupportedFlags(repr.tastyOnlyFlags &~ allowedClassFlags) - sym.owner.ensureCompleted() - readTemplate()(localCtx) - } - else { - checkUnsupportedFlags(repr.tastyOnlyFlags &~ allowedTypeFlags) - val rhs = readTpt()(if (repr.originalFlagSet.is(Opaque)) localCtx.addMode(OpaqueTypeDef) else localCtx) - val info = - if (repr.originalFlagSet.is(Opaque)) { - val (info, alias) = defn.OpaqueTypeToBounds(rhs.tpe) - ctx.markAsOpaqueType(sym, alias) - info - } - else rhs.tpe - ctx.setInfo(sym, defn.NormalisedBounds(info, sym)) - if (sym.is(Param)) sym.reset(Private | Protected) - // sym.resetFlag(Provisional) + s"$noun with unmergeable type parameters: $tname" + }) + valueClauses + } + val tpt = readTpt()(localCtx) + if (isMacro) { + val impl = tpd.Macro(readTerm()(ctx.addMode(ReadMacro))) + val annot = symbolTable.AnnotationInfo( + atp = symbolTable.definitions.MacroImplLocationAnnotation.tpe, + args = List(impl), + assocs = Nil + ) + sym.addAnnotation(annot) + } + val valueParamss = normalizeIfConstructor(vparamss, isCtor) + val resType = effectiveResultType(sym, typeParams, tpt.tpe) + ctx.setInfo(sym, defn.DefDefType(if (isCtor) Nil else typeParams, valueParamss, resType)) + } + + def ValDef(repr: TastyRepr, localCtx: Context)(implicit ctx: Context): Unit = { + // valdef in TASTy is either a singleton object or a method forwarder to a local value. + checkUnsupportedFlags(repr.tastyOnlyFlags &~ (Enum | Extension | Exported)) + val tpe = readTpt()(localCtx).tpe + ctx.setInfo(sym, + if (repr.originalFlagSet.is(SingletonEnumFlags)) { + val enumClass = sym.objectImplementation + val selfTpe = defn.SingleType(sym.owner.thisPrefix, sym) + val ctor = ctx.unsafeNewSymbol( + owner = enumClass, + name = TastyName.Constructor, + flags = Method, + info = defn.DefDefType(Nil, Nil :: Nil, selfTpe) + ) + enumClass.typeOfThis = selfTpe + ctx.setInfo(enumClass, defn.ClassInfoType(intersectionParts(tpe), ctor :: Nil, enumClass)) + prefixedRef(sym.owner.thisPrefix, enumClass) + } + else if (sym.isFinal && isConstantType(tpe)) defn.InlineExprType(tpe) + else if (sym.isMethod) defn.ExprType(tpe) + else tpe + ) + } + + def TypeDef(repr: TastyRepr, localCtx: Context)(implicit ctx: Context): Unit = { + val allowedShared = Enum | Opaque | Infix + val allowedTypeFlags = allowedShared | Exported + val allowedClassFlags = allowedShared | Open | Transparent + if (sym.isClass) { + checkUnsupportedFlags(repr.tastyOnlyFlags &~ allowedClassFlags) + sym.owner.ensureCompleted() + readTemplate()(localCtx) + } + else { + checkUnsupportedFlags(repr.tastyOnlyFlags &~ allowedTypeFlags) + val rhs = readTpt()(if (repr.originalFlagSet.is(Opaque)) localCtx.addMode(OpaqueTypeDef) else localCtx) + val info = + if (repr.originalFlagSet.is(Opaque)) { + val (info, alias) = defn.OpaqueTypeToBounds(rhs.tpe) + ctx.markAsOpaqueType(sym, alias) + info } - case PARAM => - checkUnsupportedFlags(repr.tastyOnlyFlags &~ (ParamAlias | Exported)) - val tpt = readTpt()(localCtx) - ctx.setInfo(sym, - if (nothingButMods(end) && sym.not(ParamSetter)) tpt.tpe - else defn.ExprType(tpt.tpe)) + else rhs.tpe + ctx.setInfo(sym, defn.NormalisedBounds(info, sym)) + if (sym.is(Param)) sym.reset(Private | Protected) + } + } + + def TermParam(repr: TastyRepr, localCtx: Context)(implicit ctx: Context): Unit = { + checkUnsupportedFlags(repr.tastyOnlyFlags &~ (ParamAlias | Exported)) + val tpt = readTpt()(localCtx) + ctx.setInfo(sym, + if (nothingButMods(end) && sym.not(ParamSetter)) tpt.tpe + else defn.ExprType(tpt.tpe)) + } + + def initialize()(implicit ctx: Context): Unit = { + val repr = sym.rawInfo match { + case repr: TastyRepr => repr + case _ => return () // nothing to do here (assume correctly initalised) + } + ctx.log(s"$symAddr completing ${showSym(sym)} in scope ${showSym(ctx.owner)}") + val localCtx = ctx.withOwner(sym) + tag match { + case DEFDEF => DefDef(repr, localCtx) + case VALDEF => ValDef(repr, localCtx) + case TYPEDEF | TYPEPARAM => TypeDef(repr, localCtx) + case PARAM => TermParam(repr, localCtx) } + } + + try { + initialize() ctx.log(s"$symAddr @@@ ${showSym(sym)}.tpe =:= '[${if (sym.isType) sym.tpe else sym.info}]; owned by ${location(sym.owner)}") - goto(end) NoCycle(at = symAddr) - } catch ctx.onCompletionError(sym) + } + catch ctx.onCompletionError(sym) + finally goto(end) } private def readTemplate()(implicit ctx: Context): Unit = { diff --git a/src/compiler/scala/tools/nsc/tasty/bridge/SymbolOps.scala b/src/compiler/scala/tools/nsc/tasty/bridge/SymbolOps.scala index 11625ce7f147..ab931b219fbf 100644 --- a/src/compiler/scala/tools/nsc/tasty/bridge/SymbolOps.scala +++ b/src/compiler/scala/tools/nsc/tasty/bridge/SymbolOps.scala @@ -46,16 +46,16 @@ trait SymbolOps { self: TastyUniverse => implicit final class SymbolDecorator(val sym: Symbol) { - def isScala3Macro(implicit ctx: Context): Boolean = repr.originalFlagSet.is(Inline | Macro) - def isScala3Inline(implicit ctx: Context): Boolean = repr.originalFlagSet.is(Inline) - def isScala2Macro(implicit ctx: Context): Boolean = repr.originalFlagSet.is(Erased | Macro) + def isScala3Macro: Boolean = repr.originalFlagSet.is(Inline | Macro) + def isScala3Inline: Boolean = repr.originalFlagSet.is(Inline) + def isScala2Macro: Boolean = repr.originalFlagSet.is(Erased | Macro) - def isPureMixinCtor(implicit ctx: Context): Boolean = isMixinCtor && repr.originalFlagSet.is(Stable) + def isPureMixinCtor: Boolean = isMixinCtor && repr.originalFlagSet.is(Stable) def isMixinCtor: Boolean = u.nme.MIXIN_CONSTRUCTOR == sym.name && sym.owner.isTrait - def isTraitParamAccessor(implicit ctx: Context): Boolean = sym.owner.isTrait && repr.originalFlagSet.is(FieldAccessor|ParamSetter) + def isTraitParamAccessor: Boolean = sym.owner.isTrait && repr.originalFlagSet.is(FieldAccessor|ParamSetter) - def isParamGetter(implicit ctx: Context): Boolean = + def isParamGetter: Boolean = sym.isMethod && sym.repr.originalFlagSet.is(FlagSets.FieldAccessorFlags) /** A computed property that should only be called on a symbol which is known to have been initialised by the @@ -63,13 +63,14 @@ trait SymbolOps { self: TastyUniverse => * * @todo adapt callsites and type so that this property is more safe to call (barring mutation from uncontrolled code) */ - def repr(implicit ctx: Context): TastyRepr = { - require(sym.rawInfo.isInstanceOf[TastyRepr], { - val raw = u.showRaw(sym.rawInfo) - val tastyRepr = u.typeOf[TastyRepr] - s"${showSym(sym)} is already completed. Expected $tastyRepr, is $raw. in ${ctx.source}" - }) - sym.rawInfo.asInstanceOf[TastyRepr] + def repr: TastyRepr = { + try sym.rawInfo.asInstanceOf[TastyRepr] + catch { + case err: ClassCastException => + val raw = u.showRaw(sym.rawInfo) + val tastyRepr = u.typeOf[TastyRepr] + throw new AssertionError(s"$sym is already completed. Expected $tastyRepr, is $raw.") + } } def ensureCompleted(): Unit = { diff --git a/test/tasty/neg/src-2/TestDependentExtension.check b/test/tasty/neg/src-2/TestDependentExtension.check index 4a8c391801cf..d4934359e9aa 100644 --- a/test/tasty/neg/src-2/TestDependentExtension.check +++ b/test/tasty/neg/src-2/TestDependentExtension.check @@ -1,4 +1,4 @@ -TestDependentExtension_fail.scala:11: error: Unsupported Scala 3 extension method with secondary type parameter list: extract; found in trait tastytest.DependentExtension. +TestDependentExtension_fail.scala:11: error: Unsupported Scala 3 extension method with unmergeable type parameters: extract; found in trait tastytest.DependentExtension. val res = implicitly[DependentExtension].extract(box)(_ + 1) ^ 1 error diff --git a/test/tasty/neg/src-2/TestRealFunctor.check b/test/tasty/neg/src-2/TestRealFunctor.check index f992b382783d..e873d33048a1 100644 --- a/test/tasty/neg/src-2/TestRealFunctor.check +++ b/test/tasty/neg/src-2/TestRealFunctor.check @@ -1,4 +1,4 @@ -TestRealFunctor_fail.scala:6: error: Unsupported Scala 3 extension method with secondary type parameter list: map; found in trait tastytest.RealFunctor. +TestRealFunctor_fail.scala:6: error: Unsupported Scala 3 extension method with unmergeable type parameters: map; found in trait tastytest.RealFunctor. def map[A, B](fa: List[A])(f: A => B): List[B] = fa.map(f) ^ 1 error