Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions compiler/src/dotty/tools/dotc/config/SourceVersion.scala
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,6 @@ enum SourceVersion:
def enablesNewGivens = isAtLeast(`3.6`)
def enablesNamedTuples = isAtLeast(`3.7`)
def enablesBetterFors(using Context) = isAtLeast(`3.8`) || (isAtLeast(`3.7`) && isPreviewEnabled)
/** See PR #23441 and tests/neg/i23435-min */
def enablesDistributeAnd = !isAtLeast(`3.8`)

def requiresNewSyntax = isAtLeast(future)

Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/core/Definitions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -460,7 +460,7 @@ class Definitions {
@tu lazy val CBCompanion: TypeSymbol = // type `<context-bound-companion>`[-Refs]
enterPermanentSymbol(tpnme.CBCompanion,
TypeBounds(NothingType,
HKTypeLambda(tpnme.syntheticTypeParamName(0) :: Nil)(
HKTypeLambda(tpnme.syntheticTypeParamName(0) :: Nil, Contravariant :: Nil)(
tl => TypeBounds.empty :: Nil,
tl => AnyType))).asType

Expand Down
5 changes: 3 additions & 2 deletions compiler/src/dotty/tools/dotc/core/NamerOps.scala
Original file line number Diff line number Diff line change
Expand Up @@ -307,7 +307,7 @@ object NamerOps:
* The context-bound companion has as name the name of `tsym` translated to
* a term name. We create a synthetic val of the form
*
* val A: `<context-bound-companion>`[witnessRef1] & ... & `<context-bound-companion>`[witnessRefN]
* val A: `<context-bound-companion>`[witnessRef1 | ... | witnessRefN]
*
* where
*
Expand All @@ -325,7 +325,8 @@ object NamerOps:
prefix.select(params.find(_.name == witnessName).get)
else
witnessNames.map(TermRef(prefix, _))
val cbtype = witnessRefs.map(defn.CBCompanion.typeRef.appliedTo).reduce(AndType.apply)
val cbtype = defn.CBCompanion.typeRef.appliedTo:
witnessRefs.reduce[Type](OrType(_, _, soft = false))
val cbc = newSymbol(
ctx.owner, companionName,
(tsym.flagsUNSAFE & (AccessFlags)).toTermFlags | Synthetic,
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/core/SymUtils.scala
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ class SymUtils:
}

def isContextBoundCompanion(using Context): Boolean =
self.is(Synthetic) && self.infoOrCompleter.isContextBoundCompanion
self.is(Synthetic) && self.infoOrCompleter.typeSymbol == defn.CBCompanion

def isDummyCaptureParam(using Context): Boolean =
self.is(PhantomSymbol) && self.infoOrCompleter.typeSymbol != defn.CBCompanion
Expand Down
12 changes: 2 additions & 10 deletions compiler/src/dotty/tools/dotc/core/TypeApplications.scala
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import Symbols.*
import SymDenotations.LazyType
import Decorators.*
import util.Stats.*
import config.Feature.sourceVersion
import Names.*
import StdNames.nme
import Flags.{Module, Provisional}
Expand Down Expand Up @@ -476,20 +475,13 @@ class TypeApplications(val self: Type) extends AnyVal {
self.derivedExprType(tp.translateParameterized(from, to))
case _ =>
if (self.derivesFrom(from)) {
// NOTE: we assume the `To` class is covariant s.t.
// `To[T] X To[U] <:< To[T | U]` where X ::= `&` | `|`
def elemType(tp: Type): Type = tp.widenDealias match
case tp: OrType =>
if tp.tp1.isBottomType then elemType(tp.tp2)
else if tp.tp2.isBottomType then elemType(tp.tp1)
else tp.derivedOrType(elemType(tp.tp1), elemType(tp.tp2))
case tp @ AndType(tp1, tp2) =>
if sourceVersion.enablesDistributeAnd
then tp.derivedAndType(elemType(tp1), elemType(tp2))
else OrType(elemType(tp1), elemType(tp2), soft = false)
case _ =>
tp.baseType(from).argInfos.headOption.getOrElse(defn.NothingType)
end elemType
case tp: AndType => tp.derivedAndType(elemType(tp.tp1), elemType(tp.tp2))
case _ => tp.baseType(from).argInfos.headOption.getOrElse(defn.NothingType)
val arg = elemType(self)
val arg1 = if (wildcardArg) TypeBounds.upper(arg) else arg
to.typeRef.appliedTo(arg1)
Expand Down
14 changes: 6 additions & 8 deletions compiler/src/dotty/tools/dotc/core/TypeComparer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2460,8 +2460,7 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling

/** If the range `tp1..tp2` consist of a single type, that type, otherwise NoType`.
* This is the case if `tp1 =:= tp2`, but also if `tp1 <:< tp2`, `tp1` is a singleton type,
* and `tp2` derives from `scala.Singleton` and `sourceVersion.enablesDistributeAnd` (or vice-versa).
* Examples of the latter case:
* and `tp2` derives from `scala.Singleton` (or vice-versa). Examples of the latter case:
*
* "name".type .. Singleton
* "name".type .. String & Singleton
Expand All @@ -2474,10 +2473,8 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
def isSingletonBounds(lo: Type, hi: Type) =
lo.isSingleton && hi.derivesFrom(defn.SingletonClass) && isSubTypeWhenFrozen(lo, hi)
if (isSameTypeWhenFrozen(tp1, tp2)) tp1
else if sourceVersion.enablesDistributeAnd then
if (isSingletonBounds(tp1, tp2)) tp1
else if (isSingletonBounds(tp2, tp1)) tp2
else NoType
else if (isSingletonBounds(tp1, tp2)) tp1
else if (isSingletonBounds(tp2, tp1)) tp2
else NoType
}

Expand Down Expand Up @@ -2774,7 +2771,7 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
* @pre !(tp1 <: tp2) && !(tp2 <:< tp1) -- these cases were handled before
*/
private def distributeAnd(tp1: Type, tp2: Type): Type = tp1 match {
case tp1 @ AppliedType(tycon1, args1) if sourceVersion.enablesDistributeAnd =>
case tp1 @ AppliedType(tycon1, args1) =>
tp2 match {
case AppliedType(tycon2, args2)
if tycon1.typeSymbol == tycon2.typeSymbol && tycon1 =:= tycon2 =>
Expand Down Expand Up @@ -2822,7 +2819,8 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
}

/** Try to distribute `|` inside type, detect and handle conflicts
* Note that a disjunction cannot be pushed into a refined or applied type. Example:
* Note that, unlike for `&`, a disjunction cannot be pushed into
* a refined or applied type. Example:
*
* List[T] | List[U] is not the same as List[T | U].
*
Expand Down
8 changes: 0 additions & 8 deletions compiler/src/dotty/tools/dotc/core/Types.scala
Original file line number Diff line number Diff line change
Expand Up @@ -466,14 +466,6 @@ object Types extends TypeUtils {
case AppliedType(tycon: TypeRef, arg :: Nil) => defn.isInto(tycon.symbol)
case _ => false

/** Is this type of the form `<context-bound-companion>[Ref1] & ... & <context-bound-companion>[RefN]`?
* Where the intersection may be introduced by `NamerOps.addContextBoundCompanionFor`
* or by inheriting multiple context bound companions for the same name.
*/
def isContextBoundCompanion(using Context): Boolean = this.widen match
case AndType(tp1, tp2) => tp1.isContextBoundCompanion.ensuring(_ == tp2.isContextBoundCompanion)
case tp => tp.typeSymbol == defn.CBCompanion

/** Is this type a legal target type for an implicit conversion, so that
* no `implicitConversions` language import is necessary?
*/
Expand Down
34 changes: 18 additions & 16 deletions compiler/src/dotty/tools/dotc/typer/Typer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -932,7 +932,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
// Otherwise, if the qualifier is a context bound companion, handle
// by selecting a witness in typedCBSelect
def tryCBCompanion() =
if qual.tpe.isContextBoundCompanion then
if qual.tpe.typeSymbol == defn.CBCompanion then
typedCBSelect(tree0, pt, qual)
else EmptyTree

Expand Down Expand Up @@ -1001,13 +1001,13 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
* alternatives referred to by `witnesses`.
* @param prevs a list of (ref tree, typer state, term ref) tripls that
* represents previously identified alternatives
* @param witnesses a type of the form `isContextBoundCompanion` containing references
* @param witnesses a type of the form ref_1 | ... | ref_n containing references
* still to be considered.
*/
def tryAlts(prevs: Alts, witnesses: Type): Alts = witnesses.widen match
case AndType(wit1, wit2) =>
def tryAlts(prevs: Alts, witnesses: Type): Alts = witnesses match
case OrType(wit1, wit2) =>
tryAlts(tryAlts(prevs, wit1), wit2)
case AppliedType(_, List(witness: TermRef)) =>
case witness: TermRef =>
val altQual = tpd.ref(witness).withSpan(qual.span)
val altCtx = ctx.fresh.setNewTyperState()
val alt = typedSelectWithAdapt(tree, pt, altQual)(using altCtx)
Expand All @@ -1019,17 +1019,19 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
if comparisons.exists(_ == 1) then prevs
else current :: prevs.zip(comparisons).collect{ case (prev, cmp) if cmp != -1 => prev }

tryAlts(Nil, qual.tpe) match
case Nil => EmptyTree
case (best @ (bestTree, bestState, _)) :: Nil =>
bestState.commit()
bestTree
case multiAlts =>
report.error(
em"""Ambiguous witness reference. None of the following alternatives is more specific than the other:
|${multiAlts.map((alt, _, witness) => i"\n $witness.${tree.name}: ${alt.tpe.widen}")}""",
tree.srcPos)
EmptyTree
qual.tpe.widen match
case AppliedType(_, arg :: Nil) =>
tryAlts(Nil, arg) match
case Nil => EmptyTree
case (best @ (bestTree, bestState, _)) :: Nil =>
bestState.commit()
bestTree
case multiAlts =>
report.error(
em"""Ambiguous witness reference. None of the following alternatives is more specific than the other:
|${multiAlts.map((alt, _, witness) => i"\n $witness.${tree.name}: ${alt.tpe.widen}")}""",
tree.srcPos)
EmptyTree
end typedCBSelect

def typedSelect(tree: untpd.Select, pt: Type)(using Context): Tree = {
Expand Down
5 changes: 5 additions & 0 deletions docs/_docs/reference/new-types/intersection-types-spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,11 @@ A & B <: B A & B <: A
In another word, `A & B` is the same type as `B & A`, in the sense that the two types
have the same values and are subtypes of each other.

If `C` is a co- or contravariant type constructor, then `C[A] & C[B]` can be simplified using the following rules:

- If `C` is covariant, `C[A] & C[B] ~> C[A & B]`
- If `C` is contravariant, `C[A] & C[B] ~> C[A | B]`

When `C` is covariant, `C[A & B] <: C[A] & C[B]` can be derived:

```
Expand Down
24 changes: 0 additions & 24 deletions docs/_docs/reference/new-types/union-types-spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,30 +46,6 @@ case _: (A | B) => ...
A & (B | C) =:= A & B | A & C
```

When `C` is covariant, `C[A] | C[B] <: C[A | B]` can be derived:

```
A <: A B <: B
---------- ---------
A <: A | B B <: A | B
---------------- ----------------
C[A] <: C[A | B] C[B] <: C[A | B]
-----------------------------------------
C[A] | C[B] <: C[A | B]
```

When `C` is contravariant, `C[A] | C[B] <: C[A & B]` can be derived:

```
A <: A B <: B
---------- ----------
A & B <: A A & B <: B
---------------- ----------------
C[A] <: C[A & B] C[B] <: C[A & B]
-----------------------------------------
C[A] | C[B] <: C[A & B]
```

From these rules it follows that the _least upper bound_ (LUB) of a set of types
is the union of these types. This replaces the
[definition of least upper bound in the Scala 2 specification](https://www.scala-lang.org/files/archive/spec/2.13/03-types.html#least-upper-bounds-and-greatest-lower-bounds).
Expand Down
9 changes: 9 additions & 0 deletions tests/neg-deep-subtype/i11064.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
trait TypedArray[T, Repr]

trait Ops[T <: TypedArray[_, T]] {
def typedArray(): T
}

object Test {
def test(ops: Ops[_ <: TypedArray[_ <: AnyRef, _]]) = ops.typedArray() // error: Recursion limit exceeded.
}
7 changes: 0 additions & 7 deletions tests/neg/conflicting-inst-basetypes.scala

This file was deleted.

7 changes: 7 additions & 0 deletions tests/neg/i10256.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
trait Foo[T <: Foo[T]] {
type I <: Foo[I]
}

trait Bar[T <: Foo[T]] extends Foo[T] { // error: cyclic
self: T =>
}
4 changes: 4 additions & 0 deletions tests/neg/i11103.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,8 @@
case p: P =>
new Foo // error
}

class UpBndAndB extends UpBnd[Bar] with P
// ClassCastException: Foo cannot be cast to Bar
val x = pmatch(new UpBndAndB)
}
7 changes: 0 additions & 7 deletions tests/neg/i23435-min.scala

This file was deleted.

17 changes: 0 additions & 17 deletions tests/neg/i23435.scala

This file was deleted.

4 changes: 1 addition & 3 deletions tests/neg/i3989e.scala
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
//> using options -source:3.8

object Test extends App {
trait A[+X](val x: X)
class B extends A(5) with A("hello") // error: A is extended twice // error: class B cannot be instantiated since it has conflicting base types Test.A[Int] and Test.A[String]
class B extends A(5) with A("hello") // error: A is extended twice

def f(a: A[Int]): Int = a match {
case b: B => b.x
Expand Down
15 changes: 0 additions & 15 deletions tests/neg/lucre-23441-min.scala

This file was deleted.

17 changes: 0 additions & 17 deletions tests/neg/singletonInterval.scala

This file was deleted.

2 changes: 1 addition & 1 deletion tests/pos-macros/quoted-pattern-type.scala
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ object Lib {
e

case e @ '{ Some($x: Int) } =>
e: Expr[T] & Expr[Some[Int]]
e: Expr[T & Some[Int]]
x: Expr[Int]
e

Expand Down
Loading
Loading