]> git.proxmox.com Git - rustc.git/blob - compiler/rustc_mir_build/src/check_unsafety.rs
New upstream version 1.55.0+dfsg1
[rustc.git] / compiler / rustc_mir_build / src / check_unsafety.rs
1 use crate::build::ExprCategory;
2 use crate::thir::visit::{self, Visitor};
3
4 use rustc_errors::struct_span_err;
5 use rustc_hir as hir;
6 use rustc_middle::mir::BorrowKind;
7 use rustc_middle::thir::*;
8 use rustc_middle::ty::{self, ParamEnv, TyCtxt};
9 use rustc_session::lint::builtin::{UNSAFE_OP_IN_UNSAFE_FN, UNUSED_UNSAFE};
10 use rustc_session::lint::Level;
11 use rustc_span::def_id::{DefId, LocalDefId};
12 use rustc_span::symbol::Symbol;
13 use rustc_span::Span;
14
15 use std::ops::Bound;
16
17 struct UnsafetyVisitor<'a, 'tcx> {
18 tcx: TyCtxt<'tcx>,
19 thir: &'a Thir<'tcx>,
20 /// The `HirId` of the current scope, which would be the `HirId`
21 /// of the current HIR node, modulo adjustments. Used for lint levels.
22 hir_context: hir::HirId,
23 /// The current "safety context". This notably tracks whether we are in an
24 /// `unsafe` block, and whether it has been used.
25 safety_context: SafetyContext,
26 body_unsafety: BodyUnsafety,
27 /// The `#[target_feature]` attributes of the body. Used for checking
28 /// calls to functions with `#[target_feature]` (RFC 2396).
29 body_target_features: &'tcx Vec<Symbol>,
30 in_possible_lhs_union_assign: bool,
31 in_union_destructure: bool,
32 param_env: ParamEnv<'tcx>,
33 inside_adt: bool,
34 }
35
36 impl<'tcx> UnsafetyVisitor<'_, 'tcx> {
37 fn in_safety_context(&mut self, safety_context: SafetyContext, f: impl FnOnce(&mut Self)) {
38 if let (
39 SafetyContext::UnsafeBlock { span: enclosing_span, .. },
40 SafetyContext::UnsafeBlock { span: block_span, hir_id, .. },
41 ) = (self.safety_context, safety_context)
42 {
43 self.warn_unused_unsafe(
44 hir_id,
45 block_span,
46 Some((self.tcx.sess.source_map().guess_head_span(enclosing_span), "block")),
47 );
48 f(self);
49 } else {
50 let prev_context = self.safety_context;
51 self.safety_context = safety_context;
52
53 f(self);
54
55 if let SafetyContext::UnsafeBlock { used: false, span, hir_id } = self.safety_context {
56 self.warn_unused_unsafe(
57 hir_id,
58 span,
59 if self.unsafe_op_in_unsafe_fn_allowed() {
60 self.body_unsafety.unsafe_fn_sig_span().map(|span| (span, "fn"))
61 } else {
62 None
63 },
64 );
65 }
66 self.safety_context = prev_context;
67 }
68 }
69
70 fn requires_unsafe(&mut self, span: Span, kind: UnsafeOpKind) {
71 let (description, note) = kind.description_and_note();
72 let unsafe_op_in_unsafe_fn_allowed = self.unsafe_op_in_unsafe_fn_allowed();
73 match self.safety_context {
74 SafetyContext::BuiltinUnsafeBlock => {}
75 SafetyContext::UnsafeBlock { ref mut used, .. } => {
76 if !self.body_unsafety.is_unsafe() || !unsafe_op_in_unsafe_fn_allowed {
77 // Mark this block as useful
78 *used = true;
79 }
80 }
81 SafetyContext::UnsafeFn if unsafe_op_in_unsafe_fn_allowed => {}
82 SafetyContext::UnsafeFn => {
83 // unsafe_op_in_unsafe_fn is disallowed
84 self.tcx.struct_span_lint_hir(
85 UNSAFE_OP_IN_UNSAFE_FN,
86 self.hir_context,
87 span,
88 |lint| {
89 lint.build(&format!(
90 "{} is unsafe and requires unsafe block (error E0133)",
91 description,
92 ))
93 .span_label(span, description)
94 .note(note)
95 .emit();
96 },
97 )
98 }
99 SafetyContext::Safe => {
100 let fn_sugg = if unsafe_op_in_unsafe_fn_allowed { " function or" } else { "" };
101 struct_span_err!(
102 self.tcx.sess,
103 span,
104 E0133,
105 "{} is unsafe and requires unsafe{} block",
106 description,
107 fn_sugg,
108 )
109 .span_label(span, description)
110 .note(note)
111 .emit();
112 }
113 }
114 }
115
116 fn warn_unused_unsafe(
117 &self,
118 hir_id: hir::HirId,
119 block_span: Span,
120 enclosing_unsafe: Option<(Span, &'static str)>,
121 ) {
122 let block_span = self.tcx.sess.source_map().guess_head_span(block_span);
123 self.tcx.struct_span_lint_hir(UNUSED_UNSAFE, hir_id, block_span, |lint| {
124 let msg = "unnecessary `unsafe` block";
125 let mut db = lint.build(msg);
126 db.span_label(block_span, msg);
127 if let Some((span, kind)) = enclosing_unsafe {
128 db.span_label(span, format!("because it's nested under this `unsafe` {}", kind));
129 }
130 db.emit();
131 });
132 }
133
134 /// Whether the `unsafe_op_in_unsafe_fn` lint is `allow`ed at the current HIR node.
135 fn unsafe_op_in_unsafe_fn_allowed(&self) -> bool {
136 self.tcx.lint_level_at_node(UNSAFE_OP_IN_UNSAFE_FN, self.hir_context).0 == Level::Allow
137 }
138 }
139
140 // Searches for accesses to layout constrained fields.
141 struct LayoutConstrainedPlaceVisitor<'a, 'tcx> {
142 found: bool,
143 thir: &'a Thir<'tcx>,
144 tcx: TyCtxt<'tcx>,
145 }
146
147 impl<'a, 'tcx> LayoutConstrainedPlaceVisitor<'a, 'tcx> {
148 fn new(thir: &'a Thir<'tcx>, tcx: TyCtxt<'tcx>) -> Self {
149 Self { found: false, thir, tcx }
150 }
151 }
152
153 impl<'a, 'tcx> Visitor<'a, 'tcx> for LayoutConstrainedPlaceVisitor<'a, 'tcx> {
154 fn thir(&self) -> &'a Thir<'tcx> {
155 self.thir
156 }
157
158 fn visit_expr(&mut self, expr: &Expr<'tcx>) {
159 match expr.kind {
160 ExprKind::Field { lhs, .. } => {
161 if let ty::Adt(adt_def, _) = self.thir[lhs].ty.kind() {
162 if (Bound::Unbounded, Bound::Unbounded)
163 != self.tcx.layout_scalar_valid_range(adt_def.did)
164 {
165 self.found = true;
166 }
167 }
168 visit::walk_expr(self, expr);
169 }
170
171 // Keep walking through the expression as long as we stay in the same
172 // place, i.e. the expression is a place expression and not a dereference
173 // (since dereferencing something leads us to a different place).
174 ExprKind::Deref { .. } => {}
175 ref kind if ExprCategory::of(kind).map_or(true, |cat| cat == ExprCategory::Place) => {
176 visit::walk_expr(self, expr);
177 }
178
179 _ => {}
180 }
181 }
182 }
183
184 impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
185 fn thir(&self) -> &'a Thir<'tcx> {
186 &self.thir
187 }
188
189 fn visit_block(&mut self, block: &Block) {
190 match block.safety_mode {
191 // compiler-generated unsafe code should not count towards the usefulness of
192 // an outer unsafe block
193 BlockSafety::BuiltinUnsafe => {
194 self.in_safety_context(SafetyContext::BuiltinUnsafeBlock, |this| {
195 visit::walk_block(this, block)
196 });
197 }
198 BlockSafety::ExplicitUnsafe(hir_id) => {
199 self.in_safety_context(
200 SafetyContext::UnsafeBlock { span: block.span, hir_id, used: false },
201 |this| visit::walk_block(this, block),
202 );
203 }
204 BlockSafety::Safe => {
205 visit::walk_block(self, block);
206 }
207 }
208 }
209
210 fn visit_pat(&mut self, pat: &Pat<'tcx>) {
211 if self.in_union_destructure {
212 match *pat.kind {
213 // binding to a variable allows getting stuff out of variable
214 PatKind::Binding { .. }
215 // match is conditional on having this value
216 | PatKind::Constant { .. }
217 | PatKind::Variant { .. }
218 | PatKind::Leaf { .. }
219 | PatKind::Deref { .. }
220 | PatKind::Range { .. }
221 | PatKind::Slice { .. }
222 | PatKind::Array { .. } => {
223 self.requires_unsafe(pat.span, AccessToUnionField);
224 return; // we can return here since this already requires unsafe
225 }
226 // wildcard doesn't take anything
227 PatKind::Wild |
228 // these just wrap other patterns
229 PatKind::Or { .. } |
230 PatKind::AscribeUserType { .. } => {}
231 }
232 };
233
234 match &*pat.kind {
235 PatKind::Leaf { .. } => {
236 if let ty::Adt(adt_def, ..) = pat.ty.kind() {
237 if adt_def.is_union() {
238 let old_in_union_destructure =
239 std::mem::replace(&mut self.in_union_destructure, true);
240 visit::walk_pat(self, pat);
241 self.in_union_destructure = old_in_union_destructure;
242 } else if (Bound::Unbounded, Bound::Unbounded)
243 != self.tcx.layout_scalar_valid_range(adt_def.did)
244 {
245 let old_inside_adt = std::mem::replace(&mut self.inside_adt, true);
246 visit::walk_pat(self, pat);
247 self.inside_adt = old_inside_adt;
248 } else {
249 visit::walk_pat(self, pat);
250 }
251 } else {
252 visit::walk_pat(self, pat);
253 }
254 }
255 PatKind::Binding { mode: BindingMode::ByRef(borrow_kind), ty, .. } => {
256 if self.inside_adt {
257 if let ty::Ref(_, ty, _) = ty.kind() {
258 match borrow_kind {
259 BorrowKind::Shallow | BorrowKind::Shared | BorrowKind::Unique => {
260 if !ty.is_freeze(self.tcx.at(pat.span), self.param_env) {
261 self.requires_unsafe(pat.span, BorrowOfLayoutConstrainedField);
262 }
263 }
264 BorrowKind::Mut { .. } => {
265 self.requires_unsafe(pat.span, MutationOfLayoutConstrainedField);
266 }
267 }
268 } else {
269 span_bug!(
270 pat.span,
271 "BindingMode::ByRef in pattern, but found non-reference type {}",
272 ty
273 );
274 }
275 }
276 visit::walk_pat(self, pat);
277 }
278 PatKind::Deref { .. } => {
279 let old_inside_adt = std::mem::replace(&mut self.inside_adt, false);
280 visit::walk_pat(self, pat);
281 self.inside_adt = old_inside_adt;
282 }
283 _ => {
284 visit::walk_pat(self, pat);
285 }
286 }
287 }
288
289 fn visit_expr(&mut self, expr: &Expr<'tcx>) {
290 // could we be in a the LHS of an assignment of a union?
291 match expr.kind {
292 ExprKind::Field { .. }
293 | ExprKind::VarRef { .. }
294 | ExprKind::UpvarRef { .. }
295 | ExprKind::Scope { .. }
296 | ExprKind::Cast { .. } => {}
297
298 ExprKind::AddressOf { .. }
299 | ExprKind::Adt { .. }
300 | ExprKind::Array { .. }
301 | ExprKind::Binary { .. }
302 | ExprKind::Block { .. }
303 | ExprKind::Borrow { .. }
304 | ExprKind::Literal { .. }
305 | ExprKind::ConstBlock { .. }
306 | ExprKind::Deref { .. }
307 | ExprKind::Index { .. }
308 | ExprKind::NeverToAny { .. }
309 | ExprKind::PlaceTypeAscription { .. }
310 | ExprKind::ValueTypeAscription { .. }
311 | ExprKind::Pointer { .. }
312 | ExprKind::Repeat { .. }
313 | ExprKind::StaticRef { .. }
314 | ExprKind::ThreadLocalRef { .. }
315 | ExprKind::Tuple { .. }
316 | ExprKind::Unary { .. }
317 | ExprKind::Call { .. }
318 | ExprKind::Assign { .. }
319 | ExprKind::AssignOp { .. }
320 | ExprKind::Break { .. }
321 | ExprKind::Closure { .. }
322 | ExprKind::Continue { .. }
323 | ExprKind::Return { .. }
324 | ExprKind::Yield { .. }
325 | ExprKind::Loop { .. }
326 | ExprKind::Match { .. }
327 | ExprKind::Box { .. }
328 | ExprKind::If { .. }
329 | ExprKind::InlineAsm { .. }
330 | ExprKind::LlvmInlineAsm { .. }
331 | ExprKind::LogicalOp { .. }
332 | ExprKind::Use { .. } => self.in_possible_lhs_union_assign = false,
333 };
334 match expr.kind {
335 ExprKind::Scope { value, lint_level: LintLevel::Explicit(hir_id), region_scope: _ } => {
336 let prev_id = self.hir_context;
337 self.hir_context = hir_id;
338 self.visit_expr(&self.thir[value]);
339 self.hir_context = prev_id;
340 return; // don't visit the whole expression
341 }
342 ExprKind::Call { fun, ty: _, args: _, from_hir_call: _, fn_span: _ } => {
343 if self.thir[fun].ty.fn_sig(self.tcx).unsafety() == hir::Unsafety::Unsafe {
344 self.requires_unsafe(expr.span, CallToUnsafeFunction);
345 } else if let &ty::FnDef(func_did, _) = self.thir[fun].ty.kind() {
346 // If the called function has target features the calling function hasn't,
347 // the call requires `unsafe`. Don't check this on wasm
348 // targets, though. For more information on wasm see the
349 // is_like_wasm check in typeck/src/collect.rs
350 if !self.tcx.sess.target.options.is_like_wasm
351 && !self
352 .tcx
353 .codegen_fn_attrs(func_did)
354 .target_features
355 .iter()
356 .all(|feature| self.body_target_features.contains(feature))
357 {
358 self.requires_unsafe(expr.span, CallToFunctionWith);
359 }
360 }
361 }
362 ExprKind::Deref { arg } => {
363 if let ExprKind::StaticRef { def_id, .. } = self.thir[arg].kind {
364 if self.tcx.is_mutable_static(def_id) {
365 self.requires_unsafe(expr.span, UseOfMutableStatic);
366 } else if self.tcx.is_foreign_item(def_id) {
367 self.requires_unsafe(expr.span, UseOfExternStatic);
368 }
369 } else if self.thir[arg].ty.is_unsafe_ptr() {
370 self.requires_unsafe(expr.span, DerefOfRawPointer);
371 }
372 }
373 ExprKind::InlineAsm { .. } | ExprKind::LlvmInlineAsm { .. } => {
374 self.requires_unsafe(expr.span, UseOfInlineAssembly);
375 }
376 ExprKind::Adt(box Adt {
377 adt_def,
378 variant_index: _,
379 substs: _,
380 user_ty: _,
381 fields: _,
382 base: _,
383 }) => match self.tcx.layout_scalar_valid_range(adt_def.did) {
384 (Bound::Unbounded, Bound::Unbounded) => {}
385 _ => self.requires_unsafe(expr.span, InitializingTypeWith),
386 },
387 ExprKind::Closure {
388 closure_id,
389 substs: _,
390 upvars: _,
391 movability: _,
392 fake_reads: _,
393 } => {
394 let closure_id = closure_id.expect_local();
395 let closure_def = if let Some((did, const_param_id)) =
396 ty::WithOptConstParam::try_lookup(closure_id, self.tcx)
397 {
398 ty::WithOptConstParam { did, const_param_did: Some(const_param_id) }
399 } else {
400 ty::WithOptConstParam::unknown(closure_id)
401 };
402 let (closure_thir, expr) = self.tcx.thir_body(closure_def);
403 let closure_thir = &closure_thir.borrow();
404 let hir_context = self.tcx.hir().local_def_id_to_hir_id(closure_id);
405 let mut closure_visitor =
406 UnsafetyVisitor { thir: closure_thir, hir_context, ..*self };
407 closure_visitor.visit_expr(&closure_thir[expr]);
408 // Unsafe blocks can be used in closures, make sure to take it into account
409 self.safety_context = closure_visitor.safety_context;
410 }
411 ExprKind::Field { lhs, .. } => {
412 // assigning to union field is okay for AccessToUnionField
413 if let ty::Adt(adt_def, _) = &self.thir[lhs].ty.kind() {
414 if adt_def.is_union() {
415 if self.in_possible_lhs_union_assign {
416 // FIXME: trigger AssignToDroppingUnionField unsafety if needed
417 } else {
418 self.requires_unsafe(expr.span, AccessToUnionField);
419 }
420 }
421 }
422 }
423 ExprKind::Assign { lhs, rhs } | ExprKind::AssignOp { lhs, rhs, .. } => {
424 // First, check whether we are mutating a layout constrained field
425 let mut visitor = LayoutConstrainedPlaceVisitor::new(self.thir, self.tcx);
426 visit::walk_expr(&mut visitor, &self.thir[lhs]);
427 if visitor.found {
428 self.requires_unsafe(expr.span, MutationOfLayoutConstrainedField);
429 }
430
431 // Second, check for accesses to union fields
432 // don't have any special handling for AssignOp since it causes a read *and* write to lhs
433 if matches!(expr.kind, ExprKind::Assign { .. }) {
434 // assigning to a union is safe, check here so it doesn't get treated as a read later
435 self.in_possible_lhs_union_assign = true;
436 visit::walk_expr(self, &self.thir()[lhs]);
437 self.in_possible_lhs_union_assign = false;
438 visit::walk_expr(self, &self.thir()[rhs]);
439 return; // we have already visited everything by now
440 }
441 }
442 ExprKind::Borrow { borrow_kind, arg } => match borrow_kind {
443 BorrowKind::Shallow | BorrowKind::Shared | BorrowKind::Unique => {
444 if !self.thir[arg]
445 .ty
446 .is_freeze(self.tcx.at(self.thir[arg].span), self.param_env)
447 {
448 let mut visitor = LayoutConstrainedPlaceVisitor::new(self.thir, self.tcx);
449 visit::walk_expr(&mut visitor, expr);
450 if visitor.found {
451 self.requires_unsafe(expr.span, BorrowOfLayoutConstrainedField);
452 }
453 }
454 }
455 BorrowKind::Mut { .. } => {
456 let mut visitor = LayoutConstrainedPlaceVisitor::new(self.thir, self.tcx);
457 visit::walk_expr(&mut visitor, expr);
458 if visitor.found {
459 self.requires_unsafe(expr.span, MutationOfLayoutConstrainedField);
460 }
461 }
462 },
463 _ => {}
464 }
465 visit::walk_expr(self, expr);
466 }
467 }
468
469 #[derive(Clone, Copy)]
470 enum SafetyContext {
471 Safe,
472 BuiltinUnsafeBlock,
473 UnsafeFn,
474 UnsafeBlock { span: Span, hir_id: hir::HirId, used: bool },
475 }
476
477 #[derive(Clone, Copy)]
478 enum BodyUnsafety {
479 /// The body is not unsafe.
480 Safe,
481 /// The body is an unsafe function. The span points to
482 /// the signature of the function.
483 Unsafe(Span),
484 }
485
486 impl BodyUnsafety {
487 /// Returns whether the body is unsafe.
488 fn is_unsafe(&self) -> bool {
489 matches!(self, BodyUnsafety::Unsafe(_))
490 }
491
492 /// If the body is unsafe, returns the `Span` of its signature.
493 fn unsafe_fn_sig_span(self) -> Option<Span> {
494 match self {
495 BodyUnsafety::Unsafe(span) => Some(span),
496 BodyUnsafety::Safe => None,
497 }
498 }
499 }
500
501 #[derive(Clone, Copy, PartialEq)]
502 enum UnsafeOpKind {
503 CallToUnsafeFunction,
504 UseOfInlineAssembly,
505 InitializingTypeWith,
506 UseOfMutableStatic,
507 UseOfExternStatic,
508 DerefOfRawPointer,
509 #[allow(dead_code)] // FIXME
510 AssignToDroppingUnionField,
511 AccessToUnionField,
512 #[allow(dead_code)] // FIXME
513 MutationOfLayoutConstrainedField,
514 #[allow(dead_code)] // FIXME
515 BorrowOfLayoutConstrainedField,
516 CallToFunctionWith,
517 }
518
519 use UnsafeOpKind::*;
520
521 impl UnsafeOpKind {
522 pub fn description_and_note(&self) -> (&'static str, &'static str) {
523 match self {
524 CallToUnsafeFunction => (
525 "call to unsafe function",
526 "consult the function's documentation for information on how to avoid undefined \
527 behavior",
528 ),
529 UseOfInlineAssembly => (
530 "use of inline assembly",
531 "inline assembly is entirely unchecked and can cause undefined behavior",
532 ),
533 InitializingTypeWith => (
534 "initializing type with `rustc_layout_scalar_valid_range` attr",
535 "initializing a layout restricted type's field with a value outside the valid \
536 range is undefined behavior",
537 ),
538 UseOfMutableStatic => (
539 "use of mutable static",
540 "mutable statics can be mutated by multiple threads: aliasing violations or data \
541 races will cause undefined behavior",
542 ),
543 UseOfExternStatic => (
544 "use of extern static",
545 "extern statics are not controlled by the Rust type system: invalid data, \
546 aliasing violations or data races will cause undefined behavior",
547 ),
548 DerefOfRawPointer => (
549 "dereference of raw pointer",
550 "raw pointers may be null, dangling or unaligned; they can violate aliasing rules \
551 and cause data races: all of these are undefined behavior",
552 ),
553 AssignToDroppingUnionField => (
554 "assignment to union field that might need dropping",
555 "the previous content of the field will be dropped, which causes undefined \
556 behavior if the field was not properly initialized",
557 ),
558 AccessToUnionField => (
559 "access to union field",
560 "the field may not be properly initialized: using uninitialized data will cause \
561 undefined behavior",
562 ),
563 MutationOfLayoutConstrainedField => (
564 "mutation of layout constrained field",
565 "mutating layout constrained fields cannot statically be checked for valid values",
566 ),
567 BorrowOfLayoutConstrainedField => (
568 "borrow of layout constrained field with interior mutability",
569 "references to fields of layout constrained fields lose the constraints. Coupled \
570 with interior mutability, the field can be changed to invalid values",
571 ),
572 CallToFunctionWith => (
573 "call to function with `#[target_feature]`",
574 "can only be called if the required target features are available",
575 ),
576 }
577 }
578 }
579
580 pub fn check_unsafety<'tcx>(tcx: TyCtxt<'tcx>, def: ty::WithOptConstParam<LocalDefId>) {
581 // THIR unsafeck is gated under `-Z thir-unsafeck`
582 if !tcx.sess.opts.debugging_opts.thir_unsafeck {
583 return;
584 }
585
586 // Closures are handled by their owner, if it has a body
587 if tcx.is_closure(def.did.to_def_id()) {
588 let owner = tcx.hir().local_def_id_to_hir_id(def.did).owner;
589 let owner_hir_id = tcx.hir().local_def_id_to_hir_id(owner);
590
591 if tcx.hir().maybe_body_owned_by(owner_hir_id).is_some() {
592 tcx.ensure().thir_check_unsafety(owner);
593 return;
594 }
595 }
596
597 let (thir, expr) = tcx.thir_body(def);
598 let thir = &thir.borrow();
599 // If `thir` is empty, a type error occured, skip this body.
600 if thir.exprs.is_empty() {
601 return;
602 }
603
604 let hir_id = tcx.hir().local_def_id_to_hir_id(def.did);
605 let body_unsafety = tcx.hir().fn_sig_by_hir_id(hir_id).map_or(BodyUnsafety::Safe, |fn_sig| {
606 if fn_sig.header.unsafety == hir::Unsafety::Unsafe {
607 BodyUnsafety::Unsafe(fn_sig.span)
608 } else {
609 BodyUnsafety::Safe
610 }
611 });
612 let body_target_features = &tcx.codegen_fn_attrs(def.did).target_features;
613 let safety_context =
614 if body_unsafety.is_unsafe() { SafetyContext::UnsafeFn } else { SafetyContext::Safe };
615 let mut visitor = UnsafetyVisitor {
616 tcx,
617 thir,
618 safety_context,
619 hir_context: hir_id,
620 body_unsafety,
621 body_target_features,
622 in_possible_lhs_union_assign: false,
623 in_union_destructure: false,
624 param_env: tcx.param_env(def.did),
625 inside_adt: false,
626 };
627 visitor.visit_expr(&thir[expr]);
628 }
629
630 crate fn thir_check_unsafety<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) {
631 if let Some(def) = ty::WithOptConstParam::try_lookup(def_id, tcx) {
632 tcx.thir_check_unsafety_for_const_arg(def)
633 } else {
634 check_unsafety(tcx, ty::WithOptConstParam::unknown(def_id))
635 }
636 }
637
638 crate fn thir_check_unsafety_for_const_arg<'tcx>(
639 tcx: TyCtxt<'tcx>,
640 (did, param_did): (LocalDefId, DefId),
641 ) {
642 check_unsafety(tcx, ty::WithOptConstParam { did, const_param_did: Some(param_did) })
643 }