1 // Copyright (C) 2020-2023 Free Software Foundation, Inc.
3 // This file is part of GCC.
5 // GCC is free software; you can redistribute it and/or modify it under
6 // the terms of the GNU General Public License as published by the Free
7 // Software Foundation; either version 3, or (at your option) any later
10 // GCC is distributed in the hope that it will be useful, but WITHOUT ANY
11 // WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 // You should have received a copy of the GNU General Public License
16 // along with GCC; see the file COPYING3. If not see
17 // <http://www.gnu.org/licenses/>.
19 #include "rust-unsafe-checker.h"
21 #include "rust-hir-expr.h"
22 #include "rust-hir-stmt.h"
23 #include "rust-hir-item.h"
28 UnsafeChecker::UnsafeChecker ()
29 : context (*Resolver::TypeCheckContext::get ()),
30 resolver (*Resolver::Resolver::get ()),
31 mappings (*Analysis::Mappings::get ())
35 UnsafeChecker::go (HIR::Crate
&crate
)
37 for (auto &item
: crate
.items
)
38 item
->accept_vis (*this);
42 check_static_mut (HIR::Item
*maybe_static
, Location locus
)
44 if (maybe_static
->get_hir_kind () == Node::BaseKind::VIS_ITEM
)
46 auto item
= static_cast<Item
*> (maybe_static
);
47 if (item
->get_item_kind () == Item::ItemKind::Static
)
49 auto static_item
= static_cast<StaticItem
*> (item
);
50 if (static_item
->is_mut ())
52 locus
, "use of mutable static requires unsafe function or block");
58 check_extern_static (HIR::ExternalItem
*maybe_static
, Location locus
)
60 if (maybe_static
->get_extern_kind () == ExternalItem::ExternKind::Static
)
62 "use of extern static requires unsafe function or block");
66 UnsafeChecker::check_use_of_static (HirId node_id
, Location locus
)
68 if (unsafe_context
.is_in_context ())
71 auto maybe_static_mut
= mappings
.lookup_hir_item (node_id
);
74 auto maybe_extern_static
75 = mappings
.lookup_hir_extern_item (node_id
, &extern_block
);
78 check_static_mut (maybe_static_mut
, locus
);
80 if (maybe_extern_static
)
81 check_extern_static (static_cast<ExternalItem
*> (maybe_extern_static
),
86 check_unsafe_call (HIR::Function
*fn
, Location locus
, const std::string
&kind
)
88 if (fn
->get_qualifiers ().is_unsafe ())
89 rust_error_at (locus
, "call to unsafe %s requires unsafe function or block",
94 is_safe_intrinsic (const std::string
&fn_name
)
96 static const std::unordered_set
<std::string
> safe_intrinsics
= {
117 "discriminant_value",
134 return safe_intrinsics
.find (fn_name
) != safe_intrinsics
.end ();
138 check_extern_call (HIR::ExternalItem
*maybe_fn
, HIR::ExternBlock
*parent_block
,
141 // We have multiple operations to perform here
142 // 1. Is the item an actual function we're calling
143 // 2. Is the block it's defined in an FFI block or an `extern crate` block
145 // It is not unsafe to call into other crates, so items defined in an `extern
146 // crate` must be callable without being in an unsafe context. On the other
147 // hand, any function defined in a block with a specific ABI (even `extern
148 // "Rust"` blocks) is unsafe to call
150 if (maybe_fn
->get_extern_kind () != ExternalItem::ExternKind::Function
)
153 // Some intrinsics are safe to call
154 if (parent_block
->get_abi () == Rust::ABI::INTRINSIC
155 && is_safe_intrinsic (maybe_fn
->get_item_name ()))
158 rust_error_at (locus
,
159 "call to extern function requires unsafe function or block");
163 UnsafeChecker::check_function_call (HirId node_id
, Location locus
)
165 if (unsafe_context
.is_in_context ())
168 HirId parent_extern_block
;
169 auto maybe_fn
= mappings
.lookup_hir_item (node_id
);
171 = mappings
.lookup_hir_extern_item (node_id
, &parent_extern_block
);
173 if (maybe_fn
&& maybe_fn
->get_item_kind () == Item::ItemKind::Function
)
174 check_unsafe_call (static_cast<Function
*> (maybe_fn
), locus
, "function");
177 check_extern_call (static_cast<ExternalItem
*> (maybe_extern
),
178 mappings
.lookup_hir_extern_block (parent_extern_block
),
183 UnsafeChecker::visit (Lifetime
&lifetime
)
187 UnsafeChecker::visit (LifetimeParam
&lifetime_param
)
191 UnsafeChecker::visit (PathInExpression
&path
)
193 NodeId ast_node_id
= path
.get_mappings ().get_nodeid ();
197 if (!resolver
.lookup_resolved_name (ast_node_id
, &ref_node_id
))
200 rust_assert (mappings
.lookup_node_to_hir (ref_node_id
, &definition_id
));
202 check_use_of_static (definition_id
, path
.get_locus ());
206 UnsafeChecker::visit (TypePathSegment
&segment
)
210 UnsafeChecker::visit (TypePathSegmentGeneric
&segment
)
214 UnsafeChecker::visit (TypePathSegmentFunction
&segment
)
218 UnsafeChecker::visit (TypePath
&path
)
222 UnsafeChecker::visit (QualifiedPathInExpression
&path
)
226 UnsafeChecker::visit (QualifiedPathInType
&path
)
230 UnsafeChecker::visit (LiteralExpr
&expr
)
234 UnsafeChecker::visit (BorrowExpr
&expr
)
236 expr
.get_expr ()->accept_vis (*this);
240 UnsafeChecker::visit (DereferenceExpr
&expr
)
242 TyTy::BaseType
*to_deref_type
;
243 auto to_deref
= expr
.get_expr ()->get_mappings ().get_hirid ();
245 rust_assert (context
.lookup_type (to_deref
, &to_deref_type
));
247 if (to_deref_type
->get_kind () == TyTy::TypeKind::POINTER
248 && !unsafe_context
.is_in_context ())
249 rust_error_at (expr
.get_locus (), "dereference of raw pointer requires "
250 "unsafe function or block");
254 UnsafeChecker::visit (ErrorPropagationExpr
&expr
)
256 expr
.get_expr ()->accept_vis (*this);
260 UnsafeChecker::visit (NegationExpr
&expr
)
262 expr
.get_expr ()->accept_vis (*this);
266 UnsafeChecker::visit (ArithmeticOrLogicalExpr
&expr
)
268 expr
.get_lhs ()->accept_vis (*this);
269 expr
.get_rhs ()->accept_vis (*this);
273 UnsafeChecker::visit (ComparisonExpr
&expr
)
275 expr
.get_lhs ()->accept_vis (*this);
276 expr
.get_rhs ()->accept_vis (*this);
280 UnsafeChecker::visit (LazyBooleanExpr
&expr
)
282 expr
.get_lhs ()->accept_vis (*this);
283 expr
.get_rhs ()->accept_vis (*this);
287 UnsafeChecker::visit (TypeCastExpr
&expr
)
289 expr
.get_expr ()->accept_vis (*this);
293 UnsafeChecker::visit (AssignmentExpr
&expr
)
295 expr
.get_lhs ()->accept_vis (*this);
296 expr
.get_rhs ()->accept_vis (*this);
300 UnsafeChecker::visit (CompoundAssignmentExpr
&expr
)
302 expr
.get_left_expr ()->accept_vis (*this);
303 expr
.get_right_expr ()->accept_vis (*this);
307 UnsafeChecker::visit (GroupedExpr
&expr
)
309 expr
.get_expr_in_parens ()->accept_vis (*this);
313 UnsafeChecker::visit (ArrayElemsValues
&elems
)
315 for (auto &elem
: elems
.get_values ())
316 elem
->accept_vis (*this);
320 UnsafeChecker::visit (ArrayElemsCopied
&elems
)
322 elems
.get_elem_to_copy ()->accept_vis (*this);
326 UnsafeChecker::visit (ArrayExpr
&expr
)
328 expr
.get_internal_elements ()->accept_vis (*this);
332 UnsafeChecker::visit (ArrayIndexExpr
&expr
)
334 expr
.get_array_expr ()->accept_vis (*this);
335 expr
.get_index_expr ()->accept_vis (*this);
339 UnsafeChecker::visit (TupleExpr
&expr
)
341 for (auto &elem
: expr
.get_tuple_elems ())
342 elem
->accept_vis (*this);
346 UnsafeChecker::visit (TupleIndexExpr
&expr
)
348 expr
.get_tuple_expr ()->accept_vis (*this);
352 UnsafeChecker::visit (StructExprStruct
&expr
)
356 UnsafeChecker::visit (StructExprFieldIdentifier
&field
)
360 UnsafeChecker::visit (StructExprFieldIdentifierValue
&field
)
362 field
.get_value ()->accept_vis (*this);
366 UnsafeChecker::visit (StructExprFieldIndexValue
&field
)
368 field
.get_value ()->accept_vis (*this);
372 UnsafeChecker::visit (StructExprStructFields
&expr
)
374 for (auto &field
: expr
.get_fields ())
375 field
->accept_vis (*this);
379 UnsafeChecker::visit (StructExprStructBase
&expr
)
383 UnsafeChecker::visit (CallExpr
&expr
)
385 auto fn
= expr
.get_fnexpr ();
389 NodeId ast_node_id
= fn
->get_mappings ().get_nodeid ();
393 // There are no unsafe types, and functions are defined in the name resolver.
394 // If we can't find the name, then we're dealing with a type and should return
396 if (!resolver
.lookup_resolved_name (ast_node_id
, &ref_node_id
))
399 rust_assert (mappings
.lookup_node_to_hir (ref_node_id
, &definition_id
));
401 // At this point we have the function's HIR Id. There are two checks we
403 // 1. The function is an unsafe one
404 // 2. The function is an extern one
405 check_function_call (definition_id
, expr
.get_locus ());
407 if (expr
.has_params ())
408 for (auto &arg
: expr
.get_arguments ())
409 arg
->accept_vis (*this);
413 UnsafeChecker::visit (MethodCallExpr
&expr
)
415 TyTy::BaseType
*method_type
;
416 context
.lookup_type (expr
.get_method_name ().get_mappings ().get_hirid (),
419 auto fn
= *static_cast<TyTy::FnType
*> (method_type
);
420 auto method
= mappings
.lookup_hir_implitem (fn
.get_ref (), nullptr);
422 if (!unsafe_context
.is_in_context () && method
)
423 check_unsafe_call (static_cast<Function
*> (method
), expr
.get_locus (),
426 expr
.get_receiver ()->accept_vis (*this);
428 for (auto &arg
: expr
.get_arguments ())
429 arg
->accept_vis (*this);
433 UnsafeChecker::visit (FieldAccessExpr
&expr
)
435 expr
.get_receiver_expr ()->accept_vis (*this);
437 if (unsafe_context
.is_in_context ())
440 TyTy::BaseType
*receiver_ty
;
441 auto ok
= context
.lookup_type (
442 expr
.get_receiver_expr ()->get_mappings ().get_hirid (), &receiver_ty
);
445 if (receiver_ty
->get_kind () == TyTy::TypeKind::ADT
)
447 auto maybe_union
= static_cast<TyTy::ADTType
*> (receiver_ty
);
448 if (maybe_union
->is_union ())
451 "access to union field requires unsafe function or block");
456 UnsafeChecker::visit (ClosureExprInner
&expr
)
460 UnsafeChecker::visit (BlockExpr
&expr
)
462 for (auto &stmt
: expr
.get_statements ())
463 stmt
->accept_vis (*this);
465 if (expr
.has_expr ())
466 expr
.get_final_expr ()->accept_vis (*this);
470 UnsafeChecker::visit (ClosureExprInnerTyped
&expr
)
474 UnsafeChecker::visit (ContinueExpr
&expr
)
478 UnsafeChecker::visit (BreakExpr
&expr
)
480 if (expr
.has_break_expr ())
481 expr
.get_expr ()->accept_vis (*this);
485 UnsafeChecker::visit (RangeFromToExpr
&expr
)
487 expr
.get_from_expr ()->accept_vis (*this);
488 expr
.get_to_expr ()->accept_vis (*this);
492 UnsafeChecker::visit (RangeFromExpr
&expr
)
494 expr
.get_from_expr ()->accept_vis (*this);
498 UnsafeChecker::visit (RangeToExpr
&expr
)
500 expr
.get_to_expr ()->accept_vis (*this);
504 UnsafeChecker::visit (RangeFullExpr
&expr
)
508 UnsafeChecker::visit (RangeFromToInclExpr
&expr
)
510 expr
.get_from_expr ()->accept_vis (*this);
511 expr
.get_to_expr ()->accept_vis (*this);
515 UnsafeChecker::visit (RangeToInclExpr
&expr
)
517 expr
.get_to_expr ()->accept_vis (*this);
521 UnsafeChecker::visit (ReturnExpr
&expr
)
523 if (expr
.has_return_expr ())
524 expr
.get_expr ()->accept_vis (*this);
528 UnsafeChecker::visit (UnsafeBlockExpr
&expr
)
530 unsafe_context
.enter (expr
.get_mappings ().get_hirid ());
532 expr
.get_block_expr ()->accept_vis (*this);
534 unsafe_context
.exit ();
538 UnsafeChecker::visit (LoopExpr
&expr
)
540 expr
.get_loop_block ()->accept_vis (*this);
544 UnsafeChecker::visit (WhileLoopExpr
&expr
)
546 expr
.get_predicate_expr ()->accept_vis (*this);
547 expr
.get_loop_block ()->accept_vis (*this);
551 UnsafeChecker::visit (WhileLetLoopExpr
&expr
)
553 expr
.get_cond ()->accept_vis (*this);
554 expr
.get_loop_block ()->accept_vis (*this);
558 UnsafeChecker::visit (ForLoopExpr
&expr
)
560 expr
.get_iterator_expr ()->accept_vis (*this);
561 expr
.get_loop_block ()->accept_vis (*this);
565 UnsafeChecker::visit (IfExpr
&expr
)
567 expr
.get_if_condition ()->accept_vis (*this);
568 expr
.get_if_block ()->accept_vis (*this);
572 UnsafeChecker::visit (IfExprConseqElse
&expr
)
574 expr
.get_if_condition ()->accept_vis (*this);
575 expr
.get_if_block ()->accept_vis (*this);
576 expr
.get_else_block ()->accept_vis (*this);
580 UnsafeChecker::visit (IfExprConseqIf
&expr
)
582 expr
.get_if_condition ()->accept_vis (*this);
583 expr
.get_if_block ()->accept_vis (*this);
584 expr
.get_conseq_if_expr ()->accept_vis (*this);
588 UnsafeChecker::visit (IfExprConseqIfLet
&expr
)
590 expr
.get_if_condition ()->accept_vis (*this);
591 expr
.get_if_block ()->accept_vis (*this);
593 // TODO: Visit conseq if let expression
597 UnsafeChecker::visit (IfLetExpr
&expr
)
599 expr
.get_scrutinee_expr ()->accept_vis (*this);
600 expr
.get_if_block ()->accept_vis (*this);
604 UnsafeChecker::visit (IfLetExprConseqElse
&expr
)
606 expr
.get_scrutinee_expr ()->accept_vis (*this);
607 expr
.get_if_block ()->accept_vis (*this);
609 // TODO: Visit else expression
613 UnsafeChecker::visit (IfLetExprConseqIf
&expr
)
615 expr
.get_scrutinee_expr ()->accept_vis (*this);
616 expr
.get_if_block ()->accept_vis (*this);
620 UnsafeChecker::visit (IfLetExprConseqIfLet
&expr
)
622 expr
.get_scrutinee_expr ()->accept_vis (*this);
623 expr
.get_if_block ()->accept_vis (*this);
625 // TODO: Visit conseq if let expression
629 UnsafeChecker::visit (MatchExpr
&expr
)
631 expr
.get_scrutinee_expr ()->accept_vis (*this);
633 for (auto &match_arm
: expr
.get_match_cases ())
634 match_arm
.get_expr ()->accept_vis (*this);
638 UnsafeChecker::visit (AwaitExpr
&expr
)
640 // TODO: Visit expression
644 UnsafeChecker::visit (AsyncBlockExpr
&expr
)
646 // TODO: Visit block expression
650 UnsafeChecker::visit (TypeParam
¶m
)
654 UnsafeChecker::visit (ConstGenericParam
¶m
)
658 UnsafeChecker::visit (LifetimeWhereClauseItem
&item
)
662 UnsafeChecker::visit (TypeBoundWhereClauseItem
&item
)
666 UnsafeChecker::visit (Module
&module
)
668 for (auto &item
: module
.get_items ())
669 item
->accept_vis (*this);
673 UnsafeChecker::visit (ExternCrate
&crate
)
677 UnsafeChecker::visit (UseTreeGlob
&use_tree
)
681 UnsafeChecker::visit (UseTreeList
&use_tree
)
685 UnsafeChecker::visit (UseTreeRebind
&use_tree
)
689 UnsafeChecker::visit (UseDeclaration
&use_decl
)
693 UnsafeChecker::visit (Function
&function
)
695 auto is_unsafe_fn
= function
.get_qualifiers ().is_unsafe ();
698 unsafe_context
.enter (function
.get_mappings ().get_hirid ());
700 function
.get_definition ()->accept_vis (*this);
703 unsafe_context
.exit ();
707 UnsafeChecker::visit (TypeAlias
&type_alias
)
709 // FIXME: What do we need to do to handle type aliasing? Is it possible to
710 // have unsafe types? Type aliases on unsafe functions?
714 UnsafeChecker::visit (StructStruct
&struct_item
)
718 UnsafeChecker::visit (TupleStruct
&tuple_struct
)
722 UnsafeChecker::visit (EnumItem
&item
)
726 UnsafeChecker::visit (EnumItemTuple
&item
)
730 UnsafeChecker::visit (EnumItemStruct
&item
)
734 UnsafeChecker::visit (EnumItemDiscriminant
&item
)
738 UnsafeChecker::visit (Enum
&enum_item
)
742 UnsafeChecker::visit (Union
&union_item
)
746 UnsafeChecker::visit (ConstantItem
&const_item
)
748 const_item
.get_expr ()->accept_vis (*this);
752 UnsafeChecker::visit (StaticItem
&static_item
)
754 static_item
.get_expr ()->accept_vis (*this);
758 UnsafeChecker::visit (TraitItemFunc
&item
)
760 if (item
.has_block_defined ())
761 item
.get_block_expr ()->accept_vis (*this);
765 UnsafeChecker::visit (TraitItemConst
&item
)
767 if (item
.has_expr ())
768 item
.get_expr ()->accept_vis (*this);
772 UnsafeChecker::visit (TraitItemType
&item
)
776 UnsafeChecker::visit (Trait
&trait
)
778 // FIXME: Handle unsafe traits
779 for (auto &item
: trait
.get_trait_items ())
780 item
->accept_vis (*this);
784 UnsafeChecker::visit (ImplBlock
&impl
)
786 // FIXME: Handle unsafe impls
787 for (auto &item
: impl
.get_impl_items ())
788 item
->accept_vis (*this);
792 UnsafeChecker::visit (ExternalStaticItem
&item
)
796 UnsafeChecker::visit (ExternalFunctionItem
&item
)
800 UnsafeChecker::visit (ExternBlock
&block
)
802 // FIXME: Do we need to do this?
803 for (auto &item
: block
.get_extern_items ())
804 item
->accept_vis (*this);
808 UnsafeChecker::visit (LiteralPattern
&pattern
)
812 UnsafeChecker::visit (IdentifierPattern
&pattern
)
816 UnsafeChecker::visit (WildcardPattern
&pattern
)
820 UnsafeChecker::visit (RangePatternBoundLiteral
&bound
)
824 UnsafeChecker::visit (RangePatternBoundPath
&bound
)
828 UnsafeChecker::visit (RangePatternBoundQualPath
&bound
)
832 UnsafeChecker::visit (RangePattern
&pattern
)
836 UnsafeChecker::visit (ReferencePattern
&pattern
)
840 UnsafeChecker::visit (StructPatternFieldTuplePat
&field
)
844 UnsafeChecker::visit (StructPatternFieldIdentPat
&field
)
848 UnsafeChecker::visit (StructPatternFieldIdent
&field
)
852 UnsafeChecker::visit (StructPattern
&pattern
)
856 UnsafeChecker::visit (TupleStructItemsNoRange
&tuple_items
)
860 UnsafeChecker::visit (TupleStructItemsRange
&tuple_items
)
864 UnsafeChecker::visit (TupleStructPattern
&pattern
)
868 UnsafeChecker::visit (TuplePatternItemsMultiple
&tuple_items
)
872 UnsafeChecker::visit (TuplePatternItemsRanged
&tuple_items
)
876 UnsafeChecker::visit (TuplePattern
&pattern
)
880 UnsafeChecker::visit (GroupedPattern
&pattern
)
884 UnsafeChecker::visit (SlicePattern
&pattern
)
888 UnsafeChecker::visit (EmptyStmt
&stmt
)
892 UnsafeChecker::visit (LetStmt
&stmt
)
894 if (stmt
.has_init_expr ())
895 stmt
.get_init_expr ()->accept_vis (*this);
899 UnsafeChecker::visit (ExprStmtWithoutBlock
&stmt
)
901 stmt
.get_expr ()->accept_vis (*this);
905 UnsafeChecker::visit (ExprStmtWithBlock
&stmt
)
907 stmt
.get_expr ()->accept_vis (*this);
911 UnsafeChecker::visit (TraitBound
&bound
)
915 UnsafeChecker::visit (ImplTraitType
&type
)
919 UnsafeChecker::visit (TraitObjectType
&type
)
923 UnsafeChecker::visit (ParenthesisedType
&type
)
927 UnsafeChecker::visit (ImplTraitTypeOneBound
&type
)
931 UnsafeChecker::visit (TupleType
&type
)
935 UnsafeChecker::visit (NeverType
&type
)
939 UnsafeChecker::visit (RawPointerType
&type
)
943 UnsafeChecker::visit (ReferenceType
&type
)
947 UnsafeChecker::visit (ArrayType
&type
)
951 UnsafeChecker::visit (SliceType
&type
)
955 UnsafeChecker::visit (InferredType
&type
)
959 UnsafeChecker::visit (BareFunctionType
&type
)