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
@@ -3,21 +3,37 @@ module Jade
3
3
  module TypeChecking
4
4
  module Error
5
5
  class MissingPatterns < Jade::Error
6
+ LISTED = 10
7
+
6
8
  def initialize(entry, span, missing_patterns:)
7
9
  @missing_patterns = missing_patterns
8
10
  super(entry:, span:)
9
11
  end
10
12
 
11
13
  def message
12
- patterns_str = @missing_patterns
14
+ "Pattern match is not exhaustive. Missing cases:\n#{listed}#{rest}"
15
+ end
16
+
17
+ def label
18
+ "non-exhaustive pattern match"
19
+ end
20
+
21
+ private
22
+
23
+ def listed
24
+ @missing_patterns
25
+ .take(LISTED)
13
26
  .map { |row| row.map(&:to_s).join(', ') }
14
27
  .map { " #{it}" }
15
28
  .join("\n")
16
- "Pattern match is not exhaustive. Missing cases:\n#{patterns_str}"
17
29
  end
18
30
 
19
- def label
20
- "non-exhaustive pattern match"
31
+ # A table split at every bound can leave hundreds of holes, and a
32
+ # three-hundred-line error is not one anybody reads.
33
+ def rest
34
+ return '' if @missing_patterns.size <= LISTED
35
+
36
+ "\n … and #{@missing_patterns.size - LISTED} more"
21
37
  end
22
38
  end
23
39
  end
@@ -2,9 +2,15 @@ module Jade
2
2
  module Frontend
3
3
  module TypeChecking
4
4
  module Error
5
+ # A pattern the checker could not make sense of. Coverage is a claim
6
+ # about the patterns as written, so one of these silences it.
7
+ module PatternProblem; end
8
+
5
9
  class PatternTypeMismatch < TypeMismatch
10
+ include PatternProblem
11
+
6
12
  def message
7
- "Pattern is trying to match #{@expected} with #{@actual}"
13
+ "Pattern is trying to match #{naming[@expected]} with #{naming[@actual]}"
8
14
  end
9
15
  end
10
16
  end
@@ -0,0 +1,29 @@
1
+ module Jade
2
+ module Frontend
3
+ module TypeChecking
4
+ module Error
5
+ class RangePatternType < PatternTypeMismatch
6
+ def message
7
+ "A range pattern only matches Int, and this one is matching " \
8
+ "#{naming[@expected]}."
9
+ end
10
+
11
+ def label
12
+ 'range pattern on a type that is not Int'
13
+ end
14
+
15
+ def notes
16
+ [
17
+ Jade::Diagnostics::Annotation[
18
+ :note,
19
+ 'Int is the only type a range covers checkably: 0..2 and ' \
20
+ '3..12 sit next to each other with nothing in between ' \
21
+ 'them to be missed',
22
+ ],
23
+ ]
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -4,8 +4,8 @@ module Jade
4
4
  module Error
5
5
  class RecordAccessTypeMismatch < TypeMismatch
6
6
  def message
7
- "Something is off with this record access, it expects #{@expected} " +
8
- "but found #{@actual}"
7
+ "Something is off with this record access, it expects #{naming[@expected]} " +
8
+ "but found #{naming[@actual]}"
9
9
  end
10
10
  end
11
11
  end
@@ -0,0 +1,25 @@
1
+ module Jade
2
+ module Frontend
3
+ module TypeChecking
4
+ module Error
5
+ class RecursiveDerivation < Jade::Error
6
+ attr_reader :constraint
7
+
8
+ def initialize(entry, span, constraint:)
9
+ @constraint = constraint
10
+ super(entry:, span:)
11
+ end
12
+
13
+ def message
14
+ "#{@constraint.interface} cannot be derived for #{@constraint.type} " \
15
+ 'because it contains itself; write the implementation by hand'
16
+ end
17
+
18
+ def label
19
+ "#{@constraint.type} is recursive"
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -10,11 +10,19 @@ module Jade
10
10
  end
11
11
 
12
12
  def message
13
- "Expected #{@expected} but got #{@actual}"
13
+ "Expected #{naming.annotated(@expected)} but got #{naming.annotated(@actual)}"
14
14
  end
15
15
 
16
16
  def label
17
- "expected #{@expected}, got #{@actual}"
17
+ "expected #{naming[@expected]}, got #{naming[@actual]}"
18
+ end
19
+
20
+ attr_reader :expected, :actual
21
+
22
+ private
23
+
24
+ def naming
25
+ @naming ||= Display.naming(@expected, @actual)
18
26
  end
19
27
  end
20
28
  end
@@ -0,0 +1,22 @@
1
+ module Jade
2
+ module Frontend
3
+ module TypeChecking
4
+ module Error
5
+ class UnreachableBranch < Jade::Error
6
+ def initialize(entry, span)
7
+ super(entry:, span:)
8
+ end
9
+
10
+ def message
11
+ 'This branch can never match. The branches above it already cover ' \
12
+ 'every value that would reach it.'
13
+ end
14
+
15
+ def label
16
+ 'unreachable branch'
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -1,17 +1,23 @@
1
1
  require 'jade/error'
2
2
 
3
+ require 'jade/frontend/type_checking/error/display'
3
4
  require 'jade/frontend/type_checking/error/type_mismatch'
4
5
 
5
6
  require 'jade/frontend/type_checking/error/case_of_branches_type_mismatch'
6
7
  require 'jade/frontend/type_checking/error/derivation_failed'
8
+ require 'jade/frontend/type_checking/error/recursive_derivation'
7
9
  require 'jade/frontend/type_checking/error/function_body_type_mismatch'
8
10
  require 'jade/frontend/type_checking/error/function_call_type_mismatch'
9
11
  require 'jade/frontend/type_checking/error/if_branch_type_mismatch'
10
12
  require 'jade/frontend/type_checking/error/if_branches_type_mismatch'
11
13
  require 'jade/frontend/type_checking/error/if_condition_type_mismatch'
12
14
  require 'jade/frontend/type_checking/error/missing_patterns'
15
+ require 'jade/frontend/type_checking/error/division_by_zero'
16
+ require 'jade/frontend/type_checking/error/unreachable_branch'
13
17
  require 'jade/frontend/type_checking/error/list_item_type_mismatch'
14
18
  require 'jade/frontend/type_checking/error/pattern_type_mismatch'
19
+ require 'jade/frontend/type_checking/error/empty_range_pattern'
20
+ require 'jade/frontend/type_checking/error/range_pattern_type'
15
21
  require 'jade/frontend/type_checking/error/record_access_type_mismatch'
16
22
  require 'jade/frontend/type_checking/error/unresolved_constraint'
17
23
  require 'jade/frontend/type_checking/error/implementation_type_mismatch'
@@ -34,7 +34,7 @@ module Jade
34
34
  end
35
35
 
36
36
  PatternAnalysis::Exhaustiveness
37
- .assert([pattern], pattern.range, final_state.env, registry, expr_result.type)
37
+ .assert([pattern], pattern.range, final_state.env, expr_result.type)
38
38
  .then { final_state.add_errors(it) }
39
39
  .unify_result(expr_result.with(constraints: residual_cs), expected.type)
40
40
  end
@@ -46,7 +46,7 @@ module Jade
46
46
  expr_result,
47
47
  )
48
48
  end
49
- .then { |st, cs| [check_exhaustiveness(node, st, registry, expr_result), cs] }
49
+ .then { |st, cs| [check_patterns(node, st, expr_result), cs] }
50
50
  .then do |st, cs|
51
51
  first_result
52
52
  .with(constraints: cs)
@@ -75,17 +75,34 @@ module Jade
75
75
  )
76
76
  end
77
77
 
78
- def check_exhaustiveness(node, state, registry, result)
78
+ # Coverage is a claim about the patterns as written. When one of them
79
+ # failed to type there is nothing coherent to claim, and saying it
80
+ # anyway buries the real error under invented ones.
81
+ def check_patterns(node, state, result)
79
82
  node => AST::CaseOf(branches:)
80
83
 
84
+ return state if mistyped_patterns?(node, state)
85
+
81
86
  patterns = branches.map(&:pattern)
82
87
  type = result.apply(state.env.substitution).type
83
88
 
84
- PatternAnalysis::Exhaustiveness
85
- .assert(patterns, node.range, state.env, registry, type)
89
+ [
90
+ PatternAnalysis::Exhaustiveness.assert(patterns, node.range, state.env, type),
91
+ PatternAnalysis::Redundancy.assert(patterns, state.env, type),
92
+ ]
93
+ .flatten
86
94
  .then { state.add_errors(it) }
87
95
  end
88
96
 
97
+ def mistyped_patterns?(node, state)
98
+ state
99
+ .errors
100
+ .any? do |error|
101
+ error.is_a?(Error::PatternProblem) &&
102
+ node.range.cover?(error.span.begin)
103
+ end
104
+ end
105
+
89
106
  def infer_branch(node, registry, env, expected, expression_result)
90
107
  node => AST::CaseOfBranch(pattern:, body:)
91
108
 
@@ -0,0 +1,50 @@
1
+ module Jade
2
+ module Frontend
3
+ module TypeChecking
4
+ module Inference
5
+ # `/` asks for a `NonZero`, which nothing but `non_zero` can build.
6
+ # A literal is the exception: the compiler can see whether it is
7
+ # zero, so `cents / 100` needs no ceremony and `n / 0` is caught
8
+ # where it is written.
9
+ module Division
10
+ extend self
11
+
12
+ OPERATOR = '(/)'.freeze
13
+
14
+ # Lifted even when the literal is zero: the divide-by-zero error is
15
+ # the one worth reading, and a type mismatch under it is noise.
16
+ def lift(node, acc)
17
+ return acc unless divisor(node)
18
+
19
+ acc.with(types: [*acc.types[0..-2], non_zero(acc.types.last)])
20
+ end
21
+
22
+ def by_zero(node, entry_name)
23
+ return [] unless divisor(node)&.zero?
24
+
25
+ [Error::DivisionByZero.new(entry_name, node.args.last.range)]
26
+ end
27
+
28
+ private
29
+
30
+ def divisor(node)
31
+ return nil unless node.args.size == 2 && operator?(node.callee)
32
+
33
+ case node.args.last
34
+ in AST::Literal(value: ::Integer | ::Float => value) then value
35
+ else nil
36
+ end
37
+ end
38
+
39
+ def operator?(callee)
40
+ callee in AST::VariableReference(name: OPERATOR)
41
+ end
42
+
43
+ def non_zero(type)
44
+ Type.constructor('Basics.NonZero').apply([type])
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -28,10 +28,12 @@ module Jade
28
28
  .then { |(new_state, result)| [new_state, acc.add(result)] }
29
29
  end
30
30
 
31
+ args_state = args_state.add_errors(Division.by_zero(node, args_state.env.entry_name))
32
+
31
33
  after_callee_state, result_type = unify_callee(
32
34
  args_state,
33
35
  callee_result,
34
- args_acc,
36
+ Division.lift(node, args_acc),
35
37
  node,
36
38
  state,
37
39
  )
@@ -170,9 +172,32 @@ module Jade
170
172
  expected: e.expected,
171
173
  actual: e.actual,
172
174
  infix: node.infix,
175
+ lifted_placeholder: lifted_placeholder?(node),
176
+ callee_name: source_name(node.callee),
177
+ arg_names: node.args.map { source_name(it) },
173
178
  )
174
179
  end
175
180
  end
181
+
182
+ # Desugaring lifted the `_` into a lambda parameter, so a hole
183
+ # reads as an argument referring to one by the time we get here.
184
+ def lifted_placeholder?(node)
185
+ node.args.any? { it in AST::VariableReference(name: /\A__p\d+__\z/) }
186
+ end
187
+
188
+ # What the author called this, where they called it something.
189
+ def source_name(node)
190
+ case node
191
+ in AST::ConstructorReference | AST::VariableReference
192
+ node.name
193
+
194
+ in AST::QualifiedAccess(path:)
195
+ path.join('.')
196
+
197
+ else
198
+ nil
199
+ end
200
+ end
176
201
  end
177
202
  end
178
203
  end
@@ -56,7 +56,7 @@ module Jade
56
56
  else
57
57
  concrete_t = body_state.env.substitution.apply(t)
58
58
  PatternAnalysis::Exhaustiveness
59
- .assert([p], p.range, acc.env, registry, concrete_t)
59
+ .assert([p], p.range, acc.env, concrete_t)
60
60
  .then { acc.add_errors(it) }
61
61
  end
62
62
  end
@@ -11,6 +11,32 @@ module Jade
11
11
  .then { |s, r| [s.with(env: s.env.pin_type(pattern.id, r.type)), r] }
12
12
  end
13
13
 
14
+ def range_type_error(state, pattern)
15
+ ->(error) do
16
+ Error::RangePatternType.new(
17
+ state.env.entry_name, pattern.range,
18
+ expected: error.expected, actual: error.actual,
19
+ )
20
+ end
21
+ end
22
+
23
+ def empty_range(pattern, state, result)
24
+ pattern => AST::Pattern::Range(from:, to:)
25
+
26
+ return [] unless state.env.substitution.apply(result.type) in Type::Application(
27
+ constructor: Type::Constructor(name: 'Basics.Int'),
28
+ )
29
+
30
+ return [] unless from && to && from.value > to.value
31
+
32
+ [Error::EmptyRangePattern.new(
33
+ state.env.entry_name,
34
+ pattern.range,
35
+ from: from.value,
36
+ to: to.value,
37
+ )]
38
+ end
39
+
14
40
  def infer_(pattern, registry, state, expected)
15
41
  case pattern
16
42
  in AST::Pattern::Record(fields:, symbol:)
@@ -34,6 +60,15 @@ module Jade
34
60
  .unify_result(it, expected.type, &type_error(state, pattern))
35
61
  end
36
62
 
63
+ in AST::Pattern::Range
64
+ state
65
+ .unify_result(
66
+ Result.init(Type.int),
67
+ expected.type,
68
+ &range_type_error(state, pattern)
69
+ )
70
+ .then { |(st, r)| [st.add_errors(empty_range(pattern, st, r)), r] }
71
+
37
72
  in AST::Pattern::Literal(literal:)
38
73
  new_state, literal_result = check(literal, registry, state, expected)
39
74
  new_state.unify_result(literal_result, expected.type, &type_error(state, pattern))
@@ -0,0 +1,18 @@
1
+ module Jade
2
+ module Frontend
3
+ module TypeChecking
4
+ module Inference
5
+ module TypeAliasDeclaration
6
+ extend Helpers
7
+ extend self
8
+
9
+ def infer(node, _, state, _)
10
+ node => AST::TypeAliasDeclaration
11
+
12
+ [state, Result.init(Type.unit)]
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -3,6 +3,7 @@ require 'jade/frontend/type_checking/inference/helpers'
3
3
  require 'jade/frontend/type_checking/inference/body'
4
4
  require 'jade/frontend/type_checking/inference/case_of'
5
5
  require 'jade/frontend/type_checking/inference/constructor_reference'
6
+ require 'jade/frontend/type_checking/inference/division'
6
7
  require 'jade/frontend/type_checking/inference/function_call'
7
8
  require 'jade/frontend/type_checking/inference/function_declaration'
8
9
  require 'jade/frontend/type_checking/inference/implementation'
@@ -21,6 +22,7 @@ require 'jade/frontend/type_checking/inference/record_field'
21
22
  require 'jade/frontend/type_checking/inference/record_literal'
22
23
  require 'jade/frontend/type_checking/inference/record_update'
23
24
  require 'jade/frontend/type_checking/inference/struct_declaration'
25
+ require 'jade/frontend/type_checking/inference/type_alias_declaration'
24
26
  require 'jade/frontend/type_checking/inference/interface_declaration'
25
27
  require 'jade/frontend/type_checking/inference/type_declaration'
26
28
  require 'jade/frontend/type_checking/inference/assign'
@@ -35,12 +35,22 @@ module Jade
35
35
  entry
36
36
  .types
37
37
  .reduce(env) do |e, (_, sym)|
38
+ # Aliases are transparent — they get expanded into their
39
+ # body whenever a TypeRef resolves to one. They never need
40
+ # to be looked up as definitions during unification.
41
+ next e if alias_symbol?(sym, registry)
42
+
38
43
  Definition
39
44
  .from_symbol(sym, registry)
40
45
  .then { e.define(sym.qualified_name, it) }
41
46
  end
42
47
  end
43
48
 
49
+ def alias_symbol?(sym, registry)
50
+ sym.is_a?(Symbol::Alias) ||
51
+ (sym.is_a?(Symbol::TypeRef) && registry.lookup(sym).is_a?(Symbol::Alias))
52
+ end
53
+
44
54
  def load_imports(env, entry, registry)
45
55
  entry
46
56
  .imports
@@ -65,6 +75,7 @@ module Jade
65
75
 
66
76
  in Symbol::TypeRef
67
77
  return env if env.lookup_def(sym.qualified_name)
78
+ return env if registry.lookup(sym).is_a?(Symbol::Alias)
68
79
 
69
80
  Definition
70
81
  .from_symbol(sym, registry)
@@ -36,9 +36,12 @@ module Jade
36
36
 
37
37
  private
38
38
 
39
+ # Every port reaching here returns a Task: `NonTaskPort` runs in
40
+ # semantic analysis and stops compilation before this, which is what
41
+ # lets `resolve_port` destructure the return type without checking.
39
42
  def resolve_value(sym, entry, registry)
40
43
  case sym
41
- in Symbol::InteropFunction(return_type: Symbol::TypeApplication)
44
+ in Symbol::InteropFunction
42
45
  resolve_port(sym, entry, registry)
43
46
 
44
47
  else
@@ -47,7 +50,7 @@ module Jade
47
50
  end
48
51
 
49
52
  def resolve_port(interop_fn, entry, registry)
50
- interop_fn.return_type => Symbol::TypeApplication(args: [ok_sym, err_sym])
53
+ ok_sym, err_sym = arm_symbols(interop_fn.return_type)
51
54
 
52
55
  # Single Type.from_symbol over the whole port so a var appearing in
53
56
  # more than one position gets the same Type::Var id. PortCodec relies
@@ -66,6 +69,19 @@ module Jade
66
69
  ]
67
70
  end
68
71
 
72
+ # The arms are only wanted for their spans, and an alias keeps its
73
+ # arms in another declaration, so there is often none to point at and
74
+ # the port's own return type is the best there is.
75
+ def arm_symbols(return_type)
76
+ case return_type
77
+ in Symbol::TypeApplication(constructor: Symbol::TypeRef['Task', 'Task'], args: [ok, err])
78
+ [ok, err]
79
+
80
+ else
81
+ [nil, nil]
82
+ end
83
+ end
84
+
69
85
  # Without the constraints the shared VarGen would still hand every
70
86
  # position its own var; `from_symbol` only ties them together through
71
87
  # the map it threads across params and return.
@@ -83,7 +99,7 @@ module Jade
83
99
  end
84
100
 
85
101
  def resolve_param(type_sym, type, interop_fn, index, entry, registry)
86
- return [Symbol::InteropFunction::PASS, []] if pass_through?(type_sym)
102
+ return [Symbol::InteropFunction::PASS, []] if pass_through?(type)
87
103
 
88
104
  case type
89
105
  in Type::Var(name:)
@@ -99,7 +115,7 @@ module Jade
99
115
  end
100
116
 
101
117
  def resolve_arm(type_sym, type, interop_fn, arm, entry, registry)
102
- return [Symbol::InteropFunction::PASS, []] if pass_through?(type_sym)
118
+ return [Symbol::InteropFunction::PASS, []] if pass_through?(type)
103
119
 
104
120
  case type
105
121
  in Type::Var(name:)
@@ -156,10 +172,12 @@ module Jade
156
172
  end
157
173
  end
158
174
 
159
- def pass_through?(type_sym)
160
- case type_sym
161
- in Symbol::TypeApplication(constructor: Symbol::TypeRef(module_name:, name:))
162
- [module_name, name] in ['Basics', 'Never'] | ['Decode', 'Value']
175
+ # Asked of the expanded type, so an alias over `Never` or
176
+ # `Decode.Value` passes through the same as the type it names.
177
+ def pass_through?(type)
178
+ case type
179
+ in Type::Application(constructor: Type::Constructor(name: 'Basics.Never' | 'Decode.Value'))
180
+ true
163
181
 
164
182
  else
165
183
  false
@@ -47,7 +47,7 @@ module Jade
47
47
  in Type::Application(args:)
48
48
  case apply(type.constructor)
49
49
  in Type::PartialApplication(constructor:, args: tail_args)
50
- Type::Application[constructor, args.map { apply(it) } + tail_args]
50
+ Type::Application[constructor, args.map { apply(it) } + tail_args, type.display]
51
51
 
52
52
  in constructor
53
53
  type.with(constructor:, args: args.map { apply(it) })
@@ -60,7 +60,7 @@ module Jade
60
60
 
61
61
  case apply(row_var)
62
62
  in Type::AnonymousRecord(fields: extra_fields, row_var: new_row_var)
63
- Type.anonymous_record(applied_fields.merge(extra_fields), new_row_var)
63
+ Type.anonymous_record(applied_fields.merge(extra_fields), new_row_var, type.display)
64
64
 
65
65
  in Type::Var => applied_row_var
66
66
  type.with(fields: applied_fields, row_var: applied_row_var)
@@ -1,3 +1,4 @@
1
+ require 'jade/frontend/type_checking/cascade'
1
2
  require 'jade/frontend/type_checking/canonicalize'
2
3
  require 'jade/frontend/type_checking/constraints'
3
4
  require 'jade/frontend/type_checking/definition'
@@ -97,7 +98,7 @@ module Jade
97
98
  # with those constraints — rather than being dropped silently.
98
99
 
99
100
  state
100
- .with(errors: state.errors + errors)
101
+ .with(errors: Cascade.prune(state.errors + errors))
101
102
  .to_result
102
103
  end
103
104
 
@@ -143,6 +144,7 @@ module Jade
143
144
  in AST::QualifiedAccess then Inference::QualifiedAccess
144
145
  in AST::RecordAccess then Inference::RecordAccess
145
146
  in AST::StructDeclaration then Inference::StructDeclaration
147
+ in AST::TypeAliasDeclaration then Inference::TypeAliasDeclaration
146
148
  in AST::InterfaceDeclaration then Inference::InterfaceDeclaration
147
149
  in AST::RecordField then Inference::RecordField
148
150
  in AST::RecordLiteral then Inference::RecordLiteral
@@ -97,7 +97,8 @@ module Jade
97
97
  in AST::Pattern::Record(fields:)
98
98
  fields.flat_map { walk(it.pattern, :as_value, entry, owner) }
99
99
 
100
- in AST::Pattern::Literal | AST::Pattern::Binding | AST::Pattern::Wildcard
100
+ in AST::Pattern::Literal | AST::Pattern::Binding | AST::Pattern::Wildcard |
101
+ AST::Pattern::Range
101
102
  []
102
103
 
103
104
  in AST::Grouping(expression:)
@@ -138,6 +139,9 @@ module Jade
138
139
  in AST::StructDeclaration(record_type:, symbol:)
139
140
  walk_type(record_type, entry, ReferenceIndex.key_for(symbol))
140
141
 
142
+ in AST::TypeAliasDeclaration(body_type:, symbol:)
143
+ walk_type(body_type, entry, ReferenceIndex.key_for(symbol))
144
+
141
145
  in AST::InterfaceDeclaration(functions:, symbol:)
142
146
  ReferenceIndex.key_for(symbol).then do |declared|
143
147
  functions.flat_map { walk_type(it.type, entry, declared) }