Permalink
Browse files

Fix for one of the oldest open soundness bugs.

Closes SI-963, since it was one of my random 30 it won the prize.
The trick after adding the stability check (which has been sitting
there commented out for 3+ years) was that implicit search depended
on the wrongness, because memberWildcardType would create scopes
with members of the form

  ?{ val name: tp }

And since a def shouldn't match that, fixing it broke everything
until I flipped it around: memberWildcardType should be seeking

  ?{ def name: tp }

It could also search for a mutable value: the relevant quality
is that it not be stable so it doesn't have a tighter type than
the members it hopes to match.
  • Loading branch information...
paulp committed May 4, 2012
1 parent aad6dea commit 01f6ed8e22d02811fe62b9183d9f84bdda5ede4b
@@ -5719,14 +5719,15 @@ trait Types extends api.Types { self: SymbolTable =>
val info1 = tp1.memberInfo(sym1)
val info2 = tp2.memberInfo(sym2).substThis(tp2.typeSymbol, tp1)
//System.out.println("specializes "+tp1+"."+sym1+":"+info1+sym1.locationString+" AND "+tp2+"."+sym2+":"+info2)//DEBUG
sym2.isTerm && (info1 <:< info2) /*&& (!sym2.isStable || sym1.isStable) */ ||
sym2.isAbstractType && {
val memberTp1 = tp1.memberType(sym1)
// println("kinds conform? "+(memberTp1, tp1, sym2, kindsConform(List(sym2), List(memberTp1), tp2, sym2.owner)))
info2.bounds.containsType(memberTp1) &&
kindsConform(List(sym2), List(memberTp1), tp1, sym1.owner)
} ||
sym2.isAliasType && tp2.memberType(sym2).substThis(tp2.typeSymbol, tp1) =:= tp1.memberType(sym1) //@MAT ok
( sym2.isTerm && (info1 <:< info2) && (!sym2.isStable || sym1.isStable)
|| sym2.isAbstractType && {
val memberTp1 = tp1.memberType(sym1)
// println("kinds conform? "+(memberTp1, tp1, sym2, kindsConform(List(sym2), List(memberTp1), tp2, sym2.owner)))
info2.bounds.containsType(memberTp1) &&
kindsConform(List(sym2), List(memberTp1), tp1, sym1.owner)
}
|| sym2.isAliasType && tp2.memberType(sym2).substThis(tp2.typeSymbol, tp1) =:= tp1.memberType(sym1) //@MAT ok
)
}
/** A function implementing `tp1` matches `tp2`. */
@@ -214,13 +214,13 @@ trait Implicits {
override def hashCode = 1
}
/** A constructor for types ?{ name: tp }, used in infer view to member
/** A constructor for types ?{ def/type name: tp }, used in infer view to member
* searches.
*/
def memberWildcardType(name: Name, tp: Type) = {
val result = refinedType(List(WildcardType), NoSymbol)
name match {
case x: TermName => result.typeSymbol.newValue(x) setInfoAndEnter tp
case x: TermName => result.typeSymbol.newMethod(x) setInfoAndEnter tp
case x: TypeName => result.typeSymbol.newAbstractType(x) setInfoAndEnter tp
}
result
@@ -9,10 +9,10 @@ invalidate B.scala because it references invalid (no longer inherited) definitio
compiling Set(B.scala, C.scala)
B.scala:3: error: type mismatch;
found : C
required: ?{val x: ?}
required: ?{def x: ?}
Note that implicit conversions are not applicable because they are ambiguous:
both method any2Ensuring in object Predef of type [A](x: A)Ensuring[A]
and method any2ArrowAssoc in object Predef of type [A](x: A)ArrowAssoc[A]
are possible conversion functions from C to ?{val x: ?}
are possible conversion functions from C to ?{def x: ?}
println( (new C).x )
^
@@ -1,16 +1,16 @@
logImplicits.scala:2: applied implicit conversion from xs.type to ?{val size: ?} = implicit def byteArrayOps(xs: Array[Byte]): scala.collection.mutable.ArrayOps[Byte]
logImplicits.scala:2: applied implicit conversion from xs.type to ?{def size: ?} = implicit def byteArrayOps(xs: Array[Byte]): scala.collection.mutable.ArrayOps[Byte]
def f(xs: Array[Byte]) = xs.size
^
logImplicits.scala:7: applied implicit conversion from String("abc") to ?{val map: ?} = implicit def augmentString(x: String): scala.collection.immutable.StringOps
logImplicits.scala:7: applied implicit conversion from String("abc") to ?{def map: ?} = implicit def augmentString(x: String): scala.collection.immutable.StringOps
def f = "abc" map (_ + 1)
^
logImplicits.scala:15: inferred view from String("abc") to Int = C.this.convert:(p: String("abc"))Int
math.max(122, x: Int)
^
logImplicits.scala:19: applied implicit conversion from Int(1) to ?{val ->: ?} = implicit def any2ArrowAssoc[A](x: A): ArrowAssoc[A]
logImplicits.scala:19: applied implicit conversion from Int(1) to ?{def ->: ?} = implicit def any2ArrowAssoc[A](x: A): ArrowAssoc[A]
def f = (1 -> 2) + "c"
^
logImplicits.scala:19: applied implicit conversion from (Int, Int) to ?{val +: ?} = implicit def any2stringadd(x: Any): scala.runtime.StringAdd
logImplicits.scala:19: applied implicit conversion from (Int, Int) to ?{def +: ?} = implicit def any2stringadd(x: Any): scala.runtime.StringAdd
def f = (1 -> 2) + "c"
^
logImplicits.scala:22: error: class Un needs to be abstract, since method unimplemented is not defined
View
@@ -0,0 +1,12 @@
t963.scala:14: error: stable identifier required, but Test.this.y3.x found.
val w3 : y3.x.type = y3.x
^
t963.scala:17: error: stable identifier required, but Test.this.y4.x found.
val w4 : y4.x.type = y4.x
^
t963.scala:10: error: type mismatch;
found : Object{def x: Integer}
required: AnyRef{val x: Integer}
val y2 : { val x : java.lang.Integer } = new { def x = new java.lang.Integer(r.nextInt) }
^
three errors found
View
@@ -0,0 +1,18 @@
import scala.util.Random
// Only y1 (val/val) should actually compile.
object Test {
val r = new Random()
val y1 : { val x : java.lang.Integer } = new { val x = new java.lang.Integer(r.nextInt) }
val w1 : y1.x.type = y1.x
val y2 : { val x : java.lang.Integer } = new { def x = new java.lang.Integer(r.nextInt) }
val w2 : y2.x.type = y2.x
val y3 : { def x : java.lang.Integer } = new { val x = new java.lang.Integer(r.nextInt) }
val w3 : y3.x.type = y3.x
val y4 : { def x : java.lang.Integer } = new { def x = new java.lang.Integer(r.nextInt) }
val w4 : y4.x.type = y4.x
}

3 comments on commit 01f6ed8

@adriaanm

This comment has been minimized.

Show comment
Hide comment
@adriaanm

adriaanm May 4, 2012

Member

triple digit soundness bug!? fixed!?!

name your prize

Member

adriaanm replied May 4, 2012

triple digit soundness bug!? fixed!?!

name your prize

@paulp

This comment has been minimized.

Show comment
Hide comment
@paulp

paulp May 4, 2012

Contributor

"Lucky in the triage lottery," huh? That crow must be delicious! Why not eat some along with your hat?

Contributor

paulp replied May 4, 2012

"Lucky in the triage lottery," huh? That crow must be delicious! Why not eat some along with your hat?

@adriaanm

This comment has been minimized.

Show comment
Hide comment
@adriaanm

adriaanm May 4, 2012

Member
Member

adriaanm replied May 4, 2012

Please sign in to comment.