Skip to content

Commit 2a69b8f

Browse files
committed
Avoid using NonNull directly in return types
This is required because nullability attributes in Clang are a hint, and not an ABI stable promise, so we must unwrap internally. More work is still needed to make things fully sound here, but this is at least a step towards that.
1 parent d78b047 commit 2a69b8f

File tree

4 files changed

+123
-66
lines changed

4 files changed

+123
-66
lines changed

crates/header-translator/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#![recursion_limit = "256"]
2+
#![allow(clippy::collapsible_else_if)]
23

34
#[macro_use]
45
extern crate tracing;

crates/header-translator/src/rust_type.rs

+110-53
Original file line numberDiff line numberDiff line change
@@ -1766,7 +1766,7 @@ impl Ty {
17661766
write!(f, "...")?;
17671767
}
17681768
write!(f, ")")?;
1769-
write!(f, "{}", result_type.fn_return())?;
1769+
write!(f, "{}", result_type.fn_type_return())?;
17701770
if *nullability != Nullability::NonNull {
17711771
write!(f, ">")?;
17721772
}
@@ -1880,7 +1880,7 @@ impl Ty {
18801880
write!(f, "{}, ", arg.plain())?;
18811881
}
18821882
write!(f, ")")?;
1883-
write!(f, "{}", result_type.fn_return())?;
1883+
write!(f, "{}", result_type.fn_type_return())?;
18841884
if *no_escape {
18851885
write!(f, " + '_")?;
18861886
} else {
@@ -1900,6 +1900,20 @@ impl Ty {
19001900

19011901
pub(crate) fn method_return(&self) -> impl fmt::Display + '_ {
19021902
FormatterFn(move |f| match self {
1903+
// Don't output anything here.
1904+
Self::Primitive(Primitive::Void) => Ok(()),
1905+
Self::Pointer {
1906+
nullability,
1907+
pointee,
1908+
..
1909+
} if pointee.is_static_object() => {
1910+
if *nullability == Nullability::NonNull {
1911+
// TODO: Add runtime nullability check here.
1912+
write!(f, " -> &'static {}", pointee.behind_pointer())
1913+
} else {
1914+
write!(f, " -> Option<&'static {}>", pointee.behind_pointer())
1915+
}
1916+
}
19031917
Self::Pointer {
19041918
nullability,
19051919
lifetime: _, // TODO: Use this somehow?
@@ -1921,7 +1935,7 @@ impl Ty {
19211935
write!(f, " -> bool")
19221936
}
19231937
Self::Primitive(Primitive::ObjcBool) => write!(f, " -> bool"),
1924-
_ => write!(f, "{}", self.fn_return()),
1938+
_ => write!(f, " -> {}", self.plain()),
19251939
})
19261940
}
19271941

@@ -1983,88 +1997,117 @@ impl Ty {
19831997
})
19841998
}
19851999

1986-
pub(crate) fn fn_return(&self) -> impl fmt::Display + '_ {
1987-
FormatterFn(move |f| {
1988-
if let Self::Primitive(Primitive::Void) = self {
1989-
// Don't output anything
1990-
return Ok(());
1991-
}
1992-
1993-
match self {
1994-
Self::Pointer {
1995-
nullability,
1996-
pointee,
1997-
..
1998-
} if pointee.is_static_object() => {
1999-
if *nullability == Nullability::NonNull {
2000-
write!(f, " -> &'static {}", pointee.behind_pointer())
2001-
} else {
2002-
write!(f, " -> Option<&'static {}>", pointee.behind_pointer())
2003-
}
2000+
fn fn_type_return(&self) -> impl fmt::Display + '_ {
2001+
FormatterFn(move |f| match self {
2002+
// Don't output anything here.
2003+
Self::Primitive(Primitive::Void) => Ok(()),
2004+
Self::Pointer {
2005+
nullability,
2006+
pointee,
2007+
..
2008+
} if pointee.is_static_object() => {
2009+
if *nullability == Nullability::NonNull {
2010+
// TODO: Add runtime nullability check here (can we even
2011+
// do that?).
2012+
write!(f, " -> &'static {}", pointee.behind_pointer())
2013+
} else {
2014+
write!(f, " -> Option<&'static {}>", pointee.behind_pointer())
20042015
}
2005-
_ => write!(f, " -> {}", self.plain()),
20062016
}
2017+
_ => write!(f, " -> {}", self.plain()),
20072018
})
20082019
}
20092020

20102021
pub(crate) fn fn_return_required_items(&self) -> impl Iterator<Item = ItemTree> {
20112022
let mut items: Vec<_> = self.required_items().collect();
20122023
match self {
2013-
Self::Pointer {
2014-
lifetime: Lifetime::Unspecified,
2015-
pointee,
2016-
..
2017-
} if pointee.is_cf_type() => {
2024+
Self::Pointer { pointee, .. } if pointee.is_cf_type() => {
20182025
items.push(ItemTree::cf("CFRetained"));
20192026
items.push(ItemTree::core_ptr_nonnull());
20202027
}
2021-
Self::Pointer {
2022-
lifetime: Lifetime::Unspecified,
2023-
pointee,
2024-
..
2025-
} if pointee.is_object_like() && !pointee.is_static_object() => {
2028+
Self::Pointer { pointee, .. }
2029+
if pointee.is_object_like() && !pointee.is_static_object() =>
2030+
{
20262031
items.push(ItemTree::objc("Retained"));
20272032
}
20282033
_ => {}
20292034
}
20302035
items.into_iter()
20312036
}
20322037

2033-
pub(crate) fn fn_return_converter(
2038+
pub(crate) fn fn_return(
20342039
&self,
20352040
returns_retained: bool,
2036-
) -> Option<(
2037-
impl fmt::Display + '_,
2038-
impl fmt::Display + '_,
2041+
) -> (
20392042
impl fmt::Display + '_,
2040-
)> {
2043+
Option<(
2044+
impl fmt::Display + '_,
2045+
impl fmt::Display + '_,
2046+
impl fmt::Display + '_,
2047+
)>,
2048+
) {
20412049
let start = "let ret = ";
20422050
// SAFETY: The function is marked with the correct retain semantics,
20432051
// otherwise it'd be invalid to use from Obj-C with ARC and Swift too.
2052+
let end_cf = |nullability| {
2053+
match (nullability, returns_retained) {
2054+
// TODO: Avoid NULL check, and let CFRetain do that instead?
2055+
(Nullability::NonNull, true) => ";\nlet ret = ret.expect(\"function was marked as returning non-null, but actually returned NULL\");\nunsafe { CFRetained::from_raw(ret) }",
2056+
(Nullability::NonNull, false) => ";\nlet ret = ret.expect(\"function was marked as returning non-null, but actually returned NULL\");\nunsafe { CFRetained::retain(ret) }",
2057+
// CFRetain aborts on NULL pointers, so there's not really a more
2058+
// efficient way to do this (except if we were to use e.g.
2059+
// `CGColorRetain`/`CVOpenGLBufferRetain`/..., but that's a huge
2060+
// hassle).
2061+
(_, true) => ";\nret.map(|ret| unsafe { CFRetained::from_raw(ret) })",
2062+
(_, false) => ";\nret.map(|ret| unsafe { CFRetained::retain(ret) })",
2063+
}
2064+
};
20442065
let end_objc = |nullability| {
20452066
match (nullability, returns_retained) {
20462067
(Nullability::NonNull, true) => {
2047-
";\nunsafe { Retained::from_raw(ret.as_ptr()) }.expect(\"function was marked as returning non-null, but actually returned NULL\")"
2068+
";\nunsafe { Retained::from_raw(ret) }.expect(\"function was marked as returning non-null, but actually returned NULL\")"
20482069
}
20492070
(Nullability::NonNull, false) => {
2050-
";\nunsafe { Retained::retain_autoreleased(ret.as_ptr()) }.expect(\"function was marked as returning non-null, but actually returned NULL\")"
2071+
";\nunsafe { Retained::retain_autoreleased(ret) }.expect(\"function was marked as returning non-null, but actually returned NULL\")"
20512072
}
20522073
(_, true) => ";\nunsafe { Retained::from_raw(ret) }",
20532074
(_, false) => ";\nunsafe { Retained::retain_autoreleased(ret) }",
20542075
}
20552076
};
2056-
let end_cf = |nullability| match (nullability, returns_retained) {
2057-
(Nullability::NonNull, true) => ";\nunsafe { CFRetained::from_raw(ret) }",
2058-
(Nullability::NonNull, false) => ";\nunsafe { CFRetained::retain(ret) }",
2059-
// CFRetain aborts on NULL pointers, so there's not really a more
2060-
// efficient way to do this (except if we were to use e.g.
2061-
// `CGColorRetain`/`CVOpenGLBufferRetain`/..., but that's a huge
2062-
// hassle).
2063-
(_, true) => ";\nNonNull::new(ret).map(|ret| unsafe { CFRetained::from_raw(ret) })",
2064-
(_, false) => ";\nNonNull::new(ret).map(|ret| unsafe { CFRetained::retain(ret) })",
2065-
};
20662077

2067-
match self {
2078+
let ret = FormatterFn(move |f| match self {
2079+
// Don't output anything here.
2080+
Self::Primitive(Primitive::Void) => Ok(()),
2081+
Self::Pointer {
2082+
nullability,
2083+
is_const,
2084+
pointee,
2085+
..
2086+
} => {
2087+
// Ignore nullability, always emit a nullable pointer. We will
2088+
// unwrap it later in `fn_return_converter`.
2089+
//
2090+
// This is required because nullability attributes in Clang
2091+
// are a hint, and not an ABI stable promise.
2092+
if pointee.is_static_object() {
2093+
write!(f, "-> Option<&'static {}>", pointee.behind_pointer())
2094+
} else if pointee.is_cf_type() {
2095+
write!(f, "-> Option<NonNull<{}>>", pointee.behind_pointer())
2096+
} else if pointee.is_object_like() {
2097+
write!(f, "-> *mut {}", pointee.behind_pointer())
2098+
} else {
2099+
if *nullability == Nullability::NonNull {
2100+
write!(f, "-> Option<NonNull<{}>>", pointee.behind_pointer())
2101+
} else if *is_const {
2102+
write!(f, " -> *const {}", pointee.behind_pointer())
2103+
} else {
2104+
write!(f, " -> *mut {}", pointee.behind_pointer())
2105+
}
2106+
}
2107+
}
2108+
_ => write!(f, " -> {}", self.plain()),
2109+
});
2110+
let converter = match self {
20682111
_ if self.is_objc_bool() => Some((" -> bool".to_string(), "", ".as_bool()")),
20692112
Self::TypeDef { id, .. } if matches!(&*id.name, "Boolean" | "boolean_t") => {
20702113
Some((" -> bool".to_string(), start, ";\nret != 0"))
@@ -2082,7 +2125,15 @@ impl Ty {
20822125
_ => error!(?lifetime, returns_retained, "invalid lifetime"),
20832126
}
20842127

2085-
if pointee.is_cf_type() {
2128+
if pointee.is_static_object() {
2129+
if *nullability == Nullability::NonNull {
2130+
let res = format!(" -> &'static {}", pointee.behind_pointer());
2131+
Some((res, start, ";\nret.expect(\"function was marked as returning non-null, but actually returned NULL\")"))
2132+
} else {
2133+
// No conversion necessary
2134+
None
2135+
}
2136+
} else if pointee.is_cf_type() {
20862137
let res = if *nullability == Nullability::NonNull {
20872138
format!(" -> CFRetained<{}>", pointee.behind_pointer())
20882139
} else {
@@ -2097,11 +2148,17 @@ impl Ty {
20972148
};
20982149
Some((res, start, end_objc(*nullability)))
20992150
} else {
2100-
None
2151+
if *nullability == Nullability::NonNull {
2152+
let res = format!(" -> NonNull<{}>", pointee.behind_pointer());
2153+
Some((res, start, ";\nret.expect(\"function was marked as returning non-null, but actually returned NULL\")"))
2154+
} else {
2155+
None
2156+
}
21012157
}
21022158
}
21032159
_ => None,
2104-
}
2160+
};
2161+
(ret, converter)
21052162
}
21062163

21072164
pub(crate) fn var(&self) -> impl fmt::Display + '_ {

crates/header-translator/src/stmt.rs

+11-12
Original file line numberDiff line numberDiff line change
@@ -2685,6 +2685,7 @@ impl Stmt {
26852685
arguments,
26862686
result_type,
26872687
body: Some(_),
2688+
returns_retained,
26882689
..
26892690
} => {
26902691
write!(f, "// TODO: ")?;
@@ -2693,7 +2694,8 @@ impl Stmt {
26932694
let param = handle_reserved(&crate::to_snake_case(param));
26942695
write!(f, "{param}: {},", arg_ty.fn_argument())?;
26952696
}
2696-
writeln!(f, "){};", result_type.fn_return())?;
2697+
let (ret, _) = result_type.fn_return(*returns_retained);
2698+
writeln!(f, "){ret};")?;
26972699
}
26982700
Self::FnDecl {
26992701
id,
@@ -2710,7 +2712,7 @@ impl Stmt {
27102712
} => {
27112713
let abi = if *can_unwind { "C-unwind" } else { "C" };
27122714

2713-
let return_converter = result_type.fn_return_converter(*returns_retained);
2715+
let (ret, return_converter) = result_type.fn_return(*returns_retained);
27142716

27152717
let needs_wrapper = *safe
27162718
|| return_converter.is_some()
@@ -2731,7 +2733,7 @@ impl Stmt {
27312733
let param = handle_reserved(&crate::to_snake_case(param));
27322734
write!(f, "{param}: {},", arg_ty.fn_argument())?;
27332735
}
2734-
writeln!(f, "){};", result_type.fn_return())?;
2736+
writeln!(f, "){ret};")?;
27352737

27362738
Ok(())
27372739
};
@@ -2760,7 +2762,7 @@ impl Stmt {
27602762
if let Some((ty, _, _)) = &return_converter {
27612763
write!(f, "{ty}")?;
27622764
} else {
2763-
write!(f, "{}", result_type.fn_return())?;
2765+
write!(f, "{ret}")?;
27642766
}
27652767
writeln!(f, " {{")?;
27662768

@@ -2818,7 +2820,9 @@ impl Stmt {
28182820
} => {
28192821
let abi = if *can_unwind { "C-unwind" } else { "C" };
28202822

2821-
// Only emit for base types, not for mutable subclasses,
2823+
let (ret, _) = result_type.fn_return(false);
2824+
2825+
// Only emit for base types, not foretr mutable subclasses,
28222826
// as it's unclear whether it's safe to downcast to
28232827
// mutable subclasses.
28242828
write!(f, "{}", self.cfg_gate_ln(config))?;
@@ -2827,15 +2831,10 @@ impl Stmt {
28272831
write!(f, "{}", documentation.fmt(None))?;
28282832
writeln!(f, " #[doc(alias = {:?})]", id.name)?;
28292833
writeln!(f, " #[inline]")?;
2830-
writeln!(f, " fn type_id(){} {{", result_type.fn_return())?;
2834+
writeln!(f, " fn type_id(){ret} {{")?;
28312835

28322836
writeln!(f, " extern {abi:?} {{")?;
2833-
writeln!(
2834-
f,
2835-
" fn {}(){};",
2836-
id.name,
2837-
result_type.fn_return()
2838-
)?;
2837+
writeln!(f, " fn {}(){ret};", id.name,)?;
28392838
writeln!(f, " }}")?;
28402839

28412840
writeln!(f, " unsafe {{ {}() }}", id.name)?;

generated

Submodule generated updated 190 files

0 commit comments

Comments
 (0)