Branch data Line data Source code
1 : : // Copyright (C) 2025 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 : : #include "rust-readonly-check.h"
20 : : #include "rust-hir-expr.h"
21 : : #include "rust-hir-node.h"
22 : : #include "rust-hir-path.h"
23 : : #include "rust-hir-map.h"
24 : : #include "rust-hir-pattern.h"
25 : : #include "rust-mapping-common.h"
26 : : #include "rust-system.h"
27 : : #include "rust-immutable-name-resolution-context.h"
28 : : #include "rust-tyty.h"
29 : :
30 : : namespace Rust {
31 : : namespace HIR {
32 : :
33 : : static std::set<HirId> already_assigned_variables = {};
34 : :
35 : 3998 : ReadonlyChecker::ReadonlyChecker ()
36 : 3998 : : resolver (*Resolver::Resolver::get ()),
37 : 3998 : mappings (Analysis::Mappings::get ()),
38 : 7996 : context (*Resolver::TypeCheckContext::get ())
39 : 3998 : {}
40 : :
41 : : void
42 : 3998 : ReadonlyChecker::go (Crate &crate)
43 : : {
44 : 20661 : for (auto &item : crate.get_items ())
45 : 16663 : item->accept_vis (*this);
46 : 3998 : }
47 : :
48 : : void
49 : 2392 : ReadonlyChecker::visit (AssignmentExpr &expr)
50 : : {
51 : 2392 : Expr &lhs = expr.get_lhs ();
52 : 2392 : mutable_context.enter (expr.get_mappings ().get_hirid ());
53 : 2392 : lhs.accept_vis (*this);
54 : 2392 : mutable_context.exit ();
55 : 2392 : }
56 : :
57 : : void
58 : 27197 : ReadonlyChecker::visit (PathInExpression &expr)
59 : : {
60 : 27197 : if (!mutable_context.is_in_context ())
61 : 24861 : return;
62 : :
63 : 2336 : NodeId ast_node_id = expr.get_mappings ().get_nodeid ();
64 : 2336 : NodeId def_id;
65 : :
66 : 2336 : auto &nr_ctx
67 : 2336 : = Resolver2_0::ImmutableNameResolutionContext::get ().resolver ();
68 : 2336 : if (auto id = nr_ctx.lookup (ast_node_id))
69 : 2336 : def_id = *id;
70 : : else
71 : 0 : return;
72 : :
73 : 2336 : auto hir_id = mappings.lookup_node_to_hir (def_id);
74 : 2336 : if (!hir_id)
75 : : return;
76 : :
77 : : // Check if the local variable is mutable.
78 : 2336 : auto maybe_pattern = mappings.lookup_hir_pattern (*hir_id);
79 : 2336 : if (maybe_pattern
80 : 2336 : && maybe_pattern.value ()->get_pattern_type ()
81 : : == HIR::Pattern::PatternType::IDENTIFIER)
82 : 2271 : check_variable (static_cast<IdentifierPattern *> (maybe_pattern.value ()),
83 : : expr.get_locus ());
84 : :
85 : : // Check if the static item is mutable.
86 : 2336 : auto maybe_item = mappings.lookup_hir_item (*hir_id);
87 : 2336 : if (maybe_item
88 : 2336 : && maybe_item.value ()->get_item_kind () == HIR::Item::ItemKind::Static)
89 : : {
90 : 0 : auto static_item = static_cast<HIR::StaticItem *> (*maybe_item);
91 : 0 : if (!static_item->is_mut ())
92 : 0 : rust_error_at (expr.get_locus (),
93 : : "assignment of read-only location '%s'",
94 : 0 : static_item->get_identifier ().as_string ().c_str ());
95 : : }
96 : :
97 : : // Check if the constant item is mutable.
98 : 2336 : if (maybe_item
99 : 2336 : && maybe_item.value ()->get_item_kind () == HIR::Item::ItemKind::Constant)
100 : : {
101 : 1 : auto const_item = static_cast<HIR::ConstantItem *> (*maybe_item);
102 : 1 : rust_error_at (expr.get_locus (), "assignment of read-only location '%s'",
103 : 2 : const_item->get_identifier ().as_string ().c_str ());
104 : : }
105 : : }
106 : :
107 : : void
108 : 2271 : ReadonlyChecker::check_variable (IdentifierPattern *pattern,
109 : : location_t assigned_loc)
110 : : {
111 : 2271 : if (!mutable_context.is_in_context ())
112 : 1322 : return;
113 : :
114 : 2271 : TyTy::BaseType *type;
115 : 2271 : if (context.lookup_type (pattern->get_mappings ().get_hirid (), &type)
116 : 2271 : && is_mutable_type (type))
117 : : return;
118 : 1570 : if (pattern->is_mut ())
119 : : return;
120 : :
121 : 949 : auto hir_id = pattern->get_mappings ().get_hirid ();
122 : 949 : if (already_assigned_variables.count (hir_id) > 0)
123 : 5 : rust_error_at (assigned_loc, "assignment of read-only variable '%s'",
124 : 10 : pattern->as_string ().c_str ());
125 : 949 : already_assigned_variables.insert (hir_id);
126 : : }
127 : :
128 : : void
129 : 12156 : ReadonlyChecker::collect_assignment_identifier (IdentifierPattern &pattern,
130 : : bool has_init_expr)
131 : : {
132 : 12156 : if (has_init_expr)
133 : : {
134 : 11146 : HirId pattern_id = pattern.get_mappings ().get_hirid ();
135 : 11146 : already_assigned_variables.insert (pattern_id);
136 : : }
137 : 12156 : }
138 : :
139 : : void
140 : 282 : ReadonlyChecker::collect_assignment_tuple (TuplePattern &tuple_pattern,
141 : : bool has_init_expr)
142 : : {
143 : 282 : switch (tuple_pattern.get_items ().get_item_type ())
144 : : {
145 : 280 : case HIR::TuplePatternItems::ItemType::NO_REST:
146 : 280 : {
147 : 280 : auto &items_no_rest = static_cast<HIR::TuplePatternItemsNoRest &> (
148 : 280 : tuple_pattern.get_items ());
149 : 845 : for (auto &sub : items_no_rest.get_patterns ())
150 : : {
151 : 565 : collect_assignment (*sub, has_init_expr);
152 : : }
153 : : }
154 : : break;
155 : 2 : case HIR::TuplePatternItems::ItemType::HAS_REST:
156 : 2 : {
157 : 2 : auto &items_has_rest = static_cast<HIR::TuplePatternItemsHasRest &> (
158 : 2 : tuple_pattern.get_items ());
159 : 4 : for (auto &sub : items_has_rest.get_lower_patterns ())
160 : 2 : collect_assignment (*sub, has_init_expr);
161 : 4 : for (auto &sub : items_has_rest.get_upper_patterns ())
162 : 2 : collect_assignment (*sub, has_init_expr);
163 : : }
164 : : break;
165 : : default:
166 : : break;
167 : : }
168 : 282 : }
169 : :
170 : : void
171 : 12709 : ReadonlyChecker::collect_assignment (Pattern &pattern, bool has_init_expr)
172 : : {
173 : 12709 : switch (pattern.get_pattern_type ())
174 : : {
175 : 12156 : case HIR::Pattern::PatternType::IDENTIFIER:
176 : 12156 : {
177 : 12156 : collect_assignment_identifier (static_cast<IdentifierPattern &> (
178 : : pattern),
179 : : has_init_expr);
180 : : }
181 : 12156 : break;
182 : 282 : case HIR::Pattern::PatternType::TUPLE:
183 : 282 : {
184 : 282 : auto &tuple_pattern = static_cast<HIR::TuplePattern &> (pattern);
185 : 282 : collect_assignment_tuple (tuple_pattern, has_init_expr);
186 : : }
187 : 282 : break;
188 : : default:
189 : : break;
190 : : }
191 : 12709 : }
192 : :
193 : : void
194 : 12140 : ReadonlyChecker::visit (LetStmt &stmt)
195 : : {
196 : 12140 : HIR::Pattern &pattern = stmt.get_pattern ();
197 : 12140 : collect_assignment (pattern, stmt.has_init_expr ());
198 : 12140 : }
199 : :
200 : : void
201 : 3293 : ReadonlyChecker::visit (FieldAccessExpr &expr)
202 : : {
203 : 3293 : if (mutable_context.is_in_context ())
204 : : {
205 : 800 : expr.get_receiver_expr ().accept_vis (*this);
206 : : }
207 : 3293 : }
208 : :
209 : : void
210 : 528 : ReadonlyChecker::visit (TupleIndexExpr &expr)
211 : : {
212 : 528 : if (mutable_context.is_in_context ())
213 : : {
214 : 8 : expr.get_tuple_expr ().accept_vis (*this);
215 : : }
216 : 528 : }
217 : :
218 : : void
219 : 68 : ReadonlyChecker::visit (ArrayIndexExpr &expr)
220 : : {
221 : 68 : if (mutable_context.is_in_context ())
222 : : {
223 : 10 : expr.get_array_expr ().accept_vis (*this);
224 : : }
225 : 68 : }
226 : :
227 : : void
228 : 319 : ReadonlyChecker::visit (TupleExpr &expr)
229 : : {
230 : 319 : if (mutable_context.is_in_context ())
231 : : {
232 : : // TODO: Add check for tuple expression
233 : : }
234 : 319 : }
235 : :
236 : : void
237 : 7231 : ReadonlyChecker::visit (LiteralExpr &expr)
238 : : {
239 : 7231 : if (mutable_context.is_in_context ())
240 : : {
241 : 1 : rust_error_at (expr.get_locus (), "assignment of read-only location");
242 : : }
243 : 7231 : }
244 : :
245 : : void
246 : 3700 : ReadonlyChecker::visit (DereferenceExpr &expr)
247 : : {
248 : 3700 : if (!mutable_context.is_in_context ())
249 : 3645 : return;
250 : 55 : TyTy::BaseType *to_deref_type;
251 : 55 : auto to_deref = expr.get_expr ().get_mappings ().get_hirid ();
252 : 55 : if (!context.lookup_type (to_deref, &to_deref_type))
253 : : return;
254 : 55 : if (!is_mutable_type (to_deref_type))
255 : 0 : rust_error_at (expr.get_locus (), "assignment of read-only location");
256 : : }
257 : :
258 : : bool
259 : 2326 : ReadonlyChecker::is_mutable_type (TyTy::BaseType *type)
260 : : {
261 : 2326 : if (type->get_kind () == TyTy::TypeKind::REF)
262 : 847 : return static_cast<TyTy::ReferenceType *> (type)->is_mutable ();
263 : 1479 : if (type->get_kind () == TyTy::TypeKind::POINTER)
264 : 0 : return static_cast<TyTy::PointerType *> (type)->is_mutable ();
265 : : return false;
266 : : }
267 : : } // namespace HIR
268 : : } // namespace Rust
|