jade-lang 0.9.0 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (145) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +400 -12
  3. data/docs/interop.md +18 -1
  4. data/docs/stdlib.md +3 -1
  5. data/docs/syntax.md +96 -0
  6. data/lib/jade/api.rb +9 -0
  7. data/lib/jade/ast.rb +37 -1
  8. data/lib/jade/cli/eject.rb +132 -0
  9. data/lib/jade/cli.rb +2 -0
  10. data/lib/jade/codegen/boundary/specialized/list.rb +11 -7
  11. data/lib/jade/codegen/boundary/specialized/maybe.rb +2 -2
  12. data/lib/jade/codegen/boundary/specialized/record.rb +19 -4
  13. data/lib/jade/codegen/boundary/specialized/scalar.rb +2 -2
  14. data/lib/jade/codegen/boundary/specialized.rb +13 -5
  15. data/lib/jade/codegen/boundary.rb +12 -2
  16. data/lib/jade/codegen/context.rb +17 -0
  17. data/lib/jade/codegen/error.rb +1 -0
  18. data/lib/jade/codegen/function_call.rb +23 -7
  19. data/lib/jade/codegen/function_declaration.rb +25 -13
  20. data/lib/jade/codegen/helpers.rb +27 -4
  21. data/lib/jade/codegen/implementation.rb +2 -2
  22. data/lib/jade/codegen/inline.rb +1 -1
  23. data/lib/jade/codegen/inlines.rb +6 -1
  24. data/lib/jade/codegen/names.rb +31 -0
  25. data/lib/jade/codegen/pattern/constructor.rb +2 -2
  26. data/lib/jade/codegen/transforms/fold_shape.rb +1 -1
  27. data/lib/jade/codegen.rb +76 -15
  28. data/lib/jade/compiler.rb +2 -0
  29. data/lib/jade/entry.rb +1 -1
  30. data/lib/jade/formatter/calls.rb +68 -11
  31. data/lib/jade/formatter/collections.rb +4 -4
  32. data/lib/jade/formatter/declarations.rb +13 -15
  33. data/lib/jade/formatter/helper.rb +20 -0
  34. data/lib/jade/formatter/infix_application.rb +19 -4
  35. data/lib/jade/formatter/lambda.rb +24 -12
  36. data/lib/jade/formatter/pattern.rb +3 -0
  37. data/lib/jade/frontend/desugaring/placeholder.rb +40 -17
  38. data/lib/jade/frontend/desugaring.rb +29 -9
  39. data/lib/jade/frontend/fixity_fixer.rb +6 -4
  40. data/lib/jade/frontend/forward_declaration/error/alias_expansion.rb +23 -0
  41. data/lib/jade/frontend/forward_declaration/error/duplicate_constructor_name.rb +45 -0
  42. data/lib/jade/frontend/forward_declaration/error/duplicate_type_name.rb +42 -0
  43. data/lib/jade/frontend/forward_declaration/error.rb +3 -0
  44. data/lib/jade/frontend/forward_declaration/helper.rb +58 -0
  45. data/lib/jade/frontend/forward_declaration/import_declaration.rb +4 -0
  46. data/lib/jade/frontend/forward_declaration/interface_declaration.rb +6 -0
  47. data/lib/jade/frontend/forward_declaration/module.rb +4 -0
  48. data/lib/jade/frontend/forward_declaration/struct_declaration.rb +10 -1
  49. data/lib/jade/frontend/forward_declaration/type_alias_declaration.rb +37 -0
  50. data/lib/jade/frontend/forward_declaration/type_declaration.rb +10 -1
  51. data/lib/jade/frontend/forward_declaration.rb +2 -0
  52. data/lib/jade/frontend/pattern_analysis/exhaustiveness.rb +47 -43
  53. data/lib/jade/frontend/pattern_analysis/matrix.rb +28 -219
  54. data/lib/jade/frontend/pattern_analysis/pattern.rb +132 -0
  55. data/lib/jade/frontend/pattern_analysis/redundancy.rb +24 -0
  56. data/lib/jade/frontend/pattern_analysis/signature.rb +220 -0
  57. data/lib/jade/frontend/pattern_analysis/usefulness.rb +72 -0
  58. data/lib/jade/frontend/pattern_analysis/witnesses.rb +73 -0
  59. data/lib/jade/frontend/pattern_analysis.rb +5 -38
  60. data/lib/jade/frontend/semantic_analysis/error/implementation_on_alias.rb +24 -0
  61. data/lib/jade/frontend/semantic_analysis/error/no_base_case.rb +45 -0
  62. data/lib/jade/frontend/semantic_analysis/error/placeholder_not_allowed.rb +15 -3
  63. data/lib/jade/frontend/semantic_analysis/error/recursive_type_alias.rb +24 -0
  64. data/lib/jade/frontend/semantic_analysis/error.rb +3 -0
  65. data/lib/jade/frontend/semantic_analysis/helper.rb +31 -0
  66. data/lib/jade/frontend/semantic_analysis/implementation.rb +13 -0
  67. data/lib/jade/frontend/semantic_analysis/inhabitedness.rb +90 -0
  68. data/lib/jade/frontend/semantic_analysis/interop_import_declaration.rb +28 -16
  69. data/lib/jade/frontend/semantic_analysis/keyed_call.rb +21 -5
  70. data/lib/jade/frontend/semantic_analysis/pattern_range.rb +16 -0
  71. data/lib/jade/frontend/semantic_analysis/struct_declaration.rb +1 -0
  72. data/lib/jade/frontend/semantic_analysis/type_alias_declaration/cycle_detection.rb +95 -0
  73. data/lib/jade/frontend/semantic_analysis/type_alias_declaration.rb +48 -0
  74. data/lib/jade/frontend/semantic_analysis/type_declaration.rb +1 -0
  75. data/lib/jade/frontend/semantic_analysis.rb +5 -0
  76. data/lib/jade/frontend/type_checking/canonicalize.rb +1 -0
  77. data/lib/jade/frontend/type_checking/cascade.rb +90 -0
  78. data/lib/jade/frontend/type_checking/constraints/deriving/encodable.rb +10 -0
  79. data/lib/jade/frontend/type_checking/constraints.rb +18 -3
  80. data/lib/jade/frontend/type_checking/error/display.rb +49 -0
  81. data/lib/jade/frontend/type_checking/error/division_by_zero.rb +31 -0
  82. data/lib/jade/frontend/type_checking/error/empty_range_pattern.rb +25 -0
  83. data/lib/jade/frontend/type_checking/error/function_body_type_mismatch.rb +4 -3
  84. data/lib/jade/frontend/type_checking/error/function_call_type_mismatch.rb +108 -5
  85. data/lib/jade/frontend/type_checking/error/if_branch_type_mismatch.rb +1 -1
  86. data/lib/jade/frontend/type_checking/error/if_branches_type_mismatch.rb +1 -1
  87. data/lib/jade/frontend/type_checking/error/if_condition_type_mismatch.rb +1 -1
  88. data/lib/jade/frontend/type_checking/error/implementation_type_mismatch.rb +1 -1
  89. data/lib/jade/frontend/type_checking/error/list_item_type_mismatch.rb +1 -1
  90. data/lib/jade/frontend/type_checking/error/missing_patterns.rb +20 -4
  91. data/lib/jade/frontend/type_checking/error/pattern_type_mismatch.rb +7 -1
  92. data/lib/jade/frontend/type_checking/error/range_pattern_type.rb +29 -0
  93. data/lib/jade/frontend/type_checking/error/record_access_type_mismatch.rb +2 -2
  94. data/lib/jade/frontend/type_checking/error/recursive_derivation.rb +25 -0
  95. data/lib/jade/frontend/type_checking/error/type_mismatch.rb +10 -2
  96. data/lib/jade/frontend/type_checking/error/unreachable_branch.rb +22 -0
  97. data/lib/jade/frontend/type_checking/error.rb +6 -0
  98. data/lib/jade/frontend/type_checking/inference/assign.rb +1 -1
  99. data/lib/jade/frontend/type_checking/inference/case_of.rb +21 -4
  100. data/lib/jade/frontend/type_checking/inference/division.rb +50 -0
  101. data/lib/jade/frontend/type_checking/inference/function_call.rb +26 -1
  102. data/lib/jade/frontend/type_checking/inference/lambda.rb +1 -1
  103. data/lib/jade/frontend/type_checking/inference/pattern.rb +35 -0
  104. data/lib/jade/frontend/type_checking/inference/type_alias_declaration.rb +18 -0
  105. data/lib/jade/frontend/type_checking/inference.rb +2 -0
  106. data/lib/jade/frontend/type_checking/loader.rb +11 -0
  107. data/lib/jade/frontend/type_checking/port_resolution.rb +26 -8
  108. data/lib/jade/frontend/type_checking/substitution.rb +2 -2
  109. data/lib/jade/frontend/type_checking.rb +3 -1
  110. data/lib/jade/frontend/usage_analysis.rb +5 -1
  111. data/lib/jade/interop/boundary.rb +64 -24
  112. data/lib/jade/interop/error.rb +35 -7
  113. data/lib/jade/interop/lowering.rb +8 -2
  114. data/lib/jade/lsp/converters.rb +83 -2
  115. data/lib/jade/lsp/handlers.rb +122 -25
  116. data/lib/jade/lsp/snippets.rb +12 -0
  117. data/lib/jade/lsp/state.rb +11 -4
  118. data/lib/jade/module_loader/build.rb +36 -0
  119. data/lib/jade/module_loader.rb +1 -0
  120. data/lib/jade/parsing/combinators.rb +78 -2
  121. data/lib/jade/parsing/error.rb +43 -6
  122. data/lib/jade/parsing/type.rb +5 -3
  123. data/lib/jade/parsing.rb +140 -26
  124. data/lib/jade/runtime.rb +32 -13
  125. data/lib/jade/source.rb +6 -2
  126. data/lib/jade/stdlib/basics.rb +22 -3
  127. data/lib/jade/stdlib/char.rb +2 -0
  128. data/lib/jade/stdlib/clock.rb +6 -2
  129. data/lib/jade/stdlib/decimal.rb +51 -14
  130. data/lib/jade/stdlib/intrinsics.rb +59 -6
  131. data/lib/jade/stdlib/list.rb +37 -1
  132. data/lib/jade/stdlib/number.rb +47 -0
  133. data/lib/jade/stdlib/range.rb +211 -0
  134. data/lib/jade/stdlib.rb +11 -4
  135. data/lib/jade/symbol/alias.rb +11 -0
  136. data/lib/jade/symbol/parser.rb +1 -1
  137. data/lib/jade/symbol.rb +13 -1
  138. data/lib/jade/task.rb +0 -2
  139. data/lib/jade/type/anonymous_record.rb +11 -2
  140. data/lib/jade/type/application.rb +11 -2
  141. data/lib/jade/type/base.rb +26 -0
  142. data/lib/jade/type/function.rb +11 -2
  143. data/lib/jade/type.rb +36 -10
  144. data/lib/jade/version.rb +1 -1
  145. metadata +33 -2
@@ -5,12 +5,11 @@ module Jade
5
5
  def self.wildcard(types)
6
6
  types
7
7
  .map { Wildcard[] }
8
- .then { [it] }
9
- .then { Matrix[it, types] }
8
+ .then { Matrix[[it], types] }
10
9
  end
11
10
 
12
11
  def self.empty(types = [])
13
- self.new([], types)
12
+ Matrix[[], types]
14
13
  end
15
14
 
16
15
  def map
@@ -19,248 +18,58 @@ module Jade
19
18
  .then { with(rows: it) }
20
19
  end
21
20
 
22
- def include?(row)
23
- rows.include?(row)
24
- end
25
-
26
- def size
27
- rows.size
28
- end
29
-
30
21
  def empty?
31
22
  rows.empty?
32
23
  end
33
24
 
34
- def any?
35
- rows.any?
36
- end
37
-
38
25
  def concat(other)
39
26
  with(rows: rows + other.rows)
40
27
  end
41
28
 
42
- def missing_patterns(env)
43
- if types.empty?
44
- return rows.empty? ? Matrix[[[]], types] : Matrix.empty
45
- end
46
-
47
- type = types.first
48
-
49
- return Matrix.empty if never?(type)
50
- return Matrix.wildcard(types) if rows.empty?
51
-
52
- heads = head_constructors
53
-
54
- if infinite?(type) || type_var?(type)
55
- unmatched_column(env)
56
-
57
- elsif expandable?(type, env)
58
- expand(env).missing_patterns(env)
59
-
60
- elsif heads.empty?
61
- unmatched_column(env)
62
-
63
- else
64
- constructors_of(type, env)
65
- .reduce(Matrix.empty.with(types:)) do |acc, constructor|
66
- witnesses(constructor, env, heads.include?(constructor.name))
67
- .then { acc.concat(it) }
68
- end
69
- end
29
+ def heads
30
+ rows.map(&:first)
70
31
  end
71
32
 
72
- protected
73
-
74
- # Nothing in this column narrows anything — a type with no constructors
75
- # to enumerate, or one every row left open. Whatever is missing lives in
76
- # the columns after it.
77
- def unmatched_column(env)
78
- default
79
- .missing_patterns(env)
80
- .map { [Wildcard[]] + it }
81
- end
82
-
83
- # A constructor the first column matches is explored by specializing on
84
- # it. One it never matches can only be missing as a whole, so its
85
- # witnesses come from the rows that would have covered it — the ones
86
- # headed by a wildcard. Recursing there instead of into the constructor
87
- # is what keeps a recursive type from expanding forever.
88
- def witnesses(constructor, env, matched)
89
- return Matrix.empty if constructor.args.any? { never?(it) }
90
-
91
- arity = constructor.args.size
92
-
93
- if matched
94
- specialize(constructor)
95
- .missing_patterns(env)
96
- .map { [Constructor[constructor.name, it.take(arity)]] + it.drop(arity) }
97
-
98
- else
99
- default
100
- .missing_patterns(env)
101
- .map { [Constructor[constructor.name, Array.new(arity) { Wildcard[] }]] + it }
102
- end
103
- end
104
-
105
- def head_constructors
106
- rows.filter_map { head_constructor(it.first) }
107
- end
108
-
109
- def head_constructor(pattern)
110
- case pattern
111
- in Constructor(constructor: name) then name
112
- in Literal(value: true) then 'Basics.True'
113
- in Literal(value: false) then 'Basics.False'
114
- in Literal | Record | Wildcard then nil
115
- end
116
- end
117
-
118
- def expandable?(type, env)
119
- case type
120
- in Type::AnonymousRecord
121
- true
122
-
123
- in Type::Application(constructor:)
124
- env.lookup_def(constructor.name).is_a?(TypeChecking::StructDef)
125
-
126
- else
127
- false
128
- end
129
- end
130
-
131
- def expand(env)
132
- type_fields =
133
- case types.first
134
- in Type::AnonymousRecord(fields:)
135
- fields
136
-
137
- in Type::Application(constructor:)
138
- env.lookup_def(constructor.name).body.fields
139
- end
140
-
141
- map do |row|
142
- case row.first
143
- in Record(fields:)
144
- type_fields
145
- .map do |(k, v)|
146
- fields[k] || Wildcard[]
147
- end + row.drop(1)
148
-
149
- in Wildcard
150
- type_fields.map { Wildcard[] } + row.drop(1)
151
- end
152
- end
153
- .with(types: type_fields.values + types.drop(1))
154
- end
155
-
156
- def constructors_of(type, env)
157
- case type
158
- in Type::Application(constructor: Type::Constructor(name: 'Basics.Bool'))
159
- [
160
- TypeChecking::ConstructorDef['Basics.True', 'Basics.Bool', []],
161
- TypeChecking::ConstructorDef['Basics.False', 'Basics.Bool', []],
162
- ]
163
- in Type::Application(constructor: Type::Constructor(name: /^Tuple\.Tuple[2-4]$/ => name), args:)
164
- [TypeChecking::Definition.constructor(name, name, args)]
165
-
166
- in Type::Application(constructor: Type::Constructor(name: 'List.List'), args: [elem_type])
167
- [
168
- TypeChecking::ConstructorDef['List.Nil', 'List.List', []],
169
- TypeChecking::ConstructorDef['List.Cons', 'List.List', [elem_type, type]],
170
- ]
171
-
172
- else
173
- type_def = env.lookup_def(type.constructor.name)
174
-
175
- # Type the user can't destructure from here (transitively reached
176
- # through an imported struct field, or opaque intrinsic) —
177
- # any pattern they can write is trivially exhaustive.
178
- return [] if type_def.nil? || type_def.opaque?
179
-
180
- type_def
181
- .constructors
182
- .map do |con|
183
- con
184
- .args
185
- .map do |arg|
186
- if type_var?(arg)
187
- type_def
188
- .type_params
189
- .find_index { arg.name == it.name }
190
- .then { type.args.at(it) }
191
-
192
- else
193
- arg
194
- end
195
- end
196
- .then { con.with(args: it) }
197
- end
198
- end
199
- end
200
-
201
- def specialize(constructor)
33
+ # Rows the case can match, each opened up into the columns the case
34
+ # carries. A row headed by a wildcard matches every case, and says
35
+ # nothing about the columns it opens.
36
+ def specialize(kase)
202
37
  rows
203
- .filter_map do |cols|
204
- case cols.first
205
- in Literal(value:)
206
- next [] + cols.drop(1) if value == false && "Basics.False" == constructor.name
207
- next [] + cols.drop(1) if value == true && "Basics.True" == constructor.name
208
-
209
- in Constructor(constructor: cons_name, args:)
210
- args + cols.drop(1) if cons_name == constructor.name
211
-
212
- in Wildcard
213
- constructor.args.map { Wildcard[] } + cols.drop(1)
38
+ .filter_map do |row|
39
+ head = row.first
214
40
 
41
+ if kase.matches?(head)
42
+ head.args + row.drop(1)
43
+ elsif head.wildcard?
44
+ Array.new(kase.arity) { Wildcard[] } + row.drop(1)
215
45
  end
216
46
  end
217
- .then { with(rows: it) }
218
- .then { it.with(types: constructor.args + types.drop(1)) }
47
+ .then { Matrix[it, kase.arg_types + types.drop(1)] }
219
48
  end
220
49
 
50
+ # The rows that survive when the first column is decided by no case at
51
+ # all — only the ones that were not asking about it.
221
52
  def default
222
53
  rows
223
54
  .select { it.first.wildcard? }
224
55
  .map { it.drop(1) }
225
- .then { with(rows: it) }
226
- .with(types: types.drop(1))
56
+ .then { Matrix[it, types.drop(1)] }
227
57
  end
228
58
 
229
- def add(columns)
230
- with(rows: rows.concat(columns))
231
- end
232
-
233
- private
234
-
235
- def never?(type)
236
- case type
237
- in Type::Application(constructor: Type::Constructor(name: 'Basics.Never'))
238
- true
239
- else
240
- false
241
- end
242
- end
59
+ def expand(env)
60
+ fields = Signature.fields(types.first, env)
243
61
 
244
- def type_var?(type)
245
- type.is_a?(Type::Var)
62
+ map { |row| expand_row(row, fields) }
63
+ .with(types: fields.values + types.drop(1))
246
64
  end
247
65
 
248
- def infinite?(type)
249
- case type
250
- in Type::Function
251
- true
252
-
253
- in Type::Application(constructor:)
254
- case constructor
255
- in Type::Constructor(name: 'Basics.Int') then true
256
- in Type::Constructor(name: 'Basics.Float') then true
257
- in Type::Constructor(name: 'String.String') then true
258
- else
259
- false
260
- end
66
+ def expand_row(row, fields)
67
+ case row.first
68
+ in Record(fields: matched)
69
+ fields.keys.map { matched[it] || Wildcard[] } + row.drop(1)
261
70
 
262
- else
263
- false
71
+ in Wildcard
72
+ fields.keys.map { Wildcard[] } + row.drop(1)
264
73
  end
265
74
  end
266
75
  end
@@ -0,0 +1,132 @@
1
+ module Jade
2
+ module Frontend
3
+ module PatternAnalysis
4
+ Wildcard = Data.define do
5
+ def wildcard?
6
+ true
7
+ end
8
+
9
+ def args
10
+ []
11
+ end
12
+
13
+ def to_s
14
+ '_'
15
+ end
16
+ end
17
+
18
+ Literal = Data.define(:value) do
19
+ def wildcard?
20
+ false
21
+ end
22
+
23
+ def args
24
+ []
25
+ end
26
+
27
+ def to_s
28
+ value.inspect
29
+ end
30
+ end
31
+
32
+ Constructor = Data.define(:constructor, :args) do
33
+ def wildcard?
34
+ false
35
+ end
36
+
37
+ def to_s
38
+ if constructor.start_with?('Tuple.')
39
+ "(#{args.map(&:to_s).join(', ')})"
40
+ else
41
+ name = constructor.split('.').last
42
+ args.empty? ? name : "#{name}(#{args.map(&:to_s).join(', ')})"
43
+ end
44
+ end
45
+ end
46
+
47
+ Record = Data.define(:fields) do
48
+ def wildcard?
49
+ false
50
+ end
51
+
52
+ def args
53
+ []
54
+ end
55
+
56
+ def to_s
57
+ fields
58
+ .map { |k, v| "#{k}: #{v}" }
59
+ .join(', ')
60
+ .then { "{ #{it} }" }
61
+ end
62
+ end
63
+
64
+ # An inclusive span of integers. A nil bound is unbounded on that side.
65
+ Interval = Data.define(:from, :to) do
66
+ def wildcard?
67
+ false
68
+ end
69
+
70
+ def args
71
+ []
72
+ end
73
+
74
+ def covers?(other)
75
+ (from.nil? || (other.from && other.from >= from)) &&
76
+ (to.nil? || (other.to && other.to <= to))
77
+ end
78
+
79
+ def to_s
80
+ case [from, to]
81
+ in [nil, nil] then '_'
82
+ in [n, ^n] then n.to_s
83
+ else "#{from}..#{to}"
84
+ end
85
+ end
86
+ end
87
+
88
+ module Pattern
89
+ extend self
90
+
91
+ def from_node(node)
92
+ case node
93
+ in AST::Pattern::Record(fields:)
94
+ fields
95
+ .to_h { [it.name, from_node(it.pattern)] }
96
+ .then { Record[it] }
97
+
98
+ in AST::Pattern::Constructor(constructor:, patterns:)
99
+ patterns
100
+ .map { from_node(it) }
101
+ .then { Constructor[constructor.symbol.qualified_name, it] }
102
+
103
+ # `[x, y | xs]` and `[x, y]` are the same Cons/Nil spine the checker
104
+ # already knows how to split.
105
+ in AST::Pattern::List(patterns:, rest:)
106
+ patterns
107
+ .map { from_node(it) }
108
+ .reverse
109
+ .reduce(rest ? Wildcard[] : Constructor['List.Nil', []]) do |acc, head|
110
+ Constructor['List.Cons', [head, acc]]
111
+ end
112
+
113
+ in AST::Pattern::Range(from:, to:)
114
+ Interval[from&.value, to&.value]
115
+
116
+ in AST::Pattern::Binding | AST::Pattern::Wildcard
117
+ Wildcard[]
118
+
119
+ in AST::Pattern::Literal(literal: { value: true })
120
+ Constructor['Basics.True', []]
121
+
122
+ in AST::Pattern::Literal(literal: { value: false })
123
+ Constructor['Basics.False', []]
124
+
125
+ in AST::Pattern::Literal(literal: { value: })
126
+ Literal[value]
127
+ end
128
+ end
129
+ end
130
+ end
131
+ end
132
+ end
@@ -0,0 +1,24 @@
1
+ module Jade
2
+ module Frontend
3
+ module PatternAnalysis
4
+ module Redundancy
5
+ extend self
6
+
7
+ def assert(patterns, env, expected)
8
+ rows = patterns.map { [Pattern.from_node(it)] }
9
+
10
+ rows
11
+ .each_with_index
12
+ .filter_map do |row, index|
13
+ above = Matrix[rows.take(index), [expected]]
14
+
15
+ next if Usefulness.useful?(above, row, env)
16
+
17
+ TypeChecking::Error::UnreachableBranch
18
+ .new(env.entry_name, patterns[index].range)
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,220 @@
1
+ module Jade
2
+ module Frontend
3
+ module PatternAnalysis
4
+ # One shape a column's first pattern can take. `arg_types` are the
5
+ # columns it opens up when a row is specialized on it.
6
+ ConstructorCase = Data.define(:name, :arg_types) do
7
+ def arity
8
+ arg_types.size
9
+ end
10
+
11
+ def matches?(pattern)
12
+ pattern in Constructor(constructor: ^(name))
13
+ end
14
+
15
+ def witness(args)
16
+ Constructor[name, args]
17
+ end
18
+ end
19
+
20
+ # A literal the rows mention. Types whose values are literals have no
21
+ # enumerable set of these, so one is only ever split on because some row
22
+ # asked for it by name.
23
+ ValueCase = Data.define(:value) do
24
+ def name
25
+ value
26
+ end
27
+
28
+ def arity
29
+ 0
30
+ end
31
+
32
+ def arg_types
33
+ []
34
+ end
35
+
36
+ def matches?(pattern)
37
+ pattern in Literal(value: ^(value))
38
+ end
39
+
40
+ def witness(_args)
41
+ Literal[value]
42
+ end
43
+ end
44
+
45
+ # A stretch of the integer line no pattern in the column divides any
46
+ # further, so it is either wholly matched or wholly missed.
47
+ IntervalCase = Data.define(:from, :to) do
48
+ def arity
49
+ 0
50
+ end
51
+
52
+ def arg_types
53
+ []
54
+ end
55
+
56
+ def matches?(pattern)
57
+ case pattern
58
+ in Interval then pattern.covers?(self)
59
+ in Literal(value: Integer => n) then n == from && n == to
60
+ else false
61
+ end
62
+ end
63
+
64
+ def witness(_args)
65
+ Interval[from, to]
66
+ end
67
+ end
68
+
69
+ # The cases a column of a given type can be split into, and whether they
70
+ # account for every value of that type. When they don't, a row headed by
71
+ # a wildcard covers ground no case names, so the column cannot be
72
+ # decided by splitting alone.
73
+ Split = Data.define(:cases, :total) do
74
+ def covering(head)
75
+ cases.select { it.matches?(head) }
76
+ end
77
+
78
+ def touched_by?(heads)
79
+ cases.any? { |kase| heads.any? { kase.matches?(it) } }
80
+ end
81
+
82
+ def covered_by?(heads)
83
+ total && cases.any? &&
84
+ cases.all? { |kase| heads.any? { kase.matches?(it) } }
85
+ end
86
+ end
87
+
88
+ module Signature
89
+ extend self
90
+
91
+ UNIT = Split[[], true]
92
+ OPEN = Split[[], false]
93
+
94
+ def of(type, heads, env)
95
+ case type
96
+ in Type::Application(constructor: Type::Constructor(name:), args:)
97
+ named(type, name, args, heads, env)
98
+
99
+ else
100
+ OPEN
101
+ end
102
+ end
103
+
104
+ # No value of this type exists, so no row over it can be reached and
105
+ # nothing about it can be missing.
106
+ def uninhabited?(type)
107
+ name_of(type) == 'Basics.Never'
108
+ end
109
+
110
+ # A single-shape type is destructured into its fields rather than split
111
+ # into cases, so the columns behind it stay checkable.
112
+ def expandable?(type, env)
113
+ return true if type in Type::AnonymousRecord
114
+
115
+ env.lookup_def(name_of(type)).is_a?(TypeChecking::StructDef)
116
+ end
117
+
118
+ def fields(type, env)
119
+ case type
120
+ in Type::AnonymousRecord(fields:) then fields
121
+ else env.lookup_def(name_of(type)).body.fields
122
+ end
123
+ end
124
+
125
+ private
126
+
127
+ def name_of(type)
128
+ case type
129
+ in Type::Application(constructor: Type::Constructor(name:)) then name
130
+ else nil
131
+ end
132
+ end
133
+
134
+ def named(type, name, args, heads, env)
135
+ case name
136
+ in 'Basics.Bool'
137
+ Split[
138
+ [ConstructorCase['Basics.True', []], ConstructorCase['Basics.False', []]],
139
+ true,
140
+ ]
141
+
142
+ in /^Tuple\.Tuple[2-4]$/
143
+ Split[[ConstructorCase[name, args]], true]
144
+
145
+ in 'List.List'
146
+ args => [element]
147
+
148
+ Split[
149
+ [
150
+ ConstructorCase['List.Nil', []],
151
+ ConstructorCase['List.Cons', [element, type]],
152
+ ],
153
+ true,
154
+ ]
155
+
156
+ in 'Basics.Int'
157
+ integers(heads)
158
+
159
+ in 'Basics.Float' | 'String.String'
160
+ OPEN
161
+
162
+ else
163
+ union(type, name, env)
164
+ end
165
+ end
166
+
167
+ # Every bound in the column cuts the line in two, and the pieces
168
+ # between consecutive cuts are the coarsest split that still lets each
169
+ # pattern be answered whole. The pieces partition Int, so the result is
170
+ # total even though Int cannot be enumerated: a stretch no pattern
171
+ # names comes back as a gap rather than as an open column.
172
+ def integers(heads)
173
+ cuts = heads.flat_map { cuts_of(it) }.compact.uniq.sort
174
+
175
+ return OPEN if cuts.empty?
176
+
177
+ [nil, *cuts]
178
+ .zip([*cuts.map { it - 1 }, nil])
179
+ .map { |(from, to)| IntervalCase[from, to] }
180
+ .then { Split[it, true] }
181
+ end
182
+
183
+ def cuts_of(head)
184
+ case head
185
+ in Interval(from: Integer | nil => from, to: Integer | nil => to)
186
+ [from, to && to + 1]
187
+ in Literal(value: Integer => n) then [n, n + 1]
188
+ else []
189
+ end
190
+ end
191
+
192
+ def union(type, name, env)
193
+ type_def = env.lookup_def(name)
194
+
195
+ # A type the user cannot destructure from here — reached through an
196
+ # imported struct's field, or an opaque intrinsic. Any pattern they
197
+ # can write against it is trivially exhaustive.
198
+ return UNIT if type_def.nil? || type_def.opaque?
199
+ return OPEN unless type_def.is_a?(TypeChecking::TypeDef)
200
+
201
+ type_def
202
+ .constructors
203
+ .map { ConstructorCase[it.name, instantiate(it.args, type_def, type)] }
204
+ .then { Split[it, true] }
205
+ end
206
+
207
+ def instantiate(args, type_def, type)
208
+ args.map do |arg|
209
+ next arg unless arg.is_a?(Type::Var)
210
+
211
+ type_def
212
+ .type_params
213
+ .find_index { arg.name == it.name }
214
+ .then { type.args.at(it) }
215
+ end
216
+ end
217
+ end
218
+ end
219
+ end
220
+ end