Skip to content

Commit 9fdcfd2

Browse files
authored
Merge pull request #15669 from dotty-staging/fix-15662
Don't trust case class extractors with explicit type arguments
2 parents cb4f807 + c4c4401 commit 9fdcfd2

File tree

5 files changed

+42
-7
lines changed

5 files changed

+42
-7
lines changed

Diff for: compiler/src/dotty/tools/dotc/ast/TreeInfo.scala

+6
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,12 @@ trait TreeInfo[T >: Untyped <: Type] { self: Trees.Instance[T] =>
121121
case _ => Nil
122122
}
123123

124+
/** Is tree explicitly parameterized with type arguments? */
125+
def hasExplicitTypeArgs(tree: Tree): Boolean = tree match
126+
case TypeApply(tycon, args) =>
127+
args.exists(arg => !arg.span.isZeroExtent && !tycon.span.contains(arg.span))
128+
case _ => false
129+
124130
/** Is tree a path? */
125131
def isPath(tree: Tree): Boolean = unsplice(tree) match {
126132
case Ident(_) | This(_) | Super(_, _) => true

Diff for: compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala

+9-2
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,8 @@ trait ConstraintHandling {
7474
protected def necessaryConstraintsOnly(using Context): Boolean =
7575
ctx.mode.is(Mode.GadtConstraintInference) || myNecessaryConstraintsOnly
7676

77+
protected var trustBounds = true
78+
7779
def checkReset() =
7880
assert(addConstraintInvocations == 0)
7981
assert(frozenConstraint == false)
@@ -260,12 +262,17 @@ trait ConstraintHandling {
260262
// If `isUpper` is true, ensure that `param <: `bound`, otherwise ensure
261263
// that `param >: bound`.
262264
val narrowedBounds =
263-
val saved = homogenizeArgs
265+
val savedHomogenizeArgs = homogenizeArgs
266+
val savedTrustBounds = trustBounds
264267
homogenizeArgs = Config.alignArgsInAnd
265268
try
269+
trustBounds = false
266270
if isUpper then oldBounds.derivedTypeBounds(lo, hi & bound)
267271
else oldBounds.derivedTypeBounds(lo | bound, hi)
268-
finally homogenizeArgs = saved
272+
finally
273+
homogenizeArgs = savedHomogenizeArgs
274+
trustBounds = savedTrustBounds
275+
//println(i"narrow bounds for $param from $oldBounds to $narrowedBounds")
269276
val c1 = constraint.updateEntry(param, narrowedBounds)
270277
(c1 eq constraint)
271278
|| {

Diff for: compiler/src/dotty/tools/dotc/core/TypeComparer.scala

+7-3
Original file line numberDiff line numberDiff line change
@@ -536,7 +536,10 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
536536
|| narrowGADTBounds(tp2, tp1, approx, isUpper = false))
537537
&& (isBottom(tp1) || GADTusage(tp2.symbol))
538538

539-
isSubApproxHi(tp1, info2.lo) || compareGADT || tryLiftedToThis2 || fourthTry
539+
isSubApproxHi(tp1, info2.lo) && (trustBounds || isSubApproxHi(tp1, info2.hi))
540+
|| compareGADT
541+
|| tryLiftedToThis2
542+
|| fourthTry
540543

541544
case _ =>
542545
val cls2 = tp2.symbol
@@ -786,14 +789,15 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
786789
def fourthTry: Boolean = tp1 match {
787790
case tp1: TypeRef =>
788791
tp1.info match {
789-
case TypeBounds(_, hi1) =>
792+
case info1 @ TypeBounds(lo1, hi1) =>
790793
def compareGADT =
791794
tp1.symbol.onGadtBounds(gbounds1 =>
792795
isSubTypeWhenFrozen(gbounds1.hi, tp2)
793796
|| narrowGADTBounds(tp1, tp2, approx, isUpper = true))
794797
&& (tp2.isAny || GADTusage(tp1.symbol))
795798

796-
(!caseLambda.exists || canWidenAbstract) && isSubType(hi1, tp2, approx.addLow)
799+
(!caseLambda.exists || canWidenAbstract)
800+
&& isSubType(hi1, tp2, approx.addLow) && (trustBounds || isSubType(lo1, tp2, approx.addLow))
797801
|| compareGADT
798802
|| tryLiftedToThis1
799803
case _ =>

Diff for: compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala

+5-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
package dotty.tools.dotc
1+
package dotty.tools
2+
package dotc
23
package transform
34

45
import scala.annotation.tailrec
@@ -388,7 +389,9 @@ object PatternMatcher {
388389
case Typed(pat, tpt) =>
389390
val isTrusted = pat match {
390391
case UnApply(extractor, _, _) =>
391-
extractor.symbol.is(Synthetic) && extractor.symbol.owner.linkedClass.is(Case)
392+
extractor.symbol.is(Synthetic)
393+
&& extractor.symbol.owner.linkedClass.is(Case)
394+
&& !hasExplicitTypeArgs(extractor)
392395
case _ => false
393396
}
394397
TestPlan(TypeTest(tpt, isTrusted), scrutinee, tree.span,

Diff for: tests/neg-custom-args/fatal-warnings/i15662.scala

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
case class Composite[T](v: T)
2+
3+
def m(composite: Composite[_]): Unit =
4+
composite match {
5+
case Composite[Int](v) => println(v) // error: cannot be checked at runtime
6+
case _ => println("OTHER")
7+
}
8+
9+
def m2(composite: Composite[_]): Unit =
10+
composite match {
11+
case Composite(v) => println(v) // ok
12+
}
13+
14+
@main def Test =
15+
m(Composite("This is String"))

0 commit comments

Comments
 (0)