-
Notifications
You must be signed in to change notification settings - Fork 3.1k
Erase constant value type [ci: last-only] #9431
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: 2.13.x
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -81,10 +81,10 @@ abstract class Constructors extends Statics with Transform with TypingTransforme | |
|
||
override def transform(tree: Tree): Tree = { | ||
tree match { | ||
case cd @ ClassDef(mods0, name0, tparams0, impl0) if !isPrimitiveValueClass(cd.symbol) && cd.symbol.primaryConstructor != NoSymbol => | ||
if(cd.symbol eq AnyValClass) { | ||
case cd @ ClassDef(mods0, name0, tparams0, impl0) | ||
if !isPrimitiveValueClass(cd.symbol) && cd.symbol.primaryConstructor != NoSymbol => | ||
if (cd.symbol eq AnyValClass) | ||
cd | ||
} | ||
else { | ||
checkUninitializedReads(cd) | ||
val tplTransformer = new TemplateTransformer(unit, impl0) | ||
|
@@ -250,7 +250,7 @@ abstract class Constructors extends Statics with Transform with TypingTransforme | |
* | ||
* @return the DefDef for (c) above | ||
* | ||
* */ | ||
*/ | ||
private trait DelayedInitHelper extends ConstructorTransformerBase { | ||
private def delayedEndpointDef(stats: List[Tree]): DefDef = { | ||
val methodName = currentUnit.freshTermName("delayedEndpoint$" + clazz.fullNameAsName('$').toString + "$") | ||
|
@@ -458,11 +458,9 @@ abstract class Constructors extends Statics with Transform with TypingTransforme | |
{ | ||
protected def typedPos(pos: Position)(tree: Tree): Tree = localTyper.typedPos(pos)(tree) | ||
|
||
val clazz = impl.symbol.owner // the transformed class | ||
|
||
val isDelayedInitSubclass = clazz isSubClass DelayedInitClass | ||
|
||
private val stats = impl.body // the transformed template body | ||
override val clazz = impl.symbol.owner // the transformed class | ||
private val stats = impl.body // the transformed template body | ||
private val isDelayedInitSubclass = clazz isSubClass DelayedInitClass | ||
|
||
// find and dissect primary constructor | ||
private val (primaryConstr, _primaryConstrParams, primaryConstrBody) = | ||
|
@@ -481,7 +479,7 @@ abstract class Constructors extends Statics with Transform with TypingTransforme | |
// The constructor parameter corresponding to an accessor | ||
def parameter(acc: Symbol): Symbol = { | ||
//works around the edge case where unexpandedName over-unexpands shenanigans like literal $$ or `$#` | ||
def unexpanded = parameterNamed(acc.unexpandedName.getterName) | ||
val unexpanded = parameterNamed(acc.unexpandedName.getterName) | ||
def expanded = parameterNamed(acc.getterName) | ||
unexpanded.orElse(expanded).swap.map(abort).merge | ||
} | ||
|
@@ -497,15 +495,14 @@ abstract class Constructors extends Statics with Transform with TypingTransforme | |
|
||
// A transformer for expressions that go into the constructor | ||
object intoConstructor extends AstTransformer { | ||
/* | ||
* `usesSpecializedField` makes a difference in deciding whether constructor-statements | ||
* should be guarded in a `guardSpecializedFieldInit` class, ie in a class that's the generic super-class of | ||
* one or more specialized sub-classes. | ||
* | ||
* Given that `usesSpecializedField` isn't read for any other purpose than the one described above, | ||
* we skip setting `usesSpecializedField` in case the current class isn't `guardSpecializedFieldInit` to start with. | ||
* That way, trips to a map in `specializeTypes` are saved. | ||
*/ | ||
/* `usesSpecializedField` makes a difference in deciding whether constructor-statements | ||
* should be guarded in a `guardSpecializedFieldInit` class, ie in a class that's the generic super-class of | ||
* one or more specialized sub-classes. | ||
* | ||
* Given that `usesSpecializedField` isn't read for any other purpose than the one described above, | ||
* we skip setting `usesSpecializedField` in case the current class isn't `guardSpecializedFieldInit` | ||
* to start with. That way, trips to a map in `specializeTypes` are saved. | ||
*/ | ||
var usesSpecializedField: Boolean = false | ||
|
||
private def isParamRef(sym: Symbol) = sym.isParamAccessor && sym.owner == clazz | ||
|
@@ -518,8 +515,9 @@ abstract class Constructors extends Statics with Transform with TypingTransforme | |
!sym.isVariable | ||
) | ||
|
||
/* | ||
* whether `sym` denotes a param-accessor (ie in a class a PARAMACCESSOR field, or in a trait a method with same flag) | ||
/* whether `sym` denotes a param-accessor | ||
* (ie in a class a PARAMACCESSOR field, or in a trait a method with same flag) | ||
* | ||
* that fulfills all of: | ||
* (a) has stationary value, ie the same value provided via the corresponding ctor-arg; and | ||
* (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 | |
// references to parameter accessor methods of own class become references to parameters | ||
// outer accessors become references to $outer parameter | ||
// println(s"to param ref in $clazz for ${tree.symbol} ${tree.symbol.debugFlagString} / ${tree.symbol.outerSource} / ${canBeSupplanted(tree.symbol)}") | ||
if (clazz.isTrait && !(tree.symbol hasAllFlags (ACCESSOR | PARAMACCESSOR))) | ||
if (clazz.isTrait && !tree.symbol.hasAllFlags(ACCESSOR | PARAMACCESSOR)) | ||
super.transform(tree) | ||
else if (canBeSupplanted(tree.symbol)) | ||
gen.mkAttributedIdent(parameter(tree.symbol)) setPos tree.pos | ||
|
@@ -609,7 +607,7 @@ abstract class Constructors extends Statics with Transform with TypingTransforme | |
|
||
private def triage() = { | ||
// Constant typed vals are not memoized. | ||
def memoizeValue(sym: Symbol) = !sym.info.resultType.isInstanceOf[FoldableConstantType] | ||
def memoizeValue(sym: Symbol) = enteringErasure(!sym.info.resultType.isInstanceOf[FoldableConstantType]) | ||
|
||
// The early initialized field definitions of the class (these are the class members) | ||
val presupers = treeInfo.preSuperFields(stats) | ||
|
@@ -679,7 +677,8 @@ abstract class Constructors extends Statics with Transform with TypingTransforme | |
// - the constructor, before the super call (early initialized or a parameter accessor), | ||
// - the constructor, after the super call (regular val). | ||
case vd: ValDef => | ||
if (vd.rhs eq EmptyTree) { defBuf += vd } | ||
if (vd.rhs eq EmptyTree) | ||
defBuf += vd | ||
else { | ||
val emitField = memoizeValue(statSym) | ||
|
||
|
@@ -691,14 +690,22 @@ abstract class Constructors extends Statics with Transform with TypingTransforme | |
|
||
case dd: DefDef => | ||
// either move the RHS to ctor (for getter of stored field) or just drop it (for corresponding setter) | ||
def shouldMoveRHS = | ||
clazz.isTrait && statSym.isAccessor && !statSym.isLazy && !statSym.isSpecialized && (statSym.isSetter || memoizeValue(statSym)) | ||
|
||
if ((dd.rhs eq EmptyTree) || !shouldMoveRHS) { defBuf += dd } | ||
else { | ||
if (statSym.isGetter) moveEffectToCtor(dd.mods, dd.rhs, statSym.asTerm.referenced orElse statSym.setterIn(clazz)) | ||
defBuf += deriveDefDef(stat)(_ => EmptyTree) | ||
} | ||
def shouldMoveRHS = ( | ||
(dd.rhs ne EmptyTree) | ||
&& clazz.isTrait | ||
&& statSym.isAccessor | ||
&& !statSym.isLazy | ||
&& !statSym.isSpecialized | ||
&& (statSym.isSetter || memoizeValue(statSym)) | ||
) | ||
val toMove = | ||
if (shouldMoveRHS) { | ||
if (statSym.isGetter) | ||
moveEffectToCtor(dd.mods, dd.rhs, statSym.asTerm.referenced.orElse(statSym.setterIn(clazz))) | ||
deriveDefDef(stat)(_ => EmptyTree) | ||
} | ||
else dd | ||
defBuf += toMove | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I used to think vertical boolean exprs (wrapped in parens) were too much style just to have leading infix operators, but after using actual leading infix (without parens), I find it much easier to read. I only preferred the one-liner because mentally I would skip it and that's why it felt easier to read. note that under |
||
|
||
// all other statements go into the constructor | ||
case _ => | ||
|
@@ -727,19 +734,22 @@ abstract class Constructors extends Statics with Transform with TypingTransforme | |
else clazz.constrParamAccessors | ||
|
||
// Initialize all parameters fields that must be kept. | ||
val paramInits = paramAccessors filterNot omittableSym map { acc => | ||
val paramInits = paramAccessors.filterNot(omittableSym).map { acc => | ||
// Check for conflicting field mixed in for a val/var defined in a parent trait (neg/t1960.scala). | ||
// Since the fields phase has already mixed in fields, we can just look for | ||
// an existing decl with the local variant of our paramaccessor's name. | ||
// | ||
// TODO: mangle the constructor parameter name (it can only be used internally), though we probably first need more robust name mangling | ||
// TODO: mangle the constructor parameter name (it can only be used internally), | ||
// though we probably first need more robust name mangling | ||
|
||
// 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 | ||
// sometimes it's not (just a constructor param) --> any conflicting decl is a problem | ||
val conflict = clazz.info.decl(acc.name.localName).filter(sym => sym ne acc) | ||
if (conflict ne NoSymbol) { | ||
val orig = exitingTyper(clazz.info.nonPrivateMember(acc.name).filter(_ hasFlag ACCESSOR)) | ||
reporter.error(acc.pos, s"parameter '${acc.name}' requires field but conflicts with ${(orig orElse conflict).fullLocationString}") | ||
reporter.error(acc.pos, s"parameter '${acc.name}' requires field but conflicts with ${ | ||
orig.orElse(conflict).fullLocationString | ||
}") | ||
} | ||
|
||
val accSetter = | ||
|
@@ -787,7 +797,10 @@ abstract class Constructors extends Statics with Transform with TypingTransforme | |
) | ||
} | ||
|
||
if ((exitingPickler(clazz.isAnonymousClass) || clazz.originalOwner.isTerm) && omittableAccessor.exists(_.isOuterField) && !constructorStats.exists(_.exists { case i: Ident if i.symbol.isOuterParam => true; case _ => false})) | ||
if ((exitingPickler(clazz.isAnonymousClass) || clazz.originalOwner.isTerm) | ||
&& omittableAccessor.exists(_.isOuterField) | ||
&& !constructorStats.exists(_.exists { case i: Ident if i.symbol.isOuterParam => true case _ => false }) | ||
) | ||
primaryConstructor.symbol.updateAttachment(OuterArgCanBeElided) | ||
|
||
val constructors = primaryConstructor :: auxConstructors | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2469,12 +2469,8 @@ trait Symbols extends api.Symbols { self: SymbolTable => | |
* | ||
* @param ofclazz is a subclass of this symbol's owner | ||
*/ | ||
final def overridingSymbol(ofclazz: Symbol): Symbol = ( | ||
if (canMatchInheritedSymbols) | ||
matchingSymbol(ofclazz, ofclazz.thisType) | ||
else | ||
NoSymbol | ||
) | ||
final def overridingSymbol(ofclazz: Symbol): Symbol = | ||
if (canMatchInheritedSymbols) matchingSymbol(ofclazz, ofclazz.thisType) else NoSymbol | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. One-liner has the same form as previous method; this works well because the else alternative is trivial. |
||
|
||
/** If false, this symbol cannot possibly participate in an override, | ||
* either as overrider or overridee. For internal use; you should consult | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -134,8 +134,10 @@ trait Erasure { | |
def apply(tp: Type): Type = tp match { | ||
case FoldableConstantType(ct) => | ||
// erase classOf[List[_]] to classOf[List]. special case for classOf[Unit], avoid erasing to classOf[BoxedUnit]. | ||
if (ct.tag == ClazzTag && ct.typeValue.typeSymbol != UnitClass) ConstantType(Constant(apply(ct.typeValue))) | ||
else tp | ||
if (ct.tag == ClazzTag) | ||
if (ct.typeValue.typeSymbol == UnitClass) tp | ||
else ConstantType(Constant(apply(ct.typeValue))) | ||
else ct.tpe | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The fix for erasure. There are really 2 special cases under |
||
case st: ThisType if st.sym.isPackageClass => | ||
tp | ||
case st: SubType => | ||
|
@@ -253,7 +255,7 @@ trait Erasure { | |
else scalaErasure | ||
} | ||
|
||
/** This is used as the Scala erasure during the erasure phase itself | ||
/** This is used as the Scala erasure during the erasure phase itself. | ||
* It differs from normal erasure in that value classes are erased to ErasedValueTypes which | ||
* are then later converted to the underlying parameter type in phase posterasure. | ||
*/ | ||
|
@@ -262,11 +264,10 @@ trait Erasure { | |
erasure(sym)(tp) | ||
else if (sym.isClassConstructor) | ||
specialConstructorErasure(sym.owner, tp) | ||
else { | ||
else | ||
specialScalaErasureFor(sym)(tp) | ||
} | ||
|
||
def specialConstructorErasure(clazz: Symbol, tpe: Type): Type = { | ||
def specialConstructorErasure(clazz: Symbol, tpe: Type): Type = | ||
tpe match { | ||
case PolyType(tparams, restpe) => | ||
specialConstructorErasure(clazz, restpe) | ||
|
@@ -282,7 +283,6 @@ trait Erasure { | |
assert(clazz == ArrayClass || tp.isError, s"unexpected constructor erasure $tp for $clazz") | ||
specialScalaErasureFor(clazz)(tp) | ||
} | ||
} | ||
|
||
/** Scala's more precise erasure than java's is problematic as follows: | ||
* | ||
|
@@ -530,7 +530,7 @@ trait Erasure { | |
ErasedValueType(tref.sym, erasedValueClassArg(tref)) | ||
} | ||
|
||
/** This is used as the Scala erasure during the erasure phase itself | ||
/** This is used as the Scala erasure during the erasure phase itself. | ||
* It differs from normal erasure in that value classes are erased to ErasedValueTypes which | ||
* are then later unwrapped to the underlying parameter type in phase posterasure. | ||
*/ | ||
|
@@ -541,10 +541,9 @@ trait Erasure { | |
*/ | ||
object specialScala3Erasure extends Scala3ErasureMap with SpecialScalaErasure | ||
|
||
def specialScalaErasureFor(sym: Symbol): ErasureMap = { | ||
def specialScalaErasureFor(sym: Symbol): ErasureMap = | ||
if (sym.isScala3Defined) specialScala3Erasure | ||
else specialScalaErasure | ||
} | ||
|
||
object javaErasure extends JavaErasureMap | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why it's important to study history.