From 9ada968524601f96c9866244ea663e7e34f04ff3 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 23 Apr 2021 12:00:01 +0200 Subject: [PATCH 1/2] Don't use `_` as a binder for abstract type arguments in patterns Use a freshly generated WildCardParam name instead. This is necessary so that we can pickle a reference to that symbol. WildcardParam names no longer leak into error messages, unless -Yprint-debug is set. Fixes #12169 --- .../dotty/tools/dotc/printing/PlainPrinter.scala | 2 +- .../dotty/tools/dotc/printing/RefinedPrinter.scala | 8 +++----- compiler/src/dotty/tools/dotc/typer/Typer.scala | 11 ++++++----- tests/neg-custom-args/kind-projector.check | 2 +- tests/neg/i4986c.check | 2 +- tests/pos/i12169.scala | 13 +++++++++++++ 6 files changed, 25 insertions(+), 13 deletions(-) create mode 100644 tests/pos/i12169.scala diff --git a/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala b/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala index 5f21ea284243..45ba09f9d82d 100644 --- a/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala @@ -257,7 +257,7 @@ class PlainPrinter(_ctx: Context) extends Printer { Text(lam.paramNames.lazyZip(lam.paramInfos).map(paramText), ", ") } - protected def ParamRefNameString(name: Name): String = name.toString + protected def ParamRefNameString(name: Name): String = nameString(name) protected def ParamRefNameString(param: ParamRef): String = ParamRefNameString(param.binder.paramNames(param.paramNum)) diff --git a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala index dcd6564d192d..541cb64ecc41 100644 --- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -20,6 +20,7 @@ import typer.ProtoTypes._ import Trees._ import TypeApplications._ import Decorators._ +import NameKinds.WildcardParamName import util.Chars.isOperatorPart import transform.TypeUtils._ import transform.SymUtils._ @@ -76,8 +77,8 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { } override def nameString(name: Name): String = - if ctx.settings.YdebugNames.value - then name.debugString + if ctx.settings.YdebugNames.value then name.debugString + else if name.isTypeName && name.is(WildcardParamName) && !printDebug then "_" else super.nameString(name) override protected def simpleNameString(sym: Symbol): String = @@ -963,9 +964,6 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { def optText[T >: Untyped](tree: List[Tree[T]])(encl: Text => Text): Text = if (tree.exists(!_.isEmpty)) encl(blockText(tree)) else "" - override protected def ParamRefNameString(name: Name): String = - name.toString - override protected def treatAsTypeParam(sym: Symbol): Boolean = sym.is(TypeParam) override protected def treatAsTypeArg(sym: Symbol): Boolean = diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index e91269bf0965..811646b8153d 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -1560,9 +1560,9 @@ class Typer extends Namer super.transform(trt.withType(stripTypeVars(trt.tpe))) match { case b: Bind => val sym = b.symbol - if (sym.name != tpnme.WILDCARD) - if (ctx.scope.lookup(b.name) == NoSymbol) ctx.enter(sym) - else report.error(new DuplicateBind(b, cdef), b.srcPos) + assert(sym.name != tpnme.WILDCARD) + if ctx.scope.lookup(b.name) == NoSymbol then ctx.enter(sym) + else report.error(new DuplicateBind(b, cdef), b.srcPos) if (!ctx.isAfterTyper) { val bounds = ctx.gadt.fullBounds(sym) if (bounds != null) sym.info = bounds @@ -1969,8 +1969,9 @@ class Typer extends Namer //val ptt = if (lo.isEmpty && hi.isEmpty) pt else if (ctx.isAfterTyper) tree1 else { - val wildcardSym = newPatternBoundSymbol(tpnme.WILDCARD, tree1.tpe & pt, tree.span) - untpd.Bind(tpnme.WILDCARD, tree1).withType(wildcardSym.typeRef) + val boundName = WildcardParamName.fresh().toTypeName + val wildcardSym = newPatternBoundSymbol(boundName, tree1.tpe & pt, tree.span) + untpd.Bind(boundName, tree1).withType(wildcardSym.typeRef) } else tree1 } diff --git a/tests/neg-custom-args/kind-projector.check b/tests/neg-custom-args/kind-projector.check index 1375cf22b3f9..f6c258c5c58d 100644 --- a/tests/neg-custom-args/kind-projector.check +++ b/tests/neg-custom-args/kind-projector.check @@ -9,4 +9,4 @@ -- Error: tests/neg-custom-args/kind-projector.scala:6:22 -------------------------------------------------------------- 6 |class Bar2 extends Foo[*] // error | ^ - | Type argument _$4 does not have the same kind as its bound [_$1] + | Type argument _ does not have the same kind as its bound [_$1] diff --git a/tests/neg/i4986c.check b/tests/neg/i4986c.check index b93a5520b021..a5fe0cee26bf 100644 --- a/tests/neg/i4986c.check +++ b/tests/neg/i4986c.check @@ -61,4 +61,4 @@ -- Error: tests/neg/i4986c.scala:62:19 --------------------------------------------------------------------------------- 62 | i.m[Option[Long]] // error | ^ - | String; List; [A, _$6] =>> List[Option[?]]; Int; Option[Long]; + | String; List; [A, _] =>> List[Option[?]]; Int; Option[Long]; diff --git a/tests/pos/i12169.scala b/tests/pos/i12169.scala new file mode 100644 index 000000000000..6ad5cc4fb621 --- /dev/null +++ b/tests/pos/i12169.scala @@ -0,0 +1,13 @@ +class Property[T] + +class VObject { + def properties() = { + List.empty[Property[?]].collect { + case p: Property[?] => List(p) + } + } +} + +class Event extends VObject { + override def properties() = ??? +} \ No newline at end of file From 32e287f710a03caeee45c8332d2afddc7fad019b Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 24 Apr 2021 14:45:30 +0200 Subject: [PATCH 2/2] Fix Lucre The test neg/i12169.scala explains what went wrong and how to fix it. Before this PR Lurcre compiled, since the wildcard was translated to an abstract type that was not entered in scope and that therefore escaped avoidance. --- community-build/community-projects/Lucre | 2 +- tests/neg/i12169.scala | 37 ++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 tests/neg/i12169.scala diff --git a/community-build/community-projects/Lucre b/community-build/community-projects/Lucre index 87d680baaa23..412b1ac4b263 160000 --- a/community-build/community-projects/Lucre +++ b/community-build/community-projects/Lucre @@ -1 +1 @@ -Subproject commit 87d680baaa2355de5b062adcbdfc5005e787521b +Subproject commit 412b1ac4b2630b7e883822cca4ce0e6452a1bbfd diff --git a/tests/neg/i12169.scala b/tests/neg/i12169.scala new file mode 100644 index 000000000000..78507c369b1c --- /dev/null +++ b/tests/neg/i12169.scala @@ -0,0 +1,37 @@ +object Var: + class Expanded[T <: Txn[T], B] extends Form[T] + +trait Txn[T <: Txn[T]] + +trait Form[T] + +class TT extends Txn[TT] + +private final class FlatVarCellView[T <: Txn[T], B]( + firstVr : Option[Var.Expanded[TT, B]] +) + +def Test = + val opt: Option[Form[TT]] = ??? + val firstVr = opt match + case Some(ex: Var.Expanded[TT, _]) => Some(ex) + case _ => None + new FlatVarCellView(firstVr) // error + // Found: (firstVr : Option[Var.Expanded[TT, ?]]) + // Required: Option[Var.Expanded[TT, B]] + // + // where: B is a type variable + // + // Note that we cannot do capture conversion since the `?` does not appear as an argument + // of the a type. It's dubious whether capture conversion for more deeply nested types + // would be sound. + + // Remedy: + opt match + case Some(ex: Var.Expanded[TT, _]) => new FlatVarCellView(Some(ex)) + // here, we instantiate `B` with the unnamed second parameter of `Var.Expanded` + case _ => new FlatVarCellView(None) + opt match + case Some(ex: Var.Expanded[TT, t]) => new FlatVarCellView[TT, t](Some(ex)) + // the same as above, spelt out + case _ => new FlatVarCellView(None)