Line data Source code
1 : // Copyright (C) 2021-2026 Free Software Foundation, Inc.
2 :
3 : // This file is part of GCC.
4 :
5 : // GCC is free software; you can redistribute it and/or modify it under
6 : // the terms of the GNU General Public License as published by the Free
7 : // Software Foundation; either version 3, or (at your option) any later
8 : // version.
9 :
10 : // GCC is distributed in the hope that it will be useful, but WITHOUT ANY
11 : // WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 : // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 : // for more details.
14 :
15 : // You should have received a copy of the GNU General Public License
16 : // along with GCC; see the file COPYING3. If not see
17 : // <http://www.gnu.org/licenses/>.
18 :
19 : // The idea is that all reachable symbols are live, codes called
20 : // from live codes are live, and everything else is dead.
21 :
22 : #include "rust-lint-marklive.h"
23 : #include "options.h"
24 : #include "rust-hir-full.h"
25 : #include "rust-hir-map.h"
26 : #include "rust-hir-path.h"
27 : #include "rust-name-resolver.h"
28 : #include "rust-finalized-name-resolution-context.h"
29 : #include "rust-rib.h"
30 : #include "rust-system.h"
31 : #include "rust-tyty.h"
32 :
33 : namespace Rust {
34 : namespace Analysis {
35 :
36 : // This class trys to find the live symbols which can be used as
37 : // seeds in MarkLive
38 : //
39 : // 1. TODO: explicit live
40 : // - Attribute like #[allow(dead_code)]
41 : // - Attribute like #[lang=".."], it's not a intra-crate item.
42 : // 2. TODO: foreign item
43 8610 : class FindEntryPoint : public MarkLiveBase
44 : {
45 : using Rust::Analysis::MarkLiveBase::visit;
46 :
47 : public:
48 4305 : static std::vector<HirId> find (HIR::Crate &crate)
49 : {
50 8610 : FindEntryPoint findEntryPoint;
51 22114 : for (auto &it : crate.get_items ())
52 17809 : it->accept_vis (findEntryPoint);
53 4305 : return findEntryPoint.getEntryPoint ();
54 4305 : }
55 :
56 : // TODO not only fn main can be a entry point.
57 5979 : void visit (HIR::Function &function) override
58 : {
59 5979 : if (function.get_function_name ().as_string () == "main")
60 : {
61 3935 : entryPoints.push_back (function.get_mappings ().get_hirid ());
62 : }
63 5979 : }
64 :
65 : private:
66 4305 : FindEntryPoint () : MarkLiveBase () {}
67 : std::vector<HirId> entryPoints;
68 4305 : std::vector<HirId> getEntryPoint () { return entryPoints; }
69 : };
70 :
71 : std::set<HirId>
72 4305 : MarkLive::Analysis (HIR::Crate &crate)
73 : {
74 4305 : MarkLive marklive (FindEntryPoint::find (crate));
75 4305 : marklive.go (crate);
76 :
77 4305 : return marklive.liveSymbols;
78 4305 : }
79 :
80 : // pop a live symbol from worklist every iteration,
81 : // if it's a function then walk the function body, and
82 : // 1. save all the live symbols in worklist which is
83 : // visited first time
84 : // 2. save all the live symbols in liveSymbols
85 : void
86 4305 : MarkLive::go (HIR::Crate &)
87 : {
88 45892 : while (!worklist.empty ())
89 : {
90 41587 : HirId hirId = worklist.back ();
91 41587 : worklist.pop_back ();
92 41587 : scannedSymbols.emplace (hirId);
93 41587 : liveSymbols.emplace (hirId);
94 41587 : if (auto item = mappings.lookup_hir_item (hirId))
95 16742 : item.value ()->accept_vis (*this);
96 24845 : else if (auto implItem = mappings.lookup_hir_implitem (hirId))
97 1197 : implItem->first->accept_vis (*this);
98 : }
99 4305 : }
100 :
101 : void
102 46032 : MarkLive::visit (HIR::PathInExpression &expr)
103 : {
104 : // We should iterate every path segment in order to mark the struct which
105 : // is used in expression like Foo::bar(), we should mark the Foo alive.
106 46032 : if (!expr.is_lang_item ())
107 45983 : expr.iterate_path_segments ([&] (HIR::PathExprSegment &seg) -> bool {
108 50409 : return visit_path_segment (seg);
109 : });
110 :
111 : // after iterate the path segments, we should mark functions and associated
112 : // functions alive.
113 46032 : NodeId ast_node_id = expr.get_mappings ().get_nodeid ();
114 46032 : NodeId ref_node_id = UNKNOWN_NODEID;
115 :
116 46032 : if (expr.is_lang_item ())
117 49 : ref_node_id
118 49 : = Analysis::Mappings::get ().get_lang_item_node (expr.get_lang_item ());
119 : else
120 45983 : find_value_definition (ast_node_id, ref_node_id);
121 :
122 : // node back to HIR
123 46032 : tl::optional<HirId> hid = mappings.lookup_node_to_hir (ref_node_id);
124 46032 : rust_assert (hid.has_value ());
125 46032 : auto ref = hid.value ();
126 :
127 : // it must resolve to some kind of HIR::Item or HIR::InheritImplItem
128 46032 : if (auto resolved_item = mappings.lookup_hir_item (ref))
129 6659 : mark_hir_id (resolved_item.value ()->get_mappings ().get_hirid ());
130 39373 : else if (auto resolved_item = mappings.lookup_hir_implitem (ref))
131 487 : mark_hir_id (resolved_item->first->get_impl_mappings ().get_hirid ());
132 46032 : }
133 :
134 : void
135 1598 : MarkLive::visit (HIR::MethodCallExpr &expr)
136 : {
137 1598 : expr.get_receiver ().accept_vis (*this);
138 1598 : visit_path_segment (expr.get_method_name ());
139 2152 : for (auto &argument : expr.get_arguments ())
140 554 : argument->accept_vis (*this);
141 :
142 : // Trying to find the method definition and mark it alive.
143 1598 : NodeId ast_node_id = expr.get_mappings ().get_nodeid ();
144 1598 : NodeId ref_node_id = UNKNOWN_NODEID;
145 1598 : find_value_definition (ast_node_id, ref_node_id);
146 :
147 : // node back to HIR
148 1598 : if (auto hid = mappings.lookup_node_to_hir (ref_node_id))
149 1598 : mark_hir_id (*hid);
150 : else
151 0 : rust_unreachable ();
152 1598 : }
153 :
154 : bool
155 52007 : MarkLive::visit_path_segment (HIR::PathExprSegment seg)
156 : {
157 52007 : if (seg.has_generic_args ())
158 : {
159 1298 : for (auto &type : seg.get_generic_args ().get_type_args ())
160 : {
161 649 : NodeId node_id = type->get_mappings ().get_nodeid ();
162 :
163 649 : if (auto resolved
164 649 : = resolver.lookup (node_id, Resolver2_0::Namespace::Types))
165 : {
166 599 : if (auto hid = mappings.lookup_node_to_hir (*resolved))
167 599 : mark_hir_id (*hid);
168 : }
169 : }
170 : }
171 :
172 52007 : NodeId ast_node_id = seg.get_mappings ().get_nodeid ();
173 52007 : NodeId ref_node_id = UNKNOWN_NODEID;
174 :
175 : // There are two different kinds of segment for us.
176 : // 1. function segment
177 : // like the symbol "foo" in expression `foo()`.
178 : // 2. type segment
179 : // like the symbol "Foo" in expression `Foo{a: 1, b: 2}`
180 : //
181 : // We should mark them alive all and ignoring other kind of segments.
182 : // If the segment we dont care then just return false is fine
183 : // TODO: Should we look that up in all namespaces?
184 :
185 104014 : if (auto nslookup
186 52007 : = resolver.lookup (ast_node_id, Resolver2_0::Namespace::Values,
187 52007 : Resolver2_0::Namespace::Types))
188 49540 : ref_node_id = nslookup->id;
189 : else
190 2467 : return false;
191 49540 : if (auto hid = mappings.lookup_node_to_hir (ref_node_id))
192 : {
193 49540 : mark_hir_id (*hid);
194 49540 : return true;
195 : }
196 0 : rust_unreachable ();
197 : }
198 :
199 : void
200 3573 : MarkLive::visit (HIR::FieldAccessExpr &expr)
201 : {
202 : // visit receiver at first
203 3573 : expr.get_receiver_expr ().accept_vis (*this);
204 :
205 : // resolve the receiver back to ADT type
206 3573 : TyTy::BaseType *receiver = nullptr;
207 3573 : if (!tyctx->lookup_type (
208 3573 : expr.get_receiver_expr ().get_mappings ().get_hirid (), &receiver))
209 : {
210 0 : rust_error_at (expr.get_receiver_expr ().get_locus (),
211 : "unresolved type for receiver");
212 : }
213 :
214 3573 : TyTy::ADTType *adt = nullptr;
215 3573 : if (receiver->get_kind () == TyTy::TypeKind::ADT)
216 : {
217 3093 : adt = static_cast<TyTy::ADTType *> (receiver);
218 :
219 3093 : if (auto inner_ty = TyTy::try_get_box_inner_type (receiver))
220 : {
221 1 : rust_assert ((*inner_ty)->get_kind () == TyTy::TypeKind::ADT);
222 1 : adt = static_cast<TyTy::ADTType *> (*inner_ty);
223 : }
224 : }
225 480 : else if (receiver->get_kind () == TyTy::TypeKind::REF)
226 : {
227 480 : TyTy::ReferenceType *r = static_cast<TyTy::ReferenceType *> (receiver);
228 480 : TyTy::BaseType *b = r->get_base ();
229 480 : rust_assert (b->get_kind () == TyTy::TypeKind::ADT);
230 :
231 : adt = static_cast<TyTy::ADTType *> (b);
232 : }
233 :
234 3093 : rust_assert (adt != nullptr);
235 3573 : rust_assert (!adt->is_enum ());
236 3573 : rust_assert (adt->number_of_variants () == 1);
237 :
238 3573 : TyTy::VariantDef *variant = adt->get_variants ().at (0);
239 :
240 : // get the field index
241 3573 : size_t index;
242 3573 : TyTy::StructFieldType *field;
243 3573 : bool ok = variant->lookup_field (expr.get_field_name ().as_string (), &field,
244 : &index);
245 3573 : rust_assert (ok);
246 3573 : if (index >= variant->num_fields ())
247 : {
248 0 : rust_error_at (expr.get_receiver_expr ().get_locus (),
249 : "cannot access struct %s by index: %lu",
250 0 : adt->get_name ().c_str (), (unsigned long) index);
251 0 : return;
252 : }
253 :
254 : // get the field hir id
255 3573 : HirId field_id = field->get_ref ();
256 3573 : mark_hir_id (field_id);
257 : }
258 :
259 : void
260 571 : MarkLive::visit (HIR::TupleIndexExpr &expr)
261 : {
262 : // TODO: unused tuple field detection
263 571 : expr.get_tuple_expr ().accept_vis (*this);
264 571 : }
265 :
266 : void
267 14 : MarkLive::visit (HIR::TypeAlias &alias)
268 : {
269 14 : NodeId ast_node_id;
270 :
271 14 : if (auto id = resolver.lookup (
272 14 : alias.get_type_aliased ().get_mappings ().get_nodeid (),
273 14 : Resolver2_0::Namespace::Types))
274 14 : ast_node_id = *id;
275 : else
276 0 : rust_unreachable ();
277 :
278 14 : if (auto hid = mappings.lookup_node_to_hir (ast_node_id))
279 14 : mark_hir_id (*hid);
280 : else
281 0 : rust_unreachable ();
282 14 : }
283 :
284 : void
285 62470 : MarkLive::mark_hir_id (HirId id)
286 : {
287 62470 : if (scannedSymbols.find (id) == scannedSymbols.end ())
288 : {
289 37652 : worklist.push_back (id);
290 : }
291 62470 : liveSymbols.emplace (id);
292 62470 : }
293 :
294 : void
295 47581 : MarkLive::find_value_definition (NodeId ast_node_id, NodeId &ref_node_id)
296 : {
297 47581 : auto resolved = resolver.lookup (ast_node_id, Resolver2_0::Namespace::Values,
298 : Resolver2_0::Namespace::Types);
299 47581 : rust_assert (resolved.has_value ());
300 :
301 47581 : ref_node_id = resolved->id;
302 47581 : }
303 :
304 : } // namespace Analysis
305 : } // namespace Rust
|