Skip to content

Commit 4a103c7

Browse files
committed
Tweaks after review
1 parent 84c43ed commit 4a103c7

File tree

21 files changed

+176
-118
lines changed

21 files changed

+176
-118
lines changed

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

+54-40
Original file line numberDiff line numberDiff line change
@@ -226,29 +226,40 @@ object desugar {
226226
private def defDef(meth: DefDef, isPrimaryConstructor: Boolean = false)(using Context): Tree =
227227
addDefaultGetters(elimContextBounds(meth, isPrimaryConstructor))
228228

229+
/** Drop context bounds in given TypeDef, replacing them with evidence ValDefs that
230+
* get added to a buffer.
231+
* @param tdef The given TypeDef
232+
* @param evidenceBuf The buffer to which evidence gets added. This buffer
233+
* is shared between desugarings of different type parameters
234+
* of the same method.
235+
* @param evidenceFlags The flags to use for evidence definitions
236+
* @param freshName A function to generate fresh names for evidence definitions
237+
* @param allParams If `tdef` is a type paramter, all parameters of the owning method,
238+
* otherwise the empty list.
239+
*/
229240
private def desugarContextBounds(
230241
tdef: TypeDef,
231242
evidenceBuf: ListBuffer[ValDef],
232-
flags: FlagSet,
243+
evidenceFlags: FlagSet,
233244
freshName: untpd.Tree => TermName,
234245
allParamss: List[ParamClause])(using Context): TypeDef =
235246

236247
val evidenceNames = ListBuffer[TermName]()
237248

238249
def desugarRhs(rhs: Tree): Tree = rhs match
239250
case ContextBounds(tbounds, cxbounds) =>
240-
val isMember = flags.isAllOf(DeferredGivenFlags)
251+
val isMember = evidenceFlags.isAllOf(DeferredGivenFlags)
241252
for bound <- cxbounds do
242253
val evidenceName = bound match
243254
case ContextBoundTypeTree(_, _, ownName) if !ownName.isEmpty =>
244-
ownName
255+
ownName // if there is an explicitly given name, use it.
245256
case _ if Config.nameSingleContextBounds && !isMember
246257
&& cxbounds.tail.isEmpty && Feature.enabled(Feature.modularity) =>
247258
tdef.name.toTermName
248259
case _ =>
249260
freshName(bound)
250261
evidenceNames += evidenceName
251-
val evidenceParam = ValDef(evidenceName, bound, EmptyTree).withFlags(flags)
262+
val evidenceParam = ValDef(evidenceName, bound, EmptyTree).withFlags(evidenceFlags)
252263
evidenceParam.pushAttachment(ContextBoundParam, ())
253264
evidenceBuf += evidenceParam
254265
tbounds
@@ -258,9 +269,13 @@ object desugar {
258269
rhs
259270

260271
val tdef1 = cpy.TypeDef(tdef)(rhs = desugarRhs(tdef.rhs))
272+
// Under x.modularity, if there was a context bound, and `tdef`s name as a term name is
273+
// neither a name of an existing parameter nor a name of generated evidence for
274+
// the same method, add a WitnessAnnotation with all generated evidence names to `tdef`.
275+
// This means a context bound proxy will be created later.
261276
if Feature.enabled(Feature.modularity)
262277
&& evidenceNames.nonEmpty
263-
&& !evidenceNames.contains(tdef.name.toTermName)
278+
&& !evidenceBuf.exists(_.name == tdef.name.toTermName)
264279
&& !allParamss.nestedExists(_.name == tdef.name.toTermName)
265280
then
266281
tdef1.withAddedAnnotation:
@@ -272,6 +287,7 @@ object desugar {
272287
private def elimContextBounds(meth: DefDef, isPrimaryConstructor: Boolean)(using Context): DefDef =
273288
val DefDef(_, paramss, tpt, rhs) = meth
274289
val evidenceParamBuf = ListBuffer[ValDef]()
290+
275291
var seenContextBounds: Int = 0
276292
def freshName(unused: Tree) =
277293
seenContextBounds += 1 // Start at 1 like FreshNameCreator.
@@ -331,9 +347,9 @@ object desugar {
331347

332348
def getterParamss(n: Int): List[ParamClause] =
333349
mapParamss(takeUpTo(paramssNoRHS, n)) {
334-
tparam => dropContextBounds(toDefParam(tparam, KeepAnnotations.All))
350+
tparam => dropContextBounds(toMethParam(tparam, KeepAnnotations.All))
335351
} {
336-
vparam => toDefParam(vparam, KeepAnnotations.All, keepDefault = false)
352+
vparam => toMethParam(vparam, KeepAnnotations.All, keepDefault = false)
337353
}
338354

339355
def defaultGetters(paramss: List[ParamClause], n: Int): List[DefDef] = paramss match
@@ -428,32 +444,30 @@ object desugar {
428444
* The position of the added parameters is determined as follows:
429445
*
430446
* - If there is an existing parameter list that refers to one of the added
431-
* parameters in one of its parameter types, add the new parameters
432-
* in front of the first such parameter list.
433-
* - Otherwise, if the last parameter list consists implicit or using parameters,
447+
* parameters or their future context bound proxies in one of its parameter
448+
* types, add the new parameters in front of the first such parameter list.
449+
* - Otherwise, if the last parameter list consists of implicit or using parameters,
434450
* join the new parameters in front of this parameter list, creating one
435-
* parameter list (this is equilavent to Scala 2's scheme).
451+
* parameter list (this is equivalent to Scala 2's scheme).
436452
* - Otherwise, add the new parameter list at the end as a separate parameter clause.
437453
*/
438454
private def addEvidenceParams(meth: DefDef, params: List[ValDef])(using Context): DefDef =
439455
if params.isEmpty then return meth
440456

441-
var boundNames = params.map(_.name).toSet
457+
var boundNames = params.map(_.name).toSet // all evidence parameter + context bound proxy names
442458
for mparams <- meth.paramss; mparam <- mparams do
443459
mparam match
444460
case tparam: TypeDef if tparam.mods.annotations.exists(WitnessNamesAnnot.unapply(_).isDefined) =>
445461
boundNames += tparam.name.toTermName
446462
case _ =>
447463

448-
//println(i"add ev params ${meth.name}, ${boundNames.toList}")
449-
450-
def references(vdef: ValDef): Boolean =
464+
def referencesBoundName(vdef: ValDef): Boolean =
451465
vdef.tpt.existsSubTree:
452466
case Ident(name: TermName) => boundNames.contains(name)
453467
case _ => false
454468

455469
def recur(mparamss: List[ParamClause]): List[ParamClause] = mparamss match
456-
case ValDefs(mparams) :: _ if mparams.exists(references) =>
470+
case ValDefs(mparams) :: _ if mparams.exists(referencesBoundName) =>
457471
params :: mparamss
458472
case ValDefs(mparams @ (mparam :: _)) :: Nil if mparam.mods.isOneOf(GivenOrImplicit) =>
459473
(params ++ mparams) :: Nil
@@ -467,12 +481,12 @@ object desugar {
467481

468482
/** The parameters generated from the contextual bounds of `meth`, as generated by `desugar.defDef` */
469483
private def evidenceParams(meth: DefDef)(using Context): List[ValDef] =
470-
meth.paramss.reverse match {
471-
case ValDefs(vparams @ (vparam :: _)) :: _ if vparam.mods.isOneOf(GivenOrImplicit) =>
472-
vparams.takeWhile(_.hasAttachment(ContextBoundParam))
473-
case _ =>
474-
Nil
475-
}
484+
for
485+
case ValDefs(vparams @ (vparam :: _)) <- meth.paramss
486+
if vparam.mods.isOneOf(GivenOrImplicit)
487+
param <- vparams.takeWhile(_.hasAttachment(ContextBoundParam))
488+
yield
489+
param
476490

477491
@sharable private val synthetic = Modifiers(Synthetic)
478492

@@ -490,11 +504,13 @@ object desugar {
490504
case WitnessNamesAnnot(_) => true
491505
case _ => false
492506

493-
private def toDefParam(tparam: TypeDef, keep: KeepAnnotations)(using Context): TypeDef =
507+
/** Map type parameter accessor to corresponding method (i.e. constructor) parameter */
508+
private def toMethParam(tparam: TypeDef, keep: KeepAnnotations)(using Context): TypeDef =
494509
val mods = filterAnnots(tparam.rawMods, keep)
495510
tparam.withMods(mods & EmptyFlags | Param)
496511

497-
private def toDefParam(vparam: ValDef, keep: KeepAnnotations, keepDefault: Boolean)(using Context): ValDef = {
512+
/** Map term parameter accessor to corresponding method (i.e. constructor) parameter */
513+
private def toMethParam(vparam: ValDef, keep: KeepAnnotations, keepDefault: Boolean)(using Context): ValDef = {
498514
val mods = filterAnnots(vparam.rawMods, keep)
499515
val hasDefault = if keepDefault then HasDefault else EmptyFlags
500516
// Need to ensure that tree is duplicated since term parameters can be watched
@@ -506,22 +522,16 @@ object desugar {
506522
.withMods(mods & (GivenOrImplicit | Erased | hasDefault | Tracked) | Param)
507523
}
508524

509-
def mkApply(fn: Tree, paramss: List[ParamClause])(using Context): Tree =
510-
paramss.foldLeft(fn) { (fn, params) => params match
511-
case TypeDefs(params) =>
512-
TypeApply(fn, params.map(refOfDef))
513-
case (vparam: ValDef) :: _ if vparam.mods.is(Given) =>
514-
Apply(fn, params.map(refOfDef)).setApplyKind(ApplyKind.Using)
515-
case _ =>
516-
Apply(fn, params.map(refOfDef))
517-
}
518-
525+
/** Desugar type def (not param): Under x.moduliity this can expand
526+
* context bounds, which are expanded to evidence ValDefs. These will
527+
* ultimately map to deferred givens.
528+
*/
519529
def typeDef(tdef: TypeDef)(using Context): Tree =
520530
val evidenceBuf = new ListBuffer[ValDef]
521531
val result = desugarContextBounds(
522532
tdef, evidenceBuf,
523533
(tdef.mods.flags.toTermFlags & AccessFlags) | Lazy | DeferredGivenFlags,
524-
inventGivenOrExtensionName, Nil)
534+
inventGivenName, Nil)
525535
if evidenceBuf.isEmpty then result else Thicket(result :: evidenceBuf.toList)
526536

527537
/** The expansion of a class definition. See inline comments for what is involved */
@@ -596,7 +606,7 @@ object desugar {
596606
// Annotations on class _type_ parameters are set on the derived parameters
597607
// but not on the constructor parameters. The reverse is true for
598608
// annotations on class _value_ parameters.
599-
val constrTparams = impliedTparams.map(toDefParam(_, KeepAnnotations.WitnessOnly))
609+
val constrTparams = impliedTparams.map(toMethParam(_, KeepAnnotations.WitnessOnly))
600610
val constrVparamss =
601611
if (originalVparamss.isEmpty) { // ensure parameter list is non-empty
602612
if (isCaseClass)
@@ -607,7 +617,7 @@ object desugar {
607617
report.error(CaseClassMissingNonImplicitParamList(cdef), namePos)
608618
ListOfNil
609619
}
610-
else originalVparamss.nestedMap(toDefParam(_, KeepAnnotations.All, keepDefault = true))
620+
else originalVparamss.nestedMap(toMethParam(_, KeepAnnotations.All, keepDefault = true))
611621
val derivedTparams =
612622
constrTparams.zipWithConserve(impliedTparams)((tparam, impliedParam) =>
613623
derivedTypeParam(tparam).withAnnotations(impliedParam.mods.annotations))
@@ -629,7 +639,7 @@ object desugar {
629639
defDef(
630640
addEvidenceParams(
631641
cpy.DefDef(ddef)(paramss = joinParams(constrTparams, ddef.paramss)),
632-
evidenceParams(constr1).map(toDefParam(_, KeepAnnotations.None, keepDefault = false)))))
642+
evidenceParams(constr1).map(toMethParam(_, KeepAnnotations.None, keepDefault = false)))))
633643
case stat =>
634644
stat
635645
}
@@ -1147,7 +1157,7 @@ object desugar {
11471157
*/
11481158
def normalizeName(mdef: MemberDef, impl: Tree)(using Context): Name = {
11491159
var name = mdef.name
1150-
if (name.isEmpty) name = name.likeSpaced(inventGivenOrExtensionName(impl))
1160+
if (name.isEmpty) name = name.likeSpaced(inventGivenName(impl))
11511161
def errPos = mdef.source.atSpan(mdef.nameSpan)
11521162
if (ctx.owner == defn.ScalaPackageClass && defn.reservedScalaClassNames.contains(name.toTypeName)) {
11531163
val kind = if (name.isTypeName) "class" else "object"
@@ -1194,7 +1204,7 @@ object desugar {
11941204
end makePolyFunctionType
11951205

11961206
/** Invent a name for an anonympus given of type or template `impl`. */
1197-
def inventGivenOrExtensionName(impl: Tree)(using Context): SimpleName =
1207+
def inventGivenName(impl: Tree)(using Context): SimpleName =
11981208
val str = impl match
11991209
case impl: Template =>
12001210
if impl.parents.isEmpty then
@@ -1206,6 +1216,10 @@ object desugar {
12061216
"given_" ++ inventTypeName(impl)
12071217
str.toTermName.asSimpleName
12081218

1219+
/** Extract a synthesized given name from a type tree. This is used for
1220+
* both anonymous givens and (under x.modularity) deferred givens.
1221+
* @param followArgs If true include argument types in the name
1222+
*/
12091223
private class NameExtractor(followArgs: Boolean) extends UntypedTreeAccumulator[String] {
12101224
private def extractArgs(args: List[Tree])(using Context): String =
12111225
args.map(argNameExtractor.apply("", _)).mkString("_")

compiler/src/dotty/tools/dotc/ast/TreeInfo.scala

+4-4
Original file line numberDiff line numberDiff line change
@@ -378,15 +378,15 @@ trait TreeInfo[T <: Untyped] { self: Trees.Instance[T] =>
378378
case _ =>
379379
tree.tpe.isInstanceOf[ThisType]
380380
}
381-
382-
/** Extractor for annotation.internal.WitnessNames(name_1, ..., name_n)`
381+
382+
/** Under x.modularity: Extractor for `annotation.internal.WitnessNames(name_1, ..., name_n)`
383383
* represented as an untyped or typed tree.
384384
*/
385385
object WitnessNamesAnnot:
386-
def apply(names0: List[TermName])(using Context): untpd.Tree =
386+
def apply(names: List[TermName])(using Context): untpd.Tree =
387387
untpd.TypedSplice(tpd.New(
388388
defn.WitnessNamesAnnot.typeRef,
389-
tpd.SeqLiteral(names0.map(n => tpd.Literal(Constant(n.toString))), tpd.TypeTree(defn.StringType)) :: Nil
389+
tpd.SeqLiteral(names.map(n => tpd.Literal(Constant(n.toString))), tpd.TypeTree(defn.StringType)) :: Nil
390390
))
391391

392392
def unapply(tree: Tree)(using Context): Option[List[TermName]] =

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

+1
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
119119
case class PatDef(mods: Modifiers, pats: List[Tree], tpt: Tree, rhs: Tree)(implicit @constructorOnly src: SourceFile) extends DefTree
120120
case class ExtMethods(paramss: List[ParamClause], methods: List[Tree])(implicit @constructorOnly src: SourceFile) extends Tree
121121
case class ContextBoundTypeTree(tycon: Tree, paramName: TypeName, ownName: TermName)(implicit @constructorOnly src: SourceFile) extends Tree
122+
// `paramName: tycon as ownName`, ownName != EmptyTermName only under x.modularity
122123
case class MacroTree(expr: Tree)(implicit @constructorOnly src: SourceFile) extends Tree
123124

124125
case class ImportSelector(imported: Ident, renamed: Tree = EmptyTree, bound: Tree = EmptyTree)(implicit @constructorOnly src: SourceFile) extends Tree {

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

+2-1
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,8 @@ object Config {
236236
inline val checkLevelsOnConstraints = false
237237
inline val checkLevelsOnInstantiation = true
238238

239-
/** If a type parameter `X` has a single context bound `X: C`, should the
239+
/** Under x.modularity:
240+
* If a type parameter `X` has a single context bound `X: C`, should the
240241
* witness parameter be named `X`? This would prevent the creation of a
241242
* context bound companion.
242243
*/

compiler/src/dotty/tools/dotc/core/Mode.scala

+2-2
Original file line numberDiff line numberDiff line change
@@ -104,8 +104,8 @@ object Mode {
104104
val CheckBoundsOrSelfType: Mode = newMode(14, "CheckBoundsOrSelfType")
105105

106106
/** Use previous Scheme for implicit resolution. Currently significant
107-
* in 3.0-migration where we use Scala-2's scheme instead and in 3.5-migration
108-
* where we use the previous scheme up to 3.4 instead.
107+
* in 3.0-migration where we use Scala-2's scheme instead and in 3.5 and 3.6-migration
108+
* where we use the previous scheme up to 3.4 for comparison with the new scheme.
109109
*/
110110
val OldImplicitResolution: Mode = newMode(15, "OldImplicitResolution")
111111

compiler/src/dotty/tools/dotc/core/NamerOps.scala

+4-4
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,9 @@ object NamerOps:
2424
addParamRefinements(ctor.owner.typeRef, paramss)
2525

2626
/** Given a method with tracked term-parameters `p1, ..., pn`, and result type `R`, add the
27-
* refinements R { p1 = p1' } ... { pn = pn' }, where pi' is the term parameter ref
27+
* refinements R { p1 = p1' } ... { pn = pn' }, where pi' is the TermParamRef
2828
* of the parameter and pi is its name. This matters only under experimental.modularity,
29-
* since wothout it there are no tracked parameters. Parameter refinements are added for
29+
* since without it there are no tracked parameters. Parameter refinements are added for
3030
* constructors and given companion methods.
3131
*/
3232
def addParamRefinements(resType: Type, paramss: List[List[Symbol]])(using Context): Type =
@@ -261,7 +261,7 @@ object NamerOps:
261261
/** Create a context-bound companion for type symbol `tsym`, which has a context
262262
* bound that defines a set of witnesses with names `witnessNames`.
263263
*
264-
* @param parans If `tsym` is a type parameter, a list of parameter symbols
264+
* @param params If `tsym` is a type parameter, a list of parameter symbols
265265
* that include all witnesses, otherwise the empty list.
266266
*
267267
* The context-bound companion has as name the name of `tsym` translated to
@@ -299,7 +299,7 @@ object NamerOps:
299299
* this class. This assumes that these types already have their
300300
* WitnessNames annotation set even before they are completed. This is
301301
* the case for unpickling but currently not for Namer. So the method
302-
* is only called during unpickling, and is not part of NamerOps.
302+
* is only called during unpickling.
303303
*/
304304
def addContextBoundCompanions(cls: ClassSymbol)(using Context): Unit =
305305
for sym <- cls.info.decls do

compiler/src/dotty/tools/dotc/core/SymDenotations.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -1194,7 +1194,7 @@ object SymDenotations {
11941194
|| is(JavaDefinedVal, butNot = Method)
11951195
|| isConstructor
11961196
|| !owner.isExtensibleClass && !is(Deferred)
1197-
// Deferred symbols can arise through parent refinements.
1197+
// Deferred symbols can arise through parent refinements under x.modularity.
11981198
// For them, the overriding relationship reverses anyway, so
11991199
// being in a final class does not mean the symbol cannot be
12001200
// implemented concretely in a superclass.

compiler/src/dotty/tools/dotc/core/Types.scala

+9-2
Original file line numberDiff line numberDiff line change
@@ -1654,7 +1654,7 @@ object Types extends TypeUtils {
16541654
*
16551655
* P { ... type T = / += / -= U ... } # T
16561656
*
1657-
* to just U. Analogously, `P { val x: S} # x` is reduced tp `S` is `S`
1657+
* to just U. Analogously, `P { val x: S} # x` is reduced tp `S` if `S`
16581658
* is a singleton type.
16591659
*
16601660
* Does not perform the reduction if the resulting type would contain
@@ -4935,6 +4935,7 @@ object Types extends TypeUtils {
49354935
* @param origin the parameter that's tracked by the type variable.
49364936
* @param creatorState the typer state in which the variable was created.
49374937
* @param initNestingLevel the initial nesting level of the type variable. (c.f. nestingLevel)
4938+
* @param precise whether we should use instantiation without widening for this TypeVar.
49384939
*/
49394940
final class TypeVar private(
49404941
initOrigin: TypeParamRef,
@@ -5044,6 +5045,9 @@ object Types extends TypeUtils {
50445045
else
50455046
instantiateWith(tp)
50465047

5048+
/** Should we suppress widening? True if this TypeVar is precise
5049+
* or if it has as an upper bound a precise TypeVar.
5050+
*/
50475051
def isPrecise(using Context) =
50485052
precise
50495053
|| {
@@ -5054,7 +5058,9 @@ object Types extends TypeUtils {
50545058
case _ => false
50555059
}
50565060

5057-
/** Widen unions when instantiating this variable in the current context? */
5061+
/** The policy used for widening singletons or unions when instantiating
5062+
* this variable in the current context.
5063+
*/
50585064
def widenPolicy(using Context): Widen =
50595065
if isPrecise then Widen.None
50605066
else if ctx.typerState.constraint.isHard(this) then Widen.Singletons
@@ -5106,6 +5112,7 @@ object Types extends TypeUtils {
51065112
precise: Boolean = false) =
51075113
new TypeVar(initOrigin, creatorState, nestingLevel, precise)
51085114

5115+
/** The three possible widening policies */
51095116
enum Widen:
51105117
case None // no widening
51115118
case Singletons // widen singletons but not unions

0 commit comments

Comments
 (0)