Skip to content

Commit 21f5e67

Browse files
committed
Tweaks after review
1 parent fd072dc commit 21f5e67

File tree

21 files changed

+175
-118
lines changed

21 files changed

+175
-118
lines changed

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

Lines changed: 53 additions & 40 deletions
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: mutable.ListBuffer[ValDef],
232-
flags: FlagSet,
243+
evidenceFlags: FlagSet,
233244
freshName: untpd.Tree => TermName,
234245
allParamss: List[ParamClause])(using Context): TypeDef =
235246

236247
val evidenceNames = mutable.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:
@@ -332,9 +347,9 @@ object desugar {
332347

333348
def getterParamss(n: Int): List[ParamClause] =
334349
mapParamss(takeUpTo(paramssNoRHS, n)) {
335-
tparam => dropContextBounds(toDefParam(tparam, KeepAnnotations.All))
350+
tparam => dropContextBounds(toMethParam(tparam, KeepAnnotations.All))
336351
} {
337-
vparam => toDefParam(vparam, KeepAnnotations.All, keepDefault = false)
352+
vparam => toMethParam(vparam, KeepAnnotations.All, keepDefault = false)
338353
}
339354

340355
def defaultGetters(paramss: List[ParamClause], n: Int): List[DefDef] = paramss match
@@ -429,32 +444,30 @@ object desugar {
429444
* The position of the added parameters is determined as follows:
430445
*
431446
* - If there is an existing parameter list that refers to one of the added
432-
* parameters in one of its parameter types, add the new parameters
433-
* in front of the first such parameter list.
434-
* - 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,
435450
* join the new parameters in front of this parameter list, creating one
436-
* parameter list (this is equilavent to Scala 2's scheme).
451+
* parameter list (this is equivalent to Scala 2's scheme).
437452
* - Otherwise, add the new parameter list at the end as a separate parameter clause.
438453
*/
439454
private def addEvidenceParams(meth: DefDef, params: List[ValDef])(using Context): DefDef =
440455
if params.isEmpty then return meth
441456

442-
var boundNames = params.map(_.name).toSet
457+
var boundNames = params.map(_.name).toSet // all evidence parameter + context bound proxy names
443458
for mparams <- meth.paramss; mparam <- mparams do
444459
mparam match
445460
case tparam: TypeDef if tparam.mods.annotations.exists(WitnessNamesAnnot.unapply(_).isDefined) =>
446461
boundNames += tparam.name.toTermName
447462
case _ =>
448463

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

456469
def recur(mparamss: List[ParamClause]): List[ParamClause] = mparamss match
457-
case ValDefs(mparams) :: _ if mparams.exists(references) =>
470+
case ValDefs(mparams) :: _ if mparams.exists(referencesBoundName) =>
458471
params :: mparamss
459472
case ValDefs(mparams @ (mparam :: _)) :: Nil if mparam.mods.isOneOf(GivenOrImplicit) =>
460473
(params ++ mparams) :: Nil
@@ -468,12 +481,12 @@ object desugar {
468481

469482
/** The parameters generated from the contextual bounds of `meth`, as generated by `desugar.defDef` */
470483
private def evidenceParams(meth: DefDef)(using Context): List[ValDef] =
471-
meth.paramss.reverse match {
472-
case ValDefs(vparams @ (vparam :: _)) :: _ if vparam.mods.isOneOf(GivenOrImplicit) =>
473-
vparams.takeWhile(_.hasAttachment(ContextBoundParam))
474-
case _ =>
475-
Nil
476-
}
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
477490

478491
@sharable private val synthetic = Modifiers(Synthetic)
479492

@@ -491,11 +504,13 @@ object desugar {
491504
case WitnessNamesAnnot(_) => true
492505
case _ => false
493506

494-
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 =
495509
val mods = filterAnnots(tparam.rawMods, keep)
496510
tparam.withMods(mods & EmptyFlags | Param)
497511

498-
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 = {
499514
val mods = filterAnnots(vparam.rawMods, keep)
500515
val hasDefault = if keepDefault then HasDefault else EmptyFlags
501516
// Need to ensure that tree is duplicated since term parameters can be watched
@@ -507,22 +522,16 @@ object desugar {
507522
.withMods(mods & (GivenOrImplicit | Erased | hasDefault | Tracked) | Param)
508523
}
509524

510-
def mkApply(fn: Tree, paramss: List[ParamClause])(using Context): Tree =
511-
paramss.foldLeft(fn) { (fn, params) => params match
512-
case TypeDefs(params) =>
513-
TypeApply(fn, params.map(refOfDef))
514-
case (vparam: ValDef) :: _ if vparam.mods.is(Given) =>
515-
Apply(fn, params.map(refOfDef)).setApplyKind(ApplyKind.Using)
516-
case _ =>
517-
Apply(fn, params.map(refOfDef))
518-
}
519-
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+
*/
520529
def typeDef(tdef: TypeDef)(using Context): Tree =
521530
val evidenceBuf = new mutable.ListBuffer[ValDef]
522531
val result = desugarContextBounds(
523532
tdef, evidenceBuf,
524533
(tdef.mods.flags.toTermFlags & AccessFlags) | Lazy | DeferredGivenFlags,
525-
inventGivenOrExtensionName, Nil)
534+
inventGivenName, Nil)
526535
if evidenceBuf.isEmpty then result else Thicket(result :: evidenceBuf.toList)
527536

528537
/** The expansion of a class definition. See inline comments for what is involved */
@@ -597,7 +606,7 @@ object desugar {
597606
// Annotations on class _type_ parameters are set on the derived parameters
598607
// but not on the constructor parameters. The reverse is true for
599608
// annotations on class _value_ parameters.
600-
val constrTparams = impliedTparams.map(toDefParam(_, KeepAnnotations.WitnessOnly))
609+
val constrTparams = impliedTparams.map(toMethParam(_, KeepAnnotations.WitnessOnly))
601610
val constrVparamss =
602611
if (originalVparamss.isEmpty) { // ensure parameter list is non-empty
603612
if (isCaseClass)
@@ -608,7 +617,7 @@ object desugar {
608617
report.error(CaseClassMissingNonImplicitParamList(cdef), namePos)
609618
ListOfNil
610619
}
611-
else originalVparamss.nestedMap(toDefParam(_, KeepAnnotations.All, keepDefault = true))
620+
else originalVparamss.nestedMap(toMethParam(_, KeepAnnotations.All, keepDefault = true))
612621
val derivedTparams =
613622
constrTparams.zipWithConserve(impliedTparams)((tparam, impliedParam) =>
614623
derivedTypeParam(tparam).withAnnotations(impliedParam.mods.annotations))
@@ -630,7 +639,7 @@ object desugar {
630639
defDef(
631640
addEvidenceParams(
632641
cpy.DefDef(ddef)(paramss = joinParams(constrTparams, ddef.paramss)),
633-
evidenceParams(constr1).map(toDefParam(_, KeepAnnotations.None, keepDefault = false)))))
642+
evidenceParams(constr1).map(toMethParam(_, KeepAnnotations.None, keepDefault = false)))))
634643
case stat =>
635644
stat
636645
}
@@ -1148,7 +1157,7 @@ object desugar {
11481157
*/
11491158
def normalizeName(mdef: MemberDef, impl: Tree)(using Context): Name = {
11501159
var name = mdef.name
1151-
if (name.isEmpty) name = name.likeSpaced(inventGivenOrExtensionName(impl))
1160+
if (name.isEmpty) name = name.likeSpaced(inventGivenName(impl))
11521161
def errPos = mdef.source.atSpan(mdef.nameSpan)
11531162
if (ctx.owner == defn.ScalaPackageClass && defn.reservedScalaClassNames.contains(name.toTypeName)) {
11541163
val kind = if (name.isTypeName) "class" else "object"
@@ -1195,7 +1204,7 @@ object desugar {
11951204
end makePolyFunctionType
11961205

11971206
/** Invent a name for an anonympus given of type or template `impl`. */
1198-
def inventGivenOrExtensionName(impl: Tree)(using Context): SimpleName =
1207+
def inventGivenName(impl: Tree)(using Context): SimpleName =
11991208
val str = impl match
12001209
case impl: Template =>
12011210
if impl.parents.isEmpty then
@@ -1207,6 +1216,10 @@ object desugar {
12071216
"given_" ++ inventTypeName(impl)
12081217
str.toTermName.asSimpleName
12091218

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+
*/
12101223
private class NameExtractor(followArgs: Boolean) extends UntypedTreeAccumulator[String] {
12111224
private def extractArgs(args: List[Tree])(using Context): String =
12121225
args.map(argNameExtractor.apply("", _)).mkString("_")

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

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

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

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

Lines changed: 1 addition & 0 deletions
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

Lines changed: 2 additions & 1 deletion
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

Lines changed: 2 additions & 2 deletions
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

Lines changed: 4 additions & 4 deletions
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

Lines changed: 1 addition & 1 deletion
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

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

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

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

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

0 commit comments

Comments
 (0)