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-immutable-name-resolution-context.h"
29 : #include "rust-system.h"
30 :
31 : namespace Rust {
32 : namespace Analysis {
33 :
34 : // This class trys to find the live symbols which can be used as
35 : // seeds in MarkLive
36 : //
37 : // 1. TODO: explicit live
38 : // - Attribute like #[allow(dead_code)]
39 : // - Attribute like #[lang=".."], it's not a intra-crate item.
40 : // 2. TODO: foreign item
41 8368 : class FindEntryPoint : public MarkLiveBase
42 : {
43 : using Rust::Analysis::MarkLiveBase::visit;
44 :
45 : public:
46 4184 : static std::vector<HirId> find (HIR::Crate &crate)
47 : {
48 8368 : FindEntryPoint findEntryPoint;
49 21374 : for (auto &it : crate.get_items ())
50 17190 : it->accept_vis (findEntryPoint);
51 4184 : return findEntryPoint.getEntryPoint ();
52 4184 : }
53 :
54 : // TODO not only fn main can be a entry point.
55 5805 : void visit (HIR::Function &function) override
56 : {
57 5805 : if (function.get_function_name ().as_string () == "main")
58 : {
59 7672 : entryPoints.push_back (function.get_mappings ().get_hirid ());
60 : }
61 5805 : }
62 :
63 : private:
64 4184 : FindEntryPoint () : MarkLiveBase () {}
65 : std::vector<HirId> entryPoints;
66 4184 : std::vector<HirId> getEntryPoint () { return entryPoints; }
67 : };
68 :
69 : std::set<HirId>
70 4184 : MarkLive::Analysis (HIR::Crate &crate)
71 : {
72 4184 : MarkLive marklive (FindEntryPoint::find (crate));
73 4184 : marklive.go (crate);
74 :
75 4184 : return marklive.liveSymbols;
76 4184 : }
77 :
78 : // pop a live symbol from worklist every iteration,
79 : // if it's a function then walk the function body, and
80 : // 1. save all the live symbols in worklist which is
81 : // visited first time
82 : // 2. save all the live symbols in liveSymbols
83 : void
84 4184 : MarkLive::go (HIR::Crate &)
85 : {
86 44204 : while (!worklist.empty ())
87 : {
88 40020 : HirId hirId = worklist.back ();
89 40020 : worklist.pop_back ();
90 40020 : scannedSymbols.emplace (hirId);
91 40020 : liveSymbols.emplace (hirId);
92 40020 : if (auto item = mappings.lookup_hir_item (hirId))
93 16278 : item.value ()->accept_vis (*this);
94 23742 : else if (auto implItem = mappings.lookup_hir_implitem (hirId))
95 1172 : implItem->first->accept_vis (*this);
96 : }
97 4184 : }
98 :
99 : void
100 44961 : MarkLive::visit (HIR::PathInExpression &expr)
101 : {
102 : // We should iterate every path segment in order to mark the struct which
103 : // is used in expression like Foo::bar(), we should mark the Foo alive.
104 44961 : if (!expr.is_lang_item ())
105 44912 : expr.iterate_path_segments ([&] (HIR::PathExprSegment &seg) -> bool {
106 49183 : return visit_path_segment (seg);
107 : });
108 :
109 : // after iterate the path segments, we should mark functions and associated
110 : // functions alive.
111 44961 : NodeId ast_node_id = expr.get_mappings ().get_nodeid ();
112 44961 : NodeId ref_node_id = UNKNOWN_NODEID;
113 :
114 44961 : if (expr.is_lang_item ())
115 49 : ref_node_id
116 49 : = Analysis::Mappings::get ().get_lang_item_node (expr.get_lang_item ());
117 : else
118 44912 : find_ref_node_id (ast_node_id, ref_node_id);
119 :
120 : // node back to HIR
121 44961 : tl::optional<HirId> hid = mappings.lookup_node_to_hir (ref_node_id);
122 44961 : rust_assert (hid.has_value ());
123 44961 : auto ref = hid.value ();
124 :
125 : // it must resolve to some kind of HIR::Item or HIR::InheritImplItem
126 44961 : if (auto resolved_item = mappings.lookup_hir_item (ref))
127 6489 : mark_hir_id (resolved_item.value ()->get_mappings ().get_hirid ());
128 38472 : else if (auto resolved_item = mappings.lookup_hir_implitem (ref))
129 485 : mark_hir_id (resolved_item->first->get_impl_mappings ().get_hirid ());
130 44961 : }
131 :
132 : void
133 1527 : MarkLive::visit (HIR::MethodCallExpr &expr)
134 : {
135 1527 : expr.get_receiver ().accept_vis (*this);
136 1527 : visit_path_segment (expr.get_method_name ());
137 2060 : for (auto &argument : expr.get_arguments ())
138 533 : argument->accept_vis (*this);
139 :
140 : // Trying to find the method definition and mark it alive.
141 1527 : NodeId ast_node_id = expr.get_mappings ().get_nodeid ();
142 1527 : NodeId ref_node_id = UNKNOWN_NODEID;
143 1527 : find_ref_node_id (ast_node_id, ref_node_id);
144 :
145 : // node back to HIR
146 1527 : if (auto hid = mappings.lookup_node_to_hir (ref_node_id))
147 1527 : mark_hir_id (*hid);
148 : else
149 0 : rust_unreachable ();
150 1527 : }
151 :
152 : bool
153 50710 : MarkLive::visit_path_segment (HIR::PathExprSegment seg)
154 : {
155 50710 : NodeId ast_node_id = seg.get_mappings ().get_nodeid ();
156 50710 : NodeId ref_node_id = UNKNOWN_NODEID;
157 :
158 : // There are two different kinds of segment for us.
159 : // 1. function segment
160 : // like the symbol "foo" in expression `foo()`.
161 : // 2. type segment
162 : // like the symbol "Foo" in expression `Foo{a: 1, b: 2}`
163 : //
164 : // We should mark them alive all and ignoring other kind of segments.
165 : // If the segment we dont care then just return false is fine
166 50710 : if (auto id = resolver.lookup (ast_node_id))
167 48316 : ref_node_id = *id;
168 : else
169 2394 : return false;
170 48316 : if (auto hid = mappings.lookup_node_to_hir (ref_node_id))
171 : {
172 48316 : mark_hir_id (*hid);
173 48316 : return true;
174 : }
175 0 : rust_unreachable ();
176 : }
177 :
178 : void
179 3526 : MarkLive::visit (HIR::FieldAccessExpr &expr)
180 : {
181 : // visit receiver at first
182 3526 : expr.get_receiver_expr ().accept_vis (*this);
183 :
184 : // resolve the receiver back to ADT type
185 3526 : TyTy::BaseType *receiver = nullptr;
186 3526 : if (!tyctx->lookup_type (
187 3526 : expr.get_receiver_expr ().get_mappings ().get_hirid (), &receiver))
188 : {
189 0 : rust_error_at (expr.get_receiver_expr ().get_locus (),
190 : "unresolved type for receiver");
191 : }
192 :
193 3526 : TyTy::ADTType *adt = nullptr;
194 3526 : if (receiver->get_kind () == TyTy::TypeKind::ADT)
195 : {
196 3068 : adt = static_cast<TyTy::ADTType *> (receiver);
197 : }
198 458 : else if (receiver->get_kind () == TyTy::TypeKind::REF)
199 : {
200 458 : TyTy::ReferenceType *r = static_cast<TyTy::ReferenceType *> (receiver);
201 458 : TyTy::BaseType *b = r->get_base ();
202 458 : rust_assert (b->get_kind () == TyTy::TypeKind::ADT);
203 :
204 : adt = static_cast<TyTy::ADTType *> (b);
205 : }
206 :
207 3068 : rust_assert (adt != nullptr);
208 3526 : rust_assert (!adt->is_enum ());
209 3526 : rust_assert (adt->number_of_variants () == 1);
210 :
211 3526 : TyTy::VariantDef *variant = adt->get_variants ().at (0);
212 :
213 : // get the field index
214 3526 : size_t index;
215 3526 : TyTy::StructFieldType *field;
216 3526 : bool ok = variant->lookup_field (expr.get_field_name ().as_string (), &field,
217 : &index);
218 3526 : rust_assert (ok);
219 3526 : if (index >= variant->num_fields ())
220 : {
221 0 : rust_error_at (expr.get_receiver_expr ().get_locus (),
222 : "cannot access struct %s by index: %lu",
223 0 : adt->get_name ().c_str (), (unsigned long) index);
224 0 : return;
225 : }
226 :
227 : // get the field hir id
228 3526 : HirId field_id = field->get_ref ();
229 3526 : mark_hir_id (field_id);
230 : }
231 :
232 : void
233 575 : MarkLive::visit (HIR::TupleIndexExpr &expr)
234 : {
235 : // TODO: unused tuple field detection
236 575 : expr.get_tuple_expr ().accept_vis (*this);
237 575 : }
238 :
239 : void
240 14 : MarkLive::visit (HIR::TypeAlias &alias)
241 : {
242 14 : NodeId ast_node_id;
243 :
244 14 : if (auto id = resolver.lookup (
245 28 : alias.get_type_aliased ().get_mappings ().get_nodeid ()))
246 14 : ast_node_id = *id;
247 : else
248 0 : rust_unreachable ();
249 :
250 14 : if (auto hid = mappings.lookup_node_to_hir (ast_node_id))
251 14 : mark_hir_id (*hid);
252 : else
253 0 : rust_unreachable ();
254 14 : }
255 :
256 : void
257 60357 : MarkLive::mark_hir_id (HirId id)
258 : {
259 60357 : if (scannedSymbols.find (id) == scannedSymbols.end ())
260 : {
261 36184 : worklist.push_back (id);
262 : }
263 60357 : liveSymbols.emplace (id);
264 60357 : }
265 :
266 : void
267 46439 : MarkLive::find_ref_node_id (NodeId ast_node_id, NodeId &ref_node_id)
268 : {
269 46439 : auto resolved = resolver.lookup (ast_node_id);
270 46439 : rust_assert (resolved.has_value ());
271 46439 : ref_node_id = resolved.value ();
272 46439 : }
273 :
274 : } // namespace Analysis
275 : } // namespace Rust
|