@@ -10,7 +10,7 @@ import Annotations.Annotation
10
10
import NameKinds .{UniqueName , ContextBoundParamName , ContextFunctionParamName , DefaultGetterName , WildcardParamName }
11
11
import typer .{Namer , Checking }
12
12
import util .{Property , SourceFile , SourcePosition , SrcPos , Chars }
13
- import config .Feature .{ sourceVersion , migrateTo3 , enabled }
13
+ import config .{ Feature , Config }
14
14
import config .SourceVersion .*
15
15
import collection .mutable
16
16
import reporting .*
@@ -46,6 +46,11 @@ object desugar {
46
46
*/
47
47
val UntupledParam : Property .Key [Unit ] = Property .StickyKey ()
48
48
49
+ /** An attachment key to indicate that a ValDef is an evidence parameter
50
+ * for a context bound.
51
+ */
52
+ val ContextBoundParam : Property .Key [Unit ] = Property .StickyKey ()
53
+
49
54
/** What static check should be applied to a Match? */
50
55
enum MatchCheck {
51
56
case None , Exhaustive , IrrefutablePatDef , IrrefutableGenFrom
@@ -195,17 +200,6 @@ object desugar {
195
200
else vdef1
196
201
end valDef
197
202
198
- def makeImplicitParameters (
199
- tpts : List [Tree ], implicitFlag : FlagSet ,
200
- mkParamName : Int => TermName ,
201
- forPrimaryConstructor : Boolean = false
202
- )(using Context ): List [ValDef ] =
203
- for (tpt, i) <- tpts.zipWithIndex yield {
204
- val paramFlags : FlagSet = if (forPrimaryConstructor) LocalParamAccessor else Param
205
- val epname = mkParamName(i)
206
- ValDef (epname, tpt, EmptyTree ).withFlags(paramFlags | implicitFlag)
207
- }
208
-
209
203
def mapParamss (paramss : List [ParamClause ])
210
204
(mapTypeParam : TypeDef => TypeDef )
211
205
(mapTermParam : ValDef => ValDef )(using Context ): List [ParamClause ] =
@@ -232,34 +226,57 @@ object desugar {
232
226
private def defDef (meth : DefDef , isPrimaryConstructor : Boolean = false )(using Context ): Tree =
233
227
addDefaultGetters(elimContextBounds(meth, isPrimaryConstructor))
234
228
235
- private def elimContextBounds (meth : DefDef , isPrimaryConstructor : Boolean )(using Context ): DefDef =
236
- val DefDef (_, paramss, tpt, rhs) = meth
237
- val evidenceParamBuf = mutable.ListBuffer [ValDef ]()
229
+ private def desugarContextBounds (
230
+ tdef : TypeDef ,
231
+ evidenceBuf : mutable.ListBuffer [ValDef ],
232
+ flags : FlagSet ,
233
+ freshName : untpd.Tree => TermName ,
234
+ allParamss : List [ParamClause ])(using Context ): TypeDef =
238
235
239
- var seenContextBounds : Int = 0
240
- def desugarContextBounds (rhs : Tree ): Tree = rhs match
236
+ val evidenceNames = mutable.ListBuffer [TermName ]()
237
+
238
+ def desugarRhs (rhs : Tree ): Tree = rhs match
241
239
case ContextBounds (tbounds, cxbounds) =>
242
- val iflag = if sourceVersion.isAtLeast(`future`) then Given else Implicit
243
- evidenceParamBuf ++= makeImplicitParameters(
244
- cxbounds, iflag,
245
- // Just like with `makeSyntheticParameter` on nameless parameters of
246
- // using clauses, we only need names that are unique among the
247
- // parameters of the method since shadowing does not affect
248
- // implicit resolution in Scala 3.
249
- mkParamName = i =>
250
- val index = seenContextBounds + 1 // Start at 1 like FreshNameCreator.
251
- val ret = ContextBoundParamName ( EmptyTermName , index)
252
- seenContextBounds += 1
253
- ret,
254
- forPrimaryConstructor = isPrimaryConstructor)
240
+ for bound <- cxbounds do
241
+ val evidenceName = bound match
242
+ case ContextBoundTypeTree (_, _, ownName) if ! ownName.isEmpty =>
243
+ ownName
244
+ case _ if Config .nameSingleContextBounds && cxbounds.tail.isEmpty
245
+ && Feature .enabled( Feature .modularity) =>
246
+ tdef.name.toTermName
247
+ case _ =>
248
+ freshName(bound)
249
+ evidenceNames += evidenceName
250
+ val evidenceParam = ValDef (evidenceName, bound, EmptyTree ).withFlags(flags)
251
+ evidenceParam.pushAttachment( ContextBoundParam , ())
252
+ evidenceBuf += evidenceParam
255
253
tbounds
256
254
case LambdaTypeTree (tparams, body) =>
257
- cpy.LambdaTypeTree (rhs)(tparams, desugarContextBounds (body))
255
+ cpy.LambdaTypeTree (rhs)(tparams, desugarRhs (body))
258
256
case _ =>
259
257
rhs
258
+
259
+ cpy.TypeDef (tdef)(rhs = desugarRhs(tdef.rhs))
260
+ end desugarContextBounds
261
+
262
+ private def elimContextBounds (meth : DefDef , isPrimaryConstructor : Boolean )(using Context ): DefDef =
263
+ val DefDef (_, paramss, tpt, rhs) = meth
264
+ val evidenceParamBuf = mutable.ListBuffer [ValDef ]()
265
+
266
+ var seenContextBounds : Int = 0
267
+ def freshName (unused : Tree ) =
268
+ seenContextBounds += 1 // Start at 1 like FreshNameCreator.
269
+ ContextBoundParamName (EmptyTermName , seenContextBounds)
270
+ // Just like with `makeSyntheticParameter` on nameless parameters of
271
+ // using clauses, we only need names that are unique among the
272
+ // parameters of the method since shadowing does not affect
273
+ // implicit resolution in Scala 3.
274
+
260
275
val paramssNoContextBounds =
276
+ val iflag = if Feature .sourceVersion.isAtLeast(`future`) then Given else Implicit
277
+ val flags = if isPrimaryConstructor then iflag | LocalParamAccessor else iflag | Param
261
278
mapParamss(paramss) {
262
- tparam => cpy. TypeDef (tparam)(rhs = desugarContextBounds(tparam.rhs) )
279
+ tparam => desugarContextBounds (tparam, evidenceParamBuf, flags, freshName, paramss )
263
280
}(identity)
264
281
265
282
rhs match
@@ -399,43 +416,70 @@ object desugar {
399
416
(Nil , tree)
400
417
401
418
/** Add all evidence parameters in `params` as implicit parameters to `meth`.
402
- * If the parameters of `meth` end in an implicit parameter list or using clause,
403
- * evidence parameters are added in front of that list. Otherwise they are added
404
- * as a separate parameter clause.
419
+ * The position of the added parameters is determined as follows:
420
+ *
421
+ * - If there is an existing parameter list that refers to one of the added
422
+ * parameters in one of its parameter types, add the new parameters
423
+ * in front of the first such parameter list.
424
+ * - Otherwise, if the last parameter list consists implicit or using parameters,
425
+ * join the new parameters in front of this parameter list, creating one
426
+ * parameter list (this is equilavent to Scala 2's scheme).
427
+ * - Otherwise, add the new parameter list at the end as a separate parameter clause.
405
428
*/
406
429
private def addEvidenceParams (meth : DefDef , params : List [ValDef ])(using Context ): DefDef =
407
- params match
430
+ if params.isEmpty then return meth
431
+
432
+ val boundNames = params.map(_.name).toSet
433
+
434
+ // println(i"add ev params ${meth.name}, ${boundNames.toList}")
435
+
436
+ def references (vdef : ValDef ): Boolean =
437
+ vdef.tpt.existsSubTree:
438
+ case Ident (name : TermName ) => boundNames.contains(name)
439
+ case _ => false
440
+
441
+ def recur (mparamss : List [ParamClause ]): List [ParamClause ] = mparamss match
442
+ case ValDefs (mparams) :: _ if mparams.exists(references) =>
443
+ params :: mparamss
444
+ case ValDefs (mparams @ (mparam :: _)) :: Nil if mparam.mods.isOneOf(GivenOrImplicit ) =>
445
+ (params ++ mparams) :: Nil
446
+ case mparams :: mparamss1 =>
447
+ mparams :: recur(mparamss1)
408
448
case Nil =>
409
- meth
410
- case evidenceParams =>
411
- val paramss1 = meth.paramss.reverse match
412
- case ValDefs (vparams @ (vparam :: _)) :: rparamss if vparam.mods.isOneOf(GivenOrImplicit ) =>
413
- ((evidenceParams ++ vparams) :: rparamss).reverse
414
- case _ =>
415
- meth.paramss :+ evidenceParams
416
- cpy.DefDef (meth)(paramss = paramss1)
449
+ params :: Nil
450
+
451
+ cpy.DefDef (meth)(paramss = recur(meth.paramss))
452
+ end addEvidenceParams
417
453
418
454
/** The parameters generated from the contextual bounds of `meth`, as generated by `desugar.defDef` */
419
455
private def evidenceParams (meth : DefDef )(using Context ): List [ValDef ] =
420
456
meth.paramss.reverse match {
421
457
case ValDefs (vparams @ (vparam :: _)) :: _ if vparam.mods.isOneOf(GivenOrImplicit ) =>
422
- vparams.takeWhile(_.name.is( ContextBoundParamName ))
458
+ vparams.takeWhile(_.hasAttachment( ContextBoundParam ))
423
459
case _ =>
424
460
Nil
425
461
}
426
462
427
463
@ sharable private val synthetic = Modifiers (Synthetic )
428
464
429
- private def toDefParam (tparam : TypeDef , keepAnnotations : Boolean ): TypeDef = {
430
- var mods = tparam.rawMods
431
- if (! keepAnnotations) mods = mods.withAnnotations(Nil )
465
+ /** Filter annotations in `mods` according to `keep` */
466
+ private def filterAnnots (mods : Modifiers , keep : Boolean )(using Context ) =
467
+ if keep then mods else mods.withAnnotations(Nil )
468
+
469
+ private def toDefParam (tparam : TypeDef , keepAnnotations : Boolean )(using Context ): TypeDef =
470
+ val mods = filterAnnots(tparam.rawMods, keepAnnotations)
432
471
tparam.withMods(mods & EmptyFlags | Param )
433
- }
434
- private def toDefParam (vparam : ValDef , keepAnnotations : Boolean , keepDefault : Boolean ): ValDef = {
435
- var mods = vparam.rawMods
436
- if (! keepAnnotations) mods = mods.withAnnotations(Nil )
472
+
473
+ private def toDefParam (vparam : ValDef , keepAnnotations : Boolean , keepDefault : Boolean )(using Context ): ValDef = {
474
+ val mods = filterAnnots(vparam.rawMods, keepAnnotations)
437
475
val hasDefault = if keepDefault then HasDefault else EmptyFlags
438
- vparam.withMods(mods & (GivenOrImplicit | Erased | hasDefault | Tracked ) | Param )
476
+ // Need to ensure that tree is duplicated since term parameters can be watched
477
+ // and cloning a term parameter will copy its watchers to the clone, which means
478
+ // we'd get cross-talk between the original parameter and the clone.
479
+ ValDef (vparam.name, vparam.tpt, vparam.rhs)
480
+ .withSpan(vparam.span)
481
+ .withAttachmentsFrom(vparam)
482
+ .withMods(mods & (GivenOrImplicit | Erased | hasDefault | Tracked ) | Param )
439
483
}
440
484
441
485
def mkApply (fn : Tree , paramss : List [ParamClause ])(using Context ): Tree =
@@ -609,6 +653,11 @@ object desugar {
609
653
case _ => false
610
654
}
611
655
656
+ def isRepeated (tree : Tree ): Boolean = stripByNameType(tree) match {
657
+ case PostfixOp (_, Ident (tpnme.raw.STAR )) => true
658
+ case _ => false
659
+ }
660
+
612
661
def appliedRef (tycon : Tree , tparams : List [TypeDef ] = constrTparams, widenHK : Boolean = false ) = {
613
662
val targs = for (tparam <- tparams) yield {
614
663
val targ = refOfDef(tparam)
@@ -625,11 +674,6 @@ object desugar {
625
674
appliedTypeTree(tycon, targs)
626
675
}
627
676
628
- def isRepeated (tree : Tree ): Boolean = stripByNameType(tree) match {
629
- case PostfixOp (_, Ident (tpnme.raw.STAR )) => true
630
- case _ => false
631
- }
632
-
633
677
// a reference to the class type bound by `cdef`, with type parameters coming from the constructor
634
678
val classTypeRef = appliedRef(classTycon)
635
679
@@ -667,7 +711,7 @@ object desugar {
667
711
}
668
712
ensureApplied(nu)
669
713
670
- val copiedAccessFlags = if migrateTo3 then EmptyFlags else AccessFlags
714
+ val copiedAccessFlags = if Feature . migrateTo3 then EmptyFlags else AccessFlags
671
715
672
716
// Methods to add to a case class C[..](p1: T1, ..., pN: Tn)(moreParams)
673
717
// def _1: T1 = this.p1
@@ -850,12 +894,11 @@ object desugar {
850
894
Nil
851
895
}
852
896
else {
853
- val defParamss = constrVparamss match {
897
+ val defParamss = constrVparamss match
854
898
case Nil :: paramss =>
855
899
paramss // drop leading () that got inserted by class
856
900
// TODO: drop this once we do not silently insert empty class parameters anymore
857
901
case paramss => paramss
858
- }
859
902
val finalFlag = if ctx.settings.YcompileScala2Library .value then EmptyFlags else Final
860
903
// implicit wrapper is typechecked in same scope as constructor, so
861
904
// we can reuse the constructor parameters; no derived params are needed.
@@ -1681,14 +1724,13 @@ object desugar {
1681
1724
.collect:
1682
1725
case vd : ValDef => vd
1683
1726
1684
- def makeContextualFunction (formals : List [Tree ], paramNamesOrNil : List [TermName ], body : Tree , erasedParams : List [Boolean ])(using Context ): Function = {
1685
- val mods = Given
1686
- val params = makeImplicitParameters(formals, mods,
1687
- mkParamName = i =>
1688
- if paramNamesOrNil.isEmpty then ContextFunctionParamName .fresh()
1689
- else paramNamesOrNil(i))
1690
- FunctionWithMods (params, body, Modifiers (mods), erasedParams)
1691
- }
1727
+ def makeContextualFunction (formals : List [Tree ], paramNamesOrNil : List [TermName ], body : Tree , erasedParams : List [Boolean ])(using Context ): Function =
1728
+ val paramNames =
1729
+ if paramNamesOrNil.nonEmpty then paramNamesOrNil
1730
+ else formals.map(_ => ContextFunctionParamName .fresh())
1731
+ val params = for (tpt, pname) <- formals.zip(paramNames) yield
1732
+ ValDef (pname, tpt, EmptyTree ).withFlags(Given | Param )
1733
+ FunctionWithMods (params, body, Modifiers (Given ), erasedParams)
1692
1734
1693
1735
private def derivedValDef (original : Tree , named : NameTree , tpt : Tree , rhs : Tree , mods : Modifiers )(using Context ) = {
1694
1736
val vdef = ValDef (named.name.asTermName, tpt, rhs)
0 commit comments