Skip to content

Fixes of GADTs and test recategorization. #1461

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 17 commits into from
Aug 26, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 14 additions & 2 deletions src/dotty/tools/dotc/ast/Desugar.scala
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import SymDenotations._, Symbols._, StdNames._, Annotations._, Trees._
import Decorators._
import language.higherKinds
import collection.mutable.ListBuffer
import util.Attachment
import config.Printers._

object desugar {
Expand All @@ -17,6 +18,11 @@ object desugar {

import untpd._

/** Tags a .withFilter call generated by desugaring a for expression.
* Such calls can alternatively be rewritten to use filter.
*/
val MaybeFilter = new Attachment.Key[Unit]

/** Info of a variable in a pattern: The named tree and its type */
private type VarInfo = (NameTree, Tree)

Expand Down Expand Up @@ -773,6 +779,12 @@ object desugar {
(Bind(name, pat), Ident(name))
}

/** Add MaybeFilter attachment */
def orFilter(tree: Tree): tree.type = {
tree.putAttachment(MaybeFilter, ())
tree
}

/** Make a pattern filter:
* rhs.withFilter { case pat => true case _ => false }
*
Expand Down Expand Up @@ -803,7 +815,7 @@ object desugar {
val cases = List(
CaseDef(pat, EmptyTree, Literal(Constant(true))),
CaseDef(Ident(nme.WILDCARD), EmptyTree, Literal(Constant(false))))
Apply(Select(rhs, nme.withFilter), makeCaseLambda(cases))
Apply(orFilter(Select(rhs, nme.withFilter)), makeCaseLambda(cases))
}

/** Is pattern `pat` irrefutable when matched against `rhs`?
Expand Down Expand Up @@ -858,7 +870,7 @@ object desugar {
val vfrom1 = new IrrefutableGenFrom(makeTuple(allpats), rhs1)
makeFor(mapName, flatMapName, vfrom1 :: rest1, body)
case (gen: GenFrom) :: test :: rest =>
val filtered = Apply(rhsSelect(gen, nme.withFilter), makeLambda(gen.pat, test))
val filtered = Apply(orFilter(rhsSelect(gen, nme.withFilter)), makeLambda(gen.pat, test))
val genFrom =
if (isIrrefutableGenFrom(gen)) new IrrefutableGenFrom(gen.pat, filtered)
else GenFrom(gen.pat, filtered)
Expand Down
27 changes: 22 additions & 5 deletions src/dotty/tools/dotc/core/TypeComparer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,19 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
myNothingType
}

/** Indicates whether a previous subtype check used GADT bounds */
var GADTused = false

/** Record that GADT bounds of `sym` were used in a subtype check.
* But exclude constructor type parameters, as these are aliased
* to the corresponding class parameters, which does not constitute
* a true usage of a GADT symbol.
*/
private def GADTusage(sym: Symbol) = {
if (!sym.owner.isConstructor) GADTused = true
true
}

// Subtype testing `<:<`

def topLevelSubType(tp1: Type, tp2: Type): Boolean = {
Expand Down Expand Up @@ -325,7 +338,8 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
val gbounds2 = ctx.gadt.bounds(tp2.symbol)
(gbounds2 != null) &&
(isSubTypeWhenFrozen(tp1, gbounds2.lo) ||
narrowGADTBounds(tp2, tp1, isUpper = false))
narrowGADTBounds(tp2, tp1, isUpper = false)) &&
GADTusage(tp2.symbol)
}
((frozenConstraint || !isCappable(tp1)) && isSubType(tp1, lo2) ||
compareGADT ||
Expand Down Expand Up @@ -507,7 +521,8 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
val gbounds1 = ctx.gadt.bounds(tp1.symbol)
(gbounds1 != null) &&
(isSubTypeWhenFrozen(gbounds1.hi, tp2) ||
narrowGADTBounds(tp1, tp2, isUpper = true))
narrowGADTBounds(tp1, tp2, isUpper = true)) &&
GADTusage(tp1.symbol)
}
isSubType(hi1, tp2) || compareGADT
case _ =>
Expand Down Expand Up @@ -846,11 +861,13 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
// special case for situations like:
// class C { type T }
// val foo: C
// foo.type <: C { type T = foo.T }
// foo.type <: C { type T {= , <: , >:} foo.T }
def selfReferentialMatch = tp1.isInstanceOf[SingletonType] && {
rinfo2 match {
case rinfo2: TypeAlias =>
!defn.isBottomType(tp1.widen) && (tp1 select name) =:= rinfo2.alias
case rinfo2: TypeBounds =>
val mbr1 = tp1.select(name)
!defn.isBottomType(tp1.widen) &&
(mbr1 =:= rinfo2.hi || (rinfo2.hi ne rinfo2.lo) && mbr1 =:= rinfo2.lo)
case _ => false
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/dotty/tools/dotc/transform/TreeChecker.scala
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,7 @@ class TreeChecker extends Phase with SymTransformer {
}

override def typed(tree: untpd.Tree, pt: Type = WildcardType)(implicit ctx: Context): tpd.Tree = {
val tpdTree = super.typed(tree)
val tpdTree = super.typed(tree, pt)
checkIdentNotJavaClass(tpdTree)
tpdTree
}
Expand Down
5 changes: 2 additions & 3 deletions src/dotty/tools/dotc/typer/Applications.scala
Original file line number Diff line number Diff line change
Expand Up @@ -775,14 +775,13 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
* The generalizations of a type T are the smallest set G such that
*
* - T is in G
* - If a typeref R in G represents a trait, R's superclass is in G.
* - If a typeref R in G represents a class or trait, R's superclass is in G.
* - If a type proxy P is not a reference to a class, P's supertype is in G
*/
def isSubTypeOfParent(subtp: Type, tp: Type)(implicit ctx: Context): Boolean =
if (subtp <:< tp) true
else tp match {
case tp: TypeRef if tp.symbol.isClass =>
tp.symbol.is(Trait) && isSubTypeOfParent(subtp, tp.firstParent)
case tp: TypeRef if tp.symbol.isClass => isSubTypeOfParent(subtp, tp.firstParent)
case tp: TypeProxy => isSubTypeOfParent(subtp, tp.superType)
case _ => false
}
Expand Down
27 changes: 17 additions & 10 deletions src/dotty/tools/dotc/typer/Namer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -726,7 +726,7 @@ class Namer { typer: Typer =>
// the parent types are elaborated.
index(constr)
symbolOfTree(constr).ensureCompleted()

index(rest)(inClassContext(selfInfo))

val tparamAccessors = decls.filter(_ is TypeParamAccessor).toList
Expand Down Expand Up @@ -807,20 +807,27 @@ class Namer { typer: Typer =>
lazy val schema = paramFn(WildcardType)
val site = sym.owner.thisType
((NoType: Type) /: sym.owner.info.baseClasses.tail) { (tp, cls) =>
val iRawInfo =
cls.info.nonPrivateDecl(sym.name).matchingDenotation(site, schema).info
val iInstInfo = iRawInfo match {
case iRawInfo: PolyType =>
if (iRawInfo.paramNames.length == typeParams.length)
iRawInfo.instantiate(typeParams map (_.typeRef))
def instantiatedResType(info: Type, tparams: List[Symbol], paramss: List[List[Symbol]]): Type = info match {
case info: PolyType =>
if (info.paramNames.length == typeParams.length)
instantiatedResType(info.instantiate(tparams.map(_.typeRef)), Nil, paramss)
else NoType
case info: MethodType =>
paramss match {
case params :: paramss1 if info.paramNames.length == params.length =>
instantiatedResType(info.instantiate(params.map(_.termRef)), tparams, paramss1)
case _ =>
NoType
}
case _ =>
if (typeParams.isEmpty) iRawInfo
if (tparams.isEmpty && paramss.isEmpty) info.widenExpr
else NoType
}
val iResType = iInstInfo.finalResultType.asSeenFrom(site, cls)
val iRawInfo =
cls.info.nonPrivateDecl(sym.name).matchingDenotation(site, schema).info
val iResType = instantiatedResType(iRawInfo, typeParams, paramss).asSeenFrom(site, cls)
if (iResType.exists)
typr.println(i"using inherited type for ${mdef.name}; raw: $iRawInfo, inst: $iInstInfo, inherited: $iResType")
typr.println(i"using inherited type for ${mdef.name}; raw: $iRawInfo, inherited: $iResType")
tp & iResType
}
}
Expand Down
26 changes: 21 additions & 5 deletions src/dotty/tools/dotc/typer/Typer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -346,11 +346,17 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
}
}

if (ctx.compilationUnit.isJava && tree.name.isTypeName) {
def selectWithFallback(fallBack: => Tree) =
tryEither(tryCtx => asSelect(tryCtx))((_, _) => fallBack)

if (ctx.compilationUnit.isJava && tree.name.isTypeName)
// SI-3120 Java uses the same syntax, A.B, to express selection from the
// value A and from the type A. We have to try both.
tryEither(tryCtx => asSelect(tryCtx))((_, _) => asJavaSelectFromTypeTree(ctx))
} else asSelect(ctx)
selectWithFallback(asJavaSelectFromTypeTree(ctx))
else if (tree.name == nme.withFilter && tree.getAttachment(desugar.MaybeFilter).isDefined)
selectWithFallback(typedSelect(untpd.cpy.Select(tree)(tree.qualifier, nme.filter), pt))
else
asSelect(ctx)
}

def typedSelectFromTypeTree(tree: untpd.SelectFromTypeTree, pt: Type)(implicit ctx: Context): Tree = track("typedSelectFromTypeTree") {
Expand Down Expand Up @@ -1066,8 +1072,9 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
def completeAnnotations(mdef: untpd.MemberDef, sym: Symbol)(implicit ctx: Context): Unit = {
// necessary to force annotation trees to be computed.
sym.annotations.foreach(_.tree)
val annotCtx = ctx.outersIterator.dropWhile(_.owner == sym).next
// necessary in order to mark the typed ahead annotations as definitely typed:
untpd.modsDeco(mdef).mods.annotations.foreach(typedAnnotation)
untpd.modsDeco(mdef).mods.annotations.foreach(typedAnnotation(_)(annotCtx))
}

def typedAnnotation(annot: untpd.Tree)(implicit ctx: Context): Tree = track("typedAnnotation") {
Expand Down Expand Up @@ -1715,6 +1722,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
else
missingArgs
case _ =>
ctx.typeComparer.GADTused = false
if (ctx.mode is Mode.Pattern) {
tree match {
case _: RefTree | _: Literal if !isVarPattern(tree) =>
Expand All @@ -1723,7 +1731,15 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
}
tree
}
else if (tree.tpe <:< pt) tree
else if (tree.tpe <:< pt)
if (ctx.typeComparer.GADTused && pt.isValueType)
// Insert an explicit cast, so that -Ycheck in later phases succeeds.
// I suspect, but am not 100% sure that this might affect inferred types,
// if the expected type is a supertype of the GADT bound. It would be good to come
// up with a test case for this.
tree.asInstance(pt)
else
tree
else if (wtp.isInstanceOf[MethodType]) missingArgs
else {
typr.println(i"adapt to subtype ${tree.tpe} !<:< $pt")
Expand Down
1 change: 1 addition & 0 deletions test/dotc/tests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ class tests extends CompilerTest {
@Test def rewrites = compileFile(posScala2Dir, "rewrites", "-rewrite" :: scala2mode)

@Test def pos_859 = compileFile(posSpecialDir, "i859", scala2mode)(allowDeepSubtypes)
@Test def pos_t8146a = compileFile(posSpecialDir, "t8146a")(allowDeepSubtypes)

@Test def pos_t5545 = {
// compile by hand in two batches, since junit lacks the infrastructure to
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// Uses structural types; therefore not expressible in dotty
import scala.annotation.StaticAnnotation

class ann(val bar: Any) extends StaticAnnotation
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
// no longer works because dotty uses name-nased pattern matching for case classes

case class Y(final var x: Int, final private var y: String, final val z1: Boolean, final private val z2: Any) {

import Test.{y => someY}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// no longer works because dotty does not have a concept of weak conformance
object AdaptWithWeaklyConformantType {
implicit class D(d: Double) { def double = d*2 }

Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// structural types, cannot represent
trait T {

def crashy(ma: Any): Unit = {
Expand Down
10 changes: 6 additions & 4 deletions tests/pending/pos/t7239.scala → tests/neg/t7239.scala
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
// Dotty rewrites only withFilter calls occurring in for expressions to filter calls.
// So this test does not compile.
object Test {
def BrokenMethod(): HasFilter[(Int, String)] = ???

Expand All @@ -15,12 +17,12 @@ object Test {
(implicit F0: NoImplicit): HasWithFilter = ???
}

BrokenMethod().withFilter(_ => true) // okay
BrokenMethod().filter(_ => true) // okay
BrokenMethod().withFilter(_ => true) // error
BrokenMethod().filter(_ => true) // ok

locally {
import addWithFilter._
BrokenMethod().withFilter((_: (Int, String)) => true) // okay
BrokenMethod().withFilter((_: (Int, String)) => true) // error
}

locally {
Expand All @@ -33,6 +35,6 @@ object Test {
// `(B => Boolean)`. Only later during pickling does the
// defensive check for erroneous types in the tree pick up
// the problem.
BrokenMethod().withFilter(x => true) // erroneous or inaccessible type.
BrokenMethod().withFilter(x => true) // error
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ class C {
{
val a = 0
object C {
new C().x
new C().x // error: cannot be accessed
}
}
}
Expand Down
7 changes: 7 additions & 0 deletions tests/pending/neg/i533/Compat.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
object Compat {
def main(args: Array[String]): Unit = {
val x = new Array[Int](1)
x(0) = 10
println(JA.get(x))
}
}
5 changes: 5 additions & 0 deletions tests/pending/neg/i533/JA.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class JA {
public static <T> T get(T[] arr) {
return arr[0];
}
}
18 changes: 0 additions & 18 deletions tests/pending/pos/contraImplicits.scala

This file was deleted.

9 changes: 7 additions & 2 deletions tests/pending/pos/depmet_implicit_norm_ret.scala
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,18 @@ object Test{
}
}

import ZipWith._

trait ZipWith[S] {
type T
def zipWith : S => T = sys.error("")
}

// bug: inferred return type = (Stream[A]) => java.lang.Object with Test.ZipWith[B]{type T = Stream[B]}#T
// this seems incompatible with vvvvvvvvvvvvvvvvvvvvvv -- #3731
def map[A,B](f : A => B) /* : Stream[A] => Stream[B]*/ = ZipWith(f)
val tst: Stream[Int] = map{x: String => x.length}(Stream("a"))
def map1[A,B](f : A => B) = ZipWith(f)(SuccZipWith) // this typechecks but fails in -Ycheck:first
val tst1: Stream[Int] = map1[String, Int]{x: String => x.length}.apply(Stream("a"))

def map2[A,B](f : A => B) = ZipWith(f) // this finds ZeroZipWith where scalac finds SuccZipWith and fails typechecking in the next line.
val tst2: Stream[Int] = map2{x: String => x.length}.apply(Stream("a"))
}
14 changes: 0 additions & 14 deletions tests/pending/pos/depsel.scala

This file was deleted.

4 changes: 2 additions & 2 deletions tests/pending/pos/exponential-spec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ object Test {
compose f[T] // 8s
compose f[T] // 11s
compose f[T] // 17s
compose f[T] // 29s
/* compose f[T] // 29s
compose f[T] // 54s
compose f[T]
compose f[T]
Expand All @@ -42,6 +42,6 @@ object Test {
compose f[T]
compose f[T]
compose f[T]
compose f[T]
compose f[T]*/
)(exp)
}
1 change: 0 additions & 1 deletion tests/pending/pos/generic-sigs.flags

This file was deleted.

1 change: 0 additions & 1 deletion tests/pending/pos/infersingle.flags

This file was deleted.

Loading