@@ -374,23 +374,23 @@ object Typer extends Phase[NameResolved, Typechecked] {
374
374
val declaredOperation = interface.operations.find(o => o.name.name == op.name).getOrElse {
375
375
Context .abort(pretty " Operation ${op.name} not defined in ${interface.name}. " )
376
376
}
377
- val declaredType = Context .lookupFunctionType(d.definition)
377
+ val declared = Context .lookupFunctionType(d.definition)
378
378
379
379
def assertArity (kind : String , got : Int , expected : Int ): Unit =
380
380
if (got != expected)
381
381
Context .abort(pretty " Number of ${kind} ( ${got}) does not match declaration of ' ${op.name}', which expects ${expected}. " )
382
382
383
383
// if we have zero given type parameters, we synthesize them -- no need to check then
384
- if (tparams.size != 0 ) assertArity(" type parameters" , tparams.size, declaredType .tparams.size - targs.size)
385
- assertArity(" value parameters" , vparams.size, declaredType .vparams.size)
384
+ if (tparams.nonEmpty ) assertArity(" type parameters" , tparams.size, declared .tparams.size - targs.size)
385
+ assertArity(" value parameters" , vparams.size, declared .vparams.size)
386
386
387
387
// effect E[A, B, ...] { def op[C, D, ...]() = ... } !--> op[A, B, ..., C, D, ...]
388
388
// The parameters C, D, ... are existentials
389
- val existentialParams : List [TypeVar ] = if (tparams.size == declaredType .tparams.size - targs.size) {
389
+ val existentialParams : List [TypeVar ] = if (tparams.size == declared .tparams.size - targs.size) {
390
390
tparams.map { tparam => tparam.symbol.asTypeParam }
391
391
} else {
392
392
// using the invariant that the universals are prepended to type parameters of the operation
393
- declaredType .tparams.drop(targs.size).map { tp =>
393
+ declared .tparams.drop(targs.size).map { tp =>
394
394
// recreate "fresh" type variables
395
395
val name = tp.name
396
396
TypeVar .TypeParam (name)
@@ -400,23 +400,24 @@ object Typer extends Phase[NameResolved, Typechecked] {
400
400
401
401
Context .annotate(Annotations .TypeParameters , d, existentialParams)
402
402
403
- val canonicalEffects = declaredType.effects.canonical
403
+ // canonical ordering on annotated effects
404
+ val canonical = CanonicalOrdering (declared.effects.toList)
404
405
405
406
// distinguish between handler operation or object operation (which does not capture a cont.)
406
407
val Result (_, effs) = continuationDetails match {
407
408
// normal object: no continuation there
408
409
case None =>
409
410
// block parameters are to be bound by the definition itself instead of by resume when using handlers
410
- assertArity(" block parameters" , bparams.size, declaredType .bparams.size)
411
+ assertArity(" block parameters" , bparams.size, declared .bparams.size)
411
412
412
413
val cparamsForBlocks = bparams.map { p => p.symbol.capture }
413
414
// will be introduced as capabilities in a later phase
414
- val cparamsForEffects = canonicalEffects .map { tpe => CaptureParam (tpe.name) }
415
+ val cparamsForEffects = canonical .map { tpe => CaptureParam (tpe.name) }
415
416
val cparams = cparamsForBlocks ++ cparamsForEffects
416
417
417
418
// substitute actual type parameter and capture parameters for declared ones
418
- val FunctionType ( Nil , Nil , vps, bps, tpe, effs) =
419
- Context .instantiate(declaredType , targs ++ existentials, cparams.map(cap => CaptureSet (cap))) : @ unchecked
419
+ val ( vps, bps, tpe, effs) =
420
+ Context .instantiate(declared , targs ++ existentials, cparams.map(cap => CaptureSet (cap))) : @ unchecked
420
421
421
422
(vparams zip vps).foreach {
422
423
case (param, declaredType) =>
@@ -435,29 +436,29 @@ object Typer extends Phase[NameResolved, Typechecked] {
435
436
}
436
437
437
438
// these capabilities are later introduced as parameters in capability passing
438
- val capabilities = (effs.canonical zip cparamsForEffects).map {
439
+ val capabilities = (CanonicalOrdering ( effs) zip cparamsForEffects).map {
439
440
case (tpe, capt) => Context .freshCapabilityFor(tpe, CaptureSet (capt))
440
441
}
441
442
442
443
val Result (bodyType, bodyEffs) = Context .bindingCapabilities(d, capabilities) {
443
444
body checkAgainst tpe
444
445
}
445
- Result (bodyType, bodyEffs -- effs)
446
+ Result (bodyType, bodyEffs -- Effects ( effs) )
446
447
447
448
// handler implementation: we have a continuation
448
449
case Some (ret, continuationCapt) =>
449
450
450
451
if (bparams.nonEmpty)
451
452
Context .error(" Block parameters are bound by resume and not the effect operation itself" )
452
453
453
- def isBidirectional = canonicalEffects .nonEmpty || declaredType .bparams.nonEmpty
454
+ def isBidirectional = canonical .nonEmpty || declared .bparams.nonEmpty
454
455
455
456
val cparamsForBlocks = declaredOperation.bparams.map { p => CaptureParam (p.name) } // use the original name
456
- val cparamsForEffects = canonicalEffects .map { tpe => CaptureParam (tpe.name) } // use the type name
457
+ val cparamsForEffects = canonical .map { tpe => CaptureParam (tpe.name) } // use the type name
457
458
val cparams = cparamsForBlocks ++ cparamsForEffects
458
459
459
- val FunctionType ( Nil , Nil , vps, bps, tpe, effs) =
460
- Context .instantiate(declaredType , targs ++ existentials, cparams.map(cap => CaptureSet (cap))) : @ unchecked
460
+ val ( vps, bps, tpe, effs) =
461
+ Context .instantiate(declared , targs ++ existentials, cparams.map(cap => CaptureSet (cap))) : @ unchecked
461
462
462
463
(vparams zip vps).foreach {
463
464
case (param, declaredType) =>
@@ -470,7 +471,7 @@ object Typer extends Phase[NameResolved, Typechecked] {
470
471
// (4) synthesize type of continuation
471
472
val resumeType = if (isBidirectional) {
472
473
// resume { {f} => e }
473
- val resumeType = FunctionType (Nil , cparams, Nil , bps, tpe, effs)
474
+ val resumeType = FunctionType (Nil , cparams, Nil , bps, tpe, Effects ( effs) )
474
475
val resumeCapt = CaptureParam (Name .local(" resumeBlock" ))
475
476
FunctionType (Nil , List (resumeCapt), Nil , List (resumeType), ret, Effects .Pure )
476
477
} else {
@@ -574,7 +575,7 @@ object Typer extends Phase[NameResolved, Typechecked] {
574
575
575
576
// (4) Compute blocktype of this constructor with rigid type vars
576
577
// i.e. Cons : `(?t1, List[?t1]) => List[?t1]`
577
- val FunctionType (_, _, vps, _, ret, _) = Context .instantiate(sym.toType, targs, Nil )
578
+ val ( vps, _, ret, _) = Context .instantiate(sym.toType, targs, Nil )
578
579
579
580
// (5) given a scrutinee of `List[Int]`, we learn `?t1 -> Int`
580
581
matchPattern(sc, ret, p)
@@ -1205,7 +1206,7 @@ object Typer extends Phase[NameResolved, Typechecked] {
1205
1206
1206
1207
// (1) Instantiate blocktype
1207
1208
// e.g. `[A, B] (A, A) => B` becomes `(?A, ?A) => ?B`
1208
- val (typeArgs, captArgs, bt @ FunctionType (_, _, vps, bps, ret, retEffs)) = Context .instantiateFresh(funTpe)
1209
+ val (typeArgs, captArgs, ( vps, bps, ret, retEffs)) = Context .instantiateFresh(CanonicalOrdering ( funTpe) )
1209
1210
1210
1211
// provided type arguments flow into the fresh unification variables (i.e., Int <: ?A)
1211
1212
if (targs.nonEmpty) (targs zip typeArgs).foreach { case (targ, tvar) => matchExpected(tvar, targ) }
@@ -1240,22 +1241,16 @@ object Typer extends Phase[NameResolved, Typechecked] {
1240
1241
1241
1242
// We add return effects last to have more information at this point to
1242
1243
// concretize the effect.
1243
- effs = effs ++ retEffs
1244
+ effs = effs ++ Effects ( retEffs)
1244
1245
1245
1246
// annotate call node with inferred type arguments
1246
1247
Context .annotateTypeArgs(call, typeArgs)
1247
1248
1248
1249
// Annotate the call target tree with the additional capabilities
1249
- // We need to establish the canonical ordering of capabilities.
1250
- // 1) we have to use the capabilities, which are annotated on the original function type
1251
- // 2) we need to dealias
1252
- // 3) we need to compute distinct effects on the dealiased list
1253
- // 3) and only then substitute
1254
- //
1255
- // This is important since
1256
- // [A, B](): Unit / { State[A], State[B] }
1257
- // with A := Int and B := Int requires us to pass two capabilities.
1258
- val capabilities = Context .provideCapabilities(call, retEffs.canonical.map(Context .unification.apply))
1250
+ // The canonical ordering of capabilities is defined by the ordering of the definition site,
1251
+ // not the callsite, so we need to be careful to not use `distinct` on `retEffs`
1252
+ // since this might conflate State[A] and State[B] after A -> Int, B -> Int.
1253
+ val capabilities = Context .provideCapabilities(call, retEffs.map(Context .unification.apply))
1259
1254
1260
1255
val captParams = captArgs.drop(bargs.size)
1261
1256
(captParams zip capabilities) foreach { case (param, cap) =>
0 commit comments