-
Notifications
You must be signed in to change notification settings - Fork 3
/
SelfType.scala
116 lines (105 loc) · 3.13 KB
/
SelfType.scala
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
package com.thoughtworks.feature
import scala.reflect.macros.whitebox
import scala.language.experimental.macros
/** A type class that extracts the self-type of `A`.
*
* @example Given a trait without an explicit self-type declaration
*
* {{{
* trait MyTrait
* }}}
*
* Then the self type should be itself
*
* {{{
* val selfType = SelfType[MyTrait]
* "implicitly[selfType.Out =:= MyTrait]" should compile
* }}}
*
* @example Given a trait with an explicit self-type declaration,
*
* {{{
* trait MySelfType
* trait MyTrait { this: MySelfType => }
* }}}
*
* then the self type should be itself with the type of `this`.
*
* {{{
* val selfType = SelfType[MyTrait]
* "implicitly[selfType.Out =:= (MySelfType with MyTrait)]" should compile
* }}}
*
* @example Given a compound type,
*
* {{{
* trait MySelfType1
* trait MyTrait1 { this: MySelfType1 => }
*
* trait MyTrait2
*
* trait MySelfType3
* trait MyTrait3 { this: MySelfType3 => }
*
* type MyCompoundType = MyTrait1 with MyTrait2 with MyTrait3 {
* type MyRefinement = Int
* }
* }}}
*
* then the self type should be a compound type of each mix-in part,
*
* {{{
* val selfType = SelfType[MyCompoundType]
* "implicitly[selfType.Out =:= (MySelfType1 with MySelfType3 with MyTrait1 with MyTrait2 with MyTrait3)]" should compile
* }}}
*
* and the self type should not contain the refinement statements
*
* {{{
* "implicitly[selfType.Out <:< { type MyRefinement = Int }]" shouldNot typeCheck
* }}}
*
*/
trait SelfType[A] {
type Out
}
object SelfType {
type Aux[A, Out0] = SelfType[A] {
type Out = Out0
}
def make[A, Out0]: SelfType.Aux[A, Out0] = new SelfType[A] {
type Out = Out0
}
implicit def apply[A]: SelfType[A] = macro Macros.apply[A]
private[SelfType] final class Macros(val c: whitebox.Context) {
import c.universe._
def apply[A: WeakTypeTag]: Tree = {
val a = weakTypeOf[A]
val selfTypes: List[Type] = {
val selfTypeBuilder = List.newBuilder[Type]
def buildSelfTypes(t: Type): Unit = {
val dealiased = t.dealias
dealiased match {
case RefinedType(superTypes, refinedScope) =>
superTypes.foreach(buildSelfTypes)
case typeRef: TypeRef =>
val symbol = dealiased.typeSymbol
if (symbol.isClass) {
selfTypeBuilder += symbol.asClass.selfType.asSeenFrom(dealiased, symbol)
}
case _ =>
}
}
buildSelfTypes(a)
selfTypeBuilder.result()
}
val out = selfTypes match {
case Nil =>
definitions.AnyTpe
case _ =>
internal.refinedType(selfTypes, c.internal.enclosingOwner)
}
q"_root_.com.thoughtworks.feature.SelfType.make[$a, $out]"
}
}
}