Skip to content

Fix #9965: Properly handle class type parameters when copying symbols #10915

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 4 commits into from
Jan 4, 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
32 changes: 16 additions & 16 deletions compiler/src/dotty/tools/dotc/ast/TreeTypeMap.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package dotc
package ast

import core._
import Types._, Contexts._
import Types._, Contexts._, Flags._
import Symbols._, Annotations._, Trees._, Symbols._, Constants.Constant
import Decorators._
import dotty.tools.dotc.transform.SymUtils._
Expand Down Expand Up @@ -178,25 +178,25 @@ class TreeTypeMap(
* and return a treemap that contains the substitution
* between original and mapped symbols.
*/
def withMappedSyms(syms: List[Symbol], mapAlways: Boolean = false): TreeTypeMap =
withMappedSyms(syms, mapSymbols(syms, this, mapAlways))
def withMappedSyms(syms: List[Symbol]): TreeTypeMap =
withMappedSyms(syms, mapSymbols(syms, this))

/** The tree map with the substitution between originals `syms`
* and mapped symbols `mapped`. Also goes into mapped classes
* and substitutes their declarations.
*/
def withMappedSyms(syms: List[Symbol], mapped: List[Symbol]): TreeTypeMap = {
val symsChanged = syms ne mapped
val substMap = withSubstitution(syms, mapped)
val fullMap = mapped.filter(_.isClass).foldLeft(substMap) { (tmap, cls) =>
val origDcls = cls.info.decls.toList
val mappedDcls = mapSymbols(origDcls, tmap)
val tmap1 = tmap.withMappedSyms(origDcls, mappedDcls)
if (symsChanged)
def withMappedSyms(syms: List[Symbol], mapped: List[Symbol]): TreeTypeMap =
if syms eq mapped then this
else
val substMap = withSubstitution(syms, mapped)
lazy val origCls = mapped.zip(syms).filter(_._1.isClass).toMap
mapped.filter(_.isClass).foldLeft(substMap) { (tmap, cls) =>
val origDcls = cls.info.decls.toList.filterNot(_.is(TypeParam))
val mappedDcls = mapSymbols(origDcls, tmap, mapAlways = true)
val tmap1 = tmap.withMappedSyms(
origCls(cls).typeParams ::: origDcls,
cls.typeParams ::: mappedDcls)
origDcls.lazyZip(mappedDcls).foreach(cls.asClass.replace)
tmap1
}
if (symsChanged || (fullMap eq substMap)) fullMap
else withMappedSyms(syms, mapAlways = true)
}
tmap1
}
}
36 changes: 26 additions & 10 deletions compiler/src/dotty/tools/dotc/core/Symbols.scala
Original file line number Diff line number Diff line change
Expand Up @@ -811,22 +811,38 @@ object Symbols {
val ttmap1 = ttmap.withSubstitution(originals, copies)
originals.lazyZip(copies) foreach { (original, copy) =>
val odenot = original.denot
val oinfo = original.info match {
case ClassInfo(pre, _, parents, decls, selfInfo) =>
assert(original.isClass)
ClassInfo(pre, copy.asClass, parents, decls.cloneScope, selfInfo)
case oinfo => oinfo
}
val completer = new LazyType:

def complete(denot: SymDenotation)(using Context): Unit =

val oinfo = original.info match
case ClassInfo(pre, _, parents, decls, selfInfo) =>
assert(original.isClass)
val otypeParams = original.typeParams
if otypeParams.isEmpty then
ClassInfo(pre, copy.asClass, parents, decls.cloneScope, selfInfo)
else
// copy type params, enter other definitions unchanged
// type parameters need to be copied early, since other type
// computations depend on them.
val decls1 = newScope
val newTypeParams = mapSymbols(original.typeParams, ttmap1, mapAlways = true)
newTypeParams.foreach(decls1.enter)
for sym <- decls do if !sym.is(TypeParam) then decls1.enter(sym)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
for sym <- decls do if !sym.is(TypeParam) then decls1.enter(sym)
for sym <- decls if !sym.is(TypeParam) do decls1.enter(sym)

val parents1 = parents.map(_.substSym(otypeParams, newTypeParams))
val selfInfo1 = selfInfo match
case selfInfo: Type => selfInfo.substSym(otypeParams, newTypeParams)
case _ => selfInfo
ClassInfo(pre, copy.asClass, parents1, decls1, selfInfo1)
case oinfo => oinfo

val completer = new LazyType {
def complete(denot: SymDenotation)(using Context): Unit = {
denot.info = oinfo // needed as otherwise we won't be able to go from Sym -> parents & etc
// Note that this is a hack, but hack commonly used in Dotty
// The same thing is done by other completers all the time
denot.info = ttmap1.mapType(oinfo)
denot.annotations = odenot.annotations.mapConserve(ttmap1.apply)
}
}

end completer

copy.denot = odenot.copySymDenotation(
symbol = copy,
Expand Down
19 changes: 19 additions & 0 deletions tests/pos/i9965.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
class D[T]

class C {
def f() = {
locally {
class dd[U] extends D[U] {
val xx = 1
}
class ee[V] extends dd[(V, V)]
def d[V]: dd[V] = new dd[V]
g[D[Int]](d[Int])
g[D[(Int, Int)]](new ee[Int])
}
}

inline def locally[T](inline body: T): T = body

def g[T](x: T): T = x
}