Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

First pass of combining multiple union types #20014

Closed
wants to merge 2 commits into from
Closed
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
95 changes: 88 additions & 7 deletions compiler/src/dotty/tools/dotc/typer/Typer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3414,20 +3414,101 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer

def typedType(tree: untpd.Tree, pt: Type = WildcardType, mapPatternBounds: Boolean = false)(using Context): Tree =
val tree1 = withMode(Mode.Type) { typed(tree, pt) }
val tree2 = tree1 match
case inferredTree: InferredTypeTree if inferredTree.hasType =>
inferredTree.tpe match
case or: OrType =>
val res = flattenOr(or)
tree1.withType(res)
case _ =>
tree1
case _ =>
tree1
if mapPatternBounds && ctx.mode.is(Mode.Pattern) && !ctx.isAfterTyper then
tree1 match
case tree1: TypeBoundsTree =>
tree2 match
case tree2: TypeBoundsTree =>
// Associate a pattern-bound type symbol with the wildcard.
// The bounds of the type symbol can be constrained when comparing a pattern type
// with an expected type in typedTyped. The type symbol and the defining Bind node
// are eliminated once the enclosing pattern has been typechecked; see `indexPattern`
// in `typedCase`.
val boundName = WildcardParamName.fresh().toTypeName
val wildcardSym = newPatternBoundSymbol(boundName, tree1.tpe & pt, tree.span)
untpd.Bind(boundName, tree1).withType(wildcardSym.typeRef)
case tree1 =>
tree1
else tree1
val wildcardSym = newPatternBoundSymbol(boundName, tree2.tpe & pt, tree.span)
untpd.Bind(boundName, tree2).withType(wildcardSym.typeRef)
case tree2 =>
tree2
else tree2

private def flattenOr(tp: Type)(using Context): Type =
var options: List[Type] = Nil
var doUpdate: Boolean = false
// Null and Nothing are sub types of everything, and are puled out post-typing via TypeOps, nevertheless,
// we reduce to at most one of each at this stage
var nullRef: Option[Type] = None
var nothingRef: Option[Type] = None

def offer(next: Type): Unit =
// By checking at insert time, we will never add an element to the internal state if it is invalidated by
// a later element. Thus as extract time, we only need to validate for those prepended after that point
next match
case OrType(o1, o2) =>
offer(o1)
offer(o2)
case nothing if nothing.isNothingType =>
if (nothingRef.isDefined)
doUpdate = true
else
nothingRef = Some(nothing)
case nul if nul.isNullType =>
if (nullRef.isDefined)
doUpdate = true
else
nullRef = Some(nul)
case _ =>
if (!options.exists(prior => next <:< prior))
options = next :: options
else
doUpdate = true

offer(tp)
if (doUpdate)
val distinctTypes = options.reverse.tails.map {
case curr :: allLaterAdditions
if !allLaterAdditions.exists(later => curr <:< later) =>
Some(curr)
case _ =>
doUpdate = true
None
}

val typesToAdd = (Iterator(nothingRef, nullRef) ++ distinctTypes).flatten

def addHelper(add: Type, orTree: List[Option[Type]], iter: Int = 0): List[Option[Type]] =
orTree match
case None :: more =>
var res = Some(add) :: more
for (i <- 1 to iter) {
res = None :: res
}
res
case Some(next) :: more =>
addHelper(add | next, more, iter + 1)
case Nil =>
var res: List[Option[Type]] = List(Some(add))
for (i <- 1 to iter) {
res = None :: res
}
res

val res = typesToAdd.foldLeft[List[Option[Type]]](Nil) {
case (orTree, add) =>
addHelper(add, orTree)
}.flatten.reduceLeft(_ | _)
//println(s"${tp.show} ===> ${res.show} (${ctx.tree.show})")
res
else
tp


def typedPattern(tree: untpd.Tree, selType: Type = WildcardType)(using Context): Tree =
withMode(Mode.Pattern)(typed(tree, selType))
Expand Down
23 changes: 23 additions & 0 deletions compiler/test-resources/repl/10693
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
scala> def test[A, B](a: A, b: B): A | B = a
def test[A, B](a: A, b: B): A | B

scala> def d0 = test("string", 1)
def d0: String | Int

scala> def d1 = test(1, "string")
def d1: Int | String

scala> def d2 = test(d0, d1)
def d2: Int | String

scala> def d3 = test(d1, d0)
def d3: String | Int

scala> def d4 = test(d2, d3)
def d4: String | Int

scala> def d5 = test(d3, d2)
def d5: Int | String

scala> def d6 = test(d4, d5)
def d6: Int | String
19 changes: 19 additions & 0 deletions tests/pos/i10693.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
object Example {
def test[A, B](a: A, b: B): A | B = a

val v0 = test("string", 1)
val v1 = test(1, "string")
val v2 = test(v0, v1)
val v3 = test(v1, v0)
val v4 = test(v2, v3)
val v5 = test(v3, v2)
val v6 = test(v4, v5)

def d0 = test("string", 1)
def d1 = test(1, "string")
def d2 = test(d0, d1)
def d3 = test(d1, d0)
def d4 = test(d2, d3)
def d5 = test(d3, d2)
def d6 = test(d4, d5)
}
Loading