Skip to content

Avoid field/field clashes #14081

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

Merged
merged 1 commit into from
Dec 17, 2021
Merged
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
1 change: 1 addition & 0 deletions compiler/src/dotty/tools/dotc/core/NameKinds.scala
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,7 @@ object NameKinds {
val FieldName: SuffixNameKind = new SuffixNameKind(FIELD, "$$local") {
override def mkString(underlying: TermName, info: ThisInfo) = underlying.toString
}
val ExplicitFieldName: SuffixNameKind = new SuffixNameKind(EXPLICITFIELD, "$field")
val ExtMethName: SuffixNameKind = new SuffixNameKind(EXTMETH, "$extension")
val ParamAccessorName: SuffixNameKind = new SuffixNameKind(PARAMACC, "$accessor")
val ModuleClassName: SuffixNameKind = new SuffixNameKind(OBJECTCLASS, "$", optInfoString = "ModuleClass")
Expand Down
3 changes: 3 additions & 0 deletions compiler/src/dotty/tools/dotc/core/NameTags.scala
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ object NameTags extends TastyFormat.NameTags {
final val AVOIDLOWER = 36
final val AVOIDBOTH = 37

inline val EXPLICITFIELD = 38 // An explicitly named field, introduce to avoid a clash
// with a regular field of the underlying name

def nameTagToString(tag: Int): String = tag match {
case UTF8 => "UTF8"
case QUALIFIED => "QUALIFIED"
Expand Down
23 changes: 23 additions & 0 deletions compiler/src/dotty/tools/dotc/transform/Constructors.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ import dotty.tools.dotc.core.StdNames._
import ast._
import Trees._
import Flags._
import Names.Name
import NameOps._
import NameKinds.{FieldName, ExplicitFieldName}
import SymUtils._
import Symbols._
import Decorators._
Expand Down Expand Up @@ -249,7 +251,28 @@ class Constructors extends MiniPhase with IdentityDenotTransformer { thisPhase =
splitStats(stats1)
case Nil =>
}

/** Check that we do not have both a private field with name `x` and a private field
* with name `FieldName(x)`. These will map to the same JVM name and therefore cause
* a duplicate field error. If that case arises (as in i13862.scala), use an explicit
* name `x$field` instead of `FieldName(x).
*/
def checkNoFieldClashes() =
val fieldNames = mutable.HashSet[Name]()
for case field: ValDef <- clsStats do
field.symbol.name match
case FieldName(_) =>
case name => fieldNames += name
for case field: ValDef <- clsStats do
field.symbol.name match
case fldName @ FieldName(name) if fieldNames.contains(name) =>
val newName = ExplicitFieldName(name)
report.log(i"avoid field/field conflict by renaming $fldName to $newName")
field.symbol.copySymDenotation(name = newName).installAfter(thisPhase)
case _ =>

splitStats(tree.body)
checkNoFieldClashes()

// The initializers for the retained accessors */
val copyParams = accessors flatMap { acc =>
Expand Down
11 changes: 11 additions & 0 deletions tests/run/i13862.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
trait Foo(val num: Int) // a trait with a parameter stored in a val

class Bar(num: Int) extends Foo(num): // an extending class with a parameter of the same name
def bar = this.num // implicitly creates another num in Bar

@main def Test = Bar(123)

class Bar2(n: Int) extends Foo(n): // an extending class with a parameter of the same name
private val num = n
def bar = this.num // implicitly creates another num in Bar