summaryrefslogtreecommitdiff
path: root/src/reflect/scala/reflect/internal
diff options
context:
space:
mode:
authorAdriaan Moors <adriaan@lightbend.com>2016-10-18 10:41:48 -0700
committerGitHub <noreply@github.com>2016-10-18 10:41:48 -0700
commitaf41493ea423fa49e26c3e57ad5b4adc5f20237d (patch)
tree55193808f0540136908729a678338c16a4b1f936 /src/reflect/scala/reflect/internal
parent9f7c26e8ccc809c48484921f87b52eb56b978dcf (diff)
parent9e2b10fc02a60a8e24c38a8f0e52b5196c47145f (diff)
downloadscala-af41493ea423fa49e26c3e57ad5b4adc5f20237d.tar.gz
scala-af41493ea423fa49e26c3e57ad5b4adc5f20237d.tar.bz2
scala-af41493ea423fa49e26c3e57ad5b4adc5f20237d.zip
Merge pull request #5343 from milessabin/topic/si-2712-backport
SI-2712 Add support for higher order unification
Diffstat (limited to 'src/reflect/scala/reflect/internal')
-rw-r--r--src/reflect/scala/reflect/internal/Types.scala40
-rw-r--r--src/reflect/scala/reflect/internal/settings/MutableSettings.scala1
2 files changed, 36 insertions, 5 deletions
diff --git a/src/reflect/scala/reflect/internal/Types.scala b/src/reflect/scala/reflect/internal/Types.scala
index 3a645616eb..92df4ba3af 100644
--- a/src/reflect/scala/reflect/internal/Types.scala
+++ b/src/reflect/scala/reflect/internal/Types.scala
@@ -3081,13 +3081,43 @@ trait Types
*/
def unifyFull(tpe: Type): Boolean = {
def unifySpecific(tp: Type) = {
- sameLength(typeArgs, tp.typeArgs) && {
- val lhs = if (isLowerBound) tp.typeArgs else typeArgs
- val rhs = if (isLowerBound) typeArgs else tp.typeArgs
+ val tpTypeArgs = tp.typeArgs
+ val arityDelta = compareLengths(typeArgs, tpTypeArgs)
+ if (arityDelta == 0) {
+ val lhs = if (isLowerBound) tpTypeArgs else typeArgs
+ val rhs = if (isLowerBound) typeArgs else tpTypeArgs
// This is a higher-kinded type var with same arity as tp.
// If so (see SI-7517), side effect: adds the type constructor itself as a bound.
- isSubArgs(lhs, rhs, params, AnyDepth) && { addBound(tp.typeConstructor); true }
- }
+ isSubArgs(lhs, rhs, params, AnyDepth) && {addBound(tp.typeConstructor); true}
+ } else if (settings.YpartialUnification && arityDelta < 0 && typeArgs.nonEmpty) {
+ // Simple algorithm as suggested by Paul Chiusano in the comments on SI-2712
+ //
+ // https://issues.scala-lang.org/browse/SI-2712?focusedCommentId=61270
+ //
+ // Treat the type constructor as curried and partially applied, we treat a prefix
+ // as constants and solve for the suffix. For the example in the ticket, unifying
+ // M[A] with Int => Int this unifies as,
+ //
+ // M[t] = [t][Int => t] --> abstract on the right to match the expected arity
+ // A = Int --> capture the remainder on the left
+ //
+ // A more "natural" unifier might be M[t] = [t][t => t]. There's lots of scope for
+ // experimenting with alternatives here.
+ val numCaptured = tpTypeArgs.length - typeArgs.length
+ val (captured, abstractedArgs) = tpTypeArgs.splitAt(numCaptured)
+
+ val (lhs, rhs) =
+ if (isLowerBound) (abstractedArgs, typeArgs)
+ else (typeArgs, abstractedArgs)
+
+ isSubArgs(lhs, rhs, params, AnyDepth) && {
+ val tpSym = tp.typeSymbolDirect
+ val abstractedTypeParams = tpSym.typeParams.drop(numCaptured).map(_.cloneSymbol(tpSym))
+
+ addBound(PolyType(abstractedTypeParams, appliedType(tp.typeConstructor, captured ++ abstractedTypeParams.map(_.tpeHK))))
+ true
+ }
+ } else false
}
// The type with which we can successfully unify can be hidden
// behind singleton types and type aliases.
diff --git a/src/reflect/scala/reflect/internal/settings/MutableSettings.scala b/src/reflect/scala/reflect/internal/settings/MutableSettings.scala
index 38893d8db3..3de720da11 100644
--- a/src/reflect/scala/reflect/internal/settings/MutableSettings.scala
+++ b/src/reflect/scala/reflect/internal/settings/MutableSettings.scala
@@ -53,6 +53,7 @@ abstract class MutableSettings extends AbsSettings {
def printtypes: BooleanSetting
def uniqid: BooleanSetting
def verbose: BooleanSetting
+ def YpartialUnification: BooleanSetting
def Yrecursion: IntSetting
def maxClassfileName: IntSetting