From patchwork Wed Aug 24 11:59:42 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: herron.philip@googlemail.com X-Patchwork-Id: 56997 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 8A67E383163B for ; Wed, 24 Aug 2022 12:06:00 +0000 (GMT) X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from mail-wr1-x42e.google.com (mail-wr1-x42e.google.com [IPv6:2a00:1450:4864:20::42e]) by sourceware.org (Postfix) with ESMTPS id 2C01F38983A5; Wed, 24 Aug 2022 12:01:05 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org 2C01F38983A5 Authentication-Results: sourceware.org; dmarc=pass (p=quarantine dis=none) header.from=googlemail.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=googlemail.com Received: by mail-wr1-x42e.google.com with SMTP id h5so19678471wru.7; Wed, 24 Aug 2022 05:01:05 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=googlemail.com; s=20210112; h=content-transfer-encoding:mime-version:reply-to:references :in-reply-to:message-id:date:subject:cc:to:from:from:to:cc; bh=cSpqYU1D9ItlltcLfcZWUikLm3ec3eLEHuEHj/0XdMM=; b=hzK1OSM9eOHsalI+eblxgXxW7rjOtnaXeyn2cuZapGYrr2j0CG7aRnVEfsvnehAtKu uL/kzRA1Za301yo8YtOctgMxiVUlTWacY3sLlpCk11apWq8UeNHs2w0ow8sLx+Rb5lJo Wsfcv7XLttFQnYE/IgEc2mfXJRviRNUtdm74Makqm3UJVxmR4XDZxxtGnee2mNAu4Zsf YpvF8Nbhf56bkyD9ft8p14uSyEkzQAYzDb7gvXWhJLEjK+LQ4IRcCn46+JKH8CYKXjhN rHF/tJQybWghT22G3w31eLceDZVfT4m+1Ki3wNUVEA2o0qKLRMmVbimxnh4jd+YZGivY twiw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:mime-version:reply-to:references :in-reply-to:message-id:date:subject:cc:to:from:x-gm-message-state :from:to:cc; bh=cSpqYU1D9ItlltcLfcZWUikLm3ec3eLEHuEHj/0XdMM=; b=vji5X/h92Yh8vGeboGsOimYkoYod9oJJDj6fSdDE0y3umTgtJaZktMv5qBvU54K9rB n1x1eyqsIYvwP80xgt0p/g+BvTwv3m6itr94r3yIxtxt3+qoCh5lya3iVAjsxoNUj0uU TNM2VazjrC7Q/fqhAK/NPpWTJEuykFD3fXgWhH2uLdAijZ0t1kYaKQwv4cJuFNWi5tPi HHryQRwL/DYmbkEdnsDTC97Of1MTJUrR+zXT/ANYUctvNbfGF0/uEx7elBIcb7hKTmQK HJ3JDXqfE1d41tDeMy8gzd37186cYbQTPipJFWWZy919N0MU1KLMnXVlu5OPg28t0fPj fxKA== X-Gm-Message-State: ACgBeo152wyimFFwhNEwPspF00fsA/TV3RuN2zM86YTeduEv4jjnKvTC knOmjuBRZ7+Mcd3hMZtJk0M46LXBHOo= X-Google-Smtp-Source: AA6agR5wgW8sKY1XtKq8ahN0EAQGOS3KYCNnYUTbQdQgjtfZmX2iTTv2Fb7PpuaW4hQkbvRGsIIJFg== X-Received: by 2002:a05:6000:2a9:b0:225:7388:ad75 with SMTP id l9-20020a05600002a900b002257388ad75mr1041427wry.240.1661342463117; Wed, 24 Aug 2022 05:01:03 -0700 (PDT) Received: from localhost.localdomain ([86.14.124.218]) by smtp.gmail.com with ESMTPSA id cc19-20020a5d5c13000000b0022571d43d32sm1697676wrb.21.2022.08.24.05.01.02 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 24 Aug 2022 05:01:02 -0700 (PDT) From: herron.philip@googlemail.com X-Google-Original-From: philip.herron@embecosm.com To: gcc-patches@gcc.gnu.org Subject: [PATCH Rust front-end v2 23/37] gccrs: Add unsafe checks for Rust Date: Wed, 24 Aug 2022 12:59:42 +0100 Message-Id: <20220824115956.737931-24-philip.herron@embecosm.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20220824115956.737931-1-philip.herron@embecosm.com> References: <20220824115956.737931-1-philip.herron@embecosm.com> MIME-Version: 1.0 X-Spam-Status: No, score=-11.9 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, FREEMAIL_FROM, GIT_PATCH_0, KAM_SHORT, RCVD_IN_DNSWL_NONE, SPF_HELO_NONE, SPF_PASS, TXREP, T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: philip.herron@embecosm.com Cc: Arthur Cohen , gcc-rust@gcc.gnu.org Errors-To: gcc-patches-bounces+patchwork=sourceware.org@gcc.gnu.org Sender: "Gcc-patches" From: Arthur Cohen The UnsafeChecker visitor verifies that unsafe actions are only performed in unsafe contexts. Otherwise, an error should be reported to the user and the compilation pipeline should be halted. These contexts, which include unsafe blocks or unsafe functions, are allowed to perform more actions than regular safe Rust code. These actions currently include: - Dereferencing raw pointers - Calls to unsafe functions - Use of inline assembly - Use of mutable static - Use of extern static - Access to a union's field - Call to functions with #[target(feature)] attribute - Initializing type with rustc_layout_scalar_valid_range attribute - Mutation of layout constrained field - Borrow of layout constrained field --- gcc/rust/checks/errors/rust-unsafe-checker.cc | 963 ++++++++++++++++++ gcc/rust/checks/errors/rust-unsafe-checker.h | 191 ++++ 2 files changed, 1154 insertions(+) create mode 100644 gcc/rust/checks/errors/rust-unsafe-checker.cc create mode 100644 gcc/rust/checks/errors/rust-unsafe-checker.h diff --git a/gcc/rust/checks/errors/rust-unsafe-checker.cc b/gcc/rust/checks/errors/rust-unsafe-checker.cc new file mode 100644 index 00000000000..e3f32539562 --- /dev/null +++ b/gcc/rust/checks/errors/rust-unsafe-checker.cc @@ -0,0 +1,963 @@ +// Copyright (C) 2020-2022 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 +// . + +#include "rust-unsafe-checker.h" +#include "rust-hir.h" +#include "rust-hir-expr.h" +#include "rust-hir-stmt.h" +#include "rust-hir-item.h" + +namespace Rust { +namespace HIR { + +UnsafeChecker::UnsafeChecker () + : context (*Resolver::TypeCheckContext::get ()), + resolver (*Resolver::Resolver::get ()), + mappings (*Analysis::Mappings::get ()) +{} + +void +UnsafeChecker::go (HIR::Crate &crate) +{ + for (auto &item : crate.items) + item->accept_vis (*this); +} + +static void +check_static_mut (HIR::Item *maybe_static, Location locus) +{ + if (maybe_static->get_hir_kind () == Node::BaseKind::VIS_ITEM) + { + auto item = static_cast (maybe_static); + if (item->get_item_kind () == Item::ItemKind::Static) + { + auto static_item = static_cast (item); + if (static_item->is_mut ()) + rust_error_at ( + locus, "use of mutable static requires unsafe function or block"); + } + } +} + +static void +check_extern_static (HIR::ExternalItem *maybe_static, Location locus) +{ + if (maybe_static->get_extern_kind () == ExternalItem::ExternKind::Static) + rust_error_at (locus, + "use of extern static requires unsafe function or block"); +} + +void +UnsafeChecker::check_use_of_static (HirId node_id, Location locus) +{ + if (unsafe_context.is_in_context ()) + return; + + auto maybe_static_mut = mappings.lookup_hir_item (node_id); + + HirId extern_block; + auto maybe_extern_static + = mappings.lookup_hir_extern_item (node_id, &extern_block); + + if (maybe_static_mut) + check_static_mut (maybe_static_mut, locus); + + if (maybe_extern_static) + check_extern_static (static_cast (maybe_extern_static), + locus); +} + +static void +check_unsafe_call (HIR::Function *fn, Location locus, const std::string &kind) +{ + if (fn->get_qualifiers ().is_unsafe ()) + rust_error_at (locus, "call to unsafe %s requires unsafe function or block", + kind.c_str ()); +} + +static bool +is_safe_intrinsic (const std::string &fn_name) +{ + static const std::unordered_set safe_intrinsics = { + "abort", + "size_of", + "min_align_of", + "needs_drop", + "caller_location", + "add_with_overflow", + "sub_with_overflow", + "mul_with_overflow", + "wrapping_add", + "wrapping_sub", + "wrapping_mul", + "saturating_add", + "saturating_sub", + "rotate_left", + "rotate_right", + "ctpop", + "ctlz", + "cttz", + "bswap", + "bitreverse", + "discriminant_value", + "type_id", + "likely", + "unlikely", + "ptr_guaranteed_eq", + "ptr_guaranteed_ne", + "minnumf32", + "minnumf64", + "maxnumf32", + "rustc_peek", + "maxnumf64", + "type_name", + "forget", + "black_box", + "variant_count", + }; + + return safe_intrinsics.find (fn_name) != safe_intrinsics.end (); +} + +static void +check_extern_call (HIR::ExternalItem *maybe_fn, HIR::ExternBlock *parent_block, + Location locus) +{ + // We have multiple operations to perform here + // 1. Is the item an actual function we're calling + // 2. Is the block it's defined in an FFI block or an `extern crate` block + // + // It is not unsafe to call into other crates, so items defined in an `extern + // crate` must be callable without being in an unsafe context. On the other + // hand, any function defined in a block with a specific ABI (even `extern + // "Rust"` blocks) is unsafe to call + + if (maybe_fn->get_extern_kind () != ExternalItem::ExternKind::Function) + return; + + // Some intrinsics are safe to call + if (parent_block->get_abi () == Rust::ABI::INTRINSIC + && is_safe_intrinsic (maybe_fn->get_item_name ())) + return; + + rust_error_at (locus, + "call to extern function requires unsafe function or block"); +} + +void +UnsafeChecker::check_function_call (HirId node_id, Location locus) +{ + if (unsafe_context.is_in_context ()) + return; + + HirId parent_extern_block; + auto maybe_fn = mappings.lookup_hir_item (node_id); + auto maybe_extern + = mappings.lookup_hir_extern_item (node_id, &parent_extern_block); + + if (maybe_fn && maybe_fn->get_item_kind () == Item::ItemKind::Function) + check_unsafe_call (static_cast (maybe_fn), locus, "function"); + + if (maybe_extern) + check_extern_call (static_cast (maybe_extern), + mappings.lookup_hir_extern_block (parent_extern_block), + locus); +} + +void +UnsafeChecker::visit (Lifetime &lifetime) +{} + +void +UnsafeChecker::visit (LifetimeParam &lifetime_param) +{} + +void +UnsafeChecker::visit (PathInExpression &path) +{ + NodeId ast_node_id = path.get_mappings ().get_nodeid (); + NodeId ref_node_id; + HirId definition_id; + + if (!resolver.lookup_resolved_name (ast_node_id, &ref_node_id)) + return; + + rust_assert (mappings.lookup_node_to_hir (ref_node_id, &definition_id)); + + check_use_of_static (definition_id, path.get_locus ()); +} + +void +UnsafeChecker::visit (TypePathSegment &segment) +{} + +void +UnsafeChecker::visit (TypePathSegmentGeneric &segment) +{} + +void +UnsafeChecker::visit (TypePathSegmentFunction &segment) +{} + +void +UnsafeChecker::visit (TypePath &path) +{} + +void +UnsafeChecker::visit (QualifiedPathInExpression &path) +{} + +void +UnsafeChecker::visit (QualifiedPathInType &path) +{} + +void +UnsafeChecker::visit (LiteralExpr &expr) +{} + +void +UnsafeChecker::visit (BorrowExpr &expr) +{ + expr.get_expr ()->accept_vis (*this); +} + +void +UnsafeChecker::visit (DereferenceExpr &expr) +{ + TyTy::BaseType *to_deref_type; + auto to_deref = expr.get_expr ()->get_mappings ().get_hirid (); + + rust_assert (context.lookup_type (to_deref, &to_deref_type)); + + if (to_deref_type->get_kind () == TyTy::TypeKind::POINTER + && !unsafe_context.is_in_context ()) + rust_error_at (expr.get_locus (), "dereference of raw pointer requires " + "unsafe function or block"); +} + +void +UnsafeChecker::visit (ErrorPropagationExpr &expr) +{ + expr.get_expr ()->accept_vis (*this); +} + +void +UnsafeChecker::visit (NegationExpr &expr) +{ + expr.get_expr ()->accept_vis (*this); +} + +void +UnsafeChecker::visit (ArithmeticOrLogicalExpr &expr) +{ + expr.get_lhs ()->accept_vis (*this); + expr.get_rhs ()->accept_vis (*this); +} + +void +UnsafeChecker::visit (ComparisonExpr &expr) +{ + expr.get_lhs ()->accept_vis (*this); + expr.get_rhs ()->accept_vis (*this); +} + +void +UnsafeChecker::visit (LazyBooleanExpr &expr) +{ + expr.get_lhs ()->accept_vis (*this); + expr.get_rhs ()->accept_vis (*this); +} + +void +UnsafeChecker::visit (TypeCastExpr &expr) +{ + expr.get_expr ()->accept_vis (*this); +} + +void +UnsafeChecker::visit (AssignmentExpr &expr) +{ + expr.get_lhs ()->accept_vis (*this); + expr.get_rhs ()->accept_vis (*this); +} + +void +UnsafeChecker::visit (CompoundAssignmentExpr &expr) +{ + expr.get_left_expr ()->accept_vis (*this); + expr.get_right_expr ()->accept_vis (*this); +} + +void +UnsafeChecker::visit (GroupedExpr &expr) +{ + expr.get_expr_in_parens ()->accept_vis (*this); +} + +void +UnsafeChecker::visit (ArrayElemsValues &elems) +{ + for (auto &elem : elems.get_values ()) + elem->accept_vis (*this); +} + +void +UnsafeChecker::visit (ArrayElemsCopied &elems) +{ + elems.get_elem_to_copy ()->accept_vis (*this); +} + +void +UnsafeChecker::visit (ArrayExpr &expr) +{ + expr.get_internal_elements ()->accept_vis (*this); +} + +void +UnsafeChecker::visit (ArrayIndexExpr &expr) +{ + expr.get_array_expr ()->accept_vis (*this); + expr.get_index_expr ()->accept_vis (*this); +} + +void +UnsafeChecker::visit (TupleExpr &expr) +{ + for (auto &elem : expr.get_tuple_elems ()) + elem->accept_vis (*this); +} + +void +UnsafeChecker::visit (TupleIndexExpr &expr) +{ + expr.get_tuple_expr ()->accept_vis (*this); +} + +void +UnsafeChecker::visit (StructExprStruct &expr) +{} + +void +UnsafeChecker::visit (StructExprFieldIdentifier &field) +{} + +void +UnsafeChecker::visit (StructExprFieldIdentifierValue &field) +{ + field.get_value ()->accept_vis (*this); +} + +void +UnsafeChecker::visit (StructExprFieldIndexValue &field) +{ + field.get_value ()->accept_vis (*this); +} + +void +UnsafeChecker::visit (StructExprStructFields &expr) +{ + for (auto &field : expr.get_fields ()) + field->accept_vis (*this); +} + +void +UnsafeChecker::visit (StructExprStructBase &expr) +{} + +void +UnsafeChecker::visit (CallExpr &expr) +{ + auto fn = expr.get_fnexpr (); + if (!fn) + return; + + NodeId ast_node_id = fn->get_mappings ().get_nodeid (); + NodeId ref_node_id; + HirId definition_id; + + // There are no unsafe types, and functions are defined in the name resolver. + // If we can't find the name, then we're dealing with a type and should return + // early. + if (!resolver.lookup_resolved_name (ast_node_id, &ref_node_id)) + return; + + rust_assert (mappings.lookup_node_to_hir (ref_node_id, &definition_id)); + + // At this point we have the function's HIR Id. There are two checks we + // must perform: + // 1. The function is an unsafe one + // 2. The function is an extern one + check_function_call (definition_id, expr.get_locus ()); + + if (expr.has_params ()) + for (auto &arg : expr.get_arguments ()) + arg->accept_vis (*this); +} + +void +UnsafeChecker::visit (MethodCallExpr &expr) +{ + TyTy::BaseType *method_type; + context.lookup_type (expr.get_method_name ().get_mappings ().get_hirid (), + &method_type); + + auto fn = *static_cast (method_type); + auto method = mappings.lookup_hir_implitem (fn.get_ref (), nullptr); + + if (!unsafe_context.is_in_context () && method) + check_unsafe_call (static_cast (method), expr.get_locus (), + "method"); + + expr.get_receiver ()->accept_vis (*this); + + for (auto &arg : expr.get_arguments ()) + arg->accept_vis (*this); +} + +void +UnsafeChecker::visit (FieldAccessExpr &expr) +{ + expr.get_receiver_expr ()->accept_vis (*this); + + if (unsafe_context.is_in_context ()) + return; + + TyTy::BaseType *receiver_ty; + auto ok = context.lookup_type ( + expr.get_receiver_expr ()->get_mappings ().get_hirid (), &receiver_ty); + rust_assert (ok); + + if (receiver_ty->get_kind () == TyTy::TypeKind::ADT) + { + auto maybe_union = static_cast (receiver_ty); + if (maybe_union->is_union ()) + rust_error_at ( + expr.get_locus (), + "access to union field requires unsafe function or block"); + } +} + +void +UnsafeChecker::visit (ClosureExprInner &expr) +{} + +void +UnsafeChecker::visit (BlockExpr &expr) +{ + for (auto &stmt : expr.get_statements ()) + stmt->accept_vis (*this); + + if (expr.has_expr ()) + expr.get_final_expr ()->accept_vis (*this); +} + +void +UnsafeChecker::visit (ClosureExprInnerTyped &expr) +{} + +void +UnsafeChecker::visit (ContinueExpr &expr) +{} + +void +UnsafeChecker::visit (BreakExpr &expr) +{ + if (expr.has_break_expr ()) + expr.get_expr ()->accept_vis (*this); +} + +void +UnsafeChecker::visit (RangeFromToExpr &expr) +{ + expr.get_from_expr ()->accept_vis (*this); + expr.get_to_expr ()->accept_vis (*this); +} + +void +UnsafeChecker::visit (RangeFromExpr &expr) +{ + expr.get_from_expr ()->accept_vis (*this); +} + +void +UnsafeChecker::visit (RangeToExpr &expr) +{ + expr.get_to_expr ()->accept_vis (*this); +} + +void +UnsafeChecker::visit (RangeFullExpr &expr) +{} + +void +UnsafeChecker::visit (RangeFromToInclExpr &expr) +{ + expr.get_from_expr ()->accept_vis (*this); + expr.get_to_expr ()->accept_vis (*this); +} + +void +UnsafeChecker::visit (RangeToInclExpr &expr) +{ + expr.get_to_expr ()->accept_vis (*this); +} + +void +UnsafeChecker::visit (ReturnExpr &expr) +{ + if (expr.has_return_expr ()) + expr.get_expr ()->accept_vis (*this); +} + +void +UnsafeChecker::visit (UnsafeBlockExpr &expr) +{ + unsafe_context.enter (expr.get_mappings ().get_hirid ()); + + expr.get_block_expr ()->accept_vis (*this); + + unsafe_context.exit (); +} + +void +UnsafeChecker::visit (LoopExpr &expr) +{ + expr.get_loop_block ()->accept_vis (*this); +} + +void +UnsafeChecker::visit (WhileLoopExpr &expr) +{ + expr.get_predicate_expr ()->accept_vis (*this); + expr.get_loop_block ()->accept_vis (*this); +} + +void +UnsafeChecker::visit (WhileLetLoopExpr &expr) +{ + expr.get_cond ()->accept_vis (*this); + expr.get_loop_block ()->accept_vis (*this); +} + +void +UnsafeChecker::visit (ForLoopExpr &expr) +{ + expr.get_iterator_expr ()->accept_vis (*this); + expr.get_loop_block ()->accept_vis (*this); +} + +void +UnsafeChecker::visit (IfExpr &expr) +{ + expr.get_if_condition ()->accept_vis (*this); + expr.get_if_block ()->accept_vis (*this); +} + +void +UnsafeChecker::visit (IfExprConseqElse &expr) +{ + expr.get_if_condition ()->accept_vis (*this); + expr.get_if_block ()->accept_vis (*this); + expr.get_else_block ()->accept_vis (*this); +} + +void +UnsafeChecker::visit (IfExprConseqIf &expr) +{ + expr.get_if_condition ()->accept_vis (*this); + expr.get_if_block ()->accept_vis (*this); + expr.get_conseq_if_expr ()->accept_vis (*this); +} + +void +UnsafeChecker::visit (IfExprConseqIfLet &expr) +{ + expr.get_if_condition ()->accept_vis (*this); + expr.get_if_block ()->accept_vis (*this); + + // TODO: Visit conseq if let expression +} + +void +UnsafeChecker::visit (IfLetExpr &expr) +{ + expr.get_scrutinee_expr ()->accept_vis (*this); + expr.get_if_block ()->accept_vis (*this); +} + +void +UnsafeChecker::visit (IfLetExprConseqElse &expr) +{ + expr.get_scrutinee_expr ()->accept_vis (*this); + expr.get_if_block ()->accept_vis (*this); + + // TODO: Visit else expression +} + +void +UnsafeChecker::visit (IfLetExprConseqIf &expr) +{ + expr.get_scrutinee_expr ()->accept_vis (*this); + expr.get_if_block ()->accept_vis (*this); +} + +void +UnsafeChecker::visit (IfLetExprConseqIfLet &expr) +{ + expr.get_scrutinee_expr ()->accept_vis (*this); + expr.get_if_block ()->accept_vis (*this); + + // TODO: Visit conseq if let expression +} + +void +UnsafeChecker::visit (MatchExpr &expr) +{ + expr.get_scrutinee_expr ()->accept_vis (*this); + + for (auto &match_arm : expr.get_match_cases ()) + match_arm.get_expr ()->accept_vis (*this); +} + +void +UnsafeChecker::visit (AwaitExpr &expr) +{ + // TODO: Visit expression +} + +void +UnsafeChecker::visit (AsyncBlockExpr &expr) +{ + // TODO: Visit block expression +} + +void +UnsafeChecker::visit (TypeParam ¶m) +{} + +void +UnsafeChecker::visit (ConstGenericParam ¶m) +{} + +void +UnsafeChecker::visit (LifetimeWhereClauseItem &item) +{} + +void +UnsafeChecker::visit (TypeBoundWhereClauseItem &item) +{} + +void +UnsafeChecker::visit (Module &module) +{ + for (auto &item : module.get_items ()) + item->accept_vis (*this); +} + +void +UnsafeChecker::visit (ExternCrate &crate) +{} + +void +UnsafeChecker::visit (UseTreeGlob &use_tree) +{} + +void +UnsafeChecker::visit (UseTreeList &use_tree) +{} + +void +UnsafeChecker::visit (UseTreeRebind &use_tree) +{} + +void +UnsafeChecker::visit (UseDeclaration &use_decl) +{} + +void +UnsafeChecker::visit (Function &function) +{ + auto is_unsafe_fn = function.get_qualifiers ().is_unsafe (); + + if (is_unsafe_fn) + unsafe_context.enter (function.get_mappings ().get_hirid ()); + + function.get_definition ()->accept_vis (*this); + + if (is_unsafe_fn) + unsafe_context.exit (); +} + +void +UnsafeChecker::visit (TypeAlias &type_alias) +{ + // FIXME: What do we need to do to handle type aliasing? Is it possible to + // have unsafe types? Type aliases on unsafe functions? +} + +void +UnsafeChecker::visit (StructStruct &struct_item) +{} + +void +UnsafeChecker::visit (TupleStruct &tuple_struct) +{} + +void +UnsafeChecker::visit (EnumItem &item) +{} + +void +UnsafeChecker::visit (EnumItemTuple &item) +{} + +void +UnsafeChecker::visit (EnumItemStruct &item) +{} + +void +UnsafeChecker::visit (EnumItemDiscriminant &item) +{} + +void +UnsafeChecker::visit (Enum &enum_item) +{} + +void +UnsafeChecker::visit (Union &union_item) +{} + +void +UnsafeChecker::visit (ConstantItem &const_item) +{ + const_item.get_expr ()->accept_vis (*this); +} + +void +UnsafeChecker::visit (StaticItem &static_item) +{ + static_item.get_expr ()->accept_vis (*this); +} + +void +UnsafeChecker::visit (TraitItemFunc &item) +{ + if (item.has_block_defined ()) + item.get_block_expr ()->accept_vis (*this); +} + +void +UnsafeChecker::visit (TraitItemConst &item) +{ + if (item.has_expr ()) + item.get_expr ()->accept_vis (*this); +} + +void +UnsafeChecker::visit (TraitItemType &item) +{} + +void +UnsafeChecker::visit (Trait &trait) +{ + // FIXME: Handle unsafe traits + for (auto &item : trait.get_trait_items ()) + item->accept_vis (*this); +} + +void +UnsafeChecker::visit (ImplBlock &impl) +{ + // FIXME: Handle unsafe impls + for (auto &item : impl.get_impl_items ()) + item->accept_vis (*this); +} + +void +UnsafeChecker::visit (ExternalStaticItem &item) +{} + +void +UnsafeChecker::visit (ExternalFunctionItem &item) +{} + +void +UnsafeChecker::visit (ExternBlock &block) +{ + // FIXME: Do we need to do this? + for (auto &item : block.get_extern_items ()) + item->accept_vis (*this); +} + +void +UnsafeChecker::visit (LiteralPattern &pattern) +{} + +void +UnsafeChecker::visit (IdentifierPattern &pattern) +{} + +void +UnsafeChecker::visit (WildcardPattern &pattern) +{} + +void +UnsafeChecker::visit (RangePatternBoundLiteral &bound) +{} + +void +UnsafeChecker::visit (RangePatternBoundPath &bound) +{} + +void +UnsafeChecker::visit (RangePatternBoundQualPath &bound) +{} + +void +UnsafeChecker::visit (RangePattern &pattern) +{} + +void +UnsafeChecker::visit (ReferencePattern &pattern) +{} + +void +UnsafeChecker::visit (StructPatternFieldTuplePat &field) +{} + +void +UnsafeChecker::visit (StructPatternFieldIdentPat &field) +{} + +void +UnsafeChecker::visit (StructPatternFieldIdent &field) +{} + +void +UnsafeChecker::visit (StructPattern &pattern) +{} + +void +UnsafeChecker::visit (TupleStructItemsNoRange &tuple_items) +{} + +void +UnsafeChecker::visit (TupleStructItemsRange &tuple_items) +{} + +void +UnsafeChecker::visit (TupleStructPattern &pattern) +{} + +void +UnsafeChecker::visit (TuplePatternItemsMultiple &tuple_items) +{} + +void +UnsafeChecker::visit (TuplePatternItemsRanged &tuple_items) +{} + +void +UnsafeChecker::visit (TuplePattern &pattern) +{} + +void +UnsafeChecker::visit (GroupedPattern &pattern) +{} + +void +UnsafeChecker::visit (SlicePattern &pattern) +{} + +void +UnsafeChecker::visit (EmptyStmt &stmt) +{} + +void +UnsafeChecker::visit (LetStmt &stmt) +{ + if (stmt.has_init_expr ()) + stmt.get_init_expr ()->accept_vis (*this); +} + +void +UnsafeChecker::visit (ExprStmtWithoutBlock &stmt) +{ + stmt.get_expr ()->accept_vis (*this); +} + +void +UnsafeChecker::visit (ExprStmtWithBlock &stmt) +{ + stmt.get_expr ()->accept_vis (*this); +} + +void +UnsafeChecker::visit (TraitBound &bound) +{} + +void +UnsafeChecker::visit (ImplTraitType &type) +{} + +void +UnsafeChecker::visit (TraitObjectType &type) +{} + +void +UnsafeChecker::visit (ParenthesisedType &type) +{} + +void +UnsafeChecker::visit (ImplTraitTypeOneBound &type) +{} + +void +UnsafeChecker::visit (TupleType &type) +{} + +void +UnsafeChecker::visit (NeverType &type) +{} + +void +UnsafeChecker::visit (RawPointerType &type) +{} + +void +UnsafeChecker::visit (ReferenceType &type) +{} + +void +UnsafeChecker::visit (ArrayType &type) +{} + +void +UnsafeChecker::visit (SliceType &type) +{} + +void +UnsafeChecker::visit (InferredType &type) +{} + +void +UnsafeChecker::visit (BareFunctionType &type) +{} + +} // namespace HIR +} // namespace Rust diff --git a/gcc/rust/checks/errors/rust-unsafe-checker.h b/gcc/rust/checks/errors/rust-unsafe-checker.h new file mode 100644 index 00000000000..ae1eb509d78 --- /dev/null +++ b/gcc/rust/checks/errors/rust-unsafe-checker.h @@ -0,0 +1,191 @@ +// Copyright (C) 2020-2022 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 +// . + +#ifndef RUST_UNSAFE_CHECKER_H +#define RUST_UNSAFE_CHECKER_H + +#include "rust-hir-visitor.h" +#include "rust-name-resolver.h" +#include "rust-hir-type-check.h" +#include "rust-stacked-contexts.h" + +namespace Rust { +namespace HIR { +class UnsafeChecker : public HIRFullVisitor +{ +public: + UnsafeChecker (); + + void go (HIR::Crate &crate); + +private: + /** + * Check if a mutable static or external static item is used outside of an + * unsafe context + */ + void check_use_of_static (HirId node_id, Location locus); + + /** + * Check if a call to an unsafe or external function is outside of an unsafe + * context + */ + void check_function_call (HirId node_id, Location locus); + + StackedContexts unsafe_context; + + Resolver::TypeCheckContext &context; + Resolver::Resolver &resolver; + Analysis::Mappings &mappings; + + virtual void visit (Lifetime &lifetime) override; + virtual void visit (LifetimeParam &lifetime_param) override; + virtual void visit (PathInExpression &path) override; + virtual void visit (TypePathSegment &segment) override; + virtual void visit (TypePathSegmentGeneric &segment) override; + virtual void visit (TypePathSegmentFunction &segment) override; + virtual void visit (TypePath &path) override; + virtual void visit (QualifiedPathInExpression &path) override; + virtual void visit (QualifiedPathInType &path) override; + virtual void visit (LiteralExpr &expr) override; + virtual void visit (BorrowExpr &expr) override; + virtual void visit (DereferenceExpr &expr) override; + virtual void visit (ErrorPropagationExpr &expr) override; + virtual void visit (NegationExpr &expr) override; + virtual void visit (ArithmeticOrLogicalExpr &expr) override; + virtual void visit (ComparisonExpr &expr) override; + virtual void visit (LazyBooleanExpr &expr) override; + virtual void visit (TypeCastExpr &expr) override; + virtual void visit (AssignmentExpr &expr) override; + virtual void visit (CompoundAssignmentExpr &expr) override; + virtual void visit (GroupedExpr &expr) override; + virtual void visit (ArrayElemsValues &elems) override; + virtual void visit (ArrayElemsCopied &elems) override; + virtual void visit (ArrayExpr &expr) override; + virtual void visit (ArrayIndexExpr &expr) override; + virtual void visit (TupleExpr &expr) override; + virtual void visit (TupleIndexExpr &expr) override; + virtual void visit (StructExprStruct &expr) override; + virtual void visit (StructExprFieldIdentifier &field) override; + virtual void visit (StructExprFieldIdentifierValue &field) override; + virtual void visit (StructExprFieldIndexValue &field) override; + virtual void visit (StructExprStructFields &expr) override; + virtual void visit (StructExprStructBase &expr) override; + virtual void visit (CallExpr &expr) override; + virtual void visit (MethodCallExpr &expr) override; + virtual void visit (FieldAccessExpr &expr) override; + virtual void visit (ClosureExprInner &expr) override; + virtual void visit (BlockExpr &expr) override; + virtual void visit (ClosureExprInnerTyped &expr) override; + virtual void visit (ContinueExpr &expr) override; + virtual void visit (BreakExpr &expr) override; + virtual void visit (RangeFromToExpr &expr) override; + virtual void visit (RangeFromExpr &expr) override; + virtual void visit (RangeToExpr &expr) override; + virtual void visit (RangeFullExpr &expr) override; + virtual void visit (RangeFromToInclExpr &expr) override; + virtual void visit (RangeToInclExpr &expr) override; + virtual void visit (ReturnExpr &expr) override; + virtual void visit (UnsafeBlockExpr &expr) override; + virtual void visit (LoopExpr &expr) override; + virtual void visit (WhileLoopExpr &expr) override; + virtual void visit (WhileLetLoopExpr &expr) override; + virtual void visit (ForLoopExpr &expr) override; + virtual void visit (IfExpr &expr) override; + virtual void visit (IfExprConseqElse &expr) override; + virtual void visit (IfExprConseqIf &expr) override; + virtual void visit (IfExprConseqIfLet &expr) override; + virtual void visit (IfLetExpr &expr) override; + virtual void visit (IfLetExprConseqElse &expr) override; + virtual void visit (IfLetExprConseqIf &expr) override; + virtual void visit (IfLetExprConseqIfLet &expr) override; + virtual void visit (MatchExpr &expr) override; + virtual void visit (AwaitExpr &expr) override; + virtual void visit (AsyncBlockExpr &expr) override; + virtual void visit (TypeParam ¶m) override; + virtual void visit (ConstGenericParam ¶m) override; + virtual void visit (LifetimeWhereClauseItem &item) override; + virtual void visit (TypeBoundWhereClauseItem &item) override; + virtual void visit (Module &module) override; + virtual void visit (ExternCrate &crate) override; + virtual void visit (UseTreeGlob &use_tree) override; + virtual void visit (UseTreeList &use_tree) override; + virtual void visit (UseTreeRebind &use_tree) override; + virtual void visit (UseDeclaration &use_decl) override; + virtual void visit (Function &function) override; + virtual void visit (TypeAlias &type_alias) override; + virtual void visit (StructStruct &struct_item) override; + virtual void visit (TupleStruct &tuple_struct) override; + virtual void visit (EnumItem &item) override; + virtual void visit (EnumItemTuple &item) override; + virtual void visit (EnumItemStruct &item) override; + virtual void visit (EnumItemDiscriminant &item) override; + virtual void visit (Enum &enum_item) override; + virtual void visit (Union &union_item) override; + virtual void visit (ConstantItem &const_item) override; + virtual void visit (StaticItem &static_item) override; + virtual void visit (TraitItemFunc &item) override; + virtual void visit (TraitItemConst &item) override; + virtual void visit (TraitItemType &item) override; + virtual void visit (Trait &trait) override; + virtual void visit (ImplBlock &impl) override; + virtual void visit (ExternalStaticItem &item) override; + virtual void visit (ExternalFunctionItem &item) override; + virtual void visit (ExternBlock &block) override; + virtual void visit (LiteralPattern &pattern) override; + virtual void visit (IdentifierPattern &pattern) override; + virtual void visit (WildcardPattern &pattern) override; + virtual void visit (RangePatternBoundLiteral &bound) override; + virtual void visit (RangePatternBoundPath &bound) override; + virtual void visit (RangePatternBoundQualPath &bound) override; + virtual void visit (RangePattern &pattern) override; + virtual void visit (ReferencePattern &pattern) override; + virtual void visit (StructPatternFieldTuplePat &field) override; + virtual void visit (StructPatternFieldIdentPat &field) override; + virtual void visit (StructPatternFieldIdent &field) override; + virtual void visit (StructPattern &pattern) override; + virtual void visit (TupleStructItemsNoRange &tuple_items) override; + virtual void visit (TupleStructItemsRange &tuple_items) override; + virtual void visit (TupleStructPattern &pattern) override; + virtual void visit (TuplePatternItemsMultiple &tuple_items) override; + virtual void visit (TuplePatternItemsRanged &tuple_items) override; + virtual void visit (TuplePattern &pattern) override; + virtual void visit (GroupedPattern &pattern) override; + virtual void visit (SlicePattern &pattern) override; + virtual void visit (EmptyStmt &stmt) override; + virtual void visit (LetStmt &stmt) override; + virtual void visit (ExprStmtWithoutBlock &stmt) override; + virtual void visit (ExprStmtWithBlock &stmt) override; + virtual void visit (TraitBound &bound) override; + virtual void visit (ImplTraitType &type) override; + virtual void visit (TraitObjectType &type) override; + virtual void visit (ParenthesisedType &type) override; + virtual void visit (ImplTraitTypeOneBound &type) override; + virtual void visit (TupleType &type) override; + virtual void visit (NeverType &type) override; + virtual void visit (RawPointerType &type) override; + virtual void visit (ReferenceType &type) override; + virtual void visit (ArrayType &type) override; + virtual void visit (SliceType &type) override; + virtual void visit (InferredType &type) override; + virtual void visit (BareFunctionType &type) override; +}; + +} // namespace HIR +} // namespace Rust + +#endif /* !RUST_UNSAFE_CHECKER_H */