From: Philip Herron <herron.phi...@googlemail.com>

This ensures that we handle var decls readonly checks much better

Addresses: Rust-GCC#807 Rust-GCC#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 <herron.phi...@googlemail.com>
---
 gcc/rust/checks/errors/rust-readonly-check.cc | 54 +++++++++++++++----
 .../rust/compile/mutability_checks1.rs        | 15 ++++++
 .../torture/builtin_macro_include_bytes.rs    |  2 +-
 3 files changed, 61 insertions(+), 10 deletions(-)
 create mode 100644 gcc/testsuite/rust/compile/mutability_checks1.rs

diff --git a/gcc/rust/checks/errors/rust-readonly-check.cc 
b/gcc/rust/checks/errors/rust-readonly-check.cc
index b8998985d89..c1289332116 100644
--- a/gcc/rust/checks/errors/rust-readonly-check.cc
+++ b/gcc/rust/checks/errors/rust-readonly-check.cc
@@ -19,10 +19,13 @@
 #include "rust-readonly-check.h"
 #include "rust-tree.h"
 #include "rust-gcc.h"
+#include "print-tree.h"
 
 namespace Rust {
 namespace Analysis {
 
+static std::map<tree, int> assignment_map = {};
+
 // ported over from c-family/c-warn.cc
 void
 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)
 }
 
 static void
-check_decl (tree *t)
+emit_error (tree *t, tree lhs, enum lvalue_use use)
 {
-  if (TREE_CODE (*t) == MODIFY_EXPR)
+  readonly_error (EXPR_LOCATION (*t), lhs, use);
+  TREE_OPERAND (*t, 0) = error_mark_node;
+}
+
+static void
+check_modify_expr (tree *t)
+{
+  tree lhs = TREE_OPERAND (*t, 0);
+  if (TREE_CODE (lhs) == ARRAY_REF || TREE_CODE (lhs) == COMPONENT_REF)
+    lhs = TREE_OPERAND (lhs, 0);
+
+  tree lhs_type = TREE_TYPE (lhs);
+  if (TYPE_READONLY (lhs_type) || TREE_READONLY (lhs) || TREE_CONSTANT (lhs))
     {
-      tree lhs = TREE_OPERAND (*t, 0);
-      if (TREE_READONLY (lhs) || TREE_CONSTANT (lhs))
+      if (TREE_CODE (lhs) != VAR_DECL)
+       emit_error (t, lhs, lv_assign);
+      else if (!DECL_ARTIFICIAL (lhs))
        {
-         readonly_error (EXPR_LOCATION (*t), lhs, lv_assign);
-         TREE_OPERAND (*t, 0) = error_mark_node;
+         if (DECL_INITIAL (lhs) != NULL)
+           emit_error (t, lhs, lv_assign);
+         else
+           {
+             if (assignment_map.find (lhs) == assignment_map.end ())
+               {
+                 assignment_map.insert ({lhs, 0});
+               }
+             assignment_map[lhs]++;
+
+             if (assignment_map[lhs] > 1)
+               emit_error (t, lhs, lv_assign);
+           }
        }
     }
 }
 
-static tree
-readonly_walk_fn (tree *t, int *, void *)
+static void
+check_decl (tree *t)
 {
   switch (TREE_CODE (*t))
     {
     case MODIFY_EXPR:
-      check_decl (t);
+      check_modify_expr (t);
       break;
 
     default:
       break;
     }
+}
+
+static tree
+readonly_walk_fn (tree *t, int *, void *)
+{
+  check_decl (t);
   return NULL_TREE;
 }
 
 void
 ReadonlyCheck::Lint (Compile::Context &ctx)
 {
+  assignment_map.clear ();
   for (auto &fndecl : ctx.get_func_decls ())
     {
       for (tree p = DECL_ARGUMENTS (fndecl); p != NULL_TREE; p = DECL_CHAIN 
(p))
@@ -148,12 +182,14 @@ ReadonlyCheck::Lint (Compile::Context &ctx)
                                    &readonly_walk_fn, &ctx);
     }
 
+  assignment_map.clear ();
   for (auto &var : ctx.get_var_decls ())
     {
       tree decl = var->get_decl ();
       check_decl (&decl);
     }
 
+  assignment_map.clear ();
   for (auto &const_decl : ctx.get_const_decls ())
     {
       check_decl (&const_decl);
diff --git a/gcc/testsuite/rust/compile/mutability_checks1.rs 
b/gcc/testsuite/rust/compile/mutability_checks1.rs
new file mode 100644
index 00000000000..4affae03053
--- /dev/null
+++ b/gcc/testsuite/rust/compile/mutability_checks1.rs
@@ -0,0 +1,15 @@
+pub fn test() {
+    let a;
+    a = 1;
+    a = 2 + 1;
+    // { dg-error "assignment of read-only variable" "" { target *-*-* } .-1 }
+
+    struct Foo(i32);
+    let a = Foo(1);
+    a.0 = 2;
+    // { dg-error "assignment of read-only variable" "" { target *-*-* } .-1 }
+
+    let a = [1, 2, 3, 4];
+    a[0] = 1 + 2;
+    // { dg-error "assignment of read-only variable" "" { target *-*-* } .-1 }
+}
diff --git a/gcc/testsuite/rust/execute/torture/builtin_macro_include_bytes.rs 
b/gcc/testsuite/rust/execute/torture/builtin_macro_include_bytes.rs
index 6aec417e94f..c8a2daeccd9 100644
--- a/gcc/testsuite/rust/execute/torture/builtin_macro_include_bytes.rs
+++ b/gcc/testsuite/rust/execute/torture/builtin_macro_include_bytes.rs
@@ -25,7 +25,7 @@ fn print_int(value: i32) {
 fn check_bytes(bytes: &[u8; 16]) {
     let the_bytes = b"hello, include!\n";
 
-    let x = true;
+    let mut x = true;
     let mut i = 0;
 
     // X is true iff bytes == the_bytes
-- 
2.45.2

Reply via email to