Skip to content

Commit fe6cfc6

Browse files
committed
Sort the typeMembers output list and filter out non-members
1 parent 7328cd6 commit fe6cfc6

File tree

5 files changed

+97
-1
lines changed

5 files changed

+97
-1
lines changed

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

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1049,6 +1049,23 @@ object Types extends TypeUtils {
10491049
buf.toList
10501050
}
10511051

1052+
/** For use in quotes reflect.
1053+
* A bit slower than the usual approach due to the use of LinkedHashSet.
1054+
**/
1055+
def sortedParents(using Context): mutable.LinkedHashSet[Type] = this match
1056+
case tp: ClassInfo =>
1057+
mutable.LinkedHashSet(tp) | mutable.LinkedHashSet(tp.declaredParents.flatMap(_.sortedParents.toList)*)
1058+
case tp: RefinedType =>
1059+
tp.parent.sortedParents
1060+
case tp: TypeProxy =>
1061+
tp.superType.sortedParents
1062+
case tp: AndType =>
1063+
tp.tp1.sortedParents | tp.tp2.sortedParents
1064+
case tp: OrType =>
1065+
tp.tp1.sortedParents & tp.tp2.sortedParents
1066+
case _ =>
1067+
mutable.LinkedHashSet()
1068+
10521069
/** The set of abstract term members of this type. */
10531070
final def abstractTermMembers(using Context): Seq[SingleDenotation] = {
10541071
record("abstractTermMembers")

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

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

30233049
def declarations: List[Symbol] =
30243050
self.typeRef.info.decls.toList

tests/run-macros/type-members.check

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)