Skip to content

Commit 5e63e4d

Browse files
authored
Merge pull request scala#9431 from som-snytt/issue/12301
Erase constant value type [ci: last-only]
2 parents f4ca74b + e1f22ae commit 5e63e4d

File tree

12 files changed

+165
-75
lines changed

12 files changed

+165
-75
lines changed

src/compiler/scala/tools/nsc/transform/Constructors.scala

Lines changed: 49 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -81,10 +81,10 @@ abstract class Constructors extends Statics with Transform with TypingTransforme
8181

8282
override def transform(tree: Tree): Tree = {
8383
tree match {
84-
case cd @ ClassDef(mods0, name0, tparams0, impl0) if !isPrimitiveValueClass(cd.symbol) && cd.symbol.primaryConstructor != NoSymbol =>
85-
if(cd.symbol eq AnyValClass) {
84+
case cd @ ClassDef(mods0, name0, tparams0, impl0)
85+
if !isPrimitiveValueClass(cd.symbol) && cd.symbol.primaryConstructor != NoSymbol =>
86+
if (cd.symbol eq AnyValClass)
8687
cd
87-
}
8888
else {
8989
checkUninitializedReads(cd)
9090
val tplTransformer = new TemplateTransformer(unit, impl0)
@@ -250,7 +250,7 @@ abstract class Constructors extends Statics with Transform with TypingTransforme
250250
*
251251
* @return the DefDef for (c) above
252252
*
253-
* */
253+
*/
254254
private trait DelayedInitHelper extends ConstructorTransformerBase {
255255
private def delayedEndpointDef(stats: List[Tree]): DefDef = {
256256
val methodName = currentUnit.freshTermName("delayedEndpoint$" + clazz.fullNameAsName('$').toString + "$")
@@ -458,11 +458,9 @@ abstract class Constructors extends Statics with Transform with TypingTransforme
458458
{
459459
protected def typedPos(pos: Position)(tree: Tree): Tree = localTyper.typedPos(pos)(tree)
460460

461-
val clazz = impl.symbol.owner // the transformed class
462-
463-
val isDelayedInitSubclass = clazz isSubClass DelayedInitClass
464-
465-
private val stats = impl.body // the transformed template body
461+
override val clazz = impl.symbol.owner // the transformed class
462+
private val stats = impl.body // the transformed template body
463+
private val isDelayedInitSubclass = clazz isSubClass DelayedInitClass
466464

467465
// find and dissect primary constructor
468466
private val (primaryConstr, _primaryConstrParams, primaryConstrBody) =
@@ -481,7 +479,7 @@ abstract class Constructors extends Statics with Transform with TypingTransforme
481479
// The constructor parameter corresponding to an accessor
482480
def parameter(acc: Symbol): Symbol = {
483481
//works around the edge case where unexpandedName over-unexpands shenanigans like literal $$ or `$#`
484-
def unexpanded = parameterNamed(acc.unexpandedName.getterName)
482+
val unexpanded = parameterNamed(acc.unexpandedName.getterName)
485483
def expanded = parameterNamed(acc.getterName)
486484
unexpanded.orElse(expanded).swap.map(abort).merge
487485
}
@@ -497,15 +495,14 @@ abstract class Constructors extends Statics with Transform with TypingTransforme
497495

498496
// A transformer for expressions that go into the constructor
499497
object intoConstructor extends AstTransformer {
500-
/*
501-
* `usesSpecializedField` makes a difference in deciding whether constructor-statements
502-
* should be guarded in a `guardSpecializedFieldInit` class, ie in a class that's the generic super-class of
503-
* one or more specialized sub-classes.
504-
*
505-
* Given that `usesSpecializedField` isn't read for any other purpose than the one described above,
506-
* we skip setting `usesSpecializedField` in case the current class isn't `guardSpecializedFieldInit` to start with.
507-
* That way, trips to a map in `specializeTypes` are saved.
508-
*/
498+
/* `usesSpecializedField` makes a difference in deciding whether constructor-statements
499+
* should be guarded in a `guardSpecializedFieldInit` class, ie in a class that's the generic super-class of
500+
* one or more specialized sub-classes.
501+
*
502+
* Given that `usesSpecializedField` isn't read for any other purpose than the one described above,
503+
* we skip setting `usesSpecializedField` in case the current class isn't `guardSpecializedFieldInit`
504+
* to start with. That way, trips to a map in `specializeTypes` are saved.
505+
*/
509506
var usesSpecializedField: Boolean = false
510507

511508
private def isParamRef(sym: Symbol) = sym.isParamAccessor && sym.owner == clazz
@@ -518,8 +515,9 @@ abstract class Constructors extends Statics with Transform with TypingTransforme
518515
!sym.isVariable
519516
)
520517

521-
/*
522-
* whether `sym` denotes a param-accessor (ie in a class a PARAMACCESSOR field, or in a trait a method with same flag)
518+
/* whether `sym` denotes a param-accessor
519+
* (ie in a class a PARAMACCESSOR field, or in a trait a method with same flag)
520+
*
523521
* that fulfills all of:
524522
* (a) has stationary value, ie the same value provided via the corresponding ctor-arg; and
525523
* (b) isn't subject to specialization. We might be processing statements for:
@@ -534,7 +532,7 @@ abstract class Constructors extends Statics with Transform with TypingTransforme
534532
// references to parameter accessor methods of own class become references to parameters
535533
// outer accessors become references to $outer parameter
536534
// println(s"to param ref in $clazz for ${tree.symbol} ${tree.symbol.debugFlagString} / ${tree.symbol.outerSource} / ${canBeSupplanted(tree.symbol)}")
537-
if (clazz.isTrait && !(tree.symbol hasAllFlags (ACCESSOR | PARAMACCESSOR)))
535+
if (clazz.isTrait && !tree.symbol.hasAllFlags(ACCESSOR | PARAMACCESSOR))
538536
super.transform(tree)
539537
else if (canBeSupplanted(tree.symbol))
540538
gen.mkAttributedIdent(parameter(tree.symbol)) setPos tree.pos
@@ -609,7 +607,7 @@ abstract class Constructors extends Statics with Transform with TypingTransforme
609607

610608
private def triage() = {
611609
// Constant typed vals are not memoized.
612-
def memoizeValue(sym: Symbol) = !sym.info.resultType.isInstanceOf[FoldableConstantType]
610+
def memoizeValue(sym: Symbol) = enteringErasure(!sym.info.resultType.isInstanceOf[FoldableConstantType])
613611

614612
// The early initialized field definitions of the class (these are the class members)
615613
val presupers = treeInfo.preSuperFields(stats)
@@ -679,7 +677,8 @@ abstract class Constructors extends Statics with Transform with TypingTransforme
679677
// - the constructor, before the super call (early initialized or a parameter accessor),
680678
// - the constructor, after the super call (regular val).
681679
case vd: ValDef =>
682-
if (vd.rhs eq EmptyTree) { defBuf += vd }
680+
if (vd.rhs eq EmptyTree)
681+
defBuf += vd
683682
else {
684683
val emitField = memoizeValue(statSym)
685684

@@ -691,14 +690,22 @@ abstract class Constructors extends Statics with Transform with TypingTransforme
691690

692691
case dd: DefDef =>
693692
// either move the RHS to ctor (for getter of stored field) or just drop it (for corresponding setter)
694-
def shouldMoveRHS =
695-
clazz.isTrait && statSym.isAccessor && !statSym.isLazy && !statSym.isSpecialized && (statSym.isSetter || memoizeValue(statSym))
696-
697-
if ((dd.rhs eq EmptyTree) || !shouldMoveRHS) { defBuf += dd }
698-
else {
699-
if (statSym.isGetter) moveEffectToCtor(dd.mods, dd.rhs, statSym.asTerm.referenced orElse statSym.setterIn(clazz))
700-
defBuf += deriveDefDef(stat)(_ => EmptyTree)
701-
}
693+
def shouldMoveRHS = (
694+
(dd.rhs ne EmptyTree)
695+
&& clazz.isTrait
696+
&& statSym.isAccessor
697+
&& !statSym.isLazy
698+
&& !statSym.isSpecialized
699+
&& (statSym.isSetter || memoizeValue(statSym))
700+
)
701+
val toMove =
702+
if (shouldMoveRHS) {
703+
if (statSym.isGetter)
704+
moveEffectToCtor(dd.mods, dd.rhs, statSym.asTerm.referenced.orElse(statSym.setterIn(clazz)))
705+
deriveDefDef(stat)(_ => EmptyTree)
706+
}
707+
else dd
708+
defBuf += toMove
702709

703710
// all other statements go into the constructor
704711
case _ =>
@@ -727,19 +734,22 @@ abstract class Constructors extends Statics with Transform with TypingTransforme
727734
else clazz.constrParamAccessors
728735

729736
// Initialize all parameters fields that must be kept.
730-
val paramInits = paramAccessors filterNot omittableSym map { acc =>
737+
val paramInits = paramAccessors.filterNot(omittableSym).map { acc =>
731738
// Check for conflicting field mixed in for a val/var defined in a parent trait (neg/t1960.scala).
732739
// Since the fields phase has already mixed in fields, we can just look for
733740
// an existing decl with the local variant of our paramaccessor's name.
734741
//
735-
// TODO: mangle the constructor parameter name (it can only be used internally), though we probably first need more robust name mangling
742+
// TODO: mangle the constructor parameter name (it can only be used internally),
743+
// though we probably first need more robust name mangling
736744

737745
// sometimes acc is a field with a local name (when it's a val/var constructor param) --> exclude the `acc` itself when looking for conflicting decl
738746
// sometimes it's not (just a constructor param) --> any conflicting decl is a problem
739747
val conflict = clazz.info.decl(acc.name.localName).filter(sym => sym ne acc)
740748
if (conflict ne NoSymbol) {
741749
val orig = exitingTyper(clazz.info.nonPrivateMember(acc.name).filter(_ hasFlag ACCESSOR))
742-
reporter.error(acc.pos, s"parameter '${acc.name}' requires field but conflicts with ${(orig orElse conflict).fullLocationString}")
750+
reporter.error(acc.pos, s"parameter '${acc.name}' requires field but conflicts with ${
751+
orig.orElse(conflict).fullLocationString
752+
}")
743753
}
744754

745755
val accSetter =
@@ -787,7 +797,10 @@ abstract class Constructors extends Statics with Transform with TypingTransforme
787797
)
788798
}
789799

790-
if ((exitingPickler(clazz.isAnonymousClass) || clazz.originalOwner.isTerm) && omittableAccessor.exists(_.isOuterField) && !constructorStats.exists(_.exists { case i: Ident if i.symbol.isOuterParam => true; case _ => false}))
800+
if ((exitingPickler(clazz.isAnonymousClass) || clazz.originalOwner.isTerm)
801+
&& omittableAccessor.exists(_.isOuterField)
802+
&& !constructorStats.exists(_.exists { case i: Ident if i.symbol.isOuterParam => true case _ => false })
803+
)
791804
primaryConstructor.symbol.updateAttachment(OuterArgCanBeElided)
792805

793806
val constructors = primaryConstructor :: auxConstructors

src/compiler/scala/tools/nsc/transform/Erasure.scala

Lines changed: 27 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ abstract class Erasure extends InfoTransform
4242

4343
// -------- erasure on types --------------------------------------------------------
4444

45-
// convert a numeric with a toXXX method
45+
// convert a numeric with a toNNN method
4646
def numericConversion(tree: Tree, numericSym: Symbol): Tree = {
4747
val mname = newTermName("to" + numericSym.name)
4848
val conversion = tree.tpe member mname
@@ -90,8 +90,13 @@ abstract class Erasure extends InfoTransform
9090
}
9191
}
9292
@tailrec
93-
private[this] def untilApply(ts: List[Type]): Unit =
94-
if (! ts.isEmpty && ! result) { apply(ts.head) ; untilApply(ts.tail) }
93+
private def untilApply(ts: List[Type]): Unit =
94+
ts match {
95+
case t :: ts if !result =>
96+
apply(t)
97+
untilApply(ts)
98+
case _ =>
99+
}
95100
}
96101

97102
override protected def verifyJavaErasure = settings.Xverify.value || settings.isDebug
@@ -795,9 +800,10 @@ abstract class Erasure extends InfoTransform
795800
/** A replacement for the standard typer's `typed1` method.
796801
*/
797802
override def typed1(tree: Tree, mode: Mode, pt: Type): Tree = {
798-
val tree1 = try {
803+
val tree1 = try
799804
tree match {
800-
case DefDef(_,_,_,_,_,_) if tree.symbol.isClassConstructor && tree.symbol.isPrimaryConstructor && tree.symbol.owner != ArrayClass =>
805+
case tree: DefDef
806+
if tree.symbol.isClassConstructor && tree.symbol.isPrimaryConstructor && tree.symbol.owner != ArrayClass =>
801807
super.typed1(deriveDefDef(tree)(addMixinConstructorCalls(_, tree.symbol.owner)), mode, pt) // (3)
802808
case Template(parents, self, body) =>
803809
val parents1 = tree.symbol.owner.info.parents map (t => TypeTree(t) setPos tree.pos)
@@ -820,16 +826,14 @@ abstract class Erasure extends InfoTransform
820826
case _ =>
821827
super.typed1(adaptMember(tree), mode, pt)
822828
}
823-
} catch {
829+
catch {
824830
case er: TypeError =>
825831
Console.println("exception when typing " + tree+"/"+tree.getClass)
826832
Console.println(er.msg + " in file " + context.owner.sourceFile)
827833
er.printStackTrace
828834
abort("unrecoverable error")
829835
case ex: Exception =>
830-
//if (settings.debug.value)
831-
try Console.println("exception when typing " + tree)
832-
finally throw ex
836+
try Console.println(s"exception when typing $tree") catch identity: @nowarn
833837
throw ex
834838
}
835839

@@ -846,7 +850,6 @@ abstract class Erasure extends InfoTransform
846850
case Some(SAMFunction(samTp, _, _)) => fun setType specialScalaErasure(samTp)
847851
case _ => fun
848852
}
849-
850853
case If(cond, thenp, elsep) =>
851854
treeCopy.If(tree1, cond, adaptBranch(thenp), adaptBranch(elsep))
852855
case Match(selector, cases) =>
@@ -858,7 +861,7 @@ abstract class Erasure extends InfoTransform
858861
val first = tree1.symbol.alternatives.head
859862
val firstTpe = first.tpe
860863
val sym1 = tree1.symbol.filter {
861-
alt => alt == first || !(firstTpe looselyMatches alt.tpe)
864+
alt => alt == first || !firstTpe.looselyMatches(alt.tpe)
862865
}
863866
if (tree.symbol ne sym1) {
864867
tree1 setSymbol sym1 setType sym1.tpe
@@ -884,13 +887,15 @@ abstract class Erasure extends InfoTransform
884887
else if (low.owner == base) "name clash between defined and inherited member"
885888
else "name clash between inherited members"
886889
)
887-
val when = if (exitingRefchecks(lowType matches highType)) "" else " after erasure: " + exitingPostErasure(highType)
890+
val when =
891+
if (exitingRefchecks(lowType matches highType)) ""
892+
else s" after erasure: ${exitingPostErasure(highType)}"
888893

889894
reporter.error(pos,
890-
s"""|$what:
891-
|${exitingRefchecks(highString)} and
892-
|${exitingRefchecks(lowString)}
893-
|have same type$when""".trim.stripMargin
895+
sm"""|$what:
896+
|${exitingRefchecks(highString)} and
897+
|${exitingRefchecks(lowString)}
898+
|have same type$when"""
894899
)
895900
}
896901
low setInfo ErrorType
@@ -1195,7 +1200,7 @@ abstract class Erasure extends InfoTransform
11951200
global.typer.typed(gen.mkRuntimeCall(nme.anyValClass, List(qual, typer.resolveClassTag(tree.pos, qual.tpe.widen))))
11961201
} else if (primitiveGetClassMethods.contains(fn.symbol)) {
11971202
// if we got here then we're trying to send a primitive getClass method to either
1198-
// a) an Any, in which cage Object_getClass works because Any erases to object. Or
1203+
// a) an Any, in which case Object_getClass works because Any erases to object. Or
11991204
//
12001205
// b) a non-primitive, e.g. because the qualifier's type is a refinement type where one parent
12011206
// of the refinement is a primitive and another is AnyRef. In that case
@@ -1226,10 +1231,11 @@ abstract class Erasure extends InfoTransform
12261231
case tree: Apply =>
12271232
preEraseApply(tree)
12281233

1229-
case TypeApply(fun, args) if (fun.symbol.owner != AnyClass &&
1230-
fun.symbol != Object_asInstanceOf &&
1231-
fun.symbol != Object_isInstanceOf &&
1232-
fun.symbol != Object_synchronized) =>
1234+
case TypeApply(fun, args)
1235+
if fun.symbol.owner != AnyClass
1236+
&& fun.symbol != Object_asInstanceOf
1237+
&& fun.symbol != Object_isInstanceOf
1238+
&& fun.symbol != Object_synchronized =>
12331239
// leave all other type tests/type casts, remove all other type applications
12341240
preErase(fun)
12351241

src/compiler/scala/tools/nsc/transform/Mixin.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -404,7 +404,7 @@ abstract class Mixin extends Transform with ast.TreeDSL with AccessorSynthesis {
404404
// first complete the superclass with mixed in members
405405
addMixedinMembers(clazz.superClass, unit)
406406

407-
for (mc <- clazz.mixinClasses ; if mc.isTrait) {
407+
for (mc <- clazz.mixinClasses if mc.isTrait) {
408408
// @SEAN: adding trait tracking so we don't have to recompile transitive closures
409409
unit.registerDependency(mc)
410410
publicizeTraitMethods(mc)

src/reflect/scala/reflect/internal/Symbols.scala

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2469,12 +2469,8 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
24692469
*
24702470
* @param ofclazz is a subclass of this symbol's owner
24712471
*/
2472-
final def overridingSymbol(ofclazz: Symbol): Symbol = (
2473-
if (canMatchInheritedSymbols)
2474-
matchingSymbol(ofclazz, ofclazz.thisType)
2475-
else
2476-
NoSymbol
2477-
)
2472+
final def overridingSymbol(ofclazz: Symbol): Symbol =
2473+
if (canMatchInheritedSymbols) matchingSymbol(ofclazz, ofclazz.thisType) else NoSymbol
24782474

24792475
/** If false, this symbol cannot possibly participate in an override,
24802476
* either as overrider or overridee. For internal use; you should consult

src/reflect/scala/reflect/internal/transform/Erasure.scala

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -134,8 +134,10 @@ trait Erasure {
134134
def apply(tp: Type): Type = tp match {
135135
case FoldableConstantType(ct) =>
136136
// erase classOf[List[_]] to classOf[List]. special case for classOf[Unit], avoid erasing to classOf[BoxedUnit].
137-
if (ct.tag == ClazzTag && ct.typeValue.typeSymbol != UnitClass) ConstantType(Constant(apply(ct.typeValue)))
138-
else tp
137+
if (ct.tag == ClazzTag)
138+
if (ct.typeValue.typeSymbol == UnitClass) tp
139+
else ConstantType(Constant(apply(ct.typeValue)))
140+
else ct.tpe
139141
case st: ThisType if st.sym.isPackageClass =>
140142
tp
141143
case st: SubType =>
@@ -253,7 +255,7 @@ trait Erasure {
253255
else scalaErasure
254256
}
255257

256-
/** This is used as the Scala erasure during the erasure phase itself
258+
/** This is used as the Scala erasure during the erasure phase itself.
257259
* It differs from normal erasure in that value classes are erased to ErasedValueTypes which
258260
* are then later converted to the underlying parameter type in phase posterasure.
259261
*/
@@ -262,11 +264,10 @@ trait Erasure {
262264
erasure(sym)(tp)
263265
else if (sym.isClassConstructor)
264266
specialConstructorErasure(sym.owner, tp)
265-
else {
267+
else
266268
specialScalaErasureFor(sym)(tp)
267-
}
268269

269-
def specialConstructorErasure(clazz: Symbol, tpe: Type): Type = {
270+
def specialConstructorErasure(clazz: Symbol, tpe: Type): Type =
270271
tpe match {
271272
case PolyType(tparams, restpe) =>
272273
specialConstructorErasure(clazz, restpe)
@@ -282,7 +283,6 @@ trait Erasure {
282283
assert(clazz == ArrayClass || tp.isError, s"unexpected constructor erasure $tp for $clazz")
283284
specialScalaErasureFor(clazz)(tp)
284285
}
285-
}
286286

287287
/** Scala's more precise erasure than java's is problematic as follows:
288288
*
@@ -530,7 +530,7 @@ trait Erasure {
530530
ErasedValueType(tref.sym, erasedValueClassArg(tref))
531531
}
532532

533-
/** This is used as the Scala erasure during the erasure phase itself
533+
/** This is used as the Scala erasure during the erasure phase itself.
534534
* It differs from normal erasure in that value classes are erased to ErasedValueTypes which
535535
* are then later unwrapped to the underlying parameter type in phase posterasure.
536536
*/
@@ -541,10 +541,9 @@ trait Erasure {
541541
*/
542542
object specialScala3Erasure extends Scala3ErasureMap with SpecialScalaErasure
543543

544-
def specialScalaErasureFor(sym: Symbol): ErasureMap = {
544+
def specialScalaErasureFor(sym: Symbol): ErasureMap =
545545
if (sym.isScala3Defined) specialScala3Erasure
546546
else specialScalaErasure
547-
}
548547

549548
object javaErasure extends JavaErasureMap
550549

test/files/run/patmat-seq.check

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -264,7 +264,7 @@ package <empty> {
264264
()
265265
};
266266
def t(): Object = {
267-
case <synthetic> val x1: Int(2) = 2;
267+
case <synthetic> val x1: Int = 2;
268268
case16(){
269269
<synthetic> val o18: scala.collection.SeqOps = A.unapplySeq(x1);
270270
if (scala.collection.SeqFactory.UnapplySeqWrapper.isEmpty$extension(o18).unary_!())

0 commit comments

Comments
 (0)