Skip to content
← Back

src/semantic/under_determination_detector.ghul

1
namespace Semantic is
2
use Types.Type;
3
4
use Ghul.Pipes;
5
6
// Predicate helper: does an under-determined-actual-vs-formal
7
// pair look like the constraint-push retry could fix it?
8
//
9
// The call-site path is: first overload resolve fails or returns
10
// PARTIAL; before spending the speculate / roll-back / re-walk
11
// cost, ask this helper whether at least one argument has the
12
// shape where pushing the formal as a constraint stands a chance
13
// of changing the outcome.
14
//
15
// Two shapes qualify:
16
//
17
// 1. Function-typed formal with a function-typed actual whose
18
// generic arguments contain at least one inferred
19
// placeholder. Lambda literals walked without a parameter-
20
// type constraint produce this shape (`x => ...` where the
21
// body offers no clue about `x`'s type). Pushing the formal
22
// function type as a constraint flows its argument types
23
// into the lambda's `implied_argument_types` on re-walk.
24
//
25
// 2. GENERIC formal of class C, with a NAMED actual wrapping
26
// an unspecialised C. `let b: Box[int] = ...` where the
27
// actual is `Box` (no T binding from args). Pushing
28
// `Box[int]` as a constraint lets the constructor pick
29
// `int` for T.
30
//
31
// Pushing on any other shape — an already-specialised tuple,
32
// a sequence, a primitive — typically just introduces new
33
// element-mismatch errors that don't improve on the original
34
// "no overload found" diagnostic.
35
class UNDER_DETERMINATION_DETECTOR is
36
init() is
37
super.init();
38
si
39
40
// Returns true when at least one argument's first-walk type
41
// is under-determined for the corresponding formal type.
42
any_arg_under_determined(
43
candidate: Symbols.Function,
44
argument_types: Collections.List[Type]
45
) -> bool is
46
for i in 0..argument_types.count do
47
if arg_under_determined_for_formal(candidate.arguments[i], argument_types[i]) then
48
return true;
49
fi
50
od
51
52
return false;
53
si
54
55
arg_under_determined_for_formal(
56
formal: Type?,
57
arg_value_type: Type?
58
) -> bool is
59
if !formal? \/ !arg_value_type? then
60
return false;
61
fi
62
63
let formal_generic = cast Types.GENERIC?(formal);
64
65
if !formal_generic? then
66
return false;
67
fi
68
69
// Function-typed formal with placeholder-argued function
70
// actual: lambda literal whose parameter types weren't
71
// inferred from the body. Pushing the formal flows arg
72
// types into `implied_argument_types` on re-walk.
73
if formal.is_function /\ arg_value_type.is_function then
74
let arg_generic = cast Types.GENERIC?(arg_value_type);
75
76
if arg_generic? /\ arg_generic.arguments |> any(a => a.is_inferred) then
77
return true;
78
fi
79
fi
80
81
// Already-specialised generic actual — the constraint
82
// push won't make it more bindable.
83
if isa Types.GENERIC(arg_value_type) then
84
return false;
85
fi
86
87
let arg_named = cast Types.NAMED?(arg_value_type);
88
89
if !arg_named? then
90
return false;
91
fi
92
93
let arg_classy = cast Symbols.Classy?(arg_named.symbol);
94
95
if !arg_classy? \/ !arg_classy.is_generic then
96
return false;
97
fi
98
99
let formal_symbol_generic = cast Symbols.GENERIC?(formal_generic.symbol);
100
101
if !formal_symbol_generic? then
102
return false;
103
fi
104
105
return arg_classy =~ formal_symbol_generic.symbol;
106
si
107
si
108
si