Skip to content

Commit 3bd6f8c

Browse files
committed
Sort the typeMembers output list and filter out non-members
1 parent a5e029a commit 3bd6f8c

File tree

5 files changed

+98
-1
lines changed

5 files changed

+98
-1
lines changed

compiler/src/dotty/tools/dotc/core/Types.scala

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ import CaptureSet.{CompareResult, IdempotentCaptRefMap, IdentityCaptRefMap}
4444
import scala.annotation.internal.sharable
4545
import scala.annotation.threadUnsafe
4646
import dotty.tools.dotc.cc.ccConfig
47+
import scala.collection.mutable.LinkedHashSet
4748

4849
object Types extends TypeUtils {
4950

@@ -1013,6 +1014,23 @@ object Types extends TypeUtils {
10131014
buf.toList
10141015
}
10151016

1017+
/** For use in quotes reflect.
1018+
* A bit slower than the usual approach due to the use of LinkedHashSet.
1019+
**/
1020+
def sortedParents(using Context): LinkedHashSet[Type] = this match
1021+
case tp: ClassInfo =>
1022+
LinkedHashSet(tp) | LinkedHashSet(tp.declaredParents.flatMap(_.sortedParents.toList)*)
1023+
case tp: RefinedType =>
1024+
tp.parent.sortedParents
1025+
case tp: TypeProxy =>
1026+
tp.superType.sortedParents
1027+
case tp: AndType =>
1028+
tp.tp1.sortedParents | tp.tp2.sortedParents
1029+
case tp: OrType =>
1030+
tp.tp1.sortedParents & tp.tp2.sortedParents
1031+
case _ =>
1032+
LinkedHashSet()
1033+
10161034
/** The set of abstract term members of this type. */
10171035
final def abstractTermMembers(using Context): Seq[SingleDenotation] = {
10181036
record("abstractTermMembers")

compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3010,7 +3010,33 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler
30103010
def memberTypes: List[Symbol] =
30113011
self.typeRef.decls.filter(_.isType)
30123012
def typeMembers: List[Symbol] =
3013-
lookupPrefix.typeMembers.map(_.symbol).toList
3013+
// lookupPrefix.typeMembers currently returns a Set wrapped into a unsorted Seq,
3014+
// so we try to sort that here (see discussion: https://github.com/scala/scala3/issues/22472),
3015+
// without adding too much of a performance hit.
3016+
// It first sorts by parents, then for type params by their positioning, then for members
3017+
// derived from declarations it sorts them by their name lexicographically
3018+
val parentsMap = lookupPrefix.sortedParents.map(_.typeSymbol).zipWithIndex.toList.toMap
3019+
val unsortedTypeMembers = lookupPrefix.typeMembers.map(_.symbol).filter(_.exists).toList
3020+
unsortedTypeMembers.sortWith {
3021+
case (typeA, typeB) =>
3022+
val msg = "Unknown type member found. Please consider reporting the issue to the compiler. "
3023+
assert(parentsMap.contains(typeA.owner), msg)
3024+
assert(parentsMap.contains(typeB.owner), msg)
3025+
val parentPlacementA = parentsMap(typeA.owner)
3026+
val parentPlacementB = parentsMap(typeB.owner)
3027+
if (parentPlacementA == parentPlacementB) then
3028+
if typeA.isTypeParam && typeB.isTypeParam then
3029+
// put type params at the beginning (and sort them by declaration order)
3030+
val pl = typeA.owner
3031+
val typeParamPositionMap = pl.typeParams.map(_.asInstanceOf[Symbol]).zipWithIndex.toMap
3032+
typeParamPositionMap(typeA) < typeParamPositionMap(typeB)
3033+
else if typeA.isTypeParam then true
3034+
else if typeB.isTypeParam then false
3035+
else
3036+
// sort by name lexicographically
3037+
typeA.name.toString().compareTo(typeB.name.toString()) < 0
3038+
else parentPlacementA < parentPlacementB
3039+
}.map(_.asInstanceOf[Symbol])
30143040

30153041
def declarations: List[Symbol] =
30163042
self.typeRef.info.decls.toList
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
class FooSmall: List(type A, type B, type C, type D)
2+
class FooLarge: List(type A, type B, type C, type D, type E)
3+
type FooUnion: List()
4+
type FooAnd: List(type A, type B, type C, type D, type E)
5+
trait CLS1: List(type A, type B, type C, type B1, type B2, type A3, type B3, type B4)
6+
type SharedAnd1: List(type B, type Shared, type A, type C)
7+
type SharedAnd2: List(type C, type Shared, type A, type B)
8+
type SharedUnion: List(type A, type Shared)
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package example
2+
3+
import scala.quoted.*
4+
5+
object Macro {
6+
inline def typeMembers[T <: AnyKind]: String = ${ typeMembersImpl[T] }
7+
8+
def typeMembersImpl[T <: AnyKind: Type](using quotes: Quotes): Expr[String] = {
9+
import quotes.reflect.*
10+
Expr(s"${TypeRepr.of[T].typeSymbol}: ${TypeRepr.of[T].typeSymbol.typeMembers.toString}")
11+
}
12+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import example.Macro
2+
3+
class FooSmall[A, B] { type D; type C }
4+
class FooLarge[A, B, C] { type E; type D }
5+
6+
type FooUnion[A, B] = FooSmall[A, B] | FooLarge[A, B, Int]
7+
type FooAnd[A, B] = FooSmall[A, B] & FooLarge[A, B, Int]
8+
9+
trait CLS4[A] { type B4 }
10+
trait CLS3[A] extends CLS4[A] { type B3; type A3 }
11+
trait CLS2[A] { type B2 }
12+
trait CLS1[A, B, C] extends CLS2[A] with CLS3[B] { type B1 }
13+
14+
trait SharedParent[A] { type Shared }
15+
trait SharedA[A] extends SharedParent[A] { type B }
16+
trait SharedB[A] extends SharedParent[A] { type C }
17+
type SharedAnd1[A] = SharedA[A] & SharedB[A]
18+
type SharedAnd2[A] = SharedB[A] & SharedA[A]
19+
type SharedUnion[A] = SharedA[A] | SharedB[A]
20+
21+
@main def Test(): Unit = {
22+
println(Macro.typeMembers[FooSmall])
23+
println(Macro.typeMembers[FooLarge])
24+
25+
println(Macro.typeMembers[FooUnion])
26+
println(Macro.typeMembers[FooAnd])
27+
28+
println(Macro.typeMembers[CLS1])
29+
30+
println(Macro.typeMembers[SharedAnd1])
31+
println(Macro.typeMembers[SharedAnd2])
32+
println(Macro.typeMembers[SharedUnion])
33+
}

0 commit comments

Comments
 (0)