diff --git a/compiler/src/dotty/tools/dotc/ast/untpd.scala b/compiler/src/dotty/tools/dotc/ast/untpd.scala index 817ff5c6c9fa..aabfdd97d7bd 100644 --- a/compiler/src/dotty/tools/dotc/ast/untpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/untpd.scala @@ -510,7 +510,6 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { def scalaRuntimeDot(name: Name)(using SourceFile): Select = Select(scalaDot(nme.runtime), name) def scalaUnit(implicit src: SourceFile): Select = scalaDot(tpnme.Unit) def scalaAny(implicit src: SourceFile): Select = scalaDot(tpnme.Any) - def javaDotLangDot(name: Name)(implicit src: SourceFile): Select = Select(Select(Ident(nme.java), nme.lang), name) def captureRoot(using Context): Select = Select(scalaDot(nme.caps), nme.CAPTURE_ROOT) diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index 4a21afa97054..b7a25cb75613 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -1064,37 +1064,37 @@ class TreeUnpickler(reader: TastyReader, selfInfo = if (self.isEmpty) NoType else self.tpt.tpe ).integrateOpaqueMembers - val (constr, stats0) = + val constr = if nextByte == SPLITCLAUSE then assert(unpicklingJava, s"unexpected SPLITCLAUSE at $start") val tag = readByte() def ta = ctx.typeAssigner val flags = Flags.JavaDefined | Flags.PrivateLocal | Flags.Invisible - val pflags = Flags.JavaDefined | Flags.Param - val tdefRefs = tparams.map(_.symbol.asType) val ctorCompleter = new LazyType { def complete(denot: SymDenotation)(using Context) = val sym = denot.symbol - lazy val tparamSyms: List[TypeSymbol] = tparams.map: tdef => + val pflags = flags | Flags.Param + val tparamRefs = tparams.map(_.symbol.asType) + lazy val derivedTparamSyms: List[TypeSymbol] = tparams.map: tdef => val completer = new LazyType { def complete(denot: SymDenotation)(using Context) = - denot.info = tdef.symbol.asType.info.subst(tdefRefs, tparamSyms.map(_.typeRef)) + denot.info = tdef.symbol.asType.info.subst(tparamRefs, derivedTparamRefs) } - newSymbol(sym, tdef.name, pflags, completer, coord = cls.coord) - val paramSym = + newSymbol(sym, tdef.name, Flags.JavaDefined | Flags.Param, completer, coord = cls.coord) + lazy val derivedTparamRefs: List[Type] = derivedTparamSyms.map(_.typeRef) + val vparamSym = newSymbol(sym, nme.syntheticParamName(1), pflags, defn.UnitType, coord = cls.coord) - val paramSymss = tparamSyms :: List(paramSym) :: Nil + val vparamSymss: List[List[Symbol]] = List(vparamSym) :: Nil + val paramSymss = + if derivedTparamSyms.nonEmpty then derivedTparamSyms :: vparamSymss else vparamSymss val res = effectiveResultType(sym, paramSymss) denot.info = methodType(paramSymss, res) denot.setParamss(paramSymss) } val ctorSym = newSymbol(ctx.owner, nme.CONSTRUCTOR, flags, ctorCompleter, coord = coordAt(start)) - val accSym = newSymbol(cls, nme.syntheticParamName(1), flags, defn.UnitType, coord = ctorSym.coord) - val ctorDef = tpd.DefDef(ctorSym, EmptyTree) - val accessor = tpd.ValDef(accSym, ElidedTree(accSym.info)) - (ctorDef.setDefTree, accessor.setDefTree :: Nil) + tpd.DefDef(ctorSym, EmptyTree).setDefTree // fake primary constructor else - readIndexedDef().asInstanceOf[DefDef] -> Nil + readIndexedDef().asInstanceOf[DefDef] val mappedParents: LazyTreeList = if parents.exists(_.isInstanceOf[InferredTypeTree]) then // parents were not read fully, will need to be read again later on demand @@ -1105,7 +1105,7 @@ class TreeUnpickler(reader: TastyReader, val lazyStats = readLater(end, rdr => { val stats = rdr.readIndexedStats(localDummy, end) - tparams ++ vparams ++ stats0 ++ stats + tparams ++ vparams ++ stats }) defn.patchStdLibClass(cls) NamerOps.addConstructorProxies(cls) diff --git a/compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala b/compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala index 274e9e0febb1..f7ef86ee5cde 100644 --- a/compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala @@ -12,6 +12,7 @@ import Scanners.Offset import Parsers.* import core.* import Contexts.* +import Symbols.defn import Names.* import Types.* import ast.Trees.* @@ -27,6 +28,9 @@ object JavaParsers { import ast.untpd.* + + val fakeFlags = Flags.JavaDefined | Flags.PrivateLocal | Flags.Invisible + class JavaParser(source: SourceFile)(using Context) extends ParserCommon(source) { val definitions: Definitions = ctx.definitions @@ -89,16 +93,16 @@ object JavaParsers { // --------- tree building ----------------------------- - def scalaAnnotationDot(name: Name): Select = Select(scalaDot(nme.annotation), name) - def javaDot(name: Name): Tree = Select(rootDot(nme.java), name) def javaLangDot(name: Name): Tree = Select(javaDot(nme.lang), name) - /** Tree representing `java.lang.Object` */ - def javaLangObject(): Tree = javaLangDot(tpnme.Object) + /** Synthetic tree representing `java.lang.Object`. + * The typer will type all references to `java.lang.Object` as `FromJavaObject`. + */ + def ObjectTpt(): Tree = TypeTree(defn.FromJavaObjectType) // javaLangDot(tpnme.Object) /** Tree representing `java.lang.Record` */ def javaLangRecord(): Tree = javaLangDot(tpnme.Record) @@ -107,6 +111,8 @@ object JavaParsers { AppliedTypeTree(scalaDot(tpnme.Array), List(tpt)) def makeTemplate(parents: List[Tree], stats: List[Tree], tparams: List[TypeDef], needsDummyConstr: Boolean): Template = { + def UnitTpt(): Tree = TypeTree(defn.UnitType) + def pullOutFirstConstr(stats: List[Tree]): (Tree, List[Tree]) = stats match { case (meth: DefDef) :: rest if meth.name == nme.CONSTRUCTOR => (meth, rest) case first :: rest => @@ -120,10 +126,9 @@ object JavaParsers { // can call it. // This also avoids clashes between the constructor parameter names and member names. if (needsDummyConstr) { - val fakeFlags = Flags.JavaDefined | Flags.PrivateLocal | Flags.Invisible if (constr1 == EmptyTree) constr1 = makeConstructor(List(), Nil, Parsers.unimplementedExpr) stats1 = constr1 :: stats1 - constr1 = makeConstructor(List(scalaDot(tpnme.Unit)), tparams, EmptyTree, fakeFlags) + constr1 = makeConstructor(List(UnitTpt()), tparams, EmptyTree, fakeFlags) } else if (constr1 == EmptyTree) { constr1 = makeConstructor(List(), tparams, EmptyTree) @@ -134,11 +139,11 @@ object JavaParsers { def makeSyntheticParam(count: Int, tpt: Tree): ValDef = makeParam(nme.syntheticParamName(count), tpt) def makeParam(name: TermName, tpt: Tree): ValDef = - ValDef(name, tpt, EmptyTree).withMods(Modifiers(Flags.JavaDefined | Flags.Param)) + ValDef(name, tpt, EmptyTree).withFlags(Flags.JavaDefined | Flags.Param) def makeConstructor(formals: List[Tree], tparams: List[TypeDef], body: Tree, flags: FlagSet = Flags.JavaDefined): DefDef = { - val vparams = formals.zipWithIndex.map { case (p, i) => makeSyntheticParam(i + 1, p).withMods(Modifiers(flags)) } - DefDef(nme.CONSTRUCTOR, joinParams(tparams, List(vparams)), TypeTree(), body).withMods(Modifiers(flags)) + val vparams = formals.zipWithIndex.map { case (p, i) => makeSyntheticParam(i + 1, p).withAddedFlags(flags) } + DefDef(nme.CONSTRUCTOR, joinParams(tparams, List(vparams)), TypeTree(), body).withFlags(flags) } // ------------- general parsing --------------------------- @@ -307,7 +312,7 @@ object JavaParsers { if (in.token == QMARK) { val offset = in.offset in.nextToken() - val hi = if (in.token == EXTENDS) { in.nextToken() ; typ() } else javaLangObject() + val hi = if (in.token == EXTENDS) { in.nextToken() ; typ() } else ObjectTpt() val lo = if (in.token == SUPER) { in.nextToken() ; typ() } else EmptyTree atSpan(offset) { /* @@ -508,7 +513,7 @@ object JavaParsers { atSpan(in.offset) { annotations() val name = identForType() - val hi = if (in.token == EXTENDS) { in.nextToken() ; bound() } else javaLangObject() + val hi = if (in.token == EXTENDS) { in.nextToken() ; bound() } else ObjectTpt() TypeDef(name, TypeBoundsTree(EmptyTree, hi)).withMods(Modifiers(flags)) } @@ -569,7 +574,7 @@ object JavaParsers { if in.token == IDENTIFIER && in.name == jnme.RECORDid then in.token = RECORD - def termDecl(start: Offset, mods: Modifiers, parentToken: Int, parentTParams: List[TypeDef]): List[Tree] = { + def termDecl(start: Offset, mods: Modifiers, parentToken: Int): List[Tree] = { val inInterface = definesInterface(parentToken) val tparams = if (in.token == LT) typeParams(Flags.JavaDefined | Flags.Param) else List() val isVoid = in.token == VOID @@ -741,11 +746,11 @@ object JavaParsers { ValDef(name, tpt2, if (mods.is(Flags.Param)) EmptyTree else unimplementedExpr).withMods(mods1) } - def memberDecl(start: Offset, mods: Modifiers, parentToken: Int, parentTParams: List[TypeDef]): List[Tree] = in.token match + def memberDecl(start: Offset, mods: Modifiers, parentToken: Int): List[Tree] = in.token match case CLASS | ENUM | RECORD | INTERFACE | AT => typeDecl(start, if definesInterface(parentToken) then mods | Flags.JavaStatic else mods) case _ => - termDecl(start, mods, parentToken, parentTParams) + termDecl(start, mods, parentToken) def makeCompanionObject(cdef: TypeDef, statics: List[Tree]): Tree = atSpan(cdef.span) { @@ -818,9 +823,9 @@ object JavaParsers { typ() } else - javaLangObject() + ObjectTpt() val interfaces = interfacesOpt() - val (statics, body) = typeBody(CLASS, name, tparams) + val (statics, body) = typeBody(CLASS, name) val cls = atSpan(start, nameOffset) { TypeDef(name, makeTemplate(superclass :: interfaces, body, tparams, needsDummyConstr = true)).withMods(mods) } @@ -835,7 +840,7 @@ object JavaParsers { val header = formalParams() val superclass = javaLangRecord() // records always extend java.lang.Record val interfaces = interfacesOpt() // records may implement interfaces - val (statics, body) = typeBody(RECORD, name, tparams) + val (statics, body) = typeBody(RECORD, name) // We need to generate accessors for every param, if no method with the same name is already defined @@ -883,8 +888,8 @@ object JavaParsers { repsep(() => typ(), COMMA) } else - List(javaLangObject()) - val (statics, body) = typeBody(INTERFACE, name, tparams) + List(ObjectTpt()) + val (statics, body) = typeBody(INTERFACE, name) val iface = atSpan(start, nameOffset) { TypeDef( name, @@ -893,14 +898,14 @@ object JavaParsers { addCompanionObject(statics, iface) } - def typeBody(leadingToken: Int, parentName: Name, parentTParams: List[TypeDef]): (List[Tree], List[Tree]) = { + def typeBody(leadingToken: Int, parentName: Name): (List[Tree], List[Tree]) = { accept(LBRACE) - val defs = typeBodyDecls(leadingToken, parentName, parentTParams) + val defs = typeBodyDecls(leadingToken, parentName) accept(RBRACE) defs } - def typeBodyDecls(parentToken: Int, parentName: Name, parentTParams: List[TypeDef]): (List[Tree], List[Tree]) = { + def typeBodyDecls(parentToken: Int, parentName: Name): (List[Tree], List[Tree]) = { val inInterface = definesInterface(parentToken) val statics = new ListBuffer[Tree] val members = new ListBuffer[Tree] @@ -916,7 +921,7 @@ object JavaParsers { else { adaptRecordIdentifier() if (in.token == ENUM || in.token == RECORD || definesInterface(in.token)) mods |= Flags.JavaStatic - val decls = memberDecl(start, mods, parentToken, parentTParams) + val decls = memberDecl(start, mods, parentToken) (if (mods.is(Flags.JavaStatic) || inInterface && !(decls exists (_.isInstanceOf[DefDef]))) statics else @@ -926,7 +931,7 @@ object JavaParsers { (statics.toList, members.toList) } def annotationParents: List[Tree] = List( - javaLangObject(), + ObjectTpt(), Select(javaLangDot(nme.annotation), tpnme.Annotation) ) def annotationDecl(start: Offset, mods: Modifiers): List[Tree] = { @@ -934,7 +939,7 @@ object JavaParsers { accept(INTERFACE) val nameOffset = in.offset val name = identForType() - val (statics, body) = typeBody(AT, name, List()) + val (statics, body) = typeBody(AT, name) val constructorParams = body.collect { case dd: DefDef => makeParam(dd.name, dd.tpt) @@ -969,7 +974,7 @@ object JavaParsers { val (statics, body) = if (in.token == SEMI) { in.nextToken() - typeBodyDecls(ENUM, name, List()) + typeBodyDecls(ENUM, name) } else (List(), List()) @@ -1093,7 +1098,7 @@ object JavaParsers { */ class OutlineJavaParser(source: SourceFile)(using Context) extends JavaParser(source) with OutlineParserCommon { override def skipBracesHook(): Option[Tree] = None - override def typeBody(leadingToken: Int, parentName: Name, parentTParams: List[TypeDef]): (List[Tree], List[Tree]) = { + override def typeBody(leadingToken: Int, parentName: Name): (List[Tree], List[Tree]) = { skipBraces() (List(EmptyValDef), List(EmptyTree)) } diff --git a/compiler/src/dotty/tools/dotc/printing/OutlinePrinter.scala b/compiler/src/dotty/tools/dotc/printing/OutlinePrinter.scala index 542c80be5663..cd8267355201 100644 --- a/compiler/src/dotty/tools/dotc/printing/OutlinePrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/OutlinePrinter.scala @@ -21,6 +21,18 @@ object OutlinePrinter: */ class OutlinePrinter private (_ctx: Context) extends RefinedPrinter(_ctx) { + /** print the symbol infos of type params for the fake java constructor */ + def shouldShowInfo(tsym: Symbol): Boolean = + tsym != NoSymbol && { + val ctor = tsym.owner + ctor.isAllOf(JavaDefined | PrivateLocal | Invisible) && ctor.isConstructor + } + + override def paramsText[T <: Untyped](params: ParamClause[T]): Text = (params: @unchecked) match + case untpd.TypeDefs(tparams) if shouldShowInfo(tparams.head.symbol) => + "[" ~ toText(tparams.map(_.symbol.info), ", ") ~ "]" + case _ => super.paramsText(params) + /* Typical patterns seen in output of typer for Java code, plus the output of unpickling an ELIDED tree */ def isElidableExpr[T <: Untyped](tree: Tree[T]): Boolean = tree match { case tree if tree.isEmpty => false diff --git a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala index b73f29b4af5e..44ccf5c3c9fe 100644 --- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -28,6 +28,7 @@ import config.{Config, Feature} import dotty.tools.dotc.util.SourcePosition import dotty.tools.dotc.ast.untpd.{MemberDef, Modifiers, PackageDef, RefTree, Template, TypeDef, ValOrDefDef} import cc.{CaptureSet, CapturingType, toCaptureSet, IllegalCaptureRef} +import dotty.tools.dotc.parsing.JavaParsers class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { @@ -1015,10 +1016,18 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { val (params, rest) = impl.body partition { case stat: TypeDef => stat.symbol.is(Param) case stat: ValOrDefDef => - stat.symbol.is(ParamAccessor) && !stat.symbol.isSetter + val sym = stat.symbol + sym.is(ParamAccessor) && !sym.isSetter + || sym.isAllOf(JavaParsers.fakeFlags | Param) case _ => false } - params ::: rest + val params0 = + if constr.symbol.isAllOf(JavaParsers.fakeFlags) then + // filter out fake param accessors + params.filterNot(_.symbol.isAllOf(JavaParsers.fakeFlags | Param)) + else + params + params0 ::: rest } else impl.body diff --git a/compiler/src/dotty/tools/dotc/transform/Pickler.scala b/compiler/src/dotty/tools/dotc/transform/Pickler.scala index e41c3cae8819..0be66828d58c 100644 --- a/compiler/src/dotty/tools/dotc/transform/Pickler.scala +++ b/compiler/src/dotty/tools/dotc/transform/Pickler.scala @@ -87,6 +87,10 @@ class Pickler extends Phase { Pickler.ParallelPickling && !ctx.settings.YtestPickler.value && !ctx.settings.YjavaTasty.value // disable parallel pickling when `-Yjava-tasty` is set (internal testing only) + private def printerContext(isOutline: Boolean)(using Context): Context = + if isOutline then ctx.fresh.setPrinterFn(OutlinePrinter(_)) + else ctx + override def run(using Context): Unit = { val unit = ctx.compilationUnit pickling.println(i"unpickling in run ${ctx.runId}") @@ -95,7 +99,8 @@ class Pickler extends Phase { cls <- dropCompanionModuleClasses(topLevelClasses(unit.tpdTree)) tree <- sliceTopLevel(unit.tpdTree, cls) do - if ctx.settings.YtestPickler.value then beforePickling(cls) = tree.show + if ctx.settings.YtestPickler.value then beforePickling(cls) = + tree.show(using printerContext(unit.typedAsJava)) val sourceRelativePath = val reference = ctx.settings.sourceroot.value @@ -243,16 +248,16 @@ class Pickler extends Phase { pickling.println("************* entered toplevel ***********") val rootCtx = ctx for ((cls, (unit, unpickler)) <- unpicklers) do - if unit.typedAsJava then + val testJava = unit.typedAsJava + if testJava then if unpickler.unpickler.nameAtRef.contents.exists(_ == nme.FromJavaObject) then report.error(em"Pickled reference to FromJavaObject in Java defined $cls in ${cls.source}") - else - val unpickled = unpickler.rootTrees - val freshUnit = CompilationUnit(rootCtx.compilationUnit.source) - freshUnit.needsCaptureChecking = unit.needsCaptureChecking - freshUnit.knowsPureFuns = unit.knowsPureFuns - inContext(rootCtx.fresh.setCompilationUnit(freshUnit)): - testSame(i"$unpickled%\n%", beforePickling(cls), cls) + val unpickled = unpickler.rootTrees + val freshUnit = CompilationUnit(rootCtx.compilationUnit.source) + freshUnit.needsCaptureChecking = unit.needsCaptureChecking + freshUnit.knowsPureFuns = unit.knowsPureFuns + inContext(printerContext(testJava)(using rootCtx.fresh.setCompilationUnit(freshUnit))): + testSame(i"$unpickled%\n%", beforePickling(cls), cls) private def testSame(unpickled: String, previous: String, cls: ClassSymbol)(using Context) = import java.nio.charset.StandardCharsets.UTF_8