@@ -356,13 +356,7 @@ fn arg_attrs_for_rust_scalar<'tcx>(
356356 Some ( pointee. align . min ( cx. tcx ( ) . sess . target . max_reliable_alignment ( ) ) ) ;
357357 }
358358
359- // LLVM dereferenceable attribute has unclear semantics on the return type,
360- // they seem to be "dereferenceable until the end of the program", which is
361- // generally, not valid for references. See
362- // <https://rust-lang.zulipchat.com/#narrow/channel/136281-t-opsem/topic/LLVM.20dereferenceable.20on.20return.20type/with/563001493>
363- if !is_return {
364- attrs. pointee_size = pointee. size ;
365- } ;
359+ attrs. pointee_size = pointee. size ;
366360
367361 if let Some ( kind) = pointee. safe {
368362 // The aliasing rules for `Box<T>` are still not decided, but currently we emit
@@ -407,6 +401,26 @@ fn arg_attrs_for_rust_scalar<'tcx>(
407401 }
408402 }
409403
404+ // NoFree is not valid on return values. If it were, it would mean something like
405+ // "will not be freed until the end of the program", which is generally not valid for
406+ // references.
407+ let no_free = !is_return
408+ && match kind {
409+ // Non-frozen shared references are not necessarily dereferenceable for the
410+ // entire duration of the function
411+ // (see <https://github.com/rust-lang/rust/pull/98017>).
412+ PointerKind :: SharedRef { frozen } => frozen,
413+ // Mutable references to potentially self-referential types are not necessarily
414+ // dereferenceable for the entire duration of the function
415+ // (see <https://github.com/rust-lang/unsafe-code-guidelines/issues/381>).
416+ PointerKind :: MutableRef { unpin } => unpin,
417+ // Box may be deallocated during execution of the function.
418+ PointerKind :: Box { .. } => false ,
419+ } ;
420+ if no_free {
421+ attrs. set ( ArgAttribute :: NoFree ) ;
422+ }
423+
410424 if matches ! ( kind, PointerKind :: SharedRef { frozen: true } ) && !is_return {
411425 attrs. set ( ArgAttribute :: ReadOnly ) ;
412426 attrs. set ( ArgAttribute :: CapturesReadOnly ) ;
@@ -423,11 +437,18 @@ fn fn_abi_sanity_check<'tcx>(
423437 fn_abi : & FnAbi < ' tcx , Ty < ' tcx > > ,
424438 spec_abi : ExternAbi ,
425439) {
440+ fn fn_arg_attrs_sanity_check ( attrs : & ArgAttributes , is_ret : bool ) {
441+ if attrs. regular . contains ( ArgAttribute :: NoFree ) {
442+ assert ! ( !is_ret, "NoFree not valid on return values" ) ;
443+ }
444+ }
445+
426446 fn fn_arg_sanity_check < ' tcx > (
427447 cx : & LayoutCx < ' tcx > ,
428448 fn_abi : & FnAbi < ' tcx , Ty < ' tcx > > ,
429449 spec_abi : ExternAbi ,
430450 arg : & ArgAbi < ' tcx , Ty < ' tcx > > ,
451+ is_ret : bool ,
431452 ) {
432453 let tcx = cx. tcx ( ) ;
433454
@@ -452,7 +473,7 @@ fn fn_abi_sanity_check<'tcx>(
452473 PassMode :: Ignore => {
453474 assert ! ( arg. layout. is_zst( ) ) ;
454475 }
455- PassMode :: Direct ( _ ) => {
476+ PassMode :: Direct ( attrs ) => {
456477 // Here the Rust type is used to determine the actual ABI, so we have to be very
457478 // careful. Scalar/Vector is fine, since backends will generally use
458479 // `layout.backend_repr` and ignore everything else. We should just reject
@@ -482,28 +503,33 @@ fn fn_abi_sanity_check<'tcx>(
482503 ) ;
483504 }
484505 }
506+ fn_arg_attrs_sanity_check ( attrs, is_ret) ;
485507 }
486- PassMode :: Pair ( _ , _ ) => {
508+ PassMode :: Pair ( attrs1 , attrs2 ) => {
487509 // Similar to `Direct`, we need to make sure that backends use `layout.backend_repr`
488510 // and ignore the rest of the layout.
489511 assert ! (
490512 matches!( arg. layout. backend_repr, BackendRepr :: ScalarPair ( ..) ) ,
491513 "PassMode::Pair for type {}" ,
492514 arg. layout. ty
493515 ) ;
516+ fn_arg_attrs_sanity_check ( attrs1, is_ret) ;
517+ fn_arg_attrs_sanity_check ( attrs2, is_ret) ;
494518 }
495519 PassMode :: Cast { .. } => {
496520 // `Cast` means "transmute to `CastType`"; that only makes sense for sized types.
497521 assert ! ( arg. layout. is_sized( ) ) ;
498522 }
499- PassMode :: Indirect { meta_attrs : None , .. } => {
523+ PassMode :: Indirect { meta_attrs : None , attrs , .. } => {
500524 // No metadata, must be sized.
501525 // Conceptually, unsized arguments must be copied around, which requires dynamically
502526 // determining their size, which we cannot do without metadata. Consult
503527 // t-opsem before removing this check.
504528 assert ! ( arg. layout. is_sized( ) ) ;
529+ // Indirect returns are arguments from an ABI perspective.
530+ fn_arg_attrs_sanity_check ( attrs, false ) ;
505531 }
506- PassMode :: Indirect { meta_attrs : Some ( _ ) , on_stack , .. } => {
532+ PassMode :: Indirect { meta_attrs : Some ( meta_attrs ) , attrs , on_stack } => {
507533 // With metadata. Must be unsized and not on the stack.
508534 assert ! ( arg. layout. is_unsized( ) && !on_stack) ;
509535 // Also, must not be `extern` type.
@@ -515,14 +541,17 @@ fn fn_abi_sanity_check<'tcx>(
515541 // t-opsem before removing this check.
516542 panic ! ( "unsized arguments must not be `extern` types" ) ;
517543 }
544+ // Indirect returns are arguments from an ABI perspective.
545+ fn_arg_attrs_sanity_check ( attrs, false ) ;
546+ fn_arg_attrs_sanity_check ( meta_attrs, false ) ;
518547 }
519548 }
520549 }
521550
522551 for arg in fn_abi. args . iter ( ) {
523- fn_arg_sanity_check ( cx, fn_abi, spec_abi, arg) ;
552+ fn_arg_sanity_check ( cx, fn_abi, spec_abi, arg, false ) ;
524553 }
525- fn_arg_sanity_check ( cx, fn_abi, spec_abi, & fn_abi. ret ) ;
554+ fn_arg_sanity_check ( cx, fn_abi, spec_abi, & fn_abi. ret , true ) ;
526555}
527556
528557#[ tracing:: instrument(
0 commit comments