diff --git a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala index 42d24daf3255..553a73a6f4c6 100644 --- a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala +++ b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala @@ -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 diff --git a/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala b/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala index 6c9bd1bb6577..7b96062dda95 100644 --- a/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala +++ b/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala @@ -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) @@ -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) || { diff --git a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala index 6d79f377c84e..e9e7f1b3e52d 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala @@ -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 @@ -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 _ => diff --git a/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala b/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala index 201e8a0db31f..0e4e32be52dd 100644 --- a/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala +++ b/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala @@ -1,4 +1,5 @@ -package dotty.tools.dotc +package dotty.tools +package dotc package transform import scala.annotation.tailrec @@ -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, diff --git a/tests/neg-custom-args/fatal-warnings/i15662.scala b/tests/neg-custom-args/fatal-warnings/i15662.scala new file mode 100644 index 000000000000..1d5ff21eb3ba --- /dev/null +++ b/tests/neg-custom-args/fatal-warnings/i15662.scala @@ -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"))