Module: Jade::Frontend::TypeChecking::Inference::FunctionCall
- Extended by:
- FunctionCall, Helpers
- Included in:
- FunctionCall
- Defined in:
- lib/jade/frontend/type_checking/inference/function_call.rb
Instance Method Summary collapse
Methods included from Helpers
check, generalize, instantiate, type_from_symbol, unify
Instance Method Details
#infer(node, registry, state, expected) ⇒ 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 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 |
# File 'lib/jade/frontend/type_checking/inference/function_call.rb', line 9 def infer(node, registry, state, expected) node => AST::FunctionCall(callee:, args:) callee_state, callee_result = check( callee, registry, state, Expected.infer(state.fresh), ) .then { |st, rs| [st, rs.attach_origin(node)] } args_state, args_acc = args .reduce([callee_state, Result.accumulator]) do |(state_acc, acc), arg| check( arg, registry, state_acc, Expected.infer(state_acc.fresh), ) .then { |(new_state, result)| [new_state, acc.add(result)] } end after_callee_state, result_type = unify_callee( args_state, callee_result, args_acc, node, state, ) after_callee_state.unify_result( callee_result.map { result_type }, expected.type, &type_error(state, node) ) .then do |st, rs| # When expected is authoritative and this unification failed, # adopt expected.type so the enclosing body-level unify doesn't # re-report the same mismatch. adopted = expected.check? && st.errors.size > after_callee_state.errors.size base_rs = adopted ? rs.with(type: expected.type) : rs callee_subst = base_rs .constraints .map { st.env.substitution.apply(it) } args_subst = args_acc .constraints .map { st.env.substitution.apply(it) } propagated = (callee_subst + args_subst) .select { it.type.is_a?(Type::Var) } # Pass 1 still needs propagation so constraints from inner calls # bubble up through outer constructor calls and reach the function # binding before generalization. Skip only the mutating dictionary # attachment, which would otherwise emit double dispatch code. next [st, base_rs.with(constraints: propagated)] if st.skip_constraints # Attach a resolution per callee constraint, in callee order, so codegen # can pass dicts positionally. Concrete constraints attach a resolved # Implementation; var-typed ones attach themselves as a marker meaning # "use the enclosing function's local dict". callee_errors = callee_subst .flat_map do |c| case c.type in Type::Var Constraints.attach_dictionary(c, c) [] else Constraints.solve_at_call_site(c, registry, st.env.entry_name) end end # Args' constraints dispatch at their own origins (inner call sites, # or a QualifiedAccess/VariableReference when a polymorphic fn is # passed as a value). Var-typed ones attach themselves as a marker, # mirroring the callee path, so reference-as-value codegen can # resolve via the enclosing function's dict_env. args_errors = args_subst .flat_map do |c| case c.type in Type::Var Constraints.attach_dictionary(c, c) [] else Constraints.solve_at_call_site(c, registry, st.env.entry_name) end end st .add_errors(callee_errors + args_errors) .then { [it, base_rs.with(constraints: propagated)] } end end |