Skip to content

Commit

Permalink
Add missing cache invalidations for opaque types
Browse files Browse the repository at this point in the history
Two problems:

 - Base types for applied opaque types were cached
 - Denotations for opaque types were cached

This allowed knowledge about opaque aliases to leak from companion
objects to the outside world.
  • Loading branch information
odersky committed Oct 13, 2018
1 parent 18dfe3a commit 41791f7
Show file tree
Hide file tree
Showing 4 changed files with 69 additions and 4 deletions.
8 changes: 5 additions & 3 deletions compiler/src/dotty/tools/dotc/core/SymDenotations.scala
Original file line number Diff line number Diff line change
Expand Up @@ -382,7 +382,7 @@ object SymDenotations {
else alias
addAnnotation(Annotation(tpd.TypeTree(defn.OpaqueAliasAnnot.typeRef.appliedTo(arg)).withPos(symbol.pos)))
info = TypeBounds(defn.NothingType, abstractRHS(alias))
setFlag(Deferred)
setFlag(Deferred | Provisional)
case _ =>
}
}
Expand Down Expand Up @@ -1743,15 +1743,17 @@ object SymDenotations {

def computeApplied = {
btrCache.put(tp, NoPrefix)
val tyconSym = tycon.typeSymbol
val baseTp =
if (tycon.typeSymbol eq symbol) tp
if (tyconSym `eq` symbol) tp
else (tycon.typeParams: @unchecked) match {
case LambdaParam(_, _) :: _ =>
recur(tp.superType)
case tparams: List[Symbol @unchecked] =>
recur(tycon).subst(tparams, args)
}
record(tp, baseTp)
if (!tyconSym.is(Opaque)) record(tp, baseTp)
else btrCache.remove(tp)
baseTp
}
computeApplied
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ class FirstTransform extends MiniPhase with SymTransformer { thisPhase =>
if (sym.is(Opaque)) {
val result = sym.copySymDenotation(info = TypeAlias(sym.opaqueAlias))
result.removeAnnotation(defn.OpaqueAliasAnnot)
result.resetFlag(Opaque | Deferred)
result.resetFlag(Opaque | Deferred | Provisional)
result
}
else sym
Expand Down
15 changes: 15 additions & 0 deletions tests/neg/OpaqueEscape.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
object OpaqueEscape{
opaque type Wrapped = Int
abstract class EscaperBase {
def unwrap(i:Wrapped):Int
def wrap(i:Int):Wrapped
}
class Escaper extends EscaperBase{
override def unwrap(i:Int):Int = i // error overriding method unwrap
override def wrap(i:Int):Int = i // error overriding method wrap
}

val e = new Escaper:EscaperBase
val w:Wrapped = e.wrap(1)
val u:Int = e.unwrap(w)
}
48 changes: 48 additions & 0 deletions tests/neg/opaque-immutable-array.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
object ia {

import java.util.Arrays

opaque type IArray[A1] = Array[A1]

object IArray {
def initialize[A](body: => Array[A]): IArray[A] = body
def size[A](ia: IArray[A]): Int = ia.length
def get[A](ia: IArray[A], i: Int): A = ia(i)

// return a sorted copy of the array
def sorted[A <: AnyRef : math.Ordering](ia: IArray[A]): IArray[A] = {
val arr = Arrays.copyOf(ia, ia.length)
scala.util.Sorting.quickSort(arr)
arr
}

// use a standard java method to search a sorted IArray.
// (note that this doesn't mutate the array).
def binarySearch(ia: IArray[Long], elem: Long): Int =
Arrays.binarySearch(ia, elem)
}

// same as IArray.binarySearch but implemented by-hand.
//
// given a sorted IArray, returns index of `elem`,
// or a negative value if not found.
def binaryIndexOf(ia: IArray[Long], elem: Long): Int = {
var lower: Int = 0
var upper: Int = IArray.size(ia)
while (lower <= upper) {
val middle = (lower + upper) >>> 1
val n = IArray.get(ia, middle)

if (n == elem) return middle
else if (n < elem) lower = middle + 1
else upper = middle - 1
}
-lower - 1
}

def xs: IArray[Long] = ???

xs.length // error: not a member
xs.apply(2) // error: not a member
xs(1) = 0 // error: not a member
}

0 comments on commit 41791f7

Please sign in to comment.