Skip to content

Commit a085e89

Browse files
authored
Merge pull request #1610 from dtolnay/aliasreference
Allow unpinned mutable reference for any Unpin type
2 parents fdcd94e + 3910bce commit a085e89

File tree

15 files changed

+374
-75
lines changed

15 files changed

+374
-75
lines changed

gen/src/write.rs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -527,8 +527,7 @@ fn check_trivial_extern_type(out: &mut OutFile, alias: &TypeAlias, reasons: &[Tr
527527
TrivialReason::StructField(_)
528528
| TrivialReason::FunctionArgument(_)
529529
| TrivialReason::FunctionReturn(_)
530-
| TrivialReason::SliceElement { .. }
531-
| TrivialReason::UnpinnedMut(_) => false,
530+
| TrivialReason::SliceElement { .. } => false,
532531
};
533532
// If the type is only used as a struct field or Vec element, not as
534533
// by-value function argument or return value, then C array of trivially
@@ -545,8 +544,7 @@ fn check_trivial_extern_type(out: &mut OutFile, alias: &TypeAlias, reasons: &[Tr
545544
TrivialReason::FunctionArgument(_)
546545
| TrivialReason::FunctionReturn(_)
547546
| TrivialReason::BoxTarget { .. }
548-
| TrivialReason::SliceElement { .. }
549-
| TrivialReason::UnpinnedMut(_) => false,
547+
| TrivialReason::SliceElement { .. } => false,
550548
};
551549
}
552550

macro/src/expand.rs

Lines changed: 84 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use crate::message::Message;
12
use crate::syntax::atom::Atom::*;
23
use crate::syntax::attrs::{self, OtherAttrs};
34
use crate::syntax::cfg::{CfgExpr, ComputedCfg};
@@ -9,6 +10,7 @@ use crate::syntax::report::Errors;
910
use crate::syntax::symbol::Symbol;
1011
use crate::syntax::trivial::TrivialReason;
1112
use crate::syntax::types::ConditionalImpl;
13+
use crate::syntax::unpin::UnpinReason;
1214
use crate::syntax::{
1315
self, check, mangle, Api, Doc, Enum, ExternFn, ExternType, FnKind, Lang, Lifetimes, Pair,
1416
Signature, Struct, Trait, Type, TypeAlias, Types,
@@ -1424,13 +1426,92 @@ fn expand_type_alias_verify(alias: &TypeAlias, types: &Types) -> TokenStream {
14241426
TrivialReason::StructField(_)
14251427
| TrivialReason::FunctionArgument(_)
14261428
| TrivialReason::FunctionReturn(_)
1427-
| TrivialReason::SliceElement { .. }
1428-
| TrivialReason::UnpinnedMut(_) => require_extern_type_trivial = true,
1429+
| TrivialReason::SliceElement { .. } => require_extern_type_trivial = true,
14291430
}
14301431
}
14311432
}
14321433

1433-
if require_unpin {
1434+
if let Some(reason) = types.required_unpin.get(ident) {
1435+
let ampersand;
1436+
let reference_lifetime;
1437+
let mutability;
1438+
let mut inner;
1439+
let generics;
1440+
let shorthand;
1441+
match reason {
1442+
UnpinReason::Receiver(receiver) => {
1443+
ampersand = &receiver.ampersand;
1444+
reference_lifetime = &receiver.lifetime;
1445+
mutability = &receiver.mutability;
1446+
inner = receiver.ty.rust.clone();
1447+
generics = &receiver.ty.generics;
1448+
shorthand = receiver.shorthand;
1449+
if receiver.shorthand {
1450+
inner.set_span(receiver.var.span);
1451+
}
1452+
}
1453+
UnpinReason::Ref(mutable_reference) => {
1454+
ampersand = &mutable_reference.ampersand;
1455+
reference_lifetime = &mutable_reference.lifetime;
1456+
mutability = &mutable_reference.mutability;
1457+
let Type::Ident(inner_type) = &mutable_reference.inner else {
1458+
unreachable!();
1459+
};
1460+
inner = inner_type.rust.clone();
1461+
generics = &inner_type.generics;
1462+
shorthand = false;
1463+
}
1464+
}
1465+
let trait_name = format_ident!("ReferenceToUnpin_{ident}");
1466+
let message =
1467+
format!("mutable reference to C++ type requires a pin -- use Pin<&mut {ident}>");
1468+
let label = {
1469+
let mut label = Message::new();
1470+
write!(label, "use `");
1471+
if shorthand {
1472+
write!(label, "self: ");
1473+
}
1474+
write!(label, "Pin<&");
1475+
if let Some(reference_lifetime) = reference_lifetime {
1476+
write!(label, "{reference_lifetime} ");
1477+
}
1478+
write!(label, "mut {ident}");
1479+
if !generics.lifetimes.is_empty() {
1480+
write!(label, "<");
1481+
for (i, lifetime) in generics.lifetimes.iter().enumerate() {
1482+
if i > 0 {
1483+
write!(label, ", ");
1484+
}
1485+
write!(label, "{lifetime}");
1486+
}
1487+
write!(label, ">");
1488+
} else if shorthand && !alias.generics.lifetimes.is_empty() {
1489+
write!(label, "<");
1490+
for i in 0..alias.generics.lifetimes.len() {
1491+
if i > 0 {
1492+
write!(label, ", ");
1493+
}
1494+
write!(label, "'_");
1495+
}
1496+
write!(label, ">");
1497+
}
1498+
write!(label, ">`");
1499+
label
1500+
};
1501+
let lifetimes = generics.to_underscore_lifetimes();
1502+
verify.extend(quote! {
1503+
#attrs
1504+
let _ = {
1505+
#[diagnostic::on_unimplemented(message = #message, label = #label)]
1506+
trait #trait_name {
1507+
fn check_unpin() {}
1508+
}
1509+
#[diagnostic::do_not_recommend]
1510+
impl<'a, T: ?::cxx::core::marker::Sized + ::cxx::core::marker::Unpin> #trait_name for &'a mut T {}
1511+
<#ampersand #mutability #inner #lifetimes as #trait_name>::check_unpin
1512+
};
1513+
});
1514+
} else if require_unpin {
14341515
verify.extend(quote! {
14351516
#attrs
14361517
const _: fn() = ::cxx::private::require_unpin::<#ident #lifetimes>;

macro/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ mod cfg;
2727
mod derive;
2828
mod expand;
2929
mod generics;
30+
mod message;
3031
mod syntax;
3132
mod tokens;
3233
mod type_id;

macro/src/message.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
use proc_macro2::TokenStream;
2+
use quote::ToTokens;
3+
use std::fmt;
4+
5+
pub(crate) struct Message(String);
6+
7+
impl Message {
8+
pub fn new() -> Self {
9+
Message(String::new())
10+
}
11+
12+
pub fn write_fmt(&mut self, args: fmt::Arguments) {
13+
fmt::Write::write_fmt(&mut self.0, args).unwrap();
14+
}
15+
}
16+
17+
impl ToTokens for Message {
18+
fn to_tokens(&self, tokens: &mut TokenStream) {
19+
self.0.to_tokens(tokens);
20+
}
21+
}

src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -503,7 +503,7 @@ pub mod private {
503503
#[cfg(feature = "alloc")]
504504
pub use crate::rust_string::RustString;
505505
pub use crate::rust_type::{
506-
require_box, require_unpin, require_vec, ImplBox, ImplVec, RustType,
506+
require_box, require_unpin, require_vec, with, ImplBox, ImplVec, RustType, Without,
507507
};
508508
#[cfg(feature = "alloc")]
509509
pub use crate::rust_vec::RustVec;

src/rust_type.rs

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#![allow(missing_docs)]
22

3-
use core::marker::Unpin;
3+
use core::marker::{PhantomData, Unpin};
4+
use core::ops::Deref;
45

56
pub unsafe trait RustType {}
67
pub unsafe trait ImplBox {}
@@ -11,3 +12,22 @@ pub fn require_unpin<T: ?Sized + Unpin>() {}
1112

1213
pub fn require_box<T: ImplBox>() {}
1314
pub fn require_vec<T: ImplVec>() {}
15+
16+
pub struct With<T: ?Sized>(PhantomData<T>);
17+
pub struct Without;
18+
19+
pub const fn with<T: ?Sized>() -> With<T> {
20+
With(PhantomData)
21+
}
22+
23+
impl<T: ?Sized + Unpin> With<T> {
24+
#[allow(clippy::unused_self)]
25+
pub const fn check_unpin<U>(&self) {}
26+
}
27+
28+
impl<T: ?Sized> Deref for With<T> {
29+
type Target = Without;
30+
fn deref(&self) -> &Self::Target {
31+
&Without
32+
}
33+
}

syntax/check.rs

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,11 @@ fn check_type_ref(cx: &mut Check, ty: &Ref) {
229229
if ty.mutable && !ty.pinned {
230230
if let Some(requires_pin) = match &ty.inner {
231231
Type::Ident(ident)
232-
if ident.rust == CxxString || is_opaque_cxx(cx.types, &ident.rust) =>
232+
if ident.rust == CxxString
233+
|| (cx.types.cxx.contains(&ident.rust)
234+
&& !cx.types.structs.contains_key(&ident.rust)
235+
&& !cx.types.enums.contains_key(&ident.rust)
236+
&& !cx.types.aliases.contains_key(&ident.rust)) =>
233237
{
234238
Some(ident.rust.to_string())
235239
}
@@ -284,7 +288,10 @@ fn check_type_slice_ref(cx: &mut Check, ty: &SliceRef) {
284288
let mutable = if ty.mutable { "mut " } else { "" };
285289
let mut msg = format!("unsupported &{}[T] element type", mutable);
286290
if let Type::Ident(ident) = &ty.inner {
287-
if is_opaque_cxx(cx.types, &ident.rust) {
291+
if cx.types.cxx.contains(&ident.rust)
292+
&& !cx.types.structs.contains_key(&ident.rust)
293+
&& !cx.types.enums.contains_key(&ident.rust)
294+
{
288295
msg += ": opaque C++ type is not supported yet";
289296
}
290297
}
@@ -457,7 +464,9 @@ fn check_api_fn(cx: &mut Check, efn: &ExternFn) {
457464
cx.error(span, "unrecognized receiver type");
458465
} else if receiver.mutable
459466
&& !receiver.pinned
460-
&& is_opaque_cxx(cx.types, &receiver.ty.rust)
467+
&& cx.types.cxx.contains(&receiver.ty.rust)
468+
&& !cx.types.structs.contains_key(&receiver.ty.rust)
469+
&& !cx.types.aliases.contains_key(&receiver.ty.rust)
461470
{
462471
cx.error(
463472
span,
@@ -665,7 +674,13 @@ fn is_unsized(types: &Types, ty: &Type) -> bool {
665674
match ty {
666675
Type::Ident(ident) => {
667676
let ident = &ident.rust;
668-
ident == CxxString || is_opaque_cxx(types, ident) || types.rust.contains(ident)
677+
ident == CxxString
678+
|| (types.cxx.contains(ident)
679+
&& !types.structs.contains_key(ident)
680+
&& !types.enums.contains_key(ident)
681+
&& !(types.aliases.contains_key(ident)
682+
&& types.required_trivial.contains_key(ident)))
683+
|| types.rust.contains(ident)
669684
}
670685
Type::Array(array) => is_unsized(types, &array.inner),
671686
Type::CxxVector(_) | Type::Fn(_) | Type::Void(_) => true,
@@ -681,13 +696,6 @@ fn is_unsized(types: &Types, ty: &Type) -> bool {
681696
}
682697
}
683698

684-
fn is_opaque_cxx(types: &Types, ty: &Ident) -> bool {
685-
types.cxx.contains(ty)
686-
&& !types.structs.contains_key(ty)
687-
&& !types.enums.contains_key(ty)
688-
&& !(types.aliases.contains_key(ty) && types.required_trivial.contains_key(ty))
689-
}
690-
691699
fn span_for_struct_error(strct: &Struct) -> TokenStream {
692700
let struct_token = strct.struct_token;
693701
let mut brace_token = Group::new(Delimiter::Brace, TokenStream::new());

syntax/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ mod tokens;
3131
mod toposort;
3232
pub(crate) mod trivial;
3333
pub(crate) mod types;
34+
pub(crate) mod unpin;
3435
mod visit;
3536

3637
use self::attrs::OtherAttrs;

syntax/trivial.rs

Lines changed: 6 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ pub(crate) enum TrivialReason<'a> {
2626
SliceElement {
2727
mutable: bool,
2828
},
29-
UnpinnedMut(&'a ExternFn),
3029
}
3130

3231
pub(crate) fn required_trivial_reasons<'a>(
@@ -63,45 +62,15 @@ pub(crate) fn required_trivial_reasons<'a>(
6362
}
6463
}
6564
Api::CxxFunction(efn) | Api::RustFunction(efn) => {
66-
if let Some(receiver) = &efn.receiver() {
67-
if receiver.mutable && !receiver.pinned {
68-
let reason = TrivialReason::UnpinnedMut(efn);
69-
insist_extern_types_are_trivial(&receiver.ty, reason);
70-
}
71-
}
7265
for arg in &efn.args {
73-
match &arg.ty {
74-
Type::Ident(ident) => {
75-
let reason = TrivialReason::FunctionArgument(efn);
76-
insist_extern_types_are_trivial(ident, reason);
77-
}
78-
Type::Ref(ty) => {
79-
if ty.mutable && !ty.pinned {
80-
if let Type::Ident(ident) = &ty.inner {
81-
let reason = TrivialReason::UnpinnedMut(efn);
82-
insist_extern_types_are_trivial(ident, reason);
83-
}
84-
}
85-
}
86-
_ => {}
66+
if let Type::Ident(ident) = &arg.ty {
67+
let reason = TrivialReason::FunctionArgument(efn);
68+
insist_extern_types_are_trivial(ident, reason);
8769
}
8870
}
89-
if let Some(ret) = &efn.ret {
90-
match ret {
91-
Type::Ident(ident) => {
92-
let reason = TrivialReason::FunctionReturn(efn);
93-
insist_extern_types_are_trivial(ident, reason);
94-
}
95-
Type::Ref(ty) => {
96-
if ty.mutable && !ty.pinned {
97-
if let Type::Ident(ident) = &ty.inner {
98-
let reason = TrivialReason::UnpinnedMut(efn);
99-
insist_extern_types_are_trivial(ident, reason);
100-
}
101-
}
102-
}
103-
_ => {}
104-
}
71+
if let Some(Type::Ident(ident)) = &efn.ret {
72+
let reason = TrivialReason::FunctionReturn(efn);
73+
insist_extern_types_are_trivial(ident, reason);
10574
}
10675
}
10776
_ => {}
@@ -162,7 +131,6 @@ pub(crate) fn as_what<'a>(name: &'a Pair, reasons: &'a [TrivialReason]) -> impl
162131
let mut vec_element = false;
163132
let mut slice_shared_element = false;
164133
let mut slice_mut_element = false;
165-
let mut unpinned_mut = Set::new();
166134

167135
for reason in self.reasons {
168136
match reason {
@@ -184,9 +152,6 @@ pub(crate) fn as_what<'a>(name: &'a Pair, reasons: &'a [TrivialReason]) -> impl
184152
slice_shared_element = true;
185153
}
186154
}
187-
TrivialReason::UnpinnedMut(efn) => {
188-
unpinned_mut.insert(&efn.name.rust);
189-
}
190155
}
191156
}
192157

@@ -235,13 +200,6 @@ pub(crate) fn as_what<'a>(name: &'a Pair, reasons: &'a [TrivialReason]) -> impl
235200
param: self.name,
236201
});
237202
}
238-
if !unpinned_mut.is_empty() {
239-
clauses.push(Clause::Set {
240-
article: "a",
241-
desc: "non-pinned mutable reference in signature of",
242-
set: &unpinned_mut,
243-
});
244-
}
245203

246204
for (i, clause) in clauses.iter().enumerate() {
247205
if i == 0 {

syntax/types.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use crate::syntax::report::Errors;
77
use crate::syntax::resolve::Resolution;
88
use crate::syntax::set::UnorderedSet;
99
use crate::syntax::trivial::{self, TrivialReason};
10+
use crate::syntax::unpin::{self, UnpinReason};
1011
use crate::syntax::visit::{self, Visit};
1112
use crate::syntax::{
1213
toposort, Api, Atom, Enum, ExternType, Impl, Lifetimes, Pair, Struct, Type, TypeAlias,
@@ -24,6 +25,8 @@ pub(crate) struct Types<'a> {
2425
pub aliases: UnorderedMap<&'a Ident, &'a TypeAlias>,
2526
pub untrusted: UnorderedMap<&'a Ident, &'a ExternType>,
2627
pub required_trivial: UnorderedMap<&'a Ident, Vec<TrivialReason<'a>>>,
28+
#[allow(dead_code)] // only used by cxxbridge-macro, not cxx-build
29+
pub required_unpin: UnorderedMap<&'a Ident, UnpinReason<'a>>,
2730
pub impls: OrderedMap<ImplKey<'a>, ConditionalImpl<'a>>,
2831
pub resolutions: UnorderedMap<&'a Ident, Resolution<'a>>,
2932
pub struct_improper_ctypes: UnorderedSet<&'a Ident>,
@@ -234,6 +237,9 @@ impl<'a> Types<'a> {
234237
let required_trivial =
235238
trivial::required_trivial_reasons(apis, &all, &structs, &enums, &cxx, &aliases, &impls);
236239

240+
let required_unpin =
241+
unpin::required_unpin_reasons(apis, &all, &structs, &enums, &cxx, &aliases);
242+
237243
let mut types = Types {
238244
all,
239245
structs,
@@ -243,6 +249,7 @@ impl<'a> Types<'a> {
243249
aliases,
244250
untrusted,
245251
required_trivial,
252+
required_unpin,
246253
impls,
247254
resolutions,
248255
struct_improper_ctypes,

0 commit comments

Comments
 (0)