diff --git a/compiler/src/dotty/tools/dotc/ast/TreeTypeMap.scala b/compiler/src/dotty/tools/dotc/ast/TreeTypeMap.scala index a3c1484d4e62..1d98d643ad89 100644 --- a/compiler/src/dotty/tools/dotc/ast/TreeTypeMap.scala +++ b/compiler/src/dotty/tools/dotc/ast/TreeTypeMap.scala @@ -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._ @@ -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 + } } diff --git a/compiler/src/dotty/tools/dotc/core/Symbols.scala b/compiler/src/dotty/tools/dotc/core/Symbols.scala index b1939261084f..016a8cabaf59 100644 --- a/compiler/src/dotty/tools/dotc/core/Symbols.scala +++ b/compiler/src/dotty/tools/dotc/core/Symbols.scala @@ -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) + 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, diff --git a/tests/pos/i9965.scala b/tests/pos/i9965.scala new file mode 100644 index 000000000000..8e2ceb414613 --- /dev/null +++ b/tests/pos/i9965.scala @@ -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 +} \ No newline at end of file