From 05966dc70404d52a952aad656b8b6eaa57c2fa18 Mon Sep 17 00:00:00 2001
From: Hamza REMMAL <hamza@remmal.dev>
Date: Tue, 13 Aug 2024 10:55:16 +0100
Subject: [PATCH] Bound TypeRepr by Matchable in Quotes

---
 library/src/scala/quoted/Quotes.scala |  2 +-
 tests/pos/i21282.scala                | 18 ++++++++++++++++++
 2 files changed, 19 insertions(+), 1 deletion(-)
 create mode 100644 tests/pos/i21282.scala

diff --git a/library/src/scala/quoted/Quotes.scala b/library/src/scala/quoted/Quotes.scala
index d048d8d728d5..fad769793bb7 100644
--- a/library/src/scala/quoted/Quotes.scala
+++ b/library/src/scala/quoted/Quotes.scala
@@ -2640,7 +2640,7 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching =>
     // ----- Types ----------------------------------------------------
 
     /** A type, type constructors, type bounds or NoPrefix */
-    type TypeRepr
+    type TypeRepr <: Matchable
 
     /** Module object of `type TypeRepr`  */
     val TypeRepr: TypeReprModule
diff --git a/tests/pos/i21282.scala b/tests/pos/i21282.scala
new file mode 100644
index 000000000000..a483f3763f89
--- /dev/null
+++ b/tests/pos/i21282.scala
@@ -0,0 +1,18 @@
+//> using options -Xfatal-warnings -source:future-migration
+
+import scala.quoted.*
+
+private def isUnionCanonicalImpl[U: Type](using Quotes): Expr[Unit] =
+  import quotes.reflect.*
+  val u = TypeRepr.of[U].dealiasKeepOpaques
+
+  def inner[U: Type](s: Set[TypeRepr], tr: TypeRepr): Set[TypeRepr] =
+    tr.dealiasKeepOpaques match
+      case OrType(a, b) =>
+        val ss = inner[U](s, a)
+        inner[U](ss, b)
+      case x if s.contains(x) =>
+        report.errorAndAbort(s"Type ${x.show} multiple times (CHECK ALIASES) in union ${u.show}")
+      case x => s + x
+  inner(Set.empty, u)
+  '{ () }