Skip to content

Commit 409c6c3

Browse files
authored
Merge pull request #1343 from dotty-staging/change-hk-direct2
Direct representation of higher-kinded types
2 parents 1c02c56 + 894c9fb commit 409c6c3

File tree

103 files changed

+2375
-1417
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

103 files changed

+2375
-1417
lines changed
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
object B {
2+
val x: Int = A.f1[Any](1)
3+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
object A {
2+
def f1[T](x: Int): Int = 1
3+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
object A {
2+
def f1[T](x: String): Int = 1
3+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
object A {
2+
def f1[T](x: Int): String = ""
3+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
object A {
2+
def f1[T <: Int](x: Int): Int = 1
3+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import sbt._
2+
import Keys._
3+
4+
object DottyInjectedPlugin extends AutoPlugin {
5+
override def requires = plugins.JvmPlugin
6+
override def trigger = allRequirements
7+
8+
override val projectSettings = Seq(
9+
scalaVersion := "0.1-SNAPSHOT",
10+
scalaOrganization := "ch.epfl.lamp",
11+
scalacOptions += "-language:Scala2",
12+
scalaBinaryVersion := "2.11",
13+
autoScalaLibrary := false,
14+
libraryDependencies ++= Seq("org.scala-lang" % "scala-library" % "2.11.5"),
15+
scalaCompilerBridgeSource := ("ch.epfl.lamp" % "dotty-bridge" % "0.1.1-SNAPSHOT" % "component").sources()
16+
)
17+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Case 1: parameter type changed
2+
$ copy-file changes/A0.scala A.scala
3+
> compile
4+
$ copy-file changes/A1.scala A.scala
5+
# Compilation of B.scala should fail because the signature of f changed
6+
-> compile
7+
8+
# Case 2: return type changed
9+
$ copy-file changes/A0.scala A.scala
10+
> compile
11+
$ copy-file changes/A2.scala A.scala
12+
# Compilation of B.scala should fail because the signature of f changed
13+
-> compile
14+
15+
# Case 3: type parameter bounds changed
16+
$ copy-file changes/A0.scala A.scala
17+
> compile
18+
$ copy-file changes/A3.scala A.scala
19+
# Compilation of B.scala should fail because the signature of f changed
20+
-> compile

docs/SyntaxSummary.txt

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ grammar.
9696
ClassQualifier ::= `[' id `]'
9797

9898
Type ::= FunArgTypes `=>' Type Function(ts, t)
99+
| HkTypeParamClause `->' Type TypeLambda(ps, t)
99100
| InfixType
100101
FunArgTypes ::= InfixType
101102
| `(' [ FunArgType {`,' FunArgType } ] `)'
@@ -125,7 +126,6 @@ grammar.
125126
TypeParamBounds ::= TypeBounds {`<%' Type} {`:' Type} ContextBounds(typeBounds, tps)
126127

127128
Expr ::= FunParams `=>' Expr Function(args, expr), Function(ValDef([implicit], id, TypeTree(), EmptyTree), expr)
128-
| Expr1
129129
FunParams ::= Bindings
130130
| [`implicit'] id
131131
| `_'
@@ -225,7 +225,8 @@ grammar.
225225
TypTypeParam ::= {Annotation} id [HkTypeParamClause] TypeBounds
226226

227227
HkTypeParamClause ::= `[' HkTypeParam {`,' HkTypeParam} `]'
228-
HkTypeParam ::= {Annotation} ['+' | `-'] (Id | `_') TypeBounds
228+
HkTypeParam ::= {Annotation} ['+' | `-'] (Id[HkTypeParamClause] | `_')
229+
TypeBounds
229230

230231
ClsParamClauses ::= {ClsParamClause} [[nl] `(' `implicit' ClsParams `)']
231232
ClsParamClause ::= [nl] `(' [ClsParams] ')'
@@ -280,7 +281,7 @@ grammar.
280281
DefDcl ::= DefSig [`:' Type] DefDef(_, name, tparams, vparamss, tpe, EmptyTree)
281282
DefSig ::= id [DefTypeParamClause] DefParamClauses
282283
TypeDcl ::= id [TypTypeParamClause] ['=' Type] TypeDefTree(_, name, tparams, tpt)
283-
| id [HkParamClause] TypeBounds TypeDefTree(_, name, tparams, bounds)
284+
| id [HkTypeParamClause] TypeBounds TypeDefTree(_, name, tparams, bounds)
284285

285286
Def ::= `val' PatDef
286287
| `var' VarDef

src/dotty/tools/dotc/ast/Desugar.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ object desugar {
6666
val relocate = new TypeMap {
6767
val originalOwner = sym.owner
6868
def apply(tp: Type) = tp match {
69-
case tp: NamedType if tp.symbol.owner eq originalOwner =>
69+
case tp: NamedType if tp.symbol.exists && (tp.symbol.owner eq originalOwner) =>
7070
val defctx = ctx.outersIterator.dropWhile(_.scope eq ctx.scope).next
7171
var local = defctx.denotNamed(tp.name).suchThat(_ is ParamOrAccessor).symbol
7272
if (local.exists) (defctx.owner.thisType select local).dealias

src/dotty/tools/dotc/ast/Trees.scala

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -594,6 +594,12 @@ object Trees {
594594
def forwardTo = tpt
595595
}
596596

597+
/** [typeparams] -> tpt */
598+
case class TypeLambdaTree[-T >: Untyped] private[ast] (tparams: List[TypeDef[T]], body: Tree[T])
599+
extends TypTree[T] {
600+
type ThisTree[-T >: Untyped] = TypeLambdaTree[T]
601+
}
602+
597603
/** => T */
598604
case class ByNameTypeTree[-T >: Untyped] private[ast] (result: Tree[T])
599605
extends TypTree[T] {
@@ -851,6 +857,7 @@ object Trees {
851857
type OrTypeTree = Trees.OrTypeTree[T]
852858
type RefinedTypeTree = Trees.RefinedTypeTree[T]
853859
type AppliedTypeTree = Trees.AppliedTypeTree[T]
860+
type TypeLambdaTree = Trees.TypeLambdaTree[T]
854861
type ByNameTypeTree = Trees.ByNameTypeTree[T]
855862
type TypeBoundsTree = Trees.TypeBoundsTree[T]
856863
type Bind = Trees.Bind[T]
@@ -1028,6 +1035,10 @@ object Trees {
10281035
case tree: AppliedTypeTree if (tpt eq tree.tpt) && (args eq tree.args) => tree
10291036
case _ => finalize(tree, untpd.AppliedTypeTree(tpt, args))
10301037
}
1038+
def TypeLambdaTree(tree: Tree)(tparams: List[TypeDef], body: Tree): TypeLambdaTree = tree match {
1039+
case tree: TypeLambdaTree if (tparams eq tree.tparams) && (body eq tree.body) => tree
1040+
case _ => finalize(tree, untpd.TypeLambdaTree(tparams, body))
1041+
}
10311042
def ByNameTypeTree(tree: Tree)(result: Tree): ByNameTypeTree = tree match {
10321043
case tree: ByNameTypeTree if result eq tree.result => tree
10331044
case _ => finalize(tree, untpd.ByNameTypeTree(result))
@@ -1160,6 +1171,8 @@ object Trees {
11601171
cpy.RefinedTypeTree(tree)(transform(tpt), transformSub(refinements))
11611172
case AppliedTypeTree(tpt, args) =>
11621173
cpy.AppliedTypeTree(tree)(transform(tpt), transform(args))
1174+
case TypeLambdaTree(tparams, body) =>
1175+
cpy.TypeLambdaTree(tree)(transformSub(tparams), transform(body))
11631176
case ByNameTypeTree(result) =>
11641177
cpy.ByNameTypeTree(tree)(transform(result))
11651178
case TypeBoundsTree(lo, hi) =>
@@ -1264,6 +1277,9 @@ object Trees {
12641277
this(this(x, tpt), refinements)
12651278
case AppliedTypeTree(tpt, args) =>
12661279
this(this(x, tpt), args)
1280+
case TypeLambdaTree(tparams, body) =>
1281+
implicit val ctx: Context = localCtx
1282+
this(this(x, tparams), body)
12671283
case ByNameTypeTree(result) =>
12681284
this(x, result)
12691285
case TypeBoundsTree(lo, hi) =>

src/dotty/tools/dotc/ast/tpd.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
2121
private def ta(implicit ctx: Context) = ctx.typeAssigner
2222

2323
def Modifiers(sym: Symbol)(implicit ctx: Context): Modifiers = Modifiers(
24-
sym.flags & ModifierFlags,
24+
sym.flags & (if (sym.isType) ModifierFlags | VarianceFlags else ModifierFlags),
2525
if (sym.privateWithin.exists) sym.privateWithin.asType.name else tpnme.EMPTY,
2626
sym.annotations map (_.tree))
2727

src/dotty/tools/dotc/ast/untpd.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
137137
def OrTypeTree(left: Tree, right: Tree): OrTypeTree = new OrTypeTree(left, right)
138138
def RefinedTypeTree(tpt: Tree, refinements: List[Tree]): RefinedTypeTree = new RefinedTypeTree(tpt, refinements)
139139
def AppliedTypeTree(tpt: Tree, args: List[Tree]): AppliedTypeTree = new AppliedTypeTree(tpt, args)
140+
def TypeLambdaTree(tparams: List[TypeDef], body: Tree): TypeLambdaTree = new TypeLambdaTree(tparams, body)
140141
def ByNameTypeTree(result: Tree): ByNameTypeTree = new ByNameTypeTree(result)
141142
def TypeBoundsTree(lo: Tree, hi: Tree): TypeBoundsTree = new TypeBoundsTree(lo, hi)
142143
def Bind(name: Name, body: Tree): Bind = new Bind(name, body)

src/dotty/tools/dotc/config/Config.scala

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,10 +72,9 @@ object Config {
7272
/** If this flag is set, take the fast path when comparing same-named type-aliases and types */
7373
final val fastPathForRefinedSubtype = true
7474

75-
/** If this flag is set, $apply projections are checked that they apply to a
76-
* higher-kinded type.
75+
/** If this flag is set, higher-kinded applications are checked for validity
7776
*/
78-
final val checkProjections = false
77+
final val checkHKApplications = false
7978

8079
/** The recursion depth for showing a summarized string */
8180
final val summarizeDepth = 2
@@ -98,6 +97,13 @@ object Config {
9897
*/
9998
final val splitProjections = false
10099

100+
/** If this flag is on, always rewrite an application `S[Ts]` where `S` is an alias for
101+
* `[Xs] -> U` to `[Xs := Ts]U`. If this flag is off, the rewriting is only done if `S` is a
102+
* reference to an instantiated parameter. Turning this flag on was observed to
103+
* give a ~6% speedup on the JUnit test suite.
104+
*/
105+
final val simplifyApplications = true
106+
101107
/** Initial size of superId table */
102108
final val InitialSuperIdsSize = 4096
103109

src/dotty/tools/dotc/core/Constraint.scala

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ abstract class Constraint extends Showable {
2323
type This <: Constraint
2424

2525
/** Does the constraint's domain contain the type parameters of `pt`? */
26-
def contains(pt: PolyType): Boolean
26+
def contains(pt: GenericType): Boolean
2727

2828
/** Does the constraint's domain contain the type parameter `param`? */
2929
def contains(param: PolyParam): Boolean
@@ -79,7 +79,7 @@ abstract class Constraint extends Showable {
7979
* satisfiability but will solved to give instances of
8080
* type variables.
8181
*/
82-
def add(poly: PolyType, tvars: List[TypeVar])(implicit ctx: Context): This
82+
def add(poly: GenericType, tvars: List[TypeVar])(implicit ctx: Context): This
8383

8484
/** A new constraint which is derived from this constraint by updating
8585
* the entry for parameter `param` to `tp`.
@@ -117,18 +117,17 @@ abstract class Constraint extends Showable {
117117
*/
118118
def narrowBound(param: PolyParam, bound: Type, isUpper: Boolean)(implicit ctx: Context): This
119119

120-
/** Is entry associated with `pt` removable?
121-
* @param removedParam The index of a parameter which is still present in the
122-
* entry array, but is going to be removed at the same step,
123-
* or -1 if no such parameter exists.
120+
/** Is entry associated with `pt` removable? This is the case if
121+
* all type parameters of the entry are associated with type variables
122+
* which have their `inst` fields set.
124123
*/
125-
def isRemovable(pt: PolyType, removedParam: Int = -1): Boolean
124+
def isRemovable(pt: GenericType): Boolean
126125

127126
/** A new constraint with all entries coming from `pt` removed. */
128-
def remove(pt: PolyType)(implicit ctx: Context): This
127+
def remove(pt: GenericType)(implicit ctx: Context): This
129128

130129
/** The polytypes constrained by this constraint */
131-
def domainPolys: List[PolyType]
130+
def domainPolys: List[GenericType]
132131

133132
/** The polytype parameters constrained by this constraint */
134133
def domainParams: List[PolyParam]

src/dotty/tools/dotc/core/ConstraintHandling.scala

Lines changed: 102 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import Types._, Contexts._, Symbols._
66
import Decorators._
77
import config.Config
88
import config.Printers._
9+
import TypeApplications.EtaExpansion
910
import collection.mutable
1011

1112
/** Methods for adding constraints and solving them.
@@ -34,6 +35,11 @@ trait ConstraintHandling {
3435
/** If the constraint is frozen we cannot add new bounds to the constraint. */
3536
protected var frozenConstraint = false
3637

38+
/** We are currently comparing lambdas. Used as a flag for
39+
* optimization: when `false`, no need to do an expensive `pruneLambdaParams`
40+
*/
41+
protected var comparingLambdas = false
42+
3743
private def addOneBound(param: PolyParam, bound: Type, isUpper: Boolean): Boolean =
3844
!constraint.contains(param) || {
3945
def occursIn(bound: Type): Boolean = {
@@ -163,12 +169,64 @@ trait ConstraintHandling {
163169
}
164170
}
165171
}
172+
assert(constraint.contains(param))
166173
val bound = if (fromBelow) constraint.fullLowerBound(param) else constraint.fullUpperBound(param)
167174
val inst = avoidParam(bound)
168175
typr.println(s"approx ${param.show}, from below = $fromBelow, bound = ${bound.show}, inst = ${inst.show}")
169176
inst
170177
}
171178

179+
/** The instance type of `param` in the current constraint (which contains `param`).
180+
* If `fromBelow` is true, the instance type is the lub of the parameter's
181+
* lower bounds; otherwise it is the glb of its upper bounds. However,
182+
* a lower bound instantiation can be a singleton type only if the upper bound
183+
* is also a singleton type.
184+
*/
185+
def instanceType(param: PolyParam, fromBelow: Boolean): Type = {
186+
def upperBound = constraint.fullUpperBound(param)
187+
def isSingleton(tp: Type): Boolean = tp match {
188+
case tp: SingletonType => true
189+
case AndType(tp1, tp2) => isSingleton(tp1) | isSingleton(tp2)
190+
case OrType(tp1, tp2) => isSingleton(tp1) & isSingleton(tp2)
191+
case _ => false
192+
}
193+
def isFullyDefined(tp: Type): Boolean = tp match {
194+
case tp: TypeVar => tp.isInstantiated && isFullyDefined(tp.instanceOpt)
195+
case tp: TypeProxy => isFullyDefined(tp.underlying)
196+
case tp: AndOrType => isFullyDefined(tp.tp1) && isFullyDefined(tp.tp2)
197+
case _ => true
198+
}
199+
def isOrType(tp: Type): Boolean = tp.stripTypeVar.dealias match {
200+
case tp: OrType => true
201+
case tp: RefinedOrRecType => isOrType(tp.parent)
202+
case AndType(tp1, tp2) => isOrType(tp1) | isOrType(tp2)
203+
case WildcardType(bounds: TypeBounds) => isOrType(bounds.hi)
204+
case _ => false
205+
}
206+
207+
// First, solve the constraint.
208+
var inst = approximation(param, fromBelow)
209+
210+
// Then, approximate by (1.) - (3.) and simplify as follows.
211+
// 1. If instance is from below and is a singleton type, yet
212+
// upper bound is not a singleton type, widen the instance.
213+
if (fromBelow && isSingleton(inst) && !isSingleton(upperBound))
214+
inst = inst.widen
215+
216+
inst = inst.simplified
217+
218+
// 2. If instance is from below and is a fully-defined union type, yet upper bound
219+
// is not a union type, approximate the union type from above by an intersection
220+
// of all common base types.
221+
if (fromBelow && isOrType(inst) && isFullyDefined(inst) && !isOrType(upperBound))
222+
inst = inst.approximateUnion
223+
224+
// 3. If instance is from below, and upper bound has open named parameters
225+
// make sure the instance has all named parameters of the bound.
226+
if (fromBelow) inst = inst.widenToNamedTypeParams(param.namedTypeParams)
227+
inst
228+
}
229+
172230
/** Constraint `c1` subsumes constraint `c2`, if under `c2` as constraint we have
173231
* for all poly params `p` defined in `c2` as `p >: L2 <: U2`:
174232
*
@@ -193,9 +251,9 @@ trait ConstraintHandling {
193251
}
194252

195253
/** The current bounds of type parameter `param` */
196-
final def bounds(param: PolyParam): TypeBounds = constraint.entry(param) match {
197-
case bounds: TypeBounds => bounds
198-
case _ => param.binder.paramBounds(param.paramNum)
254+
final def bounds(param: PolyParam): TypeBounds = {
255+
val e = constraint.entry(param)
256+
if (e.exists) e.bounds else param.binder.paramBounds(param.paramNum)
199257
}
200258

201259
/** Add polytype `pt`, possibly with type variables `tvars`, to current constraint
@@ -236,6 +294,36 @@ trait ConstraintHandling {
236294
checkPropagated(s"added $description") {
237295
addConstraintInvocations += 1
238296

297+
/** When comparing lambdas we might get constraints such as
298+
* `A <: X0` or `A = List[X0]` where `A` is a constrained parameter
299+
* and `X0` is a lambda parameter. The constraint for `A` is not allowed
300+
* to refer to such a lambda parameter because the lambda parameter is
301+
* not visible where `A` is defined. Consequently, we need to
302+
* approximate the bound so that the lambda parameter does not appear in it.
303+
* If `tp` is an upper bound, we need to approximate with something smaller,
304+
* otherwise something larger.
305+
* Test case in pos/i94-nada.scala. This test crashes with an illegal instance
306+
* error in Test2 when the rest of the SI-2712 fix is applied but `pruneLambdaParams` is
307+
* missing.
308+
*/
309+
def pruneLambdaParams(tp: Type) =
310+
if (comparingLambdas && param.binder.isInstanceOf[PolyType]) {
311+
val approx = new ApproximatingTypeMap {
312+
def apply(t: Type): Type = t match {
313+
case t @ PolyParam(tl: TypeLambda, n) =>
314+
val effectiveVariance = if (fromBelow) -variance else variance
315+
val bounds = tl.paramBounds(n)
316+
if (effectiveVariance > 0) bounds.lo
317+
else if (effectiveVariance < 0) bounds.hi
318+
else NoType
319+
case _ =>
320+
mapOver(t)
321+
}
322+
}
323+
approx(tp)
324+
}
325+
else tp
326+
239327
def addParamBound(bound: PolyParam) =
240328
if (fromBelow) addLess(bound, param) else addLess(param, bound)
241329

@@ -281,12 +369,18 @@ trait ConstraintHandling {
281369
else NoType
282370
case bound: TypeVar if constraint contains bound.origin =>
283371
prune(bound.underlying)
284-
case bound: PolyParam if constraint contains bound =>
285-
if (!addParamBound(bound)) NoType
286-
else if (fromBelow) defn.NothingType
287-
else defn.AnyType
372+
case bound: PolyParam =>
373+
constraint.entry(bound) match {
374+
case NoType => pruneLambdaParams(bound)
375+
case _: TypeBounds =>
376+
if (!addParamBound(bound)) NoType
377+
else if (fromBelow) defn.NothingType
378+
else defn.AnyType
379+
case inst =>
380+
prune(inst)
381+
}
288382
case _ =>
289-
bound
383+
pruneLambdaParams(bound)
290384
}
291385

292386
try bound match {

0 commit comments

Comments
 (0)