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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +400 -12
- data/docs/interop.md +18 -1
- data/docs/stdlib.md +3 -1
- data/docs/syntax.md +96 -0
- data/lib/jade/api.rb +9 -0
- data/lib/jade/ast.rb +37 -1
- data/lib/jade/cli/eject.rb +132 -0
- data/lib/jade/cli.rb +2 -0
- data/lib/jade/codegen/boundary/specialized/list.rb +11 -7
- data/lib/jade/codegen/boundary/specialized/maybe.rb +2 -2
- data/lib/jade/codegen/boundary/specialized/record.rb +19 -4
- data/lib/jade/codegen/boundary/specialized/scalar.rb +2 -2
- data/lib/jade/codegen/boundary/specialized.rb +13 -5
- data/lib/jade/codegen/boundary.rb +12 -2
- data/lib/jade/codegen/context.rb +17 -0
- data/lib/jade/codegen/error.rb +1 -0
- data/lib/jade/codegen/function_call.rb +23 -7
- data/lib/jade/codegen/function_declaration.rb +25 -13
- data/lib/jade/codegen/helpers.rb +27 -4
- data/lib/jade/codegen/implementation.rb +2 -2
- data/lib/jade/codegen/inline.rb +1 -1
- data/lib/jade/codegen/inlines.rb +6 -1
- data/lib/jade/codegen/names.rb +31 -0
- data/lib/jade/codegen/pattern/constructor.rb +2 -2
- data/lib/jade/codegen/transforms/fold_shape.rb +1 -1
- data/lib/jade/codegen.rb +76 -15
- data/lib/jade/compiler.rb +2 -0
- data/lib/jade/entry.rb +1 -1
- data/lib/jade/formatter/calls.rb +68 -11
- data/lib/jade/formatter/collections.rb +4 -4
- data/lib/jade/formatter/declarations.rb +13 -15
- data/lib/jade/formatter/helper.rb +20 -0
- data/lib/jade/formatter/infix_application.rb +19 -4
- data/lib/jade/formatter/lambda.rb +24 -12
- data/lib/jade/formatter/pattern.rb +3 -0
- data/lib/jade/frontend/desugaring/placeholder.rb +40 -17
- data/lib/jade/frontend/desugaring.rb +29 -9
- data/lib/jade/frontend/fixity_fixer.rb +6 -4
- data/lib/jade/frontend/forward_declaration/error/alias_expansion.rb +23 -0
- data/lib/jade/frontend/forward_declaration/error/duplicate_constructor_name.rb +45 -0
- data/lib/jade/frontend/forward_declaration/error/duplicate_type_name.rb +42 -0
- data/lib/jade/frontend/forward_declaration/error.rb +3 -0
- data/lib/jade/frontend/forward_declaration/helper.rb +58 -0
- data/lib/jade/frontend/forward_declaration/import_declaration.rb +4 -0
- data/lib/jade/frontend/forward_declaration/interface_declaration.rb +6 -0
- data/lib/jade/frontend/forward_declaration/module.rb +4 -0
- data/lib/jade/frontend/forward_declaration/struct_declaration.rb +10 -1
- data/lib/jade/frontend/forward_declaration/type_alias_declaration.rb +37 -0
- data/lib/jade/frontend/forward_declaration/type_declaration.rb +10 -1
- data/lib/jade/frontend/forward_declaration.rb +2 -0
- data/lib/jade/frontend/pattern_analysis/exhaustiveness.rb +47 -43
- data/lib/jade/frontend/pattern_analysis/matrix.rb +28 -219
- data/lib/jade/frontend/pattern_analysis/pattern.rb +132 -0
- data/lib/jade/frontend/pattern_analysis/redundancy.rb +24 -0
- data/lib/jade/frontend/pattern_analysis/signature.rb +220 -0
- data/lib/jade/frontend/pattern_analysis/usefulness.rb +72 -0
- data/lib/jade/frontend/pattern_analysis/witnesses.rb +73 -0
- data/lib/jade/frontend/pattern_analysis.rb +5 -38
- data/lib/jade/frontend/semantic_analysis/error/implementation_on_alias.rb +24 -0
- data/lib/jade/frontend/semantic_analysis/error/no_base_case.rb +45 -0
- data/lib/jade/frontend/semantic_analysis/error/placeholder_not_allowed.rb +15 -3
- data/lib/jade/frontend/semantic_analysis/error/recursive_type_alias.rb +24 -0
- data/lib/jade/frontend/semantic_analysis/error.rb +3 -0
- data/lib/jade/frontend/semantic_analysis/helper.rb +31 -0
- data/lib/jade/frontend/semantic_analysis/implementation.rb +13 -0
- data/lib/jade/frontend/semantic_analysis/inhabitedness.rb +90 -0
- data/lib/jade/frontend/semantic_analysis/interop_import_declaration.rb +28 -16
- data/lib/jade/frontend/semantic_analysis/keyed_call.rb +21 -5
- data/lib/jade/frontend/semantic_analysis/pattern_range.rb +16 -0
- data/lib/jade/frontend/semantic_analysis/struct_declaration.rb +1 -0
- data/lib/jade/frontend/semantic_analysis/type_alias_declaration/cycle_detection.rb +95 -0
- data/lib/jade/frontend/semantic_analysis/type_alias_declaration.rb +48 -0
- data/lib/jade/frontend/semantic_analysis/type_declaration.rb +1 -0
- data/lib/jade/frontend/semantic_analysis.rb +5 -0
- data/lib/jade/frontend/type_checking/canonicalize.rb +1 -0
- data/lib/jade/frontend/type_checking/cascade.rb +90 -0
- data/lib/jade/frontend/type_checking/constraints/deriving/encodable.rb +10 -0
- data/lib/jade/frontend/type_checking/constraints.rb +18 -3
- data/lib/jade/frontend/type_checking/error/display.rb +49 -0
- data/lib/jade/frontend/type_checking/error/division_by_zero.rb +31 -0
- data/lib/jade/frontend/type_checking/error/empty_range_pattern.rb +25 -0
- data/lib/jade/frontend/type_checking/error/function_body_type_mismatch.rb +4 -3
- data/lib/jade/frontend/type_checking/error/function_call_type_mismatch.rb +108 -5
- data/lib/jade/frontend/type_checking/error/if_branch_type_mismatch.rb +1 -1
- data/lib/jade/frontend/type_checking/error/if_branches_type_mismatch.rb +1 -1
- data/lib/jade/frontend/type_checking/error/if_condition_type_mismatch.rb +1 -1
- data/lib/jade/frontend/type_checking/error/implementation_type_mismatch.rb +1 -1
- data/lib/jade/frontend/type_checking/error/list_item_type_mismatch.rb +1 -1
- data/lib/jade/frontend/type_checking/error/missing_patterns.rb +20 -4
- data/lib/jade/frontend/type_checking/error/pattern_type_mismatch.rb +7 -1
- data/lib/jade/frontend/type_checking/error/range_pattern_type.rb +29 -0
- data/lib/jade/frontend/type_checking/error/record_access_type_mismatch.rb +2 -2
- data/lib/jade/frontend/type_checking/error/recursive_derivation.rb +25 -0
- data/lib/jade/frontend/type_checking/error/type_mismatch.rb +10 -2
- data/lib/jade/frontend/type_checking/error/unreachable_branch.rb +22 -0
- data/lib/jade/frontend/type_checking/error.rb +6 -0
- data/lib/jade/frontend/type_checking/inference/assign.rb +1 -1
- data/lib/jade/frontend/type_checking/inference/case_of.rb +21 -4
- data/lib/jade/frontend/type_checking/inference/division.rb +50 -0
- data/lib/jade/frontend/type_checking/inference/function_call.rb +26 -1
- data/lib/jade/frontend/type_checking/inference/lambda.rb +1 -1
- data/lib/jade/frontend/type_checking/inference/pattern.rb +35 -0
- data/lib/jade/frontend/type_checking/inference/type_alias_declaration.rb +18 -0
- data/lib/jade/frontend/type_checking/inference.rb +2 -0
- data/lib/jade/frontend/type_checking/loader.rb +11 -0
- data/lib/jade/frontend/type_checking/port_resolution.rb +26 -8
- data/lib/jade/frontend/type_checking/substitution.rb +2 -2
- data/lib/jade/frontend/type_checking.rb +3 -1
- data/lib/jade/frontend/usage_analysis.rb +5 -1
- data/lib/jade/interop/boundary.rb +64 -24
- data/lib/jade/interop/error.rb +35 -7
- data/lib/jade/interop/lowering.rb +8 -2
- data/lib/jade/lsp/converters.rb +83 -2
- data/lib/jade/lsp/handlers.rb +122 -25
- data/lib/jade/lsp/snippets.rb +12 -0
- data/lib/jade/lsp/state.rb +11 -4
- data/lib/jade/module_loader/build.rb +36 -0
- data/lib/jade/module_loader.rb +1 -0
- data/lib/jade/parsing/combinators.rb +78 -2
- data/lib/jade/parsing/error.rb +43 -6
- data/lib/jade/parsing/type.rb +5 -3
- data/lib/jade/parsing.rb +140 -26
- data/lib/jade/runtime.rb +32 -13
- data/lib/jade/source.rb +6 -2
- data/lib/jade/stdlib/basics.rb +22 -3
- data/lib/jade/stdlib/char.rb +2 -0
- data/lib/jade/stdlib/clock.rb +6 -2
- data/lib/jade/stdlib/decimal.rb +51 -14
- data/lib/jade/stdlib/intrinsics.rb +59 -6
- data/lib/jade/stdlib/list.rb +37 -1
- data/lib/jade/stdlib/number.rb +47 -0
- data/lib/jade/stdlib/range.rb +211 -0
- data/lib/jade/stdlib.rb +11 -4
- data/lib/jade/symbol/alias.rb +11 -0
- data/lib/jade/symbol/parser.rb +1 -1
- data/lib/jade/symbol.rb +13 -1
- data/lib/jade/task.rb +0 -2
- data/lib/jade/type/anonymous_record.rb +11 -2
- data/lib/jade/type/application.rb +11 -2
- data/lib/jade/type/base.rb +26 -0
- data/lib/jade/type/function.rb +11 -2
- data/lib/jade/type.rb +36 -10
- data/lib/jade/version.rb +1 -1
- 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
|
-
|
|
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
|
-
|
|
20
|
-
|
|
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,
|
|
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| [
|
|
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
|
-
|
|
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
|
-
|
|
85
|
-
.assert(patterns, node.range, state.env,
|
|
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
|
|
@@ -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
|
|
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
|
-
|
|
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?(
|
|
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?(
|
|
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
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
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) }
|