Module: Jade::Frontend::TypeChecking::Inference::FunctionDeclaration

Extended by:
FunctionDeclaration, Helpers
Included in:
FunctionDeclaration
Defined in:
lib/jade/frontend/type_checking/inference/function_declaration.rb

Instance Method Summary collapse

Methods included from Helpers

check, generalize, instantiate, type_from_symbol, unify

Instance Method Details

#infer(node, registry, state, _) ⇒ Object



9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
# File 'lib/jade/frontend/type_checking/inference/function_declaration.rb', line 9

def infer(node, registry, state, _)
  node => AST::FunctionDeclaration(symbol:, body:, params:)

  # Use the binding directly instead of env.lookup, which would
  # instantiate fresh vars and detach body call sites' dict markers
  # from the binding's stored constraints.
  state
    .env
    .bindings[symbol.qualified_name] => {
      type: fn_type, constraints: fn_constraints,
    }

  arg_types, return_type = Type.signature(fn_type)

  new_state, body_result = arg_types
    .zip(params)
    .reduce(state) do |acc, (t, p)|
      acc.bind(p.name, Scheme.mono(t))
    end
    .then { check(body, registry, it, Expected.check(return_type)) }

  new_state
    .unify(
      body_result.type,
      return_type,
      fn_type.unbound_vars
    ) do
      Error::FunctionBodyTypeMismatch.new(
        state.env.entry_name,
        node.range,
        expected: it.expected,
        actual: it.actual,
        function_name: node.name,
      )
    end
    .then do |st|
      next st if st.env.bindings[symbol.qualified_name].is_a?(Scheme)

      updated_constraints = (fn_constraints + body_result.constraints)
        .map { st.env.substitution.apply(it) }
        .uniq

      # TODO: for impl function declarations, unresolved constraints here
      # (e.g. Eq(a) when the body calls == on a field of type a) should
      # be stored as impl-level constraints, not function-level ones.
      # The impl finalization pass (see TypeChecking.finalize) should then
      # promote them into deps when the impl is instantiated for a concrete type.
      st.bind(
        symbol.qualified_name,
        Placeholder[
          st.env.substitution.apply(fn_type),
          updated_constraints,
        ]
      )
    end
    .then { [it, Result.init(Type.unit)] }
end