Skip to content

Commit ce98496

Browse files
authored
Replace erased class modifiers with Erased base traits (#23447)
Based on #23419 Fixes #23406
2 parents 7c34cd4 + 9804d64 commit ce98496

File tree

168 files changed

+805
-614
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

168 files changed

+805
-614
lines changed

compiler/src/dotty/tools/dotc/ast/TreeInfo.scala

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -590,9 +590,13 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] =>
590590
case New(_) | Closure(_, _, _) =>
591591
Pure
592592
case TypeApply(fn, _) =>
593+
val sym = fn.symbol
593594
if tree.tpe.isInstanceOf[MethodOrPoly] then exprPurity(fn)
594-
else if fn.symbol == defn.QuotedTypeModule_of || fn.symbol == defn.Predef_classOf then Pure
595-
else if fn.symbol == defn.Compiletime_erasedValue && tree.tpe.dealias.isInstanceOf[ConstantType] then Pure
595+
else if sym == defn.QuotedTypeModule_of
596+
|| sym == defn.Predef_classOf
597+
|| sym == defn.Compiletime_erasedValue && tree.tpe.dealias.isInstanceOf[ConstantType]
598+
|| defn.capsErasedValueMethods.contains(sym)
599+
then Pure
596600
else Impure
597601
case Apply(fn, args) =>
598602
val factorPurity = minOf(exprPurity(fn), args.map(exprPurity))
@@ -636,6 +640,15 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] =>
636640

637641
def isPureBinding(tree: Tree)(using Context): Boolean = statPurity(tree) >= Pure
638642

643+
def isPureSyntheticCaseApply(sym: Symbol)(using Context): Boolean =
644+
sym.isAllOf(SyntheticMethod)
645+
&& sym.name == nme.apply
646+
&& sym.owner.is(Module)
647+
&& {
648+
val cls = sym.owner.companionClass
649+
cls.is(Case) && cls.isNoInitsRealClass
650+
}
651+
639652
/** Is the application `tree` with function part `fn` known to be pure?
640653
* Function value and arguments can still be impure.
641654
*/
@@ -647,6 +660,7 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] =>
647660

648661
tree.tpe.isInstanceOf[ConstantType] && tree.symbol != NoSymbol && isKnownPureOp(tree.symbol) // A constant expression with pure arguments is pure.
649662
|| fn.symbol.isStableMember && fn.symbol.isConstructor // constructors of no-inits classes are stable
663+
|| isPureSyntheticCaseApply(fn.symbol)
650664

651665
/** The purity level of this reference.
652666
* @return
@@ -655,8 +669,6 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] =>
655669
* or its type is a constant type
656670
* IdempotentPath if reference is lazy and stable
657671
* Impure otherwise
658-
* @DarkDimius: need to make sure that lazy accessor methods have Lazy and Stable
659-
* flags set.
660672
*/
661673
def refPurity(tree: Tree)(using Context): PurityLevel = {
662674
val sym = tree.symbol

compiler/src/dotty/tools/dotc/ast/tpd.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -301,7 +301,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
301301
assert(vparams.hasSameLengthAs(tp.paramNames) && vparams.head.isTerm)
302302
(vparams.asInstanceOf[List[TermSymbol]], remaining1)
303303
case nil =>
304-
(tp.paramNames.lazyZip(tp.paramInfos).lazyZip(tp.erasedParams).map(valueParam), Nil)
304+
(tp.paramNames.lazyZip(tp.paramInfos).lazyZip(tp.paramErasureStatuses).map(valueParam), Nil)
305305
val (rtp, paramss) = recur(tp.instantiate(vparams.map(_.termRef)), remaining1)
306306
(rtp, vparams :: paramss)
307307
case _ =>

compiler/src/dotty/tools/dotc/cc/CaptureOps.scala

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -495,15 +495,13 @@ extension (sym: Symbol)
495495

496496
/** Does this symbol allow results carrying the universal capability?
497497
* Currently this is true only for function type applies (since their
498-
* results are unboxed) and `erasedValue` since this function is magic in
499-
* that is allows to conjure global capabilies from nothing (aside: can we find a
500-
* more controlled way to achieve this?).
498+
* results are unboxed) and `caps.{$internal,unsafe}.erasedValue` since
499+
* these function are magic in that they allow to conjure global capabilies from nothing.
501500
* But it could be generalized to other functions that so that they can take capability
502501
* classes as arguments.
503502
*/
504503
def allowsRootCapture(using Context): Boolean =
505-
sym == defn.Compiletime_erasedValue
506-
|| defn.isFunctionClass(sym.maybeOwner)
504+
defn.capsErasedValueMethods.contains(sym) || defn.isFunctionClass(sym.maybeOwner)
507505

508506
/** When applying `sym`, would the result type be unboxed?
509507
* This is the case if the result type contains a top-level reference to an enclosing

compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -576,7 +576,7 @@ class CheckCaptures extends Recheck, SymTransformer:
576576
* @param args the type arguments
577577
*/
578578
def disallowCapInTypeArgs(fn: Tree, sym: Symbol, args: List[Tree])(using Context): Unit =
579-
def isExempt = sym.isTypeTestOrCast || sym == defn.Compiletime_erasedValue
579+
def isExempt = sym.isTypeTestOrCast || defn.capsErasedValueMethods.contains(sym)
580580
if !isExempt then
581581
val paramNames = atPhase(thisPhase.prev):
582582
fn.tpe.widenDealias match

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

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,6 @@ object CheckRealizable {
4848

4949
def boundsRealizability(tp: Type)(using Context): Realizability =
5050
new CheckRealizable().boundsRealizability(tp)
51-
52-
private val LateInitializedFlags = Lazy | Erased
5351
}
5452

5553
/** Compute realizability status.
@@ -72,7 +70,7 @@ class CheckRealizable(using Context) {
7270
/** Is symbol's definitition a lazy or erased val?
7371
* (note we exclude modules here, because their realizability is ensured separately)
7472
*/
75-
private def isLateInitialized(sym: Symbol) = sym.isOneOf(LateInitializedFlags, butNot = Module)
73+
private def isLateInitialized(sym: Symbol) = sym.is(Lazy, butNot = Module)
7674

7775
/** The realizability status of given type `tp`*/
7876
def realizability(tp: Type): Realizability = tp.dealias match {
@@ -184,7 +182,7 @@ class CheckRealizable(using Context) {
184182
private def memberRealizability(tp: Type) = {
185183
def checkField(sofar: Realizability, fld: SingleDenotation): Realizability =
186184
sofar andAlso {
187-
if (checkedFields.contains(fld.symbol) || fld.symbol.isOneOf(Private | Mutable | LateInitializedFlags))
185+
if (checkedFields.contains(fld.symbol) || fld.symbol.isOneOf(Private | Mutable | Lazy))
188186
// if field is private it cannot be part of a visible path
189187
// if field is mutable it cannot be part of a path
190188
// if field is lazy or erased it does not need to be initialized when the owning object is

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

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,7 @@ class Definitions {
266266
@tu lazy val CompiletimeOpsDoubleModuleClass: Symbol = requiredModule("scala.compiletime.ops.double").moduleClass
267267
@tu lazy val CompiletimeOpsStringModuleClass: Symbol = requiredModule("scala.compiletime.ops.string").moduleClass
268268
@tu lazy val CompiletimeOpsBooleanModuleClass: Symbol = requiredModule("scala.compiletime.ops.boolean").moduleClass
269+
@tu lazy val ErasedClass: ClassSymbol = requiredClass("scala.compiletime.Erased")
269270

270271
/** Note: We cannot have same named methods defined in Object and Any (and AnyVal, for that matter)
271272
* because after erasure the Any and AnyVal references get remapped to the Object methods
@@ -543,7 +544,7 @@ class Definitions {
543544
// needed as a synthetic class because Scala 2.x refers to it in classfiles
544545
// but does not define it as an explicit class.
545546
val cls = enterCompleteClassSymbol(
546-
ScalaPackageClass, tpnme.Singleton, PureInterfaceCreationFlags | Final | Erased,
547+
ScalaPackageClass, tpnme.Singleton, PureInterfaceCreationFlags | Final,
547548
List(AnyType))
548549
enterTypeField(cls, tpnme.Self, Deferred, cls.info.decls.openForMutations)
549550
cls
@@ -1004,16 +1005,18 @@ class Definitions {
10041005
@tu lazy val Caps_Capability: ClassSymbol = requiredClass("scala.caps.Capability")
10051006
@tu lazy val Caps_CapSet: ClassSymbol = requiredClass("scala.caps.CapSet")
10061007
@tu lazy val CapsInternalModule: Symbol = requiredModule("scala.caps.internal")
1008+
@tu lazy val Caps_erasedValue: Symbol = CapsInternalModule.requiredMethod("erasedValue")
10071009
@tu lazy val CapsUnsafeModule: Symbol = requiredModule("scala.caps.unsafe")
10081010
@tu lazy val Caps_unsafeAssumePure: Symbol = CapsUnsafeModule.requiredMethod("unsafeAssumePure")
10091011
@tu lazy val Caps_unsafeAssumeSeparate: Symbol = CapsUnsafeModule.requiredMethod("unsafeAssumeSeparate")
1012+
@tu lazy val Caps_unsafeErasedValue: Symbol = CapsUnsafeModule.requiredMethod("unsafeErasedValue")
10101013
@tu lazy val Caps_ContainsTrait: TypeSymbol = CapsModule.requiredType("Contains")
10111014
@tu lazy val Caps_ContainsModule: Symbol = requiredModule("scala.caps.Contains")
10121015
@tu lazy val Caps_containsImpl: TermSymbol = Caps_ContainsModule.requiredMethod("containsImpl")
10131016
@tu lazy val Caps_Mutable: ClassSymbol = requiredClass("scala.caps.Mutable")
10141017
@tu lazy val Caps_SharedCapability: ClassSymbol = requiredClass("scala.caps.SharedCapability")
10151018

1016-
@tu lazy val PureClass: Symbol = requiredClass("scala.Pure")
1019+
@tu lazy val PureClass: ClassSymbol = requiredClass("scala.Pure")
10171020

10181021
// Annotation base classes
10191022
@tu lazy val AnnotationClass: ClassSymbol = requiredClass("scala.annotation.Annotation")
@@ -1563,6 +1566,11 @@ class Definitions {
15631566
@tu lazy val pureSimpleClasses =
15641567
Set(StringClass, NothingClass, NullClass) ++ ScalaValueClasses()
15651568

1569+
@tu lazy val capsErasedValueMethods =
1570+
Set(Caps_erasedValue, Caps_unsafeErasedValue)
1571+
@tu lazy val erasedValueMethods =
1572+
capsErasedValueMethods + Compiletime_erasedValue
1573+
15661574
@tu lazy val AbstractFunctionType: Array[TypeRef] = mkArityArray("scala.runtime.AbstractFunction", MaxImplementedFunctionArity, 0).asInstanceOf[Array[TypeRef]]
15671575
val AbstractFunctionClassPerRun: PerRun[Array[Symbol]] = new PerRun(AbstractFunctionType.map(_.symbol.asClass))
15681576
def AbstractFunctionClass(n: Int)(using Context): Symbol = AbstractFunctionClassPerRun()(using ctx)(n)
@@ -2006,7 +2014,9 @@ class Definitions {
20062014

20072015
/** A allowlist of Scala-2 classes that are known to be pure */
20082016
def isAssuredNoInits(sym: Symbol): Boolean =
2009-
(sym `eq` SomeClass) || isTupleClass(sym)
2017+
(sym `eq` SomeClass)
2018+
|| isTupleClass(sym)
2019+
|| sym.is(Module) && isAssuredNoInits(sym.companionClass)
20102020

20112021
/** If `cls` is Tuple1..Tuple22, add the corresponding *: type as last parent to `parents` */
20122022
def adjustForTuple(cls: ClassSymbol, tparams: List[TypeSymbol], parents: List[Type]): List[Type] = {

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

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -529,18 +529,18 @@ object Flags {
529529
val RetainedModuleValAndClassFlags: FlagSet =
530530
AccessFlags | Package | Case |
531531
Synthetic | JavaDefined | JavaStatic | Artifact |
532-
Lifted | MixedIn | Specialized | PhantomSymbol | Invisible | Erased
532+
Lifted | MixedIn | Specialized | PhantomSymbol | Invisible
533533

534534
/** Flags that can apply to a module val */
535535
val RetainedModuleValFlags: FlagSet = RetainedModuleValAndClassFlags |
536-
Override | Final | Method | Implicit | Given | Lazy |
536+
Override | Final | Method | Implicit | Given | Lazy | Erased |
537537
Accessor | AbsOverride | StableRealizable | Captured | Synchronized | Transparent
538538

539539
/** Flags that can apply to a module class */
540540
val RetainedModuleClassFlags: FlagSet = RetainedModuleValAndClassFlags | Enum
541541

542542
/** Flags retained in term export forwarders */
543-
val RetainedExportTermFlags = Infix | Given | Implicit | Inline | Transparent | Erased | HasDefaultParams | NoDefaultParams | ExtensionMethod
543+
val RetainedExportTermFlags = Infix | Given | Implicit | Inline | Transparent | HasDefaultParams | NoDefaultParams | ExtensionMethod
544544

545545
/** Flags retained in parameters of term export forwarders */
546546
val RetainedExportTermParamFlags = Given | Implicit | Erased | HasDefault | Inline
@@ -569,7 +569,6 @@ object Flags {
569569
val EnumCase: FlagSet = Case | Enum
570570
val CovariantLocal: FlagSet = Covariant | Local // A covariant type parameter
571571
val ContravariantLocal: FlagSet = Contravariant | Local // A contravariant type parameter
572-
val EffectivelyErased = PhantomSymbol | Erased
573572
val ConstructorProxyModule: FlagSet = PhantomSymbol | Module
574573
val CaptureParam: FlagSet = PhantomSymbol | StableRealizable | Synthetic
575574
val DefaultParameter: FlagSet = HasDefault | Param // A Scala 2x default parameter

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

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1051,8 +1051,13 @@ object SymDenotations {
10511051
&& owner.ne(defn.StringContextClass)
10521052

10531053
/** An erased value or an erased inline method or field */
1054+
def isErased(using Context): Boolean =
1055+
is(Erased) || defn.erasedValueMethods.contains(symbol)
1056+
1057+
/** An erased value, a phantom symbol or an erased inline method or field */
10541058
def isEffectivelyErased(using Context): Boolean =
1055-
isOneOf(EffectivelyErased)
1059+
isErased
1060+
|| is(PhantomSymbol)
10561061
|| is(Inline) && !isRetainedInline && !hasAnnotation(defn.ScalaStaticAnnot)
10571062

10581063
/** Is this a member that will become public in the generated binary */

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2387,7 +2387,7 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
23872387
formals2.isEmpty
23882388
}
23892389
// If methods have erased parameters, then the erased parameters must match
2390-
val erasedValid = (!tp1.hasErasedParams && !tp2.hasErasedParams) || (tp1.erasedParams == tp2.erasedParams)
2390+
val erasedValid = (!tp1.hasErasedParams && !tp2.hasErasedParams) || (tp1.paramErasureStatuses == tp2.paramErasureStatuses)
23912391

23922392
erasedValid && loop(tp1.paramInfos, tp2.paramInfos)
23932393
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -697,7 +697,7 @@ class TypeErasure(sourceLanguage: SourceLanguage, semiEraseVCs: Boolean, isConst
697697
val (names, formals0) = if tp.hasErasedParams then
698698
tp.paramNames
699699
.zip(tp.paramInfos)
700-
.zip(tp.erasedParams)
700+
.zip(tp.paramErasureStatuses)
701701
.collect{ case (param, isErased) if !isErased => param }
702702
.unzip
703703
else (tp.paramNames, tp.paramInfos)

0 commit comments

Comments
 (0)