diff options
author | Philip Herron <philip.herron@embecosm.com> | 2021-01-25 17:03:36 +0000 |
---|---|---|
committer | Philip Herron <herron.philip@googlemail.com> | 2021-01-27 10:42:25 +0000 |
commit | 8578c61be5061fab91fe679a15fd68ab5fad987c (patch) | |
tree | f09524d5f8bf17fab00618ef7e8e98fd905a557e /gcc | |
parent | 854aad3b58e747cad3e46b522c9ef765bdfadca4 (diff) | |
download | gcc-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')
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; +} |