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
@@ -7,29 +7,52 @@ module Jade
7
7
  extend self
8
8
  extend Codegen::Helpers
9
9
 
10
- def lift(call)
11
- return call unless call.args.any?(AST::Placeholder)
12
-
13
- filled_args, names = call
14
- .args
15
- .reduce([[], []]) do |(args, names), arg|
16
- case arg
17
- in AST::Placeholder
18
- param_synthetic_name(names.size)
19
- .then { [args + [AST::VariableReference[it, nil]], names + [it]] }
20
-
21
- else
22
- [args + [arg], names]
23
- end
10
+ def lift(node)
11
+ case node
12
+ in AST::FunctionCall(args:)
13
+ fill(node, args) { |filled| node.with(args: filled) }
14
+
15
+ in AST::RecordLiteral(fields:)
16
+ fill(node, fields.map(&:value)) do |filled|
17
+ fields
18
+ .zip(filled)
19
+ .map { |field, value| field.with(value:) }
20
+ .then { node.with(fields: it) }
21
+ end
22
+ end
23
+ end
24
+
25
+ private
26
+
27
+ def fill(node, slots, &rebuild)
28
+ return node unless slots.any?(AST::Placeholder)
29
+
30
+ named(slots)
31
+ .then { |filled, names| [rebuild.call(filled), names] }
32
+ .then { |body, names| wrap_in_lambdas(body, names, node.range) }
33
+ end
34
+
35
+ def named(slots)
36
+ slots.reduce([[], []]) do |(filled, names), slot|
37
+ case slot
38
+ in AST::Placeholder
39
+ param_synthetic_name(names.size)
40
+ .then { [filled + [AST::VariableReference[it, nil]], names + [it]] }
41
+
42
+ else
43
+ [filled + [slot], names]
44
+ end
24
45
  end
46
+ end
25
47
 
48
+ def wrap_in_lambdas(body, names, range)
26
49
  names
27
50
  .reverse
28
- .reduce(call.with(args: filled_args)) do |body, name|
51
+ .reduce(body) do |inner, name|
29
52
  AST::Lambda[
30
53
  [AST::Pattern::Binding[name, nil]],
31
- AST::Body[[body], nil],
32
- call.range,
54
+ AST::Body[[inner], nil],
55
+ range,
33
56
  ]
34
57
  end
35
58
  end
@@ -38,6 +38,20 @@ module Jade
38
38
  end
39
39
  .then { desugar(it) }
40
40
 
41
+ # There is no `Basics.(..)` to resolve to: the operator's result
42
+ # type lives in Range, and Basics is loaded before it.
43
+ in AST::InfixOperator(value: '..')
44
+ AST::FunctionCall.new(
45
+ callee: AST::MemberAccess[
46
+ AST::ConstructorReference['Range', operator.range],
47
+ AST::VariableReference['between', operator.range],
48
+ operator.range,
49
+ ],
50
+ args: [desugar(left), desugar(right)],
51
+ infix: operator,
52
+ range: node.range,
53
+ )
54
+
41
55
  else
42
56
  AST::FunctionCall.new(
43
57
  callee: AST::VariableReference["(#{operator.value})", operator.range],
@@ -98,7 +112,10 @@ module Jade
98
112
  .with(items: items.map { desugar(it) })
99
113
 
100
114
  in AST::RecordLiteral(fields:)
101
- fields.map { desugar(it) }.then { node.with(fields: it) }
115
+ fields
116
+ .map { desugar(it) }
117
+ .then { node.with(fields: it) }
118
+ .then { Placeholder.lift(it) }
102
119
 
103
120
  in AST::RecordUpdate(fields:)
104
121
  fields.map { desugar(it) }.then { node.with(fields: it) }
@@ -130,12 +147,14 @@ module Jade
130
147
  end
131
148
 
132
149
  in AST::Tuple(items:)
133
- AST::FunctionCall.new(
134
- callee: AST::ConstructorReference["Tuple.Tuple#{items.size}", node.range],
135
- args: items.map { desugar(it) },
136
- infix: false,
137
- range: node.range,
138
- )
150
+ AST::FunctionCall
151
+ .new(
152
+ callee: AST::ConstructorReference["Tuple.Tuple#{items.size}", node.range],
153
+ args: items.map { desugar(it) },
154
+ infix: false,
155
+ range: node.range,
156
+ )
157
+ .then { Placeholder.lift(it) }
139
158
 
140
159
  in AST::Pattern::Tuple(patterns:)
141
160
  AST::ConstructorReference[
@@ -174,9 +193,10 @@ module Jade
174
193
  in AST::Literal | AST::CharLiteral | AST::VariableReference | AST::ConstructorReference |
175
194
  AST::TypeDeclaration | AST::ImportDeclaration |
176
195
  AST::Pattern::Literal | AST::Pattern::Binding | AST::Pattern::Wildcard |
196
+ AST::Pattern::Range |
177
197
  AST::Pattern::Record | AST::InteropImportDeclaration | AST::StructDeclaration |
178
- AST::QualifiedAccess | AST::Placeholder | AST::InterfaceDeclaration |
179
- AST::CurriedConstructor
198
+ AST::TypeAliasDeclaration | AST::QualifiedAccess | AST::Placeholder |
199
+ AST::InterfaceDeclaration | AST::CurriedConstructor
180
200
 
181
201
  node
182
202
  end
@@ -51,6 +51,7 @@ module Jade
51
51
  '>=' => Fixity[4, :none],
52
52
  '>' => Fixity[4, :none],
53
53
  '<' => Fixity[4, :none],
54
+ '..' => Fixity[5, :none],
54
55
  '&&' => Fixity[3, :right],
55
56
  '||' => Fixity[2, :right],
56
57
  }.freeze
@@ -97,8 +98,9 @@ module Jade
97
98
  .then { node.with(target: it) }
98
99
 
99
100
  in AST::CaseOf(expression:, branches:)
100
- branches.map { fix(it) }
101
- .then { node.with(branches:, expression: fix(expression)) }
101
+ branches
102
+ .map { fix(it) }
103
+ .then { node.with(branches: it, expression: fix(expression)) }
102
104
 
103
105
  in AST::CaseOfBranch(body:)
104
106
  node.with(body: fix(body))
@@ -146,8 +148,8 @@ module Jade
146
148
 
147
149
  in AST::VariableReference | AST::ConstructorReference | AST::TypeDeclaration |
148
150
  AST::ImportDeclaration | AST::Literal | AST::CharLiteral | AST::RecordAccessSugar | AST::InteropImportDeclaration |
149
- AST::StructDeclaration | AST::QualifiedAccess | AST::Placeholder | AST::InterfaceDeclaration |
150
- AST::CurriedConstructor |
151
+ AST::StructDeclaration | AST::TypeAliasDeclaration | AST::QualifiedAccess |
152
+ AST::Placeholder | AST::InterfaceDeclaration | AST::CurriedConstructor |
151
153
  AST::RecordUpdateSugar
152
154
 
153
155
  node
@@ -0,0 +1,23 @@
1
+ module Jade
2
+ module Frontend
3
+ module ForwardDeclaration
4
+ module Error
5
+ class AliasExpansion < Jade::Error
6
+ def initialize(entry, span, name:)
7
+ @name = name
8
+ super(entry:, span:)
9
+ end
10
+
11
+ def message
12
+ "`#{@name}` is a type alias, so `(..)` has nothing to expose; " \
13
+ 'an alias names a shape and has no constructors of its own'
14
+ end
15
+
16
+ def label
17
+ "write `#{@name}` without `(..)`"
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,45 @@
1
+ module Jade
2
+ module Frontend
3
+ module ForwardDeclaration
4
+ module Error
5
+ # A struct and a union's variant both define a constructor, and a
6
+ # constructor is named without its type, so one module holds at most
7
+ # one of a name.
8
+ class DuplicateConstructorName < Jade::Error
9
+ def initialize(entry, span, name, declaring:, type:, owner:, first_span: nil)
10
+ @name = name
11
+ @declaring = declaring
12
+ @type = type
13
+ @owner = owner
14
+ @first_span = first_span
15
+ super(entry:, span:)
16
+ end
17
+
18
+ def message
19
+ case @owner
20
+ in Symbol::Struct then "`#{@name}` is already a struct in this module"
21
+ in Symbol::Union then "`#{@name}` is already a constructor of `#{@owner.name}`"
22
+ end
23
+ end
24
+
25
+ def label
26
+ case @declaring
27
+ in :variant then "`#{@type}` cannot also declare `#{@name}`"
28
+ in :struct then "cannot declare a struct `#{@name}`"
29
+ end
30
+ end
31
+
32
+ def secondary
33
+ return [] unless @first_span
34
+
35
+ [[@first_span, 'first declared here']]
36
+ end
37
+
38
+ def notes
39
+ [Jade::Diagnostics::Annotation[:help, 'rename one of them']]
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,42 @@
1
+ module Jade
2
+ module Frontend
3
+ module ForwardDeclaration
4
+ module Error
5
+ class DuplicateTypeName < Jade::Error
6
+ KINDS = {
7
+ Symbol::Interface => 'an interface',
8
+ Symbol::Union => 'a type',
9
+ Symbol::Struct => 'a struct',
10
+ Symbol::Alias => 'a type alias',
11
+ }.freeze
12
+
13
+ def self.kind_of(symbol)
14
+ KINDS.fetch(symbol.class)
15
+ end
16
+
17
+ def initialize(entry, span, name, declaring:, existing:, first_span: nil)
18
+ @name = name
19
+ @declaring = declaring
20
+ @existing = existing
21
+ @first_span = first_span
22
+ super(entry:, span:)
23
+ end
24
+
25
+ def message
26
+ "`#{@name}` is already declared as #{@existing} in this module"
27
+ end
28
+
29
+ def label
30
+ "cannot declare #{@declaring} `#{@name}`"
31
+ end
32
+
33
+ def secondary
34
+ return [] unless @first_span
35
+
36
+ [[@first_span, 'first declared here']]
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -1,6 +1,9 @@
1
1
  require 'jade/error'
2
2
 
3
3
  require 'jade/frontend/forward_declaration/error/bad_import'
4
+ require 'jade/frontend/forward_declaration/error/alias_expansion'
5
+ require 'jade/frontend/forward_declaration/error/duplicate_constructor_name'
6
+ require 'jade/frontend/forward_declaration/error/duplicate_type_name'
4
7
  require 'jade/frontend/forward_declaration/error/exposed_type_not_found'
5
8
  require 'jade/frontend/forward_declaration/error/exposed_value_not_found'
6
9
  require 'jade/frontend/forward_declaration/error/module_not_found'
@@ -10,6 +10,64 @@ module Jade
10
10
  ForwardDeclaration.deep_declare_node(ast, entry, registry)
11
11
  end
12
12
 
13
+ # Only the shallow pass still has the declaration being replaced;
14
+ # by the deep pass the entry holds the winner alone. The deep pass
15
+ # keeps its kind check to bail out, not to report.
16
+ def duplicate_type_error(entry, name, span, declaring:)
17
+ entry.defined_types[name].then do |existing|
18
+ next nil if existing.nil? || existing.decl_span == span
19
+
20
+ Error::DuplicateTypeName.new(
21
+ entry.name,
22
+ span,
23
+ name,
24
+ declaring:,
25
+ existing: Error::DuplicateTypeName.kind_of(existing),
26
+ first_span: existing.decl_span,
27
+ )
28
+ end
29
+ end
30
+
31
+ # A constructor is named without its type, so one module cannot hold
32
+ # two of a name — a union's variant and a struct both define one, and
33
+ # the second shadows the first. `introduced` is what this declaration
34
+ # is about to define, each with the span to point at.
35
+ def duplicate_constructor_errors(entry, name, introduced, declaring:)
36
+ declared_constructors(entry).then do |seen|
37
+ introduced.filter_map { duplicate_constructor_error(entry, name, it, seen, declaring) }
38
+ end
39
+ end
40
+
41
+ def declared_constructors(entry)
42
+ entry
43
+ .defined_types
44
+ .values
45
+ .flat_map do |symbol|
46
+ case symbol
47
+ in Symbol::Union then symbol.variants.map { [it.name, symbol] }
48
+ in Symbol::Struct then [[symbol.name, symbol]]
49
+ else []
50
+ end
51
+ end
52
+ .to_h
53
+ end
54
+
55
+ def duplicate_constructor_error(entry, name, (constructor, span), seen, declaring)
56
+ seen[constructor].then do |owner|
57
+ next nil if owner.nil? || owner.name == name
58
+
59
+ Error::DuplicateConstructorName.new(
60
+ entry.name,
61
+ span,
62
+ constructor,
63
+ declaring:,
64
+ type: name,
65
+ owner:,
66
+ first_span: owner.decl_span,
67
+ )
68
+ end
69
+ end
70
+
13
71
  def figure_out_type(entry, node)
14
72
  case node
15
73
  in AST::TypeVar(type:)
@@ -83,6 +83,10 @@ module Jade
83
83
  end
84
84
 
85
85
  def handling_exposing_type_expansion(type, name, span, current_entry, importing_module)
86
+ if importing_module.types[name].is_a?(Symbol::Alias)
87
+ return Err[Error::AliasExpansion.new(current_entry.name, span, name:)]
88
+ end
89
+
86
90
  importing_module
87
91
  .exposed_type_variants(name)
88
92
  .then do |variants|
@@ -8,6 +8,9 @@ module Jade
8
8
  def shallow(node, _registry, entry)
9
9
  node => AST::InterfaceDeclaration(name:, type_param:, functions:)
10
10
 
11
+ duplicate_type_error(entry, name, node.range, declaring: 'an interface')
12
+ .then { return Result[entry, [it]] if it }
13
+
11
14
  interface_ref = Symbol.type_ref(entry.name, name)
12
15
  type_var = Symbol.var(type_param.name, type_param.range)
13
16
 
@@ -28,6 +31,9 @@ module Jade
28
31
  node => AST::InterfaceDeclaration(name:, functions:)
29
32
 
30
33
  symbol = entry.lookup_type(name)
34
+
35
+ return Result[entry, []] unless symbol.is_a?(Symbol::Interface)
36
+
31
37
  interface_ref = symbol.to_ref
32
38
 
33
39
  functions
@@ -71,6 +71,10 @@ module Jade
71
71
  ]
72
72
  end
73
73
 
74
+ if symbol.is_a?(Symbol::Alias)
75
+ return Result[entry, [Error::AliasExpansion.new(entry.name, span, name:)]]
76
+ end
77
+
74
78
  symbol
75
79
  .constructor_refs
76
80
  .reduce(entry) { |acc, ctor| acc.expose(ctor.to_ref) }
@@ -8,11 +8,18 @@ module Jade
8
8
  def shallow(node, registry, entry)
9
9
  node => AST::StructDeclaration(name:, type_params:)
10
10
 
11
+ duplicate_type_error(entry, name, node.range, declaring: 'a struct')
12
+ .then { return Result[entry, [it]] if it }
13
+
14
+ # The struct is still declared, so a signature naming it does not
15
+ # report a second, misleading error.
16
+ errors = duplicate_constructor_errors(entry, name, [[name, node.range]], declaring: :struct)
17
+
11
18
  type_params
12
19
  .map { Symbol.var(it.name, it.range) }
13
20
  .then { Symbol.predeclared_struct(name, it, node.range) }
14
21
  .then { entry.define(it) }
15
- .then { Result[it, []] }
22
+ .then { Result[it, errors] }
16
23
  end
17
24
 
18
25
  def deep(node, entry, _)
@@ -20,6 +27,8 @@ module Jade
20
27
 
21
28
  symbol = entry.lookup_type(name)
22
29
 
30
+ return Result[entry, []] unless symbol.is_a?(Symbol::Struct)
31
+
23
32
  figure_out_type(entry, record_type)
24
33
  .map do |record_type_symbol|
25
34
  constructor_fn_symbol = Symbol.constructor(
@@ -0,0 +1,37 @@
1
+ module Jade
2
+ module Frontend
3
+ module ForwardDeclaration
4
+ module TypeAliasDeclaration
5
+ extend self
6
+ extend Helper
7
+
8
+ def shallow(node, registry, entry)
9
+ node => AST::TypeAliasDeclaration(name:, type_params:)
10
+
11
+ duplicate_type_error(entry, name, node.range, declaring: 'a type alias')
12
+ .then { return Result[entry, [it]] if it }
13
+
14
+ type_params
15
+ .map { Symbol.var(it.name, it.range) }
16
+ .then { Symbol.predeclared_alias(name, it, node.range) }
17
+ .then { entry.define(it) }
18
+ .then { Result[it, []] }
19
+ end
20
+
21
+ def deep(node, entry, _)
22
+ node => AST::TypeAliasDeclaration(name:, body_type:)
23
+
24
+ symbol = entry.lookup_type(name)
25
+
26
+ return Result[entry, []] unless symbol.is_a?(Symbol::Alias)
27
+
28
+ figure_out_type(entry, body_type)
29
+ .map do |body_symbol|
30
+ entry.define(symbol.with(body: body_symbol))
31
+ end
32
+ .then { to_declaration_result(entry, it) }
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -8,13 +8,20 @@ module Jade
8
8
  def shallow(node, registry, entry)
9
9
  node => AST::TypeDeclaration(name:, type_params:, variants:)
10
10
 
11
+ duplicate_type_error(entry, name, node.range, declaring: 'a type')
12
+ .then { return Result[entry, [it]] if it }
13
+
14
+ # The type is still declared, so a signature naming it does not
15
+ # report a second, misleading error.
16
+ errors = duplicate_constructor_errors(entry, name, variants.map { [it.name, it.range] }, declaring: :variant)
17
+
11
18
  predeclared_variants = variants
12
19
  .map { |var| Symbol.predeclared_constructor(var.name, var.range) }
13
20
 
14
21
  type_params.map { Symbol.var(it.name, it.range) }
15
22
  .then { Symbol.union(name, it, predeclared_variants, node.range) }
16
23
  .then { entry.define(it) }
17
- .then { Result[it, []] }
24
+ .then { Result[it, errors] }
18
25
  end
19
26
 
20
27
  def deep(node, entry, _)
@@ -22,6 +29,8 @@ module Jade
22
29
 
23
30
  symbol = entry.lookup_type(name)
24
31
 
32
+ return Result[entry, []] unless symbol.is_a?(Symbol::Union)
33
+
25
34
  variants
26
35
  .map do |var|
27
36
  var.args.map { figure_out_type(entry, it) }
@@ -11,6 +11,7 @@ require 'jade/frontend/forward_declaration/import_declaration'
11
11
  require 'jade/frontend/forward_declaration/interop_import_declaration'
12
12
  require 'jade/frontend/forward_declaration/module'
13
13
  require 'jade/frontend/forward_declaration/struct_declaration'
14
+ require 'jade/frontend/forward_declaration/type_alias_declaration'
14
15
  require 'jade/frontend/forward_declaration/type_declaration'
15
16
  require 'jade/frontend/forward_declaration/interface_declaration'
16
17
 
@@ -60,6 +61,7 @@ module Jade
60
61
  in AST::InteropImportDeclaration then InteropImportDeclaration
61
62
  in AST::Module then Module
62
63
  in AST::StructDeclaration then StructDeclaration
64
+ in AST::TypeAliasDeclaration then TypeAliasDeclaration
63
65
  in AST::TypeDeclaration then TypeDeclaration
64
66
  in AST::InterfaceDeclaration then InterfaceDeclaration
65
67
  else
@@ -4,17 +4,58 @@ module Jade
4
4
  module Exhaustiveness
5
5
  extend self
6
6
 
7
- def assert(patterns, range, env, registry, expected)
8
- Matrix[
9
- patterns.map { [node_to_matrix_pattern(it)] },
10
- [expected],
11
- ]
12
- .missing_patterns(env)
7
+ def assert(patterns, range, env, expected)
8
+ Matrix[patterns.map { [Pattern.from_node(it)] }, [expected]]
9
+ .then { Witnesses.missing(it, env) }
10
+ .then { it.with(rows: coalesced(it.rows)) }
13
11
  .then { to_errors(it, range, env) }
14
12
  end
15
13
 
16
14
  private
17
15
 
16
+ # Splitting a column cuts it at every bound any pattern mentions, so
17
+ # one hole can come back as several touching pieces. Report the hole.
18
+ def coalesced(rows)
19
+ joined = rows
20
+ .combination(2)
21
+ .lazy
22
+ .filter_map { |(left, right)| join(left, right)&.then { [left, right, it] } }
23
+ .first
24
+
25
+ return rows if joined.nil?
26
+
27
+ joined => [left, right, combined]
28
+
29
+ coalesced(rows - [left, right] + [combined])
30
+ end
31
+
32
+ def join(left, right)
33
+ return if left.size != right.size
34
+
35
+ differing = left.each_index.reject { left[it] == right[it] }
36
+
37
+ return if differing.size != 1
38
+
39
+ index = differing.first
40
+
41
+ span(left[index], right[index])
42
+ &.then { |joined| left.take(index) + [joined] + left.drop(index + 1) }
43
+ end
44
+
45
+ def span(left, right)
46
+ case [left, right]
47
+ in [Interval(from:, to: Integer => last), Interval(from: ^(last + 1), to:)]
48
+ Interval[from, to]
49
+
50
+ in [Constructor(constructor: name, args: left_args),
51
+ Constructor(constructor: ^name, args: right_args)]
52
+ join(left_args, right_args)&.then { Constructor[name, it] }
53
+
54
+ else
55
+ nil
56
+ end
57
+ end
58
+
18
59
  def to_errors(matrix, range, env)
19
60
  return [] if matrix.empty?
20
61
 
@@ -22,43 +63,6 @@ module Jade
22
63
  .new(env.entry_name, range, missing_patterns: matrix.rows)
23
64
  .then { [it] }
24
65
  end
25
-
26
- def node_to_matrix_pattern(pattern_node)
27
- case pattern_node
28
- in AST::Pattern::Record(fields:)
29
- Record[
30
- fields
31
- .map(&:name)
32
- .zip(fields.map(&:pattern))
33
- .to_h
34
- .transform_values { node_to_matrix_pattern(it) },
35
- ]
36
-
37
- in AST::Pattern::Constructor(constructor:, patterns:)
38
- Constructor[
39
- constructor.symbol.qualified_name,
40
- patterns.map { node_to_matrix_pattern(it) },
41
- ]
42
-
43
- in AST::Pattern::List(patterns:, rest:)
44
- # Rewrite [x, y | xs] / [x, y] into nested Cons/Nil constructors
45
- tail = rest ? Wildcard[] : Constructor['List.Nil', []]
46
- patterns
47
- .map { node_to_matrix_pattern(it) }
48
- .reverse
49
- .reduce(tail) { |acc, head| Constructor['List.Cons', [head, acc]] }
50
-
51
- in AST::Pattern::Binding | AST::Pattern::Wildcard
52
- Wildcard[]
53
-
54
- in AST::Pattern::Literal(literal: { value:, symbol: })
55
- Literal[
56
- value,
57
- symbol.qualified_name,
58
- ]
59
-
60
- end
61
- end
62
66
  end
63
67
  end
64
68
  end