1
Fork 0

Merge branch 'master' into master

This commit is contained in:
Aliénore Bouttefeux 2021-04-05 21:13:29 +02:00 committed by GitHub
commit b08b4848c3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
447 changed files with 3097 additions and 2989 deletions

View file

@ -1499,46 +1499,64 @@ impl<'hir> LoweringContext<'_, 'hir> {
// previous iteration.
required_features.clear();
// Validate register classes against currently enabled target
// features. We check that at least one type is available for
// the current target.
let reg_class = reg.reg_class();
if reg_class == asm::InlineAsmRegClass::Err {
continue;
}
for &(_, feature) in reg_class.supported_types(asm_arch.unwrap()) {
if let Some(feature) = feature {
if self.sess.target_features.contains(&Symbol::intern(feature)) {
// We ignore target feature requirements for clobbers: if the
// feature is disabled then the compiler doesn't care what we
// do with the registers.
//
// Note that this is only possible for explicit register
// operands, which cannot be used in the asm string.
let is_clobber = matches!(
op,
hir::InlineAsmOperand::Out {
reg: asm::InlineAsmRegOrRegClass::Reg(_),
late: _,
expr: None
}
);
if !is_clobber {
// Validate register classes against currently enabled target
// features. We check that at least one type is available for
// the current target.
for &(_, feature) in reg_class.supported_types(asm_arch.unwrap()) {
if let Some(feature) = feature {
if self.sess.target_features.contains(&Symbol::intern(feature)) {
required_features.clear();
break;
} else {
required_features.push(feature);
}
} else {
required_features.clear();
break;
} else {
required_features.push(feature);
}
} else {
required_features.clear();
break;
}
}
// We are sorting primitive strs here and can use unstable sort here
required_features.sort_unstable();
required_features.dedup();
match &required_features[..] {
[] => {}
[feature] => {
let msg = format!(
"register class `{}` requires the `{}` target feature",
reg_class.name(),
feature
);
sess.struct_span_err(op_sp, &msg).emit();
}
features => {
let msg = format!(
"register class `{}` requires at least one target feature: {}",
reg_class.name(),
features.join(", ")
);
sess.struct_span_err(op_sp, &msg).emit();
// We are sorting primitive strs here and can use unstable sort here
required_features.sort_unstable();
required_features.dedup();
match &required_features[..] {
[] => {}
[feature] => {
let msg = format!(
"register class `{}` requires the `{}` target feature",
reg_class.name(),
feature
);
sess.struct_span_err(op_sp, &msg).emit();
}
features => {
let msg = format!(
"register class `{}` requires at least one target feature: {}",
reg_class.name(),
features.join(", ")
);
sess.struct_span_err(op_sp, &msg).emit();
}
}
}

View file

@ -1,6 +1,6 @@
use crate::cfg_eval::cfg_eval;
use rustc_ast::{self as ast, token, ItemKind, MetaItemKind, NestedMetaItem, StmtKind};
use rustc_ast::{self as ast, attr, token, ItemKind, MetaItemKind, NestedMetaItem, StmtKind};
use rustc_errors::{struct_span_err, Applicability};
use rustc_expand::base::{Annotatable, ExpandResult, ExtCtxt, Indeterminate, MultiItemModifier};
use rustc_feature::AttributeTemplate;
@ -26,32 +26,39 @@ impl MultiItemModifier for Expander {
return ExpandResult::Ready(vec![item]);
}
let template =
AttributeTemplate { list: Some("Trait1, Trait2, ..."), ..Default::default() };
let attr = ecx.attribute(meta_item.clone());
validate_attr::check_builtin_attribute(&sess.parse_sess, &attr, sym::derive, template);
let result =
ecx.resolver.resolve_derives(ecx.current_expansion.id, ecx.force_mode, &|| {
let template =
AttributeTemplate { list: Some("Trait1, Trait2, ..."), ..Default::default() };
let attr = attr::mk_attr_outer(meta_item.clone());
validate_attr::check_builtin_attribute(
&sess.parse_sess,
&attr,
sym::derive,
template,
);
let derives: Vec<_> = attr
.meta_item_list()
.unwrap_or_default()
.into_iter()
.filter_map(|nested_meta| match nested_meta {
NestedMetaItem::MetaItem(meta) => Some(meta),
NestedMetaItem::Literal(lit) => {
// Reject `#[derive("Debug")]`.
report_unexpected_literal(sess, &lit);
None
}
})
.map(|meta| {
// Reject `#[derive(Debug = "value", Debug(abc))]`, but recover the paths.
report_path_args(sess, &meta);
meta.path
})
.collect();
attr.meta_item_list()
.unwrap_or_default()
.into_iter()
.filter_map(|nested_meta| match nested_meta {
NestedMetaItem::MetaItem(meta) => Some(meta),
NestedMetaItem::Literal(lit) => {
// Reject `#[derive("Debug")]`.
report_unexpected_literal(sess, &lit);
None
}
})
.map(|meta| {
// Reject `#[derive(Debug = "value", Debug(abc))]`, but recover the paths.
report_path_args(sess, &meta);
meta.path
})
.map(|path| (path, None))
.collect()
});
// FIXME: Try to cache intermediate results to avoid collecting same paths multiple times.
match ecx.resolver.resolve_derives(ecx.current_expansion.id, derives, ecx.force_mode) {
match result {
Ok(()) => ExpandResult::Ready(cfg_eval(ecx, item)),
Err(Indeterminate) => ExpandResult::Retry(item),
}

View file

@ -14,7 +14,7 @@ use rustc_data_structures::fx::FxHashMap;
use rustc_hir as hir;
use rustc_middle::ty::layout::TyAndLayout;
use rustc_middle::{bug, span_bug};
use rustc_span::{Pos, Span};
use rustc_span::{Pos, Span, Symbol};
use rustc_target::abi::*;
use rustc_target::asm::*;
@ -125,15 +125,39 @@ impl AsmBuilderMethods<'tcx> for Builder<'a, 'll, 'tcx> {
// Collect the types of output operands
let mut constraints = vec![];
let mut clobbers = vec![];
let mut output_types = vec![];
let mut op_idx = FxHashMap::default();
for (idx, op) in operands.iter().enumerate() {
match *op {
InlineAsmOperandRef::Out { reg, late, place } => {
let is_target_supported = |reg_class: InlineAsmRegClass| {
for &(_, feature) in reg_class.supported_types(asm_arch) {
if let Some(feature) = feature {
if self.tcx.sess.target_features.contains(&Symbol::intern(feature))
{
return true;
}
} else {
// Register class is unconditionally supported
return true;
}
}
false
};
let mut layout = None;
let ty = if let Some(ref place) = place {
layout = Some(&place.layout);
llvm_fixup_output_type(self.cx, reg.reg_class(), &place.layout)
} else if !is_target_supported(reg.reg_class()) {
// We turn discarded outputs into clobber constraints
// if the target feature needed by the register class is
// disabled. This is necessary otherwise LLVM will try
// to actually allocate a register for the dummy output.
assert!(matches!(reg, InlineAsmRegOrRegClass::Reg(_)));
clobbers.push(format!("~{}", reg_to_llvm(reg, None)));
continue;
} else {
// If the output is discarded, we don't really care what
// type is used. We're just using this to tell LLVM to
@ -244,6 +268,7 @@ impl AsmBuilderMethods<'tcx> for Builder<'a, 'll, 'tcx> {
}
}
constraints.append(&mut clobbers);
if !options.contains(InlineAsmOptions::PRESERVES_FLAGS) {
match asm_arch {
InlineAsmArch::AArch64 | InlineAsmArch::Arm => {

View file

@ -317,7 +317,7 @@ pub fn from_fn_attrs(cx: &CodegenCx<'ll, 'tcx>, llfn: &'ll Value, instance: ty::
// Note that currently the `wasm-import-module` doesn't do anything, but
// eventually LLVM 7 should read this and ferry the appropriate import
// module to the output file.
if cx.tcx.sess.target.arch == "wasm32" {
if cx.tcx.sess.target.is_like_wasm {
if let Some(module) = wasm_import_module(cx.tcx, instance.def_id()) {
llvm::AddFunctionAttrStringValue(
llfn,

View file

@ -170,10 +170,7 @@ pub fn target_machine_factory(
// On the wasm target once the `atomics` feature is enabled that means that
// we're no longer single-threaded, or otherwise we don't want LLVM to
// lower atomic operations to single-threaded operations.
if singlethread
&& sess.target.llvm_target.contains("wasm32")
&& sess.target_features.contains(&sym::atomics)
{
if singlethread && sess.target.is_like_wasm && sess.target_features.contains(&sym::atomics) {
singlethread = false;
}
@ -1050,7 +1047,7 @@ pub unsafe fn with_llvm_pmb(
// thresholds copied from clang.
match (opt_level, opt_size, inline_threshold) {
(.., Some(t)) => {
llvm::LLVMPassManagerBuilderUseInlinerWithThreshold(builder, t as u32);
llvm::LLVMPassManagerBuilderUseInlinerWithThreshold(builder, t);
}
(llvm::CodeGenOptLevel::Aggressive, ..) => {
llvm::LLVMPassManagerBuilderUseInlinerWithThreshold(builder, 275);

View file

@ -1083,9 +1083,9 @@ pub fn compile_unit_metadata(
);
}
// Insert `llvm.ident` metadata on the wasm32 targets since that will
// Insert `llvm.ident` metadata on the wasm targets since that will
// get hooked up to the "producer" sections `processed-by` information.
if tcx.sess.opts.target_triple.triple().starts_with("wasm32") {
if tcx.sess.target.is_like_wasm {
let name_metadata = llvm::LLVMMDStringInContext(
debug_context.llcontext,
rustc_producer.as_ptr().cast(),

View file

@ -1411,15 +1411,10 @@ fn add_link_script(cmd: &mut dyn Linker, sess: &Session, tmpdir: &Path, crate_ty
}
}
/// Add arbitrary "user defined" args defined from command line and by `#[link_args]` attributes.
/// Add arbitrary "user defined" args defined from command line.
/// FIXME: Determine where exactly these args need to be inserted.
fn add_user_defined_link_args(
cmd: &mut dyn Linker,
sess: &Session,
codegen_results: &CodegenResults,
) {
fn add_user_defined_link_args(cmd: &mut dyn Linker, sess: &Session) {
cmd.args(&sess.opts.cg.link_args);
cmd.args(&*codegen_results.crate_info.link_args);
}
/// Add arbitrary "late link" args defined by the target spec.
@ -1761,7 +1756,7 @@ fn linker_with_args<'a, B: ArchiveBuilder<'a>>(
add_rpath_args(cmd, sess, codegen_results, out_filename);
// OBJECT-FILES-MAYBE, CUSTOMIZATION-POINT
add_user_defined_link_args(cmd, sess, codegen_results);
add_user_defined_link_args(cmd, sess);
// NO-OPT-OUT, OBJECT-FILES-NO, AUDIT-ORDER
cmd.finalize();

View file

@ -186,7 +186,7 @@ impl<'a> GccLinker<'a> {
// * On OSX they have their own linker, not binutils'
// * For WebAssembly the only functional linker is LLD, which doesn't
// support hint flags
!self.sess.target.is_like_osx && self.sess.target.arch != "wasm32"
!self.sess.target.is_like_osx && !self.sess.target.is_like_wasm
}
// Some platforms take hints about whether a library is static or dynamic.

View file

@ -107,7 +107,7 @@ pub struct ModuleConfig {
pub vectorize_loop: bool,
pub vectorize_slp: bool,
pub merge_functions: bool,
pub inline_threshold: Option<usize>,
pub inline_threshold: Option<u32>,
pub new_llvm_pass_manager: bool,
pub emit_lifetime_markers: bool,
}

View file

@ -754,7 +754,6 @@ impl CrateInfo {
is_no_builtins: Default::default(),
native_libraries: Default::default(),
used_libraries: tcx.native_libraries(LOCAL_CRATE).iter().map(Into::into).collect(),
link_args: tcx.link_args(LOCAL_CRATE),
crate_name: Default::default(),
used_crates_dynamic: cstore::used_crates(tcx, LinkagePreference::RequireDynamic),
used_crates_static: cstore::used_crates(tcx, LinkagePreference::RequireStatic),

View file

@ -139,7 +139,6 @@ pub struct CrateInfo {
pub native_libraries: FxHashMap<CrateNum, Vec<NativeLib>>,
pub crate_name: FxHashMap<CrateNum, String>,
pub used_libraries: Vec<NativeLib>,
pub link_args: Lrc<Vec<String>>,
pub used_crate_source: FxHashMap<CrateNum, Lrc<CrateSource>>,
pub used_crates_static: Vec<(CrateNum, LibSource)>,
pub used_crates_dynamic: Vec<(CrateNum, LibSource)>,

View file

@ -161,7 +161,7 @@ pub fn supported_target_features(sess: &Session) -> &'static [(&'static str, Opt
"mips" | "mips64" => MIPS_ALLOWED_FEATURES,
"powerpc" | "powerpc64" => POWERPC_ALLOWED_FEATURES,
"riscv32" | "riscv64" => RISCV_ALLOWED_FEATURES,
"wasm32" => WASM_ALLOWED_FEATURES,
"wasm32" | "wasm64" => WASM_ALLOWED_FEATURES,
_ => &[],
}
}

View file

@ -1,5 +1,4 @@
#![cfg_attr(bootstrap, deny(invalid_codeblock_attributes))]
#![cfg_attr(not(bootstrap), deny(rustdoc::invalid_codeblock_attributes))]
#![deny(rustdoc::invalid_codeblock_attributes)]
//! This library is used to gather all error codes into one place,
//! the goal being to make their maintenance easier.

View file

@ -195,6 +195,9 @@ pub trait Emitter {
fn emit_future_breakage_report(&mut self, _diags: Vec<(FutureBreakage, Diagnostic)>) {}
/// Emit list of unused externs
fn emit_unused_externs(&mut self, _lint_level: &str, _unused_externs: &[&str]) {}
/// Checks if should show explanations about "rustc --explain"
fn should_show_explain(&self) -> bool {
true

View file

@ -159,6 +159,19 @@ impl Emitter for JsonEmitter {
}
}
fn emit_unused_externs(&mut self, lint_level: &str, unused_externs: &[&str]) {
let data = UnusedExterns { lint_level, unused_extern_names: unused_externs };
let result = if self.pretty {
writeln!(&mut self.dst, "{}", as_pretty_json(&data))
} else {
writeln!(&mut self.dst, "{}", as_json(&data))
}
.and_then(|_| self.dst.flush());
if let Err(e) = result {
panic!("failed to print unused externs: {:?}", e);
}
}
fn source_map(&self) -> Option<&Lrc<SourceMap>> {
Some(&self.sm)
}
@ -322,6 +335,18 @@ struct FutureIncompatReport {
future_incompat_report: Vec<FutureBreakageItem>,
}
// NOTE: Keep this in sync with the equivalent structs in rustdoc's
// doctest component (as well as cargo).
// We could unify this struct the one in rustdoc but they have different
// ownership semantics, so doing so would create wasteful allocations.
#[derive(Encodable)]
struct UnusedExterns<'a, 'b, 'c> {
/// The severity level of the unused dependencies lint
lint_level: &'a str,
/// List of unused externs by their names.
unused_extern_names: &'b [&'c str],
}
impl Diagnostic {
fn from_errors_diagnostic(diag: &crate::Diagnostic, je: &JsonEmitter) -> Diagnostic {
let sugg = diag.suggestions.iter().map(|sugg| Diagnostic {

View file

@ -765,6 +765,10 @@ impl Handler {
self.inner.borrow_mut().emitter.emit_future_breakage_report(diags)
}
pub fn emit_unused_externs(&self, lint_level: &str, unused_externs: &[&str]) {
self.inner.borrow_mut().emit_unused_externs(lint_level, unused_externs)
}
pub fn delay_as_bug(&self, diagnostic: Diagnostic) {
self.inner.borrow_mut().delay_as_bug(diagnostic)
}
@ -839,6 +843,10 @@ impl HandlerInner {
self.emitter.emit_artifact_notification(path, artifact_type);
}
fn emit_unused_externs(&mut self, lint_level: &str, unused_externs: &[&str]) {
self.emitter.emit_unused_externs(lint_level, unused_externs);
}
fn treat_err_as_bug(&self) -> bool {
self.flags.treat_err_as_bug.map_or(false, |c| self.err_count() >= c.get())
}

View file

@ -868,6 +868,8 @@ impl SyntaxExtension {
/// Error type that denotes indeterminacy.
pub struct Indeterminate;
pub type DeriveResolutions = Vec<(ast::Path, Option<Lrc<SyntaxExtension>>)>;
pub trait ResolverExpand {
fn next_node_id(&mut self) -> NodeId;
@ -904,15 +906,12 @@ pub trait ResolverExpand {
fn resolve_derives(
&mut self,
expn_id: ExpnId,
derives: Vec<ast::Path>,
force: bool,
derive_paths: &dyn Fn() -> DeriveResolutions,
) -> Result<(), Indeterminate>;
/// Take resolutions for paths inside the `#[derive(...)]` attribute with the given `ExpnId`
/// back from resolver.
fn take_derive_resolutions(
&mut self,
expn_id: ExpnId,
) -> Option<Vec<(Lrc<SyntaxExtension>, ast::Path)>>;
fn take_derive_resolutions(&mut self, expn_id: ExpnId) -> Option<DeriveResolutions>;
/// Path resolution logic for `#[cfg_accessible(path)]`.
fn cfg_accessible(&mut self, expn_id: ExpnId, path: &ast::Path) -> Result<bool, Indeterminate>;
}

View file

@ -515,7 +515,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
invocations.reserve(derives.len());
derives
.into_iter()
.map(|(_exts, path)| {
.map(|(path, _exts)| {
// FIXME: Consider using the derive resolutions (`_exts`)
// instead of enqueuing the derives to be resolved again later.
let expn_id = ExpnId::fresh(None);

View file

@ -258,9 +258,6 @@ declare_features! (
// feature-group-start: actual feature gates
// -------------------------------------------------------------------------
/// Allows using the `#[link_args]` attribute.
(active, link_args, "1.0.0", Some(29596), None),
/// Allows defining identifiers beyond ASCII.
(active, non_ascii_idents, "1.0.0", Some(55467), None),

View file

@ -279,11 +279,6 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
// Linking:
gated!(naked, AssumedUsed, template!(Word), naked_functions, experimental!(naked)),
gated!(
link_args, Normal, template!(NameValueStr: "args"),
"the `link_args` attribute is experimental and not portable across platforms, \
it is recommended to use `#[link(name = \"foo\")] instead",
),
gated!(
link_ordinal, AssumedUsed, template!(List: "ordinal"), raw_dylib,
experimental!(link_ordinal)

View file

@ -128,6 +128,10 @@ declare_features! (
/// Allows comparing raw pointers during const eval.
(removed, const_compare_raw_pointers, "1.46.0", Some(53020), None,
Some("cannot be allowed in const eval in any meaningful way")),
/// Allows using the `#[link_args]` attribute.
(removed, link_args, "1.53.0", Some(29596), None,
Some("removed in favor of using `-C link-arg=ARG` on command line, \
which is available from cargo build scripts with `cargo:rustc-link-arg` now")),
// -------------------------------------------------------------------------
// feature-group-end: removed features

View file

@ -16,6 +16,7 @@ use rustc_hir::def_id::{CrateNum, LOCAL_CRATE};
use rustc_hir::definitions::Definitions;
use rustc_hir::Crate;
use rustc_lint::LintStore;
use rustc_metadata::creader::CStore;
use rustc_middle::arena::Arena;
use rustc_middle::dep_graph::DepGraph;
use rustc_middle::middle;
@ -831,6 +832,12 @@ fn analysis(tcx: TyCtxt<'_>, cnum: CrateNum) -> Result<()> {
});
sess.time("looking_for_derive_registrar", || proc_macro_decls::find(tcx));
let cstore = tcx
.cstore_as_any()
.downcast_ref::<CStore>()
.expect("`tcx.cstore` is not a `CStore`");
cstore.report_unused_deps(tcx);
},
{
par_iter(&tcx.hir().krate().modules).for_each(|(&module, _)| {

View file

@ -46,6 +46,9 @@ pub struct CStore {
/// This map is used to verify we get no hash conflicts between
/// `StableCrateId` values.
stable_crate_ids: FxHashMap<StableCrateId, CrateNum>,
/// Unused externs of the crate
unused_externs: Vec<Symbol>,
}
pub struct CrateLoader<'a> {
@ -190,6 +193,27 @@ impl CStore {
crate fn has_global_allocator(&self) -> bool {
self.has_global_allocator
}
pub fn report_unused_deps(&self, tcx: TyCtxt<'_>) {
// We put the check for the option before the lint_level_at_node call
// because the call mutates internal state and introducing it
// leads to some ui tests failing.
if !tcx.sess.opts.json_unused_externs {
return;
}
let level = tcx
.lint_level_at_node(lint::builtin::UNUSED_CRATE_DEPENDENCIES, rustc_hir::CRATE_HIR_ID)
.0;
if level != lint::Level::Allow {
let unused_externs =
self.unused_externs.iter().map(|ident| ident.to_ident_string()).collect::<Vec<_>>();
let unused_externs = unused_externs.iter().map(String::as_str).collect::<Vec<&str>>();
tcx.sess
.parse_sess
.span_diagnostic
.emit_unused_externs(level.as_str(), &unused_externs);
}
}
}
impl<'a> CrateLoader<'a> {
@ -217,6 +241,7 @@ impl<'a> CrateLoader<'a> {
allocator_kind: None,
has_global_allocator: false,
stable_crate_ids,
unused_externs: Vec::new(),
},
used_extern_options: Default::default(),
}
@ -904,11 +929,17 @@ impl<'a> CrateLoader<'a> {
// Don't worry about pathless `--extern foo` sysroot references
continue;
}
if self.used_extern_options.contains(&Symbol::intern(name)) {
let name_interned = Symbol::intern(name);
if self.used_extern_options.contains(&name_interned) {
continue;
}
// Got a real unused --extern
if self.sess.opts.json_unused_externs {
self.cstore.unused_externs.push(name_interned);
continue;
}
let diag = match self.sess.opts.extern_dep_specs.get(name) {
Some(loc) => BuiltinLintDiagnostics::ExternDepSpec(name.clone(), loc.into()),
None => {
@ -941,9 +972,9 @@ impl<'a> CrateLoader<'a> {
self.inject_allocator_crate(krate);
self.inject_panic_runtime(krate);
info!("{:?}", CrateDump(&self.cstore));
self.report_unused_deps(krate);
info!("{:?}", CrateDump(&self.cstore));
}
pub fn process_extern_crate(

View file

@ -26,7 +26,6 @@ pub use rmeta::{provide, provide_extern};
mod dependency_format;
mod foreign_modules;
mod link_args;
mod native_libs;
mod rmeta;

View file

@ -1,57 +0,0 @@
use rustc_hir as hir;
use rustc_hir::itemlikevisit::ItemLikeVisitor;
use rustc_middle::ty::TyCtxt;
use rustc_span::symbol::{sym, Symbol};
use rustc_target::spec::abi::Abi;
crate fn collect(tcx: TyCtxt<'_>) -> Vec<String> {
let mut collector = Collector { tcx, args: Vec::new() };
tcx.hir().krate().visit_all_item_likes(&mut collector);
for attr in tcx.hir().attrs(hir::CRATE_HIR_ID).iter() {
if attr.has_name(sym::link_args) {
if let Some(linkarg) = attr.value_str() {
collector.add_link_args(linkarg);
}
}
}
collector.args
}
struct Collector<'tcx> {
tcx: TyCtxt<'tcx>,
args: Vec<String>,
}
impl<'tcx> ItemLikeVisitor<'tcx> for Collector<'tcx> {
fn visit_item(&mut self, it: &'tcx hir::Item<'tcx>) {
let abi = match it.kind {
hir::ItemKind::ForeignMod { abi, .. } => abi,
_ => return,
};
if abi == Abi::Rust || abi == Abi::RustIntrinsic || abi == Abi::PlatformIntrinsic {
return;
}
// First, add all of the custom #[link_args] attributes
let sess = &self.tcx.sess;
for m in
self.tcx.hir().attrs(it.hir_id()).iter().filter(|a| sess.check_name(a, sym::link_args))
{
if let Some(linkarg) = m.value_str() {
self.add_link_args(linkarg);
}
}
}
fn visit_trait_item(&mut self, _it: &'tcx hir::TraitItem<'tcx>) {}
fn visit_impl_item(&mut self, _it: &'tcx hir::ImplItem<'tcx>) {}
fn visit_foreign_item(&mut self, _it: &'tcx hir::ForeignItem<'tcx>) {}
}
impl<'tcx> Collector<'tcx> {
fn add_link_args(&mut self, args: Symbol) {
self.args.extend(args.as_str().split(' ').filter(|s| !s.is_empty()).map(|s| s.to_string()))
}
}

View file

@ -1,6 +1,5 @@
use crate::creader::{CStore, LoadedMacro};
use crate::foreign_modules;
use crate::link_args;
use crate::native_libs;
use crate::rmeta::{self, encoder};
@ -295,10 +294,6 @@ pub fn provide(providers: &mut Providers) {
foreign_modules::collect(tcx).into_iter().map(|m| (m.def_id, m)).collect();
Lrc::new(modules)
},
link_args: |tcx, cnum| {
assert_eq!(cnum, LOCAL_CRATE);
Lrc::new(link_args::collect(tcx))
},
// Returns a map from a sufficiently visible external item (i.e., an
// external item that is visible from at least one local module) to a

View file

@ -1482,7 +1482,7 @@ pub enum StatementKind<'tcx> {
///
/// Note that this also is emitted for regular `let` bindings to ensure that locals that are
/// never accessed still get some sanity checks for, e.g., `let x: ! = ..;`
FakeRead(FakeReadCause, Box<Place<'tcx>>),
FakeRead(Box<(FakeReadCause, Place<'tcx>)>),
/// Write the discriminant for a variant to the enum Place.
SetDiscriminant { place: Box<Place<'tcx>>, variant_index: VariantIdx },
@ -1575,7 +1575,12 @@ pub enum FakeReadCause {
/// `let x: !; match x {}` doesn't generate any read of x so we need to
/// generate a read of x to check that it is initialized and safe.
ForMatchedPlace,
///
/// If a closure pattern matches a Place starting with an Upvar, then we introduce a
/// FakeRead for that Place outside the closure, in such a case this option would be
/// Some(closure_def_id).
/// Otherwise, the value of the optional DefId will be None.
ForMatchedPlace(Option<DefId>),
/// A fake read of the RefWithinGuard version of a bind-by-value variable
/// in a match guard to ensure that it's value hasn't change by the time
@ -1594,7 +1599,12 @@ pub enum FakeReadCause {
/// but in some cases it can affect the borrow checker, as in #53695.
/// Therefore, we insert a "fake read" here to ensure that we get
/// appropriate errors.
ForLet,
///
/// If a closure pattern matches a Place starting with an Upvar, then we introduce a
/// FakeRead for that Place outside the closure, in such a case this option would be
/// Some(closure_def_id).
/// Otherwise, the value of the optional DefId will be None.
ForLet(Option<DefId>),
/// If we have an index expression like
///
@ -1618,7 +1628,9 @@ impl Debug for Statement<'_> {
use self::StatementKind::*;
match self.kind {
Assign(box (ref place, ref rv)) => write!(fmt, "{:?} = {:?}", place, rv),
FakeRead(ref cause, ref place) => write!(fmt, "FakeRead({:?}, {:?})", cause, place),
FakeRead(box (ref cause, ref place)) => {
write!(fmt, "FakeRead({:?}, {:?})", cause, place)
}
Retag(ref kind, ref place) => write!(
fmt,
"Retag({}{:?})",

View file

@ -380,7 +380,7 @@ macro_rules! make_mir_visitor {
) => {
self.visit_assign(place, rvalue, location);
}
StatementKind::FakeRead(_, place) => {
StatementKind::FakeRead(box (_, place)) => {
self.visit_place(
place,
PlaceContext::NonMutatingUse(NonMutatingUseContext::Inspect),

View file

@ -1253,11 +1253,6 @@ rustc_queries! {
desc { |tcx| "native_library_kind({})", tcx.def_path_str(def_id) }
}
query link_args(_: CrateNum) -> Lrc<Vec<String>> {
eval_always
desc { "looking up link arguments for a crate" }
}
/// Does lifetime resolution, but does not descend into trait items. This
/// should only be used for resolving lifetimes of on trait definitions,
/// and is used to avoid cycles. Importantly, `resolve_lifetimes` still visits

View file

@ -1728,7 +1728,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
impl<'tcx> Visitor<'tcx> for FakeReadCauseFinder<'tcx> {
fn visit_statement(&mut self, statement: &Statement<'tcx>, _: Location) {
match statement {
Statement { kind: StatementKind::FakeRead(cause, box place), .. }
Statement { kind: StatementKind::FakeRead(box (cause, place)), .. }
if *place == self.place =>
{
self.cause = Some(*cause);

View file

@ -515,7 +515,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
let block = &self.body.basic_blocks()[location.block];
let kind = if let Some(&Statement {
kind: StatementKind::FakeRead(FakeReadCause::ForLet, _),
kind: StatementKind::FakeRead(box (FakeReadCause::ForLet(_), _)),
..
}) = block.statements.get(location.statement_index)
{

View file

@ -7,8 +7,8 @@ use rustc_hir::def_id::DefId;
use rustc_hir::lang_items::LangItemGroup;
use rustc_hir::GeneratorKind;
use rustc_middle::mir::{
AggregateKind, Constant, Field, Local, LocalInfo, LocalKind, Location, Operand, Place,
PlaceRef, ProjectionElem, Rvalue, Statement, StatementKind, Terminator, TerminatorKind,
AggregateKind, Constant, FakeReadCause, Field, Local, LocalInfo, LocalKind, Location, Operand,
Place, PlaceRef, ProjectionElem, Rvalue, Statement, StatementKind, Terminator, TerminatorKind,
};
use rustc_middle::ty::print::Print;
use rustc_middle::ty::{self, DefIdTree, Instance, Ty, TyCtxt};
@ -795,6 +795,24 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
}
}
// StatementKind::FakeRead only contains a def_id if they are introduced as a result
// of pattern matching within a closure.
if let StatementKind::FakeRead(box (cause, ref place)) = stmt.kind {
match cause {
FakeReadCause::ForMatchedPlace(Some(closure_def_id))
| FakeReadCause::ForLet(Some(closure_def_id)) => {
debug!("move_spans: def_id={:?} place={:?}", closure_def_id, place);
let places = &[Operand::Move(*place)];
if let Some((args_span, generator_kind, var_span)) =
self.closure_span(closure_def_id, moved_place, places)
{
return ClosureUse { generator_kind, args_span, var_span };
}
}
_ => {}
}
}
let normal_ret =
if moved_place.projection.iter().any(|p| matches!(p, ProjectionElem::Downcast(..))) {
PatUse(stmt.source_info.span)

View file

@ -63,7 +63,7 @@ impl<'cx, 'tcx> Visitor<'tcx> for InvalidationGenerator<'cx, 'tcx> {
self.mutate_place(location, *lhs, Shallow(None), JustWrite);
}
StatementKind::FakeRead(_, _) => {
StatementKind::FakeRead(box (_, _)) => {
// Only relevant for initialized/liveness/safety checks.
}
StatementKind::SetDiscriminant { place, variant_index: _ } => {

View file

@ -574,7 +574,7 @@ impl<'cx, 'tcx> dataflow::ResultsVisitor<'cx, 'tcx> for MirBorrowckCtxt<'cx, 'tc
self.mutate_place(location, (*lhs, span), Shallow(None), JustWrite, flow_state);
}
StatementKind::FakeRead(_, box ref place) => {
StatementKind::FakeRead(box (_, ref place)) => {
// Read for match doesn't access any memory and is used to
// assert that a place is safe and live. So we don't have to
// do any checks here.

View file

@ -293,8 +293,8 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> {
}
self.gather_rvalue(rval);
}
StatementKind::FakeRead(_, place) => {
self.create_move_path(**place);
StatementKind::FakeRead(box (_, place)) => {
self.create_move_path(*place);
}
StatementKind::LlvmInlineAsm(ref asm) => {
for (output, kind) in iter::zip(&*asm.outputs, &asm.asm.outputs) {

View file

@ -683,10 +683,10 @@ pub(super) fn filtered_statement_span(
// and `_1` is the `Place` for `somenum`.
//
// If and when the Issue is resolved, remove this special case match pattern:
StatementKind::FakeRead(cause, _) if cause == FakeReadCause::ForGuardBinding => None,
StatementKind::FakeRead(box (cause, _)) if cause == FakeReadCause::ForGuardBinding => None,
// Retain spans from all other statements
StatementKind::FakeRead(_, _) // Not including `ForGuardBinding`
StatementKind::FakeRead(box (_, _)) // Not including `ForGuardBinding`
| StatementKind::CopyNonOverlapping(..)
| StatementKind::Assign(_)
| StatementKind::SetDiscriminant { .. }

View file

@ -80,7 +80,7 @@ impl<'tcx> CFG<'tcx> {
cause: FakeReadCause,
place: Place<'tcx>,
) {
let kind = StatementKind::FakeRead(cause, box place);
let kind = StatementKind::FakeRead(box (cause, place));
let stmt = Statement { source_info, kind };
self.push(block, stmt);
}

View file

@ -179,24 +179,20 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
// match x { _ => () } // fake read of `x`
// };
// ```
// FIXME(RFC2229): Remove feature gate once diagnostics are improved
if this.tcx.features().capture_disjoint_fields {
for (thir_place, cause, hir_id) in fake_reads.into_iter() {
let place_builder =
unpack!(block = this.as_place_builder(block, thir_place));
for (thir_place, cause, hir_id) in fake_reads.into_iter() {
let place_builder = unpack!(block = this.as_place_builder(block, thir_place));
if let Ok(place_builder_resolved) =
place_builder.try_upvars_resolved(this.tcx, this.typeck_results)
{
let mir_place =
place_builder_resolved.into_place(this.tcx, this.typeck_results);
this.cfg.push_fake_read(
block,
this.source_info(this.tcx.hir().span(*hir_id)),
*cause,
mir_place,
);
}
if let Ok(place_builder_resolved) =
place_builder.try_upvars_resolved(this.tcx, this.typeck_results)
{
let mir_place =
place_builder_resolved.into_place(this.tcx, this.typeck_results);
this.cfg.push_fake_read(
block,
this.source_info(this.tcx.hir().span(*hir_id)),
*cause,
mir_place,
);
}
}

View file

@ -139,7 +139,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
// uninhabited value. If we get never patterns, those will check that
// the place is initialized, and so this read would only be used to
// check safety.
let cause_matched_place = FakeReadCause::ForMatchedPlace;
let cause_matched_place = FakeReadCause::ForMatchedPlace(None);
let source_info = self.source_info(scrutinee_span);
if let Ok(scrutinee_builder) =
@ -400,7 +400,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
// Inject a fake read, see comments on `FakeReadCause::ForLet`.
let source_info = self.source_info(irrefutable_pat.span);
self.cfg.push_fake_read(block, source_info, FakeReadCause::ForLet, place);
self.cfg.push_fake_read(block, source_info, FakeReadCause::ForLet(None), place);
self.schedule_drop_for_binding(var, irrefutable_pat.span, OutsideGuard);
block.unit()
@ -435,7 +435,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
// Inject a fake read, see comments on `FakeReadCause::ForLet`.
let pattern_source_info = self.source_info(irrefutable_pat.span);
let cause_let = FakeReadCause::ForLet;
let cause_let = FakeReadCause::ForLet(None);
self.cfg.push_fake_read(block, pattern_source_info, cause_let, place);
let ty_source_info = self.source_info(user_ty_span);

View file

@ -37,7 +37,7 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap};
use rustc_data_structures::ptr_key::PtrKey;
use rustc_data_structures::sync::Lrc;
use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder};
use rustc_expand::base::{SyntaxExtension, SyntaxExtensionKind};
use rustc_expand::base::{DeriveResolutions, SyntaxExtension, SyntaxExtensionKind};
use rustc_hir::def::Namespace::*;
use rustc_hir::def::{self, CtorOf, DefKind, NonMacroAttrKind, PartialRes};
use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LocalDefId, CRATE_DEF_INDEX};
@ -851,6 +851,12 @@ enum BuiltinMacroState {
AlreadySeen(Span),
}
struct DeriveData {
resolutions: DeriveResolutions,
helper_attrs: Vec<(usize, Ident)>,
has_derive_copy: bool,
}
/// The main resolver class.
///
/// This is the visitor that walks the whole crate.
@ -973,8 +979,9 @@ pub struct Resolver<'a> {
output_macro_rules_scopes: FxHashMap<ExpnId, MacroRulesScopeRef<'a>>,
/// Helper attributes that are in scope for the given expansion.
helper_attrs: FxHashMap<ExpnId, Vec<Ident>>,
/// Resolutions for paths inside the `#[derive(...)]` attribute with the given `ExpnId`.
derive_resolutions: FxHashMap<ExpnId, Vec<(Lrc<SyntaxExtension>, ast::Path)>>,
/// Ready or in-progress results of resolving paths inside the `#[derive(...)]` attribute
/// with the given `ExpnId`.
derive_data: FxHashMap<ExpnId, DeriveData>,
/// Avoid duplicated errors for "name already defined".
name_already_seen: FxHashMap<Symbol, Span>,
@ -1310,7 +1317,7 @@ impl<'a> Resolver<'a> {
invocation_parent_scopes: Default::default(),
output_macro_rules_scopes: Default::default(),
helper_attrs: Default::default(),
derive_resolutions: Default::default(),
derive_data: Default::default(),
local_macro_def_scopes: FxHashMap::default(),
name_already_seen: FxHashMap::default(),
potentially_unused_imports: Vec::new(),

View file

@ -4,7 +4,7 @@
use crate::imports::ImportResolver;
use crate::Namespace::*;
use crate::{AmbiguityError, AmbiguityErrorMisc, AmbiguityKind, BuiltinMacroState, Determinacy};
use crate::{CrateLint, ParentScope, ResolutionError, Resolver, Scope, ScopeSet, Weak};
use crate::{CrateLint, DeriveData, ParentScope, ResolutionError, Resolver, Scope, ScopeSet, Weak};
use crate::{ModuleKind, ModuleOrUniformRoot, NameBinding, PathResult, Segment, ToNameBinding};
use rustc_ast::{self as ast, Inline, ItemKind, ModKind, NodeId};
use rustc_ast_lowering::ResolverAstLowering;
@ -14,8 +14,8 @@ use rustc_data_structures::fx::FxHashSet;
use rustc_data_structures::ptr_key::PtrKey;
use rustc_data_structures::sync::Lrc;
use rustc_errors::struct_span_err;
use rustc_expand::base::Annotatable;
use rustc_expand::base::{Indeterminate, ResolverExpand, SyntaxExtension, SyntaxExtensionKind};
use rustc_expand::base::{Annotatable, DeriveResolutions, Indeterminate, ResolverExpand};
use rustc_expand::base::{SyntaxExtension, SyntaxExtensionKind};
use rustc_expand::compile_declarative_macro;
use rustc_expand::expand::{AstFragment, Invocation, InvocationKind, SupportsMacroExpansion};
use rustc_feature::is_builtin_attr_name;
@ -359,8 +359,8 @@ impl<'a> ResolverExpand for Resolver<'a> {
fn resolve_derives(
&mut self,
expn_id: ExpnId,
derives: Vec<ast::Path>,
force: bool,
derive_paths: &dyn Fn() -> DeriveResolutions,
) -> Result<(), Indeterminate> {
// Block expansion of the container until we resolve all derives in it.
// This is required for two reasons:
@ -368,49 +368,63 @@ impl<'a> ResolverExpand for Resolver<'a> {
// is applied, so they have to be produced by the container's expansion rather
// than by individual derives.
// - Derives in the container need to know whether one of them is a built-in `Copy`.
// FIXME: Try to cache intermediate results to avoid resolving same derives multiple times.
// Temporarily take the data to avoid borrow checker conflicts.
let mut derive_data = mem::take(&mut self.derive_data);
let entry = derive_data.entry(expn_id).or_insert_with(|| DeriveData {
resolutions: derive_paths(),
helper_attrs: Vec::new(),
has_derive_copy: false,
});
let parent_scope = self.invocation_parent_scopes[&expn_id];
let mut exts = Vec::new();
let mut helper_attrs = Vec::new();
let mut has_derive_copy = false;
for path in derives {
exts.push((
match self.resolve_macro_path(
&path,
Some(MacroKind::Derive),
&parent_scope,
true,
force,
) {
Ok((Some(ext), _)) => {
let span =
path.segments.last().unwrap().ident.span.normalize_to_macros_2_0();
helper_attrs
.extend(ext.helper_attrs.iter().map(|name| Ident::new(*name, span)));
has_derive_copy |= ext.builtin_name == Some(sym::Copy);
ext
}
Ok(_) | Err(Determinacy::Determined) => self.dummy_ext(MacroKind::Derive),
Err(Determinacy::Undetermined) => return Err(Indeterminate),
},
path,
))
for (i, (path, opt_ext)) in entry.resolutions.iter_mut().enumerate() {
if opt_ext.is_none() {
*opt_ext = Some(
match self.resolve_macro_path(
&path,
Some(MacroKind::Derive),
&parent_scope,
true,
force,
) {
Ok((Some(ext), _)) => {
if !ext.helper_attrs.is_empty() {
let last_seg = path.segments.last().unwrap();
let span = last_seg.ident.span.normalize_to_macros_2_0();
entry.helper_attrs.extend(
ext.helper_attrs
.iter()
.map(|name| (i, Ident::new(*name, span))),
);
}
entry.has_derive_copy |= ext.builtin_name == Some(sym::Copy);
ext
}
Ok(_) | Err(Determinacy::Determined) => self.dummy_ext(MacroKind::Derive),
Err(Determinacy::Undetermined) => {
assert!(self.derive_data.is_empty());
self.derive_data = derive_data;
return Err(Indeterminate);
}
},
);
}
}
self.derive_resolutions.insert(expn_id, exts);
self.helper_attrs.insert(expn_id, helper_attrs);
// Sort helpers in a stable way independent from the derive resolution order.
entry.helper_attrs.sort_by_key(|(i, _)| *i);
self.helper_attrs
.insert(expn_id, entry.helper_attrs.iter().map(|(_, ident)| *ident).collect());
// Mark this derive as having `Copy` either if it has `Copy` itself or if its parent derive
// has `Copy`, to support cases like `#[derive(Clone, Copy)] #[derive(Debug)]`.
if has_derive_copy || self.has_derive_copy(parent_scope.expansion) {
if entry.has_derive_copy || self.has_derive_copy(parent_scope.expansion) {
self.containers_deriving_copy.insert(expn_id);
}
assert!(self.derive_data.is_empty());
self.derive_data = derive_data;
Ok(())
}
fn take_derive_resolutions(
&mut self,
expn_id: ExpnId,
) -> Option<Vec<(Lrc<SyntaxExtension>, ast::Path)>> {
self.derive_resolutions.remove(&expn_id)
fn take_derive_resolutions(&mut self, expn_id: ExpnId) -> Option<DeriveResolutions> {
self.derive_data.remove(&expn_id).map(|data| data.resolutions)
}
// The function that implements the resolution logic of `#[cfg_accessible(path)]`.

View file

@ -456,6 +456,10 @@ impl Externs {
pub fn iter(&self) -> BTreeMapIter<'_, String, ExternEntry> {
self.0.iter()
}
pub fn len(&self) -> usize {
self.0.len()
}
}
impl ExternEntry {
@ -698,6 +702,7 @@ impl Default for Options {
remap_path_prefix: Vec::new(),
edition: DEFAULT_EDITION,
json_artifact_notifications: false,
json_unused_externs: false,
pretty: None,
}
}
@ -817,6 +822,9 @@ pub fn default_configuration(sess: &Session) -> CrateConfig {
}
}
ret.insert((sym::target_arch, Some(Symbol::intern(arch))));
if sess.target.is_like_wasm {
ret.insert((sym::wasm, None));
}
ret.insert((sym::target_endian, Some(Symbol::intern(end.as_str()))));
ret.insert((sym::target_pointer_width, Some(Symbol::intern(&wordsz))));
ret.insert((sym::target_env, Some(Symbol::intern(env))));
@ -1196,15 +1204,23 @@ pub fn parse_color(matches: &getopts::Matches) -> ColorConfig {
}
}
/// Possible json config files
pub struct JsonConfig {
pub json_rendered: HumanReadableErrorType,
pub json_artifact_notifications: bool,
pub json_unused_externs: bool,
}
/// Parse the `--json` flag.
///
/// The first value returned is how to render JSON diagnostics, and the second
/// is whether or not artifact notifications are enabled.
pub fn parse_json(matches: &getopts::Matches) -> (HumanReadableErrorType, bool) {
pub fn parse_json(matches: &getopts::Matches) -> JsonConfig {
let mut json_rendered: fn(ColorConfig) -> HumanReadableErrorType =
HumanReadableErrorType::Default;
let mut json_color = ColorConfig::Never;
let mut json_artifact_notifications = false;
let mut json_unused_externs = false;
for option in matches.opt_strs("json") {
// For now conservatively forbid `--color` with `--json` since `--json`
// won't actually be emitting any colors and anything colorized is
@ -1221,6 +1237,7 @@ pub fn parse_json(matches: &getopts::Matches) -> (HumanReadableErrorType, bool)
"diagnostic-short" => json_rendered = HumanReadableErrorType::Short,
"diagnostic-rendered-ansi" => json_color = ColorConfig::Always,
"artifacts" => json_artifact_notifications = true,
"unused-externs" => json_unused_externs = true,
s => early_error(
ErrorOutputType::default(),
&format!("unknown `--json` option `{}`", s),
@ -1228,7 +1245,12 @@ pub fn parse_json(matches: &getopts::Matches) -> (HumanReadableErrorType, bool)
}
}
}
(json_rendered(json_color), json_artifact_notifications)
JsonConfig {
json_rendered: json_rendered(json_color),
json_artifact_notifications,
json_unused_externs,
}
}
/// Parses the `--error-format` flag.
@ -1806,7 +1828,8 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
let edition = parse_crate_edition(matches);
let (json_rendered, json_artifact_notifications) = parse_json(matches);
let JsonConfig { json_rendered, json_artifact_notifications, json_unused_externs } =
parse_json(matches);
let error_format = parse_error_format(matches, color, json_rendered);
@ -1819,6 +1842,14 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
let mut debugging_opts = build_debugging_options(matches, error_format);
check_debug_option_stability(&debugging_opts, error_format, json_rendered);
if !debugging_opts.unstable_options && json_unused_externs {
early_error(
error_format,
"the `-Z unstable-options` flag must also be passed to enable \
the flag `--json=unused-externs`",
);
}
let output_types = parse_output_types(&debugging_opts, matches, error_format);
let mut cg = build_codegen_options(matches, error_format);
@ -1979,6 +2010,7 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
remap_path_prefix,
edition,
json_artifact_notifications,
json_unused_externs,
pretty,
}
}
@ -2300,6 +2332,7 @@ crate mod dep_tracking {
impl_dep_tracking_hash_via_hash!(PathBuf);
impl_dep_tracking_hash_via_hash!(lint::Level);
impl_dep_tracking_hash_via_hash!(Option<bool>);
impl_dep_tracking_hash_via_hash!(Option<u32>);
impl_dep_tracking_hash_via_hash!(Option<usize>);
impl_dep_tracking_hash_via_hash!(Option<NonZeroUsize>);
impl_dep_tracking_hash_via_hash!(Option<String>);

View file

@ -147,6 +147,9 @@ top_level_options!(
// by the compiler.
json_artifact_notifications: bool [TRACKED],
// `true` if we're emitting a JSON blob containing the unused externs
json_unused_externs: bool [UNTRACKED],
pretty: Option<PpMode> [UNTRACKED],
}
);
@ -248,9 +251,9 @@ macro_rules! options {
pub const parse_list: &str = "a space-separated list of strings";
pub const parse_opt_list: &str = parse_list;
pub const parse_opt_comma_list: &str = "a comma-separated list of strings";
pub const parse_uint: &str = "a number";
pub const parse_opt_uint: &str = parse_uint;
pub const parse_threads: &str = parse_uint;
pub const parse_number: &str = "a number";
pub const parse_opt_number: &str = parse_number;
pub const parse_threads: &str = parse_number;
pub const parse_passes: &str = "a space-separated list of passes, or `all`";
pub const parse_panic_strategy: &str = "either `unwind` or `abort`";
pub const parse_relro_level: &str = "one of: `full`, `partial`, or `off`";
@ -414,16 +417,16 @@ macro_rules! options {
}
}
/// Use this for any uint option that has a static default.
fn parse_uint(slot: &mut usize, v: Option<&str>) -> bool {
/// Use this for any numeric option that has a static default.
fn parse_number<T: Copy + FromStr>(slot: &mut T, v: Option<&str>) -> bool {
match v.and_then(|s| s.parse().ok()) {
Some(i) => { *slot = i; true },
None => false
}
}
/// Use this for any uint option that lacks a static default.
fn parse_opt_uint(slot: &mut Option<usize>, v: Option<&str>) -> bool {
/// Use this for any numeric option that lacks a static default.
fn parse_opt_number<T: Copy + FromStr>(slot: &mut Option<T>, v: Option<&str>) -> bool {
match v {
Some(s) => { *slot = s.parse().ok(); slot.is_some() }
None => false
@ -784,13 +787,13 @@ options! {CodegenOptions, CodegenSetter, basic_codegen_options,
"this option is deprecated and does nothing"),
code_model: Option<CodeModel> = (None, parse_code_model, [TRACKED],
"choose the code model to use (`rustc --print code-models` for details)"),
codegen_units: Option<usize> = (None, parse_opt_uint, [UNTRACKED],
codegen_units: Option<usize> = (None, parse_opt_number, [UNTRACKED],
"divide crate into N units to optimize in parallel"),
control_flow_guard: CFGuard = (CFGuard::Disabled, parse_cfguard, [TRACKED],
"use Windows Control Flow Guard (default: no)"),
debug_assertions: Option<bool> = (None, parse_opt_bool, [TRACKED],
"explicitly enable the `cfg(debug_assertions)` directive"),
debuginfo: usize = (0, parse_uint, [TRACKED],
debuginfo: usize = (0, parse_number, [TRACKED],
"debug info emission level (0 = no debug info, 1 = line tables only, \
2 = full debug info with variable and type information; default: 0)"),
default_linker_libraries: bool = (false, parse_bool, [UNTRACKED],
@ -805,7 +808,7 @@ options! {CodegenOptions, CodegenSetter, basic_codegen_options,
"force use of unwind tables"),
incremental: Option<String> = (None, parse_opt_string, [UNTRACKED],
"enable incremental compilation"),
inline_threshold: Option<usize> = (None, parse_opt_uint, [TRACKED],
inline_threshold: Option<u32> = (None, parse_opt_number, [TRACKED],
"set the threshold for inlining a function"),
link_arg: (/* redirected to link_args */) = ((), parse_string_push, [UNTRACKED],
"a single extra argument to append to the linker invocation (can be used several times)"),
@ -993,9 +996,9 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options,
"verify incr. comp. hashes of green query instances (default: no)"),
inline_mir: Option<bool> = (None, parse_opt_bool, [TRACKED],
"enable MIR inlining (default: no)"),
inline_mir_threshold: Option<usize> = (None, parse_opt_uint, [TRACKED],
inline_mir_threshold: Option<usize> = (None, parse_opt_number, [TRACKED],
"a default MIR inlining threshold (default: 50)"),
inline_mir_hint_threshold: Option<usize> = (None, parse_opt_uint, [TRACKED],
inline_mir_hint_threshold: Option<usize> = (None, parse_opt_number, [TRACKED],
"inlining threshold for functions with inline hint (default: 100)"),
inline_in_all_cgus: Option<bool> = (None, parse_opt_bool, [TRACKED],
"control whether `#[inline]` functions are in all CGUs"),
@ -1031,7 +1034,7 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options,
mir_emit_retag: bool = (false, parse_bool, [TRACKED],
"emit Retagging MIR statements, interpreted e.g., by miri; implies -Zmir-opt-level=0 \
(default: no)"),
mir_opt_level: Option<usize> = (None, parse_opt_uint, [TRACKED],
mir_opt_level: Option<usize> = (None, parse_opt_number, [TRACKED],
"MIR optimization level (0-4; default: 1 in non optimized builds and 2 in optimized builds)"),
mutable_noalias: Option<bool> = (None, parse_opt_bool, [TRACKED],
"emit noalias metadata for mutable references (default: yes for LLVM >= 12, otherwise no)"),
@ -1152,7 +1155,7 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options,
"which mangling version to use for symbol names ('legacy' (default) or 'v0')"),
teach: bool = (false, parse_bool, [TRACKED],
"show extended diagnostic help (default: no)"),
terminal_width: Option<usize> = (None, parse_opt_uint, [UNTRACKED],
terminal_width: Option<usize> = (None, parse_opt_number, [UNTRACKED],
"set the current terminal width"),
tune_cpu: Option<String> = (None, parse_opt_string, [TRACKED],
"select processor to schedule for (`rustc --print target-cpus` for details)"),

View file

@ -1294,6 +1294,7 @@ symbols! {
vreg,
vreg_low16,
warn,
wasm,
wasm_import_module,
wasm_target_feature,
while_let,

View file

@ -198,7 +198,7 @@ fn compute_symbol_name(
//
// [1]: https://bugs.llvm.org/show_bug.cgi?id=44316
if is_foreign
&& (tcx.sess.target.arch != "wasm32"
&& (!tcx.sess.target.is_like_wasm
|| !tcx.wasm_import_module_map(def_id.krate).contains_key(&def_id))
{
if let Some(name) = attrs.link_name {

View file

@ -20,6 +20,7 @@ mod sparc;
mod sparc64;
mod wasm32;
mod wasm32_bindgen_compat;
mod wasm64;
mod x86;
mod x86_64;
mod x86_win64;
@ -652,6 +653,7 @@ impl<'a, Ty> FnAbi<'a, Ty> {
_ => wasm32_bindgen_compat::compute_abi_info(self),
},
"asmjs" => wasm32::compute_abi_info(cx, self),
"wasm64" => wasm64::compute_abi_info(cx, self),
a => return Err(format!("unrecognized arch \"{}\" in target specification", a)),
}

View file

@ -0,0 +1,58 @@
use crate::abi::call::{ArgAbi, FnAbi, Uniform};
use crate::abi::{HasDataLayout, LayoutOf, TyAndLayout, TyAndLayoutMethods};
fn unwrap_trivial_aggregate<'a, Ty, C>(cx: &C, val: &mut ArgAbi<'a, Ty>) -> bool
where
Ty: TyAndLayoutMethods<'a, C> + Copy,
C: LayoutOf<Ty = Ty, TyAndLayout = TyAndLayout<'a, Ty>> + HasDataLayout,
{
if val.layout.is_aggregate() {
if let Some(unit) = val.layout.homogeneous_aggregate(cx).ok().and_then(|ha| ha.unit()) {
let size = val.layout.size;
if unit.size == size {
val.cast_to(Uniform { unit, total: size });
return true;
}
}
}
false
}
fn classify_ret<'a, Ty, C>(cx: &C, ret: &mut ArgAbi<'a, Ty>)
where
Ty: TyAndLayoutMethods<'a, C> + Copy,
C: LayoutOf<Ty = Ty, TyAndLayout = TyAndLayout<'a, Ty>> + HasDataLayout,
{
ret.extend_integer_width_to(64);
if ret.layout.is_aggregate() && !unwrap_trivial_aggregate(cx, ret) {
ret.make_indirect();
}
}
fn classify_arg<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>)
where
Ty: TyAndLayoutMethods<'a, C> + Copy,
C: LayoutOf<Ty = Ty, TyAndLayout = TyAndLayout<'a, Ty>> + HasDataLayout,
{
arg.extend_integer_width_to(64);
if arg.layout.is_aggregate() && !unwrap_trivial_aggregate(cx, arg) {
arg.make_indirect_byval();
}
}
pub fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>)
where
Ty: TyAndLayoutMethods<'a, C> + Copy,
C: LayoutOf<Ty = Ty, TyAndLayout = TyAndLayout<'a, Ty>> + HasDataLayout,
{
if !fn_abi.ret.is_ignore() {
classify_ret(cx, &mut fn_abi.ret);
}
for arg in &mut fn_abi.args {
if arg.is_ignore() {
continue;
}
classify_arg(cx, arg);
}
}

View file

@ -68,7 +68,6 @@ fn frame_pointer_r11(
_arch: InlineAsmArch,
has_feature: impl FnMut(&str) -> bool,
target: &Target,
_allocating: bool,
) -> Result<(), &'static str> {
if !frame_pointer_is_r7(has_feature, target) {
Err("the frame pointer (r11) cannot be used as an operand for inline asm")
@ -81,7 +80,6 @@ fn frame_pointer_r7(
_arch: InlineAsmArch,
has_feature: impl FnMut(&str) -> bool,
target: &Target,
_allocating: bool,
) -> Result<(), &'static str> {
if frame_pointer_is_r7(has_feature, target) {
Err("the frame pointer (r7) cannot be used as an operand for inline asm")

View file

@ -90,7 +90,7 @@ macro_rules! def_regs {
match name {
$(
$($alias)|* | $reg_name => {
$($filter(_arch, &mut _has_feature, _target, false)?;)?
$($filter(_arch, &mut _has_feature, _target)?;)?
Ok(Self::$reg)
}
)*
@ -114,7 +114,7 @@ macro_rules! def_regs {
#[allow(unused_imports)]
use super::{InlineAsmReg, InlineAsmRegClass};
$(
if $($filter(_arch, &mut _has_feature, _target, true).is_ok() &&)? true {
if $($filter(_arch, &mut _has_feature, _target).is_ok() &&)? true {
if let Some(set) = _map.get_mut(&InlineAsmRegClass::$arch($arch_regclass::$class)) {
set.insert(InlineAsmReg::$arch($arch_reg::$reg));
}

View file

@ -52,7 +52,6 @@ fn not_e(
_arch: InlineAsmArch,
mut has_feature: impl FnMut(&str) -> bool,
_target: &Target,
_allocating: bool,
) -> Result<(), &'static str> {
if has_feature("e") {
Err("register can't be used with the `e` target feature")

View file

@ -133,7 +133,6 @@ fn x86_64_only(
arch: InlineAsmArch,
_has_feature: impl FnMut(&str) -> bool,
_target: &Target,
_allocating: bool,
) -> Result<(), &'static str> {
match arch {
InlineAsmArch::X86 => Err("register is only available on x86_64"),
@ -146,13 +145,9 @@ fn high_byte(
arch: InlineAsmArch,
_has_feature: impl FnMut(&str) -> bool,
_target: &Target,
allocating: bool,
) -> Result<(), &'static str> {
match arch {
InlineAsmArch::X86_64 if allocating => {
// The error message isn't actually used...
Err("high byte registers are not allocated by reg_byte")
}
InlineAsmArch::X86_64 => Err("high byte registers cannot be used as an operand on x86_64"),
_ => Ok(()),
}
}

View file

@ -79,7 +79,7 @@ mod solaris_base;
mod thumb_base;
mod uefi_msvc_base;
mod vxworks_base;
mod wasm32_base;
mod wasm_base;
mod windows_gnu_base;
mod windows_msvc_base;
mod windows_uwp_gnu_base;
@ -842,6 +842,7 @@ supported_targets! {
("wasm32-unknown-emscripten", wasm32_unknown_emscripten),
("wasm32-unknown-unknown", wasm32_unknown_unknown),
("wasm32-wasi", wasm32_wasi),
("wasm64-unknown-unknown", wasm64_unknown_unknown),
("thumbv6m-none-eabi", thumbv6m_none_eabi),
("thumbv7m-none-eabi", thumbv7m_none_eabi),
@ -1076,6 +1077,8 @@ pub struct TargetOptions {
pub is_like_emscripten: bool,
/// Whether the target toolchain is like Fuchsia's.
pub is_like_fuchsia: bool,
/// Whether a target toolchain is like WASM.
pub is_like_wasm: bool,
/// Version of DWARF to use if not using the default.
/// Useful because some platforms (osx, bsd) only want up to DWARF2.
pub dwarf_version: Option<u32>,
@ -1295,6 +1298,7 @@ impl Default for TargetOptions {
is_like_emscripten: false,
is_like_msvc: false,
is_like_fuchsia: false,
is_like_wasm: false,
dwarf_version: None,
linker_is_gnu: false,
allows_weak_linkage: true,
@ -1789,6 +1793,7 @@ impl Target {
key!(is_like_msvc, bool);
key!(is_like_emscripten, bool);
key!(is_like_fuchsia, bool);
key!(is_like_wasm, bool);
key!(dwarf_version, Option<u32>);
key!(linker_is_gnu, bool);
key!(allows_weak_linkage, bool);
@ -2027,6 +2032,7 @@ impl ToJson for Target {
target_option_val!(is_like_msvc);
target_option_val!(is_like_emscripten);
target_option_val!(is_like_fuchsia);
target_option_val!(is_like_wasm);
target_option_val!(dwarf_version);
target_option_val!(linker_is_gnu);
target_option_val!(allows_weak_linkage);

View file

@ -50,6 +50,7 @@ impl Target {
// and you certainly want "unknown" for the OS name.
fn can_use_os_unknown(&self) -> bool {
self.llvm_target == "wasm32-unknown-unknown"
|| self.llvm_target == "wasm64-unknown-unknown"
|| (self.env == "sgx" && self.vendor == "fortanix")
}
}

View file

@ -1,8 +1,8 @@
use super::wasm32_base;
use super::wasm_base;
use super::{LinkArgs, LinkerFlavor, PanicStrategy, Target, TargetOptions};
pub fn target() -> Target {
let mut options = wasm32_base::options();
let mut options = wasm_base::options();
let clang_args = options.pre_link_args.entry(LinkerFlavor::Gcc).or_default();

View file

@ -10,11 +10,11 @@
//! This target is more or less managed by the Rust and WebAssembly Working
//! Group nowadays at <https://github.com/rustwasm>.
use super::wasm32_base;
use super::wasm_base;
use super::{LinkerFlavor, LldFlavor, Target};
pub fn target() -> Target {
let mut options = wasm32_base::options();
let mut options = wasm_base::options();
options.os = "unknown".to_string();
options.linker_flavor = LinkerFlavor::Lld(LldFlavor::Wasm);
let clang_args = options.pre_link_args.entry(LinkerFlavor::Gcc).or_default();

View file

@ -72,11 +72,11 @@
//! best we can with this target. Don't start relying on too much here unless
//! you know what you're getting in to!
use super::wasm32_base;
use super::wasm_base;
use super::{crt_objects, LinkerFlavor, LldFlavor, Target};
pub fn target() -> Target {
let mut options = wasm32_base::options();
let mut options = wasm_base::options();
options.os = "wasi".to_string();
options.linker_flavor = LinkerFlavor::Lld(LldFlavor::Wasm);

View file

@ -0,0 +1,39 @@
//! A "bare wasm" target representing a WebAssembly output that makes zero
//! assumptions about its environment.
//!
//! The `wasm64-unknown-unknown` target is intended to encapsulate use cases
//! that do not rely on any imported functionality. The binaries generated are
//! entirely self-contained by default when using the standard library. Although
//! the standard library is available, most of it returns an error immediately
//! (e.g. trying to create a TCP stream or something like that).
use super::wasm_base;
use super::{LinkerFlavor, LldFlavor, Target};
pub fn target() -> Target {
let mut options = wasm_base::options();
options.os = "unknown".to_string();
options.linker_flavor = LinkerFlavor::Lld(LldFlavor::Wasm);
let clang_args = options.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap();
// Make sure clang uses LLD as its linker and is configured appropriately
// otherwise
clang_args.push("--target=wasm64-unknown-unknown".to_string());
// For now this target just never has an entry symbol no matter the output
// type, so unconditionally pass this.
clang_args.push("-Wl,--no-entry".to_string());
options
.pre_link_args
.get_mut(&LinkerFlavor::Lld(LldFlavor::Wasm))
.unwrap()
.push("--no-entry".to_string());
Target {
llvm_target: "wasm64-unknown-unknown".to_string(),
pointer_width: 64,
data_layout: "e-m:e-p:64:64-i64:64-n32:64-S128".to_string(),
arch: "wasm64".to_string(),
options,
}
}

View file

@ -60,6 +60,8 @@ pub fn options() -> TargetOptions {
pre_link_args.insert(LinkerFlavor::Gcc, clang_args);
TargetOptions {
is_like_wasm: true,
// we allow dynamic linking, but only cdylibs. Basically we allow a
// final library artifact that exports some symbols (a wasm module) but
// we don't allow intermediate `dylib` crate types

View file

@ -104,8 +104,8 @@ impl<'a, 'tcx> Expectation<'tcx> {
/// for the program to type-check). `only_has_type` will return
/// such a constraint, if it exists.
pub(super) fn only_has_type(self, fcx: &FnCtxt<'a, 'tcx>) -> Option<Ty<'tcx>> {
match self.resolve(fcx) {
ExpectHasType(ty) => Some(ty),
match self {
ExpectHasType(ty) => Some(fcx.resolve_vars_if_possible(ty)),
NoExpectation | ExpectCastableToType(_) | ExpectRvalueLikeUnsized(_) | IsLast(_) => {
None
}

View file

@ -161,7 +161,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
expr: &'tcx hir::Expr<'tcx>,
expected: Expectation<'tcx>,
) -> Ty<'tcx> {
debug!(">> type-checking: expr={:?} expected={:?}", expr, expected);
debug!(">> type-checking: expected={:?}, expr={:?} ", expected, expr);
// True if `expr` is a `Try::from_ok(())` that is a result of desugaring a try block
// without the final expr (e.g. `try { return; }`). We don't want to generate an
@ -224,7 +224,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
expr: &'tcx hir::Expr<'tcx>,
expected: Expectation<'tcx>,
) -> Ty<'tcx> {
debug!("check_expr_kind(expr={:?}, expected={:?})", expr, expected);
debug!("check_expr_kind(expected={:?}, expr={:?})", expected, expr);
let tcx = self.tcx;
match expr.kind {

View file

@ -83,6 +83,8 @@ struct ProbeContext<'a, 'tcx> {
unsatisfied_predicates: Vec<(ty::Predicate<'tcx>, Option<ty::Predicate<'tcx>>)>,
is_suggestion: IsSuggestion,
scope_expr_id: hir::HirId,
}
impl<'a, 'tcx> Deref for ProbeContext<'a, 'tcx> {
@ -448,6 +450,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
orig_values,
steps.steps,
is_suggestion,
scope_expr_id,
);
probe_cx.assemble_inherent_candidates();
@ -547,6 +550,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
orig_steps_var_values: OriginalQueryValues<'tcx>,
steps: Lrc<Vec<CandidateStep<'tcx>>>,
is_suggestion: IsSuggestion,
scope_expr_id: hir::HirId,
) -> ProbeContext<'a, 'tcx> {
ProbeContext {
fcx,
@ -564,6 +568,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
private_candidate: None,
unsatisfied_predicates: Vec::new(),
is_suggestion,
scope_expr_id,
}
}
@ -1312,7 +1317,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
) {
self.tcx.struct_span_lint_hir(
lint::builtin::UNSTABLE_NAME_COLLISIONS,
self.fcx.body_id,
self.scope_expr_id,
self.span,
|lint| {
let def_kind = stable_pick.item.kind.as_def_kind();
@ -1594,6 +1599,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
self.orig_steps_var_values.clone(),
steps,
IsSuggestion(true),
self.scope_expr_id,
);
pcx.allow_similar_names = true;
pcx.assemble_inherent_candidates();

View file

@ -280,9 +280,14 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
if needs_to_be_read {
self.borrow_expr(&discr, ty::ImmBorrow);
} else {
let closure_def_id = match discr_place.place.base {
PlaceBase::Upvar(upvar_id) => Some(upvar_id.closure_expr_id.to_def_id()),
_ => None,
};
self.delegate.fake_read(
discr_place.place.clone(),
FakeReadCause::ForMatchedPlace,
FakeReadCause::ForMatchedPlace(closure_def_id),
discr_place.hir_id,
);
@ -578,9 +583,14 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
}
fn walk_arm(&mut self, discr_place: &PlaceWithHirId<'tcx>, arm: &hir::Arm<'_>) {
let closure_def_id = match discr_place.place.base {
PlaceBase::Upvar(upvar_id) => Some(upvar_id.closure_expr_id.to_def_id()),
_ => None,
};
self.delegate.fake_read(
discr_place.place.clone(),
FakeReadCause::ForMatchedPlace,
FakeReadCause::ForMatchedPlace(closure_def_id),
discr_place.hir_id,
);
self.walk_pat(discr_place, &arm.pat);
@ -595,9 +605,14 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
/// Walks a pat that occurs in isolation (i.e., top-level of fn argument or
/// let binding, and *not* a match arm or nested pat.)
fn walk_irrefutable_pat(&mut self, discr_place: &PlaceWithHirId<'tcx>, pat: &hir::Pat<'_>) {
let closure_def_id = match discr_place.place.base {
PlaceBase::Upvar(upvar_id) => Some(upvar_id.closure_expr_id.to_def_id()),
_ => None,
};
self.delegate.fake_read(
discr_place.place.clone(),
FakeReadCause::ForLet,
FakeReadCause::ForLet(closure_def_id),
discr_place.hir_id,
);
self.walk_pat(discr_place, pat);

View file

@ -373,7 +373,9 @@ changelog-seen = 2
# Whether to download the stage 1 and 2 compilers from CI.
# This is mostly useful for tools; if you have changes to `compiler/` they will be ignored.
#
# FIXME: currently, this also uses the downloaded compiler for stage0, but that causes unnecessary rebuilds.
# You can set this to "if-unchanged" to only download if `compiler/` has not been modified.
#
# FIXME(#82739): currently, this also uses the downloaded compiler for stage0, but that causes unnecessary rebuilds.
#download-rustc = false
# Number of codegen units to use for each compiler invocation. A value of 0

View file

@ -128,106 +128,6 @@ impl<K, V> InternalNode<K, V> {
/// is not a separate type and has no destructor.
type BoxedNode<K, V> = NonNull<LeafNode<K, V>>;
/// The root node of an owned tree.
///
/// Note that this does not have a destructor, and must be cleaned up manually.
pub type Root<K, V> = NodeRef<marker::Owned, K, V, marker::LeafOrInternal>;
impl<K, V> Root<K, V> {
/// Returns a new owned tree, with its own root node that is initially empty.
pub fn new() -> Self {
NodeRef::new_leaf().forget_type()
}
}
impl<K, V> NodeRef<marker::Owned, K, V, marker::Leaf> {
fn new_leaf() -> Self {
Self::from_new_leaf(LeafNode::new())
}
fn from_new_leaf(leaf: Box<LeafNode<K, V>>) -> Self {
NodeRef { height: 0, node: NonNull::from(Box::leak(leaf)), _marker: PhantomData }
}
}
impl<K, V> NodeRef<marker::Owned, K, V, marker::Internal> {
fn new_internal(child: Root<K, V>) -> Self {
let mut new_node = unsafe { InternalNode::new() };
new_node.edges[0].write(child.node);
unsafe { NodeRef::from_new_internal(new_node, child.height + 1) }
}
/// # Safety
/// `height` must not be zero.
unsafe fn from_new_internal(internal: Box<InternalNode<K, V>>, height: usize) -> Self {
debug_assert!(height > 0);
let node = NonNull::from(Box::leak(internal)).cast();
let mut this = NodeRef { height, node, _marker: PhantomData };
this.borrow_mut().correct_all_childrens_parent_links();
this
}
}
impl<K, V, Type> NodeRef<marker::Owned, K, V, Type> {
/// Mutably borrows the owned root node. Unlike `reborrow_mut`, this is safe
/// because the return value cannot be used to destroy the root, and there
/// cannot be other references to the tree.
pub fn borrow_mut(&mut self) -> NodeRef<marker::Mut<'_>, K, V, Type> {
NodeRef { height: self.height, node: self.node, _marker: PhantomData }
}
/// Slightly mutably borrows the owned root node.
pub fn borrow_valmut(&mut self) -> NodeRef<marker::ValMut<'_>, K, V, Type> {
NodeRef { height: self.height, node: self.node, _marker: PhantomData }
}
/// Irreversibly transitions to a reference that permits traversal and offers
/// destructive methods and little else.
pub fn into_dying(self) -> NodeRef<marker::Dying, K, V, Type> {
NodeRef { height: self.height, node: self.node, _marker: PhantomData }
}
}
impl<K, V> NodeRef<marker::Owned, K, V, marker::LeafOrInternal> {
/// Adds a new internal node with a single edge pointing to the previous root node,
/// make that new node the root node, and return it. This increases the height by 1
/// and is the opposite of `pop_internal_level`.
pub fn push_internal_level(&mut self) -> NodeRef<marker::Mut<'_>, K, V, marker::Internal> {
super::mem::take_mut(self, |old_root| NodeRef::new_internal(old_root).forget_type());
// `self.borrow_mut()`, except that we just forgot we're internal now:
NodeRef { height: self.height, node: self.node, _marker: PhantomData }
}
/// Removes the internal root node, using its first child as the new root node.
/// As it is intended only to be called when the root node has only one child,
/// no cleanup is done on any of the keys, values and other children.
/// This decreases the height by 1 and is the opposite of `push_internal_level`.
///
/// Requires exclusive access to the `Root` object but not to the root node;
/// it will not invalidate other handles or references to the root node.
///
/// Panics if there is no internal level, i.e., if the root node is a leaf.
pub fn pop_internal_level(&mut self) {
assert!(self.height > 0);
let top = self.node;
// SAFETY: we asserted to be internal.
let internal_self = unsafe { self.borrow_mut().cast_to_internal_unchecked() };
// SAFETY: we borrowed `self` exclusively and its borrow type is exclusive.
let internal_node = unsafe { &mut *NodeRef::as_internal_ptr(&internal_self) };
// SAFETY: the first edge is always initialized.
self.node = unsafe { internal_node.edges[0].assume_init_read() };
self.height -= 1;
self.clear_parent_link();
unsafe {
Global.deallocate(top.cast(), Layout::new::<InternalNode<K, V>>());
}
}
}
// N.B. `NodeRef` is always covariant in `K` and `V`, even when the `BorrowType`
// is `Mut`. This is technically wrong, but cannot result in any unsafety due to
// internal use of `NodeRef` because we stay completely generic over `K` and `V`.
@ -292,6 +192,11 @@ pub struct NodeRef<BorrowType, K, V, Type> {
_marker: PhantomData<(BorrowType, Type)>,
}
/// The root node of an owned tree.
///
/// Note that this does not have a destructor, and must be cleaned up manually.
pub type Root<K, V> = NodeRef<marker::Owned, K, V, marker::LeafOrInternal>;
impl<'a, K: 'a, V: 'a, Type> Copy for NodeRef<marker::Immut<'a>, K, V, Type> {}
impl<'a, K: 'a, V: 'a, Type> Clone for NodeRef<marker::Immut<'a>, K, V, Type> {
fn clone(&self) -> Self {
@ -307,6 +212,34 @@ unsafe impl<'a, K: Send + 'a, V: Send + 'a, Type> Send for NodeRef<marker::ValMu
unsafe impl<K: Send, V: Send, Type> Send for NodeRef<marker::Owned, K, V, Type> {}
unsafe impl<K: Send, V: Send, Type> Send for NodeRef<marker::Dying, K, V, Type> {}
impl<K, V> NodeRef<marker::Owned, K, V, marker::Leaf> {
fn new_leaf() -> Self {
Self::from_new_leaf(LeafNode::new())
}
fn from_new_leaf(leaf: Box<LeafNode<K, V>>) -> Self {
NodeRef { height: 0, node: NonNull::from(Box::leak(leaf)), _marker: PhantomData }
}
}
impl<K, V> NodeRef<marker::Owned, K, V, marker::Internal> {
fn new_internal(child: Root<K, V>) -> Self {
let mut new_node = unsafe { InternalNode::new() };
new_node.edges[0].write(child.node);
unsafe { NodeRef::from_new_internal(new_node, child.height + 1) }
}
/// # Safety
/// `height` must not be zero.
unsafe fn from_new_internal(internal: Box<InternalNode<K, V>>, height: usize) -> Self {
debug_assert!(height > 0);
let node = NonNull::from(Box::leak(internal)).cast();
let mut this = NodeRef { height, node, _marker: PhantomData };
this.borrow_mut().correct_all_childrens_parent_links();
this
}
}
impl<BorrowType, K, V> NodeRef<BorrowType, K, V, marker::Internal> {
/// Unpack a node reference that was packed as `NodeRef::parent`.
fn from_internal(node: NonNull<InternalNode<K, V>>, height: usize) -> Self {
@ -420,6 +353,19 @@ impl<BorrowType: marker::BorrowType, K, V, Type> NodeRef<BorrowType, K, V, Type>
}
}
impl<BorrowType, K, V, Type> NodeRef<BorrowType, K, V, Type> {
/// Could be a public implementation of PartialEq, but only used in this module.
fn eq(&self, other: &Self) -> bool {
let Self { node, height, _marker } = self;
if node.eq(&other.node) {
debug_assert_eq!(*height, other.height);
true
} else {
false
}
}
}
impl<'a, K: 'a, V: 'a, Type> NodeRef<marker::Immut<'a>, K, V, Type> {
/// Exposes the leaf portion of any leaf or internal node in an immutable tree.
fn into_leaf(self) -> &'a LeafNode<K, V> {
@ -461,20 +407,6 @@ impl<K, V> NodeRef<marker::Dying, K, V, marker::LeafOrInternal> {
}
}
impl<'a, K, V> NodeRef<marker::Mut<'a>, K, V, marker::LeafOrInternal> {
/// Unsafely asserts to the compiler the static information that this node is a `Leaf`.
unsafe fn cast_to_leaf_unchecked(self) -> NodeRef<marker::Mut<'a>, K, V, marker::Leaf> {
debug_assert!(self.height == 0);
NodeRef { height: self.height, node: self.node, _marker: PhantomData }
}
/// Unsafely asserts to the compiler the static information that this node is an `Internal`.
unsafe fn cast_to_internal_unchecked(self) -> NodeRef<marker::Mut<'a>, K, V, marker::Internal> {
debug_assert!(self.height > 0);
NodeRef { height: self.height, node: self.node, _marker: PhantomData }
}
}
impl<'a, K, V, Type> NodeRef<marker::Mut<'a>, K, V, Type> {
/// Temporarily takes out another, mutable reference to the same node. Beware, as
/// this method is very dangerous, doubly so since it may not immediately appear
@ -577,6 +509,22 @@ impl<'a, K: 'a, V: 'a, Type> NodeRef<marker::Mut<'a>, K, V, Type> {
}
}
impl<'a, K, V> NodeRef<marker::Mut<'a>, K, V, marker::Internal> {
/// # Safety
/// Every item returned by `range` is a valid edge index for the node.
unsafe fn correct_childrens_parent_links<R: Iterator<Item = usize>>(&mut self, range: R) {
for i in range {
debug_assert!(i <= self.len());
unsafe { Handle::new_edge(self.reborrow_mut(), i) }.correct_parent_link();
}
}
fn correct_all_childrens_parent_links(&mut self) {
let len = self.len();
unsafe { self.correct_childrens_parent_links(0..=len) };
}
}
impl<'a, K: 'a, V: 'a> NodeRef<marker::Mut<'a>, K, V, marker::LeafOrInternal> {
/// Sets the node's link to its parent edge,
/// without invalidating other references to the node.
@ -596,6 +544,71 @@ impl<K, V> NodeRef<marker::Owned, K, V, marker::LeafOrInternal> {
}
}
impl<K, V> NodeRef<marker::Owned, K, V, marker::LeafOrInternal> {
/// Returns a new owned tree, with its own root node that is initially empty.
pub fn new() -> Self {
NodeRef::new_leaf().forget_type()
}
/// Adds a new internal node with a single edge pointing to the previous root node,
/// make that new node the root node, and return it. This increases the height by 1
/// and is the opposite of `pop_internal_level`.
pub fn push_internal_level(&mut self) -> NodeRef<marker::Mut<'_>, K, V, marker::Internal> {
super::mem::take_mut(self, |old_root| NodeRef::new_internal(old_root).forget_type());
// `self.borrow_mut()`, except that we just forgot we're internal now:
NodeRef { height: self.height, node: self.node, _marker: PhantomData }
}
/// Removes the internal root node, using its first child as the new root node.
/// As it is intended only to be called when the root node has only one child,
/// no cleanup is done on any of the keys, values and other children.
/// This decreases the height by 1 and is the opposite of `push_internal_level`.
///
/// Requires exclusive access to the `Root` object but not to the root node;
/// it will not invalidate other handles or references to the root node.
///
/// Panics if there is no internal level, i.e., if the root node is a leaf.
pub fn pop_internal_level(&mut self) {
assert!(self.height > 0);
let top = self.node;
// SAFETY: we asserted to be internal.
let internal_self = unsafe { self.borrow_mut().cast_to_internal_unchecked() };
// SAFETY: we borrowed `self` exclusively and its borrow type is exclusive.
let internal_node = unsafe { &mut *NodeRef::as_internal_ptr(&internal_self) };
// SAFETY: the first edge is always initialized.
self.node = unsafe { internal_node.edges[0].assume_init_read() };
self.height -= 1;
self.clear_parent_link();
unsafe {
Global.deallocate(top.cast(), Layout::new::<InternalNode<K, V>>());
}
}
}
impl<K, V, Type> NodeRef<marker::Owned, K, V, Type> {
/// Mutably borrows the owned root node. Unlike `reborrow_mut`, this is safe
/// because the return value cannot be used to destroy the root, and there
/// cannot be other references to the tree.
pub fn borrow_mut(&mut self) -> NodeRef<marker::Mut<'_>, K, V, Type> {
NodeRef { height: self.height, node: self.node, _marker: PhantomData }
}
/// Slightly mutably borrows the owned root node.
pub fn borrow_valmut(&mut self) -> NodeRef<marker::ValMut<'_>, K, V, Type> {
NodeRef { height: self.height, node: self.node, _marker: PhantomData }
}
/// Irreversibly transitions to a reference that permits traversal and offers
/// destructive methods and little else.
pub fn into_dying(self) -> NodeRef<marker::Dying, K, V, Type> {
NodeRef { height: self.height, node: self.node, _marker: PhantomData }
}
}
impl<'a, K: 'a, V: 'a> NodeRef<marker::Mut<'a>, K, V, marker::Leaf> {
/// Adds a key-value pair to the end of the node.
pub fn push(&mut self, key: K, val: V) {
@ -610,22 +623,6 @@ impl<'a, K: 'a, V: 'a> NodeRef<marker::Mut<'a>, K, V, marker::Leaf> {
}
}
impl<'a, K, V> NodeRef<marker::Mut<'a>, K, V, marker::Internal> {
/// # Safety
/// Every item returned by `range` is a valid edge index for the node.
unsafe fn correct_childrens_parent_links<R: Iterator<Item = usize>>(&mut self, range: R) {
for i in range {
debug_assert!(i <= self.len());
unsafe { Handle::new_edge(self.reborrow_mut(), i) }.correct_parent_link();
}
}
fn correct_all_childrens_parent_links(&mut self) {
let len = self.len();
unsafe { self.correct_childrens_parent_links(0..=len) };
}
}
impl<'a, K: 'a, V: 'a> NodeRef<marker::Mut<'a>, K, V, marker::Internal> {
/// Adds a key-value pair, and an edge to go to the right of that pair,
/// to the end of the node.
@ -645,6 +642,20 @@ impl<'a, K: 'a, V: 'a> NodeRef<marker::Mut<'a>, K, V, marker::Internal> {
}
}
impl<BorrowType, K, V> NodeRef<BorrowType, K, V, marker::Leaf> {
/// Removes any static information asserting that this node is a `Leaf` node.
pub fn forget_type(self) -> NodeRef<BorrowType, K, V, marker::LeafOrInternal> {
NodeRef { height: self.height, node: self.node, _marker: PhantomData }
}
}
impl<BorrowType, K, V> NodeRef<BorrowType, K, V, marker::Internal> {
/// Removes any static information asserting that this node is an `Internal` node.
pub fn forget_type(self) -> NodeRef<BorrowType, K, V, marker::LeafOrInternal> {
NodeRef { height: self.height, node: self.node, _marker: PhantomData }
}
}
impl<BorrowType, K, V> NodeRef<BorrowType, K, V, marker::LeafOrInternal> {
/// Checks whether a node is an `Internal` node or a `Leaf` node.
pub fn force(
@ -669,6 +680,20 @@ impl<BorrowType, K, V> NodeRef<BorrowType, K, V, marker::LeafOrInternal> {
}
}
impl<'a, K, V> NodeRef<marker::Mut<'a>, K, V, marker::LeafOrInternal> {
/// Unsafely asserts to the compiler the static information that this node is a `Leaf`.
unsafe fn cast_to_leaf_unchecked(self) -> NodeRef<marker::Mut<'a>, K, V, marker::Leaf> {
debug_assert!(self.height == 0);
NodeRef { height: self.height, node: self.node, _marker: PhantomData }
}
/// Unsafely asserts to the compiler the static information that this node is an `Internal`.
unsafe fn cast_to_internal_unchecked(self) -> NodeRef<marker::Mut<'a>, K, V, marker::Internal> {
debug_assert!(self.height > 0);
NodeRef { height: self.height, node: self.node, _marker: PhantomData }
}
}
/// A reference to a specific key-value pair or edge within a node. The `Node` parameter
/// must be a `NodeRef`, while the `Type` can either be `KV` (signifying a handle on a key-value
/// pair) or `Edge` (signifying a handle on an edge).
@ -722,19 +747,6 @@ impl<BorrowType, K, V, NodeType> Handle<NodeRef<BorrowType, K, V, NodeType>, mar
}
}
impl<BorrowType, K, V, NodeType> NodeRef<BorrowType, K, V, NodeType> {
/// Could be a public implementation of PartialEq, but only used in this module.
fn eq(&self, other: &Self) -> bool {
let Self { node, height, _marker } = self;
if node.eq(&other.node) {
debug_assert_eq!(*height, other.height);
true
} else {
false
}
}
}
impl<BorrowType, K, V, NodeType, HandleType> PartialEq
for Handle<NodeRef<BorrowType, K, V, NodeType>, HandleType>
{
@ -754,16 +766,6 @@ impl<BorrowType, K, V, NodeType, HandleType>
}
}
impl<'a, K, V, Type> Handle<NodeRef<marker::Mut<'a>, K, V, marker::LeafOrInternal>, Type> {
/// Unsafely asserts to the compiler the static information that the handle's node is a `Leaf`.
pub unsafe fn cast_to_leaf_unchecked(
self,
) -> Handle<NodeRef<marker::Mut<'a>, K, V, marker::Leaf>, Type> {
let node = unsafe { self.node.cast_to_leaf_unchecked() };
Handle { node, idx: self.idx, _marker: PhantomData }
}
}
impl<'a, K, V, NodeType, HandleType> Handle<NodeRef<marker::Mut<'a>, K, V, NodeType>, HandleType> {
/// Temporarily takes out another, mutable handle on the same location. Beware, as
/// this method is very dangerous, doubly so since it may not immediately appear
@ -1466,20 +1468,6 @@ impl<'a, K: 'a, V: 'a> BalancingContext<'a, K, V> {
}
}
impl<BorrowType, K, V> NodeRef<BorrowType, K, V, marker::Leaf> {
/// Removes any static information asserting that this node is a `Leaf` node.
pub fn forget_type(self) -> NodeRef<BorrowType, K, V, marker::LeafOrInternal> {
NodeRef { height: self.height, node: self.node, _marker: PhantomData }
}
}
impl<BorrowType, K, V> NodeRef<BorrowType, K, V, marker::Internal> {
/// Removes any static information asserting that this node is an `Internal` node.
pub fn forget_type(self) -> NodeRef<BorrowType, K, V, marker::LeafOrInternal> {
NodeRef { height: self.height, node: self.node, _marker: PhantomData }
}
}
impl<BorrowType, K, V> Handle<NodeRef<BorrowType, K, V, marker::Leaf>, marker::Edge> {
pub fn forget_node_type(
self,
@ -1531,6 +1519,16 @@ impl<BorrowType, K, V, Type> Handle<NodeRef<BorrowType, K, V, marker::LeafOrInte
}
}
impl<'a, K, V, Type> Handle<NodeRef<marker::Mut<'a>, K, V, marker::LeafOrInternal>, Type> {
/// Unsafely asserts to the compiler the static information that the handle's node is a `Leaf`.
pub unsafe fn cast_to_leaf_unchecked(
self,
) -> Handle<NodeRef<marker::Mut<'a>, K, V, marker::Leaf>, Type> {
let node = unsafe { self.node.cast_to_leaf_unchecked() };
Handle { node, idx: self.idx, _marker: PhantomData }
}
}
impl<'a, K, V> Handle<NodeRef<marker::Mut<'a>, K, V, marker::LeafOrInternal>, marker::Edge> {
/// Move the suffix after `self` from one node to another one. `right` must be empty.
/// The first edge of `right` remains unchanged.

View file

@ -133,7 +133,6 @@
#![feature(trusted_len)]
#![feature(unboxed_closures)]
#![feature(unicode_internals)]
#![cfg_attr(bootstrap, feature(unsafe_block_in_unsafe_fn))]
#![feature(unsize)]
#![feature(unsized_fn_params)]
#![feature(allocator_internals)]
@ -142,8 +141,7 @@
#![feature(alloc_layout_extra)]
#![feature(trusted_random_access)]
#![feature(try_trait)]
#![cfg_attr(bootstrap, feature(type_alias_impl_trait))]
#![cfg_attr(not(bootstrap), feature(min_type_alias_impl_trait))]
#![feature(min_type_alias_impl_trait)]
#![feature(associated_type_bounds)]
#![feature(slice_group_by)]
#![feature(decl_macro)]

View file

@ -1004,9 +1004,9 @@ fn test_from_iter_specialization_with_iterator_adapters() {
.map_while(Option::Some)
.peekable()
.skip(1)
.map(|e| std::num::NonZeroUsize::new(e));
.map(|e| if e != usize::MAX { Ok(std::num::NonZeroUsize::new(e)) } else { Err(()) });
assert_in_place_trait(&iter);
let sink = iter.collect::<Vec<_>>();
let sink = iter.collect::<Result<Vec<_>, _>>().unwrap();
let sinkptr = sink.as_ptr();
assert_eq!(srcptr, sinkptr as *const usize);
}
@ -1078,12 +1078,21 @@ fn test_from_iter_specialization_panic_during_drop_leaks() {
}
}
let mut to_free: *mut Droppable = core::ptr::null_mut();
let mut cap = 0;
let _ = std::panic::catch_unwind(AssertUnwindSafe(|| {
let v = vec![Droppable::DroppedTwice(Box::new(123)), Droppable::PanicOnDrop];
let mut v = vec![Droppable::DroppedTwice(Box::new(123)), Droppable::PanicOnDrop];
to_free = v.as_mut_ptr();
cap = v.capacity();
let _ = v.into_iter().take(0).collect::<Vec<_>>();
}));
assert_eq!(unsafe { DROP_COUNTER }, 1);
// clean up the leak to keep miri happy
unsafe {
drop(Vec::from_raw_parts(to_free, 0, cap));
}
}
#[test]

View file

@ -1815,7 +1815,7 @@ impl<T> UnsafeCell<T> {
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_const_stable(feature = "const_unsafe_cell_new", since = "1.32.0")]
#[inline]
#[inline(always)]
pub const fn new(value: T) -> UnsafeCell<T> {
UnsafeCell { value }
}
@ -1831,7 +1831,7 @@ impl<T> UnsafeCell<T> {
///
/// let five = uc.into_inner();
/// ```
#[inline]
#[inline(always)]
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_const_unstable(feature = "const_cell_into_inner", issue = "78729")]
pub const fn into_inner(self) -> T {
@ -1856,7 +1856,7 @@ impl<T: ?Sized> UnsafeCell<T> {
///
/// let five = uc.get();
/// ```
#[inline]
#[inline(always)]
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_const_stable(feature = "const_unsafecell_get", since = "1.32.0")]
pub const fn get(&self) -> *mut T {
@ -1881,7 +1881,7 @@ impl<T: ?Sized> UnsafeCell<T> {
///
/// assert_eq!(*c.get_mut(), 6);
/// ```
#[inline]
#[inline(always)]
#[stable(feature = "unsafe_cell_get_mut", since = "1.50.0")]
pub fn get_mut(&mut self) -> &mut T {
&mut self.value
@ -1914,7 +1914,7 @@ impl<T: ?Sized> UnsafeCell<T> {
///
/// assert_eq!(uc.into_inner(), 5);
/// ```
#[inline]
#[inline(always)]
#[unstable(feature = "unsafe_cell_raw_get", issue = "66358")]
pub const fn raw_get(this: *const Self) -> *mut T {
// We can just cast the pointer from `UnsafeCell<T>` to `T` because of

View file

@ -981,7 +981,8 @@ pub trait PartialOrd<Rhs: ?Sized = Self>: PartialEq<Rhs> {
#[must_use]
#[stable(feature = "rust1", since = "1.0.0")]
fn le(&self, other: &Rhs) -> bool {
matches!(self.partial_cmp(other), Some(Less | Equal))
// Pattern `Some(Less | Eq)` optimizes worse than negating `None | Some(Greater)`.
!matches!(self.partial_cmp(other), None | Some(Greater))
}
/// This method tests greater than (for `self` and `other`) and is used by the `>` operator.

View file

@ -691,29 +691,9 @@ mod impls {
impl<T: ?Sized> Hash for *const T {
#[inline]
fn hash<H: Hasher>(&self, state: &mut H) {
#[cfg(not(bootstrap))]
{
let (address, metadata) = self.to_raw_parts();
state.write_usize(address as usize);
metadata.hash(state);
}
#[cfg(bootstrap)]
{
if mem::size_of::<Self>() == mem::size_of::<usize>() {
// Thin pointer
state.write_usize(*self as *const () as usize);
} else {
// Fat pointer
// SAFETY: we are accessing the memory occupied by `self`
// which is guaranteed to be valid.
// This assumes a fat pointer can be represented by a `(usize, usize)`,
// which is safe to do in `std` because it is shipped and kept in sync
// with the implementation of fat pointers in `rustc`.
let (a, b) = unsafe { *(self as *const Self as *const (usize, usize)) };
state.write_usize(a);
state.write_usize(b);
}
}
let (address, metadata) = self.to_raw_parts();
state.write_usize(address as usize);
metadata.hash(state);
}
}
@ -721,29 +701,9 @@ mod impls {
impl<T: ?Sized> Hash for *mut T {
#[inline]
fn hash<H: Hasher>(&self, state: &mut H) {
#[cfg(not(bootstrap))]
{
let (address, metadata) = self.to_raw_parts();
state.write_usize(address as usize);
metadata.hash(state);
}
#[cfg(bootstrap)]
{
if mem::size_of::<Self>() == mem::size_of::<usize>() {
// Thin pointer
state.write_usize(*self as *const () as usize);
} else {
// Fat pointer
// SAFETY: we are accessing the memory occupied by `self`
// which is guaranteed to be valid.
// This assumes a fat pointer can be represented by a `(usize, usize)`,
// which is safe to do in `std` because it is shipped and kept in sync
// with the implementation of fat pointers in `rustc`.
let (a, b) = unsafe { *(self as *const Self as *const (usize, usize)) };
state.write_usize(a);
state.write_usize(b);
}
}
let (address, metadata) = self.to_raw_parts();
state.write_usize(address as usize);
metadata.hash(state);
}
}
}

View file

@ -194,3 +194,26 @@ where
self.try_fold(init, ok(fold)).unwrap()
}
}
#[unstable(issue = "none", feature = "inplace_iteration")]
unsafe impl<S: Iterator, I, E> SourceIter for ResultShunt<'_, I, E>
where
I: SourceIter<Source = S>,
{
type Source = S;
#[inline]
unsafe fn as_inner(&mut self) -> &mut S {
// SAFETY: unsafe function forwarding to unsafe function with the same requirements
unsafe { SourceIter::as_inner(&mut self.iter) }
}
}
// SAFETY: ResultShunt::next calls I::find, which has to advance `iter` in order to
// return `Some(_)`. Since `iter` has type `I: InPlaceIterable` it's guaranteed that
// at least one item will be moved out from the underlying source.
#[unstable(issue = "none", feature = "inplace_iteration")]
unsafe impl<I, T, E> InPlaceIterable for ResultShunt<'_, I, E> where
I: Iterator<Item = Result<T, E>> + InPlaceIterable
{
}

View file

@ -129,7 +129,7 @@
#![feature(auto_traits)]
#![cfg_attr(bootstrap, feature(or_patterns))]
#![feature(prelude_import)]
#![cfg_attr(not(bootstrap), feature(ptr_metadata))]
#![feature(ptr_metadata)]
#![feature(repr_simd, platform_intrinsics)]
#![feature(rustc_attrs)]
#![feature(simd_ffi)]
@ -167,7 +167,6 @@
#![feature(slice_ptr_get)]
#![feature(no_niche)] // rust-lang/rust#68303
#![feature(int_error_matching)]
#![cfg_attr(bootstrap, feature(unsafe_block_in_unsafe_fn))]
#![deny(unsafe_op_in_unsafe_fn)]
#[prelude_import]
@ -299,8 +298,7 @@ pub mod primitive;
unused_imports,
unsafe_op_in_unsafe_fn
)]
#[cfg_attr(bootstrap, allow(non_autolinks))]
#[cfg_attr(not(bootstrap), allow(rustdoc::non_autolinks))]
#[allow(rustdoc::non_autolinks)]
// FIXME: This annotation should be moved into rust-lang/stdarch after clashing_extern_declarations is
// merged. It currently cannot because bootstrap fails as the lint hasn't been defined yet.
#[allow(clashing_extern_declarations)]

View file

@ -1391,7 +1391,6 @@ pub(crate) mod builtin {
}
/// Attribute macro used to apply derive macros.
#[cfg(not(bootstrap))]
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_builtin_macro]
pub macro derive($item:item) {
@ -1453,7 +1452,6 @@ pub(crate) mod builtin {
}
/// Expands all `#[cfg]` and `#[cfg_attr]` attributes in the code fragment it's applied to.
#[cfg(not(bootstrap))]
#[unstable(
feature = "cfg_eval",
issue = "82679",

View file

@ -190,6 +190,8 @@ use crate::ptr;
/// let ptr = uninit.as_mut_ptr();
///
/// // Initializing the `name` field
/// // Using `write` instead of assignment via `=` to not call `drop` on the
/// // old, uninitialized value.
/// unsafe { addr_of_mut!((*ptr).name).write("Bob".to_string()); }
///
/// // Initializing the `list` field

View file

@ -65,7 +65,7 @@ pub trait Deref {
/// The resulting type after dereferencing.
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_diagnostic_item = "deref_target"]
#[cfg_attr(not(bootstrap), lang = "deref_target")]
#[lang = "deref_target"]
type Target: ?Sized;
/// Dereferences the value.

View file

@ -67,7 +67,6 @@ pub use crate::macros::builtin::{
bench, global_allocator, test, test_case, RustcDecodable, RustcEncodable,
};
#[cfg(not(bootstrap))]
#[stable(feature = "builtin_macro_prelude", since = "1.38.0")]
#[doc(no_inline)]
pub use crate::macros::builtin::derive;
@ -80,7 +79,6 @@ pub use crate::macros::builtin::derive;
#[doc(no_inline)]
pub use crate::macros::builtin::cfg_accessible;
#[cfg(not(bootstrap))]
#[unstable(
feature = "cfg_eval",
issue = "82679",

View file

@ -51,7 +51,6 @@ impl<T: ?Sized> *const T {
/// Decompose a (possibly wide) pointer into is address and metadata components.
///
/// The pointer can be later reconstructed with [`from_raw_parts`].
#[cfg(not(bootstrap))]
#[unstable(feature = "ptr_metadata", issue = "81513")]
#[rustc_const_unstable(feature = "ptr_metadata", issue = "81513")]
#[inline]
@ -915,13 +914,6 @@ impl<T> *const [T] {
#[unstable(feature = "slice_ptr_len", issue = "71146")]
#[rustc_const_unstable(feature = "const_slice_ptr_len", issue = "71146")]
pub const fn len(self) -> usize {
#[cfg(bootstrap)]
{
// SAFETY: this is safe because `*const [T]` and `FatPtr<T>` have the same layout.
// Only `std` can make this guarantee.
unsafe { Repr { rust: self }.raw }.len
}
#[cfg(not(bootstrap))]
metadata(self)
}

View file

@ -90,11 +90,8 @@ pub use crate::intrinsics::copy;
#[doc(inline)]
pub use crate::intrinsics::write_bytes;
#[cfg(not(bootstrap))]
mod metadata;
#[cfg(not(bootstrap))]
pub(crate) use metadata::PtrRepr;
#[cfg(not(bootstrap))]
#[unstable(feature = "ptr_metadata", issue = "81513")]
pub use metadata::{from_raw_parts, from_raw_parts_mut, metadata, DynMetadata, Pointee, Thin};
@ -236,33 +233,6 @@ pub const fn null_mut<T>() -> *mut T {
0 as *mut T
}
#[cfg(bootstrap)]
#[repr(C)]
pub(crate) union Repr<T> {
pub(crate) rust: *const [T],
rust_mut: *mut [T],
pub(crate) raw: FatPtr<T>,
}
#[cfg(bootstrap)]
#[repr(C)]
pub(crate) struct FatPtr<T> {
data: *const T,
pub(crate) len: usize,
}
#[cfg(bootstrap)]
// Manual impl needed to avoid `T: Clone` bound.
impl<T> Clone for FatPtr<T> {
fn clone(&self) -> Self {
*self
}
}
#[cfg(bootstrap)]
// Manual impl needed to avoid `T: Copy` bound.
impl<T> Copy for FatPtr<T> {}
/// Forms a raw slice from a pointer and a length.
///
/// The `len` argument is the number of **elements**, not the number of bytes.
@ -287,14 +257,6 @@ impl<T> Copy for FatPtr<T> {}
#[stable(feature = "slice_from_raw_parts", since = "1.42.0")]
#[rustc_const_unstable(feature = "const_slice_from_raw_parts", issue = "67456")]
pub const fn slice_from_raw_parts<T>(data: *const T, len: usize) -> *const [T] {
#[cfg(bootstrap)]
{
// SAFETY: Accessing the value from the `Repr` union is safe since *const [T]
// and FatPtr have the same memory layouts. Only std can make this
// guarantee.
unsafe { Repr { raw: FatPtr { data, len } }.rust }
}
#[cfg(not(bootstrap))]
from_raw_parts(data.cast(), len)
}
@ -327,13 +289,6 @@ pub const fn slice_from_raw_parts<T>(data: *const T, len: usize) -> *const [T] {
#[stable(feature = "slice_from_raw_parts", since = "1.42.0")]
#[rustc_const_unstable(feature = "const_slice_from_raw_parts", issue = "67456")]
pub const fn slice_from_raw_parts_mut<T>(data: *mut T, len: usize) -> *mut [T] {
#[cfg(bootstrap)]
{
// SAFETY: Accessing the value from the `Repr` union is safe since *mut [T]
// and FatPtr have the same memory layouts
unsafe { Repr { raw: FatPtr { data, len } }.rust_mut }
}
#[cfg(not(bootstrap))]
from_raw_parts_mut(data.cast(), len)
}
@ -473,19 +428,32 @@ pub const unsafe fn swap_nonoverlapping<T>(x: *mut T, y: *mut T, count: usize) {
#[inline]
#[rustc_const_unstable(feature = "const_swap", issue = "83163")]
pub(crate) const unsafe fn swap_nonoverlapping_one<T>(x: *mut T, y: *mut T) {
// For types smaller than the block optimization below,
// just swap directly to avoid pessimizing codegen.
if mem::size_of::<T>() < 32 {
// SAFETY: the caller must guarantee that `x` and `y` are valid
// for writes, properly aligned, and non-overlapping.
unsafe {
let z = read(x);
copy_nonoverlapping(y, x, 1);
write(y, z);
// NOTE(eddyb) SPIR-V's Logical addressing model doesn't allow for arbitrary
// reinterpretation of values as (chunkable) byte arrays, and the loop in the
// block optimization in `swap_nonoverlapping_bytes` is hard to rewrite back
// into the (unoptimized) direct swapping implementation, so we disable it.
// FIXME(eddyb) the block optimization also prevents MIR optimizations from
// understanding `mem::replace`, `Option::take`, etc. - a better overall
// solution might be to make `swap_nonoverlapping` into an intrinsic, which
// a backend can choose to implement using the block optimization, or not.
#[cfg(not(target_arch = "spirv"))]
{
// Only apply the block optimization in `swap_nonoverlapping_bytes` for types
// at least as large as the block size, to avoid pessimizing codegen.
if mem::size_of::<T>() >= 32 {
// SAFETY: the caller must uphold the safety contract for `swap_nonoverlapping`.
unsafe { swap_nonoverlapping(x, y, 1) };
return;
}
} else {
// SAFETY: the caller must uphold the safety contract for `swap_nonoverlapping`.
unsafe { swap_nonoverlapping(x, y, 1) };
}
// Direct swapping, for the cases not going through the block optimization.
// SAFETY: the caller must guarantee that `x` and `y` are valid
// for writes, properly aligned, and non-overlapping.
unsafe {
let z = read(x);
copy_nonoverlapping(y, x, 1);
write(y, z);
}
}
@ -1524,6 +1492,10 @@ fnptr_impls_args! { A, B, C, D, E, F, G, H, I, J, K, L }
/// as all other references. This macro can create a raw pointer *without* creating
/// a reference first.
///
/// Note, however, that the `expr` in `addr_of!(expr)` is still subject to all
/// the usual rules. In particular, `addr_of!(*ptr::null())` is Undefined
/// Behavior because it dereferences a NULL pointer.
///
/// # Example
///
/// ```
@ -1540,6 +1512,10 @@ fnptr_impls_args! { A, B, C, D, E, F, G, H, I, J, K, L }
/// let raw_f2 = ptr::addr_of!(packed.f2);
/// assert_eq!(unsafe { raw_f2.read_unaligned() }, 2);
/// ```
///
/// See [`addr_of_mut`] for how to create a pointer to unininitialized data.
/// Doing that with `addr_of` would not make much sense since one could only
/// read the data, and that would be Undefined Behavior.
#[stable(feature = "raw_ref_macros", since = "1.51.0")]
#[rustc_macro_transparency = "semitransparent"]
#[allow_internal_unstable(raw_ref_op)]
@ -1556,7 +1532,13 @@ pub macro addr_of($place:expr) {
/// as all other references. This macro can create a raw pointer *without* creating
/// a reference first.
///
/// # Example
/// Note, however, that the `expr` in `addr_of_mut!(expr)` is still subject to all
/// the usual rules. In particular, `addr_of_mut!(*ptr::null_mut())` is Undefined
/// Behavior because it dereferences a NULL pointer.
///
/// # Examples
///
/// **Creating a pointer to unaligned data:**
///
/// ```
/// use std::ptr;
@ -1573,6 +1555,23 @@ pub macro addr_of($place:expr) {
/// unsafe { raw_f2.write_unaligned(42); }
/// assert_eq!({packed.f2}, 42); // `{...}` forces copying the field instead of creating a reference.
/// ```
///
/// **Creating a pointer to uninitialized data:**
///
/// ```rust
/// use std::{ptr, mem::MaybeUninit};
///
/// struct Demo {
/// field: bool,
/// }
///
/// let mut uninit = MaybeUninit::<Demo>::uninit();
/// // `&uninit.as_mut().field` would create a reference to an uninitialized `bool`,
/// // and thus be Undefined Behavior!
/// let f1_ptr = unsafe { ptr::addr_of_mut!((*uninit.as_mut_ptr()).field) };
/// unsafe { f1_ptr.write(true); }
/// let init = unsafe { uninit.assume_init() };
/// ```
#[stable(feature = "raw_ref_macros", since = "1.51.0")]
#[rustc_macro_transparency = "semitransparent"]
#[allow_internal_unstable(raw_ref_op)]

View file

@ -50,7 +50,6 @@ impl<T: ?Sized> *mut T {
/// Decompose a (possibly wide) pointer into is address and metadata components.
///
/// The pointer can be later reconstructed with [`from_raw_parts_mut`].
#[cfg(not(bootstrap))]
#[unstable(feature = "ptr_metadata", issue = "81513")]
#[rustc_const_unstable(feature = "ptr_metadata", issue = "81513")]
#[inline]
@ -1175,13 +1174,6 @@ impl<T> *mut [T] {
#[unstable(feature = "slice_ptr_len", issue = "71146")]
#[rustc_const_unstable(feature = "const_slice_ptr_len", issue = "71146")]
pub const fn len(self) -> usize {
#[cfg(bootstrap)]
{
// SAFETY: this is safe because `*const [T]` and `FatPtr<T>` have the same layout.
// Only `std` can make this guarantee.
unsafe { Repr { rust_mut: self }.raw }.len
}
#[cfg(not(bootstrap))]
metadata(self)
}

View file

@ -181,7 +181,6 @@ impl<T: ?Sized> NonNull<T> {
/// See the documentation of [`std::ptr::from_raw_parts`] for more details.
///
/// [`std::ptr::from_raw_parts`]: crate::ptr::from_raw_parts
#[cfg(not(bootstrap))]
#[unstable(feature = "ptr_metadata", issue = "81513")]
#[rustc_const_unstable(feature = "ptr_metadata", issue = "81513")]
#[inline]
@ -198,7 +197,6 @@ impl<T: ?Sized> NonNull<T> {
/// Decompose a (possibly wide) pointer into is address and metadata components.
///
/// The pointer can be later reconstructed with [`NonNull::from_raw_parts`].
#[cfg(not(bootstrap))]
#[unstable(feature = "ptr_metadata", issue = "81513")]
#[rustc_const_unstable(feature = "ptr_metadata", issue = "81513")]
#[inline]

View file

@ -102,23 +102,14 @@ impl<T> [T] {
// SAFETY: const sound because we transmute out the length field as a usize (which it must be)
#[rustc_allow_const_fn_unstable(const_fn_union)]
pub const fn len(&self) -> usize {
#[cfg(bootstrap)]
{
// SAFETY: this is safe because `&[T]` and `FatPtr<T>` have the same layout.
// Only `std` can make this guarantee.
unsafe { crate::ptr::Repr { rust: self }.raw.len }
}
#[cfg(not(bootstrap))]
{
// FIXME: Replace with `crate::ptr::metadata(self)` when that is const-stable.
// As of this writing this causes a "Const-stable functions can only call other
// const-stable functions" error.
// FIXME: Replace with `crate::ptr::metadata(self)` when that is const-stable.
// As of this writing this causes a "Const-stable functions can only call other
// const-stable functions" error.
// SAFETY: Accessing the value from the `PtrRepr` union is safe since *const T
// and PtrComponents<T> have the same memory layouts. Only std can make this
// guarantee.
unsafe { crate::ptr::PtrRepr { const_ptr: self }.components.metadata }
}
// SAFETY: Accessing the value from the `PtrRepr` union is safe since *const T
// and PtrComponents<T> have the same memory layouts. Only std can make this
// guarantee.
unsafe { crate::ptr::PtrRepr { const_ptr: self }.components.metadata }
}
/// Returns `true` if the slice has a length of 0.
@ -2265,8 +2256,7 @@ impl<T> [T] {
// in crate `alloc`, and as such doesn't exists yet when building `core`.
// links to downstream crate: #74481. Since primitives are only documented in
// libstd (#73423), this never leads to broken links in practice.
#[cfg_attr(not(bootstrap), allow(rustdoc::broken_intra_doc_links))]
#[cfg_attr(bootstrap, allow(broken_intra_doc_links))]
#[allow(rustdoc::broken_intra_doc_links)]
#[stable(feature = "slice_binary_search_by_key", since = "1.10.0")]
#[inline]
pub fn binary_search_by_key<'a, B, F>(&'a self, b: &B, mut f: F) -> Result<usize, usize>

View file

@ -68,7 +68,7 @@
#![feature(option_result_unwrap_unchecked)]
#![feature(result_into_ok_or_err)]
#![feature(peekable_peek_mut)]
#![cfg_attr(not(bootstrap), feature(ptr_metadata))]
#![feature(ptr_metadata)]
#![feature(once_cell)]
#![feature(unsized_tuple_coercion)]
#![feature(nonzero_leading_trailing_zeros)]
@ -76,8 +76,7 @@
#![feature(integer_atomics)]
#![feature(slice_group_by)]
#![feature(trusted_random_access)]
#![cfg_attr(bootstrap, feature(unsafe_block_in_unsafe_fn))]
#![cfg_attr(not(bootstrap), feature(unsize))]
#![feature(unsize)]
#![deny(unsafe_op_in_unsafe_fn)]
extern crate test;

View file

@ -1,8 +1,6 @@
use core::cell::RefCell;
#[cfg(not(bootstrap))]
use core::ptr;
use core::ptr::*;
#[cfg(not(bootstrap))]
use std::fmt::{Debug, Display};
#[test]
@ -419,7 +417,6 @@ fn offset_from() {
}
#[test]
#[cfg(not(bootstrap))]
fn ptr_metadata() {
struct Unit;
struct Pair<A, B: ?Sized>(A, B);
@ -478,7 +475,6 @@ fn ptr_metadata() {
}
#[test]
#[cfg(not(bootstrap))]
fn ptr_metadata_bounds() {
fn metadata_eq_method_address<T: ?Sized>() -> usize {
// The `Metadata` associated type has an `Ord` bound, so this is valid:
@ -510,7 +506,6 @@ fn ptr_metadata_bounds() {
}
#[test]
#[cfg(not(bootstrap))]
fn dyn_metadata() {
#[derive(Debug)]
#[repr(align(32))]
@ -530,7 +525,6 @@ fn dyn_metadata() {
}
#[test]
#[cfg(not(bootstrap))]
fn from_raw_parts() {
let mut value = 5_u32;
let address = &mut value as *mut _ as *mut ();
@ -557,7 +551,6 @@ fn from_raw_parts() {
}
#[test]
#[cfg(not(bootstrap))]
fn thin_box() {
let foo = ThinBox::<dyn Display>::new(4);
assert_eq!(foo.to_string(), "4");

View file

@ -234,7 +234,7 @@
#![feature(box_syntax)]
#![feature(c_variadic)]
#![feature(cfg_accessible)]
#![cfg_attr(not(bootstrap), feature(cfg_eval))]
#![feature(cfg_eval)]
#![feature(cfg_target_has_atomic)]
#![feature(cfg_target_thread_local)]
#![feature(char_error_internals)]
@ -282,7 +282,6 @@
#![feature(intra_doc_pointers)]
#![feature(iter_zip)]
#![feature(lang_items)]
#![feature(link_args)]
#![feature(linkage)]
#![feature(llvm_asm)]
#![feature(log_syntax)]
@ -331,7 +330,6 @@
#![feature(try_blocks)]
#![feature(try_reserve)]
#![feature(unboxed_closures)]
#![cfg_attr(bootstrap, feature(unsafe_block_in_unsafe_fn))]
#![feature(unsafe_cell_raw_get)]
#![feature(unwind_attributes)]
#![feature(vec_into_raw_parts)]

View file

@ -993,6 +993,7 @@ impl Ord for Ipv4Addr {
}
impl IntoInner<c::in_addr> for Ipv4Addr {
#[inline]
fn into_inner(self) -> c::in_addr {
self.inner
}
@ -1800,11 +1801,13 @@ impl Ord for Ipv6Addr {
}
impl AsInner<c::in6_addr> for Ipv6Addr {
#[inline]
fn as_inner(&self) -> &c::in6_addr {
&self.inner
}
}
impl FromInner<c::in6_addr> for Ipv6Addr {
#[inline]
fn from_inner(addr: c::in6_addr) -> Ipv6Addr {
Ipv6Addr { inner: addr }
}

View file

@ -54,7 +54,6 @@ pub use core::prelude::v1::{
bench, global_allocator, test, test_case, RustcDecodable, RustcEncodable,
};
#[cfg(not(bootstrap))]
#[stable(feature = "builtin_macro_prelude", since = "1.38.0")]
#[doc(hidden)]
pub use core::prelude::v1::derive;
@ -67,7 +66,6 @@ pub use core::prelude::v1::derive;
#[doc(hidden)]
pub use core::prelude::v1::cfg_accessible;
#[cfg(not(bootstrap))]
#[unstable(
feature = "cfg_eval",
issue = "82679",

View file

@ -383,7 +383,7 @@ class RustBuild(object):
self.nix_deps_dir = None
self.rustc_commit = None
def download_stage0(self):
def download_toolchain(self, stage0=True, rustc_channel=None):
"""Fetch the build system for Rust, written in Rust
This method will build a cache directory, then it will fetch the
@ -393,43 +393,47 @@ class RustBuild(object):
Each downloaded tarball is extracted, after that, the script
will move all the content to the right place.
"""
rustc_channel = self.rustc_channel
if rustc_channel is None:
rustc_channel = self.rustc_channel
rustfmt_channel = self.rustfmt_channel
bin_root = self.bin_root(stage0)
if self.rustc().startswith(self.bin_root()) and \
(not os.path.exists(self.rustc()) or
self.program_out_of_date(self.rustc_stamp(), self.date + str(self.rustc_commit))):
if os.path.exists(self.bin_root()):
shutil.rmtree(self.bin_root())
download_rustc = self.rustc_commit is not None
key = self.date
if not stage0:
key += str(self.rustc_commit)
if self.rustc(stage0).startswith(bin_root) and \
(not os.path.exists(self.rustc(stage0)) or
self.program_out_of_date(self.rustc_stamp(stage0), key)):
if os.path.exists(bin_root):
shutil.rmtree(bin_root)
tarball_suffix = '.tar.xz' if support_xz() else '.tar.gz'
filename = "rust-std-{}-{}{}".format(
rustc_channel, self.build, tarball_suffix)
pattern = "rust-std-{}".format(self.build)
self._download_component_helper(filename, pattern, tarball_suffix, download_rustc)
self._download_component_helper(filename, pattern, tarball_suffix, stage0)
filename = "rustc-{}-{}{}".format(rustc_channel, self.build,
tarball_suffix)
self._download_component_helper(filename, "rustc", tarball_suffix, download_rustc)
self._download_component_helper(filename, "rustc", tarball_suffix, stage0)
filename = "cargo-{}-{}{}".format(rustc_channel, self.build,
tarball_suffix)
self._download_component_helper(filename, "cargo", tarball_suffix)
if self.rustc_commit is not None:
if not stage0:
filename = "rustc-dev-{}-{}{}".format(rustc_channel, self.build, tarball_suffix)
self._download_component_helper(
filename, "rustc-dev", tarball_suffix, download_rustc
filename, "rustc-dev", tarball_suffix, stage0
)
self.fix_bin_or_dylib("{}/bin/rustc".format(self.bin_root()))
self.fix_bin_or_dylib("{}/bin/rustdoc".format(self.bin_root()))
self.fix_bin_or_dylib("{}/bin/cargo".format(self.bin_root()))
lib_dir = "{}/lib".format(self.bin_root())
self.fix_bin_or_dylib("{}/bin/rustc".format(bin_root))
self.fix_bin_or_dylib("{}/bin/rustdoc".format(bin_root))
self.fix_bin_or_dylib("{}/bin/cargo".format(bin_root))
lib_dir = "{}/lib".format(bin_root)
for lib in os.listdir(lib_dir):
if lib.endswith(".so"):
self.fix_bin_or_dylib(os.path.join(lib_dir, lib), rpath_libz=True)
with output(self.rustc_stamp()) as rust_stamp:
rust_stamp.write(self.date + str(self.rustc_commit))
with output(self.rustc_stamp(stage0)) as rust_stamp:
rust_stamp.write(key)
if self.rustfmt() and self.rustfmt().startswith(self.bin_root()) and (
if self.rustfmt() and self.rustfmt().startswith(bin_root) and (
not os.path.exists(self.rustfmt())
or self.program_out_of_date(self.rustfmt_stamp(), self.rustfmt_channel)
):
@ -440,12 +444,13 @@ class RustBuild(object):
self._download_component_helper(
filename, "rustfmt-preview", tarball_suffix, key=date
)
self.fix_bin_or_dylib("{}/bin/rustfmt".format(self.bin_root()))
self.fix_bin_or_dylib("{}/bin/cargo-fmt".format(self.bin_root()))
self.fix_bin_or_dylib("{}/bin/rustfmt".format(bin_root))
self.fix_bin_or_dylib("{}/bin/cargo-fmt".format(bin_root))
with output(self.rustfmt_stamp()) as rustfmt_stamp:
rustfmt_stamp.write(self.rustfmt_channel)
if self.downloading_llvm():
# Avoid downloading LLVM twice (once for stage0 and once for the master rustc)
if self.downloading_llvm() and stage0:
# We want the most recent LLVM submodule update to avoid downloading
# LLVM more often than necessary.
#
@ -498,27 +503,26 @@ class RustBuild(object):
or (opt == "if-available" and self.build in supported_platforms)
def _download_component_helper(
self, filename, pattern, tarball_suffix, download_rustc=False, key=None
self, filename, pattern, tarball_suffix, stage0=True, key=None
):
if key is None:
if download_rustc:
key = self.rustc_commit
else:
if stage0:
key = self.date
else:
key = self.rustc_commit
cache_dst = os.path.join(self.build_dir, "cache")
rustc_cache = os.path.join(cache_dst, key)
if not os.path.exists(rustc_cache):
os.makedirs(rustc_cache)
if download_rustc:
url = "https://ci-artifacts.rust-lang.org/rustc-builds/{}".format(self.rustc_commit)
else:
if stage0:
url = "{}/dist/{}".format(self._download_url, key)
else:
url = "https://ci-artifacts.rust-lang.org/rustc-builds/{}".format(self.rustc_commit)
tarball = os.path.join(rustc_cache, filename)
if not os.path.exists(tarball):
do_verify = not download_rustc
get("{}/{}".format(url, filename), tarball, verbose=self.verbose, do_verify=do_verify)
unpack(tarball, tarball_suffix, self.bin_root(), match=pattern, verbose=self.verbose)
get("{}/{}".format(url, filename), tarball, verbose=self.verbose, do_verify=stage0)
unpack(tarball, tarball_suffix, self.bin_root(stage0), match=pattern, verbose=self.verbose)
def _download_ci_llvm(self, llvm_sha, llvm_assertions):
cache_prefix = "llvm-{}-{}".format(llvm_sha, llvm_assertions)
@ -576,10 +580,10 @@ class RustBuild(object):
nix_os_msg = "info: you seem to be running NixOS. Attempting to patch"
print(nix_os_msg, fname)
# Only build `stage0/.nix-deps` once.
# Only build `.nix-deps` once.
nix_deps_dir = self.nix_deps_dir
if not nix_deps_dir:
nix_deps_dir = "{}/.nix-deps".format(self.bin_root())
nix_deps_dir = ".nix-deps"
if not os.path.exists(nix_deps_dir):
os.makedirs(nix_deps_dir)
@ -637,11 +641,13 @@ class RustBuild(object):
print("warning: failed to call patchelf:", reason)
return
# Return the stage1 compiler to download, if any.
def maybe_download_rustc(self):
# If `download-rustc` is set, download the most recent commit with CI artifacts
def maybe_download_ci_toolchain(self):
# If `download-rustc` is not set, default to rebuilding.
if self.get_toml("download-rustc", section="rust") != "true":
download_rustc = self.get_toml("download-rustc", section="rust")
if download_rustc is None or download_rustc == "false":
return None
assert download_rustc == "true" or download_rustc == "if-unchanged", download_rustc
# Handle running from a directory other than the top level
rev_parse = ["git", "rev-parse", "--show-toplevel"]
@ -656,19 +662,27 @@ class RustBuild(object):
# Warn if there were changes to the compiler since the ancestor commit.
status = subprocess.call(["git", "diff-index", "--quiet", commit, "--", compiler])
if status != 0:
if download_rustc == "if-unchanged":
return None
print("warning: `download-rustc` is enabled, but there are changes to compiler/")
return commit
if self.verbose:
print("using downloaded stage1 artifacts from CI (commit {})".format(commit))
self.rustc_commit = commit
# FIXME: support downloading artifacts from the beta channel
self.download_toolchain(False, "nightly")
def rustc_stamp(self):
"""Return the path for .rustc-stamp
def rustc_stamp(self, stage0):
"""Return the path for .rustc-stamp at the given stage
>>> rb = RustBuild()
>>> rb.build_dir = "build"
>>> rb.rustc_stamp() == os.path.join("build", "stage0", ".rustc-stamp")
>>> rb.rustc_stamp(True) == os.path.join("build", "stage0", ".rustc-stamp")
True
>>> rb.rustc_stamp(False) == os.path.join("build", "ci-rustc", ".rustc-stamp")
True
"""
return os.path.join(self.bin_root(), '.rustc-stamp')
return os.path.join(self.bin_root(stage0), '.rustc-stamp')
def rustfmt_stamp(self):
"""Return the path for .rustfmt-stamp
@ -678,7 +692,7 @@ class RustBuild(object):
>>> rb.rustfmt_stamp() == os.path.join("build", "stage0", ".rustfmt-stamp")
True
"""
return os.path.join(self.bin_root(), '.rustfmt-stamp')
return os.path.join(self.bin_root(True), '.rustfmt-stamp')
def llvm_stamp(self):
"""Return the path for .rustfmt-stamp
@ -698,21 +712,27 @@ class RustBuild(object):
with open(stamp_path, 'r') as stamp:
return key != stamp.read()
def bin_root(self):
"""Return the binary root directory
def bin_root(self, stage0):
"""Return the binary root directory for the given stage
>>> rb = RustBuild()
>>> rb.build_dir = "build"
>>> rb.bin_root() == os.path.join("build", "stage0")
>>> rb.bin_root(True) == os.path.join("build", "stage0")
True
>>> rb.bin_root(False) == os.path.join("build", "ci-rustc")
True
When the 'build' property is given should be a nested directory:
>>> rb.build = "devel"
>>> rb.bin_root() == os.path.join("build", "devel", "stage0")
>>> rb.bin_root(True) == os.path.join("build", "devel", "stage0")
True
"""
return os.path.join(self.build_dir, self.build, "stage0")
if stage0:
subdir = "stage0"
else:
subdir = "ci-rustc"
return os.path.join(self.build_dir, self.build, subdir)
def llvm_root(self):
"""Return the CI LLVM root directory
@ -775,9 +795,9 @@ class RustBuild(object):
"""Return config path for cargo"""
return self.program_config('cargo')
def rustc(self):
def rustc(self, stage0):
"""Return config path for rustc"""
return self.program_config('rustc')
return self.program_config('rustc', stage0)
def rustfmt(self):
"""Return config path for rustfmt"""
@ -785,23 +805,27 @@ class RustBuild(object):
return None
return self.program_config('rustfmt')
def program_config(self, program):
"""Return config path for the given program
def program_config(self, program, stage0=True):
"""Return config path for the given program at the given stage
>>> rb = RustBuild()
>>> rb.config_toml = 'rustc = "rustc"\\n'
>>> rb.program_config('rustc')
'rustc'
>>> rb.config_toml = ''
>>> cargo_path = rb.program_config('cargo')
>>> cargo_path.rstrip(".exe") == os.path.join(rb.bin_root(),
>>> cargo_path = rb.program_config('cargo', True)
>>> cargo_path.rstrip(".exe") == os.path.join(rb.bin_root(True),
... "bin", "cargo")
True
>>> cargo_path = rb.program_config('cargo', False)
>>> cargo_path.rstrip(".exe") == os.path.join(rb.bin_root(False),
... "bin", "cargo")
True
"""
config = self.get_toml(program)
if config:
return os.path.expanduser(config)
return os.path.join(self.bin_root(), "bin", "{}{}".format(
return os.path.join(self.bin_root(stage0), "bin", "{}{}".format(
program, self.exe_suffix()))
@staticmethod
@ -856,14 +880,14 @@ class RustBuild(object):
if "CARGO_BUILD_TARGET" in env:
del env["CARGO_BUILD_TARGET"]
env["CARGO_TARGET_DIR"] = build_dir
env["RUSTC"] = self.rustc()
env["LD_LIBRARY_PATH"] = os.path.join(self.bin_root(), "lib") + \
env["RUSTC"] = self.rustc(True)
env["LD_LIBRARY_PATH"] = os.path.join(self.bin_root(True), "lib") + \
(os.pathsep + env["LD_LIBRARY_PATH"]) \
if "LD_LIBRARY_PATH" in env else ""
env["DYLD_LIBRARY_PATH"] = os.path.join(self.bin_root(), "lib") + \
env["DYLD_LIBRARY_PATH"] = os.path.join(self.bin_root(True), "lib") + \
(os.pathsep + env["DYLD_LIBRARY_PATH"]) \
if "DYLD_LIBRARY_PATH" in env else ""
env["LIBRARY_PATH"] = os.path.join(self.bin_root(), "lib") + \
env["LIBRARY_PATH"] = os.path.join(self.bin_root(True), "lib") + \
(os.pathsep + env["LIBRARY_PATH"]) \
if "LIBRARY_PATH" in env else ""
# preserve existing RUSTFLAGS
@ -886,7 +910,7 @@ class RustBuild(object):
if self.get_toml("deny-warnings", "rust") != "false":
env["RUSTFLAGS"] += " -Dwarnings"
env["PATH"] = os.path.join(self.bin_root(), "bin") + \
env["PATH"] = os.path.join(self.bin_root(True), "bin") + \
os.pathsep + env["PATH"]
if not os.path.isfile(self.cargo()):
raise Exception("no cargo executable found at `{}`".format(
@ -1137,14 +1161,9 @@ def bootstrap(help_triggered):
build.update_submodules()
# Fetch/build the bootstrap
build.rustc_commit = build.maybe_download_rustc()
if build.rustc_commit is not None:
if build.verbose:
commit = build.rustc_commit
print("using downloaded stage1 artifacts from CI (commit {})".format(commit))
# FIXME: support downloading artifacts from the beta channel
build.rustc_channel = "nightly"
build.download_stage0()
build.download_toolchain()
# Download the master compiler if `download-rustc` is set
build.maybe_download_ci_toolchain()
sys.stdout.flush()
build.ensure_vendored()
build.build_bootstrap()
@ -1160,6 +1179,8 @@ def bootstrap(help_triggered):
env["RUSTC_BOOTSTRAP"] = '1'
if toml_path:
env["BOOTSTRAP_CONFIG"] = toml_path
if build.rustc_commit is not None:
env["BOOTSTRAP_DOWNLOAD_RUSTC"] = '1'
run(args, env=env, verbose=build.verbose)

View file

@ -741,12 +741,7 @@ impl<'a> Builder<'a> {
.env("RUSTDOC_REAL", self.rustdoc(compiler))
.env("RUSTC_BOOTSTRAP", "1");
// cfg(bootstrap), can be removed on the next beta bump
if compiler.stage == 0 {
cmd.arg("-Winvalid_codeblock_attributes");
} else {
cmd.arg("-Wrustdoc::invalid_codeblock_attributes");
}
cmd.arg("-Wrustdoc::invalid_codeblock_attributes");
if self.config.deny_warnings {
cmd.arg("-Dwarnings");
@ -1303,12 +1298,7 @@ impl<'a> Builder<'a> {
// fixed via better support from Cargo.
cargo.env("RUSTC_LINT_FLAGS", lint_flags.join(" "));
// cfg(bootstrap), can be removed on the next beta bump
if compiler.stage == 0 {
rustdocflags.arg("-Winvalid_codeblock_attributes");
} else {
rustdocflags.arg("-Wrustdoc::invalid_codeblock_attributes");
}
rustdocflags.arg("-Wrustdoc::invalid_codeblock_attributes");
}
if mode == Mode::Rustc {

View file

@ -65,7 +65,9 @@ impl Step for Std {
// These artifacts were already copied (in `impl Step for Sysroot`).
// Don't recompile them.
if builder.config.download_rustc {
// NOTE: the ABI of the beta compiler is different from the ABI of the downloaded compiler,
// so its artifacts can't be reused.
if builder.config.download_rustc && compiler.stage != 0 {
return;
}
@ -513,7 +515,9 @@ impl Step for Rustc {
let compiler = self.compiler;
let target = self.target;
if builder.config.download_rustc {
// NOTE: the ABI of the beta compiler is different from the ABI of the downloaded compiler,
// so its artifacts can't be reused.
if builder.config.download_rustc && compiler.stage != 0 {
// Copy the existing artifacts instead of rebuilding them.
// NOTE: this path is only taken for tools linking to rustc-dev.
builder.ensure(Sysroot { compiler });
@ -934,14 +938,15 @@ impl Step for Sysroot {
t!(fs::create_dir_all(&sysroot));
// If we're downloading a compiler from CI, we can use the same compiler for all stages other than 0.
if builder.config.download_rustc {
if builder.config.download_rustc && compiler.stage != 0 {
assert_eq!(
builder.config.build, compiler.host,
"Cross-compiling is not yet supported with `download-rustc`",
);
// Copy the compiler into the correct sysroot.
let stage0_dir = builder.config.out.join(&*builder.config.build.triple).join("stage0");
builder.cp_r(&stage0_dir, &sysroot);
let ci_rustc_dir =
builder.config.out.join(&*builder.config.build.triple).join("ci-rustc");
builder.cp_r(&ci_rustc_dir, &sysroot);
return INTERNER.intern_path(sysroot);
}

View file

@ -510,7 +510,8 @@ struct Rust {
new_symbol_mangling: Option<bool>,
profile_generate: Option<String>,
profile_use: Option<String>,
download_rustc: Option<bool>,
// ignored; this is set from an env var set by bootstrap.py
download_rustc: Option<StringOrBool>,
}
/// TOML representation of how each build target is configured.
@ -687,51 +688,6 @@ impl Config {
set(&mut config.print_step_timings, build.print_step_timings);
set(&mut config.print_step_rusage, build.print_step_rusage);
// See https://github.com/rust-lang/compiler-team/issues/326
config.stage = match config.cmd {
Subcommand::Check { .. } => flags.stage.or(build.check_stage).unwrap_or(0),
Subcommand::Doc { .. } => flags.stage.or(build.doc_stage).unwrap_or(0),
Subcommand::Build { .. } => flags.stage.or(build.build_stage).unwrap_or(1),
Subcommand::Test { .. } => flags.stage.or(build.test_stage).unwrap_or(1),
Subcommand::Bench { .. } => flags.stage.or(build.bench_stage).unwrap_or(2),
Subcommand::Dist { .. } => flags.stage.or(build.dist_stage).unwrap_or(2),
Subcommand::Install { .. } => flags.stage.or(build.install_stage).unwrap_or(2),
// These are all bootstrap tools, which don't depend on the compiler.
// The stage we pass shouldn't matter, but use 0 just in case.
Subcommand::Clean { .. }
| Subcommand::Clippy { .. }
| Subcommand::Fix { .. }
| Subcommand::Run { .. }
| Subcommand::Setup { .. }
| Subcommand::Format { .. } => flags.stage.unwrap_or(0),
};
// CI should always run stage 2 builds, unless it specifically states otherwise
#[cfg(not(test))]
if flags.stage.is_none() && crate::CiEnv::current() != crate::CiEnv::None {
match config.cmd {
Subcommand::Test { .. }
| Subcommand::Doc { .. }
| Subcommand::Build { .. }
| Subcommand::Bench { .. }
| Subcommand::Dist { .. }
| Subcommand::Install { .. } => {
assert_eq!(
config.stage, 2,
"x.py should be run with `--stage 2` on CI, but was run with `--stage {}`",
config.stage,
);
}
Subcommand::Clean { .. }
| Subcommand::Check { .. }
| Subcommand::Clippy { .. }
| Subcommand::Fix { .. }
| Subcommand::Run { .. }
| Subcommand::Setup { .. }
| Subcommand::Format { .. } => {}
}
}
config.verbose = cmp::max(config.verbose, flags.verbose);
if let Some(install) = toml.install {
@ -897,7 +853,7 @@ impl Config {
config.rust_codegen_units_std = rust.codegen_units_std.map(threads_from_config);
config.rust_profile_use = flags.rust_profile_use.or(rust.profile_use);
config.rust_profile_generate = flags.rust_profile_generate.or(rust.profile_generate);
config.download_rustc = rust.download_rustc.unwrap_or(false);
config.download_rustc = env::var("BOOTSTRAP_DOWNLOAD_RUSTC").as_deref() == Ok("1");
} else {
config.rust_profile_use = flags.rust_profile_use;
config.rust_profile_generate = flags.rust_profile_generate;
@ -1005,6 +961,59 @@ impl Config {
let default = config.channel == "dev";
config.ignore_git = ignore_git.unwrap_or(default);
let download_rustc = config.download_rustc;
// See https://github.com/rust-lang/compiler-team/issues/326
config.stage = match config.cmd {
Subcommand::Check { .. } => flags.stage.or(build.check_stage).unwrap_or(0),
// `download-rustc` only has a speed-up for stage2 builds. Default to stage2 unless explicitly overridden.
Subcommand::Doc { .. } => {
flags.stage.or(build.doc_stage).unwrap_or(if download_rustc { 2 } else { 0 })
}
Subcommand::Build { .. } => {
flags.stage.or(build.build_stage).unwrap_or(if download_rustc { 2 } else { 1 })
}
Subcommand::Test { .. } => {
flags.stage.or(build.test_stage).unwrap_or(if download_rustc { 2 } else { 1 })
}
Subcommand::Bench { .. } => flags.stage.or(build.bench_stage).unwrap_or(2),
Subcommand::Dist { .. } => flags.stage.or(build.dist_stage).unwrap_or(2),
Subcommand::Install { .. } => flags.stage.or(build.install_stage).unwrap_or(2),
// These are all bootstrap tools, which don't depend on the compiler.
// The stage we pass shouldn't matter, but use 0 just in case.
Subcommand::Clean { .. }
| Subcommand::Clippy { .. }
| Subcommand::Fix { .. }
| Subcommand::Run { .. }
| Subcommand::Setup { .. }
| Subcommand::Format { .. } => flags.stage.unwrap_or(0),
};
// CI should always run stage 2 builds, unless it specifically states otherwise
#[cfg(not(test))]
if flags.stage.is_none() && crate::CiEnv::current() != crate::CiEnv::None {
match config.cmd {
Subcommand::Test { .. }
| Subcommand::Doc { .. }
| Subcommand::Build { .. }
| Subcommand::Bench { .. }
| Subcommand::Dist { .. }
| Subcommand::Install { .. } => {
assert_eq!(
config.stage, 2,
"x.py should be run with `--stage 2` on CI, but was run with `--stage {}`",
config.stage,
);
}
Subcommand::Clean { .. }
| Subcommand::Check { .. }
| Subcommand::Clippy { .. }
| Subcommand::Fix { .. }
| Subcommand::Run { .. }
| Subcommand::Setup { .. }
| Subcommand::Format { .. } => {}
}
}
config
}

View file

@ -461,7 +461,16 @@ impl Step for Std {
// create correct links between crates because rustdoc depends on the
// existence of the output directories to know if it should be a local
// or remote link.
let krates = ["core", "alloc", "std", "proc_macro", "test"];
//
// There's also a mild hack here where we build the first crate in this
// list, core, twice. This is currently necessary to make sure that
// cargo's cached rustc/rustdoc versions are up to date which means
// cargo won't delete the out_dir we create for the stampfile.
// Essentially any crate could go into the first slot here as it's
// output directory will be deleted by us (as cargo will purge the stamp
// file during the first slot's run), and core is relatively fast to
// build so works OK to fill this 'dummy' slot.
let krates = ["core", "core", "alloc", "std", "proc_macro", "test"];
for krate in &krates {
run_cargo_rustdoc_for(krate);
}

View file

@ -513,6 +513,19 @@ impl Step for Rustdoc {
// rustc compiler it's paired with, so it must be built with the previous stage compiler.
let build_compiler = builder.compiler(target_compiler.stage - 1, builder.config.build);
// When using `download-rustc` and a stage0 build_compiler, copying rustc doesn't actually
// build stage0 libstd (because the libstd in sysroot has the wrong ABI). Explicitly build
// it.
builder.ensure(compile::Std { compiler: build_compiler, target: target_compiler.host });
builder.ensure(compile::Rustc { compiler: build_compiler, target: target_compiler.host });
// NOTE: this implies that `download-rustc` is pretty useless when compiling with the stage0
// compiler, since you do just as much work.
if !builder.config.dry_run && builder.config.download_rustc && build_compiler.stage == 0 {
println!(
"warning: `download-rustc` does nothing when building stage1 tools; consider using `--stage 2` instead"
);
}
// The presence of `target_compiler` ensures that the necessary libraries (codegen backends,
// compiler libraries, ...) are built. Rustdoc does not require the presence of any
// libraries within sysroot_libdir (i.e., rustlib), though doctests may want it (since

View file

@ -216,6 +216,7 @@ target | std | host | notes
`thumbv7a-uwp-windows-msvc` | ✓ | |
`thumbv7neon-unknown-linux-musleabihf` | ? | | Thumb2-mode ARMv7a Linux with NEON, MUSL
`thumbv4t-none-eabi` | * | | ARMv4T T32
`wasm64-unknown-unknown` | * | | WebAssembly
`x86_64-apple-ios-macabi` | ✓ | | Apple Catalyst on x86_64
`x86_64-apple-tvos` | * | | x86 64-bit tvOS
`x86_64-unknown-none-linuxkernel` | * | | Linux kernel modules

View file

@ -1,32 +0,0 @@
# `link_args`
The tracking issue for this feature is: [#29596]
[#29596]: https://github.com/rust-lang/rust/issues/29596
------------------------
You can tell `rustc` how to customize linking, and that is via the `link_args`
attribute. This attribute is applied to `extern` blocks and specifies raw flags
which need to get passed to the linker when producing an artifact. An example
usage would be:
```rust,no_run
#![feature(link_args)]
#[link_args = "-foo -bar -baz"]
extern "C" {}
# fn main() {}
```
Note that this feature is currently hidden behind the `feature(link_args)` gate
because this is not a sanctioned way of performing linking. Right now `rustc`
shells out to the system linker (`gcc` on most systems, `link.exe` on MSVC), so
it makes sense to provide extra command line arguments, but this will not
always be the case. In the future `rustc` may use LLVM directly to link native
libraries, in which case `link_args` will have no meaning. You can achieve the
same effect as the `link_args` attribute with the `-C link-args` argument to
`rustc`.
It is highly recommended to *not* use this attribute, and rather use the more
formal `#[link(...)]` attribute on `extern` blocks instead.

View file

@ -306,13 +306,19 @@ fn call_foo(arg: i32) {
sym foo,
// 1st argument in rdi, which is caller-saved
inout("rdi") arg => _,
// All caller-saved registers must be marked as clobberred
// All caller-saved registers must be marked as clobbered
out("rax") _, out("rcx") _, out("rdx") _, out("rsi") _,
out("r8") _, out("r9") _, out("r10") _, out("r11") _,
out("xmm0") _, out("xmm1") _, out("xmm2") _, out("xmm3") _,
out("xmm4") _, out("xmm5") _, out("xmm6") _, out("xmm7") _,
out("xmm8") _, out("xmm9") _, out("xmm10") _, out("xmm11") _,
out("xmm12") _, out("xmm13") _, out("xmm14") _, out("xmm15") _,
// Also mark AVX-512 registers as clobbered. This is accepted by the
// compiler even if AVX-512 is not enabled on the current target.
out("xmm16") _, out("xmm17") _, out("xmm18") _, out("xmm19") _,
out("xmm20") _, out("xmm21") _, out("xmm22") _, out("xmm13") _,
out("xmm24") _, out("xmm25") _, out("xmm26") _, out("xmm27") _,
out("xmm28") _, out("xmm29") _, out("xmm30") _, out("xmm31") _,
)
}
}
@ -495,7 +501,7 @@ Here is the list of currently supported register classes:
| x86 | `reg` | `ax`, `bx`, `cx`, `dx`, `si`, `di`, `r[8-15]` (x86-64 only) | `r` |
| x86 | `reg_abcd` | `ax`, `bx`, `cx`, `dx` | `Q` |
| x86-32 | `reg_byte` | `al`, `bl`, `cl`, `dl`, `ah`, `bh`, `ch`, `dh` | `q` |
| x86-64 | `reg_byte` | `al`, `bl`, `cl`, `dl`, `sil`, `dil`, `r[8-15]b`, `ah`\*, `bh`\*, `ch`\*, `dh`\* | `q` |
| x86-64 | `reg_byte`\* | `al`, `bl`, `cl`, `dl`, `sil`, `dil`, `r[8-15]b` | `q` |
| x86 | `xmm_reg` | `xmm[0-7]` (x86) `xmm[0-15]` (x86-64) | `x` |
| x86 | `ymm_reg` | `ymm[0-7]` (x86) `ymm[0-15]` (x86-64) | `x` |
| x86 | `zmm_reg` | `zmm[0-7]` (x86) `zmm[0-31]` (x86-64) | `v` |
@ -526,7 +532,7 @@ Here is the list of currently supported register classes:
> **Note**: On x86 we treat `reg_byte` differently from `reg` because the compiler can allocate `al` and `ah` separately whereas `reg` reserves the whole register.
>
> Note #2: On x86-64 the high byte registers (e.g. `ah`) are only available when used as an explicit register. Specifying the `reg_byte` register class for an operand will always allocate a low byte register.
> Note #2: On x86-64 the high byte registers (e.g. `ah`) are not available in the `reg_byte` register class.
>
> Note #3: NVPTX doesn't have a fixed register set, so named registers are not supported.
>

View file

@ -487,6 +487,7 @@ impl<'a> fmt::Display for Display<'a> {
"windows" => "Windows",
_ => "",
},
(sym::wasm, None) => "WebAssembly",
(sym::target_arch, Some(arch)) => match &*arch.as_str() {
"aarch64" => "AArch64",
"arm" => "ARM",
@ -498,7 +499,7 @@ impl<'a> fmt::Display for Display<'a> {
"powerpc64" => "PowerPC-64",
"s390x" => "s390x",
"sparc64" => "SPARC64",
"wasm32" => "WebAssembly",
"wasm32" | "wasm64" => "WebAssembly",
"x86" => "x86",
"x86_64" => "x86-64",
_ => "",

View file

@ -251,19 +251,9 @@ crate fn name_from_pat(p: &hir::Pat<'_>) -> Symbol {
debug!("trying to get a name from pattern: {:?}", p);
Symbol::intern(&match p.kind {
PatKind::Wild => return kw::Underscore,
PatKind::Wild | PatKind::Struct(..) => return kw::Underscore,
PatKind::Binding(_, _, ident, _) => return ident.name,
PatKind::TupleStruct(ref p, ..) | PatKind::Path(ref p) => qpath_to_string(p),
PatKind::Struct(ref name, ref fields, etc) => format!(
"{} {{ {}{} }}",
qpath_to_string(name),
fields
.iter()
.map(|fp| format!("{}: {}", fp.ident, name_from_pat(&fp.pat)))
.collect::<Vec<String>>()
.join(", "),
if etc { ", .." } else { "" }
),
PatKind::Or(ref pats) => pats
.iter()
.map(|p| name_from_pat(&**p).to_string())

View file

@ -156,6 +156,8 @@ crate struct Options {
/// If this option is set to `true`, rustdoc will only run checks and not generate
/// documentation.
crate run_check: bool,
/// Whether doctests should emit unused externs
crate json_unused_externs: bool,
}
impl fmt::Debug for Options {
@ -355,7 +357,8 @@ impl Options {
}
let color = config::parse_color(&matches);
let (json_rendered, _artifacts) = config::parse_json(&matches);
let config::JsonConfig { json_rendered, json_unused_externs, .. } =
config::parse_json(&matches);
let error_format = config::parse_error_format(&matches, color, json_rendered);
let codegen_options = build_codegen_options(matches, error_format);
@ -484,7 +487,9 @@ impl Options {
return Err(1);
}
if theme_file.extension() != Some(OsStr::new("css")) {
diag.struct_err(&format!("invalid argument: \"{}\"", theme_s)).emit();
diag.struct_err(&format!("invalid argument: \"{}\"", theme_s))
.help("arguments to --theme must have a .css extension")
.emit();
return Err(1);
}
let (success, ret) = theme::test_theme_against(&theme_file, &paths, &diag);
@ -510,7 +515,6 @@ impl Options {
let edition = config::parse_crate_edition(&matches);
let mut id_map = html::markdown::IdMap::new();
id_map.populate(&html::render::INITIAL_IDS);
let external_html = match ExternalHtml::load(
&matches.opt_strs("html-in-header"),
&matches.opt_strs("html-before-content"),
@ -692,6 +696,7 @@ impl Options {
},
crate_name,
output_format,
json_unused_externs,
})
}

View file

@ -1,5 +1,5 @@
use rustc_ast as ast;
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_data_structures::sync::Lrc;
use rustc_errors::{ColorConfig, ErrorReported};
use rustc_hir as hir;
@ -23,6 +23,8 @@ use std::panic;
use std::path::PathBuf;
use std::process::{self, Command, Stdio};
use std::str;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::{Arc, Mutex};
use crate::clean::Attributes;
use crate::config::Options;
@ -104,8 +106,10 @@ crate fn run(options: Options) -> Result<(), ErrorReported> {
let mut test_args = options.test_args.clone();
let display_warnings = options.display_warnings;
let externs = options.externs.clone();
let json_unused_externs = options.json_unused_externs;
let tests = interface::run_compiler(config, |compiler| {
let res = interface::run_compiler(config, |compiler| {
compiler.enter(|queries| {
let _lower_to_hir = queries.lower_to_hir()?;
@ -151,12 +155,15 @@ crate fn run(options: Options) -> Result<(), ErrorReported> {
});
compiler.session().abort_if_errors();
let ret: Result<_, ErrorReported> = Ok(collector.tests);
let unused_extern_reports = collector.unused_extern_reports.clone();
let compiling_test_count = collector.compiling_test_count.load(Ordering::SeqCst);
let ret: Result<_, ErrorReported> =
Ok((collector.tests, unused_extern_reports, compiling_test_count));
ret
})
});
let tests = match tests {
Ok(tests) => tests,
let (tests, unused_extern_reports, compiling_test_count) = match res {
Ok(res) => res,
Err(ErrorReported) => return Err(ErrorReported),
};
@ -168,6 +175,44 @@ crate fn run(options: Options) -> Result<(), ErrorReported> {
Some(testing::Options::new().display_output(display_warnings)),
);
// Collect and warn about unused externs, but only if we've gotten
// reports for each doctest
if json_unused_externs {
let unused_extern_reports: Vec<_> =
std::mem::take(&mut unused_extern_reports.lock().unwrap());
if unused_extern_reports.len() == compiling_test_count {
let extern_names = externs.iter().map(|(name, _)| name).collect::<FxHashSet<&String>>();
let mut unused_extern_names = unused_extern_reports
.iter()
.map(|uexts| uexts.unused_extern_names.iter().collect::<FxHashSet<&String>>())
.fold(extern_names, |uextsa, uextsb| {
uextsa.intersection(&uextsb).map(|v| *v).collect::<FxHashSet<&String>>()
})
.iter()
.map(|v| (*v).clone())
.collect::<Vec<String>>();
unused_extern_names.sort();
// Take the most severe lint level
let lint_level = unused_extern_reports
.iter()
.map(|uexts| uexts.lint_level.as_str())
.max_by_key(|v| match *v {
"warn" => 1,
"deny" => 2,
"forbid" => 3,
// The allow lint level is not expected,
// as if allow is specified, no message
// is to be emitted.
v => unreachable!("Invalid lint level '{}'", v),
})
.unwrap_or("warn")
.to_string();
let uext = UnusedExterns { lint_level, unused_extern_names };
let unused_extern_json = serde_json::to_string(&uext).unwrap();
eprintln!("{}", unused_extern_json);
}
}
Ok(())
}
@ -235,6 +280,18 @@ impl DirState {
}
}
// NOTE: Keep this in sync with the equivalent structs in rustc
// and cargo.
// We could unify this struct the one in rustc but they have different
// ownership semantics, so doing so would create wasteful allocations.
#[derive(serde::Serialize, serde::Deserialize)]
struct UnusedExterns {
/// Lint level of the unused_crate_dependencies lint
lint_level: String,
/// List of unused externs by their names.
unused_extern_names: Vec<String>,
}
fn run_test(
test: &str,
cratename: &str,
@ -253,6 +310,7 @@ fn run_test(
outdir: DirState,
path: PathBuf,
test_id: &str,
report_unused_externs: impl Fn(UnusedExterns),
) -> Result<(), TestFailure> {
let (test, line_offset, supports_color) =
make_test(test, Some(cratename), as_test_harness, opts, edition, Some(test_id));
@ -278,6 +336,12 @@ fn run_test(
if as_test_harness {
compiler.arg("--test");
}
if options.json_unused_externs && !compile_fail {
compiler.arg("--error-format=json");
compiler.arg("--json").arg("unused-externs");
compiler.arg("-Z").arg("unstable-options");
compiler.arg("-W").arg("unused_crate_dependencies");
}
for lib_str in &options.lib_strs {
compiler.arg("-L").arg(&lib_str);
}
@ -337,7 +401,26 @@ fn run_test(
eprint!("{}", self.0);
}
}
let out = str::from_utf8(&output.stderr).unwrap();
let mut out_lines = str::from_utf8(&output.stderr)
.unwrap()
.lines()
.filter(|l| {
if let Ok(uext) = serde_json::from_str::<UnusedExterns>(l) {
report_unused_externs(uext);
false
} else {
true
}
})
.collect::<Vec<_>>();
// Add a \n to the end to properly terminate the last line,
// but only if there was output to be printed
if out_lines.len() > 0 {
out_lines.push("");
}
let out = out_lines.join("\n");
let _bomb = Bomb(&out);
match (output.status.success(), compile_fail) {
(true, true) => {
@ -721,6 +804,8 @@ crate struct Collector {
source_map: Option<Lrc<SourceMap>>,
filename: Option<PathBuf>,
visited_tests: FxHashMap<(String, usize), usize>,
unused_extern_reports: Arc<Mutex<Vec<UnusedExterns>>>,
compiling_test_count: AtomicUsize,
}
impl Collector {
@ -745,6 +830,8 @@ impl Collector {
source_map,
filename,
visited_tests: FxHashMap::default(),
unused_extern_reports: Default::default(),
compiling_test_count: AtomicUsize::new(0),
}
}
@ -791,6 +878,10 @@ impl Tester for Collector {
let runtool_args = self.options.runtool_args.clone();
let target = self.options.target.clone();
let target_str = target.to_string();
let unused_externs = self.unused_extern_reports.clone();
if !config.compile_fail {
self.compiling_test_count.fetch_add(1, Ordering::SeqCst);
}
// FIXME(#44940): if doctests ever support path remapping, then this filename
// needs to be the result of `SourceMap::span_to_unmapped_path`.
@ -846,6 +937,9 @@ impl Tester for Collector {
test_type: testing::TestType::DocTest,
},
testfn: testing::DynTestFn(box move || {
let report_unused_externs = |uext| {
unused_externs.lock().unwrap().push(uext);
};
let option_no_run = options.no_run;
let res = run_test(
&test,
@ -865,6 +959,7 @@ impl Tester for Collector {
outdir,
path,
&test_id,
report_unused_externs,
);
if let Err(err) = res {

View file

@ -189,7 +189,9 @@ impl<'a> Classifier<'a> {
// leading identifier.
TokenKind::Bang if self.in_macro => {
self.in_macro = false;
Class::Macro
sink(Highlight::Token { text, class: None });
sink(Highlight::ExitSpan);
return;
}
// Assume that '&' or '*' is the reference or dereference operator
@ -298,7 +300,9 @@ impl<'a> Classifier<'a> {
},
TokenKind::Ident | TokenKind::RawIdent if lookahead == Some(TokenKind::Bang) => {
self.in_macro = true;
Class::Macro
sink(Highlight::EnterSpan { class: Class::Macro });
sink(Highlight::Token { text, class: None });
return;
}
TokenKind::Ident => match text {
"ref" | "mut" => Class::RefKeyWord,

View file

@ -1,3 +1,3 @@
<span class="kw">pub</span> <span class="kw">fn</span> <span class="ident">foo</span>() {
<span class="macro">println</span><span class="macro">!</span>(<span class="string">&quot;foo&quot;</span>);
<span class="macro">println!</span>(<span class="string">&quot;foo&quot;</span>);
}

Some files were not shown because too many files have changed in this diff Show more