summaryrefslogtreecommitdiff
path: root/src/reflect
diff options
context:
space:
mode:
authorMiles Sabin <miles@milessabin.com>2016-05-20 12:49:25 +0100
committerMiles Sabin <miles@milessabin.com>2016-08-15 11:08:42 +0100
commit9e2b10fc02a60a8e24c38a8f0e52b5196c47145f (patch)
treeda071cc172274c2a02baa232b70fe54c2a24a10f /src/reflect
parent81a67eeacc7d2622ee364a21203b227142e2043e (diff)
downloadscala-9e2b10fc02a60a8e24c38a8f0e52b5196c47145f.tar.gz
scala-9e2b10fc02a60a8e24c38a8f0e52b5196c47145f.tar.bz2
scala-9e2b10fc02a60a8e24c38a8f0e52b5196c47145f.zip
SI-2712 Add support for higher order unification
Diffstat (limited to 'src/reflect')
-rw-r--r--src/reflect/scala/reflect/internal/Types.scala40
-rw-r--r--src/reflect/scala/reflect/internal/settings/MutableSettings.scala1
-rw-r--r--src/reflect/scala/reflect/runtime/Settings.scala1
3 files changed, 37 insertions, 5 deletions
diff --git a/src/reflect/scala/reflect/internal/Types.scala b/src/reflect/scala/reflect/internal/Types.scala
index 9697e16da7..98510825c0 100644
--- a/src/reflect/scala/reflect/internal/Types.scala
+++ b/src/reflect/scala/reflect/internal/Types.scala
@@ -3076,13 +3076,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
diff --git a/src/reflect/scala/reflect/runtime/Settings.scala b/src/reflect/scala/reflect/runtime/Settings.scala
index 27d574b1de..1081218a70 100644
--- a/src/reflect/scala/reflect/runtime/Settings.scala
+++ b/src/reflect/scala/reflect/runtime/Settings.scala
@@ -47,6 +47,7 @@ private[reflect] class Settings extends MutableSettings {
val printtypes = new BooleanSetting(false)
val uniqid = new BooleanSetting(false)
val verbose = new BooleanSetting(false)
+ val YpartialUnification = new BooleanSetting(false)
val Yrecursion = new IntSetting(0)
val maxClassfileName = new IntSetting(255)