Merge from rustc
This commit is contained in:
commit
1c73fb9fc4
274 changed files with 3618 additions and 2749 deletions
|
|
@ -118,15 +118,25 @@ impl Step for Miri {
|
|||
fn run(self, builder: &Builder<'_>) {
|
||||
let host = builder.build.build;
|
||||
let target = self.target;
|
||||
let stage = builder.top_stage;
|
||||
|
||||
// `x run` uses stage 0 by default but miri does not work well with stage 0.
|
||||
// Change the stage to 1 if it's not set explicitly.
|
||||
let stage = if builder.config.is_explicit_stage() || builder.top_stage >= 1 {
|
||||
builder.top_stage
|
||||
} else {
|
||||
1
|
||||
};
|
||||
|
||||
if stage == 0 {
|
||||
eprintln!("miri cannot be run at stage 0");
|
||||
std::process::exit(1);
|
||||
}
|
||||
|
||||
// This compiler runs on the host, we'll just use it for the target.
|
||||
let target_compiler = builder.compiler(stage, host);
|
||||
let host_compiler = tool::get_tool_rustc_compiler(builder, target_compiler);
|
||||
let target_compiler = builder.compiler(stage, target);
|
||||
let miri_build = builder.ensure(tool::Miri { compiler: target_compiler, target });
|
||||
// Rustc tools are off by one stage, so use the build compiler to run miri.
|
||||
let host_compiler = miri_build.build_compiler;
|
||||
|
||||
// Get a target sysroot for Miri.
|
||||
let miri_sysroot = test::Miri::build_miri_sysroot(builder, target_compiler, target);
|
||||
|
|
|
|||
|
|
@ -739,7 +739,7 @@ impl Step for Clippy {
|
|||
const DEFAULT: bool = false;
|
||||
|
||||
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
|
||||
run.path("src/tools/clippy")
|
||||
run.suite_path("src/tools/clippy/tests").path("src/tools/clippy")
|
||||
}
|
||||
|
||||
fn make_run(run: RunConfig<'_>) {
|
||||
|
|
@ -783,6 +783,23 @@ impl Step for Clippy {
|
|||
let host_libs = builder.stage_out(compiler, Mode::ToolRustc).join(builder.cargo_dir());
|
||||
cargo.env("HOST_LIBS", host_libs);
|
||||
|
||||
// Collect paths of tests to run
|
||||
'partially_test: {
|
||||
let paths = &builder.config.paths[..];
|
||||
let mut test_names = Vec::new();
|
||||
for path in paths {
|
||||
if let Some(path) =
|
||||
helpers::is_valid_test_suite_arg(path, "src/tools/clippy/tests", builder)
|
||||
{
|
||||
test_names.push(path);
|
||||
} else if path.ends_with("src/tools/clippy") {
|
||||
// When src/tools/clippy is called directly, all tests should be run.
|
||||
break 'partially_test;
|
||||
}
|
||||
}
|
||||
cargo.env("TESTNAME", test_names.join(","));
|
||||
}
|
||||
|
||||
cargo.add_rustc_lib_path(builder);
|
||||
let cargo = prepare_cargo_test(cargo, &[], &[], host, builder);
|
||||
|
||||
|
|
@ -2947,7 +2964,14 @@ impl Step for Distcheck {
|
|||
run.builder.ensure(Distcheck);
|
||||
}
|
||||
|
||||
/// Runs "distcheck", a 'make check' from a tarball
|
||||
/// Runs `distcheck`, which is a collection of smoke tests:
|
||||
///
|
||||
/// - Run `make check` from an unpacked dist tarball to make sure we can at the minimum run
|
||||
/// check steps from those sources.
|
||||
/// - Check that selected dist components (`rust-src` only at the moment) at least have expected
|
||||
/// directory shape and crate manifests that cargo can generate a lockfile from.
|
||||
///
|
||||
/// FIXME(#136822): dist components are under-tested.
|
||||
fn run(self, builder: &Builder<'_>) {
|
||||
builder.info("Distcheck");
|
||||
let dir = builder.tempdir().join("distcheck");
|
||||
|
|
|
|||
|
|
@ -1,3 +1,15 @@
|
|||
# Runs `distcheck`, which is a collection of smoke tests:
|
||||
#
|
||||
# - Run `make check` from an unpacked dist tarball to make sure we can at the
|
||||
# minimum run check steps from those sources.
|
||||
# - Check that selected dist components at least have expected directory shape
|
||||
# and crate manifests that cargo can generate a lockfile from.
|
||||
#
|
||||
# Refer to `src/bootstrap/src/core/build_steps/test.rs` `Distcheck::run` for
|
||||
# specifics.
|
||||
#
|
||||
# FIXME(#136822): dist components are generally under-tested.
|
||||
|
||||
FROM ubuntu:22.04
|
||||
|
||||
ARG DEBIAN_FRONTEND=noninteractive
|
||||
|
|
|
|||
|
|
@ -123,7 +123,7 @@ pr:
|
|||
DOCKER_SCRIPT: x86_64-gnu-llvm.sh
|
||||
<<: *job-linux-16c
|
||||
- name: x86_64-gnu-tools
|
||||
<<: *job-linux-16c
|
||||
<<: *job-linux-36c-codebuild
|
||||
|
||||
# Jobs that run when you perform a try build (@bors try)
|
||||
# These jobs automatically inherit envs.try, to avoid repeating
|
||||
|
|
|
|||
8
src/doc/unstable-book/README.md
Normal file
8
src/doc/unstable-book/README.md
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
# Unstable Book
|
||||
|
||||
These are the sources for <https://doc.rust-lang.org/nightly/unstable-book/>.
|
||||
To generate them, run `./x doc unstable-book`, which will generate HTML files in `build/host/doc/unstable-book` using `src/tools/rustbook`.
|
||||
If you need to change the overall structure, modify `src/tools/unstable-book-gen/src/SUMMARY.md`.
|
||||
|
||||
Note that most of this book is autogenerated by `unstable-book-gen`, with the exception of `compiler-flags` and `compiler-environment-variables`.
|
||||
As a result, it does not integrate well with `mdbook`. Use `./x doc` instead.
|
||||
|
|
@ -0,0 +1 @@
|
|||
# Compiler environment variables
|
||||
|
|
@ -14,7 +14,7 @@ Cargo disallows setting `cargo::rustc-env=RUSTC_BOOTSTRAP` in build scripts.
|
|||
Build systems can limit the features they enable with [`-Z allow-features=feature1,feature2`][Z-allow-features].
|
||||
Crates can fully opt out of unstable features by using [`#![forbid(unstable_features)]`][unstable-features] at the crate root (or any other way of enabling lints, such as `-F unstable-features`).
|
||||
|
||||
[Z-allow-features]: ./allow-features.html
|
||||
[Z-allow-features]: ../compiler-flags/allow-features.html
|
||||
[unstable-features]: ../../rustc/lints/listing/allowed-by-default.html#unstable-features
|
||||
|
||||
## Why does this environment variable exist?
|
||||
|
|
@ -11,4 +11,4 @@ Features are comma-separated, for example `-Z allow-features=ffi_pure,f16`.
|
|||
If the flag is present, any feature listed will be allowed and any feature not listed will be disallowed.
|
||||
Any unrecognized feature is ignored.
|
||||
|
||||
[`RUSTC_BOOTSTRAP`]: ./rustc-bootstrap.html
|
||||
[`RUSTC_BOOTSTRAP`]: ../compiler-environment-variables/RUSTC_BOOTSTRAP.html
|
||||
|
|
|
|||
|
|
@ -610,6 +610,9 @@ impl Item {
|
|||
UnionItem(ref union_) => Some(union_.has_stripped_entries()),
|
||||
EnumItem(ref enum_) => Some(enum_.has_stripped_entries()),
|
||||
VariantItem(ref v) => v.has_stripped_entries(),
|
||||
TypeAliasItem(ref type_alias) => {
|
||||
type_alias.inner_type.as_ref().and_then(|t| t.has_stripped_entries())
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
|
@ -761,14 +764,11 @@ impl Item {
|
|||
Some(tcx.visibility(def_id))
|
||||
}
|
||||
|
||||
pub(crate) fn attributes(&self, tcx: TyCtxt<'_>, cache: &Cache, is_json: bool) -> Vec<String> {
|
||||
pub(crate) fn attributes_without_repr(&self, tcx: TyCtxt<'_>, is_json: bool) -> Vec<String> {
|
||||
const ALLOWED_ATTRIBUTES: &[Symbol] =
|
||||
&[sym::export_name, sym::link_section, sym::no_mangle, sym::non_exhaustive];
|
||||
|
||||
use rustc_abi::IntegerType;
|
||||
|
||||
let mut attrs: Vec<String> = self
|
||||
.attrs
|
||||
self.attrs
|
||||
.other_attrs
|
||||
.iter()
|
||||
.filter_map(|attr| {
|
||||
|
|
@ -796,74 +796,28 @@ impl Item {
|
|||
None
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
.collect()
|
||||
}
|
||||
|
||||
// Add #[repr(...)]
|
||||
if let Some(def_id) = self.def_id()
|
||||
&& let ItemType::Struct | ItemType::Enum | ItemType::Union = self.type_()
|
||||
{
|
||||
let adt = tcx.adt_def(def_id);
|
||||
let repr = adt.repr();
|
||||
let mut out = Vec::new();
|
||||
if repr.c() {
|
||||
out.push("C");
|
||||
}
|
||||
if repr.transparent() {
|
||||
// Render `repr(transparent)` iff the non-1-ZST field is public or at least one
|
||||
// field is public in case all fields are 1-ZST fields.
|
||||
let render_transparent = is_json
|
||||
|| cache.document_private
|
||||
|| adt
|
||||
.all_fields()
|
||||
.find(|field| {
|
||||
let ty =
|
||||
field.ty(tcx, ty::GenericArgs::identity_for_item(tcx, field.did));
|
||||
tcx.layout_of(
|
||||
ty::TypingEnv::post_analysis(tcx, field.did).as_query_input(ty),
|
||||
)
|
||||
.is_ok_and(|layout| !layout.is_1zst())
|
||||
})
|
||||
.map_or_else(
|
||||
|| adt.all_fields().any(|field| field.vis.is_public()),
|
||||
|field| field.vis.is_public(),
|
||||
);
|
||||
pub(crate) fn attributes_and_repr(
|
||||
&self,
|
||||
tcx: TyCtxt<'_>,
|
||||
cache: &Cache,
|
||||
is_json: bool,
|
||||
) -> Vec<String> {
|
||||
let mut attrs = self.attributes_without_repr(tcx, is_json);
|
||||
|
||||
if render_transparent {
|
||||
out.push("transparent");
|
||||
}
|
||||
}
|
||||
if repr.simd() {
|
||||
out.push("simd");
|
||||
}
|
||||
let pack_s;
|
||||
if let Some(pack) = repr.pack {
|
||||
pack_s = format!("packed({})", pack.bytes());
|
||||
out.push(&pack_s);
|
||||
}
|
||||
let align_s;
|
||||
if let Some(align) = repr.align {
|
||||
align_s = format!("align({})", align.bytes());
|
||||
out.push(&align_s);
|
||||
}
|
||||
let int_s;
|
||||
if let Some(int) = repr.int {
|
||||
int_s = match int {
|
||||
IntegerType::Pointer(is_signed) => {
|
||||
format!("{}size", if is_signed { 'i' } else { 'u' })
|
||||
}
|
||||
IntegerType::Fixed(size, is_signed) => {
|
||||
format!("{}{}", if is_signed { 'i' } else { 'u' }, size.size().bytes() * 8)
|
||||
}
|
||||
};
|
||||
out.push(&int_s);
|
||||
}
|
||||
if !out.is_empty() {
|
||||
attrs.push(format!("#[repr({})]", out.join(", ")));
|
||||
}
|
||||
if let Some(repr_attr) = self.repr(tcx, cache, is_json) {
|
||||
attrs.push(repr_attr);
|
||||
}
|
||||
attrs
|
||||
}
|
||||
|
||||
/// Returns a stringified `#[repr(...)]` attribute.
|
||||
pub(crate) fn repr(&self, tcx: TyCtxt<'_>, cache: &Cache, is_json: bool) -> Option<String> {
|
||||
repr_attributes(tcx, cache, self.def_id()?, self.type_(), is_json)
|
||||
}
|
||||
|
||||
pub fn is_doc_hidden(&self) -> bool {
|
||||
self.attrs.is_doc_hidden()
|
||||
}
|
||||
|
|
@ -873,6 +827,73 @@ impl Item {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn repr_attributes(
|
||||
tcx: TyCtxt<'_>,
|
||||
cache: &Cache,
|
||||
def_id: DefId,
|
||||
item_type: ItemType,
|
||||
is_json: bool,
|
||||
) -> Option<String> {
|
||||
use rustc_abi::IntegerType;
|
||||
|
||||
if !matches!(item_type, ItemType::Struct | ItemType::Enum | ItemType::Union) {
|
||||
return None;
|
||||
}
|
||||
let adt = tcx.adt_def(def_id);
|
||||
let repr = adt.repr();
|
||||
let mut out = Vec::new();
|
||||
if repr.c() {
|
||||
out.push("C");
|
||||
}
|
||||
if repr.transparent() {
|
||||
// Render `repr(transparent)` iff the non-1-ZST field is public or at least one
|
||||
// field is public in case all fields are 1-ZST fields.
|
||||
let render_transparent = cache.document_private
|
||||
|| is_json
|
||||
|| adt
|
||||
.all_fields()
|
||||
.find(|field| {
|
||||
let ty = field.ty(tcx, ty::GenericArgs::identity_for_item(tcx, field.did));
|
||||
tcx.layout_of(ty::TypingEnv::post_analysis(tcx, field.did).as_query_input(ty))
|
||||
.is_ok_and(|layout| !layout.is_1zst())
|
||||
})
|
||||
.map_or_else(
|
||||
|| adt.all_fields().any(|field| field.vis.is_public()),
|
||||
|field| field.vis.is_public(),
|
||||
);
|
||||
|
||||
if render_transparent {
|
||||
out.push("transparent");
|
||||
}
|
||||
}
|
||||
if repr.simd() {
|
||||
out.push("simd");
|
||||
}
|
||||
let pack_s;
|
||||
if let Some(pack) = repr.pack {
|
||||
pack_s = format!("packed({})", pack.bytes());
|
||||
out.push(&pack_s);
|
||||
}
|
||||
let align_s;
|
||||
if let Some(align) = repr.align {
|
||||
align_s = format!("align({})", align.bytes());
|
||||
out.push(&align_s);
|
||||
}
|
||||
let int_s;
|
||||
if let Some(int) = repr.int {
|
||||
int_s = match int {
|
||||
IntegerType::Pointer(is_signed) => {
|
||||
format!("{}size", if is_signed { 'i' } else { 'u' })
|
||||
}
|
||||
IntegerType::Fixed(size, is_signed) => {
|
||||
format!("{}{}", if is_signed { 'i' } else { 'u' }, size.size().bytes() * 8)
|
||||
}
|
||||
};
|
||||
out.push(&int_s);
|
||||
}
|
||||
if !out.is_empty() { Some(format!("#[repr({})]", out.join(", "))) } else { None }
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) enum ItemKind {
|
||||
ExternCrateItem {
|
||||
|
|
@ -2107,7 +2128,7 @@ impl Enum {
|
|||
self.variants.iter().any(|f| f.is_stripped())
|
||||
}
|
||||
|
||||
pub(crate) fn variants(&self) -> impl Iterator<Item = &Item> {
|
||||
pub(crate) fn non_stripped_variants(&self) -> impl Iterator<Item = &Item> {
|
||||
self.variants.iter().filter(|v| !v.is_stripped())
|
||||
}
|
||||
}
|
||||
|
|
@ -2345,6 +2366,17 @@ pub(crate) enum TypeAliasInnerType {
|
|||
Struct { ctor_kind: Option<CtorKind>, fields: Vec<Item> },
|
||||
}
|
||||
|
||||
impl TypeAliasInnerType {
|
||||
fn has_stripped_entries(&self) -> Option<bool> {
|
||||
Some(match self {
|
||||
Self::Enum { variants, .. } => variants.iter().any(|v| v.is_stripped()),
|
||||
Self::Union { fields } | Self::Struct { fields, .. } => {
|
||||
fields.iter().any(|f| f.is_stripped())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) struct TypeAlias {
|
||||
pub(crate) type_: Type,
|
||||
|
|
|
|||
|
|
@ -1194,18 +1194,36 @@ fn render_assoc_item(
|
|||
// a whitespace prefix and newline.
|
||||
fn render_attributes_in_pre(it: &clean::Item, prefix: &str, cx: &Context<'_>) -> impl fmt::Display {
|
||||
fmt::from_fn(move |f| {
|
||||
for a in it.attributes(cx.tcx(), cx.cache(), false) {
|
||||
for a in it.attributes_and_repr(cx.tcx(), cx.cache(), false) {
|
||||
writeln!(f, "{prefix}{a}")?;
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
struct CodeAttribute(String);
|
||||
|
||||
fn render_code_attribute(code_attr: CodeAttribute, w: &mut impl fmt::Write) {
|
||||
write!(w, "<div class=\"code-attribute\">{}</div>", code_attr.0).unwrap();
|
||||
}
|
||||
|
||||
// When an attribute is rendered inside a <code> tag, it is formatted using
|
||||
// a div to produce a newline after it.
|
||||
fn render_attributes_in_code(w: &mut impl fmt::Write, it: &clean::Item, cx: &Context<'_>) {
|
||||
for attr in it.attributes(cx.tcx(), cx.cache(), false) {
|
||||
write!(w, "<div class=\"code-attribute\">{attr}</div>").unwrap();
|
||||
for attr in it.attributes_and_repr(cx.tcx(), cx.cache(), false) {
|
||||
render_code_attribute(CodeAttribute(attr), w);
|
||||
}
|
||||
}
|
||||
|
||||
/// used for type aliases to only render their `repr` attribute.
|
||||
fn render_repr_attributes_in_code(
|
||||
w: &mut impl fmt::Write,
|
||||
cx: &Context<'_>,
|
||||
def_id: DefId,
|
||||
item_type: ItemType,
|
||||
) {
|
||||
if let Some(repr) = clean::repr_attributes(cx.tcx(), cx.cache(), def_id, item_type, false) {
|
||||
render_code_attribute(CodeAttribute(repr), w);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ use super::{
|
|||
collect_paths_for_type, document, ensure_trailing_slash, get_filtered_impls_for_reference,
|
||||
item_ty_to_section, notable_traits_button, notable_traits_json, render_all_impls,
|
||||
render_assoc_item, render_assoc_items, render_attributes_in_code, render_attributes_in_pre,
|
||||
render_impl, render_rightside, render_stability_since_raw,
|
||||
render_impl, render_repr_attributes_in_code, render_rightside, render_stability_since_raw,
|
||||
render_stability_since_raw_with_extra, write_section_heading,
|
||||
};
|
||||
use crate::clean;
|
||||
|
|
@ -1278,94 +1278,58 @@ fn item_type_alias(cx: &Context<'_>, it: &clean::Item, t: &clean::TypeAlias) ->
|
|||
|
||||
match inner_type {
|
||||
clean::TypeAliasInnerType::Enum { variants, is_non_exhaustive } => {
|
||||
let variants_iter = || variants.iter().filter(|i| !i.is_stripped());
|
||||
let ty = cx.tcx().type_of(it.def_id().unwrap()).instantiate_identity();
|
||||
let enum_def_id = ty.ty_adt_def().unwrap().did();
|
||||
|
||||
wrap_item(w, |w| {
|
||||
let variants_len = variants.len();
|
||||
let variants_count = variants_iter().count();
|
||||
let has_stripped_entries = variants_len != variants_count;
|
||||
|
||||
write!(
|
||||
w,
|
||||
"enum {}{}{}",
|
||||
it.name.unwrap(),
|
||||
t.generics.print(cx),
|
||||
render_enum_fields(
|
||||
cx,
|
||||
Some(&t.generics),
|
||||
variants,
|
||||
variants_count,
|
||||
has_stripped_entries,
|
||||
*is_non_exhaustive,
|
||||
enum_def_id,
|
||||
)
|
||||
)
|
||||
})?;
|
||||
write!(w, "{}", item_variants(cx, it, variants, enum_def_id))?;
|
||||
DisplayEnum {
|
||||
variants,
|
||||
generics: &t.generics,
|
||||
is_non_exhaustive: *is_non_exhaustive,
|
||||
def_id: enum_def_id,
|
||||
}
|
||||
.render_into(cx, it, true, w)?;
|
||||
}
|
||||
clean::TypeAliasInnerType::Union { fields } => {
|
||||
wrap_item(w, |w| {
|
||||
let fields_count = fields.iter().filter(|i| !i.is_stripped()).count();
|
||||
let has_stripped_fields = fields.len() != fields_count;
|
||||
let ty = cx.tcx().type_of(it.def_id().unwrap()).instantiate_identity();
|
||||
let union_def_id = ty.ty_adt_def().unwrap().did();
|
||||
|
||||
write!(
|
||||
w,
|
||||
"union {}{}{}",
|
||||
it.name.unwrap(),
|
||||
t.generics.print(cx),
|
||||
render_struct_fields(
|
||||
Some(&t.generics),
|
||||
None,
|
||||
fields,
|
||||
"",
|
||||
true,
|
||||
has_stripped_fields,
|
||||
cx,
|
||||
),
|
||||
)
|
||||
})?;
|
||||
write!(w, "{}", item_fields(cx, it, fields, None))?;
|
||||
ItemUnion {
|
||||
cx,
|
||||
it,
|
||||
fields,
|
||||
generics: &t.generics,
|
||||
is_type_alias: true,
|
||||
def_id: union_def_id,
|
||||
}
|
||||
.render_into(w)?;
|
||||
}
|
||||
clean::TypeAliasInnerType::Struct { ctor_kind, fields } => {
|
||||
wrap_item(w, |w| {
|
||||
let fields_count = fields.iter().filter(|i| !i.is_stripped()).count();
|
||||
let has_stripped_fields = fields.len() != fields_count;
|
||||
let ty = cx.tcx().type_of(it.def_id().unwrap()).instantiate_identity();
|
||||
let struct_def_id = ty.ty_adt_def().unwrap().did();
|
||||
|
||||
write!(
|
||||
w,
|
||||
"struct {}{}{}",
|
||||
it.name.unwrap(),
|
||||
t.generics.print(cx),
|
||||
render_struct_fields(
|
||||
Some(&t.generics),
|
||||
*ctor_kind,
|
||||
fields,
|
||||
"",
|
||||
true,
|
||||
has_stripped_fields,
|
||||
cx,
|
||||
),
|
||||
)
|
||||
})?;
|
||||
write!(w, "{}", item_fields(cx, it, fields, None))?;
|
||||
DisplayStruct {
|
||||
ctor_kind: *ctor_kind,
|
||||
generics: &t.generics,
|
||||
fields,
|
||||
def_id: struct_def_id,
|
||||
}
|
||||
.render_into(cx, it, true, w)?;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let def_id = it.item_id.expect_def_id();
|
||||
// Render any items associated directly to this alias, as otherwise they
|
||||
// won't be visible anywhere in the docs. It would be nice to also show
|
||||
// associated items from the aliased type (see discussion in #32077), but
|
||||
// we need #14072 to make sense of the generics.
|
||||
write!(
|
||||
w,
|
||||
"{}{}",
|
||||
render_assoc_items(cx, it, def_id, AssocItemRender::All),
|
||||
document_type_layout(cx, def_id)
|
||||
)?;
|
||||
}
|
||||
|
||||
let def_id = it.item_id.expect_def_id();
|
||||
// Render any items associated directly to this alias, as otherwise they
|
||||
// won't be visible anywhere in the docs. It would be nice to also show
|
||||
// associated items from the aliased type (see discussion in #32077), but
|
||||
// we need #14072 to make sense of the generics.
|
||||
write!(
|
||||
w,
|
||||
"{}{}",
|
||||
render_assoc_items(cx, it, def_id, AssocItemRender::All),
|
||||
document_type_layout(cx, def_id)
|
||||
)?;
|
||||
|
||||
// [RUSTDOCIMPL] type.impl
|
||||
//
|
||||
// Include type definitions from the alias target type.
|
||||
|
|
@ -1463,50 +1427,83 @@ fn item_type_alias(cx: &Context<'_>, it: &clean::Item, t: &clean::TypeAlias) ->
|
|||
})
|
||||
}
|
||||
|
||||
fn item_union(cx: &Context<'_>, it: &clean::Item, s: &clean::Union) -> impl fmt::Display {
|
||||
item_template!(
|
||||
#[template(path = "item_union.html")]
|
||||
struct ItemUnion<'a, 'cx> {
|
||||
cx: &'a Context<'cx>,
|
||||
it: &'a clean::Item,
|
||||
s: &'a clean::Union,
|
||||
},
|
||||
methods = [document, document_type_layout, render_attributes_in_pre, render_assoc_items]
|
||||
);
|
||||
item_template!(
|
||||
#[template(path = "item_union.html")]
|
||||
struct ItemUnion<'a, 'cx> {
|
||||
cx: &'a Context<'cx>,
|
||||
it: &'a clean::Item,
|
||||
fields: &'a [clean::Item],
|
||||
generics: &'a clean::Generics,
|
||||
is_type_alias: bool,
|
||||
def_id: DefId,
|
||||
},
|
||||
methods = [document, document_type_layout, render_assoc_items]
|
||||
);
|
||||
|
||||
impl<'a, 'cx: 'a> ItemUnion<'a, 'cx> {
|
||||
fn render_union(&self) -> impl Display {
|
||||
render_union(self.it, Some(&self.s.generics), &self.s.fields, self.cx)
|
||||
}
|
||||
|
||||
fn document_field(&self, field: &'a clean::Item) -> impl Display {
|
||||
document(self.cx, field, Some(self.it), HeadingOffset::H3)
|
||||
}
|
||||
|
||||
fn stability_field(&self, field: &clean::Item) -> Option<String> {
|
||||
field.stability_class(self.cx.tcx())
|
||||
}
|
||||
|
||||
fn print_ty(&self, ty: &'a clean::Type) -> impl Display {
|
||||
ty.print(self.cx)
|
||||
}
|
||||
|
||||
fn fields_iter(
|
||||
&self,
|
||||
) -> iter::Peekable<impl Iterator<Item = (&'a clean::Item, &'a clean::Type)>> {
|
||||
self.s
|
||||
.fields
|
||||
.iter()
|
||||
.filter_map(|f| match f.kind {
|
||||
clean::StructFieldItem(ref ty) => Some((f, ty)),
|
||||
_ => None,
|
||||
})
|
||||
.peekable()
|
||||
}
|
||||
impl<'a, 'cx: 'a> ItemUnion<'a, 'cx> {
|
||||
fn render_union(&self) -> impl Display {
|
||||
render_union(self.it, Some(&self.generics), &self.fields, self.cx)
|
||||
}
|
||||
|
||||
fn document_field(&self, field: &'a clean::Item) -> impl Display {
|
||||
document(self.cx, field, Some(self.it), HeadingOffset::H3)
|
||||
}
|
||||
|
||||
fn stability_field(&self, field: &clean::Item) -> Option<String> {
|
||||
field.stability_class(self.cx.tcx())
|
||||
}
|
||||
|
||||
fn print_ty(&self, ty: &'a clean::Type) -> impl Display {
|
||||
ty.print(self.cx)
|
||||
}
|
||||
|
||||
// FIXME (GuillaumeGomez): When <https://github.com/askama-rs/askama/issues/452> is implemented,
|
||||
// we can replace the returned value with:
|
||||
//
|
||||
// `iter::Peekable<impl Iterator<Item = (&'a clean::Item, &'a clean::Type)>>`
|
||||
//
|
||||
// And update `item_union.html`.
|
||||
fn fields_iter(&self) -> impl Iterator<Item = (&'a clean::Item, &'a clean::Type)> {
|
||||
self.fields.iter().filter_map(|f| match f.kind {
|
||||
clean::StructFieldItem(ref ty) => Some((f, ty)),
|
||||
_ => None,
|
||||
})
|
||||
}
|
||||
|
||||
fn render_attributes_in_pre(&self) -> impl fmt::Display {
|
||||
fmt::from_fn(move |f| {
|
||||
if self.is_type_alias {
|
||||
// For now the only attributes we render for type aliases are `repr` attributes.
|
||||
if let Some(repr) = clean::repr_attributes(
|
||||
self.cx.tcx(),
|
||||
self.cx.cache(),
|
||||
self.def_id,
|
||||
ItemType::Union,
|
||||
false,
|
||||
) {
|
||||
writeln!(f, "{repr}")?;
|
||||
};
|
||||
} else {
|
||||
for a in self.it.attributes_and_repr(self.cx.tcx(), self.cx.cache(), false) {
|
||||
writeln!(f, "{a}")?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn item_union(cx: &Context<'_>, it: &clean::Item, s: &clean::Union) -> impl fmt::Display {
|
||||
fmt::from_fn(|w| {
|
||||
ItemUnion { cx, it, s }.render_into(w).unwrap();
|
||||
ItemUnion {
|
||||
cx,
|
||||
it,
|
||||
fields: &s.fields,
|
||||
generics: &s.generics,
|
||||
is_type_alias: false,
|
||||
def_id: it.def_id().unwrap(),
|
||||
}
|
||||
.render_into(w)?;
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
|
@ -1533,41 +1530,81 @@ fn print_tuple_struct_fields(cx: &Context<'_>, s: &[clean::Item]) -> impl Displa
|
|||
})
|
||||
}
|
||||
|
||||
fn item_enum(cx: &Context<'_>, it: &clean::Item, e: &clean::Enum) -> impl fmt::Display {
|
||||
fmt::from_fn(|w| {
|
||||
let count_variants = e.variants().count();
|
||||
struct DisplayEnum<'clean> {
|
||||
variants: &'clean IndexVec<VariantIdx, clean::Item>,
|
||||
generics: &'clean clean::Generics,
|
||||
is_non_exhaustive: bool,
|
||||
def_id: DefId,
|
||||
}
|
||||
|
||||
impl<'clean> DisplayEnum<'clean> {
|
||||
fn render_into<W: fmt::Write>(
|
||||
self,
|
||||
cx: &Context<'_>,
|
||||
it: &clean::Item,
|
||||
is_type_alias: bool,
|
||||
w: &mut W,
|
||||
) -> fmt::Result {
|
||||
let non_stripped_variant_count = self.variants.iter().filter(|i| !i.is_stripped()).count();
|
||||
let variants_len = self.variants.len();
|
||||
let has_stripped_entries = variants_len != non_stripped_variant_count;
|
||||
|
||||
wrap_item(w, |w| {
|
||||
render_attributes_in_code(w, it, cx);
|
||||
if is_type_alias {
|
||||
// For now the only attributes we render for type aliases are `repr` attributes.
|
||||
render_repr_attributes_in_code(w, cx, self.def_id, ItemType::Enum);
|
||||
} else {
|
||||
render_attributes_in_code(w, it, cx);
|
||||
}
|
||||
write!(
|
||||
w,
|
||||
"{}enum {}{}{}",
|
||||
visibility_print_with_space(it, cx),
|
||||
it.name.unwrap(),
|
||||
e.generics.print(cx),
|
||||
self.generics.print(cx),
|
||||
render_enum_fields(
|
||||
cx,
|
||||
Some(&e.generics),
|
||||
&e.variants,
|
||||
count_variants,
|
||||
e.has_stripped_entries(),
|
||||
it.is_non_exhaustive(),
|
||||
it.def_id().unwrap(),
|
||||
Some(self.generics),
|
||||
self.variants,
|
||||
non_stripped_variant_count,
|
||||
has_stripped_entries,
|
||||
self.is_non_exhaustive,
|
||||
self.def_id,
|
||||
),
|
||||
)
|
||||
})?;
|
||||
|
||||
write!(w, "{}", document(cx, it, None, HeadingOffset::H2))?;
|
||||
|
||||
if count_variants != 0 {
|
||||
write!(w, "{}", item_variants(cx, it, &e.variants, it.def_id().unwrap()))?;
|
||||
}
|
||||
let def_id = it.item_id.expect_def_id();
|
||||
let layout_def_id = if is_type_alias {
|
||||
self.def_id
|
||||
} else {
|
||||
write!(w, "{}", document(cx, it, None, HeadingOffset::H2))?;
|
||||
// We don't return the same `DefId` since the layout size of the type alias might be
|
||||
// different since we might have more information on the generics.
|
||||
def_id
|
||||
};
|
||||
|
||||
if non_stripped_variant_count != 0 {
|
||||
write!(w, "{}", item_variants(cx, it, self.variants, self.def_id))?;
|
||||
}
|
||||
write!(
|
||||
w,
|
||||
"{}{}",
|
||||
render_assoc_items(cx, it, def_id, AssocItemRender::All),
|
||||
document_type_layout(cx, def_id)
|
||||
document_type_layout(cx, layout_def_id)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn item_enum(cx: &Context<'_>, it: &clean::Item, e: &clean::Enum) -> impl fmt::Display {
|
||||
fmt::from_fn(|w| {
|
||||
DisplayEnum {
|
||||
variants: &e.variants,
|
||||
generics: &e.generics,
|
||||
is_non_exhaustive: it.is_non_exhaustive(),
|
||||
def_id: it.def_id().unwrap(),
|
||||
}
|
||||
.render_into(cx, it, false, w)
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -1955,27 +1992,59 @@ fn item_constant(
|
|||
})
|
||||
}
|
||||
|
||||
fn item_struct(cx: &Context<'_>, it: &clean::Item, s: &clean::Struct) -> impl fmt::Display {
|
||||
fmt::from_fn(|w| {
|
||||
struct DisplayStruct<'a> {
|
||||
ctor_kind: Option<CtorKind>,
|
||||
generics: &'a clean::Generics,
|
||||
fields: &'a [clean::Item],
|
||||
def_id: DefId,
|
||||
}
|
||||
|
||||
impl<'a> DisplayStruct<'a> {
|
||||
fn render_into<W: fmt::Write>(
|
||||
self,
|
||||
cx: &Context<'_>,
|
||||
it: &clean::Item,
|
||||
is_type_alias: bool,
|
||||
w: &mut W,
|
||||
) -> fmt::Result {
|
||||
wrap_item(w, |w| {
|
||||
render_attributes_in_code(w, it, cx);
|
||||
if is_type_alias {
|
||||
// For now the only attributes we render for type aliases are `repr` attributes.
|
||||
render_repr_attributes_in_code(w, cx, self.def_id, ItemType::Struct);
|
||||
} else {
|
||||
render_attributes_in_code(w, it, cx);
|
||||
}
|
||||
write!(
|
||||
w,
|
||||
"{}",
|
||||
render_struct(it, Some(&s.generics), s.ctor_kind, &s.fields, "", true, cx)
|
||||
render_struct(it, Some(self.generics), self.ctor_kind, self.fields, "", true, cx)
|
||||
)
|
||||
})?;
|
||||
|
||||
let def_id = it.item_id.expect_def_id();
|
||||
if !is_type_alias {
|
||||
write!(w, "{}", document(cx, it, None, HeadingOffset::H2))?;
|
||||
}
|
||||
|
||||
let def_id = it.item_id.expect_def_id();
|
||||
write!(
|
||||
w,
|
||||
"{}{}{}{}",
|
||||
document(cx, it, None, HeadingOffset::H2),
|
||||
item_fields(cx, it, &s.fields, s.ctor_kind),
|
||||
"{}{}{}",
|
||||
item_fields(cx, it, self.fields, self.ctor_kind),
|
||||
render_assoc_items(cx, it, def_id, AssocItemRender::All),
|
||||
document_type_layout(cx, def_id),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn item_struct(cx: &Context<'_>, it: &clean::Item, s: &clean::Struct) -> impl fmt::Display {
|
||||
fmt::from_fn(|w| {
|
||||
DisplayStruct {
|
||||
ctor_kind: s.ctor_kind,
|
||||
generics: &s.generics,
|
||||
fields: s.fields.as_slice(),
|
||||
def_id: it.def_id().unwrap(),
|
||||
}
|
||||
.render_into(cx, it, false, w)
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -599,7 +599,7 @@ fn sidebar_enum<'a>(
|
|||
deref_id_map: &'a DefIdMap<String>,
|
||||
) {
|
||||
let mut variants = e
|
||||
.variants()
|
||||
.non_stripped_variants()
|
||||
.filter_map(|v| v.name)
|
||||
.map(|name| Link::new(format!("variant.{name}"), name.to_string()))
|
||||
.collect::<Vec<_>>();
|
||||
|
|
|
|||
|
|
@ -2527,9 +2527,12 @@ in src-script.js and main.js
|
|||
z-index: 11;
|
||||
/* Reduce height slightly to account for mobile topbar. */
|
||||
height: calc(100vh - 45px);
|
||||
width: 200px;
|
||||
/* resize indicator: hide this when on touch or mobile */
|
||||
border-right: none;
|
||||
width: 100%;
|
||||
}
|
||||
.sidebar-elems .block li a {
|
||||
white-space: wrap;
|
||||
}
|
||||
|
||||
/* The source view uses a different design for the sidebar toggle, and doesn't have a topbar,
|
||||
|
|
|
|||
|
|
@ -2,15 +2,16 @@
|
|||
{{ self.render_attributes_in_pre()|safe }}
|
||||
{{ self.render_union()|safe }}
|
||||
</code></pre>
|
||||
{{ self.document()|safe }}
|
||||
{% if self.fields_iter().peek().is_some() %}
|
||||
{% if !self.is_type_alias %}
|
||||
{{ self.document()|safe }}
|
||||
{% endif %}
|
||||
{% if self.fields_iter().next().is_some() %}
|
||||
<h2 id="fields" class="fields section-header"> {# #}
|
||||
Fields<a href="#fields" class="anchor">§</a> {# #}
|
||||
</h2>
|
||||
{% for (field, ty) in self.fields_iter() %}
|
||||
{% let name = field.name.expect("union field name") %}
|
||||
<span id="structfield.{{ name }}" {#+ #}
|
||||
class="{{ ItemType::StructField +}} section-header"> {# #}
|
||||
<span id="structfield.{{ name }}" class="{{ ItemType::StructField +}} section-header"> {# #}
|
||||
<a href="#structfield.{{ name }}" class="anchor field">§</a> {# #}
|
||||
<code>{{ name }}: {{+ self.print_ty(ty)|safe }}</code> {# #}
|
||||
</span>
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ impl JsonRenderer<'_> {
|
|||
})
|
||||
.collect();
|
||||
let docs = item.opt_doc_value();
|
||||
let attrs = item.attributes(self.tcx, self.cache(), true);
|
||||
let attrs = item.attributes_and_repr(self.tcx, self.cache(), true);
|
||||
let span = item.span(self.tcx);
|
||||
let visibility = item.visibility(self.tcx);
|
||||
let clean::ItemInner { name, item_id, .. } = *item.inner;
|
||||
|
|
|
|||
|
|
@ -941,6 +941,8 @@ impl<'tcx> Delegate<'tcx> for DerefDelegate<'_, 'tcx> {
|
|||
ProjectionKind::Subslice |
|
||||
// Doesn't have surface syntax. Only occurs in patterns.
|
||||
ProjectionKind::OpaqueCast => (),
|
||||
// Only occurs in closure captures.
|
||||
ProjectionKind::UnwrapUnsafeBinder => (),
|
||||
ProjectionKind::Deref => {
|
||||
// Explicit derefs are typically handled later on, but
|
||||
// some items do not need explicit deref, such as array accesses,
|
||||
|
|
|
|||
|
|
@ -139,7 +139,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
AllocKind::LiveData => {
|
||||
if memory_kind == MiriMemoryKind::Global.into() {
|
||||
// For new global allocations, we always pre-allocate the memory to be able use the machine address directly.
|
||||
let prepared_bytes = MiriAllocBytes::zeroed(info.size, info.align)
|
||||
let prepared_bytes = MiriAllocBytes::zeroed(info.size, info.align, ())
|
||||
.unwrap_or_else(|| {
|
||||
panic!("Miri ran out of memory: cannot create allocation of {size:?} bytes", size = info.size)
|
||||
});
|
||||
|
|
@ -159,7 +159,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
AllocKind::Function | AllocKind::VTable => {
|
||||
// Allocate some dummy memory to get a unique address for this function/vtable.
|
||||
let alloc_bytes =
|
||||
MiriAllocBytes::from_bytes(&[0u8; 1], Align::from_bytes(1).unwrap());
|
||||
MiriAllocBytes::from_bytes(&[0u8; 1], Align::from_bytes(1).unwrap(), ());
|
||||
let ptr = alloc_bytes.as_ptr();
|
||||
// Leak the underlying memory to ensure it remains unique.
|
||||
std::mem::forget(alloc_bytes);
|
||||
|
|
@ -429,7 +429,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
prepared_alloc_bytes.copy_from_slice(bytes);
|
||||
interp_ok(prepared_alloc_bytes)
|
||||
} else {
|
||||
interp_ok(MiriAllocBytes::from_bytes(std::borrow::Cow::Borrowed(bytes), align))
|
||||
interp_ok(MiriAllocBytes::from_bytes(std::borrow::Cow::Borrowed(bytes), align, ()))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ impl Clone for MiriAllocBytes {
|
|||
fn clone(&self) -> Self {
|
||||
let bytes: Cow<'_, [u8]> = Cow::Borrowed(self);
|
||||
let align = Align::from_bytes(self.layout.align().to_u64()).unwrap();
|
||||
MiriAllocBytes::from_bytes(bytes, align)
|
||||
MiriAllocBytes::from_bytes(bytes, align, ())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -86,7 +86,10 @@ impl MiriAllocBytes {
|
|||
}
|
||||
|
||||
impl AllocBytes for MiriAllocBytes {
|
||||
fn from_bytes<'a>(slice: impl Into<Cow<'a, [u8]>>, align: Align) -> Self {
|
||||
/// Placeholder!
|
||||
type AllocParams = ();
|
||||
|
||||
fn from_bytes<'a>(slice: impl Into<Cow<'a, [u8]>>, align: Align, _params: ()) -> Self {
|
||||
let slice = slice.into();
|
||||
let size = slice.len();
|
||||
let align = align.bytes();
|
||||
|
|
@ -102,7 +105,7 @@ impl AllocBytes for MiriAllocBytes {
|
|||
alloc_bytes
|
||||
}
|
||||
|
||||
fn zeroed(size: Size, align: Align) -> Option<Self> {
|
||||
fn zeroed(size: Size, align: Align, _params: ()) -> Option<Self> {
|
||||
let size = size.bytes();
|
||||
let align = align.bytes();
|
||||
// SAFETY: `alloc_fn` will only be used with `size != 0`.
|
||||
|
|
|
|||
|
|
@ -218,34 +218,37 @@ impl<'tcx> Thread<'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Return the top user-relevant frame, if there is one.
|
||||
/// Return the top user-relevant frame, if there is one. `skip` indicates how many top frames
|
||||
/// should be skipped.
|
||||
/// Note that the choice to return `None` here when there is no user-relevant frame is part of
|
||||
/// justifying the optimization that only pushes of user-relevant frames require updating the
|
||||
/// `top_user_relevant_frame` field.
|
||||
fn compute_top_user_relevant_frame(&self) -> Option<usize> {
|
||||
fn compute_top_user_relevant_frame(&self, skip: usize) -> Option<usize> {
|
||||
self.stack
|
||||
.iter()
|
||||
.enumerate()
|
||||
.rev()
|
||||
.skip(skip)
|
||||
.find_map(|(idx, frame)| if frame.extra.is_user_relevant { Some(idx) } else { None })
|
||||
}
|
||||
|
||||
/// Re-compute the top user-relevant frame from scratch.
|
||||
pub fn recompute_top_user_relevant_frame(&mut self) {
|
||||
self.top_user_relevant_frame = self.compute_top_user_relevant_frame();
|
||||
/// Re-compute the top user-relevant frame from scratch. `skip` indicates how many top frames
|
||||
/// should be skipped.
|
||||
pub fn recompute_top_user_relevant_frame(&mut self, skip: usize) {
|
||||
self.top_user_relevant_frame = self.compute_top_user_relevant_frame(skip);
|
||||
}
|
||||
|
||||
/// Set the top user-relevant frame to the given value. Must be equal to what
|
||||
/// `get_top_user_relevant_frame` would return!
|
||||
pub fn set_top_user_relevant_frame(&mut self, frame_idx: usize) {
|
||||
debug_assert_eq!(Some(frame_idx), self.compute_top_user_relevant_frame());
|
||||
debug_assert_eq!(Some(frame_idx), self.compute_top_user_relevant_frame(0));
|
||||
self.top_user_relevant_frame = Some(frame_idx);
|
||||
}
|
||||
|
||||
/// Returns the topmost frame that is considered user-relevant, or the
|
||||
/// top of the stack if there is no such frame, or `None` if the stack is empty.
|
||||
pub fn top_user_relevant_frame(&self) -> Option<usize> {
|
||||
debug_assert_eq!(self.top_user_relevant_frame, self.compute_top_user_relevant_frame());
|
||||
debug_assert_eq!(self.top_user_relevant_frame, self.compute_top_user_relevant_frame(0));
|
||||
// This can be called upon creation of an allocation. We create allocations while setting up
|
||||
// parts of the Rust runtime when we do not have any stack frames yet, so we need to handle
|
||||
// empty stacks.
|
||||
|
|
@ -899,7 +902,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
let mut alloc = alloc.inner().adjust_from_tcx(
|
||||
&this.tcx,
|
||||
|bytes, align| {
|
||||
interp_ok(MiriAllocBytes::from_bytes(std::borrow::Cow::Borrowed(bytes), align))
|
||||
interp_ok(MiriAllocBytes::from_bytes(std::borrow::Cow::Borrowed(bytes), align, ()))
|
||||
},
|
||||
|ptr| this.global_root_pointer(ptr),
|
||||
)?;
|
||||
|
|
|
|||
|
|
@ -354,11 +354,10 @@ pub fn create_ecx<'tcx>(
|
|||
argvs.push(arg_place.to_ref(&ecx));
|
||||
}
|
||||
// Make an array with all these pointers, in the Miri memory.
|
||||
let argvs_layout = ecx.layout_of(Ty::new_array(
|
||||
tcx,
|
||||
Ty::new_imm_ptr(tcx, tcx.types.u8),
|
||||
u64::try_from(argvs.len()).unwrap(),
|
||||
))?;
|
||||
let u8_ptr_type = Ty::new_imm_ptr(tcx, tcx.types.u8);
|
||||
let u8_ptr_ptr_type = Ty::new_imm_ptr(tcx, u8_ptr_type);
|
||||
let argvs_layout =
|
||||
ecx.layout_of(Ty::new_array(tcx, u8_ptr_type, u64::try_from(argvs.len()).unwrap()))?;
|
||||
let argvs_place = ecx.allocate(argvs_layout, MiriMemoryKind::Machine.into())?;
|
||||
for (idx, arg) in argvs.into_iter().enumerate() {
|
||||
let place = ecx.project_field(&argvs_place, idx)?;
|
||||
|
|
@ -373,10 +372,8 @@ pub fn create_ecx<'tcx>(
|
|||
ecx.mark_immutable(&argc_place);
|
||||
ecx.machine.argc = Some(argc_place.ptr());
|
||||
|
||||
let argv_place = ecx.allocate(
|
||||
ecx.layout_of(Ty::new_imm_ptr(tcx, tcx.types.unit))?,
|
||||
MiriMemoryKind::Machine.into(),
|
||||
)?;
|
||||
let argv_place =
|
||||
ecx.allocate(ecx.layout_of(u8_ptr_ptr_type)?, MiriMemoryKind::Machine.into())?;
|
||||
ecx.write_pointer(argvs_place.ptr(), &argv_place)?;
|
||||
ecx.mark_immutable(&argv_place);
|
||||
ecx.machine.argv = Some(argv_place.ptr());
|
||||
|
|
@ -398,7 +395,9 @@ pub fn create_ecx<'tcx>(
|
|||
}
|
||||
ecx.mark_immutable(&cmd_place);
|
||||
}
|
||||
ecx.mplace_to_ref(&argvs_place)?
|
||||
let imm = argvs_place.to_ref(&ecx);
|
||||
let layout = ecx.layout_of(u8_ptr_ptr_type)?;
|
||||
ImmTy::from_immediate(imm, layout)
|
||||
};
|
||||
|
||||
// Return place (in static memory so that it does not count as leak).
|
||||
|
|
|
|||
|
|
@ -470,7 +470,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
caller_fn_abi,
|
||||
&args.iter().map(|a| FnArg::Copy(a.clone().into())).collect::<Vec<_>>(),
|
||||
/*with_caller_location*/ false,
|
||||
&dest,
|
||||
&dest.into(),
|
||||
stack_pop,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
&mut self,
|
||||
instance: ty::Instance<'tcx>,
|
||||
args: &[OpTy<'tcx>],
|
||||
dest: &MPlaceTy<'tcx>,
|
||||
dest: &PlaceTy<'tcx>,
|
||||
ret: Option<mir::BasicBlock>,
|
||||
unwind: mir::UnwindAction,
|
||||
) -> InterpResult<'tcx, Option<ty::Instance<'tcx>>> {
|
||||
|
|
@ -45,7 +45,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
let intrinsic_name = this.tcx.item_name(instance.def_id());
|
||||
let intrinsic_name = intrinsic_name.as_str();
|
||||
|
||||
match this.emulate_intrinsic_by_name(intrinsic_name, instance.args, args, dest, ret)? {
|
||||
// FIXME: avoid allocating memory
|
||||
let dest = this.force_allocation(dest)?;
|
||||
|
||||
match this.emulate_intrinsic_by_name(intrinsic_name, instance.args, args, &dest, ret)? {
|
||||
EmulateItemResult::NotSupported => {
|
||||
// We haven't handled the intrinsic, let's see if we can use a fallback body.
|
||||
if this.tcx.intrinsic(instance.def_id()).unwrap().must_be_overridden {
|
||||
|
|
|
|||
|
|
@ -1115,7 +1115,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> {
|
|||
instance: ty::Instance<'tcx>,
|
||||
abi: &FnAbi<'tcx, Ty<'tcx>>,
|
||||
args: &[FnArg<'tcx, Provenance>],
|
||||
dest: &MPlaceTy<'tcx>,
|
||||
dest: &PlaceTy<'tcx>,
|
||||
ret: Option<mir::BasicBlock>,
|
||||
unwind: mir::UnwindAction,
|
||||
) -> InterpResult<'tcx, Option<(&'tcx mir::Body<'tcx>, ty::Instance<'tcx>)>> {
|
||||
|
|
@ -1142,7 +1142,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> {
|
|||
fn_val: DynSym,
|
||||
abi: &FnAbi<'tcx, Ty<'tcx>>,
|
||||
args: &[FnArg<'tcx, Provenance>],
|
||||
dest: &MPlaceTy<'tcx>,
|
||||
dest: &PlaceTy<'tcx>,
|
||||
ret: Option<mir::BasicBlock>,
|
||||
unwind: mir::UnwindAction,
|
||||
) -> InterpResult<'tcx> {
|
||||
|
|
@ -1155,7 +1155,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> {
|
|||
ecx: &mut MiriInterpCx<'tcx>,
|
||||
instance: ty::Instance<'tcx>,
|
||||
args: &[OpTy<'tcx>],
|
||||
dest: &MPlaceTy<'tcx>,
|
||||
dest: &PlaceTy<'tcx>,
|
||||
ret: Option<mir::BasicBlock>,
|
||||
unwind: mir::UnwindAction,
|
||||
) -> InterpResult<'tcx, Option<ty::Instance<'tcx>>> {
|
||||
|
|
@ -1634,15 +1634,21 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> {
|
|||
interp_ok(())
|
||||
}
|
||||
|
||||
fn before_stack_pop(
|
||||
ecx: &InterpCx<'tcx, Self>,
|
||||
frame: &Frame<'tcx, Self::Provenance, Self::FrameExtra>,
|
||||
) -> InterpResult<'tcx> {
|
||||
fn before_stack_pop(ecx: &mut InterpCx<'tcx, Self>) -> InterpResult<'tcx> {
|
||||
let frame = ecx.frame();
|
||||
// We want this *before* the return value copy, because the return place itself is protected
|
||||
// until we do `end_call` here.
|
||||
if ecx.machine.borrow_tracker.is_some() {
|
||||
ecx.on_stack_pop(frame)?;
|
||||
}
|
||||
if frame.extra.is_user_relevant {
|
||||
// All that we store is whether or not the frame we just removed is local, so now we
|
||||
// have no idea where the next topmost local frame is. So we recompute it.
|
||||
// (If this ever becomes a bottleneck, we could have `push` store the previous
|
||||
// user-relevant frame and restore that here.)
|
||||
// We have to skip the frame that is just being popped.
|
||||
ecx.active_thread_mut().recompute_top_user_relevant_frame(/* skip */ 1);
|
||||
}
|
||||
// tracing-tree can autoamtically annotate scope changes, but it gets very confused by our
|
||||
// concurrency and what it prints is just plain wrong. So we print our own information
|
||||
// instead. (Cc https://github.com/rust-lang/miri/issues/2266)
|
||||
|
|
@ -1656,15 +1662,8 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> {
|
|||
frame: Frame<'tcx, Provenance, FrameExtra<'tcx>>,
|
||||
unwinding: bool,
|
||||
) -> InterpResult<'tcx, ReturnAction> {
|
||||
if frame.extra.is_user_relevant {
|
||||
// All that we store is whether or not the frame we just removed is local, so now we
|
||||
// have no idea where the next topmost local frame is. So we recompute it.
|
||||
// (If this ever becomes a bottleneck, we could have `push` store the previous
|
||||
// user-relevant frame and restore that here.)
|
||||
ecx.active_thread_mut().recompute_top_user_relevant_frame();
|
||||
}
|
||||
let res = {
|
||||
// Move `frame`` into a sub-scope so we control when it will be dropped.
|
||||
// Move `frame` into a sub-scope so we control when it will be dropped.
|
||||
let mut frame = frame;
|
||||
let timing = frame.extra.timing.take();
|
||||
let res = ecx.handle_stack_pop_unwind(frame.extra, unwinding);
|
||||
|
|
@ -1804,6 +1803,9 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> {
|
|||
) -> Cow<'e, RangeSet> {
|
||||
Cow::Borrowed(ecx.machine.union_data_ranges.entry(ty).or_insert_with(compute_range))
|
||||
}
|
||||
|
||||
/// Placeholder!
|
||||
fn get_default_alloc_params(&self) -> <Self::Bytes as AllocBytes>::AllocParams { () }
|
||||
}
|
||||
|
||||
/// Trait for callbacks handling asynchronous machine operations.
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
link_name: Symbol,
|
||||
abi: &FnAbi<'tcx, Ty<'tcx>>,
|
||||
args: &[OpTy<'tcx>],
|
||||
dest: &MPlaceTy<'tcx>,
|
||||
dest: &PlaceTy<'tcx>,
|
||||
ret: Option<mir::BasicBlock>,
|
||||
unwind: mir::UnwindAction,
|
||||
) -> InterpResult<'tcx, Option<(&'tcx mir::Body<'tcx>, ty::Instance<'tcx>)>> {
|
||||
|
|
@ -69,8 +69,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
_ => {}
|
||||
}
|
||||
|
||||
// FIXME: avoid allocating memory
|
||||
let dest = this.force_allocation(dest)?;
|
||||
|
||||
// The rest either implements the logic, or falls back to `lookup_exported_symbol`.
|
||||
match this.emulate_foreign_item_inner(link_name, abi, args, dest)? {
|
||||
match this.emulate_foreign_item_inner(link_name, abi, args, &dest)? {
|
||||
EmulateItemResult::NeedsReturn => {
|
||||
trace!("{:?}", this.dump_place(&dest.clone().into()));
|
||||
this.return_to_block(ret)?;
|
||||
|
|
@ -111,7 +114,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
sym: DynSym,
|
||||
abi: &FnAbi<'tcx, Ty<'tcx>>,
|
||||
args: &[OpTy<'tcx>],
|
||||
dest: &MPlaceTy<'tcx>,
|
||||
dest: &PlaceTy<'tcx>,
|
||||
ret: Option<mir::BasicBlock>,
|
||||
unwind: mir::UnwindAction,
|
||||
) -> InterpResult<'tcx> {
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ struct MakeSend(*const i32);
|
|||
unsafe impl Send for MakeSend {}
|
||||
|
||||
fn main() {
|
||||
race(0);
|
||||
race(0); //~ERROR: Data race detected between (1) non-atomic read on thread `unnamed-1` and (2) deallocation on thread `main`
|
||||
}
|
||||
|
||||
// Using an argument for the ptr to point to, since those do not get StorageDead.
|
||||
|
|
@ -22,5 +22,4 @@ fn race(local: i32) {
|
|||
thread::yield_now();
|
||||
// Deallocating the local (when `main` returns)
|
||||
// races with the read in the other thread.
|
||||
// Make sure the error points at this function's end, not just the call site.
|
||||
} //~ERROR: Data race detected between (1) non-atomic read on thread `unnamed-1` and (2) deallocation on thread `main`
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
error: Undefined Behavior: Data race detected between (1) non-atomic read on thread `unnamed-ID` and (2) deallocation on thread `main` at ALLOC. (2) just happened here
|
||||
--> tests/fail/data_race/stack_pop_race.rs:LL:CC
|
||||
|
|
||||
LL | }
|
||||
| ^ Data race detected between (1) non-atomic read on thread `unnamed-ID` and (2) deallocation on thread `main` at ALLOC. (2) just happened here
|
||||
LL | race(0);
|
||||
| ^^^^^^^ Data race detected between (1) non-atomic read on thread `unnamed-ID` and (2) deallocation on thread `main` at ALLOC. (2) just happened here
|
||||
|
|
||||
help: and (1) occurred earlier here
|
||||
--> tests/fail/data_race/stack_pop_race.rs:LL:CC
|
||||
|
|
@ -12,12 +12,7 @@ LL | let _val = unsafe { *ptr.0 };
|
|||
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
|
||||
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
|
||||
= note: BACKTRACE (of the first span):
|
||||
= note: inside `race` at tests/fail/data_race/stack_pop_race.rs:LL:CC
|
||||
note: inside `main`
|
||||
--> tests/fail/data_race/stack_pop_race.rs:LL:CC
|
||||
|
|
||||
LL | race(0);
|
||||
| ^^^^^^^
|
||||
= note: inside `main` at tests/fail/data_race/stack_pop_race.rs:LL:CC
|
||||
|
||||
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
|
||||
|
||||
|
|
|
|||
|
|
@ -14,8 +14,8 @@ LL | let local = 0;
|
|||
help: ALLOC was deallocated here:
|
||||
--> tests/fail/tail_calls/dangling-local-var.rs:LL:CC
|
||||
|
|
||||
LL | become g(ptr)
|
||||
| ^^^^^^^^^^^^^
|
||||
LL | f(std::ptr::null());
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
= note: BACKTRACE (of the first span):
|
||||
= note: inside `g` at tests/fail/tail_calls/dangling-local-var.rs:LL:CC
|
||||
note: inside `main`
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#![no_std]
|
||||
#![no_main]
|
||||
//@compile-flags: -Zmiri-track-alloc-id=21 -Zmiri-track-alloc-accesses -Cpanic=abort
|
||||
//@normalize-stderr-test: "id 21" -> "id $$ALLOC"
|
||||
//@compile-flags: -Zmiri-track-alloc-id=20 -Zmiri-track-alloc-accesses -Cpanic=abort
|
||||
//@normalize-stderr-test: "id 20" -> "id $$ALLOC"
|
||||
//@only-target: linux # alloc IDs differ between OSes (due to extern static allocations)
|
||||
|
||||
extern "Rust" {
|
||||
|
|
|
|||
|
|
@ -200,6 +200,7 @@ const EXCEPTIONS_CRANELIFT: ExceptionList = &[
|
|||
("cranelift-module", "Apache-2.0 WITH LLVM-exception"),
|
||||
("cranelift-native", "Apache-2.0 WITH LLVM-exception"),
|
||||
("cranelift-object", "Apache-2.0 WITH LLVM-exception"),
|
||||
("cranelift-srcgen", "Apache-2.0 WITH LLVM-exception"),
|
||||
("foldhash", "Zlib"),
|
||||
("mach2", "BSD-2-Clause OR MIT OR Apache-2.0"),
|
||||
("regalloc2", "Apache-2.0 WITH LLVM-exception"),
|
||||
|
|
@ -525,6 +526,7 @@ const PERMITTED_CRANELIFT_DEPENDENCIES: &[&str] = &[
|
|||
"cranelift-module",
|
||||
"cranelift-native",
|
||||
"cranelift-object",
|
||||
"cranelift-srcgen",
|
||||
"crc32fast",
|
||||
"equivalent",
|
||||
"fallible-iterator",
|
||||
|
|
|
|||
|
|
@ -54,6 +54,7 @@ pub struct Feature {
|
|||
pub tracking_issue: Option<NonZeroU32>,
|
||||
pub file: PathBuf,
|
||||
pub line: usize,
|
||||
pub description: Option<String>,
|
||||
}
|
||||
impl Feature {
|
||||
fn tracking_issue_display(&self) -> impl fmt::Display {
|
||||
|
|
@ -296,6 +297,7 @@ fn collect_lang_features_in(features: &mut Features, base: &Path, file: &str, ba
|
|||
let mut prev_names = vec![];
|
||||
|
||||
let lines = contents.lines().zip(1..);
|
||||
let mut doc_comments: Vec<String> = Vec::new();
|
||||
for (line, line_number) in lines {
|
||||
let line = line.trim();
|
||||
|
||||
|
|
@ -332,6 +334,13 @@ fn collect_lang_features_in(features: &mut Features, base: &Path, file: &str, ba
|
|||
continue;
|
||||
}
|
||||
|
||||
if in_feature_group {
|
||||
if let Some(doc_comment) = line.strip_prefix("///") {
|
||||
doc_comments.push(doc_comment.trim().to_string());
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
let mut parts = line.split(',');
|
||||
let level = match parts.next().map(|l| l.trim().trim_start_matches('(')) {
|
||||
Some("unstable") => Status::Unstable,
|
||||
|
|
@ -438,9 +447,15 @@ fn collect_lang_features_in(features: &mut Features, base: &Path, file: &str, ba
|
|||
tracking_issue,
|
||||
file: path.to_path_buf(),
|
||||
line: line_number,
|
||||
description: if doc_comments.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(doc_comments.join(" "))
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
doc_comments.clear();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -564,6 +579,7 @@ fn map_lib_features(
|
|||
tracking_issue: find_attr_val(line, "issue").and_then(handle_issue_none),
|
||||
file: file.to_path_buf(),
|
||||
line: i + 1,
|
||||
description: None,
|
||||
};
|
||||
mf(Ok((feature_name, feature)), file, i + 1);
|
||||
continue;
|
||||
|
|
@ -600,6 +616,7 @@ fn map_lib_features(
|
|||
tracking_issue,
|
||||
file: file.to_path_buf(),
|
||||
line: i + 1,
|
||||
description: None,
|
||||
};
|
||||
if line.contains(']') {
|
||||
mf(Ok((feature_name, feature)), file, i + 1);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
[The Unstable Book](the-unstable-book.md)
|
||||
|
||||
- [Compiler environment variables](compiler-environment-variables.md)
|
||||
{compiler_env_vars}
|
||||
- [Compiler flags](compiler-flags.md)
|
||||
{compiler_flags}
|
||||
- [Language features](language-features.md)
|
||||
|
|
|
|||
|
|
@ -12,13 +12,18 @@ use tidy::unstable_book::{
|
|||
collect_unstable_feature_names,
|
||||
};
|
||||
|
||||
fn generate_stub_issue(path: &Path, name: &str, issue: u32) {
|
||||
let content = format!(include_str!("stub-issue.md"), name = name, issue = issue);
|
||||
fn generate_stub_issue(path: &Path, name: &str, issue: u32, description: &str) {
|
||||
let content = format!(
|
||||
include_str!("stub-issue.md"),
|
||||
name = name,
|
||||
issue = issue,
|
||||
description = description
|
||||
);
|
||||
t!(write(path, content), path);
|
||||
}
|
||||
|
||||
fn generate_stub_no_issue(path: &Path, name: &str) {
|
||||
let content = format!(include_str!("stub-no-issue.md"), name = name);
|
||||
fn generate_stub_no_issue(path: &Path, name: &str, description: &str) {
|
||||
let content = format!(include_str!("stub-no-issue.md"), name = name, description = description);
|
||||
t!(write(path, content), path);
|
||||
}
|
||||
|
||||
|
|
@ -30,8 +35,12 @@ fn set_to_summary_str(set: &BTreeSet<String>, dir: &str) -> String {
|
|||
|
||||
fn generate_summary(path: &Path, lang_features: &Features, lib_features: &Features) {
|
||||
let compiler_flags = collect_unstable_book_section_file_names(&path.join("src/compiler-flags"));
|
||||
let compiler_env_vars =
|
||||
collect_unstable_book_section_file_names(&path.join("src/compiler-environment-variables"));
|
||||
|
||||
let compiler_flags_str = set_to_summary_str(&compiler_flags, "compiler-flags");
|
||||
let compiler_env_vars_str =
|
||||
set_to_summary_str(&compiler_env_vars, "compiler-environment-variables");
|
||||
|
||||
let unstable_lang_features = collect_unstable_feature_names(&lang_features);
|
||||
let unstable_lib_features = collect_unstable_feature_names(&lib_features);
|
||||
|
|
@ -42,6 +51,7 @@ fn generate_summary(path: &Path, lang_features: &Features, lib_features: &Featur
|
|||
let summary_path = path.join("src/SUMMARY.md");
|
||||
let content = format!(
|
||||
include_str!("SUMMARY.md"),
|
||||
compiler_env_vars = compiler_env_vars_str,
|
||||
compiler_flags = compiler_flags_str,
|
||||
language_features = lang_features_str,
|
||||
library_features = lib_features_str
|
||||
|
|
@ -58,11 +68,17 @@ fn generate_unstable_book_files(src: &Path, out: &Path, features: &Features) {
|
|||
let file_name = format!("{feature_name}.md");
|
||||
let out_file_path = out.join(&file_name);
|
||||
let feature = &features[&feature_name_underscore];
|
||||
let description = feature.description.as_deref().unwrap_or_default();
|
||||
|
||||
if let Some(issue) = feature.tracking_issue {
|
||||
generate_stub_issue(&out_file_path, &feature_name_underscore, issue.get());
|
||||
generate_stub_issue(
|
||||
&out_file_path,
|
||||
&feature_name_underscore,
|
||||
issue.get(),
|
||||
&description,
|
||||
);
|
||||
} else {
|
||||
generate_stub_no_issue(&out_file_path, &feature_name_underscore);
|
||||
generate_stub_no_issue(&out_file_path, &feature_name_underscore, &description);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
# `{name}`
|
||||
|
||||
{description}
|
||||
|
||||
The tracking issue for this feature is: [#{issue}]
|
||||
|
||||
[#{issue}]: https://github.com/rust-lang/rust/issues/{issue}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
# `{name}`
|
||||
|
||||
{description}
|
||||
|
||||
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
|
||||
|
||||
------------------------
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue