-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Simplify handling of union types #2330
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -830,23 +830,23 @@ object Types { | |
* def o: Outer | ||
* <o.x.type>.widen = o.C | ||
*/ | ||
@tailrec final def widen(implicit ctx: Context): Type = widenSingleton match { | ||
final def widen(implicit ctx: Context): Type = widenSingleton match { | ||
case tp: ExprType => tp.resultType.widen | ||
case tp => tp | ||
} | ||
|
||
/** Widen from singleton type to its underlying non-singleton | ||
* base type by applying one or more `underlying` dereferences. | ||
*/ | ||
@tailrec final def widenSingleton(implicit ctx: Context): Type = stripTypeVar match { | ||
final def widenSingleton(implicit ctx: Context): Type = stripTypeVar match { | ||
case tp: SingletonType if !tp.isOverloaded => tp.underlying.widenSingleton | ||
case _ => this | ||
} | ||
|
||
/** Widen from TermRef to its underlying non-termref | ||
* base type, while also skipping Expr types. | ||
*/ | ||
@tailrec final def widenTermRefExpr(implicit ctx: Context): Type = stripTypeVar match { | ||
final def widenTermRefExpr(implicit ctx: Context): Type = stripTypeVar match { | ||
case tp: TermRef if !tp.isOverloaded => tp.underlying.widenExpr.widenTermRefExpr | ||
case _ => this | ||
} | ||
|
@@ -860,7 +860,7 @@ object Types { | |
} | ||
|
||
/** Widen type if it is unstable (i.e. an ExprType, or TermRef to unstable symbol */ | ||
@tailrec final def widenIfUnstable(implicit ctx: Context): Type = stripTypeVar match { | ||
final def widenIfUnstable(implicit ctx: Context): Type = stripTypeVar match { | ||
case tp: ExprType => tp.resultType.widenIfUnstable | ||
case tp: TermRef if !tp.symbol.isStable => tp.underlying.widenIfUnstable | ||
case _ => this | ||
|
@@ -872,6 +872,35 @@ object Types { | |
case _ => this | ||
} | ||
|
||
/** If this type contains embedded union types, replace them by their joins. | ||
* "Embedded" means: inside intersectons or recursive types, or in prefixes of refined types. | ||
* If an embedded union is found, we first try to simplify or eliminate it by | ||
* re-lubbing it while allowing type parameters to be constrained further. | ||
* Any remaining union types are replaced by their joins. | ||
* | ||
* For instance, if `A` is an unconstrained type variable, then | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Incorrect indentation around here. |
||
* | ||
* ArrayBuffer[Int] | ArrayBuffer[A] | ||
* | ||
* is approximated by constraining `A` to be =:= to `Int` and returning `ArrayBuffer[Int]` | ||
* instead of `ArrayBuffer[_ >: Int | A <: Int & A]` | ||
*/ | ||
def widenUnion(implicit ctx: Context): Type = this match { | ||
case OrType(tp1, tp2) => | ||
ctx.typeComparer.lub(tp1.widenUnion, tp2.widenUnion, canConstrain = true) match { | ||
case union: OrType => union.join | ||
case res => res | ||
} | ||
case tp @ AndType(tp1, tp2) => | ||
tp derived_& (tp1.widenUnion, tp2.widenUnion) | ||
case tp: RefinedType => | ||
tp.derivedRefinedType(tp.parent.widenUnion, tp.refinedName, tp.refinedInfo) | ||
case tp: RecType => | ||
tp.rebind(tp.parent.widenUnion) | ||
case _ => | ||
this | ||
} | ||
|
||
/** Eliminate anonymous classes */ | ||
final def deAnonymize(implicit ctx: Context): Type = this match { | ||
case tp:TypeRef if tp.symbol.isAnonymousClass => | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -167,6 +167,7 @@ class tests extends CompilerTest { | |
@Test def rewrites = compileFile(posScala2Dir, "rewrites", "-rewrite" :: scala2mode) | ||
|
||
@Test def pos_t8146a = compileFile(posSpecialDir, "t8146a")(allowDeepSubtypes) | ||
@Test def pos_jon = compileFile(posSpecialDir, "jon")(allowDeepSubtypes) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Isn't it surprising that this requires deep subtypes? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I did not look too deeply. But SeqFactory has a funky recursive type
so it's not so implausible. |
||
|
||
@Test def pos_t5545 = { | ||
// compile by hand in two batches, since junit lacks the infrastructure to | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
object Test { | ||
|
||
class A | ||
class B extends A | ||
class C extends A | ||
class D extends A | ||
|
||
val b = true | ||
val x = if (b) new B else new C | ||
val y: B | C = x // error | ||
} | ||
|
||
object O { | ||
class A | ||
class B | ||
def f[T](x: T, y: T): T = x | ||
|
||
val x: A = f(new A { }, new A) | ||
|
||
val y1: A | B = f(new A { }, new B) // error | ||
val y2: A | B = f[A | B](new A { }, new B) // ok | ||
|
||
val z = if (???) new A{} else new B | ||
|
||
val z1: A | B = z // error | ||
|
||
val z2: A | B = if (???) new A else new B // ok | ||
} |
This file was deleted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why was the tailrec annotation removed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It was noise. I was against adding it, but it got merged before I could comment.
As I wrote then: Add a @tailrec only if it is important that the method is in fact tail-recursive. Either it is super-performance critical or can potentially recurse deeply. Do not add @tailrec just because the method happens to be tail recursive.