Open scabug opened 16 years ago
Imported From: https://issues.scala-lang.org/browse/SI-1352?orig=1 Reporter: @michael-nischt
@paulp said: See also #1994.
@odersky said: This unfortunately would require some major re-engineering. The problem is that right now the type of the anonymous class is defined as the least refinement type that contains the type given. And subtyping does not take protected modifiers into account. I agree the spec is ambiguous on what should happen, so something needs to be done. It won't be easy, though.
@Atry said: Isn't an anonymous class same as a singleton?
object buffer extends collection.mutable.ArrayBuffer[Int]
{
// chagne from public to protected
override def ensureSize(n : Int) = super.ensureSize(n)
def foo = println("new method")
}
@paulp said: These are just bugs. There's no reason for "override def foo()" not to widen the access to public just because it's an anonymous class. If you wanted it to remain protected, you would have written "override protected def foo()".
scala> abstract class FooClass { protected def bar: Int }
defined class FooClass
scala> val x = new FooClass { def bar = 1 }
x: FooClass = $anon$1@7da72267
scala> x.bar
<console>:10: error: method bar in class FooClass cannot be accessed in FooClass
Access to protected method bar not permitted because
enclosing object $iw is not a subclass of
class FooClass where target is defined
x.bar
^
scala> val x = new FooClass { val bar = 1 }
x: FooClass{val bar: Int} = $anon$1@393ddf54
scala> x.bar
res0: Int = 1
But...
scala> abstract class FooClass { protected val bar: Int }
defined class FooClass
scala> val x = new FooClass { val bar = 1 }
x: FooClass = $anon$1@72f9a516
scala> x.bar
<console>:10: error: value bar in class FooClass cannot be accessed in FooClass
Access to protected value bar not permitted because
enclosing object $iw is not a subclass of
class FooClass where target is defined
x.bar
^
scala insists I bring SOMETHING new to the table if I want to widen the access: def becomes val, more specific return type... but with no "public" keyword, it is impossible to just say it. But there's no reason we should make you say it anyway, because the absence of access modifiers means public! Why would it infer a more restrictive type than the one we declared?
Here's the kind of desperation one ends up with.
scala> abstract class FooClass { protected val bar: Int }
defined class FooClass
scala> val x = new FooClass { override val bar = 1.asInstanceOf[Int with Nothing] }
x: FooClass{val bar: Int with Nothing} = $anon$1@6e963c02
scala> x.bar
res0: Int with Nothing = 1
scala> x.bar: Int
res1: Int = 1
@gkossakowski said: Unassigning and rescheduling to M7 as previous deadline was missed.
@Atry said:
Welcome to Scala version 2.10.3 (Java HotSpot(TM) Client VM, Java 1.7.0_25).
Type in expressions to have them evaluated.
Type :help for more information.
scala> trait A { protected val foo: Int }
defined trait A
scala> trait B { protected val foo: Int }
defined trait B
scala> new A with B { val foo = 1; val bar = 2 }
res0: A with B{val bar: Int} = $anon$1@1e3c5fd
scala> res0.bar
warning: there were 1 feature warning(s); re-run with -feature for details
res1: Int = 2
scala> res0.foo
<console>:11: error: value foo in trait B cannot be accessed in A with B{val bar: Int}
Access to protected value foo not permitted because
enclosing object $iw is not a subclass of
trait B where target is defined
res0.foo
^
scala> (new A with B { val foo = 1; val bar = 2 }).foo // It works, why?
res3: Int = 1
@retronym said: You don't really want to know:
def typedBlock(block0: Block, mode: Mode, pt: Type): Block = {
val syntheticPrivates = new ListBuffer[Symbol]
try {
namer.enterSyms(block0.stats)
val block = treeCopy.Block(block0, pluginsEnterStats(this, block0.stats), block0.expr)
for (stat <- block.stats) enterLabelDef(stat)
if (phaseId(currentPeriod) <= currentRun.typerPhase.id) {
// This is very tricky stuff, because we are navigating the Skylla and Charybdis of
// anonymous classes and what to return from them here. On the one hand, we cannot admit
// every non-private member of an anonymous class as a part of the structural type of the
// enclosing block. This runs afoul of the restriction that a structural type may not
// refer to an enclosing type parameter or abstract types (which in turn is necessitated
// by what can be done in Java reflection). On the other hand, making every term member
// private conflicts with private escape checking - see ticket #3174 for an example.
//
// The cleanest way forward is if we would find a way to suppress structural type checking
// for these members and maybe defer type errors to the places where members are called.
// But that would be a big refactoring and also a big departure from existing code. The
// probably safest fix for 2.8 is to keep members of an anonymous class that are not
// mentioned in a parent type private (as before) but to disable escape checking for code
// that's in the same anonymous class. That's what's done here.
//
// We really should go back and think hard whether we find a better way to address the
// problem of escaping idents on the one hand and well-formed structural types on the
// other.
block match {
case Block(List(classDef @ ClassDef(_, _, _, _)), Apply(Select(New(_), _), _)) =>
val classDecls = classDef.symbol.info.decls
val visibleMembers = pt match {
case WildcardType => classDecls.toList
case BoundedWildcardType(TypeBounds(lo, _)) => lo.members
case _ => pt.members
}
def matchesVisibleMember(member: Symbol) = visibleMembers exists { vis =>
(member.name == vis.name) &&
(member.tpe <:< vis.tpe.substThis(vis.owner, classDef.symbol))
}
// The block is an anonymous class definitions/instantiation pair
// -> members that are hidden by the type of the block are made private
val toHide = (
classDecls filter (member =>
member.isTerm
&& member.isPossibleInRefinement
&& member.isPublic
&& !matchesVisibleMember(member)
) map (member => member
resetFlag (PROTECTED | LOCAL)
setFlag (PRIVATE | SYNTHETIC_PRIVATE)
setPrivateWithin NoSymbol
)
)
syntheticPrivates ++= toHide
case _ =>
}
}
val stats1 = if (isPastTyper) block.stats else
block.stats.flatMap(stat => stat match {
case vd@ValDef(_, _, _, _) if vd.symbol.isLazy =>
namer.addDerivedTrees(Typer.this, vd)
case _ => stat::Nil
})
val stats2 = typedStats(stats1, context.owner)
val expr1 = typed(block.expr, mode &~ (FUNmode | QUALmode), pt)
treeCopy.Block(block, stats2, expr1)
.setType(if (treeInfo.isExprSafeToInline(block)) expr1.tpe else expr1.tpe.deconst)
} finally {
// enable escaping privates checking from the outside and recycle
// transient flag
syntheticPrivates foreach (_ resetFlag SYNTHETIC_PRIVATE)
}
}
error: method ensureSize cannot be accessed in scala.collection.mutable.ArrayBuffer[Int]{def foo: Unit}