From 5fce9c364ea2fe498bd6115949cf8025e64584a4 Mon Sep 17 00:00:00 2001 From: Guillaume Martres Date: Wed, 18 May 2022 20:26:44 +0200 Subject: [PATCH] Fix lack of type avoidance in argument lifting We need to manually increase the nestingLevel of symbols created by EtaExpansion#lift to compensate for the fact that they will end up in a block. Fixes #15174. --- .../src/dotty/tools/dotc/core/Symbols.scala | 7 ++-- .../dotty/tools/dotc/typer/EtaExpansion.scala | 5 ++- tests/pos/i15174.scala | 34 +++++++++++++++++++ 3 files changed, 42 insertions(+), 4 deletions(-) create mode 100644 tests/pos/i15174.scala diff --git a/compiler/src/dotty/tools/dotc/core/Symbols.scala b/compiler/src/dotty/tools/dotc/core/Symbols.scala index 7c3f1ae36017..73fbcca6f6ed 100644 --- a/compiler/src/dotty/tools/dotc/core/Symbols.scala +++ b/compiler/src/dotty/tools/dotc/core/Symbols.scala @@ -512,14 +512,15 @@ object Symbols { // ---- Symbol creation methods ---------------------------------- /** Create a symbol from its fields (info may be lazy) */ - def newSymbol[N <: Name]( + def newSymbol[N <: Name](using Context)( owner: Symbol, name: N, flags: FlagSet, info: Type, privateWithin: Symbol = NoSymbol, - coord: Coord = NoCoord)(using Context): Symbol { type ThisName = N } = { - val sym = new Symbol(coord, ctx.base.nextSymId, ctx.nestingLevel).asInstanceOf[Symbol { type ThisName = N }] + coord: Coord = NoCoord, + nestingLevel: Int = ctx.nestingLevel): Symbol { type ThisName = N } = { + val sym = new Symbol(coord, ctx.base.nextSymId, nestingLevel).asInstanceOf[Symbol { type ThisName = N }] val denot = SymDenotation(sym, owner, name, flags, info, privateWithin) sym.denot = denot sym diff --git a/compiler/src/dotty/tools/dotc/typer/EtaExpansion.scala b/compiler/src/dotty/tools/dotc/typer/EtaExpansion.scala index 0d955651dbd9..14edd1947060 100644 --- a/compiler/src/dotty/tools/dotc/typer/EtaExpansion.scala +++ b/compiler/src/dotty/tools/dotc/typer/EtaExpansion.scala @@ -49,7 +49,10 @@ abstract class Lifter { // don't instantiate here, as the type params could be further constrained, see tests/pos/pickleinf.scala var liftedType = expr.tpe.widen.deskolemized if (liftedFlags.is(Method)) liftedType = ExprType(liftedType) - val lifted = newSymbol(ctx.owner, name, liftedFlags | Synthetic, liftedType, coord = spanCoord(expr.span)) + val lifted = newSymbol(ctx.owner, name, liftedFlags | Synthetic, liftedType, coord = spanCoord(expr.span), + // Lifted definitions will be added to a local block, so they need to be + // at a higher nesting level to prevent leaks. See tests/pos/i15174.scala + nestingLevel = ctx.nestingLevel + 1) defs += liftedDef(lifted, expr) .withSpan(expr.span) .changeNonLocalOwners(lifted) diff --git a/tests/pos/i15174.scala b/tests/pos/i15174.scala new file mode 100644 index 000000000000..6c592e73f0fd --- /dev/null +++ b/tests/pos/i15174.scala @@ -0,0 +1,34 @@ +trait Error +sealed abstract class Codec[A] { + type AvroType + def encode(a: A): Either[Error, AvroType] + // def decode(value: Any): Either[Error, A] +} + +object Codec { + type Aux[AvroType0, A] = Codec[A] { + type AvroType = AvroType0 + } + + final def instance[AvroType0, A]( + encode: A => Either[Error, AvroType0], + // decode: Any => Either[Error, A] + ): Codec.Aux[AvroType0, A] = ??? + + implicit final def option[A](implicit codec: Codec[A]): Codec[Option[A]] = ??? + given Codec.Aux[Int, Int] = ??? +} + + +@main def test() = { + implicit val codec: Codec[Option[Int]] = + Codec.instance( + Codec.option[Int].encode + // expands to: + // { + // val a: Codec[Option[Int]] = Codec.option[Int](Codec.given_Aux_Int_Int) + // a.encode + // }, + // Codec.option[Int].decode + ) +}