diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index 955f1a34342c..d3db4845e8b3 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -1466,7 +1466,7 @@ object SymDenotations { val builder = new BaseDataBuilder for (p <- classParents) { if (p.typeSymbol.isClass) builder.addAll(p.typeSymbol.asClass.baseClasses) - else assert(ctx.mode.is(Mode.Interactive), s"$this has non-class parent: $p") + else assert(isRefinementClass || ctx.mode.is(Mode.Interactive), s"$this has non-class parent: $p") } (classSymbol :: builder.baseClasses, builder.baseClassSet) } diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 30a1c4b1aa3b..6b68ea100c12 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -989,7 +989,7 @@ object Types { case tp: TypeRef => if (tp.symbol.isClass) tp else tp.info match { - case TypeAlias(tp) => tp.dealias1(keepAnnots): @tailrec + case TypeAlias(alias) => alias.dealias1(keepAnnots): @tailrec case _ => tp } case app @ AppliedType(tycon, args) => @@ -1967,37 +1967,35 @@ object Types { def derivedSelect(prefix: Type)(implicit ctx: Context): Type = if (prefix eq this.prefix) this else if (prefix.isBottomType) prefix - else if (isType) { - val res = - if (currentSymbol.is(ClassTypeParam)) argForParam(prefix) - else prefix.lookupRefined(name) - if (res.exists) res - else if (Config.splitProjections) - prefix match { - case prefix: AndType => - def isMissing(tp: Type) = tp match { - case tp: TypeRef => !tp.info.exists - case _ => false - } - val derived1 = derivedSelect(prefix.tp1) - val derived2 = derivedSelect(prefix.tp2) - return ( - if (isMissing(derived1)) derived2 - else if (isMissing(derived2)) derived1 - else prefix.derivedAndType(derived1, derived2)) - case prefix: OrType => - val derived1 = derivedSelect(prefix.tp1) - val derived2 = derivedSelect(prefix.tp2) - return prefix.derivedOrType(derived1, derived2) - case _ => - withPrefix(prefix) - } + else { + if (isType) { + val res = + if (currentSymbol.is(ClassTypeParam)) argForParam(prefix) + else prefix.lookupRefined(name) + if (res.exists) return res + if (Config.splitProjections) + prefix match { + case prefix: AndType => + def isMissing(tp: Type) = tp match { + case tp: TypeRef => !tp.info.exists + case _ => false + } + val derived1 = derivedSelect(prefix.tp1) + val derived2 = derivedSelect(prefix.tp2) + return ( + if (isMissing(derived1)) derived2 + else if (isMissing(derived2)) derived1 + else prefix.derivedAndType(derived1, derived2)) + case prefix: OrType => + val derived1 = derivedSelect(prefix.tp1) + val derived2 = derivedSelect(prefix.tp2) + return prefix.derivedOrType(derived1, derived2) + case _ => + } + } + if (prefix.isInstanceOf[WildcardType]) WildcardType else withPrefix(prefix) } - else prefix match { - case _: WildcardType => WildcardType - case _ => withPrefix(prefix) - } /** A reference like this one, but with the given symbol, if it exists */ final def withSym(sym: Symbol)(implicit ctx: Context): ThisType = diff --git a/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala b/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala index 3f47d650a77f..85a828b9a401 100644 --- a/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala +++ b/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala @@ -105,6 +105,7 @@ object ProtoTypes { private def hasUnknownMembers(tp: Type)(implicit ctx: Context): Boolean = tp match { case tp: TypeVar => !tp.isInstantiated case tp: WildcardType => true + case NoType => true case tp: TypeRef => val sym = tp.symbol sym == defn.NothingClass || @@ -114,6 +115,7 @@ object ProtoTypes { bound.isProvisional && hasUnknownMembers(bound) } } + case tp: AppliedType => hasUnknownMembers(tp.tycon) || hasUnknownMembers(tp.superType) case tp: TypeProxy => hasUnknownMembers(tp.superType) case _ => false } diff --git a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala index a0ad815fa6e1..2d0ae6dd29f8 100644 --- a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala +++ b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala @@ -343,7 +343,10 @@ object RefChecks { if (autoOverride(member) || other.owner.is(JavaTrait) && ctx.testScala2Mode("`override' modifier required when a Java 8 default method is re-implemented", member.pos)) member.setFlag(Override) - else if (member.owner != clazz && other.owner != clazz && !(other.owner derivesFrom member.owner)) + else if (member.isType && self.memberInfo(member) =:= self.memberInfo(other)) + () // OK, don't complain about type aliases which are equal + else if (member.owner != clazz && other.owner != clazz && + !(other.owner derivesFrom member.owner)) emitOverrideError( clazz + " inherits conflicting members:\n " + infoStringWithLocation(other) + " and\n " + infoStringWithLocation(member) diff --git a/tests/neg/typeclass-encoding2.scala b/tests/neg/typeclass-encoding2.scala new file mode 100644 index 000000000000..d0fd4534e586 --- /dev/null +++ b/tests/neg/typeclass-encoding2.scala @@ -0,0 +1,26 @@ +// Used to crash with: +// assertion failed: class has non-class parent +// Once that was fixed, another crash with: +// assertion failed: invalid prefix WildcardType(NoType) +object runtime1 { + + trait TypeClass1[A] { + val common: TypeClassCommon1 + type This[X] = common.This[X] + } + + trait TypeClassCommon1 { self => + type This[X] + type Instance[X] <: TypeClass1[X] + def inject[A](x: This[A]): Instance[A]// { val common: self.type } + } + + trait Extension1[From[_], To[X] <: TypeClass1[X]] extends TypeClassCommon1 { + type This[X] = From[X] + type Instance[X] = To[X] + } + + implicit def inject[A, From[_]](x: From[A]) + (implicit ev: Extension1[From, _]): ev.Instance[A] { type This[X] = From[X] } = + ev.inject(x) // error: found: ev.To[A], required: ev.To[A]{This = From} +} \ No newline at end of file diff --git a/tests/pos/this-types.scala b/tests/pos/this-types.scala new file mode 100644 index 000000000000..d75de54b3ffa --- /dev/null +++ b/tests/pos/this-types.scala @@ -0,0 +1,16 @@ +trait A { + type A_This <: A +} +trait B extends A { + type A_This = B_This + type B_This <: B +} +trait C extends A { + type A_This = C_This + type C_This <: C +} +trait D extends B with C { + type B_This = D_This + type C_This = D_This + type D_This <: D +} \ No newline at end of file diff --git a/tests/pos/typeclass-encoding2.scala b/tests/pos/typeclass-encoding2.scala new file mode 100644 index 000000000000..3269430435de --- /dev/null +++ b/tests/pos/typeclass-encoding2.scala @@ -0,0 +1,346 @@ +/** 1. Simple type classes with monomorphic implementations and direct extensions. + + trait SemiGroup extends TypeClass { + def add(that: This): This + } + + trait Monoid extends SemiGroup + common { + def unit: This + } + + extension IntOps for Int : Monoid { + def add(that: Int) = this + that + } + common { + def unit = 0 + } + + extension StringOps for String : Monoid { + def add(that: Int) = this ++ that + } + common { + def unit = "" + } + + enum Nat extends Monoid { + case Z + case S(n: Nat) + + def add(that: Nat): Nat = this match { + case S => that + case S(n) => S(n.add(that)) + } + } + common { + def unit = Z + } + + def sum[T: Monoid](xs: List[T]): T = + (Monod.impl[T].unit /: xs)(_ `add` _) +*/ +object runtime { + + trait TypeClass { + val commons: TypeClassCommon + type This = commons.This + } + + trait TypeClassCommon { self => + type This + type Instance <: TypeClass + def inject(x: This): Instance { val commons: self.type } + } + + trait TypeClassCompanion { + type Impl[T] <: TypeClassCommon { type This = T } + def impl[T](implicit ev: Impl[T]): Impl[T] = ev + } + + implicit def inject[From](x: From) + (implicit ev: TypeClassCommon { type This = From }): ev.Instance { type This = From } = + ev.inject(x) +} +import runtime._ + +object semiGroups { + + trait SemiGroup extends TypeClass { + val commons: SemiGroupCommon + import commons._ + def add(that: This): This + } + trait SemiGroupCommon extends TypeClassCommon { + type Instance <: SemiGroup + } + object SemiGroup extends TypeClassCompanion { + type Impl[T] = SemiGroupCommon { type This = T } + } + + trait Monoid extends SemiGroup { + val commons: MonoidCommon + import commons._ + } + trait MonoidCommon extends SemiGroupCommon { + type Instance <: Monoid + def unit: This + } + object Monoid extends TypeClassCompanion { + type Impl[T] = MonoidCommon { type This = T } + } + + implicit object IntOps extends MonoidCommon { + type This = Int + type Instance = Monoid + def unit: Int = 0 + def inject($this: Int) = new Monoid { + val commons: IntOps.this.type = IntOps.this + def add(that: This): This = $this + that + } + } + + implicit object StringOps extends MonoidCommon { + type This = String + type Instance = Monoid + def unit = "" + def inject($this: String) = new Monoid { + val commons: StringOps.this.type = StringOps.this + def add(that: This): This = $this.concat(that) + } + } + + enum Nat extends Monoid { + case Z + case S(n: Nat) + + def add(that: Nat): Nat = this match { + case Z => that + case S(n) => S(n.add(that)) + } + + val commons: Nat.type = Nat + } + object Nat extends MonoidCommon { + type This = Nat + type Instance = Nat + def unit = Nat.Z + def inject($this: Nat) = $this + } + import Nat.{Z, S} + + implicit def NatOps: Nat.type = Nat + + def sum[T](xs: List[T])(implicit ev: Monoid.Impl[T]) = + (Monoid.impl[T].unit /: xs)((x, y) => x `add` y) + + sum(List(1, 2, 3)) + sum(List("hello ", "world!")) + sum(List(Z, S(Z), S(S(Z)))) +} + +/** 2. Generic implementations of simple type classes. + + trait Ord extends TypeClass { + def compareTo(that: This): Int + def < (that: This) = compareTo(that) < 0 + def > (that: This) = compareTo(that) > 0 + } + common { + val minimum: This + } + + extension IntOrd for Int : Ord { + def compareTo(that: Int) = + if (this < that) -1 else if (this > that) +1 else 0 + } + common { + val minimum = Int.MinValue + } + + extension ListOrd[T : Ord] for List[T] : Ord { + def compareTo(that: List[T]): Int = (this, that) match { + case (Nil, Nil) => 0 + case (Nil, _) => -1 + case (_, Nil) => +1 + case (x :: xs, y :: ys) => + val fst = x.compareTo(y) + if (fst != 0) fst else xs.compareTo(ys) + } + } + common { + val minimum = Nil + } + + def min[T: Ord](x: T, y: T) = if (x < y) x else y + + def inf[T: Ord](xs: List[T]): T = (Ord.impl[T].minimum /: xs)(min) +*/ +object ord { + + trait Ord extends TypeClass { + val commons: OrdCommon + import commons._ + def compareTo(that: This): Int + def < (that: This) = compareTo(that) < 0 + def > (that: This) = compareTo(that) > 0 + } + trait OrdCommon extends TypeClassCommon { + type Instance <: Ord + def minimum: This + } + object Ord extends TypeClassCompanion { + type Impl[T] = OrdCommon { type This = T } + } + + implicit object IntOrd extends OrdCommon { + type This = Int + type Instance = Ord + val minimum: Int = Int.MinValue + def inject($this: Int) = new Ord { + val commons: IntOrd.this.type = IntOrd.this + import commons._ + def compareTo(that: This): Int = + if (this < that) -1 else if (this > that) +1 else 0 + } + } + + class ListOrd[T](implicit ev: Ord.Impl[T]) extends OrdCommon { self => + type This = List[T] + type Instance = Ord + def minimum: List[T] = Nil + def inject($this: List[T]) = new Ord { + val commons: self.type = self + import commons._ + def compareTo(that: List[T]): Int = ($this, that) match { + case (Nil, Nil) => 0 + case (Nil, _) => -1 + case (_, Nil) => +1 + case (x :: xs, y :: ys) => + val fst = x.compareTo(y) + if (fst != 0) fst else xs.compareTo(ys) + } + } + } + + implicit def listOrd[T](implicit ev: Ord.Impl[T]): ListOrd[T] = + new ListOrd[T] + + def min[T](x: T, y: T)(implicit ev: Ord.Impl[T]): T = + if (x < y) x else y + + def inf[T](xs: List[T])(implicit ev: Ord.Impl[T]): T = { + val smallest = Ord.impl[T].minimum + (smallest /: xs)(min) + } + + inf(List[Int]()) + inf(List(List(1, 2), List(1, 2, 3))) + inf(List(List(List(1), List(2)), List(List(1), List(2), List(3)))) +} + +/** 3. Higher-kinded type classes + + trait Functor[A] extends TypeClass1 { + def map[B](f: A => B): This[B] + } + common { + def pure[A](x: A): This[A] + } + + // Generically, `pure[A]{.map(f)}^n` + def develop[A, F[X] : Functor[X]](n: Int, f: A => A): F[A] = + if (n == 0) Functor.impl[F].pure[A] + else develop[A, F](n - 1, f).map(f) + + trait Monad[A] extends Functor[A] { + def flatMap[B](f: A => This[B]): This[B] + def map[B](f: A => B) = this.flatMap(f.andThen(pure)) + } + + extension ListMonad[T] for List[T] : Monad[T] { + static def pure[A] = Nil + + def flatMap[B](f: A => List[B]): List[B] = this match { + case x :: xs => f(x) ++ xs.flatMap(f) + case Nil => Nil + } + } + + extension MonadFlatten[T[X]: Monad[X]] for T[T[A]] { + def flatten: T[A] = this.flatMap(identity) + } +*/ +object runtime1 { + + trait TypeClass1 { + val commons: TypeClassCommon1 + type This = commons.This + } + + trait TypeClassCommon1 { self => + type This[X] + type Instance[X] <: TypeClass1 + def inject[A](x: This[A]): Instance[A] { val commons: self.type } + } + + trait TypeClassCompanion1 { + type Impl[T[_]] <: TypeClassCommon1 { type This = T } + def impl[T[_]](implicit ev: Impl[T]): Impl[T] = ev + } + + implicit def inject1[A, From[_]](x: From[A]) + (implicit ev: TypeClassCommon1 { type This = From }): ev.Instance[A] { type This = From } = + ev.inject(x) +} +import runtime1._ + +object functors { + + trait Functor[A] extends TypeClass1 { + val commons: FunctorCommon + import commons._ + def map[B](f: A => B): This[B] + } + trait FunctorCommon extends TypeClassCommon1 { + type Instance[X] <: Functor[X] + def pure[A](x: A): This[A] + } + object Functor extends TypeClassCompanion1 { + type Impl[T[_]] = FunctorCommon { type This = T } + } + + trait Monad[A] extends Functor[A] { + val commons: MonadCommon + import commons._ + def flatMap[B](f: A => This[B]): This[B] + def map[B](f: A => B) = this.flatMap(f.andThen(commons.pure)) + } + trait MonadCommon extends FunctorCommon { + type Instance[X] <: Monad[X] + } + object Monad extends TypeClassCompanion1 { + type Impl[T[_]] = MonadCommon { type This = T } + } + + def develop[A, F[X]](n: Int, x: A, f: A => A)(implicit ev: Functor.Impl[F]): F[A] = + if (n == 0) Functor.impl[F].pure(x) + else develop(n - 1, x, f).map(f) + + implicit object ListMonad extends MonadCommon { + type This = List + type Instance = Monad + def pure[A](x: A) = x :: Nil + def inject[A]($this: List[A]) = new Monad[A] { + val commons: ListMonad.this.type = ListMonad + import commons._ + def flatMap[B](f: A => List[B]): List[B] = $this.flatMap(f) + } + } + + object MonadFlatten { + def flattened[T[_], A]($this: T[T[A]])(implicit ev: Monad.Impl[T]): T[A] = + $this.flatMap(identity ) + } + + MonadFlatten.flattened(List(List(1, 2, 3), List(4, 5))) +} \ No newline at end of file