aboutsummaryrefslogtreecommitdiff
path: root/gcc
diff options
context:
space:
mode:
authorPhilip Herron <philip.herron@embecosm.com>2021-01-25 17:03:36 +0000
committerPhilip Herron <herron.philip@googlemail.com>2021-01-27 10:42:25 +0000
commit8578c61be5061fab91fe679a15fd68ab5fad987c (patch)
treef09524d5f8bf17fab00618ef7e8e98fd905a557e /gcc
parent854aad3b58e747cad3e46b522c9ef765bdfadca4 (diff)
downloadgcc-8578c61be5061fab91fe679a15fd68ab5fad987c.zip
gcc-8578c61be5061fab91fe679a15fd68ab5fad987c.tar.gz
gcc-8578c61be5061fab91fe679a15fd68ab5fad987c.tar.bz2
Add mutablity checks and left hand size assignee checker
In order to assign to a name we must ensure the LHS is a valid expression to assign to. This leads onto actually checking if this is a mutable declaration or not. Once these checks pass the name resolver we can in GIMPLE create immutable types for these declarations to help with optimization. Fixes #77
Diffstat (limited to 'gcc')
-rw-r--r--gcc/rust/ast/rust-ast.h2
-rw-r--r--gcc/rust/backend/rust-compile-fnparam.h67
-rw-r--r--gcc/rust/backend/rust-compile-item.h18
-rw-r--r--gcc/rust/backend/rust-compile-var-decl.h3
-rw-r--r--gcc/rust/resolve/rust-ast-resolve-expr.h7
-rw-r--r--gcc/rust/resolve/rust-ast-resolve-item.h15
-rw-r--r--gcc/rust/resolve/rust-ast-resolve-pattern.h2
-rw-r--r--gcc/rust/resolve/rust-ast-resolve-stmt.h8
-rw-r--r--gcc/rust/resolve/rust-ast-resolve-toplevel.h1
-rw-r--r--gcc/rust/resolve/rust-ast-verify-assignee.h83
-rw-r--r--gcc/rust/resolve/rust-name-resolver.h36
-rw-r--r--gcc/testsuite/rust.test/compilable/arrays_index2.rs4
-rw-r--r--gcc/testsuite/rust.test/compilable/scoping1.rs2
-rw-r--r--gcc/testsuite/rust.test/compilable/tuple_field_access.rs6
-rw-r--r--gcc/testsuite/rust.test/fail_compilation/immutable1.rs5
-rw-r--r--gcc/testsuite/rust.test/fail_compilation/immutable2.rs5
-rw-r--r--gcc/testsuite/rust.test/fail_compilation/immutable3.rs4
-rw-r--r--gcc/testsuite/rust.test/fail_compilation/immutable4.rs4
-rw-r--r--gcc/testsuite/rust.test/fail_compilation/immutable5.rs6
19 files changed, 268 insertions, 10 deletions
diff --git a/gcc/rust/ast/rust-ast.h b/gcc/rust/ast/rust-ast.h
index 34122bb..7b325830 100644
--- a/gcc/rust/ast/rust-ast.h
+++ b/gcc/rust/ast/rust-ast.h
@@ -962,7 +962,7 @@ public:
std::string as_string () const override { return ident; }
Location get_locus () const { return locus; }
- Location get_locus_slow () const override { return get_locus (); }
+ Location get_locus_slow () const override { return locus; }
Identifier get_ident () const { return ident; }
diff --git a/gcc/rust/backend/rust-compile-fnparam.h b/gcc/rust/backend/rust-compile-fnparam.h
new file mode 100644
index 0000000..cf6e6f7
--- /dev/null
+++ b/gcc/rust/backend/rust-compile-fnparam.h
@@ -0,0 +1,67 @@
+// Copyright (C) 2020 Free Software Foundation, Inc.
+
+// This file is part of GCC.
+
+// GCC is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3, or (at your option) any later
+// version.
+
+// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+// for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with GCC; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+#ifndef RUST_COMPILE_FNPARAM
+#define RUST_COMPILE_FNPARAM
+
+#include "rust-compile-base.h"
+
+namespace Rust {
+namespace Compile {
+
+class CompileFnParam : public HIRCompileBase
+{
+public:
+ static Bvariable *compile (Context *ctx, Bfunction *fndecl,
+ HIR::FunctionParam *param, Btype *decl_type,
+ Location locus)
+ {
+ CompileFnParam compiler (ctx, fndecl, decl_type, locus);
+ param->get_param_name ()->accept_vis (compiler);
+ return compiler.translated;
+ }
+
+ void visit (HIR::IdentifierPattern &pattern)
+ {
+ if (!pattern.is_mut)
+ decl_type = ctx->get_backend ()->immutable_type (decl_type);
+
+ translated
+ = ctx->get_backend ()->parameter_variable (fndecl, pattern.variable_ident,
+ decl_type,
+ false /* address_taken */,
+ locus);
+ }
+
+private:
+ CompileFnParam (Context *ctx, ::Bfunction *fndecl, ::Btype *decl_type,
+ Location locus)
+ : HIRCompileBase (ctx), fndecl (fndecl), decl_type (decl_type),
+ locus (locus), translated (nullptr)
+ {}
+
+ ::Bfunction *fndecl;
+ ::Btype *decl_type;
+ Location locus;
+ ::Bvariable *translated;
+};
+
+} // namespace Compile
+} // namespace Rust
+
+#endif // RUST_COMPILE_FNPARAM
diff --git a/gcc/rust/backend/rust-compile-item.h b/gcc/rust/backend/rust-compile-item.h
index bf899d8..1bb7c91 100644
--- a/gcc/rust/backend/rust-compile-item.h
+++ b/gcc/rust/backend/rust-compile-item.h
@@ -24,6 +24,7 @@
#include "rust-compile-var-decl.h"
#include "rust-compile-stmt.h"
#include "rust-compile-expr.h"
+#include "rust-compile-fnparam.h"
namespace Rust {
namespace Compile {
@@ -174,17 +175,20 @@ public:
for (auto &it : fntype->get_params ())
{
HIR::FunctionParam &referenced_param = function.function_params.at (i);
- auto param_pattern = it.first;
auto param_tyty = it.second;
-
auto compiled_param_type
= TyTyResolveCompile::compile (ctx, param_tyty);
- bool tree_addressable = false;
- auto compiled_param_var = ctx->get_backend ()->parameter_variable (
- fndecl, param_pattern->as_string (), compiled_param_type,
- tree_addressable,
- ctx->get_mappings ()->lookup_location (param_tyty->get_ref ()));
+ Location param_locus
+ = ctx->get_mappings ()->lookup_location (param_tyty->get_ref ());
+ Bvariable *compiled_param_var
+ = CompileFnParam::compile (ctx, fndecl, &referenced_param,
+ compiled_param_type, param_locus);
+ if (compiled_param_var == nullptr)
+ {
+ rust_error_at (param_locus, "failed to compile parameter variable");
+ return;
+ }
param_vars.push_back (compiled_param_var);
diff --git a/gcc/rust/backend/rust-compile-var-decl.h b/gcc/rust/backend/rust-compile-var-decl.h
index cf73820..06ea5a9 100644
--- a/gcc/rust/backend/rust-compile-var-decl.h
+++ b/gcc/rust/backend/rust-compile-var-decl.h
@@ -54,6 +54,9 @@ public:
void visit (HIR::IdentifierPattern &pattern)
{
+ if (!pattern.is_mut)
+ translated_type = ctx->get_backend ()->immutable_type (translated_type);
+
translated
= ctx->get_backend ()->local_variable (fndecl, pattern.variable_ident,
translated_type, NULL /*decl_var*/,
diff --git a/gcc/rust/resolve/rust-ast-resolve-expr.h b/gcc/rust/resolve/rust-ast-resolve-expr.h
index 229b26a..9366818 100644
--- a/gcc/rust/resolve/rust-ast-resolve-expr.h
+++ b/gcc/rust/resolve/rust-ast-resolve-expr.h
@@ -22,6 +22,7 @@
#include "rust-ast-resolve-base.h"
#include "rust-ast-full.h"
#include "rust-ast-resolve-struct-expr-field.h"
+#include "rust-ast-verify-assignee.h"
namespace Rust {
namespace Resolver {
@@ -94,6 +95,9 @@ public:
{
ResolveExpr::go (expr.get_left_expr ().get (), expr.get_node_id ());
ResolveExpr::go (expr.get_right_expr ().get (), expr.get_node_id ());
+
+ // need to verify the assignee
+ VerifyAsignee::go (expr.get_left_expr ().get (), expr.get_node_id ());
}
void visit (AST::IdentifierExpr &expr)
@@ -120,6 +124,9 @@ public:
{
ResolveExpr::go (expr.get_left_expr ().get (), expr.get_node_id ());
ResolveExpr::go (expr.get_right_expr ().get (), expr.get_node_id ());
+
+ // need to verify the assignee
+ VerifyAsignee::go (expr.get_left_expr ().get (), expr.get_node_id ());
}
void visit (AST::ComparisonExpr &expr)
diff --git a/gcc/rust/resolve/rust-ast-resolve-item.h b/gcc/rust/resolve/rust-ast-resolve-item.h
index 02d6dfa..0f45ba0 100644
--- a/gcc/rust/resolve/rust-ast-resolve-item.h
+++ b/gcc/rust/resolve/rust-ast-resolve-item.h
@@ -62,12 +62,22 @@ public:
{
ResolveType::go (var.get_type ().get (), var.get_node_id ());
ResolveExpr::go (var.get_expr ().get (), var.get_node_id ());
+
+ // the mutability checker needs to verify for immutable decls the number
+ // of assignments are <1. This marks an implicit assignment
+ resolver->mark_assignment_to_decl (var.get_node_id (), var.get_node_id ());
}
void visit (AST::ConstantItem &constant)
{
ResolveType::go (constant.get_type ().get (), constant.get_node_id ());
ResolveExpr::go (constant.get_expr ().get (), constant.get_node_id ());
+
+ // the mutability checker needs to verify for immutable decls the number
+ // of assignments are <1. This marks an implicit assignment
+ resolver->mark_decl_mutability (constant.get_node_id (), false);
+ resolver->mark_assignment_to_decl (constant.get_node_id (),
+ constant.get_node_id ());
}
void visit (AST::Function &function)
@@ -89,6 +99,11 @@ public:
ResolveType::go (param.get_type ().get (), param.get_node_id ());
PatternDeclaration::go (param.get_pattern ().get (),
param.get_node_id ());
+
+ // the mutability checker needs to verify for immutable decls the number
+ // of assignments are <1. This marks an implicit assignment
+ resolver->mark_assignment_to_decl (param.get_pattern ()->get_node_id (),
+ param.get_node_id ());
}
// resolve the function body
diff --git a/gcc/rust/resolve/rust-ast-resolve-pattern.h b/gcc/rust/resolve/rust-ast-resolve-pattern.h
index c79f7d3..f7618ab 100644
--- a/gcc/rust/resolve/rust-ast-resolve-pattern.h
+++ b/gcc/rust/resolve/rust-ast-resolve-pattern.h
@@ -86,6 +86,8 @@ public:
resolver->insert_new_definition (pattern.get_node_id (),
Definition{pattern.get_node_id (),
parent});
+ resolver->mark_decl_mutability (pattern.get_node_id (),
+ pattern.get_is_mut ());
}
private:
diff --git a/gcc/rust/resolve/rust-ast-resolve-stmt.h b/gcc/rust/resolve/rust-ast-resolve-stmt.h
index 6d751e6..8904ce9 100644
--- a/gcc/rust/resolve/rust-ast-resolve-stmt.h
+++ b/gcc/rust/resolve/rust-ast-resolve-stmt.h
@@ -52,7 +52,13 @@ public:
void visit (AST::LetStmt &stmt)
{
if (stmt.has_init_expr ())
- ResolveExpr::go (stmt.get_init_expr ().get (), stmt.get_node_id ());
+ {
+ ResolveExpr::go (stmt.get_init_expr ().get (), stmt.get_node_id ());
+
+ // mark the assignment
+ resolver->mark_assignment_to_decl (stmt.get_pattern ()->get_node_id (),
+ stmt.get_node_id ());
+ }
PatternDeclaration::go (stmt.get_pattern ().get (), stmt.get_node_id ());
if (stmt.has_type ())
diff --git a/gcc/rust/resolve/rust-ast-resolve-toplevel.h b/gcc/rust/resolve/rust-ast-resolve-toplevel.h
index 47435a7..90f9cd6 100644
--- a/gcc/rust/resolve/rust-ast-resolve-toplevel.h
+++ b/gcc/rust/resolve/rust-ast-resolve-toplevel.h
@@ -57,6 +57,7 @@ public:
resolver->insert_new_definition (var.get_node_id (),
Definition{var.get_node_id (),
var.get_node_id ()});
+ resolver->mark_decl_mutability (var.get_node_id (), var.is_mutable ());
}
void visit (AST::ConstantItem &constant)
diff --git a/gcc/rust/resolve/rust-ast-verify-assignee.h b/gcc/rust/resolve/rust-ast-verify-assignee.h
new file mode 100644
index 0000000..6cfe2c2
--- /dev/null
+++ b/gcc/rust/resolve/rust-ast-verify-assignee.h
@@ -0,0 +1,83 @@
+// Copyright (C) 2020 Free Software Foundation, Inc.
+
+// This file is part of GCC.
+
+// GCC is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3, or (at your option) any later
+// version.
+
+// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+// for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with GCC; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+#ifndef RUST_AST_VERIFY_ASSIGNEE
+#define RUST_AST_VERIFY_ASSIGNEE
+
+#include "rust-ast-resolve-base.h"
+#include "rust-ast-full.h"
+
+namespace Rust {
+namespace Resolver {
+
+class VerifyAsignee : public ResolverBase
+{
+public:
+ static bool go (AST::Expr *assignee, NodeId parent)
+ {
+ VerifyAsignee checker (parent);
+ assignee->accept_vis (checker);
+ if (!checker.ok)
+ rust_error_at (assignee->get_locus_slow (),
+ "invalid left-hand side of assignment");
+ return checker.ok;
+ }
+
+ void visit (AST::ArrayIndexExpr &expr)
+ {
+ expr.get_array_expr ()->accept_vis (*this);
+ }
+
+ void visit (AST::FieldAccessExpr &expr)
+ {
+ expr.get_receiver_expr ()->accept_vis (*this);
+ }
+
+ void visit (AST::TupleIndexExpr &expr)
+ {
+ expr.get_tuple_expr ()->accept_vis (*this);
+ }
+
+ void visit (AST::IdentifierExpr &expr)
+ {
+ if (!resolver->get_name_scope ().lookup (expr.as_string (), &resolved_node))
+ return;
+
+ ok = true;
+ // mark the assignment to the name
+ resolver->mark_assignment_to_decl (resolved_node, parent);
+
+ // check is mutable
+ if (!resolver->decl_is_mutable (resolved_node))
+ {
+ // we only allow a single assignment to immutable decls
+ if (resolver->get_num_assignments_to_decl (resolved_node) > 1)
+ rust_error_at (expr.get_locus (), "cannot assign to immutable");
+ }
+ }
+
+private:
+ VerifyAsignee (NodeId parent) : ResolverBase (parent), ok (false) {}
+
+ bool ok;
+};
+
+} // namespace Resolver
+} // namespace Rust
+
+#endif // RUST_AST_VERIFY_ASSIGNEE
diff --git a/gcc/rust/resolve/rust-name-resolver.h b/gcc/rust/resolve/rust-name-resolver.h
index 4d98e7f..5bc6aba 100644
--- a/gcc/rust/resolve/rust-name-resolver.h
+++ b/gcc/rust/resolve/rust-name-resolver.h
@@ -235,6 +235,37 @@ public:
void set_unit_type_node_id (NodeId id) { unit_ty_node_id = id; }
NodeId get_unit_type_node_id () { return unit_ty_node_id; }
+ void mark_decl_mutability (NodeId id, bool mut)
+ {
+ rust_assert (decl_mutability.find (id) == decl_mutability.end ());
+ decl_mutability[id] = mut;
+ }
+
+ bool decl_is_mutable (NodeId id) const
+ {
+ auto it = decl_mutability.find (id);
+ rust_assert (it != decl_mutability.end ());
+ return it->second;
+ }
+
+ void mark_assignment_to_decl (NodeId id, NodeId assignment)
+ {
+ auto it = assignment_to_decl.find (id);
+ if (it == assignment_to_decl.end ())
+ assignment_to_decl[id] = {};
+
+ assignment_to_decl[id].insert (assignment);
+ }
+
+ size_t get_num_assignments_to_decl (NodeId id) const
+ {
+ auto it = assignment_to_decl.find (id);
+ if (it == assignment_to_decl.end ())
+ return 0;
+
+ return it->second.size ();
+ }
+
private:
Resolver ();
@@ -269,6 +300,11 @@ private:
// we need two namespaces one for names and ones for types
std::map<NodeId, NodeId> resolved_names;
std::map<NodeId, NodeId> resolved_types;
+
+ // map of resolved names mutability flag
+ std::map<NodeId, bool> decl_mutability;
+ // map of resolved names and set of assignments to the decl
+ std::map<NodeId, std::set<NodeId> > assignment_to_decl;
};
} // namespace Resolver
diff --git a/gcc/testsuite/rust.test/compilable/arrays_index2.rs b/gcc/testsuite/rust.test/compilable/arrays_index2.rs
new file mode 100644
index 0000000..f9bee77
--- /dev/null
+++ b/gcc/testsuite/rust.test/compilable/arrays_index2.rs
@@ -0,0 +1,4 @@
+fn main() {
+ let mut array: [i32; 3] = [0; 3];
+ array[0] = 1;
+}
diff --git a/gcc/testsuite/rust.test/compilable/scoping1.rs b/gcc/testsuite/rust.test/compilable/scoping1.rs
index 02b5f93..74af41e 100644
--- a/gcc/testsuite/rust.test/compilable/scoping1.rs
+++ b/gcc/testsuite/rust.test/compilable/scoping1.rs
@@ -1,7 +1,7 @@
fn main() {
let x = 1;
{
- let x = true;
+ let mut x = true;
{
x = false;
}
diff --git a/gcc/testsuite/rust.test/compilable/tuple_field_access.rs b/gcc/testsuite/rust.test/compilable/tuple_field_access.rs
new file mode 100644
index 0000000..8d1bbe9
--- /dev/null
+++ b/gcc/testsuite/rust.test/compilable/tuple_field_access.rs
@@ -0,0 +1,6 @@
+struct Foo(i32, i32);
+
+fn main() {
+ let mut a = Foo(1, 2);
+ a.0 = 22;
+}
diff --git a/gcc/testsuite/rust.test/fail_compilation/immutable1.rs b/gcc/testsuite/rust.test/fail_compilation/immutable1.rs
new file mode 100644
index 0000000..5713d3b1
--- /dev/null
+++ b/gcc/testsuite/rust.test/fail_compilation/immutable1.rs
@@ -0,0 +1,5 @@
+static x: i32 = 3;
+
+fn main() {
+ x = 1;
+}
diff --git a/gcc/testsuite/rust.test/fail_compilation/immutable2.rs b/gcc/testsuite/rust.test/fail_compilation/immutable2.rs
new file mode 100644
index 0000000..e316e72
--- /dev/null
+++ b/gcc/testsuite/rust.test/fail_compilation/immutable2.rs
@@ -0,0 +1,5 @@
+const TEST_CONST: i32 = 10;
+
+fn main() {
+ TEST_CONST = 1;
+}
diff --git a/gcc/testsuite/rust.test/fail_compilation/immutable3.rs b/gcc/testsuite/rust.test/fail_compilation/immutable3.rs
new file mode 100644
index 0000000..b310193
--- /dev/null
+++ b/gcc/testsuite/rust.test/fail_compilation/immutable3.rs
@@ -0,0 +1,4 @@
+fn main() {
+ let a = 1;
+ a += 2;
+}
diff --git a/gcc/testsuite/rust.test/fail_compilation/immutable4.rs b/gcc/testsuite/rust.test/fail_compilation/immutable4.rs
new file mode 100644
index 0000000..d2e740f
--- /dev/null
+++ b/gcc/testsuite/rust.test/fail_compilation/immutable4.rs
@@ -0,0 +1,4 @@
+fn main() {
+ let array: [i32; 3] = [0; 3];
+ array[0] = 1;
+}
diff --git a/gcc/testsuite/rust.test/fail_compilation/immutable5.rs b/gcc/testsuite/rust.test/fail_compilation/immutable5.rs
new file mode 100644
index 0000000..c442ba5
--- /dev/null
+++ b/gcc/testsuite/rust.test/fail_compilation/immutable5.rs
@@ -0,0 +1,6 @@
+struct Foo(f32, i32);
+
+fn main() {
+ let a = Foo(1, 2);
+ a.0 = 22;
+}