diff options
21 files changed, 115 insertions, 38 deletions
diff --git a/spec/01-lexical-syntax.md b/spec/01-lexical-syntax.md index e4764c10dc..78f1a1a408 100644 --- a/spec/01-lexical-syntax.md +++ b/spec/01-lexical-syntax.md @@ -55,7 +55,7 @@ plainid ::= upper idrest | varid | op id ::= plainid - | ‘`’ stringLiteral ‘`’ + | ‘`’ { charNoBackQuoteOrNewline | UnicodeEscape | charEscapeSeq } ‘`’ idrest ::= {letter | digit} [‘_’ op] ``` diff --git a/spec/13-syntax-summary.md b/spec/13-syntax-summary.md index dd042824f4..be5cc1324e 100644 --- a/spec/13-syntax-summary.md +++ b/spec/13-syntax-summary.md @@ -38,7 +38,7 @@ plainid ::= upper idrest | varid | op id ::= plainid - | ‘`’ stringLiteral ‘`’ + | ‘`’ { charNoBackQuoteOrNewline | UnicodeEscape | charEscapeSeq } ‘`’ idrest ::= {letter | digit} [‘_’ op] integerLiteral ::= (decimalNumeral | hexNumeral) [‘L’ | ‘l’] diff --git a/src/compiler/scala/tools/nsc/transform/AccessorSynthesis.scala b/src/compiler/scala/tools/nsc/transform/AccessorSynthesis.scala index a1923ead21..a0bba46398 100644 --- a/src/compiler/scala/tools/nsc/transform/AccessorSynthesis.scala +++ b/src/compiler/scala/tools/nsc/transform/AccessorSynthesis.scala @@ -332,7 +332,7 @@ trait AccessorSynthesis extends Transform with ast.TreeDSL { val isUnit = isUnitGetter(lazyAccessor) val selectVar = if (isUnit) UNIT else Select(thisRef, lazyVar) - val storeRes = if (isUnit) rhsAtSlowDef else Assign(selectVar, rhsAtSlowDef) + val storeRes = if (isUnit) rhsAtSlowDef else Assign(selectVar, fields.castHack(rhsAtSlowDef, lazyVar.info)) def needsInit = mkTest(lazyAccessor) val doInit = Block(List(storeRes), mkSetFlag(lazyAccessor)) diff --git a/src/compiler/scala/tools/nsc/transform/Fields.scala b/src/compiler/scala/tools/nsc/transform/Fields.scala index 0fe7a82b15..b09223110a 100644 --- a/src/compiler/scala/tools/nsc/transform/Fields.scala +++ b/src/compiler/scala/tools/nsc/transform/Fields.scala @@ -510,6 +510,16 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor def nonStaticModuleToMethod(module: Symbol): Unit = if (!module.isStatic) module setFlag METHOD | STABLE + // scala/scala-dev#219, scala/scala-dev#268 + // Cast to avoid spurious mismatch in paths containing trait vals that have + // not been rebound to accessors in the subclass we're in now. + // For example, for a lazy val mixed into a class, the lazy var's info + // will not refer to symbols created during our info transformer, + // so if its type depends on a val that is now implemented after the info transformer, + // we'll get a mismatch when assigning `rhs` to `lazyVarOf(getter)`. + // TODO: could we rebind more aggressively? consider overriding in type equality? + def castHack(tree: Tree, pt: Type) = gen.mkAsInstanceOf(tree, pt) + class FieldsTransformer(unit: CompilationUnit) extends TypingTransformer(unit) with CheckedAccessorTreeSynthesis { protected def typedPos(pos: Position)(tree: Tree): Tree = localTyper.typedPos(pos)(tree) @@ -596,15 +606,6 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor // synth trees for accessors/fields and trait setters when they are mixed into a class def fieldsAndAccessors(clazz: Symbol): List[Tree] = { - // scala/scala-dev#219 - // Cast to avoid spurious mismatch in paths containing trait vals that have - // not been rebound to accessors in the subclass we're in now. - // For example, for a lazy val mixed into a class, the lazy var's info - // will not refer to symbols created during our info transformer, - // so if its type depends on a val that is now implemented after the info transformer, - // we'll get a mismatch when assigning `rhs` to `lazyVarOf(getter)`. - // TODO: could we rebind more aggressively? consider overriding in type equality? - def cast(tree: Tree, pt: Type) = gen.mkAsInstanceOf(tree, pt) // Could be NoSymbol, which denotes an error, but it's refchecks' job to report it (this fallback is for robustness). // This is the result of overriding a val with a def, so that no field is found in the subclass. @@ -615,14 +616,14 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor // accessor created by newMatchingModuleAccessor for a static module that does need an accessor // (because there's a matching member in a super class) if (getter.asTerm.referenced.isModule) - mkAccessor(getter)(cast(Select(This(clazz), getter.asTerm.referenced), getter.info.resultType)) + mkAccessor(getter)(castHack(Select(This(clazz), getter.asTerm.referenced), getter.info.resultType)) else { val fieldMemoization = fieldMemoizationIn(getter, clazz) // TODO: drop getter for constant? (when we no longer care about producing identical bytecode?) if (fieldMemoization.constantTyped) mkAccessor(getter)(gen.mkAttributedQualifier(fieldMemoization.tp)) else fieldAccess(getter) match { case NoSymbol => EmptyTree - case fieldSel => mkAccessor(getter)(cast(Select(This(clazz), fieldSel), getter.info.resultType)) + case fieldSel => mkAccessor(getter)(castHack(Select(This(clazz), fieldSel), getter.info.resultType)) } } @@ -636,7 +637,7 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor else fieldAccess(setter) match { case NoSymbol => EmptyTree case fieldSel => afterOwnPhase { // the assign only type checks after our phase (assignment to val) - mkAccessor(setter)(Assign(Select(This(clazz), fieldSel), cast(Ident(setter.firstParam), fieldSel.info))) + mkAccessor(setter)(Assign(Select(This(clazz), fieldSel), castHack(Ident(setter.firstParam), fieldSel.info))) } } @@ -657,7 +658,7 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor val selectSuper = Select(Super(This(clazz), tpnme.EMPTY), getter.name) val lazyVar = lazyVarOf(getter) - val rhs = cast(Apply(selectSuper, Nil), lazyVar.info) + val rhs = castHack(Apply(selectSuper, Nil), lazyVar.info) synthAccessorInClass.expandLazyClassMember(lazyVar, getter, rhs) } @@ -708,7 +709,7 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor val transformedRhs = atOwner(statSym)(transform(rhs)) if (rhs == EmptyTree) mkAccessor(statSym)(EmptyTree) - else if (currOwner.isTrait) mkAccessor(statSym)(transformedRhs) + else if (currOwner.isTrait) mkAccessor(statSym)(castHack(transformedRhs, statSym.info.resultType)) else if (!currOwner.isClass) mkLazyLocalDef(vd.symbol, transformedRhs) else { // TODO: make `synthAccessorInClass` a field and update it in atOwner? diff --git a/src/compiler/scala/tools/nsc/transform/patmat/Logic.scala b/src/compiler/scala/tools/nsc/transform/patmat/Logic.scala index 4ae97ce281..cb3759e5fa 100644 --- a/src/compiler/scala/tools/nsc/transform/patmat/Logic.scala +++ b/src/compiler/scala/tools/nsc/transform/patmat/Logic.scala @@ -682,7 +682,7 @@ trait ScalaLogic extends Interface with Logic with TreeAndTypeAnalysis { private[TreesAndTypesDomain] def uniqueTpForTree(t: Tree): Type = { def freshExistentialSubtype(tp: Type): Type = { // SI-8611 tp.narrow is tempting, but unsuitable. See `testRefinedTypeSI8611` for an explanation. - NoSymbol.freshExistential("").setInfo(TypeBounds.upper(tp)).tpe + NoSymbol.freshExistential("", 0).setInfo(TypeBounds.upper(tp)).tpe } if (!t.symbol.isStable) { diff --git a/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala b/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala index d11417192d..0f257d3717 100644 --- a/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala +++ b/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala @@ -132,7 +132,11 @@ trait MethodSynthesis { // only one symbol can have `tree.pos`, the others must focus their position // normally the field gets the range position, but if there is none, give it to the getter + // + // SI-10009 the tree's modifiers can be temporarily out of sync with the new symbol's flags. + // typedValDef corrects this later on. tree.symbol = fieldSym orElse (getterSym setPos tree.pos) + val namer = namerOf(tree.symbol) // the valdef gets the accessor symbol for a lazy val (too much going on in its RHS) diff --git a/src/compiler/scala/tools/nsc/typechecker/PatternTypers.scala b/src/compiler/scala/tools/nsc/typechecker/PatternTypers.scala index 1df3449ce6..cd0c292d90 100644 --- a/src/compiler/scala/tools/nsc/typechecker/PatternTypers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/PatternTypers.scala @@ -123,7 +123,7 @@ trait PatternTypers { } private def boundedArrayType(bound: Type): Type = { - val tparam = context.owner freshExistential "" setInfo (TypeBounds upper bound) + val tparam = context.owner.freshExistential("", 0) setInfo (TypeBounds upper bound) newExistentialType(tparam :: Nil, arrayType(tparam.tpe_*)) } diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index cca6f280e3..78533bdfc5 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -13,11 +13,12 @@ package scala package tools.nsc package typechecker -import scala.collection.{mutable, immutable} -import scala.reflect.internal.util.{ Statistics, ListOfNil } +import scala.collection.{immutable, mutable} +import scala.reflect.internal.util.{ListOfNil, Statistics} import mutable.ListBuffer import symtab.Flags._ import Mode._ +import scala.reflect.macros.whitebox // Suggestion check whether we can do without priming scopes with symbols of outer scopes, // like the IDE does. @@ -2020,7 +2021,12 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper // use typedValDef instead. this version is called after creating a new context for the ValDef private def typedValDefImpl(vdef: ValDef) = { val sym = vdef.symbol.initialize - val typedMods = typedModifiers(vdef.mods) + val typedMods = if (nme.isLocalName(sym.name) && sym.isPrivateThis && !vdef.mods.isPrivateLocal) { + // SI-10009 This tree has been given a field symbol by `enterGetterSetter`, patch up the + // modifiers accordingly so that we can survive resetAttrs and retypechecking. + // Similarly, we use `sym.name` rather than `vdef.name` below to use the local name. + typedModifiers(vdef.mods.copy(flags = sym.flags, privateWithin = tpnme.EMPTY)) + } else typedModifiers(vdef.mods) sym.annotations.map(_.completeInfo()) val tpt1 = checkNoEscaping.privates(sym, typedType(vdef.tpt)) @@ -2055,7 +2061,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper } else tpt1.tpe transformedOrTyped(vdef.rhs, EXPRmode | BYVALmode, tpt2) } - treeCopy.ValDef(vdef, typedMods, vdef.name, tpt1, checkDead(rhs1)) setType NoType + treeCopy.ValDef(vdef, typedMods, sym.name, tpt1, checkDead(rhs1)) setType NoType } /** Enter all aliases of local parameter accessors. diff --git a/src/reflect/scala/reflect/internal/Symbols.scala b/src/reflect/scala/reflect/internal/Symbols.scala index 56b6dc078d..5077169525 100644 --- a/src/reflect/scala/reflect/internal/Symbols.scala +++ b/src/reflect/scala/reflect/internal/Symbols.scala @@ -34,9 +34,13 @@ trait Symbols extends api.Symbols { self: SymbolTable => def recursionTable = _recursionTable def recursionTable_=(value: immutable.Map[Symbol, Int]) = _recursionTable = value + @deprecated("Global existential IDs no longer used", "2.12.1") private var existentialIds = 0 + @deprecated("Global existential IDs no longer used", "2.12.1") protected def nextExistentialId() = { existentialIds += 1; existentialIds } - protected def freshExistentialName(suffix: String) = newTypeName("_" + nextExistentialId() + suffix) + @deprecated("Use overload that accepts an id", "2.12.1") + protected def freshExistentialName(suffix: String): TypeName = freshExistentialName(suffix, nextExistentialId()) + protected def freshExistentialName(suffix: String, id: Int): TypeName = newTypeName("_" + id + suffix) // Set the fields which point companions at one another. Returns the module. def connectModuleToClass(m: ModuleSymbol, moduleClass: ClassSymbol): ModuleSymbol = { @@ -440,8 +444,11 @@ trait Symbols extends api.Symbols { self: SymbolTable => def newGADTSkolem(name: TypeName, origin: Symbol, info: Type): TypeSkolem = newTypeSkolemSymbol(name, origin, origin.pos, origin.flags & ~(EXISTENTIAL | PARAM) | GADT_SKOLEM_FLAGS) setInfo info + @deprecated("Use overload that accepts an id", "2.12.1") final def freshExistential(suffix: String): TypeSymbol = newExistential(freshExistentialName(suffix), pos) + final def freshExistential(suffix: String, id: Int): TypeSymbol = + newExistential(freshExistentialName(suffix, id), pos) /** Type skolems are type parameters ''seen from the inside'' * Assuming a polymorphic method m[T], its type is a PolyType which has a TypeParameter diff --git a/src/reflect/scala/reflect/internal/TreeInfo.scala b/src/reflect/scala/reflect/internal/TreeInfo.scala index 61937958dd..1aef30819a 100644 --- a/src/reflect/scala/reflect/internal/TreeInfo.scala +++ b/src/reflect/scala/reflect/internal/TreeInfo.scala @@ -480,7 +480,8 @@ abstract class TreeInfo { } map { dd => val DefDef(dmods, dname, _, _, _, drhs) = dd // get access flags from DefDef - val vdMods = (vmods &~ Flags.AccessFlags) | (dmods & Flags.AccessFlags).flags + val defDefMask = Flags.AccessFlags | OVERRIDE | IMPLICIT | DEFERRED + val vdMods = (vmods &~ defDefMask) | (dmods & defDefMask).flags // for most cases lazy body should be taken from accessor DefDef val vdRhs = if (vmods.isLazy) lazyValDefRhs(drhs) else vrhs copyValDef(vd)(mods = vdMods, name = dname, rhs = vdRhs) diff --git a/src/reflect/scala/reflect/internal/Types.scala b/src/reflect/scala/reflect/internal/Types.scala index f8679616d1..5e1bf37b80 100644 --- a/src/reflect/scala/reflect/internal/Types.scala +++ b/src/reflect/scala/reflect/internal/Types.scala @@ -4487,6 +4487,7 @@ trait Types debuglog(s"transposed irregular matrix!? tps=$tps argss=$argss") NoType case Some(argsst) => + var capturedParamIds = 0 val args = map2(sym.typeParams, argsst) { (tparam, as0) => val as = as0.distinct if (as.size == 1) as.head @@ -4508,8 +4509,10 @@ trait Types else { // Martin: I removed this, because incomplete. Not sure there is a good way to fix it. For the moment we // just err on the conservative side, i.e. with a bound that is too high. // if(!(tparam.info.bounds contains tparam)) //@M can't deal with f-bounds, see #2251 + capturedParamIds += 1 + val capturedParamId = capturedParamIds - val qvar = commonOwner(as) freshExistential "" setInfo TypeBounds(g, l) + val qvar = commonOwner(as).freshExistential("", capturedParamId) setInfo TypeBounds(g, l) capturedParams += qvar qvar.tpe } diff --git a/src/reflect/scala/reflect/internal/tpe/TypeMaps.scala b/src/reflect/scala/reflect/internal/tpe/TypeMaps.scala index ba4f2bec4b..08219c0634 100644 --- a/src/reflect/scala/reflect/internal/tpe/TypeMaps.scala +++ b/src/reflect/scala/reflect/internal/tpe/TypeMaps.scala @@ -512,6 +512,8 @@ private[internal] trait TypeMaps { && isBaseClassOfEnclosingClass(sym.owner) ) + private var capturedThisIds= 0 + private def nextCapturedThisId() = { capturedThisIds += 1; capturedThisIds } /** Creates an existential representing a type parameter which appears * in the prefix of a ThisType. */ @@ -519,7 +521,7 @@ private[internal] trait TypeMaps { capturedParams find (_.owner == clazz) match { case Some(p) => p.tpe case _ => - val qvar = clazz freshExistential nme.SINGLETON_SUFFIX setInfo singletonBounds(pre) + val qvar = clazz.freshExistential(nme.SINGLETON_SUFFIX, nextCapturedThisId()) setInfo singletonBounds(pre) _capturedParams ::= qvar debuglog(s"Captured This(${clazz.fullNameString}) seen from $seenFromPrefix: ${qvar.defString}") qvar.tpe diff --git a/src/reflect/scala/reflect/runtime/SynchronizedSymbols.scala b/src/reflect/scala/reflect/runtime/SynchronizedSymbols.scala index 237afa082b..4e7ddda54e 100644 --- a/src/reflect/scala/reflect/runtime/SynchronizedSymbols.scala +++ b/src/reflect/scala/reflect/runtime/SynchronizedSymbols.scala @@ -10,7 +10,9 @@ private[reflect] trait SynchronizedSymbols extends internal.Symbols { self: Symb private lazy val atomicIds = new java.util.concurrent.atomic.AtomicInteger(0) override protected def nextId() = atomicIds.incrementAndGet() + @deprecated("Global existential IDs no longer used", "2.12.1") private lazy val atomicExistentialIds = new java.util.concurrent.atomic.AtomicInteger(0) + @deprecated("Global existential IDs no longer used", "2.12.1") override protected def nextExistentialId() = atomicExistentialIds.incrementAndGet() private lazy val _recursionTable = mkThreadLocalStorage(immutable.Map.empty[Symbol, Int]) diff --git a/test/files/neg/sabin2.check b/test/files/neg/sabin2.check index aa0e8f734c..cd6fde4608 100644 --- a/test/files/neg/sabin2.check +++ b/test/files/neg/sabin2.check @@ -1,6 +1,6 @@ sabin2.scala:22: error: type mismatch; found : Test.Base#T - required: _5.T where val _5: Test.Base + required: _1.T where val _1: Test.Base a.set(b.get()) // Error ^ one error found diff --git a/test/files/neg/t0764.check b/test/files/neg/t0764.check index 830278e715..0c7cff1e1e 100644 --- a/test/files/neg/t0764.check +++ b/test/files/neg/t0764.check @@ -1,5 +1,5 @@ t0764.scala:13: error: type mismatch; - found : Node{type T = _2.type} where val _2: Node{type T = NextType} + found : Node{type T = _1.type} where val _1: Node{type T = NextType} required: Node{type T = Main.this.AType} (which expands to) Node{type T = Node{type T = NextType}} new Main[AType]( (value: AType).prepend ) diff --git a/test/files/neg/t1010.check b/test/files/neg/t1010.check index 2cc8f9d986..d412d8ac1e 100644 --- a/test/files/neg/t1010.check +++ b/test/files/neg/t1010.check @@ -1,6 +1,6 @@ t1010.scala:14: error: type mismatch; found : MailBox#Message - required: _3.in.Message where val _3: Actor + required: _1.in.Message where val _1: Actor unstable.send(msg) // in.Message becomes unstable.Message, but that's ok since Message is a concrete type member ^ one error found diff --git a/test/files/neg/t5120.check b/test/files/neg/t5120.check index 34d4ebde31..b6a3cb96aa 100644 --- a/test/files/neg/t5120.check +++ b/test/files/neg/t5120.check @@ -6,7 +6,7 @@ t5120.scala:11: error: type mismatch; t5120.scala:25: error: type mismatch; found : Thread required: h.T - (which expands to) _2 + (which expands to) _1 List(str, num).foreach(h => h.f1 = new Thread()) ^ two errors found diff --git a/test/files/neg/t6829.check b/test/files/neg/t6829.check index 274094f791..5ccd531be1 100644 --- a/test/files/neg/t6829.check +++ b/test/files/neg/t6829.check @@ -1,6 +1,6 @@ t6829.scala:35: error: type mismatch; found : AgentSimulation.this.state.type (with underlying type G#State) - required: _9.State + required: _1.State lazy val actions: Map[G#Agent,G#Action] = agents.map(a => a -> a.chooseAction(state)).toMap ^ t6829.scala:45: error: trait AgentSimulation takes type parameters @@ -17,32 +17,32 @@ t6829.scala:49: error: not found: value nextState ^ t6829.scala:50: error: type mismatch; found : s.type (with underlying type Any) - required: _30.State where val _30: G + required: _1.State where val _1: G val r = rewards(agent).r(s,a,s2) ^ t6829.scala:50: error: type mismatch; found : a.type (with underlying type Any) - required: _30.Action where val _30: G + required: _1.Action where val _1: G val r = rewards(agent).r(s,a,s2) ^ t6829.scala:50: error: type mismatch; found : s2.type (with underlying type Any) - required: _30.State where val _30: G + required: _1.State where val _1: G val r = rewards(agent).r(s,a,s2) ^ t6829.scala:51: error: type mismatch; found : s.type (with underlying type Any) - required: _25.State + required: _1.State agent.learn(s,a,s2,r): G#Agent ^ t6829.scala:51: error: type mismatch; found : a.type (with underlying type Any) - required: _25.Action + required: _1.Action agent.learn(s,a,s2,r): G#Agent ^ t6829.scala:51: error: type mismatch; found : s2.type (with underlying type Any) - required: _25.State + required: _1.State agent.learn(s,a,s2,r): G#Agent ^ t6829.scala:53: error: not found: value nextState diff --git a/test/files/pos/sd268.scala b/test/files/pos/sd268.scala new file mode 100644 index 0000000000..8839651501 --- /dev/null +++ b/test/files/pos/sd268.scala @@ -0,0 +1,17 @@ +class Context(val v : AnyRef) + +trait AbidePlugin { + val someVal = "" + + val x = null.asInstanceOf[Context { val v : someVal.type }] // CRASH + lazy val y = null.asInstanceOf[Context { val v : someVal.type }] // CRASH + var z = null.asInstanceOf[Context { val v : someVal.type }] // CRASH +} + +class C { + val someVal = "" + + val x = null.asInstanceOf[Context { val v : someVal.type }] + lazy val y = null.asInstanceOf[Context { val v : someVal.type }] // CRASH + var z = null.asInstanceOf[Context { val v : someVal.type }] +} diff --git a/test/files/pos/t10009.scala b/test/files/pos/t10009.scala new file mode 100644 index 0000000000..7cd96f0f3d --- /dev/null +++ b/test/files/pos/t10009.scala @@ -0,0 +1,6 @@ +class C { + def c(a: Any, b: Any*) = a +} +object Test { + new C().c(b = new { val x = 42 }, a = 0) +} diff --git a/test/files/run/t10009.scala b/test/files/run/t10009.scala new file mode 100644 index 0000000000..2a318752f1 --- /dev/null +++ b/test/files/run/t10009.scala @@ -0,0 +1,28 @@ +import scala.reflect.runtime.currentMirror +import scala.reflect.runtime.universe._ +import scala.tools.reflect.ToolBox + +object Test { + def test(code: String, log: Boolean = false) { + val tb = currentMirror.mkToolBox() + val tree = tb.parse(code) + val typed = tb.typecheck(tree) + if (log) { + println("=" * 80) + println(typed) + } + val untyped = tb.untypecheck(typed) + if (log) println(untyped) + val retyped = tb.typecheck(untyped) + if (log) println(retyped) + } + def main(args: Array[String]): Unit = { + test("{ class a { val x = 42 }; new a }") // failed + test("{ trait a { val x = 42 }; new a {} }") // worked + test("{ abstract class a { val x: Int } }") // worked + test("{ abstract class a { val x: Int }; new a { val x = 42 } }") // failed + test("{ class a { private val x = 42 }; new a }") // failed + test("{ class a { protected val x = 42 }; new a { x } }") // failed + test("{ class a { protected[a] val x = 42 }; new a }") // failed + } +}
\ No newline at end of file |