Merge branch 'master' into master
This commit is contained in:
commit
b08b4848c3
447 changed files with 3097 additions and 2989 deletions
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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),
|
||||
}
|
||||
|
|
|
@ -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 => {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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)>,
|
||||
|
|
|
@ -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,
|
||||
_ => &[],
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
|
|
|
@ -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>;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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),
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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, _)| {
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -26,7 +26,6 @@ pub use rmeta::{provide, provide_extern};
|
|||
|
||||
mod dependency_format;
|
||||
mod foreign_modules;
|
||||
mod link_args;
|
||||
mod native_libs;
|
||||
mod rmeta;
|
||||
|
||||
|
|
|
@ -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()))
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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({}{:?})",
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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: _ } => {
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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 { .. }
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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)]`.
|
||||
|
|
|
@ -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>);
|
||||
|
|
|
@ -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)"),
|
||||
|
|
|
@ -1294,6 +1294,7 @@ symbols! {
|
|||
vreg,
|
||||
vreg_low16,
|
||||
warn,
|
||||
wasm,
|
||||
wasm_import_module,
|
||||
wasm_target_feature,
|
||||
while_let,
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)),
|
||||
}
|
||||
|
||||
|
|
58
compiler/rustc_target/src/abi/call/wasm64.rs
Normal file
58
compiler/rustc_target/src/abi/call/wasm64.rs
Normal 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);
|
||||
}
|
||||
}
|
|
@ -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")
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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(()),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
|
39
compiler/rustc_target/src/spec/wasm64_unknown_unknown.rs
Normal file
39
compiler/rustc_target/src/spec/wasm64_unknown_unknown.rs
Normal 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,
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
{
|
||||
}
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -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 }
|
||||
}
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
|
@ -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.
|
||||
>
|
||||
|
|
|
@ -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",
|
||||
_ => "",
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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">"foo"</span>);
|
||||
<span class="macro">println!</span>(<span class="string">"foo"</span>);
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue