Skip to content

Commit 8e71d6d

Browse files
unsafe_method: next solver (WIP)
1 parent 5ea53ce commit 8e71d6d

File tree

4 files changed

+102
-14
lines changed

4 files changed

+102
-14
lines changed

Cargo.toml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,23 @@ homepage = "https://github.com/prudent-rs/prudent"
2424
unexpected_cfgs = { level = "forbid"}
2525

2626
[features]
27+
# UNSTABLE Check that the method actually is unsafe. This requires nightly Rust version until
28+
# https://github.com/rust-lang/rust/issues/88531 is implemented. Subject to CHANGE.
29+
#
30+
# If you enable this
31+
# - and use `nightly` compiler
32+
# - either
33+
# - have `#![feature(type_alias_impl_trait)]` at the top of your crate (normally your
34+
# `src/lib.rs`, `src/main.rs` or `src/bin/*.rs`), or
35+
# - use it in rustdocs with injection - TODO check:
36+
# ```
37+
# // at the top of your crate:
38+
# #![doc(test(attr = ::prudent::top_header_assert_unsafe_methods!()))]
39+
# ```
40+
# - if you enable it conditionally, and if your feature is also called `assert_unsafe_methods`,
41+
# have:
42+
# ```
43+
# #![cfg_attr(feature = "assert_unsafe_methods", doc(test(attr = ::prudent::top_header_assert_unsafe_methods!())))]
44+
# ```
45+
assert_unsafe_methods = []
2746
#default = []

README.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -521,10 +521,16 @@ Please contribute, or at least subscribe, and give thumbs up, to:
521521

522522
## Related issues
523523
Sorted by importance (for `prudent`):
524+
- [rust-lang/rust#43031](https://github.com/rust-lang/rust/issues/43031) rustdoc: allow full set of
525+
compiler options to be specified
526+
- [rust-lang/rust#29625](https://github.com/rust-lang/rust/issues/29625) `unboxed_closures` and
527+
`fn_traits` feature.
524528
- [rust-lang/rust#88531](https://github.com/rust-lang/rust/issues/88531) No way to get compile-time
525529
info from the type of local
526530
- [rust-lang/rust#63063](https://github.com/rust-lang/rust/issues/63063) Tracking issue for RFC
527531
2515, "Permit impl Trait in type aliases"
532+
- [rust-lang/rust#54726](https://github.com/rust-lang/rust/issues/54726) Tracking issue for custom
533+
inner attributes
528534
- [rust-lang/rust#110613](https://github.com/rust-lang/rust/issues/110613) Forbidding lints doesn't
529535
really work in macros
530536
- [rust-lang/rust#148942](https://github.com/rust-lang/rust/issues/148942) cfg(test) is not set
@@ -547,8 +553,6 @@ Sorted by importance (for `prudent`):
547553
`#![feature(const_trait_impl)]`
548554
- [rust-lang/rust#83527](https://github.com/rust-lang/rust/issues/83527) `${ignore(..._}`
549555
metavariable/metafunction in `macro_rules!`
550-
- [rust-lang/rust#29625](https://github.com/rust-lang/rust/issues/29625) `unboxed_closures` and
551-
`fn_traits` feature.
552556

553557
## Side fruit
554558
- [rust-lang/rust#148599](https://github.com/rust-lang/rust/issues/148599) forward compatibility of

src/frontend.rs

Lines changed: 45 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,19 @@ macro_rules! unsafe_fn {
4444
let (tuple_tree, fun) = ($crate::unsafe_fn_internal_build_tuple_tree!{ $($arg),+ }, $fn);
4545

4646
if false {
47+
// Detect code where `unsafe_fn!` is not needed at all. Maybe the function used
48+
// to be `unsafe`, but not anymore.
49+
//
4750
// Ensure that $fn is not safe, but `unsafe`. Using
4851
// https://doc.rust-lang.org/reference/types/function-item.html#r-type.fn-item.coercion
52+
//
53+
// We can't just use
54+
// ```
55+
// let _: unsafe fn(_, _,... ) -> _ = fun;
56+
// ```
57+
// (with the appropriate number of _ for arguments), because that would coerce a
58+
// safe function into unsafe, and we would lose the ability to verify that it's
59+
// indeed unsafe!
4960
let _ = if false {
5061
$crate::expecting_unsafe_fn_path!( $( $arg ),+ )
5162
} else {
@@ -181,28 +192,51 @@ macro_rules! unsafe_method {
181192
// See unsafe_fn for why here we enclose in (...) and not in {...}.
182193
(
183194
if false {
184-
// "Make" an owned_receiver, an instance/owned value of the same
185-
// type as $self. (Of course, the instance is invalid - this is for compile-time
186-
// checks only, hence `if false {...}`.)
195+
// "Make" an owned_receiver, an instance/owned value of the same type as $self. (Of
196+
// course, the instance is invalid - this is for compile-time checks only, hence `if
197+
// false {...}`.)
187198
//
188-
// Then we simulate invocation of the given method inside `unsafe {...}``, BUT
189-
// without evaluating the given $self expression inside that same `unsafe
190-
// {...}`` block, so that we isolate/catch any `unsafe`` code in $self.
199+
// Then we simulate invocation of the given method inside `unsafe {...}`, BUT
200+
// without evaluating the given $self expression inside that same `unsafe {...}`
201+
// block, so that we isolate/catch any `unsafe` code in $self.
191202
//
192-
// We **cannot** just move/take/assign $self by value, in case it's a non-Copy
193-
// **static** variable. See also comments in
203+
// We **cannot** just move/take/assign $self by value, in case it's a non-`Copy`
204+
// `static` variable (or a deref of a non-`Copy` raw pointer). See also comments in
194205
// unsafe_method_internal_build_accessors_check_args_call.
195206
let mref = {
196207
let rref = &( $self );
197208
$crate::backend::shared_to_mut( rref )
198209
};
199-
#[allow(unused_mut)]
210+
#[allow(unused_mut)] // in case the method takes &mut self.
200211
#[allow(invalid_value)] // for &str and other types where zeroed() issues invalid_value warning.
201212
let mut owned_receiver = ::core::mem::replace(mref, unsafe{ ::core::mem::zeroed() });
213+
214+
#[cfg(feature="assert_unsafe_methods")]
215+
if false {
216+
type OwnedReceiver = impl Sized;
217+
//let _ = move || -> OwnedReceiver { owned_receiver };
218+
let owned_receiver: OwnedReceiver = owned_receiver;
219+
220+
// Detect code where `unsafe_method!` is not needed at all. Maybe the method used
221+
// to be `unsafe`, but not anymore.
222+
//
223+
// See unsafe_fn for why we can't just use simple coercion like:
224+
// ```
225+
// let _: unsafe fn(_, _,... ) -> _ = OwnedReceiver::$method;
226+
// ```
227+
228+
let _ = OwnedReceiver::$method;
229+
/*let _ = if false {
230+
$crate::expecting_unsafe_fn_path!( first_goes_receiver $(, $arg )* )
231+
} else {
232+
OwnedReceiver::$method
233+
};*/
234+
::core::unreachable!();
235+
};
236+
// @TODO double check and remove:
237+
//
202238
// Detect code where `unsafe_method!` is not needed at all. Maybe the method used
203239
// to be `unsafe`, but not anymore.
204-
//
205-
// @TODO This is the only place where we "need" #[deny(unused_unsafe)]
206240
#[deny(unused_unsafe)]
207241
let _ = unsafe { owned_receiver. $method( $( $arg ),* ) };
208242
::core::unreachable!()

src/lib.rs

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,18 @@
1+
// @TODO feature NOT actually needed in the crate itself, only in consumers
2+
//
3+
//#![cfg_attr(feature = "assert_unsafe_methods", feature(type_alias_impl_trait))]
4+
5+
// @TODO e-considr:
6+
//
7+
//#![doc(test(no_crate_inject))]
8+
//
9+
// #![cfg_attr(feature = "assert_unsafe_methods", doc(test(attr = ::prudent::top_header_assert_unsafe_methods!())))]
10+
//
11+
//#![doc(test(attr = ::prudent::top_header_assert_unsafe_methods!()))]
12+
#![cfg_attr(
13+
feature = "assert_unsafe_methods",
14+
doc(test(attr(feature(type_alias_impl_trait))))
15+
)]
116
#![allow(clippy::useless_attribute)]
217
#![allow(clippy::needless_doctest_main)]
318
//! # Examples
@@ -18,7 +33,8 @@
1833
// rustdoc lints: https://doc.rust-lang.org/rustdoc/lints.html
1934
//
2035
// rustdoc::missing_doc_code_examples we don't apply here, because it's nightly-only. Instead, we
21-
// invoke it with `nightly` toolchain from .github/workflows/main.yml.
36+
// invoke it by specifying it in `RUSTDOCFLAGS` with `nightly` toolchain. See
37+
// .github/workflows/main.yml.
2238
//
2339
// rustdoc::invalid_codeblock_attributes we do NOT warn/deny/forbid here, even though it IS stable.
2440
// That's because in `compile_fail` doctests we use error numbers, which are UNSTABLE only. But, to
@@ -56,6 +72,21 @@
5672
#[cfg(doc)]
5773
extern crate alloc;
5874

75+
#[cfg(feature = "assert_unsafe_methods")]
76+
/// Enable a necessary nightly feature IF prudent is configured to use it.
77+
#[macro_export]
78+
macro_rules! top_header_assert_unsafe_methods {
79+
() => {
80+
"#![feature(type_alias_impl_trait)]"
81+
};
82+
}
83+
#[cfg(not(feature = "assert_unsafe_methods"))]
84+
/// Enable a necessary nightly feature IF prudent is configured to use it.
85+
#[macro_export]
86+
macro_rules! top_header_assert_unsafe_methods {
87+
() => {};
88+
}
89+
5990
#[doc(hidden)]
6091
#[macro_export]
6192
macro_rules! internal_coverage_positive {

0 commit comments

Comments
 (0)