Skip to content

Commit 228b61e

Browse files
Merge pull request #7789 from dotty-staging/init-dotty
Safe initialization for Scala
2 parents a71e099 + 6a4d06a commit 228b61e

File tree

166 files changed

+5080
-4
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

166 files changed

+5080
-4
lines changed

compiler/src/dotty/tools/dotc/Compiler.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,8 @@ class Compiler {
5858
new CheckReentrant, // Internal use only: Check that compiled program has no data races involving global vars
5959
new ElimPackagePrefixes, // Eliminate references to package prefixes in Select nodes
6060
new CookComments, // Cook the comments: expand variables, doc, etc.
61-
new CheckStatic) :: // Check restrictions that apply to @static members
61+
new CheckStatic, // Check restrictions that apply to @static members
62+
new init.Checker) :: // Check initialization of objects
6263
List(new CompleteJavaEnums, // Fill in constructors for Java enums
6364
new ElimRepeated, // Rewrite vararg parameters and arguments
6465
new ExpandSAMs, // Expand single abstract method closures to anonymous classes

compiler/src/dotty/tools/dotc/ast/TreeInfo.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -630,7 +630,7 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] =>
630630
/** An extractor for def of a closure contained the block of the closure. */
631631
object closureDef {
632632
def unapply(tree: Tree)(implicit ctx: Context): Option[DefDef] = tree match {
633-
case Block((meth @ DefDef(nme.ANON_FUN, _, _, _, _)) :: Nil, closure: Closure) =>
633+
case Block((meth : DefDef) :: Nil, closure: Closure) if meth.symbol == closure.meth.symbol =>
634634
Some(meth)
635635
case Block(Nil, expr) =>
636636
unapply(expr)

compiler/src/dotty/tools/dotc/config/Printers.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ object Printers {
2828
val implicits: Printer = noPrinter
2929
val implicitsDetailed: Printer = noPrinter
3030
val lexical: Printer = noPrinter
31+
val init: Printer = noPrinter
3132
val inlining: Printer = noPrinter
3233
val interactiv: Printer = noPrinter
3334
val nullables: Printer = noPrinter

compiler/src/dotty/tools/dotc/config/ScalaSettings.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,7 @@ class ScalaSettings extends Settings.SettingGroup {
167167
val YnoKindPolymorphism: Setting[Boolean] = BooleanSetting("-Yno-kind-polymorphism", "Enable kind polymorphism (see https://dotty.epfl.ch/docs/reference/kind-polymorphism.html). Potentially unsound.")
168168
val YexplicitNulls: Setting[Boolean] = BooleanSetting("-Yexplicit-nulls", "Make reference types non-nullable. Nullable types can be expressed with unions: e.g. String|Null.")
169169
val YerasedTerms: Setting[Boolean] = BooleanSetting("-Yerased-terms", "Allows the use of erased terms.")
170+
val YcheckInit: Setting[Boolean] = BooleanSetting("-Ycheck-init", "Check initialization of objects")
170171

171172
/** Area-specific debug output */
172173
val YexplainLowlevel: Setting[Boolean] = BooleanSetting("-Yexplain-lowlevel", "When explaining type errors, show types at a lower level.")

compiler/src/dotty/tools/dotc/core/Symbols.scala

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -461,8 +461,9 @@ object Symbols {
461461
*/
462462
def retainsDefTree(implicit ctx: Context): Boolean =
463463
ctx.settings.YretainTrees.value ||
464-
denot.owner.isTerm || // no risk of leaking memory after a run for these
465-
denot.isOneOf(InlineOrProxy) // need to keep inline info
464+
denot.owner.isTerm || // no risk of leaking memory after a run for these
465+
denot.isOneOf(InlineOrProxy) || // need to keep inline info
466+
ctx.settings.YcheckInit.value // initialization check
466467

467468
/** The last denotation of this symbol */
468469
private var lastDenot: SymDenotation = _
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
package dotty.tools.dotc
2+
package transform
3+
package init
4+
5+
6+
import dotty.tools.dotc._
7+
import ast.tpd
8+
9+
import dotty.tools.dotc.core._
10+
import Contexts.Context
11+
import Types._
12+
13+
import dotty.tools.dotc.transform._
14+
import MegaPhase._
15+
16+
17+
import scala.collection.mutable
18+
19+
20+
class Checker extends MiniPhase {
21+
import tpd._
22+
23+
val phaseName = "initChecker"
24+
25+
// cache of class summary
26+
private val baseEnv = Env(null, mutable.Map.empty)
27+
28+
override val runsAfter = Set(Pickler.name)
29+
30+
override def isEnabled(implicit ctx: Context): Boolean =
31+
super.isEnabled && ctx.settings.YcheckInit.value
32+
33+
override def transformTypeDef(tree: TypeDef)(implicit ctx: Context): tpd.Tree = {
34+
if (!tree.isClassDef) return tree
35+
36+
val cls = tree.symbol.asClass
37+
val instantiable: Boolean =
38+
cls.is(Flags.Module) ||
39+
!cls.isOneOf(Flags.AbstractOrTrait) && {
40+
// see `Checking.checkInstantiable` in typer
41+
val tp = cls.appliedRef
42+
val stp = SkolemType(tp)
43+
val selfType = cls.givenSelfType.asSeenFrom(stp, cls)
44+
!selfType.exists || stp <:< selfType
45+
}
46+
47+
// A concrete class may not be instantiated if the self type is not satisfied
48+
if (instantiable) {
49+
implicit val state = Checking.State(
50+
visited = mutable.Set.empty,
51+
path = Vector.empty,
52+
thisClass = cls,
53+
fieldsInited = mutable.Set.empty,
54+
parentsInited = mutable.Set.empty,
55+
env = baseEnv.withCtx(ctx)
56+
)
57+
58+
Checking.checkClassBody(tree)
59+
}
60+
61+
tree
62+
}
63+
}

0 commit comments

Comments
 (0)