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-check2.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 : 0 : ReadonlyChecker::ReadonlyChecker ()
36 : 0 : : resolver (*Resolver::Resolver::get ()),
37 : 0 : mappings (Analysis::Mappings::get ()),
38 : 0 : context (*Resolver::TypeCheckContext::get ())
39 : 0 : {}
40 : :
41 : : void
42 : 0 : ReadonlyChecker::go (Crate &crate)
43 : : {
44 : 0 : for (auto &item : crate.get_items ())
45 : 0 : item->accept_vis (*this);
46 : 0 : }
47 : :
48 : : void
49 : 0 : ReadonlyChecker::visit (AssignmentExpr &expr)
50 : : {
51 : 0 : Expr &lhs = expr.get_lhs ();
52 : 0 : mutable_context.enter (expr.get_mappings ().get_hirid ());
53 : 0 : lhs.accept_vis (*this);
54 : 0 : mutable_context.exit ();
55 : 0 : }
56 : :
57 : : void
58 : 0 : ReadonlyChecker::visit (PathInExpression &expr)
59 : : {
60 : 0 : if (!mutable_context.is_in_context ())
61 : 0 : return;
62 : :
63 : 0 : NodeId ast_node_id = expr.get_mappings ().get_nodeid ();
64 : 0 : NodeId def_id;
65 : :
66 : 0 : auto &nr_ctx
67 : 0 : = Resolver2_0::ImmutableNameResolutionContext::get ().resolver ();
68 : 0 : if (auto id = nr_ctx.lookup (ast_node_id))
69 : 0 : def_id = *id;
70 : : else
71 : 0 : return;
72 : :
73 : 0 : auto hir_id = mappings.lookup_node_to_hir (def_id);
74 : 0 : if (!hir_id)
75 : : return;
76 : :
77 : : // Check if the local variable is mutable.
78 : 0 : auto maybe_pattern = mappings.lookup_hir_pattern (*hir_id);
79 : 0 : if (maybe_pattern
80 : 0 : && maybe_pattern.value ()->get_pattern_type ()
81 : : == HIR::Pattern::PatternType::IDENTIFIER)
82 : 0 : check_variable (static_cast<IdentifierPattern *> (maybe_pattern.value ()),
83 : : expr.get_locus ());
84 : :
85 : : // Check if the static item is mutable.
86 : 0 : auto maybe_item = mappings.lookup_hir_item (*hir_id);
87 : 0 : if (maybe_item
88 : 0 : && 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 : 0 : if (maybe_item
99 : 0 : && maybe_item.value ()->get_item_kind () == HIR::Item::ItemKind::Constant)
100 : : {
101 : 0 : auto const_item = static_cast<HIR::ConstantItem *> (*maybe_item);
102 : 0 : rust_error_at (expr.get_locus (), "assignment of read-only location '%s'",
103 : 0 : const_item->get_identifier ().as_string ().c_str ());
104 : : }
105 : : }
106 : :
107 : : void
108 : 0 : ReadonlyChecker::check_variable (IdentifierPattern *pattern,
109 : : location_t assigned_loc)
110 : : {
111 : 0 : if (!mutable_context.is_in_context ())
112 : 0 : return;
113 : 0 : if (pattern->is_mut ())
114 : : return;
115 : :
116 : 0 : auto hir_id = pattern->get_mappings ().get_hirid ();
117 : 0 : if (already_assigned_variables.count (hir_id) > 0)
118 : 0 : rust_error_at (assigned_loc, "assignment of read-only variable '%s'",
119 : 0 : pattern->as_string ().c_str ());
120 : 0 : already_assigned_variables.insert (hir_id);
121 : : }
122 : :
123 : : void
124 : 0 : ReadonlyChecker::collect_assignment_identifier (IdentifierPattern &pattern,
125 : : bool has_init_expr)
126 : : {
127 : 0 : if (has_init_expr)
128 : : {
129 : 0 : HirId pattern_id = pattern.get_mappings ().get_hirid ();
130 : 0 : already_assigned_variables.insert (pattern_id);
131 : : }
132 : 0 : }
133 : :
134 : : void
135 : 0 : ReadonlyChecker::collect_assignment_tuple (TuplePattern &tuple_pattern,
136 : : bool has_init_expr)
137 : : {
138 : 0 : switch (tuple_pattern.get_items ().get_item_type ())
139 : : {
140 : 0 : case HIR::TuplePatternItems::ItemType::MULTIPLE:
141 : 0 : {
142 : 0 : auto &items = static_cast<HIR::TuplePatternItemsMultiple &> (
143 : 0 : tuple_pattern.get_items ());
144 : 0 : for (auto &sub : items.get_patterns ())
145 : : {
146 : 0 : collect_assignment (*sub, has_init_expr);
147 : : }
148 : : }
149 : : break;
150 : : default:
151 : : break;
152 : : }
153 : 0 : }
154 : :
155 : : void
156 : 0 : ReadonlyChecker::collect_assignment (Pattern &pattern, bool has_init_expr)
157 : : {
158 : 0 : switch (pattern.get_pattern_type ())
159 : : {
160 : 0 : case HIR::Pattern::PatternType::IDENTIFIER:
161 : 0 : {
162 : 0 : collect_assignment_identifier (static_cast<IdentifierPattern &> (
163 : : pattern),
164 : : has_init_expr);
165 : : }
166 : 0 : break;
167 : 0 : case HIR::Pattern::PatternType::TUPLE:
168 : 0 : {
169 : 0 : auto &tuple_pattern = static_cast<HIR::TuplePattern &> (pattern);
170 : 0 : collect_assignment_tuple (tuple_pattern, has_init_expr);
171 : : }
172 : 0 : break;
173 : : default:
174 : : break;
175 : : }
176 : 0 : }
177 : :
178 : : void
179 : 0 : ReadonlyChecker::visit (LetStmt &stmt)
180 : : {
181 : 0 : HIR::Pattern &pattern = stmt.get_pattern ();
182 : 0 : collect_assignment (pattern, stmt.has_init_expr ());
183 : 0 : }
184 : :
185 : : void
186 : 0 : ReadonlyChecker::visit (FieldAccessExpr &expr)
187 : : {
188 : 0 : if (mutable_context.is_in_context ())
189 : : {
190 : 0 : expr.get_receiver_expr ().accept_vis (*this);
191 : : }
192 : 0 : }
193 : :
194 : : void
195 : 0 : ReadonlyChecker::visit (TupleIndexExpr &expr)
196 : : {
197 : 0 : if (mutable_context.is_in_context ())
198 : : {
199 : 0 : expr.get_tuple_expr ().accept_vis (*this);
200 : : }
201 : 0 : }
202 : :
203 : : void
204 : 0 : ReadonlyChecker::visit (ArrayIndexExpr &expr)
205 : : {
206 : 0 : if (mutable_context.is_in_context ())
207 : : {
208 : 0 : expr.get_array_expr ().accept_vis (*this);
209 : : }
210 : 0 : }
211 : :
212 : : void
213 : 0 : ReadonlyChecker::visit (TupleExpr &expr)
214 : : {
215 : 0 : if (mutable_context.is_in_context ())
216 : : {
217 : : // TODO: Add check for tuple expression
218 : : }
219 : 0 : }
220 : :
221 : : void
222 : 0 : ReadonlyChecker::visit (LiteralExpr &expr)
223 : : {
224 : 0 : if (mutable_context.is_in_context ())
225 : : {
226 : 0 : rust_error_at (expr.get_locus (), "assignment of read-only location");
227 : : }
228 : 0 : }
229 : :
230 : : void
231 : 0 : ReadonlyChecker::visit (DereferenceExpr &expr)
232 : : {
233 : 0 : if (!mutable_context.is_in_context ())
234 : 0 : return;
235 : 0 : TyTy::BaseType *to_deref_type;
236 : 0 : auto to_deref = expr.get_expr ().get_mappings ().get_hirid ();
237 : 0 : if (!context.lookup_type (to_deref, &to_deref_type))
238 : : return;
239 : 0 : if (to_deref_type->get_kind () == TyTy::TypeKind::REF)
240 : : {
241 : 0 : auto ref_type = static_cast<TyTy::ReferenceType *> (to_deref_type);
242 : 0 : if (!ref_type->is_mutable ())
243 : 0 : rust_error_at (expr.get_locus (), "assignment of read-only location");
244 : : }
245 : 0 : if (to_deref_type->get_kind () == TyTy::TypeKind::POINTER)
246 : : {
247 : 0 : auto ptr_type = static_cast<TyTy::PointerType *> (to_deref_type);
248 : 0 : if (!ptr_type->is_mutable ())
249 : 0 : rust_error_at (expr.get_locus (), "assignment of read-only location");
250 : : }
251 : : }
252 : : } // namespace HIR
253 : : } // namespace Rust
|