Skip to content
← Back

src/semantic/symbols/ineffective_trait_override_checker.ghul

1
namespace Semantic.Symbols is
2
use Logging;
3
use Source;
4
5
use Types.Type;
6
7
// Detects the .NET DIM trap where a class B overrides a trait default
8
// method whose owning trait T enters B's class chain at an intermediate
9
// class A that does not itself redeclare the member. In that case A has
10
// no vtable slot for the member, so B's "override" lands in a new slot
11
// and any dispatch routed through an A- or T-typed reference resolves
12
// to T's default rather than B's body.
13
//
14
// The fix is to redeclare the member in A (typically as
15
// `name: T => super.name;`), which forces a vtable slot the override
16
// can latch onto.
17
class INEFFECTIVE_TRAIT_OVERRIDE_CHECKER is
18
init() is si
19
20
check(
21
into: Classy?,
22
overrider_location: LOCATION,
23
overrider_description: string,
24
overridee: Symbol,
25
member_name: string,
26
trait_owner: Classy?,
27
logger: Logger
28
)
29
is
30
if !into? \/ !trait_owner? \/ !trait_owner.is_trait then
31
return;
32
fi
33
34
if !into.is_class \/ into.is_reflected then
35
return;
36
fi
37
38
let intermediate = _find_intermediate_without_redeclaration(into, trait_owner, member_name);
39
40
if intermediate? then
41
logger.error(
42
overrider_location,
43
"{overrider_description} does not override {overridee} because {intermediate.qualified_name} does not redeclare it"
44
);
45
logger.hint(
46
overrider_location,
47
"redeclare {member_name} in {intermediate.name} to allow overriding"
48
);
49
fi
50
si
51
52
_find_intermediate_without_redeclaration(start: Classy, trait_owner: Classy, member_name: string) -> Classy? is
53
let current: Classy? mut = _direct_class_parent(start);
54
55
while current? do
56
if _has_trait_in_direct_ancestors(current, trait_owner) then
57
if !_class_declares_member(current, member_name) then
58
return current;
59
fi
60
return null;
61
fi
62
63
current = _direct_class_parent(current);
64
od
65
66
return null;
67
si
68
69
_direct_class_parent(classy: Classy) -> Classy? is
70
for a in classy.ancestors do
71
if !a.is_trait /\ isa Classy(a.symbol) then
72
return cast Classy?(a.symbol)!;
73
fi
74
od
75
76
return null;
77
si
78
79
_has_trait_in_direct_ancestors(classy: Classy, trait_owner: Classy) -> bool is
80
let trait_unspec = trait_owner.unspecialized_symbol;
81
82
for a in classy.ancestors do
83
if a.is_trait /\ a.unspecialized_symbol == trait_unspec then
84
return true;
85
fi
86
od
87
88
return false;
89
si
90
91
_class_declares_member(classy: Classy, name: string) -> bool is
92
let direct = classy.find_direct(name);
93
94
if !direct? then
95
return false;
96
fi
97
98
let classy_unspec = classy.unspecialized_symbol;
99
100
if isa FUNCTION_GROUP(direct) then
101
for f in direct.functions do
102
if f.is_default_trait_method \/ f.is_abstract then
103
continue;
104
fi
105
106
if f.owner? /\ f.owner.unspecialized_symbol == classy_unspec then
107
return true;
108
fi
109
od
110
111
return false;
112
fi
113
114
if isa Property(direct) then
115
let prop = direct;
116
let read_is_default = prop.read_function? /\ prop.read_function.is_default_trait_method;
117
let assign_is_default = prop.assign_function? /\ prop.assign_function.is_default_trait_method;
118
119
if read_is_default /\ (!prop.assign_function? \/ assign_is_default) then
120
return false;
121
fi
122
fi
123
124
return direct.owner? /\ direct.owner.unspecialized_symbol == classy_unspec;
125
si
126
si
127
si