Skip to content

Commit 2b1e46d

Browse files
ssbrcopybara-github
authored andcommitted
When a trait has a nontrivial parameter, materialize it in the caller.
(Technically, this does let you call trait methods with nontrivial arguments, but it's not in a final/decent place.) This is an alternate version of unknown commit which tried to continue to defer materialization. However, as discussed in[] currently impossible. In particular, we cannot define a trait like this: ```rs impl<T: Ctor<Output=A>> MyTrait<T> for S {...} impl<T: Ctor<Output=B>> MyTrait<T> for S {...} ``` ... because Rust does not understand that these impls are disjoint: rust-lang/rust#20400 #### What's next? (Apologies if this is a bit unorganized, I've spent too much time in the trenches.) So this CL is just a first step: we *must* monomorphize the implementation. Rather than accepting any `T: Ctor`, accept an `RvalueReference` (a concrete type). After this CL, I think we have a slightly more open field than I thought. In particular, we should be able to regain the `Ctor` API, except using only *one* parameterized impl. So, instead of the broken impls above, we can have this impl: ```rs impl<'a> MyTrait<RvalueReference<'a, A>> for S {...} impl<'a> MyTrait<RvalueReference<'a, B>> for S {...} impl<U, CtorType> MyTrait<CtorType> for S where &C : for<'a> MyTrait<RvalueReference<'a, U>>, CtorType: Ctor<Output=U> {...} ``` Because this is only _one_ parameterized impl, there's no conflicts. It is implemented in terms of the concrete non-parameterized impls as generated by this change. However, I'm not yet 100% certain this will work, and it is actually not a small task to do, even on top of this CL. For example, there's a bunch of refactoring to let one generate a second blanket impl using knowledge about the trait function etc. from the concrete impl. ##### RvalueReference might need to get replaced. If we can use the `Ctor` approach described above... we can't use `RvalueReference`, actually, because Rust will then recurse infinitely. The `RvalueReference` type used for the `for<'a> MyTrait<RvalueReference<...>>` bound must be in the _same_ crate so that Rust knows that `RvalueReference` doesn't itself impl `Ctor`. And unfortunately, no, negative impls aren't good enough here, yet, apparently. At least, it didn't resolve it when I tried it! You can test this in a local two-crate setup. Crate 1: ```rs pub trait Ctor { type Output; } pub struct RvalueReference<'a, T>(&'a T); ``` Crate 2: ```rs use lib1::*; pub struct A1; pub struct A2; pub struct B; impl <'a> From<RvalueReference<'a, A1>> for B { fn from(_: RvalueReference<'a, A1>) -> Self { todo!(); } } impl <'a> From<RvalueReference<'a, A2>> for B { fn from(_: RvalueReference<'a, A2>) -> Self { todo!(); } } impl <T: Ctor> From<T> for B where B : for<'a> From<RvalueReference<'a, T::Output>> { fn from(_: T) -> Self { todo!(); } } ``` If you build crate 2, it will fail with the following error: ``` error[E0275]: overflow evaluating the requirement `for<'a> B: From<lib1::RvalueReference<'a, _>>` | = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`lib2`) note: required because of the requirements on the impl of `for<'a> From<lib1::RvalueReference<'a, _>>` for `B` --> src/lib.rs:15:16 | 15 | impl <T: Ctor> From<T> for B | ^^^^^^^ ^ = note: 126 redundant requirements hidden = note: required because of the requirements on the impl of `for<'a> From<lib1::RvalueReference<'a, _>>` for `B` For more information about this error, try `rustc --explain E0275`. error: could not compile `lib2` due to previous error ``` But it will work fine if you move `RvalueReference` to another crate! ##### If all else fails, we'll force the caller to materialize the Ctor If even the approach outlined above doesn't work, well, we'll just have to force callers to materialize the `Ctor`: call `Trait::method(mov(emplace!(foo())))` instead of `Trait::method(foo())`. That's what I described in[] but I'm hoping we can work our way out after all! Either way, both approaches build on this change. Investigating the followups may take some time, so I'd rather not leave this change sitting around generating merge conflicts, if possible. :X PiperOrigin-RevId: 464613254
1 parent 7ed8c6f commit 2b1e46d

File tree

4 files changed

+200
-44
lines changed

4 files changed

+200
-44
lines changed

rs_bindings_from_cc/src_code_gen.rs

Lines changed: 83 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -495,6 +495,10 @@ fn is_visible_by_adl(enclosing_record: &Record, param_types: &[RsTypeKind]) -> b
495495

496496
/// Returns the shape of the generated Rust API for a given function definition.
497497
///
498+
/// If the shape is a trait, this also mutates the parameter types to be
499+
/// trait-compatible. In particular, types which would be `impl Ctor<Output=T>`
500+
/// become a `RvalueReference<'_, T>`.
501+
///
498502
/// Returns:
499503
///
500504
/// * `Err(_)`: something went wrong importing this function.
@@ -504,7 +508,7 @@ fn is_visible_by_adl(enclosing_record: &Record, param_types: &[RsTypeKind]) -> b
504508
fn api_func_shape(
505509
func: &Func,
506510
ir: &IR,
507-
param_types: &[RsTypeKind],
511+
param_types: &mut [RsTypeKind],
508512
) -> Result<Option<(Ident, ImplKind)>> {
509513
let maybe_record: Option<&Rc<Record>> = ir.record_for_member_func(func)?;
510514
let has_pointer_params = param_types.iter().any(|p| matches!(p, RsTypeKind::Pointer { .. }));
@@ -555,6 +559,7 @@ fn api_func_shape(
555559
if record.is_unpin() {
556560
bail!("operator= for Unpin types is not yet supported.");
557561
}
562+
materialize_ctor_in_caller(param_types);
558563
let rhs = &param_types[1];
559564
impl_kind = ImplKind::new_trait(
560565
TraitName::Other {
@@ -575,6 +580,7 @@ fn api_func_shape(
575580
}
576581
}
577582

583+
materialize_ctor_in_caller(param_types);
578584
let (record, impl_for) = if let Some(record) = maybe_record {
579585
(&**record, ImplFor::RefT)
580586
} else {
@@ -660,6 +666,7 @@ fn api_func_shape(
660666
);
661667
func_name = make_rs_ident("drop");
662668
} else {
669+
materialize_ctor_in_caller(param_types);
663670
impl_kind = ImplKind::new_trait(
664671
TraitName::Other {
665672
name: quote! {::ctor::PinnedDrop},
@@ -693,6 +700,7 @@ fn api_func_shape(
693700
);
694701
}
695702

703+
materialize_ctor_in_caller(param_types);
696704
let record_name = make_rs_ident(&record.rs_name);
697705
if !record.is_unpin() {
698706
func_name = make_rs_ident("ctor_new");
@@ -763,6 +771,45 @@ fn api_func_shape(
763771
Ok(Some((func_name, impl_kind)))
764772
}
765773

774+
/// Mutates the provided parameters so that nontrivial by-value parameters are,
775+
/// instead, materialized in the caller and passed by rvalue reference.
776+
///
777+
/// This produces rvalue references with elided lifetimes. If they compile,
778+
/// they are correct, but the resulting signatures can be surprising.
779+
///
780+
/// For example, consider `From`. This rewriting of function parameters might
781+
/// create an impl as follows:
782+
///
783+
/// ```
784+
/// // C++: struct Foo{ Foo(Nontrivial x); };
785+
/// impl From<RvalueReference<'_, Nontrivial>> for Foo {
786+
/// fn from(x: RvalueReference<'_, Nontrivial>) -> Self { ... }
787+
/// }
788+
/// ```
789+
///
790+
/// Each `'_` is actually a different lifetime! However, due to lifetime
791+
/// subtyping, they are allowed to be used in this sort of way, interchangeably.
792+
/// And indeed, this is even idiomatic. For example, the exact same pattern is
793+
/// in use here:
794+
///
795+
/// https://doc.rust-lang.org/std/string/struct.String.html#impl-From%3C%26%27_%20String%3E
796+
///
797+
/// If there is some case where this is actually _not_ okay, then we would need
798+
/// to generate new named lifetimes, rather than elided lifetimes.
799+
fn materialize_ctor_in_caller(params: &mut [RsTypeKind]) {
800+
for param in params {
801+
if param.is_unpin() {
802+
continue;
803+
}
804+
let value = std::mem::replace(param, RsTypeKind::Unit); // Temporarily swap in a garbage value.
805+
*param = RsTypeKind::RvalueReference {
806+
referent: Rc::new(value),
807+
mutability: Mutability::Mut,
808+
lifetime: Lifetime::Elided,
809+
};
810+
}
811+
}
812+
766813
/// Generates Rust source code for a given `Func`.
767814
///
768815
/// Returns:
@@ -778,7 +825,7 @@ fn generate_func(
778825
) -> Result<Option<RcEq<(RsSnippet, RsSnippet, Rc<FunctionId>)>>> {
779826
let ir = db.ir();
780827
let mut features = BTreeSet::new();
781-
let param_types = func
828+
let mut param_types = func
782829
.params
783830
.iter()
784831
.map(|p| {
@@ -788,12 +835,12 @@ fn generate_func(
788835
})
789836
.collect::<Result<Vec<_>>>()?;
790837

791-
let (func_name, mut impl_kind) = if let Some(values) = api_func_shape(&func, &ir, &param_types)?
792-
{
793-
values
794-
} else {
795-
return Ok(None);
796-
};
838+
let (func_name, mut impl_kind) =
839+
if let Some(values) = api_func_shape(&func, &ir, &mut param_types)? {
840+
values
841+
} else {
842+
return Ok(None);
843+
};
797844

798845
let mut return_type = db
799846
.rs_type_kind(func.return_type.rs_type.clone())
@@ -807,8 +854,12 @@ fn generate_func(
807854
for (i, (ident, type_)) in param_idents.iter().zip(param_types.iter()).enumerate() {
808855
if !type_.is_unpin() {
809856
// `impl Ctor` will fail to compile in a trait.
857+
// This will only be hit if there was a bug in api_func_shape.
810858
if let ImplKind::Trait { .. } = &impl_kind {
811-
bail!("b/200067242: non-Unpin types are not yet supported by value in traits");
859+
panic!(
860+
"non-Unpin types cannot work by value in traits; this should have instead \
861+
become an rvalue reference to force the caller to materialize the Ctor."
862+
);
812863
}
813864
// The generated bindings require a move constructor.
814865
if !type_.is_move_constructible() {
@@ -1016,8 +1067,7 @@ fn generate_func(
10161067
};
10171068

10181069
let fn_generic_params: TokenStream;
1019-
if let ImplKind::Trait { trait_name, trait_generic_params, impl_for, .. } = &mut impl_kind
1020-
{
1070+
if let ImplKind::Trait { trait_name, trait_generic_params, impl_for, .. } = &mut impl_kind {
10211071
// When the impl block is for some kind of reference to T, consider the lifetime
10221072
// parameters on the self parameter to be trait lifetimes so they can be
10231073
// introduced before they are used.
@@ -6388,12 +6438,31 @@ mod tests {
63886438
Nontrivial& operator=(Nontrivial) {}
63896439
~Nontrivial();
63906440
};
6441+
6442+
struct Trivial final {
6443+
/*implicit*/ Trivial(Nontrivial) {}
6444+
};
63916445
"#,
63926446
)?;
63936447
let rs_api = generate_bindings_tokens(ir)?.rs_api;
6394-
// the assign trait shouldn't be implemented for `Nontrivial`.
6395-
// (In fact, it shouldn't be referenced at all -- thus the very minimal test!)
6396-
assert_rs_not_matches!(rs_api, quote! {Assign});
6448+
assert_rs_matches!(
6449+
rs_api,
6450+
quote! {
6451+
impl From<::ctor::RvalueReference<'_, crate::Nontrivial>> for Trivial {
6452+
#[inline(always)]
6453+
fn from(__param_0: ::ctor::RvalueReference<'_, crate::Nontrivial>) -> Self {
6454+
let mut tmp = ::std::mem::MaybeUninit::<Self>::zeroed();
6455+
unsafe {
6456+
crate::detail::__rust_thunk___ZN7TrivialC1E10Nontrivial(
6457+
&mut tmp,
6458+
__param_0
6459+
);
6460+
tmp.assume_init()
6461+
}
6462+
}
6463+
}
6464+
}
6465+
);
63976466
Ok(())
63986467
}
63996468

rs_bindings_from_cc/test/golden/nontrivial_type.h

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,8 +93,12 @@ NontrivialUnpin& TakesByReferenceUnpin(NontrivialUnpin& nontrivial);
9393

9494
// Finally, testing for strange by-value APIs.
9595
struct NontrivialByValue {
96-
// NOLINTNEXTLINE(misc-unconventional-assign-operator)
97-
NontrivialByValue operator=(NontrivialByValue other);
96+
NontrivialByValue(const NontrivialByValue& other) = default;
97+
NontrivialByValue(NontrivialByValue&& other) = default;
98+
NontrivialByValue& operator=(const NontrivialByValue& other) = default;
99+
NontrivialByValue& operator=(NontrivialByValue&& other) = default;
100+
// // NOLINTNEXTLINE(misc-unconventional-assign-operator)
101+
NontrivialByValue operator=(Nontrivial other);
98102
NontrivialByValue operator==(NontrivialByValue other);
99103
};
100104

rs_bindings_from_cc/test/golden/nontrivial_type_rs_api.rs

Lines changed: 91 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -645,50 +645,106 @@ forward_declare::unsafe_define!(
645645
crate::NontrivialByValue
646646
);
647647

648-
impl ::ctor::CtorNew<()> for NontrivialByValue {
649-
type CtorType = impl ::ctor::Ctor<Output = Self>;
648+
impl<'b> ::ctor::CtorNew<&'b crate::NontrivialByValue> for NontrivialByValue {
649+
type CtorType = impl ::ctor::Ctor<Output = Self> + ::ctor::Captures<'b>;
650650
#[inline(always)]
651-
fn ctor_new(args: ()) -> Self::CtorType {
652-
let () = args;
651+
fn ctor_new(args: &'b crate::NontrivialByValue) -> Self::CtorType {
652+
let other = args;
653653
::ctor::FnCtor::new(
654654
move |dest: ::std::pin::Pin<&mut ::std::mem::MaybeUninit<Self>>| unsafe {
655-
crate::detail::__rust_thunk___ZN17NontrivialByValueC1Ev(
655+
crate::detail::__rust_thunk___ZN17NontrivialByValueC1ERKS_(
656656
::std::pin::Pin::into_inner_unchecked(dest),
657+
other,
657658
);
658659
},
659660
)
660661
}
661662
}
663+
impl<'b> ::ctor::CtorNew<(&'b crate::NontrivialByValue,)> for NontrivialByValue {
664+
type CtorType = impl ::ctor::Ctor<Output = Self> + ::ctor::Captures<'b>;
665+
#[inline(always)]
666+
fn ctor_new(args: (&'b crate::NontrivialByValue,)) -> Self::CtorType {
667+
let (arg,) = args;
668+
<Self as ::ctor::CtorNew<&'b crate::NontrivialByValue>>::ctor_new(arg)
669+
}
670+
}
662671

663-
impl<'b> ::ctor::CtorNew<&'b crate::NontrivialByValue> for NontrivialByValue {
672+
impl<'b> ::ctor::CtorNew<::ctor::RvalueReference<'b, crate::NontrivialByValue>>
673+
for NontrivialByValue
674+
{
664675
type CtorType = impl ::ctor::Ctor<Output = Self> + ::ctor::Captures<'b>;
665676
#[inline(always)]
666-
fn ctor_new(args: &'b crate::NontrivialByValue) -> Self::CtorType {
667-
let __param_0 = args;
677+
fn ctor_new(args: ::ctor::RvalueReference<'b, crate::NontrivialByValue>) -> Self::CtorType {
678+
let other = args;
668679
::ctor::FnCtor::new(
669680
move |dest: ::std::pin::Pin<&mut ::std::mem::MaybeUninit<Self>>| unsafe {
670-
crate::detail::__rust_thunk___ZN17NontrivialByValueC1ERKS_(
681+
crate::detail::__rust_thunk___ZN17NontrivialByValueC1EOS_(
671682
::std::pin::Pin::into_inner_unchecked(dest),
672-
__param_0,
683+
other,
673684
);
674685
},
675686
)
676687
}
677688
}
678-
impl<'b> ::ctor::CtorNew<(&'b crate::NontrivialByValue,)> for NontrivialByValue {
689+
impl<'b> ::ctor::CtorNew<(::ctor::RvalueReference<'b, crate::NontrivialByValue>,)>
690+
for NontrivialByValue
691+
{
679692
type CtorType = impl ::ctor::Ctor<Output = Self> + ::ctor::Captures<'b>;
680693
#[inline(always)]
681-
fn ctor_new(args: (&'b crate::NontrivialByValue,)) -> Self::CtorType {
694+
fn ctor_new(args: (::ctor::RvalueReference<'b, crate::NontrivialByValue>,)) -> Self::CtorType {
682695
let (arg,) = args;
683-
<Self as ::ctor::CtorNew<&'b crate::NontrivialByValue>>::ctor_new(arg)
696+
<Self as ::ctor::CtorNew<::ctor::RvalueReference<'b, crate::NontrivialByValue>>>::ctor_new(
697+
arg,
698+
)
699+
}
700+
}
701+
702+
impl<'b> ::ctor::Assign<&'b crate::NontrivialByValue> for NontrivialByValue {
703+
#[inline(always)]
704+
fn assign<'a>(self: ::std::pin::Pin<&'a mut Self>, other: &'b crate::NontrivialByValue) {
705+
unsafe {
706+
crate::detail::__rust_thunk___ZN17NontrivialByValueaSERKS_(self, other);
707+
}
684708
}
685709
}
686710

687-
// rs_bindings_from_cc/test/golden/nontrivial_type.h;l=97
688-
// Error while generating bindings for item 'NontrivialByValue::operator=':
689-
// b/200067242: non-Unpin types are not yet supported by value in traits
711+
impl<'b> ::ctor::Assign<::ctor::RvalueReference<'b, crate::NontrivialByValue>>
712+
for NontrivialByValue
713+
{
714+
#[inline(always)]
715+
fn assign<'a>(
716+
self: ::std::pin::Pin<&'a mut Self>,
717+
other: ::ctor::RvalueReference<'b, crate::NontrivialByValue>,
718+
) {
719+
unsafe {
720+
crate::detail::__rust_thunk___ZN17NontrivialByValueaSEOS_(self, other);
721+
}
722+
}
723+
}
724+
725+
impl ::ctor::Assign<::ctor::RvalueReference<'_, crate::Nontrivial>> for NontrivialByValue {
726+
#[inline(always)]
727+
fn assign<'a>(
728+
self: ::std::pin::Pin<&'a mut Self>,
729+
other: ::ctor::RvalueReference<'_, crate::Nontrivial>,
730+
) {
731+
unsafe {
732+
let _ = ::ctor::emplace!(::ctor::FnCtor::new(
733+
move |dest: ::std::pin::Pin<
734+
&mut ::std::mem::MaybeUninit<crate::NontrivialByValue>,
735+
>| {
736+
crate::detail::__rust_thunk___ZN17NontrivialByValueaSE10Nontrivial(
737+
::std::pin::Pin::into_inner_unchecked(dest),
738+
self,
739+
other,
740+
);
741+
}
742+
));
743+
}
744+
}
745+
}
690746

691-
// rs_bindings_from_cc/test/golden/nontrivial_type.h;l=98
747+
// rs_bindings_from_cc/test/golden/nontrivial_type.h;l=102
692748
// Error while generating bindings for item 'NontrivialByValue::operator==':
693749
// operator== where operands are not const references
694750

@@ -728,7 +784,7 @@ impl Nonmovable {
728784
}
729785
}
730786

731-
// rs_bindings_from_cc/test/golden/nontrivial_type.h;l=110
787+
// rs_bindings_from_cc/test/golden/nontrivial_type.h;l=114
732788
// Error while generating bindings for item 'TakesNonmovableByValue':
733789
// Non-movable, non-trivial_abi type 'crate :: Nonmovable' is not supported by value as parameter #0
734790

@@ -922,12 +978,26 @@ mod detail {
922978
pub(crate) fn __rust_thunk___Z21TakesByReferenceUnpinR15NontrivialUnpin<'a>(
923979
nontrivial: &'a mut crate::NontrivialUnpin,
924980
) -> &'a mut crate::NontrivialUnpin;
925-
pub(crate) fn __rust_thunk___ZN17NontrivialByValueC1Ev<'a>(
981+
pub(crate) fn __rust_thunk___ZN17NontrivialByValueC1ERKS_<'a, 'b>(
926982
__this: &'a mut ::std::mem::MaybeUninit<crate::NontrivialByValue>,
983+
other: &'b crate::NontrivialByValue,
927984
);
928-
pub(crate) fn __rust_thunk___ZN17NontrivialByValueC1ERKS_<'a, 'b>(
985+
pub(crate) fn __rust_thunk___ZN17NontrivialByValueC1EOS_<'a, 'b>(
929986
__this: &'a mut ::std::mem::MaybeUninit<crate::NontrivialByValue>,
930-
__param_0: &'b crate::NontrivialByValue,
987+
other: ::ctor::RvalueReference<'b, crate::NontrivialByValue>,
988+
);
989+
pub(crate) fn __rust_thunk___ZN17NontrivialByValueaSERKS_<'a, 'b>(
990+
__this: ::std::pin::Pin<&'a mut crate::NontrivialByValue>,
991+
other: &'b crate::NontrivialByValue,
992+
) -> ::std::pin::Pin<&'a mut crate::NontrivialByValue>;
993+
pub(crate) fn __rust_thunk___ZN17NontrivialByValueaSEOS_<'a, 'b>(
994+
__this: ::std::pin::Pin<&'a mut crate::NontrivialByValue>,
995+
other: ::ctor::RvalueReference<'b, crate::NontrivialByValue>,
996+
) -> ::std::pin::Pin<&'a mut crate::NontrivialByValue>;
997+
pub(crate) fn __rust_thunk___ZN17NontrivialByValueaSE10Nontrivial<'a>(
998+
__return: &mut ::std::mem::MaybeUninit<crate::NontrivialByValue>,
999+
__this: ::std::pin::Pin<&'a mut crate::NontrivialByValue>,
1000+
other: ::ctor::RvalueReference<'_, crate::Nontrivial>,
9311001
);
9321002
#[link_name = "_ZN10NonmovableC1Ev"]
9331003
pub(crate) fn __rust_thunk___ZN10NonmovableC1Ev<'a>(

rs_bindings_from_cc/test/golden/nontrivial_type_rs_api_impl.cc

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -94,14 +94,27 @@ extern "C" void __rust_thunk___Z18TakesByValueInline16NontrivialInline(
9494
extern "C" void __rust_thunk___Z14ReturnsByValuev(struct Nontrivial* __return) {
9595
new (__return) auto(ReturnsByValue());
9696
}
97-
extern "C" void __rust_thunk___ZN17NontrivialByValueC1Ev(
98-
struct NontrivialByValue* __this) {
99-
crubit::construct_at(__this);
100-
}
10197
extern "C" void __rust_thunk___ZN17NontrivialByValueC1ERKS_(
102-
struct NontrivialByValue* __this,
103-
const struct NontrivialByValue* __param_0) {
104-
crubit::construct_at(__this, *__param_0);
98+
struct NontrivialByValue* __this, const struct NontrivialByValue* other) {
99+
crubit::construct_at(__this, *other);
100+
}
101+
extern "C" void __rust_thunk___ZN17NontrivialByValueC1EOS_(
102+
struct NontrivialByValue* __this, struct NontrivialByValue* other) {
103+
crubit::construct_at(__this, std::move(*other));
104+
}
105+
extern "C" struct NontrivialByValue*
106+
__rust_thunk___ZN17NontrivialByValueaSERKS_(
107+
struct NontrivialByValue* __this, const struct NontrivialByValue* other) {
108+
return &__this->operator=(*other);
109+
}
110+
extern "C" struct NontrivialByValue* __rust_thunk___ZN17NontrivialByValueaSEOS_(
111+
struct NontrivialByValue* __this, struct NontrivialByValue* other) {
112+
return &__this->operator=(std::move(*other));
113+
}
114+
extern "C" void __rust_thunk___ZN17NontrivialByValueaSE10Nontrivial(
115+
struct NontrivialByValue* __return, struct NontrivialByValue* __this,
116+
struct Nontrivial* other) {
117+
new (__return) auto(__this->operator=(std::move(*other)));
105118
}
106119
extern "C" void __rust_thunk___Z24ReturnsNonmovableByValuev(
107120
struct Nonmovable* __return) {

0 commit comments

Comments
 (0)