diff --git a/compiler/src/dotty/tools/dotc/core/Scopes.scala b/compiler/src/dotty/tools/dotc/core/Scopes.scala index 7df5a7fa3c09..d6387ea1fe46 100644 --- a/compiler/src/dotty/tools/dotc/core/Scopes.scala +++ b/compiler/src/dotty/tools/dotc/core/Scopes.scala @@ -159,19 +159,28 @@ object Scopes { } /** The scope that keeps only those symbols from this scope that match the - * given predicates. If all symbols match, returns the scope itself, otherwise - * a copy with the matching symbols. + * given predicates, renamed with the given rename function. + * If renaming is not needed for a symbol, the rename function should return `null`. + * If all symbols match and none are renamed, returns the scope itself, otherwise + * a copy with the matching and renamed symbols. */ - final def filteredScope(p: Symbol => Boolean)(using Context): Scope = { + final def filteredScope( + keep: Symbol => Boolean, + rename: Symbol => Name | Null = _ => null)(using Context): Scope = var result: MutableScope | Null = null - for (sym <- iterator) - if (!p(sym)) { - if (result == null) result = cloneScope + for sym <- iterator do + def drop() = + if result == null then result = cloneScope result.nn.unlink(sym) - } + if keep(sym) then + val newName = rename(sym) + if newName != null then + drop() + result.nn.enter(newName, sym) + else + drop() // TODO: improve flow typing to handle this case - if (result == null) this else result.uncheckedNN - } + if result == null then this else result.uncheckedNN def implicitDecls(using Context): List[TermRef] = Nil diff --git a/compiler/src/dotty/tools/dotc/core/TypeErasure.scala b/compiler/src/dotty/tools/dotc/core/TypeErasure.scala index 48559787c6a1..0474aff4087a 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeErasure.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeErasure.scala @@ -725,14 +725,14 @@ class TypeErasure(sourceLanguage: SourceLanguage, semiEraseVCs: Boolean, isConst tr1 :: trs1.filterNot(_.isAnyRef) case nil => nil } - var erasedDecls = decls.filteredScope(sym => !sym.isType || sym.isClass).openForMutations - for dcl <- erasedDecls.iterator do - if dcl.lastKnownDenotation.unforcedAnnotation(defn.TargetNameAnnot).isDefined - && dcl.targetName != dcl.name - then - if erasedDecls eq decls then erasedDecls = erasedDecls.cloneScope - erasedDecls.unlink(dcl) - erasedDecls.enter(dcl.targetName, dcl) + val erasedDecls = decls.filteredScope( + keep = sym => !sym.isType || sym.isClass, + rename = sym => + if sym.lastKnownDenotation.unforcedAnnotation(defn.TargetNameAnnot).isDefined + && sym.targetName != sym.name + then sym.targetName + else null + ) val selfType1 = if cls.is(Module) then cls.sourceModule.termRef else NoType tp.derivedClassInfo(NoPrefix, erasedParents, erasedDecls, selfType1) // can't replace selftype by NoType because this would lose the sourceModule link @@ -814,7 +814,8 @@ class TypeErasure(sourceLanguage: SourceLanguage, semiEraseVCs: Boolean, isConst eraseResult(tp1.resultType) match case rt: MethodType => rt case rt => MethodType(Nil, Nil, rt) - case tp1 => this(tp1) + case tp1 => + this(tp1) private def eraseDerivedValueClass(tp: Type)(using Context): Type = { val cls = tp.classSymbol.asClass diff --git a/tests/pos/i19530.scala b/tests/pos/i19530.scala new file mode 100644 index 000000000000..01c3cc50a12d --- /dev/null +++ b/tests/pos/i19530.scala @@ -0,0 +1,3 @@ +object A { + def x = classOf[scala.Singleton] +} \ No newline at end of file