From 36b02e0c4e29c88333091cb0111fc5dd52817190 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 16 Jan 2020 10:54:03 +0100 Subject: [PATCH 1/3] Fix #8012: Disallow conversion to underspecified SAM type We cannot write class C extends B[?] By the same token, we cannot do a SAM type conversion to B[?] since this would create code like the one above. --- .../src/dotty/tools/dotc/typer/Typer.scala | 19 ++++++++++--------- tests/neg/i8012.scala | 13 +++++++++++++ 2 files changed, 23 insertions(+), 9 deletions(-) create mode 100644 tests/neg/i8012.scala diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 554380596567..89ae587659dc 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -1100,15 +1100,16 @@ class Typer extends Namer case SAMType(sam) if !defn.isFunctionType(pt) && mt <:< sam => val targetTpe = - if (!isFullyDefined(pt, ForceDegree.all)) - if (pt.isRef(defn.PartialFunctionClass)) - // Replace the underspecified expected type by one based on the closure method type - defn.PartialFunctionOf(mt.firstParamTypes.head, mt.resultType) - else { - ctx.error(ex"result type of lambda is an underspecified SAM type $pt", tree.sourcePos) - pt - } - else pt + if isFullyDefined(pt, ForceDegree.all) + && !pt.argInfos.exists(_.isInstanceOf[TypeBounds]) + then + pt + else if pt.isRef(defn.PartialFunctionClass) then + // Replace the underspecified expected type by one based on the closure method type + defn.PartialFunctionOf(mt.firstParamTypes.head, mt.resultType) + else + ctx.error(ex"result type of lambda is an underspecified SAM type $pt", tree.sourcePos) + pt if (pt.classSymbol.isOneOf(FinalOrSealed)) { val offendingFlag = pt.classSymbol.flags & FinalOrSealed ctx.error(ex"lambda cannot implement $offendingFlag ${pt.classSymbol}", tree.sourcePos) diff --git a/tests/neg/i8012.scala b/tests/neg/i8012.scala new file mode 100644 index 000000000000..01171fd3f80c --- /dev/null +++ b/tests/neg/i8012.scala @@ -0,0 +1,13 @@ + + +@FunctionalInterface +abstract class Q[A] { + def apply(a: A): Int +} + +class C extends Q[?] // error: Type argument must be fully defined + +object O { + def m(i: Int): Int = i + val x: Q[_] = m // error: result type of lambda is an underspecified SAM type Q[?] +} \ No newline at end of file From 2afc24594fc6b365dde15a9e26182ade6a971432 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 16 Jan 2020 11:45:29 +0100 Subject: [PATCH 2/3] Restrict wildcard SAM criterion to proper classes Traits are OK, since traits with wildcard arguments can be produced in source. --- compiler/src/dotty/tools/dotc/typer/Typer.scala | 6 +++--- tests/pos/i8012.scala | 16 ++++++++++++++++ 2 files changed, 19 insertions(+), 3 deletions(-) create mode 100644 tests/pos/i8012.scala diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 89ae587659dc..fb05b9aae273 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -1099,10 +1099,10 @@ class Typer extends Namer pt match { case SAMType(sam) if !defn.isFunctionType(pt) && mt <:< sam => + def isWildcardClassSAM = + !pt.classSymbol.is(Trait) && pt.argInfos.exists(_.isInstanceOf[TypeBounds]) val targetTpe = - if isFullyDefined(pt, ForceDegree.all) - && !pt.argInfos.exists(_.isInstanceOf[TypeBounds]) - then + if isFullyDefined(pt, ForceDegree.all) && !isWildcardClassSAM then pt else if pt.isRef(defn.PartialFunctionClass) then // Replace the underspecified expected type by one based on the closure method type diff --git a/tests/pos/i8012.scala b/tests/pos/i8012.scala new file mode 100644 index 000000000000..a7153761f9b5 --- /dev/null +++ b/tests/pos/i8012.scala @@ -0,0 +1,16 @@ + + +@FunctionalInterface +abstract class Q[A] { + def apply(a: A): Int +} + +trait T[A] + +class C extends Q[?] // error: Type argument must be fully defined +class D extends T[?] // error: Type argument must be fully defined + +object O { + def m(i: Int): Int = i + val x: Q[_] = m // error: result type of lambda is an underspecified SAM type Q[?] +} \ No newline at end of file From 28ac0e4876fbb20a5dc39e7fa4300eca991f395a Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 16 Jan 2020 11:51:41 +0100 Subject: [PATCH 3/3] Add explanation in comment --- compiler/src/dotty/tools/dotc/typer/Typer.scala | 3 +++ tests/pos/i8012.scala | 16 ---------------- 2 files changed, 3 insertions(+), 16 deletions(-) delete mode 100644 tests/pos/i8012.scala diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index fb05b9aae273..0f240f31a205 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -1099,6 +1099,9 @@ class Typer extends Namer pt match { case SAMType(sam) if !defn.isFunctionType(pt) && mt <:< sam => + // SAMs of the form C[?] where C is a class cannot be conversion targets. + // The resulting class `class $anon extends C[?] {...}` would be illegal, + // since type arguments to `C`'s super constructor cannot be constructed. def isWildcardClassSAM = !pt.classSymbol.is(Trait) && pt.argInfos.exists(_.isInstanceOf[TypeBounds]) val targetTpe = diff --git a/tests/pos/i8012.scala b/tests/pos/i8012.scala deleted file mode 100644 index a7153761f9b5..000000000000 --- a/tests/pos/i8012.scala +++ /dev/null @@ -1,16 +0,0 @@ - - -@FunctionalInterface -abstract class Q[A] { - def apply(a: A): Int -} - -trait T[A] - -class C extends Q[?] // error: Type argument must be fully defined -class D extends T[?] // error: Type argument must be fully defined - -object O { - def m(i: Int): Int = i - val x: Q[_] = m // error: result type of lambda is an underspecified SAM type Q[?] -} \ No newline at end of file