Skip to content
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

Fix #19746: Do not follow param term refs in isConcrete. #20015

Merged
merged 1 commit into from
Apr 4, 2024
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
57 changes: 32 additions & 25 deletions compiler/src/dotty/tools/dotc/core/TypeComparer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3360,37 +3360,44 @@ class MatchReducer(initctx: Context) extends TypeComparer(initctx) {
*
* See notably neg/wildcard-match.scala for examples of this.
*
* See neg/i13780.scala and neg/i13780-1.scala for ClassCastException
* reproducers if we disable this check.
* See neg/i13780.scala, neg/i13780-1.scala and neg/i19746.scala for
* ClassCastException reproducers if we disable this check.
*/

def followEverythingConcrete(tp: Type): Type =
val widenedTp = tp.widenDealias
val tp1 = widenedTp.normalized

def followTp1: Type =
// If both widenDealias and normalized did something, start again
if (tp1 ne widenedTp) && (widenedTp ne tp) then followEverythingConcrete(tp1)
else tp1
def isConcrete(tp: Type): Boolean =
val tp1 = tp.normalized

tp1 match
case tp1: TypeRef =>
tp1.info match
case TypeAlias(tl: HKTypeLambda) => tl
case MatchAlias(tl: HKTypeLambda) => tl
case _ => followTp1
case tp1 @ AppliedType(tycon, args) =>
val concreteTycon = followEverythingConcrete(tycon)
if concreteTycon eq tycon then followTp1
else followEverythingConcrete(concreteTycon.applyIfParameterized(args))
if tp1.symbol.isClass then true
else
tp1.info match
case info: AliasingBounds => isConcrete(info.alias)
case _ => false
case tp1: AppliedType =>
isConcrete(tp1.tycon) && isConcrete(tp1.superType)
case tp1: HKTypeLambda =>
true
case tp1: TermRef =>
!tp1.symbol.is(Param) && isConcrete(tp1.underlying)
case tp1: TermParamRef =>
false
case tp1: SingletonType =>
isConcrete(tp1.underlying)
case tp1: ExprType =>
isConcrete(tp1.underlying)
case tp1: AnnotatedType =>
isConcrete(tp1.parent)
case tp1: RefinedType =>
isConcrete(tp1.underlying)
case tp1: RecType =>
isConcrete(tp1.underlying)
case tp1: AndOrType =>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note to @noti0na1 : This will need to be updated for flexibletypes.

isConcrete(tp1.tp1) && isConcrete(tp1.tp2)
case _ =>
followTp1
end followEverythingConcrete

def isConcrete(tp: Type): Boolean =
followEverythingConcrete(tp) match
case tp1: AndOrType => isConcrete(tp1.tp1) && isConcrete(tp1.tp2)
case tp1 => tp1.underlyingClassRef(refinementOK = true).exists
val tp2 = tp1.stripped.stripLazyRef
(tp2 ne tp) && isConcrete(tp2)
end isConcrete

// Actual matching logic

Expand Down
7 changes: 7 additions & 0 deletions tests/neg/i19746.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
-- [E007] Type Mismatch Error: tests/neg/i19746.scala:9:30 -------------------------------------------------------------
9 | def asX(w: W[Any]): w.X = self // error: Type Mismatch
| ^^^^
| Found: (self : Any)
| Required: w.X
|
| longer explanation available when compiling with `-explain`
15 changes: 15 additions & 0 deletions tests/neg/i19746.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
trait V:
type X = this.type match
case W[x] => x

trait W[+Y] extends V

object Test:
extension (self: Any) def as[T]: T =
def asX(w: W[Any]): w.X = self // error: Type Mismatch
asX(new W[T] {})

def main(args: Array[String]): Unit =
val b = 0.as[Boolean] // java.lang.ClassCastException if the code is allowed to compile
println(b)
end Test
5 changes: 3 additions & 2 deletions tests/pos/TupleReverse.scala
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,6 @@ def test[T1, T2, T3, T4] =
def test2[Tup <: Tuple] =
summon[Reverse[Tup] =:= Reverse[Tup]]

def test3[T1, T2, T3, T4](tup1: (T1, T2, T3, T4)) =
summon[Reverse[tup1.type] =:= (T4, T3, T2, T1)]
def test3[T1, T2, T3, T4](tup1: (T1, T2, T3, T4)): Unit =
val tup11: (T1, T2, T3, T4) = tup1
summon[Reverse[tup11.type] =:= (T4, T3, T2, T1)]
7 changes: 4 additions & 3 deletions tests/pos/TupleReverseOnto.scala
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ def test2[Tup1 <: Tuple, Tup2 <: Tuple] =
summon[ReverseOnto[EmptyTuple, Tup1] =:= Tup1]
summon[ReverseOnto[Tup1, EmptyTuple] =:= Reverse[Tup1]]

def test3[T1, T2, T3, T4](tup1: (T1, T2), tup2: (T3, T4)) =
summon[ReverseOnto[tup1.type, tup2.type] <:< (T2, T1, T3, T4)]
summon[ReverseOnto[tup1.type, tup2.type] =:= T2 *: T1 *: tup2.type]
def test3[T1, T2, T3, T4](tup1: (T1, T2), tup2: (T3, T4)): Unit =
val tup11: (T1, T2) = tup1
summon[ReverseOnto[tup11.type, tup2.type] <:< (T2, T1, T3, T4)]
summon[ReverseOnto[tup11.type, tup2.type] =:= T2 *: T1 *: tup2.type]