From d25fc4973e341f3033f6870623326b3f01039851 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=8BAndrzej=20Ressel?= Date: Mon, 21 Feb 2022 00:38:08 +0100 Subject: [PATCH 1/4] Don't generate Mirror.Sum for simple enum cases --- .../dotty/tools/dotc/typer/Synthesizer.scala | 2 +- tests/neg/scala_enum_testing.scala | 15 +++++++++++++++ tests/run/scala_enum_testing.scala | 17 +++++++++++++++++ 3 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 tests/neg/scala_enum_testing.scala create mode 100644 tests/run/scala_enum_testing.scala diff --git a/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala b/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala index fe4feecd6100..9bb57992f54d 100644 --- a/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala @@ -273,7 +273,7 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context): val cls = mirroredType.classSymbol val useCompanion = cls.useCompanionAsSumMirror - if cls.isGenericSum(if useCompanion then cls.linkedClass else ctx.owner) then + if (!mirroredType.termSymbol.isEnumCase && (cls.isGenericSum(if useCompanion then cls.linkedClass else ctx.owner))) then val elemLabels = cls.children.map(c => ConstantType(Constant(c.name.toString))) def solve(sym: Symbol): Type = sym match diff --git a/tests/neg/scala_enum_testing.scala b/tests/neg/scala_enum_testing.scala new file mode 100644 index 000000000000..baa2854af981 --- /dev/null +++ b/tests/neg/scala_enum_testing.scala @@ -0,0 +1,15 @@ +import scala.util.NotGiven +import scala.compiletime.* +import scala.deriving.Mirror + +enum Color: + case Red, Green, Blue +end Color + +object Test { + + def main(args: Array[String]): Unit = { + summon[Mirror.ProductOf[Color]] // error + summon[Mirror.SumOf[Color.Red.type]] // error + } +} diff --git a/tests/run/scala_enum_testing.scala b/tests/run/scala_enum_testing.scala new file mode 100644 index 000000000000..f529b900e875 --- /dev/null +++ b/tests/run/scala_enum_testing.scala @@ -0,0 +1,17 @@ +import scala.util.NotGiven +import scala.compiletime.* +import scala.deriving.Mirror + +enum Color: + case Red, Green, Blue +end Color + +object Test { + + def main(args: Array[String]): Unit = { + summon[Mirror.Of[Color.Red.type]] + summon[Mirror.Of[Color]] + summon[Mirror.ProductOf[Color.Red.type]] + summon[Mirror.SumOf[Color]] + } +} From d317135a63a9054cd73b28829ea3c721cb2adb74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=8BAndrzej=20Ressel?= Date: Wed, 2 Mar 2022 19:38:34 +0100 Subject: [PATCH 2/4] Disallow Sum with enum or discointed class --- .../src/dotty/tools/dotc/typer/Synthesizer.scala | 14 +++++++++++++- tests/neg/scala_enum_testing.scala | 13 +++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala b/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala index 9bb57992f54d..9442401c0d57 100644 --- a/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala @@ -273,7 +273,19 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context): val cls = mirroredType.classSymbol val useCompanion = cls.useCompanionAsSumMirror - if (!mirroredType.termSymbol.isEnumCase && (cls.isGenericSum(if useCompanion then cls.linkedClass else ctx.owner))) then + val isDisjointed = mirroredType match { + case OrType(t1, t2) => TypeComparer.provablyDisjoint(t1, t2) + case _ => false + } + + def isSumWithSingleton(t: Type): Boolean = { + t match { + case OrType(t1, t2) => isSumWithSingleton(t1) || isSumWithSingleton(t2) + case _ => t.termSymbol.isEnumCase + } + } + + if (!isDisjointed && !isSumWithSingleton(mirroredType) && cls.isGenericSum(if useCompanion then cls.linkedClass else ctx.owner)) then val elemLabels = cls.children.map(c => ConstantType(Constant(c.name.toString))) def solve(sym: Symbol): Type = sym match diff --git a/tests/neg/scala_enum_testing.scala b/tests/neg/scala_enum_testing.scala index baa2854af981..599022ef0225 100644 --- a/tests/neg/scala_enum_testing.scala +++ b/tests/neg/scala_enum_testing.scala @@ -6,10 +6,23 @@ enum Color: case Red, Green, Blue end Color +enum Bar: + case A(i: Int) + case B(b: Boolean) + case C(s: String) + +object Singletons { + object A + object B +} + object Test { def main(args: Array[String]): Unit = { summon[Mirror.ProductOf[Color]] // error summon[Mirror.SumOf[Color.Red.type]] // error + summon[Mirror.SumOf[Color.Red.type | Color.Green.type]] // error + summon[Mirror.SumOf[Bar.A | Bar.B]] // error + summon[Mirror.SumOf[Singletons.A.type | Singletons.B.type]] // error } } From 74eeebd970a732e63241af04317527893f819c8b Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Wed, 9 Mar 2022 10:52:05 +0000 Subject: [PATCH 3/4] Use strict classSymbol equality to block bad unions --- .../src/dotty/tools/dotc/typer/Synthesizer.scala | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala b/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala index 9442401c0d57..61f3f98cb9bf 100644 --- a/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala @@ -273,19 +273,11 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context): val cls = mirroredType.classSymbol val useCompanion = cls.useCompanionAsSumMirror - val isDisjointed = mirroredType match { - case OrType(t1, t2) => TypeComparer.provablyDisjoint(t1, t2) - case _ => false - } - - def isSumWithSingleton(t: Type): Boolean = { - t match { - case OrType(t1, t2) => isSumWithSingleton(t1) || isSumWithSingleton(t2) - case _ => t.termSymbol.isEnumCase - } - } + def acceptable(tp: Type): Boolean = tp match + case OrType(tp1, tp2) => acceptable(tp1) && acceptable(tp2) + case _ => !tp.termSymbol.isEnumCase && (tp.classSymbol eq cls) - if (!isDisjointed && !isSumWithSingleton(mirroredType) && cls.isGenericSum(if useCompanion then cls.linkedClass else ctx.owner)) then + if acceptable(mirroredType) && cls.isGenericSum(if useCompanion then cls.linkedClass else ctx.owner) then val elemLabels = cls.children.map(c => ConstantType(Constant(c.name.toString))) def solve(sym: Symbol): Type = sym match From e80dcba69eb554723cbed8e5e58f37a7d326caeb Mon Sep 17 00:00:00 2001 From: Jamie Thompson Date: Mon, 14 Mar 2022 17:21:44 +0100 Subject: [PATCH 4/4] add checks for HK and annots --- .../src/dotty/tools/dotc/typer/Synthesizer.scala | 4 +++- tests/neg/scala_enum_testing.scala | 12 ++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala b/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala index 61f3f98cb9bf..cb45e5343810 100644 --- a/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala @@ -274,8 +274,10 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context): val useCompanion = cls.useCompanionAsSumMirror def acceptable(tp: Type): Boolean = tp match + case tp: TermRef => false + case tp: TypeProxy => acceptable(tp.underlying) case OrType(tp1, tp2) => acceptable(tp1) && acceptable(tp2) - case _ => !tp.termSymbol.isEnumCase && (tp.classSymbol eq cls) + case _ => tp.classSymbol eq cls if acceptable(mirroredType) && cls.isGenericSum(if useCompanion then cls.linkedClass else ctx.owner) then val elemLabels = cls.children.map(c => ConstantType(Constant(c.name.toString))) diff --git a/tests/neg/scala_enum_testing.scala b/tests/neg/scala_enum_testing.scala index 599022ef0225..043995460742 100644 --- a/tests/neg/scala_enum_testing.scala +++ b/tests/neg/scala_enum_testing.scala @@ -16,6 +16,10 @@ object Singletons { object B } +type SumOfK1[F[_]] = Mirror.Sum { type MirroredType[T] = F[T] } + +class refined extends scala.annotation.RefiningAnnotation + object Test { def main(args: Array[String]): Unit = { @@ -24,5 +28,13 @@ object Test { summon[Mirror.SumOf[Color.Red.type | Color.Green.type]] // error summon[Mirror.SumOf[Bar.A | Bar.B]] // error summon[Mirror.SumOf[Singletons.A.type | Singletons.B.type]] // error + summon[SumOfK1[[X] =>> Bar]] + summon[SumOfK1[[X] =>> Bar.A | Bar.B]] // error + summon[SumOfK1[[X] =>> (Bar.A | Bar.B) @refined]] // error + object opaques { + opaque type Query[X] = (Bar.A | Bar.B) @refined + } + summon[SumOfK1[opaques.Query]] // error + summon[SumOfK1[[X] =>> (Bar.A @refined) | Bar.B]] // error } }