diff options
Diffstat (limited to 'gcc/rust/checks/errors/rust-readonly-check.cc')
| -rw-r--r-- | gcc/rust/checks/errors/rust-readonly-check.cc | 340 |
1 files changed, 204 insertions, 136 deletions
diff --git a/gcc/rust/checks/errors/rust-readonly-check.cc b/gcc/rust/checks/errors/rust-readonly-check.cc index c128933..f484047 100644 --- a/gcc/rust/checks/errors/rust-readonly-check.cc +++ b/gcc/rust/checks/errors/rust-readonly-check.cc @@ -1,4 +1,4 @@ -// Copyright (C) 2021-2025 Free Software Foundation, Inc. +// Copyright (C) 2025 Free Software Foundation, Inc. // This file is part of GCC. @@ -17,184 +17,252 @@ // <http://www.gnu.org/licenses/>. #include "rust-readonly-check.h" -#include "rust-tree.h" -#include "rust-gcc.h" -#include "print-tree.h" +#include "rust-hir-expr.h" +#include "rust-hir-node.h" +#include "rust-hir-path.h" +#include "rust-hir-map.h" +#include "rust-hir-pattern.h" +#include "rust-mapping-common.h" +#include "rust-system.h" +#include "rust-immutable-name-resolution-context.h" +#include "rust-tyty.h" namespace Rust { -namespace Analysis { +namespace HIR { -static std::map<tree, int> assignment_map = {}; +static std::set<HirId> already_assigned_variables = {}; + +ReadonlyChecker::ReadonlyChecker () + : resolver (*Resolver::Resolver::get ()), + mappings (Analysis::Mappings::get ()), + context (*Resolver::TypeCheckContext::get ()) +{} + +void +ReadonlyChecker::go (Crate &crate) +{ + for (auto &item : crate.get_items ()) + item->accept_vis (*this); +} -// ported over from c-family/c-warn.cc void -readonly_error (location_t loc, tree arg, enum lvalue_use use) +ReadonlyChecker::visit (AssignmentExpr &expr) { - gcc_assert (use == lv_assign || use == lv_increment || use == lv_decrement - || use == lv_asm); - STRIP_ANY_LOCATION_WRAPPER (arg); - /* Using this macro rather than (for example) arrays of messages - ensures that all the format strings are checked at compile - time. */ -#define READONLY_MSG(A, I, D, AS) \ - (use == lv_assign \ - ? (A) \ - : (use == lv_increment ? (I) : (use == lv_decrement ? (D) : (AS)))) - if (TREE_CODE (arg) == COMPONENT_REF) + Expr &lhs = expr.get_lhs (); + mutable_context.enter (expr.get_mappings ().get_hirid ()); + lhs.accept_vis (*this); + mutable_context.exit (); +} + +void +ReadonlyChecker::visit (PathInExpression &expr) +{ + if (!mutable_context.is_in_context ()) + return; + + NodeId ast_node_id = expr.get_mappings ().get_nodeid (); + NodeId def_id; + + auto &nr_ctx + = Resolver2_0::ImmutableNameResolutionContext::get ().resolver (); + if (auto id = nr_ctx.lookup (ast_node_id)) + def_id = *id; + else + return; + + auto hir_id = mappings.lookup_node_to_hir (def_id); + if (!hir_id) + return; + + // Check if the local variable is mutable. + auto maybe_pattern = mappings.lookup_hir_pattern (*hir_id); + if (maybe_pattern + && maybe_pattern.value ()->get_pattern_type () + == HIR::Pattern::PatternType::IDENTIFIER) + check_variable (static_cast<IdentifierPattern *> (maybe_pattern.value ()), + expr.get_locus ()); + + // Check if the static item is mutable. + auto maybe_item = mappings.lookup_hir_item (*hir_id); + if (maybe_item + && maybe_item.value ()->get_item_kind () == HIR::Item::ItemKind::Static) { - if (TYPE_READONLY (TREE_TYPE (TREE_OPERAND (arg, 0)))) - error_at (loc, - READONLY_MSG (G_ ("assignment of member " - "%qD in read-only object"), - G_ ("increment of member " - "%qD in read-only object"), - G_ ("decrement of member " - "%qD in read-only object"), - G_ ("member %qD in read-only object " - "used as %<asm%> output")), - TREE_OPERAND (arg, 1)); - else - error_at ( - loc, - READONLY_MSG (G_ ("assignment of read-only member %qD"), - G_ ("increment of read-only member %qD"), - G_ ("decrement of read-only member %qD"), - G_ ("read-only member %qD used as %<asm%> output")), - TREE_OPERAND (arg, 1)); + auto static_item = static_cast<HIR::StaticItem *> (*maybe_item); + if (!static_item->is_mut ()) + rust_error_at (expr.get_locus (), + "assignment of read-only location '%s'", + static_item->get_identifier ().as_string ().c_str ()); } - else if (VAR_P (arg)) - error_at (loc, - READONLY_MSG (G_ ("assignment of read-only variable %qD"), - G_ ("increment of read-only variable %qD"), - G_ ("decrement of read-only variable %qD"), - G_ ( - "read-only variable %qD used as %<asm%> output")), - arg); - else if (TREE_CODE (arg) == PARM_DECL) - error_at (loc, - READONLY_MSG (G_ ("assignment of read-only parameter %qD"), - G_ ("increment of read-only parameter %qD"), - G_ ("decrement of read-only parameter %qD"), - G_ ( - "read-only parameter %qD use as %<asm%> output")), - arg); - else if (TREE_CODE (arg) == RESULT_DECL) + + // Check if the constant item is mutable. + if (maybe_item + && maybe_item.value ()->get_item_kind () == HIR::Item::ItemKind::Constant) { - error_at (loc, - READONLY_MSG (G_ ("assignment of " - "read-only named return value %qD"), - G_ ("increment of " - "read-only named return value %qD"), - G_ ("decrement of " - "read-only named return value %qD"), - G_ ("read-only named return value %qD " - "used as %<asm%>output")), - arg); + auto const_item = static_cast<HIR::ConstantItem *> (*maybe_item); + rust_error_at (expr.get_locus (), "assignment of read-only location '%s'", + const_item->get_identifier ().as_string ().c_str ()); } - else if (TREE_CODE (arg) == FUNCTION_DECL) - error_at (loc, - READONLY_MSG (G_ ("assignment of function %qD"), - G_ ("increment of function %qD"), - G_ ("decrement of function %qD"), - G_ ("function %qD used as %<asm%> output")), - arg); - else - error_at (loc, - READONLY_MSG (G_ ("assignment of read-only location %qE"), - G_ ("increment of read-only location %qE"), - G_ ("decrement of read-only location %qE"), - G_ ( - "read-only location %qE used as %<asm%> output")), - arg); } -static void -emit_error (tree *t, tree lhs, enum lvalue_use use) +void +ReadonlyChecker::check_variable (IdentifierPattern *pattern, + location_t assigned_loc) { - readonly_error (EXPR_LOCATION (*t), lhs, use); - TREE_OPERAND (*t, 0) = error_mark_node; + if (!mutable_context.is_in_context ()) + return; + + TyTy::BaseType *type; + if (context.lookup_type (pattern->get_mappings ().get_hirid (), &type) + && is_mutable_type (type)) + return; + if (pattern->is_mut ()) + return; + + auto hir_id = pattern->get_mappings ().get_hirid (); + if (already_assigned_variables.count (hir_id) > 0) + rust_error_at (assigned_loc, "assignment of read-only variable '%s'", + pattern->as_string ().c_str ()); + already_assigned_variables.insert (hir_id); } -static void -check_modify_expr (tree *t) +void +ReadonlyChecker::collect_assignment_identifier (IdentifierPattern &pattern, + bool has_init_expr) { - 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)) + if (has_init_expr) { - if (TREE_CODE (lhs) != VAR_DECL) - emit_error (t, lhs, lv_assign); - else if (!DECL_ARTIFICIAL (lhs)) - { - 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); - } - } + HirId pattern_id = pattern.get_mappings ().get_hirid (); + already_assigned_variables.insert (pattern_id); } } -static void -check_decl (tree *t) +void +ReadonlyChecker::collect_assignment_tuple (TuplePattern &tuple_pattern, + bool has_init_expr) { - switch (TREE_CODE (*t)) + switch (tuple_pattern.get_items ().get_item_type ()) { - case MODIFY_EXPR: - check_modify_expr (t); + case HIR::TuplePatternItems::ItemType::NO_REST: + { + auto &items_no_rest = static_cast<HIR::TuplePatternItemsNoRest &> ( + tuple_pattern.get_items ()); + for (auto &sub : items_no_rest.get_patterns ()) + { + collect_assignment (*sub, has_init_expr); + } + } + break; + case HIR::TuplePatternItems::ItemType::HAS_REST: + { + auto &items_has_rest = static_cast<HIR::TuplePatternItemsHasRest &> ( + tuple_pattern.get_items ()); + for (auto &sub : items_has_rest.get_lower_patterns ()) + collect_assignment (*sub, has_init_expr); + for (auto &sub : items_has_rest.get_upper_patterns ()) + collect_assignment (*sub, has_init_expr); + } + break; + default: break; + } +} +void +ReadonlyChecker::collect_assignment (Pattern &pattern, bool has_init_expr) +{ + switch (pattern.get_pattern_type ()) + { + case HIR::Pattern::PatternType::IDENTIFIER: + { + collect_assignment_identifier (static_cast<IdentifierPattern &> ( + pattern), + has_init_expr); + } + break; + case HIR::Pattern::PatternType::TUPLE: + { + auto &tuple_pattern = static_cast<HIR::TuplePattern &> (pattern); + collect_assignment_tuple (tuple_pattern, has_init_expr); + } + break; default: break; } } -static tree -readonly_walk_fn (tree *t, int *, void *) +void +ReadonlyChecker::visit (LetStmt &stmt) +{ + HIR::Pattern &pattern = stmt.get_pattern (); + collect_assignment (pattern, stmt.has_init_expr ()); +} + +void +ReadonlyChecker::visit (FieldAccessExpr &expr) { - check_decl (t); - return NULL_TREE; + if (mutable_context.is_in_context ()) + { + expr.get_receiver_expr ().accept_vis (*this); + } } void -ReadonlyCheck::Lint (Compile::Context &ctx) +ReadonlyChecker::visit (TupleIndexExpr &expr) { - assignment_map.clear (); - for (auto &fndecl : ctx.get_func_decls ()) + if (mutable_context.is_in_context ()) { - for (tree p = DECL_ARGUMENTS (fndecl); p != NULL_TREE; p = DECL_CHAIN (p)) - { - check_decl (&p); - } + expr.get_tuple_expr ().accept_vis (*this); + } +} - walk_tree_without_duplicates (&DECL_SAVED_TREE (fndecl), - &readonly_walk_fn, &ctx); +void +ReadonlyChecker::visit (ArrayIndexExpr &expr) +{ + if (mutable_context.is_in_context ()) + { + expr.get_array_expr ().accept_vis (*this); } +} - assignment_map.clear (); - for (auto &var : ctx.get_var_decls ()) +void +ReadonlyChecker::visit (TupleExpr &expr) +{ + if (mutable_context.is_in_context ()) { - tree decl = var->get_decl (); - check_decl (&decl); + // TODO: Add check for tuple expression } +} - assignment_map.clear (); - for (auto &const_decl : ctx.get_const_decls ()) +void +ReadonlyChecker::visit (LiteralExpr &expr) +{ + if (mutable_context.is_in_context ()) { - check_decl (&const_decl); + rust_error_at (expr.get_locus (), "assignment of read-only location"); } } -} // namespace Analysis +void +ReadonlyChecker::visit (DereferenceExpr &expr) +{ + if (!mutable_context.is_in_context ()) + return; + TyTy::BaseType *to_deref_type; + auto to_deref = expr.get_expr ().get_mappings ().get_hirid (); + if (!context.lookup_type (to_deref, &to_deref_type)) + return; + if (!is_mutable_type (to_deref_type)) + rust_error_at (expr.get_locus (), "assignment of read-only location"); +} + +bool +ReadonlyChecker::is_mutable_type (TyTy::BaseType *type) +{ + if (type->get_kind () == TyTy::TypeKind::REF) + return static_cast<TyTy::ReferenceType *> (type)->is_mutable (); + if (type->get_kind () == TyTy::TypeKind::POINTER) + return static_cast<TyTy::PointerType *> (type)->is_mutable (); + return false; +} +} // namespace HIR } // namespace Rust |
