Skip to content

Commit

Permalink
Merge pull request #15669 from dotty-staging/fix-15662
Browse files Browse the repository at this point in the history
Don't trust case class extractors with explicit type arguments
  • Loading branch information
odersky authored Jul 14, 2022
2 parents cb4f807 + c4c4401 commit 9fdcfd2
Show file tree
Hide file tree
Showing 5 changed files with 42 additions and 7 deletions.
6 changes: 6 additions & 0 deletions compiler/src/dotty/tools/dotc/ast/TreeInfo.scala
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,12 @@ trait TreeInfo[T >: Untyped <: Type] { self: Trees.Instance[T] =>
case _ => Nil
}

/** Is tree explicitly parameterized with type arguments? */
def hasExplicitTypeArgs(tree: Tree): Boolean = tree match
case TypeApply(tycon, args) =>
args.exists(arg => !arg.span.isZeroExtent && !tycon.span.contains(arg.span))
case _ => false

/** Is tree a path? */
def isPath(tree: Tree): Boolean = unsplice(tree) match {
case Ident(_) | This(_) | Super(_, _) => true
Expand Down
11 changes: 9 additions & 2 deletions compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ trait ConstraintHandling {
protected def necessaryConstraintsOnly(using Context): Boolean =
ctx.mode.is(Mode.GadtConstraintInference) || myNecessaryConstraintsOnly

protected var trustBounds = true

def checkReset() =
assert(addConstraintInvocations == 0)
assert(frozenConstraint == false)
Expand Down Expand Up @@ -260,12 +262,17 @@ trait ConstraintHandling {
// If `isUpper` is true, ensure that `param <: `bound`, otherwise ensure
// that `param >: bound`.
val narrowedBounds =
val saved = homogenizeArgs
val savedHomogenizeArgs = homogenizeArgs
val savedTrustBounds = trustBounds
homogenizeArgs = Config.alignArgsInAnd
try
trustBounds = false
if isUpper then oldBounds.derivedTypeBounds(lo, hi & bound)
else oldBounds.derivedTypeBounds(lo | bound, hi)
finally homogenizeArgs = saved
finally
homogenizeArgs = savedHomogenizeArgs
trustBounds = savedTrustBounds
//println(i"narrow bounds for $param from $oldBounds to $narrowedBounds")
val c1 = constraint.updateEntry(param, narrowedBounds)
(c1 eq constraint)
|| {
Expand Down
10 changes: 7 additions & 3 deletions compiler/src/dotty/tools/dotc/core/TypeComparer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -536,7 +536,10 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
|| narrowGADTBounds(tp2, tp1, approx, isUpper = false))
&& (isBottom(tp1) || GADTusage(tp2.symbol))

isSubApproxHi(tp1, info2.lo) || compareGADT || tryLiftedToThis2 || fourthTry
isSubApproxHi(tp1, info2.lo) && (trustBounds || isSubApproxHi(tp1, info2.hi))
|| compareGADT
|| tryLiftedToThis2
|| fourthTry

case _ =>
val cls2 = tp2.symbol
Expand Down Expand Up @@ -786,14 +789,15 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
def fourthTry: Boolean = tp1 match {
case tp1: TypeRef =>
tp1.info match {
case TypeBounds(_, hi1) =>
case info1 @ TypeBounds(lo1, hi1) =>
def compareGADT =
tp1.symbol.onGadtBounds(gbounds1 =>
isSubTypeWhenFrozen(gbounds1.hi, tp2)
|| narrowGADTBounds(tp1, tp2, approx, isUpper = true))
&& (tp2.isAny || GADTusage(tp1.symbol))

(!caseLambda.exists || canWidenAbstract) && isSubType(hi1, tp2, approx.addLow)
(!caseLambda.exists || canWidenAbstract)
&& isSubType(hi1, tp2, approx.addLow) && (trustBounds || isSubType(lo1, tp2, approx.addLow))
|| compareGADT
|| tryLiftedToThis1
case _ =>
Expand Down
7 changes: 5 additions & 2 deletions compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
package dotty.tools.dotc
package dotty.tools
package dotc
package transform

import scala.annotation.tailrec
Expand Down Expand Up @@ -388,7 +389,9 @@ object PatternMatcher {
case Typed(pat, tpt) =>
val isTrusted = pat match {
case UnApply(extractor, _, _) =>
extractor.symbol.is(Synthetic) && extractor.symbol.owner.linkedClass.is(Case)
extractor.symbol.is(Synthetic)
&& extractor.symbol.owner.linkedClass.is(Case)
&& !hasExplicitTypeArgs(extractor)
case _ => false
}
TestPlan(TypeTest(tpt, isTrusted), scrutinee, tree.span,
Expand Down
15 changes: 15 additions & 0 deletions tests/neg-custom-args/fatal-warnings/i15662.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
case class Composite[T](v: T)

def m(composite: Composite[_]): Unit =
composite match {
case Composite[Int](v) => println(v) // error: cannot be checked at runtime
case _ => println("OTHER")
}

def m2(composite: Composite[_]): Unit =
composite match {
case Composite(v) => println(v) // ok
}

@main def Test =
m(Composite("This is String"))

0 comments on commit 9fdcfd2

Please sign in to comment.