Skip to content

Commit 1fb6775

Browse files
committed
gccrs: improve mutability checks
This ensures that we handle var decls readonly checks much better Addresses: #807 #3287 gcc/rust/ChangeLog: * checks/errors/rust-readonly-check.cc (check_decl): improve mut check (emit_error): helper (check_modify_expr): likewise (readonly_walk_fn): reuse helper (ReadonlyCheck::Lint): cleanup context each run gcc/testsuite/ChangeLog: * rust/execute/torture/builtin_macro_include_bytes.rs: needs mut * rust/compile/mutability_checks1.rs: New test. Signed-off-by: Philip Herron <[email protected]>
1 parent daa2977 commit 1fb6775

File tree

3 files changed

+61
-10
lines changed

3 files changed

+61
-10
lines changed

Diff for: gcc/rust/checks/errors/rust-readonly-check.cc

+45-9
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,13 @@
1919
#include "rust-readonly-check.h"
2020
#include "rust-tree.h"
2121
#include "rust-gcc.h"
22+
#include "print-tree.h"
2223

2324
namespace Rust {
2425
namespace Analysis {
2526

27+
static std::map<tree, int> assignment_map = {};
28+
2629
// ported over from c-family/c-warn.cc
2730
void
2831
readonly_error (location_t loc, tree arg, enum lvalue_use use)
@@ -106,37 +109,68 @@ readonly_error (location_t loc, tree arg, enum lvalue_use use)
106109
}
107110

108111
static void
109-
check_decl (tree *t)
112+
emit_error (tree *t, tree lhs, enum lvalue_use use)
110113
{
111-
if (TREE_CODE (*t) == MODIFY_EXPR)
114+
readonly_error (EXPR_LOCATION (*t), lhs, use);
115+
TREE_OPERAND (*t, 0) = error_mark_node;
116+
}
117+
118+
static void
119+
check_modify_expr (tree *t)
120+
{
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))
112127
{
113-
tree lhs = TREE_OPERAND (*t, 0);
114-
if (TREE_READONLY (lhs) || TREE_CONSTANT (lhs))
128+
if (TREE_CODE (lhs) != VAR_DECL)
129+
emit_error (t, lhs, lv_assign);
130+
else if (!DECL_ARTIFICIAL (lhs))
115131
{
116-
readonly_error (EXPR_LOCATION (*t), lhs, lv_assign);
117-
TREE_OPERAND (*t, 0) = error_mark_node;
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+
}
118145
}
119146
}
120147
}
121148

122-
static tree
123-
readonly_walk_fn (tree *t, int *, void *)
149+
static void
150+
check_decl (tree *t)
124151
{
125152
switch (TREE_CODE (*t))
126153
{
127154
case MODIFY_EXPR:
128-
check_decl (t);
155+
check_modify_expr (t);
129156
break;
130157

131158
default:
132159
break;
133160
}
161+
}
162+
163+
static tree
164+
readonly_walk_fn (tree *t, int *, void *)
165+
{
166+
check_decl (t);
134167
return NULL_TREE;
135168
}
136169

137170
void
138171
ReadonlyCheck::Lint (Compile::Context &ctx)
139172
{
173+
assignment_map.clear ();
140174
for (auto &fndecl : ctx.get_func_decls ())
141175
{
142176
for (tree p = DECL_ARGUMENTS (fndecl); p != NULL_TREE; p = DECL_CHAIN (p))
@@ -148,12 +182,14 @@ ReadonlyCheck::Lint (Compile::Context &ctx)
148182
&readonly_walk_fn, &ctx);
149183
}
150184

185+
assignment_map.clear ();
151186
for (auto &var : ctx.get_var_decls ())
152187
{
153188
tree decl = var->get_decl ();
154189
check_decl (&decl);
155190
}
156191

192+
assignment_map.clear ();
157193
for (auto &const_decl : ctx.get_const_decls ())
158194
{
159195
check_decl (&const_decl);

Diff for: gcc/testsuite/rust/compile/mutability_checks1.rs

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
pub fn test() {
2+
let a;
3+
a = 1;
4+
a = 2 + 1;
5+
// { dg-error "assignment of read-only variable" "" { target *-*-* } .-1 }
6+
7+
struct Foo(i32);
8+
let a = Foo(1);
9+
a.0 = 2;
10+
// { dg-error "assignment of read-only variable" "" { target *-*-* } .-1 }
11+
12+
let a = [1, 2, 3, 4];
13+
a[0] = 1 + 2;
14+
// { dg-error "assignment of read-only variable" "" { target *-*-* } .-1 }
15+
}

Diff for: gcc/testsuite/rust/execute/torture/builtin_macro_include_bytes.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ fn print_int(value: i32) {
2525
fn check_bytes(bytes: &[u8; 16]) {
2626
let the_bytes = b"hello, include!\n";
2727

28-
let x = true;
28+
let mut x = true;
2929
let mut i = 0;
3030

3131
// X is true iff bytes == the_bytes

0 commit comments

Comments
 (0)