From b84d17dda0057ce3c46d934ee60bc9408cc39a8f Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 18 Jun 2018 13:43:24 -0700 Subject: [PATCH 1/7] Check baseclasses when determining purity of class A class is pure for the purpose of reducing projections in inlinig if none of its baseclasses has an initializer. To make this robust wrt compilation order, we need to move computation of NoInits flags from Typer to the class completer. # Conflicts: # compiler/src/dotty/tools/dotc/typer/Inliner.scala --- .../src/dotty/tools/dotc/ast/TreeInfo.scala | 22 +++++++++---------- .../dotty/tools/dotc/core/Definitions.scala | 5 +++-- .../tools/dotc/core/SymDenotations.scala | 6 +++++ .../src/dotty/tools/dotc/typer/Namer.scala | 2 ++ .../src/dotty/tools/dotc/typer/Typer.scala | 2 -- 5 files changed, 22 insertions(+), 15 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala index 69c415ff7e66..8ea555e5cc4b 100644 --- a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala +++ b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala @@ -240,6 +240,17 @@ trait TreeInfo[T >: Untyped <: Type] { self: Trees.Instance[T] => case y => y } + /** The largest subset of {NoInits, PureInterface} that a + * trait enclosing this statement can have as flags. + */ + def defKind(tree: Tree): FlagSet = unsplice(tree) match { + case EmptyTree | _: Import => NoInitsInterface + case tree: TypeDef => if (tree.isClassDef) NoInits else NoInitsInterface + case tree: DefDef => if (tree.unforcedRhs == EmptyTree) NoInitsInterface else NoInits + case tree: ValDef => if (tree.unforcedRhs == EmptyTree) NoInitsInterface else EmptyFlags + case _ => EmptyFlags + } + /** Checks whether predicate `p` is true for all result parts of this expression, * where we zoom into Ifs, Matches, and Blocks. */ @@ -623,17 +634,6 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] => accum(Nil, root) } - /** The largest subset of {NoInits, PureInterface} that a - * trait enclosing this statement can have as flags. - */ - def defKind(tree: Tree): FlagSet = unsplice(tree) match { - case EmptyTree | _: Import => NoInitsInterface - case tree: TypeDef => if (tree.isClassDef) NoInits else NoInitsInterface - case tree: DefDef => if (tree.unforcedRhs == EmptyTree) NoInitsInterface else NoInits - case tree: ValDef => if (tree.unforcedRhs == EmptyTree) NoInitsInterface else EmptyFlags - case _ => EmptyFlags - } - /** The top level classes in this tree, including only those module classes that * are not a linked class of some other class in the result. */ diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index f5cfb43745f8..b8635b90d85a 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -55,10 +55,10 @@ class Definitions { ctx.newSymbol(owner, name, flags | Permanent, info) private def newClassSymbol(owner: Symbol, name: TypeName, flags: FlagSet, infoFn: ClassSymbol => Type) = - ctx.newClassSymbol(owner, name, flags | Permanent, infoFn) + ctx.newClassSymbol(owner, name, flags | Permanent | NoInits, infoFn) private def enterCompleteClassSymbol(owner: Symbol, name: TypeName, flags: FlagSet, parents: List[TypeRef], decls: Scope = newScope) = - ctx.newCompleteClassSymbol(owner, name, flags | Permanent, parents, decls).entered + ctx.newCompleteClassSymbol(owner, name, flags | Permanent | NoInits, parents, decls).entered private def enterTypeField(cls: ClassSymbol, name: TypeName, flags: FlagSet, scope: MutableScope) = scope.enter(newSymbol(cls, name, flags, TypeBounds.empty)) @@ -275,6 +275,7 @@ class Definitions { val cls = ctx.requiredClass("java.lang.Object") assert(!cls.isCompleted, "race for completing java.lang.Object") cls.info = ClassInfo(cls.owner.thisType, cls, AnyClass.typeRef :: Nil, newScope) + cls.setFlag(NoInits) // The companion object doesn't really exist, `NoType` is the general // technique to do that. Here we need to set it before completing diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index 95301176d78f..b5d308e43c46 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -599,6 +599,12 @@ object SymDenotations { final def isStable(implicit ctx: Context) = isType || !is(Erased) && (is(Stable) || !(is(UnstableValue) || info.isInstanceOf[ExprType])) + /** Is this a denotation of a class that does not have - either direct or inherited - + * initaliazion code? + */ + def isNoInitsClass(implicit ctx: Context) = + isClass && asClass.baseClasses.forall(_.is(NoInits)) + /** Is this a "real" method? A real method is a method which is: * - not an accessor * - not a label diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala index 6e6c44f7a02d..205fb2dad26d 100644 --- a/compiler/src/dotty/tools/dotc/typer/Namer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala @@ -991,6 +991,8 @@ class Namer { typer: Typer => if (isDerivedValueClass(cls)) cls.setFlag(Final) cls.info = avoidPrivateLeaks(cls, cls.pos) cls.baseClasses.foreach(_.invalidateBaseTypeCache()) // we might have looked before and found nothing + cls.setNoInitsFlags((NoInitsInterface /: impl.body) ((fs, stat) => fs & untpd.defKind(stat))) + if (cls.isNoInitsClass) cls.primaryConstructor.setFlag(Stable) } } diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index fa48b6315a63..cb7672832a76 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -1534,8 +1534,6 @@ class Typer extends Namer val dummy = localDummy(cls, impl) val body1 = addAccessorDefs(cls, typedStats(impl.body, dummy)(ctx.inClassContext(self1.symbol))) - if (!ctx.isAfterTyper) - cls.setNoInitsFlags((NoInitsInterface /: body1) ((fs, stat) => fs & defKind(stat))) // Expand comments and type usecases if `-Ycook-comments` is set. if (ctx.settings.YcookComments.value) { From 9a52dbdf8d38e90f76c9bd8117b3a9888d3e5264 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 18 Jun 2018 18:04:05 -0700 Subject: [PATCH 2/7] Better purity predictions Four improvments: 1. Applications of stable and pure functions to pure arguments are pure. This is important since constructors of pure classes are marked Stable. 2. New(...) expressions are pure (any side effects are caused by the constructor application). 3. Module values are pure of their module classes are pure. 4. scala.Product and scala.Serializable are assumed to be pure. Therefore, case classes can also be pure. These predictons remove most remaining unused bindings in the run/typelevel.scala test. --- .../src/dotty/tools/dotc/ast/TreeInfo.scala | 25 +++++++++++-------- .../dotty/tools/dotc/core/Definitions.scala | 7 ++++++ 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala index 8ea555e5cc4b..88c53a122643 100644 --- a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala +++ b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala @@ -369,6 +369,8 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] => refPurity(tree) case Select(qual, _) => refPurity(tree).min(exprPurity(qual)) + case New(_) => + SimplyPure case TypeApply(fn, _) => exprPurity(fn) /* @@ -380,13 +382,12 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] => case Apply(fn, args) => def isKnownPureOp(sym: Symbol) = sym.owner.isPrimitiveValueClass || sym.owner == defn.StringClass - // Note: After uncurry, field accesses are represented as Apply(getter, Nil), - // so an Apply can also be pure. - if (args.isEmpty && fn.symbol.is(Stable)) exprPurity(fn) - else if (tree.tpe.isInstanceOf[ConstantType] && isKnownPureOp(tree.symbol)) - // A constant expression with pure arguments is pure. + if (tree.tpe.isInstanceOf[ConstantType] && isKnownPureOp(tree.symbol) + // A constant expression with pure arguments is pure. + || fn.symbol.isStable) minOf(exprPurity(fn), args.map(exprPurity)) `min` Pure - else Impure + else + Impure case Typed(expr, _) => exprPurity(expr) case Block(stats, expr) => @@ -413,11 +414,15 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] => * @DarkDimius: need to make sure that lazy accessor methods have Lazy and Stable * flags set. */ - def refPurity(tree: Tree)(implicit ctx: Context): PurityLevel = - if (!tree.tpe.widen.isParameterless || tree.symbol.is(Erased)) SimplyPure - else if (!tree.symbol.isStable) Impure - else if (tree.symbol.is(Lazy)) Idempotent // TODO add Module flag, sinxce Module vals or not Lazy from the start. + def refPurity(tree: Tree)(implicit ctx: Context): PurityLevel = { + val sym = tree.symbol + if (!tree.tpe.widen.isParameterless || sym.is(Erased)) SimplyPure + else if (!sym.isStable) Impure + else if (sym.is(Module)) + if (sym.moduleClass.isNoInitsClass) Pure else Idempotent + else if (sym.is(Lazy)) Idempotent else SimplyPure + } def isPureRef(tree: Tree)(implicit ctx: Context) = refPurity(tree) == SimplyPure diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index b8635b90d85a..53642ac4b1c2 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -1216,6 +1216,13 @@ class Definitions { for (m <- ScalaShadowingPackageClass.info.decls) ScalaPackageClass.enter(m) + // Temporary measure, as long as we do not read these classes from Tasty. + // Scala-2 classes don't have NoInits set even if they are pure. We override this + // for Product and Serializable so that case classes can be pure. A full solution + // requiers that we read all Scala code from Tasty. + ProductClass.setFlag(NoInits) + SerializableClass.setFlag(NoInits) + // force initialization of every symbol that is synthesized or hijacked by the compiler val forced = syntheticCoreClasses ++ syntheticCoreMethods ++ ScalaValueClasses() From 462c38867f35de01f8777220432443de51ee89fc Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 21 Jun 2018 12:15:33 -0400 Subject: [PATCH 3/7] Exclude self and super constructor calls from purity warnings Don't issue a "pure expression does nothing in statement position" for self and super constructor calls. They are conceptually unit-returning in this position anyway. --- compiler/src/dotty/tools/dotc/typer/Typer.scala | 3 ++- tests/neg-custom-args/fatal-warnings/i2333.scala | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index cb7672832a76..4daac8b3bcb6 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -1917,7 +1917,8 @@ class Typer extends Namer traverse(stats ++ rest) case stat :: rest => val stat1 = typed(stat)(ctx.exprContext(stat, exprOwner)) - if (!ctx.isAfterTyper && isPureExpr(stat1) && !stat1.tpe.isRef(defn.UnitClass)) + if (!ctx.isAfterTyper && isPureExpr(stat1) && + !stat1.tpe.isRef(defn.UnitClass) && !isSelfOrSuperConstrCall(stat1)) ctx.warning(em"a pure expression does nothing in statement position", stat.pos) buf += stat1 traverse(rest) diff --git a/tests/neg-custom-args/fatal-warnings/i2333.scala b/tests/neg-custom-args/fatal-warnings/i2333.scala index a6772e77783e..a22433394346 100644 --- a/tests/neg-custom-args/fatal-warnings/i2333.scala +++ b/tests/neg-custom-args/fatal-warnings/i2333.scala @@ -1,4 +1,5 @@ @deprecated("bla", "2.11.0") class Foo { + println("") def this(x: Int) = this() } From e42acaaefad9852881b09aa606f2ce3f05f6b3ac Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 22 Jun 2018 21:10:47 -0400 Subject: [PATCH 4/7] Fix pure interface prediction for methods with default arguments Methods with default arguments in traits generate defender methods, so the trait cannot be a pure interface. Checking def kinds earlier in Namer rather than Typer exhibited that special case. --- compiler/src/dotty/tools/dotc/ast/TreeInfo.scala | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala index 88c53a122643..2d0b175f0aad 100644 --- a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala +++ b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala @@ -243,10 +243,13 @@ trait TreeInfo[T >: Untyped <: Type] { self: Trees.Instance[T] => /** The largest subset of {NoInits, PureInterface} that a * trait enclosing this statement can have as flags. */ - def defKind(tree: Tree): FlagSet = unsplice(tree) match { + def defKind(tree: Tree)(implicit ctx: Context): FlagSet = unsplice(tree) match { case EmptyTree | _: Import => NoInitsInterface case tree: TypeDef => if (tree.isClassDef) NoInits else NoInitsInterface - case tree: DefDef => if (tree.unforcedRhs == EmptyTree) NoInitsInterface else NoInits + case tree: DefDef => + if (tree.unforcedRhs == EmptyTree && + tree.vparamss.forall(_.forall(_.rhs.isEmpty))) NoInitsInterface + else NoInits case tree: ValDef => if (tree.unforcedRhs == EmptyTree) NoInitsInterface else EmptyFlags case _ => EmptyFlags } @@ -416,7 +419,8 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] => */ def refPurity(tree: Tree)(implicit ctx: Context): PurityLevel = { val sym = tree.symbol - if (!tree.tpe.widen.isParameterless || sym.is(Erased)) SimplyPure + if (!tree.hasType) Impure + else if (!tree.tpe.widen.isParameterless || sym.is(Erased)) SimplyPure else if (!sym.isStable) Impure else if (sym.is(Module)) if (sym.moduleClass.isNoInitsClass) Pure else Idempotent From 2a0ab6f69ea100c1ffe09f9d75c64a7bbd323e9b Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 22 Jun 2018 21:12:26 -0400 Subject: [PATCH 5/7] Account for stable constrictors in ExtractAPI Constructors (and potentially, in the future, other methods) can have the stable flag set. We need to adapt ExtractAPI to this change. --- compiler/src/dotty/tools/dotc/sbt/ExtractAPI.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/sbt/ExtractAPI.scala b/compiler/src/dotty/tools/dotc/sbt/ExtractAPI.scala index e26fe556510b..4dcd282852c3 100644 --- a/compiler/src/dotty/tools/dotc/sbt/ExtractAPI.scala +++ b/compiler/src/dotty/tools/dotc/sbt/ExtractAPI.scala @@ -328,7 +328,7 @@ private class ExtractAPICollector(implicit val ctx: Context) extends ThunkHolder } else if (sym.is(Mutable, butNot = Accessor)) { api.Var.of(sym.name.toString, apiAccess(sym), apiModifiers(sym), apiAnnotations(sym).toArray, apiType(sym.info)) - } else if (sym.isStable) { + } else if (sym.isStable && !sym.isRealMethod) { api.Val.of(sym.name.toString, apiAccess(sym), apiModifiers(sym), apiAnnotations(sym).toArray, apiType(sym.info)) } else { From 0c99a37b248b2d7ada4cb5e96fa726ca5ba80e22 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 23 Jun 2018 08:00:36 -0400 Subject: [PATCH 6/7] Take parents of class into account when determining NoInits flags We declare a class (not a trait) impure if it passes arguments to its parents. This is a very conservative estimate. It's hard to do better, though, since parent expressions are not always typed when we need to decide class purity. So we cannot use `isPureExpr` on argument expressions to find out more. --- .../src/dotty/tools/dotc/ast/TreeInfo.scala | 17 ++++++++++++++++- .../dotty/tools/dotc/core/SymDenotations.scala | 13 ++++++++----- .../tools/dotc/core/tasty/TreeUnpickler.scala | 2 +- compiler/src/dotty/tools/dotc/typer/Namer.scala | 2 +- 4 files changed, 26 insertions(+), 8 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala index 2d0b175f0aad..2516d4bf406e 100644 --- a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala +++ b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala @@ -241,7 +241,7 @@ trait TreeInfo[T >: Untyped <: Type] { self: Trees.Instance[T] => } /** The largest subset of {NoInits, PureInterface} that a - * trait enclosing this statement can have as flags. + * trait or class enclosing this statement can have as flags. */ def defKind(tree: Tree)(implicit ctx: Context): FlagSet = unsplice(tree) match { case EmptyTree | _: Import => NoInitsInterface @@ -254,6 +254,21 @@ trait TreeInfo[T >: Untyped <: Type] { self: Trees.Instance[T] => case _ => EmptyFlags } + /** The largest subset of {NoInits, PureInterface} that a + * trait or class with these parents can have as flags. + */ + def parentsKind(parents: List[Tree])(implicit ctx: Context): FlagSet = parents match { + case Nil => NoInitsInterface + case Apply(_, _ :: _) :: _ => EmptyFlags + case _ :: parents1 => parentsKind(parents1) + } + + /** The largest subset of {NoInits, PureInterface} that a + * trait or class with this body can have as flags. + */ + def bodyKind(body: List[Tree])(implicit ctx: Context): FlagSet = + (NoInitsInterface /: body)((fs, stat) => fs & defKind(stat)) + /** Checks whether predicate `p` is true for all result parts of this expression, * where we zoom into Ifs, Matches, and Blocks. */ diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index b5d308e43c46..1d7bc871c860 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -167,11 +167,14 @@ object SymDenotations { /** Unset given flags(s) of this denotation */ final def resetFlag(flags: FlagSet): Unit = { myFlags &~= flags } - /** Set applicable flags from `flags` which is a subset of {NoInits, PureInterface} */ - final def setNoInitsFlags(flags: FlagSet): Unit = { - val mask = if (myFlags.is(Trait)) NoInitsInterface else NoInits - setFlag(flags & mask) - } + /** Set applicable flags in {NoInits, PureInterface} + * @param parentFlags The flags that match the class or trait's parents + * @param bodyFlags The flags that match the class or trait's body + */ + final def setNoInitsFlags(parentFlags: FlagSet, bodyFlags: FlagSet): Unit = + setFlag( + if (myFlags.is(Trait)) NoInitsInterface & bodyFlags // no parents are initialized from a trait + else NoInits & bodyFlags & parentFlags) private def isCurrent(fs: FlagSet) = fs <= ( diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index ce955cee96c3..558d6c2559c5 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -851,7 +851,7 @@ class TreeUnpickler(reader: TastyReader, else EmptyValDef cls.info = ClassInfo(cls.owner.thisType, cls, parentTypes, cls.unforcedDecls, if (self.isEmpty) NoType else self.tpt.tpe) - cls.setNoInitsFlags(fork.indexStats(end)) + cls.setNoInitsFlags(parentsKind(parents), fork.indexStats(end)) val constr = readIndexedDef().asInstanceOf[DefDef] val mappedParents = parents.map(_.changeOwner(localDummy, constr.symbol)) diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala index 205fb2dad26d..2b6491f0de68 100644 --- a/compiler/src/dotty/tools/dotc/typer/Namer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala @@ -991,7 +991,7 @@ class Namer { typer: Typer => if (isDerivedValueClass(cls)) cls.setFlag(Final) cls.info = avoidPrivateLeaks(cls, cls.pos) cls.baseClasses.foreach(_.invalidateBaseTypeCache()) // we might have looked before and found nothing - cls.setNoInitsFlags((NoInitsInterface /: impl.body) ((fs, stat) => fs & untpd.defKind(stat))) + cls.setNoInitsFlags(parentsKind(parents), bodyKind(rest)) if (cls.isNoInitsClass) cls.primaryConstructor.setFlag(Stable) } } From 958bc4835c322176a6c3beb04c1f7f870e9c0642 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 23 Jun 2018 16:54:19 +0200 Subject: [PATCH 7/7] Fix ErrorMessagesTest Avoid "pure expression does nothing in statement position" warnings. --- .../dotty/tools/dotc/reporting/ErrorMessagesTests.scala | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTests.scala b/compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTests.scala index d8327bc3a56b..0f7605966b98 100644 --- a/compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTests.scala +++ b/compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTests.scala @@ -163,7 +163,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { """ |object Scope { | abstract class Concept - | new Concept() + | val x = new Concept() |} """.stripMargin } @@ -181,7 +181,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { """ |object Scope { | trait Concept - | new Concept() + | val x = new Concept() |} """.stripMargin } @@ -508,7 +508,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { """class Base |class RequiresBase { self: Base => } |object Scope { - | new RequiresBase + | val x = new RequiresBase |} |""".stripMargin }