1use rustc_hir::def_id::DefId;
17use rustc_middle::mir::Operand;
18use rustc_middle::ty::{GenericArgKind, Ty, TyCtxt, TyKind};
19
20use super::{CallDependencySummary, CallEffect, CallEffectSummary};
21use crate::helpers::api_classify;
22use crate::helpers::mir_utils::{
23 type_layout, destination_stride, pointee_alignment,
24 nonnull_inner_ty, slice_element_size, vec_element_size,
25};
26
27pub struct EffCtx<'a, 'tcx> {
30 pub tcx: TyCtxt<'tcx>,
31 pub caller: DefId,
32 pub callee: Option<DefId>,
33 pub name: &'a str,
34 pub func: &'a Operand<'tcx>,
35 pub dest: Option<rustc_middle::mir::Local>,
36}
37
38struct Entry {
41 matches: fn(&str) -> bool,
42 dep_on: &'static [usize],
43 dep_on_all: bool,
44 writes: &'static [usize],
45 effects: fn(&EffCtx<'_, '_>) -> Vec<CallEffect>,
46}
47
48macro_rules! none { () => { &[] } }
49macro_rules! dep0 { () => { &[0usize] } }
50macro_rules! dep01 { () => { &[0usize, 1] } }
51
52macro_rules! E {
53 ($m:expr, $d:expr, $all:expr, $w:expr, $e:ident) => {
54 Entry { matches: $m, dep_on: $d, dep_on_all: $all, writes: $w, effects: $e }
55 };
56}
57
58const ALL: &[usize] = &[];
59
60static REGISTRY: &[Entry] = &[
61 E!(mem_forget, dep0!(), false, none!(), eff_forget),
63
64 E!(transmute, dep0!(), false, none!(), eff_none),
66 E!(api_classify::is_maybe_uninit_uninit,none!(), false, none!(), eff_none),
67 E!(api_classify::is_maybe_uninit_assume_init,dep0!(), false, none!(), eff_none),
68 E!(int_max, ALL, true, none!(), eff_return_max),
75 E!(int_clamp, ALL, true, none!(), eff_return_clamp),
76 E!(int_abs, ALL, true, none!(), eff_return_abs),
77 E!(int_neg, ALL, true, none!(), eff_return_neg),
78 E!(int_add, ALL, true, none!(), eff_return_add),
79 E!(int_mul, ALL, true, none!(), eff_return_mul),
80 E!(int_checked_add, ALL, true, none!(), eff_return_option_some_add),
81 E!(int_checked_mul, ALL, true, none!(), eff_return_option_some_mul),
82 E!(overflowing_nz, ALL, true, none!(), eff_overflowing_nz),
83 E!(api_classify::is_numeric_arith, ALL, true, none!(), eff_none),
84 E!(saturating_sub, ALL, true, none!(), eff_return_sub),
85 E!(api_classify::is_offset_from_unsigned, dep01!(), false, none!(), eff_offset_from_unsigned),
86 E!(api_classify::is_option_unwrap, dep0!(), false, none!(), eff_alias_arg0),
87 E!(from_trait_call, dep0!(), false, none!(), eff_from_trait),
88
89 E!(nonnull_from, dep0!(), false, none!(), eff_alias_ptr),
91 E!(nonnull_new_unchecked, dep0!(), false, none!(), eff_none),
92 E!(nonnull_new, dep0!(), false, none!(), eff_alias_ptr),
93 E!(nonnull_as_ref, dep0!(), false, none!(), eff_alias_ptr),
94 E!(nonnull_as_mut, dep0!(), false, none!(), eff_alias_ptr),
95 E!(api_classify::is_as_ptr, dep0!(), false, none!(), eff_alias_ptr),
96 E!(api_classify::is_as_ptr_range, dep0!(), false, none!(), eff_alias_arg0),
97 E!(api_classify::is_as_mut_ptr_range, dep0!(), false, none!(), eff_alias_arg0),
98
99 E!(|n| api_classify::is_pointer_add(n) && !api_classify::is_byte_ptr_arith(n), dep01!(), false, none!(), eff_ptr_add),
101 E!(|n| api_classify::is_pointer_sub(n) && !api_classify::is_byte_ptr_arith(n), dep01!(), false, none!(), eff_ptr_sub),
102 E!(|n| api_classify::is_pointer_add(n) && api_classify::is_byte_ptr_arith(n), dep01!(), false, none!(), eff_ptr_add),
103 E!(|n| api_classify::is_pointer_sub(n) && api_classify::is_byte_ptr_arith(n), dep01!(), false, none!(), eff_ptr_sub),
104
105 E!(ptr_read, dep0!(), false, none!(), eff_read_mem),
107 E!(api_classify::is_ptr_write, none!(), false, dep0!(), eff_write_mem),
108 E!(api_classify::is_maybe_uninit_write, none!(), false, dep0!(), eff_write_mem),
109
110 E!(api_classify::is_len, dep0!(), false, none!(), eff_len),
112 E!(is_empty, dep0!(), false, none!(), eff_is_empty),
113 E!(cmp_min, ALL, true, none!(), eff_cmp_min),
114 E!(bit_preserving_nz, ALL, true, none!(), eff_return_nonzero_iff),
115 E!(checked_pow_nz, ALL, true, none!(), eff_return_option_some_nonzero_iff),
116
117 E!(is_slice_get_unchecked, dep0!(), false, none!(), eff_alias_ptr),
119
120 E!(api_classify::is_ownership_reconstruction, dep0!(), false, none!(), eff_ownership_recon),
122
123 E!(slice_index, dep01!(), false, none!(), eff_alias_arg0),
125 E!(align_to_local, dep0!(), false, none!(), eff_align_to),
126 E!(into_iter_local, dep0!(), false, none!(), eff_return_iter),
127 E!(iter_position, dep0!(), false, none!(), eff_option_scan_index),
128 E!(is_strlen, dep0!(), false, none!(), eff_scan_length),
129 E!(split_at, dep01!(), false, none!(), eff_split_at),
130 E!(api_classify::is_from_raw_parts, dep01!(), false, none!(), eff_from_raw_parts),
131 E!(api_classify::is_align_offset, dep01!(), false, none!(), eff_align_offset),
132
133 E!(api_classify::is_vec_alloc_constructor, dep01!(), false, none!(), eff_new_allocation),
135 E!(api_classify::is_vec_from_box, dep0!(), false, none!(), eff_vec_from_box),
136 E!(api_classify::is_vec_with_capacity, dep0!(), false, none!(), eff_new_allocation_from_cap),
137 E!(api_classify::is_into_boxed_slice, dep0!(), false, none!(), eff_box_from_vec),
138
139 E!(allocator_allocate, dep01!(), false, none!(), eff_allocator_allocate),
141
142 E!(layout_align, none!(), false, none!(), eff_layout_align),
144
145 E!(api_classify::is_layout_constant, none!(), false, none!(), eff_layout_const),
147
148 E!(api_classify::is_cstr_from_ptr, dep0!(), false, none!(), eff_alias_arg0),
150 E!(api_classify::is_cstr_from_bytes_with_nul_unchecked, dep0!(), false, none!(), eff_alias_arg0),
151 E!(api_classify::is_vec_push, none!(), false, dep0!(), eff_write_mem),
152];
153
154pub fn lookup_dependency(
155 callee: Option<DefId>,
156 name: &str,
157 arg_count: usize,
158) -> Option<CallDependencySummary> {
159 for e in REGISTRY {
160 if (e.matches)(name) {
161 let args = if e.dep_on_all { (0..arg_count).collect() } else { e.dep_on.to_vec() };
162 return Some(CallDependencySummary {
163 callee,
164 name: name.to_string(),
165 return_depends_on_args: args,
166 may_write_args: e.writes.to_vec(),
167 unsupported: false,
168 });
169 }
170 }
171 None
172}
173
174pub fn lookup_effect<'tcx>(
175 tcx: TyCtxt<'tcx>,
176 caller: DefId,
177 callee: Option<DefId>,
178 name: &str,
179 func: &Operand<'tcx>,
180 destination: rustc_middle::mir::Local,
181) -> Option<CallEffectSummary> {
182 let dest = Some(destination);
183 for e in REGISTRY {
184 if (e.matches)(name) {
185 let ctx = EffCtx { tcx, caller, callee, name, func, dest };
186 return Some(CallEffectSummary {
187 callee,
188 name: name.to_string(),
189 destination: dest,
190 effects: (e.effects)(&ctx),
191 unsupported: false,
192 });
193 }
194 }
195 None
196}
197
198fn eff_none(_: &EffCtx<'_, '_>) -> Vec<CallEffect> { Vec::new() }
201
202fn eff_alias_ptr(ctx: &EffCtx<'_, '_>) -> Vec<CallEffect> {
203 let mut eff = vec![
204 CallEffect::ReturnPointerFromArg { arg: 0 },
205 CallEffect::ReturnNonZero,
206 ];
207 if let Some((a, n)) = pointee_alignment(ctx.tcx, ctx.caller, ctx.dest) {
208 eff.push(CallEffect::ReturnAligned { align: a, ty_name: n });
209 }
210 eff
211}
212
213fn eff_alias_nonnull(ctx: &EffCtx<'_, '_>) -> Vec<CallEffect> {
214 let mut eff = vec![
215 CallEffect::ReturnPointerFromArg { arg: 0 },
216 CallEffect::ReturnNonZero,
217 ];
218 if let Some((a, n)) = nonnull_pointee_alignment(ctx.tcx, ctx.caller, ctx.dest) {
219 eff.push(CallEffect::ReturnAligned { align: a, ty_name: n });
220 }
221 eff
222}
223
224fn eff_from_trait(ctx: &EffCtx<'_, '_>) -> Vec<CallEffect> {
225 if is_nonnull_dest(ctx.tcx, ctx.caller, ctx.dest) {
226 eff_alias_nonnull(ctx)
227 } else {
228 Vec::new()
229 }
230}
231
232fn eff_alias_arg0(_: &EffCtx<'_, '_>) -> Vec<CallEffect> {
233 vec![CallEffect::ReturnAliasArg { arg: 0 }]
234}
235
236fn eff_ptr_add(ctx: &EffCtx<'_, '_>) -> Vec<CallEffect> {
237 let stride = if api_classify::is_byte_ptr_arith(ctx.name) {
238 Some(1)
239 } else {
240 destination_stride(ctx.tcx, ctx.caller, ctx.dest)
241 };
242 vec![CallEffect::ReturnPointerAdd { base_arg: 0, offset_arg: 1, stride }]
243}
244
245fn eff_ptr_sub(ctx: &EffCtx<'_, '_>) -> Vec<CallEffect> {
246 let stride = if api_classify::is_byte_ptr_arith(ctx.name) {
247 Some(1)
248 } else {
249 destination_stride(ctx.tcx, ctx.caller, ctx.dest)
250 };
251 vec![CallEffect::ReturnPointerSub { base_arg: 0, offset_arg: 1, stride }]
252}
253
254fn eff_offset_from_unsigned(_ctx: &EffCtx<'_, '_>) -> Vec<CallEffect> {
255 vec![CallEffect::ReturnOffsetFromUnsigned { self_arg: 0, origin_arg: 1 }]
256}
257
258fn eff_read_mem(_: &EffCtx<'_, '_>) -> Vec<CallEffect> {
259 vec![CallEffect::ReadMemory { arg: 0 }]
260}
261
262fn eff_write_mem(_: &EffCtx<'_, '_>) -> Vec<CallEffect> {
263 vec![CallEffect::WriteMemory { pointer_arg: 0 }]
264}
265
266fn eff_len(_: &EffCtx<'_, '_>) -> Vec<CallEffect> {
267 vec![CallEffect::ReturnLengthOfArg { arg: 0 }]
268}
269
270fn eff_is_empty(_: &EffCtx<'_, '_>) -> Vec<CallEffect> {
271 vec![CallEffect::ReturnIsEmptyOfArg { arg: 0 }]
272}
273
274fn eff_cmp_min(_: &EffCtx<'_, '_>) -> Vec<CallEffect> {
275 vec![CallEffect::ReturnMin { lhs_arg: 0, rhs_arg: 1 }]
276}
277
278fn eff_return_nonzero_iff(_: &EffCtx<'_, '_>) -> Vec<CallEffect> {
279 vec![CallEffect::ReturnNonZeroIff { arg: 0 }]
280}
281
282fn eff_return_option_some_nonzero_iff(_: &EffCtx<'_, '_>) -> Vec<CallEffect> {
283 vec![CallEffect::ReturnOptionSomeNonZeroIff { arg: 0 }]
284}
285
286fn eff_return_max(_: &EffCtx<'_, '_>) -> Vec<CallEffect> {
287 vec![CallEffect::ReturnMax { lhs_arg: 0, rhs_arg: 1 }]
288}
289
290fn eff_return_clamp(_: &EffCtx<'_, '_>) -> Vec<CallEffect> {
291 vec![CallEffect::ReturnClamp { value_arg: 0, min_arg: 1, max_arg: 2 }]
292}
293
294fn eff_return_abs(_: &EffCtx<'_, '_>) -> Vec<CallEffect> {
295 vec![CallEffect::ReturnAbs { arg: 0 }]
296}
297
298fn eff_return_neg(_: &EffCtx<'_, '_>) -> Vec<CallEffect> {
299 vec![CallEffect::ReturnNeg { arg: 0 }]
300}
301
302fn eff_return_add(_: &EffCtx<'_, '_>) -> Vec<CallEffect> {
303 vec![CallEffect::ReturnAdd { lhs_arg: 0, rhs_arg: 1 }]
304}
305
306fn eff_return_sub(_: &EffCtx<'_, '_>) -> Vec<CallEffect> {
307 vec![CallEffect::ReturnSub { lhs_arg: 0, rhs_arg: 1 }]
308}
309
310fn eff_return_mul(_: &EffCtx<'_, '_>) -> Vec<CallEffect> {
311 vec![CallEffect::ReturnMul { lhs_arg: 0, rhs_arg: 1 }]
312}
313
314fn eff_return_option_some_add(_: &EffCtx<'_, '_>) -> Vec<CallEffect> {
315 vec![CallEffect::ReturnOptionSomeAdd { lhs_arg: 0, rhs_arg: 1 }]
316}
317
318fn eff_return_option_some_mul(_: &EffCtx<'_, '_>) -> Vec<CallEffect> {
319 vec![CallEffect::ReturnOptionSomeMul { lhs_arg: 0, rhs_arg: 1 }]
320}
321
322fn eff_overflowing_nz(_: &EffCtx<'_, '_>) -> Vec<CallEffect> {
323 vec![CallEffect::ReturnTupleFieldNonZero { field: 0 }]
324}
325
326fn eff_ownership_recon(_: &EffCtx<'_, '_>) -> Vec<CallEffect> {
327 vec![
328 CallEffect::ReturnAliasArg { arg: 0 },
329 CallEffect::ReturnNonZero,
330 CallEffect::OwnsInitMemory { arg: 0 },
331 ]
332}
333
334fn eff_align_to(_: &EffCtx<'_, '_>) -> Vec<CallEffect> {
335 vec![CallEffect::ReturnAlignTo { receiver_arg: 0 }]
336}
337
338fn eff_return_iter(_: &EffCtx<'_, '_>) -> Vec<CallEffect> {
339 vec![CallEffect::ReturnIter { receiver_arg: 0 }]
340}
341
342fn eff_option_scan_index(_: &EffCtx<'_, '_>) -> Vec<CallEffect> {
343 vec![CallEffect::ReturnOptionSomeScanIndex { self_arg: 0 }]
344}
345
346fn eff_scan_length(_: &EffCtx<'_, '_>) -> Vec<CallEffect> {
347 vec![CallEffect::ReturnScanLength { ptr_arg: 0 }]
348}
349
350fn eff_align_offset(_: &EffCtx<'_, '_>) -> Vec<CallEffect> {
351 vec![CallEffect::ReturnAlignOffset { ptr_arg: 0, align_arg: 1 }]
352}
353
354fn eff_split_at(_: &EffCtx<'_, '_>) -> Vec<CallEffect> {
355 vec![
356 CallEffect::ReturnAliasArg { arg: 0 },
357 CallEffect::ReturnTupleFieldLength { field: 0, from_arg: 1 },
358 ]
359}
360
361fn eff_from_raw_parts(ctx: &EffCtx<'_, '_>) -> Vec<CallEffect> {
362 let elem = slice_element_size(ctx.tcx, ctx.caller, ctx.dest);
363 let mut eff = vec![
364 CallEffect::ReturnAliasArg { arg: 0 },
369 CallEffect::ReturnFreshAllocation {
372 pointer_arg: 0,
373 size_arg: 1,
374 elem_size: elem,
375 },
376 CallEffect::ReturnNonZero,
377 ];
378 if let Some((a, n)) = pointee_alignment(ctx.tcx, ctx.caller, ctx.dest) {
379 eff.push(CallEffect::ReturnAligned { align: a, ty_name: n });
380 }
381 eff
382}
383
384fn eff_new_allocation(ctx: &EffCtx<'_, '_>) -> Vec<CallEffect> {
385 let elem = vec_element_size(ctx.tcx, ctx.caller, ctx.dest);
386 vec![
387 CallEffect::ReturnNewAllocation {
388 size_arg: 1,
389 elem_size: elem,
390 },
391 ]
392}
393
394fn eff_new_allocation_from_cap(ctx: &EffCtx<'_, '_>) -> Vec<CallEffect> {
395 let elem = vec_element_size(ctx.tcx, ctx.caller, ctx.dest);
396 vec![
397 CallEffect::ReturnNewAllocation {
398 size_arg: 0,
399 elem_size: elem,
400 },
401 ]
402}
403
404fn eff_vec_from_box(_ctx: &EffCtx<'_, '_>) -> Vec<CallEffect> {
405 vec![
406 CallEffect::ReturnNewAllocationFromBox { box_arg: 0 },
407 ]
408}
409
410fn eff_allocator_allocate(_ctx: &EffCtx<'_, '_>) -> Vec<CallEffect> {
411 vec![CallEffect::ReturnAllocBuffer]
412}
413
414fn eff_layout_align(_ctx: &EffCtx<'_, '_>) -> Vec<CallEffect> {
415 vec![CallEffect::ReturnPowerOfTwo]
416}
417
418fn eff_forget(_ctx: &EffCtx<'_, '_>) -> Vec<CallEffect> {
419 vec![
420 CallEffect::CleanSliceDataLinks { arg: 0 },
421 ]
422}
423
424fn eff_layout_const(ctx: &EffCtx<'_, '_>) -> Vec<CallEffect> {
425 layout_constant_effect(ctx.tcx, ctx.caller, ctx.func, ctx.name)
426 .into_iter()
427 .collect()
428}
429
430fn mem_forget(n: &str) -> bool { n.ends_with("mem::forget") }
433fn transmute(n: &str) -> bool { n.contains("::transmute") || n.contains("intrinsics::transmute") }
434fn slice_index(n: &str) -> bool { n.ends_with("::Index::index") || n.ends_with("::IndexMut::index_mut") }
435fn align_to_local(n: &str) -> bool {
436 n.ends_with("align_to_ext") || n.ends_with("align_to_mut_ext")
437}
438fn into_iter_local(n: &str) -> bool {
439 (n.contains("into_iter") && (n.contains("IntoIterator") || n.contains("slice::into_iter")))
440 || n.contains("slice::<impl [T]>::iter")
441}
442fn iter_position(n: &str) -> bool { n.contains("Iterator::position") || n.contains("Iterator::find") }
443fn is_strlen(n: &str) -> bool { n == "strlen" || n.ends_with("::strlen") }
444fn from_trait_call(n: &str) -> bool { n == "std::convert::From::from" || n == "core::convert::From::from" }
445fn nonnull_from(n: &str) -> bool { n.ends_with("::from") && api_classify::is_nonnull_api(n) }
446fn nonnull_new_unchecked(n: &str) -> bool { n.ends_with("::new_unchecked") && api_classify::is_nonnull_api(n) }
447fn nonnull_new(n: &str) -> bool { n.ends_with("::new") && api_classify::is_nonnull_api(n) && !n.ends_with("::new_unchecked") }
448fn nonnull_as_ref(n: &str) -> bool { n.ends_with("::as_ref") && api_classify::is_nonnull_api(n) }
449fn nonnull_as_mut(n: &str) -> bool { n.ends_with("::as_mut") && api_classify::is_nonnull_api(n) }
450fn ptr_read(n: &str) -> bool { n.ends_with("::read") && n.contains("::ptr::") }
451fn is_empty(n: &str) -> bool { n.ends_with("::is_empty") }
452fn cmp_min(n: &str) -> bool { (n.contains("::cmp::min") || n.contains("::Ord::min") || n.starts_with("core::cmp::min")) && !n.contains("min_by") }
453
454fn bit_preserving_nz(n: &str) -> bool {
459 n.contains("::rotate_left")
460 || n.contains("::rotate_right")
461 || n.contains("::swap_bytes")
462 || n.contains("::reverse_bits")
463 || n.contains("::from_be")
464 || n.contains("::from_le")
465 || n.contains("::to_be")
466 || n.contains("::to_le")
467 || n.contains("::count_ones")
468 || n.contains("::isqrt")
469 || n.contains("::saturating_pow")
470}
471
472fn checked_pow_nz(n: &str) -> bool {
475 n.ends_with("::checked_pow")
476}
477
478fn int_max(n: &str) -> bool { n.ends_with("::max") }
483fn int_clamp(n: &str) -> bool { n.ends_with("::clamp") }
484fn int_abs(n: &str) -> bool {
485 n.ends_with("::abs")
486 || n.ends_with("::saturating_abs")
487 || n.ends_with("::wrapping_abs")
488 || n.ends_with("::unsigned_abs")
489}
490fn int_neg(n: &str) -> bool {
491 n.ends_with("::neg")
492 || n.ends_with("::wrapping_neg")
493 || n.ends_with("::saturating_neg")
494}
495fn int_add(n: &str) -> bool {
496 n.ends_with("::saturating_add") || n.ends_with("::unchecked_add")
497}
498fn int_mul(n: &str) -> bool {
499 n.ends_with("::saturating_mul") || n.ends_with("::unchecked_mul")
500}
501fn int_checked_add(n: &str) -> bool { n.ends_with("::checked_add") }
502fn int_checked_mul(n: &str) -> bool { n.ends_with("::checked_mul") }
503
504fn overflowing_nz(n: &str) -> bool {
508 n.ends_with("::overflowing_abs") || n.ends_with("::overflowing_neg")
509}
510fn allocator_allocate(n: &str) -> bool {
511 n.ends_with("::Allocator::allocate")
512 || n.ends_with("::Allocator::allocate_zeroed")
513 || n.ends_with("::Allocator::grow")
514 || n.ends_with("::Allocator::shrink")
515}
516fn layout_align(n: &str) -> bool { n.ends_with("Layout::align") && !n.ends_with("Layout::alignment") }
517fn saturating_sub(n: &str) -> bool { n.contains("::saturating_sub") }
518fn split_at(n: &str) -> bool { n.contains("::split_at") }
519fn is_slice_get_unchecked(n: &str) -> bool {
520 (n.contains("::get_unchecked") || n.contains("::get_unchecked_mut"))
521 && (n.contains("::SliceIndex")
522 || n.contains("::<impl [T]>::get_unchecked")
523 || n.contains("::impl [T]>::get_unchecked")
524 || n.contains("::mut_ptr::get_unchecked")
525 || n.contains("::const_ptr::get_unchecked"))
526}
527
528fn nonnull_pointee_alignment<'tcx>(
531 tcx: TyCtxt<'tcx>, caller: DefId, dest: Option<rustc_middle::mir::Local>,
532) -> Option<(u64, String)> {
533 let d = dest?;
534 let ty = tcx.optimized_mir(caller).local_decls[d].ty;
535 let pointee = nonnull_inner_ty(tcx, ty)?;
536 type_layout(tcx, caller, pointee).map(|(a, _)| (a, format!("{pointee:?}")))
537}
538
539fn is_nonnull_dest(tcx: TyCtxt<'_>, caller: DefId, dest: Option<rustc_middle::mir::Local>) -> bool {
540 let Some(d) = dest else { return false };
541 nonnull_inner_ty(tcx, tcx.optimized_mir(caller).local_decls[d].ty).is_some()
542}
543
544fn layout_call_ty<'tcx>(func: &Operand<'tcx>) -> Option<Ty<'tcx>> {
545 let Operand::Constant(c) = func else { return None };
546 let TyKind::FnDef(_, args) = c.const_.ty().kind() else { return None };
547 args.iter().find_map(|a| {
548 #[cfg(rapx_ge_99)] let a = a.skip_binder();
549 match a.kind() { GenericArgKind::Type(t) => Some(t), _ => None }
550 })
551}
552
553fn layout_constant_effect<'tcx>(
554 tcx: TyCtxt<'tcx>, caller: DefId, func: &Operand<'tcx>, name: &str,
555) -> Option<CallEffect> {
556 let ty = layout_call_ty(func)?;
557 let (align, size) = type_layout(tcx, caller, ty)?;
558 if name.contains("align_of") {
559 Some(CallEffect::ReturnConst { value: align, label: format!("align_of::<{ty:?}>()") })
560 } else if name.contains("size_of") {
561 Some(CallEffect::ReturnConst { value: size, label: format!("size_of::<{ty:?}>()") })
562 } else {
563 None
564 }
565}
566
567fn eff_box_from_vec(_: &EffCtx<'_, '_>) -> Vec<CallEffect> {
568 vec![CallEffect::ReturnBoxFromVec { arg: 0 }]
569}