From 7c542fc651b5ea83a681382e418fb90f8dad493b Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 26 Dec 2020 10:59:49 +0100 Subject: [PATCH 1/4] Fix #9965: Properly handle class type parameters when copying symbols --- .../dotty/tools/dotc/ast/TreeTypeMap.scala | 15 +++++--- .../src/dotty/tools/dotc/core/Symbols.scala | 36 +++++++++++++------ tests/pos/i9965.scala | 19 ++++++++++ 3 files changed, 56 insertions(+), 14 deletions(-) create mode 100644 tests/pos/i9965.scala diff --git a/compiler/src/dotty/tools/dotc/ast/TreeTypeMap.scala b/compiler/src/dotty/tools/dotc/ast/TreeTypeMap.scala index a3c1484d4e62..6fb74673f25d 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._ @@ -187,11 +187,18 @@ class TreeTypeMap( */ def withMappedSyms(syms: List[Symbol], mapped: List[Symbol]): TreeTypeMap = { val symsChanged = syms ne mapped - val substMap = withSubstitution(syms, mapped) + val origTypeParams = syms.filter(_.isClass).flatMap(_.typeParams) + val mappedTypeParams = mapped.filter(_.isClass).flatMap(_.typeParams) + assert(origTypeParams.hasSameLengthAs(mappedTypeParams)) + val substMap = withSubstitution(syms ++ origTypeParams, mapped ++ mappedTypeParams) val fullMap = mapped.filter(_.isClass).foldLeft(substMap) { (tmap, cls) => - val origDcls = cls.info.decls.toList + val origDcls = cls.info.decls.toList.filterNot(_.is(TypeParam)) val mappedDcls = mapSymbols(origDcls, tmap) - val tmap1 = tmap.withMappedSyms(origDcls, mappedDcls) + // type parameters were already copied in the `mapSymbols` call which produced `mapped`. + val tmap1 = tmap +// .withSubstitution(origTypeParams, cls.typeParams) + // type parameters were already mapped in the `mapSymbols` call which produced `mapped`. + .withMappedSyms(origDcls, mappedDcls) if (symsChanged) origDcls.lazyZip(mappedDcls).foreach(cls.asClass.replace) 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 From f6ffe67cb732bad545226b2476c44cae6269457b Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 26 Dec 2020 11:47:15 +0100 Subject: [PATCH 2/4] Simplify TreeTypeMap#withMappedSyms --- .../src/dotty/tools/dotc/ast/TreeTypeMap.scala | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/TreeTypeMap.scala b/compiler/src/dotty/tools/dotc/ast/TreeTypeMap.scala index 6fb74673f25d..98870399f20f 100644 --- a/compiler/src/dotty/tools/dotc/ast/TreeTypeMap.scala +++ b/compiler/src/dotty/tools/dotc/ast/TreeTypeMap.scala @@ -187,23 +187,19 @@ class TreeTypeMap( */ def withMappedSyms(syms: List[Symbol], mapped: List[Symbol]): TreeTypeMap = { val symsChanged = syms ne mapped - val origTypeParams = syms.filter(_.isClass).flatMap(_.typeParams) - val mappedTypeParams = mapped.filter(_.isClass).flatMap(_.typeParams) - assert(origTypeParams.hasSameLengthAs(mappedTypeParams)) - val substMap = withSubstitution(syms ++ origTypeParams, mapped ++ mappedTypeParams) + val substMap = withSubstitution(syms, mapped) + lazy val origCls = mapped.zip(syms).filter(_._1.isClass).toMap val fullMap = mapped.filter(_.isClass).foldLeft(substMap) { (tmap, cls) => val origDcls = cls.info.decls.toList.filterNot(_.is(TypeParam)) val mappedDcls = mapSymbols(origDcls, tmap) - // type parameters were already copied in the `mapSymbols` call which produced `mapped`. - val tmap1 = tmap -// .withSubstitution(origTypeParams, cls.typeParams) - // type parameters were already mapped in the `mapSymbols` call which produced `mapped`. - .withMappedSyms(origDcls, mappedDcls) - if (symsChanged) + val tmap1 = tmap.withMappedSyms( + origCls(cls).typeParams ::: origDcls, + cls.typeParams ::: mappedDcls) + if symsChanged then origDcls.lazyZip(mappedDcls).foreach(cls.asClass.replace) tmap1 } - if (symsChanged || (fullMap eq substMap)) fullMap + if symsChanged || (fullMap eq substMap) then fullMap else withMappedSyms(syms, mapAlways = true) } } From c5348079829cff2b02d1a8058342ff12bf8f872d Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 26 Dec 2020 11:48:33 +0100 Subject: [PATCH 3/4] Simplify TreeTypeMap#withMappedSyms more --- compiler/src/dotty/tools/dotc/ast/TreeTypeMap.scala | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/TreeTypeMap.scala b/compiler/src/dotty/tools/dotc/ast/TreeTypeMap.scala index 98870399f20f..64400e9c2b41 100644 --- a/compiler/src/dotty/tools/dotc/ast/TreeTypeMap.scala +++ b/compiler/src/dotty/tools/dotc/ast/TreeTypeMap.scala @@ -185,11 +185,11 @@ class TreeTypeMap( * and mapped symbols `mapped`. Also goes into mapped classes * and substitutes their declarations. */ - def withMappedSyms(syms: List[Symbol], mapped: List[Symbol]): TreeTypeMap = { + def withMappedSyms(syms: List[Symbol], mapped: List[Symbol]): TreeTypeMap = val symsChanged = syms ne mapped val substMap = withSubstitution(syms, mapped) lazy val origCls = mapped.zip(syms).filter(_._1.isClass).toMap - val fullMap = mapped.filter(_.isClass).foldLeft(substMap) { (tmap, cls) => + mapped.filter(_.isClass).foldLeft(substMap) { (tmap, cls) => val origDcls = cls.info.decls.toList.filterNot(_.is(TypeParam)) val mappedDcls = mapSymbols(origDcls, tmap) val tmap1 = tmap.withMappedSyms( @@ -199,7 +199,4 @@ class TreeTypeMap( origDcls.lazyZip(mappedDcls).foreach(cls.asClass.replace) tmap1 } - if symsChanged || (fullMap eq substMap) then fullMap - else withMappedSyms(syms, mapAlways = true) - } } From 1687717d5d5e42fdafd0aae180801aaf9bb20a70 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 26 Dec 2020 11:58:34 +0100 Subject: [PATCH 4/4] Simplify TreeTypeMap further --- .../dotty/tools/dotc/ast/TreeTypeMap.scala | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/TreeTypeMap.scala b/compiler/src/dotty/tools/dotc/ast/TreeTypeMap.scala index 64400e9c2b41..1d98d643ad89 100644 --- a/compiler/src/dotty/tools/dotc/ast/TreeTypeMap.scala +++ b/compiler/src/dotty/tools/dotc/ast/TreeTypeMap.scala @@ -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) - 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) - val tmap1 = tmap.withMappedSyms( - origCls(cls).typeParams ::: origDcls, - cls.typeParams ::: mappedDcls) - if symsChanged then + 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 - } + tmap1 + } }