1 // Copyright (C) 2020-2024 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 check_target_attr (HIR::Function
*fn
, Location locus
)
185 if (std::any_of (fn
->get_outer_attrs ().begin (),
186 fn
->get_outer_attrs ().end (),
187 [] (const AST::Attribute
&attr
) {
188 return attr
.get_path ().as_string () == "target_feature";
190 rust_error_at (locus
,
191 "call to function with %<#[target_feature]%> requires "
192 "unsafe function or block");
196 UnsafeChecker::check_function_attr (HirId node_id
, Location locus
)
198 if (unsafe_context
.is_in_context ())
201 auto maybe_fn
= mappings
.lookup_hir_item (node_id
);
203 if (maybe_fn
&& maybe_fn
->get_item_kind () == Item::ItemKind::Function
)
204 check_target_attr (static_cast<Function
*> (maybe_fn
), locus
);
208 UnsafeChecker::visit (Lifetime
&)
212 UnsafeChecker::visit (LifetimeParam
&)
216 UnsafeChecker::visit (PathInExpression
&path
)
218 NodeId ast_node_id
= path
.get_mappings ().get_nodeid ();
222 if (!resolver
.lookup_resolved_name (ast_node_id
, &ref_node_id
))
225 rust_assert (mappings
.lookup_node_to_hir (ref_node_id
, &definition_id
));
227 check_use_of_static (definition_id
, path
.get_locus ());
231 UnsafeChecker::visit (TypePathSegment
&)
235 UnsafeChecker::visit (TypePathSegmentGeneric
&)
239 UnsafeChecker::visit (TypePathSegmentFunction
&)
243 UnsafeChecker::visit (TypePath
&)
247 UnsafeChecker::visit (QualifiedPathInExpression
&)
251 UnsafeChecker::visit (QualifiedPathInType
&)
255 UnsafeChecker::visit (LiteralExpr
&)
259 UnsafeChecker::visit (BorrowExpr
&expr
)
261 expr
.get_expr ()->accept_vis (*this);
265 UnsafeChecker::visit (DereferenceExpr
&expr
)
267 TyTy::BaseType
*to_deref_type
;
268 auto to_deref
= expr
.get_expr ()->get_mappings ().get_hirid ();
270 rust_assert (context
.lookup_type (to_deref
, &to_deref_type
));
272 if (to_deref_type
->get_kind () == TyTy::TypeKind::POINTER
273 && !unsafe_context
.is_in_context ())
274 rust_error_at (expr
.get_locus (), "dereference of raw pointer requires "
275 "unsafe function or block");
279 UnsafeChecker::visit (ErrorPropagationExpr
&expr
)
281 expr
.get_expr ()->accept_vis (*this);
285 UnsafeChecker::visit (NegationExpr
&expr
)
287 expr
.get_expr ()->accept_vis (*this);
291 UnsafeChecker::visit (ArithmeticOrLogicalExpr
&expr
)
293 expr
.get_lhs ()->accept_vis (*this);
294 expr
.get_rhs ()->accept_vis (*this);
298 UnsafeChecker::visit (ComparisonExpr
&expr
)
300 expr
.get_lhs ()->accept_vis (*this);
301 expr
.get_rhs ()->accept_vis (*this);
305 UnsafeChecker::visit (LazyBooleanExpr
&expr
)
307 expr
.get_lhs ()->accept_vis (*this);
308 expr
.get_rhs ()->accept_vis (*this);
312 UnsafeChecker::visit (TypeCastExpr
&expr
)
314 expr
.get_expr ()->accept_vis (*this);
318 UnsafeChecker::visit (AssignmentExpr
&expr
)
320 expr
.get_lhs ()->accept_vis (*this);
321 expr
.get_rhs ()->accept_vis (*this);
325 UnsafeChecker::visit (CompoundAssignmentExpr
&expr
)
327 expr
.get_left_expr ()->accept_vis (*this);
328 expr
.get_right_expr ()->accept_vis (*this);
332 UnsafeChecker::visit (GroupedExpr
&expr
)
334 expr
.get_expr_in_parens ()->accept_vis (*this);
338 UnsafeChecker::visit (ArrayElemsValues
&elems
)
340 for (auto &elem
: elems
.get_values ())
341 elem
->accept_vis (*this);
345 UnsafeChecker::visit (ArrayElemsCopied
&elems
)
347 elems
.get_elem_to_copy ()->accept_vis (*this);
351 UnsafeChecker::visit (ArrayExpr
&expr
)
353 expr
.get_internal_elements ()->accept_vis (*this);
357 UnsafeChecker::visit (ArrayIndexExpr
&expr
)
359 expr
.get_array_expr ()->accept_vis (*this);
360 expr
.get_index_expr ()->accept_vis (*this);
364 UnsafeChecker::visit (TupleExpr
&expr
)
366 for (auto &elem
: expr
.get_tuple_elems ())
367 elem
->accept_vis (*this);
371 UnsafeChecker::visit (TupleIndexExpr
&expr
)
373 expr
.get_tuple_expr ()->accept_vis (*this);
377 UnsafeChecker::visit (StructExprStruct
&)
381 UnsafeChecker::visit (StructExprFieldIdentifier
&)
385 UnsafeChecker::visit (StructExprFieldIdentifierValue
&field
)
387 field
.get_value ()->accept_vis (*this);
391 UnsafeChecker::visit (StructExprFieldIndexValue
&field
)
393 field
.get_value ()->accept_vis (*this);
397 UnsafeChecker::visit (StructExprStructFields
&expr
)
399 for (auto &field
: expr
.get_fields ())
400 field
->accept_vis (*this);
404 UnsafeChecker::visit (StructExprStructBase
&)
408 UnsafeChecker::visit (CallExpr
&expr
)
410 auto fn
= expr
.get_fnexpr ();
414 NodeId ast_node_id
= fn
->get_mappings ().get_nodeid ();
418 // There are no unsafe types, and functions are defined in the name resolver.
419 // If we can't find the name, then we're dealing with a type and should return
421 if (!resolver
.lookup_resolved_name (ast_node_id
, &ref_node_id
))
424 rust_assert (mappings
.lookup_node_to_hir (ref_node_id
, &definition_id
));
426 // At this point we have the function's HIR Id. There are three checks we
428 // 1. The function is an unsafe one
429 // 2. The function is an extern one
430 // 3. The function is marked with a target_feature attribute
431 check_function_call (definition_id
, expr
.get_locus ());
432 check_function_attr (definition_id
, expr
.get_locus ());
434 if (expr
.has_params ())
435 for (auto &arg
: expr
.get_arguments ())
436 arg
->accept_vis (*this);
440 UnsafeChecker::visit (MethodCallExpr
&expr
)
442 TyTy::BaseType
*method_type
;
443 context
.lookup_type (expr
.get_method_name ().get_mappings ().get_hirid (),
446 auto fn
= *static_cast<TyTy::FnType
*> (method_type
);
447 auto method
= mappings
.lookup_hir_implitem (fn
.get_ref (), nullptr);
449 if (!unsafe_context
.is_in_context () && method
)
450 check_unsafe_call (static_cast<Function
*> (method
), expr
.get_locus (),
453 expr
.get_receiver ()->accept_vis (*this);
455 for (auto &arg
: expr
.get_arguments ())
456 arg
->accept_vis (*this);
460 UnsafeChecker::visit (FieldAccessExpr
&expr
)
462 expr
.get_receiver_expr ()->accept_vis (*this);
464 if (unsafe_context
.is_in_context ())
467 TyTy::BaseType
*receiver_ty
;
468 auto ok
= context
.lookup_type (
469 expr
.get_receiver_expr ()->get_mappings ().get_hirid (), &receiver_ty
);
472 if (receiver_ty
->get_kind () == TyTy::TypeKind::ADT
)
474 auto maybe_union
= static_cast<TyTy::ADTType
*> (receiver_ty
);
475 if (maybe_union
->is_union ())
478 "access to union field requires unsafe function or block");
483 UnsafeChecker::visit (ClosureExpr
&expr
)
485 expr
.get_expr ()->accept_vis (*this);
489 UnsafeChecker::visit (BlockExpr
&expr
)
491 for (auto &stmt
: expr
.get_statements ())
492 stmt
->accept_vis (*this);
494 if (expr
.has_expr ())
495 expr
.get_final_expr ()->accept_vis (*this);
499 UnsafeChecker::visit (ContinueExpr
&)
503 UnsafeChecker::visit (BreakExpr
&expr
)
505 if (expr
.has_break_expr ())
506 expr
.get_expr ()->accept_vis (*this);
510 UnsafeChecker::visit (RangeFromToExpr
&expr
)
512 expr
.get_from_expr ()->accept_vis (*this);
513 expr
.get_to_expr ()->accept_vis (*this);
517 UnsafeChecker::visit (RangeFromExpr
&expr
)
519 expr
.get_from_expr ()->accept_vis (*this);
523 UnsafeChecker::visit (RangeToExpr
&expr
)
525 expr
.get_to_expr ()->accept_vis (*this);
529 UnsafeChecker::visit (RangeFullExpr
&)
533 UnsafeChecker::visit (RangeFromToInclExpr
&expr
)
535 expr
.get_from_expr ()->accept_vis (*this);
536 expr
.get_to_expr ()->accept_vis (*this);
540 UnsafeChecker::visit (RangeToInclExpr
&expr
)
542 expr
.get_to_expr ()->accept_vis (*this);
546 UnsafeChecker::visit (ReturnExpr
&expr
)
548 if (expr
.has_return_expr ())
549 expr
.get_expr ()->accept_vis (*this);
553 UnsafeChecker::visit (UnsafeBlockExpr
&expr
)
555 unsafe_context
.enter (expr
.get_mappings ().get_hirid ());
557 expr
.get_block_expr ()->accept_vis (*this);
559 unsafe_context
.exit ();
563 UnsafeChecker::visit (LoopExpr
&expr
)
565 expr
.get_loop_block ()->accept_vis (*this);
569 UnsafeChecker::visit (WhileLoopExpr
&expr
)
571 expr
.get_predicate_expr ()->accept_vis (*this);
572 expr
.get_loop_block ()->accept_vis (*this);
576 UnsafeChecker::visit (WhileLetLoopExpr
&expr
)
578 expr
.get_cond ()->accept_vis (*this);
579 expr
.get_loop_block ()->accept_vis (*this);
583 UnsafeChecker::visit (ForLoopExpr
&expr
)
585 expr
.get_iterator_expr ()->accept_vis (*this);
586 expr
.get_loop_block ()->accept_vis (*this);
590 UnsafeChecker::visit (IfExpr
&expr
)
592 expr
.get_if_condition ()->accept_vis (*this);
593 expr
.get_if_block ()->accept_vis (*this);
597 UnsafeChecker::visit (IfExprConseqElse
&expr
)
599 expr
.get_if_condition ()->accept_vis (*this);
600 expr
.get_if_block ()->accept_vis (*this);
601 expr
.get_else_block ()->accept_vis (*this);
605 UnsafeChecker::visit (IfExprConseqIf
&expr
)
607 expr
.get_if_condition ()->accept_vis (*this);
608 expr
.get_if_block ()->accept_vis (*this);
609 expr
.get_conseq_if_expr ()->accept_vis (*this);
613 UnsafeChecker::visit (IfExprConseqIfLet
&expr
)
615 expr
.get_if_condition ()->accept_vis (*this);
616 expr
.get_if_block ()->accept_vis (*this);
618 // TODO: Visit conseq if let expression
622 UnsafeChecker::visit (IfLetExpr
&expr
)
624 expr
.get_scrutinee_expr ()->accept_vis (*this);
625 expr
.get_if_block ()->accept_vis (*this);
629 UnsafeChecker::visit (IfLetExprConseqElse
&expr
)
631 expr
.get_scrutinee_expr ()->accept_vis (*this);
632 expr
.get_if_block ()->accept_vis (*this);
634 // TODO: Visit else expression
638 UnsafeChecker::visit (IfLetExprConseqIf
&expr
)
640 expr
.get_scrutinee_expr ()->accept_vis (*this);
641 expr
.get_if_block ()->accept_vis (*this);
645 UnsafeChecker::visit (IfLetExprConseqIfLet
&expr
)
647 expr
.get_scrutinee_expr ()->accept_vis (*this);
648 expr
.get_if_block ()->accept_vis (*this);
650 // TODO: Visit conseq if let expression
654 UnsafeChecker::visit (MatchExpr
&expr
)
656 expr
.get_scrutinee_expr ()->accept_vis (*this);
658 for (auto &match_arm
: expr
.get_match_cases ())
659 match_arm
.get_expr ()->accept_vis (*this);
663 UnsafeChecker::visit (AwaitExpr
&)
665 // TODO: Visit expression
669 UnsafeChecker::visit (AsyncBlockExpr
&)
671 // TODO: Visit block expression
675 UnsafeChecker::visit (TypeParam
&)
679 UnsafeChecker::visit (ConstGenericParam
&)
683 UnsafeChecker::visit (LifetimeWhereClauseItem
&)
687 UnsafeChecker::visit (TypeBoundWhereClauseItem
&)
691 UnsafeChecker::visit (Module
&module
)
693 for (auto &item
: module
.get_items ())
694 item
->accept_vis (*this);
698 UnsafeChecker::visit (ExternCrate
&)
702 UnsafeChecker::visit (UseTreeGlob
&)
706 UnsafeChecker::visit (UseTreeList
&)
710 UnsafeChecker::visit (UseTreeRebind
&)
714 UnsafeChecker::visit (UseDeclaration
&)
718 UnsafeChecker::visit (Function
&function
)
720 auto is_unsafe_fn
= function
.get_qualifiers ().is_unsafe ();
723 unsafe_context
.enter (function
.get_mappings ().get_hirid ());
725 function
.get_definition ()->accept_vis (*this);
728 unsafe_context
.exit ();
732 UnsafeChecker::visit (TypeAlias
&)
734 // FIXME: What do we need to do to handle type aliasing? Is it possible to
735 // have unsafe types? Type aliases on unsafe functions?
739 UnsafeChecker::visit (StructStruct
&)
743 UnsafeChecker::visit (TupleStruct
&)
747 UnsafeChecker::visit (EnumItem
&)
751 UnsafeChecker::visit (EnumItemTuple
&)
755 UnsafeChecker::visit (EnumItemStruct
&)
759 UnsafeChecker::visit (EnumItemDiscriminant
&)
763 UnsafeChecker::visit (Enum
&)
767 UnsafeChecker::visit (Union
&)
771 UnsafeChecker::visit (ConstantItem
&const_item
)
773 const_item
.get_expr ()->accept_vis (*this);
777 UnsafeChecker::visit (StaticItem
&static_item
)
779 static_item
.get_expr ()->accept_vis (*this);
783 UnsafeChecker::visit (TraitItemFunc
&item
)
785 if (item
.has_block_defined ())
786 item
.get_block_expr ()->accept_vis (*this);
790 UnsafeChecker::visit (TraitItemConst
&item
)
792 if (item
.has_expr ())
793 item
.get_expr ()->accept_vis (*this);
797 UnsafeChecker::visit (TraitItemType
&)
801 UnsafeChecker::visit (Trait
&trait
)
803 // FIXME: Handle unsafe traits
804 for (auto &item
: trait
.get_trait_items ())
805 item
->accept_vis (*this);
809 UnsafeChecker::visit (ImplBlock
&impl
)
811 // FIXME: Handle unsafe impls
812 for (auto &item
: impl
.get_impl_items ())
813 item
->accept_vis (*this);
817 UnsafeChecker::visit (ExternalStaticItem
&)
821 UnsafeChecker::visit (ExternalFunctionItem
&)
825 UnsafeChecker::visit (ExternBlock
&block
)
827 // FIXME: Do we need to do this?
828 for (auto &item
: block
.get_extern_items ())
829 item
->accept_vis (*this);
833 UnsafeChecker::visit (LiteralPattern
&)
837 UnsafeChecker::visit (IdentifierPattern
&)
841 UnsafeChecker::visit (WildcardPattern
&)
845 UnsafeChecker::visit (RangePatternBoundLiteral
&)
849 UnsafeChecker::visit (RangePatternBoundPath
&)
853 UnsafeChecker::visit (RangePatternBoundQualPath
&)
857 UnsafeChecker::visit (RangePattern
&)
861 UnsafeChecker::visit (ReferencePattern
&)
865 UnsafeChecker::visit (StructPatternFieldTuplePat
&)
869 UnsafeChecker::visit (StructPatternFieldIdentPat
&)
873 UnsafeChecker::visit (StructPatternFieldIdent
&)
877 UnsafeChecker::visit (StructPattern
&)
881 UnsafeChecker::visit (TupleStructItemsNoRange
&)
885 UnsafeChecker::visit (TupleStructItemsRange
&)
889 UnsafeChecker::visit (TupleStructPattern
&)
893 UnsafeChecker::visit (TuplePatternItemsMultiple
&)
897 UnsafeChecker::visit (TuplePatternItemsRanged
&)
901 UnsafeChecker::visit (TuplePattern
&)
905 UnsafeChecker::visit (SlicePattern
&)
909 UnsafeChecker::visit (EmptyStmt
&)
913 UnsafeChecker::visit (LetStmt
&stmt
)
915 if (stmt
.has_init_expr ())
916 stmt
.get_init_expr ()->accept_vis (*this);
920 UnsafeChecker::visit (ExprStmtWithoutBlock
&stmt
)
922 stmt
.get_expr ()->accept_vis (*this);
926 UnsafeChecker::visit (ExprStmtWithBlock
&stmt
)
928 stmt
.get_expr ()->accept_vis (*this);
932 UnsafeChecker::visit (TraitBound
&)
936 UnsafeChecker::visit (ImplTraitType
&)
940 UnsafeChecker::visit (TraitObjectType
&)
944 UnsafeChecker::visit (ParenthesisedType
&)
948 UnsafeChecker::visit (ImplTraitTypeOneBound
&)
952 UnsafeChecker::visit (TupleType
&)
956 UnsafeChecker::visit (NeverType
&)
960 UnsafeChecker::visit (RawPointerType
&)
964 UnsafeChecker::visit (ReferenceType
&)
968 UnsafeChecker::visit (ArrayType
&)
972 UnsafeChecker::visit (SliceType
&)
976 UnsafeChecker::visit (InferredType
&)
980 UnsafeChecker::visit (BareFunctionType
&)