Skip to content

Commit f17a926

Browse files
committed
rustdoc: implement RFC 3842 with safety::requires attribute
* Support "#[safety::requires()]" attribute for documentation injection. * Support "{Tag}={description}" format with customized tags. * Prototype implementation of Safety contract insertion.
1 parent 540f43a commit f17a926

11 files changed

Lines changed: 429 additions & 7 deletions

File tree

Cargo.lock

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4885,6 +4885,7 @@ dependencies = [
48854885
"stringdex",
48864886
"tempfile",
48874887
"threadpool",
4888+
"toml 0.8.23",
48884889
"tracing",
48894890
"tracing-subscriber",
48904891
"tracing-tree",

library/core/src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,7 @@
157157
#![feature(optimize_attribute)]
158158
#![feature(pattern_types)]
159159
#![feature(prelude_import)]
160+
#![feature(register_tool)]
160161
#![feature(repr_simd)]
161162
#![feature(rustc_attrs)]
162163
#![feature(rustdoc_internals)]
@@ -192,6 +193,9 @@
192193
#![feature(x86_amx_intrinsics)]
193194
// tidy-alphabetical-end
194195

196+
// Inert attributes for rustdoc `inject-safety-docs` (`--safety-spec`); see `safety::requires`.
197+
#![register_tool(safety)]
198+
195199
// allow using `core::` in intra-doc links
196200
#[allow(unused_extern_crates)]
197201
extern crate self as core;

library/core/src/ptr/mod.rs

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1729,12 +1729,6 @@ pub const unsafe fn read<T>(src: *const T) -> T {
17291729
///
17301730
/// # Safety
17311731
///
1732-
/// Behavior is undefined if any of the following conditions are violated:
1733-
///
1734-
/// * `src` must be [valid] for reads.
1735-
///
1736-
/// * `src` must point to a properly initialized value of type `T`.
1737-
///
17381732
/// Like [`read`], `read_unaligned` creates a bitwise copy of `T`, regardless of
17391733
/// whether `T` is [`Copy`]. If `T` is not [`Copy`], using both the returned
17401734
/// value and the value at `*src` can [violate memory safety][read-ownership].
@@ -1797,6 +1791,7 @@ pub const unsafe fn read<T>(src: *const T) -> T {
17971791
#[rustc_const_stable(feature = "const_ptr_read", since = "1.71.0")]
17981792
#[track_caller]
17991793
#[rustc_diagnostic_item = "ptr_read_unaligned"]
1794+
#[safety::requires(ValidPtrRead(src, T, 1), Init(src, T))]
18001795
pub const unsafe fn read_unaligned<T>(src: *const T) -> T {
18011796
let mut tmp = MaybeUninit::<T>::uninit();
18021797
// SAFETY: the caller must guarantee that `src` is valid for reads.

src/bootstrap/src/core/build_steps/doc.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -813,6 +813,15 @@ fn doc_std(
813813
.rustdocflag("--extern-html-root-takes-precedence")
814814
.rustdocflag("--resource-suffix")
815815
.rustdocflag(&builder.version);
816+
let safety_spec_in_rustdocflags =
817+
env::var("RUSTDOCFLAGS").map(|s| s.contains("--safety-spec")).unwrap_or(false);
818+
// If `--safety-spec` is not set in `RUSTDOCFLAGS`, set it to the default spec file.
819+
if !safety_spec_in_rustdocflags {
820+
let safety_spec_path = builder.src.join("src/librustdoc/assets/sp-core.toml");
821+
if let Some(p) = safety_spec_path.to_str() {
822+
cargo.rustdocflag("--safety-spec").rustdocflag(p);
823+
}
824+
}
816825
for arg in extra_args {
817826
cargo.rustdocflag(arg);
818827
}

src/librustdoc/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ smallvec = "1.8.1"
2525
stringdex = "=0.0.6"
2626
tempfile = "3"
2727
threadpool = "1.8.1"
28+
toml = "0.8"
2829
tracing = "0.1"
2930
tracing-tree = "0.3.0"
3031
unicode-segmentation = "1.9"

src/librustdoc/assets/sp-core.toml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package.name = "core"
2+
3+
[tag.ValidPtrRead]
4+
args = [ "p", "T", "len" ]
5+
desc = "pointer `{p}` must be [valid](crate::ptr#safety) for reading the `sizeof({T})*{len}` memory from it."
6+
7+
[tag.Init]
8+
args = [ "p", "T" ]
9+
desc = "`{p}` must point to a properly initialized value of type `{T}`."

src/librustdoc/config.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,8 @@ pub(crate) struct RenderOptions {
312312
pub(crate) disable_minification: bool,
313313
/// If `true`, HTML source pages will generate the possibility to expand macros.
314314
pub(crate) generate_macro_expansion: bool,
315+
/// Optional TOML spec for `inject-safety-docs` (`#[safety::requires]`).
316+
pub(crate) safety_spec: Option<PathBuf>,
315317
}
316318

317319
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
@@ -852,6 +854,8 @@ impl Options {
852854

853855
let disable_minification = matches.opt_present("disable-minification");
854856

857+
let safety_spec = matches.opt_str("safety-spec").map(PathBuf::from);
858+
855859
let options = Options {
856860
bin_crate,
857861
proc_macro_crate,
@@ -930,6 +934,7 @@ impl Options {
930934
include_parts_dir,
931935
parts_out_dir,
932936
disable_minification,
937+
safety_spec,
933938
};
934939
Some((input, options, render_options, loaded_paths))
935940
}

src/librustdoc/core.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use rustc_errors::emitter::{DynEmitter, HumanReadableErrorType, OutputTheme, std
1111
use rustc_errors::json::JsonEmitter;
1212
use rustc_feature::UnstableFeatures;
1313
use rustc_hir::def::Res;
14-
use rustc_hir::def_id::{DefId, DefIdMap, DefIdSet, LocalDefId};
14+
use rustc_hir::def_id::{DefId, DefIdMap, DefIdSet, LOCAL_CRATE, LocalDefId};
1515
use rustc_hir::intravisit::{self, Visitor};
1616
use rustc_hir::{HirId, Path};
1717
use rustc_lint::{MissingDoc, late_lint_mod};
@@ -68,6 +68,8 @@ pub(crate) struct DocContext<'tcx> {
6868
pub(crate) output_format: OutputFormat,
6969
/// Used by `strip_private`.
7070
pub(crate) show_coverage: bool,
71+
/// Used by `inject-safety-docs` to transform `#[safety::requires]` into documentation text.
72+
pub(crate) safety_spec: Option<Arc<crate::passes::inject_safety_docs::SafetySpec>>,
7173
}
7274

7375
impl<'tcx> DocContext<'tcx> {
@@ -359,6 +361,14 @@ pub(crate) fn run_global_ctxt(
359361
let auto_traits =
360362
tcx.visible_traits().filter(|&trait_def_id| tcx.trait_is_auto(trait_def_id)).collect();
361363

364+
let safety_spec = render_options.safety_spec.as_ref().and_then(|path| {
365+
crate::passes::inject_safety_docs::load_safety_spec(
366+
path,
367+
tcx.crate_name(LOCAL_CRATE).as_str(),
368+
tcx.dcx(),
369+
)
370+
});
371+
362372
let mut ctxt = DocContext {
363373
tcx,
364374
param_env: ParamEnv::empty(),
@@ -373,6 +383,7 @@ pub(crate) fn run_global_ctxt(
373383
inlined: FxHashSet::default(),
374384
output_format,
375385
show_coverage,
386+
safety_spec,
376387
};
377388

378389
for cnum in tcx.crates(()) {

src/librustdoc/lib.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -562,6 +562,14 @@ fn opts() -> Vec<RustcOptGroup> {
562562
"Include the memory layout of types in the docs",
563563
"",
564564
),
565+
opt(
566+
Unstable,
567+
Opt,
568+
"",
569+
"safety-spec",
570+
"transform the safety specification into the documentation from the given TOML file or literal string",
571+
"PATH",
572+
),
565573
opt(Unstable, Flag, "", "no-capture", "Don't capture stdout and stderr of tests", ""),
566574
opt(
567575
Unstable,

0 commit comments

Comments
 (0)