jade-lang 0.10.0 → 0.11.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 +78 -0
- data/README.md +1 -0
- data/docs/json.md +34 -3
- data/lib/jade/ast.rb +15 -3
- data/lib/jade/cli/init.rb +86 -0
- data/lib/jade/cli.rb +2 -0
- data/lib/jade/codegen/function_call.rb +25 -10
- data/lib/jade/codegen/function_declaration.rb +2 -10
- data/lib/jade/codegen/helpers.rb +42 -0
- data/lib/jade/codegen/implementation.rb +33 -8
- data/lib/jade/formatter/leaves.rb +12 -1
- data/lib/jade/formatter/type.rb +3 -5
- data/lib/jade/frontend/forward_declaration/interface_declaration.rb +16 -3
- data/lib/jade/frontend/type_checking/error/cross_module_requirement.rb +36 -0
- data/lib/jade/frontend/type_checking/error/implementation_function_constraint.rb +36 -0
- data/lib/jade/frontend/type_checking/error/record_update_type_mismatch.rb +21 -0
- data/lib/jade/frontend/type_checking/error/undeclared_requirement.rb +35 -0
- data/lib/jade/frontend/type_checking/error.rb +4 -0
- data/lib/jade/frontend/type_checking/inference/function_declaration.rb +0 -5
- data/lib/jade/frontend/type_checking/inference/helpers.rb +9 -0
- data/lib/jade/frontend/type_checking/inference/implementation.rb +93 -16
- data/lib/jade/frontend/type_checking/inference/qualified_access.rb +1 -0
- data/lib/jade/frontend/type_checking/inference/record_update.rb +8 -1
- data/lib/jade/frontend/type_checking/inference/variable_reference.rb +1 -0
- data/lib/jade/frontend/type_checking/requirements.rb +115 -0
- data/lib/jade/frontend/type_checking/state.rb +10 -2
- data/lib/jade/frontend/type_checking.rb +9 -3
- data/lib/jade/parsing/type.rb +1 -1
- data/lib/jade/parsing.rb +14 -1
- data/lib/jade/stdlib/decode.rb +11 -0
- data/lib/jade/symbol/interface_function.rb +2 -1
- data/lib/jade/symbol/stdlib_function.rb +1 -1
- data/lib/jade/symbol.rb +2 -2
- data/lib/jade/type/function.rb +12 -13
- data/lib/jade/type.rb +12 -3
- data/lib/jade/version.rb +1 -1
- metadata +8 -2
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
module Jade
|
|
2
|
+
module Frontend
|
|
3
|
+
module TypeChecking
|
|
4
|
+
module Error
|
|
5
|
+
# An update produces a record of its own, which then has to be the
|
|
6
|
+
# record the surrounding code wanted. Changing a field's type is the
|
|
7
|
+
# usual way to break that, and any other disagreement lands here too.
|
|
8
|
+
class RecordUpdateTypeMismatch < TypeMismatch
|
|
9
|
+
def message
|
|
10
|
+
"This update produces #{naming.annotated(@actual)}, " \
|
|
11
|
+
"but #{naming.annotated(@expected)} was expected"
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def label
|
|
15
|
+
"produces #{naming[@actual]}, expected #{naming[@expected]}"
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
module Jade
|
|
2
|
+
module Frontend
|
|
3
|
+
module TypeChecking
|
|
4
|
+
module Error
|
|
5
|
+
class UndeclaredRequirement < Jade::Error
|
|
6
|
+
def initialize(entry, span, interface:, fn_name:, constraint:, declared:)
|
|
7
|
+
super(entry:, span:)
|
|
8
|
+
@interface = interface
|
|
9
|
+
@fn_name = fn_name
|
|
10
|
+
@constraint = constraint
|
|
11
|
+
@declared = declared
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def message
|
|
15
|
+
"Implementation of #{@interface}.#{@fn_name} requires #{@constraint}, " \
|
|
16
|
+
"which #{@interface} does not declare. It declares #{@declared}"
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def label
|
|
20
|
+
"requires #{@constraint}"
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def notes
|
|
24
|
+
[
|
|
25
|
+
Jade::Diagnostics::Annotation[
|
|
26
|
+
:help,
|
|
27
|
+
"add it to the method: `#{@fn_name} : ... with #{@constraint}`",
|
|
28
|
+
],
|
|
29
|
+
]
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -19,6 +19,10 @@ require 'jade/frontend/type_checking/error/pattern_type_mismatch'
|
|
|
19
19
|
require 'jade/frontend/type_checking/error/empty_range_pattern'
|
|
20
20
|
require 'jade/frontend/type_checking/error/range_pattern_type'
|
|
21
21
|
require 'jade/frontend/type_checking/error/record_access_type_mismatch'
|
|
22
|
+
require 'jade/frontend/type_checking/error/record_update_type_mismatch'
|
|
22
23
|
require 'jade/frontend/type_checking/error/unresolved_constraint'
|
|
23
24
|
require 'jade/frontend/type_checking/error/implementation_type_mismatch'
|
|
25
|
+
require 'jade/frontend/type_checking/error/implementation_function_constraint'
|
|
26
|
+
require 'jade/frontend/type_checking/error/cross_module_requirement'
|
|
27
|
+
require 'jade/frontend/type_checking/error/undeclared_requirement'
|
|
24
28
|
require 'jade/frontend/type_checking/error/missing_implementation'
|
|
@@ -48,11 +48,6 @@ module Jade
|
|
|
48
48
|
.map { st.env.substitution.apply(it) }
|
|
49
49
|
.uniq { it.type.is_a?(Type::Var) ? [it.interface, it.type] : it }
|
|
50
50
|
|
|
51
|
-
# TODO: for impl function declarations, unresolved constraints here
|
|
52
|
-
# (e.g. Eq(a) when the body calls == on a field of type a) should
|
|
53
|
-
# be stored as impl-level constraints, not function-level ones.
|
|
54
|
-
# The impl finalization pass (see TypeChecking.finalize) should then
|
|
55
|
-
# promote them into deps when the impl is instantiated for a concrete type.
|
|
56
51
|
st.bind(
|
|
57
52
|
symbol.qualified_name,
|
|
58
53
|
Placeholder[
|
|
@@ -27,6 +27,15 @@ module Jade
|
|
|
27
27
|
def type_from_symbol(symbol, registry, var_gen)
|
|
28
28
|
Type.from_symbol(symbol, registry, var_gen)
|
|
29
29
|
end
|
|
30
|
+
|
|
31
|
+
def attach_markers(result)
|
|
32
|
+
result
|
|
33
|
+
.constraints
|
|
34
|
+
.select { it.type.is_a?(Type::Var) && it.index != :unindex }
|
|
35
|
+
.each { Constraints.attach_dictionary(it, it) }
|
|
36
|
+
|
|
37
|
+
result
|
|
38
|
+
end
|
|
30
39
|
end
|
|
31
40
|
end
|
|
32
41
|
end
|
|
@@ -57,6 +57,8 @@ module Jade
|
|
|
57
57
|
st_after_bind,
|
|
58
58
|
Expected.check(expected_type),
|
|
59
59
|
interface_qname,
|
|
60
|
+
concrete_type,
|
|
61
|
+
iface_fn_type,
|
|
60
62
|
)
|
|
61
63
|
end
|
|
62
64
|
.then { [it, Result.init(Type.unit)] }
|
|
@@ -64,7 +66,7 @@ module Jade
|
|
|
64
66
|
|
|
65
67
|
private
|
|
66
68
|
|
|
67
|
-
def infer_fn(impl_fn, registry, state, expected, interface_qname)
|
|
69
|
+
def infer_fn(impl_fn, registry, state, expected, interface_qname, head_type, sig_type)
|
|
68
70
|
impl_fn => AST::ImplementationFunction(name:, fn:)
|
|
69
71
|
|
|
70
72
|
case fn
|
|
@@ -83,29 +85,63 @@ module Jade
|
|
|
83
85
|
state,
|
|
84
86
|
Expected.infer(state.fresh),
|
|
85
87
|
)
|
|
86
|
-
body_state
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
88
|
+
body_state
|
|
89
|
+
.unify(
|
|
90
|
+
body_result.type,
|
|
91
|
+
expected.type,
|
|
92
|
+
&mismatch_error(state.env.entry_name, impl_fn, interface_qname, name)
|
|
93
|
+
)
|
|
94
|
+
.then do |unified|
|
|
95
|
+
record_requirements(
|
|
96
|
+
unified,
|
|
97
|
+
body_result.constraints,
|
|
98
|
+
head_type,
|
|
99
|
+
sig_type,
|
|
100
|
+
interface_qname,
|
|
101
|
+
impl_fn,
|
|
102
|
+
name,
|
|
103
|
+
)
|
|
104
|
+
end
|
|
91
105
|
end
|
|
92
106
|
|
|
93
107
|
in AST::Lambda
|
|
94
|
-
check(fn, registry, state, expected)
|
|
108
|
+
lambda_state, lambda_result = check(fn, registry, state, expected)
|
|
109
|
+
|
|
110
|
+
record_requirements(
|
|
111
|
+
lambda_state,
|
|
112
|
+
lambda_result.constraints,
|
|
113
|
+
head_type,
|
|
114
|
+
sig_type,
|
|
115
|
+
interface_qname,
|
|
116
|
+
impl_fn,
|
|
117
|
+
name,
|
|
118
|
+
)
|
|
95
119
|
|
|
96
120
|
in AST::VariableReference
|
|
97
121
|
ref_state, ref_result = check(fn, registry, state, expected)
|
|
98
122
|
|
|
99
|
-
ref_state
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
123
|
+
ref_state
|
|
124
|
+
.unify(
|
|
125
|
+
ref_result.type,
|
|
126
|
+
expected.type,
|
|
127
|
+
&mismatch_error(
|
|
128
|
+
state.env.entry_name,
|
|
129
|
+
impl_fn,
|
|
130
|
+
interface_qname,
|
|
131
|
+
name
|
|
132
|
+
)
|
|
107
133
|
)
|
|
108
|
-
|
|
134
|
+
.then do |unified|
|
|
135
|
+
record_requirements(
|
|
136
|
+
unified,
|
|
137
|
+
ref_result.constraints,
|
|
138
|
+
head_type,
|
|
139
|
+
sig_type,
|
|
140
|
+
interface_qname,
|
|
141
|
+
impl_fn,
|
|
142
|
+
name,
|
|
143
|
+
)
|
|
144
|
+
end
|
|
109
145
|
|
|
110
146
|
# `decoder: zero_arg_fn` becomes `FunctionCall(zero_arg_fn, [])`
|
|
111
147
|
# after ZeroArgRewrite. Type the call; its result must match the
|
|
@@ -115,6 +151,47 @@ module Jade
|
|
|
115
151
|
end
|
|
116
152
|
end
|
|
117
153
|
|
|
154
|
+
def record_requirements(state, constraints, head_type, sig_type, interface_qname, impl_fn, fn_name)
|
|
155
|
+
substitution = state.env.substitution
|
|
156
|
+
head_vars = substitution.apply(head_type).unbound_vars.map(&:id).to_set
|
|
157
|
+
names = signature_names(sig_type, substitution)
|
|
158
|
+
|
|
159
|
+
free = constraints
|
|
160
|
+
.map { substitution.apply(it) }
|
|
161
|
+
.select { it.type.is_a?(Type::Var) && !head_vars.include?(it.type.id) }
|
|
162
|
+
.uniq { [it.interface, it.type.id] }
|
|
163
|
+
|
|
164
|
+
free
|
|
165
|
+
.reject { names.key?(it.type.id) }
|
|
166
|
+
.map { unsatisfiable(state, impl_fn, interface_qname, fn_name, it) }
|
|
167
|
+
.then { state.add_errors(it) }
|
|
168
|
+
.require_impl(
|
|
169
|
+
[interface_qname, fn_name],
|
|
170
|
+
free.filter_map { |c| names[c.type.id]&.then { |n| [c.interface, n] } },
|
|
171
|
+
)
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
def signature_names(sig_type, substitution)
|
|
175
|
+
sig_type
|
|
176
|
+
.unbound_vars
|
|
177
|
+
.filter_map do |var|
|
|
178
|
+
substitution
|
|
179
|
+
.apply(var)
|
|
180
|
+
.then { it.is_a?(Type::Var) ? [it.id, var.name] : nil }
|
|
181
|
+
end
|
|
182
|
+
.to_h
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
def unsatisfiable(state, impl_fn, interface_qname, fn_name, constraint)
|
|
186
|
+
Error::ImplementationFunctionConstraint.new(
|
|
187
|
+
state.env.entry_name,
|
|
188
|
+
impl_fn.range,
|
|
189
|
+
interface: interface_qname,
|
|
190
|
+
fn_name:,
|
|
191
|
+
constraint: constraint.to_s,
|
|
192
|
+
)
|
|
193
|
+
end
|
|
194
|
+
|
|
118
195
|
def constructor_var_in?(type, var_id)
|
|
119
196
|
case type
|
|
120
197
|
in Type::Application(constructor: Type::Var(id:)) if id == var_id
|
|
@@ -28,7 +28,14 @@ module Jade
|
|
|
28
28
|
)
|
|
29
29
|
end
|
|
30
30
|
|
|
31
|
-
after_state.unify_result(result, expected.type)
|
|
31
|
+
after_state.unify_result(result, expected.type) do
|
|
32
|
+
Error::RecordUpdateTypeMismatch.new(
|
|
33
|
+
state.env.entry_name,
|
|
34
|
+
node.range,
|
|
35
|
+
expected: it.expected,
|
|
36
|
+
actual: it.actual,
|
|
37
|
+
)
|
|
38
|
+
end
|
|
32
39
|
end
|
|
33
40
|
end
|
|
34
41
|
end
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
module Jade
|
|
2
|
+
module Frontend
|
|
3
|
+
module TypeChecking
|
|
4
|
+
module Requirements
|
|
5
|
+
extend self
|
|
6
|
+
|
|
7
|
+
def reconcile(entry, registry, state)
|
|
8
|
+
pending = state
|
|
9
|
+
.impl_requirements
|
|
10
|
+
.to_h { |key, pairs| [key, pairs - declared_for(key, registry)] }
|
|
11
|
+
.reject { |_, pairs| pairs.empty? }
|
|
12
|
+
|
|
13
|
+
errors(entry, registry, pending)
|
|
14
|
+
.then { return [entry, state.add_errors(it)] if it.any? }
|
|
15
|
+
|
|
16
|
+
amended = amend_all(entry, pending)
|
|
17
|
+
return [entry, state] if amended.equal?(entry)
|
|
18
|
+
|
|
19
|
+
rebind(state.env, amended, registry)
|
|
20
|
+
.then { recheck(entry, registry, it) }
|
|
21
|
+
.then { |rechecked| [amended, rechecked] }
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
private
|
|
25
|
+
|
|
26
|
+
def recheck(entry, registry, env)
|
|
27
|
+
TypeChecking
|
|
28
|
+
.check_node(entry.ast, registry, State.init(env), Expected.infer(env.fresh))
|
|
29
|
+
.first
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def rebind(env, entry, registry)
|
|
33
|
+
entry
|
|
34
|
+
.defined_values
|
|
35
|
+
.each_value
|
|
36
|
+
.select { it.is_a?(Symbol::InterfaceFunction) && it.constraints.any? }
|
|
37
|
+
.reduce(env) do |acc, sym|
|
|
38
|
+
Type
|
|
39
|
+
.from_symbol(sym, registry, acc.var_gen)
|
|
40
|
+
.then { Inference::Helpers.generalize(acc, *it) }
|
|
41
|
+
.then { acc.bind(sym.qualified_name, it) }
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def declared_for((interface, fn_name), registry)
|
|
46
|
+
Symbol
|
|
47
|
+
.type_ref_from_qualified_name(interface)
|
|
48
|
+
.then { registry.lookup(it) }
|
|
49
|
+
.then { it.is_a?(Symbol::Interface) ? it.functions : [] }
|
|
50
|
+
.find { it.name == fn_name }
|
|
51
|
+
&.constraints || []
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def errors(entry, registry, pending)
|
|
55
|
+
pending.flat_map do |(interface, fn_name), constraints|
|
|
56
|
+
declared = declared_for([interface, fn_name], registry)
|
|
57
|
+
next [] if declared.empty? && local?(entry, interface)
|
|
58
|
+
|
|
59
|
+
constraints.map do |iface, var|
|
|
60
|
+
error_for(entry, interface, fn_name, "#{iface} #{var}", declared)
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def local?(entry, interface)
|
|
66
|
+
entry.defined_types.key?(interface.split('.').last)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def error_for(entry, interface, fn_name, constraint, declared)
|
|
70
|
+
return cross_module_error(entry, interface, fn_name, constraint) if declared.empty?
|
|
71
|
+
|
|
72
|
+
Error::UndeclaredRequirement.new(
|
|
73
|
+
entry.name,
|
|
74
|
+
nil,
|
|
75
|
+
interface:,
|
|
76
|
+
fn_name:,
|
|
77
|
+
constraint:,
|
|
78
|
+
declared: declared.map { |iface, var| "#{iface} #{var}" }.join(', '),
|
|
79
|
+
)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def cross_module_error(entry, interface, fn_name, constraint)
|
|
83
|
+
Error::CrossModuleRequirement.new(
|
|
84
|
+
entry.name,
|
|
85
|
+
nil,
|
|
86
|
+
interface:,
|
|
87
|
+
fn_name:,
|
|
88
|
+
constraint:,
|
|
89
|
+
)
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def amend_all(entry, pending)
|
|
93
|
+
pending.reduce(entry) do |acc, ((interface, fn_name), constraints)|
|
|
94
|
+
amend_method(acc, interface, fn_name, constraints)
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def amend_method(entry, interface_qname, fn_name, constraints)
|
|
99
|
+
symbol = entry.defined_types[interface_qname.split('.').last]
|
|
100
|
+
return entry unless symbol.is_a?(Symbol::Interface)
|
|
101
|
+
|
|
102
|
+
symbol
|
|
103
|
+
.functions
|
|
104
|
+
.map { it.name == fn_name ? with_constraints(it, constraints) : it }
|
|
105
|
+
.then { symbol.with(functions: it) }
|
|
106
|
+
.then { |iface| iface.functions.reduce(entry.define(iface)) { |acc, fn| acc.define(fn) } }
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def with_constraints(fn, constraints)
|
|
110
|
+
fn.with(constraints: (fn.constraints + constraints).uniq)
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
end
|
|
@@ -1,9 +1,17 @@
|
|
|
1
1
|
module Jade
|
|
2
2
|
module Frontend
|
|
3
3
|
module TypeChecking
|
|
4
|
-
State = Data.define(:env, :errors, :skip_constraints) do
|
|
4
|
+
State = Data.define(:env, :errors, :skip_constraints, :impl_requirements) do
|
|
5
5
|
def self.init(env, skip_constraints: false)
|
|
6
|
-
new(env, [], skip_constraints)
|
|
6
|
+
new(env, [], skip_constraints, {})
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def require_impl(key, constraints)
|
|
10
|
+
return self if constraints.empty?
|
|
11
|
+
|
|
12
|
+
impl_requirements
|
|
13
|
+
.merge(key => constraints) { |_, old, new| (old + new).uniq }
|
|
14
|
+
.then { with(impl_requirements: it) }
|
|
7
15
|
end
|
|
8
16
|
|
|
9
17
|
def unify_result(result, right, rigid_vars = [], &block)
|
|
@@ -8,6 +8,7 @@ require 'jade/frontend/type_checking/expected'
|
|
|
8
8
|
require 'jade/frontend/type_checking/inference'
|
|
9
9
|
require 'jade/frontend/type_checking/loader'
|
|
10
10
|
require 'jade/frontend/type_checking/port_resolution'
|
|
11
|
+
require 'jade/frontend/type_checking/requirements'
|
|
11
12
|
require 'jade/frontend/type_checking/result'
|
|
12
13
|
require 'jade/frontend/type_checking/state'
|
|
13
14
|
require 'jade/frontend/type_checking/substitution'
|
|
@@ -26,11 +27,16 @@ module Jade
|
|
|
26
27
|
.load(entry, registry)
|
|
27
28
|
.then { collect_constraints(entry, registry, it) }
|
|
28
29
|
.then { check_node(entry.ast, registry, State.init(it), Expected.infer(it.fresh)) }
|
|
29
|
-
.then {
|
|
30
|
+
.then { |state, _| Requirements.reconcile(entry, registry, state) }
|
|
31
|
+
.then { |amended, state| complete(amended, state, registry) }
|
|
32
|
+
.and_then { PortResolution.resolve(it, registry) }
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def complete(entry, state, registry)
|
|
36
|
+
finalize(state, registry)
|
|
30
37
|
.map { Canonicalize.run(entry.ast, it, registry) }
|
|
31
38
|
.map { it.canonicalize_node_types }
|
|
32
39
|
.map { entry.with(env: it) }
|
|
33
|
-
.and_then { PortResolution.resolve(it, registry) }
|
|
34
40
|
end
|
|
35
41
|
|
|
36
42
|
# A collecting pass walks declarations in source order, so a call to a
|
|
@@ -76,7 +82,7 @@ module Jade
|
|
|
76
82
|
.then { it + 1 }
|
|
77
83
|
end
|
|
78
84
|
|
|
79
|
-
def finalize(state,
|
|
85
|
+
def finalize(state, registry)
|
|
80
86
|
state.env => { bindings:, entry_name: }
|
|
81
87
|
|
|
82
88
|
errors = bindings
|
data/lib/jade/parsing/type.rb
CHANGED
data/lib/jade/parsing.rb
CHANGED
|
@@ -753,10 +753,23 @@ module Jade
|
|
|
753
753
|
}
|
|
754
754
|
|
|
755
755
|
parser(:interface_function_decl) {
|
|
756
|
+
constraint =
|
|
757
|
+
(
|
|
758
|
+
constant >>
|
|
759
|
+
type(:lparen).skip >>
|
|
760
|
+
type_param >>
|
|
761
|
+
type(:rparen).skip
|
|
762
|
+
).map(&AST.interface_constraint)
|
|
763
|
+
|
|
764
|
+
constraints =
|
|
765
|
+
type(:with).skip >>
|
|
766
|
+
at_least_one(constraint, separated_by: type(:comma).skip)
|
|
767
|
+
|
|
756
768
|
(
|
|
757
769
|
(type(:identifier) | (type(:lparen).skip >> operator >> type(:rparen).skip)) >>
|
|
758
770
|
type(:colon).skip >>
|
|
759
|
-
type_expression
|
|
771
|
+
type_expression >>
|
|
772
|
+
optional(constraints, default: []).map { [it] }
|
|
760
773
|
).map(&AST.interface_function_decl)
|
|
761
774
|
}
|
|
762
775
|
|
data/lib/jade/stdlib/decode.rb
CHANGED
|
@@ -268,6 +268,17 @@ module Jade
|
|
|
268
268
|
|
|
269
269
|
# Constrained helpers — pick the decoder via Decodable.
|
|
270
270
|
|
|
271
|
+
function(
|
|
272
|
+
'decoder',
|
|
273
|
+
{},
|
|
274
|
+
'Decoder(a)',
|
|
275
|
+
constraints: [['Decode.Decodable', 'a']],
|
|
276
|
+
body: Symbol::DerivedFunction.new(
|
|
277
|
+
params: [],
|
|
278
|
+
body: [:impl_arg, 0, 'decoder'],
|
|
279
|
+
),
|
|
280
|
+
)
|
|
281
|
+
|
|
271
282
|
function(
|
|
272
283
|
'from_value',
|
|
273
284
|
{ value: 'Value' },
|
data/lib/jade/symbol.rb
CHANGED
|
@@ -134,8 +134,8 @@ module Jade
|
|
|
134
134
|
Interface[nil, name, type_var, functions, default, span]
|
|
135
135
|
end
|
|
136
136
|
|
|
137
|
-
def interface_function(name, inteface, params, return_type, span)
|
|
138
|
-
InterfaceFunction[nil, name, inteface, params, return_type, span]
|
|
137
|
+
def interface_function(name, inteface, params, return_type, span, constraints = [])
|
|
138
|
+
InterfaceFunction[nil, name, inteface, params, return_type, span, constraints]
|
|
139
139
|
end
|
|
140
140
|
|
|
141
141
|
def implementation(
|
data/lib/jade/type/function.rb
CHANGED
|
@@ -1,27 +1,26 @@
|
|
|
1
1
|
module Jade
|
|
2
2
|
module Type
|
|
3
|
-
Function = Data.define(:args, :return_type
|
|
3
|
+
Function = Data.define(:args, :return_type) do
|
|
4
4
|
include Base
|
|
5
|
-
include Displayable
|
|
6
5
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
def identity
|
|
12
|
-
[args, return_type]
|
|
13
|
-
end
|
|
6
|
+
# `Int, Int -> Int`, the spelling the formatter writes. Parentheses
|
|
7
|
+
# around a comma list are a tuple, so a nested function keeps its own.
|
|
8
|
+
def to_s
|
|
9
|
+
params = args.empty? ? '()' : args.map { delimited(it) }.join(', ')
|
|
14
10
|
|
|
15
|
-
|
|
16
|
-
args
|
|
17
|
-
.map(&:to_s).join(', ')
|
|
18
|
-
.then { "(#{it})"} + " -> " + return_type.to_s
|
|
11
|
+
"#{params} -> #{delimited(return_type)}"
|
|
19
12
|
end
|
|
20
13
|
|
|
21
14
|
def unbound_vars
|
|
22
15
|
(args.flat_map(&:unbound_vars) + return_type.unbound_vars)
|
|
23
16
|
.to_set.to_a
|
|
24
17
|
end
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
|
|
21
|
+
def delimited(type)
|
|
22
|
+
type.is_a?(Function) ? "(#{type})" : type.to_s
|
|
23
|
+
end
|
|
25
24
|
end
|
|
26
25
|
end
|
|
27
26
|
end
|
data/lib/jade/type.rb
CHANGED
|
@@ -178,9 +178,18 @@ module Jade
|
|
|
178
178
|
constraint = Type
|
|
179
179
|
.constraint(symbol.interface.qualified_name, param_type, nil)
|
|
180
180
|
|
|
181
|
-
|
|
182
|
-
.
|
|
183
|
-
|
|
181
|
+
ret_type, ret_cs, full_map =
|
|
182
|
+
from_symbol_r(symbol.return_type, registry, var_gen, local_map)
|
|
183
|
+
|
|
184
|
+
extra_cs = symbol.constraints.map { |iface, var_name|
|
|
185
|
+
Type.constraint(iface, full_map.fetch(var_name), nil)
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
[
|
|
189
|
+
args.empty? ? ret_type : Type.function(args, ret_type),
|
|
190
|
+
ret_cs + arg_cs + [constraint] + extra_cs,
|
|
191
|
+
var_map,
|
|
192
|
+
]
|
|
184
193
|
|
|
185
194
|
in Symbol::Constructor if symbol.args.empty?
|
|
186
195
|
from_symbol_r(symbol.parent, registry, var_gen, var_map)
|
data/lib/jade/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: jade-lang
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.11.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Agustin Cornu
|
|
8
8
|
bindir: exe
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date: 2026-09-
|
|
10
|
+
date: 2026-09-15 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: base64
|
|
@@ -57,6 +57,7 @@ files:
|
|
|
57
57
|
- lib/jade/cli/check.rb
|
|
58
58
|
- lib/jade/cli/eject.rb
|
|
59
59
|
- lib/jade/cli/fmt.rb
|
|
60
|
+
- lib/jade/cli/init.rb
|
|
60
61
|
- lib/jade/cli/lsp.rb
|
|
61
62
|
- lib/jade/cli/q.rb
|
|
62
63
|
- lib/jade/clock/runtime.rb
|
|
@@ -248,6 +249,7 @@ files:
|
|
|
248
249
|
- lib/jade/frontend/type_checking/env.rb
|
|
249
250
|
- lib/jade/frontend/type_checking/error.rb
|
|
250
251
|
- lib/jade/frontend/type_checking/error/case_of_branches_type_mismatch.rb
|
|
252
|
+
- lib/jade/frontend/type_checking/error/cross_module_requirement.rb
|
|
251
253
|
- lib/jade/frontend/type_checking/error/derivation_failed.rb
|
|
252
254
|
- lib/jade/frontend/type_checking/error/display.rb
|
|
253
255
|
- lib/jade/frontend/type_checking/error/division_by_zero.rb
|
|
@@ -257,6 +259,7 @@ files:
|
|
|
257
259
|
- lib/jade/frontend/type_checking/error/if_branch_type_mismatch.rb
|
|
258
260
|
- lib/jade/frontend/type_checking/error/if_branches_type_mismatch.rb
|
|
259
261
|
- lib/jade/frontend/type_checking/error/if_condition_type_mismatch.rb
|
|
262
|
+
- lib/jade/frontend/type_checking/error/implementation_function_constraint.rb
|
|
260
263
|
- lib/jade/frontend/type_checking/error/implementation_type_mismatch.rb
|
|
261
264
|
- lib/jade/frontend/type_checking/error/list_item_type_mismatch.rb
|
|
262
265
|
- lib/jade/frontend/type_checking/error/missing_implementation.rb
|
|
@@ -266,8 +269,10 @@ files:
|
|
|
266
269
|
- lib/jade/frontend/type_checking/error/port_not_encodable.rb
|
|
267
270
|
- lib/jade/frontend/type_checking/error/range_pattern_type.rb
|
|
268
271
|
- lib/jade/frontend/type_checking/error/record_access_type_mismatch.rb
|
|
272
|
+
- lib/jade/frontend/type_checking/error/record_update_type_mismatch.rb
|
|
269
273
|
- lib/jade/frontend/type_checking/error/recursive_derivation.rb
|
|
270
274
|
- lib/jade/frontend/type_checking/error/type_mismatch.rb
|
|
275
|
+
- lib/jade/frontend/type_checking/error/undeclared_requirement.rb
|
|
271
276
|
- lib/jade/frontend/type_checking/error/unreachable_branch.rb
|
|
272
277
|
- lib/jade/frontend/type_checking/error/unresolved_constraint.rb
|
|
273
278
|
- lib/jade/frontend/type_checking/expected.rb
|
|
@@ -306,6 +311,7 @@ files:
|
|
|
306
311
|
- lib/jade/frontend/type_checking/loader.rb
|
|
307
312
|
- lib/jade/frontend/type_checking/placeholder.rb
|
|
308
313
|
- lib/jade/frontend/type_checking/port_resolution.rb
|
|
314
|
+
- lib/jade/frontend/type_checking/requirements.rb
|
|
309
315
|
- lib/jade/frontend/type_checking/result.rb
|
|
310
316
|
- lib/jade/frontend/type_checking/scheme.rb
|
|
311
317
|
- lib/jade/frontend/type_checking/state.rb
|