Skip to content

Commit a5c934a

Browse files
authored
Merge pull request #8637 from dotty-staging/change-default-params
Change default params representation
2 parents 8436ed5 + 7e8014e commit a5c934a

30 files changed

+286
-110
lines changed

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

Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ package ast
55
import core._
66
import util.Spans._, Types._, Contexts._, Constants._, Names._, NameOps._, Flags._
77
import Symbols._, StdNames._, Trees._
8-
import Decorators._, transform.SymUtils._
8+
import Decorators.{given _}, transform.SymUtils._
99
import NameKinds.{UniqueName, EvidenceParamName, DefaultGetterName}
1010
import typer.{FrontEnd, Namer}
1111
import util.{Property, SourceFile, SourcePosition}
@@ -247,16 +247,18 @@ object desugar {
247247
Nil
248248
}
249249

250-
def normalizedVparamss = meth1.vparamss.map(_.map(vparam =>
251-
cpy.ValDef(vparam)(rhs = EmptyTree)))
250+
def normalizedVparamss = meth1.vparamss.nestedMapConserve(vparam =>
251+
if vparam.rhs.isEmpty then vparam
252+
else cpy.ValDef(vparam)(rhs = EmptyTree).withMods(vparam.mods | HasDefault)
253+
)
252254

253255
def defaultGetters(vparamss: List[List[ValDef]], n: Int): List[DefDef] = vparamss match {
254256
case (vparam :: vparams) :: vparamss1 =>
255257
def defaultGetter: DefDef =
256258
DefDef(
257259
name = DefaultGetterName(methName, n),
258260
tparams = meth.tparams.map(tparam => dropContextBounds(toDefParam(tparam, keepAnnotations = true))),
259-
vparamss = takeUpTo(normalizedVparamss.nestedMap(toDefParam(_, keepAnnotations = true)), n),
261+
vparamss = takeUpTo(normalizedVparamss.nestedMap(toDefParam(_, keepAnnotations = true, keepDefault = false)), n),
260262
tpt = TypeTree(),
261263
rhs = vparam.rhs
262264
)
@@ -273,7 +275,6 @@ object desugar {
273275
if (defGetters.isEmpty) meth1
274276
else {
275277
val meth2 = cpy.DefDef(meth1)(vparamss = normalizedVparamss)
276-
.withMods(meth1.mods | DefaultParameterized)
277278
Thicket(meth2 :: defGetters)
278279
}
279280
}
@@ -365,10 +366,11 @@ object desugar {
365366
if (!keepAnnotations) mods = mods.withAnnotations(Nil)
366367
tparam.withMods(mods & EmptyFlags | Param)
367368
}
368-
private def toDefParam(vparam: ValDef, keepAnnotations: Boolean): ValDef = {
369+
private def toDefParam(vparam: ValDef, keepAnnotations: Boolean, keepDefault: Boolean): ValDef = {
369370
var mods = vparam.rawMods
370371
if (!keepAnnotations) mods = mods.withAnnotations(Nil)
371-
vparam.withMods(mods & (GivenOrImplicit | Erased) | Param)
372+
val hasDefault = if keepDefault then HasDefault else EmptyFlags
373+
vparam.withMods(mods & (GivenOrImplicit | Erased | hasDefault) | Param)
372374
}
373375

374376
/** The expansion of a class definition. See inline comments for what is involved */
@@ -442,7 +444,7 @@ object desugar {
442444
ctx.error(CaseClassMissingNonImplicitParamList(cdef), namePos)
443445
ListOfNil
444446
}
445-
else originalVparamss.nestedMap(toDefParam(_, keepAnnotations = false))
447+
else originalVparamss.nestedMap(toDefParam(_, keepAnnotations = false, keepDefault = true))
446448
val constr = cpy.DefDef(constr1)(tparams = constrTparams, vparamss = constrVparamss)
447449

448450
val (normalizedBody, enumCases, enumCompanionRef) = {
@@ -454,7 +456,7 @@ object desugar {
454456
defDef(
455457
addEvidenceParams(
456458
cpy.DefDef(ddef)(tparams = constrTparams ++ ddef.tparams),
457-
evidenceParams(constr1).map(toDefParam(_, keepAnnotations = false)))))
459+
evidenceParams(constr1).map(toDefParam(_, keepAnnotations = false, keepDefault = false)))))
458460
case stat =>
459461
stat
460462
}
@@ -480,16 +482,11 @@ object desugar {
480482

481483
// Annotations are dropped from the constructor parameters but should be
482484
// preserved in all derived parameters.
483-
val derivedTparams = {
484-
val impliedTparamsIt = impliedTparams.iterator
485-
constrTparams.map(tparam => derivedTypeParam(tparam)
486-
.withAnnotations(impliedTparamsIt.next().mods.annotations))
487-
}
488-
val derivedVparamss = {
489-
val constrVparamsIt = constrVparamss.iterator.flatten
490-
constrVparamss.nestedMap(vparam => derivedTermParam(vparam)
491-
.withAnnotations(constrVparamsIt.next().mods.annotations))
492-
}
485+
val derivedTparams =
486+
constrTparams.zipWithConserve(impliedTparams)((tparam, impliedParam) =>
487+
derivedTypeParam(tparam).withAnnotations(impliedParam.mods.annotations))
488+
val derivedVparamss =
489+
constrVparamss.nestedMap(vparam => derivedTermParam(vparam))
493490

494491
val arity = constrVparamss.head.length
495492

@@ -691,13 +688,16 @@ object desugar {
691688
val applyMeths =
692689
if (mods.is(Abstract)) Nil
693690
else {
694-
val copiedFlagsMask = DefaultParameterized | (copiedAccessFlags & Private)
691+
val copiedFlagsMask = copiedAccessFlags & Private
695692
val appMods = {
696693
val mods = Modifiers(Synthetic | constr1.mods.flags & copiedFlagsMask)
697694
if (restrictedAccess) mods.withPrivateWithin(constr1.mods.privateWithin)
698695
else mods
699696
}
700-
val app = DefDef(nme.apply, derivedTparams, derivedVparamss, applyResultTpt, widenedCreatorExpr)
697+
val appParamss =
698+
derivedVparamss.nestedZipWithConserve(constrVparamss)((ap, cp) =>
699+
ap.withMods(ap.mods | (cp.mods.flags & HasDefault)))
700+
val app = DefDef(nme.apply, derivedTparams, appParamss, applyResultTpt, widenedCreatorExpr)
701701
.withMods(appMods)
702702
app :: widenDefs
703703
}

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ class TreeTypeMap(
9494
val (tmap1, tparams1) = transformDefs(ddef.tparams)
9595
val (tmap2, vparamss1) = tmap1.transformVParamss(vparamss)
9696
val res = cpy.DefDef(ddef)(name, tparams1, vparamss1, tmap2.transform(tpt), tmap2.transform(ddef.rhs))
97+
res.symbol.setParamssFromDefs(tparams1, vparamss1)
9798
res.symbol.transformAnnotations {
9899
case ann: BodyAnnotation => ann.derivedAnnotation(transform(ann.tree))
99100
case ann => ann

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

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import transform.TypeUtils._
99
import core._
1010
import util.Spans._, Types._, Contexts._, Constants._, Names._, Flags._, NameOps._
1111
import Symbols._, StdNames._, Annotations._, Trees._, Symbols._
12-
import Decorators._, DenotTransformers._
12+
import Decorators.{given _}, DenotTransformers._
1313
import collection.{immutable, mutable}
1414
import util.{Property, SourceFile, NoSource}
1515
import NameKinds.{TempResultName, OuterSelectName}
@@ -208,6 +208,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
208208

209209
def DefDef(sym: TermSymbol, tparams: List[TypeSymbol], vparamss: List[List[TermSymbol]],
210210
resultType: Type, rhs: Tree)(implicit ctx: Context): DefDef =
211+
sym.setParamss(tparams, vparamss)
211212
ta.assignType(
212213
untpd.DefDef(
213214
sym.name,
@@ -223,15 +224,27 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
223224
def DefDef(sym: TermSymbol, rhsFn: List[List[Tree]] => Tree)(implicit ctx: Context): DefDef =
224225
polyDefDef(sym, Function.const(rhsFn))
225226

227+
/** A DefDef with given method symbol `sym`.
228+
* @rhsFn A function from type parameter types and term parameter references
229+
* to the method's right-hand side.
230+
* Parameter symbols are taken from the `rawParamss` field of `sym`, or
231+
* are freshly generated if `rawParamss` is empty.
232+
*/
226233
def polyDefDef(sym: TermSymbol, rhsFn: List[Type] => List[List[Tree]] => Tree)(implicit ctx: Context): DefDef = {
227-
val (tparams, mtp) = sym.info match {
234+
235+
val (tparams, existingParamss, mtp) = sym.info match {
228236
case tp: PolyType =>
229-
val tparams = ctx.newTypeParams(sym, tp.paramNames, EmptyFlags, tp.instantiateParamInfos(_))
230-
(tparams, tp.instantiate(tparams map (_.typeRef)))
231-
case tp => (Nil, tp)
237+
val (tparams, existingParamss) = sym.rawParamss match
238+
case tparams :: vparamss =>
239+
assert(tparams.hasSameLengthAs(tp.paramNames) && tparams.head.isType)
240+
(tparams.asInstanceOf[List[TypeSymbol]], vparamss)
241+
case _ =>
242+
(ctx.newTypeParams(sym, tp.paramNames, EmptyFlags, tp.instantiateParamInfos(_)), Nil)
243+
(tparams, existingParamss, tp.instantiate(tparams map (_.typeRef)))
244+
case tp => (Nil, sym.rawParamss, tp)
232245
}
233246

234-
def valueParamss(tp: Type): (List[List[TermSymbol]], Type) = tp match {
247+
def valueParamss(tp: Type, existingParamss: List[List[Symbol]]): (List[List[TermSymbol]], Type) = tp match {
235248
case tp: MethodType =>
236249
val isParamDependent = tp.isParamDependent
237250
val previousParamRefs = if (isParamDependent) mutable.ListBuffer[TermRef]() else null
@@ -254,14 +267,23 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
254267
makeSym(origInfo)
255268
}
256269

257-
val params = tp.paramNames.lazyZip(tp.paramInfos).map(valueParam)
258-
val (paramss, rtp) = valueParamss(tp.instantiate(params map (_.termRef)))
270+
val (params, existingParamss1) =
271+
if tp.paramInfos.isEmpty then (Nil, existingParamss)
272+
else existingParamss match
273+
case vparams :: existingParamss1 =>
274+
assert(vparams.hasSameLengthAs(tp.paramNames) && vparams.head.isTerm)
275+
(vparams.asInstanceOf[List[TermSymbol]], existingParamss1)
276+
case _ =>
277+
(tp.paramNames.lazyZip(tp.paramInfos).map(valueParam), Nil)
278+
val (paramss, rtp) =
279+
valueParamss(tp.instantiate(params map (_.termRef)), existingParamss1)
259280
(params :: paramss, rtp)
260281
case tp => (Nil, tp.widenExpr)
261282
}
262-
val (vparamss, rtp) = valueParamss(mtp)
283+
val (vparamss, rtp) = valueParamss(mtp, existingParamss)
263284
val targs = tparams map (_.typeRef)
264285
val argss = vparamss.nestedMap(vparam => Ident(vparam.termRef))
286+
sym.setParamss(tparams, vparamss)
265287
DefDef(sym, tparams, vparamss, rtp, rhsFn(targs)(argss))
266288
}
267289

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

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -155,10 +155,13 @@ object Decorators {
155155
def & (ys: List[T]): List[T] = xs filter (ys contains _)
156156
}
157157

158-
implicit class ListOfListDecorator[T](val xss: List[List[T]]) extends AnyVal {
159-
def nestedMap[U](f: T => U): List[List[U]] = xss map (_ map f)
160-
def nestedMapconserve[U](f: T => U): List[List[U]] = xss mapconserve (_ mapconserve f)
161-
}
158+
extension ListOfListDecorator on [T, U](xss: List[List[T]]):
159+
def nestedMap(f: T => U): List[List[U]] =
160+
xss.map(_.map(f))
161+
def nestedMapConserve(f: T => U): List[List[U]] =
162+
xss.mapconserve(_.mapconserve(f))
163+
def nestedZipWithConserve(yss: List[List[U]])(f: (T, U) => T): List[List[T]] =
164+
xss.zipWithConserve(yss)((xs, ys) => xs.zipWithConserve(ys)(f))
162165

163166
implicit class TextToString(val text: Text) extends AnyVal {
164167
def show(implicit ctx: Context): String = text.mkString(ctx.settings.pageWidth.value, ctx.settings.printLines.value)

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

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -299,7 +299,7 @@ object Flags {
299299
val (SuperParamAliasOrScala2x @ _, SuperParamAlias @ _, Scala2x @ _) = newFlags(26, "<super-param-alias>", "<scala-2.x>")
300300

301301
/** A method that has default params */
302-
val (_, DefaultParameterized @ _, _) = newFlags(27, "<defaultparam>")
302+
val (_, HasDefault @ _, _) = newFlags(27, "<hasdefault>")
303303

304304
/** An extension method, or a collective extension instance */
305305
val (_, Extension @ _, _) = newFlags(28, "<extension>")
@@ -391,18 +391,18 @@ object Flags {
391391

392392
/** Translation of Scala2's EXPANDEDNAME flag. This flag is never stored in
393393
* symbols, is only used locally when reading the flags of a Scala2 symbol.
394-
* It's therefore safe to share the code with `InheritedDefaultParams` because
394+
* It's therefore safe to share the code with `HasDefaultParams` because
395395
* the latter is never present in Scala2 unpickle info.
396396
* /
397-
* A method that is known to have inherited default parameters
397+
* A method that is known to have (defined or inherited) default parameters
398398
*/
399-
val (Scala2ExpandedName @ _, InheritedDefaultParams @ _, _) = newFlags(59, "<inherited-default-param>")
399+
val (Scala2ExpandedName @ _, HasDefaultParams @ _, _) = newFlags(59, "<has-default-params>")
400400

401401
/** A method that is known to have no default parameters
402402
* /
403403
* A type symbol with provisional empty bounds
404404
*/
405-
val (_, NoDefaultParams @ _, Provisional @ _) = newFlags(60, "<no-default-param>", "<provisional>")
405+
val (_, NoDefaultParams @ _, Provisional @ _) = newFlags(60, "<no-default-params>", "<provisional>")
406406

407407
/** A denotation that is valid in all run-ids */
408408
val (Permanent @ _, _, _) = newFlags(61, "<permanent>")
@@ -525,8 +525,7 @@ object Flags {
525525
val EnumCase: FlagSet = Case | Enum
526526
val CovariantLocal: FlagSet = Covariant | Local // A covariant type parameter
527527
val ContravariantLocal: FlagSet = Contravariant | Local // A contravariant type parameter
528-
val HasDefaultParamsFlags: FlagSet = DefaultParameterized | InheritedDefaultParams // Has defined or inherited default parameters
529-
val DefaultParameter: FlagSet = DefaultParameterized | Param // A Scala 2x default parameter
528+
val DefaultParameter: FlagSet = HasDefault | Param // A Scala 2x default parameter
530529
val DeferredOrLazy: FlagSet = Deferred | Lazy
531530
val DeferredOrLazyOrMethod: FlagSet = Deferred | Lazy | Method
532531
val DeferredOrTermParamOrAccessor: FlagSet = Deferred | ParamAccessor | TermParam // term symbols without right-hand sides
@@ -576,6 +575,7 @@ object Flags {
576575
val SyntheticGivenMethod: FlagSet = Synthetic | Given | Method
577576
val SyntheticModule: FlagSet = Synthetic | Module
578577
val SyntheticOpaque: FlagSet = Synthetic | Opaque
578+
val SyntheticParam: FlagSet = Synthetic | Param
579579
val SyntheticTermParam: FlagSet = Synthetic | TermParam
580580
val SyntheticTypeParam: FlagSet = Synthetic | TypeParam
581581
}

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

Lines changed: 71 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import Scopes.Scope
1111
import dotty.tools.io.AbstractFile
1212
import Decorators.SymbolIteratorDecorator
1313
import ast._
14-
import ast.Trees.{LambdaTypeTree, TypeBoundsTree}
14+
import ast.Trees.{LambdaTypeTree, TypeBoundsTree, ValDef, TypeDef}
1515
import Trees.Literal
1616
import Variances.Variance
1717
import annotation.tailrec
@@ -150,6 +150,7 @@ object SymDenotations {
150150
private var myFlags: FlagSet = adaptFlags(initFlags)
151151
private var myPrivateWithin: Symbol = initPrivateWithin
152152
private var myAnnotations: List[Annotation] = Nil
153+
private var myParamss: List[List[Symbol]] = Nil
153154

154155
/** The owner of the symbol; overridden in NoDenotation */
155156
def owner: Symbol = maybeOwner
@@ -372,6 +373,59 @@ object SymDenotations {
372373
case Nil => Nil
373374
}
374375

376+
/** If this is a method, the parameter symbols, by section.
377+
* Both type and value parameters are included. Empty sections are skipped.
378+
*/
379+
final def rawParamss: List[List[Symbol]] = myParamss
380+
final def rawParamss_=(pss: List[List[Symbol]]): Unit =
381+
myParamss = pss
382+
383+
final def setParamss(tparams: List[Symbol], vparamss: List[List[Symbol]])(using Context): Unit =
384+
rawParamss = (if tparams.isEmpty then vparamss else tparams :: vparamss)
385+
.filterConserve(!_.isEmpty)
386+
387+
final def setParamssFromDefs(tparams: List[TypeDef[?]], vparamss: List[List[ValDef[?]]])(using Context): Unit =
388+
setParamss(tparams.map(_.symbol), vparamss.map(_.map(_.symbol)))
389+
390+
/** A pair consistsing of type paremeter symbols and value parameter symbol lists
391+
* of this method definition, or (Nil, Nil) for other symbols.
392+
* Makes use of `rawParamss` when present, or constructs fresh parameter symbols otherwise.
393+
* This method can be allocation-heavy.
394+
*/
395+
final def paramSymss(using ctx: Context): (List[TypeSymbol], List[List[TermSymbol]]) =
396+
397+
def recurWithParamss(info: Type, paramss: List[List[Symbol]]): List[List[Symbol]] =
398+
info match
399+
case info: LambdaType =>
400+
if info.paramNames.isEmpty then Nil :: recurWithParamss(info.resType, paramss)
401+
else paramss.head :: recurWithParamss(info.resType, paramss.tail)
402+
case _ =>
403+
Nil
404+
405+
def recurWithoutParamss(info: Type): List[List[Symbol]] = info match
406+
case info: LambdaType =>
407+
val params = info.paramNames.lazyZip(info.paramInfos).map((pname, ptype) =>
408+
ctx.newSymbol(symbol, pname, SyntheticParam, ptype))
409+
val prefs = params.map(_.namedType)
410+
for param <- params do
411+
param.info = param.info.substParams(info, prefs)
412+
params :: recurWithoutParamss(info.instantiate(prefs))
413+
case _ =>
414+
Nil
415+
416+
try
417+
val allParamss =
418+
if rawParamss.isEmpty then recurWithoutParamss(info)
419+
else recurWithParamss(info, rawParamss)
420+
val result = info match
421+
case info: PolyType => (allParamss.head, allParamss.tail)
422+
case _ => (Nil, allParamss)
423+
result.asInstanceOf[(List[TypeSymbol], List[List[TermSymbol]])]
424+
catch case NonFatal(ex) =>
425+
println(i"paramSymss failure for $symbol, $info, $rawParamss")
426+
throw ex
427+
end paramSymss
428+
375429
/** The denotation is completed: info is not a lazy type and attributes have defined values */
376430
final def isCompleted: Boolean = !myInfo.isInstanceOf[LazyType]
377431

@@ -916,15 +970,19 @@ object SymDenotations {
916970
def isAsConcrete(that: Symbol)(implicit ctx: Context): Boolean =
917971
!this.is(Deferred) || that.is(Deferred)
918972

919-
/** Does this symbol have defined or inherited default parameters? */
973+
/** Does this symbol have defined or inherited default parameters?
974+
* Default parameters are recognized until erasure.
975+
*/
920976
def hasDefaultParams(implicit ctx: Context): Boolean =
921-
if (this.isOneOf(HasDefaultParamsFlags)) true
922-
else if (this.is(NoDefaultParams)) false
923-
else {
924-
val result = allOverriddenSymbols exists (_.hasDefaultParams)
925-
setFlag(if (result) InheritedDefaultParams else NoDefaultParams)
977+
if ctx.erasedTypes then false
978+
else if is(HasDefaultParams) then true
979+
else if is(NoDefaultParams) then false
980+
else
981+
val result =
982+
rawParamss.exists(_.exists(_.is(HasDefault)))
983+
|| allOverriddenSymbols.exists(_.hasDefaultParams)
984+
setFlag(if result then HasDefaultParams else NoDefaultParams)
926985
result
927-
}
928986

929987
/** Symbol is an owner that would be skipped by effectiveOwner. Skipped are
930988
* - package objects
@@ -1452,16 +1510,20 @@ object SymDenotations {
14521510
initFlags: FlagSet = UndefinedFlags,
14531511
info: Type = null,
14541512
privateWithin: Symbol = null,
1455-
annotations: List[Annotation] = null)(implicit ctx: Context): SymDenotation = {
1513+
annotations: List[Annotation] = null,
1514+
rawParamss: List[List[Symbol]] = null)(
1515+
using ctx: Context): SymDenotation = {
14561516
// simulate default parameters, while also passing implicit context ctx to the default values
14571517
val initFlags1 = (if (initFlags != UndefinedFlags) initFlags else this.flags)
14581518
val info1 = if (info != null) info else this.info
14591519
if (ctx.isAfterTyper && changedClassParents(info, info1, completersMatter = false))
14601520
assert(ctx.phase.changesParents, i"undeclared parent change at ${ctx.phase} for $this, was: $info, now: $info1")
14611521
val privateWithin1 = if (privateWithin != null) privateWithin else this.privateWithin
14621522
val annotations1 = if (annotations != null) annotations else this.annotations
1523+
val rawParamss1 = if rawParamss != null then rawParamss else this.rawParamss
14631524
val d = ctx.SymDenotation(symbol, owner, name, initFlags1, info1, privateWithin1)
14641525
d.annotations = annotations1
1526+
d.rawParamss = rawParamss1
14651527
d.registeredCompanion = registeredCompanion
14661528
d
14671529
}

compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -681,7 +681,7 @@ class TreePickler(pickler: TastyPickler) {
681681
if (flags.is(Mutable)) writeModTag(MUTABLE)
682682
if (flags.is(Accessor)) writeModTag(FIELDaccessor)
683683
if (flags.is(CaseAccessor)) writeModTag(CASEaccessor)
684-
if (flags.is(DefaultParameterized)) writeModTag(DEFAULTparameterized)
684+
if (flags.is(HasDefault)) writeModTag(HASDEFAULT)
685685
if (flags.is(StableRealizable)) writeModTag(STABLE)
686686
if (flags.is(Extension)) writeModTag(EXTENSION)
687687
if (flags.is(ParamAccessor)) writeModTag(PARAMsetter)

0 commit comments

Comments
 (0)