diff --git a/community-build/community-projects/upickle b/community-build/community-projects/upickle index 0213eea95b28..39336b970b09 160000 --- a/community-build/community-projects/upickle +++ b/community-build/community-projects/upickle @@ -1 +1 @@ -Subproject commit 0213eea95b282b1e961b1d5ad68031365c9a8bb2 +Subproject commit 39336b970b09a011913542ba658c3916734bff22 diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 3e2373d3bd4b..9ce945d7b9d7 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -1765,7 +1765,9 @@ class Definitions { Set[Symbol](ComparableClass, ProductClass, SerializableClass, // add these for now, until we had a chance to retrofit 2.13 stdlib // we should do a more through sweep through it then. + requiredClass("scala.collection.IterableFactoryDefaults"), requiredClass("scala.collection.SortedOps"), + requiredClass("scala.collection.StrictOptimizedSetOps"), requiredClass("scala.collection.StrictOptimizedSortedSetOps"), requiredClass("scala.collection.generic.DefaultSerializable"), requiredClass("scala.collection.generic.IsIterable"), diff --git a/compiler/src/dotty/tools/dotc/core/PatternTypeConstrainer.scala b/compiler/src/dotty/tools/dotc/core/PatternTypeConstrainer.scala index c5f126580df5..8ddf60b4392a 100644 --- a/compiler/src/dotty/tools/dotc/core/PatternTypeConstrainer.scala +++ b/compiler/src/dotty/tools/dotc/core/PatternTypeConstrainer.scala @@ -209,8 +209,9 @@ trait PatternTypeConstrainer { self: TypeComparer => * are used to infer type arguments to Unapply trees. * * ## Invariant refinement - * Essentially, we say that `D[B] extends C[B]` s.t. refines parameter `A` of `trait C[A]` invariantly if - * when `c: C[T]` and `c` is instance of `D`, then necessarily `c: D[T]`. This is violated if `A` is variant: + * Essentially, we say that `D[B] extends C[B]` refines parameter `A` of `trait C[A]` invariantly if + * when `c: C[T]` and `c` is instance of `D`, then necessarily `c: D[T]`. + * This is violated if `A` is variant and `C` is mixed in with an incompatible type argument: * * trait C[+A] * trait D[+B](val b: B) extends C[B] @@ -224,29 +225,30 @@ trait PatternTypeConstrainer { self: TypeComparer => * } * * It'd be unsound for us to say that `t <: T`, even though that follows from `D[t] <: C[T]`. - * Note, however, that if `D` was a final class, we *could* rely on that relationship. - * To support typical case classes, we also assume that this relationship holds for them and their parent traits. - * This is enforced by checking that classes inheriting from case classes do not extend the parent traits of those - * case classes without also appropriately extending the relevant case class - * (see `RefChecks#checkCaseClassInheritanceInvariant`). + * Note, however, that if `D` was a concrete class, we can rely on that relationship. + * We can assume this relationship holds for them and their parent traits + * by checking that classes inheriting from those classes do not mix-in any parent traits + * with a type parameter that isn't the same type, a subtype, or a super type, depending on if the + * trait's parameter is invariant, covariant or contravariant, respectively + * (see `RefChecks#checkClassInheritanceInvariant`). */ def constrainSimplePatternType(patternTp: Type, scrutineeTp: Type, forceInvariantRefinement: Boolean): Boolean = { def refinementIsInvariant(tp: Type): Boolean = tp match { case tp: SingletonType => true - case tp: ClassInfo => tp.cls.is(Final) || tp.cls.is(Case) + case tp: ClassInfo => tp.cls.is(Final) case tp: TypeProxy => refinementIsInvariant(tp.superType) case _ => false } + def refinementIsInvariant2(tp: Type): Boolean = tp match + case tp: SingletonType => true + case tp: ClassInfo => !tp.cls.isOneOf(AbstractOrTrait) || tp.cls.isOneOf(Private | Sealed) + case tp: TypeProxy => refinementIsInvariant2(tp.superType) + case _ => false - def widenVariantParams(tp: Type) = tp match { - case tp @ AppliedType(tycon, args) => - val args1 = args.zipWithConserve(tycon.typeParams)((arg, tparam) => - if (tparam.paramVarianceSign != 0) TypeBounds.empty else arg - ) - tp.derivedAppliedType(tycon, args1) - case tp => - tp - } + extension (tp: Type) def isAbstract: Boolean = tp.stripped match + case _: TypeParamRef => true + case tp: TypeRef => !tp.symbol.isClass + case _ => false val patternCls = patternTp.classSymbol val scrutineeCls = scrutineeTp.classSymbol @@ -269,10 +271,11 @@ trait PatternTypeConstrainer { self: TypeComparer => val result = tyconS.typeParams.lazyZip(argsS).lazyZip(argsP).forall { (param, argS, argP) => val variance = param.paramVarianceSign - if variance == 0 || assumeInvariantRefinement || + if variance == 0 || assumeInvariantRefinement + || refinementIsInvariant2(patternTp) && (argP.isAbstract || patternTp.argInfos.contains(argP)) // As a special case, when pattern and scrutinee types have the same type constructor, // we infer better bounds for pattern-bound abstract types. - argP.typeSymbol.isPatternBound && patternTp.classSymbol == scrutineeTp.classSymbol + || argP.typeSymbol.isPatternBound && patternTp.classSymbol == scrutineeTp.classSymbol then val TypeBounds(loS, hiS) = argS.bounds val TypeBounds(loP, hiP) = argP.bounds diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index a1752ccc0976..f381a557eb66 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -2074,9 +2074,9 @@ object SymDenotations { required: FlagSet = EmptyFlags, excluded: FlagSet = EmptyFlags)(using Context): Denotation = membersNamedNoShadowingBasedOnFlags(name, required, excluded).asSeenFrom(pre).toDenot(pre) - /** Compute tp.baseType(this) */ - final def baseTypeOf(tp: Type)(using Context): Type = { - val btrCache = baseTypeCache + /** Compute tp.baseType(this) or tp.baseType(this, without) */ + final def baseTypeOf(tp: Type, without: Option[Symbol] = None)(using Context): Type = { + val btrCache = if without.isEmpty then baseTypeCache else new BaseTypeMap() def inCache(tp: Type) = tp match case tp: CachedType => btrCache.contains(tp) case _ => false @@ -2130,6 +2130,8 @@ object SymDenotations { val baseTp = if (tpSym eq symbol) tp + else if without.exists(tpSym eq _) then + defn.AnyType else if (isOwnThis) if (clsd.baseClassSet.contains(symbol)) if (symbol.isStatic && symbol.typeParams.isEmpty) symbol.typeRef @@ -2156,6 +2158,7 @@ object SymDenotations { btrCache(tp) = NoPrefix val baseTp = if (tycon.typeSymbol eq symbol) tp + else if without.exists(tycon.typeSymbol eq _) then defn.AnyType else (tycon.typeParams: @unchecked) match { case LambdaParam(_, _) :: _ => recur(tp.superType) diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 4f7e79f36cf8..161d3718da8f 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -1129,6 +1129,12 @@ object Types { } } + /** `basetype`, but ignoring any base classes that have the given `without` class symbol. */ + final def baseTypeWithout(base: Symbol, without: Symbol)(using Context): Type = + base.denot match + case classd: ClassDenotation => classd.baseTypeOf(this, Some(without)) + case _ => NoType + def & (that: Type)(using Context): Type = { record("&") TypeComparer.glb(this, that) diff --git a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala index b416b028efe8..d13d15472908 100644 --- a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala +++ b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala @@ -770,16 +770,18 @@ object RefChecks { } } - /** Check that inheriting a case class does not constitute a variant refinement - * of a base type of the case class. It is because of this restriction that we - * can assume invariant refinement for case classes in `constrainPatternType`. + /** Check that inheriting a class does not constitute a variant refinement + * of a base type of the class. It is because of this restriction that we + * can assume invariant refinement for concrete classes in `constrainPatternType`. */ - def checkCaseClassInheritanceInvariant() = - for (caseCls <- clazz.info.baseClasses.tail.find(_.is(Case))) - for (baseCls <- caseCls.info.baseClasses.tail) + def checkClassInheritanceInvariant() = + for (middle <- clazz.info.baseClasses.tail.filter(!_.isTransparentTrait)) + for (baseCls <- middle.info.baseClasses.tail) if (baseCls.typeParams.exists(_.paramVarianceSign != 0)) - for (problem <- variantInheritanceProblems(baseCls, caseCls, "non-variant", "case ")) + val middleStr = if middle.is(Case) then "case " else "" + for (problem <- variantInheritanceProblems(baseCls, middle, "variant", middleStr)) report.errorOrMigrationWarning(problem(), clazz.srcPos, from = `3.0`) + checkNoAbstractMembers() if (abstractErrors.isEmpty) checkNoAbstractDecls(clazz) @@ -788,7 +790,7 @@ object RefChecks { report.error(abstractErrorMessage, clazz.srcPos) checkMemberTypesOK() - checkCaseClassInheritanceInvariant() + checkClassInheritanceInvariant() } if (!clazz.is(Trait)) { @@ -825,16 +827,28 @@ object RefChecks { */ def variantInheritanceProblems( baseCls: Symbol, middle: Symbol, baseStr: String, middleStr: String): Option[() => String] = { + if baseCls == middle then return None val superBT = self.baseType(middle) - val thisBT = self.baseType(baseCls) val combinedBT = superBT.baseType(baseCls) - if (combinedBT =:= thisBT) None // ok + val withoutMiddleBT = self.baseTypeWithout(baseCls, middle) + val allOk = (combinedBT, withoutMiddleBT) match + case (AppliedType(tycon, args1), AppliedType(_, args2)) => + val superBTArgs = superBT.argInfos.toSet + tycon.typeParams.lazyZip(args1).lazyZip(args2).forall { (param, arg1, arg2) => + if superBTArgs.contains(arg1) then + val variance = param.paramVarianceSign + (variance > 0 || (arg2 <:< arg1)) && + (variance < 0 || (arg1 <:< arg2)) + else true // e.g. CovBoth in neg/i11834 + } + case _ => combinedBT =:= self.baseType(baseCls) + if allOk then None // ok else Some(() => em"""illegal inheritance: $clazz inherits conflicting instances of $baseStr base $baseCls. | - | Direct basetype: $thisBT - | Basetype via $middleStr$middle: $combinedBT""") + | Basetype via $middleStr$middle: $combinedBT + | Basetype without $middleStr$middle: $withoutMiddleBT""") } /* Returns whether there is a symbol declared in class `inclazz` diff --git a/tests/neg-custom-args/isInstanceOf/3324g.erasure.scala b/tests/neg-custom-args/isInstanceOf/3324g.erasure.scala new file mode 100644 index 000000000000..07fa4dc3dcff --- /dev/null +++ b/tests/neg-custom-args/isInstanceOf/3324g.erasure.scala @@ -0,0 +1,12 @@ +// like neg-custom-args/isInstanceOf/3324g, +// but verifying the fatal type test/unchecked warning +// emitted during Erasure +// by not being trumped by the fatal refcheck warning on C subclass +class Test { + trait A[+T] + class B[T] extends A[T] + + def quux[T](a: A[T]): Unit = a match { + case _: B[T] => // error + } +} diff --git a/tests/neg-custom-args/isInstanceOf/3324g.scala b/tests/neg-custom-args/isInstanceOf/3324g.scala index 5c37929eb464..390b5e819f8c 100644 --- a/tests/neg-custom-args/isInstanceOf/3324g.scala +++ b/tests/neg-custom-args/isInstanceOf/3324g.scala @@ -1,19 +1,5 @@ class Test { trait A[+T] class B[T] extends A[T] - class C[T] extends B[Any] with A[T] - - def foo[T](c: C[T]): Unit = c match { - case _: B[T] => // error - } - - def bar[T](b: B[T]): Unit = b match { - case _: A[T] => - } - - def quux[T](a: A[T]): Unit = a match { - case _: B[T] => // error!! - } - - quux(new C[Int]) + class C[T] extends B[Any] with A[T] // error } diff --git a/tests/neg/i11018.scala b/tests/neg/i11018.scala index 983722997582..f83ee1a52893 100644 --- a/tests/neg/i11018.scala +++ b/tests/neg/i11018.scala @@ -13,9 +13,19 @@ trait CTrait[+A](val a: A) { trait DTrait[+B] extends CTrait[B] trait DClass[+B] extends CClass[B] -final class F1 extends DTrait[Foo] with CTrait[Bar](new Bar) // error: illegal parameter -final class F2 extends CTrait[Bar](new Bar) with DTrait[Foo] // error: illegal parameter -final class F3 extends DClass[Foo] with CClass[Bar](new Bar) // error: illegal parameter -final class F4 extends CClass[Bar](new Bar) with DClass[Foo] // error: illegal parameter +final class F1 // error: illegal inheritance + extends DTrait[Foo] + with CTrait[Bar](new Bar) // error: illegal parameter +final class F2 // error: illegal inheritance + extends CTrait[Bar](new Bar) // error: illegal parameter + with DTrait[Foo] +final class F3 // error: illegal inheritance + extends DClass[Foo] + with CClass[Bar](new Bar) // error: illegal parameter +final class F4 // error: illegal inheritance + extends CClass[Bar](new Bar) // error: illegal parameter + with DClass[Foo] -final class F5 extends DTrait[Foo] with CTrait[Foo & Bar](new Bar with Foo { def name = "hello"}) // ok +final class F5 // error: illegal inheritance + extends DTrait[Foo] + with CTrait[Foo & Bar](new Bar with Foo { def name = "hello"}) diff --git a/tests/neg/i11834.con.scala b/tests/neg/i11834.con.scala new file mode 100644 index 000000000000..f331527e2ace --- /dev/null +++ b/tests/neg/i11834.con.scala @@ -0,0 +1,62 @@ +// || Put[Any] | Put[AnyRef] | Put[Z] | Put[Int] | Put[String] | Put[Null] | Put[Nothing] | +// ============ || ======== | =========== | ====== | ======== | =========== | ========= | ============ | +// Con[Any] || -- | error | error | error | error | error | error | +// Con[AnyRef] || ok | - | error | error | error | error | error | +// Con[Z] || ok | error | - | error | error | error | error | +// Con[Int] || ok | error | error | - | error | error | error | +// Con[String] || ok | ok | error | error | - | error | error | +// Con[Null] || ok | ok | error | error | ok | - | error | +// Con[Nothing] || ok | ok | ok | ok | ok | ok | - | + +trait Con[-X] +trait Put[-Y] extends Con[Y] { def put: Y => Boolean } + +class PutAR (val put: Any => Boolean) extends Put[Any] with Con[AnyRef] // ok // val x: Con[AnyRef] = PutAR(_ == ""); if x.is[Put[AnyRef]] then x.as[Put[AnyRef]].put("") +class PutAZ[-Z](val put: Any => Boolean) extends Put[Any] with Con[Z] // ok // val x: Con[String] = PutAZ(_ == ""); if x.is[Put[String]] then x.as[Put[String]].put("") +class PutAI (val put: Any => Boolean) extends Put[Any] with Con[Int] // ok // val x: Con[Int] = PutAI(_ == 10); if x.is[Put[Int]] then x.as[Put[Int]].put(10) +class PutAS (val put: Any => Boolean) extends Put[Any] with Con[String] // ok // val x: Con[String] = PutAS(_ == ()); if x.is[Put[String] then x.as[Put[String]].put("") +class PutAU (val put: Any => Boolean) extends Put[Any] with Con[Null] // ok // val x: Con[Null] = PutAU(_ == ()); if x.is[Put[Null] then x.as[Put[Null]].put(null) +class PutAN (val put: Any => Boolean) extends Put[Any] with Con[Nothing] // ok // val x: Con[Nothing] = PutAN(_ == ()); if x.is[Put[Nothing]] then x.as[Put[Nothing]].put(???) + +class PutRA (val put: AnyRef => Boolean) extends Put[AnyRef] with Con[Any] // error // val x: Con[Any] = PutRA(_ == ""); if x.is[Put[Any]] then x.as[Put[Any]].put(()) +class PutRZ[-Z](val put: AnyRef => Boolean) extends Put[AnyRef] with Con[Z] // error // val x: Con[String] = PutRZ(_ == ""); if x.is[Put[String]] then x.as[Put[String]].put("") +class PutRI (val put: AnyRef => Boolean) extends Put[AnyRef] with Con[Int] // error // val x: Con[Int] = PutRI(_ == 10); if x.is[Put[Int]] then x.as[Put[Int]].put(10) +class PutRS (val put: AnyRef => Boolean) extends Put[AnyRef] with Con[String] // ok // val x: Con[String] = PutRS(_ == ()); if x.is[Put[String] then x.as[Put[String]].put("") +class PutRU (val put: AnyRef => Boolean) extends Put[AnyRef] with Con[Null] // ok // val x: Con[Null] = PutRU(_ == ()); if x.is[Put[Null] then x.as[Put[Null]].put(null) +class PutRN (val put: AnyRef => Boolean) extends Put[AnyRef] with Con[Nothing] // ok // val x: Con[Nothing] = PutRN(_ == ()); if x.is[Put[Nothing]] then x.as[Put[Nothing]].put(???) + +class PutZA[-Z](val put: Z => Boolean) extends Put[Z] with Con[Any] // error // val x: Con[Any] = PutZA[String](_.isEmpty); if x.is[Put[Any]] then x.as[Put[Any]].put(()) +class PutZR[-Z](val put: Z => Boolean) extends Put[Z] with Con[AnyRef] // error // val x: Con[AnyRef] = PutZR[String](_.isEmpty); if x.is[Put[AnyRef]] then x.as[Put[AnyRef]].put("") +class PutZI[-Z](val put: Z => Boolean) extends Put[Z] with Con[Int] // error // val x: Con[Int] = PutZI[String](_.isEmpty); if x.is[Put[Int]] then x.as[Put[Int]].put(10) +class PutZS[-Z](val put: Z => Boolean) extends Put[Z] with Con[String] // error // val x: Con[String] = PutZS[Int ](_ == 1); if x.is[Put[String]] then x.as[Put[String]].put("") +class PutZU[-Z](val put: Z => Boolean) extends Put[Z] with Con[Null] // error // val x: Con[Null] = PutZU[String](_.isEmpty); if x.is[Put[Null]] then x.as[Put[Null]].put(???) +class PutZN[-Z](val put: Z => Boolean) extends Put[Z] with Con[Nothing] // ok // val x: Con[Nothing] = PutZN[String](_.isEmpty); if x.is[Put[Nothing]] then x.as[Put[Nothing]].put(???) + +class PutIA (val put: Int => Boolean) extends Put[Int] with Con[Any] // error // val x: Con[Any] = PutIA(_ == 1); if x.is[Put[Any]] then x.as[Put[Any]].put(()) +class PutIR (val put: Int => Boolean) extends Put[Int] with Con[AnyRef] // error // val x: Con[AnyRef] = PutIR(_ == 1); if x.is[Put[AnyRef]] then x.as[Put[AnyRef]].put("") +class PutIZ[-Z](val put: Int => Boolean) extends Put[Int] with Con[Z] // error // val x: Con[String] = PutIZ(_ == 1); if x.is[Put[String]] then x.as[Put[String]].put("") +class PutIS (val put: Int => Boolean) extends Put[Int] with Con[String] // error // val x: Con[String] = PutZI(_ == 1); if x.is[Put[String]] then x.as[Put[String]].put("") +class PutIU (val put: Int => Boolean) extends Put[Int] with Con[Null] // error // val x: Con[Null] = PutIU(_ == 1); if x.is[Put[Null]] then x.as[Put[Null]].put(null) +class PutIN (val put: Int => Boolean) extends Put[Int] with Con[Nothing] // ok // val x: Con[Nothing] = PutIN(_ == 1); if x.is[Put[Nothing]] then x.as[Put[Nothing]].put(???) + +class PutSA (val put: String => Boolean) extends Put[String] with Con[Any] // error // val x: Con[Any] = PutSA(_.isEmpty); if x.is[Put[Any]] then x.as[Put[Any]].put(()) +class PutSR (val put: String => Boolean) extends Put[String] with Con[AnyRef] // error // val x: Con[AnyRef] = PutSR(_.isEmpty); if x.is[Put[AnyRef]] then x.as[Put[AnyRef]].put("") +class PutSZ[-Z](val put: String => Boolean) extends Put[String] with Con[Z] // error // val x: Con[Int] = PutSZ(_.isEmpty); if x.is[Put[Int]] then x.as[Put[Int]].put(10) +class PutSI (val put: String => Boolean) extends Put[String] with Con[Int] // error // val x: Con[Int] = PutSI(_.isEmpty); if x.is[Put[Int]] then x.as[Put[Int]].put(10) +class PutSU (val put: String => Boolean) extends Put[String] with Con[Null] // ok // val x: Con[Null] = PutSU(_.isEmpty); if x.is[Put[Null]] then x.as[Put[Null]].put(null) +class PutSN (val put: String => Boolean) extends Put[String] with Con[Nothing] // ok // val x: Con[Nothing] = PutSN(_.isEmpty); if x.is[Put[Nothing]] then x.as[Put[Nothing]].put(???) + +class PutUA (val put: Null => Boolean) extends Put[Null] with Con[Any] // error // val x: Con[Any] = PutUA(_ == ""); if x.is[Put[Any]] then x.as[Put[Any]].put(()) +class PutUR (val put: Null => Boolean) extends Put[Null] with Con[AnyRef] // error // val x: Con[AnyRef] = PutUR(_ == ""); if x.is[Put[AnyRef] then x.as[Put[AnyRef]].put("") +class PutUZ[-Z](val put: Null => Boolean) extends Put[Null] with Con[Z] // error // val x: Con[String] = PutUZ(_ == ""); if x.is[Put[String]] then x.as[Put[String]].put("") +class PutUI (val put: Null => Boolean) extends Put[Null] with Con[Int] // error // val x: Con[Int] = PutUI(_ == ""); if x.is[Put[Int]] then x.as[Put[Int]].put(10) +class PutUS (val put: Null => Boolean) extends Put[Null] with Con[String] // error // val x: Con[String] = PutUS(_ == ""); if x.is[Put[String]] then x.as[Put[String]].put("") +class PutUN (val put: Null => Boolean) extends Put[Null] with Con[Nothing] // ok // val x: Con[Nothing] = PutUN(_ == ""); if x.is[Put[Nothing]] then x.as[Put[Nothing]].put(???) + +class PutNA extends PutNothing with Con[Any] // error // val x: Con[Any] = PutNA(); if x.is[Put[Any]] then x.as[Put[Any]].put(()) +class PutNR extends PutNothing with Con[AnyRef] // error // val x: Con[AnyRef] = PutNA(); if x.is[Put[AnyRef] then x.as[Put[AnyRef]].put(()) +class PutNZ[-Z] extends PutNothing with Con[Z] // error // val x: Con[String] = PutNZ(); if x.is[Put[String]] then x.as[Put[String]].put("") +class PutNI extends PutNothing with Con[Int] // error // val x: Con[Int] = PutNI(); if x.is[Put[Int]] then x.as[Put[Int]].put(10) +class PutNS extends PutNothing with Con[String] // error // val x: Con[String] = PutNI(); if x.is[Put[String]] then x.as[Put[String]].put("") +class PutNU extends PutNothing with Con[Null] // error // val x: Con[Null] = PutNI(); if x.is[Put[Null]] then x.as[Put[Null]].put(null) +class PutNothing extends Put[Nothing] { def put = (n: Nothing) => n } diff --git a/tests/neg/i11834.cov.scala b/tests/neg/i11834.cov.scala new file mode 100644 index 000000000000..9c08cf9289fe --- /dev/null +++ b/tests/neg/i11834.cov.scala @@ -0,0 +1,62 @@ +// || Get[Any] | Get[AnyRef] | Get[Z] | Get[Int] | Get[String] | Get[Null] | Get[Nothing] | +// ============ || ======== | =========== | ====== | ======== | =========== | ========= | ============ | +// Cov[Any] || - | ok | ok | ok | ok | ok | ok | +// Cov[AnyRef] || error | - | error | error | ok | ok | ok | +// Cov[Z] || error | error | - | error | error | error | ok | +// Cov[Int] || error | error | error | - | error | error | ok | +// Cov[String] || error | error | error | error | - | ok | ok | +// Cov[Null] || error | error | error | error | error | - | ok | +// Cov[Nothing] || error | eoror | error | error | error | error | -- | + +trait Cov[+X] +trait Get[+Y] extends Cov[Y] { def get: Y } + +class GetAR (val get: Any) extends Get[Any] with Cov[AnyRef] // error // val x: Cov[AnyRef] = GetAR(10); if x.is[Get[AnyRef]] then x.as[Get[AnyRef]].get +class GetAZ[+Z](val get: Any) extends Get[Any] with Cov[Z] // error // val x: Cov[Int] = GetAZ(()); if x.is[Get[Int]] then x.as[Get[Int]].get +class GetAI (val get: Any) extends Get[Any] with Cov[Int] // error // val x: Cov[Int] = GetAI(()); if x.is[Get[Int]] then x.as[Get[Int]].get +class GetAS (val get: Any) extends Get[Any] with Cov[String] // error // val x: Cov[String] = GetAI(()); if x.is[Get[String]] then x.as[Get[String]].get +class GetAU (val get: Any) extends Get[Any] with Cov[Null] // error // val x: Cov[Null] = GetAU(()); if x.is[Get[Null]] then x.as[Get[Null]].get +class GetAN (val get: Any) extends Get[Any] with Cov[Nothing] // error // val x: Cov[Nothing] = GetAN(()); if x.is[Get[Nothing]] then x.as[Get[Nothing]].get + +class GetRA (val get: AnyRef) extends Get[AnyRef] with Cov[Any] // ok // val x: Cov[Any] = GetRA(10); if x.is[Get[Any]] then x.as[Get[Any]].get +class GetRZ[+Z](val get: AnyRef) extends Get[AnyRef] with Cov[Z] // error // val x: Cov[Int] = GetRZ(()); if x.is[Get[Int]] then x.as[Get[Int]].get +class GetRI (val get: AnyRef) extends Get[AnyRef] with Cov[Int] // error // val x: Cov[Int] = GetRI(()); if x.is[Get[Int]] then x.as[Get[Int]].get +class GetRS (val get: AnyRef) extends Get[AnyRef] with Cov[String] // error // val x: Cov[String] = GetRS(()); if x.is[Get[String]] then x.as[Get[String]].get +class GetRU (val get: AnyRef) extends Get[AnyRef] with Cov[Null] // error // val x: Cov[Null] = GetRU(()); if x.is[Get[Null]] then x.as[Get[Null]].get +class GetRN (val get: AnyRef) extends Get[AnyRef] with Cov[Nothing] // error // val x: Cov[Nothing] = GetRN(()); if x.is[Get[Nothing]] then x.as[Get[Nothing]].get + +class GetZA[+Z](val get: Z) extends Get[Z] with Cov[Any] // ok // val x: Cov[Any] = GetZA(()); if x.is[Get[Any]] then x.as[Get[Any]].get +class GetZR[+Z](val get: Z) extends Get[Z] with Cov[AnyRef] // error // val x: Cov[AnyRef] = GetZR(()); if x.is[Get[AnyRef]] then x.as[Get[AnyRef]].get +class GetZI[+Z](val get: Z) extends Get[Z] with Cov[Int] // error // val x: Cov[Int] = GetZI(()); if x.is[Get[Int]] then x.as[Get[Int]].get +class GetZS[+Z](val get: Z) extends Get[Z] with Cov[String] // error // val x: Cov[String] = GetZS(()); if x.is[Get[String]] then x.as[Get[String]].get +class GetZU[+Z](val get: Z) extends Get[Z] with Cov[Null] // error // val x: Cov[Null] = GetZU(()); if x.is[Get[Null]] then x.as[Get[Null]].get +class GetZN[+Z](val get: Z) extends Get[Z] with Cov[Nothing] // error // val x: Cov[Nothing] = GetZN(()); if x.is[Get[Nothing]] then x.as[Get[Nothing]].get + +class GetIA (val get: Int) extends Get[Int] with Cov[Any] // ok // val x: Cov[Any] = GetIA(10); if x.is[Get[Any]] then x.as[Get[Any]].get +class GetIR (val get: Int) extends Get[Int] with Cov[AnyRef] // error // val x: Cov[AnyRef] = GetIR(10); if x.is[Get[AnyRef]] then x.as[Get[AnyRef]].get +class GetIZ[+Z](val get: Int) extends Get[Int] with Cov[Z] // error // val x: Cov[String] = GetIZ(10); if x.is[Get[String]] then x.as[Get[String]].get +class GetIS (val get: Int) extends Get[Int] with Cov[String] // error // val x: Cov[String] = GetIS(10); if x.is[Get[String]] then x.as[Get[String]].get +class GetIU (val get: Int) extends Get[Int] with Cov[Null] // error // val x: Cov[Null] = GetIU(10); if x.is[Get[Null]] then x.as[Get[Null]].get +class GetIN (val get: Int) extends Get[Int] with Cov[Nothing] // error // val x: Cov[Nothing] = GetIN(10); if x.is[Get[Nothing]] then x.as[Get[Nothing]].get + +class GetSA (val get: String) extends Get[String] with Cov[Any] // ok // val x: Cov[Any] = GetSA(""); if x.is[Get[Any]] then x.as[Get[Any]].get +class GetSR (val get: String) extends Get[String] with Cov[AnyRef] // ok // val x: Cov[AnyRef] = GetSR(""); if x.is[Get[AnyRef]] then x.as[Get[AnyRef]].get +class GetSZ[+Z](val get: String) extends Get[String] with Cov[Z] // error // val x: Cov[Int] = GetSZ(""); if x.is[Get[Int]] then x.as[Get[Int]].get +class GetSI (val get: String) extends Get[String] with Cov[Int] // error // val x: Cov[Int] = GetSI(""); if x.is[Get[Int]] then x.as[Get[Int]].get +class GetSU (val get: String) extends Get[String] with Cov[Null] // error // val x: Cov[Null] = GetSU(""); if x.is[Get[Null]] then x.as[Get[Null]].get +class GetSN (val get: String) extends Get[String] with Cov[Nothing] // error // val x: Cov[Nothing] = GetSN(""); if x.is[Get[Nothing]] then x.as[Get[Nothing]].get + +class GetUA (val get: Null) extends Get[Null] with Cov[Any] // ok // val x: Cov[Any] = GetUA(null); if x.is[Get[Any]] then x.as[Get[Any]].get +class GetUR (val get: Null) extends Get[Null] with Cov[AnyRef] // ok // val x: Cov[AnyRef] = GetUR(null); if x.is[Get[AnyRef]] then x.as[Get[AnyRef]].get +class GetUZ[+Z](val get: Null) extends Get[Null] with Cov[Z] // error // val x: Cov[String] = GetUZ(null); if x.is[Get[String]] then x.as[Get[String]].get +class GetUI (val get: Null) extends Get[Null] with Cov[Int] // error // val x: Cov[Int] = GetUI(null); if x.is[Get[Int]] then x.as[Get[Int]].get +class GetUS (val get: Null) extends Get[Null] with Cov[String] // ok // val x: Cov[String] = GetUI(null); if x.is[Get[String]] then x.as[Get[String]].get +class GetUN (val get: Null) extends Get[Null] with Cov[Nothing] // error // val x: Cov[Nothing] = GetUN(null); if x.is[Get[Nothing]] then x.as[Get[Nothing]].get + +class GetNA extends GetNothing with Cov[Any] // ok // val x: Cov[Any] = GetNA(); if x.is[Get[Any]] then x.as[Get[Any]].get +class GetNR extends GetNothing with Cov[AnyRef] // ok // val x: Cov[AnyRef] = GetNR(); if x.is[Get[AnyRef]] then x.as[Get[AnyRef]].get +class GetNZ[+Z] extends GetNothing with Cov[Z] // ok // val x: Cov[String] = GetNZ(); if x.is[Get[String]] then x.as[Get[String]].get +class GetNI extends GetNothing with Cov[Int] // ok // val x: Cov[Int] = GetNI(); if x.is[Get[Int]] then x.as[Get[Int]].get +class GetNS extends GetNothing with Cov[String] // ok // val x: Cov[String] = GetNI(); if x.is[Get[String]] then x.as[Get[String]].get +class GetNU extends GetNothing with Cov[Null] // ok // val x: Cov[Null] = GetNU(); if x.is[Get[Null]] then x.as[Get[Null]].get +class GetNothing extends Get[Nothing] { def get = throw new AssertionError(s"I am ${getClass.getSimpleName}") } diff --git a/tests/neg/i11834.inv.scala b/tests/neg/i11834.inv.scala new file mode 100644 index 000000000000..4700b61a86e8 --- /dev/null +++ b/tests/neg/i11834.inv.scala @@ -0,0 +1,12 @@ +class Animal +class Cat extends Animal +class Dog extends Animal + +trait Cov[+X] +trait Con[-X] +trait Mod[Z] extends AnyRef with Cov[Z] with Con[Z] { def mod: Z => Z } + +class ModCovCD(val mod: Cat => Cat) extends Mod[Cat] with Cov[Dog] // error +class ModCovDC(val mod: Dog => Dog) extends Mod[Dog] with Cov[Cat] // error +class ModConCD(val mod: Cat => Cat) extends Mod[Cat] with Con[Dog] // error +class ModConDC(val mod: Dog => Dog) extends Mod[Dog] with Con[Cat] // error diff --git a/tests/neg/i11834.misc.scala b/tests/neg/i11834.misc.scala new file mode 100644 index 000000000000..4652000a01b9 --- /dev/null +++ b/tests/neg/i11834.misc.scala @@ -0,0 +1,43 @@ +class Animal +class Cat extends Animal +class Dog extends Animal + +trait Cov[+X] +trait CovCat extends Cov[Cat] +class CovBoth extends CovCat with Cov[Dog] // ok // val x: Cov[Dog] = CovBoth(); if x.is[CovCat] then x.as[CovCat].getCat +// baseCls = Cov +// middle = CovCat +// self = CovBoth +// superBT = CovCat +// combinedBT = Cov[Cat] +// withoutMiddleBT = Cov[Dog] +// Comparing the arguments of `Cov[Cat]` and `Cov[Dog]`, +// `Cat` isn't a type argument of the super base type `CovCat` +// so we can ignore it. + +object CollectionStrawMan4: + trait Iter[+CC[_]] + trait Seq[+A] extends Iter[Seq] + class List[+A] extends Seq[A] with Iter[List] // ok // val x: Iter[List] = List[Int](); if x.is[Seq[?]] then x.as[Seq[?]] + class Cons[+A] extends List[A] // ok // val x: Iter[List] = Cons[Int](); if x.is[Seq[?]] then x.as[Seq[?]] + +object CollectionStrawMan5: + trait Build[+X, +C[X] <: Iter[X]] + trait Iter[+Y] extends Build[Y, Iter] + class List[+Z] extends Iter[Z] with Build[Z, List] // ok // val x: Build[Int, Iter] = List[Int](); if x.is[Iter[Int]] then x.as[Iter[Int]] + +object PolyInheritanceCase: + trait IOps[+A, +CC[_], +C] + class Iter[+A] extends IOps[A, Iter, Iter[A]] + class List[A] extends Iter[A] with IOps[A, List, List[A]] // ok // val x: IOps[Int, List, List[Int]] = List[Int](); if x.is[Iter[Int]] then x.as[Iter[Int]] + +trait Get[+X] +class GetAnimal extends Get[Animal] +class GetCat extends GetAnimal, Get[Cat] + +class Test: + def test[X](get: Get[X]): X = get match + case _: GetAnimal => // X >: Animal (wrong!) + new Dog // error: Found: Dog; Required: X + + def cat: Cat = test(new GetCat) // was: ClassCastException diff --git a/tests/neg/i3989a.scala b/tests/neg/i3989a.scala index 8ff02ac0c2f1..de7045ac9468 100644 --- a/tests/neg/i3989a.scala +++ b/tests/neg/i3989a.scala @@ -1,10 +1,5 @@ object Test extends App { trait A[+X] class B[+X](val x: X) extends A[X] - class C[+X](x: Any) extends B[Any](x) with A[X] - def f(a: A[Int]): Int = a match { - case a: B[_] => a.x // error - case _ => 0 - } - f(new C[Int]("foo")) + class C[+X](x: Any) extends B[Any](x) with A[X] // error } diff --git a/tests/pos-special/isInstanceOf/3324g.scala b/tests/pos-special/isInstanceOf/3324g.scala new file mode 100644 index 000000000000..5c55c62c40e0 --- /dev/null +++ b/tests/pos-special/isInstanceOf/3324g.scala @@ -0,0 +1,8 @@ +class Test { + trait A[+T] + class B[T] extends A[T] + + def bar[T](b: B[T]): Unit = b match { + case _: A[T] => + } +} diff --git a/tests/pos/i3989a.scala b/tests/pos/i3989a.scala new file mode 100644 index 000000000000..7bfd564c36b3 --- /dev/null +++ b/tests/pos/i3989a.scala @@ -0,0 +1,8 @@ +object Test extends App { + trait A[+X] + class B[+X](val x: X) extends A[X] + def f(a: A[Int]): Int = a match { + case a: B[_] => a.x + case _ => 0 + } +} diff --git a/tests/neg/i3989c.scala b/tests/pos/i3989c.scala similarity index 68% rename from tests/neg/i3989c.scala rename to tests/pos/i3989c.scala index 1d631ea1273d..27d34427eac7 100644 --- a/tests/neg/i3989c.scala +++ b/tests/pos/i3989c.scala @@ -6,10 +6,8 @@ object Test extends App { def unapply[X](b: B[X]): Option[X] = Some(b.x) } - class C[+X](x: Any) extends B[Any](x) with A[X] def f(a: A[Int]): Int = a match { - case B(i) => i // error + case B(i) => i case _ => 0 } - f(new C[Int]("foo")) }