Skip to content

Commit 922f98b

Browse files
authored
Merge pull request #1111 from bsilver8192/subclass-unsafe
Fix and test subclass without `safety!(unsafe)`
2 parents bfbcc6b + 733d751 commit 922f98b

File tree

6 files changed

+84
-12
lines changed

6 files changed

+84
-12
lines changed

engine/src/conversion/analysis/fun/mod.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -270,7 +270,7 @@ enum TypeConversionSophistication {
270270
}
271271

272272
pub(crate) struct FnAnalyzer<'a> {
273-
unsafe_policy: UnsafePolicy,
273+
unsafe_policy: &'a UnsafePolicy,
274274
extra_apis: ApiVec<NullPhase>,
275275
type_converter: TypeConverter<'a>,
276276
bridge_name_tracker: BridgeNameTracker,
@@ -288,7 +288,7 @@ pub(crate) struct FnAnalyzer<'a> {
288288
impl<'a> FnAnalyzer<'a> {
289289
pub(crate) fn analyze_functions(
290290
apis: ApiVec<PodPhase>,
291-
unsafe_policy: UnsafePolicy,
291+
unsafe_policy: &'a UnsafePolicy,
292292
config: &'a IncludeCppConfig,
293293
) -> ApiVec<FnPrePhase2> {
294294
let mut me = Self {
@@ -476,7 +476,9 @@ impl<'a> FnAnalyzer<'a> {
476476
UnsafetyNeeded::Always => UnsafetyNeeded::JustBridge,
477477
_ => unsafest_param,
478478
},
479-
_ if self.unsafe_policy == UnsafePolicy::AllFunctionsUnsafe => UnsafetyNeeded::Always,
479+
_ if matches!(self.unsafe_policy, UnsafePolicy::AllFunctionsUnsafe) => {
480+
UnsafetyNeeded::Always
481+
}
480482
_ => match unsafest_non_placement_param {
481483
UnsafetyNeeded::Always => UnsafetyNeeded::Always,
482484
UnsafetyNeeded::JustBridge => match unsafest_param {
@@ -638,6 +640,7 @@ impl<'a> FnAnalyzer<'a> {
638640
receiver_mutability,
639641
sup,
640642
subclass_fn_deps,
643+
self.unsafe_policy,
641644
));
642645

643646
// Create the trait item for the <superclass>_methods and <superclass>_supers
@@ -655,6 +658,7 @@ impl<'a> FnAnalyzer<'a> {
655658
receiver_mutability,
656659
sup.clone(),
657660
is_pure_virtual,
661+
self.unsafe_policy,
658662
));
659663
}
660664
}

engine/src/conversion/analysis/fun/subclass.rs

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use indexmap::map::IndexMap as HashMap;
1010

1111
use syn::{parse_quote, FnArg, PatType, Type, TypePtr};
1212

13-
use crate::conversion::analysis::fun::{FnKind, MethodKind, ReceiverMutability};
13+
use crate::conversion::analysis::fun::{FnKind, MethodKind, ReceiverMutability, UnsafePolicy};
1414
use crate::conversion::analysis::pod::PodPhase;
1515
use crate::conversion::api::{
1616
CppVisibility, FuncToConvert, Provenance, RustSubclassFnDetails, SubclassConstructorDetails,
@@ -79,12 +79,18 @@ pub(super) fn create_subclass_trait_item(
7979
receiver_mutability: &ReceiverMutability,
8080
receiver: QualifiedName,
8181
is_pure_virtual: bool,
82+
unsafe_policy: &UnsafePolicy,
8283
) -> Api<FnPrePhase1> {
8384
let param_names = analysis
8485
.param_details
8586
.iter()
8687
.map(|pd| pd.name.clone())
8788
.collect();
89+
let requires_unsafe = if matches!(unsafe_policy, UnsafePolicy::AllFunctionsUnsafe) {
90+
UnsafetyNeeded::Always
91+
} else {
92+
UnsafetyNeeded::from_param_details(&analysis.param_details, false)
93+
};
8894
Api::SubclassTraitItem {
8995
name,
9096
details: SuperclassMethod {
@@ -93,7 +99,7 @@ pub(super) fn create_subclass_trait_item(
9399
ret_type: analysis.ret_type.clone(),
94100
param_names,
95101
receiver_mutability: receiver_mutability.clone(),
96-
requires_unsafe: UnsafetyNeeded::from_param_details(&analysis.param_details, false),
102+
requires_unsafe,
97103
is_pure_virtual,
98104
receiver,
99105
},
@@ -107,6 +113,7 @@ pub(super) fn create_subclass_function(
107113
receiver_mutability: &ReceiverMutability,
108114
superclass: &QualifiedName,
109115
dependencies: Vec<QualifiedName>,
116+
unsafe_policy: &UnsafePolicy,
110117
) -> Api<FnPrePhase1> {
111118
let cpp = sub.cpp();
112119
let holder_name = sub.holder();
@@ -131,6 +138,11 @@ pub(super) fn create_subclass_function(
131138
.skip(1)
132139
.map(|p| p.conversion.clone())
133140
.collect();
141+
let requires_unsafe = if matches!(unsafe_policy, UnsafePolicy::AllFunctionsUnsafe) {
142+
UnsafetyNeeded::Always
143+
} else {
144+
UnsafetyNeeded::from_param_details(&analysis.param_details, false)
145+
};
134146
Api::RustSubclassFn {
135147
name: ApiName::new_in_root_namespace(rust_call_name.clone()),
136148
subclass: sub.clone(),
@@ -151,7 +163,7 @@ pub(super) fn create_subclass_function(
151163
superclass: superclass.clone(),
152164
receiver_mutability: receiver_mutability.clone(),
153165
dependencies,
154-
requires_unsafe: UnsafetyNeeded::from_param_details(&analysis.param_details, false),
166+
requires_unsafe,
155167
is_pure_virtual: matches!(
156168
analysis.kind,
157169
FnKind::Method {

engine/src/conversion/codegen_rs/mod.rs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ pub(crate) mod unqualify;
1717
use indexmap::map::IndexMap as HashMap;
1818
use indexmap::set::IndexSet as HashSet;
1919

20-
use autocxx_parser::{ExternCppType, IncludeCppConfig, RustFun};
20+
use autocxx_parser::{ExternCppType, IncludeCppConfig, RustFun, UnsafePolicy};
2121

2222
use itertools::Itertools;
2323
use proc_macro2::{Span, TokenStream};
@@ -134,6 +134,7 @@ fn get_string_items() -> Vec<Item> {
134134
/// In practice, much of the "generation" involves connecting together
135135
/// existing lumps of code within the Api structures.
136136
pub(crate) struct RsCodeGenerator<'a> {
137+
unsafe_policy: &'a UnsafePolicy,
137138
include_list: &'a [String],
138139
bindgen_mod: ItemMod,
139140
original_name_map: CppNameMap,
@@ -145,12 +146,14 @@ impl<'a> RsCodeGenerator<'a> {
145146
/// Generate code for a set of APIs that was discovered during parsing.
146147
pub(crate) fn generate_rs_code(
147148
all_apis: ApiVec<FnPhase>,
149+
unsafe_policy: &'a UnsafePolicy,
148150
include_list: &'a [String],
149151
bindgen_mod: ItemMod,
150152
config: &'a IncludeCppConfig,
151153
header_name: Option<String>,
152154
) -> Vec<Item> {
153155
let c = Self {
156+
unsafe_policy,
154157
include_list,
155158
bindgen_mod,
156159
original_name_map: original_name_map_from_apis(&all_apis),
@@ -609,8 +612,11 @@ impl<'a> RsCodeGenerator<'a> {
609612
name, superclass, ..
610613
} => {
611614
let methods = associated_methods.get(&superclass);
612-
let generate_peer_constructor =
613-
subclasses_with_a_single_trivial_constructor.contains(&name.0.name);
615+
let generate_peer_constructor = subclasses_with_a_single_trivial_constructor.contains(&name.0.name) &&
616+
// TODO: Create an UnsafeCppPeerConstructor trait for calling an unsafe
617+
// constructor instead? Need to create unsafe versions of everything that uses
618+
// it too.
619+
matches!(self.unsafe_policy, UnsafePolicy::AllFunctionsSafe);
614620
self.generate_subclass(name, &superclass, methods, generate_peer_constructor)
615621
}
616622
Api::ExternCppType {

engine/src/conversion/mod.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ impl<'a> BridgeConverter<'a> {
157157
// parameterized by a richer set of metadata.
158158
Self::dump_apis("adding casts", &analyzed_apis);
159159
let analyzed_apis =
160-
FnAnalyzer::analyze_functions(analyzed_apis, unsafe_policy, self.config);
160+
FnAnalyzer::analyze_functions(analyzed_apis, &unsafe_policy, self.config);
161161
// If any of those functions turned out to be pure virtual, don't attempt
162162
// to generate UniquePtr implementations for the type, since it can't
163163
// be instantiated.
@@ -197,6 +197,7 @@ impl<'a> BridgeConverter<'a> {
197197
)?;
198198
let rs = RsCodeGenerator::generate_rs_code(
199199
analyzed_apis,
200+
&unsafe_policy,
200201
self.include_list,
201202
bindgen_mod,
202203
self.config,

integration-tests/tests/integration_test.rs

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7691,6 +7691,54 @@ fn test_two_superclasses_with_same_name_method() {
76917691
);
76927692
}
76937693

7694+
#[test]
7695+
fn test_subclass_no_safety() {
7696+
let hdr = indoc! {"
7697+
#include <cstdint>
7698+
7699+
class Observer {
7700+
public:
7701+
Observer() {}
7702+
virtual void foo() = 0;
7703+
virtual ~Observer() {}
7704+
};
7705+
"};
7706+
let hexathorpe = Token![#](Span::call_site());
7707+
let unexpanded_rust = quote! {
7708+
use autocxx::prelude::*;
7709+
7710+
include_cpp!(
7711+
#hexathorpe include "input.h"
7712+
subclass!("Observer",MyObserver)
7713+
);
7714+
7715+
use ffi::Observer_methods;
7716+
#hexathorpe [autocxx::subclass::subclass]
7717+
pub struct MyObserver;
7718+
impl Observer_methods for MyObserver {
7719+
unsafe fn foo(&mut self) {}
7720+
}
7721+
7722+
use autocxx::subclass::{CppSubclass, CppPeerConstructor, CppSubclassRustPeerHolder};
7723+
use cxx::UniquePtr;
7724+
impl CppPeerConstructor<ffi::MyObserverCpp> for MyObserver {
7725+
fn make_peer(
7726+
&mut self,
7727+
peer_holder: CppSubclassRustPeerHolder<Self>,
7728+
) -> UniquePtr<ffi::MyObserverCpp> {
7729+
UniquePtr::emplace(unsafe { ffi::MyObserverCpp::new(peer_holder) })
7730+
}
7731+
}
7732+
7733+
fn main() {
7734+
let obs = MyObserver::new_rust_owned(MyObserver { cpp_peer: Default::default() });
7735+
unsafe { obs.borrow_mut().foo() };
7736+
}
7737+
};
7738+
7739+
do_run_test_manual("", hdr, unexpanded_rust, None, None).unwrap()
7740+
}
7741+
76947742
#[test]
76957743
fn test_pv_protected_constructor() {
76967744
let hdr = indoc! {"

src/subclass.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -207,8 +207,9 @@ pub trait CppPeerConstructor<CppPeer: CppSubclassCppPeer>: Sized {
207207
/// * You _may_ need to implement [`CppPeerConstructor`] for your subclass,
208208
/// but only if autocxx determines that there are multiple possible superclass
209209
/// constructors so you need to call one explicitly (or if there's a single
210-
/// non-trivial superclass constructor.) autocxx will implemente this trait
211-
/// for you if there's no ambiguity.
210+
/// non-trivial superclass constructor.) autocxx will implement this trait
211+
/// for you if there's no ambiguity and FFI functions are safe to call due to
212+
/// `autocxx::safety!` being used.
212213
///
213214
/// # How to access your Rust structure from outside
214215
///

0 commit comments

Comments
 (0)