Skip to content

Commit 6b7db02

Browse files
Merge pull request #14081 from dotty-staging/fix-13862
Avoid field/field clashes
2 parents 7a47334 + 34e0b4d commit 6b7db02

File tree

4 files changed

+38
-0
lines changed

4 files changed

+38
-0
lines changed

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,7 @@ object NameKinds {
369369
val FieldName: SuffixNameKind = new SuffixNameKind(FIELD, "$$local") {
370370
override def mkString(underlying: TermName, info: ThisInfo) = underlying.toString
371371
}
372+
val ExplicitFieldName: SuffixNameKind = new SuffixNameKind(EXPLICITFIELD, "$field")
372373
val ExtMethName: SuffixNameKind = new SuffixNameKind(EXTMETH, "$extension")
373374
val ParamAccessorName: SuffixNameKind = new SuffixNameKind(PARAMACC, "$accessor")
374375
val ModuleClassName: SuffixNameKind = new SuffixNameKind(OBJECTCLASS, "$", optInfoString = "ModuleClass")

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@ object NameTags extends TastyFormat.NameTags {
3737
final val AVOIDLOWER = 36
3838
final val AVOIDBOTH = 37
3939

40+
inline val EXPLICITFIELD = 38 // An explicitly named field, introduce to avoid a clash
41+
// with a regular field of the underlying name
42+
4043
def nameTagToString(tag: Int): String = tag match {
4144
case UTF8 => "UTF8"
4245
case QUALIFIED => "QUALIFIED"

compiler/src/dotty/tools/dotc/transform/Constructors.scala

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@ import dotty.tools.dotc.core.StdNames._
99
import ast._
1010
import Trees._
1111
import Flags._
12+
import Names.Name
1213
import NameOps._
14+
import NameKinds.{FieldName, ExplicitFieldName}
1315
import SymUtils._
1416
import Symbols._
1517
import Decorators._
@@ -249,7 +251,28 @@ class Constructors extends MiniPhase with IdentityDenotTransformer { thisPhase =
249251
splitStats(stats1)
250252
case Nil =>
251253
}
254+
255+
/** Check that we do not have both a private field with name `x` and a private field
256+
* with name `FieldName(x)`. These will map to the same JVM name and therefore cause
257+
* a duplicate field error. If that case arises (as in i13862.scala), use an explicit
258+
* name `x$field` instead of `FieldName(x).
259+
*/
260+
def checkNoFieldClashes() =
261+
val fieldNames = mutable.HashSet[Name]()
262+
for case field: ValDef <- clsStats do
263+
field.symbol.name match
264+
case FieldName(_) =>
265+
case name => fieldNames += name
266+
for case field: ValDef <- clsStats do
267+
field.symbol.name match
268+
case fldName @ FieldName(name) if fieldNames.contains(name) =>
269+
val newName = ExplicitFieldName(name)
270+
report.log(i"avoid field/field conflict by renaming $fldName to $newName")
271+
field.symbol.copySymDenotation(name = newName).installAfter(thisPhase)
272+
case _ =>
273+
252274
splitStats(tree.body)
275+
checkNoFieldClashes()
253276

254277
// The initializers for the retained accessors */
255278
val copyParams = accessors flatMap { acc =>

tests/run/i13862.scala

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
trait Foo(val num: Int) // a trait with a parameter stored in a val
2+
3+
class Bar(num: Int) extends Foo(num): // an extending class with a parameter of the same name
4+
def bar = this.num // implicitly creates another num in Bar
5+
6+
@main def Test = Bar(123)
7+
8+
class Bar2(n: Int) extends Foo(n): // an extending class with a parameter of the same name
9+
private val num = n
10+
def bar = this.num // implicitly creates another num in Bar
11+

0 commit comments

Comments
 (0)