mirror of
https://github.com/rust-lang/rust.git
synced 2026-05-29 12:36:35 +03:00
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:
@@ -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_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::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 @@ fn metadata_loader(&self) -> Box<MetadataLoaderDyn> {
|
||||
}
|
||||
|
||||
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 @@ fn join_codegen(
|
||||
&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 @@ fn link(
|
||||
);
|
||||
});
|
||||
|
||||
// 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::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 @@ pub fn check(&self, sess: &Session, blocking: bool) {
|
||||
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 @@ pub fn join(self, sess: &Session) -> (CodegenResults, FxHashMap<WorkProductId, W
|
||||
(
|
||||
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 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 @@ fn join_codegen(
|
||||
&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 @@ fn join_codegen(
|
||||
fn link(
|
||||
&self,
|
||||
sess: &Session,
|
||||
codegen_results: Box<dyn Any>,
|
||||
codegen_results: CodegenResults,
|
||||
outputs: &OutputFilenames,
|
||||
) -> Result<(), ErrorReported>;
|
||||
}
|
||||
|
||||
@@ -642,7 +642,7 @@ fn process_rlink(sess: &Session, compiler: &interface::Compiler) -> Result<(), E
|
||||
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 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::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 @@ fn check_for_rustc_errors_attr(tcx: TyCtxt<'_>) {
|
||||
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 @@ pub fn linker(&'tcx self) -> Result<Linker> {
|
||||
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 @@ pub fn link(self) -> Result<()> {
|
||||
{
|
||||
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 @@ fn default() -> 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 @@ macro_rules! key {
|
||||
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 @@ macro_rules! key {
|
||||
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 @@ macro_rules! target_option_val {
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3200 +0,0 @@
|
||||
// ignore-tidy-filelength
|
||||
// FIXME: This file seems to have too much functionality wrapped into it,
|
||||
// leading to it being too long.
|
||||
// Splitting this file may involve abstracting functionality into other files.
|
||||
|
||||
use super::callee::{self, DeferredCallResolution};
|
||||
use super::coercion::{CoerceMany, DynamicCoerceMany};
|
||||
use super::method::{self, MethodCallee, SelfSource};
|
||||
use super::Expectation::*;
|
||||
use super::TupleArgumentsFlag::*;
|
||||
use super::{
|
||||
potentially_plural_count, struct_span_err, BreakableCtxt, Diverges, EnclosingBreakables,
|
||||
Expectation, FallbackMode, Inherited, LocalTy, Needs, TupleArgumentsFlag, UnsafetyState,
|
||||
};
|
||||
use crate::astconv::{
|
||||
AstConv, ExplicitLateBound, GenericArgCountMismatch, GenericArgCountResult, PathSeg,
|
||||
};
|
||||
|
||||
use rustc_ast as ast;
|
||||
use rustc_ast::util::parser::ExprPrecedence;
|
||||
use rustc_data_structures::captures::Captures;
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_errors::ErrorReported;
|
||||
use rustc_errors::{Applicability, DiagnosticBuilder, DiagnosticId};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def::{CtorOf, DefKind, Res};
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_hir::lang_items::LangItem;
|
||||
use rustc_hir::{ExprKind, GenericArg, ItemKind, Node, QPath};
|
||||
use rustc_infer::infer::canonical::{Canonical, OriginalQueryValues, QueryResponse};
|
||||
use rustc_infer::infer::error_reporting::TypeAnnotationNeeded::E0282;
|
||||
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
|
||||
use rustc_infer::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind};
|
||||
use rustc_infer::infer::{self, InferOk, InferResult};
|
||||
use rustc_middle::hir::map::blocks::FnLikeNode;
|
||||
use rustc_middle::ty::adjustment::{
|
||||
Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability,
|
||||
};
|
||||
use rustc_middle::ty::fold::TypeFoldable;
|
||||
use rustc_middle::ty::subst::{
|
||||
self, GenericArgKind, InternalSubsts, Subst, SubstsRef, UserSelfTy, UserSubsts,
|
||||
};
|
||||
use rustc_middle::ty::{
|
||||
self, AdtKind, CanonicalUserType, Const, DefIdTree, GenericParamDefKind, ToPolyTraitRef,
|
||||
ToPredicate, Ty, TyCtxt, UserType,
|
||||
};
|
||||
use rustc_session::{lint, Session};
|
||||
use rustc_span::hygiene::DesugaringKind;
|
||||
use rustc_span::source_map::{original_sp, DUMMY_SP};
|
||||
use rustc_span::symbol::{kw, sym, Ident};
|
||||
use rustc_span::{self, BytePos, MultiSpan, Span};
|
||||
use rustc_trait_selection::infer::InferCtxtExt as _;
|
||||
use rustc_trait_selection::opaque_types::InferCtxtExt as _;
|
||||
use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _;
|
||||
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
|
||||
use rustc_trait_selection::traits::{
|
||||
self, ObligationCause, ObligationCauseCode, TraitEngine, TraitEngineExt,
|
||||
};
|
||||
|
||||
use std::cell::{Cell, RefCell};
|
||||
use std::collections::hash_map::Entry;
|
||||
use std::iter;
|
||||
use std::mem::replace;
|
||||
use std::ops::Deref;
|
||||
use std::slice;
|
||||
|
||||
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 sess(&self) -> &Session {
|
||||
&self.tcx.sess
|
||||
}
|
||||
|
||||
pub fn errors_reported_since_creation(&self) -> bool {
|
||||
self.tcx.sess.err_count() > self.err_count_on_creation
|
||||
}
|
||||
|
||||
/// Produces warning on the given node, if the current point in the
|
||||
/// function is unreachable, and there hasn't been another warning.
|
||||
pub(super) fn warn_if_unreachable(&self, id: hir::HirId, span: Span, kind: &str) {
|
||||
// FIXME: Combine these two 'if' expressions into one once
|
||||
// let chains are implemented
|
||||
if let Diverges::Always { span: orig_span, custom_note } = self.diverges.get() {
|
||||
// If span arose from a desugaring of `if` or `while`, then it is the condition itself,
|
||||
// which diverges, that we are about to lint on. This gives suboptimal diagnostics.
|
||||
// Instead, stop here so that the `if`- or `while`-expression's block is linted instead.
|
||||
if !span.is_desugaring(DesugaringKind::CondTemporary)
|
||||
&& !span.is_desugaring(DesugaringKind::Async)
|
||||
&& !orig_span.is_desugaring(DesugaringKind::Await)
|
||||
{
|
||||
self.diverges.set(Diverges::WarnedAlways);
|
||||
|
||||
debug!("warn_if_unreachable: id={:?} span={:?} kind={}", id, span, kind);
|
||||
|
||||
self.tcx().struct_span_lint_hir(lint::builtin::UNREACHABLE_CODE, id, span, |lint| {
|
||||
let msg = format!("unreachable {}", kind);
|
||||
lint.build(&msg)
|
||||
.span_label(span, &msg)
|
||||
.span_label(
|
||||
orig_span,
|
||||
custom_note
|
||||
.unwrap_or("any code following this expression is unreachable"),
|
||||
)
|
||||
.emit();
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
/// Resolves type and const variables in `ty` if possible. Unlike the infcx
|
||||
/// version (resolve_vars_if_possible), this version will
|
||||
/// also select obligations if it seems useful, in an effort
|
||||
/// to get more type information.
|
||||
pub(super) fn resolve_vars_with_obligations(&self, mut ty: Ty<'tcx>) -> Ty<'tcx> {
|
||||
debug!("resolve_vars_with_obligations(ty={:?})", ty);
|
||||
|
||||
// No Infer()? Nothing needs doing.
|
||||
if !ty.has_infer_types_or_consts() {
|
||||
debug!("resolve_vars_with_obligations: ty={:?}", ty);
|
||||
return ty;
|
||||
}
|
||||
|
||||
// If `ty` is a type variable, see whether we already know what it is.
|
||||
ty = self.resolve_vars_if_possible(&ty);
|
||||
if !ty.has_infer_types_or_consts() {
|
||||
debug!("resolve_vars_with_obligations: ty={:?}", ty);
|
||||
return ty;
|
||||
}
|
||||
|
||||
// If not, try resolving pending obligations as much as
|
||||
// possible. This can help substantially when there are
|
||||
// indirect dependencies that don't seem worth tracking
|
||||
// precisely.
|
||||
self.select_obligations_where_possible(false, |_| {});
|
||||
ty = self.resolve_vars_if_possible(&ty);
|
||||
|
||||
debug!("resolve_vars_with_obligations: ty={:?}", ty);
|
||||
ty
|
||||
}
|
||||
|
||||
pub(super) fn record_deferred_call_resolution(
|
||||
&self,
|
||||
closure_def_id: DefId,
|
||||
r: DeferredCallResolution<'tcx>,
|
||||
) {
|
||||
let mut deferred_call_resolutions = self.deferred_call_resolutions.borrow_mut();
|
||||
deferred_call_resolutions.entry(closure_def_id).or_default().push(r);
|
||||
}
|
||||
|
||||
pub(super) fn remove_deferred_call_resolutions(
|
||||
&self,
|
||||
closure_def_id: DefId,
|
||||
) -> Vec<DeferredCallResolution<'tcx>> {
|
||||
let mut deferred_call_resolutions = self.deferred_call_resolutions.borrow_mut();
|
||||
deferred_call_resolutions.remove(&closure_def_id).unwrap_or(vec![])
|
||||
}
|
||||
|
||||
pub fn tag(&self) -> String {
|
||||
format!("{:p}", self)
|
||||
}
|
||||
|
||||
pub fn local_ty(&self, span: Span, nid: hir::HirId) -> LocalTy<'tcx> {
|
||||
self.locals.borrow().get(&nid).cloned().unwrap_or_else(|| {
|
||||
span_bug!(span, "no type for local variable {}", self.tcx.hir().node_to_string(nid))
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn write_ty(&self, id: hir::HirId, ty: Ty<'tcx>) {
|
||||
debug!(
|
||||
"write_ty({:?}, {:?}) in fcx {}",
|
||||
id,
|
||||
self.resolve_vars_if_possible(&ty),
|
||||
self.tag()
|
||||
);
|
||||
self.typeck_results.borrow_mut().node_types_mut().insert(id, ty);
|
||||
|
||||
if ty.references_error() {
|
||||
self.has_errors.set(true);
|
||||
self.set_tainted_by_errors();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write_field_index(&self, hir_id: hir::HirId, index: usize) {
|
||||
self.typeck_results.borrow_mut().field_indices_mut().insert(hir_id, index);
|
||||
}
|
||||
|
||||
fn write_resolution(&self, hir_id: hir::HirId, r: Result<(DefKind, DefId), ErrorReported>) {
|
||||
self.typeck_results.borrow_mut().type_dependent_defs_mut().insert(hir_id, r);
|
||||
}
|
||||
|
||||
pub fn write_method_call(&self, hir_id: hir::HirId, method: MethodCallee<'tcx>) {
|
||||
debug!("write_method_call(hir_id={:?}, method={:?})", hir_id, method);
|
||||
self.write_resolution(hir_id, Ok((DefKind::AssocFn, method.def_id)));
|
||||
self.write_substs(hir_id, method.substs);
|
||||
|
||||
// When the method is confirmed, the `method.substs` includes
|
||||
// parameters from not just the method, but also the impl of
|
||||
// the method -- in particular, the `Self` type will be fully
|
||||
// resolved. However, those are not something that the "user
|
||||
// specified" -- i.e., those types come from the inferred type
|
||||
// of the receiver, not something the user wrote. So when we
|
||||
// create the user-substs, we want to replace those earlier
|
||||
// types with just the types that the user actually wrote --
|
||||
// that is, those that appear on the *method itself*.
|
||||
//
|
||||
// As an example, if the user wrote something like
|
||||
// `foo.bar::<u32>(...)` -- the `Self` type here will be the
|
||||
// type of `foo` (possibly adjusted), but we don't want to
|
||||
// include that. We want just the `[_, u32]` part.
|
||||
if !method.substs.is_noop() {
|
||||
let method_generics = self.tcx.generics_of(method.def_id);
|
||||
if !method_generics.params.is_empty() {
|
||||
let user_type_annotation = self.infcx.probe(|_| {
|
||||
let user_substs = UserSubsts {
|
||||
substs: InternalSubsts::for_item(self.tcx, method.def_id, |param, _| {
|
||||
let i = param.index as usize;
|
||||
if i < method_generics.parent_count {
|
||||
self.infcx.var_for_def(DUMMY_SP, param)
|
||||
} else {
|
||||
method.substs[i]
|
||||
}
|
||||
}),
|
||||
user_self_ty: None, // not relevant here
|
||||
};
|
||||
|
||||
self.infcx.canonicalize_user_type_annotation(&UserType::TypeOf(
|
||||
method.def_id,
|
||||
user_substs,
|
||||
))
|
||||
});
|
||||
|
||||
debug!("write_method_call: user_type_annotation={:?}", user_type_annotation);
|
||||
self.write_user_type_annotation(hir_id, user_type_annotation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write_substs(&self, node_id: hir::HirId, substs: SubstsRef<'tcx>) {
|
||||
if !substs.is_noop() {
|
||||
debug!("write_substs({:?}, {:?}) in fcx {}", node_id, substs, self.tag());
|
||||
|
||||
self.typeck_results.borrow_mut().node_substs_mut().insert(node_id, substs);
|
||||
}
|
||||
}
|
||||
|
||||
/// Given the substs that we just converted from the HIR, try to
|
||||
/// canonicalize them and store them as user-given substitutions
|
||||
/// (i.e., substitutions that must be respected by the NLL check).
|
||||
///
|
||||
/// This should be invoked **before any unifications have
|
||||
/// occurred**, so that annotations like `Vec<_>` are preserved
|
||||
/// properly.
|
||||
pub fn write_user_type_annotation_from_substs(
|
||||
&self,
|
||||
hir_id: hir::HirId,
|
||||
def_id: DefId,
|
||||
substs: SubstsRef<'tcx>,
|
||||
user_self_ty: Option<UserSelfTy<'tcx>>,
|
||||
) {
|
||||
debug!(
|
||||
"write_user_type_annotation_from_substs: hir_id={:?} def_id={:?} substs={:?} \
|
||||
user_self_ty={:?} in fcx {}",
|
||||
hir_id,
|
||||
def_id,
|
||||
substs,
|
||||
user_self_ty,
|
||||
self.tag(),
|
||||
);
|
||||
|
||||
if Self::can_contain_user_lifetime_bounds((substs, user_self_ty)) {
|
||||
let canonicalized = self.infcx.canonicalize_user_type_annotation(&UserType::TypeOf(
|
||||
def_id,
|
||||
UserSubsts { substs, user_self_ty },
|
||||
));
|
||||
debug!("write_user_type_annotation_from_substs: canonicalized={:?}", canonicalized);
|
||||
self.write_user_type_annotation(hir_id, canonicalized);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write_user_type_annotation(
|
||||
&self,
|
||||
hir_id: hir::HirId,
|
||||
canonical_user_type_annotation: CanonicalUserType<'tcx>,
|
||||
) {
|
||||
debug!(
|
||||
"write_user_type_annotation: hir_id={:?} canonical_user_type_annotation={:?} tag={}",
|
||||
hir_id,
|
||||
canonical_user_type_annotation,
|
||||
self.tag(),
|
||||
);
|
||||
|
||||
if !canonical_user_type_annotation.is_identity() {
|
||||
self.typeck_results
|
||||
.borrow_mut()
|
||||
.user_provided_types_mut()
|
||||
.insert(hir_id, canonical_user_type_annotation);
|
||||
} else {
|
||||
debug!("write_user_type_annotation: skipping identity substs");
|
||||
}
|
||||
}
|
||||
|
||||
pub fn apply_adjustments(&self, expr: &hir::Expr<'_>, adj: Vec<Adjustment<'tcx>>) {
|
||||
debug!("apply_adjustments(expr={:?}, adj={:?})", expr, adj);
|
||||
|
||||
if adj.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
let autoborrow_mut = adj.iter().any(|adj| {
|
||||
matches!(adj, &Adjustment {
|
||||
kind: Adjust::Borrow(AutoBorrow::Ref(_, AutoBorrowMutability::Mut { .. })),
|
||||
..
|
||||
})
|
||||
});
|
||||
|
||||
match self.typeck_results.borrow_mut().adjustments_mut().entry(expr.hir_id) {
|
||||
Entry::Vacant(entry) => {
|
||||
entry.insert(adj);
|
||||
}
|
||||
Entry::Occupied(mut entry) => {
|
||||
debug!(" - composing on top of {:?}", entry.get());
|
||||
match (&entry.get()[..], &adj[..]) {
|
||||
// Applying any adjustment on top of a NeverToAny
|
||||
// is a valid NeverToAny adjustment, because it can't
|
||||
// be reached.
|
||||
(&[Adjustment { kind: Adjust::NeverToAny, .. }], _) => return,
|
||||
(&[
|
||||
Adjustment { kind: Adjust::Deref(_), .. },
|
||||
Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(..)), .. },
|
||||
], &[
|
||||
Adjustment { kind: Adjust::Deref(_), .. },
|
||||
.. // Any following adjustments are allowed.
|
||||
]) => {
|
||||
// A reborrow has no effect before a dereference.
|
||||
}
|
||||
// FIXME: currently we never try to compose autoderefs
|
||||
// and ReifyFnPointer/UnsafeFnPointer, but we could.
|
||||
_ =>
|
||||
bug!("while adjusting {:?}, can't compose {:?} and {:?}",
|
||||
expr, entry.get(), adj)
|
||||
};
|
||||
*entry.get_mut() = adj;
|
||||
}
|
||||
}
|
||||
|
||||
// If there is an mutable auto-borrow, it is equivalent to `&mut <expr>`.
|
||||
// In this case implicit use of `Deref` and `Index` within `<expr>` should
|
||||
// instead be `DerefMut` and `IndexMut`, so fix those up.
|
||||
if autoborrow_mut {
|
||||
self.convert_place_derefs_to_mutable(expr);
|
||||
}
|
||||
}
|
||||
|
||||
/// Basically whenever we are converting from a type scheme into
|
||||
/// the fn body space, we always want to normalize associated
|
||||
/// types as well. This function combines the two.
|
||||
fn instantiate_type_scheme<T>(&self, span: Span, substs: SubstsRef<'tcx>, value: &T) -> T
|
||||
where
|
||||
T: TypeFoldable<'tcx>,
|
||||
{
|
||||
let value = value.subst(self.tcx, substs);
|
||||
let result = self.normalize_associated_types_in(span, &value);
|
||||
debug!("instantiate_type_scheme(value={:?}, substs={:?}) = {:?}", value, substs, result);
|
||||
result
|
||||
}
|
||||
|
||||
/// As `instantiate_type_scheme`, but for the bounds found in a
|
||||
/// generic type scheme.
|
||||
fn instantiate_bounds(
|
||||
&self,
|
||||
span: Span,
|
||||
def_id: DefId,
|
||||
substs: SubstsRef<'tcx>,
|
||||
) -> (ty::InstantiatedPredicates<'tcx>, Vec<Span>) {
|
||||
let bounds = self.tcx.predicates_of(def_id);
|
||||
let spans: Vec<Span> = bounds.predicates.iter().map(|(_, span)| *span).collect();
|
||||
let result = bounds.instantiate(self.tcx, substs);
|
||||
let result = self.normalize_associated_types_in(span, &result);
|
||||
debug!(
|
||||
"instantiate_bounds(bounds={:?}, substs={:?}) = {:?}, {:?}",
|
||||
bounds, substs, result, spans,
|
||||
);
|
||||
(result, spans)
|
||||
}
|
||||
|
||||
/// Replaces the opaque types from the given value with type variables,
|
||||
/// and records the `OpaqueTypeMap` for later use during writeback. See
|
||||
/// `InferCtxt::instantiate_opaque_types` for more details.
|
||||
pub(super) fn instantiate_opaque_types_from_value<T: TypeFoldable<'tcx>>(
|
||||
&self,
|
||||
parent_id: hir::HirId,
|
||||
value: &T,
|
||||
value_span: Span,
|
||||
) -> T {
|
||||
let parent_def_id = self.tcx.hir().local_def_id(parent_id);
|
||||
debug!(
|
||||
"instantiate_opaque_types_from_value(parent_def_id={:?}, value={:?})",
|
||||
parent_def_id, value
|
||||
);
|
||||
|
||||
let (value, opaque_type_map) =
|
||||
self.register_infer_ok_obligations(self.instantiate_opaque_types(
|
||||
parent_def_id,
|
||||
self.body_id,
|
||||
self.param_env,
|
||||
value,
|
||||
value_span,
|
||||
));
|
||||
|
||||
let mut opaque_types = self.opaque_types.borrow_mut();
|
||||
let mut opaque_types_vars = self.opaque_types_vars.borrow_mut();
|
||||
for (ty, decl) in opaque_type_map {
|
||||
let _ = opaque_types.insert(ty, decl);
|
||||
let _ = opaque_types_vars.insert(decl.concrete_ty, decl.opaque_type);
|
||||
}
|
||||
|
||||
value
|
||||
}
|
||||
|
||||
pub(super) fn normalize_associated_types_in<T>(&self, span: Span, value: &T) -> T
|
||||
where
|
||||
T: TypeFoldable<'tcx>,
|
||||
{
|
||||
self.inh.normalize_associated_types_in(span, self.body_id, self.param_env, value)
|
||||
}
|
||||
|
||||
pub(super) fn normalize_associated_types_in_as_infer_ok<T>(
|
||||
&self,
|
||||
span: Span,
|
||||
value: &T,
|
||||
) -> InferOk<'tcx, T>
|
||||
where
|
||||
T: TypeFoldable<'tcx>,
|
||||
{
|
||||
self.inh.partially_normalize_associated_types_in(span, self.body_id, self.param_env, value)
|
||||
}
|
||||
|
||||
pub fn require_type_meets(
|
||||
&self,
|
||||
ty: Ty<'tcx>,
|
||||
span: Span,
|
||||
code: traits::ObligationCauseCode<'tcx>,
|
||||
def_id: DefId,
|
||||
) {
|
||||
self.register_bound(ty, def_id, traits::ObligationCause::new(span, self.body_id, code));
|
||||
}
|
||||
|
||||
pub fn require_type_is_sized(
|
||||
&self,
|
||||
ty: Ty<'tcx>,
|
||||
span: Span,
|
||||
code: traits::ObligationCauseCode<'tcx>,
|
||||
) {
|
||||
if !ty.references_error() {
|
||||
let lang_item = self.tcx.require_lang_item(LangItem::Sized, None);
|
||||
self.require_type_meets(ty, span, code, lang_item);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn require_type_is_sized_deferred(
|
||||
&self,
|
||||
ty: Ty<'tcx>,
|
||||
span: Span,
|
||||
code: traits::ObligationCauseCode<'tcx>,
|
||||
) {
|
||||
if !ty.references_error() {
|
||||
self.deferred_sized_obligations.borrow_mut().push((ty, span, code));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn register_bound(
|
||||
&self,
|
||||
ty: Ty<'tcx>,
|
||||
def_id: DefId,
|
||||
cause: traits::ObligationCause<'tcx>,
|
||||
) {
|
||||
if !ty.references_error() {
|
||||
self.fulfillment_cx.borrow_mut().register_bound(
|
||||
self,
|
||||
self.param_env,
|
||||
ty,
|
||||
def_id,
|
||||
cause,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_ty(&self, ast_t: &hir::Ty<'_>) -> Ty<'tcx> {
|
||||
let t = AstConv::ast_ty_to_ty(self, ast_t);
|
||||
self.register_wf_obligation(t.into(), ast_t.span, traits::MiscObligation);
|
||||
t
|
||||
}
|
||||
|
||||
pub fn to_ty_saving_user_provided_ty(&self, ast_ty: &hir::Ty<'_>) -> Ty<'tcx> {
|
||||
let ty = self.to_ty(ast_ty);
|
||||
debug!("to_ty_saving_user_provided_ty: ty={:?}", ty);
|
||||
|
||||
if Self::can_contain_user_lifetime_bounds(ty) {
|
||||
let c_ty = self.infcx.canonicalize_response(&UserType::Ty(ty));
|
||||
debug!("to_ty_saving_user_provided_ty: c_ty={:?}", c_ty);
|
||||
self.typeck_results.borrow_mut().user_provided_types_mut().insert(ast_ty.hir_id, c_ty);
|
||||
}
|
||||
|
||||
ty
|
||||
}
|
||||
|
||||
pub fn to_const(&self, ast_c: &hir::AnonConst) -> &'tcx ty::Const<'tcx> {
|
||||
let const_def_id = self.tcx.hir().local_def_id(ast_c.hir_id);
|
||||
let c = ty::Const::from_anon_const(self.tcx, const_def_id);
|
||||
self.register_wf_obligation(
|
||||
c.into(),
|
||||
self.tcx.hir().span(ast_c.hir_id),
|
||||
ObligationCauseCode::MiscObligation,
|
||||
);
|
||||
c
|
||||
}
|
||||
|
||||
pub fn const_arg_to_const(
|
||||
&self,
|
||||
ast_c: &hir::AnonConst,
|
||||
param_def_id: DefId,
|
||||
) -> &'tcx ty::Const<'tcx> {
|
||||
let const_def = ty::WithOptConstParam {
|
||||
did: self.tcx.hir().local_def_id(ast_c.hir_id),
|
||||
const_param_did: Some(param_def_id),
|
||||
};
|
||||
let c = ty::Const::from_opt_const_arg_anon_const(self.tcx, const_def);
|
||||
self.register_wf_obligation(
|
||||
c.into(),
|
||||
self.tcx.hir().span(ast_c.hir_id),
|
||||
ObligationCauseCode::MiscObligation,
|
||||
);
|
||||
c
|
||||
}
|
||||
|
||||
// If the type given by the user has free regions, save it for later, since
|
||||
// NLL would like to enforce those. Also pass in types that involve
|
||||
// projections, since those can resolve to `'static` bounds (modulo #54940,
|
||||
// which hopefully will be fixed by the time you see this comment, dear
|
||||
// reader, although I have my doubts). Also pass in types with inference
|
||||
// types, because they may be repeated. Other sorts of things are already
|
||||
// sufficiently enforced with erased regions. =)
|
||||
fn can_contain_user_lifetime_bounds<T>(t: T) -> bool
|
||||
where
|
||||
T: TypeFoldable<'tcx>,
|
||||
{
|
||||
t.has_free_regions() || t.has_projections() || t.has_infer_types()
|
||||
}
|
||||
|
||||
pub fn node_ty(&self, id: hir::HirId) -> Ty<'tcx> {
|
||||
match self.typeck_results.borrow().node_types().get(id) {
|
||||
Some(&t) => t,
|
||||
None if self.is_tainted_by_errors() => self.tcx.ty_error(),
|
||||
None => {
|
||||
bug!(
|
||||
"no type for node {}: {} in fcx {}",
|
||||
id,
|
||||
self.tcx.hir().node_to_string(id),
|
||||
self.tag()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Registers an obligation for checking later, during regionck, that `arg` is well-formed.
|
||||
pub fn register_wf_obligation(
|
||||
&self,
|
||||
arg: subst::GenericArg<'tcx>,
|
||||
span: Span,
|
||||
code: traits::ObligationCauseCode<'tcx>,
|
||||
) {
|
||||
// WF obligations never themselves fail, so no real need to give a detailed cause:
|
||||
let cause = traits::ObligationCause::new(span, self.body_id, code);
|
||||
self.register_predicate(traits::Obligation::new(
|
||||
cause,
|
||||
self.param_env,
|
||||
ty::PredicateAtom::WellFormed(arg).to_predicate(self.tcx),
|
||||
));
|
||||
}
|
||||
|
||||
/// Registers obligations that all `substs` are well-formed.
|
||||
pub fn add_wf_bounds(&self, substs: SubstsRef<'tcx>, expr: &hir::Expr<'_>) {
|
||||
for arg in substs.iter().filter(|arg| {
|
||||
matches!(arg.unpack(), GenericArgKind::Type(..) | GenericArgKind::Const(..))
|
||||
}) {
|
||||
self.register_wf_obligation(arg, expr.span, traits::MiscObligation);
|
||||
}
|
||||
}
|
||||
|
||||
/// Given a fully substituted set of bounds (`generic_bounds`), and the values with which each
|
||||
/// type/region parameter was instantiated (`substs`), creates and registers suitable
|
||||
/// trait/region obligations.
|
||||
///
|
||||
/// For example, if there is a function:
|
||||
///
|
||||
/// ```
|
||||
/// fn foo<'a,T:'a>(...)
|
||||
/// ```
|
||||
///
|
||||
/// and a reference:
|
||||
///
|
||||
/// ```
|
||||
/// let f = foo;
|
||||
/// ```
|
||||
///
|
||||
/// Then we will create a fresh region variable `'$0` and a fresh type variable `$1` for `'a`
|
||||
/// and `T`. This routine will add a region obligation `$1:'$0` and register it locally.
|
||||
pub fn add_obligations_for_parameters(
|
||||
&self,
|
||||
cause: traits::ObligationCause<'tcx>,
|
||||
predicates: ty::InstantiatedPredicates<'tcx>,
|
||||
) {
|
||||
assert!(!predicates.has_escaping_bound_vars());
|
||||
|
||||
debug!("add_obligations_for_parameters(predicates={:?})", predicates);
|
||||
|
||||
for obligation in traits::predicates_for_generics(cause, self.param_env, predicates) {
|
||||
self.register_predicate(obligation);
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME(arielb1): use this instead of field.ty everywhere
|
||||
// Only for fields! Returns <none> for methods>
|
||||
// Indifferent to privacy flags
|
||||
pub fn field_ty(
|
||||
&self,
|
||||
span: Span,
|
||||
field: &'tcx ty::FieldDef,
|
||||
substs: SubstsRef<'tcx>,
|
||||
) -> Ty<'tcx> {
|
||||
self.normalize_associated_types_in(span, &field.ty(self.tcx, substs))
|
||||
}
|
||||
|
||||
pub(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(super) fn resolve_generator_interiors(&self, def_id: DefId) {
|
||||
let mut generators = self.deferred_generator_interiors.borrow_mut();
|
||||
for (body_id, interior, kind) in generators.drain(..) {
|
||||
self.select_obligations_where_possible(false, |_| {});
|
||||
super::generator_interior::resolve_interior(self, def_id, body_id, interior, kind);
|
||||
}
|
||||
}
|
||||
|
||||
// Tries to apply a fallback to `ty` if it is an unsolved variable.
|
||||
//
|
||||
// - Unconstrained ints are replaced with `i32`.
|
||||
//
|
||||
// - Unconstrained floats are replaced with with `f64`.
|
||||
//
|
||||
// - Non-numerics get replaced with `!` when `#![feature(never_type_fallback)]`
|
||||
// is enabled. Otherwise, they are replaced with `()`.
|
||||
//
|
||||
// Fallback becomes very dubious if we have encountered type-checking errors.
|
||||
// In that case, fallback to Error.
|
||||
// The return value indicates whether fallback has occurred.
|
||||
pub(super) fn fallback_if_possible(&self, ty: Ty<'tcx>, mode: FallbackMode) -> bool {
|
||||
use rustc_middle::ty::error::UnconstrainedNumeric::Neither;
|
||||
use rustc_middle::ty::error::UnconstrainedNumeric::{UnconstrainedFloat, UnconstrainedInt};
|
||||
|
||||
assert!(ty.is_ty_infer());
|
||||
let fallback = match self.type_is_unconstrained_numeric(ty) {
|
||||
_ if self.is_tainted_by_errors() => self.tcx().ty_error(),
|
||||
UnconstrainedInt => self.tcx.types.i32,
|
||||
UnconstrainedFloat => self.tcx.types.f64,
|
||||
Neither if self.type_var_diverges(ty) => self.tcx.mk_diverging_default(),
|
||||
Neither => {
|
||||
// This type variable was created from the instantiation of an opaque
|
||||
// type. The fact that we're attempting to perform fallback for it
|
||||
// means that the function neither constrained it to a concrete
|
||||
// type, nor to the opaque type itself.
|
||||
//
|
||||
// For example, in this code:
|
||||
//
|
||||
//```
|
||||
// type MyType = impl Copy;
|
||||
// fn defining_use() -> MyType { true }
|
||||
// fn other_use() -> MyType { defining_use() }
|
||||
// ```
|
||||
//
|
||||
// `defining_use` will constrain the instantiated inference
|
||||
// variable to `bool`, while `other_use` will constrain
|
||||
// the instantiated inference variable to `MyType`.
|
||||
//
|
||||
// When we process opaque types during writeback, we
|
||||
// will handle cases like `other_use`, and not count
|
||||
// them as defining usages
|
||||
//
|
||||
// However, we also need to handle cases like this:
|
||||
//
|
||||
// ```rust
|
||||
// pub type Foo = impl Copy;
|
||||
// fn produce() -> Option<Foo> {
|
||||
// None
|
||||
// }
|
||||
// ```
|
||||
//
|
||||
// In the above snippet, the inference variable created by
|
||||
// instantiating `Option<Foo>` will be completely unconstrained.
|
||||
// We treat this as a non-defining use by making the inference
|
||||
// variable fall back to the opaque type itself.
|
||||
if let FallbackMode::All = mode {
|
||||
if let Some(opaque_ty) = self.opaque_types_vars.borrow().get(ty) {
|
||||
debug!(
|
||||
"fallback_if_possible: falling back opaque type var {:?} to {:?}",
|
||||
ty, opaque_ty
|
||||
);
|
||||
*opaque_ty
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
};
|
||||
debug!("fallback_if_possible: defaulting `{:?}` to `{:?}`", ty, fallback);
|
||||
self.demand_eqtype(rustc_span::DUMMY_SP, ty, fallback);
|
||||
true
|
||||
}
|
||||
|
||||
pub(super) fn select_all_obligations_or_error(&self) {
|
||||
debug!("select_all_obligations_or_error");
|
||||
if let Err(errors) = self.fulfillment_cx.borrow_mut().select_all_or_error(&self) {
|
||||
self.report_fulfillment_errors(&errors, self.inh.body_id, false);
|
||||
}
|
||||
}
|
||||
|
||||
/// Select as many obligations as we can at present.
|
||||
pub(super) fn select_obligations_where_possible(
|
||||
&self,
|
||||
fallback_has_occurred: bool,
|
||||
mutate_fullfillment_errors: impl Fn(&mut Vec<traits::FulfillmentError<'tcx>>),
|
||||
) {
|
||||
let result = self.fulfillment_cx.borrow_mut().select_where_possible(self);
|
||||
if let Err(mut errors) = result {
|
||||
mutate_fullfillment_errors(&mut errors);
|
||||
self.report_fulfillment_errors(&errors, self.inh.body_id, fallback_has_occurred);
|
||||
}
|
||||
}
|
||||
|
||||
/// For the overloaded place expressions (`*x`, `x[3]`), the trait
|
||||
/// returns a type of `&T`, but the actual type we assign to the
|
||||
/// *expression* is `T`. So this function just peels off the return
|
||||
/// type by one layer to yield `T`.
|
||||
pub(super) fn make_overloaded_place_return_type(
|
||||
&self,
|
||||
method: MethodCallee<'tcx>,
|
||||
) -> ty::TypeAndMut<'tcx> {
|
||||
// extract method return type, which will be &T;
|
||||
let ret_ty = method.sig.output();
|
||||
|
||||
// method returns &T, but the type as visible to user is T, so deref
|
||||
ret_ty.builtin_deref(true).unwrap()
|
||||
}
|
||||
|
||||
pub(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()
|
||||
}
|
||||
|
||||
fn self_type_matches_expected_vid(
|
||||
&self,
|
||||
trait_ref: ty::PolyTraitRef<'tcx>,
|
||||
expected_vid: ty::TyVid,
|
||||
) -> bool {
|
||||
let self_ty = self.shallow_resolve(trait_ref.skip_binder().self_ty());
|
||||
debug!(
|
||||
"self_type_matches_expected_vid(trait_ref={:?}, self_ty={:?}, expected_vid={:?})",
|
||||
trait_ref, self_ty, expected_vid
|
||||
);
|
||||
match *self_ty.kind() {
|
||||
ty::Infer(ty::TyVar(found_vid)) => {
|
||||
// FIXME: consider using `sub_root_var` here so we
|
||||
// can see through subtyping.
|
||||
let found_vid = self.root_var(found_vid);
|
||||
debug!("self_type_matches_expected_vid - found_vid={:?}", found_vid);
|
||||
expected_vid == found_vid
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn obligations_for_self_ty<'b>(
|
||||
&'b self,
|
||||
self_ty: ty::TyVid,
|
||||
) -> impl Iterator<Item = (ty::PolyTraitRef<'tcx>, traits::PredicateObligation<'tcx>)>
|
||||
+ Captures<'tcx>
|
||||
+ 'b {
|
||||
// FIXME: consider using `sub_root_var` here so we
|
||||
// can see through subtyping.
|
||||
let ty_var_root = self.root_var(self_ty);
|
||||
debug!(
|
||||
"obligations_for_self_ty: self_ty={:?} ty_var_root={:?} pending_obligations={:?}",
|
||||
self_ty,
|
||||
ty_var_root,
|
||||
self.fulfillment_cx.borrow().pending_obligations()
|
||||
);
|
||||
|
||||
self.fulfillment_cx
|
||||
.borrow()
|
||||
.pending_obligations()
|
||||
.into_iter()
|
||||
.filter_map(move |obligation| {
|
||||
match obligation.predicate.skip_binders() {
|
||||
ty::PredicateAtom::Projection(data) => {
|
||||
Some((ty::Binder::bind(data).to_poly_trait_ref(self.tcx), obligation))
|
||||
}
|
||||
ty::PredicateAtom::Trait(data, _) => {
|
||||
Some((ty::Binder::bind(data).to_poly_trait_ref(), obligation))
|
||||
}
|
||||
ty::PredicateAtom::Subtype(..) => None,
|
||||
ty::PredicateAtom::RegionOutlives(..) => None,
|
||||
ty::PredicateAtom::TypeOutlives(..) => None,
|
||||
ty::PredicateAtom::WellFormed(..) => None,
|
||||
ty::PredicateAtom::ObjectSafe(..) => None,
|
||||
ty::PredicateAtom::ConstEvaluatable(..) => None,
|
||||
ty::PredicateAtom::ConstEquate(..) => None,
|
||||
// N.B., this predicate is created by breaking down a
|
||||
// `ClosureType: FnFoo()` predicate, where
|
||||
// `ClosureType` represents some `Closure`. It can't
|
||||
// possibly be referring to the current closure,
|
||||
// because we haven't produced the `Closure` for
|
||||
// this closure yet; this is exactly why the other
|
||||
// code is looking for a self type of a unresolved
|
||||
// inference variable.
|
||||
ty::PredicateAtom::ClosureKind(..) => None,
|
||||
ty::PredicateAtom::TypeWellFormedFromEnv(..) => None,
|
||||
}
|
||||
})
|
||||
.filter(move |(tr, _)| self.self_type_matches_expected_vid(*tr, ty_var_root))
|
||||
}
|
||||
|
||||
pub(super) fn type_var_is_sized(&self, self_ty: ty::TyVid) -> bool {
|
||||
self.obligations_for_self_ty(self_ty)
|
||||
.any(|(tr, _)| Some(tr.def_id()) == self.tcx.lang_items().sized_trait())
|
||||
}
|
||||
|
||||
/// Generic function that factors out common logic from function calls,
|
||||
/// method calls and overloaded operators.
|
||||
pub(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());
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn err_args(&self, len: usize) -> Vec<Ty<'tcx>> {
|
||||
vec![self.tcx.ty_error(); len]
|
||||
}
|
||||
|
||||
/// 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// AST fragment checking
|
||||
pub(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(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Unifies the output type with the expected type early, for more coercions
|
||||
/// and forward type information on the input expressions.
|
||||
pub(super) fn expected_inputs_for_expected_output(
|
||||
&self,
|
||||
call_span: Span,
|
||||
expected_ret: Expectation<'tcx>,
|
||||
formal_ret: Ty<'tcx>,
|
||||
formal_args: &[Ty<'tcx>],
|
||||
) -> Vec<Ty<'tcx>> {
|
||||
let formal_ret = self.resolve_vars_with_obligations(formal_ret);
|
||||
let ret_ty = match expected_ret.only_has_type(self) {
|
||||
Some(ret) => ret,
|
||||
None => return Vec::new(),
|
||||
};
|
||||
let expect_args = self
|
||||
.fudge_inference_if_ok(|| {
|
||||
// Attempt to apply a subtyping relationship between the formal
|
||||
// return type (likely containing type variables if the function
|
||||
// is polymorphic) and the expected return type.
|
||||
// No argument expectations are produced if unification fails.
|
||||
let origin = self.misc(call_span);
|
||||
let ures = self.at(&origin, self.param_env).sup(ret_ty, &formal_ret);
|
||||
|
||||
// FIXME(#27336) can't use ? here, Try::from_error doesn't default
|
||||
// to identity so the resulting type is not constrained.
|
||||
match ures {
|
||||
Ok(ok) => {
|
||||
// Process any obligations locally as much as
|
||||
// we can. We don't care if some things turn
|
||||
// out unconstrained or ambiguous, as we're
|
||||
// just trying to get hints here.
|
||||
self.save_and_restore_in_snapshot_flag(|_| {
|
||||
let mut fulfill = TraitEngine::new(self.tcx);
|
||||
for obligation in ok.obligations {
|
||||
fulfill.register_predicate_obligation(self, obligation);
|
||||
}
|
||||
fulfill.select_where_possible(self)
|
||||
})
|
||||
.map_err(|_| ())?;
|
||||
}
|
||||
Err(_) => return Err(()),
|
||||
}
|
||||
|
||||
// Record all the argument types, with the substitutions
|
||||
// produced from the above subtyping unification.
|
||||
Ok(formal_args.iter().map(|ty| self.resolve_vars_if_possible(ty)).collect())
|
||||
})
|
||||
.unwrap_or_default();
|
||||
debug!(
|
||||
"expected_inputs_for_expected_output(formal={:?} -> {:?}, expected={:?} -> {:?})",
|
||||
formal_args, formal_ret, expect_args, expected_ret
|
||||
);
|
||||
expect_args
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn resolve_lang_item_path(
|
||||
&self,
|
||||
lang_item: hir::LangItem,
|
||||
span: Span,
|
||||
hir_id: hir::HirId,
|
||||
) -> (Res, Ty<'tcx>) {
|
||||
let def_id = self.tcx.require_lang_item(lang_item, Some(span));
|
||||
let def_kind = self.tcx.def_kind(def_id);
|
||||
|
||||
let item_ty = if let DefKind::Variant = def_kind {
|
||||
self.tcx.type_of(self.tcx.parent(def_id).expect("variant w/out parent"))
|
||||
} else {
|
||||
self.tcx.type_of(def_id)
|
||||
};
|
||||
let substs = self.infcx.fresh_substs_for_item(span, def_id);
|
||||
let ty = item_ty.subst(self.tcx, substs);
|
||||
|
||||
self.write_resolution(hir_id, Ok((def_kind, def_id)));
|
||||
self.add_required_obligations(span, def_id, &substs);
|
||||
(Res::Def(def_kind, def_id), ty)
|
||||
}
|
||||
|
||||
/// Resolves an associated value path into a base type and associated constant, or method
|
||||
/// resolution. The newly resolved definition is written into `type_dependent_defs`.
|
||||
pub fn resolve_ty_and_res_ufcs<'b>(
|
||||
&self,
|
||||
qpath: &'b QPath<'b>,
|
||||
hir_id: hir::HirId,
|
||||
span: Span,
|
||||
) -> (Res, Option<Ty<'tcx>>, &'b [hir::PathSegment<'b>]) {
|
||||
debug!("resolve_ty_and_res_ufcs: qpath={:?} hir_id={:?} span={:?}", qpath, hir_id, span);
|
||||
let (ty, qself, item_segment) = match *qpath {
|
||||
QPath::Resolved(ref opt_qself, ref path) => {
|
||||
return (
|
||||
path.res,
|
||||
opt_qself.as_ref().map(|qself| self.to_ty(qself)),
|
||||
&path.segments[..],
|
||||
);
|
||||
}
|
||||
QPath::TypeRelative(ref qself, ref segment) => (self.to_ty(qself), qself, segment),
|
||||
QPath::LangItem(..) => bug!("`resolve_ty_and_res_ufcs` called on `LangItem`"),
|
||||
};
|
||||
if let Some(&cached_result) = self.typeck_results.borrow().type_dependent_defs().get(hir_id)
|
||||
{
|
||||
// Return directly on cache hit. This is useful to avoid doubly reporting
|
||||
// errors with default match binding modes. See #44614.
|
||||
let def =
|
||||
cached_result.map(|(kind, def_id)| Res::Def(kind, def_id)).unwrap_or(Res::Err);
|
||||
return (def, Some(ty), slice::from_ref(&**item_segment));
|
||||
}
|
||||
let item_name = item_segment.ident;
|
||||
let result = self.resolve_ufcs(span, item_name, ty, hir_id).or_else(|error| {
|
||||
let result = match error {
|
||||
method::MethodError::PrivateMatch(kind, def_id, _) => Ok((kind, def_id)),
|
||||
_ => Err(ErrorReported),
|
||||
};
|
||||
if item_name.name != kw::Invalid {
|
||||
if let Some(mut e) = self.report_method_error(
|
||||
span,
|
||||
ty,
|
||||
item_name,
|
||||
SelfSource::QPath(qself),
|
||||
error,
|
||||
None,
|
||||
) {
|
||||
e.emit();
|
||||
}
|
||||
}
|
||||
result
|
||||
});
|
||||
|
||||
// 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),
|
||||
Some(ty),
|
||||
slice::from_ref(&**item_segment),
|
||||
)
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
pub(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,
|
||||
);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/// 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
|
||||
}
|
||||
|
||||
pub(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
|
||||
}
|
||||
|
||||
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))
|
||||
}
|
||||
|
||||
/// Given a function `Node`, return its `FnDecl` if it exists, or `None` otherwise.
|
||||
pub(super) fn get_node_fn_decl(
|
||||
&self,
|
||||
node: Node<'tcx>,
|
||||
) -> Option<(&'tcx hir::FnDecl<'tcx>, Ident, bool)> {
|
||||
match node {
|
||||
Node::Item(&hir::Item { ident, kind: hir::ItemKind::Fn(ref sig, ..), .. }) => {
|
||||
// This is less than ideal, it will not suggest a return type span on any
|
||||
// method called `main`, regardless of whether it is actually the entry point,
|
||||
// but it will still present it as the reason for the expected type.
|
||||
Some((&sig.decl, ident, ident.name != sym::main))
|
||||
}
|
||||
Node::TraitItem(&hir::TraitItem {
|
||||
ident,
|
||||
kind: hir::TraitItemKind::Fn(ref sig, ..),
|
||||
..
|
||||
}) => Some((&sig.decl, ident, true)),
|
||||
Node::ImplItem(&hir::ImplItem {
|
||||
ident,
|
||||
kind: hir::ImplItemKind::Fn(ref sig, ..),
|
||||
..
|
||||
}) => Some((&sig.decl, ident, false)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Given a `HirId`, return the `FnDecl` of the method it is enclosed by and whether a
|
||||
/// suggestion can be made, `None` otherwise.
|
||||
pub fn get_fn_decl(&self, blk_id: hir::HirId) -> Option<(&'tcx hir::FnDecl<'tcx>, bool)> {
|
||||
// Get enclosing Fn, if it is a function or a trait method, unless there's a `loop` or
|
||||
// `while` before reaching it, as block tail returns are not available in them.
|
||||
self.tcx.hir().get_return_block(blk_id).and_then(|blk_id| {
|
||||
let parent = self.tcx.hir().get(blk_id);
|
||||
self.get_node_fn_decl(parent).map(|(fn_decl, _, is_main)| (fn_decl, is_main))
|
||||
})
|
||||
}
|
||||
|
||||
/// 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(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",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn note_internal_mutation_in_method(
|
||||
&self,
|
||||
err: &mut DiagnosticBuilder<'_>,
|
||||
expr: &hir::Expr<'_>,
|
||||
expected: Ty<'tcx>,
|
||||
found: Ty<'tcx>,
|
||||
) {
|
||||
if found != self.tcx.types.unit {
|
||||
return;
|
||||
}
|
||||
if let ExprKind::MethodCall(path_segment, _, [rcvr, ..], _) = expr.kind {
|
||||
if self
|
||||
.typeck_results
|
||||
.borrow()
|
||||
.expr_ty_adjusted_opt(rcvr)
|
||||
.map_or(true, |ty| expected.peel_refs() != ty.peel_refs())
|
||||
{
|
||||
return;
|
||||
}
|
||||
let mut sp = MultiSpan::from_span(path_segment.ident.span);
|
||||
sp.push_span_label(
|
||||
path_segment.ident.span,
|
||||
format!(
|
||||
"this call modifies {} in-place",
|
||||
match rcvr.kind {
|
||||
ExprKind::Path(QPath::Resolved(
|
||||
None,
|
||||
hir::Path { segments: [segment], .. },
|
||||
)) => format!("`{}`", segment.ident),
|
||||
_ => "its receiver".to_string(),
|
||||
}
|
||||
),
|
||||
);
|
||||
sp.push_span_label(
|
||||
rcvr.span,
|
||||
"you probably want to use this value after calling the method...".to_string(),
|
||||
);
|
||||
err.span_note(
|
||||
sp,
|
||||
&format!("method `{}` modifies its receiver in-place", path_segment.ident),
|
||||
);
|
||||
err.note(&format!("...instead of the `()` output of method `{}`", path_segment.ident));
|
||||
}
|
||||
}
|
||||
|
||||
/// When encountering an `impl Future` where `BoxFuture` is expected, suggest `Box::pin`.
|
||||
pub(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(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(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(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);
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn note_need_for_fn_pointer(
|
||||
&self,
|
||||
err: &mut DiagnosticBuilder<'_>,
|
||||
expected: Ty<'tcx>,
|
||||
found: Ty<'tcx>,
|
||||
) {
|
||||
let (sig, did, substs) = match (&expected.kind(), &found.kind()) {
|
||||
(ty::FnDef(did1, substs1), ty::FnDef(did2, substs2)) => {
|
||||
let sig1 = self.tcx.fn_sig(*did1).subst(self.tcx, substs1);
|
||||
let sig2 = self.tcx.fn_sig(*did2).subst(self.tcx, substs2);
|
||||
if sig1 != sig2 {
|
||||
return;
|
||||
}
|
||||
err.note(
|
||||
"different `fn` items always have unique types, even if their signatures are \
|
||||
the same",
|
||||
);
|
||||
(sig1, *did1, substs1)
|
||||
}
|
||||
(ty::FnDef(did, substs), ty::FnPtr(sig2)) => {
|
||||
let sig1 = self.tcx.fn_sig(*did).subst(self.tcx, substs);
|
||||
if sig1 != *sig2 {
|
||||
return;
|
||||
}
|
||||
(sig1, *did, substs)
|
||||
}
|
||||
_ => return,
|
||||
};
|
||||
err.help(&format!("change the expected type to be function pointer `{}`", sig));
|
||||
err.help(&format!(
|
||||
"if the expected type is due to type inference, cast the expected `fn` to a function \
|
||||
pointer: `{} as {}`",
|
||||
self.tcx.def_path_str_with_substs(did, substs),
|
||||
sig
|
||||
));
|
||||
}
|
||||
|
||||
/// 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,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn could_remove_semicolon(
|
||||
&self,
|
||||
blk: &'tcx hir::Block<'tcx>,
|
||||
expected_ty: Ty<'tcx>,
|
||||
) -> Option<Span> {
|
||||
// Be helpful when the user wrote `{... expr;}` and
|
||||
// taking the `;` off is enough to fix the error.
|
||||
let last_stmt = blk.stmts.last()?;
|
||||
let last_expr = match last_stmt.kind {
|
||||
hir::StmtKind::Semi(ref e) => e,
|
||||
_ => return None,
|
||||
};
|
||||
let last_expr_ty = self.node_ty(last_expr.hir_id);
|
||||
if matches!(last_expr_ty.kind(), ty::Error(_))
|
||||
|| self.can_sub(self.param_env, last_expr_ty, expected_ty).is_err()
|
||||
{
|
||||
return None;
|
||||
}
|
||||
let original_span = original_sp(last_stmt.span, blk.span);
|
||||
Some(original_span.with_lo(original_span.hi() - BytePos(1)))
|
||||
}
|
||||
|
||||
// Instantiates the given path, which must refer to an item with the given
|
||||
// number of type parameters and type.
|
||||
pub fn instantiate_value_path(
|
||||
&self,
|
||||
segments: &[hir::PathSegment<'_>],
|
||||
self_ty: Option<Ty<'tcx>>,
|
||||
res: Res,
|
||||
span: Span,
|
||||
hir_id: hir::HirId,
|
||||
) -> (Ty<'tcx>, Res) {
|
||||
debug!(
|
||||
"instantiate_value_path(segments={:?}, self_ty={:?}, res={:?}, hir_id={})",
|
||||
segments, self_ty, res, hir_id,
|
||||
);
|
||||
|
||||
let tcx = self.tcx;
|
||||
|
||||
let path_segs = match res {
|
||||
Res::Local(_) | Res::SelfCtor(_) => vec![],
|
||||
Res::Def(kind, def_id) => {
|
||||
AstConv::def_ids_for_value_path_segments(self, segments, self_ty, kind, def_id)
|
||||
}
|
||||
_ => bug!("instantiate_value_path on {:?}", res),
|
||||
};
|
||||
|
||||
let mut user_self_ty = None;
|
||||
let mut is_alias_variant_ctor = false;
|
||||
match res {
|
||||
Res::Def(DefKind::Ctor(CtorOf::Variant, _), _) => {
|
||||
if let Some(self_ty) = self_ty {
|
||||
let adt_def = self_ty.ty_adt_def().unwrap();
|
||||
user_self_ty = Some(UserSelfTy { impl_def_id: adt_def.did, self_ty });
|
||||
is_alias_variant_ctor = true;
|
||||
}
|
||||
}
|
||||
Res::Def(DefKind::AssocFn | DefKind::AssocConst, def_id) => {
|
||||
let container = tcx.associated_item(def_id).container;
|
||||
debug!("instantiate_value_path: def_id={:?} container={:?}", def_id, container);
|
||||
match container {
|
||||
ty::TraitContainer(trait_did) => {
|
||||
callee::check_legal_trait_for_method_call(tcx, span, None, trait_did)
|
||||
}
|
||||
ty::ImplContainer(impl_def_id) => {
|
||||
if segments.len() == 1 {
|
||||
// `<T>::assoc` will end up here, and so
|
||||
// can `T::assoc`. It this came from an
|
||||
// inherent impl, we need to record the
|
||||
// `T` for posterity (see `UserSelfTy` for
|
||||
// details).
|
||||
let self_ty = self_ty.expect("UFCS sugared assoc missing Self");
|
||||
user_self_ty = Some(UserSelfTy { impl_def_id, self_ty });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
// Now that we have categorized what space the parameters for each
|
||||
// segment belong to, let's sort out the parameters that the user
|
||||
// provided (if any) into their appropriate spaces. We'll also report
|
||||
// errors if type parameters are provided in an inappropriate place.
|
||||
|
||||
let generic_segs: FxHashSet<_> = path_segs.iter().map(|PathSeg(_, index)| index).collect();
|
||||
let generics_has_err = AstConv::prohibit_generics(
|
||||
self,
|
||||
segments.iter().enumerate().filter_map(|(index, seg)| {
|
||||
if !generic_segs.contains(&index) || is_alias_variant_ctor {
|
||||
Some(seg)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
if let Res::Local(hid) = res {
|
||||
let ty = self.local_ty(span, hid).decl_ty;
|
||||
let ty = self.normalize_associated_types_in(span, &ty);
|
||||
self.write_ty(hir_id, ty);
|
||||
return (ty, res);
|
||||
}
|
||||
|
||||
if generics_has_err {
|
||||
// Don't try to infer type parameters when prohibited generic arguments were given.
|
||||
user_self_ty = None;
|
||||
}
|
||||
|
||||
// Now we have to compare the types that the user *actually*
|
||||
// provided against the types that were *expected*. If the user
|
||||
// did not provide any types, then we want to substitute inference
|
||||
// variables. If the user provided some types, we may still need
|
||||
// to add defaults. If the user provided *too many* types, that's
|
||||
// a problem.
|
||||
|
||||
let mut infer_args_for_err = FxHashSet::default();
|
||||
for &PathSeg(def_id, index) in &path_segs {
|
||||
let seg = &segments[index];
|
||||
let generics = tcx.generics_of(def_id);
|
||||
// Argument-position `impl Trait` is treated as a normal generic
|
||||
// parameter internally, but we don't allow users to specify the
|
||||
// parameter's value explicitly, so we have to do some error-
|
||||
// checking here.
|
||||
if let GenericArgCountResult {
|
||||
correct: Err(GenericArgCountMismatch { reported: Some(ErrorReported), .. }),
|
||||
..
|
||||
} = AstConv::check_generic_arg_count_for_call(
|
||||
tcx, span, &generics, &seg, false, // `is_method_call`
|
||||
) {
|
||||
infer_args_for_err.insert(index);
|
||||
self.set_tainted_by_errors(); // See issue #53251.
|
||||
}
|
||||
}
|
||||
|
||||
let has_self = path_segs
|
||||
.last()
|
||||
.map(|PathSeg(def_id, _)| tcx.generics_of(*def_id).has_self)
|
||||
.unwrap_or(false);
|
||||
|
||||
let (res, self_ctor_substs) = if let Res::SelfCtor(impl_def_id) = res {
|
||||
let ty = self.normalize_ty(span, tcx.at(span).type_of(impl_def_id));
|
||||
match *ty.kind() {
|
||||
ty::Adt(adt_def, substs) if adt_def.has_ctor() => {
|
||||
let variant = adt_def.non_enum_variant();
|
||||
let ctor_def_id = variant.ctor_def_id.unwrap();
|
||||
(
|
||||
Res::Def(DefKind::Ctor(CtorOf::Struct, variant.ctor_kind), ctor_def_id),
|
||||
Some(substs),
|
||||
)
|
||||
}
|
||||
_ => {
|
||||
let mut err = tcx.sess.struct_span_err(
|
||||
span,
|
||||
"the `Self` constructor can only be used with tuple or unit structs",
|
||||
);
|
||||
if let Some(adt_def) = ty.ty_adt_def() {
|
||||
match adt_def.adt_kind() {
|
||||
AdtKind::Enum => {
|
||||
err.help("did you mean to use one of the enum's variants?");
|
||||
}
|
||||
AdtKind::Struct | AdtKind::Union => {
|
||||
err.span_suggestion(
|
||||
span,
|
||||
"use curly brackets",
|
||||
String::from("Self { /* fields */ }"),
|
||||
Applicability::HasPlaceholders,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
err.emit();
|
||||
|
||||
return (tcx.ty_error(), res);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
(res, None)
|
||||
};
|
||||
let def_id = res.def_id();
|
||||
|
||||
// The things we are substituting into the type should not contain
|
||||
// escaping late-bound regions, and nor should the base type scheme.
|
||||
let ty = tcx.type_of(def_id);
|
||||
|
||||
let arg_count = GenericArgCountResult {
|
||||
explicit_late_bound: ExplicitLateBound::No,
|
||||
correct: if infer_args_for_err.is_empty() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(GenericArgCountMismatch::default())
|
||||
},
|
||||
};
|
||||
|
||||
let substs = self_ctor_substs.unwrap_or_else(|| {
|
||||
AstConv::create_substs_for_generic_args(
|
||||
tcx,
|
||||
def_id,
|
||||
&[][..],
|
||||
has_self,
|
||||
self_ty,
|
||||
arg_count,
|
||||
// Provide the generic args, and whether types should be inferred.
|
||||
|def_id| {
|
||||
if let Some(&PathSeg(_, index)) =
|
||||
path_segs.iter().find(|&PathSeg(did, _)| *did == def_id)
|
||||
{
|
||||
// If we've encountered an `impl Trait`-related error, we're just
|
||||
// going to infer the arguments for better error messages.
|
||||
if !infer_args_for_err.contains(&index) {
|
||||
// Check whether the user has provided generic arguments.
|
||||
if let Some(ref data) = segments[index].args {
|
||||
return (Some(data), segments[index].infer_args);
|
||||
}
|
||||
}
|
||||
return (None, segments[index].infer_args);
|
||||
}
|
||||
|
||||
(None, true)
|
||||
},
|
||||
// Provide substitutions for parameters for which (valid) arguments have been provided.
|
||||
|param, arg| match (¶m.kind, arg) {
|
||||
(GenericParamDefKind::Lifetime, GenericArg::Lifetime(lt)) => {
|
||||
AstConv::ast_region_to_region(self, lt, Some(param)).into()
|
||||
}
|
||||
(GenericParamDefKind::Type { .. }, GenericArg::Type(ty)) => {
|
||||
self.to_ty(ty).into()
|
||||
}
|
||||
(GenericParamDefKind::Const, GenericArg::Const(ct)) => {
|
||||
self.const_arg_to_const(&ct.value, param.def_id).into()
|
||||
}
|
||||
_ => unreachable!(),
|
||||
},
|
||||
// Provide substitutions for parameters for which arguments are inferred.
|
||||
|substs, param, infer_args| {
|
||||
match param.kind {
|
||||
GenericParamDefKind::Lifetime => {
|
||||
self.re_infer(Some(param), span).unwrap().into()
|
||||
}
|
||||
GenericParamDefKind::Type { has_default, .. } => {
|
||||
if !infer_args && has_default {
|
||||
// If we have a default, then we it doesn't matter that we're not
|
||||
// inferring the type arguments: we provide the default where any
|
||||
// is missing.
|
||||
let default = tcx.type_of(param.def_id);
|
||||
self.normalize_ty(
|
||||
span,
|
||||
default.subst_spanned(tcx, substs.unwrap(), Some(span)),
|
||||
)
|
||||
.into()
|
||||
} else {
|
||||
// If no type arguments were provided, we have to infer them.
|
||||
// This case also occurs as a result of some malformed input, e.g.
|
||||
// a lifetime argument being given instead of a type parameter.
|
||||
// Using inference instead of `Error` gives better error messages.
|
||||
self.var_for_def(span, param)
|
||||
}
|
||||
}
|
||||
GenericParamDefKind::Const => {
|
||||
// FIXME(const_generics:defaults)
|
||||
// No const parameters were provided, we have to infer them.
|
||||
self.var_for_def(span, param)
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
});
|
||||
assert!(!substs.has_escaping_bound_vars());
|
||||
assert!(!ty.has_escaping_bound_vars());
|
||||
|
||||
// First, store the "user substs" for later.
|
||||
self.write_user_type_annotation_from_substs(hir_id, def_id, substs, user_self_ty);
|
||||
|
||||
self.add_required_obligations(span, def_id, &substs);
|
||||
|
||||
// Substitute the values for the type parameters into the type of
|
||||
// the referenced item.
|
||||
let ty_substituted = self.instantiate_type_scheme(span, &substs, &ty);
|
||||
|
||||
if let Some(UserSelfTy { impl_def_id, self_ty }) = user_self_ty {
|
||||
// In the case of `Foo<T>::method` and `<Foo<T>>::method`, if `method`
|
||||
// is inherent, there is no `Self` parameter; instead, the impl needs
|
||||
// type parameters, which we can infer by unifying the provided `Self`
|
||||
// with the substituted impl type.
|
||||
// This also occurs for an enum variant on a type alias.
|
||||
let ty = tcx.type_of(impl_def_id);
|
||||
|
||||
let impl_ty = self.instantiate_type_scheme(span, &substs, &ty);
|
||||
match self.at(&self.misc(span), self.param_env).sup(impl_ty, self_ty) {
|
||||
Ok(ok) => self.register_infer_ok_obligations(ok),
|
||||
Err(_) => {
|
||||
self.tcx.sess.delay_span_bug(
|
||||
span,
|
||||
&format!(
|
||||
"instantiate_value_path: (UFCS) {:?} was a subtype of {:?} but now is not?",
|
||||
self_ty,
|
||||
impl_ty,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.check_rustc_args_require_const(def_id, hir_id, span);
|
||||
|
||||
debug!("instantiate_value_path: type of {:?} is {:?}", hir_id, ty_substituted);
|
||||
self.write_substs(hir_id, substs);
|
||||
|
||||
(ty_substituted, res)
|
||||
}
|
||||
|
||||
/// Add all the obligations that are required, substituting and normalized appropriately.
|
||||
fn add_required_obligations(&self, span: Span, def_id: DefId, substs: &SubstsRef<'tcx>) {
|
||||
let (bounds, spans) = self.instantiate_bounds(span, def_id, &substs);
|
||||
|
||||
for (i, mut obligation) in traits::predicates_for_generics(
|
||||
traits::ObligationCause::new(span, self.body_id, traits::ItemObligation(def_id)),
|
||||
self.param_env,
|
||||
bounds,
|
||||
)
|
||||
.enumerate()
|
||||
{
|
||||
// This makes the error point at the bound, but we want to point at the argument
|
||||
if let Some(span) = spans.get(i) {
|
||||
obligation.cause.make_mut().code = traits::BindingObligation(def_id, *span);
|
||||
}
|
||||
self.register_predicate(obligation);
|
||||
}
|
||||
}
|
||||
|
||||
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",
|
||||
);
|
||||
}
|
||||
|
||||
/// Resolves `typ` by a single level if `typ` is a type variable.
|
||||
/// If no resolution is possible, then an error is reported.
|
||||
/// Numeric inference variables may be left unresolved.
|
||||
pub fn structurally_resolved_type(&self, sp: Span, ty: Ty<'tcx>) -> Ty<'tcx> {
|
||||
let ty = self.resolve_vars_with_obligations(ty);
|
||||
if !ty.is_ty_var() {
|
||||
ty
|
||||
} else {
|
||||
if !self.is_tainted_by_errors() {
|
||||
self.emit_inference_failure_err((**self).body_id, sp, ty.into(), E0282)
|
||||
.note("type must be known at this point")
|
||||
.emit();
|
||||
}
|
||||
let err = self.tcx.ty_error();
|
||||
self.demand_suptype(sp, err, ty);
|
||||
err
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn with_breakable_ctxt<F: FnOnce() -> R, R>(
|
||||
&self,
|
||||
id: hir::HirId,
|
||||
ctxt: BreakableCtxt<'tcx>,
|
||||
f: F,
|
||||
) -> (BreakableCtxt<'tcx>, R) {
|
||||
let index;
|
||||
{
|
||||
let mut enclosing_breakables = self.enclosing_breakables.borrow_mut();
|
||||
index = enclosing_breakables.stack.len();
|
||||
enclosing_breakables.by_id.insert(id, index);
|
||||
enclosing_breakables.stack.push(ctxt);
|
||||
}
|
||||
let result = f();
|
||||
let ctxt = {
|
||||
let mut enclosing_breakables = self.enclosing_breakables.borrow_mut();
|
||||
debug_assert!(enclosing_breakables.stack.len() == index + 1);
|
||||
enclosing_breakables.by_id.remove(&id).expect("missing breakable context");
|
||||
enclosing_breakables.stack.pop().expect("missing breakable context")
|
||||
};
|
||||
(ctxt, result)
|
||||
}
|
||||
|
||||
/// Instantiate a QueryResponse in a probe context, without a
|
||||
/// good ObligationCause.
|
||||
pub(super) fn probe_instantiate_query_response(
|
||||
&self,
|
||||
span: Span,
|
||||
original_values: &OriginalQueryValues<'tcx>,
|
||||
query_result: &Canonical<'tcx, QueryResponse<'tcx, Ty<'tcx>>>,
|
||||
) -> InferResult<'tcx, Ty<'tcx>> {
|
||||
self.instantiate_query_response_and_region_obligations(
|
||||
&traits::ObligationCause::misc(span, self.body_id),
|
||||
self.param_env,
|
||||
original_values,
|
||||
query_result,
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns `true` if an expression is contained inside the LHS of an assignment expression.
|
||||
pub(super) fn expr_in_place(&self, mut expr_id: hir::HirId) -> bool {
|
||||
let mut contained_in_place = false;
|
||||
|
||||
while let hir::Node::Expr(parent_expr) =
|
||||
self.tcx.hir().get(self.tcx.hir().get_parent_node(expr_id))
|
||||
{
|
||||
match &parent_expr.kind {
|
||||
hir::ExprKind::Assign(lhs, ..) | hir::ExprKind::AssignOp(_, lhs, ..) => {
|
||||
if lhs.hir_id == expr_id {
|
||||
contained_in_place = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
expr_id = parent_expr.hir_id;
|
||||
}
|
||||
|
||||
contained_in_place
|
||||
}
|
||||
}
|
||||
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,1469 @@
|
||||
use crate::astconv::{
|
||||
AstConv, ExplicitLateBound, GenericArgCountMismatch, GenericArgCountResult, PathSeg,
|
||||
};
|
||||
use crate::check::callee::{self, DeferredCallResolution};
|
||||
use crate::check::method::{self, MethodCallee, SelfSource};
|
||||
use crate::check::{BreakableCtxt, Diverges, Expectation, FallbackMode, FnCtxt, LocalTy};
|
||||
|
||||
use rustc_data_structures::captures::Captures;
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_errors::{Applicability, DiagnosticBuilder, ErrorReported};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def::{CtorOf, DefKind, Res};
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_hir::lang_items::LangItem;
|
||||
use rustc_hir::{ExprKind, GenericArg, Node, QPath};
|
||||
use rustc_infer::infer::canonical::{Canonical, OriginalQueryValues, QueryResponse};
|
||||
use rustc_infer::infer::error_reporting::TypeAnnotationNeeded::E0282;
|
||||
use rustc_infer::infer::{InferOk, InferResult};
|
||||
use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability};
|
||||
use rustc_middle::ty::fold::TypeFoldable;
|
||||
use rustc_middle::ty::subst::{
|
||||
self, GenericArgKind, InternalSubsts, Subst, SubstsRef, UserSelfTy, UserSubsts,
|
||||
};
|
||||
use rustc_middle::ty::{
|
||||
self, AdtKind, CanonicalUserType, DefIdTree, GenericParamDefKind, ToPolyTraitRef, ToPredicate,
|
||||
Ty, UserType,
|
||||
};
|
||||
use rustc_session::lint;
|
||||
use rustc_span::hygiene::DesugaringKind;
|
||||
use rustc_span::source_map::{original_sp, DUMMY_SP};
|
||||
use rustc_span::symbol::{kw, sym, Ident};
|
||||
use rustc_span::{self, BytePos, MultiSpan, Span};
|
||||
use rustc_trait_selection::infer::InferCtxtExt as _;
|
||||
use rustc_trait_selection::opaque_types::InferCtxtExt as _;
|
||||
use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _;
|
||||
use rustc_trait_selection::traits::{self, ObligationCauseCode, TraitEngine, TraitEngineExt};
|
||||
|
||||
use std::collections::hash_map::Entry;
|
||||
use std::slice;
|
||||
|
||||
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
/// Produces warning on the given node, if the current point in the
|
||||
/// function is unreachable, and there hasn't been another warning.
|
||||
pub(in super::super) fn warn_if_unreachable(&self, id: hir::HirId, span: Span, kind: &str) {
|
||||
// FIXME: Combine these two 'if' expressions into one once
|
||||
// let chains are implemented
|
||||
if let Diverges::Always { span: orig_span, custom_note } = self.diverges.get() {
|
||||
// If span arose from a desugaring of `if` or `while`, then it is the condition itself,
|
||||
// which diverges, that we are about to lint on. This gives suboptimal diagnostics.
|
||||
// Instead, stop here so that the `if`- or `while`-expression's block is linted instead.
|
||||
if !span.is_desugaring(DesugaringKind::CondTemporary)
|
||||
&& !span.is_desugaring(DesugaringKind::Async)
|
||||
&& !orig_span.is_desugaring(DesugaringKind::Await)
|
||||
{
|
||||
self.diverges.set(Diverges::WarnedAlways);
|
||||
|
||||
debug!("warn_if_unreachable: id={:?} span={:?} kind={}", id, span, kind);
|
||||
|
||||
self.tcx().struct_span_lint_hir(lint::builtin::UNREACHABLE_CODE, id, span, |lint| {
|
||||
let msg = format!("unreachable {}", kind);
|
||||
lint.build(&msg)
|
||||
.span_label(span, &msg)
|
||||
.span_label(
|
||||
orig_span,
|
||||
custom_note
|
||||
.unwrap_or("any code following this expression is unreachable"),
|
||||
)
|
||||
.emit();
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Resolves type and const variables in `ty` if possible. Unlike the infcx
|
||||
/// version (resolve_vars_if_possible), this version will
|
||||
/// also select obligations if it seems useful, in an effort
|
||||
/// to get more type information.
|
||||
pub(in super::super) fn resolve_vars_with_obligations(&self, mut ty: Ty<'tcx>) -> Ty<'tcx> {
|
||||
debug!("resolve_vars_with_obligations(ty={:?})", ty);
|
||||
|
||||
// No Infer()? Nothing needs doing.
|
||||
if !ty.has_infer_types_or_consts() {
|
||||
debug!("resolve_vars_with_obligations: ty={:?}", ty);
|
||||
return ty;
|
||||
}
|
||||
|
||||
// If `ty` is a type variable, see whether we already know what it is.
|
||||
ty = self.resolve_vars_if_possible(&ty);
|
||||
if !ty.has_infer_types_or_consts() {
|
||||
debug!("resolve_vars_with_obligations: ty={:?}", ty);
|
||||
return ty;
|
||||
}
|
||||
|
||||
// If not, try resolving pending obligations as much as
|
||||
// possible. This can help substantially when there are
|
||||
// indirect dependencies that don't seem worth tracking
|
||||
// precisely.
|
||||
self.select_obligations_where_possible(false, |_| {});
|
||||
ty = self.resolve_vars_if_possible(&ty);
|
||||
|
||||
debug!("resolve_vars_with_obligations: ty={:?}", ty);
|
||||
ty
|
||||
}
|
||||
|
||||
pub(in super::super) fn record_deferred_call_resolution(
|
||||
&self,
|
||||
closure_def_id: DefId,
|
||||
r: DeferredCallResolution<'tcx>,
|
||||
) {
|
||||
let mut deferred_call_resolutions = self.deferred_call_resolutions.borrow_mut();
|
||||
deferred_call_resolutions.entry(closure_def_id).or_default().push(r);
|
||||
}
|
||||
|
||||
pub(in super::super) fn remove_deferred_call_resolutions(
|
||||
&self,
|
||||
closure_def_id: DefId,
|
||||
) -> Vec<DeferredCallResolution<'tcx>> {
|
||||
let mut deferred_call_resolutions = self.deferred_call_resolutions.borrow_mut();
|
||||
deferred_call_resolutions.remove(&closure_def_id).unwrap_or(vec![])
|
||||
}
|
||||
|
||||
pub fn tag(&self) -> String {
|
||||
format!("{:p}", self)
|
||||
}
|
||||
|
||||
pub fn local_ty(&self, span: Span, nid: hir::HirId) -> LocalTy<'tcx> {
|
||||
self.locals.borrow().get(&nid).cloned().unwrap_or_else(|| {
|
||||
span_bug!(span, "no type for local variable {}", self.tcx.hir().node_to_string(nid))
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn write_ty(&self, id: hir::HirId, ty: Ty<'tcx>) {
|
||||
debug!(
|
||||
"write_ty({:?}, {:?}) in fcx {}",
|
||||
id,
|
||||
self.resolve_vars_if_possible(&ty),
|
||||
self.tag()
|
||||
);
|
||||
self.typeck_results.borrow_mut().node_types_mut().insert(id, ty);
|
||||
|
||||
if ty.references_error() {
|
||||
self.has_errors.set(true);
|
||||
self.set_tainted_by_errors();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write_field_index(&self, hir_id: hir::HirId, index: usize) {
|
||||
self.typeck_results.borrow_mut().field_indices_mut().insert(hir_id, index);
|
||||
}
|
||||
|
||||
pub(in super::super) fn write_resolution(
|
||||
&self,
|
||||
hir_id: hir::HirId,
|
||||
r: Result<(DefKind, DefId), ErrorReported>,
|
||||
) {
|
||||
self.typeck_results.borrow_mut().type_dependent_defs_mut().insert(hir_id, r);
|
||||
}
|
||||
|
||||
pub fn write_method_call(&self, hir_id: hir::HirId, method: MethodCallee<'tcx>) {
|
||||
debug!("write_method_call(hir_id={:?}, method={:?})", hir_id, method);
|
||||
self.write_resolution(hir_id, Ok((DefKind::AssocFn, method.def_id)));
|
||||
self.write_substs(hir_id, method.substs);
|
||||
|
||||
// When the method is confirmed, the `method.substs` includes
|
||||
// parameters from not just the method, but also the impl of
|
||||
// the method -- in particular, the `Self` type will be fully
|
||||
// resolved. However, those are not something that the "user
|
||||
// specified" -- i.e., those types come from the inferred type
|
||||
// of the receiver, not something the user wrote. So when we
|
||||
// create the user-substs, we want to replace those earlier
|
||||
// types with just the types that the user actually wrote --
|
||||
// that is, those that appear on the *method itself*.
|
||||
//
|
||||
// As an example, if the user wrote something like
|
||||
// `foo.bar::<u32>(...)` -- the `Self` type here will be the
|
||||
// type of `foo` (possibly adjusted), but we don't want to
|
||||
// include that. We want just the `[_, u32]` part.
|
||||
if !method.substs.is_noop() {
|
||||
let method_generics = self.tcx.generics_of(method.def_id);
|
||||
if !method_generics.params.is_empty() {
|
||||
let user_type_annotation = self.infcx.probe(|_| {
|
||||
let user_substs = UserSubsts {
|
||||
substs: InternalSubsts::for_item(self.tcx, method.def_id, |param, _| {
|
||||
let i = param.index as usize;
|
||||
if i < method_generics.parent_count {
|
||||
self.infcx.var_for_def(DUMMY_SP, param)
|
||||
} else {
|
||||
method.substs[i]
|
||||
}
|
||||
}),
|
||||
user_self_ty: None, // not relevant here
|
||||
};
|
||||
|
||||
self.infcx.canonicalize_user_type_annotation(&UserType::TypeOf(
|
||||
method.def_id,
|
||||
user_substs,
|
||||
))
|
||||
});
|
||||
|
||||
debug!("write_method_call: user_type_annotation={:?}", user_type_annotation);
|
||||
self.write_user_type_annotation(hir_id, user_type_annotation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write_substs(&self, node_id: hir::HirId, substs: SubstsRef<'tcx>) {
|
||||
if !substs.is_noop() {
|
||||
debug!("write_substs({:?}, {:?}) in fcx {}", node_id, substs, self.tag());
|
||||
|
||||
self.typeck_results.borrow_mut().node_substs_mut().insert(node_id, substs);
|
||||
}
|
||||
}
|
||||
|
||||
/// Given the substs that we just converted from the HIR, try to
|
||||
/// canonicalize them and store them as user-given substitutions
|
||||
/// (i.e., substitutions that must be respected by the NLL check).
|
||||
///
|
||||
/// This should be invoked **before any unifications have
|
||||
/// occurred**, so that annotations like `Vec<_>` are preserved
|
||||
/// properly.
|
||||
pub fn write_user_type_annotation_from_substs(
|
||||
&self,
|
||||
hir_id: hir::HirId,
|
||||
def_id: DefId,
|
||||
substs: SubstsRef<'tcx>,
|
||||
user_self_ty: Option<UserSelfTy<'tcx>>,
|
||||
) {
|
||||
debug!(
|
||||
"write_user_type_annotation_from_substs: hir_id={:?} def_id={:?} substs={:?} \
|
||||
user_self_ty={:?} in fcx {}",
|
||||
hir_id,
|
||||
def_id,
|
||||
substs,
|
||||
user_self_ty,
|
||||
self.tag(),
|
||||
);
|
||||
|
||||
if Self::can_contain_user_lifetime_bounds((substs, user_self_ty)) {
|
||||
let canonicalized = self.infcx.canonicalize_user_type_annotation(&UserType::TypeOf(
|
||||
def_id,
|
||||
UserSubsts { substs, user_self_ty },
|
||||
));
|
||||
debug!("write_user_type_annotation_from_substs: canonicalized={:?}", canonicalized);
|
||||
self.write_user_type_annotation(hir_id, canonicalized);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write_user_type_annotation(
|
||||
&self,
|
||||
hir_id: hir::HirId,
|
||||
canonical_user_type_annotation: CanonicalUserType<'tcx>,
|
||||
) {
|
||||
debug!(
|
||||
"write_user_type_annotation: hir_id={:?} canonical_user_type_annotation={:?} tag={}",
|
||||
hir_id,
|
||||
canonical_user_type_annotation,
|
||||
self.tag(),
|
||||
);
|
||||
|
||||
if !canonical_user_type_annotation.is_identity() {
|
||||
self.typeck_results
|
||||
.borrow_mut()
|
||||
.user_provided_types_mut()
|
||||
.insert(hir_id, canonical_user_type_annotation);
|
||||
} else {
|
||||
debug!("write_user_type_annotation: skipping identity substs");
|
||||
}
|
||||
}
|
||||
|
||||
pub fn apply_adjustments(&self, expr: &hir::Expr<'_>, adj: Vec<Adjustment<'tcx>>) {
|
||||
debug!("apply_adjustments(expr={:?}, adj={:?})", expr, adj);
|
||||
|
||||
if adj.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
let autoborrow_mut = adj.iter().any(|adj| {
|
||||
matches!(adj, &Adjustment {
|
||||
kind: Adjust::Borrow(AutoBorrow::Ref(_, AutoBorrowMutability::Mut { .. })),
|
||||
..
|
||||
})
|
||||
});
|
||||
|
||||
match self.typeck_results.borrow_mut().adjustments_mut().entry(expr.hir_id) {
|
||||
Entry::Vacant(entry) => {
|
||||
entry.insert(adj);
|
||||
}
|
||||
Entry::Occupied(mut entry) => {
|
||||
debug!(" - composing on top of {:?}", entry.get());
|
||||
match (&entry.get()[..], &adj[..]) {
|
||||
// Applying any adjustment on top of a NeverToAny
|
||||
// is a valid NeverToAny adjustment, because it can't
|
||||
// be reached.
|
||||
(&[Adjustment { kind: Adjust::NeverToAny, .. }], _) => return,
|
||||
(&[
|
||||
Adjustment { kind: Adjust::Deref(_), .. },
|
||||
Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(..)), .. },
|
||||
], &[
|
||||
Adjustment { kind: Adjust::Deref(_), .. },
|
||||
.. // Any following adjustments are allowed.
|
||||
]) => {
|
||||
// A reborrow has no effect before a dereference.
|
||||
}
|
||||
// FIXME: currently we never try to compose autoderefs
|
||||
// and ReifyFnPointer/UnsafeFnPointer, but we could.
|
||||
_ =>
|
||||
bug!("while adjusting {:?}, can't compose {:?} and {:?}",
|
||||
expr, entry.get(), adj)
|
||||
};
|
||||
*entry.get_mut() = adj;
|
||||
}
|
||||
}
|
||||
|
||||
// If there is an mutable auto-borrow, it is equivalent to `&mut <expr>`.
|
||||
// In this case implicit use of `Deref` and `Index` within `<expr>` should
|
||||
// instead be `DerefMut` and `IndexMut`, so fix those up.
|
||||
if autoborrow_mut {
|
||||
self.convert_place_derefs_to_mutable(expr);
|
||||
}
|
||||
}
|
||||
|
||||
/// Basically whenever we are converting from a type scheme into
|
||||
/// the fn body space, we always want to normalize associated
|
||||
/// types as well. This function combines the two.
|
||||
fn instantiate_type_scheme<T>(&self, span: Span, substs: SubstsRef<'tcx>, value: &T) -> T
|
||||
where
|
||||
T: TypeFoldable<'tcx>,
|
||||
{
|
||||
let value = value.subst(self.tcx, substs);
|
||||
let result = self.normalize_associated_types_in(span, &value);
|
||||
debug!("instantiate_type_scheme(value={:?}, substs={:?}) = {:?}", value, substs, result);
|
||||
result
|
||||
}
|
||||
|
||||
/// As `instantiate_type_scheme`, but for the bounds found in a
|
||||
/// generic type scheme.
|
||||
pub(in super::super) fn instantiate_bounds(
|
||||
&self,
|
||||
span: Span,
|
||||
def_id: DefId,
|
||||
substs: SubstsRef<'tcx>,
|
||||
) -> (ty::InstantiatedPredicates<'tcx>, Vec<Span>) {
|
||||
let bounds = self.tcx.predicates_of(def_id);
|
||||
let spans: Vec<Span> = bounds.predicates.iter().map(|(_, span)| *span).collect();
|
||||
let result = bounds.instantiate(self.tcx, substs);
|
||||
let result = self.normalize_associated_types_in(span, &result);
|
||||
debug!(
|
||||
"instantiate_bounds(bounds={:?}, substs={:?}) = {:?}, {:?}",
|
||||
bounds, substs, result, spans,
|
||||
);
|
||||
(result, spans)
|
||||
}
|
||||
|
||||
/// Replaces the opaque types from the given value with type variables,
|
||||
/// and records the `OpaqueTypeMap` for later use during writeback. See
|
||||
/// `InferCtxt::instantiate_opaque_types` for more details.
|
||||
pub(in super::super) fn instantiate_opaque_types_from_value<T: TypeFoldable<'tcx>>(
|
||||
&self,
|
||||
parent_id: hir::HirId,
|
||||
value: &T,
|
||||
value_span: Span,
|
||||
) -> T {
|
||||
let parent_def_id = self.tcx.hir().local_def_id(parent_id);
|
||||
debug!(
|
||||
"instantiate_opaque_types_from_value(parent_def_id={:?}, value={:?})",
|
||||
parent_def_id, value
|
||||
);
|
||||
|
||||
let (value, opaque_type_map) =
|
||||
self.register_infer_ok_obligations(self.instantiate_opaque_types(
|
||||
parent_def_id,
|
||||
self.body_id,
|
||||
self.param_env,
|
||||
value,
|
||||
value_span,
|
||||
));
|
||||
|
||||
let mut opaque_types = self.opaque_types.borrow_mut();
|
||||
let mut opaque_types_vars = self.opaque_types_vars.borrow_mut();
|
||||
for (ty, decl) in opaque_type_map {
|
||||
let _ = opaque_types.insert(ty, decl);
|
||||
let _ = opaque_types_vars.insert(decl.concrete_ty, decl.opaque_type);
|
||||
}
|
||||
|
||||
value
|
||||
}
|
||||
|
||||
pub(in super::super) fn normalize_associated_types_in<T>(&self, span: Span, value: &T) -> T
|
||||
where
|
||||
T: TypeFoldable<'tcx>,
|
||||
{
|
||||
self.inh.normalize_associated_types_in(span, self.body_id, self.param_env, value)
|
||||
}
|
||||
|
||||
pub(in super::super) fn normalize_associated_types_in_as_infer_ok<T>(
|
||||
&self,
|
||||
span: Span,
|
||||
value: &T,
|
||||
) -> InferOk<'tcx, T>
|
||||
where
|
||||
T: TypeFoldable<'tcx>,
|
||||
{
|
||||
self.inh.partially_normalize_associated_types_in(span, self.body_id, self.param_env, value)
|
||||
}
|
||||
|
||||
pub fn require_type_meets(
|
||||
&self,
|
||||
ty: Ty<'tcx>,
|
||||
span: Span,
|
||||
code: traits::ObligationCauseCode<'tcx>,
|
||||
def_id: DefId,
|
||||
) {
|
||||
self.register_bound(ty, def_id, traits::ObligationCause::new(span, self.body_id, code));
|
||||
}
|
||||
|
||||
pub fn require_type_is_sized(
|
||||
&self,
|
||||
ty: Ty<'tcx>,
|
||||
span: Span,
|
||||
code: traits::ObligationCauseCode<'tcx>,
|
||||
) {
|
||||
if !ty.references_error() {
|
||||
let lang_item = self.tcx.require_lang_item(LangItem::Sized, None);
|
||||
self.require_type_meets(ty, span, code, lang_item);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn require_type_is_sized_deferred(
|
||||
&self,
|
||||
ty: Ty<'tcx>,
|
||||
span: Span,
|
||||
code: traits::ObligationCauseCode<'tcx>,
|
||||
) {
|
||||
if !ty.references_error() {
|
||||
self.deferred_sized_obligations.borrow_mut().push((ty, span, code));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn register_bound(
|
||||
&self,
|
||||
ty: Ty<'tcx>,
|
||||
def_id: DefId,
|
||||
cause: traits::ObligationCause<'tcx>,
|
||||
) {
|
||||
if !ty.references_error() {
|
||||
self.fulfillment_cx.borrow_mut().register_bound(
|
||||
self,
|
||||
self.param_env,
|
||||
ty,
|
||||
def_id,
|
||||
cause,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_ty(&self, ast_t: &hir::Ty<'_>) -> Ty<'tcx> {
|
||||
let t = AstConv::ast_ty_to_ty(self, ast_t);
|
||||
self.register_wf_obligation(t.into(), ast_t.span, traits::MiscObligation);
|
||||
t
|
||||
}
|
||||
|
||||
pub fn to_ty_saving_user_provided_ty(&self, ast_ty: &hir::Ty<'_>) -> Ty<'tcx> {
|
||||
let ty = self.to_ty(ast_ty);
|
||||
debug!("to_ty_saving_user_provided_ty: ty={:?}", ty);
|
||||
|
||||
if Self::can_contain_user_lifetime_bounds(ty) {
|
||||
let c_ty = self.infcx.canonicalize_response(&UserType::Ty(ty));
|
||||
debug!("to_ty_saving_user_provided_ty: c_ty={:?}", c_ty);
|
||||
self.typeck_results.borrow_mut().user_provided_types_mut().insert(ast_ty.hir_id, c_ty);
|
||||
}
|
||||
|
||||
ty
|
||||
}
|
||||
|
||||
pub fn to_const(&self, ast_c: &hir::AnonConst) -> &'tcx ty::Const<'tcx> {
|
||||
let const_def_id = self.tcx.hir().local_def_id(ast_c.hir_id);
|
||||
let c = ty::Const::from_anon_const(self.tcx, const_def_id);
|
||||
self.register_wf_obligation(
|
||||
c.into(),
|
||||
self.tcx.hir().span(ast_c.hir_id),
|
||||
ObligationCauseCode::MiscObligation,
|
||||
);
|
||||
c
|
||||
}
|
||||
|
||||
pub fn const_arg_to_const(
|
||||
&self,
|
||||
ast_c: &hir::AnonConst,
|
||||
param_def_id: DefId,
|
||||
) -> &'tcx ty::Const<'tcx> {
|
||||
let const_def = ty::WithOptConstParam {
|
||||
did: self.tcx.hir().local_def_id(ast_c.hir_id),
|
||||
const_param_did: Some(param_def_id),
|
||||
};
|
||||
let c = ty::Const::from_opt_const_arg_anon_const(self.tcx, const_def);
|
||||
self.register_wf_obligation(
|
||||
c.into(),
|
||||
self.tcx.hir().span(ast_c.hir_id),
|
||||
ObligationCauseCode::MiscObligation,
|
||||
);
|
||||
c
|
||||
}
|
||||
|
||||
// If the type given by the user has free regions, save it for later, since
|
||||
// NLL would like to enforce those. Also pass in types that involve
|
||||
// projections, since those can resolve to `'static` bounds (modulo #54940,
|
||||
// which hopefully will be fixed by the time you see this comment, dear
|
||||
// reader, although I have my doubts). Also pass in types with inference
|
||||
// types, because they may be repeated. Other sorts of things are already
|
||||
// sufficiently enforced with erased regions. =)
|
||||
fn can_contain_user_lifetime_bounds<T>(t: T) -> bool
|
||||
where
|
||||
T: TypeFoldable<'tcx>,
|
||||
{
|
||||
t.has_free_regions() || t.has_projections() || t.has_infer_types()
|
||||
}
|
||||
|
||||
pub fn node_ty(&self, id: hir::HirId) -> Ty<'tcx> {
|
||||
match self.typeck_results.borrow().node_types().get(id) {
|
||||
Some(&t) => t,
|
||||
None if self.is_tainted_by_errors() => self.tcx.ty_error(),
|
||||
None => {
|
||||
bug!(
|
||||
"no type for node {}: {} in fcx {}",
|
||||
id,
|
||||
self.tcx.hir().node_to_string(id),
|
||||
self.tag()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Registers an obligation for checking later, during regionck, that `arg` is well-formed.
|
||||
pub fn register_wf_obligation(
|
||||
&self,
|
||||
arg: subst::GenericArg<'tcx>,
|
||||
span: Span,
|
||||
code: traits::ObligationCauseCode<'tcx>,
|
||||
) {
|
||||
// WF obligations never themselves fail, so no real need to give a detailed cause:
|
||||
let cause = traits::ObligationCause::new(span, self.body_id, code);
|
||||
self.register_predicate(traits::Obligation::new(
|
||||
cause,
|
||||
self.param_env,
|
||||
ty::PredicateAtom::WellFormed(arg).to_predicate(self.tcx),
|
||||
));
|
||||
}
|
||||
|
||||
/// Registers obligations that all `substs` are well-formed.
|
||||
pub fn add_wf_bounds(&self, substs: SubstsRef<'tcx>, expr: &hir::Expr<'_>) {
|
||||
for arg in substs.iter().filter(|arg| {
|
||||
matches!(arg.unpack(), GenericArgKind::Type(..) | GenericArgKind::Const(..))
|
||||
}) {
|
||||
self.register_wf_obligation(arg, expr.span, traits::MiscObligation);
|
||||
}
|
||||
}
|
||||
|
||||
/// Given a fully substituted set of bounds (`generic_bounds`), and the values with which each
|
||||
/// type/region parameter was instantiated (`substs`), creates and registers suitable
|
||||
/// trait/region obligations.
|
||||
///
|
||||
/// For example, if there is a function:
|
||||
///
|
||||
/// ```
|
||||
/// fn foo<'a,T:'a>(...)
|
||||
/// ```
|
||||
///
|
||||
/// and a reference:
|
||||
///
|
||||
/// ```
|
||||
/// let f = foo;
|
||||
/// ```
|
||||
///
|
||||
/// Then we will create a fresh region variable `'$0` and a fresh type variable `$1` for `'a`
|
||||
/// and `T`. This routine will add a region obligation `$1:'$0` and register it locally.
|
||||
pub fn add_obligations_for_parameters(
|
||||
&self,
|
||||
cause: traits::ObligationCause<'tcx>,
|
||||
predicates: ty::InstantiatedPredicates<'tcx>,
|
||||
) {
|
||||
assert!(!predicates.has_escaping_bound_vars());
|
||||
|
||||
debug!("add_obligations_for_parameters(predicates={:?})", predicates);
|
||||
|
||||
for obligation in traits::predicates_for_generics(cause, self.param_env, predicates) {
|
||||
self.register_predicate(obligation);
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME(arielb1): use this instead of field.ty everywhere
|
||||
// Only for fields! Returns <none> for methods>
|
||||
// Indifferent to privacy flags
|
||||
pub fn field_ty(
|
||||
&self,
|
||||
span: Span,
|
||||
field: &'tcx ty::FieldDef,
|
||||
substs: SubstsRef<'tcx>,
|
||||
) -> Ty<'tcx> {
|
||||
self.normalize_associated_types_in(span, &field.ty(self.tcx, substs))
|
||||
}
|
||||
|
||||
pub(in super::super) fn resolve_generator_interiors(&self, def_id: DefId) {
|
||||
let mut generators = self.deferred_generator_interiors.borrow_mut();
|
||||
for (body_id, interior, kind) in generators.drain(..) {
|
||||
self.select_obligations_where_possible(false, |_| {});
|
||||
crate::check::generator_interior::resolve_interior(
|
||||
self, def_id, body_id, interior, kind,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Tries to apply a fallback to `ty` if it is an unsolved variable.
|
||||
//
|
||||
// - Unconstrained ints are replaced with `i32`.
|
||||
//
|
||||
// - Unconstrained floats are replaced with with `f64`.
|
||||
//
|
||||
// - Non-numerics get replaced with `!` when `#![feature(never_type_fallback)]`
|
||||
// is enabled. Otherwise, they are replaced with `()`.
|
||||
//
|
||||
// Fallback becomes very dubious if we have encountered type-checking errors.
|
||||
// In that case, fallback to Error.
|
||||
// The return value indicates whether fallback has occurred.
|
||||
pub(in super::super) fn fallback_if_possible(&self, ty: Ty<'tcx>, mode: FallbackMode) -> bool {
|
||||
use rustc_middle::ty::error::UnconstrainedNumeric::Neither;
|
||||
use rustc_middle::ty::error::UnconstrainedNumeric::{UnconstrainedFloat, UnconstrainedInt};
|
||||
|
||||
assert!(ty.is_ty_infer());
|
||||
let fallback = match self.type_is_unconstrained_numeric(ty) {
|
||||
_ if self.is_tainted_by_errors() => self.tcx().ty_error(),
|
||||
UnconstrainedInt => self.tcx.types.i32,
|
||||
UnconstrainedFloat => self.tcx.types.f64,
|
||||
Neither if self.type_var_diverges(ty) => self.tcx.mk_diverging_default(),
|
||||
Neither => {
|
||||
// This type variable was created from the instantiation of an opaque
|
||||
// type. The fact that we're attempting to perform fallback for it
|
||||
// means that the function neither constrained it to a concrete
|
||||
// type, nor to the opaque type itself.
|
||||
//
|
||||
// For example, in this code:
|
||||
//
|
||||
//```
|
||||
// type MyType = impl Copy;
|
||||
// fn defining_use() -> MyType { true }
|
||||
// fn other_use() -> MyType { defining_use() }
|
||||
// ```
|
||||
//
|
||||
// `defining_use` will constrain the instantiated inference
|
||||
// variable to `bool`, while `other_use` will constrain
|
||||
// the instantiated inference variable to `MyType`.
|
||||
//
|
||||
// When we process opaque types during writeback, we
|
||||
// will handle cases like `other_use`, and not count
|
||||
// them as defining usages
|
||||
//
|
||||
// However, we also need to handle cases like this:
|
||||
//
|
||||
// ```rust
|
||||
// pub type Foo = impl Copy;
|
||||
// fn produce() -> Option<Foo> {
|
||||
// None
|
||||
// }
|
||||
// ```
|
||||
//
|
||||
// In the above snippet, the inference variable created by
|
||||
// instantiating `Option<Foo>` will be completely unconstrained.
|
||||
// We treat this as a non-defining use by making the inference
|
||||
// variable fall back to the opaque type itself.
|
||||
if let FallbackMode::All = mode {
|
||||
if let Some(opaque_ty) = self.opaque_types_vars.borrow().get(ty) {
|
||||
debug!(
|
||||
"fallback_if_possible: falling back opaque type var {:?} to {:?}",
|
||||
ty, opaque_ty
|
||||
);
|
||||
*opaque_ty
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
};
|
||||
debug!("fallback_if_possible: defaulting `{:?}` to `{:?}`", ty, fallback);
|
||||
self.demand_eqtype(rustc_span::DUMMY_SP, ty, fallback);
|
||||
true
|
||||
}
|
||||
|
||||
pub(in super::super) fn select_all_obligations_or_error(&self) {
|
||||
debug!("select_all_obligations_or_error");
|
||||
if let Err(errors) = self.fulfillment_cx.borrow_mut().select_all_or_error(&self) {
|
||||
self.report_fulfillment_errors(&errors, self.inh.body_id, false);
|
||||
}
|
||||
}
|
||||
|
||||
/// Select as many obligations as we can at present.
|
||||
pub(in super::super) fn select_obligations_where_possible(
|
||||
&self,
|
||||
fallback_has_occurred: bool,
|
||||
mutate_fullfillment_errors: impl Fn(&mut Vec<traits::FulfillmentError<'tcx>>),
|
||||
) {
|
||||
let result = self.fulfillment_cx.borrow_mut().select_where_possible(self);
|
||||
if let Err(mut errors) = result {
|
||||
mutate_fullfillment_errors(&mut errors);
|
||||
self.report_fulfillment_errors(&errors, self.inh.body_id, fallback_has_occurred);
|
||||
}
|
||||
}
|
||||
|
||||
/// For the overloaded place expressions (`*x`, `x[3]`), the trait
|
||||
/// returns a type of `&T`, but the actual type we assign to the
|
||||
/// *expression* is `T`. So this function just peels off the return
|
||||
/// type by one layer to yield `T`.
|
||||
pub(in super::super) fn make_overloaded_place_return_type(
|
||||
&self,
|
||||
method: MethodCallee<'tcx>,
|
||||
) -> ty::TypeAndMut<'tcx> {
|
||||
// extract method return type, which will be &T;
|
||||
let ret_ty = method.sig.output();
|
||||
|
||||
// method returns &T, but the type as visible to user is T, so deref
|
||||
ret_ty.builtin_deref(true).unwrap()
|
||||
}
|
||||
|
||||
fn self_type_matches_expected_vid(
|
||||
&self,
|
||||
trait_ref: ty::PolyTraitRef<'tcx>,
|
||||
expected_vid: ty::TyVid,
|
||||
) -> bool {
|
||||
let self_ty = self.shallow_resolve(trait_ref.skip_binder().self_ty());
|
||||
debug!(
|
||||
"self_type_matches_expected_vid(trait_ref={:?}, self_ty={:?}, expected_vid={:?})",
|
||||
trait_ref, self_ty, expected_vid
|
||||
);
|
||||
match *self_ty.kind() {
|
||||
ty::Infer(ty::TyVar(found_vid)) => {
|
||||
// FIXME: consider using `sub_root_var` here so we
|
||||
// can see through subtyping.
|
||||
let found_vid = self.root_var(found_vid);
|
||||
debug!("self_type_matches_expected_vid - found_vid={:?}", found_vid);
|
||||
expected_vid == found_vid
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub(in super::super) fn obligations_for_self_ty<'b>(
|
||||
&'b self,
|
||||
self_ty: ty::TyVid,
|
||||
) -> impl Iterator<Item = (ty::PolyTraitRef<'tcx>, traits::PredicateObligation<'tcx>)>
|
||||
+ Captures<'tcx>
|
||||
+ 'b {
|
||||
// FIXME: consider using `sub_root_var` here so we
|
||||
// can see through subtyping.
|
||||
let ty_var_root = self.root_var(self_ty);
|
||||
debug!(
|
||||
"obligations_for_self_ty: self_ty={:?} ty_var_root={:?} pending_obligations={:?}",
|
||||
self_ty,
|
||||
ty_var_root,
|
||||
self.fulfillment_cx.borrow().pending_obligations()
|
||||
);
|
||||
|
||||
self.fulfillment_cx
|
||||
.borrow()
|
||||
.pending_obligations()
|
||||
.into_iter()
|
||||
.filter_map(move |obligation| {
|
||||
match obligation.predicate.skip_binders() {
|
||||
ty::PredicateAtom::Projection(data) => {
|
||||
Some((ty::Binder::bind(data).to_poly_trait_ref(self.tcx), obligation))
|
||||
}
|
||||
ty::PredicateAtom::Trait(data, _) => {
|
||||
Some((ty::Binder::bind(data).to_poly_trait_ref(), obligation))
|
||||
}
|
||||
ty::PredicateAtom::Subtype(..) => None,
|
||||
ty::PredicateAtom::RegionOutlives(..) => None,
|
||||
ty::PredicateAtom::TypeOutlives(..) => None,
|
||||
ty::PredicateAtom::WellFormed(..) => None,
|
||||
ty::PredicateAtom::ObjectSafe(..) => None,
|
||||
ty::PredicateAtom::ConstEvaluatable(..) => None,
|
||||
ty::PredicateAtom::ConstEquate(..) => None,
|
||||
// N.B., this predicate is created by breaking down a
|
||||
// `ClosureType: FnFoo()` predicate, where
|
||||
// `ClosureType` represents some `Closure`. It can't
|
||||
// possibly be referring to the current closure,
|
||||
// because we haven't produced the `Closure` for
|
||||
// this closure yet; this is exactly why the other
|
||||
// code is looking for a self type of a unresolved
|
||||
// inference variable.
|
||||
ty::PredicateAtom::ClosureKind(..) => None,
|
||||
ty::PredicateAtom::TypeWellFormedFromEnv(..) => None,
|
||||
}
|
||||
})
|
||||
.filter(move |(tr, _)| self.self_type_matches_expected_vid(*tr, ty_var_root))
|
||||
}
|
||||
|
||||
pub(in super::super) fn type_var_is_sized(&self, self_ty: ty::TyVid) -> bool {
|
||||
self.obligations_for_self_ty(self_ty)
|
||||
.any(|(tr, _)| Some(tr.def_id()) == self.tcx.lang_items().sized_trait())
|
||||
}
|
||||
|
||||
pub(in super::super) fn err_args(&self, len: usize) -> Vec<Ty<'tcx>> {
|
||||
vec![self.tcx.ty_error(); len]
|
||||
}
|
||||
|
||||
/// Unifies the output type with the expected type early, for more coercions
|
||||
/// and forward type information on the input expressions.
|
||||
pub(in super::super) fn expected_inputs_for_expected_output(
|
||||
&self,
|
||||
call_span: Span,
|
||||
expected_ret: Expectation<'tcx>,
|
||||
formal_ret: Ty<'tcx>,
|
||||
formal_args: &[Ty<'tcx>],
|
||||
) -> Vec<Ty<'tcx>> {
|
||||
let formal_ret = self.resolve_vars_with_obligations(formal_ret);
|
||||
let ret_ty = match expected_ret.only_has_type(self) {
|
||||
Some(ret) => ret,
|
||||
None => return Vec::new(),
|
||||
};
|
||||
let expect_args = self
|
||||
.fudge_inference_if_ok(|| {
|
||||
// Attempt to apply a subtyping relationship between the formal
|
||||
// return type (likely containing type variables if the function
|
||||
// is polymorphic) and the expected return type.
|
||||
// No argument expectations are produced if unification fails.
|
||||
let origin = self.misc(call_span);
|
||||
let ures = self.at(&origin, self.param_env).sup(ret_ty, &formal_ret);
|
||||
|
||||
// FIXME(#27336) can't use ? here, Try::from_error doesn't default
|
||||
// to identity so the resulting type is not constrained.
|
||||
match ures {
|
||||
Ok(ok) => {
|
||||
// Process any obligations locally as much as
|
||||
// we can. We don't care if some things turn
|
||||
// out unconstrained or ambiguous, as we're
|
||||
// just trying to get hints here.
|
||||
self.save_and_restore_in_snapshot_flag(|_| {
|
||||
let mut fulfill = TraitEngine::new(self.tcx);
|
||||
for obligation in ok.obligations {
|
||||
fulfill.register_predicate_obligation(self, obligation);
|
||||
}
|
||||
fulfill.select_where_possible(self)
|
||||
})
|
||||
.map_err(|_| ())?;
|
||||
}
|
||||
Err(_) => return Err(()),
|
||||
}
|
||||
|
||||
// Record all the argument types, with the substitutions
|
||||
// produced from the above subtyping unification.
|
||||
Ok(formal_args.iter().map(|ty| self.resolve_vars_if_possible(ty)).collect())
|
||||
})
|
||||
.unwrap_or_default();
|
||||
debug!(
|
||||
"expected_inputs_for_expected_output(formal={:?} -> {:?}, expected={:?} -> {:?})",
|
||||
formal_args, formal_ret, expect_args, expected_ret
|
||||
);
|
||||
expect_args
|
||||
}
|
||||
|
||||
pub(in super::super) fn resolve_lang_item_path(
|
||||
&self,
|
||||
lang_item: hir::LangItem,
|
||||
span: Span,
|
||||
hir_id: hir::HirId,
|
||||
) -> (Res, Ty<'tcx>) {
|
||||
let def_id = self.tcx.require_lang_item(lang_item, Some(span));
|
||||
let def_kind = self.tcx.def_kind(def_id);
|
||||
|
||||
let item_ty = if let DefKind::Variant = def_kind {
|
||||
self.tcx.type_of(self.tcx.parent(def_id).expect("variant w/out parent"))
|
||||
} else {
|
||||
self.tcx.type_of(def_id)
|
||||
};
|
||||
let substs = self.infcx.fresh_substs_for_item(span, def_id);
|
||||
let ty = item_ty.subst(self.tcx, substs);
|
||||
|
||||
self.write_resolution(hir_id, Ok((def_kind, def_id)));
|
||||
self.add_required_obligations(span, def_id, &substs);
|
||||
(Res::Def(def_kind, def_id), ty)
|
||||
}
|
||||
|
||||
/// Resolves an associated value path into a base type and associated constant, or method
|
||||
/// resolution. The newly resolved definition is written into `type_dependent_defs`.
|
||||
pub fn resolve_ty_and_res_ufcs<'b>(
|
||||
&self,
|
||||
qpath: &'b QPath<'b>,
|
||||
hir_id: hir::HirId,
|
||||
span: Span,
|
||||
) -> (Res, Option<Ty<'tcx>>, &'b [hir::PathSegment<'b>]) {
|
||||
debug!("resolve_ty_and_res_ufcs: qpath={:?} hir_id={:?} span={:?}", qpath, hir_id, span);
|
||||
let (ty, qself, item_segment) = match *qpath {
|
||||
QPath::Resolved(ref opt_qself, ref path) => {
|
||||
return (
|
||||
path.res,
|
||||
opt_qself.as_ref().map(|qself| self.to_ty(qself)),
|
||||
&path.segments[..],
|
||||
);
|
||||
}
|
||||
QPath::TypeRelative(ref qself, ref segment) => (self.to_ty(qself), qself, segment),
|
||||
QPath::LangItem(..) => bug!("`resolve_ty_and_res_ufcs` called on `LangItem`"),
|
||||
};
|
||||
if let Some(&cached_result) = self.typeck_results.borrow().type_dependent_defs().get(hir_id)
|
||||
{
|
||||
// Return directly on cache hit. This is useful to avoid doubly reporting
|
||||
// errors with default match binding modes. See #44614.
|
||||
let def =
|
||||
cached_result.map(|(kind, def_id)| Res::Def(kind, def_id)).unwrap_or(Res::Err);
|
||||
return (def, Some(ty), slice::from_ref(&**item_segment));
|
||||
}
|
||||
let item_name = item_segment.ident;
|
||||
let result = self.resolve_ufcs(span, item_name, ty, hir_id).or_else(|error| {
|
||||
let result = match error {
|
||||
method::MethodError::PrivateMatch(kind, def_id, _) => Ok((kind, def_id)),
|
||||
_ => Err(ErrorReported),
|
||||
};
|
||||
if item_name.name != kw::Invalid {
|
||||
if let Some(mut e) = self.report_method_error(
|
||||
span,
|
||||
ty,
|
||||
item_name,
|
||||
SelfSource::QPath(qself),
|
||||
error,
|
||||
None,
|
||||
) {
|
||||
e.emit();
|
||||
}
|
||||
}
|
||||
result
|
||||
});
|
||||
|
||||
// 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),
|
||||
Some(ty),
|
||||
slice::from_ref(&**item_segment),
|
||||
)
|
||||
}
|
||||
|
||||
/// Given a function `Node`, return its `FnDecl` if it exists, or `None` otherwise.
|
||||
pub(in super::super) fn get_node_fn_decl(
|
||||
&self,
|
||||
node: Node<'tcx>,
|
||||
) -> Option<(&'tcx hir::FnDecl<'tcx>, Ident, bool)> {
|
||||
match node {
|
||||
Node::Item(&hir::Item { ident, kind: hir::ItemKind::Fn(ref sig, ..), .. }) => {
|
||||
// This is less than ideal, it will not suggest a return type span on any
|
||||
// method called `main`, regardless of whether it is actually the entry point,
|
||||
// but it will still present it as the reason for the expected type.
|
||||
Some((&sig.decl, ident, ident.name != sym::main))
|
||||
}
|
||||
Node::TraitItem(&hir::TraitItem {
|
||||
ident,
|
||||
kind: hir::TraitItemKind::Fn(ref sig, ..),
|
||||
..
|
||||
}) => Some((&sig.decl, ident, true)),
|
||||
Node::ImplItem(&hir::ImplItem {
|
||||
ident,
|
||||
kind: hir::ImplItemKind::Fn(ref sig, ..),
|
||||
..
|
||||
}) => Some((&sig.decl, ident, false)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Given a `HirId`, return the `FnDecl` of the method it is enclosed by and whether a
|
||||
/// suggestion can be made, `None` otherwise.
|
||||
pub fn get_fn_decl(&self, blk_id: hir::HirId) -> Option<(&'tcx hir::FnDecl<'tcx>, bool)> {
|
||||
// Get enclosing Fn, if it is a function or a trait method, unless there's a `loop` or
|
||||
// `while` before reaching it, as block tail returns are not available in them.
|
||||
self.tcx.hir().get_return_block(blk_id).and_then(|blk_id| {
|
||||
let parent = self.tcx.hir().get(blk_id);
|
||||
self.get_node_fn_decl(parent).map(|(fn_decl, _, is_main)| (fn_decl, is_main))
|
||||
})
|
||||
}
|
||||
|
||||
pub(in super::super) fn note_internal_mutation_in_method(
|
||||
&self,
|
||||
err: &mut DiagnosticBuilder<'_>,
|
||||
expr: &hir::Expr<'_>,
|
||||
expected: Ty<'tcx>,
|
||||
found: Ty<'tcx>,
|
||||
) {
|
||||
if found != self.tcx.types.unit {
|
||||
return;
|
||||
}
|
||||
if let ExprKind::MethodCall(path_segment, _, [rcvr, ..], _) = expr.kind {
|
||||
if self
|
||||
.typeck_results
|
||||
.borrow()
|
||||
.expr_ty_adjusted_opt(rcvr)
|
||||
.map_or(true, |ty| expected.peel_refs() != ty.peel_refs())
|
||||
{
|
||||
return;
|
||||
}
|
||||
let mut sp = MultiSpan::from_span(path_segment.ident.span);
|
||||
sp.push_span_label(
|
||||
path_segment.ident.span,
|
||||
format!(
|
||||
"this call modifies {} in-place",
|
||||
match rcvr.kind {
|
||||
ExprKind::Path(QPath::Resolved(
|
||||
None,
|
||||
hir::Path { segments: [segment], .. },
|
||||
)) => format!("`{}`", segment.ident),
|
||||
_ => "its receiver".to_string(),
|
||||
}
|
||||
),
|
||||
);
|
||||
sp.push_span_label(
|
||||
rcvr.span,
|
||||
"you probably want to use this value after calling the method...".to_string(),
|
||||
);
|
||||
err.span_note(
|
||||
sp,
|
||||
&format!("method `{}` modifies its receiver in-place", path_segment.ident),
|
||||
);
|
||||
err.note(&format!("...instead of the `()` output of method `{}`", path_segment.ident));
|
||||
}
|
||||
}
|
||||
|
||||
pub(in super::super) fn note_need_for_fn_pointer(
|
||||
&self,
|
||||
err: &mut DiagnosticBuilder<'_>,
|
||||
expected: Ty<'tcx>,
|
||||
found: Ty<'tcx>,
|
||||
) {
|
||||
let (sig, did, substs) = match (&expected.kind(), &found.kind()) {
|
||||
(ty::FnDef(did1, substs1), ty::FnDef(did2, substs2)) => {
|
||||
let sig1 = self.tcx.fn_sig(*did1).subst(self.tcx, substs1);
|
||||
let sig2 = self.tcx.fn_sig(*did2).subst(self.tcx, substs2);
|
||||
if sig1 != sig2 {
|
||||
return;
|
||||
}
|
||||
err.note(
|
||||
"different `fn` items always have unique types, even if their signatures are \
|
||||
the same",
|
||||
);
|
||||
(sig1, *did1, substs1)
|
||||
}
|
||||
(ty::FnDef(did, substs), ty::FnPtr(sig2)) => {
|
||||
let sig1 = self.tcx.fn_sig(*did).subst(self.tcx, substs);
|
||||
if sig1 != *sig2 {
|
||||
return;
|
||||
}
|
||||
(sig1, *did, substs)
|
||||
}
|
||||
_ => return,
|
||||
};
|
||||
err.help(&format!("change the expected type to be function pointer `{}`", sig));
|
||||
err.help(&format!(
|
||||
"if the expected type is due to type inference, cast the expected `fn` to a function \
|
||||
pointer: `{} as {}`",
|
||||
self.tcx.def_path_str_with_substs(did, substs),
|
||||
sig
|
||||
));
|
||||
}
|
||||
|
||||
pub(in super::super) fn could_remove_semicolon(
|
||||
&self,
|
||||
blk: &'tcx hir::Block<'tcx>,
|
||||
expected_ty: Ty<'tcx>,
|
||||
) -> Option<Span> {
|
||||
// Be helpful when the user wrote `{... expr;}` and
|
||||
// taking the `;` off is enough to fix the error.
|
||||
let last_stmt = blk.stmts.last()?;
|
||||
let last_expr = match last_stmt.kind {
|
||||
hir::StmtKind::Semi(ref e) => e,
|
||||
_ => return None,
|
||||
};
|
||||
let last_expr_ty = self.node_ty(last_expr.hir_id);
|
||||
if matches!(last_expr_ty.kind(), ty::Error(_))
|
||||
|| self.can_sub(self.param_env, last_expr_ty, expected_ty).is_err()
|
||||
{
|
||||
return None;
|
||||
}
|
||||
let original_span = original_sp(last_stmt.span, blk.span);
|
||||
Some(original_span.with_lo(original_span.hi() - BytePos(1)))
|
||||
}
|
||||
|
||||
// Instantiates the given path, which must refer to an item with the given
|
||||
// number of type parameters and type.
|
||||
pub fn instantiate_value_path(
|
||||
&self,
|
||||
segments: &[hir::PathSegment<'_>],
|
||||
self_ty: Option<Ty<'tcx>>,
|
||||
res: Res,
|
||||
span: Span,
|
||||
hir_id: hir::HirId,
|
||||
) -> (Ty<'tcx>, Res) {
|
||||
debug!(
|
||||
"instantiate_value_path(segments={:?}, self_ty={:?}, res={:?}, hir_id={})",
|
||||
segments, self_ty, res, hir_id,
|
||||
);
|
||||
|
||||
let tcx = self.tcx;
|
||||
|
||||
let path_segs = match res {
|
||||
Res::Local(_) | Res::SelfCtor(_) => vec![],
|
||||
Res::Def(kind, def_id) => {
|
||||
AstConv::def_ids_for_value_path_segments(self, segments, self_ty, kind, def_id)
|
||||
}
|
||||
_ => bug!("instantiate_value_path on {:?}", res),
|
||||
};
|
||||
|
||||
let mut user_self_ty = None;
|
||||
let mut is_alias_variant_ctor = false;
|
||||
match res {
|
||||
Res::Def(DefKind::Ctor(CtorOf::Variant, _), _) => {
|
||||
if let Some(self_ty) = self_ty {
|
||||
let adt_def = self_ty.ty_adt_def().unwrap();
|
||||
user_self_ty = Some(UserSelfTy { impl_def_id: adt_def.did, self_ty });
|
||||
is_alias_variant_ctor = true;
|
||||
}
|
||||
}
|
||||
Res::Def(DefKind::AssocFn | DefKind::AssocConst, def_id) => {
|
||||
let container = tcx.associated_item(def_id).container;
|
||||
debug!("instantiate_value_path: def_id={:?} container={:?}", def_id, container);
|
||||
match container {
|
||||
ty::TraitContainer(trait_did) => {
|
||||
callee::check_legal_trait_for_method_call(tcx, span, None, trait_did)
|
||||
}
|
||||
ty::ImplContainer(impl_def_id) => {
|
||||
if segments.len() == 1 {
|
||||
// `<T>::assoc` will end up here, and so
|
||||
// can `T::assoc`. It this came from an
|
||||
// inherent impl, we need to record the
|
||||
// `T` for posterity (see `UserSelfTy` for
|
||||
// details).
|
||||
let self_ty = self_ty.expect("UFCS sugared assoc missing Self");
|
||||
user_self_ty = Some(UserSelfTy { impl_def_id, self_ty });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
// Now that we have categorized what space the parameters for each
|
||||
// segment belong to, let's sort out the parameters that the user
|
||||
// provided (if any) into their appropriate spaces. We'll also report
|
||||
// errors if type parameters are provided in an inappropriate place.
|
||||
|
||||
let generic_segs: FxHashSet<_> = path_segs.iter().map(|PathSeg(_, index)| index).collect();
|
||||
let generics_has_err = AstConv::prohibit_generics(
|
||||
self,
|
||||
segments.iter().enumerate().filter_map(|(index, seg)| {
|
||||
if !generic_segs.contains(&index) || is_alias_variant_ctor {
|
||||
Some(seg)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
if let Res::Local(hid) = res {
|
||||
let ty = self.local_ty(span, hid).decl_ty;
|
||||
let ty = self.normalize_associated_types_in(span, &ty);
|
||||
self.write_ty(hir_id, ty);
|
||||
return (ty, res);
|
||||
}
|
||||
|
||||
if generics_has_err {
|
||||
// Don't try to infer type parameters when prohibited generic arguments were given.
|
||||
user_self_ty = None;
|
||||
}
|
||||
|
||||
// Now we have to compare the types that the user *actually*
|
||||
// provided against the types that were *expected*. If the user
|
||||
// did not provide any types, then we want to substitute inference
|
||||
// variables. If the user provided some types, we may still need
|
||||
// to add defaults. If the user provided *too many* types, that's
|
||||
// a problem.
|
||||
|
||||
let mut infer_args_for_err = FxHashSet::default();
|
||||
for &PathSeg(def_id, index) in &path_segs {
|
||||
let seg = &segments[index];
|
||||
let generics = tcx.generics_of(def_id);
|
||||
// Argument-position `impl Trait` is treated as a normal generic
|
||||
// parameter internally, but we don't allow users to specify the
|
||||
// parameter's value explicitly, so we have to do some error-
|
||||
// checking here.
|
||||
if let GenericArgCountResult {
|
||||
correct: Err(GenericArgCountMismatch { reported: Some(ErrorReported), .. }),
|
||||
..
|
||||
} = AstConv::check_generic_arg_count_for_call(
|
||||
tcx, span, &generics, &seg, false, // `is_method_call`
|
||||
) {
|
||||
infer_args_for_err.insert(index);
|
||||
self.set_tainted_by_errors(); // See issue #53251.
|
||||
}
|
||||
}
|
||||
|
||||
let has_self = path_segs
|
||||
.last()
|
||||
.map(|PathSeg(def_id, _)| tcx.generics_of(*def_id).has_self)
|
||||
.unwrap_or(false);
|
||||
|
||||
let (res, self_ctor_substs) = if let Res::SelfCtor(impl_def_id) = res {
|
||||
let ty = self.normalize_ty(span, tcx.at(span).type_of(impl_def_id));
|
||||
match *ty.kind() {
|
||||
ty::Adt(adt_def, substs) if adt_def.has_ctor() => {
|
||||
let variant = adt_def.non_enum_variant();
|
||||
let ctor_def_id = variant.ctor_def_id.unwrap();
|
||||
(
|
||||
Res::Def(DefKind::Ctor(CtorOf::Struct, variant.ctor_kind), ctor_def_id),
|
||||
Some(substs),
|
||||
)
|
||||
}
|
||||
_ => {
|
||||
let mut err = tcx.sess.struct_span_err(
|
||||
span,
|
||||
"the `Self` constructor can only be used with tuple or unit structs",
|
||||
);
|
||||
if let Some(adt_def) = ty.ty_adt_def() {
|
||||
match adt_def.adt_kind() {
|
||||
AdtKind::Enum => {
|
||||
err.help("did you mean to use one of the enum's variants?");
|
||||
}
|
||||
AdtKind::Struct | AdtKind::Union => {
|
||||
err.span_suggestion(
|
||||
span,
|
||||
"use curly brackets",
|
||||
String::from("Self { /* fields */ }"),
|
||||
Applicability::HasPlaceholders,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
err.emit();
|
||||
|
||||
return (tcx.ty_error(), res);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
(res, None)
|
||||
};
|
||||
let def_id = res.def_id();
|
||||
|
||||
// The things we are substituting into the type should not contain
|
||||
// escaping late-bound regions, and nor should the base type scheme.
|
||||
let ty = tcx.type_of(def_id);
|
||||
|
||||
let arg_count = GenericArgCountResult {
|
||||
explicit_late_bound: ExplicitLateBound::No,
|
||||
correct: if infer_args_for_err.is_empty() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(GenericArgCountMismatch::default())
|
||||
},
|
||||
};
|
||||
|
||||
let substs = self_ctor_substs.unwrap_or_else(|| {
|
||||
AstConv::create_substs_for_generic_args(
|
||||
tcx,
|
||||
def_id,
|
||||
&[][..],
|
||||
has_self,
|
||||
self_ty,
|
||||
arg_count,
|
||||
// Provide the generic args, and whether types should be inferred.
|
||||
|def_id| {
|
||||
if let Some(&PathSeg(_, index)) =
|
||||
path_segs.iter().find(|&PathSeg(did, _)| *did == def_id)
|
||||
{
|
||||
// If we've encountered an `impl Trait`-related error, we're just
|
||||
// going to infer the arguments for better error messages.
|
||||
if !infer_args_for_err.contains(&index) {
|
||||
// Check whether the user has provided generic arguments.
|
||||
if let Some(ref data) = segments[index].args {
|
||||
return (Some(data), segments[index].infer_args);
|
||||
}
|
||||
}
|
||||
return (None, segments[index].infer_args);
|
||||
}
|
||||
|
||||
(None, true)
|
||||
},
|
||||
// Provide substitutions for parameters for which (valid) arguments have been provided.
|
||||
|param, arg| match (¶m.kind, arg) {
|
||||
(GenericParamDefKind::Lifetime, GenericArg::Lifetime(lt)) => {
|
||||
AstConv::ast_region_to_region(self, lt, Some(param)).into()
|
||||
}
|
||||
(GenericParamDefKind::Type { .. }, GenericArg::Type(ty)) => {
|
||||
self.to_ty(ty).into()
|
||||
}
|
||||
(GenericParamDefKind::Const, GenericArg::Const(ct)) => {
|
||||
self.const_arg_to_const(&ct.value, param.def_id).into()
|
||||
}
|
||||
_ => unreachable!(),
|
||||
},
|
||||
// Provide substitutions for parameters for which arguments are inferred.
|
||||
|substs, param, infer_args| {
|
||||
match param.kind {
|
||||
GenericParamDefKind::Lifetime => {
|
||||
self.re_infer(Some(param), span).unwrap().into()
|
||||
}
|
||||
GenericParamDefKind::Type { has_default, .. } => {
|
||||
if !infer_args && has_default {
|
||||
// If we have a default, then we it doesn't matter that we're not
|
||||
// inferring the type arguments: we provide the default where any
|
||||
// is missing.
|
||||
let default = tcx.type_of(param.def_id);
|
||||
self.normalize_ty(
|
||||
span,
|
||||
default.subst_spanned(tcx, substs.unwrap(), Some(span)),
|
||||
)
|
||||
.into()
|
||||
} else {
|
||||
// If no type arguments were provided, we have to infer them.
|
||||
// This case also occurs as a result of some malformed input, e.g.
|
||||
// a lifetime argument being given instead of a type parameter.
|
||||
// Using inference instead of `Error` gives better error messages.
|
||||
self.var_for_def(span, param)
|
||||
}
|
||||
}
|
||||
GenericParamDefKind::Const => {
|
||||
// FIXME(const_generics:defaults)
|
||||
// No const parameters were provided, we have to infer them.
|
||||
self.var_for_def(span, param)
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
});
|
||||
assert!(!substs.has_escaping_bound_vars());
|
||||
assert!(!ty.has_escaping_bound_vars());
|
||||
|
||||
// First, store the "user substs" for later.
|
||||
self.write_user_type_annotation_from_substs(hir_id, def_id, substs, user_self_ty);
|
||||
|
||||
self.add_required_obligations(span, def_id, &substs);
|
||||
|
||||
// Substitute the values for the type parameters into the type of
|
||||
// the referenced item.
|
||||
let ty_substituted = self.instantiate_type_scheme(span, &substs, &ty);
|
||||
|
||||
if let Some(UserSelfTy { impl_def_id, self_ty }) = user_self_ty {
|
||||
// In the case of `Foo<T>::method` and `<Foo<T>>::method`, if `method`
|
||||
// is inherent, there is no `Self` parameter; instead, the impl needs
|
||||
// type parameters, which we can infer by unifying the provided `Self`
|
||||
// with the substituted impl type.
|
||||
// This also occurs for an enum variant on a type alias.
|
||||
let ty = tcx.type_of(impl_def_id);
|
||||
|
||||
let impl_ty = self.instantiate_type_scheme(span, &substs, &ty);
|
||||
match self.at(&self.misc(span), self.param_env).sup(impl_ty, self_ty) {
|
||||
Ok(ok) => self.register_infer_ok_obligations(ok),
|
||||
Err(_) => {
|
||||
self.tcx.sess.delay_span_bug(
|
||||
span,
|
||||
&format!(
|
||||
"instantiate_value_path: (UFCS) {:?} was a subtype of {:?} but now is not?",
|
||||
self_ty,
|
||||
impl_ty,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.check_rustc_args_require_const(def_id, hir_id, span);
|
||||
|
||||
debug!("instantiate_value_path: type of {:?} is {:?}", hir_id, ty_substituted);
|
||||
self.write_substs(hir_id, substs);
|
||||
|
||||
(ty_substituted, res)
|
||||
}
|
||||
|
||||
/// Add all the obligations that are required, substituting and normalized appropriately.
|
||||
fn add_required_obligations(&self, span: Span, def_id: DefId, substs: &SubstsRef<'tcx>) {
|
||||
let (bounds, spans) = self.instantiate_bounds(span, def_id, &substs);
|
||||
|
||||
for (i, mut obligation) in traits::predicates_for_generics(
|
||||
traits::ObligationCause::new(span, self.body_id, traits::ItemObligation(def_id)),
|
||||
self.param_env,
|
||||
bounds,
|
||||
)
|
||||
.enumerate()
|
||||
{
|
||||
// This makes the error point at the bound, but we want to point at the argument
|
||||
if let Some(span) = spans.get(i) {
|
||||
obligation.cause.make_mut().code = traits::BindingObligation(def_id, *span);
|
||||
}
|
||||
self.register_predicate(obligation);
|
||||
}
|
||||
}
|
||||
|
||||
/// Resolves `typ` by a single level if `typ` is a type variable.
|
||||
/// If no resolution is possible, then an error is reported.
|
||||
/// Numeric inference variables may be left unresolved.
|
||||
pub fn structurally_resolved_type(&self, sp: Span, ty: Ty<'tcx>) -> Ty<'tcx> {
|
||||
let ty = self.resolve_vars_with_obligations(ty);
|
||||
if !ty.is_ty_var() {
|
||||
ty
|
||||
} else {
|
||||
if !self.is_tainted_by_errors() {
|
||||
self.emit_inference_failure_err((**self).body_id, sp, ty.into(), E0282)
|
||||
.note("type must be known at this point")
|
||||
.emit();
|
||||
}
|
||||
let err = self.tcx.ty_error();
|
||||
self.demand_suptype(sp, err, ty);
|
||||
err
|
||||
}
|
||||
}
|
||||
|
||||
pub(in super::super) fn with_breakable_ctxt<F: FnOnce() -> R, R>(
|
||||
&self,
|
||||
id: hir::HirId,
|
||||
ctxt: BreakableCtxt<'tcx>,
|
||||
f: F,
|
||||
) -> (BreakableCtxt<'tcx>, R) {
|
||||
let index;
|
||||
{
|
||||
let mut enclosing_breakables = self.enclosing_breakables.borrow_mut();
|
||||
index = enclosing_breakables.stack.len();
|
||||
enclosing_breakables.by_id.insert(id, index);
|
||||
enclosing_breakables.stack.push(ctxt);
|
||||
}
|
||||
let result = f();
|
||||
let ctxt = {
|
||||
let mut enclosing_breakables = self.enclosing_breakables.borrow_mut();
|
||||
debug_assert!(enclosing_breakables.stack.len() == index + 1);
|
||||
enclosing_breakables.by_id.remove(&id).expect("missing breakable context");
|
||||
enclosing_breakables.stack.pop().expect("missing breakable context")
|
||||
};
|
||||
(ctxt, result)
|
||||
}
|
||||
|
||||
/// Instantiate a QueryResponse in a probe context, without a
|
||||
/// good ObligationCause.
|
||||
pub(in super::super) fn probe_instantiate_query_response(
|
||||
&self,
|
||||
span: Span,
|
||||
original_values: &OriginalQueryValues<'tcx>,
|
||||
query_result: &Canonical<'tcx, QueryResponse<'tcx, Ty<'tcx>>>,
|
||||
) -> InferResult<'tcx, Ty<'tcx>> {
|
||||
self.instantiate_query_response_and_region_obligations(
|
||||
&traits::ObligationCause::misc(span, self.body_id),
|
||||
self.param_env,
|
||||
original_values,
|
||||
query_result,
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns `true` if an expression is contained inside the LHS of an assignment expression.
|
||||
pub(in super::super) fn expr_in_place(&self, mut expr_id: hir::HirId) -> bool {
|
||||
let mut contained_in_place = false;
|
||||
|
||||
while let hir::Node::Expr(parent_expr) =
|
||||
self.tcx.hir().get(self.tcx.hir().get_parent_node(expr_id))
|
||||
{
|
||||
match &parent_expr.kind {
|
||||
hir::ExprKind::Assign(lhs, ..) | hir::ExprKind::AssignOp(_, lhs, ..) => {
|
||||
if lhs.hir_id == expr_id {
|
||||
contained_in_place = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
expr_id = parent_expr.hir_id;
|
||||
}
|
||||
|
||||
contained_in_place
|
||||
}
|
||||
}
|
||||
@@ -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 @@
|
||||
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 @@
|
||||
|
||||
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 @@
|
||||
/// 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 @@
|
||||
/// 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 @@ pub fn as_slice_of_cells(&self) -> &[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 @@ fn clone(&self) -> Self {
|
||||
/// 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 @@ fn clone(&self) -> 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 @@
|
||||
|
||||
/// `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 @@
|
||||
/// [`&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 @@ fn as_ref(&self) -> &OsStr {
|
||||
/// 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")
|
||||
|
||||
+32
-31
@@ -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,
|
||||
});
|
||||
|
||||
+16
-38
@@ -19,7 +19,6 @@
|
||||
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 @@ fn clean(&self, cx: &DocContext<'_>) -> Item {
|
||||
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 @@ fn clean(&self, cx: &DocContext<'_>) -> Item {
|
||||
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 @@ fn clean(&self, cx: &DocContext<'_>) -> Item {
|
||||
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 @@ fn clean(&self, cx: &DocContext<'_>) -> Item {
|
||||
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 @@ fn clean(&self, cx: &DocContext<'_>) -> Item {
|
||||
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 @@ fn clean(&self, cx: &DocContext<'_>) -> Item {
|
||||
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 @@ fn clean(&self, cx: &DocContext<'_>) -> Item {
|
||||
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 @@ fn clean(&self, cx: &DocContext<'_>) -> Item {
|
||||
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 @@ fn clean(&self, cx: &DocContext<'_>) -> Item {
|
||||
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 @@ fn clean(&self, cx: &DocContext<'_>) -> Item {
|
||||
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 @@ fn clean(&self, cx: &DocContext<'_>) -> Item {
|
||||
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 @@ fn clean(&self, cx: &DocContext<'_>) -> Item {
|
||||
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 @@ fn clean(&self, cx: &DocContext<'_>) -> Vec<Item> {
|
||||
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 @@ fn clean(&self, cx: &DocContext<'_>) -> Item {
|
||||
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 @@ fn clean(&self, cx: &DocContext<'_>) -> Item {
|
||||
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 @@ fn clean(&self, cx: &DocContext<'_>) -> Item {
|
||||
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 @@ fn clean(&self, cx: &DocContext<'_>) -> Item {
|
||||
}
|
||||
}
|
||||
|
||||
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::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::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::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 @@ pub fn stability_class(&self) -> Option<String> {
|
||||
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 @@ pub fn stability_class(&self) -> Option<String> {
|
||||
})
|
||||
}
|
||||
|
||||
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::{
|
||||
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 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 cmp(i1: &clean::Item, i2: &clean::Item, idx1: usize, idx2: usize) -> Ordering
|
||||
}
|
||||
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 tag_html(class: &str, title: &str, contents: &str) -> 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 trait_item(w: &mut Buffer, cx: &Context, m: &clean::Item, t: &clean::Item) {
|
||||
&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 doc_impl_item(
|
||||
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 doc_impl_item(
|
||||
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_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 @@
|
||||
|
||||
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 @@ fn metadata_loader(&self) -> Box<MetadataLoaderDyn> {
|
||||
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));
|
||||
|
||||
Reference in New Issue
Block a user