Skip to content

Commit 3c20740

Browse files
committed
Name mangle methods with erased context results
Add a `$` to the name of a method that has erased context results and that may override some other method. This is to prevent the two methods from having the same names and parameters in their erased signatures. We need a bridge between the two methods, so they are not allowed to already override after erasure.
1 parent a771c86 commit 3c20740

File tree

5 files changed

+35
-32
lines changed

5 files changed

+35
-32
lines changed

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

Lines changed: 4 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1353,39 +1353,19 @@ class Definitions {
13531353
def isBoxedUnitClass(cls: Symbol): Boolean =
13541354
cls.isClass && (cls.owner eq ScalaRuntimePackageClass) && cls.name == tpnme.BoxedUnit
13551355

1356-
/** Returns the erased class of the function class `cls`
1357-
* - FunctionN for N > 22 becomes FunctionXXL
1358-
* - FunctionN for 22 > N >= 0 remains as FunctionN
1359-
* - ContextFunctionN for N > 22 becomes FunctionXXL
1360-
* - ContextFunctionN for N <= 22 becomes FunctionN
1361-
* - ErasedFunctionN becomes Function0
1362-
* - ImplicitErasedFunctionN becomes Function0
1363-
* - anything else becomes a NoSymbol
1364-
*/
1365-
def erasedFunctionClass(cls: Symbol): Symbol = {
1366-
val arity = scalaClassName(cls).functionArity
1367-
if (cls.name.isErasedFunction) FunctionClass(0)
1368-
else if (arity > 22) FunctionXXLClass
1369-
else if (arity >= 0) FunctionClass(arity)
1370-
else NoSymbol
1371-
}
1372-
13731356
/** Returns the erased type of the function class `cls`
13741357
* - FunctionN for N > 22 becomes FunctionXXL
13751358
* - FunctionN for 22 > N >= 0 remains as FunctionN
13761359
* - ContextFunctionN for N > 22 becomes FunctionXXL
13771360
* - ContextFunctionN for N <= 22 becomes FunctionN
1378-
* - ErasedFunctionN becomes Function0
1379-
* - ImplicitErasedFunctionN becomes Function0
13801361
* - anything else becomes a NoType
13811362
*/
1382-
def erasedFunctionType(cls: Symbol): Type = {
1363+
def functionTypeErasure(cls: Symbol): Type =
13831364
val arity = scalaClassName(cls).functionArity
1384-
if (cls.name.isErasedFunction) FunctionType(0)
1385-
else if (arity > 22) FunctionXXLClass.typeRef
1386-
else if (arity >= 0) FunctionType(arity)
1365+
if cls.name.isErasedFunction then FunctionType(0)
1366+
else if arity > 22 then FunctionXXLClass.typeRef
1367+
else if arity >= 0 then FunctionType(arity)
13871368
else NoType
1388-
}
13891369

13901370
val predefClassNames: Set[Name] =
13911371
Set("Predef$", "DeprecatedPredef", "LowPriorityImplicits").map(_.toTypeName.unmangleClassName)

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -581,7 +581,7 @@ class TypeErasure(sourceLanguage: SourceLanguage, semiEraseVCs: Boolean, isConst
581581
val sym = tp.symbol
582582
if (!sym.isClass) this(tp.translucentSuperType)
583583
else if (semiEraseVCs && isDerivedValueClass(sym)) eraseDerivedValueClass(tp)
584-
else if (defn.isSyntheticFunctionClass(sym)) defn.erasedFunctionType(sym)
584+
else if (defn.isSyntheticFunctionClass(sym)) defn.functionTypeErasure(sym)
585585
else eraseNormalClassRef(tp)
586586
case tp: AppliedType =>
587587
val tycon = tp.tycon
@@ -791,7 +791,7 @@ class TypeErasure(sourceLanguage: SourceLanguage, semiEraseVCs: Boolean, isConst
791791
if (erasedVCRef.exists) return sigName(erasedVCRef)
792792
}
793793
if (defn.isSyntheticFunctionClass(sym))
794-
sigName(defn.erasedFunctionType(sym))
794+
sigName(defn.functionTypeErasure(sym))
795795
else
796796
val cls = normalizeClass(sym.asClass)
797797
val fullName =

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

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,16 @@ import StdNames.nme
88
import ast.untpd
99
import ast.tpd._
1010
import config.Config
11+
import Decorators.*
1112

1213
object ContextFunctionResults:
1314

1415
/** Annotate methods that have context function result types directly matched by context
1516
* closures on their right-hand side. Parameters to such closures will be integrated
1617
* as additional method parameters in erasure.
18+
*
19+
* A @ContextResultCount(n) annotation means that the method's result type
20+
* consists of a string of `n` nested context closures.
1721
*/
1822
def annotateContextResults(mdef: DefDef)(using Context): Unit =
1923
def contextResultCount(rhs: Tree, tp: Type): Int = tp match
@@ -50,6 +54,15 @@ object ContextFunctionResults:
5054
crCount
5155
case none => 0
5256

57+
/** True iff `ContextResultCount` is not zero and all context functions in the result
58+
* type are erased.
59+
*/
60+
def contextResultsAreErased(sym: Symbol)(using Context): Boolean =
61+
def allErased(tp: Type): Boolean = tp.dealias match
62+
case defn.ContextFunctionType(_, resTpe, isErased) => isErased && allErased(resTpe)
63+
case _ => true
64+
contextResultCount(sym) > 0 && allErased(sym.info.finalResultType)
65+
5366
/** Turn the first `crCount` context function types in the result type of `tp`
5467
* into the curried method types.
5568
*/

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

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import core.Types._
1212
import core.Names._
1313
import core.StdNames._
1414
import core.NameOps._
15-
import core.NameKinds.{AdaptedClosureName, BodyRetainerName}
15+
import core.NameKinds.{AdaptedClosureName, BodyRetainerName, ImplMethName}
1616
import core.Scopes.newScopeWith
1717
import core.Decorators._
1818
import core.Constants._
@@ -57,6 +57,17 @@ class Erasure extends Phase with DenotTransformer {
5757
case _ => false
5858
}
5959
}
60+
def erasedName =
61+
if ref.is(Flags.Method)
62+
&& contextResultsAreErased(ref.symbol)
63+
&& (ref.owner.is(Flags.Trait) || ref.symbol.allOverriddenSymbols.hasNext)
64+
then
65+
// Add a `$` to prevent this method from having the same signature
66+
// as a method it overrides. We need a bridge between the
67+
// two methods, so they are not allowed to already override after erasure.
68+
ImplMethName(ref.targetName.asTermName)
69+
else
70+
ref.targetName
6071

6172
assert(ctx.phase == this, s"transforming $ref at ${ctx.phase}")
6273
if (ref.symbol eq defn.ObjectClass) {
@@ -80,7 +91,7 @@ class Erasure extends Phase with DenotTransformer {
8091
val oldOwner = ref.owner
8192
val newOwner = if oldOwner == defn.AnyClass then defn.ObjectClass else oldOwner
8293
val oldName = ref.name
83-
val newName = ref.targetName
94+
val newName = erasedName
8495
val oldInfo = ref.info
8596
var newInfo = transformInfo(oldSymbol, oldInfo)
8697
val oldFlags = ref.flags
@@ -392,7 +403,6 @@ object Erasure {
392403
cast(tree, pt)
393404
end adaptToType
394405

395-
396406
/** The following code:
397407
*
398408
* val f: Function1[Int, Any] = x => ...
@@ -714,7 +724,7 @@ object Erasure {
714724
assert(sym.isConstructor, s"${sym.showLocated}")
715725
defn.specialErasure(owner)
716726
else if defn.isSyntheticFunctionClass(owner) then
717-
defn.erasedFunctionClass(owner)
727+
defn.functionTypeErasure(owner).typeSymbol
718728
else
719729
owner
720730

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -274,10 +274,10 @@ object GenericSignatures {
274274
jsig(erasedUnderlying, toplevel, primitiveOK)
275275
}
276276
else if (defn.isSyntheticFunctionClass(sym)) {
277-
val erasedSym = defn.erasedFunctionClass(sym)
277+
val erasedSym = defn.functionTypeErasure(sym).typeSymbol
278278
classSig(erasedSym, pre, if (erasedSym.typeParams.isEmpty) Nil else args)
279279
}
280-
else if (sym.isClass)
280+
else if sym.isClass then
281281
classSig(sym, pre, args)
282282
else
283283
jsig(erasure(tp), toplevel, primitiveOK)

0 commit comments

Comments
 (0)