-
Notifications
You must be signed in to change notification settings - Fork 1.1k
/
Copy pathSignatureTest.scala
115 lines (101 loc) · 5.12 KB
/
SignatureTest.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
package dotty.tools
import vulpix.TestConfiguration
import org.junit.Assert._
import org.junit.Test
import dotc.ast.untpd
import dotc.core.Decorators._
import dotc.core.Contexts._
import dotc.core.Flags._
import dotc.core.Phases._
import dotc.core.Names._
import dotc.core.Types._
import dotc.core.Symbols._
import dotc.core.StdNames._
import dotc.core.Signature
import dotc.typer.ProtoTypes.constrained
import dotc.typer.Inferencing.isFullyDefined
import dotc.typer.ForceDegree
import dotc.util.NoSourcePosition
import java.io.File
import java.nio.file._
class SignatureTest:
@Test def signatureCaching: Unit =
inCompilerContext(TestConfiguration.basicClasspath, separateRun = true, "case class Foo(value: Unit)") {
val (ref, refSig) = atPhase(erasurePhase.next) {
val cls = requiredClass("Foo")
val ref = cls.requiredMethod("value").termRef
(ref, ref.signature)
}
atPhase(typerPhase) {
// NamedType#signature is always computed before erasure, which ensures
// that it stays stable and therefore can be cached as long as
// signatures are guaranteed to be stable before erasure, see the
// comment above `Compiler#phases`.
assert(refSig == ref.signature,
s"""The signature of a type should never change but the signature of $ref was:
|${ref.signature} at typer, whereas it was:
|${refSig} after erasure""".stripMargin)
assert(ref.signature == ref.denot.signature,
s"""Before erasure, the signature of a TypeRef should be the signature of its denotation,
|but the cached signature of $ref was:
|${ref.signature}, whereas its denotation signature at typer was:
|${ref.denot.signature}""".stripMargin)
}
}
/** Ensure that signature computation returns an underdefined signature when
* the signature depends on uninstantiated type variables.
*/
@Test def underdefined: Unit =
inCompilerContext(TestConfiguration.basicClasspath, separateRun = false,
"""trait Foo
|trait Bar
|class A[T <: Tuple]:
| def and(x: T & Foo): Unit = {}
| def andor(x: (T | Bar) & Foo): Unit = {}
| def array(x: Array[(T | Bar) & Foo]): Unit = {}
| def tuple(x: Foo *: T): Unit = {}
| def tuple2(x: Foo *: (T | Tuple) & Foo): Unit = {}
|""".stripMargin):
val cls = requiredClass("A")
val tvar = constrained(cls.requiredMethod(nme.CONSTRUCTOR).info.asInstanceOf[TypeLambda], untpd.EmptyTree, alwaysAddTypeVars = true)._2.head.tpe
tvar <:< defn.TupleTypeRef
val prefix = cls.typeRef.appliedTo(tvar)
def checkSignatures(expectedIsUnderDefined: Boolean)(using Context): Unit =
for decl <- cls.info.decls.toList if decl.is(Method) && !decl.isConstructor do
val meth = decl.asSeenFrom(prefix)
val sig = meth.info.signature
val what = if expectedIsUnderDefined then "underdefined" else "fully-defined"
assert(sig.isUnderDefined == expectedIsUnderDefined, i"Signature of `$meth` with prefix `$prefix` and type `${meth.info}` should be $what but is `$sig`")
checkSignatures(expectedIsUnderDefined = true)
assert(isFullyDefined(tvar, force = ForceDegree.all), s"Could not instantiate $tvar")
checkSignatures(expectedIsUnderDefined = false)
/** Check that signature caching behaves correctly with respect to retracted
* instantiations of type variables.
*/
@Test def cachingWithRetraction: Unit =
inCompilerContext(TestConfiguration.basicClasspath, separateRun = false,
"""trait Foo
|trait Bar
|class A[T]:
| def and(x: T & Foo): Unit = {}
|""".stripMargin):
val cls = requiredClass("A")
val tvar = constrained(cls.requiredMethod(nme.CONSTRUCTOR).info.asInstanceOf[TypeLambda], untpd.EmptyTree, alwaysAddTypeVars = true)._2.head.tpe
val prefix = cls.typeRef.appliedTo(tvar)
val ref = prefix.select(cls.requiredMethod("and")).asInstanceOf[TermRef]
/** Check that the signature of the first parameter of `ref` is equal to `expectedParamSig`. */
def checkParamSig(ref: TermRef, expectedParamSig: TypeName)(using Context): Unit =
assertEquals(i"Check failed for param signature of $ref",
expectedParamSig, ref.signature.paramsSig.head)
// Both NamedType and MethodOrPoly cache signatures, so check both caches.
assertEquals(i"Check failed for param signature of ${ref.info} (but not for $ref itself)",
expectedParamSig, ref.info.signature.paramsSig.head)
// Initially, the param signature is Uninstantiated since it depends on an uninstantiated type variable
checkParamSig(ref, tpnme.Uninstantiated)
// In this context, the signature is the erasure of `Bar & Foo`.
inContext(ctx.fresh.setNewTyperState()):
tvar =:= requiredClass("Bar").typeRef
assert(isFullyDefined(tvar, force = ForceDegree.all), s"Could not instantiate $tvar")
checkParamSig(ref, "Bar".toTypeName)
// If our caching logic is working correctly, we should get the original signature here.
checkParamSig(ref, tpnme.Uninstantiated)