diff --git a/compiler/src/dotty/tools/dotc/Compiler.scala b/compiler/src/dotty/tools/dotc/Compiler.scala index 4818fca07d0b..b619dc9d7b6a 100644 --- a/compiler/src/dotty/tools/dotc/Compiler.scala +++ b/compiler/src/dotty/tools/dotc/Compiler.scala @@ -82,6 +82,7 @@ class Compiler { new ElimByName, // Expand by-name parameter references new StringInterpolatorOpt) :: // Optimizes raw and s string interpolators by rewriting them to string concatenations List(new PruneErasedDefs, // Drop erased definitions from scopes and simplify erased expressions + new UninitializedDefs, // Replaces `compiletime.uninitialized` by `_` new InlinePatterns, // Remove placeholders of inlined patterns new VCInlineMethods, // Inlines calls to value class methods new SeqLiterals, // Express vararg arguments as arrays diff --git a/compiler/src/dotty/tools/dotc/transform/PruneErasedDefs.scala b/compiler/src/dotty/tools/dotc/transform/PruneErasedDefs.scala index e18de6e60876..5412736628db 100644 --- a/compiler/src/dotty/tools/dotc/transform/PruneErasedDefs.scala +++ b/compiler/src/dotty/tools/dotc/transform/PruneErasedDefs.scala @@ -19,10 +19,6 @@ import ast.tpd * The phase also replaces all expressions that appear in an erased context by * default values. This is necessary so that subsequent checking phases such * as IsInstanceOfChecker don't give false negatives. - * Finally, the phase replaces `compiletime.uninitialized` on the right hand side - * of a mutable field definition by `_`. This avoids a "is declared erased, but is - * in fact used" error in Erasure and communicates to Constructors that the - * variable does not have an initializer. */ class PruneErasedDefs extends MiniPhase with SymTransformer { thisTransform => import tpd._ @@ -34,39 +30,20 @@ class PruneErasedDefs extends MiniPhase with SymTransformer { thisTransform => override def runsAfterGroupsOf: Set[String] = Set(RefChecks.name, ExplicitOuter.name) override def transformSym(sym: SymDenotation)(using Context): SymDenotation = - if (sym.isEffectivelyErased && sym.isTerm && !sym.is(Private) && sym.owner.isClass) - sym.copySymDenotation(initFlags = sym.flags | Private) - else sym + if !sym.isEffectivelyErased || !sym.isTerm || sym.is(Private) || !sym.owner.isClass then sym + else sym.copySymDenotation(initFlags = sym.flags | Private) override def transformApply(tree: Apply)(using Context): Tree = - if (tree.fun.tpe.widen.isErasedMethod) - cpy.Apply(tree)(tree.fun, tree.args.map(trivialErasedTree)) - else tree - - private def hasUninitializedRHS(tree: ValOrDefDef)(using Context): Boolean = - def recur(rhs: Tree): Boolean = rhs match - case rhs: RefTree => - rhs.symbol == defn.Compiletime_uninitialized - && tree.symbol.is(Mutable) && tree.symbol.owner.isClass - case closureDef(ddef) if defn.isContextFunctionType(tree.tpt.tpe.dealias) => - recur(ddef.rhs) - case _ => - false - recur(tree.rhs) + if !tree.fun.tpe.widen.isErasedMethod then tree + else cpy.Apply(tree)(tree.fun, tree.args.map(trivialErasedTree)) override def transformValDef(tree: ValDef)(using Context): Tree = - val sym = tree.symbol - if tree.symbol.isEffectivelyErased && !tree.rhs.isEmpty then - cpy.ValDef(tree)(rhs = trivialErasedTree(tree)) - else if hasUninitializedRHS(tree) then - cpy.ValDef(tree)(rhs = cpy.Ident(tree.rhs)(nme.WILDCARD).withType(tree.tpt.tpe)) - else - tree + if !tree.symbol.isEffectivelyErased || tree.rhs.isEmpty then tree + else cpy.ValDef(tree)(rhs = trivialErasedTree(tree)) override def transformDefDef(tree: DefDef)(using Context): Tree = - if (tree.symbol.isEffectivelyErased && !tree.rhs.isEmpty) - cpy.DefDef(tree)(rhs = trivialErasedTree(tree)) - else tree + if !tree.symbol.isEffectivelyErased || tree.rhs.isEmpty then tree + else cpy.DefDef(tree)(rhs = trivialErasedTree(tree)) private def trivialErasedTree(tree: Tree)(using Context): Tree = tree.tpe.widenTermRefExpr.dealias.normalized match diff --git a/compiler/src/dotty/tools/dotc/transform/UninitializedDefs.scala b/compiler/src/dotty/tools/dotc/transform/UninitializedDefs.scala new file mode 100644 index 000000000000..629868e2426d --- /dev/null +++ b/compiler/src/dotty/tools/dotc/transform/UninitializedDefs.scala @@ -0,0 +1,49 @@ +package dotty.tools.dotc +package transform + +import core._ +import Contexts._ +import DenotTransformers.SymTransformer +import Flags._ +import SymDenotations._ +import Symbols._ +import Types._ +import typer.RefChecks +import MegaPhase.MiniPhase +import StdNames.nme +import ast.tpd + +/** This phase replaces `compiletime.uninitialized` on the right hand side of a mutable field definition by `_`. + * This avoids a + * ```scala + * "@compileTimeOnly("`uninitialized` can only be used as the right hand side of a mutable field definition")` + * ``` + * error in Erasure and communicates to Constructors that the variable does not have an initializer. + * + * @syntax markdown + */ +class UninitializedDefs extends MiniPhase: + import tpd._ + + override def phaseName: String = UninitializedDefs.name + + override def transformValDef(tree: ValDef)(using Context): Tree = + if !hasUninitializedRHS(tree) then tree + else cpy.ValDef(tree)(rhs = cpy.Ident(tree.rhs)(nme.WILDCARD).withType(tree.tpt.tpe)) + + private def hasUninitializedRHS(tree: ValOrDefDef)(using Context): Boolean = + def recur(rhs: Tree): Boolean = rhs match + case rhs: RefTree => + rhs.symbol == defn.Compiletime_uninitialized + && tree.symbol.is(Mutable) && tree.symbol.owner.isClass + case closureDef(ddef) if defn.isContextFunctionType(tree.tpt.tpe.dealias) => + recur(ddef.rhs) + case _ => + false + recur(tree.rhs) + +end UninitializedDefs + +object UninitializedDefs: + val name: String = "uninitializedDefs" +end UninitializedDefs