Thanks to visit codestin.com
Credit goes to github.com

Skip to content

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

Draft
wants to merge 2 commits into
base: 2.13.x
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 49 additions & 36 deletions src/compiler/scala/tools/nsc/transform/Constructors.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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 + "$")
Expand Down Expand Up @@ -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) =
Expand All @@ -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
}
Expand All @@ -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
Expand All @@ -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:
Expand All @@ -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
Expand Down Expand Up @@ -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])
Copy link
Contributor Author

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.


// The early initialized field definitions of the class (these are the class members)
val presupers = treeInfo.preSuperFields(stats)
Expand Down Expand Up @@ -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)

Expand All @@ -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
Copy link
Contributor Author

Choose a reason for hiding this comment

The 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 -Xsource:3 we could be using leading infix.


// all other statements go into the constructor
case _ =>
Expand Down Expand Up @@ -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 =
Expand Down Expand Up @@ -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
Expand Down
48 changes: 27 additions & 21 deletions src/compiler/scala/tools/nsc/transform/Erasure.scala
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ abstract class Erasure extends InfoTransform

// -------- erasure on types --------------------------------------------------------

// convert a numeric with a toXXX method
// convert a numeric with a toNNN method
def numericConversion(tree: Tree, numericSym: Symbol): Tree = {
val mname = newTermName("to" + numericSym.name)
val conversion = tree.tpe member mname
Expand Down Expand Up @@ -90,8 +90,13 @@ abstract class Erasure extends InfoTransform
}
}
@tailrec
private[this] def untilApply(ts: List[Type]): Unit =
if (! ts.isEmpty && ! result) { apply(ts.head) ; untilApply(ts.tail) }
private def untilApply(ts: List[Type]): Unit =
ts match {
case t :: ts if !result =>
apply(t)
untilApply(ts)
case _ =>
}
}

override protected def verifyJavaErasure = settings.Xverify.value || settings.isDebug
Expand Down Expand Up @@ -795,9 +800,10 @@ abstract class Erasure extends InfoTransform
/** A replacement for the standard typer's `typed1` method.
*/
override def typed1(tree: Tree, mode: Mode, pt: Type): Tree = {
val tree1 = try {
val tree1 = try
tree match {
case DefDef(_,_,_,_,_,_) if tree.symbol.isClassConstructor && tree.symbol.isPrimaryConstructor && tree.symbol.owner != ArrayClass =>
case tree: DefDef
if tree.symbol.isClassConstructor && tree.symbol.isPrimaryConstructor && tree.symbol.owner != ArrayClass =>
super.typed1(deriveDefDef(tree)(addMixinConstructorCalls(_, tree.symbol.owner)), mode, pt) // (3)
case Template(parents, self, body) =>
val parents1 = tree.symbol.owner.info.parents map (t => TypeTree(t) setPos tree.pos)
Expand All @@ -820,16 +826,14 @@ abstract class Erasure extends InfoTransform
case _ =>
super.typed1(adaptMember(tree), mode, pt)
}
} catch {
catch {
case er: TypeError =>
Console.println("exception when typing " + tree+"/"+tree.getClass)
Console.println(er.msg + " in file " + context.owner.sourceFile)
er.printStackTrace
abort("unrecoverable error")
case ex: Exception =>
//if (settings.debug.value)
try Console.println("exception when typing " + tree)
finally throw ex
try Console.println(s"exception when typing $tree") catch identity: @nowarn
throw ex
}

Expand All @@ -846,7 +850,6 @@ abstract class Erasure extends InfoTransform
case Some(SAMFunction(samTp, _, _)) => fun setType specialScalaErasure(samTp)
case _ => fun
}

case If(cond, thenp, elsep) =>
treeCopy.If(tree1, cond, adaptBranch(thenp), adaptBranch(elsep))
case Match(selector, cases) =>
Expand All @@ -858,7 +861,7 @@ abstract class Erasure extends InfoTransform
val first = tree1.symbol.alternatives.head
val firstTpe = first.tpe
val sym1 = tree1.symbol.filter {
alt => alt == first || !(firstTpe looselyMatches alt.tpe)
alt => alt == first || !firstTpe.looselyMatches(alt.tpe)
}
if (tree.symbol ne sym1) {
tree1 setSymbol sym1 setType sym1.tpe
Expand All @@ -884,13 +887,15 @@ abstract class Erasure extends InfoTransform
else if (low.owner == base) "name clash between defined and inherited member"
else "name clash between inherited members"
)
val when = if (exitingRefchecks(lowType matches highType)) "" else " after erasure: " + exitingPostErasure(highType)
val when =
if (exitingRefchecks(lowType matches highType)) ""
else s" after erasure: ${exitingPostErasure(highType)}"

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

case TypeApply(fun, args) if (fun.symbol.owner != AnyClass &&
fun.symbol != Object_asInstanceOf &&
fun.symbol != Object_isInstanceOf &&
fun.symbol != Object_synchronized) =>
case TypeApply(fun, args)
if fun.symbol.owner != AnyClass
&& fun.symbol != Object_asInstanceOf
&& fun.symbol != Object_isInstanceOf
&& fun.symbol != Object_synchronized =>
// leave all other type tests/type casts, remove all other type applications
preErase(fun)

Expand Down
2 changes: 1 addition & 1 deletion src/compiler/scala/tools/nsc/transform/Mixin.scala
Original file line number Diff line number Diff line change
Expand Up @@ -404,7 +404,7 @@ abstract class Mixin extends Transform with ast.TreeDSL with AccessorSynthesis {
// first complete the superclass with mixed in members
addMixedinMembers(clazz.superClass, unit)

for (mc <- clazz.mixinClasses ; if mc.isTrait) {
for (mc <- clazz.mixinClasses if mc.isTrait) {
// @SEAN: adding trait tracking so we don't have to recompile transitive closures
unit.registerDependency(mc)
publicizeTraitMethods(mc)
Expand Down
8 changes: 2 additions & 6 deletions src/reflect/scala/reflect/internal/Symbols.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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
Copy link
Contributor Author

Choose a reason for hiding this comment

The 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
Expand Down
19 changes: 9 additions & 10 deletions src/reflect/scala/reflect/internal/transform/Erasure.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fix for erasure. There are really 2 special cases under Class constant. Presumably the Unit case is handled specially later, but from this perspective, the encoding doesn't look regular.

case st: ThisType if st.sym.isPackageClass =>
tp
case st: SubType =>
Expand Down Expand Up @@ -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.
*/
Expand All @@ -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)
Expand All @@ -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:
*
Expand Down Expand Up @@ -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.
*/
Expand All @@ -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

Expand Down
2 changes: 1 addition & 1 deletion test/files/run/patmat-seq.check
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,7 @@ package <empty> {
()
};
def t(): Object = {
case <synthetic> val x1: Int(2) = 2;
case <synthetic> val x1: Int = 2;
case16(){
<synthetic> val o18: scala.collection.SeqOps = A.unapplySeq(x1);
if (scala.collection.SeqFactory.UnapplySeqWrapper.isEmpty$extension(o18).unary_!())
Expand Down
Loading