Skip to content

Fix 11220 alt #12015

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

Closed
wants to merge 3 commits into from
Closed
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
1 change: 1 addition & 0 deletions compiler/src/dotty/tools/dotc/core/CheckRealizable.scala
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ class CheckRealizable(using Context) {
case tp =>
def isConcrete(tp: Type): Boolean = tp.dealias match {
case tp: TypeRef => tp.symbol.isClass
case tp: TypeParamRef => false
case tp: TypeProxy => isConcrete(tp.underlying)
case tp: AndType => isConcrete(tp.tp1) && isConcrete(tp.tp2)
case tp: OrType => isConcrete(tp.tp1) && isConcrete(tp.tp2)
Expand Down
1 change: 1 addition & 0 deletions compiler/src/dotty/tools/dotc/core/Definitions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -919,6 +919,7 @@ class Definitions {
@tu lazy val ParamMetaAnnot: ClassSymbol = requiredClass("scala.annotation.meta.param")
@tu lazy val SetterMetaAnnot: ClassSymbol = requiredClass("scala.annotation.meta.setter")
@tu lazy val ShowAsInfixAnnot: ClassSymbol = requiredClass("scala.annotation.showAsInfix")
@tu lazy val StableAnnot: ClassSymbol = requiredClass("scala.annotation.internal.Stable")
@tu lazy val FunctionalInterfaceAnnot: ClassSymbol = requiredClass("java.lang.FunctionalInterface")
@tu lazy val TargetNameAnnot: ClassSymbol = requiredClass("scala.annotation.targetName")
@tu lazy val VarargsAnnot: ClassSymbol = requiredClass("scala.annotation.varargs")
Expand Down
17 changes: 15 additions & 2 deletions compiler/src/dotty/tools/dotc/typer/Typer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3530,12 +3530,25 @@ class Typer extends Namer
typr.println(i"adapt to subtype ${tree.tpe} !<:< $pt")
//typr.println(TypeComparer.explained(tree.tpe <:< pt))
adaptToSubType(wtp)
case CompareResult.OKwithGADTUsed if pt.isValueType =>
case CompareResult.OKwithGADTUsed
if pt.isValueType
&& !inContext(ctx.fresh.setGadt(EmptyGadtConstraint)) {
(tree.tpe.widenExpr frozen_<:< pt)
// we overshot; a cast is not needed, after all. (this happens a lot. We should
// find out whether we can make GADTused more precise)
.showing(i"false alarm for $tree: ${tree.tpe.widenExpr} vs $pt in ${ctx.source}", gadts)
} =>
// 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.cast(pt)
val target =
if tree.tpe.isSingleton then
val conj = AndType(tree.tpe, pt)
conj
else pt
gadts.println(i"insert GADT cast from $tree to $target")
tree.cast(target)
case _ =>
tree
}
Expand Down
4 changes: 4 additions & 0 deletions compiler/test/dotc/pos-test-pickling.blacklist
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,7 @@ i8182.scala

# local lifted value in annotation argument has different position after pickling
i2797a

# GADT cast applied to singleton type difference
i4176-gadt.scala

6 changes: 6 additions & 0 deletions library/src/scala/annotation/internal/Stable.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package scala.annotation.internal

import scala.annotation.Annotation

/** An annotation asserting that the annotated type is stable */
final class Stable() extends Annotation
8 changes: 8 additions & 0 deletions tests/pos/i11220.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import scala.annotation.tailrec
class Context {
type Tree
}

final def loop3[C <: Context](): Unit =
@tailrec
def loop4[A <: C](c: A): c.Tree = loop4(c)
25 changes: 25 additions & 0 deletions tests/pos/i11955.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
object Hello {

sealed abstract class X[+A] {
type This[+A] <: X[A]
def asThis: This[A]
}

class Y[+A] extends X[A] {
override type This[+AA] = Y[AA]
override def asThis: This[A] = this
}

def hackBackToSelf[F[+u] <: X[u], A](f: F[Any])(f2: f.This[A]): F[A] =
f2.asInstanceOf[F[A]]

case class G[F[+u] <: X[u], A](wrapped: F[A]) {

def mapF[F2[+u] <: X[u]](f: F[A] => F2[A]): G[F2, A] =
G[F2, A](f(wrapped))

def test_ko_1: G[F, A] = mapF(ct => hackBackToSelf(ct)(ct.asThis)) // error
def test_ko_2: G[F, A] = mapF[F](ct => hackBackToSelf(ct)(ct.asThis)) // error
def test_ok : G[F, A] = mapF(ct => hackBackToSelf[F, A](ct)(ct.asThis)) // ok
}
}