Skip to content

Commit 386a185

Browse files
sakupan102philberty
authored andcommitted
Replace old read-only checker with new implementation.
gcc/rust/ChangeLog: * Make-lang.in: Replace old read-only checker with new implementation. * checks/errors/rust-readonly-check.cc (ReadonlyChecker::ReadonlyChecker): Replace old read-only checker with new implementation. * checks/errors/rust-readonly-check.h: Replace old read-only checker with new implementation. * rust-session-manager.cc (Session::compile_crate): Switch to new read-only checker. * checks/errors/rust-readonly-check2.cc: Removed. * checks/errors/rust-readonly-check2.h: Removed. Signed-off-by: Ryutaro Okada <[email protected]>
1 parent 5af110d commit 386a185

File tree

6 files changed

+244
-478
lines changed

6 files changed

+244
-478
lines changed

gcc/rust/Make-lang.in

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,6 @@ GRS_OBJS = \
209209
rust/rust-lint-marklive.o \
210210
rust/rust-lint-unused-var.o \
211211
rust/rust-readonly-check.o \
212-
rust/rust-readonly-check2.o \
213212
rust/rust-hir-type-check-path.o \
214213
rust/rust-unsafe-checker.o \
215214
rust/rust-hir-pattern-analysis.o \
Lines changed: 194 additions & 136 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (C) 2021-2025 Free Software Foundation, Inc.
1+
// Copyright (C) 2025 Free Software Foundation, Inc.
22

33
// This file is part of GCC.
44

@@ -17,184 +17,242 @@
1717
// <http://www.gnu.org/licenses/>.
1818

1919
#include "rust-readonly-check.h"
20-
#include "rust-tree.h"
21-
#include "rust-gcc.h"
22-
#include "print-tree.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"
2329

2430
namespace Rust {
25-
namespace Analysis {
31+
namespace HIR {
2632

27-
static std::map<tree, int> assignment_map = {};
33+
static std::set<HirId> already_assigned_variables = {};
34+
35+
ReadonlyChecker::ReadonlyChecker ()
36+
: resolver (*Resolver::Resolver::get ()),
37+
mappings (Analysis::Mappings::get ()),
38+
context (*Resolver::TypeCheckContext::get ())
39+
{}
40+
41+
void
42+
ReadonlyChecker::go (Crate &crate)
43+
{
44+
for (auto &item : crate.get_items ())
45+
item->accept_vis (*this);
46+
}
47+
48+
void
49+
ReadonlyChecker::visit (AssignmentExpr &expr)
50+
{
51+
Expr &lhs = expr.get_lhs ();
52+
mutable_context.enter (expr.get_mappings ().get_hirid ());
53+
lhs.accept_vis (*this);
54+
mutable_context.exit ();
55+
}
2856

29-
// ported over from c-family/c-warn.cc
3057
void
31-
readonly_error (location_t loc, tree arg, enum lvalue_use use)
58+
ReadonlyChecker::visit (PathInExpression &expr)
3259
{
33-
gcc_assert (use == lv_assign || use == lv_increment || use == lv_decrement
34-
|| use == lv_asm);
35-
STRIP_ANY_LOCATION_WRAPPER (arg);
36-
/* Using this macro rather than (for example) arrays of messages
37-
ensures that all the format strings are checked at compile
38-
time. */
39-
#define READONLY_MSG(A, I, D, AS) \
40-
(use == lv_assign \
41-
? (A) \
42-
: (use == lv_increment ? (I) : (use == lv_decrement ? (D) : (AS))))
43-
if (TREE_CODE (arg) == COMPONENT_REF)
60+
if (!mutable_context.is_in_context ())
61+
return;
62+
63+
NodeId ast_node_id = expr.get_mappings ().get_nodeid ();
64+
NodeId def_id;
65+
66+
auto &nr_ctx
67+
= Resolver2_0::ImmutableNameResolutionContext::get ().resolver ();
68+
if (auto id = nr_ctx.lookup (ast_node_id))
69+
def_id = *id;
70+
else
71+
return;
72+
73+
auto hir_id = mappings.lookup_node_to_hir (def_id);
74+
if (!hir_id)
75+
return;
76+
77+
// Check if the local variable is mutable.
78+
auto maybe_pattern = mappings.lookup_hir_pattern (*hir_id);
79+
if (maybe_pattern
80+
&& maybe_pattern.value ()->get_pattern_type ()
81+
== HIR::Pattern::PatternType::IDENTIFIER)
82+
check_variable (static_cast<IdentifierPattern *> (maybe_pattern.value ()),
83+
expr.get_locus ());
84+
85+
// Check if the static item is mutable.
86+
auto maybe_item = mappings.lookup_hir_item (*hir_id);
87+
if (maybe_item
88+
&& maybe_item.value ()->get_item_kind () == HIR::Item::ItemKind::Static)
4489
{
45-
if (TYPE_READONLY (TREE_TYPE (TREE_OPERAND (arg, 0))))
46-
error_at (loc,
47-
READONLY_MSG (G_ ("assignment of member "
48-
"%qD in read-only object"),
49-
G_ ("increment of member "
50-
"%qD in read-only object"),
51-
G_ ("decrement of member "
52-
"%qD in read-only object"),
53-
G_ ("member %qD in read-only object "
54-
"used as %<asm%> output")),
55-
TREE_OPERAND (arg, 1));
56-
else
57-
error_at (
58-
loc,
59-
READONLY_MSG (G_ ("assignment of read-only member %qD"),
60-
G_ ("increment of read-only member %qD"),
61-
G_ ("decrement of read-only member %qD"),
62-
G_ ("read-only member %qD used as %<asm%> output")),
63-
TREE_OPERAND (arg, 1));
90+
auto static_item = static_cast<HIR::StaticItem *> (*maybe_item);
91+
if (!static_item->is_mut ())
92+
rust_error_at (expr.get_locus (),
93+
"assignment of read-only location '%s'",
94+
static_item->get_identifier ().as_string ().c_str ());
6495
}
65-
else if (VAR_P (arg))
66-
error_at (loc,
67-
READONLY_MSG (G_ ("assignment of read-only variable %qD"),
68-
G_ ("increment of read-only variable %qD"),
69-
G_ ("decrement of read-only variable %qD"),
70-
G_ (
71-
"read-only variable %qD used as %<asm%> output")),
72-
arg);
73-
else if (TREE_CODE (arg) == PARM_DECL)
74-
error_at (loc,
75-
READONLY_MSG (G_ ("assignment of read-only parameter %qD"),
76-
G_ ("increment of read-only parameter %qD"),
77-
G_ ("decrement of read-only parameter %qD"),
78-
G_ (
79-
"read-only parameter %qD use as %<asm%> output")),
80-
arg);
81-
else if (TREE_CODE (arg) == RESULT_DECL)
96+
97+
// Check if the constant item is mutable.
98+
if (maybe_item
99+
&& maybe_item.value ()->get_item_kind () == HIR::Item::ItemKind::Constant)
82100
{
83-
error_at (loc,
84-
READONLY_MSG (G_ ("assignment of "
85-
"read-only named return value %qD"),
86-
G_ ("increment of "
87-
"read-only named return value %qD"),
88-
G_ ("decrement of "
89-
"read-only named return value %qD"),
90-
G_ ("read-only named return value %qD "
91-
"used as %<asm%>output")),
92-
arg);
101+
auto const_item = static_cast<HIR::ConstantItem *> (*maybe_item);
102+
rust_error_at (expr.get_locus (), "assignment of read-only location '%s'",
103+
const_item->get_identifier ().as_string ().c_str ());
93104
}
94-
else if (TREE_CODE (arg) == FUNCTION_DECL)
95-
error_at (loc,
96-
READONLY_MSG (G_ ("assignment of function %qD"),
97-
G_ ("increment of function %qD"),
98-
G_ ("decrement of function %qD"),
99-
G_ ("function %qD used as %<asm%> output")),
100-
arg);
101-
else
102-
error_at (loc,
103-
READONLY_MSG (G_ ("assignment of read-only location %qE"),
104-
G_ ("increment of read-only location %qE"),
105-
G_ ("decrement of read-only location %qE"),
106-
G_ (
107-
"read-only location %qE used as %<asm%> output")),
108-
arg);
109105
}
110106

111-
static void
112-
emit_error (tree *t, tree lhs, enum lvalue_use use)
107+
void
108+
ReadonlyChecker::check_variable (IdentifierPattern *pattern,
109+
location_t assigned_loc)
113110
{
114-
readonly_error (EXPR_LOCATION (*t), lhs, use);
115-
TREE_OPERAND (*t, 0) = error_mark_node;
111+
if (!mutable_context.is_in_context ())
112+
return;
113+
114+
TyTy::BaseType *type;
115+
if (context.lookup_type (pattern->get_mappings ().get_hirid (), &type)
116+
&& is_mutable_type (type))
117+
return;
118+
if (pattern->is_mut ())
119+
return;
120+
121+
auto hir_id = pattern->get_mappings ().get_hirid ();
122+
if (already_assigned_variables.count (hir_id) > 0)
123+
rust_error_at (assigned_loc, "assignment of read-only variable '%s'",
124+
pattern->as_string ().c_str ());
125+
already_assigned_variables.insert (hir_id);
116126
}
117127

118-
static void
119-
check_modify_expr (tree *t)
128+
void
129+
ReadonlyChecker::collect_assignment_identifier (IdentifierPattern &pattern,
130+
bool has_init_expr)
120131
{
121-
tree lhs = TREE_OPERAND (*t, 0);
122-
if (TREE_CODE (lhs) == ARRAY_REF || TREE_CODE (lhs) == COMPONENT_REF)
123-
lhs = TREE_OPERAND (lhs, 0);
124-
125-
tree lhs_type = TREE_TYPE (lhs);
126-
if (TYPE_READONLY (lhs_type) || TREE_READONLY (lhs) || TREE_CONSTANT (lhs))
132+
if (has_init_expr)
127133
{
128-
if (TREE_CODE (lhs) != VAR_DECL)
129-
emit_error (t, lhs, lv_assign);
130-
else if (!DECL_ARTIFICIAL (lhs))
131-
{
132-
if (DECL_INITIAL (lhs) != NULL)
133-
emit_error (t, lhs, lv_assign);
134-
else
135-
{
136-
if (assignment_map.find (lhs) == assignment_map.end ())
137-
{
138-
assignment_map.insert ({lhs, 0});
139-
}
140-
assignment_map[lhs]++;
141-
142-
if (assignment_map[lhs] > 1)
143-
emit_error (t, lhs, lv_assign);
144-
}
145-
}
134+
HirId pattern_id = pattern.get_mappings ().get_hirid ();
135+
already_assigned_variables.insert (pattern_id);
146136
}
147137
}
148138

149-
static void
150-
check_decl (tree *t)
139+
void
140+
ReadonlyChecker::collect_assignment_tuple (TuplePattern &tuple_pattern,
141+
bool has_init_expr)
151142
{
152-
switch (TREE_CODE (*t))
143+
switch (tuple_pattern.get_items ().get_item_type ())
153144
{
154-
case MODIFY_EXPR:
155-
check_modify_expr (t);
145+
case HIR::TuplePatternItems::ItemType::NO_REST:
146+
{
147+
auto &items = static_cast<HIR::TuplePatternItemsNoRest &> (
148+
tuple_pattern.get_items ());
149+
for (auto &sub : items.get_patterns ())
150+
{
151+
collect_assignment (*sub, has_init_expr);
152+
}
153+
}
154+
break;
155+
default:
156156
break;
157+
}
158+
}
157159

160+
void
161+
ReadonlyChecker::collect_assignment (Pattern &pattern, bool has_init_expr)
162+
{
163+
switch (pattern.get_pattern_type ())
164+
{
165+
case HIR::Pattern::PatternType::IDENTIFIER:
166+
{
167+
collect_assignment_identifier (static_cast<IdentifierPattern &> (
168+
pattern),
169+
has_init_expr);
170+
}
171+
break;
172+
case HIR::Pattern::PatternType::TUPLE:
173+
{
174+
auto &tuple_pattern = static_cast<HIR::TuplePattern &> (pattern);
175+
collect_assignment_tuple (tuple_pattern, has_init_expr);
176+
}
177+
break;
158178
default:
159179
break;
160180
}
161181
}
162182

163-
static tree
164-
readonly_walk_fn (tree *t, int *, void *)
183+
void
184+
ReadonlyChecker::visit (LetStmt &stmt)
185+
{
186+
HIR::Pattern &pattern = stmt.get_pattern ();
187+
collect_assignment (pattern, stmt.has_init_expr ());
188+
}
189+
190+
void
191+
ReadonlyChecker::visit (FieldAccessExpr &expr)
165192
{
166-
check_decl (t);
167-
return NULL_TREE;
193+
if (mutable_context.is_in_context ())
194+
{
195+
expr.get_receiver_expr ().accept_vis (*this);
196+
}
168197
}
169198

170199
void
171-
ReadonlyCheck::Lint (Compile::Context &ctx)
200+
ReadonlyChecker::visit (TupleIndexExpr &expr)
172201
{
173-
assignment_map.clear ();
174-
for (auto &fndecl : ctx.get_func_decls ())
202+
if (mutable_context.is_in_context ())
175203
{
176-
for (tree p = DECL_ARGUMENTS (fndecl); p != NULL_TREE; p = DECL_CHAIN (p))
177-
{
178-
check_decl (&p);
179-
}
204+
expr.get_tuple_expr ().accept_vis (*this);
205+
}
206+
}
180207

181-
walk_tree_without_duplicates (&DECL_SAVED_TREE (fndecl),
182-
&readonly_walk_fn, &ctx);
208+
void
209+
ReadonlyChecker::visit (ArrayIndexExpr &expr)
210+
{
211+
if (mutable_context.is_in_context ())
212+
{
213+
expr.get_array_expr ().accept_vis (*this);
183214
}
215+
}
184216

185-
assignment_map.clear ();
186-
for (auto &var : ctx.get_var_decls ())
217+
void
218+
ReadonlyChecker::visit (TupleExpr &expr)
219+
{
220+
if (mutable_context.is_in_context ())
187221
{
188-
tree decl = var->get_decl ();
189-
check_decl (&decl);
222+
// TODO: Add check for tuple expression
190223
}
224+
}
191225

192-
assignment_map.clear ();
193-
for (auto &const_decl : ctx.get_const_decls ())
226+
void
227+
ReadonlyChecker::visit (LiteralExpr &expr)
228+
{
229+
if (mutable_context.is_in_context ())
194230
{
195-
check_decl (&const_decl);
231+
rust_error_at (expr.get_locus (), "assignment of read-only location");
196232
}
197233
}
198234

199-
} // namespace Analysis
235+
void
236+
ReadonlyChecker::visit (DereferenceExpr &expr)
237+
{
238+
if (!mutable_context.is_in_context ())
239+
return;
240+
TyTy::BaseType *to_deref_type;
241+
auto to_deref = expr.get_expr ().get_mappings ().get_hirid ();
242+
if (!context.lookup_type (to_deref, &to_deref_type))
243+
return;
244+
if (!is_mutable_type (to_deref_type))
245+
rust_error_at (expr.get_locus (), "assignment of read-only location");
246+
}
247+
248+
bool
249+
ReadonlyChecker::is_mutable_type (TyTy::BaseType *type)
250+
{
251+
if (type->get_kind () == TyTy::TypeKind::REF)
252+
return static_cast<TyTy::ReferenceType *> (type)->is_mutable ();
253+
if (type->get_kind () == TyTy::TypeKind::POINTER)
254+
return static_cast<TyTy::PointerType *> (type)->is_mutable ();
255+
return false;
256+
}
257+
} // namespace HIR
200258
} // namespace Rust

0 commit comments

Comments
 (0)