mirror of
https://github.com/rust-lang/rust.git
synced 2025-09-27 12:48:20 +00:00
Compare commits
53 Commits
eabf390b4c
...
0c34d2c01e
Author | SHA1 | Date | |
---|---|---|---|
![]() |
0c34d2c01e | ||
![]() |
6a2313d5f8 | ||
![]() |
7ff5c85cf0 | ||
![]() |
d06bc1d271 | ||
![]() |
64d43d9d5a | ||
![]() |
9c0f389cbe | ||
![]() |
d2b5c3dcaa | ||
![]() |
1c4bbd8c43 | ||
![]() |
b69548c886 | ||
![]() |
b733736ea2 | ||
![]() |
51ae86dec9 | ||
![]() |
0ede3fe48c | ||
![]() |
8daad494b1 | ||
![]() |
7ac0330c6d | ||
![]() |
c98b3b2da4 | ||
![]() |
a2d2869924 | ||
![]() |
8ea9122c9b | ||
![]() |
5c710d770c | ||
![]() |
a7862da48a | ||
![]() |
0374df1b50 | ||
![]() |
03fc2990b9 | ||
![]() |
c6d0059fb8 | ||
![]() |
781f71a6fe | ||
![]() |
83cf8f9860 | ||
![]() |
fea9196e52 | ||
![]() |
958d1438b6 | ||
![]() |
e3f7626732 | ||
![]() |
a028b7a9e3 | ||
![]() |
a39d5134cd | ||
![]() |
5a6e3ccceb | ||
![]() |
5595f437c7 | ||
![]() |
185ae698aa | ||
![]() |
85c193a4ed | ||
![]() |
d7d7725b8c | ||
![]() |
88e797784e | ||
![]() |
137d4eaf12 | ||
![]() |
56734495e2 | ||
![]() |
7a0adc0878 | ||
![]() |
83532f8544 | ||
![]() |
4fcafc9daa | ||
![]() |
e697f206fe | ||
![]() |
aaff372f4b | ||
![]() |
58c7505d86 | ||
![]() |
739e89980f | ||
![]() |
e9b2c4f395 | ||
![]() |
15f6d2e8d2 | ||
![]() |
4d273c4d91 | ||
![]() |
ec5375fc55 | ||
![]() |
25e767b0ee | ||
![]() |
d528a49fd7 | ||
![]() |
1a6d6363d0 | ||
![]() |
f1649a4e43 | ||
![]() |
49044d0d40 |
@ -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,
|
||||
);
|
||||
|
@ -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,
|
||||
|
@ -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 };
|
||||
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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))
|
||||
{
|
||||
|
@ -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) => {
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
|
@ -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>>,
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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)]
|
||||
|
@ -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];
|
||||
|
@ -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>;
|
||||
|
@ -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() {
|
||||
|
@ -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![],
|
||||
};
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -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");
|
||||
}
|
||||
|
@ -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()
|
||||
|
@ -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 {
|
||||
|
@ -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/.
|
||||
|
@ -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());
|
||||
|
@ -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() {
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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})")
|
||||
});
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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.
|
||||
|
@ -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)
|
||||
|
217
src/doc/rustc/src/platform-support/arm-linux.md
Normal file
217
src/doc/rustc/src/platform-support/arm-linux.md
Normal 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
|
||||
```
|
@ -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).
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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 → \
|
||||
> *[basic-type]* \
|
||||
@ -722,6 +723,7 @@ A *placeholder* may occur in circumstances where a type or const value is not re
|
||||
> | *[mut-ptr-type]* \
|
||||
> | *[fn-type]* \
|
||||
> | *[dyn-trait-type]* \
|
||||
> | *[pattern-type]* \
|
||||
> | *[path]* \
|
||||
> | *[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> → \
|
||||
> *[range-pattern-kind]* \
|
||||
> *[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.
|
||||
> | *[mut-ptr-type]* \
|
||||
> | *[fn-type]* \
|
||||
> | *[dyn-trait-type]* \
|
||||
> | *[pattern-type]* \
|
||||
> | *[path]* \
|
||||
> | *[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] → \
|
||||
> *[range-pattern-kind]* \
|
||||
> *[or-pattern-kind]*
|
||||
>
|
||||
> [range-pattern-kind] -> `R` *[const]* *[const]* \
|
||||
> [or-pattern-kind] -> `O` *[pattern-kind]* `E` \
|
||||
>
|
||||
> [namespace] → *[lower]* | *[upper]*
|
||||
>
|
||||
|
@ -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
|
||||
|
@ -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 `()`, `{}`,
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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()))
|
||||
}
|
||||
|
@ -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())
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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")
|
||||
|
@ -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)),
|
||||
};
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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>();
|
||||
}
|
||||
|
@ -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'}
|
||||
|
@ -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 {
|
||||
|
@ -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 {}
|
@ -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
|
||||
|
14
tests/rustdoc-ui/intra-doc/type-alias-primitive.rs
Normal file
14
tests/rustdoc-ui/intra-doc/type-alias-primitive.rs
Normal 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 {}
|
@ -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() {}
|
@ -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() {}
|
||||
|
5
tests/rustdoc/auxiliary/ext-repr.rs
Normal file
5
tests/rustdoc/auxiliary/ext-repr.rs
Normal file
@ -0,0 +1,5 @@
|
||||
#[repr(i8)]
|
||||
pub enum ReprI8 {
|
||||
Var0,
|
||||
Var1,
|
||||
}
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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
|
@ -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;
|
21
tests/rustdoc/intra-doc/type-alias-primitive.rs
Normal file
21
tests/rustdoc/intra-doc/type-alias-primitive.rs
Normal 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. }
|
54
tests/rustdoc/jump-to-def-assoc-items.rs
Normal file
54
tests/rustdoc/jump-to-def-assoc-items.rs
Normal 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,
|
||||
}
|
20
tests/rustdoc/jump-to-def-ice-assoc-types.rs
Normal file
20
tests/rustdoc/jump-to-def-ice-assoc-types.rs
Normal 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;
|
||||
}
|
||||
}
|
24
tests/rustdoc/jump-to-def-ice.rs
Normal file
24
tests/rustdoc/jump-to-def-ice.rs
Normal 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);
|
@ -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()
|
||||
}
|
||||
|
@ -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",
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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 {}
|
@ -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;
|
@ -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;
|
||||
|
@ -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;
|
||||
|
40
tests/ui/coroutine/copy-fast-path-query-cycle.rs
Normal file
40
tests/ui/coroutine/copy-fast-path-query-cycle.rs
Normal 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() {}
|
@ -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
|
||||
}
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user