mirror of https://github.com/rust-lang/rust.git
Auto merge of #77926 - Dylan-DPC:rollup-wttr8a1, r=Dylan-DPC
Rollup of 8 pull requests Successful merges: - #77765 (Add LLVM flags to limit DWARF version to 2 on BSD) - #77788 (BTreeMap: fix gdb provider on BTreeMap with ZST keys or values) - #77795 (Codegen backend interface refactor) - #77808 (Moved the main `impl` for FnCtxt to its own file.) - #77817 (Switch rustdoc from `clean::Stability` to `rustc_attr::Stability`) - #77829 (bootstrap: only use compiler-builtins-c if they exist) - #77870 (Use intra-doc links for links to module-level docs) - #77897 (Move `Strip` into a separate rustdoc pass) Failed merges: - #77879 (Provide better documentation and help messages for x.py setup) - #77902 (Include aarch64-pc-windows-msvc in the dist manifests) r? `@ghost`
This commit is contained in:
commit
31e4087b90
|
@ -154,7 +154,7 @@ pub struct ConstStability {
|
|||
}
|
||||
|
||||
/// The available stability levels.
|
||||
#[derive(Encodable, Decodable, PartialEq, PartialOrd, Copy, Clone, Debug, Eq, Hash)]
|
||||
#[derive(Encodable, Decodable, PartialEq, Copy, Clone, Debug, Eq, Hash)]
|
||||
#[derive(HashStable_Generic)]
|
||||
pub enum StabilityLevel {
|
||||
// Reason for the current stability level and the relevant rust-lang issue
|
||||
|
|
|
@ -6,7 +6,7 @@ use rustc_codegen_ssa::traits::*;
|
|||
use rustc_data_structures::const_cstr;
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_data_structures::small_c_str::SmallCStr;
|
||||
use rustc_hir::def_id::{DefId, LOCAL_CRATE};
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
|
||||
use rustc_middle::ty::layout::HasTyCtxt;
|
||||
use rustc_middle::ty::query::Providers;
|
||||
|
@ -367,23 +367,7 @@ pub fn from_fn_attrs(cx: &CodegenCx<'ll, 'tcx>, llfn: &'ll Value, instance: ty::
|
|||
}
|
||||
}
|
||||
|
||||
pub fn provide(providers: &mut Providers) {
|
||||
use rustc_codegen_ssa::target_features::{all_known_features, supported_target_features};
|
||||
providers.supported_target_features = |tcx, cnum| {
|
||||
assert_eq!(cnum, LOCAL_CRATE);
|
||||
if tcx.sess.opts.actually_rustdoc {
|
||||
// rustdoc needs to be able to document functions that use all the features, so
|
||||
// provide them all.
|
||||
all_known_features().map(|(a, b)| (a.to_string(), b)).collect()
|
||||
} else {
|
||||
supported_target_features(tcx.sess).iter().map(|&(a, b)| (a.to_string(), b)).collect()
|
||||
}
|
||||
};
|
||||
|
||||
provide_extern(providers);
|
||||
}
|
||||
|
||||
pub fn provide_extern(providers: &mut Providers) {
|
||||
pub fn provide_both(providers: &mut Providers) {
|
||||
providers.wasm_import_module_map = |tcx, cnum| {
|
||||
// Build up a map from DefId to a `NativeLib` structure, where
|
||||
// `NativeLib` internally contains information about
|
||||
|
|
|
@ -120,10 +120,8 @@ pub fn finalize(cx: &CodegenCx<'_, '_>) {
|
|||
// for macOS to understand. For more info see #11352
|
||||
// This can be overridden using --llvm-opts -dwarf-version,N.
|
||||
// Android has the same issue (#22398)
|
||||
if cx.sess().target.target.options.is_like_osx
|
||||
|| cx.sess().target.target.options.is_like_android
|
||||
{
|
||||
llvm::LLVMRustAddModuleFlag(cx.llmod, "Dwarf Version\0".as_ptr().cast(), 2)
|
||||
if let Some(version) = cx.sess().target.target.options.dwarf_version {
|
||||
llvm::LLVMRustAddModuleFlag(cx.llmod, "Dwarf Version\0".as_ptr().cast(), version)
|
||||
}
|
||||
|
||||
// Indicate that we want CodeView debug information on MSVC
|
||||
|
|
|
@ -23,18 +23,17 @@ use rustc_codegen_ssa::back::write::{CodegenContext, FatLTOInput, ModuleConfig};
|
|||
use rustc_codegen_ssa::traits::*;
|
||||
use rustc_codegen_ssa::ModuleCodegen;
|
||||
use rustc_codegen_ssa::{CodegenResults, CompiledModule};
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_errors::{ErrorReported, FatalError, Handler};
|
||||
use rustc_middle::dep_graph::{DepGraph, WorkProduct};
|
||||
use rustc_middle::dep_graph::{WorkProduct, WorkProductId};
|
||||
use rustc_middle::middle::cstore::{EncodedMetadata, MetadataLoaderDyn};
|
||||
use rustc_middle::ty::{self, TyCtxt};
|
||||
use rustc_serialize::json;
|
||||
use rustc_session::config::{self, OptLevel, OutputFilenames, PrintRequest};
|
||||
use rustc_session::config::{OptLevel, OutputFilenames, PrintRequest};
|
||||
use rustc_session::Session;
|
||||
use rustc_span::symbol::Symbol;
|
||||
|
||||
use std::any::Any;
|
||||
use std::ffi::CStr;
|
||||
use std::fs;
|
||||
use std::sync::Arc;
|
||||
|
||||
mod back {
|
||||
|
@ -252,11 +251,11 @@ impl CodegenBackend for LlvmCodegenBackend {
|
|||
}
|
||||
|
||||
fn provide(&self, providers: &mut ty::query::Providers) {
|
||||
attributes::provide(providers);
|
||||
attributes::provide_both(providers);
|
||||
}
|
||||
|
||||
fn provide_extern(&self, providers: &mut ty::query::Providers) {
|
||||
attributes::provide_extern(providers);
|
||||
attributes::provide_both(providers);
|
||||
}
|
||||
|
||||
fn codegen_crate<'tcx>(
|
||||
|
@ -277,47 +276,27 @@ impl CodegenBackend for LlvmCodegenBackend {
|
|||
&self,
|
||||
ongoing_codegen: Box<dyn Any>,
|
||||
sess: &Session,
|
||||
dep_graph: &DepGraph,
|
||||
) -> Result<Box<dyn Any>, ErrorReported> {
|
||||
) -> Result<(CodegenResults, FxHashMap<WorkProductId, WorkProduct>), ErrorReported> {
|
||||
let (codegen_results, work_products) = ongoing_codegen
|
||||
.downcast::<rustc_codegen_ssa::back::write::OngoingCodegen<LlvmCodegenBackend>>()
|
||||
.expect("Expected LlvmCodegenBackend's OngoingCodegen, found Box<Any>")
|
||||
.join(sess);
|
||||
if sess.opts.debugging_opts.incremental_info {
|
||||
rustc_codegen_ssa::back::write::dump_incremental_data(&codegen_results);
|
||||
}
|
||||
|
||||
sess.time("serialize_work_products", move || {
|
||||
rustc_incremental::save_work_product_index(sess, &dep_graph, work_products)
|
||||
sess.time("llvm_dump_timing_file", || {
|
||||
if sess.opts.debugging_opts.llvm_time_trace {
|
||||
llvm_util::time_trace_profiler_finish("llvm_timings.json");
|
||||
}
|
||||
});
|
||||
|
||||
sess.compile_status()?;
|
||||
|
||||
Ok(Box::new(codegen_results))
|
||||
Ok((codegen_results, work_products))
|
||||
}
|
||||
|
||||
fn link(
|
||||
&self,
|
||||
sess: &Session,
|
||||
codegen_results: Box<dyn Any>,
|
||||
codegen_results: CodegenResults,
|
||||
outputs: &OutputFilenames,
|
||||
) -> Result<(), ErrorReported> {
|
||||
let codegen_results = codegen_results
|
||||
.downcast::<CodegenResults>()
|
||||
.expect("Expected CodegenResults, found Box<Any>");
|
||||
|
||||
if sess.opts.debugging_opts.no_link {
|
||||
// FIXME: use a binary format to encode the `.rlink` file
|
||||
let rlink_data = json::encode(&codegen_results).map_err(|err| {
|
||||
sess.fatal(&format!("failed to encode rlink: {}", err));
|
||||
})?;
|
||||
let rlink_file = outputs.with_extension(config::RLINK_EXT);
|
||||
fs::write(&rlink_file, rlink_data).map_err(|err| {
|
||||
sess.fatal(&format!("failed to write file {}: {}", rlink_file.display(), err));
|
||||
})?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// Run the linker on any artifacts that resulted from the LLVM run.
|
||||
// This should produce either a finished executable or library.
|
||||
sess.time("link_crate", || {
|
||||
|
@ -334,16 +313,6 @@ impl CodegenBackend for LlvmCodegenBackend {
|
|||
);
|
||||
});
|
||||
|
||||
// Now that we won't touch anything in the incremental compilation directory
|
||||
// any more, we can finalize it (which involves renaming it)
|
||||
rustc_incremental::finalize_session_directory(sess, codegen_results.crate_hash);
|
||||
|
||||
sess.time("llvm_dump_timing_file", || {
|
||||
if sess.opts.debugging_opts.llvm_time_trace {
|
||||
llvm_util::time_trace_profiler_finish("llvm_timings.json");
|
||||
}
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,7 +13,6 @@ use rustc_data_structures::fx::FxHashMap;
|
|||
use rustc_data_structures::profiling::SelfProfilerRef;
|
||||
use rustc_data_structures::profiling::TimingGuard;
|
||||
use rustc_data_structures::profiling::VerboseTimingGuard;
|
||||
use rustc_data_structures::svh::Svh;
|
||||
use rustc_data_structures::sync::Lrc;
|
||||
use rustc_errors::emitter::Emitter;
|
||||
use rustc_errors::{DiagnosticId, FatalError, Handler, Level};
|
||||
|
@ -414,7 +413,6 @@ pub fn start_async_codegen<B: ExtraBackendMethods>(
|
|||
let sess = tcx.sess;
|
||||
|
||||
let crate_name = tcx.crate_name(LOCAL_CRATE);
|
||||
let crate_hash = tcx.crate_hash(LOCAL_CRATE);
|
||||
let no_builtins = tcx.sess.contains_name(&tcx.hir().krate().item.attrs, sym::no_builtins);
|
||||
let is_compiler_builtins =
|
||||
tcx.sess.contains_name(&tcx.hir().krate().item.attrs, sym::compiler_builtins);
|
||||
|
@ -463,7 +461,6 @@ pub fn start_async_codegen<B: ExtraBackendMethods>(
|
|||
OngoingCodegen {
|
||||
backend,
|
||||
crate_name,
|
||||
crate_hash,
|
||||
metadata,
|
||||
windows_subsystem,
|
||||
linker_info,
|
||||
|
@ -658,15 +655,6 @@ fn produce_final_output_artifacts(
|
|||
// These are used in linking steps and will be cleaned up afterward.
|
||||
}
|
||||
|
||||
pub fn dump_incremental_data(_codegen_results: &CodegenResults) {
|
||||
// FIXME(mw): This does not work at the moment because the situation has
|
||||
// become more complicated due to incremental LTO. Now a CGU
|
||||
// can have more than two caching states.
|
||||
// println!("[incremental] Re-using {} out of {} modules",
|
||||
// codegen_results.modules.iter().filter(|m| m.pre_existing).count(),
|
||||
// codegen_results.modules.len());
|
||||
}
|
||||
|
||||
pub enum WorkItem<B: WriteBackendMethods> {
|
||||
/// Optimize a newly codegened, totally unoptimized module.
|
||||
Optimize(ModuleCodegen<B::Module>),
|
||||
|
@ -1720,7 +1708,6 @@ impl SharedEmitterMain {
|
|||
pub struct OngoingCodegen<B: ExtraBackendMethods> {
|
||||
pub backend: B,
|
||||
pub crate_name: Symbol,
|
||||
pub crate_hash: Svh,
|
||||
pub metadata: EncodedMetadata,
|
||||
pub windows_subsystem: Option<String>,
|
||||
pub linker_info: LinkerInfo,
|
||||
|
@ -1766,7 +1753,6 @@ impl<B: ExtraBackendMethods> OngoingCodegen<B> {
|
|||
(
|
||||
CodegenResults {
|
||||
crate_name: self.crate_name,
|
||||
crate_hash: self.crate_hash,
|
||||
metadata: self.metadata,
|
||||
windows_subsystem: self.windows_subsystem,
|
||||
linker_info: self.linker_info,
|
||||
|
|
|
@ -21,7 +21,6 @@ extern crate tracing;
|
|||
extern crate rustc_middle;
|
||||
|
||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
||||
use rustc_data_structures::svh::Svh;
|
||||
use rustc_data_structures::sync::Lrc;
|
||||
use rustc_hir::def_id::CrateNum;
|
||||
use rustc_hir::LangItem;
|
||||
|
@ -134,7 +133,6 @@ pub struct CodegenResults {
|
|||
pub modules: Vec<CompiledModule>,
|
||||
pub allocator_module: Option<CompiledModule>,
|
||||
pub metadata_module: Option<CompiledModule>,
|
||||
pub crate_hash: Svh,
|
||||
pub metadata: rustc_middle::middle::cstore::EncodedMetadata,
|
||||
pub windows_subsystem: Option<String>,
|
||||
pub linker_info: back::linker::LinkerInfo,
|
||||
|
@ -144,6 +142,7 @@ pub struct CodegenResults {
|
|||
pub fn provide(providers: &mut Providers) {
|
||||
crate::back::symbol_export::provide(providers);
|
||||
crate::base::provide_both(providers);
|
||||
crate::target_features::provide(providers);
|
||||
}
|
||||
|
||||
pub fn provide_extern(providers: &mut Providers) {
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
use rustc_hir::def_id::LOCAL_CRATE;
|
||||
use rustc_middle::ty::query::Providers;
|
||||
use rustc_session::Session;
|
||||
use rustc_span::symbol::sym;
|
||||
use rustc_span::symbol::Symbol;
|
||||
|
@ -148,3 +150,16 @@ pub fn supported_target_features(sess: &Session) -> &'static [(&'static str, Opt
|
|||
_ => &[],
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn provide(providers: &mut Providers) {
|
||||
providers.supported_target_features = |tcx, cnum| {
|
||||
assert_eq!(cnum, LOCAL_CRATE);
|
||||
if tcx.sess.opts.actually_rustdoc {
|
||||
// rustdoc needs to be able to document functions that use all the features, so
|
||||
// whitelist them all
|
||||
all_known_features().map(|(a, b)| (a.to_string(), b)).collect()
|
||||
} else {
|
||||
supported_target_features(tcx.sess).iter().map(|&(a, b)| (a.to_string(), b)).collect()
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
use super::write::WriteBackendMethods;
|
||||
use super::CodegenObject;
|
||||
use crate::ModuleCodegen;
|
||||
use crate::{CodegenResults, ModuleCodegen};
|
||||
|
||||
use rustc_ast::expand::allocator::AllocatorKind;
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_errors::ErrorReported;
|
||||
use rustc_middle::dep_graph::DepGraph;
|
||||
use rustc_middle::dep_graph::{WorkProduct, WorkProductId};
|
||||
use rustc_middle::middle::cstore::{EncodedMetadata, MetadataLoaderDyn};
|
||||
use rustc_middle::ty::layout::{HasTyCtxt, TyAndLayout};
|
||||
use rustc_middle::ty::query::Providers;
|
||||
|
@ -80,8 +81,7 @@ pub trait CodegenBackend {
|
|||
&self,
|
||||
ongoing_codegen: Box<dyn Any>,
|
||||
sess: &Session,
|
||||
dep_graph: &DepGraph,
|
||||
) -> Result<Box<dyn Any>, ErrorReported>;
|
||||
) -> Result<(CodegenResults, FxHashMap<WorkProductId, WorkProduct>), ErrorReported>;
|
||||
|
||||
/// This is called on the returned `Box<dyn Any>` from `join_codegen`
|
||||
///
|
||||
|
@ -91,7 +91,7 @@ pub trait CodegenBackend {
|
|||
fn link(
|
||||
&self,
|
||||
sess: &Session,
|
||||
codegen_results: Box<dyn Any>,
|
||||
codegen_results: CodegenResults,
|
||||
outputs: &OutputFilenames,
|
||||
) -> Result<(), ErrorReported>;
|
||||
}
|
||||
|
|
|
@ -642,7 +642,7 @@ impl RustcDefaultCalls {
|
|||
let codegen_results: CodegenResults = json::decode(&rlink_data).unwrap_or_else(|err| {
|
||||
sess.fatal(&format!("failed to decode rlink: {}", err));
|
||||
});
|
||||
compiler.codegen_backend().link(&sess, Box::new(codegen_results), &outputs)
|
||||
compiler.codegen_backend().link(&sess, codegen_results, &outputs)
|
||||
} else {
|
||||
sess.fatal("rlink must be a file")
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ use crate::passes::{self, BoxedResolver, QueryContext};
|
|||
|
||||
use rustc_ast as ast;
|
||||
use rustc_codegen_ssa::traits::CodegenBackend;
|
||||
use rustc_data_structures::svh::Svh;
|
||||
use rustc_data_structures::sync::{Lrc, OnceCell, WorkerLocal};
|
||||
use rustc_errors::ErrorReported;
|
||||
use rustc_hir::def_id::LOCAL_CRATE;
|
||||
|
@ -13,7 +14,8 @@ use rustc_middle::arena::Arena;
|
|||
use rustc_middle::dep_graph::DepGraph;
|
||||
use rustc_middle::ty::steal::Steal;
|
||||
use rustc_middle::ty::{GlobalCtxt, ResolverOutputs, TyCtxt};
|
||||
use rustc_session::config::{OutputFilenames, OutputType};
|
||||
use rustc_serialize::json;
|
||||
use rustc_session::config::{self, OutputFilenames, OutputType};
|
||||
use rustc_session::{output::find_crate_name, Session};
|
||||
use rustc_span::symbol::sym;
|
||||
use std::any::Any;
|
||||
|
@ -331,6 +333,7 @@ impl<'tcx> Queries<'tcx> {
|
|||
pub fn linker(&'tcx self) -> Result<Linker> {
|
||||
let dep_graph = self.dep_graph()?;
|
||||
let prepare_outputs = self.prepare_outputs()?;
|
||||
let crate_hash = self.global_ctxt()?.peek_mut().enter(|tcx| tcx.crate_hash(LOCAL_CRATE));
|
||||
let ongoing_codegen = self.ongoing_codegen()?;
|
||||
|
||||
let sess = self.session().clone();
|
||||
|
@ -340,6 +343,7 @@ impl<'tcx> Queries<'tcx> {
|
|||
sess,
|
||||
dep_graph: dep_graph.peek().clone(),
|
||||
prepare_outputs: prepare_outputs.take(),
|
||||
crate_hash,
|
||||
ongoing_codegen: ongoing_codegen.take(),
|
||||
codegen_backend,
|
||||
})
|
||||
|
@ -350,18 +354,31 @@ pub struct Linker {
|
|||
sess: Lrc<Session>,
|
||||
dep_graph: DepGraph,
|
||||
prepare_outputs: OutputFilenames,
|
||||
crate_hash: Svh,
|
||||
ongoing_codegen: Box<dyn Any>,
|
||||
codegen_backend: Lrc<Box<dyn CodegenBackend>>,
|
||||
}
|
||||
|
||||
impl Linker {
|
||||
pub fn link(self) -> Result<()> {
|
||||
let codegen_results =
|
||||
self.codegen_backend.join_codegen(self.ongoing_codegen, &self.sess, &self.dep_graph)?;
|
||||
let prof = self.sess.prof.clone();
|
||||
let (codegen_results, work_products) =
|
||||
self.codegen_backend.join_codegen(self.ongoing_codegen, &self.sess)?;
|
||||
|
||||
self.sess.compile_status()?;
|
||||
|
||||
let sess = &self.sess;
|
||||
let dep_graph = self.dep_graph;
|
||||
sess.time("serialize_work_products", || {
|
||||
rustc_incremental::save_work_product_index(&sess, &dep_graph, work_products)
|
||||
});
|
||||
|
||||
let prof = self.sess.prof.clone();
|
||||
prof.generic_activity("drop_dep_graph").run(move || drop(dep_graph));
|
||||
|
||||
// Now that we won't touch anything in the incremental compilation directory
|
||||
// any more, we can finalize it (which involves renaming it)
|
||||
rustc_incremental::finalize_session_directory(&self.sess, self.crate_hash);
|
||||
|
||||
if !self
|
||||
.sess
|
||||
.opts
|
||||
|
@ -371,6 +388,19 @@ impl Linker {
|
|||
{
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if sess.opts.debugging_opts.no_link {
|
||||
// FIXME: use a binary format to encode the `.rlink` file
|
||||
let rlink_data = json::encode(&codegen_results).map_err(|err| {
|
||||
sess.fatal(&format!("failed to encode rlink: {}", err));
|
||||
})?;
|
||||
let rlink_file = self.prepare_outputs.with_extension(config::RLINK_EXT);
|
||||
std::fs::write(&rlink_file, rlink_data).map_err(|err| {
|
||||
sess.fatal(&format!("failed to write file {}: {}", rlink_file.display(), err));
|
||||
})?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
self.codegen_backend.link(&self.sess, codegen_results, &self.prepare_outputs)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ pub fn opts() -> TargetOptions {
|
|||
.unwrap()
|
||||
.push("-Wl,--allow-multiple-definition".to_string());
|
||||
base.is_like_android = true;
|
||||
base.dwarf_version = Some(2);
|
||||
base.position_independent_executables = true;
|
||||
base.has_elf_tls = false;
|
||||
base.requires_uwtable = true;
|
||||
|
|
|
@ -23,6 +23,7 @@ pub fn opts() -> TargetOptions {
|
|||
executables: true,
|
||||
target_family: Some("unix".to_string()),
|
||||
is_like_osx: true,
|
||||
dwarf_version: Some(2),
|
||||
has_rpath: true,
|
||||
dll_prefix: "lib".to_string(),
|
||||
dll_suffix: ".dylib".to_string(),
|
||||
|
|
|
@ -24,6 +24,7 @@ pub fn opts() -> TargetOptions {
|
|||
pre_link_args: args,
|
||||
position_independent_executables: true,
|
||||
relro_level: RelroLevel::Full,
|
||||
dwarf_version: Some(2),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@ pub fn opts() -> TargetOptions {
|
|||
eliminate_frame_pointer: false, // FIXME 43575
|
||||
relro_level: RelroLevel::Full,
|
||||
abi_return_struct_as_int: true,
|
||||
dwarf_version: Some(2),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -816,6 +816,9 @@ pub struct TargetOptions {
|
|||
pub is_like_emscripten: bool,
|
||||
/// Whether the target toolchain is like Fuchsia's.
|
||||
pub is_like_fuchsia: 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>,
|
||||
/// Whether the linker support GNU-like arguments such as -O. Defaults to false.
|
||||
pub linker_is_gnu: bool,
|
||||
/// The MinGW toolchain has a known issue that prevents it from correctly
|
||||
|
@ -1012,6 +1015,7 @@ impl Default for TargetOptions {
|
|||
is_like_emscripten: false,
|
||||
is_like_msvc: false,
|
||||
is_like_fuchsia: false,
|
||||
dwarf_version: None,
|
||||
linker_is_gnu: false,
|
||||
allows_weak_linkage: true,
|
||||
has_rpath: false,
|
||||
|
@ -1165,6 +1169,15 @@ impl Target {
|
|||
base.options.$key_name = s;
|
||||
}
|
||||
} );
|
||||
($key_name:ident, Option<u32>) => ( {
|
||||
let name = (stringify!($key_name)).replace("_", "-");
|
||||
if let Some(s) = obj.find(&name).and_then(Json::as_u64) {
|
||||
if s < 1 || s > 5 {
|
||||
return Err("Not a valid DWARF version number".to_string());
|
||||
}
|
||||
base.options.$key_name = Some(s as u32);
|
||||
}
|
||||
} );
|
||||
($key_name:ident, Option<u64>) => ( {
|
||||
let name = (stringify!($key_name)).replace("_", "-");
|
||||
if let Some(s) = obj.find(&name).and_then(Json::as_u64) {
|
||||
|
@ -1417,6 +1430,7 @@ impl Target {
|
|||
key!(is_like_emscripten, bool);
|
||||
key!(is_like_android, bool);
|
||||
key!(is_like_fuchsia, bool);
|
||||
key!(dwarf_version, Option<u32>);
|
||||
key!(linker_is_gnu, bool);
|
||||
key!(allows_weak_linkage, bool);
|
||||
key!(has_rpath, bool);
|
||||
|
@ -1654,6 +1668,7 @@ impl ToJson for Target {
|
|||
target_option_val!(is_like_emscripten);
|
||||
target_option_val!(is_like_android);
|
||||
target_option_val!(is_like_fuchsia);
|
||||
target_option_val!(dwarf_version);
|
||||
target_option_val!(linker_is_gnu);
|
||||
target_option_val!(allows_weak_linkage);
|
||||
target_option_val!(has_rpath);
|
||||
|
|
|
@ -24,6 +24,7 @@ pub fn opts() -> TargetOptions {
|
|||
position_independent_executables: true,
|
||||
relro_level: RelroLevel::Full,
|
||||
use_ctors_section: true,
|
||||
dwarf_version: Some(2),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@ pub fn opts() -> TargetOptions {
|
|||
position_independent_executables: true,
|
||||
eliminate_frame_pointer: false, // FIXME 43575
|
||||
relro_level: RelroLevel::Full,
|
||||
dwarf_version: Some(2),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,979 @@
|
|||
use crate::astconv::AstConv;
|
||||
use crate::check::coercion::CoerceMany;
|
||||
use crate::check::method::MethodCallee;
|
||||
use crate::check::Expectation::*;
|
||||
use crate::check::TupleArgumentsFlag::*;
|
||||
use crate::check::{
|
||||
potentially_plural_count, struct_span_err, BreakableCtxt, Diverges, Expectation, FnCtxt,
|
||||
LocalTy, Needs, TupleArgumentsFlag,
|
||||
};
|
||||
|
||||
use rustc_ast as ast;
|
||||
use rustc_errors::{Applicability, DiagnosticBuilder, DiagnosticId};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def::{DefKind, Res};
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_hir::{ExprKind, Node, QPath};
|
||||
use rustc_middle::ty::adjustment::AllowTwoPhase;
|
||||
use rustc_middle::ty::fold::TypeFoldable;
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
use rustc_session::Session;
|
||||
use rustc_span::symbol::{sym, Ident};
|
||||
use rustc_span::{self, Span};
|
||||
use rustc_trait_selection::traits::{self, ObligationCauseCode};
|
||||
|
||||
use std::mem::replace;
|
||||
use std::slice;
|
||||
|
||||
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
pub(in super::super) fn check_casts(&self) {
|
||||
let mut deferred_cast_checks = self.deferred_cast_checks.borrow_mut();
|
||||
for cast in deferred_cast_checks.drain(..) {
|
||||
cast.check(self);
|
||||
}
|
||||
}
|
||||
|
||||
pub(in super::super) fn check_method_argument_types(
|
||||
&self,
|
||||
sp: Span,
|
||||
expr: &'tcx hir::Expr<'tcx>,
|
||||
method: Result<MethodCallee<'tcx>, ()>,
|
||||
args_no_rcvr: &'tcx [hir::Expr<'tcx>],
|
||||
tuple_arguments: TupleArgumentsFlag,
|
||||
expected: Expectation<'tcx>,
|
||||
) -> Ty<'tcx> {
|
||||
let has_error = match method {
|
||||
Ok(method) => method.substs.references_error() || method.sig.references_error(),
|
||||
Err(_) => true,
|
||||
};
|
||||
if has_error {
|
||||
let err_inputs = self.err_args(args_no_rcvr.len());
|
||||
|
||||
let err_inputs = match tuple_arguments {
|
||||
DontTupleArguments => err_inputs,
|
||||
TupleArguments => vec![self.tcx.intern_tup(&err_inputs[..])],
|
||||
};
|
||||
|
||||
self.check_argument_types(
|
||||
sp,
|
||||
expr,
|
||||
&err_inputs[..],
|
||||
&[],
|
||||
args_no_rcvr,
|
||||
false,
|
||||
tuple_arguments,
|
||||
None,
|
||||
);
|
||||
return self.tcx.ty_error();
|
||||
}
|
||||
|
||||
let method = method.unwrap();
|
||||
// HACK(eddyb) ignore self in the definition (see above).
|
||||
let expected_arg_tys = self.expected_inputs_for_expected_output(
|
||||
sp,
|
||||
expected,
|
||||
method.sig.output(),
|
||||
&method.sig.inputs()[1..],
|
||||
);
|
||||
self.check_argument_types(
|
||||
sp,
|
||||
expr,
|
||||
&method.sig.inputs()[1..],
|
||||
&expected_arg_tys[..],
|
||||
args_no_rcvr,
|
||||
method.sig.c_variadic,
|
||||
tuple_arguments,
|
||||
self.tcx.hir().span_if_local(method.def_id),
|
||||
);
|
||||
method.sig.output()
|
||||
}
|
||||
|
||||
/// Generic function that factors out common logic from function calls,
|
||||
/// method calls and overloaded operators.
|
||||
pub(in super::super) fn check_argument_types(
|
||||
&self,
|
||||
sp: Span,
|
||||
expr: &'tcx hir::Expr<'tcx>,
|
||||
fn_inputs: &[Ty<'tcx>],
|
||||
expected_arg_tys: &[Ty<'tcx>],
|
||||
args: &'tcx [hir::Expr<'tcx>],
|
||||
c_variadic: bool,
|
||||
tuple_arguments: TupleArgumentsFlag,
|
||||
def_span: Option<Span>,
|
||||
) {
|
||||
let tcx = self.tcx;
|
||||
// Grab the argument types, supplying fresh type variables
|
||||
// if the wrong number of arguments were supplied
|
||||
let supplied_arg_count = if tuple_arguments == DontTupleArguments { args.len() } else { 1 };
|
||||
|
||||
// All the input types from the fn signature must outlive the call
|
||||
// so as to validate implied bounds.
|
||||
for (&fn_input_ty, arg_expr) in fn_inputs.iter().zip(args.iter()) {
|
||||
self.register_wf_obligation(fn_input_ty.into(), arg_expr.span, traits::MiscObligation);
|
||||
}
|
||||
|
||||
let expected_arg_count = fn_inputs.len();
|
||||
|
||||
let param_count_error = |expected_count: usize,
|
||||
arg_count: usize,
|
||||
error_code: &str,
|
||||
c_variadic: bool,
|
||||
sugg_unit: bool| {
|
||||
let (span, start_span, args) = match &expr.kind {
|
||||
hir::ExprKind::Call(hir::Expr { span, .. }, args) => (*span, *span, &args[..]),
|
||||
hir::ExprKind::MethodCall(path_segment, span, args, _) => (
|
||||
*span,
|
||||
// `sp` doesn't point at the whole `foo.bar()`, only at `bar`.
|
||||
path_segment
|
||||
.args
|
||||
.and_then(|args| args.args.iter().last())
|
||||
// Account for `foo.bar::<T>()`.
|
||||
.map(|arg| {
|
||||
// Skip the closing `>`.
|
||||
tcx.sess
|
||||
.source_map()
|
||||
.next_point(tcx.sess.source_map().next_point(arg.span()))
|
||||
})
|
||||
.unwrap_or(*span),
|
||||
&args[1..], // Skip the receiver.
|
||||
),
|
||||
k => span_bug!(sp, "checking argument types on a non-call: `{:?}`", k),
|
||||
};
|
||||
let arg_spans = if args.is_empty() {
|
||||
// foo()
|
||||
// ^^^-- supplied 0 arguments
|
||||
// |
|
||||
// expected 2 arguments
|
||||
vec![tcx.sess.source_map().next_point(start_span).with_hi(sp.hi())]
|
||||
} else {
|
||||
// foo(1, 2, 3)
|
||||
// ^^^ - - - supplied 3 arguments
|
||||
// |
|
||||
// expected 2 arguments
|
||||
args.iter().map(|arg| arg.span).collect::<Vec<Span>>()
|
||||
};
|
||||
|
||||
let mut err = tcx.sess.struct_span_err_with_code(
|
||||
span,
|
||||
&format!(
|
||||
"this function takes {}{} but {} {} supplied",
|
||||
if c_variadic { "at least " } else { "" },
|
||||
potentially_plural_count(expected_count, "argument"),
|
||||
potentially_plural_count(arg_count, "argument"),
|
||||
if arg_count == 1 { "was" } else { "were" }
|
||||
),
|
||||
DiagnosticId::Error(error_code.to_owned()),
|
||||
);
|
||||
let label = format!("supplied {}", potentially_plural_count(arg_count, "argument"));
|
||||
for (i, span) in arg_spans.into_iter().enumerate() {
|
||||
err.span_label(
|
||||
span,
|
||||
if arg_count == 0 || i + 1 == arg_count { &label } else { "" },
|
||||
);
|
||||
}
|
||||
|
||||
if let Some(def_s) = def_span.map(|sp| tcx.sess.source_map().guess_head_span(sp)) {
|
||||
err.span_label(def_s, "defined here");
|
||||
}
|
||||
if sugg_unit {
|
||||
let sugg_span = tcx.sess.source_map().end_point(expr.span);
|
||||
// remove closing `)` from the span
|
||||
let sugg_span = sugg_span.shrink_to_lo();
|
||||
err.span_suggestion(
|
||||
sugg_span,
|
||||
"expected the unit value `()`; create it with empty parentheses",
|
||||
String::from("()"),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
} else {
|
||||
err.span_label(
|
||||
span,
|
||||
format!(
|
||||
"expected {}{}",
|
||||
if c_variadic { "at least " } else { "" },
|
||||
potentially_plural_count(expected_count, "argument")
|
||||
),
|
||||
);
|
||||
}
|
||||
err.emit();
|
||||
};
|
||||
|
||||
let mut expected_arg_tys = expected_arg_tys.to_vec();
|
||||
|
||||
let formal_tys = if tuple_arguments == TupleArguments {
|
||||
let tuple_type = self.structurally_resolved_type(sp, fn_inputs[0]);
|
||||
match tuple_type.kind() {
|
||||
ty::Tuple(arg_types) if arg_types.len() != args.len() => {
|
||||
param_count_error(arg_types.len(), args.len(), "E0057", false, false);
|
||||
expected_arg_tys = vec![];
|
||||
self.err_args(args.len())
|
||||
}
|
||||
ty::Tuple(arg_types) => {
|
||||
expected_arg_tys = match expected_arg_tys.get(0) {
|
||||
Some(&ty) => match ty.kind() {
|
||||
ty::Tuple(ref tys) => tys.iter().map(|k| k.expect_ty()).collect(),
|
||||
_ => vec![],
|
||||
},
|
||||
None => vec![],
|
||||
};
|
||||
arg_types.iter().map(|k| k.expect_ty()).collect()
|
||||
}
|
||||
_ => {
|
||||
struct_span_err!(
|
||||
tcx.sess,
|
||||
sp,
|
||||
E0059,
|
||||
"cannot use call notation; the first type parameter \
|
||||
for the function trait is neither a tuple nor unit"
|
||||
)
|
||||
.emit();
|
||||
expected_arg_tys = vec![];
|
||||
self.err_args(args.len())
|
||||
}
|
||||
}
|
||||
} else if expected_arg_count == supplied_arg_count {
|
||||
fn_inputs.to_vec()
|
||||
} else if c_variadic {
|
||||
if supplied_arg_count >= expected_arg_count {
|
||||
fn_inputs.to_vec()
|
||||
} else {
|
||||
param_count_error(expected_arg_count, supplied_arg_count, "E0060", true, false);
|
||||
expected_arg_tys = vec![];
|
||||
self.err_args(supplied_arg_count)
|
||||
}
|
||||
} else {
|
||||
// is the missing argument of type `()`?
|
||||
let sugg_unit = if expected_arg_tys.len() == 1 && supplied_arg_count == 0 {
|
||||
self.resolve_vars_if_possible(&expected_arg_tys[0]).is_unit()
|
||||
} else if fn_inputs.len() == 1 && supplied_arg_count == 0 {
|
||||
self.resolve_vars_if_possible(&fn_inputs[0]).is_unit()
|
||||
} else {
|
||||
false
|
||||
};
|
||||
param_count_error(expected_arg_count, supplied_arg_count, "E0061", false, sugg_unit);
|
||||
|
||||
expected_arg_tys = vec![];
|
||||
self.err_args(supplied_arg_count)
|
||||
};
|
||||
|
||||
debug!(
|
||||
"check_argument_types: formal_tys={:?}",
|
||||
formal_tys.iter().map(|t| self.ty_to_string(*t)).collect::<Vec<String>>()
|
||||
);
|
||||
|
||||
// If there is no expectation, expect formal_tys.
|
||||
let expected_arg_tys =
|
||||
if !expected_arg_tys.is_empty() { expected_arg_tys } else { formal_tys.clone() };
|
||||
|
||||
let mut final_arg_types: Vec<(usize, Ty<'_>, Ty<'_>)> = vec![];
|
||||
|
||||
// Check the arguments.
|
||||
// We do this in a pretty awful way: first we type-check any arguments
|
||||
// that are not closures, then we type-check the closures. This is so
|
||||
// that we have more information about the types of arguments when we
|
||||
// type-check the functions. This isn't really the right way to do this.
|
||||
for &check_closures in &[false, true] {
|
||||
debug!("check_closures={}", check_closures);
|
||||
|
||||
// More awful hacks: before we check argument types, try to do
|
||||
// an "opportunistic" trait resolution of any trait bounds on
|
||||
// the call. This helps coercions.
|
||||
if check_closures {
|
||||
self.select_obligations_where_possible(false, |errors| {
|
||||
self.point_at_type_arg_instead_of_call_if_possible(errors, expr);
|
||||
self.point_at_arg_instead_of_call_if_possible(
|
||||
errors,
|
||||
&final_arg_types[..],
|
||||
sp,
|
||||
&args,
|
||||
);
|
||||
})
|
||||
}
|
||||
|
||||
// For C-variadic functions, we don't have a declared type for all of
|
||||
// the arguments hence we only do our usual type checking with
|
||||
// the arguments who's types we do know.
|
||||
let t = if c_variadic {
|
||||
expected_arg_count
|
||||
} else if tuple_arguments == TupleArguments {
|
||||
args.len()
|
||||
} else {
|
||||
supplied_arg_count
|
||||
};
|
||||
for (i, arg) in args.iter().take(t).enumerate() {
|
||||
// Warn only for the first loop (the "no closures" one).
|
||||
// Closure arguments themselves can't be diverging, but
|
||||
// a previous argument can, e.g., `foo(panic!(), || {})`.
|
||||
if !check_closures {
|
||||
self.warn_if_unreachable(arg.hir_id, arg.span, "expression");
|
||||
}
|
||||
|
||||
let is_closure = match arg.kind {
|
||||
ExprKind::Closure(..) => true,
|
||||
_ => false,
|
||||
};
|
||||
|
||||
if is_closure != check_closures {
|
||||
continue;
|
||||
}
|
||||
|
||||
debug!("checking the argument");
|
||||
let formal_ty = formal_tys[i];
|
||||
|
||||
// The special-cased logic below has three functions:
|
||||
// 1. Provide as good of an expected type as possible.
|
||||
let expected = Expectation::rvalue_hint(self, expected_arg_tys[i]);
|
||||
|
||||
let checked_ty = self.check_expr_with_expectation(&arg, expected);
|
||||
|
||||
// 2. Coerce to the most detailed type that could be coerced
|
||||
// to, which is `expected_ty` if `rvalue_hint` returns an
|
||||
// `ExpectHasType(expected_ty)`, or the `formal_ty` otherwise.
|
||||
let coerce_ty = expected.only_has_type(self).unwrap_or(formal_ty);
|
||||
// We're processing function arguments so we definitely want to use
|
||||
// two-phase borrows.
|
||||
self.demand_coerce(&arg, checked_ty, coerce_ty, None, AllowTwoPhase::Yes);
|
||||
final_arg_types.push((i, checked_ty, coerce_ty));
|
||||
|
||||
// 3. Relate the expected type and the formal one,
|
||||
// if the expected type was used for the coercion.
|
||||
self.demand_suptype(arg.span, formal_ty, coerce_ty);
|
||||
}
|
||||
}
|
||||
|
||||
// We also need to make sure we at least write the ty of the other
|
||||
// arguments which we skipped above.
|
||||
if c_variadic {
|
||||
fn variadic_error<'tcx>(s: &Session, span: Span, t: Ty<'tcx>, cast_ty: &str) {
|
||||
use crate::structured_errors::{StructuredDiagnostic, VariadicError};
|
||||
VariadicError::new(s, span, t, cast_ty).diagnostic().emit();
|
||||
}
|
||||
|
||||
for arg in args.iter().skip(expected_arg_count) {
|
||||
let arg_ty = self.check_expr(&arg);
|
||||
|
||||
// There are a few types which get autopromoted when passed via varargs
|
||||
// in C but we just error out instead and require explicit casts.
|
||||
let arg_ty = self.structurally_resolved_type(arg.span, arg_ty);
|
||||
match arg_ty.kind() {
|
||||
ty::Float(ast::FloatTy::F32) => {
|
||||
variadic_error(tcx.sess, arg.span, arg_ty, "c_double");
|
||||
}
|
||||
ty::Int(ast::IntTy::I8 | ast::IntTy::I16) | ty::Bool => {
|
||||
variadic_error(tcx.sess, arg.span, arg_ty, "c_int");
|
||||
}
|
||||
ty::Uint(ast::UintTy::U8 | ast::UintTy::U16) => {
|
||||
variadic_error(tcx.sess, arg.span, arg_ty, "c_uint");
|
||||
}
|
||||
ty::FnDef(..) => {
|
||||
let ptr_ty = self.tcx.mk_fn_ptr(arg_ty.fn_sig(self.tcx));
|
||||
let ptr_ty = self.resolve_vars_if_possible(&ptr_ty);
|
||||
variadic_error(tcx.sess, arg.span, arg_ty, &ptr_ty.to_string());
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// AST fragment checking
|
||||
pub(in super::super) fn check_lit(
|
||||
&self,
|
||||
lit: &hir::Lit,
|
||||
expected: Expectation<'tcx>,
|
||||
) -> Ty<'tcx> {
|
||||
let tcx = self.tcx;
|
||||
|
||||
match lit.node {
|
||||
ast::LitKind::Str(..) => tcx.mk_static_str(),
|
||||
ast::LitKind::ByteStr(ref v) => {
|
||||
tcx.mk_imm_ref(tcx.lifetimes.re_static, tcx.mk_array(tcx.types.u8, v.len() as u64))
|
||||
}
|
||||
ast::LitKind::Byte(_) => tcx.types.u8,
|
||||
ast::LitKind::Char(_) => tcx.types.char,
|
||||
ast::LitKind::Int(_, ast::LitIntType::Signed(t)) => tcx.mk_mach_int(t),
|
||||
ast::LitKind::Int(_, ast::LitIntType::Unsigned(t)) => tcx.mk_mach_uint(t),
|
||||
ast::LitKind::Int(_, ast::LitIntType::Unsuffixed) => {
|
||||
let opt_ty = expected.to_option(self).and_then(|ty| match ty.kind() {
|
||||
ty::Int(_) | ty::Uint(_) => Some(ty),
|
||||
ty::Char => Some(tcx.types.u8),
|
||||
ty::RawPtr(..) => Some(tcx.types.usize),
|
||||
ty::FnDef(..) | ty::FnPtr(_) => Some(tcx.types.usize),
|
||||
_ => None,
|
||||
});
|
||||
opt_ty.unwrap_or_else(|| self.next_int_var())
|
||||
}
|
||||
ast::LitKind::Float(_, ast::LitFloatType::Suffixed(t)) => tcx.mk_mach_float(t),
|
||||
ast::LitKind::Float(_, ast::LitFloatType::Unsuffixed) => {
|
||||
let opt_ty = expected.to_option(self).and_then(|ty| match ty.kind() {
|
||||
ty::Float(_) => Some(ty),
|
||||
_ => None,
|
||||
});
|
||||
opt_ty.unwrap_or_else(|| self.next_float_var())
|
||||
}
|
||||
ast::LitKind::Bool(_) => tcx.types.bool,
|
||||
ast::LitKind::Err(_) => tcx.ty_error(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn check_struct_path(
|
||||
&self,
|
||||
qpath: &QPath<'_>,
|
||||
hir_id: hir::HirId,
|
||||
) -> Option<(&'tcx ty::VariantDef, Ty<'tcx>)> {
|
||||
let path_span = qpath.qself_span();
|
||||
let (def, ty) = self.finish_resolving_struct_path(qpath, path_span, hir_id);
|
||||
let variant = match def {
|
||||
Res::Err => {
|
||||
self.set_tainted_by_errors();
|
||||
return None;
|
||||
}
|
||||
Res::Def(DefKind::Variant, _) => match ty.kind() {
|
||||
ty::Adt(adt, substs) => Some((adt.variant_of_res(def), adt.did, substs)),
|
||||
_ => bug!("unexpected type: {:?}", ty),
|
||||
},
|
||||
Res::Def(DefKind::Struct | DefKind::Union | DefKind::TyAlias | DefKind::AssocTy, _)
|
||||
| Res::SelfTy(..) => match ty.kind() {
|
||||
ty::Adt(adt, substs) if !adt.is_enum() => {
|
||||
Some((adt.non_enum_variant(), adt.did, substs))
|
||||
}
|
||||
_ => None,
|
||||
},
|
||||
_ => bug!("unexpected definition: {:?}", def),
|
||||
};
|
||||
|
||||
if let Some((variant, did, substs)) = variant {
|
||||
debug!("check_struct_path: did={:?} substs={:?}", did, substs);
|
||||
self.write_user_type_annotation_from_substs(hir_id, did, substs, None);
|
||||
|
||||
// Check bounds on type arguments used in the path.
|
||||
let (bounds, _) = self.instantiate_bounds(path_span, did, substs);
|
||||
let cause =
|
||||
traits::ObligationCause::new(path_span, self.body_id, traits::ItemObligation(did));
|
||||
self.add_obligations_for_parameters(cause, bounds);
|
||||
|
||||
Some((variant, ty))
|
||||
} else {
|
||||
struct_span_err!(
|
||||
self.tcx.sess,
|
||||
path_span,
|
||||
E0071,
|
||||
"expected struct, variant or union type, found {}",
|
||||
ty.sort_string(self.tcx)
|
||||
)
|
||||
.span_label(path_span, "not a struct")
|
||||
.emit();
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn check_decl_initializer(
|
||||
&self,
|
||||
local: &'tcx hir::Local<'tcx>,
|
||||
init: &'tcx hir::Expr<'tcx>,
|
||||
) -> Ty<'tcx> {
|
||||
// FIXME(tschottdorf): `contains_explicit_ref_binding()` must be removed
|
||||
// for #42640 (default match binding modes).
|
||||
//
|
||||
// See #44848.
|
||||
let ref_bindings = local.pat.contains_explicit_ref_binding();
|
||||
|
||||
let local_ty = self.local_ty(init.span, local.hir_id).revealed_ty;
|
||||
if let Some(m) = ref_bindings {
|
||||
// Somewhat subtle: if we have a `ref` binding in the pattern,
|
||||
// we want to avoid introducing coercions for the RHS. This is
|
||||
// both because it helps preserve sanity and, in the case of
|
||||
// ref mut, for soundness (issue #23116). In particular, in
|
||||
// the latter case, we need to be clear that the type of the
|
||||
// referent for the reference that results is *equal to* the
|
||||
// type of the place it is referencing, and not some
|
||||
// supertype thereof.
|
||||
let init_ty = self.check_expr_with_needs(init, Needs::maybe_mut_place(m));
|
||||
self.demand_eqtype(init.span, local_ty, init_ty);
|
||||
init_ty
|
||||
} else {
|
||||
self.check_expr_coercable_to_type(init, local_ty, None)
|
||||
}
|
||||
}
|
||||
|
||||
/// Type check a `let` statement.
|
||||
pub fn check_decl_local(&self, local: &'tcx hir::Local<'tcx>) {
|
||||
// Determine and write the type which we'll check the pattern against.
|
||||
let ty = self.local_ty(local.span, local.hir_id).decl_ty;
|
||||
self.write_ty(local.hir_id, ty);
|
||||
|
||||
// Type check the initializer.
|
||||
if let Some(ref init) = local.init {
|
||||
let init_ty = self.check_decl_initializer(local, &init);
|
||||
self.overwrite_local_ty_if_err(local, ty, init_ty);
|
||||
}
|
||||
|
||||
// Does the expected pattern type originate from an expression and what is the span?
|
||||
let (origin_expr, ty_span) = match (local.ty, local.init) {
|
||||
(Some(ty), _) => (false, Some(ty.span)), // Bias towards the explicit user type.
|
||||
(_, Some(init)) => (true, Some(init.span)), // No explicit type; so use the scrutinee.
|
||||
_ => (false, None), // We have `let $pat;`, so the expected type is unconstrained.
|
||||
};
|
||||
|
||||
// Type check the pattern. Override if necessary to avoid knock-on errors.
|
||||
self.check_pat_top(&local.pat, ty, ty_span, origin_expr);
|
||||
let pat_ty = self.node_ty(local.pat.hir_id);
|
||||
self.overwrite_local_ty_if_err(local, ty, pat_ty);
|
||||
}
|
||||
|
||||
pub fn check_stmt(&self, stmt: &'tcx hir::Stmt<'tcx>) {
|
||||
// Don't do all the complex logic below for `DeclItem`.
|
||||
match stmt.kind {
|
||||
hir::StmtKind::Item(..) => return,
|
||||
hir::StmtKind::Local(..) | hir::StmtKind::Expr(..) | hir::StmtKind::Semi(..) => {}
|
||||
}
|
||||
|
||||
self.warn_if_unreachable(stmt.hir_id, stmt.span, "statement");
|
||||
|
||||
// Hide the outer diverging and `has_errors` flags.
|
||||
let old_diverges = self.diverges.replace(Diverges::Maybe);
|
||||
let old_has_errors = self.has_errors.replace(false);
|
||||
|
||||
match stmt.kind {
|
||||
hir::StmtKind::Local(ref l) => {
|
||||
self.check_decl_local(&l);
|
||||
}
|
||||
// Ignore for now.
|
||||
hir::StmtKind::Item(_) => {}
|
||||
hir::StmtKind::Expr(ref expr) => {
|
||||
// Check with expected type of `()`.
|
||||
self.check_expr_has_type_or_error(&expr, self.tcx.mk_unit(), |err| {
|
||||
self.suggest_semicolon_at_end(expr.span, err);
|
||||
});
|
||||
}
|
||||
hir::StmtKind::Semi(ref expr) => {
|
||||
self.check_expr(&expr);
|
||||
}
|
||||
}
|
||||
|
||||
// Combine the diverging and `has_error` flags.
|
||||
self.diverges.set(self.diverges.get() | old_diverges);
|
||||
self.has_errors.set(self.has_errors.get() | old_has_errors);
|
||||
}
|
||||
|
||||
pub fn check_block_no_value(&self, blk: &'tcx hir::Block<'tcx>) {
|
||||
let unit = self.tcx.mk_unit();
|
||||
let ty = self.check_block_with_expected(blk, ExpectHasType(unit));
|
||||
|
||||
// if the block produces a `!` value, that can always be
|
||||
// (effectively) coerced to unit.
|
||||
if !ty.is_never() {
|
||||
self.demand_suptype(blk.span, unit, ty);
|
||||
}
|
||||
}
|
||||
|
||||
pub(in super::super) fn check_block_with_expected(
|
||||
&self,
|
||||
blk: &'tcx hir::Block<'tcx>,
|
||||
expected: Expectation<'tcx>,
|
||||
) -> Ty<'tcx> {
|
||||
let prev = {
|
||||
let mut fcx_ps = self.ps.borrow_mut();
|
||||
let unsafety_state = fcx_ps.recurse(blk);
|
||||
replace(&mut *fcx_ps, unsafety_state)
|
||||
};
|
||||
|
||||
// In some cases, blocks have just one exit, but other blocks
|
||||
// can be targeted by multiple breaks. This can happen both
|
||||
// with labeled blocks as well as when we desugar
|
||||
// a `try { ... }` expression.
|
||||
//
|
||||
// Example 1:
|
||||
//
|
||||
// 'a: { if true { break 'a Err(()); } Ok(()) }
|
||||
//
|
||||
// Here we would wind up with two coercions, one from
|
||||
// `Err(())` and the other from the tail expression
|
||||
// `Ok(())`. If the tail expression is omitted, that's a
|
||||
// "forced unit" -- unless the block diverges, in which
|
||||
// case we can ignore the tail expression (e.g., `'a: {
|
||||
// break 'a 22; }` would not force the type of the block
|
||||
// to be `()`).
|
||||
let tail_expr = blk.expr.as_ref();
|
||||
let coerce_to_ty = expected.coercion_target_type(self, blk.span);
|
||||
let coerce = if blk.targeted_by_break {
|
||||
CoerceMany::new(coerce_to_ty)
|
||||
} else {
|
||||
let tail_expr: &[&hir::Expr<'_>] = match tail_expr {
|
||||
Some(e) => slice::from_ref(e),
|
||||
None => &[],
|
||||
};
|
||||
CoerceMany::with_coercion_sites(coerce_to_ty, tail_expr)
|
||||
};
|
||||
|
||||
let prev_diverges = self.diverges.get();
|
||||
let ctxt = BreakableCtxt { coerce: Some(coerce), may_break: false };
|
||||
|
||||
let (ctxt, ()) = self.with_breakable_ctxt(blk.hir_id, ctxt, || {
|
||||
for s in blk.stmts {
|
||||
self.check_stmt(s);
|
||||
}
|
||||
|
||||
// check the tail expression **without** holding the
|
||||
// `enclosing_breakables` lock below.
|
||||
let tail_expr_ty = tail_expr.map(|t| self.check_expr_with_expectation(t, expected));
|
||||
|
||||
let mut enclosing_breakables = self.enclosing_breakables.borrow_mut();
|
||||
let ctxt = enclosing_breakables.find_breakable(blk.hir_id);
|
||||
let coerce = ctxt.coerce.as_mut().unwrap();
|
||||
if let Some(tail_expr_ty) = tail_expr_ty {
|
||||
let tail_expr = tail_expr.unwrap();
|
||||
let span = self.get_expr_coercion_span(tail_expr);
|
||||
let cause = self.cause(span, ObligationCauseCode::BlockTailExpression(blk.hir_id));
|
||||
coerce.coerce(self, &cause, tail_expr, tail_expr_ty);
|
||||
} else {
|
||||
// Subtle: if there is no explicit tail expression,
|
||||
// that is typically equivalent to a tail expression
|
||||
// of `()` -- except if the block diverges. In that
|
||||
// case, there is no value supplied from the tail
|
||||
// expression (assuming there are no other breaks,
|
||||
// this implies that the type of the block will be
|
||||
// `!`).
|
||||
//
|
||||
// #41425 -- label the implicit `()` as being the
|
||||
// "found type" here, rather than the "expected type".
|
||||
if !self.diverges.get().is_always() {
|
||||
// #50009 -- Do not point at the entire fn block span, point at the return type
|
||||
// span, as it is the cause of the requirement, and
|
||||
// `consider_hint_about_removing_semicolon` will point at the last expression
|
||||
// if it were a relevant part of the error. This improves usability in editors
|
||||
// that highlight errors inline.
|
||||
let mut sp = blk.span;
|
||||
let mut fn_span = None;
|
||||
if let Some((decl, ident)) = self.get_parent_fn_decl(blk.hir_id) {
|
||||
let ret_sp = decl.output.span();
|
||||
if let Some(block_sp) = self.parent_item_span(blk.hir_id) {
|
||||
// HACK: on some cases (`ui/liveness/liveness-issue-2163.rs`) the
|
||||
// output would otherwise be incorrect and even misleading. Make sure
|
||||
// the span we're aiming at correspond to a `fn` body.
|
||||
if block_sp == blk.span {
|
||||
sp = ret_sp;
|
||||
fn_span = Some(ident.span);
|
||||
}
|
||||
}
|
||||
}
|
||||
coerce.coerce_forced_unit(
|
||||
self,
|
||||
&self.misc(sp),
|
||||
&mut |err| {
|
||||
if let Some(expected_ty) = expected.only_has_type(self) {
|
||||
self.consider_hint_about_removing_semicolon(blk, expected_ty, err);
|
||||
}
|
||||
if let Some(fn_span) = fn_span {
|
||||
err.span_label(
|
||||
fn_span,
|
||||
"implicitly returns `()` as its body has no tail or `return` \
|
||||
expression",
|
||||
);
|
||||
}
|
||||
},
|
||||
false,
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if ctxt.may_break {
|
||||
// If we can break from the block, then the block's exit is always reachable
|
||||
// (... as long as the entry is reachable) - regardless of the tail of the block.
|
||||
self.diverges.set(prev_diverges);
|
||||
}
|
||||
|
||||
let mut ty = ctxt.coerce.unwrap().complete(self);
|
||||
|
||||
if self.has_errors.get() || ty.references_error() {
|
||||
ty = self.tcx.ty_error()
|
||||
}
|
||||
|
||||
self.write_ty(blk.hir_id, ty);
|
||||
|
||||
*self.ps.borrow_mut() = prev;
|
||||
ty
|
||||
}
|
||||
|
||||
pub(in super::super) fn check_rustc_args_require_const(
|
||||
&self,
|
||||
def_id: DefId,
|
||||
hir_id: hir::HirId,
|
||||
span: Span,
|
||||
) {
|
||||
// We're only interested in functions tagged with
|
||||
// #[rustc_args_required_const], so ignore anything that's not.
|
||||
if !self.tcx.has_attr(def_id, sym::rustc_args_required_const) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If our calling expression is indeed the function itself, we're good!
|
||||
// If not, generate an error that this can only be called directly.
|
||||
if let Node::Expr(expr) = self.tcx.hir().get(self.tcx.hir().get_parent_node(hir_id)) {
|
||||
if let ExprKind::Call(ref callee, ..) = expr.kind {
|
||||
if callee.hir_id == hir_id {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.tcx.sess.span_err(
|
||||
span,
|
||||
"this function can only be invoked directly, not through a function pointer",
|
||||
);
|
||||
}
|
||||
|
||||
/// A common error is to add an extra semicolon:
|
||||
///
|
||||
/// ```
|
||||
/// fn foo() -> usize {
|
||||
/// 22;
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// This routine checks if the final statement in a block is an
|
||||
/// expression with an explicit semicolon whose type is compatible
|
||||
/// with `expected_ty`. If so, it suggests removing the semicolon.
|
||||
fn consider_hint_about_removing_semicolon(
|
||||
&self,
|
||||
blk: &'tcx hir::Block<'tcx>,
|
||||
expected_ty: Ty<'tcx>,
|
||||
err: &mut DiagnosticBuilder<'_>,
|
||||
) {
|
||||
if let Some(span_semi) = self.could_remove_semicolon(blk, expected_ty) {
|
||||
err.span_suggestion(
|
||||
span_semi,
|
||||
"consider removing this semicolon",
|
||||
String::new(),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn parent_item_span(&self, id: hir::HirId) -> Option<Span> {
|
||||
let node = self.tcx.hir().get(self.tcx.hir().get_parent_item(id));
|
||||
match node {
|
||||
Node::Item(&hir::Item { kind: hir::ItemKind::Fn(_, _, body_id), .. })
|
||||
| Node::ImplItem(&hir::ImplItem { kind: hir::ImplItemKind::Fn(_, body_id), .. }) => {
|
||||
let body = self.tcx.hir().body(body_id);
|
||||
if let ExprKind::Block(block, _) = &body.value.kind {
|
||||
return Some(block.span);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// Given a function block's `HirId`, returns its `FnDecl` if it exists, or `None` otherwise.
|
||||
fn get_parent_fn_decl(&self, blk_id: hir::HirId) -> Option<(&'tcx hir::FnDecl<'tcx>, Ident)> {
|
||||
let parent = self.tcx.hir().get(self.tcx.hir().get_parent_item(blk_id));
|
||||
self.get_node_fn_decl(parent).map(|(fn_decl, ident, _)| (fn_decl, ident))
|
||||
}
|
||||
|
||||
/// If `expr` is a `match` expression that has only one non-`!` arm, use that arm's tail
|
||||
/// expression's `Span`, otherwise return `expr.span`. This is done to give better errors
|
||||
/// when given code like the following:
|
||||
/// ```text
|
||||
/// if false { return 0i32; } else { 1u32 }
|
||||
/// // ^^^^ point at this instead of the whole `if` expression
|
||||
/// ```
|
||||
fn get_expr_coercion_span(&self, expr: &hir::Expr<'_>) -> rustc_span::Span {
|
||||
if let hir::ExprKind::Match(_, arms, _) = &expr.kind {
|
||||
let arm_spans: Vec<Span> = arms
|
||||
.iter()
|
||||
.filter_map(|arm| {
|
||||
self.in_progress_typeck_results
|
||||
.and_then(|typeck_results| {
|
||||
typeck_results.borrow().node_type_opt(arm.body.hir_id)
|
||||
})
|
||||
.and_then(|arm_ty| {
|
||||
if arm_ty.is_never() {
|
||||
None
|
||||
} else {
|
||||
Some(match &arm.body.kind {
|
||||
// Point at the tail expression when possible.
|
||||
hir::ExprKind::Block(block, _) => {
|
||||
block.expr.as_ref().map(|e| e.span).unwrap_or(block.span)
|
||||
}
|
||||
_ => arm.body.span,
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
.collect();
|
||||
if arm_spans.len() == 1 {
|
||||
return arm_spans[0];
|
||||
}
|
||||
}
|
||||
expr.span
|
||||
}
|
||||
|
||||
fn overwrite_local_ty_if_err(
|
||||
&self,
|
||||
local: &'tcx hir::Local<'tcx>,
|
||||
decl_ty: Ty<'tcx>,
|
||||
ty: Ty<'tcx>,
|
||||
) {
|
||||
if ty.references_error() {
|
||||
// Override the types everywhere with `err()` to avoid knock on errors.
|
||||
self.write_ty(local.hir_id, ty);
|
||||
self.write_ty(local.pat.hir_id, ty);
|
||||
let local_ty = LocalTy { decl_ty, revealed_ty: ty };
|
||||
self.locals.borrow_mut().insert(local.hir_id, local_ty);
|
||||
self.locals.borrow_mut().insert(local.pat.hir_id, local_ty);
|
||||
}
|
||||
}
|
||||
|
||||
// Finish resolving a path in a struct expression or pattern `S::A { .. }` if necessary.
|
||||
// The newly resolved definition is written into `type_dependent_defs`.
|
||||
fn finish_resolving_struct_path(
|
||||
&self,
|
||||
qpath: &QPath<'_>,
|
||||
path_span: Span,
|
||||
hir_id: hir::HirId,
|
||||
) -> (Res, Ty<'tcx>) {
|
||||
match *qpath {
|
||||
QPath::Resolved(ref maybe_qself, ref path) => {
|
||||
let self_ty = maybe_qself.as_ref().map(|qself| self.to_ty(qself));
|
||||
let ty = AstConv::res_to_ty(self, self_ty, path, true);
|
||||
(path.res, ty)
|
||||
}
|
||||
QPath::TypeRelative(ref qself, ref segment) => {
|
||||
let ty = self.to_ty(qself);
|
||||
|
||||
let res = if let hir::TyKind::Path(QPath::Resolved(_, ref path)) = qself.kind {
|
||||
path.res
|
||||
} else {
|
||||
Res::Err
|
||||
};
|
||||
let result =
|
||||
AstConv::associated_path_to_ty(self, hir_id, path_span, ty, res, segment, true);
|
||||
let ty = result.map(|(ty, _, _)| ty).unwrap_or_else(|_| self.tcx().ty_error());
|
||||
let result = result.map(|(_, kind, def_id)| (kind, def_id));
|
||||
|
||||
// Write back the new resolution.
|
||||
self.write_resolution(hir_id, result);
|
||||
|
||||
(result.map(|(kind, def_id)| Res::Def(kind, def_id)).unwrap_or(Res::Err), ty)
|
||||
}
|
||||
QPath::LangItem(lang_item, span) => {
|
||||
self.resolve_lang_item_path(lang_item, span, hir_id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Given a vec of evaluated `FulfillmentError`s and an `fn` call argument expressions, we walk
|
||||
/// the checked and coerced types for each argument to see if any of the `FulfillmentError`s
|
||||
/// reference a type argument. The reason to walk also the checked type is that the coerced type
|
||||
/// can be not easily comparable with predicate type (because of coercion). If the types match
|
||||
/// for either checked or coerced type, and there's only *one* argument that does, we point at
|
||||
/// the corresponding argument's expression span instead of the `fn` call path span.
|
||||
fn point_at_arg_instead_of_call_if_possible(
|
||||
&self,
|
||||
errors: &mut Vec<traits::FulfillmentError<'tcx>>,
|
||||
final_arg_types: &[(usize, Ty<'tcx>, Ty<'tcx>)],
|
||||
call_sp: Span,
|
||||
args: &'tcx [hir::Expr<'tcx>],
|
||||
) {
|
||||
// We *do not* do this for desugared call spans to keep good diagnostics when involving
|
||||
// the `?` operator.
|
||||
if call_sp.desugaring_kind().is_some() {
|
||||
return;
|
||||
}
|
||||
|
||||
for error in errors {
|
||||
// Only if the cause is somewhere inside the expression we want try to point at arg.
|
||||
// Otherwise, it means that the cause is somewhere else and we should not change
|
||||
// anything because we can break the correct span.
|
||||
if !call_sp.contains(error.obligation.cause.span) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if let ty::PredicateAtom::Trait(predicate, _) =
|
||||
error.obligation.predicate.skip_binders()
|
||||
{
|
||||
// Collect the argument position for all arguments that could have caused this
|
||||
// `FulfillmentError`.
|
||||
let mut referenced_in = final_arg_types
|
||||
.iter()
|
||||
.map(|&(i, checked_ty, _)| (i, checked_ty))
|
||||
.chain(final_arg_types.iter().map(|&(i, _, coerced_ty)| (i, coerced_ty)))
|
||||
.flat_map(|(i, ty)| {
|
||||
let ty = self.resolve_vars_if_possible(&ty);
|
||||
// We walk the argument type because the argument's type could have
|
||||
// been `Option<T>`, but the `FulfillmentError` references `T`.
|
||||
if ty.walk().any(|arg| arg == predicate.self_ty().into()) {
|
||||
Some(i)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect::<Vec<usize>>();
|
||||
|
||||
// Both checked and coerced types could have matched, thus we need to remove
|
||||
// duplicates.
|
||||
|
||||
// We sort primitive type usize here and can use unstable sort
|
||||
referenced_in.sort_unstable();
|
||||
referenced_in.dedup();
|
||||
|
||||
if let (Some(ref_in), None) = (referenced_in.pop(), referenced_in.pop()) {
|
||||
// We make sure that only *one* argument matches the obligation failure
|
||||
// and we assign the obligation's span to its expression's.
|
||||
error.obligation.cause.make_mut().span = args[ref_in].span;
|
||||
error.points_at_arg_span = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Given a vec of evaluated `FulfillmentError`s and an `fn` call expression, we walk the
|
||||
/// `PathSegment`s and resolve their type parameters to see if any of the `FulfillmentError`s
|
||||
/// were caused by them. If they were, we point at the corresponding type argument's span
|
||||
/// instead of the `fn` call path span.
|
||||
fn point_at_type_arg_instead_of_call_if_possible(
|
||||
&self,
|
||||
errors: &mut Vec<traits::FulfillmentError<'tcx>>,
|
||||
call_expr: &'tcx hir::Expr<'tcx>,
|
||||
) {
|
||||
if let hir::ExprKind::Call(path, _) = &call_expr.kind {
|
||||
if let hir::ExprKind::Path(qpath) = &path.kind {
|
||||
if let hir::QPath::Resolved(_, path) = &qpath {
|
||||
for error in errors {
|
||||
if let ty::PredicateAtom::Trait(predicate, _) =
|
||||
error.obligation.predicate.skip_binders()
|
||||
{
|
||||
// If any of the type arguments in this path segment caused the
|
||||
// `FullfillmentError`, point at its span (#61860).
|
||||
for arg in path
|
||||
.segments
|
||||
.iter()
|
||||
.filter_map(|seg| seg.args.as_ref())
|
||||
.flat_map(|a| a.args.iter())
|
||||
{
|
||||
if let hir::GenericArg::Type(hir_ty) = &arg {
|
||||
if let hir::TyKind::Path(hir::QPath::TypeRelative(..)) =
|
||||
&hir_ty.kind
|
||||
{
|
||||
// Avoid ICE with associated types. As this is best
|
||||
// effort only, it's ok to ignore the case. It
|
||||
// would trigger in `is_send::<T::AssocType>();`
|
||||
// from `typeck-default-trait-impl-assoc-type.rs`.
|
||||
} else {
|
||||
let ty = AstConv::ast_ty_to_ty(self, hir_ty);
|
||||
let ty = self.resolve_vars_if_possible(&ty);
|
||||
if ty == predicate.self_ty() {
|
||||
error.obligation.cause.make_mut().span = hir_ty.span;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,295 @@
|
|||
mod _impl;
|
||||
mod checks;
|
||||
mod suggestions;
|
||||
|
||||
pub use _impl::*;
|
||||
pub use checks::*;
|
||||
pub use suggestions::*;
|
||||
|
||||
use crate::astconv::AstConv;
|
||||
use crate::check::coercion::DynamicCoerceMany;
|
||||
use crate::check::{Diverges, EnclosingBreakables, Inherited, UnsafetyState};
|
||||
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_infer::infer;
|
||||
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
|
||||
use rustc_infer::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind};
|
||||
use rustc_middle::hir::map::blocks::FnLikeNode;
|
||||
use rustc_middle::ty::fold::TypeFoldable;
|
||||
use rustc_middle::ty::subst::GenericArgKind;
|
||||
use rustc_middle::ty::{self, Const, Ty, TyCtxt};
|
||||
use rustc_session::Session;
|
||||
use rustc_span::{self, Span};
|
||||
use rustc_trait_selection::traits::{ObligationCause, ObligationCauseCode};
|
||||
|
||||
use std::cell::{Cell, RefCell};
|
||||
use std::ops::Deref;
|
||||
|
||||
pub struct FnCtxt<'a, 'tcx> {
|
||||
pub(super) body_id: hir::HirId,
|
||||
|
||||
/// The parameter environment used for proving trait obligations
|
||||
/// in this function. This can change when we descend into
|
||||
/// closures (as they bring new things into scope), hence it is
|
||||
/// not part of `Inherited` (as of the time of this writing,
|
||||
/// closures do not yet change the environment, but they will
|
||||
/// eventually).
|
||||
pub(super) param_env: ty::ParamEnv<'tcx>,
|
||||
|
||||
/// Number of errors that had been reported when we started
|
||||
/// checking this function. On exit, if we find that *more* errors
|
||||
/// have been reported, we will skip regionck and other work that
|
||||
/// expects the types within the function to be consistent.
|
||||
// FIXME(matthewjasper) This should not exist, and it's not correct
|
||||
// if type checking is run in parallel.
|
||||
err_count_on_creation: usize,
|
||||
|
||||
/// If `Some`, this stores coercion information for returned
|
||||
/// expressions. If `None`, this is in a context where return is
|
||||
/// inappropriate, such as a const expression.
|
||||
///
|
||||
/// This is a `RefCell<DynamicCoerceMany>`, which means that we
|
||||
/// can track all the return expressions and then use them to
|
||||
/// compute a useful coercion from the set, similar to a match
|
||||
/// expression or other branching context. You can use methods
|
||||
/// like `expected_ty` to access the declared return type (if
|
||||
/// any).
|
||||
pub(super) ret_coercion: Option<RefCell<DynamicCoerceMany<'tcx>>>,
|
||||
|
||||
pub(super) ret_coercion_impl_trait: Option<Ty<'tcx>>,
|
||||
|
||||
pub(super) ret_type_span: Option<Span>,
|
||||
|
||||
/// Used exclusively to reduce cost of advanced evaluation used for
|
||||
/// more helpful diagnostics.
|
||||
pub(super) in_tail_expr: bool,
|
||||
|
||||
/// First span of a return site that we find. Used in error messages.
|
||||
pub(super) ret_coercion_span: RefCell<Option<Span>>,
|
||||
|
||||
pub(super) resume_yield_tys: Option<(Ty<'tcx>, Ty<'tcx>)>,
|
||||
|
||||
pub(super) ps: RefCell<UnsafetyState>,
|
||||
|
||||
/// Whether the last checked node generates a divergence (e.g.,
|
||||
/// `return` will set this to `Always`). In general, when entering
|
||||
/// an expression or other node in the tree, the initial value
|
||||
/// indicates whether prior parts of the containing expression may
|
||||
/// have diverged. It is then typically set to `Maybe` (and the
|
||||
/// old value remembered) for processing the subparts of the
|
||||
/// current expression. As each subpart is processed, they may set
|
||||
/// the flag to `Always`, etc. Finally, at the end, we take the
|
||||
/// result and "union" it with the original value, so that when we
|
||||
/// return the flag indicates if any subpart of the parent
|
||||
/// expression (up to and including this part) has diverged. So,
|
||||
/// if you read it after evaluating a subexpression `X`, the value
|
||||
/// you get indicates whether any subexpression that was
|
||||
/// evaluating up to and including `X` diverged.
|
||||
///
|
||||
/// We currently use this flag only for diagnostic purposes:
|
||||
///
|
||||
/// - To warn about unreachable code: if, after processing a
|
||||
/// sub-expression but before we have applied the effects of the
|
||||
/// current node, we see that the flag is set to `Always`, we
|
||||
/// can issue a warning. This corresponds to something like
|
||||
/// `foo(return)`; we warn on the `foo()` expression. (We then
|
||||
/// update the flag to `WarnedAlways` to suppress duplicate
|
||||
/// reports.) Similarly, if we traverse to a fresh statement (or
|
||||
/// tail expression) from a `Always` setting, we will issue a
|
||||
/// warning. This corresponds to something like `{return;
|
||||
/// foo();}` or `{return; 22}`, where we would warn on the
|
||||
/// `foo()` or `22`.
|
||||
///
|
||||
/// An expression represents dead code if, after checking it,
|
||||
/// the diverges flag is set to something other than `Maybe`.
|
||||
pub(super) diverges: Cell<Diverges>,
|
||||
|
||||
/// Whether any child nodes have any type errors.
|
||||
pub(super) has_errors: Cell<bool>,
|
||||
|
||||
pub(super) enclosing_breakables: RefCell<EnclosingBreakables<'tcx>>,
|
||||
|
||||
pub(super) inh: &'a Inherited<'a, 'tcx>,
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
pub fn new(
|
||||
inh: &'a Inherited<'a, 'tcx>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
body_id: hir::HirId,
|
||||
) -> FnCtxt<'a, 'tcx> {
|
||||
FnCtxt {
|
||||
body_id,
|
||||
param_env,
|
||||
err_count_on_creation: inh.tcx.sess.err_count(),
|
||||
ret_coercion: None,
|
||||
ret_coercion_impl_trait: None,
|
||||
ret_type_span: None,
|
||||
in_tail_expr: false,
|
||||
ret_coercion_span: RefCell::new(None),
|
||||
resume_yield_tys: None,
|
||||
ps: RefCell::new(UnsafetyState::function(hir::Unsafety::Normal, hir::CRATE_HIR_ID)),
|
||||
diverges: Cell::new(Diverges::Maybe),
|
||||
has_errors: Cell::new(false),
|
||||
enclosing_breakables: RefCell::new(EnclosingBreakables {
|
||||
stack: Vec::new(),
|
||||
by_id: Default::default(),
|
||||
}),
|
||||
inh,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cause(&self, span: Span, code: ObligationCauseCode<'tcx>) -> ObligationCause<'tcx> {
|
||||
ObligationCause::new(span, self.body_id, code)
|
||||
}
|
||||
|
||||
pub fn misc(&self, span: Span) -> ObligationCause<'tcx> {
|
||||
self.cause(span, ObligationCauseCode::MiscObligation)
|
||||
}
|
||||
|
||||
pub fn sess(&self) -> &Session {
|
||||
&self.tcx.sess
|
||||
}
|
||||
|
||||
pub fn errors_reported_since_creation(&self) -> bool {
|
||||
self.tcx.sess.err_count() > self.err_count_on_creation
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> Deref for FnCtxt<'a, 'tcx> {
|
||||
type Target = Inherited<'a, 'tcx>;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.inh
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> {
|
||||
fn tcx<'b>(&'b self) -> TyCtxt<'tcx> {
|
||||
self.tcx
|
||||
}
|
||||
|
||||
fn item_def_id(&self) -> Option<DefId> {
|
||||
None
|
||||
}
|
||||
|
||||
fn default_constness_for_trait_bounds(&self) -> hir::Constness {
|
||||
// FIXME: refactor this into a method
|
||||
let node = self.tcx.hir().get(self.body_id);
|
||||
if let Some(fn_like) = FnLikeNode::from_node(node) {
|
||||
fn_like.constness()
|
||||
} else {
|
||||
hir::Constness::NotConst
|
||||
}
|
||||
}
|
||||
|
||||
fn get_type_parameter_bounds(&self, _: Span, def_id: DefId) -> ty::GenericPredicates<'tcx> {
|
||||
let tcx = self.tcx;
|
||||
let hir_id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local());
|
||||
let item_id = tcx.hir().ty_param_owner(hir_id);
|
||||
let item_def_id = tcx.hir().local_def_id(item_id);
|
||||
let generics = tcx.generics_of(item_def_id);
|
||||
let index = generics.param_def_id_to_index[&def_id];
|
||||
ty::GenericPredicates {
|
||||
parent: None,
|
||||
predicates: tcx.arena.alloc_from_iter(
|
||||
self.param_env.caller_bounds().iter().filter_map(|predicate| {
|
||||
match predicate.skip_binders() {
|
||||
ty::PredicateAtom::Trait(data, _) if data.self_ty().is_param(index) => {
|
||||
// HACK(eddyb) should get the original `Span`.
|
||||
let span = tcx.def_span(def_id);
|
||||
Some((predicate, span))
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
fn re_infer(&self, def: Option<&ty::GenericParamDef>, span: Span) -> Option<ty::Region<'tcx>> {
|
||||
let v = match def {
|
||||
Some(def) => infer::EarlyBoundRegion(span, def.name),
|
||||
None => infer::MiscVariable(span),
|
||||
};
|
||||
Some(self.next_region_var(v))
|
||||
}
|
||||
|
||||
fn allow_ty_infer(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn ty_infer(&self, param: Option<&ty::GenericParamDef>, span: Span) -> Ty<'tcx> {
|
||||
if let Some(param) = param {
|
||||
if let GenericArgKind::Type(ty) = self.var_for_def(span, param).unpack() {
|
||||
return ty;
|
||||
}
|
||||
unreachable!()
|
||||
} else {
|
||||
self.next_ty_var(TypeVariableOrigin {
|
||||
kind: TypeVariableOriginKind::TypeInference,
|
||||
span,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn ct_infer(
|
||||
&self,
|
||||
ty: Ty<'tcx>,
|
||||
param: Option<&ty::GenericParamDef>,
|
||||
span: Span,
|
||||
) -> &'tcx Const<'tcx> {
|
||||
if let Some(param) = param {
|
||||
if let GenericArgKind::Const(ct) = self.var_for_def(span, param).unpack() {
|
||||
return ct;
|
||||
}
|
||||
unreachable!()
|
||||
} else {
|
||||
self.next_const_var(
|
||||
ty,
|
||||
ConstVariableOrigin { kind: ConstVariableOriginKind::ConstInference, span },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn projected_ty_from_poly_trait_ref(
|
||||
&self,
|
||||
span: Span,
|
||||
item_def_id: DefId,
|
||||
item_segment: &hir::PathSegment<'_>,
|
||||
poly_trait_ref: ty::PolyTraitRef<'tcx>,
|
||||
) -> Ty<'tcx> {
|
||||
let (trait_ref, _) = self.replace_bound_vars_with_fresh_vars(
|
||||
span,
|
||||
infer::LateBoundRegionConversionTime::AssocTypeProjection(item_def_id),
|
||||
&poly_trait_ref,
|
||||
);
|
||||
|
||||
let item_substs = <dyn AstConv<'tcx>>::create_substs_for_associated_item(
|
||||
self,
|
||||
self.tcx,
|
||||
span,
|
||||
item_def_id,
|
||||
item_segment,
|
||||
trait_ref.substs,
|
||||
);
|
||||
|
||||
self.tcx().mk_projection(item_def_id, item_substs)
|
||||
}
|
||||
|
||||
fn normalize_ty(&self, span: Span, ty: Ty<'tcx>) -> Ty<'tcx> {
|
||||
if ty.has_escaping_bound_vars() {
|
||||
ty // FIXME: normalization and escaping regions
|
||||
} else {
|
||||
self.normalize_associated_types_in(span, &ty)
|
||||
}
|
||||
}
|
||||
|
||||
fn set_tainted_by_errors(&self) {
|
||||
self.infcx.set_tainted_by_errors()
|
||||
}
|
||||
|
||||
fn record_ty(&self, hir_id: hir::HirId, ty: Ty<'tcx>, _span: Span) {
|
||||
self.write_ty(hir_id, ty)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,528 @@
|
|||
use super::FnCtxt;
|
||||
use crate::astconv::AstConv;
|
||||
|
||||
use rustc_ast::util::parser::ExprPrecedence;
|
||||
use rustc_span::{self, Span};
|
||||
use rustc_trait_selection::traits;
|
||||
|
||||
use rustc_errors::{Applicability, DiagnosticBuilder};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def::{CtorOf, DefKind};
|
||||
use rustc_hir::lang_items::LangItem;
|
||||
use rustc_hir::{ExprKind, ItemKind, Node};
|
||||
use rustc_infer::infer;
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
use rustc_span::symbol::kw;
|
||||
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
|
||||
|
||||
use std::iter;
|
||||
|
||||
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
pub(in super::super) fn suggest_semicolon_at_end(
|
||||
&self,
|
||||
span: Span,
|
||||
err: &mut DiagnosticBuilder<'_>,
|
||||
) {
|
||||
err.span_suggestion_short(
|
||||
span.shrink_to_hi(),
|
||||
"consider using a semicolon here",
|
||||
";".to_string(),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
|
||||
/// On implicit return expressions with mismatched types, provides the following suggestions:
|
||||
///
|
||||
/// - Points out the method's return type as the reason for the expected type.
|
||||
/// - Possible missing semicolon.
|
||||
/// - Possible missing return type if the return type is the default, and not `fn main()`.
|
||||
pub fn suggest_mismatched_types_on_tail(
|
||||
&self,
|
||||
err: &mut DiagnosticBuilder<'_>,
|
||||
expr: &'tcx hir::Expr<'tcx>,
|
||||
expected: Ty<'tcx>,
|
||||
found: Ty<'tcx>,
|
||||
cause_span: Span,
|
||||
blk_id: hir::HirId,
|
||||
) -> bool {
|
||||
let expr = expr.peel_drop_temps();
|
||||
self.suggest_missing_semicolon(err, expr, expected, cause_span);
|
||||
let mut pointing_at_return_type = false;
|
||||
if let Some((fn_decl, can_suggest)) = self.get_fn_decl(blk_id) {
|
||||
pointing_at_return_type =
|
||||
self.suggest_missing_return_type(err, &fn_decl, expected, found, can_suggest);
|
||||
}
|
||||
pointing_at_return_type
|
||||
}
|
||||
|
||||
/// When encountering an fn-like ctor that needs to unify with a value, check whether calling
|
||||
/// the ctor would successfully solve the type mismatch and if so, suggest it:
|
||||
/// ```
|
||||
/// fn foo(x: usize) -> usize { x }
|
||||
/// let x: usize = foo; // suggest calling the `foo` function: `foo(42)`
|
||||
/// ```
|
||||
fn suggest_fn_call(
|
||||
&self,
|
||||
err: &mut DiagnosticBuilder<'_>,
|
||||
expr: &hir::Expr<'_>,
|
||||
expected: Ty<'tcx>,
|
||||
found: Ty<'tcx>,
|
||||
) -> bool {
|
||||
let hir = self.tcx.hir();
|
||||
let (def_id, sig) = match *found.kind() {
|
||||
ty::FnDef(def_id, _) => (def_id, found.fn_sig(self.tcx)),
|
||||
ty::Closure(def_id, substs) => (def_id, substs.as_closure().sig()),
|
||||
_ => return false,
|
||||
};
|
||||
|
||||
let sig = self.replace_bound_vars_with_fresh_vars(expr.span, infer::FnCall, &sig).0;
|
||||
let sig = self.normalize_associated_types_in(expr.span, &sig);
|
||||
if self.can_coerce(sig.output(), expected) {
|
||||
let (mut sugg_call, applicability) = if sig.inputs().is_empty() {
|
||||
(String::new(), Applicability::MachineApplicable)
|
||||
} else {
|
||||
("...".to_string(), Applicability::HasPlaceholders)
|
||||
};
|
||||
let mut msg = "call this function";
|
||||
match hir.get_if_local(def_id) {
|
||||
Some(
|
||||
Node::Item(hir::Item { kind: ItemKind::Fn(.., body_id), .. })
|
||||
| Node::ImplItem(hir::ImplItem {
|
||||
kind: hir::ImplItemKind::Fn(_, body_id), ..
|
||||
})
|
||||
| Node::TraitItem(hir::TraitItem {
|
||||
kind: hir::TraitItemKind::Fn(.., hir::TraitFn::Provided(body_id)),
|
||||
..
|
||||
}),
|
||||
) => {
|
||||
let body = hir.body(*body_id);
|
||||
sugg_call = body
|
||||
.params
|
||||
.iter()
|
||||
.map(|param| match ¶m.pat.kind {
|
||||
hir::PatKind::Binding(_, _, ident, None)
|
||||
if ident.name != kw::SelfLower =>
|
||||
{
|
||||
ident.to_string()
|
||||
}
|
||||
_ => "_".to_string(),
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ");
|
||||
}
|
||||
Some(Node::Expr(hir::Expr {
|
||||
kind: ExprKind::Closure(_, _, body_id, _, _),
|
||||
span: full_closure_span,
|
||||
..
|
||||
})) => {
|
||||
if *full_closure_span == expr.span {
|
||||
return false;
|
||||
}
|
||||
msg = "call this closure";
|
||||
let body = hir.body(*body_id);
|
||||
sugg_call = body
|
||||
.params
|
||||
.iter()
|
||||
.map(|param| match ¶m.pat.kind {
|
||||
hir::PatKind::Binding(_, _, ident, None)
|
||||
if ident.name != kw::SelfLower =>
|
||||
{
|
||||
ident.to_string()
|
||||
}
|
||||
_ => "_".to_string(),
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ");
|
||||
}
|
||||
Some(Node::Ctor(hir::VariantData::Tuple(fields, _))) => {
|
||||
sugg_call = fields.iter().map(|_| "_").collect::<Vec<_>>().join(", ");
|
||||
match def_id.as_local().map(|def_id| hir.def_kind(def_id)) {
|
||||
Some(DefKind::Ctor(hir::def::CtorOf::Variant, _)) => {
|
||||
msg = "instantiate this tuple variant";
|
||||
}
|
||||
Some(DefKind::Ctor(CtorOf::Struct, _)) => {
|
||||
msg = "instantiate this tuple struct";
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
Some(Node::ForeignItem(hir::ForeignItem {
|
||||
kind: hir::ForeignItemKind::Fn(_, idents, _),
|
||||
..
|
||||
})) => {
|
||||
sugg_call = idents
|
||||
.iter()
|
||||
.map(|ident| {
|
||||
if ident.name != kw::SelfLower {
|
||||
ident.to_string()
|
||||
} else {
|
||||
"_".to_string()
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ")
|
||||
}
|
||||
Some(Node::TraitItem(hir::TraitItem {
|
||||
kind: hir::TraitItemKind::Fn(.., hir::TraitFn::Required(idents)),
|
||||
..
|
||||
})) => {
|
||||
sugg_call = idents
|
||||
.iter()
|
||||
.map(|ident| {
|
||||
if ident.name != kw::SelfLower {
|
||||
ident.to_string()
|
||||
} else {
|
||||
"_".to_string()
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ")
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
err.span_suggestion_verbose(
|
||||
expr.span.shrink_to_hi(),
|
||||
&format!("use parentheses to {}", msg),
|
||||
format!("({})", sugg_call),
|
||||
applicability,
|
||||
);
|
||||
return true;
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
pub fn suggest_deref_ref_or_into(
|
||||
&self,
|
||||
err: &mut DiagnosticBuilder<'_>,
|
||||
expr: &hir::Expr<'_>,
|
||||
expected: Ty<'tcx>,
|
||||
found: Ty<'tcx>,
|
||||
expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>,
|
||||
) {
|
||||
if let Some((sp, msg, suggestion, applicability)) = self.check_ref(expr, found, expected) {
|
||||
err.span_suggestion(sp, msg, suggestion, applicability);
|
||||
} else if let (ty::FnDef(def_id, ..), true) =
|
||||
(&found.kind(), self.suggest_fn_call(err, expr, expected, found))
|
||||
{
|
||||
if let Some(sp) = self.tcx.hir().span_if_local(*def_id) {
|
||||
let sp = self.sess().source_map().guess_head_span(sp);
|
||||
err.span_label(sp, &format!("{} defined here", found));
|
||||
}
|
||||
} else if !self.check_for_cast(err, expr, found, expected, expected_ty_expr) {
|
||||
let is_struct_pat_shorthand_field =
|
||||
self.is_hir_id_from_struct_pattern_shorthand_field(expr.hir_id, expr.span);
|
||||
let methods = self.get_conversion_methods(expr.span, expected, found, expr.hir_id);
|
||||
if let Ok(expr_text) = self.sess().source_map().span_to_snippet(expr.span) {
|
||||
let mut suggestions = iter::repeat(&expr_text)
|
||||
.zip(methods.iter())
|
||||
.filter_map(|(receiver, method)| {
|
||||
let method_call = format!(".{}()", method.ident);
|
||||
if receiver.ends_with(&method_call) {
|
||||
None // do not suggest code that is already there (#53348)
|
||||
} else {
|
||||
let method_call_list = [".to_vec()", ".to_string()"];
|
||||
let sugg = if receiver.ends_with(".clone()")
|
||||
&& method_call_list.contains(&method_call.as_str())
|
||||
{
|
||||
let max_len = receiver.rfind('.').unwrap();
|
||||
format!("{}{}", &receiver[..max_len], method_call)
|
||||
} else {
|
||||
if expr.precedence().order() < ExprPrecedence::MethodCall.order() {
|
||||
format!("({}){}", receiver, method_call)
|
||||
} else {
|
||||
format!("{}{}", receiver, method_call)
|
||||
}
|
||||
};
|
||||
Some(if is_struct_pat_shorthand_field {
|
||||
format!("{}: {}", receiver, sugg)
|
||||
} else {
|
||||
sugg
|
||||
})
|
||||
}
|
||||
})
|
||||
.peekable();
|
||||
if suggestions.peek().is_some() {
|
||||
err.span_suggestions(
|
||||
expr.span,
|
||||
"try using a conversion method",
|
||||
suggestions,
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// When encountering the expected boxed value allocated in the stack, suggest allocating it
|
||||
/// in the heap by calling `Box::new()`.
|
||||
pub(in super::super) fn suggest_boxing_when_appropriate(
|
||||
&self,
|
||||
err: &mut DiagnosticBuilder<'_>,
|
||||
expr: &hir::Expr<'_>,
|
||||
expected: Ty<'tcx>,
|
||||
found: Ty<'tcx>,
|
||||
) {
|
||||
if self.tcx.hir().is_inside_const_context(expr.hir_id) {
|
||||
// Do not suggest `Box::new` in const context.
|
||||
return;
|
||||
}
|
||||
if !expected.is_box() || found.is_box() {
|
||||
return;
|
||||
}
|
||||
let boxed_found = self.tcx.mk_box(found);
|
||||
if let (true, Ok(snippet)) = (
|
||||
self.can_coerce(boxed_found, expected),
|
||||
self.sess().source_map().span_to_snippet(expr.span),
|
||||
) {
|
||||
err.span_suggestion(
|
||||
expr.span,
|
||||
"store this in the heap by calling `Box::new`",
|
||||
format!("Box::new({})", snippet),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
err.note(
|
||||
"for more on the distinction between the stack and the heap, read \
|
||||
https://doc.rust-lang.org/book/ch15-01-box.html, \
|
||||
https://doc.rust-lang.org/rust-by-example/std/box.html, and \
|
||||
https://doc.rust-lang.org/std/boxed/index.html",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// When encountering an `impl Future` where `BoxFuture` is expected, suggest `Box::pin`.
|
||||
pub(in super::super) fn suggest_calling_boxed_future_when_appropriate(
|
||||
&self,
|
||||
err: &mut DiagnosticBuilder<'_>,
|
||||
expr: &hir::Expr<'_>,
|
||||
expected: Ty<'tcx>,
|
||||
found: Ty<'tcx>,
|
||||
) -> bool {
|
||||
// Handle #68197.
|
||||
|
||||
if self.tcx.hir().is_inside_const_context(expr.hir_id) {
|
||||
// Do not suggest `Box::new` in const context.
|
||||
return false;
|
||||
}
|
||||
let pin_did = self.tcx.lang_items().pin_type();
|
||||
match expected.kind() {
|
||||
ty::Adt(def, _) if Some(def.did) != pin_did => return false,
|
||||
// This guards the `unwrap` and `mk_box` below.
|
||||
_ if pin_did.is_none() || self.tcx.lang_items().owned_box().is_none() => return false,
|
||||
_ => {}
|
||||
}
|
||||
let boxed_found = self.tcx.mk_box(found);
|
||||
let new_found = self.tcx.mk_lang_item(boxed_found, LangItem::Pin).unwrap();
|
||||
if let (true, Ok(snippet)) = (
|
||||
self.can_coerce(new_found, expected),
|
||||
self.sess().source_map().span_to_snippet(expr.span),
|
||||
) {
|
||||
match found.kind() {
|
||||
ty::Adt(def, _) if def.is_box() => {
|
||||
err.help("use `Box::pin`");
|
||||
}
|
||||
_ => {
|
||||
err.span_suggestion(
|
||||
expr.span,
|
||||
"you need to pin and box this expression",
|
||||
format!("Box::pin({})", snippet),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// A common error is to forget to add a semicolon at the end of a block, e.g.,
|
||||
///
|
||||
/// ```
|
||||
/// fn foo() {
|
||||
/// bar_that_returns_u32()
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// This routine checks if the return expression in a block would make sense on its own as a
|
||||
/// statement and the return type has been left as default or has been specified as `()`. If so,
|
||||
/// it suggests adding a semicolon.
|
||||
fn suggest_missing_semicolon(
|
||||
&self,
|
||||
err: &mut DiagnosticBuilder<'_>,
|
||||
expression: &'tcx hir::Expr<'tcx>,
|
||||
expected: Ty<'tcx>,
|
||||
cause_span: Span,
|
||||
) {
|
||||
if expected.is_unit() {
|
||||
// `BlockTailExpression` only relevant if the tail expr would be
|
||||
// useful on its own.
|
||||
match expression.kind {
|
||||
ExprKind::Call(..)
|
||||
| ExprKind::MethodCall(..)
|
||||
| ExprKind::Loop(..)
|
||||
| ExprKind::Match(..)
|
||||
| ExprKind::Block(..) => {
|
||||
err.span_suggestion(
|
||||
cause_span.shrink_to_hi(),
|
||||
"try adding a semicolon",
|
||||
";".to_string(),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A possible error is to forget to add a return type that is needed:
|
||||
///
|
||||
/// ```
|
||||
/// fn foo() {
|
||||
/// bar_that_returns_u32()
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// This routine checks if the return type is left as default, the method is not part of an
|
||||
/// `impl` block and that it isn't the `main` method. If so, it suggests setting the return
|
||||
/// type.
|
||||
pub(in super::super) fn suggest_missing_return_type(
|
||||
&self,
|
||||
err: &mut DiagnosticBuilder<'_>,
|
||||
fn_decl: &hir::FnDecl<'_>,
|
||||
expected: Ty<'tcx>,
|
||||
found: Ty<'tcx>,
|
||||
can_suggest: bool,
|
||||
) -> bool {
|
||||
// Only suggest changing the return type for methods that
|
||||
// haven't set a return type at all (and aren't `fn main()` or an impl).
|
||||
match (&fn_decl.output, found.is_suggestable(), can_suggest, expected.is_unit()) {
|
||||
(&hir::FnRetTy::DefaultReturn(span), true, true, true) => {
|
||||
err.span_suggestion(
|
||||
span,
|
||||
"try adding a return type",
|
||||
format!("-> {} ", self.resolve_vars_with_obligations(found)),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
true
|
||||
}
|
||||
(&hir::FnRetTy::DefaultReturn(span), false, true, true) => {
|
||||
err.span_label(span, "possibly return type missing here?");
|
||||
true
|
||||
}
|
||||
(&hir::FnRetTy::DefaultReturn(span), _, false, true) => {
|
||||
// `fn main()` must return `()`, do not suggest changing return type
|
||||
err.span_label(span, "expected `()` because of default return type");
|
||||
true
|
||||
}
|
||||
// expectation was caused by something else, not the default return
|
||||
(&hir::FnRetTy::DefaultReturn(_), _, _, false) => false,
|
||||
(&hir::FnRetTy::Return(ref ty), _, _, _) => {
|
||||
// Only point to return type if the expected type is the return type, as if they
|
||||
// are not, the expectation must have been caused by something else.
|
||||
debug!("suggest_missing_return_type: return type {:?} node {:?}", ty, ty.kind);
|
||||
let sp = ty.span;
|
||||
let ty = AstConv::ast_ty_to_ty(self, ty);
|
||||
debug!("suggest_missing_return_type: return type {:?}", ty);
|
||||
debug!("suggest_missing_return_type: expected type {:?}", ty);
|
||||
if ty.kind() == expected.kind() {
|
||||
err.span_label(sp, format!("expected `{}` because of return type", expected));
|
||||
return true;
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A possible error is to forget to add `.await` when using futures:
|
||||
///
|
||||
/// ```
|
||||
/// async fn make_u32() -> u32 {
|
||||
/// 22
|
||||
/// }
|
||||
///
|
||||
/// fn take_u32(x: u32) {}
|
||||
///
|
||||
/// async fn foo() {
|
||||
/// let x = make_u32();
|
||||
/// take_u32(x);
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// This routine checks if the found type `T` implements `Future<Output=U>` where `U` is the
|
||||
/// expected type. If this is the case, and we are inside of an async body, it suggests adding
|
||||
/// `.await` to the tail of the expression.
|
||||
pub(in super::super) fn suggest_missing_await(
|
||||
&self,
|
||||
err: &mut DiagnosticBuilder<'_>,
|
||||
expr: &hir::Expr<'_>,
|
||||
expected: Ty<'tcx>,
|
||||
found: Ty<'tcx>,
|
||||
) {
|
||||
debug!("suggest_missing_await: expr={:?} expected={:?}, found={:?}", expr, expected, found);
|
||||
// `.await` is not permitted outside of `async` bodies, so don't bother to suggest if the
|
||||
// body isn't `async`.
|
||||
let item_id = self.tcx().hir().get_parent_node(self.body_id);
|
||||
if let Some(body_id) = self.tcx().hir().maybe_body_owned_by(item_id) {
|
||||
let body = self.tcx().hir().body(body_id);
|
||||
if let Some(hir::GeneratorKind::Async(_)) = body.generator_kind {
|
||||
let sp = expr.span;
|
||||
// Check for `Future` implementations by constructing a predicate to
|
||||
// prove: `<T as Future>::Output == U`
|
||||
let future_trait = self.tcx.require_lang_item(LangItem::Future, Some(sp));
|
||||
let item_def_id = self
|
||||
.tcx
|
||||
.associated_items(future_trait)
|
||||
.in_definition_order()
|
||||
.next()
|
||||
.unwrap()
|
||||
.def_id;
|
||||
// `<T as Future>::Output`
|
||||
let projection_ty = ty::ProjectionTy {
|
||||
// `T`
|
||||
substs: self
|
||||
.tcx
|
||||
.mk_substs_trait(found, self.fresh_substs_for_item(sp, item_def_id)),
|
||||
// `Future::Output`
|
||||
item_def_id,
|
||||
};
|
||||
|
||||
let predicate = ty::PredicateAtom::Projection(ty::ProjectionPredicate {
|
||||
projection_ty,
|
||||
ty: expected,
|
||||
})
|
||||
.potentially_quantified(self.tcx, ty::PredicateKind::ForAll);
|
||||
let obligation = traits::Obligation::new(self.misc(sp), self.param_env, predicate);
|
||||
|
||||
debug!("suggest_missing_await: trying obligation {:?}", obligation);
|
||||
|
||||
if self.infcx.predicate_may_hold(&obligation) {
|
||||
debug!("suggest_missing_await: obligation held: {:?}", obligation);
|
||||
if let Ok(code) = self.sess().source_map().span_to_snippet(sp) {
|
||||
err.span_suggestion(
|
||||
sp,
|
||||
"consider using `.await` here",
|
||||
format!("{}.await", code),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
} else {
|
||||
debug!("suggest_missing_await: no snippet for {:?}", sp);
|
||||
}
|
||||
} else {
|
||||
debug!("suggest_missing_await: obligation did not hold: {:?}", obligation)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(in super::super) fn suggest_missing_parentheses(
|
||||
&self,
|
||||
err: &mut DiagnosticBuilder<'_>,
|
||||
expr: &hir::Expr<'_>,
|
||||
) {
|
||||
let sp = self.tcx.sess.source_map().start_point(expr.span);
|
||||
if let Some(sp) = self.tcx.sess.parse_sess.ambiguous_block_expr_parse.borrow().get(&sp) {
|
||||
// `{ 42 } &&x` (#61475) or `{ 42 } && if x { 1 } else { 0 }`
|
||||
self.tcx.sess.parse_sess.expr_parentheses_needed(err, *sp, None);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -96,7 +96,7 @@ use check::{
|
|||
pub use check::{check_item_type, check_wf_new};
|
||||
pub use diverges::Diverges;
|
||||
pub use expectation::Expectation;
|
||||
pub use fn_ctxt::FnCtxt;
|
||||
pub use fn_ctxt::*;
|
||||
pub use inherited::{Inherited, InheritedBuilder};
|
||||
|
||||
use crate::astconv::AstConv;
|
||||
|
|
|
@ -14,9 +14,9 @@ mod tests;
|
|||
|
||||
extern "Rust" {
|
||||
// These are the magic symbols to call the global allocator. rustc generates
|
||||
// them to call `__rg_alloc` etc if there is a `#[global_allocator]` attribute
|
||||
// them to call `__rg_alloc` etc. if there is a `#[global_allocator]` attribute
|
||||
// (the code expanding that attribute macro generates those functions), or to call
|
||||
// the default implementations in libstd (`__rdl_alloc` etc in `src/libstd/alloc.rs`)
|
||||
// the default implementations in libstd (`__rdl_alloc` etc. in `library/std/src/alloc.rs`)
|
||||
// otherwise.
|
||||
#[rustc_allocator]
|
||||
#[rustc_allocator_nounwind]
|
||||
|
@ -36,7 +36,7 @@ extern "Rust" {
|
|||
/// if there is one, or the `std` crate’s default.
|
||||
///
|
||||
/// Note: while this type is unstable, the functionality it provides can be
|
||||
/// accessed through the [free functions in `alloc`](index.html#functions).
|
||||
/// accessed through the [free functions in `alloc`](self#functions).
|
||||
#[unstable(feature = "allocator_api", issue = "32838")]
|
||||
#[derive(Copy, Clone, Default, Debug)]
|
||||
pub struct Global;
|
||||
|
|
|
@ -226,7 +226,7 @@ use crate::ptr;
|
|||
/// assert_eq!(my_struct.special_field.get(), new_value);
|
||||
/// ```
|
||||
///
|
||||
/// See the [module-level documentation](index.html) for more.
|
||||
/// See the [module-level documentation](self) for more.
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[repr(transparent)]
|
||||
pub struct Cell<T: ?Sized> {
|
||||
|
@ -566,7 +566,7 @@ impl<T> Cell<[T]> {
|
|||
|
||||
/// A mutable memory location with dynamically checked borrow rules
|
||||
///
|
||||
/// See the [module-level documentation](index.html) for more.
|
||||
/// See the [module-level documentation](self) for more.
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub struct RefCell<T: ?Sized> {
|
||||
borrow: Cell<BorrowFlag>,
|
||||
|
@ -1203,7 +1203,7 @@ impl Clone for BorrowRef<'_> {
|
|||
/// Wraps a borrowed reference to a value in a `RefCell` box.
|
||||
/// A wrapper type for an immutably borrowed value from a `RefCell<T>`.
|
||||
///
|
||||
/// See the [module-level documentation](index.html) for more.
|
||||
/// See the [module-level documentation](self) for more.
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub struct Ref<'b, T: ?Sized + 'b> {
|
||||
value: &'b T,
|
||||
|
@ -1493,7 +1493,7 @@ impl<'b> BorrowRefMut<'b> {
|
|||
|
||||
/// A wrapper type for a mutably borrowed value from a `RefCell<T>`.
|
||||
///
|
||||
/// See the [module-level documentation](index.html) for more.
|
||||
/// See the [module-level documentation](self) for more.
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub struct RefMut<'b, T: ?Sized + 'b> {
|
||||
value: &'b mut T,
|
||||
|
|
|
@ -501,9 +501,9 @@ pub fn once_with<A, F: FnOnce() -> A>(gen: F) -> OnceWith<F> {
|
|||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Let’s re-implement the counter iterator from [module-level documentation]:
|
||||
/// Let’s re-implement the counter iterator from the [module-level documentation]:
|
||||
///
|
||||
/// [module-level documentation]: index.html
|
||||
/// [module-level documentation]: super
|
||||
///
|
||||
/// ```
|
||||
/// let mut count = 0;
|
||||
|
|
|
@ -94,7 +94,7 @@ pub trait FromIterator<A>: Sized {
|
|||
///
|
||||
/// See the [module-level documentation] for more.
|
||||
///
|
||||
/// [module-level documentation]: index.html
|
||||
/// [module-level documentation]: crate::iter
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
|
@ -120,7 +120,7 @@ pub trait FromIterator<A>: Sized {
|
|||
/// collection of some kind.
|
||||
///
|
||||
/// One benefit of implementing `IntoIterator` is that your type will [work
|
||||
/// with Rust's `for` loop syntax](index.html#for-loops-and-intoiterator).
|
||||
/// with Rust's `for` loop syntax](crate::iter#for-loops-and-intoiterator).
|
||||
///
|
||||
/// See also: [`FromIterator`].
|
||||
///
|
||||
|
@ -212,7 +212,7 @@ pub trait IntoIterator {
|
|||
///
|
||||
/// See the [module-level documentation] for more.
|
||||
///
|
||||
/// [module-level documentation]: index.html
|
||||
/// [module-level documentation]: crate::iter
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
|
|
|
@ -26,10 +26,10 @@
|
|||
/// assert_eq!(5, five.len());
|
||||
/// ```
|
||||
///
|
||||
/// In the [module level docs][moddocs], we implemented an [`Iterator`],
|
||||
/// `Counter`. Let's implement `ExactSizeIterator` for it as well:
|
||||
/// In the [module-level docs], we implemented an [`Iterator`], `Counter`.
|
||||
/// Let's implement `ExactSizeIterator` for it as well:
|
||||
///
|
||||
/// [moddocs]: index.html
|
||||
/// [module-level docs]: crate::iter
|
||||
///
|
||||
/// ```
|
||||
/// # struct Counter {
|
||||
|
|
|
@ -233,7 +233,7 @@ use crate::{convert, fmt};
|
|||
|
||||
/// `Result` is a type that represents either success ([`Ok`]) or failure ([`Err`]).
|
||||
///
|
||||
/// See the [`std::result`](index.html) module documentation for details.
|
||||
/// See the [module documentation](self) for details.
|
||||
#[derive(Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Hash)]
|
||||
#[must_use = "this `Result` may be an `Err` variant, which should be handled"]
|
||||
#[rustc_diagnostic_item = "result_type"]
|
||||
|
|
|
@ -69,7 +69,7 @@ use crate::sys_common::{AsInner, FromInner, IntoInner};
|
|||
/// [`&OsStr`]: OsStr
|
||||
/// [`&str`]: str
|
||||
/// [`CStr`]: crate::ffi::CStr
|
||||
/// [conversions]: index.html#conversions
|
||||
/// [conversions]: super#conversions
|
||||
#[derive(Clone)]
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub struct OsString {
|
||||
|
@ -88,7 +88,7 @@ pub struct OsString {
|
|||
/// the traits which `OsStr` implements for [conversions] from/to native representations.
|
||||
///
|
||||
/// [`&str`]: str
|
||||
/// [conversions]: index.html#conversions
|
||||
/// [conversions]: super#conversions
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
// FIXME:
|
||||
// `OsStr::from_inner` current implementation relies
|
||||
|
|
|
@ -1009,7 +1009,7 @@ impl FusedIterator for Ancestors<'_> {}
|
|||
/// [`set_extension`]: PathBuf::set_extension
|
||||
///
|
||||
/// More details about the overall approach can be found in
|
||||
/// the [module documentation](index.html).
|
||||
/// the [module documentation](self).
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
|
@ -1655,7 +1655,7 @@ impl AsRef<OsStr> for PathBuf {
|
|||
/// see [`PathBuf`].
|
||||
///
|
||||
/// More details about the overall approach can be found in
|
||||
/// the [module documentation](index.html).
|
||||
/// the [module documentation](self).
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
|
|
|
@ -234,14 +234,14 @@ pub fn std_cargo(builder: &Builder<'_>, target: TargetSelection, stage: u32, car
|
|||
// Note that `libprofiler_builtins/build.rs` also computes this so if
|
||||
// you're changing something here please also change that.
|
||||
cargo.env("RUST_COMPILER_RT_ROOT", &compiler_builtins_root);
|
||||
" compiler-builtins-c".to_string()
|
||||
" compiler-builtins-c"
|
||||
} else {
|
||||
String::new()
|
||||
""
|
||||
};
|
||||
|
||||
if builder.no_std(target) == Some(true) {
|
||||
let mut features = "compiler-builtins-mem".to_string();
|
||||
features.push_str(&compiler_builtins_c_feature);
|
||||
features.push_str(compiler_builtins_c_feature);
|
||||
|
||||
// for no-std targets we only compile a few no_std crates
|
||||
cargo
|
||||
|
@ -249,10 +249,10 @@ pub fn std_cargo(builder: &Builder<'_>, target: TargetSelection, stage: u32, car
|
|||
.arg("--manifest-path")
|
||||
.arg(builder.src.join("library/alloc/Cargo.toml"))
|
||||
.arg("--features")
|
||||
.arg("compiler-builtins-mem compiler-builtins-c");
|
||||
.arg(features);
|
||||
} else {
|
||||
let mut features = builder.std_features();
|
||||
features.push_str(&compiler_builtins_c_feature);
|
||||
features.push_str(compiler_builtins_c_feature);
|
||||
|
||||
cargo
|
||||
.arg("--features")
|
||||
|
|
|
@ -207,30 +207,46 @@ class StdRefCellProvider:
|
|||
yield "borrow", self.borrow
|
||||
|
||||
|
||||
# Yield each key (and optionally value) from a BoxedNode.
|
||||
def children_of_node(boxed_node, height, want_values):
|
||||
# Yields children (in a provider's sense of the word) for a tree headed by a BoxedNode.
|
||||
# In particular, yields each key/value pair in the node and in any child nodes.
|
||||
def children_of_node(boxed_node, height):
|
||||
def cast_to_internal(node):
|
||||
internal_type_name = str(node.type.target()).replace("LeafNode", "InternalNode", 1)
|
||||
internal_type_name = node.type.target().name.replace("LeafNode", "InternalNode", 1)
|
||||
internal_type = lookup_type(internal_type_name)
|
||||
return node.cast(internal_type.pointer())
|
||||
|
||||
node_ptr = unwrap_unique_or_non_null(boxed_node["ptr"])
|
||||
node_ptr = cast_to_internal(node_ptr) if height > 0 else node_ptr
|
||||
leaf = node_ptr["data"] if height > 0 else node_ptr.dereference()
|
||||
leaf = node_ptr.dereference()
|
||||
keys = leaf["keys"]
|
||||
values = leaf["vals"]
|
||||
vals = leaf["vals"]
|
||||
edges = cast_to_internal(node_ptr)["edges"] if height > 0 else None
|
||||
length = int(leaf["len"])
|
||||
|
||||
for i in xrange(0, length + 1):
|
||||
if height > 0:
|
||||
child_ptr = node_ptr["edges"][i]["value"]["value"]
|
||||
for child in children_of_node(child_ptr, height - 1, want_values):
|
||||
boxed_child_node = edges[i]["value"]["value"]
|
||||
for child in children_of_node(boxed_child_node, height - 1):
|
||||
yield child
|
||||
if i < length:
|
||||
if want_values:
|
||||
yield keys[i]["value"]["value"], values[i]["value"]["value"]
|
||||
else:
|
||||
yield keys[i]["value"]["value"]
|
||||
# Avoid "Cannot perform pointer math on incomplete type" on zero-sized arrays.
|
||||
key = keys[i]["value"]["value"] if keys.type.sizeof > 0 else None
|
||||
val = vals[i]["value"]["value"] if vals.type.sizeof > 0 else None
|
||||
yield key, val
|
||||
|
||||
|
||||
# Yields children for a BTreeMap.
|
||||
def children_of_map(map):
|
||||
if map["length"] > 0:
|
||||
root = map["root"]
|
||||
if root.type.name.startswith("core::option::Option<"):
|
||||
root = root.cast(gdb.lookup_type(root.type.name[21:-1]))
|
||||
boxed_root_node = root["node"]
|
||||
height = root["height"]
|
||||
for i, (key, val) in enumerate(children_of_node(boxed_root_node, height)):
|
||||
if key is not None:
|
||||
yield "key{}".format(i), key
|
||||
if val is not None:
|
||||
yield "val{}".format(i), val
|
||||
|
||||
|
||||
class StdBTreeSetProvider:
|
||||
|
@ -242,15 +258,8 @@ class StdBTreeSetProvider:
|
|||
|
||||
def children(self):
|
||||
inner_map = self.valobj["map"]
|
||||
if inner_map["length"] > 0:
|
||||
root = inner_map["root"]
|
||||
if "core::option::Option<" in root.type.name:
|
||||
type_name = str(root.type.name).replace("core::option::Option<", "", 1)[:-1]
|
||||
root = root.cast(gdb.lookup_type(type_name))
|
||||
|
||||
node_ptr = root["node"]
|
||||
for i, child in enumerate(children_of_node(node_ptr, root["height"], False)):
|
||||
yield "[{}]".format(i), child
|
||||
for child in children_of_map(inner_map):
|
||||
yield child
|
||||
|
||||
@staticmethod
|
||||
def display_hint():
|
||||
|
@ -265,16 +274,8 @@ class StdBTreeMapProvider:
|
|||
return "BTreeMap(size={})".format(self.valobj["length"])
|
||||
|
||||
def children(self):
|
||||
if self.valobj["length"] > 0:
|
||||
root = self.valobj["root"]
|
||||
if "core::option::Option<" in root.type.name:
|
||||
type_name = str(root.type.name).replace("core::option::Option<", "", 1)[:-1]
|
||||
root = root.cast(gdb.lookup_type(type_name))
|
||||
|
||||
node_ptr = root["node"]
|
||||
for i, child in enumerate(children_of_node(node_ptr, root["height"], True)):
|
||||
yield "key{}".format(i), child[0]
|
||||
yield "val{}".format(i), child[1]
|
||||
for child in children_of_map(self.valobj):
|
||||
yield child
|
||||
|
||||
@staticmethod
|
||||
def display_hint():
|
||||
|
|
|
@ -130,7 +130,7 @@ pub fn try_inline(
|
|||
attrs,
|
||||
inner,
|
||||
visibility: clean::Public,
|
||||
stability: cx.tcx.lookup_stability(did).clean(cx),
|
||||
stability: cx.tcx.lookup_stability(did).cloned(),
|
||||
deprecation: cx.tcx.lookup_deprecation(did).clean(cx),
|
||||
def_id: did,
|
||||
});
|
||||
|
@ -461,7 +461,7 @@ pub fn build_impl(
|
|||
name: None,
|
||||
attrs,
|
||||
visibility: clean::Inherited,
|
||||
stability: tcx.lookup_stability(did).clean(cx),
|
||||
stability: tcx.lookup_stability(did).cloned(),
|
||||
deprecation: tcx.lookup_deprecation(did).clean(cx),
|
||||
def_id: did,
|
||||
});
|
||||
|
|
|
@ -19,7 +19,6 @@ use rustc_index::vec::{Idx, IndexVec};
|
|||
use rustc_infer::infer::region_constraints::{Constraint, RegionConstraintData};
|
||||
use rustc_middle::bug;
|
||||
use rustc_middle::middle::resolve_lifetime as rl;
|
||||
use rustc_middle::middle::stability;
|
||||
use rustc_middle::ty::fold::TypeFolder;
|
||||
use rustc_middle::ty::subst::{InternalSubsts, Subst};
|
||||
use rustc_middle::ty::{self, AdtKind, Lift, Ty, TyCtxt};
|
||||
|
@ -274,7 +273,7 @@ impl Clean<Item> for doctree::Module<'_> {
|
|||
attrs,
|
||||
source: span.clean(cx),
|
||||
visibility: self.vis.clean(cx),
|
||||
stability: cx.stability(self.id).clean(cx),
|
||||
stability: cx.stability(self.id),
|
||||
deprecation: cx.deprecation(self.id).clean(cx),
|
||||
def_id: cx.tcx.hir().local_def_id(self.id).to_def_id(),
|
||||
inner: ModuleItem(Module { is_crate: self.is_crate, items }),
|
||||
|
@ -914,7 +913,7 @@ impl Clean<Item> for doctree::Function<'_> {
|
|||
attrs: self.attrs.clean(cx),
|
||||
source: self.span.clean(cx),
|
||||
visibility: self.vis.clean(cx),
|
||||
stability: cx.stability(self.id).clean(cx),
|
||||
stability: cx.stability(self.id),
|
||||
deprecation: cx.deprecation(self.id).clean(cx),
|
||||
def_id: did.to_def_id(),
|
||||
inner: FunctionItem(Function {
|
||||
|
@ -1023,7 +1022,7 @@ impl Clean<Item> for doctree::Trait<'_> {
|
|||
source: self.span.clean(cx),
|
||||
def_id: cx.tcx.hir().local_def_id(self.id).to_def_id(),
|
||||
visibility: self.vis.clean(cx),
|
||||
stability: cx.stability(self.id).clean(cx),
|
||||
stability: cx.stability(self.id),
|
||||
deprecation: cx.deprecation(self.id).clean(cx),
|
||||
inner: TraitItem(Trait {
|
||||
auto: self.is_auto.clean(cx),
|
||||
|
@ -1047,7 +1046,7 @@ impl Clean<Item> for doctree::TraitAlias<'_> {
|
|||
source: self.span.clean(cx),
|
||||
def_id: cx.tcx.hir().local_def_id(self.id).to_def_id(),
|
||||
visibility: self.vis.clean(cx),
|
||||
stability: cx.stability(self.id).clean(cx),
|
||||
stability: cx.stability(self.id),
|
||||
deprecation: cx.deprecation(self.id).clean(cx),
|
||||
inner: TraitAliasItem(TraitAlias {
|
||||
generics: self.generics.clean(cx),
|
||||
|
@ -1832,7 +1831,7 @@ impl Clean<Item> for doctree::Struct<'_> {
|
|||
source: self.span.clean(cx),
|
||||
def_id: cx.tcx.hir().local_def_id(self.id).to_def_id(),
|
||||
visibility: self.vis.clean(cx),
|
||||
stability: cx.stability(self.id).clean(cx),
|
||||
stability: cx.stability(self.id),
|
||||
deprecation: cx.deprecation(self.id).clean(cx),
|
||||
inner: StructItem(Struct {
|
||||
struct_type: self.struct_type,
|
||||
|
@ -1852,7 +1851,7 @@ impl Clean<Item> for doctree::Union<'_> {
|
|||
source: self.span.clean(cx),
|
||||
def_id: cx.tcx.hir().local_def_id(self.id).to_def_id(),
|
||||
visibility: self.vis.clean(cx),
|
||||
stability: cx.stability(self.id).clean(cx),
|
||||
stability: cx.stability(self.id),
|
||||
deprecation: cx.deprecation(self.id).clean(cx),
|
||||
inner: UnionItem(Union {
|
||||
struct_type: self.struct_type,
|
||||
|
@ -1882,7 +1881,7 @@ impl Clean<Item> for doctree::Enum<'_> {
|
|||
source: self.span.clean(cx),
|
||||
def_id: cx.tcx.hir().local_def_id(self.id).to_def_id(),
|
||||
visibility: self.vis.clean(cx),
|
||||
stability: cx.stability(self.id).clean(cx),
|
||||
stability: cx.stability(self.id),
|
||||
deprecation: cx.deprecation(self.id).clean(cx),
|
||||
inner: EnumItem(Enum {
|
||||
variants: self.variants.iter().map(|v| v.clean(cx)).collect(),
|
||||
|
@ -1900,7 +1899,7 @@ impl Clean<Item> for doctree::Variant<'_> {
|
|||
attrs: self.attrs.clean(cx),
|
||||
source: self.span.clean(cx),
|
||||
visibility: Inherited,
|
||||
stability: cx.stability(self.id).clean(cx),
|
||||
stability: cx.stability(self.id),
|
||||
deprecation: cx.deprecation(self.id).clean(cx),
|
||||
def_id: cx.tcx.hir().local_def_id(self.id).to_def_id(),
|
||||
inner: VariantItem(Variant { kind: self.def.clean(cx) }),
|
||||
|
@ -2049,7 +2048,7 @@ impl Clean<Item> for doctree::Typedef<'_> {
|
|||
source: self.span.clean(cx),
|
||||
def_id: cx.tcx.hir().local_def_id(self.id).to_def_id(),
|
||||
visibility: self.vis.clean(cx),
|
||||
stability: cx.stability(self.id).clean(cx),
|
||||
stability: cx.stability(self.id),
|
||||
deprecation: cx.deprecation(self.id).clean(cx),
|
||||
inner: TypedefItem(Typedef { type_, generics: self.gen.clean(cx), item_type }, false),
|
||||
}
|
||||
|
@ -2064,7 +2063,7 @@ impl Clean<Item> for doctree::OpaqueTy<'_> {
|
|||
source: self.span.clean(cx),
|
||||
def_id: cx.tcx.hir().local_def_id(self.id).to_def_id(),
|
||||
visibility: self.vis.clean(cx),
|
||||
stability: cx.stability(self.id).clean(cx),
|
||||
stability: cx.stability(self.id),
|
||||
deprecation: cx.deprecation(self.id).clean(cx),
|
||||
inner: OpaqueTyItem(OpaqueTy {
|
||||
bounds: self.opaque_ty.bounds.clean(cx),
|
||||
|
@ -2092,7 +2091,7 @@ impl Clean<Item> for doctree::Static<'_> {
|
|||
source: self.span.clean(cx),
|
||||
def_id: cx.tcx.hir().local_def_id(self.id).to_def_id(),
|
||||
visibility: self.vis.clean(cx),
|
||||
stability: cx.stability(self.id).clean(cx),
|
||||
stability: cx.stability(self.id),
|
||||
deprecation: cx.deprecation(self.id).clean(cx),
|
||||
inner: StaticItem(Static {
|
||||
type_: self.type_.clean(cx),
|
||||
|
@ -2113,7 +2112,7 @@ impl Clean<Item> for doctree::Constant<'_> {
|
|||
source: self.span.clean(cx),
|
||||
def_id: def_id.to_def_id(),
|
||||
visibility: self.vis.clean(cx),
|
||||
stability: cx.stability(self.id).clean(cx),
|
||||
stability: cx.stability(self.id),
|
||||
deprecation: cx.deprecation(self.id).clean(cx),
|
||||
inner: ConstantItem(Constant {
|
||||
type_: self.type_.clean(cx),
|
||||
|
@ -2167,7 +2166,7 @@ impl Clean<Vec<Item>> for doctree::Impl<'_> {
|
|||
source: self.span.clean(cx),
|
||||
def_id: def_id.to_def_id(),
|
||||
visibility: self.vis.clean(cx),
|
||||
stability: cx.stability(self.id).clean(cx),
|
||||
stability: cx.stability(self.id),
|
||||
deprecation: cx.deprecation(self.id).clean(cx),
|
||||
inner: ImplItem(Impl {
|
||||
unsafety: self.unsafety,
|
||||
|
@ -2349,7 +2348,7 @@ impl Clean<Item> for doctree::ForeignItem<'_> {
|
|||
source: self.span.clean(cx),
|
||||
def_id: cx.tcx.hir().local_def_id(self.id).to_def_id(),
|
||||
visibility: self.vis.clean(cx),
|
||||
stability: cx.stability(self.id).clean(cx),
|
||||
stability: cx.stability(self.id),
|
||||
deprecation: cx.deprecation(self.id).clean(cx),
|
||||
inner,
|
||||
}
|
||||
|
@ -2364,7 +2363,7 @@ impl Clean<Item> for doctree::Macro<'_> {
|
|||
attrs: self.attrs.clean(cx),
|
||||
source: self.span.clean(cx),
|
||||
visibility: Public,
|
||||
stability: cx.stability(self.hid).clean(cx),
|
||||
stability: cx.stability(self.hid),
|
||||
deprecation: cx.deprecation(self.hid).clean(cx),
|
||||
def_id: self.def_id,
|
||||
inner: MacroItem(Macro {
|
||||
|
@ -2389,7 +2388,7 @@ impl Clean<Item> for doctree::ProcMacro<'_> {
|
|||
attrs: self.attrs.clean(cx),
|
||||
source: self.span.clean(cx),
|
||||
visibility: Public,
|
||||
stability: cx.stability(self.id).clean(cx),
|
||||
stability: cx.stability(self.id),
|
||||
deprecation: cx.deprecation(self.id).clean(cx),
|
||||
def_id: cx.tcx.hir().local_def_id(self.id).to_def_id(),
|
||||
inner: ProcMacroItem(ProcMacro { kind: self.kind, helpers: self.helpers.clean(cx) }),
|
||||
|
@ -2397,27 +2396,6 @@ impl Clean<Item> for doctree::ProcMacro<'_> {
|
|||
}
|
||||
}
|
||||
|
||||
impl Clean<Stability> for attr::Stability {
|
||||
fn clean(&self, _: &DocContext<'_>) -> Stability {
|
||||
Stability {
|
||||
level: stability::StabilityLevel::from_attr_level(&self.level),
|
||||
feature: self.feature.to_string(),
|
||||
since: match self.level {
|
||||
attr::Stable { ref since } => since.to_string(),
|
||||
_ => String::new(),
|
||||
},
|
||||
unstable_reason: match self.level {
|
||||
attr::Unstable { reason: Some(ref reason), .. } => Some(reason.to_string()),
|
||||
_ => None,
|
||||
},
|
||||
issue: match self.level {
|
||||
attr::Unstable { issue, .. } => issue,
|
||||
_ => None,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Clean<Deprecation> for attr::Deprecation {
|
||||
fn clean(&self, _: &DocContext<'_>) -> Deprecation {
|
||||
Deprecation {
|
||||
|
|
|
@ -4,7 +4,6 @@ use std::fmt;
|
|||
use std::hash::{Hash, Hasher};
|
||||
use std::iter::FromIterator;
|
||||
use std::lazy::SyncOnceCell as OnceCell;
|
||||
use std::num::NonZeroU32;
|
||||
use std::rc::Rc;
|
||||
use std::sync::Arc;
|
||||
use std::{slice, vec};
|
||||
|
@ -13,6 +12,7 @@ use rustc_ast::attr;
|
|||
use rustc_ast::util::comments::beautify_doc_string;
|
||||
use rustc_ast::{self as ast, AttrStyle};
|
||||
use rustc_ast::{FloatTy, IntTy, UintTy};
|
||||
use rustc_attr::{Stability, StabilityLevel};
|
||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def::Res;
|
||||
|
@ -20,11 +20,10 @@ use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE};
|
|||
use rustc_hir::lang_items::LangItem;
|
||||
use rustc_hir::Mutability;
|
||||
use rustc_index::vec::IndexVec;
|
||||
use rustc_middle::middle::stability;
|
||||
use rustc_middle::ty::{AssocKind, TyCtxt};
|
||||
use rustc_span::hygiene::MacroKind;
|
||||
use rustc_span::source_map::DUMMY_SP;
|
||||
use rustc_span::symbol::{kw, sym, Ident, Symbol};
|
||||
use rustc_span::symbol::{kw, sym, Ident, Symbol, SymbolStr};
|
||||
use rustc_span::{self, FileName};
|
||||
use rustc_target::abi::VariantIdx;
|
||||
use rustc_target::spec::abi::Abi;
|
||||
|
@ -197,7 +196,7 @@ impl Item {
|
|||
self.stability.as_ref().and_then(|ref s| {
|
||||
let mut classes = Vec::with_capacity(2);
|
||||
|
||||
if s.level == stability::Unstable {
|
||||
if s.level.is_unstable() {
|
||||
classes.push("unstable");
|
||||
}
|
||||
|
||||
|
@ -210,8 +209,11 @@ impl Item {
|
|||
})
|
||||
}
|
||||
|
||||
pub fn stable_since(&self) -> Option<&str> {
|
||||
self.stability.as_ref().map(|s| &s.since[..])
|
||||
pub fn stable_since(&self) -> Option<SymbolStr> {
|
||||
match self.stability?.level {
|
||||
StabilityLevel::Stable { since, .. } => Some(since.as_str()),
|
||||
StabilityLevel::Unstable { .. } => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_non_exhaustive(&self) -> bool {
|
||||
|
@ -1698,15 +1700,6 @@ pub struct ProcMacro {
|
|||
pub helpers: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Stability {
|
||||
pub level: stability::StabilityLevel,
|
||||
pub feature: String,
|
||||
pub since: String,
|
||||
pub unstable_reason: Option<String>,
|
||||
pub issue: Option<NonZeroU32>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Deprecation {
|
||||
pub since: Option<String>,
|
||||
|
|
|
@ -3,12 +3,13 @@ use crate::clean::blanket_impl::BlanketImplFinder;
|
|||
use crate::clean::{
|
||||
inline, Clean, Crate, Deprecation, ExternalCrate, FnDecl, FnRetTy, Generic, GenericArg,
|
||||
GenericArgs, GenericBound, Generics, GetDefId, ImportSource, Item, ItemEnum, Lifetime,
|
||||
MacroKind, Path, PathSegment, Primitive, PrimitiveType, ResolvedPath, Span, Stability, Type,
|
||||
TypeBinding, TypeKind, Visibility, WherePredicate,
|
||||
MacroKind, Path, PathSegment, Primitive, PrimitiveType, ResolvedPath, Span, Type, TypeBinding,
|
||||
TypeKind, Visibility, WherePredicate,
|
||||
};
|
||||
use crate::core::DocContext;
|
||||
|
||||
use itertools::Itertools;
|
||||
use rustc_attr::Stability;
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def::{DefKind, Res};
|
||||
|
@ -102,7 +103,7 @@ pub fn krate(mut cx: &mut DocContext<'_>) -> Crate {
|
|||
|
||||
// extract the stability index for a node from tcx, if possible
|
||||
pub fn get_stability(cx: &DocContext<'_>, def_id: DefId) -> Option<Stability> {
|
||||
cx.tcx.lookup_stability(def_id).clean(cx)
|
||||
cx.tcx.lookup_stability(def_id).cloned()
|
||||
}
|
||||
|
||||
pub fn get_deprecation(cx: &DocContext<'_>, def_id: DefId) -> Option<Deprecation> {
|
||||
|
|
|
@ -49,6 +49,7 @@ use std::sync::Arc;
|
|||
|
||||
use itertools::Itertools;
|
||||
use rustc_ast_pretty::pprust;
|
||||
use rustc_attr::StabilityLevel;
|
||||
use rustc_data_structures::flock;
|
||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
||||
use rustc_feature::UnstableFeatures;
|
||||
|
@ -1983,10 +1984,12 @@ fn item_module(w: &mut Buffer, cx: &Context, item: &clean::Item, items: &[clean:
|
|||
}
|
||||
let s1 = i1.stability.as_ref().map(|s| s.level);
|
||||
let s2 = i2.stability.as_ref().map(|s| s.level);
|
||||
match (s1, s2) {
|
||||
(Some(stability::Unstable), Some(stability::Stable)) => return Ordering::Greater,
|
||||
(Some(stability::Stable), Some(stability::Unstable)) => return Ordering::Less,
|
||||
_ => {}
|
||||
if let (Some(a), Some(b)) = (s1, s2) {
|
||||
match (a.is_stable(), b.is_stable()) {
|
||||
(true, true) | (false, false) => {}
|
||||
(false, true) => return Ordering::Less,
|
||||
(true, false) => return Ordering::Greater,
|
||||
}
|
||||
}
|
||||
let lhs = i1.name.as_ref().map_or("", |s| &**s);
|
||||
let rhs = i2.name.as_ref().map_or("", |s| &**s);
|
||||
|
@ -2150,10 +2153,7 @@ fn stability_tags(item: &clean::Item) -> String {
|
|||
|
||||
// The "rustc_private" crates are permanently unstable so it makes no sense
|
||||
// to render "unstable" everywhere.
|
||||
if item
|
||||
.stability
|
||||
.as_ref()
|
||||
.map(|s| s.level == stability::Unstable && s.feature != "rustc_private")
|
||||
if item.stability.as_ref().map(|s| s.level.is_unstable() && s.feature != sym::rustc_private)
|
||||
== Some(true)
|
||||
{
|
||||
tags += &tag_html("unstable", "", "Experimental");
|
||||
|
@ -2204,16 +2204,17 @@ fn short_stability(item: &clean::Item, cx: &Context) -> Vec<String> {
|
|||
|
||||
// Render unstable items. But don't render "rustc_private" crates (internal compiler crates).
|
||||
// Those crates are permanently unstable so it makes no sense to render "unstable" everywhere.
|
||||
if let Some(stab) = item
|
||||
if let Some((StabilityLevel::Unstable { reason, issue, .. }, feature)) = item
|
||||
.stability
|
||||
.as_ref()
|
||||
.filter(|stab| stab.level == stability::Unstable && stab.feature != "rustc_private")
|
||||
.filter(|stab| stab.feature != sym::rustc_private)
|
||||
.map(|stab| (stab.level, stab.feature))
|
||||
{
|
||||
let mut message =
|
||||
"<span class='emoji'>🔬</span> This is a nightly-only experimental API.".to_owned();
|
||||
|
||||
let mut feature = format!("<code>{}</code>", Escape(&stab.feature));
|
||||
if let (Some(url), Some(issue)) = (&cx.shared.issue_tracker_base_url, stab.issue) {
|
||||
let mut feature = format!("<code>{}</code>", Escape(&feature.as_str()));
|
||||
if let (Some(url), Some(issue)) = (&cx.shared.issue_tracker_base_url, issue) {
|
||||
feature.push_str(&format!(
|
||||
" <a href=\"{url}{issue}\">#{issue}</a>",
|
||||
url = url,
|
||||
|
@ -2223,13 +2224,13 @@ fn short_stability(item: &clean::Item, cx: &Context) -> Vec<String> {
|
|||
|
||||
message.push_str(&format!(" ({})", feature));
|
||||
|
||||
if let Some(unstable_reason) = &stab.unstable_reason {
|
||||
if let Some(unstable_reason) = reason {
|
||||
let mut ids = cx.id_map.borrow_mut();
|
||||
message = format!(
|
||||
"<details><summary>{}</summary>{}</details>",
|
||||
message,
|
||||
MarkdownHtml(
|
||||
&unstable_reason,
|
||||
&unstable_reason.as_str(),
|
||||
&mut ids,
|
||||
error_codes,
|
||||
cx.shared.edition,
|
||||
|
@ -2355,7 +2356,7 @@ fn render_implementor(
|
|||
implementor,
|
||||
AssocItemLink::Anchor(None),
|
||||
RenderMode::Normal,
|
||||
implementor.impl_item.stable_since(),
|
||||
implementor.impl_item.stable_since().as_deref(),
|
||||
false,
|
||||
Some(use_absolute),
|
||||
false,
|
||||
|
@ -2384,7 +2385,7 @@ fn render_impls(
|
|||
i,
|
||||
assoc_link,
|
||||
RenderMode::Normal,
|
||||
containing_item.stable_since(),
|
||||
containing_item.stable_since().as_deref(),
|
||||
true,
|
||||
None,
|
||||
false,
|
||||
|
@ -2629,7 +2630,7 @@ fn item_trait(w: &mut Buffer, cx: &Context, it: &clean::Item, t: &clean::Trait,
|
|||
&implementor,
|
||||
assoc_link,
|
||||
RenderMode::Normal,
|
||||
implementor.impl_item.stable_since(),
|
||||
implementor.impl_item.stable_since().as_deref(),
|
||||
false,
|
||||
None,
|
||||
true,
|
||||
|
@ -2780,7 +2781,11 @@ fn render_stability_since_raw(w: &mut Buffer, ver: Option<&str>, containing_ver:
|
|||
}
|
||||
|
||||
fn render_stability_since(w: &mut Buffer, item: &clean::Item, containing_item: &clean::Item) {
|
||||
render_stability_since_raw(w, item.stable_since(), containing_item.stable_since())
|
||||
render_stability_since_raw(
|
||||
w,
|
||||
item.stable_since().as_deref(),
|
||||
containing_item.stable_since().as_deref(),
|
||||
)
|
||||
}
|
||||
|
||||
fn render_assoc_item(
|
||||
|
@ -3324,7 +3329,7 @@ fn render_assoc_items(
|
|||
i,
|
||||
AssocItemLink::Anchor(None),
|
||||
render_mode,
|
||||
containing_item.stable_since(),
|
||||
containing_item.stable_since().as_deref(),
|
||||
true,
|
||||
None,
|
||||
false,
|
||||
|
@ -3564,8 +3569,11 @@ fn render_impl(
|
|||
);
|
||||
}
|
||||
write!(w, "<a href='#{}' class='anchor'></a>", id);
|
||||
let since = i.impl_item.stability.as_ref().map(|s| &s.since[..]);
|
||||
render_stability_since_raw(w, since, outer_version);
|
||||
let since = i.impl_item.stability.as_ref().and_then(|s| match s.level {
|
||||
StabilityLevel::Stable { since } => Some(since.as_str()),
|
||||
StabilityLevel::Unstable { .. } => None,
|
||||
});
|
||||
render_stability_since_raw(w, since.as_deref(), outer_version);
|
||||
if let Some(l) = cx.src_href(&i.impl_item, cache) {
|
||||
write!(w, "<a class='srclink' href='{}' title='{}'>[src]</a>", l, "goto source code");
|
||||
}
|
||||
|
@ -3626,7 +3634,7 @@ fn render_impl(
|
|||
write!(w, "<code>");
|
||||
render_assoc_item(w, item, link.anchor(&id), ItemType::Impl);
|
||||
write!(w, "</code>");
|
||||
render_stability_since_raw(w, item.stable_since(), outer_version);
|
||||
render_stability_since_raw(w, item.stable_since().as_deref(), outer_version);
|
||||
if let Some(l) = cx.src_href(item, cache) {
|
||||
write!(
|
||||
w,
|
||||
|
@ -3648,7 +3656,7 @@ fn render_impl(
|
|||
write!(w, "<h4 id='{}' class=\"{}{}\"><code>", id, item_type, extra_class);
|
||||
assoc_const(w, item, ty, default.as_ref(), link.anchor(&id), "");
|
||||
write!(w, "</code>");
|
||||
render_stability_since_raw(w, item.stable_since(), outer_version);
|
||||
render_stability_since_raw(w, item.stable_since().as_deref(), outer_version);
|
||||
if let Some(l) = cx.src_href(item, cache) {
|
||||
write!(
|
||||
w,
|
||||
|
|
|
@ -1,16 +1,15 @@
|
|||
//! Contains information about "passes", used to modify crate information during the documentation
|
||||
//! process.
|
||||
|
||||
use rustc_hir::def_id::{DefId, DefIdSet};
|
||||
use rustc_middle::middle::privacy::AccessLevels;
|
||||
use rustc_span::{InnerSpan, Span, DUMMY_SP};
|
||||
use std::mem;
|
||||
use std::ops::Range;
|
||||
|
||||
use self::Condition::*;
|
||||
use crate::clean::{self, DocFragmentKind, GetDefId, Item};
|
||||
use crate::clean::{self, DocFragmentKind};
|
||||
use crate::core::DocContext;
|
||||
use crate::fold::{DocFolder, StripItem};
|
||||
|
||||
mod stripper;
|
||||
pub use stripper::*;
|
||||
|
||||
mod collapse_docs;
|
||||
pub use self::collapse_docs::COLLAPSE_DOCS;
|
||||
|
@ -149,171 +148,6 @@ pub fn find_pass(pass_name: &str) -> Option<Pass> {
|
|||
PASSES.iter().find(|p| p.name == pass_name).copied()
|
||||
}
|
||||
|
||||
struct Stripper<'a> {
|
||||
retained: &'a mut DefIdSet,
|
||||
access_levels: &'a AccessLevels<DefId>,
|
||||
update_retained: bool,
|
||||
}
|
||||
|
||||
impl<'a> DocFolder for Stripper<'a> {
|
||||
fn fold_item(&mut self, i: Item) -> Option<Item> {
|
||||
match i.inner {
|
||||
clean::StrippedItem(..) => {
|
||||
// We need to recurse into stripped modules to strip things
|
||||
// like impl methods but when doing so we must not add any
|
||||
// items to the `retained` set.
|
||||
debug!("Stripper: recursing into stripped {:?} {:?}", i.type_(), i.name);
|
||||
let old = mem::replace(&mut self.update_retained, false);
|
||||
let ret = self.fold_item_recur(i);
|
||||
self.update_retained = old;
|
||||
return ret;
|
||||
}
|
||||
// These items can all get re-exported
|
||||
clean::OpaqueTyItem(..)
|
||||
| clean::TypedefItem(..)
|
||||
| clean::StaticItem(..)
|
||||
| clean::StructItem(..)
|
||||
| clean::EnumItem(..)
|
||||
| clean::TraitItem(..)
|
||||
| clean::FunctionItem(..)
|
||||
| clean::VariantItem(..)
|
||||
| clean::MethodItem(..)
|
||||
| clean::ForeignFunctionItem(..)
|
||||
| clean::ForeignStaticItem(..)
|
||||
| clean::ConstantItem(..)
|
||||
| clean::UnionItem(..)
|
||||
| clean::AssocConstItem(..)
|
||||
| clean::TraitAliasItem(..)
|
||||
| clean::ForeignTypeItem => {
|
||||
if i.def_id.is_local() {
|
||||
if !self.access_levels.is_exported(i.def_id) {
|
||||
debug!("Stripper: stripping {:?} {:?}", i.type_(), i.name);
|
||||
return None;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
clean::StructFieldItem(..) => {
|
||||
if i.visibility != clean::Public {
|
||||
return StripItem(i).strip();
|
||||
}
|
||||
}
|
||||
|
||||
clean::ModuleItem(..) => {
|
||||
if i.def_id.is_local() && i.visibility != clean::Public {
|
||||
debug!("Stripper: stripping module {:?}", i.name);
|
||||
let old = mem::replace(&mut self.update_retained, false);
|
||||
let ret = StripItem(self.fold_item_recur(i).unwrap()).strip();
|
||||
self.update_retained = old;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
// handled in the `strip-priv-imports` pass
|
||||
clean::ExternCrateItem(..) | clean::ImportItem(..) => {}
|
||||
|
||||
clean::ImplItem(..) => {}
|
||||
|
||||
// tymethods/macros have no control over privacy
|
||||
clean::MacroItem(..) | clean::TyMethodItem(..) => {}
|
||||
|
||||
// Proc-macros are always public
|
||||
clean::ProcMacroItem(..) => {}
|
||||
|
||||
// Primitives are never stripped
|
||||
clean::PrimitiveItem(..) => {}
|
||||
|
||||
// Associated types are never stripped
|
||||
clean::AssocTypeItem(..) => {}
|
||||
|
||||
// Keywords are never stripped
|
||||
clean::KeywordItem(..) => {}
|
||||
}
|
||||
|
||||
let fastreturn = match i.inner {
|
||||
// nothing left to do for traits (don't want to filter their
|
||||
// methods out, visibility controlled by the trait)
|
||||
clean::TraitItem(..) => true,
|
||||
|
||||
// implementations of traits are always public.
|
||||
clean::ImplItem(ref imp) if imp.trait_.is_some() => true,
|
||||
// Struct variant fields have inherited visibility
|
||||
clean::VariantItem(clean::Variant { kind: clean::VariantKind::Struct(..) }) => true,
|
||||
_ => false,
|
||||
};
|
||||
|
||||
let i = if fastreturn {
|
||||
if self.update_retained {
|
||||
self.retained.insert(i.def_id);
|
||||
}
|
||||
return Some(i);
|
||||
} else {
|
||||
self.fold_item_recur(i)
|
||||
};
|
||||
|
||||
if let Some(ref i) = i {
|
||||
if self.update_retained {
|
||||
self.retained.insert(i.def_id);
|
||||
}
|
||||
}
|
||||
i
|
||||
}
|
||||
}
|
||||
|
||||
// This stripper discards all impls which reference stripped items
|
||||
struct ImplStripper<'a> {
|
||||
retained: &'a DefIdSet,
|
||||
}
|
||||
|
||||
impl<'a> DocFolder for ImplStripper<'a> {
|
||||
fn fold_item(&mut self, i: Item) -> Option<Item> {
|
||||
if let clean::ImplItem(ref imp) = i.inner {
|
||||
// emptied none trait impls can be stripped
|
||||
if imp.trait_.is_none() && imp.items.is_empty() {
|
||||
return None;
|
||||
}
|
||||
if let Some(did) = imp.for_.def_id() {
|
||||
if did.is_local() && !imp.for_.is_generic() && !self.retained.contains(&did) {
|
||||
debug!("ImplStripper: impl item for stripped type; removing");
|
||||
return None;
|
||||
}
|
||||
}
|
||||
if let Some(did) = imp.trait_.def_id() {
|
||||
if did.is_local() && !self.retained.contains(&did) {
|
||||
debug!("ImplStripper: impl item for stripped trait; removing");
|
||||
return None;
|
||||
}
|
||||
}
|
||||
if let Some(generics) = imp.trait_.as_ref().and_then(|t| t.generics()) {
|
||||
for typaram in generics {
|
||||
if let Some(did) = typaram.def_id() {
|
||||
if did.is_local() && !self.retained.contains(&did) {
|
||||
debug!(
|
||||
"ImplStripper: stripped item in trait's generics; removing impl"
|
||||
);
|
||||
return None;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
self.fold_item_recur(i)
|
||||
}
|
||||
}
|
||||
|
||||
// This stripper discards all private import statements (`use`, `extern crate`)
|
||||
struct ImportStripper;
|
||||
impl DocFolder for ImportStripper {
|
||||
fn fold_item(&mut self, i: Item) -> Option<Item> {
|
||||
match i.inner {
|
||||
clean::ExternCrateItem(..) | clean::ImportItem(..) if i.visibility != clean::Public => {
|
||||
None
|
||||
}
|
||||
_ => self.fold_item_recur(i),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a span encompassing all the given attributes.
|
||||
crate fn span_of_attrs(attrs: &clean::Attributes) -> Option<Span> {
|
||||
if attrs.doc_strings.is_empty() {
|
||||
|
|
|
@ -0,0 +1,172 @@
|
|||
use rustc_hir::def_id::{DefId, DefIdSet};
|
||||
use rustc_middle::middle::privacy::AccessLevels;
|
||||
use std::mem;
|
||||
|
||||
use crate::clean::{self, GetDefId, Item};
|
||||
use crate::fold::{DocFolder, StripItem};
|
||||
|
||||
pub struct Stripper<'a> {
|
||||
pub retained: &'a mut DefIdSet,
|
||||
pub access_levels: &'a AccessLevels<DefId>,
|
||||
pub update_retained: bool,
|
||||
}
|
||||
|
||||
impl<'a> DocFolder for Stripper<'a> {
|
||||
fn fold_item(&mut self, i: Item) -> Option<Item> {
|
||||
match i.inner {
|
||||
clean::StrippedItem(..) => {
|
||||
// We need to recurse into stripped modules to strip things
|
||||
// like impl methods but when doing so we must not add any
|
||||
// items to the `retained` set.
|
||||
debug!("Stripper: recursing into stripped {:?} {:?}", i.type_(), i.name);
|
||||
let old = mem::replace(&mut self.update_retained, false);
|
||||
let ret = self.fold_item_recur(i);
|
||||
self.update_retained = old;
|
||||
return ret;
|
||||
}
|
||||
// These items can all get re-exported
|
||||
clean::OpaqueTyItem(..)
|
||||
| clean::TypedefItem(..)
|
||||
| clean::StaticItem(..)
|
||||
| clean::StructItem(..)
|
||||
| clean::EnumItem(..)
|
||||
| clean::TraitItem(..)
|
||||
| clean::FunctionItem(..)
|
||||
| clean::VariantItem(..)
|
||||
| clean::MethodItem(..)
|
||||
| clean::ForeignFunctionItem(..)
|
||||
| clean::ForeignStaticItem(..)
|
||||
| clean::ConstantItem(..)
|
||||
| clean::UnionItem(..)
|
||||
| clean::AssocConstItem(..)
|
||||
| clean::TraitAliasItem(..)
|
||||
| clean::ForeignTypeItem => {
|
||||
if i.def_id.is_local() {
|
||||
if !self.access_levels.is_exported(i.def_id) {
|
||||
debug!("Stripper: stripping {:?} {:?}", i.type_(), i.name);
|
||||
return None;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
clean::StructFieldItem(..) => {
|
||||
if i.visibility != clean::Public {
|
||||
return StripItem(i).strip();
|
||||
}
|
||||
}
|
||||
|
||||
clean::ModuleItem(..) => {
|
||||
if i.def_id.is_local() && i.visibility != clean::Public {
|
||||
debug!("Stripper: stripping module {:?}", i.name);
|
||||
let old = mem::replace(&mut self.update_retained, false);
|
||||
let ret = StripItem(self.fold_item_recur(i).unwrap()).strip();
|
||||
self.update_retained = old;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
// handled in the `strip-priv-imports` pass
|
||||
clean::ExternCrateItem(..) | clean::ImportItem(..) => {}
|
||||
|
||||
clean::ImplItem(..) => {}
|
||||
|
||||
// tymethods/macros have no control over privacy
|
||||
clean::MacroItem(..) | clean::TyMethodItem(..) => {}
|
||||
|
||||
// Proc-macros are always public
|
||||
clean::ProcMacroItem(..) => {}
|
||||
|
||||
// Primitives are never stripped
|
||||
clean::PrimitiveItem(..) => {}
|
||||
|
||||
// Associated types are never stripped
|
||||
clean::AssocTypeItem(..) => {}
|
||||
|
||||
// Keywords are never stripped
|
||||
clean::KeywordItem(..) => {}
|
||||
}
|
||||
|
||||
let fastreturn = match i.inner {
|
||||
// nothing left to do for traits (don't want to filter their
|
||||
// methods out, visibility controlled by the trait)
|
||||
clean::TraitItem(..) => true,
|
||||
|
||||
// implementations of traits are always public.
|
||||
clean::ImplItem(ref imp) if imp.trait_.is_some() => true,
|
||||
// Struct variant fields have inherited visibility
|
||||
clean::VariantItem(clean::Variant { kind: clean::VariantKind::Struct(..) }) => true,
|
||||
_ => false,
|
||||
};
|
||||
|
||||
let i = if fastreturn {
|
||||
if self.update_retained {
|
||||
self.retained.insert(i.def_id);
|
||||
}
|
||||
return Some(i);
|
||||
} else {
|
||||
self.fold_item_recur(i)
|
||||
};
|
||||
|
||||
if let Some(ref i) = i {
|
||||
if self.update_retained {
|
||||
self.retained.insert(i.def_id);
|
||||
}
|
||||
}
|
||||
i
|
||||
}
|
||||
}
|
||||
|
||||
/// This stripper discards all impls which reference stripped items
|
||||
pub struct ImplStripper<'a> {
|
||||
pub retained: &'a DefIdSet,
|
||||
}
|
||||
|
||||
impl<'a> DocFolder for ImplStripper<'a> {
|
||||
fn fold_item(&mut self, i: Item) -> Option<Item> {
|
||||
if let clean::ImplItem(ref imp) = i.inner {
|
||||
// emptied none trait impls can be stripped
|
||||
if imp.trait_.is_none() && imp.items.is_empty() {
|
||||
return None;
|
||||
}
|
||||
if let Some(did) = imp.for_.def_id() {
|
||||
if did.is_local() && !imp.for_.is_generic() && !self.retained.contains(&did) {
|
||||
debug!("ImplStripper: impl item for stripped type; removing");
|
||||
return None;
|
||||
}
|
||||
}
|
||||
if let Some(did) = imp.trait_.def_id() {
|
||||
if did.is_local() && !self.retained.contains(&did) {
|
||||
debug!("ImplStripper: impl item for stripped trait; removing");
|
||||
return None;
|
||||
}
|
||||
}
|
||||
if let Some(generics) = imp.trait_.as_ref().and_then(|t| t.generics()) {
|
||||
for typaram in generics {
|
||||
if let Some(did) = typaram.def_id() {
|
||||
if did.is_local() && !self.retained.contains(&did) {
|
||||
debug!(
|
||||
"ImplStripper: stripped item in trait's generics; removing impl"
|
||||
);
|
||||
return None;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
self.fold_item_recur(i)
|
||||
}
|
||||
}
|
||||
|
||||
/// This stripper discards all private import statements (`use`, `extern crate`)
|
||||
pub struct ImportStripper;
|
||||
|
||||
impl DocFolder for ImportStripper {
|
||||
fn fold_item(&mut self, i: Item) -> Option<Item> {
|
||||
match i.inner {
|
||||
clean::ExternCrateItem(..) | clean::ImportItem(..) if i.visibility != clean::Public => {
|
||||
None
|
||||
}
|
||||
_ => self.fold_item_recur(i),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -34,17 +34,20 @@
|
|||
// gdb-check:$6 = BTreeMap(size=15) = {[0] = pretty_std_collections::MyLeafNode (0), [...]}
|
||||
// (abbreviated because it's boring but we need enough elements to include internal nodes)
|
||||
|
||||
// gdb-command: print zst_btree_map
|
||||
// gdb-check:$7 = BTreeMap(size=1)
|
||||
|
||||
// gdb-command: print vec_deque
|
||||
// gdb-check:$7 = VecDeque(size=3) = {5, 3, 7}
|
||||
// gdb-check:$8 = VecDeque(size=3) = {5, 3, 7}
|
||||
|
||||
// gdb-command: print vec_deque2
|
||||
// gdb-check:$8 = VecDeque(size=7) = {2, 3, 4, 5, 6, 7, 8}
|
||||
// gdb-check:$9 = VecDeque(size=7) = {2, 3, 4, 5, 6, 7, 8}
|
||||
|
||||
// gdb-command: print hash_map
|
||||
// gdb-check:$9 = HashMap(size=4) = {[1] = 10, [2] = 20, [3] = 30, [4] = 40}
|
||||
// gdb-check:$10 = HashMap(size=4) = {[1] = 10, [2] = 20, [3] = 30, [4] = 40}
|
||||
|
||||
// gdb-command: print hash_set
|
||||
// gdb-check:$10 = HashSet(size=4) = {1, 2, 3, 4}
|
||||
// gdb-check:$11 = HashSet(size=4) = {1, 2, 3, 4}
|
||||
|
||||
// === LLDB TESTS ==================================================================================
|
||||
|
||||
|
@ -69,9 +72,9 @@
|
|||
#![allow(unused_variables)]
|
||||
use std::collections::BTreeMap;
|
||||
use std::collections::BTreeSet;
|
||||
use std::collections::VecDeque;
|
||||
use std::collections::HashMap;
|
||||
use std::collections::HashSet;
|
||||
use std::collections::VecDeque;
|
||||
use std::hash::{BuildHasherDefault, Hasher};
|
||||
|
||||
struct MyLeafNode(i32); // helps to ensure we don't blindly replace substring "LeafNode"
|
||||
|
@ -111,6 +114,9 @@ fn main() {
|
|||
nasty_btree_map.insert(i, MyLeafNode(i));
|
||||
}
|
||||
|
||||
let mut zst_btree_map: BTreeMap<(), ()> = BTreeMap::new();
|
||||
zst_btree_map.insert((), ());
|
||||
|
||||
// VecDeque
|
||||
let mut vec_deque = VecDeque::new();
|
||||
vec_deque.push_back(5);
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
extern crate rustc_codegen_ssa;
|
||||
extern crate rustc_errors;
|
||||
extern crate rustc_middle;
|
||||
#[macro_use]
|
||||
extern crate rustc_data_structures;
|
||||
extern crate rustc_driver;
|
||||
extern crate rustc_hir;
|
||||
|
@ -12,17 +11,19 @@ extern crate rustc_span;
|
|||
extern crate rustc_symbol_mangling;
|
||||
extern crate rustc_target;
|
||||
|
||||
use rustc_codegen_ssa::back::linker::LinkerInfo;
|
||||
use rustc_codegen_ssa::traits::CodegenBackend;
|
||||
use rustc_data_structures::owning_ref::OwningRef;
|
||||
use rustc_codegen_ssa::{CodegenResults, CrateInfo};
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_data_structures::sync::MetadataRef;
|
||||
use rustc_errors::ErrorReported;
|
||||
use rustc_middle::dep_graph::DepGraph;
|
||||
use rustc_middle::dep_graph::{WorkProduct, WorkProductId};
|
||||
use rustc_middle::middle::cstore::{EncodedMetadata, MetadataLoader, MetadataLoaderDyn};
|
||||
use rustc_middle::ty::query::Providers;
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
use rustc_session::config::OutputFilenames;
|
||||
use rustc_session::Session;
|
||||
use rustc_span::symbol::Symbol;
|
||||
use rustc_target::spec::Target;
|
||||
use std::any::Any;
|
||||
use std::path::Path;
|
||||
|
@ -31,14 +32,11 @@ pub struct NoLlvmMetadataLoader;
|
|||
|
||||
impl MetadataLoader for NoLlvmMetadataLoader {
|
||||
fn get_rlib_metadata(&self, _: &Target, filename: &Path) -> Result<MetadataRef, String> {
|
||||
let buf =
|
||||
std::fs::read(filename).map_err(|e| format!("metadata file open err: {:?}", e))?;
|
||||
let buf: OwningRef<Vec<u8>, [u8]> = OwningRef::new(buf);
|
||||
Ok(rustc_erase_owner!(buf.map_owner_box()))
|
||||
unreachable!("some_crate.rs shouldn't depend on any external crates");
|
||||
}
|
||||
|
||||
fn get_dylib_metadata(&self, target: &Target, filename: &Path) -> Result<MetadataRef, String> {
|
||||
self.get_rlib_metadata(target, filename)
|
||||
unreachable!("some_crate.rs shouldn't depend on any external crates");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -49,53 +47,49 @@ impl CodegenBackend for TheBackend {
|
|||
Box::new(NoLlvmMetadataLoader)
|
||||
}
|
||||
|
||||
fn provide(&self, providers: &mut Providers) {
|
||||
rustc_symbol_mangling::provide(providers);
|
||||
|
||||
providers.supported_target_features = |tcx, _cnum| {
|
||||
Default::default() // Just a dummy
|
||||
};
|
||||
providers.is_reachable_non_generic = |_tcx, _defid| true;
|
||||
providers.exported_symbols = |_tcx, _crate| &[];
|
||||
}
|
||||
|
||||
fn provide_extern(&self, providers: &mut Providers) {
|
||||
providers.is_reachable_non_generic = |_tcx, _defid| true;
|
||||
}
|
||||
fn provide(&self, providers: &mut Providers) {}
|
||||
fn provide_extern(&self, providers: &mut Providers) {}
|
||||
|
||||
fn codegen_crate<'a, 'tcx>(
|
||||
&self,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
_metadata: EncodedMetadata,
|
||||
metadata: EncodedMetadata,
|
||||
_need_metadata_module: bool,
|
||||
) -> Box<dyn Any> {
|
||||
use rustc_hir::def_id::LOCAL_CRATE;
|
||||
|
||||
Box::new(tcx.crate_name(LOCAL_CRATE) as Symbol)
|
||||
Box::new(CodegenResults {
|
||||
crate_name: tcx.crate_name(LOCAL_CRATE),
|
||||
modules: vec![],
|
||||
allocator_module: None,
|
||||
metadata_module: None,
|
||||
metadata,
|
||||
windows_subsystem: None,
|
||||
linker_info: LinkerInfo::new(tcx),
|
||||
crate_info: CrateInfo::new(tcx),
|
||||
})
|
||||
}
|
||||
|
||||
fn join_codegen(
|
||||
&self,
|
||||
ongoing_codegen: Box<dyn Any>,
|
||||
_sess: &Session,
|
||||
_dep_graph: &DepGraph,
|
||||
) -> Result<Box<dyn Any>, ErrorReported> {
|
||||
let crate_name = ongoing_codegen
|
||||
.downcast::<Symbol>()
|
||||
.expect("in join_codegen: ongoing_codegen is not a Symbol");
|
||||
Ok(crate_name)
|
||||
) -> Result<(CodegenResults, FxHashMap<WorkProductId, WorkProduct>), ErrorReported> {
|
||||
let codegen_results = ongoing_codegen
|
||||
.downcast::<CodegenResults>()
|
||||
.expect("in join_codegen: ongoing_codegen is not a CodegenResults");
|
||||
Ok((*codegen_results, FxHashMap::default()))
|
||||
}
|
||||
|
||||
fn link(
|
||||
&self,
|
||||
sess: &Session,
|
||||
codegen_results: Box<dyn Any>,
|
||||
codegen_results: CodegenResults,
|
||||
outputs: &OutputFilenames,
|
||||
) -> Result<(), ErrorReported> {
|
||||
use rustc_session::{config::CrateType, output::out_filename};
|
||||
use std::io::Write;
|
||||
let crate_name =
|
||||
codegen_results.downcast::<Symbol>().expect("in link: codegen_results is not a Symbol");
|
||||
let crate_name = codegen_results.crate_name;
|
||||
for &crate_type in sess.opts.crate_types.iter() {
|
||||
if crate_type != CrateType::Rlib {
|
||||
sess.fatal(&format!("Crate type is {:?}", crate_type));
|
||||
|
|
Loading…
Reference in New Issue