Open scabug opened 11 years ago
Imported From: https://issues.scala-lang.org/browse/SI-6904?orig=1 Reporter: @djspiewak Affected Versions: 2.9.2, 2.10.0-RC1 See #4400
@milessabin said: Taking the private modifier off type Elem allows this to compile. I'd say the behaviour is probably correct as-is.
@djspiewak said: I'm not convinced. Private type members behave as true type aliases, with no polymorphic semantics. This precisely describes the behavior we see when I extract the anonymous class into a named class (which works just fine). More importantly, you'll note that the inferred type does not actually involve Elem, it involves Result. The compiler is failing to prove that Result[Int] = Bippy[Int], types which are entirely independent of Elem.
@paulp said: Simplified, with enhanced contrast (not that I object to bringing in Bippy.)
object Test {
def foo1[A1] = new { private type A2 = A1 ; def bar: A2 = ??? }
def foo2[A1] = new { type A2 = A1 ; def bar: A2 = ??? }
def f1 = check[Int](foo1[Int].bar)
def f2 = check[Int](foo2[Int].bar)
def check[A] = new { def apply[B](b: B)(implicit ev: A =:= B) = () }
}
There's no question this is a bug - look at the inferred type of foo1. Not only is A2 private, it is no longer in scope. Two for the price of one.
def foo1[A1 >: Nothing <: Any]: AnyRef{def bar: this.A2}
def foo2[A1 >: Nothing <: Any]: AnyRef{type A2 = A1; def bar: this.A2}
@paulp said: ...though one could see those as manifestation of the same thing.
Here's some code from one of my infinite variety of unpublished branches.
/** Weaken the type of the symbol's info such that it doesn't contain
* symbols with tighter access restriction than the symbol itself.
* Since lubs are calculated without considering these requirements,
* they tend to include inaccessible types.
*/
class WeakenToVisible(owner: Symbol) extends TypeMap {
def isTighter(tp: Type) = (
(tp.typeSymbol isLessAccessibleThan owner)
&& (tp.typeSymbolDirect isLessAccessibleThan owner)
)
def visibleParents(tps: List[Type]): List[Type] = {
val tps1 = tps flatMap (tp =>
if (isTighter(tp)) visibleParents(tp.parents)
else List(tp)
)
// "flatMapConserve" to avoid new refined types.
if ((tps corresponds tps1)(_ eq _)) tps
else elimSuper(tps1)
}
def apply(tp: Type): Type = tp match {
case rt @ RefinedType(ps, decls) => copyRefinedType(rt, visibleParents(ps), mapOver(decls))
case PolyType(_, _) => mapOver(tp)
case MethodType(_, _) => mapOver(tp)
case ExistentialType(_, _) => mapOver(tp)
case TypeRef(_, sym, _) if sym.isClass => mapOver(tp)
case _ => tp
}
}
The "check" function is simply to assert inferred type equivalence without triggering implicit conversion or subtype coercion. If I inline "Elem", this works just fine. Similarly, if I use a named class as the return type of "foo", rather than new {}, there is again no problem.