Compare commits

...

53 Commits

Author SHA1 Message Date
bors
0c34d2c01e Auto merge of #147053 - jhpratt:rollup-tek9gok, r=jhpratt
Rollup of 8 pull requests

Successful merges:

 - rust-lang/rust#146283 (Resolve: (Ref)Cell wrappers to deny mutation during spec resolution.)
 - rust-lang/rust#146453 (Add general arm-linux.md platform doc.)
 - rust-lang/rust#146991 (const_caller_location to use real Span instead of `DUMMY_SP`)
 - rust-lang/rust#146994 (Add `clippy::unconditional_recursion` to `./x clippy ci`)
 - rust-lang/rust#147027 (Add new `tyalias` intra-doc link disambiguator)
 - rust-lang/rust#147038 (Rename verbosity functions in bootstrap)
 - rust-lang/rust#147047 (rustdoc: put the toolbar on the all item index)
 - rust-lang/rust#147049 (std: fix warning in VEXos stdio module)

r? `@ghost`
`@rustbot` modify labels: rollup
2025-09-26 03:10:49 +00:00
Jacob Pratt
6a2313d5f8
Rollup merge of #147049 - vexide:vex-std, r=workingjubilee
std: fix warning in VEXos stdio module

Fixes building `std` on the `armv7a-vex-v5` target due to an unnecessarily mutable argument in `Stdin`.

This was a stupid oversight on my part towards the end of rust-lang/rust#145973's review process. Missed a warning and had a bad bootstrap config that didn't tell me about it when testing changes.
2025-09-25 21:15:12 -04:00
Jacob Pratt
7ff5c85cf0
Rollup merge of #147047 - notriddle:toolbar-index, r=GuillaumeGomez
rustdoc: put the toolbar on the all item index
2025-09-25 21:15:11 -04:00
Jacob Pratt
d06bc1d271
Rollup merge of #147038 - Kobzol:bootstrap-verbose-fn, r=jieyouxu
Rename verbosity functions in bootstrap

Just a small cleanup, these function names have been bothering me for a while. I realized that we can delete some of them outright, rather than just renaming them.

r? `@jieyouxu`
2025-09-25 21:15:11 -04:00
Jacob Pratt
64d43d9d5a
Rollup merge of #147027 - GuillaumeGomez:tyalias-disambiguator, r=lolbinarycat
Add new `tyalias` intra-doc link disambiguator

Fixes https://github.com/rust-lang/rust/issues/146855.

Alternative to rust-lang/rust#146866.

This adds support for a new disambiguator: `tyalias`. I think it's common enough to have type aliases nowaday, so no reason to not be able to have a disambiguator for them.

cc `@fmease`
r? lolbinarycat
2025-09-25 21:15:10 -04:00
Jacob Pratt
9c0f389cbe
Rollup merge of #146994 - cuviper:clippy-ci-recursion, r=Kobzol
Add `clippy::unconditional_recursion` to `./x clippy ci`

The clippy lint catches some things that rustc's equivalent builtin lint
does not, for example rust-lang/rust#146940:

    error: function cannot return without recursing
        --> library/std/src/path.rs:3428:5
         |
    3428 | /     fn eq(&self, other: &String) -> bool {
    3429 | |         self == &*other
    3430 | |     }
         | |_____^
         |
    note: recursive call site
        --> library/std/src/path.rs:3429:9
         |
    3429 |         self == &*other
         |         ^^^^^^^^^^^^^^^
         = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#unconditional_recursion
         = note: requested on the command line with `-D clippy::unconditional-recursion`
2025-09-25 21:15:09 -04:00
Jacob Pratt
d2b5c3dcaa
Rollup merge of #146991 - cachebag:span-caller-location, r=RalfJung
const_caller_location to use real Span instead of `DUMMY_SP`

Closes: rust-lang/rust#146990
2025-09-25 21:15:09 -04:00
Jacob Pratt
1c4bbd8c43
Rollup merge of #146453 - thejpster:arm-linux-docs, r=petrochenkov
Add general arm-linux.md platform doc.

Adds a new page that covers all 32-bit Arm Linux systems. This means that we can reduce the amount of information required in the target specific pages to just the Tier level, the maintainer, and any specific details for that target.

I have no changed those pages yet, though. Let's start with this.
2025-09-25 21:15:08 -04:00
Jacob Pratt
b69548c886
Rollup merge of #146283 - LorrensP-2158466:resolve-cm-cell, r=petrochenkov
Resolve: (Ref)Cell wrappers to deny mutation during spec resolution.

Introduces wrappers around `Cell` and `RefCell` that only allow mutation when we are not in speculative resolution. This is preparatory work for rust-lang/rust#145108.

It would allow us to make `ImportData` and `ModuleData` sync and send safe.

r? `@petrochenkov`
2025-09-25 21:15:08 -04:00
bors
b733736ea2 Auto merge of #146919 - lcnr:yeet-fast-path, r=fee1-dead
remove incorrect fast path

Using `tcx.is_copy_modulo_regions` drops information from the current `typing_env`. Writing a regression test for this is really hard. We need to prove `Copy` of something that doesn't directly reference a coroutine or an opaque, but does so indirectly.

cc rust-lang/rust#146813.
2025-09-25 23:50:40 +00:00
Michael Howell
51ae86dec9 rustdoc-search: add test case for all/index.html search 2025-09-25 15:45:11 -07:00
Tropical
0ede3fe48c std: fix warning in VEXos stdio module 2025-09-25 16:34:20 -05:00
Michael Howell
8daad494b1 rustdoc: put the toolbar on the all item index 2025-09-25 14:19:22 -07:00
bors
7ac0330c6d Auto merge of #147037 - matthiaskrgr:rollup-xtgqzuu, r=matthiaskrgr
Rollup of 8 pull requests

Successful merges:

 - rust-lang/rust#116882 (rustdoc: hide `#[repr]` if it isn't part of the public ABI)
 - rust-lang/rust#135771 ([rustdoc] Add support for associated items in "jump to def" feature)
 - rust-lang/rust#141032 (avoid violating `slice::from_raw_parts` safety contract in `Vec::extract_if`)
 - rust-lang/rust#142401 (Add proper name mangling for pattern types)
 - rust-lang/rust#146293 (feat: non-panicking `Vec::try_remove`)
 - rust-lang/rust#146859 (BTreeMap: Don't leak allocators when initializing nodes)
 - rust-lang/rust#146924 (Add doc for `NonZero*` const creation)
 - rust-lang/rust#146933 (Make `render_example_with_highlighting` return an `impl fmt::Display`)

r? `@ghost`
`@rustbot` modify labels: rollup
2025-09-25 20:35:49 +00:00
Akrm Al-Hakimi
c98b3b2da4 const_caller_location: edit FIXME to explain use of DUMMY_SP
Co-authored-by: Ralf Jung <post@ralfj.de>
2025-09-25 16:09:27 -04:00
Josh Stone
a2d2869924 Add clippy::unconditional_recursion to ./x clippy ci
The clippy lint catches some things that rustc's equivalent builtin lint
does not, for example rust-lang/rust#146940:

    error: function cannot return without recursing
        --> library/std/src/path.rs:3428:5
         |
    3428 | /     fn eq(&self, other: &String) -> bool {
    3429 | |         self == &*other
    3430 | |     }
         | |_____^
         |
    note: recursive call site
        --> library/std/src/path.rs:3429:9
         |
    3429 |         self == &*other
         |         ^^^^^^^^^^^^^^^
         = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#unconditional_recursion
         = note: requested on the command line with `-D clippy::unconditional-recursion`
2025-09-25 10:08:23 -07:00
Jakub Beránek
8ea9122c9b
Rename verbose to do_if_verbose 2025-09-25 18:55:59 +02:00
Guillaume Gomez
5c710d770c Mention tyalias in intra-doc link rustdoc book chapter 2025-09-25 18:41:16 +02:00
Guillaume Gomez
a7862da48a Add tests for new tyalias intra-doc link disambiguator 2025-09-25 18:41:16 +02:00
LorrensP-2158466
0374df1b50 Introduce and use CmCell during import resolution. 2025-09-25 18:38:36 +02:00
Guillaume Gomez
03fc2990b9 Add new tyalias intra-doc link disambiguator 2025-09-25 18:30:03 +02:00
Matthias Krüger
c6d0059fb8
Rollup merge of #146933 - yotamofek:pr/rustdoc/highlight_no_write_str, r=GuillaumeGomez
Make `render_example_with_highlighting` return an `impl fmt::Display`

Removes some more usages of `write_str`. Shouldn't affect runtime, but makes the codebase a bit more consistent.
Each commit can be reviewed separately
2025-09-25 18:15:11 +02:00
Matthias Krüger
781f71a6fe
Rollup merge of #146924 - cptpiepmatz:doc-nonzero-const-creation, r=joboet
Add doc for `NonZero*` const creation

I ran into trouble using `NonZero*` values because I didn’t see any clear way to create them at compile time. At first I ended up using `NonZero*::new_unchecked` a lot, until I realized that `Option::unwrap` and `Option::expect` are `const` and can be used in a `const` context. With that, you can create non-zero values at compile time safely, without touching `unsafe`. This wasn’t obvious to me and my peers who’ve been using Rust for a while, so I thought adding a note to the docs would make it easier for others to discover.

If this should be worded differently or placed in another location, we can do that. I just want to make this more obvious.
2025-09-25 18:15:10 +02:00
Matthias Krüger
83cf8f9860
Rollup merge of #146859 - cammeresi:btree-alloc-20250920, r=joboet
BTreeMap: Don't leak allocators when initializing nodes

Memory was allocated via `Box::leak` and thence intended to be tracked and deallocated manually, but the allocator was also leaked, not tracked, and never dropped.  Now it is dropped immediately.

According to my reading of the `Allocator` trait, if a copy of the allocator remains live, then its allocations must remain live.  Since the B-tree has a copy of the allocator that will only be dropped after the nodes, it's safe to not store the allocator in each node (which would be a much more intrusive change).

Fixes: rust-lang/rust#106203
2025-09-25 18:15:09 +02:00
Matthias Krüger
fea9196e52
Rollup merge of #146293 - BenjaminBrienen:try_remove, r=joboet
feat: non-panicking `Vec::try_remove`

`if index < my_vector.len() { Some(my_vector.remove(index)) } else { None }` is annoying to write and non-panicking functions are broadly useful.

APC: https://github.com/rust-lang/libs-team/issues/649

Tracking issue: https://github.com/rust-lang/rust/issues/146954
2025-09-25 18:15:09 +02:00
Matthias Krüger
958d1438b6
Rollup merge of #142401 - oli-obk:pattern-mango, r=petrochenkov
Add proper name mangling for pattern types

requires adding demangler support first https://github.com/rust-lang/rustc-demangle/pull/81

needed for https://github.com/rust-lang/rust/pull/136006#discussion_r2139792593 as otherwise we will have symbol collisions
2025-09-25 18:15:08 +02:00
Matthias Krüger
e3f7626732
Rollup merge of #141032 - petrosagg:extract-if-ub, r=joboet
avoid violating `slice::from_raw_parts` safety contract in `Vec::extract_if`

The implementation of the `Vec::extract_if` iterator violates the safety contract adverized by `slice::from_raw_parts` by always constructing a mutable slice for the entire length of the vector even though that span of memory can contain holes from items already drained. The safety contract of `slice::from_raw_parts` requires that all elements must be properly
initialized.

As an example we can look at the following code:

```rust
let mut v = vec![Box::new(0u64), Box::new(1u64)];
for item in v.extract_if(.., |x| **x == 0) {
    drop(item);
}
```

In the second iteration a `&mut [Box<u64>]` slice of length 2 will be constructed. The first slot of the slice contains the bitpattern of an already deallocated box, which is invalid.

This fixes the issue by only creating references to valid items and using pointer manipulation for the rest. I have also taken the liberty to remove the big `unsafe` blocks in place of targetted ones with a SAFETY comment. The approach closely mirrors the implementation of `Vec::retain_mut`.

**Note to reviewers:** The diff is easier to follow with whitespace hidden.
2025-09-25 18:15:07 +02:00
Matthias Krüger
a028b7a9e3
Rollup merge of #135771 - GuillaumeGomez:jump-to-def-perf, r=fmease
[rustdoc] Add support for associated items in "jump to def" feature

Fixes https://github.com/rust-lang/rust/issues/135485.

r? ``@fmease``
2025-09-25 18:15:06 +02:00
Matthias Krüger
a39d5134cd
Rollup merge of #116882 - fmease:rustdoc-generalized-priv-repr-heuristic, r=rustdoc
rustdoc: hide `#[repr]` if it isn't part of the public ABI

> [!IMPORTANT]
> Temporarily stacked on top of PR https://github.com/rust-lang/rust/pull/146527; only the last commit is relevant!

Follow-up to rust-lang/rust#115439.
Unblocks rust-lang/rust#116743, CC ``@dtolnay.``

Fixes rust-lang/rust#66401.
Fixes rust-lang/rust#128364.
Fixes rust-lang/rust#137440.

Only display the representation `#[repr(REPR)]` (where `REPR` is not `Rust` or `transparent`) of a given type if none of its variants (incl. the synthetic variants of structs) are `#[doc(hidden)]` and all of its fields are public and not `#[doc(hidden)]` since it's likely not meant to be considered part of the public ABI otherwise.

`--document-{private,hidden}-items` works as expected in this context, too.

Moreover, we now also factor in the presence of `#[doc(hidden)]` when checking whether to show `repr(transparent)` or not.
2025-09-25 18:15:06 +02:00
Jakub Beránek
5a6e3ccceb
Remove is_verbose_than function 2025-09-25 18:14:33 +02:00
Jakub Beránek
5595f437c7
Remove verbose_than function 2025-09-25 18:13:34 +02:00
Tim 'Piepmatz' Hesse
185ae698aa add doc for NonZero* const creation 2025-09-25 17:52:24 +02:00
León Orell Valerian Liehr
85c193a4ed
rustdoc: hide #[repr(...)] if it isn't part of the public ABI 2025-09-25 11:49:27 +02:00
León Orell Valerian Liehr
d7d7725b8c
rustdoc: Fully escape link section and export name
Escape "special characters" (e.g., double quotes `"` and line breaks `\n`).
Escape HTML.

Lastly, add regression tests and clean up existing tests.
2025-09-25 11:30:58 +02:00
León Orell Valerian Liehr
88e797784e
rustdoc: Slightly clean up attr rendering 2025-09-25 10:16:52 +02:00
Sidney Cammeresi
137d4eaf12
BTreeMap: Don't leak allocators when initializing nodes
Memory was allocated via `Box::leak` and thence intended to be tracked
and deallocated manually, but the allocator was also leaked, not
tracked, and never dropped.  Now it is dropped immediately.

According to my reading of the `Allocator` trait, if a copy of the
allocator remains live, then its allocations must remain live.  Since
the B-tree has a copy of the allocator that will only be dropped after
the nodes, it's safe to not store the allocator in each node (which
would be a much more intrusive change).
2025-09-24 18:08:32 -07:00
BenjaminBrienen
56734495e2 feature: Implement vec_try_remove
Vec::try_remove is a non-panicking version of Vec::remove
2025-09-24 15:29:09 +02:00
lcnr
7a0adc0878 add test 2025-09-24 15:02:41 +02:00
lcnr
83532f8544 remove test for no_core 2025-09-23 17:08:29 +02:00
lcnr
4fcafc9daa yeet fastpath 2025-09-23 17:02:55 +02:00
Yotam Ofek
e697f206fe Remove unused param from write_header 2025-09-23 17:29:47 +03:00
Yotam Ofek
aaff372f4b Remove Tooltip::None variant, use Option::None 2025-09-23 17:29:47 +03:00
Yotam Ofek
58c7505d86 Make render_example_with_highlighting return an impl fmt::Display 2025-09-23 17:29:30 +03:00
Oli Scherer
739e89980f Add proper name mangling for pattern types 2025-09-23 10:59:29 +00:00
Petros Angelatos
e9b2c4f395 avoid violating slice::from_raw_parts safety contract in Vec::extract_if
The implementation of the `Vec::extract_if` iterator violates the safety
contract adverized by `slice::from_raw_parts` by always constructing a
mutable slice for the entire length of the vector even though that span
of memory can contain holes from items already drained. The safety
contract of `slice::from_raw_parts` requires that all elements must be
properly initialized.

As an example we can look at the following code:

```rust
let mut v = vec![Box::new(0u64), Box::new(1u64)];
for item in v.extract_if(.., |x| **x == 0) {
    drop(item);
}
```

In the second iteration a `&mut [Box<u64>]` slice of length 2 will be
constructed. The first slot of the slice contains the bitpattern of an
already deallocated box, which is invalid.

This fixes the issue by only creating references to valid items and
using pointer manipulation for the rest. I have also taken the liberty
to remove the big `unsafe` blocks in place of targetted ones with a
SAFETY comment. The approach closely mirrors the implementation of
`Vec::retain_mut`.

Signed-off-by: Petros Angelatos <petrosagg@gmail.com>
2025-09-22 10:59:52 +03:00
León Orell Valerian Liehr
15f6d2e8d2
rustdoc: Move HTML-specific attr rendering code into HTML rendering mod
These functions used to be shared with the JSON backend but nowadays they
aren't.
2025-09-14 01:30:05 +02:00
Jonathan 'theJPster' Pallant
4d273c4d91
Fix two typos spotted in review 2025-09-12 20:45:34 +01:00
Jonathan 'theJPster' Pallant
ec5375fc55
Add general arm-linux.md platform doc.
Covers all Arm Linux systems, and means that we can reduce the amount
of information required in the target specific pages to just the Tier
level, the maintainer, and any specific details for that target.
2025-09-11 21:53:29 +01:00
Guillaume Gomez
25e767b0ee Ignore impl associated types in jump to def feature 2025-08-10 15:49:22 +02:00
Guillaume Gomez
d528a49fd7 Fix panic if an item does not have a body 2025-08-10 12:00:08 +02:00
Guillaume Gomez
1a6d6363d0 Update to last rustc_hir Visitor changes 2025-08-10 12:00:08 +02:00
Guillaume Gomez
f1649a4e43 Better handling of paths in link to def feature 2025-08-10 12:00:08 +02:00
Guillaume Gomez
49044d0d40 Add support for trait associated items 2025-08-10 12:00:08 +02:00
75 changed files with 1432 additions and 684 deletions

View File

@ -61,7 +61,7 @@ pub(crate) fn const_caller_location_provider(
trace!("const_caller_location: {}:{}:{}", file, line, col);
let mut ecx = mk_eval_cx_to_read_const_val(
tcx,
rustc_span::DUMMY_SP, // FIXME: use a proper span here?
rustc_span::DUMMY_SP, // This interpreter cannot fail, so the span is irrelevant.
ty::TypingEnv::fully_monomorphized(),
CanAccessMutGlobal::No,
);

View File

@ -5,7 +5,6 @@
//! unexpanded macros in the fragment are visited and registered.
//! Imports are also considered items and placed into modules here, but not resolved yet.
use std::cell::Cell;
use std::sync::Arc;
use rustc_ast::visit::{self, AssocCtxt, Visitor, WalkItemKind};
@ -35,6 +34,7 @@ use crate::Namespace::{MacroNS, TypeNS, ValueNS};
use crate::def_collector::collect_definitions;
use crate::imports::{ImportData, ImportKind};
use crate::macros::{MacroRulesBinding, MacroRulesScope, MacroRulesScopeRef};
use crate::ref_mut::CmCell;
use crate::{
BindingKey, ExternPreludeEntry, Finalize, MacroData, Module, ModuleKind, ModuleOrUniformRoot,
NameBinding, ParentScope, PathResult, ResolutionError, Resolver, Segment, Used,
@ -87,7 +87,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
// because they can be fetched by glob imports from those modules, and bring traits
// into scope both directly and through glob imports.
let key = BindingKey::new_disambiguated(ident, ns, || {
parent.underscore_disambiguator.update(|d| d + 1);
// FIXME(batched): Will be fixed in batched resolution.
parent.underscore_disambiguator.update_unchecked(|d| d + 1);
parent.underscore_disambiguator.get()
});
if self
@ -477,7 +478,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
kind,
parent_scope: self.parent_scope,
module_path,
imported_module: Cell::new(None),
imported_module: CmCell::new(None),
span,
use_span: item.span,
use_span_with_attributes: item.span_with_attributes(),
@ -505,7 +506,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
});
}
}
ImportKind::Glob { .. } => current_module.globs.borrow_mut().push(import),
ImportKind::Glob { .. } => current_module.globs.borrow_mut(self.r).push(import),
_ => unreachable!(),
}
}
@ -668,7 +669,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
}
ast::UseTreeKind::Glob => {
if !ast::attr::contains_name(&item.attrs, sym::prelude_import) {
let kind = ImportKind::Glob { max_vis: Cell::new(None), id };
let kind = ImportKind::Glob { max_vis: CmCell::new(None), id };
self.add_import(prefix, kind, use_tree.span, item, root_span, item.id, vis);
} else {
// Resolve the prelude import early.
@ -971,7 +972,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
kind: ImportKind::ExternCrate { source: orig_name, target: ident, id: item.id },
root_id: item.id,
parent_scope: self.parent_scope,
imported_module: Cell::new(module),
imported_module: CmCell::new(module),
has_attributes: !item.attrs.is_empty(),
use_span_with_attributes: item.span_with_attributes(),
use_span: item.span,
@ -1103,7 +1104,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
kind: ImportKind::MacroUse { warn_private },
root_id: item.id,
parent_scope: this.parent_scope,
imported_module: Cell::new(Some(ModuleOrUniformRoot::Module(module))),
imported_module: CmCell::new(Some(ModuleOrUniformRoot::Module(module))),
use_span_with_attributes: item.span_with_attributes(),
has_attributes: !item.attrs.is_empty(),
use_span: item.span,
@ -1196,7 +1197,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
/// directly into its parent scope's module.
fn visit_invoc_in_module(&mut self, id: NodeId) -> MacroRulesScopeRef<'ra> {
let invoc_id = self.visit_invoc(id);
self.parent_scope.module.unexpanded_invocations.borrow_mut().insert(invoc_id);
self.parent_scope.module.unexpanded_invocations.borrow_mut(self.r).insert(invoc_id);
self.r.arenas.alloc_macro_rules_scope(MacroRulesScope::Invocation(invoc_id))
}
@ -1274,7 +1275,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
kind: ImportKind::MacroExport,
root_id: item.id,
parent_scope: self.parent_scope,
imported_module: Cell::new(None),
imported_module: CmCell::new(None),
has_attributes: false,
use_span_with_attributes: span,
use_span: span,

View File

@ -1,6 +1,5 @@
//! A bunch of methods and structures more or less related to resolving imports.
use std::cell::Cell;
use std::mem;
use rustc_ast::NodeId;
@ -32,6 +31,7 @@ use crate::errors::{
CannotBeReexportedPrivateNS, CannotDetermineImportResolution, CannotGlobImportAllCrates,
ConsiderAddingMacroExport, ConsiderMarkingAsPub, ConsiderMarkingAsPubCrate,
};
use crate::ref_mut::CmCell;
use crate::{
AmbiguityError, AmbiguityKind, BindingKey, CmResolver, Determinacy, Finalize, ImportSuggestion,
Module, ModuleOrUniformRoot, NameBinding, NameBindingData, NameBindingKind, ParentScope,
@ -68,7 +68,7 @@ pub(crate) enum ImportKind<'ra> {
/// It will directly use `source` when the format is `use prefix::source`.
target: Ident,
/// Bindings introduced by the import.
bindings: PerNS<Cell<PendingBinding<'ra>>>,
bindings: PerNS<CmCell<PendingBinding<'ra>>>,
/// `true` for `...::{self [as target]}` imports, `false` otherwise.
type_ns_only: bool,
/// Did this import result from a nested import? ie. `use foo::{bar, baz};`
@ -89,7 +89,7 @@ pub(crate) enum ImportKind<'ra> {
Glob {
// The visibility of the greatest re-export.
// n.b. `max_vis` is only used in `finalize_import` to check for re-export errors.
max_vis: Cell<Option<Visibility>>,
max_vis: CmCell<Option<Visibility>>,
id: NodeId,
},
ExternCrate {
@ -182,7 +182,7 @@ pub(crate) struct ImportData<'ra> {
/// |`use ::foo` | `ModuleOrUniformRoot::ExternPrelude` | 2018+ editions |
/// |`use ::foo` | `ModuleOrUniformRoot::ModuleAndExternPrelude` | a special case in 2015 edition |
/// |`use foo` | `ModuleOrUniformRoot::CurrentScope` | - |
pub imported_module: Cell<Option<ModuleOrUniformRoot<'ra>>>,
pub imported_module: CmCell<Option<ModuleOrUniformRoot<'ra>>>,
pub vis: Visibility,
/// Span of the visibility.
@ -320,7 +320,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
&& (vis == import_vis
|| max_vis.get().is_none_or(|max_vis| vis.is_at_least(max_vis, self.tcx)))
{
max_vis.set(Some(vis.expect_local()))
// FIXME(batched): Will be fixed in batched import resolution.
max_vis.set_unchecked(Some(vis.expect_local()))
}
self.arenas.alloc_name_binding(NameBindingData {
@ -349,7 +350,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
// because they can be fetched by glob imports from those modules, and bring traits
// into scope both directly and through glob imports.
let key = BindingKey::new_disambiguated(ident, ns, || {
module.underscore_disambiguator.update(|d| d + 1);
// FIXME(batched): Will be fixed in batched resolution.
module.underscore_disambiguator.update_unchecked(|d| d + 1);
module.underscore_disambiguator.get()
});
self.update_local_resolution(module, key, warn_ambiguity, |this, resolution| {
@ -482,7 +484,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
}
};
let Ok(glob_importers) = module.glob_importers.try_borrow_mut() else {
let Ok(glob_importers) = module.glob_importers.try_borrow_mut_unchecked() else {
return t;
};
@ -862,7 +864,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
}
};
import.imported_module.set(Some(module));
// FIXME(batched): Will be fixed in batched import resolution.
import.imported_module.set_unchecked(Some(module));
let (source, target, bindings, type_ns_only) = match import.kind {
ImportKind::Single { source, target, ref bindings, type_ns_only, .. } => {
(source, target, bindings, type_ns_only)
@ -937,7 +940,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
PendingBinding::Pending
}
};
bindings[ns].set(binding);
// FIXME(batched): Will be fixed in batched import resolution.
bindings[ns].set_unchecked(binding);
}
});
@ -1508,7 +1512,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
}
// Add to module's glob_importers
module.glob_importers.borrow_mut().push(import);
module.glob_importers.borrow_mut_unchecked().push(import);
// Ensure that `resolutions` isn't borrowed during `try_define`,
// since it might get updated via a glob cycle.
@ -1550,7 +1554,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
// reporting conflicts, and reporting unresolved imports.
fn finalize_resolutions_in(&mut self, module: Module<'ra>) {
// Since import resolution is finished, globs will not define any more names.
*module.globs.borrow_mut() = Vec::new();
*module.globs.borrow_mut(self) = Vec::new();
let Some(def_id) = module.opt_def_id() else { return };

View File

@ -19,6 +19,7 @@
#![feature(default_field_values)]
#![feature(if_let_guard)]
#![feature(iter_intersperse)]
#![feature(ptr_as_ref_unchecked)]
#![feature(rustc_attrs)]
#![feature(rustdoc_internals)]
#![recursion_limit = "256"]
@ -26,7 +27,7 @@
use std::cell::{Cell, Ref, RefCell};
use std::collections::BTreeSet;
use std::fmt;
use std::fmt::{self};
use std::sync::Arc;
use diagnostics::{ImportSuggestion, LabelSuggestion, Suggestion};
@ -95,6 +96,8 @@ pub mod rustdoc;
pub use macros::registered_tools_ast;
use crate::ref_mut::{CmCell, CmRefCell};
rustc_fluent_macro::fluent_messages! { "../messages.ftl" }
#[derive(Debug)]
@ -592,22 +595,22 @@ struct ModuleData<'ra> {
/// Resolutions in modules from other crates are not populated until accessed.
lazy_resolutions: Resolutions<'ra>,
/// True if this is a module from other crate that needs to be populated on access.
populate_on_access: Cell<bool>,
populate_on_access: Cell<bool>, // FIXME(parallel): Use an atomic in parallel import resolution
/// Used to disambiguate underscore items (`const _: T = ...`) in the module.
underscore_disambiguator: Cell<u32>,
underscore_disambiguator: CmCell<u32>,
/// Macro invocations that can expand into items in this module.
unexpanded_invocations: RefCell<FxHashSet<LocalExpnId>>,
unexpanded_invocations: CmRefCell<FxHashSet<LocalExpnId>>,
/// Whether `#[no_implicit_prelude]` is active.
no_implicit_prelude: bool,
glob_importers: RefCell<Vec<Import<'ra>>>,
globs: RefCell<Vec<Import<'ra>>>,
glob_importers: CmRefCell<Vec<Import<'ra>>>,
globs: CmRefCell<Vec<Import<'ra>>>,
/// Used to memoize the traits in this module for faster searches through all traits in scope.
traits:
RefCell<Option<Box<[(Macros20NormalizedIdent, NameBinding<'ra>, Option<Module<'ra>>)]>>>,
CmRefCell<Option<Box<[(Macros20NormalizedIdent, NameBinding<'ra>, Option<Module<'ra>>)]>>>,
/// Span of the module itself. Used for error reporting.
span: Span,
@ -656,12 +659,12 @@ impl<'ra> ModuleData<'ra> {
kind,
lazy_resolutions: Default::default(),
populate_on_access: Cell::new(is_foreign),
underscore_disambiguator: Cell::new(0),
underscore_disambiguator: CmCell::new(0),
unexpanded_invocations: Default::default(),
no_implicit_prelude,
glob_importers: RefCell::new(Vec::new()),
globs: RefCell::new(Vec::new()),
traits: RefCell::new(None),
glob_importers: CmRefCell::new(Vec::new()),
globs: CmRefCell::new(Vec::new()),
traits: CmRefCell::new(None),
span,
expansion,
self_binding,
@ -696,7 +699,7 @@ impl<'ra> Module<'ra> {
/// This modifies `self` in place. The traits will be stored in `self.traits`.
fn ensure_traits<'tcx>(self, resolver: &impl AsRef<Resolver<'ra, 'tcx>>) {
let mut traits = self.traits.borrow_mut();
let mut traits = self.traits.borrow_mut(resolver.as_ref());
if traits.is_none() {
let mut collected_traits = Vec::new();
self.for_each_child(resolver, |r, name, ns, binding| {
@ -1974,6 +1977,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
fn resolutions(&self, module: Module<'ra>) -> &'ra Resolutions<'ra> {
if module.populate_on_access.get() {
// FIXME(batched): Will be fixed in batched import resolution.
module.populate_on_access.set(false);
self.build_reduced_graph_external(module);
}
@ -2504,9 +2508,20 @@ pub fn provide(providers: &mut Providers) {
providers.registered_tools = macros::registered_tools;
}
/// A wrapper around `&mut Resolver` that may be mutable or immutable, depending on a conditions.
///
/// `Cm` stands for "conditionally mutable".
///
/// Prefer constructing it through [`Resolver::cm`] to ensure correctness.
type CmResolver<'r, 'ra, 'tcx> = ref_mut::RefOrMut<'r, Resolver<'ra, 'tcx>>;
mod ref_mut {
use std::cell::{BorrowMutError, Cell, Ref, RefCell, RefMut};
use std::fmt;
use std::ops::Deref;
use crate::Resolver;
/// A wrapper around a mutable reference that conditionally allows mutable access.
pub(crate) struct RefOrMut<'a, T> {
p: &'a mut T,
@ -2555,11 +2570,86 @@ mod ref_mut {
self.p
}
}
}
/// A wrapper around `&mut Resolver` that may be mutable or immutable, depending on a conditions.
///
/// `Cm` stands for "conditionally mutable".
///
/// Prefer constructing it through [`Resolver::cm`] to ensure correctness.
type CmResolver<'r, 'ra, 'tcx> = ref_mut::RefOrMut<'r, Resolver<'ra, 'tcx>>;
/// A wrapper around a [`Cell`] that only allows mutation based on a condition in the resolver.
#[derive(Default)]
pub(crate) struct CmCell<T>(Cell<T>);
impl<T: Copy + fmt::Debug> fmt::Debug for CmCell<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("CmCell").field(&self.get()).finish()
}
}
impl<T: Copy> Clone for CmCell<T> {
#[inline]
fn clone(&self) -> CmCell<T> {
CmCell::new(self.get())
}
}
impl<T: Copy> CmCell<T> {
pub(crate) const fn get(&self) -> T {
self.0.get()
}
pub(crate) fn update_unchecked(&self, f: impl FnOnce(T) -> T)
where
T: Copy,
{
let old = self.get();
self.set_unchecked(f(old));
}
}
impl<T> CmCell<T> {
pub(crate) const fn new(value: T) -> CmCell<T> {
CmCell(Cell::new(value))
}
pub(crate) fn set_unchecked(&self, val: T) {
self.0.set(val);
}
pub(crate) fn into_inner(self) -> T {
self.0.into_inner()
}
}
/// A wrapper around a [`RefCell`] that only allows mutable borrows based on a condition in the resolver.
#[derive(Default)]
pub(crate) struct CmRefCell<T>(RefCell<T>);
impl<T> CmRefCell<T> {
pub(crate) const fn new(value: T) -> CmRefCell<T> {
CmRefCell(RefCell::new(value))
}
#[inline]
#[track_caller]
pub(crate) fn borrow_mut_unchecked(&self) -> RefMut<'_, T> {
self.0.borrow_mut()
}
#[inline]
#[track_caller]
pub(crate) fn borrow_mut<'ra, 'tcx>(&self, r: &Resolver<'ra, 'tcx>) -> RefMut<'_, T> {
if r.assert_speculative {
panic!("Not allowed to mutably borrow a CmRefCell during speculative resolution");
}
self.borrow_mut_unchecked()
}
#[inline]
#[track_caller]
pub(crate) fn try_borrow_mut_unchecked(&self) -> Result<RefMut<'_, T>, BorrowMutError> {
self.0.try_borrow_mut()
}
#[inline]
#[track_caller]
pub(crate) fn borrow(&self) -> Ref<'_, T> {
self.0.borrow()
}
}
}

View File

@ -189,7 +189,7 @@ impl<'ra, 'tcx> ResolverExpand for Resolver<'ra, 'tcx> {
let output_macro_rules_scope = self.build_reduced_graph(fragment, parent_scope);
self.output_macro_rules_scopes.insert(expansion, output_macro_rules_scope);
parent_scope.module.unexpanded_invocations.borrow_mut().remove(&expansion);
parent_scope.module.unexpanded_invocations.borrow_mut(self).remove(&expansion);
if let Some(unexpanded_invocations) =
self.impl_unexpanded_invocations.get_mut(&self.invocation_parent(expansion))
{

View File

@ -262,15 +262,16 @@ impl<'tcx> V0SymbolMangler<'tcx> {
fn print_pat(&mut self, pat: ty::Pattern<'tcx>) -> Result<(), std::fmt::Error> {
Ok(match *pat {
ty::PatternKind::Range { start, end } => {
let consts = [start, end];
for ct in consts {
Ty::new_array_with_const_len(self.tcx, self.tcx.types.unit, ct).print(self)?;
}
self.push("R");
self.print_const(start)?;
self.print_const(end)?;
}
ty::PatternKind::Or(patterns) => {
self.push("O");
for pat in patterns {
self.print_pat(pat)?;
}
self.push("E");
}
})
}
@ -498,12 +499,9 @@ impl<'tcx> Printer<'tcx> for V0SymbolMangler<'tcx> {
}
ty::Pat(ty, pat) => {
// HACK: Represent as tuple until we have something better.
// HACK: constants are used in arrays, even if the types don't match.
self.push("T");
self.push("W");
ty.print(self)?;
self.print_pat(pat)?;
self.push("E");
}
ty::Array(ty, len) => {

View File

@ -9,7 +9,7 @@ use rustc_middle::infer::canonical::{
Canonical, CanonicalQueryInput, CanonicalQueryResponse, QueryResponse,
};
use rustc_middle::traits::query::NoSolution;
use rustc_middle::ty::{self, GenericArg, Ty, TyCtxt, TypeFoldable, TypeVisitableExt, Upcast};
use rustc_middle::ty::{self, GenericArg, Ty, TyCtxt, TypeFoldable, Upcast};
use rustc_span::DUMMY_SP;
use tracing::instrument;
@ -31,19 +31,7 @@ impl<'tcx> InferCtxt<'tcx> {
fn type_is_copy_modulo_regions(&self, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>) -> bool {
let ty = self.resolve_vars_if_possible(ty);
// FIXME(#132279): This should be removed as it causes us to incorrectly
// handle opaques in their defining scope, and stalled coroutines.
if !self.next_trait_solver() && !(param_env, ty).has_infer() && !ty.has_coroutines() {
return self.tcx.type_is_copy_modulo_regions(self.typing_env(param_env), ty);
}
let copy_def_id = self.tcx.require_lang_item(LangItem::Copy, DUMMY_SP);
// This can get called from typeck (by euv), and `moves_by_default`
// rightly refuses to work with inference variables, but
// moves_by_default has a cache, which we want to use in other
// cases.
traits::type_known_to_meet_bound_modulo_regions(self, param_env, ty, copy_def_id)
}

View File

@ -194,6 +194,9 @@ pub struct BTreeMap<
root: Option<Root<K, V>>,
length: usize,
/// `ManuallyDrop` to control drop order (needs to be dropped after all the nodes).
// Although some of the accessory types store a copy of the allocator, the nodes do not.
// Because allocations will remain live as long as any copy (like this one) of the allocator
// is live, it's unnecessary to store the allocator in each node.
pub(super) alloc: ManuallyDrop<A>,
// For dropck; the `Box` avoids making the `Unpin` impl more strict than before
_marker: PhantomData<crate::boxed::Box<(K, V), A>>,

View File

@ -225,7 +225,11 @@ impl<K, V> NodeRef<marker::Owned, K, V, marker::Leaf> {
}
fn from_new_leaf<A: Allocator + Clone>(leaf: Box<LeafNode<K, V>, A>) -> Self {
NodeRef { height: 0, node: NonNull::from(Box::leak(leaf)), _marker: PhantomData }
// The allocator must be dropped, not leaked. See also `BTreeMap::alloc`.
let (leaf, _alloc) = Box::into_raw_with_allocator(leaf);
// SAFETY: the node was just allocated.
let node = unsafe { NonNull::new_unchecked(leaf) };
NodeRef { height: 0, node, _marker: PhantomData }
}
}
@ -243,7 +247,11 @@ impl<K, V> NodeRef<marker::Owned, K, V, marker::Internal> {
height: usize,
) -> Self {
debug_assert!(height > 0);
let node = NonNull::from(Box::leak(internal)).cast();
// The allocator must be dropped, not leaked. See also `BTreeMap::alloc`.
let (internal, _alloc) = Box::into_raw_with_allocator(internal);
// SAFETY: the node was just allocated.
let internal = unsafe { NonNull::new_unchecked(internal) };
let node = internal.cast();
let mut this = NodeRef { height, node, _marker: PhantomData };
this.borrow_mut().correct_all_childrens_parent_links();
this

View File

@ -64,27 +64,37 @@ where
type Item = T;
fn next(&mut self) -> Option<T> {
unsafe {
while self.idx < self.end {
let i = self.idx;
let v = slice::from_raw_parts_mut(self.vec.as_mut_ptr(), self.old_len);
let drained = (self.pred)(&mut v[i]);
// Update the index *after* the predicate is called. If the index
// is updated prior and the predicate panics, the element at this
// index would be leaked.
self.idx += 1;
if drained {
self.del += 1;
return Some(ptr::read(&v[i]));
} else if self.del > 0 {
let del = self.del;
let src: *const T = &v[i];
let dst: *mut T = &mut v[i - del];
ptr::copy_nonoverlapping(src, dst, 1);
while self.idx < self.end {
let i = self.idx;
// SAFETY:
// We know that `i < self.end` from the if guard and that `self.end <= self.old_len` from
// the validity of `Self`. Therefore `i` points to an element within `vec`.
//
// Additionally, the i-th element is valid because each element is visited at most once
// and it is the first time we access vec[i].
//
// Note: we can't use `vec.get_unchecked_mut(i)` here since the precondition for that
// function is that i < vec.len(), but we've set vec's length to zero.
let cur = unsafe { &mut *self.vec.as_mut_ptr().add(i) };
let drained = (self.pred)(cur);
// Update the index *after* the predicate is called. If the index
// is updated prior and the predicate panics, the element at this
// index would be leaked.
self.idx += 1;
if drained {
self.del += 1;
// SAFETY: We never touch this element again after returning it.
return Some(unsafe { ptr::read(cur) });
} else if self.del > 0 {
// SAFETY: `self.del` > 0, so the hole slot must not overlap with current element.
// We use copy for move, and never touch this element again.
unsafe {
let hole_slot = self.vec.as_mut_ptr().add(i - self.del);
ptr::copy_nonoverlapping(cur, hole_slot, 1);
}
}
None
}
None
}
fn size_hint(&self) -> (usize, Option<usize>) {
@ -95,14 +105,18 @@ where
#[stable(feature = "extract_if", since = "1.87.0")]
impl<T, F, A: Allocator> Drop for ExtractIf<'_, T, F, A> {
fn drop(&mut self) {
unsafe {
if self.idx < self.old_len && self.del > 0 {
let ptr = self.vec.as_mut_ptr();
let src = ptr.add(self.idx);
let dst = src.sub(self.del);
let tail_len = self.old_len - self.idx;
src.copy_to(dst, tail_len);
if self.del > 0 {
// SAFETY: Trailing unchecked items must be valid since we never touch them.
unsafe {
ptr::copy(
self.vec.as_ptr().add(self.idx),
self.vec.as_mut_ptr().add(self.idx - self.del),
self.old_len - self.idx,
);
}
}
// SAFETY: After filling holes, all items are in contiguous memory.
unsafe {
self.vec.set_len(self.old_len - self.del);
}
}

View File

@ -2173,9 +2173,37 @@ impl<T, A: Allocator> Vec<T, A> {
panic!("removal index (is {index}) should be < len (is {len})");
}
match self.try_remove(index) {
Some(elem) => elem,
None => assert_failed(index, self.len()),
}
}
/// Remove and return the element at position `index` within the vector,
/// shifting all elements after it to the left, or [`None`] if it does not
/// exist.
///
/// Note: Because this shifts over the remaining elements, it has a
/// worst-case performance of *O*(*n*). If you'd like to remove
/// elements from the beginning of the `Vec`, consider using
/// [`VecDeque::pop_front`] instead.
///
/// [`VecDeque::pop_front`]: crate::collections::VecDeque::pop_front
///
/// # Examples
///
/// ```
/// #![feature(vec_try_remove)]
/// let mut v = vec![1, 2, 3];
/// assert_eq!(v.try_remove(0), Some(1));
/// assert_eq!(v.try_remove(2), None);
/// ```
#[unstable(feature = "vec_try_remove", issue = "146954")]
#[rustc_confusables("delete", "take", "remove")]
pub fn try_remove(&mut self, index: usize) -> Option<T> {
let len = self.len();
if index >= len {
assert_failed(index, len);
return None;
}
unsafe {
// infallible
@ -2191,7 +2219,7 @@ impl<T, A: Allocator> Vec<T, A> {
ptr::copy(ptr.add(1), ptr, len - index - 1);
}
self.set_len(len - 1);
ret
Some(ret)
}
}

View File

@ -41,6 +41,7 @@
#![feature(unique_rc_arc)]
#![feature(macro_metavar_expr_concat)]
#![feature(vec_peek_mut)]
#![feature(vec_try_remove)]
#![allow(internal_features)]
#![deny(fuzzy_provenance_casts)]
#![deny(unsafe_op_in_unsafe_fn)]

View File

@ -630,6 +630,21 @@ fn test_swap_remove_empty() {
vec.swap_remove(0);
}
#[test]
fn test_try_remove() {
let mut vec = vec![1, 2, 3];
// We are attempting to remove vec[0] which contains 1
assert_eq!(vec.try_remove(0), Some(1));
// Now `vec` looks like: [2, 3]
// We will now try to remove vec[2] which does not exist
// This should return `None`
assert_eq!(vec.try_remove(2), None);
// We will try the same thing with an empty vector
let mut v: Vec<u8> = vec![];
assert!(v.try_remove(0).is_none());
}
#[test]
fn test_move_items() {
let vec = vec![1, 2, 3];

View File

@ -548,6 +548,18 @@ macro_rules! nonzero_integer {
#[doc = concat!("assert_eq!(align_of::<", stringify!($Ty), ">(), align_of::<Option<", stringify!($Ty), ">>());")]
/// ```
///
/// # Compile-time creation
///
/// Since both [`Option::unwrap()`] and [`Option::expect()`] are `const`, it is possible to
/// define a new
#[doc = concat!("`", stringify!($Ty), "`")]
/// at compile time via:
/// ```
#[doc = concat!("use std::num::", stringify!($Ty), ";")]
///
#[doc = concat!("const TEN: ", stringify!($Ty), " = ", stringify!($Ty) , r#"::new(10).expect("ten is non-zero");"#)]
/// ```
///
/// [null pointer optimization]: crate::option#representation
#[$stability]
pub type $Ty = NonZero<$Int>;

View File

@ -13,7 +13,7 @@ impl Stdin {
}
impl io::Read for Stdin {
fn read(&mut self, mut buf: &mut [u8]) -> io::Result<usize> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
let mut count = 0;
for out_byte in buf.iter_mut() {

View File

@ -564,6 +564,7 @@ impl Step for CI {
"clippy::same_item_push".into(),
"clippy::single_char_add_str".into(),
"clippy::to_string_in_format_args".into(),
"clippy::unconditional_recursion".into(),
],
forbid: vec![],
};
@ -591,6 +592,7 @@ impl Step for CI {
"clippy::same_item_push".into(),
"clippy::single_char_add_str".into(),
"clippy::to_string_in_format_args".into(),
"clippy::unconditional_recursion".into(),
],
forbid: vec![],
};

View File

@ -1832,8 +1832,9 @@ impl Step for Sysroot {
let sysroot = sysroot_dir(compiler.stage);
trace!(stage = ?compiler.stage, ?sysroot);
builder
.verbose(|| println!("Removing sysroot {} to avoid caching bugs", sysroot.display()));
builder.do_if_verbose(|| {
println!("Removing sysroot {} to avoid caching bugs", sysroot.display())
});
let _ = fs::remove_dir_all(&sysroot);
t!(fs::create_dir_all(&sysroot));
@ -1902,12 +1903,7 @@ impl Step for Sysroot {
if !path.parent().is_none_or(|p| p.ends_with(&suffix)) {
return true;
}
if !filtered_files.iter().all(|f| f != path.file_name().unwrap()) {
builder.verbose_than(1, || println!("ignoring {}", path.display()));
false
} else {
true
}
filtered_files.iter().all(|f| f != path.file_name().unwrap())
});
}
@ -2596,7 +2592,7 @@ pub fn stream_cargo(
cmd.arg(arg);
}
builder.verbose(|| println!("running: {cmd:?}"));
builder.do_if_verbose(|| println!("running: {cmd:?}"));
let streaming_command = cmd.stream_capture_stdout(&builder.config.exec_ctx);

View File

@ -2304,7 +2304,7 @@ fn maybe_install_llvm(
let mut cmd = command(host_llvm_config);
cmd.cached();
cmd.arg("--libfiles");
builder.verbose(|| println!("running {cmd:?}"));
builder.do_if_verbose(|| println!("running {cmd:?}"));
let files = cmd.run_capture_stdout(builder).stdout();
let build_llvm_out = &builder.llvm_out(builder.config.host_target);
let target_llvm_out = &builder.llvm_out(target);

View File

@ -128,7 +128,7 @@ fn try_download_gcc(builder: &Builder<'_>, target: TargetSelection) -> Option<Pa
&builder.config,
builder.config.rust_info.is_managed_git_subrepository(),
);
builder.verbose(|| {
builder.do_if_verbose(|| {
eprintln!("GCC freshness: {source:?}");
});
match source {

View File

@ -582,11 +582,11 @@ impl Miri {
// We re-use the `cargo` from above.
cargo.arg("--print-sysroot");
builder.verbose(|| println!("running: {cargo:?}"));
builder.do_if_verbose(|| println!("running: {cargo:?}"));
let stdout = cargo.run_capture_stdout(builder).stdout();
// Output is "<sysroot>\n".
let sysroot = stdout.trim_end();
builder.verbose(|| println!("`cargo miri setup --print-sysroot` said: {sysroot:?}"));
builder.do_if_verbose(|| println!("`cargo miri setup --print-sysroot` said: {sysroot:?}"));
PathBuf::from(sysroot)
}
}
@ -2675,7 +2675,7 @@ fn markdown_test(builder: &Builder<'_>, compiler: Compiler, markdown: &Path) ->
return true;
}
builder.verbose(|| println!("doc tests for: {}", markdown.display()));
builder.do_if_verbose(|| println!("doc tests for: {}", markdown.display()));
let mut cmd = builder.rustdoc_cmd(compiler);
builder.add_rust_test_threads(&mut cmd);
// allow for unstable options such as new editions

View File

@ -1139,7 +1139,7 @@ impl Builder<'_> {
cargo.env("RUSTC_BACKTRACE_ON_ICE", "1");
}
if self.is_verbose_than(1) {
if self.verbosity >= 2 {
// This provides very useful logs especially when debugging build cache-related stuff.
cargo.env("CARGO_LOG", "cargo::core::compiler::fingerprint=info");
}

View File

@ -545,7 +545,7 @@ impl StepDescription {
if !builder.config.skip.is_empty()
&& !matches!(builder.config.get_dry_run(), DryRun::SelfCheck)
{
builder.verbose(|| {
builder.do_if_verbose(|| {
println!(
"{:?} not skipped for {:?} -- not in {:?}",
pathset, self.name, builder.config.skip
@ -947,7 +947,7 @@ impl Step for Libdir {
// Sysroot`).
if !builder.download_rustc() {
let sysroot_target_libdir = sysroot.join(self.target).join("lib");
builder.verbose(|| {
builder.do_if_verbose(|| {
eprintln!(
"Removing sysroot {} to avoid caching bugs",
sysroot_target_libdir.display()

View File

@ -1571,8 +1571,8 @@ impl Config {
}
/// Runs a function if verbosity is greater than 0
pub fn verbose(&self, f: impl Fn()) {
self.exec_ctx.verbose(f);
pub fn do_if_verbose(&self, f: impl Fn()) {
self.exec_ctx.do_if_verbose(f);
}
pub fn any_sanitizers_to_build(&self) -> bool {
@ -2061,7 +2061,7 @@ pub fn download_ci_rustc_commit<'a>(
// Look for a version to compare to based on the current commit.
// Only commits merged by bors will have CI artifacts.
let freshness = check_path_modifications_(dwn_ctx, RUSTC_IF_UNCHANGED_ALLOWED_PATHS);
dwn_ctx.exec_ctx.verbose(|| {
dwn_ctx.exec_ctx.do_if_verbose(|| {
eprintln!("rustc freshness: {freshness:?}");
});
match freshness {

View File

@ -106,7 +106,7 @@ enum DownloadSource {
/// Functions that are only ever called once, but named for clarity and to avoid thousand-line functions.
impl Config {
pub(crate) fn download_clippy(&self) -> PathBuf {
self.verbose(|| println!("downloading stage0 clippy artifacts"));
self.do_if_verbose(|| println!("downloading stage0 clippy artifacts"));
let date = &self.stage0_metadata.compiler.date;
let version = &self.stage0_metadata.compiler.version;
@ -151,7 +151,9 @@ impl Config {
}
pub(crate) fn download_ci_rustc(&self, commit: &str) {
self.verbose(|| println!("using downloaded stage2 artifacts from CI (commit {commit})"));
self.do_if_verbose(|| {
println!("using downloaded stage2 artifacts from CI (commit {commit})")
});
let version = self.artifact_version_part(commit);
// download-rustc doesn't need its own cargo, it can just use beta's. But it does need the
@ -258,7 +260,7 @@ impl Config {
let llvm_root = self.ci_llvm_root();
let llvm_freshness =
detect_llvm_freshness(self, self.rust_info.is_managed_git_subrepository());
self.verbose(|| {
self.do_if_verbose(|| {
eprintln!("LLVM freshness: {llvm_freshness:?}");
});
let llvm_sha = match llvm_freshness {
@ -557,7 +559,7 @@ pub(crate) fn download_beta_toolchain<'a>(dwn_ctx: impl AsRef<DownloadContext<'a
#[cfg(not(test))]
pub(crate) fn download_beta_toolchain<'a>(dwn_ctx: impl AsRef<DownloadContext<'a>>, out: &Path) {
let dwn_ctx = dwn_ctx.as_ref();
dwn_ctx.exec_ctx.verbose(|| {
dwn_ctx.exec_ctx.do_if_verbose(|| {
println!("downloading stage0 beta artifacts");
});
@ -812,7 +814,7 @@ fn download_component<'a>(
unpack(dwn_ctx.exec_ctx, &tarball, &bin_root, prefix);
return;
} else {
dwn_ctx.exec_ctx.verbose(|| {
dwn_ctx.exec_ctx.do_if_verbose(|| {
println!(
"ignoring cached file {} due to failed verification",
tarball.display()
@ -853,7 +855,7 @@ download-rustc = false
pub(crate) fn verify(exec_ctx: &ExecutionContext, path: &Path, expected: &str) -> bool {
use sha2::Digest;
exec_ctx.verbose(|| {
exec_ctx.do_if_verbose(|| {
println!("verifying {}", path.display());
});
@ -934,7 +936,7 @@ fn unpack(exec_ctx: &ExecutionContext, tarball: &Path, dst: &Path, pattern: &str
short_path = short_path.strip_prefix(pattern).unwrap_or(short_path);
let dst_path = dst.join(short_path);
exec_ctx.verbose(|| {
exec_ctx.do_if_verbose(|| {
println!("extracting {} to {}", original_path.display(), dst.display());
});
@ -965,7 +967,7 @@ fn download_file<'a>(
) {
let dwn_ctx = dwn_ctx.as_ref();
dwn_ctx.exec_ctx.verbose(|| {
dwn_ctx.exec_ctx.do_if_verbose(|| {
println!("download {url}");
});
// Use a temporary file in case we crash while downloading, to avoid a corrupt download in cache/.

View File

@ -415,7 +415,7 @@ macro_rules! forward {
}
forward! {
verbose(f: impl Fn()),
do_if_verbose(f: impl Fn()),
is_verbose() -> bool,
create(path: &Path, s: &str),
remove(f: &Path),
@ -601,11 +601,11 @@ impl Build {
.unwrap()
.trim();
if local_release.split('.').take(2).eq(version.split('.').take(2)) {
build.verbose(|| println!("auto-detected local-rebuild {local_release}"));
build.do_if_verbose(|| println!("auto-detected local-rebuild {local_release}"));
build.local_rebuild = true;
}
build.verbose(|| println!("finding compilers"));
build.do_if_verbose(|| println!("finding compilers"));
utils::cc_detect::fill_compilers(&mut build);
// When running `setup`, the profile is about to change, so any requirements we have now may
// be different on the next invocation. Don't check for them until the next time x.py is
@ -613,7 +613,7 @@ impl Build {
//
// Similarly, for `setup` we don't actually need submodules or cargo metadata.
if !matches!(build.config.cmd, Subcommand::Setup { .. }) {
build.verbose(|| println!("running sanity check"));
build.do_if_verbose(|| println!("running sanity check"));
crate::core::sanity::check(&mut build);
// Make sure we update these before gathering metadata so we don't get an error about missing
@ -631,7 +631,7 @@ impl Build {
// Now, update all existing submodules.
build.update_existing_submodules();
build.verbose(|| println!("learning about cargo"));
build.do_if_verbose(|| println!("learning about cargo"));
crate::core::metadata::build(&mut build);
}
@ -1087,18 +1087,6 @@ impl Build {
})
}
/// Check if verbosity is greater than the `level`
pub fn is_verbose_than(&self, level: usize) -> bool {
self.verbosity > level
}
/// Runs a function if verbosity is greater than `level`.
fn verbose_than(&self, level: usize, f: impl Fn()) {
if self.is_verbose_than(level) {
f()
}
}
fn info(&self, msg: &str) {
match self.config.get_dry_run() {
DryRun::SelfCheck => (),
@ -1816,7 +1804,6 @@ impl Build {
if self.config.dry_run() {
return;
}
self.verbose_than(1, || println!("Copy/Link {src:?} to {dst:?}"));
if src == dst {
return;
}
@ -1933,7 +1920,10 @@ impl Build {
return;
}
let dst = dstdir.join(src.file_name().unwrap());
self.verbose_than(1, || println!("Install {src:?} to {dst:?}"));
#[cfg(feature = "tracing")]
let _span = trace_io!("install", ?src, ?dst);
t!(fs::create_dir_all(dstdir));
if !src.exists() {
panic!("ERROR: File \"{}\" not found!", src.display());

View File

@ -112,7 +112,7 @@ pub fn clear_if_dirty(builder: &Builder<'_>, dir: &Path, input: &Path) -> bool {
let stamp = BuildStamp::new(dir);
let mut cleared = false;
if mtime(stamp.path()) < mtime(input) {
builder.verbose(|| println!("Dirty - {}", dir.display()));
builder.do_if_verbose(|| println!("Dirty - {}", dir.display()));
let _ = fs::remove_dir_all(dir);
cleared = true;
} else if stamp.path().exists() {

View File

@ -137,16 +137,16 @@ pub fn fill_target_compiler(build: &mut Build, target: TargetSelection) {
build.cxx.insert(target, compiler);
}
build.verbose(|| println!("CC_{} = {:?}", target.triple, build.cc(target)));
build.verbose(|| println!("CFLAGS_{} = {cflags:?}", target.triple));
build.do_if_verbose(|| println!("CC_{} = {:?}", target.triple, build.cc(target)));
build.do_if_verbose(|| println!("CFLAGS_{} = {cflags:?}", target.triple));
if let Ok(cxx) = build.cxx(target) {
let mut cxxflags = build.cc_handled_clags(target, CLang::Cxx);
cxxflags.extend(build.cc_unhandled_cflags(target, GitRepo::Rustc, CLang::Cxx));
build.verbose(|| println!("CXX_{} = {cxx:?}", target.triple));
build.verbose(|| println!("CXXFLAGS_{} = {cxxflags:?}", target.triple));
build.do_if_verbose(|| println!("CXX_{} = {cxx:?}", target.triple));
build.do_if_verbose(|| println!("CXXFLAGS_{} = {cxxflags:?}", target.triple));
}
if let Some(ar) = ar {
build.verbose(|| println!("AR_{} = {ar:?}", target.triple));
build.do_if_verbose(|| println!("AR_{} = {ar:?}", target.triple));
build.ar.insert(target, ar);
}

View File

@ -630,7 +630,7 @@ impl ExecutionContext {
&self.dry_run
}
pub fn verbose(&self, f: impl Fn()) {
pub fn do_if_verbose(&self, f: impl Fn()) {
if self.is_verbose() {
f()
}
@ -686,7 +686,7 @@ impl ExecutionContext {
if let Some(cached_output) = self.command_cache.get(&fingerprint) {
command.mark_as_executed();
self.verbose(|| println!("Cache hit: {command:?}"));
self.do_if_verbose(|| println!("Cache hit: {command:?}"));
self.profiler.record_cache_hit(fingerprint);
return DeferredCommand { state: CommandState::Cached(cached_output) };
}
@ -713,7 +713,7 @@ impl ExecutionContext {
};
}
self.verbose(|| {
self.do_if_verbose(|| {
println!("running: {command:?} (created at {created_at}, executed at {executed_at})")
});

View File

@ -48,7 +48,7 @@ pub(crate) fn try_run_tests(
}
fn run_tests(builder: &Builder<'_>, cmd: &mut BootstrapCommand, stream: bool) -> bool {
builder.verbose(|| println!("running: {cmd:?}"));
builder.do_if_verbose(|| println!("running: {cmd:?}"));
let Some(mut streaming_command) = cmd.stream_capture_stdout(&builder.config.exec_ctx) else {
return true;

View File

@ -356,7 +356,7 @@ impl<'a> Tarball<'a> {
// For `x install` tarball files aren't needed, so we can speed up the process by not producing them.
let compression_profile = if self.builder.kind == Kind::Install {
self.builder.verbose(|| {
self.builder.do_if_verbose(|| {
println!("Forcing dist.compression-profile = 'no-op' for `x install`.")
});
// "no-op" indicates that the rust-installer won't produce compressed tarball sources.

View File

@ -52,7 +52,6 @@
- [aarch64_be-unknown-none-softfloat](platform-support/aarch64_be-unknown-none-softfloat.md)
- [aarch64_be-unknown-linux-musl](platform-support/aarch64_be-unknown-linux-musl.md)
- [amdgcn-amd-amdhsa](platform-support/amdgcn-amd-amdhsa.md)
- [armeb-unknown-linux-gnueabi](platform-support/armeb-unknown-linux-gnueabi.md)
- [arm-none-eabi](platform-support/arm-none-eabi.md)
- [armv4t-none-eabi](platform-support/armv4t-none-eabi.md)
- [armv5te-none-eabi](platform-support/armv5te-none-eabi.md)
@ -65,12 +64,14 @@
- [thumbv7m-none-eabi](./platform-support/thumbv7m-none-eabi.md)
- [thumbv8m.base-none-eabi](./platform-support/thumbv8m.base-none-eabi.md)
- [thumbv8m.main-none-eabi\*](./platform-support/thumbv8m.main-none-eabi.md)
- [armv5te-unknown-linux-gnueabi](platform-support/armv5te-unknown-linux-gnueabi.md)
- [arm\*-unknown-linux-\*](./platform-support/arm-linux.md)
- [armeb-unknown-linux-gnueabi](platform-support/armeb-unknown-linux-gnueabi.md)
- [armv5te-unknown-linux-gnueabi](platform-support/armv5te-unknown-linux-gnueabi.md)
- [armv7-unknown-linux-uclibceabi](platform-support/armv7-unknown-linux-uclibceabi.md)
- [armv7-unknown-linux-uclibceabihf](platform-support/armv7-unknown-linux-uclibceabihf.md)
- [armv6k-nintendo-3ds](platform-support/armv6k-nintendo-3ds.md)
- [armv7-rtems-eabihf](platform-support/armv7-rtems-eabihf.md)
- [armv7-sony-vita-newlibeabihf](platform-support/armv7-sony-vita-newlibeabihf.md)
- [armv7-unknown-linux-uclibceabi](platform-support/armv7-unknown-linux-uclibceabi.md)
- [armv7-unknown-linux-uclibceabihf](platform-support/armv7-unknown-linux-uclibceabihf.md)
- [armv7a-vex-v5](platform-support/armv7a-vex-v5.md)
- [\*-android and \*-androideabi](platform-support/android.md)
- [\*-linux-ohos](platform-support/openharmony.md)

View File

@ -0,0 +1,217 @@
# Arm Linux support in Rust
The Arm Architecture has been around since the mid-1980s, going through nine
major revisions, many minor revisions, and spanning both 32-bith and 64-bit
architectures. This page covers 32-bit Arm platforms that run some form of
Linux (but not Android). Those targets are:
* `arm-unknown-linux-gnueabi`
* `arm-unknown-linux-gnueabihf`
* `arm-unknown-linux-musleabi`
* `arm-unknown-linux-musleabihf`
* [`armeb-unknown-linux-gnueabi`](armeb-unknown-linux-gnueabi.md)
* `armv4t-unknown-linux-gnueabi`
* [`armv5te-unknown-linux-gnueabi`](armv5te-unknown-linux-gnueabi.md)
* `armv5te-unknown-linux-musleabi`
* `armv5te-unknown-linux-uclibceabi`
* `armv7-unknown-linux-gnueabi`
* `armv7-unknown-linux-gnueabihf`
* `armv7-unknown-linux-musleabi`
* `armv7-unknown-linux-musleabihf`
* `armv7-unknown-linux-ohos`
* [`armv7-unknown-linux-uclibceabi`](armv7-unknown-linux-uclibceabi.md)
* [`armv7-unknown-linux-uclibceabihf`](armv7-unknown-linux-uclibceabihf.md)
* `thumbv7neon-unknown-linux-gnueabihf`
* `thumbv7neon-unknown-linux-musleabihf`
Some of these targets have dedicated pages and some do not. This is largely
due to historical accident, or the enthusiasm of the maintainers. This
document attempts to cover all the targets, but only in broad terms.
To make sense of this list, the architecture and ABI component of the
`<architecture>-unknown-linux-<abi>` tuple will be discussed separately.
The second part of the tuple is `unknown` because these systems don't come
from any one specific vendor (like `powerpc-ibm-aix` or
`aarch64-apple-darwin`). The third part is `linux`, because this page only
discusses Linux targets.
## Architecture Component
* `arm`
* `armeb`
* `armv4t`
* `armv5te`
* `armv7`
* `thumbv7neon`
The architecture component simply called `arm` corresponds to the Armv6
architecture - that is, version 6 of the Arm Architecture as defined in
version 6 of the Arm Architecture Reference Manual (the Arm ARM). This was the
last 'legacy' release of the Arm architecture, before they split into
Application, Real-Time and Microcontroller profiles (leading to Armv7-A,
Armv7-R and Armv7-M). Processors that implement the Armv6 architecture include
the ARM1176JZF-S, as found in BCM2835 SoC that powers the Raspberry Pi Zero.
Arm processors are generally fairly backwards compatible, especially for
user-mode code, so code compiled for the `arm` architecture should also work
on newer ARMv7-A systems, or even 64/32-bit Armv8-A systems.
The `armeb` architecture component specifies an Armv6 processor running in Big
Endian mode (`eb` is for big-endian - the letters are backwards because
engineers used to little-endian systems perceive big-endian numbers to be
written into memory backwards, and they thought it was funnier like that).
Most Arm processors can operate in either little-endian or big-endian mode and
little-endian mode is by far the most common. However, if for whatever reason
you wish to store your Most Significant Bytes first, these targets are
available. They just aren't terribly well tested, or compatible with most
existing pre-compiled Arm libraries.
Targets that start with `armv4t` are for processors implementing the Armv4T
architecture from 1994. These include the ARM7TDMI, as found in the Nokia 6110
brick-phone and the Game Boy Advance. The 'T' stands for *Thumb* and indicate
that the processors can execute smaller 16-bit versions of some of the 32-bit
Arm instructions. Because a Thumb is like a small version of an Arm.
Targets that start with `armv5te` are for processors implementing the Armv5TE
architecture. These are mostly from the ARM9 family, like the ARM946E-S found
in the Nintendo DS. If you are programming an Arm machine from the early
2000s, this might be what you need.
The `armv7` is arguably a misnomer, and it should be `armv7a`. This is because
it corresponds to the Application profile of Armv7 (i.e. Armv7-A), as opposed
to the Real-Time or Microcontroller profile. Processors implementing this
architecture include the Cortex-A7 and Cortex-A8.
The `thumbv7neon` component indicates support for a processor that implements
ARMv7-A (the same as `armv7`), it generates Thumb instructions (technically
Thumb-2, also known as the T32 ISA) as opposed to Arm instructions (also known
as the A32 ISA). These instructions are smaller, giving more code per KB of
RAM, but may have a performance penalty if they take two instructions to do
something Arm instructions could do in one. It's a complex trade-off and you
should be doing benchmarks to work out which is better for you, if you
strongly care about code size and/or performance. This component also enables
support for Arm's SIMD extensions, known as Neon. These extensions will
improve performance for certain kinds of repetitive operations.
## ABI Component
* `gnueabi`
* `gnueabihf`
* `musleabi`
* `musleabihf`
* `ohos`
* `uclibceabi`
* `uclibceabihf`
You will need to select the appropriate ABI to match the system you want to be
running this code on. For example, running `eabihf` code on an `eabi` system
will not work correctly.
The `gnueabi` ABI component indicates support for using the GNU C Library
(glibc), and the Arm Embedded ABI (EABI). The EABI is a replacement for the
original ABI (now called the Old ABI or OABI), and it is the standard ABI for
32-bit Arm systems. With this ABI, function parameters that are `f32` or `f64`
are passed as if they were integers, instead of being passed via in FPU
registers. Generally these targets also disable the use of the FPU entirely,
although that isn't always true.
The `gnueabihf` ABI component is like `gnueabi`, except that it support the
'hard-float' of the EABI. That is, function parameters that are `f32` or `f64`
are passed in FPU registers. Naturally, this makes the FPU mandatory.
Most 'desktop' Linux distributions (Debian, Ubuntu, Fedora, etc) use the GNU C
Library and so you should probably select either `gnueabi` or `gnueabihf`,
depending on whether your distribution is using 'soft-float' (EABI) or
'hard-float' (EABIHF). Debian happens to offer
[both](https://wiki.debian.org/ArmEabiPort)
[kinds](https://wiki.debian.org/ArmHardFloatPort).
The `musleabi` and `musleabihf` ABI components offer support for the [musl C
library](https://musl.libc.org/). This C library can be used to create 'static
binaries' that have no run-time library requirements (a feature that glibc
does not support). There are soft-float (`eabi`) and hard-float (`eabihf`)
variants, as per the `gnu*` targets above.
The `uclibceabi` and `uclibceabihf` ABI components are for the [uClibc-ng C
library](https://uclibc-ng.org/). This is sometimes used in light-weight
embedded Linux distributions, like those created with
[buildroot](https://www.buildroot.org/).
## Cross Compilation
Unfortunately, 32-bit Arm machines are generally not the fastest around, and
they don't have much RAM. This means you are likely to be cross-compiling.
To do this, you need to give Rust a suitable linker to use - one that knows
the Arm architecture, and more importantly, knows where to find a suitable C
Library to link against.
To do that, you can add the `linker` property to your `.cargo/config.toml`.
Typically you would refer to a suitable copy of GCC that has built as a
cross-compiler, alongside a C library.
```toml
[target.arm-unknown-linux-gnueabi]
linker = "arm-linux-gnueabi-gcc"
```
On Debian Linux, you could install such a cross-compilation toolchain with
`apt install gcc-arm-linux-gnueabi`. For more exotic combinations, you might
need to build a bespoke version of GCC using [crosstool-ng].
[crosstool-ng]: https://github.com/crosstool-ng/crosstool-ng
Note that for GCC, all 32-bit Arm architectures are handled in the same build
- there are no separate Armv4T or Armv6 builds of GCC. The architecture is
selected with flags, like `-march=armv6`, but they aren't required for the
linker.
Let's assume we are on some Debian machine, and we want to build a basic Arm
Linux binary for a distribution using the GNU C Library, targeting Armv6 with
a hard-float ABI. Such a binary should work on a Raspberry Pi, for example.
The commands are:
```bash
sudo apt install -y gcc-arm-linux-gnueabihf
rustup target add arm-unknown-linux-gnueabihf
cargo new --bin armdemo
cd armdemo
mkdir .cargo
cat > .cargo/config.toml << EOF
[target.arm-unknown-linux-gnueabihf]
linker = "arm-linux-gnueabihf-gcc"
EOF
cargo build --target=arm-unknown-linux-gnueabihf
```
This will give us our ARM Linux binary for the GNU C Library with a soft-float ABI:
```console
$ file ./target/arm-unknown-linux-gnueabi/debug/armdemo
./target/arm-unknown-linux-gnueabi/debug/armdemo: ELF 32-bit LSB pie
executable, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter
/lib/ld-linux.so.3, BuildID[sha1]=dd0b9aa5ae876330fd4e2fcf393850f083ec7fcd,
for GNU/Linux 3.2.0, with debug_info, not stripped
```
If you are building C code as part of your Rust project, you may want to
direct `cc-rs` to use an appropriate cross-compiler with the `CROSS_COMPILE`
environment variable. You may also want to set the CFLAGS environment variable
for the target. For example:
```bash
export CROSS_COMPILE=arm-linux-gnueabi
export CFLAGS_arm_unknown_linux_gnueabi="-march=armv6"
```
(Note that the dashes (`-`) turn to underscores (`_`) to form the name of the
CFLAGS environment variable)
If you are building for a Tier 3 target using `-Zbuild-std` (on Nightly Rust),
you need to set these variables as well:
```bash
export CXX_arm_unknown_linux_gnueabi=arm-linux-gnueabi-g++
export CC_arm_unknown_linux_gnueabi=arm-linux-gnueabi-gcc
cargo +nightly build -Zbuild-std --target=arm-unknown-linux-gnueabi
```

View File

@ -3,6 +3,9 @@
Target for cross-compiling Linux user-mode applications targeting the Arm BE8 architecture.
See [`arm-linux`](arm-linux.md) for information applicable to all Arm Linux
targets.
## Overview
BE8 architecture retains the same little-endian ordered code-stream used by conventional little endian Arm systems, however the data accesses are in big-endian. BE8 is used primarily in high-performance networking applications where the ability to read packets in their native "Network Byte Order" is important (many network protocols transmit data in big-endian byte order for their wire formats).

View File

@ -5,6 +5,9 @@
This target supports Linux programs with glibc on ARMv5TE CPUs without
floating-point units.
See [`arm-linux`](arm-linux.md) for information applicable to all Arm Linux
targets.
## Target maintainers
There are currently no formally documented target maintainers.

View File

@ -4,6 +4,9 @@
This target supports Armv7-A softfloat CPUs and uses the uclibc-ng standard library. This is a common configuration on many consumer routers (e.g., Netgear R7000, Asus RT-AC68U).
See [`arm-linux`](arm-linux.md) for information applicable to all Arm Linux
targets.
## Target maintainers
[@lancethepants](https://github.com/lancethepants)

View File

@ -4,6 +4,9 @@
This tier supports the Armv7-A processor running a Linux kernel and uClibc-ng standard library. It provides full support for rust and the rust standard library.
See [`arm-linux`](arm-linux.md) for information applicable to all Arm Linux
targets.
## Target Maintainers
[@skrap](https://github.com/skrap)

View File

@ -710,6 +710,7 @@ A *placeholder* may occur in circumstances where a type or const value is not re
[mut-ptr-type]: #mut-ptr-type
[fn-type]: #fn-type
[dyn-trait-type]: #dyn-trait-type
[pattern-type]: #pattern-type
> type → \
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; *[basic-type]* \
@ -722,6 +723,7 @@ A *placeholder* may occur in circumstances where a type or const value is not re
> &nbsp;&nbsp; | *[mut-ptr-type]* \
> &nbsp;&nbsp; | *[fn-type]* \
> &nbsp;&nbsp; | *[dyn-trait-type]* \
> &nbsp;&nbsp; | *[pattern-type]* \
> &nbsp;&nbsp; | *[path]* \
> &nbsp;&nbsp; | *[backref]*
@ -830,6 +832,23 @@ Remaining primitives are encoded as a crate production, e.g. `C4f128`.
[fn-sig]: #fn-sig
[abi]: #abi
* `W` — A [pattern-type][pattern-tpye] `u32 is 0..100`.
> <span id="pattern-type">pattern-type</span>`W` *[pattern-kind]*
>
> <span id="pattern-kind">pattern-kind</span> → \
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; *[range-pattern-kind]* \
> &nbsp;&nbsp; *[or-pattern-kind]*
>
> <span id="range-pattern-kind">range-pattern-kind</span>`R` *[const]* *[const]*
>
> <span id="or-pattern-kind">or-pattern-kind</span>`O` *[pattern-kind]* `E`
While or patterns can be nested in theory, in practice this does not happen and they are instead flattened.
Range patterns have a start and end constant that are both included in the range.
The end must be larger than the start (there can be no wraparound). To emulate wraparound,
you need to use an or pattern of the two ranges to the upper limit and from the lower limit.
* `D` — A [trait object][reference-trait-object] `dyn Trait<Assoc=X> + Send + 'a`.
> <span id="dyn-trait-type">dyn-trait-type</span>`D` *[dyn-bounds]* *[lifetime]*
@ -1139,6 +1158,7 @@ The following is a summary of all of the productions of the symbol grammar.
> &nbsp;&nbsp; | *[mut-ptr-type]* \
> &nbsp;&nbsp; | *[fn-type]* \
> &nbsp;&nbsp; | *[dyn-trait-type]* \
> &nbsp;&nbsp; | *[pattern-type]* \
> &nbsp;&nbsp; | *[path]* \
> &nbsp;&nbsp; | *[backref]*
>
@ -1152,6 +1172,14 @@ The following is a summary of all of the productions of the symbol grammar.
> [mut-ptr-type] → `O` *[type]* \
> [fn-type] → `F` *[fn-sig]* \
> [dyn-trait-type] → `D` *[dyn-bounds]* *[lifetime]*
> [pattern-type] → `W` *[pattern-kind]*
>
> [pattern-kind] → \
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; *[range-pattern-kind]* \
> &nbsp;&nbsp; *[or-pattern-kind]*
>
> [range-pattern-kind] -> `R` *[const]* *[const]* \
> [or-pattern-kind] -> `O` *[pattern-kind]* `E` \
>
> [namespace] → *[lower]* | *[upper]*
>

View File

@ -89,20 +89,30 @@ https://doc.rust-lang.org/stable/std/?search=%s&go_to_first=true
This URL adds the `go_to_first=true` query parameter which can be appended to any `rustdoc` search URL
to automatically go to the first result.
## `#[repr(transparent)]`: Documenting the transparent representation
## `#[repr(...)]`: Documenting the representation of a type
Generally, rustdoc only displays the representation of a given type if none of its variants are
`#[doc(hidden)]` and if all of its fields are public and not `#[doc(hidden)]` since it's likely
not meant to be considered part of the public ABI otherwise.
Note that there's no way to overwrite that heuristic and force rustdoc to show the representation
regardless.
### `#[repr(transparent)]`
You can read more about `#[repr(transparent)]` itself in the [Rust Reference][repr-trans-ref] and
in the [Rustonomicon][repr-trans-nomicon].
Since this representation is only considered part of the public ABI if the single field with non-trivial
size or alignment is public and if the documentation does not state otherwise, Rustdoc helpfully displays
the attribute if and only if the non-1-ZST field is public or at least one field is public in case all
fields are 1-ZST fields. The term *1-ZST* refers to types that are one-aligned and zero-sized.
size or alignment is public and if the documentation does not state otherwise, rustdoc helpfully displays
the attribute if and only if the non-1-ZST field is public and not `#[doc(hidden)]` or
in case all fields are 1-ZST fields — at least one field is public and not `#[doc(hidden)]`.
The term *1-ZST* refers to types that are one-aligned and zero-sized.
It would seem that one can manually hide the attribute with `#[cfg_attr(not(doc), repr(transparent))]`
if one wishes to declare the representation as private even if the non-1-ZST field is public.
However, due to [current limitations][cross-crate-cfg-doc], this method is not always guaranteed to work.
Therefore, if you would like to do so, you should always write it down in prose independently of whether
Therefore, if you would like to do so, you should always write that down in prose independently of whether
you use `cfg_attr` or not.
[repr-trans-ref]: https://doc.rust-lang.org/reference/type-layout.html#the-transparent-representation

View File

@ -90,7 +90,7 @@ fn Foo() {}
These prefixes will be stripped when displayed in the documentation, so `[struct@Foo]` will be
rendered as `Foo`. The following prefixes are available: `struct`, `enum`, `trait`, `union`,
`mod`, `module`, `const`, `constant`, `fn`, `function`, `field`, `variant`, `method`, `derive`,
`type`, `value`, `macro`, `prim` or `primitive`.
`type`, `value`, `macro`, `tyalias`, `typealias`, `prim` or `primitive`.
You can also disambiguate for functions by adding `()` after the function name,
or for macros by adding `!` after the macro name. The macro `!` can be followed by `()`, `{}`,

View File

@ -794,50 +794,6 @@ impl Item {
Some(tcx.visibility(def_id))
}
/// Get a list of attributes excluding `#[repr]` to display.
///
/// Only used by the HTML output-format.
fn attributes_without_repr(&self) -> Vec<String> {
self.attrs
.other_attrs
.iter()
.filter_map(|attr| match attr {
hir::Attribute::Parsed(AttributeKind::LinkSection { name, .. }) => {
Some(format!("#[unsafe(link_section = \"{name}\")]"))
}
hir::Attribute::Parsed(AttributeKind::NoMangle(..)) => {
Some("#[unsafe(no_mangle)]".to_string())
}
hir::Attribute::Parsed(AttributeKind::ExportName { name, .. }) => {
Some(format!("#[unsafe(export_name = \"{name}\")]"))
}
hir::Attribute::Parsed(AttributeKind::NonExhaustive(..)) => {
Some("#[non_exhaustive]".to_string())
}
_ => None,
})
.collect()
}
/// Get a list of attributes to display on this item.
///
/// Only used by the HTML output-format.
pub(crate) fn attributes(&self, tcx: TyCtxt<'_>, cache: &Cache) -> Vec<String> {
let mut attrs = self.attributes_without_repr();
if let Some(repr_attr) = self.repr(tcx, cache) {
attrs.push(repr_attr);
}
attrs
}
/// Returns a stringified `#[repr(...)]` attribute.
///
/// Only used by the HTML output-format.
pub(crate) fn repr(&self, tcx: TyCtxt<'_>, cache: &Cache) -> Option<String> {
repr_attributes(tcx, cache, self.def_id()?, self.type_())
}
pub fn is_doc_hidden(&self) -> bool {
self.attrs.is_doc_hidden()
}
@ -847,74 +803,6 @@ impl Item {
}
}
/// Return a string representing the `#[repr]` attribute if present.
///
/// Only used by the HTML output-format.
pub(crate) fn repr_attributes(
tcx: TyCtxt<'_>,
cache: &Cache,
def_id: DefId,
item_type: ItemType,
) -> 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
|| 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 {

View File

@ -8,6 +8,7 @@
use std::borrow::Cow;
use std::collections::VecDeque;
use std::fmt::{self, Display, Write};
use std::iter;
use rustc_data_structures::fx::FxIndexMap;
use rustc_lexer::{Cursor, FrontmatterAllowed, LiteralKind, TokenKind};
@ -15,8 +16,9 @@ use rustc_span::edition::Edition;
use rustc_span::symbol::Symbol;
use rustc_span::{BytePos, DUMMY_SP, Span};
use super::format::{self, write_str};
use super::format;
use crate::clean::PrimitiveType;
use crate::display::Joined as _;
use crate::html::escape::EscapeBodyText;
use crate::html::macro_expansion::ExpandedCode;
use crate::html::render::{Context, LinkFromSrc};
@ -45,92 +47,72 @@ pub(crate) enum Tooltip {
CompileFail,
ShouldPanic,
Edition(Edition),
None,
}
/// Highlights `src` as an inline example, returning the HTML output.
pub(crate) fn render_example_with_highlighting(
src: &str,
out: &mut String,
tooltip: Tooltip,
tooltip: Option<&Tooltip>,
playground_button: Option<&str>,
extra_classes: &[String],
) {
write_header(out, "rust-example-rendered", None, tooltip, extra_classes);
write_code(out, src, None, None, None);
write_footer(out, playground_button);
) -> impl Display {
fmt::from_fn(move |f| {
write_header("rust-example-rendered", tooltip, extra_classes).fmt(f)?;
write_code(f, src, None, None, None);
write_footer(playground_button).fmt(f)
})
}
fn write_header(
out: &mut String,
class: &str,
extra_content: Option<&str>,
tooltip: Tooltip,
extra_classes: &[String],
) {
write_str(
out,
format_args!(
fn write_header(class: &str, tooltip: Option<&Tooltip>, extra_classes: &[String]) -> impl Display {
fmt::from_fn(move |f| {
write!(
f,
"<div class=\"example-wrap{}\">",
match tooltip {
Tooltip::IgnoreAll | Tooltip::IgnoreSome(_) => " ignore",
Tooltip::CompileFail => " compile_fail",
Tooltip::ShouldPanic => " should_panic",
Tooltip::Edition(_) => " edition",
Tooltip::None => "",
}
),
);
tooltip
.map(|tooltip| match tooltip {
Tooltip::IgnoreAll | Tooltip::IgnoreSome(_) => " ignore",
Tooltip::CompileFail => " compile_fail",
Tooltip::ShouldPanic => " should_panic",
Tooltip::Edition(_) => " edition",
})
.unwrap_or_default()
)?;
if tooltip != Tooltip::None {
let tooltip = fmt::from_fn(|f| match &tooltip {
Tooltip::IgnoreAll => f.write_str("This example is not tested"),
Tooltip::IgnoreSome(platforms) => {
f.write_str("This example is not tested on ")?;
match &platforms[..] {
[] => unreachable!(),
[platform] => f.write_str(platform)?,
[first, second] => write!(f, "{first} or {second}")?,
[platforms @ .., last] => {
for platform in platforms {
write!(f, "{platform}, ")?;
if let Some(tooltip) = tooltip {
let tooltip = fmt::from_fn(|f| match tooltip {
Tooltip::IgnoreAll => f.write_str("This example is not tested"),
Tooltip::IgnoreSome(platforms) => {
f.write_str("This example is not tested on ")?;
match &platforms[..] {
[] => unreachable!(),
[platform] => f.write_str(platform)?,
[first, second] => write!(f, "{first} or {second}")?,
[platforms @ .., last] => {
for platform in platforms {
write!(f, "{platform}, ")?;
}
write!(f, "or {last}")?;
}
write!(f, "or {last}")?;
}
Ok(())
}
Ok(())
}
Tooltip::CompileFail => f.write_str("This example deliberately fails to compile"),
Tooltip::ShouldPanic => f.write_str("This example panics"),
Tooltip::Edition(edition) => write!(f, "This example runs with edition {edition}"),
Tooltip::None => unreachable!(),
});
write_str(out, format_args!("<a href=\"#\" class=\"tooltip\" title=\"{tooltip}\">ⓘ</a>"));
}
Tooltip::CompileFail => f.write_str("This example deliberately fails to compile"),
Tooltip::ShouldPanic => f.write_str("This example panics"),
Tooltip::Edition(edition) => write!(f, "This example runs with edition {edition}"),
});
if let Some(extra) = extra_content {
out.push_str(extra);
}
if class.is_empty() {
write_str(
out,
format_args!(
"<pre class=\"rust{}{}\">",
if extra_classes.is_empty() { "" } else { " " },
extra_classes.join(" ")
),
);
} else {
write_str(
out,
format_args!(
"<pre class=\"rust {class}{}{}\">",
if extra_classes.is_empty() { "" } else { " " },
extra_classes.join(" ")
),
);
}
write_str(out, format_args!("<code>"));
write!(f, "<a href=\"#\" class=\"tooltip\" title=\"{tooltip}\">ⓘ</a>")?;
}
let classes = fmt::from_fn(|f| {
iter::once("rust")
.chain(Some(class).filter(|class| !class.is_empty()))
.chain(extra_classes.iter().map(String::as_str))
.joined(" ", f)
});
write!(f, "<pre class=\"{classes}\"><code>")
})
}
/// Check if two `Class` can be merged together. In the following rules, "unclassified" means `None`
@ -577,8 +559,8 @@ pub(super) fn write_code(
});
}
fn write_footer(out: &mut String, playground_button: Option<&str>) {
write_str(out, format_args!("</code></pre>{}</div>", playground_button.unwrap_or_default()));
fn write_footer(playground_button: Option<&str>) -> impl Display {
fmt::from_fn(move |f| write!(f, "</code></pre>{}</div>", playground_button.unwrap_or_default()))
}
/// How a span of text is classified. Mostly corresponds to token kinds.
@ -1262,6 +1244,64 @@ fn string<W: Write>(
}
}
fn generate_link_to_def(
out: &mut impl Write,
text_s: &str,
klass: Class,
href_context: &Option<HrefContext<'_, '_>>,
def_span: Span,
open_tag: bool,
) -> bool {
if let Some(href_context) = href_context
&& let Some(href) =
href_context.context.shared.span_correspondence_map.get(&def_span).and_then(|href| {
let context = href_context.context;
// FIXME: later on, it'd be nice to provide two links (if possible) for all items:
// one to the documentation page and one to the source definition.
// FIXME: currently, external items only generate a link to their documentation,
// a link to their definition can be generated using this:
// https://github.com/rust-lang/rust/blob/60f1a2fc4b535ead9c85ce085fdce49b1b097531/src/librustdoc/html/render/context.rs#L315-L338
match href {
LinkFromSrc::Local(span) => {
context.href_from_span_relative(*span, &href_context.current_href)
}
LinkFromSrc::External(def_id) => {
format::href_with_root_path(*def_id, context, Some(href_context.root_path))
.ok()
.map(|(url, _, _)| url)
}
LinkFromSrc::Primitive(prim) => format::href_with_root_path(
PrimitiveType::primitive_locations(context.tcx())[prim],
context,
Some(href_context.root_path),
)
.ok()
.map(|(url, _, _)| url),
LinkFromSrc::Doc(def_id) => {
format::href_with_root_path(*def_id, context, Some(href_context.root_path))
.ok()
.map(|(doc_link, _, _)| doc_link)
}
}
})
{
if !open_tag {
// We're already inside an element which has the same klass, no need to give it
// again.
write!(out, "<a href=\"{href}\">{text_s}").unwrap();
} else {
let klass_s = klass.as_html();
if klass_s.is_empty() {
write!(out, "<a href=\"{href}\">{text_s}").unwrap();
} else {
write!(out, "<a class=\"{klass_s}\" href=\"{href}\">{text_s}").unwrap();
}
}
return true;
}
false
}
/// This function writes `text` into `out` with some modifications depending on `klass`:
///
/// * If `klass` is `None`, `text` is written into `out` with no modification.
@ -1291,10 +1331,14 @@ fn string_without_closing_tag<T: Display>(
return Some("</span>");
};
let mut added_links = false;
let mut text_s = text.to_string();
if text_s.contains("::") {
let mut span = def_span.with_hi(def_span.lo());
text_s = text_s.split("::").intersperse("::").fold(String::new(), |mut path, t| {
span = span.with_hi(span.hi() + BytePos(t.len() as _));
match t {
"::" => write!(&mut path, "::"),
"self" | "Self" => write!(
&mut path,
"<span class=\"{klass}\">{t}</span>",
@ -1307,58 +1351,24 @@ fn string_without_closing_tag<T: Display>(
klass = Class::KeyWord.as_html(),
)
}
t => write!(&mut path, "{t}"),
t => {
if !t.is_empty()
&& generate_link_to_def(&mut path, t, klass, href_context, span, open_tag)
{
added_links = true;
write!(&mut path, "</a>")
} else {
write!(&mut path, "{t}")
}
}
}
.expect("Failed to build source HTML path");
span = span.with_lo(span.lo() + BytePos(t.len() as _));
path
});
}
if let Some(href_context) = href_context
&& let Some(href) = href_context.context.shared.span_correspondence_map.get(&def_span)
&& let Some(href) = {
let context = href_context.context;
// FIXME: later on, it'd be nice to provide two links (if possible) for all items:
// one to the documentation page and one to the source definition.
// FIXME: currently, external items only generate a link to their documentation,
// a link to their definition can be generated using this:
// https://github.com/rust-lang/rust/blob/60f1a2fc4b535ead9c85ce085fdce49b1b097531/src/librustdoc/html/render/context.rs#L315-L338
match href {
LinkFromSrc::Local(span) => {
context.href_from_span_relative(*span, &href_context.current_href)
}
LinkFromSrc::External(def_id) => {
format::href_with_root_path(*def_id, context, Some(href_context.root_path))
.ok()
.map(|(url, _, _)| url)
}
LinkFromSrc::Primitive(prim) => format::href_with_root_path(
PrimitiveType::primitive_locations(context.tcx())[prim],
context,
Some(href_context.root_path),
)
.ok()
.map(|(url, _, _)| url),
LinkFromSrc::Doc(def_id) => {
format::href_with_root_path(*def_id, context, Some(href_context.root_path))
.ok()
.map(|(doc_link, _, _)| doc_link)
}
}
}
{
if !open_tag {
// We're already inside an element which has the same klass, no need to give it
// again.
write!(out, "<a href=\"{href}\">{text_s}").unwrap();
} else {
let klass_s = klass.as_html();
if klass_s.is_empty() {
write!(out, "<a href=\"{href}\">{text_s}").unwrap();
} else {
write!(out, "<a class=\"{klass_s}\" href=\"{href}\">{text_s}").unwrap();
}
}
if !added_links && generate_link_to_def(out, &text_s, klass, href_context, def_span, open_tag) {
return Some("</a>");
}
if !open_tag {

View File

@ -321,31 +321,34 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for CodeBlocks<'_, 'a, I> {
))
});
let tooltip = if ignore == Ignore::All {
highlight::Tooltip::IgnoreAll
} else if let Ignore::Some(platforms) = ignore {
highlight::Tooltip::IgnoreSome(platforms)
} else if compile_fail {
highlight::Tooltip::CompileFail
} else if should_panic {
highlight::Tooltip::ShouldPanic
} else if explicit_edition {
highlight::Tooltip::Edition(edition)
} else {
highlight::Tooltip::None
let tooltip = {
use highlight::Tooltip::*;
if ignore == Ignore::All {
Some(IgnoreAll)
} else if let Ignore::Some(platforms) = ignore {
Some(IgnoreSome(platforms))
} else if compile_fail {
Some(CompileFail)
} else if should_panic {
Some(ShouldPanic)
} else if explicit_edition {
Some(Edition(edition))
} else {
None
}
};
// insert newline to clearly separate it from the
// previous block so we can shorten the html output
let mut s = String::new();
s.push('\n');
highlight::render_example_with_highlighting(
&text,
&mut s,
tooltip,
playground_button.as_deref(),
&added_classes,
let s = format!(
"\n{}",
highlight::render_example_with_highlighting(
&text,
tooltip.as_ref(),
playground_button.as_deref(),
&added_classes,
)
);
Some(Event::Html(s.into()))
}

View File

@ -51,7 +51,9 @@ use askama::Template;
use itertools::Either;
use rustc_ast::join_path_syms;
use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
use rustc_hir::attrs::{DeprecatedSince, Deprecation};
use rustc_hir as hir;
use rustc_hir::attrs::{AttributeKind, DeprecatedSince, Deprecation};
use rustc_hir::def::DefKind;
use rustc_hir::def_id::{DefId, DefIdSet};
use rustc_hir::{ConstStability, Mutability, RustcVersion, StabilityLevel, StableSince};
use rustc_middle::ty::print::PrintTraitRefExt;
@ -601,7 +603,12 @@ impl AllTypes {
}
fmt::from_fn(|f| {
f.write_str("<h1>List of all items</h1>")?;
f.write_str(
"<div class=\"main-heading\">\
<h1>List of all items</h1>\
<rustdoc-toolbar></rustdoc-toolbar>\
</div>",
)?;
// Note: print_entries does not escape the title, because we know the current set of titles
// doesn't require escaping.
print_entries(&self.structs, ItemSection::Structs).fmt(f)?;
@ -1310,43 +1317,6 @@ fn render_assoc_item(
})
}
struct CodeAttribute(String);
fn render_code_attribute(prefix: &str, code_attr: CodeAttribute, w: &mut impl fmt::Write) {
write!(
w,
"<div class=\"code-attribute\">{prefix}{attr}</div>",
prefix = prefix,
attr = 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,
prefix: &str,
cx: &Context<'_>,
) {
for attr in it.attributes(cx.tcx(), cx.cache()) {
render_code_attribute(prefix, 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) {
render_code_attribute("", CodeAttribute(repr), w);
}
}
#[derive(Copy, Clone)]
enum AssocItemLink<'a> {
Anchor(Option<&'a str>),
@ -2959,3 +2929,142 @@ fn render_call_locations<W: fmt::Write>(
w.write_str("</div>")
}
fn render_attributes_in_code(
w: &mut impl fmt::Write,
item: &clean::Item,
prefix: &str,
cx: &Context<'_>,
) {
for attr in &item.attrs.other_attrs {
let hir::Attribute::Parsed(kind) = attr else { continue };
let attr = match kind {
AttributeKind::LinkSection { name, .. } => {
Cow::Owned(format!("#[unsafe(link_section = {})]", Escape(&format!("{name:?}"))))
}
AttributeKind::NoMangle(..) => Cow::Borrowed("#[unsafe(no_mangle)]"),
AttributeKind::ExportName { name, .. } => {
Cow::Owned(format!("#[unsafe(export_name = {})]", Escape(&format!("{name:?}"))))
}
AttributeKind::NonExhaustive(..) => Cow::Borrowed("#[non_exhaustive]"),
_ => continue,
};
render_code_attribute(prefix, attr.as_ref(), w);
}
if let Some(def_id) = item.def_id()
&& let Some(repr) = repr_attribute(cx.tcx(), cx.cache(), def_id)
{
render_code_attribute(prefix, &repr, w);
}
}
fn render_repr_attribute_in_code(w: &mut impl fmt::Write, cx: &Context<'_>, def_id: DefId) {
if let Some(repr) = repr_attribute(cx.tcx(), cx.cache(), def_id) {
render_code_attribute("", &repr, w);
}
}
fn render_code_attribute(prefix: &str, attr: &str, w: &mut impl fmt::Write) {
write!(w, "<div class=\"code-attribute\">{prefix}{attr}</div>").unwrap();
}
/// Compute the *public* `#[repr]` of the item given by `DefId`.
///
/// Read more about it here:
/// <https://doc.rust-lang.org/nightly/rustdoc/advanced-features.html#repr-documenting-the-representation-of-a-type>.
fn repr_attribute<'tcx>(
tcx: TyCtxt<'tcx>,
cache: &Cache,
def_id: DefId,
) -> Option<Cow<'static, str>> {
let adt = match tcx.def_kind(def_id) {
DefKind::Struct | DefKind::Enum | DefKind::Union => tcx.adt_def(def_id),
_ => return None,
};
let repr = adt.repr();
let is_visible = |def_id| cache.document_hidden || !tcx.is_doc_hidden(def_id);
let is_public_field = |field: &ty::FieldDef| {
(cache.document_private || field.vis.is_public()) && is_visible(field.did)
};
if repr.transparent() {
// The transparent repr is public iff the non-1-ZST field is public and visible or
// in case all fields are 1-ZST fields — at least one field is public and visible.
let is_public = 'is_public: {
// `#[repr(transparent)]` can only be applied to structs and single-variant enums.
let var = adt.variant(rustc_abi::FIRST_VARIANT); // the first and only variant
if !is_visible(var.def_id) {
break 'is_public false;
}
// Side note: There can only ever be one or zero non-1-ZST fields.
let non_1zst_field = var.fields.iter().find(|field| {
let ty = ty::TypingEnv::post_analysis(tcx, field.did)
.as_query_input(tcx.type_of(field.did).instantiate_identity());
tcx.layout_of(ty).is_ok_and(|layout| !layout.is_1zst())
});
match non_1zst_field {
Some(field) => is_public_field(field),
None => var.fields.is_empty() || var.fields.iter().any(is_public_field),
}
};
// Since the transparent repr can't have any other reprs or
// repr modifiers beside it, we can safely return early here.
return is_public.then(|| "#[repr(transparent)]".into());
}
// Fast path which avoids looking through the variants and fields in
// the common case of no `#[repr]` or in the case of `#[repr(Rust)]`.
// FIXME: This check is not very robust / forward compatible!
if !repr.c()
&& !repr.simd()
&& repr.int.is_none()
&& repr.pack.is_none()
&& repr.align.is_none()
{
return None;
}
// The repr is public iff all components are public and visible.
let is_public = adt
.variants()
.iter()
.all(|variant| is_visible(variant.def_id) && variant.fields.iter().all(is_public_field));
if !is_public {
return None;
}
let mut result = Vec::<Cow<'_, _>>::new();
if repr.c() {
result.push("C".into());
}
if repr.simd() {
result.push("simd".into());
}
if let Some(int) = repr.int {
let prefix = if int.is_signed() { 'i' } else { 'u' };
let int = match int {
rustc_abi::IntegerType::Pointer(_) => format!("{prefix}size"),
rustc_abi::IntegerType::Fixed(int, _) => {
format!("{prefix}{}", int.size().bytes() * 8)
}
};
result.push(int.into());
}
// Render modifiers last.
if let Some(pack) = repr.pack {
result.push(format!("packed({})", pack.bytes()).into());
}
if let Some(align) = repr.align {
result.push(format!("align({})", align.bytes()).into());
}
(!result.is_empty()).then(|| format!("#[repr({})]", result.join(", ")).into())
}

View File

@ -21,7 +21,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_impl,
render_repr_attributes_in_code, render_rightside, render_stability_since_raw,
render_repr_attribute_in_code, render_rightside, render_stability_since_raw,
render_stability_since_raw_with_extra, write_section_heading,
};
use crate::clean;
@ -1555,7 +1555,7 @@ impl<'clean> DisplayEnum<'clean> {
wrap_item(w, |w| {
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);
render_repr_attribute_in_code(w, cx, self.def_id);
} else {
render_attributes_in_code(w, it, "", cx);
}
@ -2017,7 +2017,7 @@ impl<'a> DisplayStruct<'a> {
wrap_item(w, |w| {
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);
render_repr_attribute_in_code(w, cx, self.def_id);
} else {
render_attributes_in_code(w, it, "", cx);
}
@ -2371,7 +2371,7 @@ fn render_union(
fmt::from_fn(move |mut f| {
if is_type_alias {
// For now the only attributes we render for type aliases are `repr` attributes.
render_repr_attributes_in_code(f, cx, def_id, ItemType::Union);
render_repr_attribute_in_code(f, cx, def_id);
} else {
render_attributes_in_code(f, it, "", cx);
}

View File

@ -2,11 +2,9 @@ use std::path::{Path, PathBuf};
use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::{DefId, LOCAL_CRATE};
use rustc_hir::intravisit::{self, Visitor};
use rustc_hir::{
ExprKind, HirId, Item, ItemKind, Mod, Node, Pat, PatExpr, PatExprKind, PatKind, QPath,
};
use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId};
use rustc_hir::intravisit::{self, Visitor, VisitorExt};
use rustc_hir::{ExprKind, HirId, Item, ItemKind, Mod, Node, QPath};
use rustc_middle::hir::nested_filter;
use rustc_middle::ty::TyCtxt;
use rustc_span::hygiene::MacroKind;
@ -67,7 +65,7 @@ struct SpanMapVisitor<'tcx> {
impl SpanMapVisitor<'_> {
/// This function is where we handle `hir::Path` elements and add them into the "span map".
fn handle_path(&mut self, path: &rustc_hir::Path<'_>) {
fn handle_path(&mut self, path: &rustc_hir::Path<'_>, only_use_last_segment: bool) {
match path.res {
// FIXME: For now, we handle `DefKind` if it's not a `DefKind::TyParam`.
// Would be nice to support them too alongside the other `DefKind`
@ -79,24 +77,36 @@ impl SpanMapVisitor<'_> {
LinkFromSrc::External(def_id)
};
// In case the path ends with generics, we remove them from the span.
let span = path
.segments
.last()
.map(|last| {
// In `use` statements, the included item is not in the path segments.
// However, it doesn't matter because you can't have generics on `use`
// statements.
if path.span.contains(last.ident.span) {
path.span.with_hi(last.ident.span.hi())
} else {
path.span
}
})
.unwrap_or(path.span);
let span = if only_use_last_segment
&& let Some(path_span) = path.segments.last().map(|segment| segment.ident.span)
{
path_span
} else {
path.segments
.last()
.map(|last| {
// In `use` statements, the included item is not in the path segments.
// However, it doesn't matter because you can't have generics on `use`
// statements.
if path.span.contains(last.ident.span) {
path.span.with_hi(last.ident.span.hi())
} else {
path.span
}
})
.unwrap_or(path.span)
};
self.matches.insert(span, link);
}
Res::Local(_) if let Some(span) = self.tcx.hir_res_span(path.res) => {
self.matches.insert(path.span, LinkFromSrc::Local(clean::Span::new(span)));
let path_span = if only_use_last_segment
&& let Some(path_span) = path.segments.last().map(|segment| segment.ident.span)
{
path_span
} else {
path.span
};
self.matches.insert(path_span, LinkFromSrc::Local(clean::Span::new(span)));
}
Res::PrimTy(p) => {
// FIXME: Doesn't handle "path-like" primitives like arrays or tuples.
@ -189,31 +199,23 @@ impl SpanMapVisitor<'_> {
self.matches.insert(span, link);
}
}
}
fn handle_pat(&mut self, p: &Pat<'_>) {
let mut check_qpath = |qpath, hir_id| match qpath {
QPath::TypeRelative(_, path) if matches!(path.res, Res::Err) => {
self.infer_id(path.hir_id, Some(hir_id), qpath.span());
}
QPath::Resolved(_, path) => self.handle_path(path),
_ => {}
};
match p.kind {
PatKind::Binding(_, _, _, Some(p)) => self.handle_pat(p),
PatKind::Struct(qpath, _, _) | PatKind::TupleStruct(qpath, _, _) => {
check_qpath(qpath, p.hir_id)
}
PatKind::Expr(PatExpr { kind: PatExprKind::Path(qpath), hir_id, .. }) => {
check_qpath(*qpath, *hir_id)
}
PatKind::Or(pats) => {
for pat in pats {
self.handle_pat(pat);
}
}
_ => {}
// This is a reimplementation of `hir_enclosing_body_owner` which allows to fail without
// panicking.
fn hir_enclosing_body_owner(tcx: TyCtxt<'_>, hir_id: HirId) -> Option<LocalDefId> {
for (_, node) in tcx.hir_parent_iter(hir_id) {
// FIXME: associated type impl items don't have an associated body, so we don't handle
// them currently.
if let Node::ImplItem(impl_item) = node
&& matches!(impl_item.kind, rustc_hir::ImplItemKind::Type(_))
{
return None;
} else if let Some((def_id, _)) = node.associated_body() {
return Some(def_id);
}
}
None
}
impl<'tcx> Visitor<'tcx> for SpanMapVisitor<'tcx> {
@ -227,12 +229,42 @@ impl<'tcx> Visitor<'tcx> for SpanMapVisitor<'tcx> {
if self.handle_macro(path.span) {
return;
}
self.handle_path(path);
self.handle_path(path, false);
intravisit::walk_path(self, path);
}
fn visit_pat(&mut self, p: &Pat<'tcx>) {
self.handle_pat(p);
fn visit_qpath(&mut self, qpath: &QPath<'tcx>, id: HirId, _span: Span) {
match *qpath {
QPath::TypeRelative(qself, path) => {
if matches!(path.res, Res::Err) {
let tcx = self.tcx;
if let Some(body_id) = hir_enclosing_body_owner(tcx, id) {
let typeck_results = tcx.typeck_body(tcx.hir_body_owned_by(body_id).id());
let path = rustc_hir::Path {
// We change the span to not include parens.
span: path.ident.span,
res: typeck_results.qpath_res(qpath, id),
segments: &[],
};
self.handle_path(&path, false);
}
} else {
self.infer_id(path.hir_id, Some(id), path.ident.span);
}
rustc_ast::visit::try_visit!(self.visit_ty_unambig(qself));
self.visit_path_segment(path);
}
QPath::Resolved(maybe_qself, path) => {
self.handle_path(path, true);
rustc_ast::visit::visit_opt!(self, visit_ty_unambig, maybe_qself);
if !self.handle_macro(path.span) {
intravisit::walk_path(self, path);
}
}
_ => {}
}
}
fn visit_mod(&mut self, m: &'tcx Mod<'tcx>, span: Span, id: HirId) {

View File

@ -386,8 +386,13 @@ impl CratesIndexPart {
let layout = &cx.shared.layout;
let style_files = &cx.shared.style_files;
const DELIMITER: &str = "\u{FFFC}"; // users are being naughty if they have this
let content =
format!("<h1>List of all crates</h1><ul class=\"all-items\">{DELIMITER}</ul>");
let content = format!(
"<div class=\"main-heading\">\
<h1>List of all crates</h1>\
<rustdoc-toolbar></rustdoc-toolbar>\
</div>\
<ul class=\"all-items\">{DELIMITER}</ul>"
);
let template = layout::render(layout, &page, "", content, style_files);
SortedTemplate::from_template(&template, DELIMITER)
.expect("Object Replacement Character (U+FFFC) should not appear in the --index-page")

View File

@ -130,6 +130,7 @@ impl Res {
DefKind::Static { .. } => "static",
DefKind::Field => "field",
DefKind::Variant | DefKind::Ctor(..) => "variant",
DefKind::TyAlias => "tyalias",
// Now handle things that don't have a specific disambiguator
_ => match kind
.ns()
@ -1708,6 +1709,7 @@ impl Disambiguator {
"value" => NS(Namespace::ValueNS),
"macro" => NS(Namespace::MacroNS),
"prim" | "primitive" => Primitive,
"tyalias" | "typealias" => Kind(DefKind::TyAlias),
_ => return Err((format!("unknown disambiguator `{prefix}`"), 0..idx)),
};

View File

@ -169,6 +169,15 @@ fn miri_issue_2759() {
input.replace_range(0..0, "0");
}
/// This was skirting the edge of UB, let's make sure it remains on the sound side.
/// Context: <https://github.com/rust-lang/rust/pull/141032>.
fn extract_if() {
let mut v = vec![Box::new(0u64), Box::new(1u64)];
for item in v.extract_if(.., |x| **x == 0) {
drop(item);
}
}
fn main() {
assert_eq!(vec_reallocate().len(), 5);
@ -199,4 +208,5 @@ fn main() {
swap_remove();
reverse();
miri_issue_2759();
extract_if();
}

View File

@ -16,7 +16,7 @@ pub fn bar() {
// CHECK: call pattern_type_symbols::foo::<u32>
// CHECK: call void @_RINvC[[CRATE_IDENT:[a-zA-Z0-9]{12}]]_20pattern_type_symbols3foomEB2_
foo::<u32>();
// CHECK: call pattern_type_symbols::foo::<(u32, [(); 0], [(); 999999999])>
// CHECK: call void @_RINvC[[CRATE_IDENT]]_20pattern_type_symbols3fooTmAum0_Aum3b9ac9ff_EEB2_
// CHECK: call pattern_type_symbols::foo::<u32 is 0..=999999999>
// CHECK: call void @_RINvC[[CRATE_IDENT]]_20pattern_type_symbols3fooWmRm0_m3b9ac9ff_EB2_
foo::<NanoU32>();
}

View File

@ -20,3 +20,15 @@ assert-document-property: {"title": '"another one" Search - Rust'}
press-key: "Escape"
assert-document-property: {"title": |title|}
// check that all.html does it correctly, too.
go-to: "file://" + |DOC_PATH| + "/test_docs/all.html"
assert-document-property: {"title": "List of all items in this crate"}
call-function: ("perform-search", {"query": "verify"})
assert-document-property: {"title": '"verify" Search - Rust'}
// check that index.html does it correctly, too.
go-to: "file://" + |DOC_PATH| + "/index.html"
assert-document-property: {"title": "Index of crates"}
call-function: ("perform-search", {"query": "verify"})
assert-document-property: {"title": '"verify" Search - Rust'}

View File

@ -1,3 +1,4 @@
//@ compile-flags: --enable-index-page -Z unstable-options
//! The point of this crate is to be able to have enough different "kinds" of
//! documentation generated so we can test each different features.
#![doc(html_playground_url="https://play.rust-lang.org/")]
@ -459,10 +460,10 @@ pub fn safe_fn() {}
#[repr(C)]
pub struct WithGenerics<T: TraitWithNoDocblocks, S = String, E = WhoLetTheDogOut, P = i8> {
s: S,
t: T,
e: E,
p: P,
pub s: S,
pub t: T,
pub e: E,
pub p: P,
}
pub struct StructWithPublicUndocumentedFields {

View File

@ -0,0 +1,21 @@
// Ensure that no warning is emitted if the disambiguator is used for type alias.
// Regression test for <https://github.com/rust-lang/rust/issues/146855>.
#![deny(rustdoc::broken_intra_doc_links)]
pub struct Foo;
#[allow(non_camel_case_types)]
pub type f32 = Foo;
/// This function returns [`f32`].
//~^ ERROR: `f32` is both a type alias and a primitive type
//~| HELP: to link to the type alias, prefix with `tyalias@`
//~| HELP: to link to the primitive type, prefix with `prim@`
pub fn my_fn() -> f32 {}
/// This function returns [type@f32].
//~^ ERROR: `f32` is both a type alias and a primitive type
//~| HELP: to link to the type alias, prefix with `tyalias@`
//~| HELP: to link to the primitive type, prefix with `prim@`
pub fn my_fn2() -> f32 {}

View File

@ -0,0 +1,39 @@
error: `f32` is both a type alias and a primitive type
--> $DIR/type-alias-primitive-suggestion.rs:11:29
|
LL | /// This function returns [`f32`].
| ^^^ ambiguous link
|
note: the lint level is defined here
--> $DIR/type-alias-primitive-suggestion.rs:4:9
|
LL | #![deny(rustdoc::broken_intra_doc_links)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
help: to link to the type alias, prefix with `tyalias@`
|
LL | /// This function returns [`tyalias@f32`].
| ++++++++
help: to link to the primitive type, prefix with `prim@`
|
LL | /// This function returns [`prim@f32`].
| +++++
error: `f32` is both a type alias and a primitive type
--> $DIR/type-alias-primitive-suggestion.rs:17:28
|
LL | /// This function returns [type@f32].
| ^^^^^^^^ ambiguous link
|
help: to link to the type alias, prefix with `tyalias@`
|
LL - /// This function returns [type@f32].
LL + /// This function returns [tyalias@f32].
|
help: to link to the primitive type, prefix with `prim@`
|
LL - /// This function returns [type@f32].
LL + /// This function returns [prim@f32].
|
error: aborting due to 2 previous errors

View File

@ -0,0 +1,14 @@
// Ensure that no warning is emitted if the disambiguator is used for type alias.
// Regression test for <https://github.com/rust-lang/rust/issues/146855>.
//@ check-pass
#![deny(rustdoc::broken_intra_doc_links)]
pub struct Foo;
#[allow(non_camel_case_types)]
pub type f32 = Foo;
/// This function returns [`tyalias@f32`] and not [`prim@f32`].
pub fn my_fn() -> f32 {}

View File

@ -1,8 +0,0 @@
#![crate_name = "foo"]
//@ has 'foo/fn.f.html'
//@ has - //*[@'class="code-attribute"]' '#[unsafe(export_name = "f")]'
//@ has - //*[@'class="rust item-decl"]' 'pub fn f()'
#[unsafe(export_name = "\
f")]
pub fn f() {}

View File

@ -9,6 +9,18 @@ pub extern "C" fn f() {}
#[unsafe(export_name = "bar")]
pub extern "C" fn g() {}
//@ has foo/fn.escape_special.html '//*[@class="code-attribute"]' \
// '#[unsafe(export_name = "\n\"\n")]'
#[unsafe(export_name = "\n\"
")]
pub extern "C" fn escape_special() {}
// issue: <https://github.com/rust-lang/rust/issues/142835>
//@ has foo/fn.escape_html.html '//*[@class="code-attribute"]' \
// '#[unsafe(export_name = "<script>alert()</script>")]'
#[unsafe(export_name = "<script>alert()</script>")]
pub extern "C" fn escape_html() {}
//@ has foo/fn.example.html '//*[@class="code-attribute"]' '#[unsafe(link_section = ".text")]'
#[unsafe(link_section = ".text")]
pub extern "C" fn example() {}

View File

@ -0,0 +1,5 @@
#[repr(i8)]
pub enum ReprI8 {
Var0,
Var1,
}

View File

@ -1,7 +1,20 @@
// Ensure that we render attributes on inlined cross-crate re-exported items.
// issue: <https://github.com/rust-lang/rust/issues/144004>
//@ aux-crate:attributes=attributes.rs
//@ edition:2021
#![crate_name = "user"]
//@ has 'user/struct.NonExhaustive.html'
//@ has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[non_exhaustive]'
//@ has 'user/fn.no_mangle.html' '//pre[@class="rust item-decl"]' '#[unsafe(no_mangle)]'
pub use attributes::no_mangle;
//@ has 'user/fn.link_section.html' '//pre[@class="rust item-decl"]' \
// '#[unsafe(link_section = ".here")]'
pub use attributes::link_section;
//@ has 'user/fn.export_name.html' '//pre[@class="rust item-decl"]' \
// '#[unsafe(export_name = "exonym")]'
pub use attributes::export_name;
//@ has 'user/struct.NonExhaustive.html' '//pre[@class="rust item-decl"]' '#[non_exhaustive]'
pub use attributes::NonExhaustive;

View File

@ -1,2 +1,11 @@
#[unsafe(no_mangle)]
pub fn no_mangle() {}
#[unsafe(link_section = ".here")]
pub fn link_section() {}
#[unsafe(export_name = "exonym")]
pub fn export_name() {}
#[non_exhaustive]
pub struct NonExhaustive;

View File

@ -1,42 +0,0 @@
#![feature(repr_simd)]
#[repr(C, align(8))]
pub struct ReprC {
field: u8,
}
#[repr(simd, packed(2))]
pub struct ReprSimd {
field: [u8; 1],
}
#[repr(transparent)]
pub struct ReprTransparent {
pub field: u8,
}
#[repr(isize)]
pub enum ReprIsize {
Bla,
}
#[repr(u8)]
pub enum ReprU8 {
Bla,
}
#[repr(transparent)] // private
pub struct ReprTransparentPrivField {
field: u32, // non-1-ZST field
}
#[repr(transparent)] // public
pub struct ReprTransparentPriv1ZstFields {
marker0: Marker,
pub main: u64, // non-1-ZST field
marker1: Marker,
}
#[repr(transparent)] // private
pub struct ReprTransparentPrivFieldPub1ZstFields {
main: [u16; 0], // non-1-ZST field
pub marker: Marker,
}
pub struct Marker; // 1-ZST

View File

@ -1,40 +0,0 @@
// Regression test for <https://github.com/rust-lang/rust/issues/110698>.
// This test ensures that the re-exported items still have the `#[repr(...)]` attribute.
//@ aux-build:repr.rs
#![crate_name = "foo"]
extern crate repr;
//@ has 'foo/struct.ReprC.html'
//@ has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[repr(C, align(8))]'
pub use repr::ReprC;
//@ has 'foo/struct.ReprSimd.html'
//@ has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[repr(simd, packed(2))]'
pub use repr::ReprSimd;
//@ has 'foo/struct.ReprTransparent.html'
//@ has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[repr(transparent)]'
pub use repr::ReprTransparent;
//@ has 'foo/enum.ReprIsize.html'
//@ has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[repr(isize)]'
pub use repr::ReprIsize;
//@ has 'foo/enum.ReprU8.html'
//@ has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[repr(u8)]'
pub use repr::ReprU8;
// Regression test for <https://github.com/rust-lang/rust/issues/90435>.
// Check that we show `#[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.
//@ has 'foo/struct.ReprTransparentPrivField.html'
//@ !has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[repr(transparent)]'
pub use repr::ReprTransparentPrivField;
//@ has 'foo/struct.ReprTransparentPriv1ZstFields.html'
//@ has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[repr(transparent)]'
pub use repr::ReprTransparentPriv1ZstFields;
//@ has 'foo/struct.ReprTransparentPrivFieldPub1ZstFields.html'
//@ !has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[repr(transparent)]'
pub use repr::ReprTransparentPrivFieldPub1ZstFields;

View File

@ -0,0 +1,21 @@
// Ensure that no warning is emitted if the disambiguator is used for type alias.
// Regression test for <https://github.com/rust-lang/rust/issues/146855>.
#![crate_name = "foo"]
#![deny(rustdoc::broken_intra_doc_links)]
pub struct Foo;
#[allow(non_camel_case_types)]
pub type f32 = Foo;
/// This function returns [`tyalias@f32`] and not [bla][`prim@f32`].
//@ has 'foo/fn.my_fn.html'
//@ has - '//a[@href="type.f32.html"]' "f32"
//@ has - '//a[@href="https://doc.rust-lang.org/nightly/std/primitive.f32.html"]' "bla"
pub fn my_fn() -> f32 { 0. }
/// This function returns [`typealias@f32`].
//@ has 'foo/fn.my_other_fn.html'
//@ has - '//a[@href="type.f32.html"]' "f32"
pub fn my_other_fn() -> f32 { 0. }

View File

@ -0,0 +1,54 @@
// This test ensures that patterns also get a link generated.
//@ compile-flags: -Zunstable-options --generate-link-to-definition
#![crate_name = "foo"]
//@ has 'src/foo/jump-to-def-assoc-items.rs.html'
pub trait Trait {
type T;
}
pub trait Another {
type T;
const X: u32;
}
pub struct Foo;
impl Foo {
pub fn new() -> Self { Foo }
}
pub struct C;
impl C {
pub fn wat() {}
}
pub struct Bar;
impl Trait for Bar {
type T = Foo;
}
impl Another for Bar {
type T = C;
const X: u32 = 12;
}
pub fn bar() {
//@ has - '//a[@href="#20"]' 'new'
<Bar as Trait>::T::new();
//@ has - '//a[@href="#26"]' 'wat'
<Bar as Another>::T::wat();
match 12u32 {
//@ has - '//a[@href="#14"]' 'X'
<Bar as Another>::X => {}
_ => {}
}
}
pub struct Far {
//@ has - '//a[@href="#10"]' 'T'
x: <Bar as Trait>::T,
}

View File

@ -0,0 +1,20 @@
// This test ensures that associated types don't crash rustdoc jump to def.
//@ compile-flags: -Zunstable-options --generate-link-to-definition
#![crate_name = "foo"]
//@ has 'src/foo/jump-to-def-ice-assoc-types.rs.html'
pub trait Trait {
type Node;
}
pub fn y<G: Trait>() {
struct X<G>(G);
impl<G: Trait> Trait for X<G> {
type Node = G::Node;
}
}

View File

@ -0,0 +1,24 @@
// This test ensures that items with no body don't panic when generating
// jump to def links.
//@ compile-flags: -Zunstable-options --generate-link-to-definition
#![crate_name = "foo"]
//@ has 'src/foo/jump-to-def-ice.rs.html'
pub trait A {
type T;
type U;
}
impl A for () {
type T = Self::U;
type U = ();
}
pub trait C {
type X;
}
pub struct F<T: C>(pub T::X);

View File

@ -8,7 +8,7 @@
pub struct Bar;
impl std::default::Default for Bar {
//@ has - '//a[@href="#20-22"]' 'Self::new'
//@ has - '//a[@href="#20-22"]' 'new'
fn default() -> Self {
Self::new()
}
@ -16,7 +16,7 @@ impl std::default::Default for Bar {
//@ has - '//a[@href="#8"]' 'Bar'
impl Bar {
//@ has - '//a[@href="#24-26"]' 'Self::bar'
//@ has - '//a[@href="#24-26"]' 'bar'
pub fn new()-> Self {
Self::bar()
}

View File

@ -30,13 +30,13 @@ pub fn foo() -> Result<(), ()> {
impl<T, E> fmt::Display for MyEnum<T, E> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
//@ has - '//a[@href="#12"]' 'Self::Ok'
//@ has - '//a[@href="#12"]' 'Ok'
Self::Ok(_) => f.write_str("MyEnum::Ok"),
//@ has - '//a[@href="#13"]' 'MyEnum::Err'
//@ has - '//a[@href="#13"]' 'Err'
MyEnum::Err(_) => f.write_str("MyEnum::Err"),
//@ has - '//a[@href="#14"]' 'Self::Some'
//@ has - '//a[@href="#14"]' 'Some'
Self::Some(_) => f.write_str("MyEnum::Some"),
//@ has - '//a[@href="#15"]' 'Self::None'
//@ has - '//a[@href="#15"]' 'None'
Self::None => f.write_str("MyEnum::None"),
}
}
@ -45,7 +45,7 @@ impl<T, E> fmt::Display for MyEnum<T, E> {
impl X {
fn p(&self) -> &str {
match self {
//@ has - '//a[@href="#19"]' 'Self::A'
//@ has - '//a[@href="#19"]' 'A'
Self::A => "X::A",
}
}

View File

@ -21,9 +21,10 @@ pub fn bar2<T: Read>(readable: T) {
}
pub fn bar() {
//@ has - '//a[@href="{{channel}}/core/sync/atomic/struct.AtomicIsize.html#method.new"]' 'AtomicIsize::new'
//@ has - '//a[@href="{{channel}}/core/sync/atomic/struct.AtomicIsize.html"]' 'AtomicIsize'
//@ has - '//a[@href="{{channel}}/core/sync/atomic/struct.AtomicIsize.html#method.new"]' 'new'
let _ = AtomicIsize::new(0);
//@ has - '//a[@href="#48"]' 'local_private'
//@ has - '//a[@href="#49"]' 'local_private'
local_private();
}
@ -39,7 +40,7 @@ pub fn macro_call() -> Result<(), ()> {
}
pub fn variant() {
//@ has - '//a[@href="{{channel}}/core/cmp/enum.Ordering.html#variant.Less"]' 'Ordering::Less'
//@ has - '//a[@href="{{channel}}/core/cmp/enum.Ordering.html#variant.Less"]' 'Less'
let _ = Ordering::Less;
//@ has - '//a[@href="{{channel}}/core/marker/struct.PhantomData.html"]' 'PhantomData'
let _: PhantomData::<usize> = PhantomData;

View File

@ -1,14 +0,0 @@
#[unsafe(no_mangle)]
pub fn f0() {}
#[unsafe(link_section = ".here")]
pub fn f1() {}
#[unsafe(export_name = "f2export")]
pub fn f2() {}
#[repr(u8)]
pub enum T0 { V1 }
#[non_exhaustive]
pub enum T1 {}

View File

@ -1,20 +0,0 @@
//@ aux-build: reexports-attrs.rs
#![crate_name = "foo"]
extern crate reexports_attrs;
//@ has 'foo/fn.f0.html' '//pre[@class="rust item-decl"]' '#[unsafe(no_mangle)]'
pub use reexports_attrs::f0;
//@ has 'foo/fn.f1.html' '//pre[@class="rust item-decl"]' '#[unsafe(link_section = ".here")]'
pub use reexports_attrs::f1;
//@ has 'foo/fn.f2.html' '//pre[@class="rust item-decl"]' '#[unsafe(export_name = "f2export")]'
pub use reexports_attrs::f2;
//@ has 'foo/enum.T0.html' '//pre[@class="rust item-decl"]' '#[repr(u8)]'
pub use reexports_attrs::T0;
//@ has 'foo/enum.T1.html' '//pre[@class="rust item-decl"]' '#[non_exhaustive]'
pub use reexports_attrs::T1;

View File

@ -1,29 +1,163 @@
// Regression test for <https://github.com/rust-lang/rust/issues/90435>.
// Check that we show `#[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.
// Test the rendering of `#[repr]` on ADTs.
#![feature(repr_simd)] // only used for the `ReprSimd` test case
// Check the "local case" (HIR cleaning) //
// Don't render the default repr which is `Rust`.
//@ has 'repr/struct.ReprDefault.html'
//@ !has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[repr(Rust)]'
pub struct ReprDefault;
// Don't render the `Rust` repr — even if given explicitly — since it's the default.
//@ has 'repr/struct.ReprRust.html'
//@ !has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[repr(Rust)]'
#[repr(Rust)] // omitted
pub struct ReprRust;
//@ has 'repr/struct.ReprCPubFields.html'
//@ has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[repr(C)]'
#[repr(C)] // public
pub struct ReprCPubFields {
pub a: u32,
pub b: u32,
}
//@ has 'repr/struct.ReprCPrivField.html'
//@ !has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[repr(C)]'
#[repr(C)] // private...
pub struct ReprCPrivField {
a: u32, // ...since this is private
pub b: u32,
}
//@ has 'repr/enum.ReprIsize.html'
//@ has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[repr(isize)]'
#[repr(isize)] // public
pub enum ReprIsize {
Bla,
}
//@ has 'repr/enum.ReprU32HiddenVariant.html'
//@ !has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[repr(u32)]'
#[repr(u32)] // private...
pub enum ReprU32HiddenVariant {
#[doc(hidden)]
Hidden, // ...since this is hidden
Public,
}
//@ has 'repr/struct.ReprAlignHiddenField.html'
//@ !has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[repr(align(4))]'
#[repr(align(4))] // private...
pub struct ReprAlignHiddenField {
#[doc(hidden)]
pub hidden: i16, // ...since this field is hidden
}
//@ has 'repr/struct.ReprSimd.html'
//@ has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[repr(simd, packed(2))]'
#[repr(simd, packed(2))] // public
pub struct ReprSimd {
pub field: [u8; 1],
}
//@ has 'repr/enum.ReprU32Align.html'
//@ has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[repr(u32, align(8))]'
#[repr(u32, align(8))] // public
pub enum ReprU32Align {
Variant(u16),
}
//@ has 'repr/enum.ReprCHiddenVariantField.html'
//@ !has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[repr(C)]'
#[repr(C)] // private...
pub enum ReprCHiddenVariantField {
Variant { #[doc(hidden)] field: () }, //...since this field is hidden
}
//@ has 'repr/struct.ReprTransparentPrivField.html'
//@ !has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[repr(transparent)]'
#[repr(transparent)] // private
#[repr(transparent)] // private...
pub struct ReprTransparentPrivField {
field: u32, // non-1-ZST field
field: u32, // ...since the non-1-ZST field is private
}
//@ has 'repr/struct.ReprTransparentPriv1ZstFields.html'
//@ has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[repr(transparent)]'
#[repr(transparent)] // public
#[repr(transparent)] // public...
pub struct ReprTransparentPriv1ZstFields {
marker0: Marker,
pub main: u64, // non-1-ZST field
pub main: u64, // ...since the non-1-ZST field is public and visible
marker1: Marker,
} // the two private 1-ZST fields don't matter
//@ has 'repr/struct.ReprTransparentPrivFieldPub1ZstField.html'
//@ !has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[repr(transparent)]'
#[repr(transparent)] // private...
pub struct ReprTransparentPrivFieldPub1ZstField {
main: [u16; 0], // ...since the non-1-ZST field is private
pub marker: Marker, // this public 1-ZST field doesn't matter
}
//@ has 'repr/struct.ReprTransparentPub1ZstField.html'
//@ has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[repr(transparent)]'
#[repr(transparent)] // public
#[repr(transparent)] // public...
pub struct ReprTransparentPub1ZstField {
marker0: Marker,
pub marker1: Marker,
marker0: Marker, // ...since we don't have a non-1-ZST field...
pub marker1: Marker, // ...and this field is public and visible
}
//@ has 'repr/struct.ReprTransparentUnitStruct.html'
//@ has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[repr(transparent)]'
#[repr(transparent)] // public
pub struct ReprTransparentUnitStruct;
//@ has 'repr/enum.ReprTransparentEnumUnitVariant.html'
//@ has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[repr(transparent)]'
#[repr(transparent)] // public
pub enum ReprTransparentEnumUnitVariant {
Variant,
}
//@ has 'repr/enum.ReprTransparentEnumHiddenUnitVariant.html'
//@ !has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[repr(transparent)]'
#[repr(transparent)] // private
pub enum ReprTransparentEnumHiddenUnitVariant {
#[doc(hidden)] Variant(u32),
}
//@ has 'repr/enum.ReprTransparentEnumPub1ZstField.html'
//@ has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[repr(transparent)]'
#[repr(transparent)] // public...
pub enum ReprTransparentEnumPub1ZstField {
Variant {
field: u64, // ...since the non-1-ZST field is public
#[doc(hidden)]
marker: Marker, // this hidden 1-ZST field doesn't matter
},
}
//@ has 'repr/enum.ReprTransparentEnumHidden1ZstField.html'
//@ !has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[repr(transparent)]'
#[repr(transparent)] // private...
pub enum ReprTransparentEnumHidden1ZstField {
Variant {
#[doc(hidden)]
field: u64, // ...since the non-1-ZST field is public
},
}
struct Marker; // 1-ZST
// Check the "extern case" (middle cleaning) //
// Internally, HIR and middle cleaning share `#[repr]` rendering.
// Thus we'll only test the very basics in this section.
//@ aux-build: ext-repr.rs
extern crate ext_repr as ext;
// Regression test for <https://github.com/rust-lang/rust/issues/110698>.
//@ has 'repr/enum.ReprI8.html'
//@ has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[repr(i8)]'
pub use ext::ReprI8;

View File

@ -34,7 +34,7 @@ fn babar() {}
// The 5 links to line 23 and the line 23 itself.
//@ count - '//pre[@class="rust"]//a[@href="#23"]' 6
//@ has - '//pre[@class="rust"]//a[@href="../../source_code/struct.SourceCode.html"]' \
// 'source_code::SourceCode'
// 'SourceCode'
pub fn foo(a: u32, b: &str, c: String, d: Foo, e: bar::Bar, f: source_code::SourceCode) {
let x = 12;
let y: Foo = Foo;

View File

@ -0,0 +1,40 @@
//@ edition: 2024
//@ revisions: current next
//@[next] compile-flags: -Znext-solver
//@ check-pass
// Regression test for #146813. We previously used a pseudo-canonical
// query during HIR typeck which caused a query cycle when looking at the
// witness of a coroutine.
use std::future::Future;
trait ConnectMiddleware {}
trait ConnectHandler: Sized {
fn with<M>(self, _: M) -> impl ConnectHandler
where
M: ConnectMiddleware,
{
LayeredConnectHandler
}
}
struct LayeredConnectHandler;
impl ConnectHandler for LayeredConnectHandler {}
impl<F> ConnectHandler for F where F: FnOnce() {}
impl<F, Fut> ConnectMiddleware for F
where
F: FnOnce() -> Fut,
Fut: Future<Output = ()> + Send,
{
}
pub async fn fails() {
{ || {} }
.with(async || ())
.with(async || ())
.with(async || ());
}
fn main() {}

View File

@ -1,19 +0,0 @@
#![feature(lang_items, no_core)]
#![no_core]
#![no_main]
#[lang = "pointee_sized"]
pub trait PointeeSized {}
#[lang = "meta_sized"]
pub trait MetaSized: PointeeSized {}
#[lang = "sized"]
trait Sized: MetaSized { }
struct S;
#[no_mangle]
extern "C" fn main(argc: i32, _argv: *const *const u8) -> i32 {
argc //~ ERROR requires `copy` lang_item
}

View File

@ -1,8 +0,0 @@
error: requires `copy` lang_item
--> $DIR/missing-copy-lang-item-issue-19660.rs:18:5
|
LL | argc
| ^^^^
error: aborting due to 1 previous error