diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 955a5a11c6f2..5be397a3e5e4 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -2219,7 +2219,7 @@ object Types { def derivedAndType(tp1: Type, tp2: Type)(implicit ctx: Context): Type = if ((tp1 eq this.tp1) && (tp2 eq this.tp2)) this - else AndType.make(tp1, tp2) + else AndType.make(tp1, tp2, checkValid = true) def derived_& (tp1: Type, tp2: Type)(implicit ctx: Context): Type = if ((tp1 eq this.tp1) && (tp2 eq this.tp2)) this @@ -2234,21 +2234,26 @@ object Types { final class CachedAndType(tp1: Type, tp2: Type) extends AndType(tp1, tp2) object AndType { - def apply(tp1: Type, tp2: Type)(implicit ctx: Context) = { + def apply(tp1: Type, tp2: Type)(implicit ctx: Context): AndType = { assert(tp1.isValueType && tp2.isValueType, i"$tp1 & $tp2 / " + s"$tp1 & $tp2") unchecked(tp1, tp2) } - def unchecked(tp1: Type, tp2: Type)(implicit ctx: Context) = { + + def unchecked(tp1: Type, tp2: Type)(implicit ctx: Context): AndType = { assertUnerased() unique(new CachedAndType(tp1, tp2)) } - def make(tp1: Type, tp2: Type)(implicit ctx: Context): Type = + + /** Make an AndType using `op` unless clearly unnecessary (i.e. without + * going through `&`). + */ + def make(tp1: Type, tp2: Type, checkValid: Boolean = false)(implicit ctx: Context): Type = if ((tp1 eq tp2) || (tp2 eq defn.AnyType)) tp1 else if (tp1 eq defn.AnyType) tp2 else - apply(tp1, tp2) + if (checkValid) apply(tp1, tp2) else unchecked(tp1, tp2) } abstract case class OrType(tp1: Type, tp2: Type) extends CachedGroundType with AndOrType { @@ -2994,7 +2999,7 @@ object Types { // ----- Skolem types ----------------------------------------------- /** A skolem type reference with underlying type `binder`. */ - abstract case class SkolemType(info: Type) extends UncachedProxyType with ValueType with SingletonType { + case class SkolemType(info: Type) extends UncachedProxyType with ValueType with SingletonType { override def underlying(implicit ctx: Context) = info def derivedSkolemType(info: Type)(implicit ctx: Context) = if (info eq this.info) this else SkolemType(info) @@ -3010,13 +3015,6 @@ object Types { override def toString = s"Skolem($hashCode)" } - final class CachedSkolemType(info: Type) extends SkolemType(info) - - object SkolemType { - def apply(info: Type)(implicit ctx: Context) = - unique(new CachedSkolemType(info)) - } - // ------------ Type variables ---------------------------------------- /** In a TypeApply tree, a TypeVar is created for each argument type to be inferred. diff --git a/tests/neg/i2232.scala b/tests/neg/i2232.scala new file mode 100644 index 000000000000..e0f83683d42a --- /dev/null +++ b/tests/neg/i2232.scala @@ -0,0 +1,37 @@ +object Cats { + trait Trivial[A] + implicit def trivial[A]: Trivial[A] = new Trivial[A] { } + + type Obj[C[_[_[_], _[_, _]]]] = + [A] => C[({type l[c0[_], c1[_, _]] = c0[A] })#l] + type Cat[C[_[_[_], _[_, _]]]] = + [A, B] => C[({type l[c0[_], c1[_, _]] = c1[A, B]})#l] + + trait Category[C[_[_[_], _[_, _]]]] { + type -> = Cats.Cat[C] + type Obj = Cats.Obj[C] + + def id[A: Obj]: A -> A + def andThen[A, B, C](ab: A -> B, bc: B -> C): A -> C + } + + object Category { + type ByF[F[_, _]] = Category[_] { type -> = F } + } + + type Scal[f[_[_], _[_, _]]] = f[Trivial, Function1] + + implicit val scal: Category[Scal] = new Category[Scal] { + def id[A: Obj]: A -> A = a => a + def andThen[A, B, C](ab: A -> B, bc: B -> C): A -> C = ab.andThen(bc) + } + + implicit class CategoryOps[F[_, _], A, B](ab: F[A, B]) { + def >>>[C](bc: F[B, C])(implicit F: Category.ByF[F]): F[A, C] = + F.andThen(ab, bc) + } + + val f: Int => Int = _ + 1 + val g: Int => String = _.toString + f >>> g // error: no implicit arg found +}