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/rust/resolve | |
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/rust/resolve')
-rw-r--r-- | gcc/rust/resolve/rust-ast-resolve-expr.h | 7 | ||||
-rw-r--r-- | gcc/rust/resolve/rust-ast-resolve-item.h | 15 | ||||
-rw-r--r-- | gcc/rust/resolve/rust-ast-resolve-pattern.h | 2 | ||||
-rw-r--r-- | gcc/rust/resolve/rust-ast-resolve-stmt.h | 8 | ||||
-rw-r--r-- | gcc/rust/resolve/rust-ast-resolve-toplevel.h | 1 | ||||
-rw-r--r-- | gcc/rust/resolve/rust-ast-verify-assignee.h | 83 | ||||
-rw-r--r-- | gcc/rust/resolve/rust-name-resolver.h | 36 |
7 files changed, 151 insertions, 1 deletions
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 |