@@ -78,3 +78,92 @@ f (forall 'a. fun (x: 'a) -> x)
78
78
//│ = [Function (anonymous)]
79
79
80
80
81
+ // * Example 2
82
+
83
+ def g(x: 'a | {f: nothing}) = x.f(0)
84
+ //│ g: {f: 0 -> 'a} -> 'a
85
+ //│ = [Function: g]
86
+
87
+ foo = forall 'x. fun (x: 'x) -> g(x)
88
+ //│ foo: anything -> nothing
89
+ //│ = [Function: foo]
90
+
91
+ :re
92
+ foo 0
93
+ //│ res: nothing
94
+ //│ Runtime error:
95
+ //│ TypeError: x.f is not a function
96
+
97
+
98
+
99
+ // * Now let's consider why functions and classes can't intersect to nothing due to distributivity
100
+
101
+
102
+ class Foo: { x: anything }
103
+ //│ Defined class Foo
104
+
105
+
106
+ // * These two types should be equivalent, but they visibly aren't:
107
+
108
+ def a: (int -> int | {x: int}) & Foo
109
+ def b: int -> int & Foo | {x: int} & Foo
110
+ //│ a: Foo
111
+ //│ = <missing implementation>
112
+ //│ b: Foo & {x: int}
113
+ //│ = <missing implementation>
114
+
115
+ :ne
116
+ ax = a.x
117
+ bx = b.x
118
+ //│ ax: anything
119
+ //│ bx: int
120
+
121
+
122
+ // * Yet, this does not immediately lead to unsoundness due to the aggressive normalization
123
+ // * performed during constraint solving:
124
+
125
+ :ne
126
+ a = b
127
+ //│ Foo & {x: int}
128
+ //│ <: a:
129
+ //│ Foo
130
+
131
+ :e
132
+ :ne
133
+ b = a
134
+ //│ Foo
135
+ //│ <: b:
136
+ //│ Foo & {x: int}
137
+ //│ ╔══[ERROR] Type mismatch in def definition:
138
+ //│ ║ l.133: b = a
139
+ //│ ║ ^^^^^
140
+ //│ ╟── expression of type `anything` is not an instance of type `int`
141
+ //│ ╟── Note: constraint arises from type reference:
142
+ //│ ║ l.109: def b: int -> int & Foo | {x: int} & Foo
143
+ //│ ╙── ^^^
144
+
145
+
146
+ // * To expose the unsoundness, we need some indirection with abstract types
147
+ // * that prevent eagerly distributing the intersection type:
148
+
149
+ class Test1[A, B]: { f: A & Foo }
150
+ method M: B & Foo | {x: int} & Foo
151
+ //│ Defined class Test1[+A, +B]
152
+ //│ Declared Test1.M: Test1[?, 'B] -> (Foo & 'B | Foo & {x: int})
153
+
154
+ class Test2[B]: Test1[B | {x: int}, B]
155
+ method M = this.f : B & Foo | {x: int} & Foo
156
+ //│ Defined class Test2[+B]
157
+ //│ Defined Test2.M: Test2['B] -> (Foo & 'B | Foo & {x: int})
158
+
159
+ oops = (Test2{f = Foo{x = "oops"}} : Test1[anything, int -> int]).M
160
+ //│ oops: Foo & {x: int}
161
+ //│ = Foo { x: 'oops' }
162
+
163
+ // * Notice the type confusion:
164
+ oops.x + 1
165
+ //│ res: int
166
+ //│ = 'oops1'
167
+
168
+
169
+
0 commit comments