diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala
index 48c81a2c4000..c3a81bf71571 100644
--- a/compiler/src/dotty/tools/dotc/core/Types.scala
+++ b/compiler/src/dotty/tools/dotc/core/Types.scala
@@ -3540,6 +3540,8 @@ object Types extends TypeUtils {
       else this match
         case tp: OrType => OrType.make(tp1, tp2, tp.isSoft)
         case tp: AndType => AndType.make(tp1, tp2, checkValid = true)
+
+    override def hashIsStable: Boolean = tp1.hashIsStable && tp2.hashIsStable
   }
 
   abstract case class AndType(tp1: Type, tp2: Type) extends AndOrType {
@@ -3585,6 +3587,10 @@ object Types extends TypeUtils {
       case that: AndType => tp1.eq(that.tp1) && tp2.eq(that.tp2)
       case _ => false
     }
+
+    override protected def iso(that: Any, bs: BinderPairs) = that match
+      case that: AndType => tp1.equals(that.tp1, bs) && tp2.equals(that.tp2, bs)
+      case _ => false
   }
 
   final class CachedAndType(tp1: Type, tp2: Type) extends AndType(tp1, tp2)
@@ -3736,6 +3742,10 @@ object Types extends TypeUtils {
       case that: OrType => tp1.eq(that.tp1) && tp2.eq(that.tp2) && isSoft == that.isSoft
       case _ => false
     }
+
+    override protected def iso(that: Any, bs: BinderPairs) = that match
+      case that: OrType => tp1.equals(that.tp1, bs) && tp2.equals(that.tp2, bs) && isSoft == that.isSoft
+      case _ => false
   }
 
   final class CachedOrType(tp1: Type, tp2: Type, override val isSoft: Boolean) extends OrType(tp1, tp2)
diff --git a/docs/_docs/reference/contextual/givens.md b/docs/_docs/reference/contextual/givens.md
index bf018278c9fc..5499fc39b53c 100644
--- a/docs/_docs/reference/contextual/givens.md
+++ b/docs/_docs/reference/contextual/givens.md
@@ -88,6 +88,45 @@ given (using config: Config): Factory = MemoizingFactory(config)
 An alias given can have type parameters and context parameters just like any other given,
 but it can only implement a single type.
 
+## Abstract Givens
+
+A given may be an abstract member, with the restriction that it must have an explicit name.
+
+```scala
+trait HasOrd[T]:
+  given ord: Ord[T]
+```
+
+## More Structural Givens
+
+If an alias given instance is analogous to a lazy val,
+and a structural given instance is analogous to an object,
+albeit an object with an explicit type,
+then a structural given may also be specified without an explicit type:
+
+```scala
+class IntOrd extends Ord[Int]:
+  def compare(x: Int, y: Int) =
+    if x < y then -1 else if x > y then +1 else 0
+
+given IntOrd()
+```
+
+Compare this syntax to:
+
+```scala
+object intOrd extends IntOrd()
+```
+
+The empty parentheses are optional in the extends clause when defining a class,
+but are required when defining a given.
+
+Further mixins are allowed as usual:
+
+```scala
+given IntOrd() with OrdOps[Int]
+```
+
 ## Given Macros
 
 Given aliases can have the `inline` and `transparent` modifiers.
@@ -191,4 +230,4 @@ of given instances:
 - A _structural instance_ contains one or more types or constructor applications,
   followed by `with` and a template body that contains member definitions of the instance.
 - An _alias instance_ contains a type, followed by `=` and a right-hand side expression.
-- An _abstract instance_ contains just the type, which is not followed by anything.
+- An _abstract instance_ contains just the name and type, which is not followed by anything.
diff --git a/tests/pos/i20858-min.scala b/tests/pos/i20858-min.scala
new file mode 100644
index 000000000000..9c47b04031e6
--- /dev/null
+++ b/tests/pos/i20858-min.scala
@@ -0,0 +1,10 @@
+
+type M[F[_,_]] = Int match
+  case 0 => String & M[F]
+
+type M1 = M[[x,y] =>> x | y]
+type M2 = M[[x,y] =>> x | y]
+
+def Test: Unit =
+  val x: M1 = ???
+  val _: M2 = x // was error
diff --git a/tests/pos/i20858/defns_1.scala b/tests/pos/i20858/defns_1.scala
new file mode 100644
index 000000000000..7b4b84745b58
--- /dev/null
+++ b/tests/pos/i20858/defns_1.scala
@@ -0,0 +1,27 @@
+import scala.compiletime.*
+import scala.deriving.*
+
+sealed trait ZIO[-R, +E, +A]
+sealed abstract class ZLayer[-RIn, +E, +ROut]
+object ZLayer:
+  def apply[RIn, E, ROut](zio: => ZIO[RIn, E, ROut]): ZLayer[RIn, E, ROut] = ???
+type URIO[-R, +A] = ZIO[R, Nothing, A]
+type IAnyType[T <: Tuple] = Tuple.Fold[T, Any, [x, y] =>> x & y]
+type UAnyType[T <: Tuple] = Tuple.Fold[T, Any, [x, y] =>> x | y]
+
+
+trait AutoLayer[A]:
+  def zlayer(using
+             p: Mirror.ProductOf[A]
+            ): ZLayer[IAnyType[p.MirroredElemTypes], Nothing, A]
+
+object AutoLayer:
+  inline given derived[A](using p: Mirror.ProductOf[A]): AutoLayer[A] = {
+    val a: ZIO[IAnyType[p.MirroredElemTypes], Nothing, A] = ???
+    new AutoLayer[A]:
+      override def zlayer(using
+                          pp: Mirror.ProductOf[A]
+                         ): ZLayer[IAnyType[pp.MirroredElemTypes], Nothing, A] = ZLayer {
+        a.asInstanceOf[ZIO[IAnyType[pp.MirroredElemTypes], Nothing, A]]
+      }
+  }
\ No newline at end of file
diff --git a/tests/pos/i20858/usages_2.scala b/tests/pos/i20858/usages_2.scala
new file mode 100644
index 000000000000..3a05ba54e97a
--- /dev/null
+++ b/tests/pos/i20858/usages_2.scala
@@ -0,0 +1,2 @@
+
+case class TestService(port: Int) derives AutoLayer // was error