mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-22 14:55:26 +00:00
Merge from rustc
This commit is contained in:
commit
8ae7c743bb
12
.github/workflows/ci.yml
vendored
12
.github/workflows/ci.yml
vendored
@ -305,7 +305,7 @@ jobs:
|
||||
SCRIPT: "./x.py dist bootstrap --include-default-paths --host=x86_64-apple-darwin --target=x86_64-apple-darwin"
|
||||
RUST_CONFIGURE_ARGS: "--enable-full-tools --enable-sanitizers --enable-profiler --set rust.jemalloc --set llvm.ninja=false --set rust.lto=thin"
|
||||
RUSTC_RETRY_LINKER_ON_SEGFAULT: 1
|
||||
MACOSX_DEPLOYMENT_TARGET: 10.7
|
||||
MACOSX_DEPLOYMENT_TARGET: 10.12
|
||||
SELECT_XCODE: /Applications/Xcode_13.4.1.app
|
||||
NO_LLVM_ASSERTIONS: 1
|
||||
NO_DEBUG_ASSERTIONS: 1
|
||||
@ -317,7 +317,7 @@ jobs:
|
||||
SCRIPT: "./x.py dist bootstrap --include-default-paths --host='' --target=aarch64-apple-ios,x86_64-apple-ios,aarch64-apple-ios-sim"
|
||||
RUST_CONFIGURE_ARGS: "--enable-sanitizers --enable-profiler --set rust.jemalloc --set llvm.ninja=false"
|
||||
RUSTC_RETRY_LINKER_ON_SEGFAULT: 1
|
||||
MACOSX_DEPLOYMENT_TARGET: 10.7
|
||||
MACOSX_DEPLOYMENT_TARGET: 10.12
|
||||
SELECT_XCODE: /Applications/Xcode_13.4.1.app
|
||||
NO_LLVM_ASSERTIONS: 1
|
||||
NO_DEBUG_ASSERTIONS: 1
|
||||
@ -328,8 +328,8 @@ jobs:
|
||||
SCRIPT: "./x.py --stage 2 test --skip tests/ui --skip tests/rustdoc --skip tests/run-make-fulldeps"
|
||||
RUST_CONFIGURE_ARGS: "--build=x86_64-apple-darwin --enable-sanitizers --enable-profiler --set rust.jemalloc --set llvm.ninja=false"
|
||||
RUSTC_RETRY_LINKER_ON_SEGFAULT: 1
|
||||
MACOSX_DEPLOYMENT_TARGET: 10.8
|
||||
MACOSX_STD_DEPLOYMENT_TARGET: 10.7
|
||||
MACOSX_DEPLOYMENT_TARGET: 10.12
|
||||
MACOSX_STD_DEPLOYMENT_TARGET: 10.12
|
||||
NO_LLVM_ASSERTIONS: 1
|
||||
NO_DEBUG_ASSERTIONS: 1
|
||||
NO_OVERFLOW_CHECKS: 1
|
||||
@ -339,8 +339,8 @@ jobs:
|
||||
SCRIPT: "./x.py --stage 2 test tests/ui tests/rustdoc tests/run-make-fulldeps"
|
||||
RUST_CONFIGURE_ARGS: "--build=x86_64-apple-darwin --enable-sanitizers --enable-profiler --set rust.jemalloc --set llvm.ninja=false"
|
||||
RUSTC_RETRY_LINKER_ON_SEGFAULT: 1
|
||||
MACOSX_DEPLOYMENT_TARGET: 10.8
|
||||
MACOSX_STD_DEPLOYMENT_TARGET: 10.7
|
||||
MACOSX_DEPLOYMENT_TARGET: 10.12
|
||||
MACOSX_STD_DEPLOYMENT_TARGET: 10.12
|
||||
NO_LLVM_ASSERTIONS: 1
|
||||
NO_DEBUG_ASSERTIONS: 1
|
||||
NO_OVERFLOW_CHECKS: 1
|
||||
|
@ -80,7 +80,7 @@ use rustc_errors::{DiagnosticMessage, ErrorGuaranteed, Handler, SubdiagnosticMes
|
||||
use rustc_fluent_macro::fluent_messages;
|
||||
use rustc_metadata::EncodedMetadata;
|
||||
use rustc_middle::dep_graph::{WorkProduct, WorkProductId};
|
||||
use rustc_middle::query::Providers;
|
||||
use rustc_middle::util::Providers;
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
use rustc_session::config::{Lto, OptLevel, OutputFilenames};
|
||||
use rustc_session::Session;
|
||||
|
@ -39,8 +39,8 @@ use rustc_errors::{DiagnosticMessage, ErrorGuaranteed, FatalError, Handler, Subd
|
||||
use rustc_fluent_macro::fluent_messages;
|
||||
use rustc_metadata::EncodedMetadata;
|
||||
use rustc_middle::dep_graph::{WorkProduct, WorkProductId};
|
||||
use rustc_middle::query::Providers;
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
use rustc_middle::util::Providers;
|
||||
use rustc_session::config::{OptLevel, OutputFilenames, PrintKind, PrintRequest};
|
||||
use rustc_session::Session;
|
||||
use rustc_span::symbol::Symbol;
|
||||
|
@ -11,10 +11,10 @@ use rustc_middle::middle::exported_symbols::{
|
||||
metadata_symbol_name, ExportedSymbol, SymbolExportInfo, SymbolExportKind, SymbolExportLevel,
|
||||
};
|
||||
use rustc_middle::query::LocalCrate;
|
||||
use rustc_middle::query::{ExternProviders, Providers};
|
||||
use rustc_middle::ty::Instance;
|
||||
use rustc_middle::ty::{self, SymbolName, TyCtxt};
|
||||
use rustc_middle::ty::{GenericArgKind, GenericArgsRef};
|
||||
use rustc_middle::util::Providers;
|
||||
use rustc_session::config::{CrateType, OomStrategy};
|
||||
use rustc_target::spec::SanitizerSet;
|
||||
|
||||
@ -457,11 +457,9 @@ pub fn provide(providers: &mut Providers) {
|
||||
providers.is_unreachable_local_definition = is_unreachable_local_definition_provider;
|
||||
providers.upstream_drop_glue_for = upstream_drop_glue_for_provider;
|
||||
providers.wasm_import_module_map = wasm_import_module_map;
|
||||
}
|
||||
|
||||
pub fn provide_extern(providers: &mut ExternProviders) {
|
||||
providers.is_reachable_non_generic = is_reachable_non_generic_provider_extern;
|
||||
providers.upstream_monomorphizations_for = upstream_monomorphizations_for_provider;
|
||||
providers.extern_queries.is_reachable_non_generic = is_reachable_non_generic_provider_extern;
|
||||
providers.extern_queries.upstream_monomorphizations_for =
|
||||
upstream_monomorphizations_for_provider;
|
||||
}
|
||||
|
||||
fn symbol_export_level(tcx: TyCtxt<'_>, sym_def_id: DefId) -> SymbolExportLevel {
|
||||
|
@ -426,7 +426,6 @@ fn push_debuginfo_type_name<'tcx>(
|
||||
| ty::Placeholder(..)
|
||||
| ty::Alias(..)
|
||||
| ty::Bound(..)
|
||||
| ty::GeneratorWitnessMIR(..)
|
||||
| ty::GeneratorWitness(..) => {
|
||||
bug!(
|
||||
"debuginfo: Trying to create type name for \
|
||||
|
@ -31,7 +31,7 @@ use rustc_middle::dep_graph::WorkProduct;
|
||||
use rustc_middle::middle::debugger_visualizer::DebuggerVisualizerFile;
|
||||
use rustc_middle::middle::dependency_format::Dependencies;
|
||||
use rustc_middle::middle::exported_symbols::SymbolExportKind;
|
||||
use rustc_middle::query::{ExternProviders, Providers};
|
||||
use rustc_middle::util::Providers;
|
||||
use rustc_serialize::opaque::{FileEncoder, MemDecoder};
|
||||
use rustc_serialize::{Decodable, Decoder, Encodable, Encoder};
|
||||
use rustc_session::config::{CrateType, OutputFilenames, OutputType, RUST_CGU_EXT};
|
||||
@ -190,10 +190,6 @@ pub fn provide(providers: &mut Providers) {
|
||||
crate::codegen_attrs::provide(providers);
|
||||
}
|
||||
|
||||
pub fn provide_extern(providers: &mut ExternProviders) {
|
||||
crate::back::symbol_export::provide_extern(providers);
|
||||
}
|
||||
|
||||
/// Checks if the given filename ends with the `.rcgu.o` extension that `rustc`
|
||||
/// uses for the object files it generates.
|
||||
pub fn looks_like_rust_object_file(filename: &str) -> bool {
|
||||
|
@ -158,8 +158,7 @@ fn calculate_debuginfo_offset<
|
||||
L: DebugInfoOffsetLocation<'tcx, Bx>,
|
||||
>(
|
||||
bx: &mut Bx,
|
||||
local: mir::Local,
|
||||
var: &PerLocalVarDebugInfo<'tcx, Bx::DIVariable>,
|
||||
projection: &[mir::PlaceElem<'tcx>],
|
||||
base: L,
|
||||
) -> DebugInfoOffset<L> {
|
||||
let mut direct_offset = Size::ZERO;
|
||||
@ -167,7 +166,7 @@ fn calculate_debuginfo_offset<
|
||||
let mut indirect_offsets = vec![];
|
||||
let mut place = base;
|
||||
|
||||
for elem in &var.projection[..] {
|
||||
for elem in projection {
|
||||
match *elem {
|
||||
mir::ProjectionElem::Deref => {
|
||||
indirect_offsets.push(Size::ZERO);
|
||||
@ -188,11 +187,7 @@ fn calculate_debuginfo_offset<
|
||||
} => {
|
||||
let offset = indirect_offsets.last_mut().unwrap_or(&mut direct_offset);
|
||||
let FieldsShape::Array { stride, count: _ } = place.layout().fields else {
|
||||
span_bug!(
|
||||
var.source_info.span,
|
||||
"ConstantIndex on non-array type {:?}",
|
||||
place.layout()
|
||||
)
|
||||
bug!("ConstantIndex on non-array type {:?}", place.layout())
|
||||
};
|
||||
*offset += stride * index;
|
||||
place = place.project_constant_index(bx, index);
|
||||
@ -200,11 +195,7 @@ fn calculate_debuginfo_offset<
|
||||
_ => {
|
||||
// Sanity check for `can_use_in_debuginfo`.
|
||||
debug_assert!(!elem.can_use_in_debuginfo());
|
||||
span_bug!(
|
||||
var.source_info.span,
|
||||
"unsupported var debuginfo place `{:?}`",
|
||||
mir::Place { local, projection: var.projection },
|
||||
)
|
||||
bug!("unsupported var debuginfo projection `{:?}`", projection)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -407,7 +398,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
||||
let Some(dbg_loc) = self.dbg_loc(var.source_info) else { return };
|
||||
|
||||
let DebugInfoOffset { direct_offset, indirect_offsets, result: _ } =
|
||||
calculate_debuginfo_offset(bx, local, &var, base.layout);
|
||||
calculate_debuginfo_offset(bx, &var.projection, base.layout);
|
||||
|
||||
// When targeting MSVC, create extra allocas for arguments instead of pointing multiple
|
||||
// dbg_var_addr() calls into the same alloca with offsets. MSVC uses CodeView records
|
||||
@ -425,7 +416,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
||||
|
||||
if should_create_individual_allocas {
|
||||
let DebugInfoOffset { direct_offset: _, indirect_offsets: _, result: place } =
|
||||
calculate_debuginfo_offset(bx, local, &var, base);
|
||||
calculate_debuginfo_offset(bx, &var.projection, base);
|
||||
|
||||
// Create a variable which will be a pointer to the actual value
|
||||
let ptr_ty = Ty::new_ptr(
|
||||
@ -532,23 +523,9 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
||||
let fragment = if let Some(ref fragment) = var.composite {
|
||||
let var_layout = self.cx.layout_of(var_ty);
|
||||
|
||||
let mut fragment_start = Size::ZERO;
|
||||
let mut fragment_layout = var_layout;
|
||||
|
||||
for elem in &fragment.projection {
|
||||
match *elem {
|
||||
mir::ProjectionElem::Field(field, _) => {
|
||||
let i = field.index();
|
||||
fragment_start += fragment_layout.fields.offset(i);
|
||||
fragment_layout = fragment_layout.field(self.cx, i);
|
||||
}
|
||||
_ => span_bug!(
|
||||
var.source_info.span,
|
||||
"unsupported fragment projection `{:?}`",
|
||||
elem,
|
||||
),
|
||||
}
|
||||
}
|
||||
let DebugInfoOffset { direct_offset, indirect_offsets, result: fragment_layout } =
|
||||
calculate_debuginfo_offset(bx, &fragment.projection, var_layout);
|
||||
debug_assert!(indirect_offsets.is_empty());
|
||||
|
||||
if fragment_layout.size == Size::ZERO {
|
||||
// Fragment is a ZST, so does not represent anything. Avoid generating anything
|
||||
@ -559,7 +536,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
||||
// DWARF is concerned, it's not really a fragment.
|
||||
None
|
||||
} else {
|
||||
Some(fragment_start..fragment_start + fragment_layout.size)
|
||||
Some(direct_offset..direct_offset + fragment_layout.size)
|
||||
}
|
||||
} else {
|
||||
None
|
||||
|
@ -11,9 +11,9 @@ use rustc_data_structures::sync::{DynSend, DynSync};
|
||||
use rustc_errors::ErrorGuaranteed;
|
||||
use rustc_metadata::EncodedMetadata;
|
||||
use rustc_middle::dep_graph::{WorkProduct, WorkProductId};
|
||||
use rustc_middle::query::{ExternProviders, Providers};
|
||||
use rustc_middle::ty::layout::{FnAbiOf, HasTyCtxt, LayoutOf, TyAndLayout};
|
||||
use rustc_middle::ty::{Ty, TyCtxt};
|
||||
use rustc_middle::util::Providers;
|
||||
use rustc_session::{
|
||||
config::{self, OutputFilenames, PrintRequest},
|
||||
cstore::MetadataLoaderDyn,
|
||||
@ -85,7 +85,6 @@ pub trait CodegenBackend {
|
||||
}
|
||||
|
||||
fn provide(&self, _providers: &mut Providers) {}
|
||||
fn provide_extern(&self, _providers: &mut ExternProviders) {}
|
||||
fn codegen_crate<'tcx>(
|
||||
&self,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
|
@ -4,6 +4,7 @@ use crate::errors::MaxNumNodesInConstErr;
|
||||
use crate::interpret::{intern_const_alloc_recursive, InternKind, InterpCx, Scalar};
|
||||
use rustc_middle::mir;
|
||||
use rustc_middle::mir::interpret::{EvalToValTreeResult, GlobalId};
|
||||
use rustc_middle::query::TyCtxtAt;
|
||||
use rustc_middle::ty::{self, Ty, TyCtxt};
|
||||
use rustc_span::{source_map::DUMMY_SP, symbol::Symbol};
|
||||
|
||||
@ -86,17 +87,17 @@ pub(crate) fn eval_to_valtree<'tcx>(
|
||||
|
||||
#[instrument(skip(tcx), level = "debug")]
|
||||
pub(crate) fn try_destructure_mir_constant_for_diagnostics<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
tcx: TyCtxtAt<'tcx>,
|
||||
val: mir::ConstValue<'tcx>,
|
||||
ty: Ty<'tcx>,
|
||||
) -> Option<mir::DestructuredConstant<'tcx>> {
|
||||
let param_env = ty::ParamEnv::reveal_all();
|
||||
let ecx = mk_eval_cx(tcx, DUMMY_SP, param_env, CanAccessStatics::No);
|
||||
let ecx = mk_eval_cx(tcx.tcx, tcx.span, param_env, CanAccessStatics::No);
|
||||
let op = ecx.const_val_to_op(val, ty, None).ok()?;
|
||||
|
||||
// We go to `usize` as we cannot allocate anything bigger anyway.
|
||||
let (field_count, variant, down) = match ty.kind() {
|
||||
ty::Array(_, len) => (len.eval_target_usize(tcx, param_env) as usize, None, op),
|
||||
ty::Array(_, len) => (len.eval_target_usize(tcx.tcx, param_env) as usize, None, op),
|
||||
ty::Adt(def, _) if def.variants().is_empty() => {
|
||||
return None;
|
||||
}
|
||||
|
@ -152,7 +152,7 @@ pub(crate) fn const_to_valtree_inner<'tcx>(
|
||||
// FIXME(oli-obk): we can probably encode closures just like structs
|
||||
| ty::Closure(..)
|
||||
| ty::Generator(..)
|
||||
| ty::GeneratorWitness(..) |ty::GeneratorWitnessMIR(..)=> Err(ValTreeCreationError::NonSupportedType),
|
||||
| ty::GeneratorWitness(..) => Err(ValTreeCreationError::NonSupportedType),
|
||||
}
|
||||
}
|
||||
|
||||
@ -280,7 +280,6 @@ pub fn valtree_to_const_value<'tcx>(
|
||||
| ty::Closure(..)
|
||||
| ty::Generator(..)
|
||||
| ty::GeneratorWitness(..)
|
||||
| ty::GeneratorWitnessMIR(..)
|
||||
| ty::FnPtr(_)
|
||||
| ty::RawPtr(_)
|
||||
| ty::Str
|
||||
|
@ -963,7 +963,6 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||
| ty::Ref(..)
|
||||
| ty::Generator(..)
|
||||
| ty::GeneratorWitness(..)
|
||||
| ty::GeneratorWitnessMIR(..)
|
||||
| ty::Array(..)
|
||||
| ty::Closure(..)
|
||||
| ty::Never
|
||||
|
@ -100,8 +100,7 @@ pub(crate) fn eval_nullary_intrinsic<'tcx>(
|
||||
| ty::Dynamic(_, _, _)
|
||||
| ty::Closure(_, _)
|
||||
| ty::Generator(_, _, _)
|
||||
| ty::GeneratorWitness(_)
|
||||
| ty::GeneratorWitnessMIR(_, _)
|
||||
| ty::GeneratorWitness(..)
|
||||
| ty::Never
|
||||
| ty::Tuple(_)
|
||||
| ty::Error(_) => ConstValue::from_target_usize(0u64, &tcx),
|
||||
|
@ -583,7 +583,6 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
|
||||
| ty::Bound(..)
|
||||
| ty::Param(..)
|
||||
| ty::Alias(..)
|
||||
| ty::GeneratorWitnessMIR(..)
|
||||
| ty::GeneratorWitness(..) => bug!("Encountered invalid type {:?}", ty),
|
||||
}
|
||||
}
|
||||
|
@ -38,8 +38,7 @@ pub use errors::ReportErrorExt;
|
||||
|
||||
use rustc_errors::{DiagnosticMessage, SubdiagnosticMessage};
|
||||
use rustc_fluent_macro::fluent_messages;
|
||||
use rustc_middle::query::Providers;
|
||||
use rustc_middle::ty;
|
||||
use rustc_middle::{ty, util::Providers};
|
||||
|
||||
fluent_messages! { "../messages.ftl" }
|
||||
|
||||
@ -52,8 +51,8 @@ pub fn provide(providers: &mut Providers) {
|
||||
let (param_env, raw) = param_env_and_value.into_parts();
|
||||
const_eval::eval_to_valtree(tcx, param_env, raw)
|
||||
};
|
||||
providers.try_destructure_mir_constant_for_diagnostics =
|
||||
|tcx, (cv, ty)| const_eval::try_destructure_mir_constant_for_diagnostics(tcx, cv, ty);
|
||||
providers.hooks.try_destructure_mir_constant_for_diagnostics =
|
||||
const_eval::try_destructure_mir_constant_for_diagnostics;
|
||||
providers.valtree_to_const_val = |tcx, (ty, valtree)| {
|
||||
const_eval::valtree_to_const_value(tcx, ty::ParamEnv::empty().and(ty), valtree)
|
||||
};
|
||||
|
@ -64,8 +64,7 @@ impl<'tcx> Printer<'tcx> for AbsolutePathPrinter<'tcx> {
|
||||
|
||||
ty::Alias(ty::Weak, _) => bug!("type_name: unexpected weak projection"),
|
||||
ty::Alias(ty::Inherent, _) => bug!("type_name: unexpected inherent projection"),
|
||||
ty::GeneratorWitness(_) => bug!("type_name: unexpected `GeneratorWitness`"),
|
||||
ty::GeneratorWitnessMIR(..) => bug!("type_name: unexpected `GeneratorWitnessMIR`"),
|
||||
ty::GeneratorWitness(..) => bug!("type_name: unexpected `GeneratorWitness`"),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,9 +1,11 @@
|
||||
#### Note: this error code is no longer emitted by the compiler.
|
||||
|
||||
When using generators (or async) all type variables must be bound so a
|
||||
generator can be constructed.
|
||||
|
||||
Erroneous code example:
|
||||
|
||||
```edition2018,compile_fail,E0698
|
||||
```edition2018,compile_fail,E0282
|
||||
async fn bar<T>() -> () {}
|
||||
|
||||
async fn foo() {
|
||||
|
@ -1579,13 +1579,7 @@ fn opaque_type_cycle_error(
|
||||
label_match(capture.place.ty(), capture.get_path_span(tcx));
|
||||
}
|
||||
// Label any generator locals that capture the opaque
|
||||
for interior_ty in
|
||||
typeck_results.generator_interior_types.as_ref().skip_binder()
|
||||
{
|
||||
label_match(interior_ty.ty, interior_ty.span);
|
||||
}
|
||||
if tcx.sess.opts.unstable_opts.drop_tracking_mir
|
||||
&& let DefKind::Generator = tcx.def_kind(closure_def_id)
|
||||
if let DefKind::Generator = tcx.def_kind(closure_def_id)
|
||||
&& let Some(generator_layout) = tcx.mir_generator_witnesses(closure_def_id)
|
||||
{
|
||||
for interior_ty in &generator_layout.field_tys {
|
||||
@ -1603,7 +1597,6 @@ fn opaque_type_cycle_error(
|
||||
}
|
||||
|
||||
pub(super) fn check_generator_obligations(tcx: TyCtxt<'_>, def_id: LocalDefId) {
|
||||
debug_assert!(tcx.sess.opts.unstable_opts.drop_tracking_mir);
|
||||
debug_assert!(matches!(tcx.def_kind(def_id), DefKind::Generator));
|
||||
|
||||
let typeck = tcx.typeck(def_id);
|
||||
|
@ -1134,7 +1134,10 @@ fn report_trait_method_mismatch<'tcx>(
|
||||
&mut diag,
|
||||
&cause,
|
||||
trait_err_span.map(|sp| (sp, Cow::from("type in trait"))),
|
||||
Some(infer::ValuePairs::Sigs(ExpectedFound { expected: trait_sig, found: impl_sig })),
|
||||
Some(infer::ValuePairs::PolySigs(ExpectedFound {
|
||||
expected: ty::Binder::dummy(trait_sig),
|
||||
found: ty::Binder::dummy(impl_sig),
|
||||
})),
|
||||
terr,
|
||||
false,
|
||||
false,
|
||||
|
@ -573,10 +573,7 @@ pub fn check_function_signature<'tcx>(
|
||||
let norm_cause = ObligationCause::misc(cause.span, local_id);
|
||||
let actual_sig = ocx.normalize(&norm_cause, param_env, actual_sig);
|
||||
|
||||
let expected_ty = Ty::new_fn_ptr(tcx, expected_sig);
|
||||
let actual_ty = Ty::new_fn_ptr(tcx, actual_sig);
|
||||
|
||||
match ocx.eq(&cause, param_env, expected_ty, actual_ty) {
|
||||
match ocx.eq(&cause, param_env, expected_sig, actual_sig) {
|
||||
Ok(()) => {
|
||||
let errors = ocx.select_all_or_error();
|
||||
if !errors.is_empty() {
|
||||
@ -595,9 +592,9 @@ pub fn check_function_signature<'tcx>(
|
||||
&mut diag,
|
||||
&cause,
|
||||
None,
|
||||
Some(infer::ValuePairs::Sigs(ExpectedFound {
|
||||
expected: tcx.liberate_late_bound_regions(fn_id, expected_sig),
|
||||
found: tcx.liberate_late_bound_regions(fn_id, actual_sig),
|
||||
Some(infer::ValuePairs::PolySigs(ExpectedFound {
|
||||
expected: expected_sig,
|
||||
found: actual_sig,
|
||||
})),
|
||||
err,
|
||||
false,
|
||||
|
@ -1755,6 +1755,8 @@ fn check_variances_for_type_defn<'tcx>(
|
||||
.collect::<FxHashSet<_>>()
|
||||
});
|
||||
|
||||
let ty_generics = tcx.generics_of(item.owner_id);
|
||||
|
||||
for (index, _) in variances.iter().enumerate() {
|
||||
let parameter = Parameter(index as u32);
|
||||
|
||||
@ -1762,13 +1764,27 @@ fn check_variances_for_type_defn<'tcx>(
|
||||
continue;
|
||||
}
|
||||
|
||||
let param = &hir_generics.params[index];
|
||||
let ty_param = &ty_generics.params[index];
|
||||
let hir_param = &hir_generics.params[index];
|
||||
|
||||
match param.name {
|
||||
if ty_param.def_id != hir_param.def_id.into() {
|
||||
// valid programs always have lifetimes before types in the generic parameter list
|
||||
// ty_generics are normalized to be in this required order, and variances are built
|
||||
// from ty generics, not from hir generics. but we need hir generics to get
|
||||
// a span out
|
||||
//
|
||||
// if they aren't in the same order, then the user has written invalid code, and already
|
||||
// got an error about it (or I'm wrong about this)
|
||||
tcx.sess
|
||||
.delay_span_bug(hir_param.span, "hir generics and ty generics in different order");
|
||||
continue;
|
||||
}
|
||||
|
||||
match hir_param.name {
|
||||
hir::ParamName::Error => {}
|
||||
_ => {
|
||||
let has_explicit_bounds = explicitly_bounded_params.contains(¶meter);
|
||||
report_bivariance(tcx, param, has_explicit_bounds);
|
||||
report_bivariance(tcx, hir_param, has_explicit_bounds);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -157,7 +157,6 @@ impl<'tcx> InherentCollect<'tcx> {
|
||||
| ty::Closure(..)
|
||||
| ty::Generator(..)
|
||||
| ty::GeneratorWitness(..)
|
||||
| ty::GeneratorWitnessMIR(..)
|
||||
| ty::Bound(..)
|
||||
| ty::Placeholder(_)
|
||||
| ty::Infer(_) => {
|
||||
|
@ -245,7 +245,6 @@ fn do_orphan_check_impl<'tcx>(
|
||||
ty::Closure(..)
|
||||
| ty::Generator(..)
|
||||
| ty::GeneratorWitness(..)
|
||||
| ty::GeneratorWitnessMIR(..)
|
||||
| ty::Bound(..)
|
||||
| ty::Placeholder(..)
|
||||
| ty::Infer(..) => {
|
||||
|
@ -314,11 +314,7 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> {
|
||||
// types, where we use Error as the Self type
|
||||
}
|
||||
|
||||
ty::Placeholder(..)
|
||||
| ty::GeneratorWitness(..)
|
||||
| ty::GeneratorWitnessMIR(..)
|
||||
| ty::Bound(..)
|
||||
| ty::Infer(..) => {
|
||||
ty::Placeholder(..) | ty::GeneratorWitness(..) | ty::Bound(..) | ty::Infer(..) => {
|
||||
bug!("unexpected type encountered in variance inference: {}", ty);
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ use crate::coercion::{AsCoercionSite, CoerceMany};
|
||||
use crate::{Diverges, Expectation, FnCtxt, Needs};
|
||||
use rustc_errors::Diagnostic;
|
||||
use rustc_hir::{self as hir, ExprKind};
|
||||
use rustc_hir_pretty::ty_to_string;
|
||||
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
|
||||
use rustc_infer::traits::Obligation;
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
@ -252,7 +253,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
{
|
||||
// If this `if` expr is the parent's function return expr,
|
||||
// the cause of the type coercion is the return type, point at it. (#25228)
|
||||
let ret_reason = self.maybe_get_coercion_reason(then_expr.hir_id, span);
|
||||
let hir_id = self.tcx.hir().parent_id(self.tcx.hir().parent_id(then_expr.hir_id));
|
||||
let ret_reason = self.maybe_get_coercion_reason(hir_id, span);
|
||||
let cause = self.cause(span, ObligationCauseCode::IfExpressionWithNoElse);
|
||||
let mut error = false;
|
||||
coercion.coerce_forced_unit(
|
||||
@ -275,11 +277,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
error
|
||||
}
|
||||
|
||||
fn maybe_get_coercion_reason(&self, hir_id: hir::HirId, sp: Span) -> Option<(Span, String)> {
|
||||
let node = {
|
||||
let rslt = self.tcx.hir().parent_id(self.tcx.hir().parent_id(hir_id));
|
||||
self.tcx.hir().get(rslt)
|
||||
};
|
||||
pub fn maybe_get_coercion_reason(
|
||||
&self,
|
||||
hir_id: hir::HirId,
|
||||
sp: Span,
|
||||
) -> Option<(Span, String)> {
|
||||
let node = self.tcx.hir().get(hir_id);
|
||||
if let hir::Node::Block(block) = node {
|
||||
// check that the body's parent is an fn
|
||||
let parent = self.tcx.hir().get_parent(self.tcx.hir().parent_id(block.hir_id));
|
||||
@ -289,9 +292,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
// check that the `if` expr without `else` is the fn body's expr
|
||||
if expr.span == sp {
|
||||
return self.get_fn_decl(hir_id).and_then(|(_, fn_decl, _)| {
|
||||
let span = fn_decl.output.span();
|
||||
let snippet = self.tcx.sess.source_map().span_to_snippet(span).ok()?;
|
||||
Some((span, format!("expected `{snippet}` because of this return type")))
|
||||
let (ty, span) = match fn_decl.output {
|
||||
hir::FnRetTy::DefaultReturn(span) => ("()".to_string(), span),
|
||||
hir::FnRetTy::Return(ty) => (ty_to_string(ty), ty.span),
|
||||
};
|
||||
Some((span, format!("expected `{ty}` because of this return type")))
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -129,7 +129,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
| ty::Float(_)
|
||||
| ty::Array(..)
|
||||
| ty::GeneratorWitness(..)
|
||||
| ty::GeneratorWitnessMIR(..)
|
||||
| ty::RawPtr(_)
|
||||
| ty::Ref(..)
|
||||
| ty::FnDef(..)
|
||||
|
@ -83,6 +83,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
}
|
||||
|
||||
self.annotate_expected_due_to_let_ty(err, expr, error);
|
||||
self.annotate_loop_expected_due_to_inference(err, expr, error);
|
||||
|
||||
// FIXME(#73154): For now, we do leak check when coercing function
|
||||
// pointers in typeck, instead of only during borrowck. This can lead
|
||||
@ -527,6 +528,67 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
false
|
||||
}
|
||||
|
||||
// When encountering a type error on the value of a `break`, try to point at the reason for the
|
||||
// expected type.
|
||||
fn annotate_loop_expected_due_to_inference(
|
||||
&self,
|
||||
err: &mut Diagnostic,
|
||||
expr: &hir::Expr<'_>,
|
||||
error: Option<TypeError<'tcx>>,
|
||||
) {
|
||||
let Some(TypeError::Sorts(ExpectedFound { expected, .. })) = error else {
|
||||
return;
|
||||
};
|
||||
let mut parent_id = self.tcx.hir().parent_id(expr.hir_id);
|
||||
loop {
|
||||
// Climb the HIR tree to see if the current `Expr` is part of a `break;` statement.
|
||||
let Some(hir::Node::Expr(parent)) = self.tcx.hir().find(parent_id) else {
|
||||
break;
|
||||
};
|
||||
parent_id = self.tcx.hir().parent_id(parent.hir_id);
|
||||
let hir::ExprKind::Break(destination, _) = parent.kind else {
|
||||
continue;
|
||||
};
|
||||
let mut parent_id = parent.hir_id;
|
||||
loop {
|
||||
// Climb the HIR tree to find the (desugared) `loop` this `break` corresponds to.
|
||||
let parent = match self.tcx.hir().find(parent_id) {
|
||||
Some(hir::Node::Expr(&ref parent)) => {
|
||||
parent_id = self.tcx.hir().parent_id(parent.hir_id);
|
||||
parent
|
||||
}
|
||||
Some(hir::Node::Stmt(hir::Stmt {
|
||||
hir_id,
|
||||
kind: hir::StmtKind::Semi(&ref parent) | hir::StmtKind::Expr(&ref parent),
|
||||
..
|
||||
})) => {
|
||||
parent_id = self.tcx.hir().parent_id(*hir_id);
|
||||
parent
|
||||
}
|
||||
Some(hir::Node::Block(hir::Block { .. })) => {
|
||||
parent_id = self.tcx.hir().parent_id(parent_id);
|
||||
parent
|
||||
}
|
||||
_ => break,
|
||||
};
|
||||
if let hir::ExprKind::Loop(_, label, _, span) = parent.kind
|
||||
&& destination.label == label
|
||||
{
|
||||
if let Some((reason_span, message)) =
|
||||
self.maybe_get_coercion_reason(parent_id, parent.span)
|
||||
{
|
||||
err.span_label(reason_span, message);
|
||||
err.span_label(
|
||||
span,
|
||||
format!("this loop is expected to be of type `{expected}`"),
|
||||
);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn annotate_expected_due_to_let_ty(
|
||||
&self,
|
||||
err: &mut Diagnostic,
|
||||
|
@ -509,28 +509,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
typeck_results.rvalue_scopes = rvalue_scopes;
|
||||
}
|
||||
|
||||
pub(in super::super) fn resolve_generator_interiors(&self, def_id: DefId) {
|
||||
if self.tcx.sess.opts.unstable_opts.drop_tracking_mir {
|
||||
self.save_generator_interior_predicates(def_id);
|
||||
return;
|
||||
}
|
||||
|
||||
self.select_obligations_where_possible(|_| {});
|
||||
|
||||
let mut generators = self.deferred_generator_interiors.borrow_mut();
|
||||
for (generator_def_id, body_id, interior, kind) in generators.drain(..) {
|
||||
crate::generator_interior::resolve_interior(
|
||||
self,
|
||||
def_id,
|
||||
generator_def_id,
|
||||
body_id,
|
||||
interior,
|
||||
kind,
|
||||
);
|
||||
self.select_obligations_where_possible(|_| {});
|
||||
}
|
||||
}
|
||||
|
||||
/// Unify the inference variables corresponding to generator witnesses, and save all the
|
||||
/// predicates that were stalled on those inference variables.
|
||||
///
|
||||
@ -540,7 +518,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
/// We must not attempt to select obligations after this method has run, or risk query cycle
|
||||
/// ICE.
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
fn save_generator_interior_predicates(&self, def_id: DefId) {
|
||||
pub(in super::super) fn resolve_generator_interiors(&self, def_id: DefId) {
|
||||
// Try selecting all obligations that are not blocked on inference variables.
|
||||
// Once we start unifying generator witnesses, trying to select obligations on them will
|
||||
// trigger query cycle ICEs, as doing so requires MIR.
|
||||
@ -557,7 +535,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
self.tcx,
|
||||
self.tcx.typeck_root_def_id(expr_def_id.to_def_id()),
|
||||
);
|
||||
let witness = Ty::new_generator_witness_mir(self.tcx, expr_def_id.to_def_id(), args);
|
||||
let witness = Ty::new_generator_witness(self.tcx, expr_def_id.to_def_id(), args);
|
||||
|
||||
// Unify `interior` with `witness` and collect all the resulting obligations.
|
||||
let span = self.tcx.hir().body(body_id).value.span;
|
||||
|
@ -1,601 +0,0 @@
|
||||
use super::{
|
||||
for_each_consumable, record_consumed_borrow::ConsumedAndBorrowedPlaces, DropRangesBuilder,
|
||||
NodeInfo, PostOrderId, TrackedValue, TrackedValueIndex,
|
||||
};
|
||||
use hir::{
|
||||
intravisit::{self, Visitor},
|
||||
Body, Expr, ExprKind, Guard, HirId, LoopIdError,
|
||||
};
|
||||
use rustc_data_structures::unord::{UnordMap, UnordSet};
|
||||
use rustc_hir as hir;
|
||||
use rustc_index::IndexVec;
|
||||
use rustc_infer::infer::InferCtxt;
|
||||
use rustc_middle::{
|
||||
hir::map::Map,
|
||||
ty::{ParamEnv, TyCtxt, TypeVisitableExt, TypeckResults},
|
||||
};
|
||||
use std::mem::swap;
|
||||
|
||||
/// Traverses the body to find the control flow graph and locations for the
|
||||
/// relevant places are dropped or reinitialized.
|
||||
///
|
||||
/// The resulting structure still needs to be iterated to a fixed point, which
|
||||
/// can be done with propagate_to_fixpoint in cfg_propagate.
|
||||
pub(super) fn build_control_flow_graph<'tcx>(
|
||||
infcx: &InferCtxt<'tcx>,
|
||||
typeck_results: &TypeckResults<'tcx>,
|
||||
param_env: ParamEnv<'tcx>,
|
||||
consumed_borrowed_places: ConsumedAndBorrowedPlaces,
|
||||
body: &'tcx Body<'tcx>,
|
||||
num_exprs: usize,
|
||||
) -> (DropRangesBuilder, UnordSet<HirId>) {
|
||||
let mut drop_range_visitor = DropRangeVisitor::new(
|
||||
infcx,
|
||||
typeck_results,
|
||||
param_env,
|
||||
consumed_borrowed_places,
|
||||
num_exprs,
|
||||
);
|
||||
intravisit::walk_body(&mut drop_range_visitor, body);
|
||||
|
||||
drop_range_visitor.drop_ranges.process_deferred_edges();
|
||||
if let Some(filename) = &infcx.tcx.sess.opts.unstable_opts.dump_drop_tracking_cfg {
|
||||
super::cfg_visualize::write_graph_to_file(
|
||||
&drop_range_visitor.drop_ranges,
|
||||
filename,
|
||||
infcx.tcx,
|
||||
);
|
||||
}
|
||||
|
||||
(drop_range_visitor.drop_ranges, drop_range_visitor.places.borrowed_temporaries)
|
||||
}
|
||||
|
||||
/// This struct is used to gather the information for `DropRanges` to determine the regions of the
|
||||
/// HIR tree for which a value is dropped.
|
||||
///
|
||||
/// We are interested in points where a variables is dropped or initialized, and the control flow
|
||||
/// of the code. We identify locations in code by their post-order traversal index, so it is
|
||||
/// important for this traversal to match that in `RegionResolutionVisitor` and `InteriorVisitor`.
|
||||
///
|
||||
/// We make several simplifying assumptions, with the goal of being more conservative than
|
||||
/// necessary rather than less conservative (since being less conservative is unsound, but more
|
||||
/// conservative is still safe). These assumptions are:
|
||||
///
|
||||
/// 1. Moving a variable `a` counts as a move of the whole variable.
|
||||
/// 2. Moving a partial path like `a.b.c` is ignored.
|
||||
/// 3. Reinitializing through a field (e.g. `a.b.c = 5`) counts as a reinitialization of all of
|
||||
/// `a`.
|
||||
///
|
||||
/// Some examples:
|
||||
///
|
||||
/// Rule 1:
|
||||
/// ```rust
|
||||
/// let mut a = (vec![0], vec![0]);
|
||||
/// drop(a);
|
||||
/// // `a` is not considered initialized.
|
||||
/// ```
|
||||
///
|
||||
/// Rule 2:
|
||||
/// ```rust
|
||||
/// let mut a = (vec![0], vec![0]);
|
||||
/// drop(a.0);
|
||||
/// drop(a.1);
|
||||
/// // `a` is still considered initialized.
|
||||
/// ```
|
||||
///
|
||||
/// Rule 3:
|
||||
/// ```compile_fail,E0382
|
||||
/// let mut a = (vec![0], vec![0]);
|
||||
/// drop(a);
|
||||
/// a.1 = vec![1];
|
||||
/// // all of `a` is considered initialized
|
||||
/// ```
|
||||
|
||||
struct DropRangeVisitor<'a, 'tcx> {
|
||||
typeck_results: &'a TypeckResults<'tcx>,
|
||||
infcx: &'a InferCtxt<'tcx>,
|
||||
param_env: ParamEnv<'tcx>,
|
||||
places: ConsumedAndBorrowedPlaces,
|
||||
drop_ranges: DropRangesBuilder,
|
||||
expr_index: PostOrderId,
|
||||
label_stack: Vec<(Option<rustc_ast::Label>, PostOrderId)>,
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> DropRangeVisitor<'a, 'tcx> {
|
||||
fn new(
|
||||
infcx: &'a InferCtxt<'tcx>,
|
||||
typeck_results: &'a TypeckResults<'tcx>,
|
||||
param_env: ParamEnv<'tcx>,
|
||||
places: ConsumedAndBorrowedPlaces,
|
||||
num_exprs: usize,
|
||||
) -> Self {
|
||||
debug!("consumed_places: {:?}", places.consumed);
|
||||
let drop_ranges = DropRangesBuilder::new(
|
||||
places.consumed.iter().flat_map(|(_, places)| places.iter().cloned()),
|
||||
infcx.tcx.hir(),
|
||||
num_exprs,
|
||||
);
|
||||
Self {
|
||||
infcx,
|
||||
typeck_results,
|
||||
param_env,
|
||||
places,
|
||||
drop_ranges,
|
||||
expr_index: PostOrderId::from_u32(0),
|
||||
label_stack: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
fn tcx(&self) -> TyCtxt<'tcx> {
|
||||
self.infcx.tcx
|
||||
}
|
||||
|
||||
fn record_drop(&mut self, value: TrackedValue) {
|
||||
if self.places.borrowed.contains(&value) {
|
||||
debug!("not marking {:?} as dropped because it is borrowed at some point", value);
|
||||
} else {
|
||||
debug!("marking {:?} as dropped at {:?}", value, self.expr_index);
|
||||
let count = self.expr_index;
|
||||
self.drop_ranges.drop_at(value, count);
|
||||
}
|
||||
}
|
||||
|
||||
/// ExprUseVisitor's consume callback doesn't go deep enough for our purposes in all
|
||||
/// expressions. This method consumes a little deeper into the expression when needed.
|
||||
fn consume_expr(&mut self, expr: &hir::Expr<'_>) {
|
||||
debug!("consuming expr {:?}, count={:?}", expr.kind, self.expr_index);
|
||||
let places = self
|
||||
.places
|
||||
.consumed
|
||||
.get(&expr.hir_id)
|
||||
.map_or(vec![], |places| places.iter().cloned().collect());
|
||||
for place in places {
|
||||
trace!(?place, "consuming place");
|
||||
for_each_consumable(self.tcx().hir(), place, |value| self.record_drop(value));
|
||||
}
|
||||
}
|
||||
|
||||
/// Marks an expression as being reinitialized.
|
||||
///
|
||||
/// Note that we always approximated on the side of things being more
|
||||
/// initialized than they actually are, as opposed to less. In cases such
|
||||
/// as `x.y = ...`, we would consider all of `x` as being initialized
|
||||
/// instead of just the `y` field.
|
||||
///
|
||||
/// This is because it is always safe to consider something initialized
|
||||
/// even when it is not, but the other way around will cause problems.
|
||||
///
|
||||
/// In the future, we will hopefully tighten up these rules to be more
|
||||
/// precise.
|
||||
fn reinit_expr(&mut self, expr: &hir::Expr<'_>) {
|
||||
// Walk the expression to find the base. For example, in an expression
|
||||
// like `*a[i].x`, we want to find the `a` and mark that as
|
||||
// reinitialized.
|
||||
match expr.kind {
|
||||
ExprKind::Path(hir::QPath::Resolved(
|
||||
_,
|
||||
hir::Path { res: hir::def::Res::Local(hir_id), .. },
|
||||
)) => {
|
||||
// This is the base case, where we have found an actual named variable.
|
||||
|
||||
let location = self.expr_index;
|
||||
debug!("reinitializing {:?} at {:?}", hir_id, location);
|
||||
self.drop_ranges.reinit_at(TrackedValue::Variable(*hir_id), location);
|
||||
}
|
||||
|
||||
ExprKind::Field(base, _) => self.reinit_expr(base),
|
||||
|
||||
// Most expressions do not refer to something where we need to track
|
||||
// reinitializations.
|
||||
//
|
||||
// Some of these may be interesting in the future
|
||||
ExprKind::Path(..)
|
||||
| ExprKind::ConstBlock(..)
|
||||
| ExprKind::Array(..)
|
||||
| ExprKind::Call(..)
|
||||
| ExprKind::MethodCall(..)
|
||||
| ExprKind::Tup(..)
|
||||
| ExprKind::Binary(..)
|
||||
| ExprKind::Unary(..)
|
||||
| ExprKind::Lit(..)
|
||||
| ExprKind::Cast(..)
|
||||
| ExprKind::Type(..)
|
||||
| ExprKind::DropTemps(..)
|
||||
| ExprKind::Let(..)
|
||||
| ExprKind::If(..)
|
||||
| ExprKind::Loop(..)
|
||||
| ExprKind::Match(..)
|
||||
| ExprKind::Closure { .. }
|
||||
| ExprKind::Block(..)
|
||||
| ExprKind::Assign(..)
|
||||
| ExprKind::AssignOp(..)
|
||||
| ExprKind::Index(..)
|
||||
| ExprKind::AddrOf(..)
|
||||
| ExprKind::Break(..)
|
||||
| ExprKind::Continue(..)
|
||||
| ExprKind::Ret(..)
|
||||
| ExprKind::Become(..)
|
||||
| ExprKind::InlineAsm(..)
|
||||
| ExprKind::OffsetOf(..)
|
||||
| ExprKind::Struct(..)
|
||||
| ExprKind::Repeat(..)
|
||||
| ExprKind::Yield(..)
|
||||
| ExprKind::Err(_) => (),
|
||||
}
|
||||
}
|
||||
|
||||
/// For an expression with an uninhabited return type (e.g. a function that returns !),
|
||||
/// this adds a self edge to the CFG to model the fact that the function does not
|
||||
/// return.
|
||||
fn handle_uninhabited_return(&mut self, expr: &Expr<'tcx>) {
|
||||
let ty = self.typeck_results.expr_ty(expr);
|
||||
let ty = self.infcx.resolve_vars_if_possible(ty);
|
||||
if ty.has_non_region_infer() {
|
||||
self.tcx()
|
||||
.sess
|
||||
.delay_span_bug(expr.span, format!("could not resolve infer vars in `{ty}`"));
|
||||
return;
|
||||
}
|
||||
let ty = self.tcx().erase_regions(ty);
|
||||
let m = self.tcx().parent_module(expr.hir_id).to_def_id();
|
||||
if !ty.is_inhabited_from(self.tcx(), m, self.param_env) {
|
||||
// This function will not return. We model this fact as an infinite loop.
|
||||
self.drop_ranges.add_control_edge(self.expr_index + 1, self.expr_index + 1);
|
||||
}
|
||||
}
|
||||
|
||||
/// Map a Destination to an equivalent expression node
|
||||
///
|
||||
/// The destination field of a Break or Continue expression can target either an
|
||||
/// expression or a block. The drop range analysis, however, only deals in
|
||||
/// expression nodes, so blocks that might be the destination of a Break or Continue
|
||||
/// will not have a PostOrderId.
|
||||
///
|
||||
/// If the destination is an expression, this function will simply return that expression's
|
||||
/// hir_id. If the destination is a block, this function will return the hir_id of last
|
||||
/// expression in the block.
|
||||
fn find_target_expression_from_destination(
|
||||
&self,
|
||||
destination: hir::Destination,
|
||||
) -> Result<HirId, LoopIdError> {
|
||||
destination.target_id.map(|target| {
|
||||
let node = self.tcx().hir().get(target);
|
||||
match node {
|
||||
hir::Node::Expr(_) => target,
|
||||
hir::Node::Block(b) => find_last_block_expression(b),
|
||||
hir::Node::Param(..)
|
||||
| hir::Node::Item(..)
|
||||
| hir::Node::ForeignItem(..)
|
||||
| hir::Node::TraitItem(..)
|
||||
| hir::Node::ImplItem(..)
|
||||
| hir::Node::Variant(..)
|
||||
| hir::Node::Field(..)
|
||||
| hir::Node::AnonConst(..)
|
||||
| hir::Node::ConstBlock(..)
|
||||
| hir::Node::Stmt(..)
|
||||
| hir::Node::PathSegment(..)
|
||||
| hir::Node::Ty(..)
|
||||
| hir::Node::TypeBinding(..)
|
||||
| hir::Node::TraitRef(..)
|
||||
| hir::Node::Pat(..)
|
||||
| hir::Node::PatField(..)
|
||||
| hir::Node::ExprField(..)
|
||||
| hir::Node::Arm(..)
|
||||
| hir::Node::Local(..)
|
||||
| hir::Node::Ctor(..)
|
||||
| hir::Node::Lifetime(..)
|
||||
| hir::Node::GenericParam(..)
|
||||
| hir::Node::Crate(..)
|
||||
| hir::Node::Infer(..) => bug!("Unsupported branch target: {:?}", node),
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn find_last_block_expression(block: &hir::Block<'_>) -> HirId {
|
||||
block.expr.map_or_else(
|
||||
// If there is no tail expression, there will be at least one statement in the
|
||||
// block because the block contains a break or continue statement.
|
||||
|| block.stmts.last().unwrap().hir_id,
|
||||
|expr| expr.hir_id,
|
||||
)
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> Visitor<'tcx> for DropRangeVisitor<'a, 'tcx> {
|
||||
fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
|
||||
let mut reinit = None;
|
||||
match expr.kind {
|
||||
ExprKind::Assign(lhs, rhs, _) => {
|
||||
self.visit_expr(rhs);
|
||||
self.visit_expr(lhs);
|
||||
|
||||
reinit = Some(lhs);
|
||||
}
|
||||
|
||||
ExprKind::If(test, if_true, if_false) => {
|
||||
self.visit_expr(test);
|
||||
|
||||
let fork = self.expr_index;
|
||||
|
||||
self.drop_ranges.add_control_edge(fork, self.expr_index + 1);
|
||||
self.visit_expr(if_true);
|
||||
let true_end = self.expr_index;
|
||||
|
||||
self.drop_ranges.add_control_edge(fork, self.expr_index + 1);
|
||||
if let Some(if_false) = if_false {
|
||||
self.visit_expr(if_false);
|
||||
}
|
||||
|
||||
self.drop_ranges.add_control_edge(true_end, self.expr_index + 1);
|
||||
}
|
||||
ExprKind::Match(scrutinee, arms, ..) => {
|
||||
// We walk through the match expression almost like a chain of if expressions.
|
||||
// Here's a diagram to follow along with:
|
||||
//
|
||||
// ┌─┐
|
||||
// match │A│ {
|
||||
// ┌───┴─┘
|
||||
// │
|
||||
// ┌▼┌───►┌─┐ ┌─┐
|
||||
// │B│ if │C│ =>│D│,
|
||||
// └─┘ ├─┴──►└─┴──────┐
|
||||
// ┌──┘ │
|
||||
// ┌──┘ │
|
||||
// │ │
|
||||
// ┌▼┌───►┌─┐ ┌─┐ │
|
||||
// │E│ if │F│ =>│G│, │
|
||||
// └─┘ ├─┴──►└─┴┐ │
|
||||
// │ │ │
|
||||
// } ▼ ▼ │
|
||||
// ┌─┐◄───────────────────┘
|
||||
// │H│
|
||||
// └─┘
|
||||
//
|
||||
// The order we want is that the scrutinee (A) flows into the first pattern (B),
|
||||
// which flows into the guard (C). Then the guard either flows into the arm body
|
||||
// (D) or into the start of the next arm (E). Finally, the body flows to the end
|
||||
// of the match block (H).
|
||||
//
|
||||
// The subsequent arms follow the same ordering. First we go to the pattern, then
|
||||
// the guard (if present, otherwise it flows straight into the body), then into
|
||||
// the body and then to the end of the match expression.
|
||||
//
|
||||
// The comments below show which edge is being added.
|
||||
self.visit_expr(scrutinee);
|
||||
|
||||
let (guard_exit, arm_end_ids) = arms.iter().fold(
|
||||
(self.expr_index, vec![]),
|
||||
|(incoming_edge, mut arm_end_ids), hir::Arm { pat, body, guard, .. }| {
|
||||
// A -> B, or C -> E
|
||||
self.drop_ranges.add_control_edge(incoming_edge, self.expr_index + 1);
|
||||
self.visit_pat(pat);
|
||||
// B -> C and E -> F are added implicitly due to the traversal order.
|
||||
match guard {
|
||||
Some(Guard::If(expr)) => self.visit_expr(expr),
|
||||
Some(Guard::IfLet(let_expr)) => {
|
||||
self.visit_let_expr(let_expr);
|
||||
}
|
||||
None => (),
|
||||
}
|
||||
// Likewise, C -> D and F -> G are added implicitly.
|
||||
|
||||
// Save C, F, so we can add the other outgoing edge.
|
||||
let to_next_arm = self.expr_index;
|
||||
|
||||
// The default edge does not get added since we also have an explicit edge,
|
||||
// so we also need to add an edge to the next node as well.
|
||||
//
|
||||
// This adds C -> D, F -> G
|
||||
self.drop_ranges.add_control_edge(self.expr_index, self.expr_index + 1);
|
||||
self.visit_expr(body);
|
||||
|
||||
// Save the end of the body so we can add the exit edge once we know where
|
||||
// the exit is.
|
||||
arm_end_ids.push(self.expr_index);
|
||||
|
||||
// Pass C to the next iteration, as well as vec![D]
|
||||
//
|
||||
// On the last round through, we pass F and vec![D, G] so that we can
|
||||
// add all the exit edges.
|
||||
(to_next_arm, arm_end_ids)
|
||||
},
|
||||
);
|
||||
// F -> H
|
||||
self.drop_ranges.add_control_edge(guard_exit, self.expr_index + 1);
|
||||
|
||||
arm_end_ids.into_iter().for_each(|arm_end| {
|
||||
// D -> H, G -> H
|
||||
self.drop_ranges.add_control_edge(arm_end, self.expr_index + 1)
|
||||
});
|
||||
}
|
||||
|
||||
ExprKind::Loop(body, label, ..) => {
|
||||
let loop_begin = self.expr_index + 1;
|
||||
self.label_stack.push((label, loop_begin));
|
||||
if body.stmts.is_empty() && body.expr.is_none() {
|
||||
// For empty loops we won't have updated self.expr_index after visiting the
|
||||
// body, meaning we'd get an edge from expr_index to expr_index + 1, but
|
||||
// instead we want an edge from expr_index + 1 to expr_index + 1.
|
||||
self.drop_ranges.add_control_edge(loop_begin, loop_begin);
|
||||
} else {
|
||||
self.visit_block(body);
|
||||
self.drop_ranges.add_control_edge(self.expr_index, loop_begin);
|
||||
}
|
||||
self.label_stack.pop();
|
||||
}
|
||||
// Find the loop entry by searching through the label stack for either the last entry
|
||||
// (if label is none), or the first entry where the label matches this one. The Loop
|
||||
// case maintains this stack mapping labels to the PostOrderId for the loop entry.
|
||||
ExprKind::Continue(hir::Destination { label, .. }, ..) => self
|
||||
.label_stack
|
||||
.iter()
|
||||
.rev()
|
||||
.find(|(loop_label, _)| label.is_none() || *loop_label == label)
|
||||
.map_or((), |(_, target)| {
|
||||
self.drop_ranges.add_control_edge(self.expr_index, *target)
|
||||
}),
|
||||
|
||||
ExprKind::Break(destination, value) => {
|
||||
// destination either points to an expression or to a block. We use
|
||||
// find_target_expression_from_destination to use the last expression of the block
|
||||
// if destination points to a block.
|
||||
//
|
||||
// We add an edge to the hir_id of the expression/block we are breaking out of, and
|
||||
// then in process_deferred_edges we will map this hir_id to its PostOrderId, which
|
||||
// will refer to the end of the block due to the post order traversal.
|
||||
if let Ok(target) = self.find_target_expression_from_destination(destination) {
|
||||
self.drop_ranges.add_control_edge_hir_id(self.expr_index, target)
|
||||
}
|
||||
|
||||
if let Some(value) = value {
|
||||
self.visit_expr(value);
|
||||
}
|
||||
}
|
||||
|
||||
ExprKind::Become(_call) => bug!("encountered a tail-call inside a generator"),
|
||||
|
||||
ExprKind::Call(f, args) => {
|
||||
self.visit_expr(f);
|
||||
for arg in args {
|
||||
self.visit_expr(arg);
|
||||
}
|
||||
|
||||
self.handle_uninhabited_return(expr);
|
||||
}
|
||||
ExprKind::MethodCall(_, receiver, exprs, _) => {
|
||||
self.visit_expr(receiver);
|
||||
for expr in exprs {
|
||||
self.visit_expr(expr);
|
||||
}
|
||||
|
||||
self.handle_uninhabited_return(expr);
|
||||
}
|
||||
|
||||
ExprKind::AddrOf(..)
|
||||
| ExprKind::Array(..)
|
||||
// FIXME(eholk): We probably need special handling for AssignOps. The ScopeTree builder
|
||||
// in region.rs runs both lhs then rhs and rhs then lhs and then sets all yields to be
|
||||
// the latest they show up in either traversal. With the older scope-based
|
||||
// approximation, this was fine, but it's probably not right now. What we probably want
|
||||
// to do instead is still run both orders, but consider anything that showed up as a
|
||||
// yield in either order.
|
||||
| ExprKind::AssignOp(..)
|
||||
| ExprKind::Binary(..)
|
||||
| ExprKind::Block(..)
|
||||
| ExprKind::Cast(..)
|
||||
| ExprKind::Closure { .. }
|
||||
| ExprKind::ConstBlock(..)
|
||||
| ExprKind::DropTemps(..)
|
||||
| ExprKind::Err(_)
|
||||
| ExprKind::Field(..)
|
||||
| ExprKind::Index(..)
|
||||
| ExprKind::InlineAsm(..)
|
||||
| ExprKind::OffsetOf(..)
|
||||
| ExprKind::Let(..)
|
||||
| ExprKind::Lit(..)
|
||||
| ExprKind::Path(..)
|
||||
| ExprKind::Repeat(..)
|
||||
| ExprKind::Ret(..)
|
||||
| ExprKind::Struct(..)
|
||||
| ExprKind::Tup(..)
|
||||
| ExprKind::Type(..)
|
||||
| ExprKind::Unary(..)
|
||||
| ExprKind::Yield(..) => intravisit::walk_expr(self, expr),
|
||||
}
|
||||
|
||||
self.expr_index = self.expr_index + 1;
|
||||
self.drop_ranges.add_node_mapping(expr.hir_id, self.expr_index);
|
||||
self.consume_expr(expr);
|
||||
if let Some(expr) = reinit {
|
||||
self.reinit_expr(expr);
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_pat(&mut self, pat: &'tcx hir::Pat<'tcx>) {
|
||||
intravisit::walk_pat(self, pat);
|
||||
|
||||
// Increment expr_count here to match what InteriorVisitor expects.
|
||||
self.expr_index = self.expr_index + 1;
|
||||
|
||||
// Save a node mapping to get better CFG visualization
|
||||
self.drop_ranges.add_node_mapping(pat.hir_id, self.expr_index);
|
||||
}
|
||||
}
|
||||
|
||||
impl DropRangesBuilder {
|
||||
fn new(
|
||||
tracked_values: impl Iterator<Item = TrackedValue>,
|
||||
hir: Map<'_>,
|
||||
num_exprs: usize,
|
||||
) -> Self {
|
||||
let mut tracked_value_map = UnordMap::<_, TrackedValueIndex>::default();
|
||||
let mut next = <_>::from(0u32);
|
||||
for value in tracked_values {
|
||||
for_each_consumable(hir, value, |value| {
|
||||
if let std::collections::hash_map::Entry::Vacant(e) = tracked_value_map.entry(value)
|
||||
{
|
||||
e.insert(next);
|
||||
next = next + 1;
|
||||
}
|
||||
});
|
||||
}
|
||||
debug!("hir_id_map: {:#?}", tracked_value_map);
|
||||
let num_values = tracked_value_map.len();
|
||||
Self {
|
||||
tracked_value_map,
|
||||
nodes: IndexVec::from_fn_n(|_| NodeInfo::new(num_values), num_exprs + 1),
|
||||
deferred_edges: <_>::default(),
|
||||
post_order_map: <_>::default(),
|
||||
}
|
||||
}
|
||||
|
||||
fn tracked_value_index(&self, tracked_value: TrackedValue) -> TrackedValueIndex {
|
||||
*self.tracked_value_map.get(&tracked_value).unwrap()
|
||||
}
|
||||
|
||||
/// Adds an entry in the mapping from HirIds to PostOrderIds
|
||||
///
|
||||
/// Needed so that `add_control_edge_hir_id` can work.
|
||||
fn add_node_mapping(&mut self, node_hir_id: HirId, post_order_id: PostOrderId) {
|
||||
self.post_order_map.insert(node_hir_id, post_order_id);
|
||||
}
|
||||
|
||||
/// Like add_control_edge, but uses a hir_id as the target.
|
||||
///
|
||||
/// This can be used for branches where we do not know the PostOrderId of the target yet,
|
||||
/// such as when handling `break` or `continue`.
|
||||
fn add_control_edge_hir_id(&mut self, from: PostOrderId, to: HirId) {
|
||||
self.deferred_edges.push((from, to));
|
||||
}
|
||||
|
||||
fn drop_at(&mut self, value: TrackedValue, location: PostOrderId) {
|
||||
let value = self.tracked_value_index(value);
|
||||
self.node_mut(location).drops.push(value);
|
||||
}
|
||||
|
||||
fn reinit_at(&mut self, value: TrackedValue, location: PostOrderId) {
|
||||
let value = match self.tracked_value_map.get(&value) {
|
||||
Some(value) => *value,
|
||||
// If there's no value, this is never consumed and therefore is never dropped. We can
|
||||
// ignore this.
|
||||
None => return,
|
||||
};
|
||||
self.node_mut(location).reinits.push(value);
|
||||
}
|
||||
|
||||
/// Looks up PostOrderId for any control edges added by HirId and adds a proper edge for them.
|
||||
///
|
||||
/// Should be called after visiting the HIR but before solving the control flow, otherwise some
|
||||
/// edges will be missed.
|
||||
fn process_deferred_edges(&mut self) {
|
||||
trace!("processing deferred edges. post_order_map={:#?}", self.post_order_map);
|
||||
let mut edges = vec![];
|
||||
swap(&mut edges, &mut self.deferred_edges);
|
||||
edges.into_iter().for_each(|(from, to)| {
|
||||
trace!("Adding deferred edge from {:?} to {:?}", from, to);
|
||||
let to = *self.post_order_map.get(&to).expect("Expression ID not found");
|
||||
trace!("target edge PostOrderId={:?}", to);
|
||||
self.add_control_edge(from, to)
|
||||
});
|
||||
}
|
||||
}
|
@ -1,92 +0,0 @@
|
||||
use super::{DropRangesBuilder, PostOrderId};
|
||||
use rustc_index::{bit_set::BitSet, IndexVec};
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
impl DropRangesBuilder {
|
||||
pub fn propagate_to_fixpoint(&mut self) {
|
||||
trace!("before fixpoint: {:#?}", self);
|
||||
let preds = self.compute_predecessors();
|
||||
|
||||
trace!("predecessors: {:#?}", preds.iter_enumerated().collect::<BTreeMap<_, _>>());
|
||||
|
||||
let mut new_state = BitSet::new_empty(self.num_values());
|
||||
let mut changed_nodes = BitSet::new_empty(self.nodes.len());
|
||||
let mut unchanged_mask = BitSet::new_filled(self.nodes.len());
|
||||
changed_nodes.insert(0u32.into());
|
||||
|
||||
let mut propagate = || {
|
||||
let mut changed = false;
|
||||
unchanged_mask.insert_all();
|
||||
for id in self.nodes.indices() {
|
||||
trace!("processing {:?}, changed_nodes: {:?}", id, changed_nodes);
|
||||
// Check if any predecessor has changed, and if not then short-circuit.
|
||||
//
|
||||
// We handle the start node specially, since it doesn't have any predecessors,
|
||||
// but we need to start somewhere.
|
||||
if match id.index() {
|
||||
0 => !changed_nodes.contains(id),
|
||||
_ => !preds[id].iter().any(|pred| changed_nodes.contains(*pred)),
|
||||
} {
|
||||
trace!("short-circuiting because none of {:?} have changed", preds[id]);
|
||||
unchanged_mask.remove(id);
|
||||
continue;
|
||||
}
|
||||
|
||||
if id.index() == 0 {
|
||||
new_state.clear();
|
||||
} else {
|
||||
// If we are not the start node and we have no predecessors, treat
|
||||
// everything as dropped because there's no way to get here anyway.
|
||||
new_state.insert_all();
|
||||
};
|
||||
|
||||
for pred in &preds[id] {
|
||||
new_state.intersect(&self.nodes[*pred].drop_state);
|
||||
}
|
||||
|
||||
for drop in &self.nodes[id].drops {
|
||||
new_state.insert(*drop);
|
||||
}
|
||||
|
||||
for reinit in &self.nodes[id].reinits {
|
||||
new_state.remove(*reinit);
|
||||
}
|
||||
|
||||
if self.nodes[id].drop_state.intersect(&new_state) {
|
||||
changed_nodes.insert(id);
|
||||
changed = true;
|
||||
} else {
|
||||
unchanged_mask.remove(id);
|
||||
}
|
||||
}
|
||||
|
||||
changed_nodes.intersect(&unchanged_mask);
|
||||
changed
|
||||
};
|
||||
|
||||
while propagate() {
|
||||
trace!("drop_state changed, re-running propagation");
|
||||
}
|
||||
|
||||
trace!("after fixpoint: {:#?}", self);
|
||||
}
|
||||
|
||||
fn compute_predecessors(&self) -> IndexVec<PostOrderId, Vec<PostOrderId>> {
|
||||
let mut preds = IndexVec::from_fn_n(|_| vec![], self.nodes.len());
|
||||
for (id, node) in self.nodes.iter_enumerated() {
|
||||
// If the node has no explicit successors, we assume that control
|
||||
// will from this node into the next one.
|
||||
//
|
||||
// If there are successors listed, then we assume that all
|
||||
// possible successors are given and we do not include the default.
|
||||
if node.successors.len() == 0 && id.index() != self.nodes.len() - 1 {
|
||||
preds[id + 1].push(id);
|
||||
} else {
|
||||
for succ in &node.successors {
|
||||
preds[*succ].push(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
preds
|
||||
}
|
||||
}
|
@ -1,96 +0,0 @@
|
||||
//! Implementation of GraphWalk for DropRanges so we can visualize the control
|
||||
//! flow graph when needed for debugging.
|
||||
|
||||
use rustc_graphviz as dot;
|
||||
use rustc_hir::{Expr, ExprKind, Node};
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
|
||||
use super::{DropRangesBuilder, PostOrderId};
|
||||
|
||||
/// Writes the CFG for DropRangesBuilder to a .dot file for visualization.
|
||||
///
|
||||
/// It is not normally called, but is kept around to easily add debugging
|
||||
/// code when needed.
|
||||
pub(super) fn write_graph_to_file(
|
||||
drop_ranges: &DropRangesBuilder,
|
||||
filename: &str,
|
||||
tcx: TyCtxt<'_>,
|
||||
) {
|
||||
dot::render(
|
||||
&DropRangesGraph { drop_ranges, tcx },
|
||||
&mut std::fs::File::create(filename).unwrap(),
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
struct DropRangesGraph<'a, 'tcx> {
|
||||
drop_ranges: &'a DropRangesBuilder,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
}
|
||||
|
||||
impl<'a> dot::GraphWalk<'a> for DropRangesGraph<'_, '_> {
|
||||
type Node = PostOrderId;
|
||||
|
||||
type Edge = (PostOrderId, PostOrderId);
|
||||
|
||||
fn nodes(&'a self) -> dot::Nodes<'a, Self::Node> {
|
||||
self.drop_ranges.nodes.iter_enumerated().map(|(i, _)| i).collect()
|
||||
}
|
||||
|
||||
fn edges(&'a self) -> dot::Edges<'a, Self::Edge> {
|
||||
self.drop_ranges
|
||||
.nodes
|
||||
.iter_enumerated()
|
||||
.flat_map(|(i, node)| {
|
||||
if node.successors.len() == 0 {
|
||||
vec![(i, i + 1)]
|
||||
} else {
|
||||
node.successors.iter().map(move |&s| (i, s)).collect()
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn source(&'a self, edge: &Self::Edge) -> Self::Node {
|
||||
edge.0
|
||||
}
|
||||
|
||||
fn target(&'a self, edge: &Self::Edge) -> Self::Node {
|
||||
edge.1
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> dot::Labeller<'a> for DropRangesGraph<'_, '_> {
|
||||
type Node = PostOrderId;
|
||||
|
||||
type Edge = (PostOrderId, PostOrderId);
|
||||
|
||||
fn graph_id(&'a self) -> dot::Id<'a> {
|
||||
dot::Id::new("drop_ranges").unwrap()
|
||||
}
|
||||
|
||||
fn node_id(&'a self, n: &Self::Node) -> dot::Id<'a> {
|
||||
dot::Id::new(format!("id{}", n.index())).unwrap()
|
||||
}
|
||||
|
||||
fn node_label(&'a self, n: &Self::Node) -> dot::LabelText<'a> {
|
||||
dot::LabelText::LabelStr(
|
||||
format!(
|
||||
"{n:?}: {}",
|
||||
self.drop_ranges
|
||||
.post_order_map
|
||||
.iter()
|
||||
.find(|(_hir_id, &post_order_id)| post_order_id == *n)
|
||||
.map_or("<unknown>".into(), |(hir_id, _)| format!(
|
||||
"{}{}",
|
||||
self.tcx.hir().node_to_string(*hir_id),
|
||||
match self.tcx.hir().find(*hir_id) {
|
||||
Some(Node::Expr(Expr { kind: ExprKind::Yield(..), .. })) => " (yield)",
|
||||
_ => "",
|
||||
}
|
||||
))
|
||||
)
|
||||
.into(),
|
||||
)
|
||||
}
|
||||
}
|
@ -1,306 +0,0 @@
|
||||
//! Drop range analysis finds the portions of the tree where a value is guaranteed to be dropped
|
||||
//! (i.e. moved, uninitialized, etc.). This is used to exclude the types of those values from the
|
||||
//! generator type. See `InteriorVisitor::record` for where the results of this analysis are used.
|
||||
//!
|
||||
//! There are three phases to this analysis:
|
||||
//! 1. Use `ExprUseVisitor` to identify the interesting values that are consumed and borrowed.
|
||||
//! 2. Use `DropRangeVisitor` to find where the interesting values are dropped or reinitialized,
|
||||
//! and also build a control flow graph.
|
||||
//! 3. Use `DropRanges::propagate_to_fixpoint` to flow the dropped/reinitialized information through
|
||||
//! the CFG and find the exact points where we know a value is definitely dropped.
|
||||
//!
|
||||
//! The end result is a data structure that maps the post-order index of each node in the HIR tree
|
||||
//! to a set of values that are known to be dropped at that location.
|
||||
|
||||
use self::cfg_build::build_control_flow_graph;
|
||||
use self::record_consumed_borrow::find_consumed_and_borrowed;
|
||||
use crate::FnCtxt;
|
||||
use hir::def_id::DefId;
|
||||
use hir::{Body, HirId, HirIdMap, Node};
|
||||
use rustc_data_structures::unord::{UnordMap, UnordSet};
|
||||
use rustc_hir as hir;
|
||||
use rustc_index::bit_set::BitSet;
|
||||
use rustc_index::IndexVec;
|
||||
use rustc_middle::hir::map::Map;
|
||||
use rustc_middle::hir::place::{PlaceBase, PlaceWithHirId};
|
||||
use rustc_middle::ty;
|
||||
use std::collections::BTreeMap;
|
||||
use std::fmt::Debug;
|
||||
|
||||
mod cfg_build;
|
||||
mod cfg_propagate;
|
||||
mod cfg_visualize;
|
||||
mod record_consumed_borrow;
|
||||
|
||||
pub fn compute_drop_ranges<'a, 'tcx>(
|
||||
fcx: &'a FnCtxt<'a, 'tcx>,
|
||||
def_id: DefId,
|
||||
body: &'tcx Body<'tcx>,
|
||||
) -> DropRanges {
|
||||
if fcx.sess().opts.unstable_opts.drop_tracking {
|
||||
let consumed_borrowed_places = find_consumed_and_borrowed(fcx, def_id, body);
|
||||
|
||||
let typeck_results = &fcx.typeck_results.borrow();
|
||||
let num_exprs = fcx.tcx.region_scope_tree(def_id).body_expr_count(body.id()).unwrap_or(0);
|
||||
let (mut drop_ranges, borrowed_temporaries) = build_control_flow_graph(
|
||||
&fcx,
|
||||
typeck_results,
|
||||
fcx.param_env,
|
||||
consumed_borrowed_places,
|
||||
body,
|
||||
num_exprs,
|
||||
);
|
||||
|
||||
drop_ranges.propagate_to_fixpoint();
|
||||
|
||||
debug!("borrowed_temporaries = {borrowed_temporaries:?}");
|
||||
DropRanges {
|
||||
tracked_value_map: drop_ranges.tracked_value_map,
|
||||
nodes: drop_ranges.nodes,
|
||||
borrowed_temporaries: Some(borrowed_temporaries),
|
||||
}
|
||||
} else {
|
||||
// If drop range tracking is not enabled, skip all the analysis and produce an
|
||||
// empty set of DropRanges.
|
||||
DropRanges {
|
||||
tracked_value_map: UnordMap::default(),
|
||||
nodes: IndexVec::new(),
|
||||
borrowed_temporaries: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Applies `f` to consumable node in the HIR subtree pointed to by `place`.
|
||||
///
|
||||
/// This includes the place itself, and if the place is a reference to a local
|
||||
/// variable then `f` is also called on the HIR node for that variable as well.
|
||||
///
|
||||
/// For example, if `place` points to `foo()`, then `f` is called once for the
|
||||
/// result of `foo`. On the other hand, if `place` points to `x` then `f` will
|
||||
/// be called both on the `ExprKind::Path` node that represents the expression
|
||||
/// as well as the HirId of the local `x` itself.
|
||||
fn for_each_consumable(hir: Map<'_>, place: TrackedValue, mut f: impl FnMut(TrackedValue)) {
|
||||
f(place);
|
||||
let node = hir.find(place.hir_id());
|
||||
if let Some(Node::Expr(expr)) = node {
|
||||
match expr.kind {
|
||||
hir::ExprKind::Path(hir::QPath::Resolved(
|
||||
_,
|
||||
hir::Path { res: hir::def::Res::Local(hir_id), .. },
|
||||
)) => {
|
||||
f(TrackedValue::Variable(*hir_id));
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rustc_index::newtype_index! {
|
||||
#[debug_format = "id({})"]
|
||||
pub struct PostOrderId {}
|
||||
}
|
||||
|
||||
rustc_index::newtype_index! {
|
||||
#[debug_format = "hidx({})"]
|
||||
pub struct TrackedValueIndex {}
|
||||
}
|
||||
|
||||
/// Identifies a value whose drop state we need to track.
|
||||
#[derive(PartialEq, Eq, Hash, Clone, Copy)]
|
||||
enum TrackedValue {
|
||||
/// Represents a named variable, such as a let binding, parameter, or upvar.
|
||||
///
|
||||
/// The HirId points to the variable's definition site.
|
||||
Variable(HirId),
|
||||
/// A value produced as a result of an expression.
|
||||
///
|
||||
/// The HirId points to the expression that returns this value.
|
||||
Temporary(HirId),
|
||||
}
|
||||
|
||||
impl Debug for TrackedValue {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
ty::tls::with_opt(|opt_tcx| {
|
||||
if let Some(tcx) = opt_tcx {
|
||||
write!(f, "{}", tcx.hir().node_to_string(self.hir_id()))
|
||||
} else {
|
||||
match self {
|
||||
Self::Variable(hir_id) => write!(f, "Variable({hir_id:?})"),
|
||||
Self::Temporary(hir_id) => write!(f, "Temporary({hir_id:?})"),
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl TrackedValue {
|
||||
fn hir_id(&self) -> HirId {
|
||||
match self {
|
||||
TrackedValue::Variable(hir_id) | TrackedValue::Temporary(hir_id) => *hir_id,
|
||||
}
|
||||
}
|
||||
|
||||
fn from_place_with_projections_allowed(place_with_id: &PlaceWithHirId<'_>) -> Self {
|
||||
match place_with_id.place.base {
|
||||
PlaceBase::Rvalue | PlaceBase::StaticItem => {
|
||||
TrackedValue::Temporary(place_with_id.hir_id)
|
||||
}
|
||||
PlaceBase::Local(hir_id)
|
||||
| PlaceBase::Upvar(ty::UpvarId { var_path: ty::UpvarPath { hir_id }, .. }) => {
|
||||
TrackedValue::Variable(hir_id)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a reason why we might not be able to convert a HirId or Place
|
||||
/// into a tracked value.
|
||||
#[derive(Debug)]
|
||||
enum TrackedValueConversionError {
|
||||
/// Place projects are not currently supported.
|
||||
///
|
||||
/// The reasoning around these is kind of subtle, so we choose to be more
|
||||
/// conservative around these for now. There is no reason in theory we
|
||||
/// cannot support these, we just have not implemented it yet.
|
||||
PlaceProjectionsNotSupported,
|
||||
}
|
||||
|
||||
impl TryFrom<&PlaceWithHirId<'_>> for TrackedValue {
|
||||
type Error = TrackedValueConversionError;
|
||||
|
||||
fn try_from(place_with_id: &PlaceWithHirId<'_>) -> Result<Self, Self::Error> {
|
||||
if !place_with_id.place.projections.is_empty() {
|
||||
debug!(
|
||||
"TrackedValue from PlaceWithHirId: {:?} has projections, which are not supported.",
|
||||
place_with_id
|
||||
);
|
||||
return Err(TrackedValueConversionError::PlaceProjectionsNotSupported);
|
||||
}
|
||||
|
||||
Ok(TrackedValue::from_place_with_projections_allowed(place_with_id))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DropRanges {
|
||||
tracked_value_map: UnordMap<TrackedValue, TrackedValueIndex>,
|
||||
nodes: IndexVec<PostOrderId, NodeInfo>,
|
||||
borrowed_temporaries: Option<UnordSet<HirId>>,
|
||||
}
|
||||
|
||||
impl DropRanges {
|
||||
pub fn is_dropped_at(&self, hir_id: HirId, location: usize) -> bool {
|
||||
self.tracked_value_map
|
||||
.get(&TrackedValue::Temporary(hir_id))
|
||||
.or(self.tracked_value_map.get(&TrackedValue::Variable(hir_id)))
|
||||
.cloned()
|
||||
.is_some_and(|tracked_value_id| {
|
||||
self.expect_node(location.into()).drop_state.contains(tracked_value_id)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn is_borrowed_temporary(&self, expr: &hir::Expr<'_>) -> bool {
|
||||
if let Some(b) = &self.borrowed_temporaries { b.contains(&expr.hir_id) } else { true }
|
||||
}
|
||||
|
||||
/// Returns a reference to the NodeInfo for a node, panicking if it does not exist
|
||||
fn expect_node(&self, id: PostOrderId) -> &NodeInfo {
|
||||
&self.nodes[id]
|
||||
}
|
||||
}
|
||||
|
||||
/// Tracks information needed to compute drop ranges.
|
||||
struct DropRangesBuilder {
|
||||
/// The core of DropRangesBuilder is a set of nodes, which each represent
|
||||
/// one expression. We primarily refer to them by their index in a
|
||||
/// post-order traversal of the HIR tree, since this is what
|
||||
/// generator_interior uses to talk about yield positions.
|
||||
///
|
||||
/// This IndexVec keeps the relevant details for each node. See the
|
||||
/// NodeInfo struct for more details, but this information includes things
|
||||
/// such as the set of control-flow successors, which variables are dropped
|
||||
/// or reinitialized, and whether each variable has been inferred to be
|
||||
/// known-dropped or potentially reinitialized at each point.
|
||||
nodes: IndexVec<PostOrderId, NodeInfo>,
|
||||
/// We refer to values whose drop state we are tracking by the HirId of
|
||||
/// where they are defined. Within a NodeInfo, however, we store the
|
||||
/// drop-state in a bit vector indexed by a HirIdIndex
|
||||
/// (see NodeInfo::drop_state). The hir_id_map field stores the mapping
|
||||
/// from HirIds to the HirIdIndex that is used to represent that value in
|
||||
/// bitvector.
|
||||
tracked_value_map: UnordMap<TrackedValue, TrackedValueIndex>,
|
||||
|
||||
/// When building the control flow graph, we don't always know the
|
||||
/// post-order index of the target node at the point we encounter it.
|
||||
/// For example, this happens with break and continue. In those cases,
|
||||
/// we store a pair of the PostOrderId of the source and the HirId
|
||||
/// of the target. Once we have gathered all of these edges, we make a
|
||||
/// pass over the set of deferred edges (see process_deferred_edges in
|
||||
/// cfg_build.rs), look up the PostOrderId for the target (since now the
|
||||
/// post-order index for all nodes is known), and add missing control flow
|
||||
/// edges.
|
||||
deferred_edges: Vec<(PostOrderId, HirId)>,
|
||||
/// This maps HirIds of expressions to their post-order index. It is
|
||||
/// used in process_deferred_edges to correctly add back-edges.
|
||||
post_order_map: HirIdMap<PostOrderId>,
|
||||
}
|
||||
|
||||
impl Debug for DropRangesBuilder {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("DropRanges")
|
||||
.field("hir_id_map", &self.tracked_value_map)
|
||||
.field("post_order_maps", &self.post_order_map)
|
||||
.field("nodes", &self.nodes.iter_enumerated().collect::<BTreeMap<_, _>>())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
/// DropRanges keeps track of what values are definitely dropped at each point in the code.
|
||||
///
|
||||
/// Values of interest are defined by the hir_id of their place. Locations in code are identified
|
||||
/// by their index in the post-order traversal. At its core, DropRanges maps
|
||||
/// (hir_id, post_order_id) -> bool, where a true value indicates that the value is definitely
|
||||
/// dropped at the point of the node identified by post_order_id.
|
||||
impl DropRangesBuilder {
|
||||
/// Returns the number of values (hir_ids) that are tracked
|
||||
fn num_values(&self) -> usize {
|
||||
self.tracked_value_map.len()
|
||||
}
|
||||
|
||||
fn node_mut(&mut self, id: PostOrderId) -> &mut NodeInfo {
|
||||
let size = self.num_values();
|
||||
self.nodes.ensure_contains_elem(id, || NodeInfo::new(size))
|
||||
}
|
||||
|
||||
fn add_control_edge(&mut self, from: PostOrderId, to: PostOrderId) {
|
||||
trace!("adding control edge from {:?} to {:?}", from, to);
|
||||
self.node_mut(from).successors.push(to);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct NodeInfo {
|
||||
/// IDs of nodes that can follow this one in the control flow
|
||||
///
|
||||
/// If the vec is empty, then control proceeds to the next node.
|
||||
successors: Vec<PostOrderId>,
|
||||
|
||||
/// List of hir_ids that are dropped by this node.
|
||||
drops: Vec<TrackedValueIndex>,
|
||||
|
||||
/// List of hir_ids that are reinitialized by this node.
|
||||
reinits: Vec<TrackedValueIndex>,
|
||||
|
||||
/// Set of values that are definitely dropped at this point.
|
||||
drop_state: BitSet<TrackedValueIndex>,
|
||||
}
|
||||
|
||||
impl NodeInfo {
|
||||
fn new(num_values: usize) -> Self {
|
||||
Self {
|
||||
successors: vec![],
|
||||
drops: vec![],
|
||||
reinits: vec![],
|
||||
drop_state: BitSet::new_filled(num_values),
|
||||
}
|
||||
}
|
||||
}
|
@ -1,242 +0,0 @@
|
||||
use super::TrackedValue;
|
||||
use crate::{
|
||||
expr_use_visitor::{self, ExprUseVisitor},
|
||||
FnCtxt,
|
||||
};
|
||||
use hir::{def_id::DefId, Body, HirId, HirIdMap};
|
||||
use rustc_data_structures::{fx::FxIndexSet, unord::UnordSet};
|
||||
use rustc_hir as hir;
|
||||
use rustc_middle::ty::{ParamEnv, TyCtxt};
|
||||
use rustc_middle::{
|
||||
hir::place::{PlaceBase, Projection, ProjectionKind},
|
||||
ty::TypeVisitableExt,
|
||||
};
|
||||
|
||||
pub(super) fn find_consumed_and_borrowed<'a, 'tcx>(
|
||||
fcx: &'a FnCtxt<'a, 'tcx>,
|
||||
def_id: DefId,
|
||||
body: &'tcx Body<'tcx>,
|
||||
) -> ConsumedAndBorrowedPlaces {
|
||||
let mut expr_use_visitor = ExprUseDelegate::new(fcx.tcx, fcx.param_env);
|
||||
expr_use_visitor.consume_body(fcx, def_id, body);
|
||||
expr_use_visitor.places
|
||||
}
|
||||
|
||||
pub(super) struct ConsumedAndBorrowedPlaces {
|
||||
/// Records the variables/expressions that are dropped by a given expression.
|
||||
///
|
||||
/// The key is the hir-id of the expression, and the value is a set or hir-ids for variables
|
||||
/// or values that are consumed by that expression.
|
||||
///
|
||||
/// Note that this set excludes "partial drops" -- for example, a statement like `drop(x.y)` is
|
||||
/// not considered a drop of `x`, although it would be a drop of `x.y`.
|
||||
pub(super) consumed: HirIdMap<FxIndexSet<TrackedValue>>,
|
||||
|
||||
/// A set of hir-ids of values or variables that are borrowed at some point within the body.
|
||||
pub(super) borrowed: UnordSet<TrackedValue>,
|
||||
|
||||
/// A set of hir-ids of values or variables that are borrowed at some point within the body.
|
||||
pub(super) borrowed_temporaries: UnordSet<HirId>,
|
||||
}
|
||||
|
||||
/// Works with ExprUseVisitor to find interesting values for the drop range analysis.
|
||||
///
|
||||
/// Interesting values are those that are either dropped or borrowed. For dropped values, we also
|
||||
/// record the parent expression, which is the point where the drop actually takes place.
|
||||
struct ExprUseDelegate<'tcx> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
param_env: ParamEnv<'tcx>,
|
||||
places: ConsumedAndBorrowedPlaces,
|
||||
}
|
||||
|
||||
impl<'tcx> ExprUseDelegate<'tcx> {
|
||||
fn new(tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> Self {
|
||||
Self {
|
||||
tcx,
|
||||
param_env,
|
||||
places: ConsumedAndBorrowedPlaces {
|
||||
consumed: <_>::default(),
|
||||
borrowed: <_>::default(),
|
||||
borrowed_temporaries: <_>::default(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn consume_body(&mut self, fcx: &'_ FnCtxt<'_, 'tcx>, def_id: DefId, body: &'tcx Body<'tcx>) {
|
||||
// Run ExprUseVisitor to find where values are consumed.
|
||||
ExprUseVisitor::new(
|
||||
self,
|
||||
&fcx.infcx,
|
||||
def_id.expect_local(),
|
||||
fcx.param_env,
|
||||
&fcx.typeck_results.borrow(),
|
||||
)
|
||||
.consume_body(body);
|
||||
}
|
||||
|
||||
fn mark_consumed(&mut self, consumer: HirId, target: TrackedValue) {
|
||||
self.places.consumed.entry(consumer).or_insert_with(|| <_>::default());
|
||||
|
||||
debug!(?consumer, ?target, "mark_consumed");
|
||||
self.places.consumed.get_mut(&consumer).map(|places| places.insert(target));
|
||||
}
|
||||
|
||||
fn borrow_place(&mut self, place_with_id: &expr_use_visitor::PlaceWithHirId<'tcx>) {
|
||||
self.places
|
||||
.borrowed
|
||||
.insert(TrackedValue::from_place_with_projections_allowed(place_with_id));
|
||||
|
||||
// Ordinarily a value is consumed by it's parent, but in the special case of a
|
||||
// borrowed RValue, we create a reference that lives as long as the temporary scope
|
||||
// for that expression (typically, the innermost statement, but sometimes the enclosing
|
||||
// block). We record this fact here so that later in generator_interior
|
||||
// we can use the correct scope.
|
||||
//
|
||||
// We special case borrows through a dereference (`&*x`, `&mut *x` where `x` is
|
||||
// some rvalue expression), since these are essentially a copy of a pointer.
|
||||
// In other words, this borrow does not refer to the
|
||||
// temporary (`*x`), but to the referent (whatever `x` is a borrow of).
|
||||
//
|
||||
// We were considering that we might encounter problems down the line if somehow,
|
||||
// some part of the compiler were to look at this result and try to use it to
|
||||
// drive a borrowck-like analysis (this does not currently happen, as of this writing).
|
||||
// But even this should be fine, because the lifetime of the dereferenced reference
|
||||
// found in the rvalue is only significant as an intermediate 'link' to the value we
|
||||
// are producing, and we separately track whether that value is live over a yield.
|
||||
// Example:
|
||||
//
|
||||
// ```notrust
|
||||
// fn identity<T>(x: &mut T) -> &mut T { x }
|
||||
// let a: A = ...;
|
||||
// let y: &'y mut A = &mut *identity(&'a mut a);
|
||||
// ^^^^^^^^^^^^^^^^^^^^^^^^^ the borrow we are talking about
|
||||
// ```
|
||||
//
|
||||
// The expression `*identity(...)` is a deref of an rvalue,
|
||||
// where the `identity(...)` (the rvalue) produces a return type
|
||||
// of `&'rv mut A`, where `'a: 'rv`. We then assign this result to
|
||||
// `'y`, resulting in (transitively) `'a: 'y` (i.e., while `y` is in use,
|
||||
// `a` will be considered borrowed). Other parts of the code will ensure
|
||||
// that if `y` is live over a yield, `&'y mut A` appears in the generator
|
||||
// state. If `'y` is live, then any sound region analysis must conclude
|
||||
// that `'a` is also live. So if this causes a bug, blame some other
|
||||
// part of the code!
|
||||
let is_deref = place_with_id
|
||||
.place
|
||||
.projections
|
||||
.iter()
|
||||
.any(|Projection { kind, .. }| *kind == ProjectionKind::Deref);
|
||||
|
||||
if let (false, PlaceBase::Rvalue) = (is_deref, place_with_id.place.base) {
|
||||
self.places.borrowed_temporaries.insert(place_with_id.hir_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> expr_use_visitor::Delegate<'tcx> for ExprUseDelegate<'tcx> {
|
||||
fn consume(
|
||||
&mut self,
|
||||
place_with_id: &expr_use_visitor::PlaceWithHirId<'tcx>,
|
||||
diag_expr_id: HirId,
|
||||
) {
|
||||
let hir = self.tcx.hir();
|
||||
let parent = match hir.opt_parent_id(place_with_id.hir_id) {
|
||||
Some(parent) => parent,
|
||||
None => place_with_id.hir_id,
|
||||
};
|
||||
debug!(
|
||||
"consume {:?}; diag_expr_id={}, using parent {}",
|
||||
place_with_id,
|
||||
hir.node_to_string(diag_expr_id),
|
||||
hir.node_to_string(parent)
|
||||
);
|
||||
|
||||
if let Ok(tracked_value) = place_with_id.try_into() {
|
||||
self.mark_consumed(parent, tracked_value)
|
||||
}
|
||||
}
|
||||
|
||||
fn borrow(
|
||||
&mut self,
|
||||
place_with_id: &expr_use_visitor::PlaceWithHirId<'tcx>,
|
||||
diag_expr_id: HirId,
|
||||
bk: rustc_middle::ty::BorrowKind,
|
||||
) {
|
||||
debug!(
|
||||
"borrow: place_with_id = {place_with_id:#?}, diag_expr_id={diag_expr_id:#?}, \
|
||||
borrow_kind={bk:#?}"
|
||||
);
|
||||
|
||||
self.borrow_place(place_with_id);
|
||||
}
|
||||
|
||||
fn copy(
|
||||
&mut self,
|
||||
place_with_id: &expr_use_visitor::PlaceWithHirId<'tcx>,
|
||||
_diag_expr_id: HirId,
|
||||
) {
|
||||
debug!("copy: place_with_id = {place_with_id:?}");
|
||||
|
||||
self.places
|
||||
.borrowed
|
||||
.insert(TrackedValue::from_place_with_projections_allowed(place_with_id));
|
||||
|
||||
// For copied we treat this mostly like a borrow except that we don't add the place
|
||||
// to borrowed_temporaries because the copy is consumed.
|
||||
}
|
||||
|
||||
fn mutate(
|
||||
&mut self,
|
||||
assignee_place: &expr_use_visitor::PlaceWithHirId<'tcx>,
|
||||
diag_expr_id: HirId,
|
||||
) {
|
||||
debug!("mutate {assignee_place:?}; diag_expr_id={diag_expr_id:?}");
|
||||
|
||||
if assignee_place.place.base == PlaceBase::Rvalue
|
||||
&& assignee_place.place.projections.is_empty()
|
||||
{
|
||||
// Assigning to an Rvalue is illegal unless done through a dereference. We would have
|
||||
// already gotten a type error, so we will just return here.
|
||||
return;
|
||||
}
|
||||
|
||||
// If the type being assigned needs dropped, then the mutation counts as a borrow
|
||||
// since it is essentially doing `Drop::drop(&mut x); x = new_value;`.
|
||||
let ty = self.tcx.erase_regions(assignee_place.place.base_ty);
|
||||
if ty.has_infer() {
|
||||
self.tcx.sess.delay_span_bug(
|
||||
self.tcx.hir().span(assignee_place.hir_id),
|
||||
format!("inference variables in {ty}"),
|
||||
);
|
||||
} else if ty.needs_drop(self.tcx, self.param_env) {
|
||||
self.places
|
||||
.borrowed
|
||||
.insert(TrackedValue::from_place_with_projections_allowed(assignee_place));
|
||||
}
|
||||
}
|
||||
|
||||
fn bind(
|
||||
&mut self,
|
||||
binding_place: &expr_use_visitor::PlaceWithHirId<'tcx>,
|
||||
diag_expr_id: HirId,
|
||||
) {
|
||||
debug!("bind {binding_place:?}; diag_expr_id={diag_expr_id:?}");
|
||||
}
|
||||
|
||||
fn fake_read(
|
||||
&mut self,
|
||||
place_with_id: &expr_use_visitor::PlaceWithHirId<'tcx>,
|
||||
cause: rustc_middle::mir::FakeReadCause,
|
||||
diag_expr_id: HirId,
|
||||
) {
|
||||
debug!(
|
||||
"fake_read place_with_id={place_with_id:?}; cause={cause:?}; diag_expr_id={diag_expr_id:?}"
|
||||
);
|
||||
|
||||
// fake reads happen in places like the scrutinee of a match expression.
|
||||
// we treat those as a borrow, much like a copy: the idea is that we are
|
||||
// transiently creating a `&T` ref that we can read from to observe the current
|
||||
// value (this `&T` is immediately dropped afterwards).
|
||||
self.borrow_place(place_with_id);
|
||||
}
|
||||
}
|
@ -1,723 +0,0 @@
|
||||
//! This calculates the types which has storage which lives across a suspension point in a
|
||||
//! generator from the perspective of typeck. The actual types used at runtime
|
||||
//! is calculated in `rustc_mir_transform::generator` and may be a subset of the
|
||||
//! types computed here.
|
||||
|
||||
use self::drop_ranges::DropRanges;
|
||||
use super::FnCtxt;
|
||||
use rustc_data_structures::fx::{FxHashSet, FxIndexSet};
|
||||
use rustc_errors::{pluralize, DelayDm};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def::{CtorKind, DefKind, Res};
|
||||
use rustc_hir::def_id::{DefId, LocalDefId};
|
||||
use rustc_hir::hir_id::HirIdSet;
|
||||
use rustc_hir::intravisit::{self, Visitor};
|
||||
use rustc_hir::{Arm, Expr, ExprKind, Guard, HirId, Pat, PatKind};
|
||||
use rustc_infer::infer::{DefineOpaqueTypes, RegionVariableOrigin};
|
||||
use rustc_middle::middle::region::{self, Scope, ScopeData, YieldData};
|
||||
use rustc_middle::traits::ObligationCauseCode;
|
||||
use rustc_middle::ty::fold::FnMutDelegate;
|
||||
use rustc_middle::ty::{self, BoundVariableKind, RvalueScopes, Ty, TyCtxt, TypeVisitableExt};
|
||||
use rustc_span::symbol::sym;
|
||||
use rustc_span::Span;
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
|
||||
mod drop_ranges;
|
||||
|
||||
struct InteriorVisitor<'a, 'tcx> {
|
||||
fcx: &'a FnCtxt<'a, 'tcx>,
|
||||
region_scope_tree: &'a region::ScopeTree,
|
||||
types: FxIndexSet<ty::GeneratorInteriorTypeCause<'tcx>>,
|
||||
rvalue_scopes: &'a RvalueScopes,
|
||||
expr_count: usize,
|
||||
kind: hir::GeneratorKind,
|
||||
prev_unresolved_span: Option<Span>,
|
||||
linted_values: HirIdSet,
|
||||
drop_ranges: DropRanges,
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> InteriorVisitor<'a, 'tcx> {
|
||||
fn record(
|
||||
&mut self,
|
||||
ty: Ty<'tcx>,
|
||||
hir_id: HirId,
|
||||
scope: Option<region::Scope>,
|
||||
expr: Option<&'tcx Expr<'tcx>>,
|
||||
source_span: Span,
|
||||
) {
|
||||
use rustc_span::DUMMY_SP;
|
||||
|
||||
let ty = self.fcx.resolve_vars_if_possible(ty);
|
||||
|
||||
debug!(
|
||||
"attempting to record type ty={:?}; hir_id={:?}; scope={:?}; expr={:?}; source_span={:?}; expr_count={:?}",
|
||||
ty, hir_id, scope, expr, source_span, self.expr_count,
|
||||
);
|
||||
|
||||
let live_across_yield = scope
|
||||
.map(|s| {
|
||||
self.region_scope_tree.yield_in_scope(s).and_then(|yield_data| {
|
||||
// If we are recording an expression that is the last yield
|
||||
// in the scope, or that has a postorder CFG index larger
|
||||
// than the one of all of the yields, then its value can't
|
||||
// be storage-live (and therefore live) at any of the yields.
|
||||
//
|
||||
// See the mega-comment at `yield_in_scope` for a proof.
|
||||
|
||||
yield_data
|
||||
.iter()
|
||||
.find(|yield_data| {
|
||||
debug!(
|
||||
"comparing counts yield: {} self: {}, source_span = {:?}",
|
||||
yield_data.expr_and_pat_count, self.expr_count, source_span
|
||||
);
|
||||
|
||||
if self
|
||||
.is_dropped_at_yield_location(hir_id, yield_data.expr_and_pat_count)
|
||||
{
|
||||
debug!("value is dropped at yield point; not recording");
|
||||
return false;
|
||||
}
|
||||
|
||||
// If it is a borrowing happening in the guard,
|
||||
// it needs to be recorded regardless because they
|
||||
// do live across this yield point.
|
||||
yield_data.expr_and_pat_count >= self.expr_count
|
||||
})
|
||||
.cloned()
|
||||
})
|
||||
})
|
||||
.unwrap_or_else(|| {
|
||||
Some(YieldData { span: DUMMY_SP, expr_and_pat_count: 0, source: self.kind.into() })
|
||||
});
|
||||
|
||||
if let Some(yield_data) = live_across_yield {
|
||||
debug!(
|
||||
"type in expr = {:?}, scope = {:?}, type = {:?}, count = {}, yield_span = {:?}",
|
||||
expr, scope, ty, self.expr_count, yield_data.span
|
||||
);
|
||||
|
||||
if let Some((unresolved_term, unresolved_type_span)) =
|
||||
self.fcx.first_unresolved_const_or_ty_var(&ty)
|
||||
{
|
||||
// If unresolved type isn't a ty_var then unresolved_type_span is None
|
||||
let span = self
|
||||
.prev_unresolved_span
|
||||
.unwrap_or_else(|| unresolved_type_span.unwrap_or(source_span));
|
||||
|
||||
// If we encounter an int/float variable, then inference fallback didn't
|
||||
// finish due to some other error. Don't emit spurious additional errors.
|
||||
if let Some(unresolved_ty) = unresolved_term.ty()
|
||||
&& let ty::Infer(ty::InferTy::IntVar(_) | ty::InferTy::FloatVar(_)) = unresolved_ty.kind()
|
||||
{
|
||||
self.fcx
|
||||
.tcx
|
||||
.sess
|
||||
.delay_span_bug(span, format!("Encountered var {unresolved_term:?}"));
|
||||
} else {
|
||||
let note = format!(
|
||||
"the type is part of the {} because of this {}",
|
||||
self.kind.descr(),
|
||||
yield_data.source
|
||||
);
|
||||
|
||||
self.fcx
|
||||
.need_type_info_err_in_generator(self.kind, span, unresolved_term)
|
||||
.span_note(yield_data.span, note)
|
||||
.emit();
|
||||
}
|
||||
} else {
|
||||
// Insert the type into the ordered set.
|
||||
let scope_span = scope.map(|s| s.span(self.fcx.tcx, self.region_scope_tree));
|
||||
|
||||
if !self.linted_values.contains(&hir_id) {
|
||||
check_must_not_suspend_ty(
|
||||
self.fcx,
|
||||
ty,
|
||||
hir_id,
|
||||
SuspendCheckData {
|
||||
expr,
|
||||
source_span,
|
||||
yield_span: yield_data.span,
|
||||
plural_len: 1,
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
self.linted_values.insert(hir_id);
|
||||
}
|
||||
|
||||
self.types.insert(ty::GeneratorInteriorTypeCause {
|
||||
span: source_span,
|
||||
ty,
|
||||
scope_span,
|
||||
yield_span: yield_data.span,
|
||||
expr: expr.map(|e| e.hir_id),
|
||||
});
|
||||
}
|
||||
} else {
|
||||
debug!(
|
||||
"no type in expr = {:?}, count = {:?}, span = {:?}",
|
||||
expr,
|
||||
self.expr_count,
|
||||
expr.map(|e| e.span)
|
||||
);
|
||||
if let Some((unresolved_type, unresolved_type_span)) =
|
||||
self.fcx.first_unresolved_const_or_ty_var(&ty)
|
||||
{
|
||||
debug!(
|
||||
"remained unresolved_type = {:?}, unresolved_type_span: {:?}",
|
||||
unresolved_type, unresolved_type_span
|
||||
);
|
||||
self.prev_unresolved_span = unresolved_type_span;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// If drop tracking is enabled, consult drop_ranges to see if a value is
|
||||
/// known to be dropped at a yield point and therefore can be omitted from
|
||||
/// the generator witness.
|
||||
fn is_dropped_at_yield_location(&self, value_hir_id: HirId, yield_location: usize) -> bool {
|
||||
// short-circuit if drop tracking is not enabled.
|
||||
if !self.fcx.sess().opts.unstable_opts.drop_tracking {
|
||||
return false;
|
||||
}
|
||||
|
||||
self.drop_ranges.is_dropped_at(value_hir_id, yield_location)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn resolve_interior<'a, 'tcx>(
|
||||
fcx: &'a FnCtxt<'a, 'tcx>,
|
||||
def_id: DefId,
|
||||
generator_def_id: LocalDefId,
|
||||
body_id: hir::BodyId,
|
||||
interior: Ty<'tcx>,
|
||||
kind: hir::GeneratorKind,
|
||||
) {
|
||||
let body = fcx.tcx.hir().body(body_id);
|
||||
let typeck_results = fcx.inh.typeck_results.borrow();
|
||||
let mut visitor = InteriorVisitor {
|
||||
fcx,
|
||||
types: FxIndexSet::default(),
|
||||
region_scope_tree: fcx.tcx.region_scope_tree(def_id),
|
||||
rvalue_scopes: &typeck_results.rvalue_scopes,
|
||||
expr_count: 0,
|
||||
kind,
|
||||
prev_unresolved_span: None,
|
||||
linted_values: <_>::default(),
|
||||
drop_ranges: drop_ranges::compute_drop_ranges(fcx, def_id, body),
|
||||
};
|
||||
intravisit::walk_body(&mut visitor, body);
|
||||
|
||||
// Check that we visited the same amount of expressions as the RegionResolutionVisitor
|
||||
let region_expr_count = fcx.tcx.region_scope_tree(def_id).body_expr_count(body_id).unwrap();
|
||||
assert_eq!(region_expr_count, visitor.expr_count);
|
||||
|
||||
// The types are already kept in insertion order.
|
||||
let types = visitor.types;
|
||||
|
||||
if fcx.tcx.features().unsized_locals || fcx.tcx.features().unsized_fn_params {
|
||||
for interior_ty in &types {
|
||||
fcx.require_type_is_sized(
|
||||
interior_ty.ty,
|
||||
interior_ty.span,
|
||||
ObligationCauseCode::SizedGeneratorInterior(generator_def_id),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// The types in the generator interior contain lifetimes local to the generator itself,
|
||||
// which should not be exposed outside of the generator. Therefore, we replace these
|
||||
// lifetimes with existentially-bound lifetimes, which reflect the exact value of the
|
||||
// lifetimes not being known by users.
|
||||
//
|
||||
// These lifetimes are used in auto trait impl checking (for example,
|
||||
// if a Sync generator contains an &'α T, we need to check whether &'α T: Sync),
|
||||
// so knowledge of the exact relationships between them isn't particularly important.
|
||||
|
||||
debug!("types in generator {:?}, span = {:?}", types, body.value.span);
|
||||
|
||||
// We want to deduplicate if the lifetimes are the same modulo some non-informative counter.
|
||||
// So, we need to actually do two passes: first by type to anonymize (preserving information
|
||||
// required for diagnostics), then a second pass over all captured types to reassign disjoint
|
||||
// region indices.
|
||||
let mut captured_tys = FxHashSet::default();
|
||||
let type_causes: Vec<_> = types
|
||||
.into_iter()
|
||||
.filter_map(|mut cause| {
|
||||
// Replace all regions inside the generator interior with late bound regions.
|
||||
// Note that each region slot in the types gets a new fresh late bound region,
|
||||
// which means that none of the regions inside relate to any other, even if
|
||||
// typeck had previously found constraints that would cause them to be related.
|
||||
|
||||
let mut counter = 0;
|
||||
let mut mk_bound_region = |kind| {
|
||||
let var = ty::BoundVar::from_u32(counter);
|
||||
counter += 1;
|
||||
ty::BoundRegion { var, kind }
|
||||
};
|
||||
let ty = fcx.normalize(cause.span, cause.ty);
|
||||
let ty = fcx.tcx.fold_regions(ty, |region, current_depth| {
|
||||
let br = match region.kind() {
|
||||
ty::ReVar(vid) => {
|
||||
let origin = fcx.region_var_origin(vid);
|
||||
match origin {
|
||||
RegionVariableOrigin::EarlyBoundRegion(span, _) => {
|
||||
mk_bound_region(ty::BrAnon(Some(span)))
|
||||
}
|
||||
_ => mk_bound_region(ty::BrAnon(None)),
|
||||
}
|
||||
}
|
||||
ty::ReEarlyBound(region) => {
|
||||
mk_bound_region(ty::BrNamed(region.def_id, region.name))
|
||||
}
|
||||
ty::ReLateBound(_, ty::BoundRegion { kind, .. })
|
||||
| ty::ReFree(ty::FreeRegion { bound_region: kind, .. }) => match kind {
|
||||
ty::BoundRegionKind::BrAnon(span) => mk_bound_region(ty::BrAnon(span)),
|
||||
ty::BoundRegionKind::BrNamed(def_id, sym) => {
|
||||
mk_bound_region(ty::BrNamed(def_id, sym))
|
||||
}
|
||||
ty::BoundRegionKind::BrEnv => mk_bound_region(ty::BrAnon(None)),
|
||||
},
|
||||
_ => mk_bound_region(ty::BrAnon(None)),
|
||||
};
|
||||
let r = ty::Region::new_late_bound(fcx.tcx, current_depth, br);
|
||||
r
|
||||
});
|
||||
captured_tys.insert(ty).then(|| {
|
||||
cause.ty = ty;
|
||||
cause
|
||||
})
|
||||
})
|
||||
.collect();
|
||||
|
||||
let mut bound_vars: SmallVec<[BoundVariableKind; 4]> = smallvec![];
|
||||
let mut counter = 0;
|
||||
// Optimization: If there is only one captured type, then we don't actually
|
||||
// need to fold and reindex (since the first type doesn't change).
|
||||
let type_causes = if captured_tys.len() > 0 {
|
||||
// Optimization: Use `replace_escaping_bound_vars_uncached` instead of
|
||||
// `fold_regions`, since we only have late bound regions, and it skips
|
||||
// types without bound regions.
|
||||
fcx.tcx.replace_escaping_bound_vars_uncached(
|
||||
type_causes,
|
||||
FnMutDelegate {
|
||||
regions: &mut |br| {
|
||||
let kind = br.kind;
|
||||
let var = ty::BoundVar::from_usize(bound_vars.len());
|
||||
bound_vars.push(ty::BoundVariableKind::Region(kind));
|
||||
counter += 1;
|
||||
ty::Region::new_late_bound(
|
||||
fcx.tcx,
|
||||
ty::INNERMOST,
|
||||
ty::BoundRegion { var, kind },
|
||||
)
|
||||
},
|
||||
types: &mut |b| bug!("unexpected bound ty in binder: {b:?}"),
|
||||
consts: &mut |b, ty| bug!("unexpected bound ct in binder: {b:?} {ty}"),
|
||||
},
|
||||
)
|
||||
} else {
|
||||
type_causes
|
||||
};
|
||||
|
||||
// Extract type components to build the witness type.
|
||||
let type_list = fcx.tcx.mk_type_list_from_iter(type_causes.iter().map(|cause| cause.ty));
|
||||
let bound_vars = fcx.tcx.mk_bound_variable_kinds(&bound_vars);
|
||||
let witness =
|
||||
Ty::new_generator_witness(fcx.tcx, ty::Binder::bind_with_vars(type_list, bound_vars));
|
||||
|
||||
drop(typeck_results);
|
||||
// Store the generator types and spans into the typeck results for this generator.
|
||||
fcx.inh.typeck_results.borrow_mut().generator_interior_types =
|
||||
ty::Binder::bind_with_vars(type_causes, bound_vars);
|
||||
|
||||
debug!(
|
||||
"types in generator after region replacement {:?}, span = {:?}",
|
||||
witness, body.value.span
|
||||
);
|
||||
|
||||
// Unify the type variable inside the generator with the new witness
|
||||
match fcx.at(&fcx.misc(body.value.span), fcx.param_env).eq(
|
||||
DefineOpaqueTypes::No,
|
||||
interior,
|
||||
witness,
|
||||
) {
|
||||
Ok(ok) => fcx.register_infer_ok_obligations(ok),
|
||||
_ => bug!("failed to relate {interior} and {witness}"),
|
||||
}
|
||||
}
|
||||
|
||||
// This visitor has to have the same visit_expr calls as RegionResolutionVisitor in
|
||||
// librustc_middle/middle/region.rs since `expr_count` is compared against the results
|
||||
// there.
|
||||
impl<'a, 'tcx> Visitor<'tcx> for InteriorVisitor<'a, 'tcx> {
|
||||
fn visit_arm(&mut self, arm: &'tcx Arm<'tcx>) {
|
||||
let Arm { guard, pat, body, .. } = arm;
|
||||
self.visit_pat(pat);
|
||||
if let Some(ref g) = guard {
|
||||
{
|
||||
// If there is a guard, we need to count all variables bound in the pattern as
|
||||
// borrowed for the entire guard body, regardless of whether they are accessed.
|
||||
// We do this by walking the pattern bindings and recording `&T` for any `x: T`
|
||||
// that is bound.
|
||||
|
||||
struct ArmPatCollector<'a, 'b, 'tcx> {
|
||||
interior_visitor: &'a mut InteriorVisitor<'b, 'tcx>,
|
||||
scope: Scope,
|
||||
}
|
||||
|
||||
impl<'a, 'b, 'tcx> Visitor<'tcx> for ArmPatCollector<'a, 'b, 'tcx> {
|
||||
fn visit_pat(&mut self, pat: &'tcx Pat<'tcx>) {
|
||||
intravisit::walk_pat(self, pat);
|
||||
if let PatKind::Binding(_, id, ident, ..) = pat.kind {
|
||||
let ty =
|
||||
self.interior_visitor.fcx.typeck_results.borrow().node_type(id);
|
||||
let tcx = self.interior_visitor.fcx.tcx;
|
||||
let ty = Ty::new_ref(
|
||||
tcx,
|
||||
// Use `ReErased` as `resolve_interior` is going to replace all the
|
||||
// regions anyway.
|
||||
tcx.lifetimes.re_erased,
|
||||
ty::TypeAndMut { ty, mutbl: hir::Mutability::Not },
|
||||
);
|
||||
self.interior_visitor.record(
|
||||
ty,
|
||||
id,
|
||||
Some(self.scope),
|
||||
None,
|
||||
ident.span,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ArmPatCollector {
|
||||
interior_visitor: self,
|
||||
scope: Scope { id: g.body().hir_id.local_id, data: ScopeData::Node },
|
||||
}
|
||||
.visit_pat(pat);
|
||||
}
|
||||
|
||||
match g {
|
||||
Guard::If(ref e) => {
|
||||
self.visit_expr(e);
|
||||
}
|
||||
Guard::IfLet(ref l) => {
|
||||
self.visit_let_expr(l);
|
||||
}
|
||||
}
|
||||
}
|
||||
self.visit_expr(body);
|
||||
}
|
||||
|
||||
fn visit_pat(&mut self, pat: &'tcx Pat<'tcx>) {
|
||||
intravisit::walk_pat(self, pat);
|
||||
|
||||
self.expr_count += 1;
|
||||
|
||||
if let PatKind::Binding(..) = pat.kind {
|
||||
let scope = self.region_scope_tree.var_scope(pat.hir_id.local_id).unwrap();
|
||||
let ty = self.fcx.typeck_results.borrow().pat_ty(pat);
|
||||
self.record(ty, pat.hir_id, Some(scope), None, pat.span);
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
|
||||
match &expr.kind {
|
||||
ExprKind::Call(callee, args) => match &callee.kind {
|
||||
ExprKind::Path(qpath) => {
|
||||
let res = self.fcx.typeck_results.borrow().qpath_res(qpath, callee.hir_id);
|
||||
match res {
|
||||
// Direct calls never need to keep the callee `ty::FnDef`
|
||||
// ZST in a temporary, so skip its type, just in case it
|
||||
// can significantly complicate the generator type.
|
||||
Res::Def(
|
||||
DefKind::Fn | DefKind::AssocFn | DefKind::Ctor(_, CtorKind::Fn),
|
||||
_,
|
||||
) => {
|
||||
// NOTE(eddyb) this assumes a path expression has
|
||||
// no nested expressions to keep track of.
|
||||
self.expr_count += 1;
|
||||
|
||||
// Record the rest of the call expression normally.
|
||||
for arg in *args {
|
||||
self.visit_expr(arg);
|
||||
}
|
||||
}
|
||||
_ => intravisit::walk_expr(self, expr),
|
||||
}
|
||||
}
|
||||
_ => intravisit::walk_expr(self, expr),
|
||||
},
|
||||
_ => intravisit::walk_expr(self, expr),
|
||||
}
|
||||
|
||||
self.expr_count += 1;
|
||||
|
||||
debug!("is_borrowed_temporary: {:?}", self.drop_ranges.is_borrowed_temporary(expr));
|
||||
|
||||
let ty = self.fcx.typeck_results.borrow().expr_ty_adjusted_opt(expr);
|
||||
|
||||
// Typically, the value produced by an expression is consumed by its parent in some way,
|
||||
// so we only have to check if the parent contains a yield (note that the parent may, for
|
||||
// example, store the value into a local variable, but then we already consider local
|
||||
// variables to be live across their scope).
|
||||
//
|
||||
// However, in the case of temporary values, we are going to store the value into a
|
||||
// temporary on the stack that is live for the current temporary scope and then return a
|
||||
// reference to it. That value may be live across the entire temporary scope.
|
||||
//
|
||||
// There's another subtlety: if the type has an observable drop, it must be dropped after
|
||||
// the yield, even if it's not borrowed or referenced after the yield. Ideally this would
|
||||
// *only* happen for types with observable drop, not all types which wrap them, but that
|
||||
// doesn't match the behavior of MIR borrowck and causes ICEs. See the FIXME comment in
|
||||
// tests/ui/generator/drop-tracking-parent-expression.rs.
|
||||
let scope = if self.drop_ranges.is_borrowed_temporary(expr)
|
||||
|| ty.map_or(true, |ty| {
|
||||
// Avoid ICEs in needs_drop.
|
||||
let ty = self.fcx.resolve_vars_if_possible(ty);
|
||||
let ty = self.fcx.tcx.erase_regions(ty);
|
||||
if ty.has_infer() {
|
||||
self.fcx
|
||||
.tcx
|
||||
.sess
|
||||
.delay_span_bug(expr.span, format!("inference variables in {ty}"));
|
||||
true
|
||||
} else {
|
||||
ty.needs_drop(self.fcx.tcx, self.fcx.param_env)
|
||||
}
|
||||
}) {
|
||||
self.rvalue_scopes.temporary_scope(self.region_scope_tree, expr.hir_id.local_id)
|
||||
} else {
|
||||
let parent_expr = self
|
||||
.fcx
|
||||
.tcx
|
||||
.hir()
|
||||
.parent_iter(expr.hir_id)
|
||||
.find(|(_, node)| matches!(node, hir::Node::Expr(_)))
|
||||
.map(|(id, _)| id);
|
||||
debug!("parent_expr: {:?}", parent_expr);
|
||||
match parent_expr {
|
||||
Some(parent) => Some(Scope { id: parent.local_id, data: ScopeData::Node }),
|
||||
None => {
|
||||
self.rvalue_scopes.temporary_scope(self.region_scope_tree, expr.hir_id.local_id)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// If there are adjustments, then record the final type --
|
||||
// this is the actual value that is being produced.
|
||||
if let Some(adjusted_ty) = ty {
|
||||
self.record(adjusted_ty, expr.hir_id, scope, Some(expr), expr.span);
|
||||
}
|
||||
|
||||
// Also record the unadjusted type (which is the only type if
|
||||
// there are no adjustments). The reason for this is that the
|
||||
// unadjusted value is sometimes a "temporary" that would wind
|
||||
// up in a MIR temporary.
|
||||
//
|
||||
// As an example, consider an expression like `vec![].push(x)`.
|
||||
// Here, the `vec![]` would wind up MIR stored into a
|
||||
// temporary variable `t` which we can borrow to invoke
|
||||
// `<Vec<_>>::push(&mut t, x)`.
|
||||
//
|
||||
// Note that an expression can have many adjustments, and we
|
||||
// are just ignoring those intermediate types. This is because
|
||||
// those intermediate values are always linearly "consumed" by
|
||||
// the other adjustments, and hence would never be directly
|
||||
// captured in the MIR.
|
||||
//
|
||||
// (Note that this partly relies on the fact that the `Deref`
|
||||
// traits always return references, which means their content
|
||||
// can be reborrowed without needing to spill to a temporary.
|
||||
// If this were not the case, then we could conceivably have
|
||||
// to create intermediate temporaries.)
|
||||
//
|
||||
// The type table might not have information for this expression
|
||||
// if it is in a malformed scope. (#66387)
|
||||
if let Some(ty) = self.fcx.typeck_results.borrow().expr_ty_opt(expr) {
|
||||
self.record(ty, expr.hir_id, scope, Some(expr), expr.span);
|
||||
} else {
|
||||
self.fcx.tcx.sess.delay_span_bug(expr.span, "no type for node");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct SuspendCheckData<'a, 'tcx> {
|
||||
expr: Option<&'tcx Expr<'tcx>>,
|
||||
source_span: Span,
|
||||
yield_span: Span,
|
||||
descr_pre: &'a str,
|
||||
descr_post: &'a str,
|
||||
plural_len: usize,
|
||||
}
|
||||
|
||||
// Returns whether it emitted a diagnostic or not
|
||||
// Note that this fn and the proceeding one are based on the code
|
||||
// for creating must_use diagnostics
|
||||
//
|
||||
// Note that this technique was chosen over things like a `Suspend` marker trait
|
||||
// as it is simpler and has precedent in the compiler
|
||||
fn check_must_not_suspend_ty<'tcx>(
|
||||
fcx: &FnCtxt<'_, 'tcx>,
|
||||
ty: Ty<'tcx>,
|
||||
hir_id: HirId,
|
||||
data: SuspendCheckData<'_, 'tcx>,
|
||||
) -> bool {
|
||||
if ty.is_unit()
|
||||
// FIXME: should this check `Ty::is_inhabited_from`. This query is not available in this stage
|
||||
// of typeck (before ReVar and RePlaceholder are removed), but may remove noise, like in
|
||||
// `must_use`
|
||||
// || !ty.is_inhabited_from(fcx.tcx, fcx.tcx.parent_module(hir_id).to_def_id(), fcx.param_env)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
let plural_suffix = pluralize!(data.plural_len);
|
||||
|
||||
debug!("Checking must_not_suspend for {}", ty);
|
||||
|
||||
match *ty.kind() {
|
||||
ty::Adt(..) if ty.is_box() => {
|
||||
let boxed_ty = ty.boxed_ty();
|
||||
let descr_pre = &format!("{}boxed ", data.descr_pre);
|
||||
check_must_not_suspend_ty(fcx, boxed_ty, hir_id, SuspendCheckData { descr_pre, ..data })
|
||||
}
|
||||
ty::Adt(def, _) => check_must_not_suspend_def(fcx.tcx, def.did(), hir_id, data),
|
||||
// FIXME: support adding the attribute to TAITs
|
||||
ty::Alias(ty::Opaque, ty::AliasTy { def_id: def, .. }) => {
|
||||
let mut has_emitted = false;
|
||||
for &(predicate, _) in fcx.tcx.explicit_item_bounds(def).skip_binder() {
|
||||
// We only look at the `DefId`, so it is safe to skip the binder here.
|
||||
if let ty::ClauseKind::Trait(ref poly_trait_predicate) =
|
||||
predicate.kind().skip_binder()
|
||||
{
|
||||
let def_id = poly_trait_predicate.trait_ref.def_id;
|
||||
let descr_pre = &format!("{}implementer{} of ", data.descr_pre, plural_suffix);
|
||||
if check_must_not_suspend_def(
|
||||
fcx.tcx,
|
||||
def_id,
|
||||
hir_id,
|
||||
SuspendCheckData { descr_pre, ..data },
|
||||
) {
|
||||
has_emitted = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
has_emitted
|
||||
}
|
||||
ty::Dynamic(binder, _, _) => {
|
||||
let mut has_emitted = false;
|
||||
for predicate in binder.iter() {
|
||||
if let ty::ExistentialPredicate::Trait(ref trait_ref) = predicate.skip_binder() {
|
||||
let def_id = trait_ref.def_id;
|
||||
let descr_post = &format!(" trait object{}{}", plural_suffix, data.descr_post);
|
||||
if check_must_not_suspend_def(
|
||||
fcx.tcx,
|
||||
def_id,
|
||||
hir_id,
|
||||
SuspendCheckData { descr_post, ..data },
|
||||
) {
|
||||
has_emitted = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
has_emitted
|
||||
}
|
||||
ty::Tuple(fields) => {
|
||||
let mut has_emitted = false;
|
||||
let comps = match data.expr.map(|e| &e.kind) {
|
||||
Some(hir::ExprKind::Tup(comps)) if comps.len() == fields.len() => Some(comps),
|
||||
_ => None,
|
||||
};
|
||||
for (i, ty) in fields.iter().enumerate() {
|
||||
let descr_post = &format!(" in tuple element {i}");
|
||||
let span = comps.and_then(|c| c.get(i)).map(|e| e.span).unwrap_or(data.source_span);
|
||||
if check_must_not_suspend_ty(
|
||||
fcx,
|
||||
ty,
|
||||
hir_id,
|
||||
SuspendCheckData {
|
||||
descr_post,
|
||||
expr: comps.and_then(|comps| comps.get(i)),
|
||||
source_span: span,
|
||||
..data
|
||||
},
|
||||
) {
|
||||
has_emitted = true;
|
||||
}
|
||||
}
|
||||
has_emitted
|
||||
}
|
||||
ty::Array(ty, len) => {
|
||||
let descr_pre = &format!("{}array{} of ", data.descr_pre, plural_suffix);
|
||||
let target_usize =
|
||||
len.try_eval_target_usize(fcx.tcx, fcx.param_env).unwrap_or(0) as usize;
|
||||
let plural_len = target_usize.saturating_add(1);
|
||||
check_must_not_suspend_ty(
|
||||
fcx,
|
||||
ty,
|
||||
hir_id,
|
||||
SuspendCheckData { descr_pre, plural_len, ..data },
|
||||
)
|
||||
}
|
||||
// If drop tracking is enabled, we want to look through references, since the referent
|
||||
// may not be considered live across the await point.
|
||||
ty::Ref(_region, ty, _mutability) if fcx.sess().opts.unstable_opts.drop_tracking => {
|
||||
let descr_pre = &format!("{}reference{} to ", data.descr_pre, plural_suffix);
|
||||
check_must_not_suspend_ty(fcx, ty, hir_id, SuspendCheckData { descr_pre, ..data })
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn check_must_not_suspend_def(
|
||||
tcx: TyCtxt<'_>,
|
||||
def_id: DefId,
|
||||
hir_id: HirId,
|
||||
data: SuspendCheckData<'_, '_>,
|
||||
) -> bool {
|
||||
if let Some(attr) = tcx.get_attr(def_id, sym::must_not_suspend) {
|
||||
tcx.struct_span_lint_hir(
|
||||
rustc_session::lint::builtin::MUST_NOT_SUSPEND,
|
||||
hir_id,
|
||||
data.source_span,
|
||||
DelayDm(|| {
|
||||
format!(
|
||||
"{}`{}`{} held across a suspend point, but should not be",
|
||||
data.descr_pre,
|
||||
tcx.def_path_str(def_id),
|
||||
data.descr_post,
|
||||
)
|
||||
}),
|
||||
|lint| {
|
||||
// add span pointing to the offending yield/await
|
||||
lint.span_label(data.yield_span, "the value is held across this suspend point");
|
||||
|
||||
// Add optional reason note
|
||||
if let Some(note) = attr.value_str() {
|
||||
// FIXME(guswynn): consider formatting this better
|
||||
lint.span_note(data.source_span, note.to_string());
|
||||
}
|
||||
|
||||
// Add some quick suggestions on what to do
|
||||
// FIXME: can `drop` work as a suggestion here as well?
|
||||
lint.span_help(
|
||||
data.source_span,
|
||||
"consider using a block (`{ ... }`) \
|
||||
to shrink the value's scope, ending before the suspend point",
|
||||
);
|
||||
|
||||
lint
|
||||
},
|
||||
);
|
||||
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
@ -32,7 +32,6 @@ pub mod expr_use_visitor;
|
||||
mod fallback;
|
||||
mod fn_ctxt;
|
||||
mod gather_locals;
|
||||
mod generator_interior;
|
||||
mod inherited;
|
||||
mod intrinsicck;
|
||||
mod mem_categorization;
|
||||
|
@ -63,7 +63,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
wbcx.visit_coercion_casts();
|
||||
wbcx.visit_user_provided_tys();
|
||||
wbcx.visit_user_provided_sigs();
|
||||
wbcx.visit_generator_interior_types();
|
||||
wbcx.visit_generator_interior();
|
||||
wbcx.visit_offset_of_container_types();
|
||||
|
||||
wbcx.typeck_results.rvalue_scopes =
|
||||
@ -538,11 +538,9 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
|
||||
);
|
||||
}
|
||||
|
||||
fn visit_generator_interior_types(&mut self) {
|
||||
fn visit_generator_interior(&mut self) {
|
||||
let fcx_typeck_results = self.fcx.typeck_results.borrow();
|
||||
assert_eq!(fcx_typeck_results.hir_owner, self.typeck_results.hir_owner);
|
||||
self.typeck_results.generator_interior_types =
|
||||
fcx_typeck_results.generator_interior_types.clone();
|
||||
self.tcx().with_stable_hashing_context(move |ref hcx| {
|
||||
for (&expr_def_id, predicates) in
|
||||
fcx_typeck_results.generator_interior_predicates.to_sorted(hcx, false).into_iter()
|
||||
|
@ -14,8 +14,7 @@ use rustc_span::{symbol::Ident, BytePos, Span};
|
||||
|
||||
use crate::fluent_generated as fluent;
|
||||
use crate::infer::error_reporting::{
|
||||
need_type_info::{GeneratorKindAsDiagArg, UnderspecifiedArgKind},
|
||||
nice_region_error::placeholder_error::Highlighted,
|
||||
need_type_info::UnderspecifiedArgKind, nice_region_error::placeholder_error::Highlighted,
|
||||
ObligationCauseAsDiagArg,
|
||||
};
|
||||
|
||||
@ -86,16 +85,6 @@ pub struct AmbiguousReturn<'a> {
|
||||
pub multi_suggestions: Vec<SourceKindMultiSuggestion<'a>>,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(infer_need_type_info_in_generator, code = "E0698")]
|
||||
pub struct NeedTypeInfoInGenerator<'a> {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
pub generator_kind: GeneratorKindAsDiagArg,
|
||||
#[subdiagnostic]
|
||||
pub bad_label: InferenceBadError<'a>,
|
||||
}
|
||||
|
||||
// Used when a better one isn't available
|
||||
#[derive(Subdiagnostic)]
|
||||
#[label(infer_label_bad)]
|
||||
|
@ -478,7 +478,28 @@ impl<'tcx> ToTrace<'tcx> for ty::FnSig<'tcx> {
|
||||
a: Self,
|
||||
b: Self,
|
||||
) -> TypeTrace<'tcx> {
|
||||
TypeTrace { cause: cause.clone(), values: Sigs(ExpectedFound::new(a_is_expected, a, b)) }
|
||||
TypeTrace {
|
||||
cause: cause.clone(),
|
||||
values: PolySigs(ExpectedFound::new(
|
||||
a_is_expected,
|
||||
ty::Binder::dummy(a),
|
||||
ty::Binder::dummy(b),
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> ToTrace<'tcx> for ty::PolyFnSig<'tcx> {
|
||||
fn to_trace(
|
||||
cause: &ObligationCause<'tcx>,
|
||||
a_is_expected: bool,
|
||||
a: Self,
|
||||
b: Self,
|
||||
) -> TypeTrace<'tcx> {
|
||||
TypeTrace {
|
||||
cause: cause.clone(),
|
||||
values: PolySigs(ExpectedFound::new(a_is_expected, a, b)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -459,7 +459,6 @@ impl<'cx, 'tcx> TypeFolder<TyCtxt<'tcx>> for Canonicalizer<'cx, 'tcx> {
|
||||
ty::Closure(..)
|
||||
| ty::Generator(..)
|
||||
| ty::GeneratorWitness(..)
|
||||
| ty::GeneratorWitnessMIR(..)
|
||||
| ty::Bool
|
||||
| ty::Char
|
||||
| ty::Int(..)
|
||||
|
@ -119,26 +119,6 @@ impl<'tcx> TypeRelation<'tcx> for Equate<'_, '_, 'tcx> {
|
||||
.obligations,
|
||||
);
|
||||
}
|
||||
// Optimization of GeneratorWitness relation since we know that all
|
||||
// free regions are replaced with bound regions during construction.
|
||||
// This greatly speeds up equating of GeneratorWitness.
|
||||
(&ty::GeneratorWitness(a_types), &ty::GeneratorWitness(b_types)) => {
|
||||
let a_types = infcx.tcx.anonymize_bound_vars(a_types);
|
||||
let b_types = infcx.tcx.anonymize_bound_vars(b_types);
|
||||
if a_types.bound_vars() == b_types.bound_vars() {
|
||||
let (a_types, b_types) = infcx.instantiate_binder_with_placeholders(
|
||||
a_types.map_bound(|a_types| (a_types, b_types.skip_binder())),
|
||||
);
|
||||
for (a, b) in std::iter::zip(a_types, b_types) {
|
||||
self.relate(a, b)?;
|
||||
}
|
||||
} else {
|
||||
return Err(ty::error::TypeError::Sorts(ty::relate::expected_found(
|
||||
self, a, b,
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
_ => {
|
||||
self.fields.infcx.super_combine_tys(self, a, b)?;
|
||||
}
|
||||
|
@ -1660,7 +1660,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
||||
_ => (false, Mismatch::Fixed("type")),
|
||||
}
|
||||
}
|
||||
ValuePairs::Sigs(infer::ExpectedFound { expected, found }) => {
|
||||
ValuePairs::PolySigs(infer::ExpectedFound { expected, found }) => {
|
||||
OpaqueTypesVisitor::visit_expected_found(self.tcx, expected, found, span)
|
||||
.report(diag);
|
||||
(false, Mismatch::Fixed("signature"))
|
||||
@ -2232,15 +2232,12 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
||||
ret => ret,
|
||||
}
|
||||
}
|
||||
infer::Sigs(exp_found) => {
|
||||
infer::PolySigs(exp_found) => {
|
||||
let exp_found = self.resolve_vars_if_possible(exp_found);
|
||||
if exp_found.references_error() {
|
||||
return None;
|
||||
}
|
||||
let (exp, fnd) = self.cmp_fn_sig(
|
||||
&ty::Binder::dummy(exp_found.expected),
|
||||
&ty::Binder::dummy(exp_found.found),
|
||||
);
|
||||
let (exp, fnd) = self.cmp_fn_sig(&exp_found.expected, &exp_found.found);
|
||||
Some((exp, fnd, None, None))
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::errors::{
|
||||
AmbiguousImpl, AmbiguousReturn, AnnotationRequired, InferenceBadError, NeedTypeInfoInGenerator,
|
||||
AmbiguousImpl, AmbiguousReturn, AnnotationRequired, InferenceBadError,
|
||||
SourceKindMultiSuggestion, SourceKindSubdiag,
|
||||
};
|
||||
use crate::infer::error_reporting::TypeErrCtxt;
|
||||
@ -595,39 +595,6 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> InferCtxt<'tcx> {
|
||||
pub fn need_type_info_err_in_generator(
|
||||
&self,
|
||||
kind: hir::GeneratorKind,
|
||||
span: Span,
|
||||
ty: ty::Term<'tcx>,
|
||||
) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
|
||||
let ty = self.resolve_vars_if_possible(ty);
|
||||
let data = self.extract_inference_diagnostics_data(ty.into(), None);
|
||||
|
||||
NeedTypeInfoInGenerator {
|
||||
bad_label: data.make_bad_error(span),
|
||||
span,
|
||||
generator_kind: GeneratorKindAsDiagArg(kind),
|
||||
}
|
||||
.into_diagnostic(&self.tcx.sess.parse_sess.span_diagnostic)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct GeneratorKindAsDiagArg(pub hir::GeneratorKind);
|
||||
|
||||
impl IntoDiagnosticArg for GeneratorKindAsDiagArg {
|
||||
fn into_diagnostic_arg(self) -> rustc_errors::DiagnosticArgValue<'static> {
|
||||
let kind = match self.0 {
|
||||
hir::GeneratorKind::Async(hir::AsyncGeneratorKind::Block) => "async_block",
|
||||
hir::GeneratorKind::Async(hir::AsyncGeneratorKind::Closure) => "async_closure",
|
||||
hir::GeneratorKind::Async(hir::AsyncGeneratorKind::Fn) => "async_fn",
|
||||
hir::GeneratorKind::Gen => "generator",
|
||||
};
|
||||
rustc_errors::DiagnosticArgValue::Str(kind.into())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct InferSource<'tcx> {
|
||||
span: Span,
|
||||
|
@ -35,14 +35,14 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
|
||||
&& let (Subtype(sup_trace), Subtype(sub_trace)) = (&sup_origin, &sub_origin)
|
||||
&& let CompareImplItemObligation { trait_item_def_id, .. } = sub_trace.cause.code()
|
||||
&& sub_trace.values == sup_trace.values
|
||||
&& let ValuePairs::Sigs(ExpectedFound { expected, found }) = sub_trace.values
|
||||
&& let ValuePairs::PolySigs(ExpectedFound { expected, found }) = sub_trace.values
|
||||
{
|
||||
// FIXME(compiler-errors): Don't like that this needs `Ty`s, but
|
||||
// all of the region highlighting machinery only deals with those.
|
||||
let guar = self.emit_err(
|
||||
var_origin.span(),
|
||||
Ty::new_fn_ptr(self.cx.tcx,ty::Binder::dummy(expected)),
|
||||
Ty::new_fn_ptr(self.cx.tcx,ty::Binder::dummy(found)),
|
||||
Ty::new_fn_ptr(self.cx.tcx, expected),
|
||||
Ty::new_fn_ptr(self.cx.tcx, found),
|
||||
*trait_item_def_id,
|
||||
);
|
||||
return Some(guar);
|
||||
|
@ -616,9 +616,13 @@ fn foo(&self) -> Self::T { String::new() }
|
||||
for item in &items[..] {
|
||||
if let hir::AssocItemKind::Type = item.kind {
|
||||
let assoc_ty = tcx.type_of(item.id.owner_id).instantiate_identity();
|
||||
|
||||
if self.infcx.can_eq(param_env, assoc_ty, found) {
|
||||
diag.span_label(item.span, "expected this associated type");
|
||||
if let hir::Defaultness::Default { has_value: true } = tcx.defaultness(item.id.owner_id)
|
||||
&& self.infcx.can_eq(param_env, assoc_ty, found)
|
||||
{
|
||||
diag.span_label(
|
||||
item.span,
|
||||
format!("associated type is `default` and may be overridden"),
|
||||
);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -383,7 +383,7 @@ pub enum ValuePairs<'tcx> {
|
||||
Aliases(ExpectedFound<ty::AliasTy<'tcx>>),
|
||||
TraitRefs(ExpectedFound<ty::TraitRef<'tcx>>),
|
||||
PolyTraitRefs(ExpectedFound<ty::PolyTraitRef<'tcx>>),
|
||||
Sigs(ExpectedFound<ty::FnSig<'tcx>>),
|
||||
PolySigs(ExpectedFound<ty::PolyFnSig<'tcx>>),
|
||||
ExistentialTraitRef(ExpectedFound<ty::PolyExistentialTraitRef<'tcx>>),
|
||||
ExistentialProjection(ExpectedFound<ty::PolyExistentialProjection<'tcx>>),
|
||||
}
|
||||
|
@ -112,7 +112,7 @@ fn compute_components<'tcx>(
|
||||
}
|
||||
|
||||
// All regions are bound inside a witness
|
||||
ty::GeneratorWitness(..) | ty::GeneratorWitnessMIR(..) => (),
|
||||
ty::GeneratorWitness(..) => (),
|
||||
|
||||
// OutlivesTypeParameterEnv -- the actual checking that `X:'a`
|
||||
// is implied by the environment is done in regionck.
|
||||
|
@ -147,25 +147,6 @@ impl<'tcx> TypeRelation<'tcx> for Sub<'_, '_, 'tcx> {
|
||||
);
|
||||
Ok(a)
|
||||
}
|
||||
// Optimization of GeneratorWitness relation since we know that all
|
||||
// free regions are replaced with bound regions during construction.
|
||||
// This greatly speeds up subtyping of GeneratorWitness.
|
||||
(&ty::GeneratorWitness(a_types), &ty::GeneratorWitness(b_types)) => {
|
||||
let a_types = infcx.tcx.anonymize_bound_vars(a_types);
|
||||
let b_types = infcx.tcx.anonymize_bound_vars(b_types);
|
||||
if a_types.bound_vars() == b_types.bound_vars() {
|
||||
let (a_types, b_types) = infcx.instantiate_binder_with_placeholders(
|
||||
a_types.map_bound(|a_types| (a_types, b_types.skip_binder())),
|
||||
);
|
||||
for (a, b) in std::iter::zip(a_types, b_types) {
|
||||
self.relate(a, b)?;
|
||||
}
|
||||
Ok(a)
|
||||
} else {
|
||||
Err(ty::error::TypeError::Sorts(ty::relate::expected_found(self, a, b)))
|
||||
}
|
||||
}
|
||||
|
||||
_ => {
|
||||
self.fields.infcx.super_combine_tys(self, a, b)?;
|
||||
Ok(a)
|
||||
|
@ -9,7 +9,7 @@ use rustc_data_structures::sync::Lrc;
|
||||
use rustc_errors::registry::Registry;
|
||||
use rustc_errors::{ErrorGuaranteed, Handler};
|
||||
use rustc_lint::LintStore;
|
||||
use rustc_middle::query::{ExternProviders, Providers};
|
||||
use rustc_middle::util::Providers;
|
||||
use rustc_middle::{bug, ty};
|
||||
use rustc_parse::maybe_new_parser_from_source_str;
|
||||
use rustc_query_impl::QueryCtxt;
|
||||
@ -37,7 +37,7 @@ pub struct Compiler {
|
||||
pub(crate) sess: Lrc<Session>,
|
||||
codegen_backend: Lrc<dyn CodegenBackend>,
|
||||
pub(crate) register_lints: Option<Box<dyn Fn(&Session, &mut LintStore) + Send + Sync>>,
|
||||
pub(crate) override_queries: Option<fn(&Session, &mut Providers, &mut ExternProviders)>,
|
||||
pub(crate) override_queries: Option<fn(&Session, &mut Providers)>,
|
||||
}
|
||||
|
||||
impl Compiler {
|
||||
@ -271,7 +271,7 @@ pub struct Config {
|
||||
/// the list of queries.
|
||||
///
|
||||
/// The second parameter is local providers and the third parameter is external providers.
|
||||
pub override_queries: Option<fn(&Session, &mut Providers, &mut ExternProviders)>,
|
||||
pub override_queries: Option<fn(&Session, &mut Providers)>,
|
||||
|
||||
/// This is a callback from the driver that is called to create a codegen backend.
|
||||
pub make_codegen_backend:
|
||||
|
@ -25,7 +25,7 @@ pub mod util;
|
||||
|
||||
pub use callbacks::setup_callbacks;
|
||||
pub use interface::{run_compiler, Config};
|
||||
pub use passes::{DEFAULT_EXTERN_QUERY_PROVIDERS, DEFAULT_QUERY_PROVIDERS};
|
||||
pub use passes::DEFAULT_QUERY_PROVIDERS;
|
||||
pub use queries::Queries;
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -18,8 +18,8 @@ use rustc_lint::{unerased_lint_store, BufferedEarlyLint, EarlyCheckNode, LintSto
|
||||
use rustc_metadata::creader::CStore;
|
||||
use rustc_middle::arena::Arena;
|
||||
use rustc_middle::dep_graph::DepGraph;
|
||||
use rustc_middle::query::{ExternProviders, Providers};
|
||||
use rustc_middle::ty::{self, GlobalCtxt, RegisteredTools, TyCtxt};
|
||||
use rustc_middle::util::Providers;
|
||||
use rustc_mir_build as mir_build;
|
||||
use rustc_parse::{parse_crate_from_file, parse_crate_from_source_str, validate_attr};
|
||||
use rustc_passes::{self, abi_test, hir_stats, layout_test};
|
||||
@ -675,13 +675,6 @@ pub static DEFAULT_QUERY_PROVIDERS: LazyLock<Providers> = LazyLock::new(|| {
|
||||
*providers
|
||||
});
|
||||
|
||||
pub static DEFAULT_EXTERN_QUERY_PROVIDERS: LazyLock<ExternProviders> = LazyLock::new(|| {
|
||||
let mut extern_providers = ExternProviders::default();
|
||||
rustc_metadata::provide_extern(&mut extern_providers);
|
||||
rustc_codegen_ssa::provide_extern(&mut extern_providers);
|
||||
extern_providers
|
||||
});
|
||||
|
||||
pub fn create_global_ctxt<'tcx>(
|
||||
compiler: &'tcx Compiler,
|
||||
crate_types: Vec<CrateType>,
|
||||
@ -702,14 +695,11 @@ pub fn create_global_ctxt<'tcx>(
|
||||
let query_result_on_disk_cache = rustc_incremental::load_query_result_cache(sess);
|
||||
|
||||
let codegen_backend = compiler.codegen_backend();
|
||||
let mut local_providers = *DEFAULT_QUERY_PROVIDERS;
|
||||
codegen_backend.provide(&mut local_providers);
|
||||
|
||||
let mut extern_providers = *DEFAULT_EXTERN_QUERY_PROVIDERS;
|
||||
codegen_backend.provide_extern(&mut extern_providers);
|
||||
let mut providers = *DEFAULT_QUERY_PROVIDERS;
|
||||
codegen_backend.provide(&mut providers);
|
||||
|
||||
if let Some(callback) = compiler.override_queries {
|
||||
callback(sess, &mut local_providers, &mut extern_providers);
|
||||
callback(sess, &mut providers);
|
||||
}
|
||||
|
||||
let incremental = dep_graph.is_fully_enabled();
|
||||
@ -727,11 +717,12 @@ pub fn create_global_ctxt<'tcx>(
|
||||
dep_graph,
|
||||
rustc_query_impl::query_callbacks(arena),
|
||||
rustc_query_impl::query_system(
|
||||
local_providers,
|
||||
extern_providers,
|
||||
providers.queries,
|
||||
providers.extern_queries,
|
||||
query_result_on_disk_cache,
|
||||
incremental,
|
||||
),
|
||||
providers.hooks,
|
||||
)
|
||||
})
|
||||
})
|
||||
@ -807,14 +798,12 @@ fn analysis(tcx: TyCtxt<'_>, (): ()) -> Result<()> {
|
||||
}
|
||||
});
|
||||
|
||||
if tcx.sess.opts.unstable_opts.drop_tracking_mir {
|
||||
tcx.hir().par_body_owners(|def_id| {
|
||||
if let rustc_hir::def::DefKind::Generator = tcx.def_kind(def_id) {
|
||||
tcx.ensure().mir_generator_witnesses(def_id);
|
||||
tcx.ensure().check_generator_obligations(def_id);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
sess.time("layout_testing", || layout_test::test_layout(tcx));
|
||||
sess.time("abi_testing", || abi_test::test_abi(tcx));
|
||||
|
@ -684,7 +684,6 @@ fn test_unstable_options_tracking_hash() {
|
||||
untracked!(dep_tasks, true);
|
||||
untracked!(dont_buffer_diagnostics, true);
|
||||
untracked!(dump_dep_graph, true);
|
||||
untracked!(dump_drop_tracking_cfg, Some("cfg.dot".to_string()));
|
||||
untracked!(dump_mir, Some(String::from("abc")));
|
||||
untracked!(dump_mir_dataflow, true);
|
||||
untracked!(dump_mir_dir, String::from("abc"));
|
||||
@ -773,7 +772,6 @@ fn test_unstable_options_tracking_hash() {
|
||||
tracked!(debug_info_for_profiling, true);
|
||||
tracked!(debug_macros, true);
|
||||
tracked!(dep_info_omit_d_target, true);
|
||||
tracked!(drop_tracking, true);
|
||||
tracked!(dual_proc_macros, true);
|
||||
tracked!(dwarf_version, Some(5));
|
||||
tracked!(emit_thin_lto, false);
|
||||
|
@ -1271,7 +1271,6 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
|
||||
| ty::Closure(..)
|
||||
| ty::Generator(..)
|
||||
| ty::GeneratorWitness(..)
|
||||
| ty::GeneratorWitnessMIR(..)
|
||||
| ty::Placeholder(..)
|
||||
| ty::FnDef(..) => bug!("unexpected type in foreign function: {:?}", ty),
|
||||
}
|
||||
|
@ -306,6 +306,7 @@ declare_lint! {
|
||||
/// pub async fn uhoh() {
|
||||
/// let guard = SyncThing {};
|
||||
/// yield_now().await;
|
||||
/// let _guard = guard;
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
|
@ -26,7 +26,7 @@ extern crate rustc_middle;
|
||||
#[macro_use]
|
||||
extern crate tracing;
|
||||
|
||||
pub use rmeta::{provide, provide_extern};
|
||||
pub use rmeta::provide;
|
||||
use rustc_errors::{DiagnosticMessage, SubdiagnosticMessage};
|
||||
use rustc_fluent_macro::fluent_messages;
|
||||
|
||||
|
@ -24,7 +24,6 @@ use rustc_middle::middle::exported_symbols::{ExportedSymbol, SymbolExportInfo};
|
||||
use rustc_middle::mir::interpret::{AllocDecodingSession, AllocDecodingState};
|
||||
use rustc_middle::ty::codec::TyDecoder;
|
||||
use rustc_middle::ty::fast_reject::SimplifiedType;
|
||||
use rustc_middle::ty::GeneratorDiagnosticData;
|
||||
use rustc_middle::ty::{self, ParameterizedOverTcx, Ty, TyCtxt, Visibility};
|
||||
use rustc_serialize::opaque::MemDecoder;
|
||||
use rustc_serialize::{Decodable, Decoder};
|
||||
@ -44,7 +43,6 @@ use std::sync::atomic::Ordering;
|
||||
use std::{io, iter, mem};
|
||||
|
||||
pub(super) use cstore_impl::provide;
|
||||
pub use cstore_impl::provide_extern;
|
||||
use rustc_span::hygiene::HygieneDecodeContext;
|
||||
|
||||
mod cstore_impl;
|
||||
@ -1751,24 +1749,6 @@ impl<'a, 'tcx> CrateMetadataRef<'a> {
|
||||
.clone()
|
||||
}
|
||||
|
||||
fn get_generator_diagnostic_data(
|
||||
self,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
id: DefIndex,
|
||||
) -> Option<GeneratorDiagnosticData<'tcx>> {
|
||||
self.root
|
||||
.tables
|
||||
.generator_diagnostic_data
|
||||
.get(self, id)
|
||||
.map(|param| param.decode((self, tcx)))
|
||||
.map(|generator_data| GeneratorDiagnosticData {
|
||||
generator_interior_types: generator_data.generator_interior_types,
|
||||
hir_owner: generator_data.hir_owner,
|
||||
nodes_types: generator_data.nodes_types,
|
||||
adjustments: generator_data.adjustments,
|
||||
})
|
||||
}
|
||||
|
||||
fn get_attr_flags(self, index: DefIndex) -> AttrFlags {
|
||||
self.root.tables.attr_flags.get(self, index)
|
||||
}
|
||||
|
@ -14,10 +14,11 @@ use rustc_middle::arena::ArenaAllocatable;
|
||||
use rustc_middle::metadata::ModChild;
|
||||
use rustc_middle::middle::exported_symbols::ExportedSymbol;
|
||||
use rustc_middle::middle::stability::DeprecationEntry;
|
||||
use rustc_middle::query::ExternProviders;
|
||||
use rustc_middle::query::LocalCrate;
|
||||
use rustc_middle::query::{ExternProviders, Providers};
|
||||
use rustc_middle::ty::fast_reject::SimplifiedType;
|
||||
use rustc_middle::ty::{self, TyCtxt};
|
||||
use rustc_middle::util::Providers;
|
||||
use rustc_session::cstore::CrateStore;
|
||||
use rustc_session::{Session, StableCrateId};
|
||||
use rustc_span::hygiene::{ExpnHash, ExpnId};
|
||||
@ -147,7 +148,7 @@ macro_rules! provide_one {
|
||||
macro_rules! provide {
|
||||
($tcx:ident, $def_id:ident, $other:ident, $cdata:ident,
|
||||
$($name:ident => { $($compute:tt)* })*) => {
|
||||
pub fn provide_extern(providers: &mut ExternProviders) {
|
||||
fn provide_extern(providers: &mut ExternProviders) {
|
||||
$(provide_one! {
|
||||
$tcx, $def_id, $other, $cdata, $name => { $($compute)* }
|
||||
})*
|
||||
@ -373,7 +374,6 @@ provide! { tcx, def_id, other, cdata,
|
||||
|
||||
crate_extern_paths => { cdata.source().paths().cloned().collect() }
|
||||
expn_that_defined => { cdata.get_expn_that_defined(def_id.index, tcx.sess) }
|
||||
generator_diagnostic_data => { cdata.get_generator_diagnostic_data(tcx, def_id.index) }
|
||||
is_doc_hidden => { cdata.get_attr_flags(def_id.index).contains(AttrFlags::IS_DOC_HIDDEN) }
|
||||
doc_link_resolutions => { tcx.arena.alloc(cdata.get_doc_link_resolutions(def_id.index)) }
|
||||
doc_link_traits_in_scope => {
|
||||
@ -385,7 +385,7 @@ pub(in crate::rmeta) fn provide(providers: &mut Providers) {
|
||||
// FIXME(#44234) - almost all of these queries have no sub-queries and
|
||||
// therefore no actual inputs, they're just reading tables calculated in
|
||||
// resolve! Does this work? Unsure! That's what the issue is about
|
||||
*providers = Providers {
|
||||
providers.queries = rustc_middle::query::Providers {
|
||||
allocator_kind: |tcx, ()| CStore::from_tcx(tcx).allocator_kind(),
|
||||
alloc_error_handler_kind: |tcx, ()| CStore::from_tcx(tcx).alloc_error_handler_kind(),
|
||||
is_private_dep: |_tcx, LocalCrate| false,
|
||||
@ -513,8 +513,9 @@ pub(in crate::rmeta) fn provide(providers: &mut Providers) {
|
||||
tcx.untracked().cstore.freeze();
|
||||
tcx.arena.alloc_from_iter(CStore::from_tcx(tcx).iter_crate_data().map(|(cnum, _)| cnum))
|
||||
},
|
||||
..*providers
|
||||
..providers.queries
|
||||
};
|
||||
provide_extern(&mut providers.extern_queries);
|
||||
}
|
||||
|
||||
impl CStore {
|
||||
|
@ -1439,7 +1439,8 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
|
||||
}
|
||||
}
|
||||
if let DefKind::Generator = def_kind {
|
||||
self.encode_info_for_generator(local_id);
|
||||
let data = self.tcx.generator_kind(def_id).unwrap();
|
||||
record!(self.tables.generator_kind[def_id] <- data);
|
||||
}
|
||||
if let DefKind::Enum | DefKind::Struct | DefKind::Union = def_kind {
|
||||
self.encode_info_for_adt(local_id);
|
||||
@ -1612,8 +1613,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
|
||||
record!(self.tables.closure_saved_names_of_captured_variables[def_id.to_def_id()]
|
||||
<- tcx.closure_saved_names_of_captured_variables(def_id));
|
||||
|
||||
if tcx.sess.opts.unstable_opts.drop_tracking_mir
|
||||
&& let DefKind::Generator = self.tcx.def_kind(def_id)
|
||||
if let DefKind::Generator = self.tcx.def_kind(def_id)
|
||||
&& let Some(witnesses) = tcx.mir_generator_witnesses(def_id)
|
||||
{
|
||||
record!(self.tables.mir_generator_witnesses[def_id.to_def_id()] <- witnesses);
|
||||
@ -1640,6 +1640,12 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
|
||||
}
|
||||
record!(self.tables.promoted_mir[def_id.to_def_id()] <- tcx.promoted_mir(def_id));
|
||||
|
||||
if let DefKind::Generator = self.tcx.def_kind(def_id)
|
||||
&& let Some(witnesses) = tcx.mir_generator_witnesses(def_id)
|
||||
{
|
||||
record!(self.tables.mir_generator_witnesses[def_id.to_def_id()] <- witnesses);
|
||||
}
|
||||
|
||||
let instance = ty::InstanceDef::Item(def_id.to_def_id());
|
||||
let unused = tcx.unused_generic_params(instance);
|
||||
self.tables.unused_generic_params.set(def_id.local_def_index, unused);
|
||||
@ -1712,15 +1718,6 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
|
||||
record!(self.tables.macro_definition[def_id.to_def_id()] <- &*macro_def.body);
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
fn encode_info_for_generator(&mut self, def_id: LocalDefId) {
|
||||
let typeck_result: &'tcx ty::TypeckResults<'tcx> = self.tcx.typeck(def_id);
|
||||
let data = self.tcx.generator_kind(def_id).unwrap();
|
||||
let generator_diagnostic_data = typeck_result.get_generator_diagnostic_data();
|
||||
record!(self.tables.generator_kind[def_id.to_def_id()] <- data);
|
||||
record!(self.tables.generator_diagnostic_data[def_id.to_def_id()] <- generator_diagnostic_data);
|
||||
}
|
||||
|
||||
fn encode_native_libraries(&mut self) -> LazyArray<NativeLib> {
|
||||
empty_proc_macro!(self);
|
||||
let used_libraries = self.tcx.native_libraries(LOCAL_CRATE);
|
||||
|
@ -21,10 +21,10 @@ use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs;
|
||||
use rustc_middle::middle::exported_symbols::{ExportedSymbol, SymbolExportInfo};
|
||||
use rustc_middle::middle::resolve_bound_vars::ObjectLifetimeDefault;
|
||||
use rustc_middle::mir;
|
||||
use rustc_middle::query::Providers;
|
||||
use rustc_middle::ty::fast_reject::SimplifiedType;
|
||||
use rustc_middle::ty::{self, ReprOptions, Ty, UnusedGenericParams};
|
||||
use rustc_middle::ty::{DeducedParamAttrs, GeneratorDiagnosticData, ParameterizedOverTcx, TyCtxt};
|
||||
use rustc_middle::ty::{DeducedParamAttrs, ParameterizedOverTcx, TyCtxt};
|
||||
use rustc_middle::util::Providers;
|
||||
use rustc_serialize::opaque::FileEncoder;
|
||||
use rustc_session::config::SymbolManglingVersion;
|
||||
use rustc_session::cstore::{CrateDepKind, ForeignModule, LinkagePreference, NativeLib};
|
||||
@ -38,7 +38,6 @@ use rustc_target::spec::{PanicStrategy, TargetTriple};
|
||||
use std::marker::PhantomData;
|
||||
use std::num::NonZeroUsize;
|
||||
|
||||
pub use decoder::provide_extern;
|
||||
use decoder::DecodeContext;
|
||||
pub(crate) use decoder::{CrateMetadata, CrateNumMap, MetadataBlob};
|
||||
use encoder::EncodeContext;
|
||||
@ -453,7 +452,6 @@ define_tables! {
|
||||
// definitions from any given crate.
|
||||
def_keys: Table<DefIndex, LazyValue<DefKey>>,
|
||||
proc_macro_quoted_spans: Table<usize, LazyValue<Span>>,
|
||||
generator_diagnostic_data: Table<DefIndex, LazyValue<GeneratorDiagnosticData<'static>>>,
|
||||
variant_data: Table<DefIndex, LazyValue<VariantData>>,
|
||||
assoc_container: Table<DefIndex, ty::AssocItemContainer>,
|
||||
macro_definition: Table<DefIndex, LazyValue<ast::DelimArgs>>,
|
||||
|
65
compiler/rustc_middle/src/hooks/mod.rs
Normal file
65
compiler/rustc_middle/src/hooks/mod.rs
Normal file
@ -0,0 +1,65 @@
|
||||
use crate::mir;
|
||||
use crate::query::TyCtxtAt;
|
||||
use crate::ty::{Ty, TyCtxt};
|
||||
use rustc_span::DUMMY_SP;
|
||||
|
||||
macro_rules! declare_hooks {
|
||||
($($(#[$attr:meta])*hook $name:ident($($arg:ident: $K:ty),*) -> $V:ty;)*) => {
|
||||
|
||||
impl<'tcx> TyCtxt<'tcx> {
|
||||
$(
|
||||
$(#[$attr])*
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn $name(self, $($arg: $K,)*) -> $V
|
||||
{
|
||||
self.at(DUMMY_SP).$name($($arg,)*)
|
||||
}
|
||||
)*
|
||||
}
|
||||
|
||||
impl<'tcx> TyCtxtAt<'tcx> {
|
||||
$(
|
||||
$(#[$attr])*
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
#[instrument(level = "debug", skip(self), ret)]
|
||||
pub fn $name(self, $($arg: $K,)*) -> $V
|
||||
{
|
||||
(self.tcx.hooks.$name)(self, $($arg,)*)
|
||||
}
|
||||
)*
|
||||
}
|
||||
|
||||
pub struct Providers {
|
||||
$(pub $name: for<'tcx> fn(
|
||||
TyCtxtAt<'tcx>,
|
||||
$($arg: $K,)*
|
||||
) -> $V,)*
|
||||
}
|
||||
|
||||
impl Default for Providers {
|
||||
fn default() -> Self {
|
||||
Providers {
|
||||
$($name: |_, $($arg,)*| bug!(
|
||||
"`tcx.{}{:?}` cannot be called as `{}` was never assigned to a provider function.\n",
|
||||
stringify!($name),
|
||||
($($arg,)*),
|
||||
stringify!($name),
|
||||
),)*
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Copy for Providers {}
|
||||
impl Clone for Providers {
|
||||
fn clone(&self) -> Self { *self }
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
declare_hooks! {
|
||||
/// Tries to destructure an `mir::Const` ADT or array into its variant index
|
||||
/// and its field values. This should only be used for pretty printing.
|
||||
hook try_destructure_mir_constant_for_diagnostics(val: mir::ConstValue<'tcx>, ty: Ty<'tcx>) -> Option<mir::DestructuredConstant<'tcx>>;
|
||||
}
|
@ -89,6 +89,7 @@ mod macros;
|
||||
pub mod arena;
|
||||
pub mod error;
|
||||
pub mod hir;
|
||||
pub mod hooks;
|
||||
pub mod infer;
|
||||
pub mod lint;
|
||||
pub mod metadata;
|
||||
|
@ -1691,7 +1691,7 @@ fn pretty_print_const_value_tcx<'tcx>(
|
||||
(_, ty::Array(..) | ty::Tuple(..) | ty::Adt(..)) if !ty.has_non_region_param() => {
|
||||
let ct = tcx.lift(ct).unwrap();
|
||||
let ty = tcx.lift(ty).unwrap();
|
||||
if let Some(contents) = tcx.try_destructure_mir_constant_for_diagnostics((ct, ty)) {
|
||||
if let Some(contents) = tcx.try_destructure_mir_constant_for_diagnostics(ct, ty) {
|
||||
let fields: Vec<(ConstValue<'_>, Ty<'_>)> = contents.fields.to_vec();
|
||||
match *ty.kind() {
|
||||
ty::Array(..) => {
|
||||
|
@ -44,7 +44,6 @@ use crate::traits::{
|
||||
use crate::ty::fast_reject::SimplifiedType;
|
||||
use crate::ty::layout::ValidityRequirement;
|
||||
use crate::ty::util::AlwaysRequiresDrop;
|
||||
use crate::ty::GeneratorDiagnosticData;
|
||||
use crate::ty::TyCtxtFeed;
|
||||
use crate::ty::{
|
||||
self, print::describe_as_module, CrateInherentImpls, ParamEnvAnd, Ty, TyCtxt,
|
||||
@ -1100,16 +1099,6 @@ rustc_queries! {
|
||||
desc { "destructuring type level constant"}
|
||||
}
|
||||
|
||||
/// Tries to destructure an `mir::Const` ADT or array into its variant index
|
||||
/// and its field values. This should only be used for pretty printing.
|
||||
query try_destructure_mir_constant_for_diagnostics(
|
||||
key: (mir::ConstValue<'tcx>, Ty<'tcx>)
|
||||
) -> Option<mir::DestructuredConstant<'tcx>> {
|
||||
desc { "destructuring MIR constant"}
|
||||
no_hash
|
||||
eval_always
|
||||
}
|
||||
|
||||
query const_caller_location(key: (rustc_span::Symbol, u32, u32)) -> mir::ConstValue<'tcx> {
|
||||
desc { "getting a &core::panic::Location referring to a span" }
|
||||
}
|
||||
@ -2159,12 +2148,6 @@ rustc_queries! {
|
||||
desc { "computing the backend features for CLI flags" }
|
||||
}
|
||||
|
||||
query generator_diagnostic_data(key: DefId) -> &'tcx Option<GeneratorDiagnosticData<'tcx>> {
|
||||
arena_cache
|
||||
desc { |tcx| "looking up generator diagnostic data of `{}`", tcx.def_path_str(key) }
|
||||
separate_provide_extern
|
||||
}
|
||||
|
||||
query check_validity_requirement(key: (ValidityRequirement, ty::ParamEnvAnd<'tcx, Ty<'tcx>>)) -> Result<bool, &'tcx ty::layout::LayoutError<'tcx>> {
|
||||
desc { "checking validity requirement for `{}`: {}", key.1.value, key.0 }
|
||||
}
|
||||
|
@ -566,6 +566,5 @@ impl_binder_encode_decode! {
|
||||
ty::TraitPredicate<'tcx>,
|
||||
ty::ExistentialPredicate<'tcx>,
|
||||
ty::TraitRef<'tcx>,
|
||||
Vec<ty::GeneratorInteriorTypeCause<'tcx>>,
|
||||
ty::ExistentialTraitRef<'tcx>,
|
||||
}
|
||||
|
@ -554,6 +554,10 @@ pub struct GlobalCtxt<'tcx> {
|
||||
/// Common consts, pre-interned for your convenience.
|
||||
pub consts: CommonConsts<'tcx>,
|
||||
|
||||
/// Hooks to be able to register functions in other crates that can then still
|
||||
/// be called from rustc_middle.
|
||||
pub(crate) hooks: crate::hooks::Providers,
|
||||
|
||||
untracked: Untracked,
|
||||
|
||||
pub query_system: QuerySystem<'tcx>,
|
||||
@ -703,6 +707,7 @@ impl<'tcx> TyCtxt<'tcx> {
|
||||
dep_graph: DepGraph,
|
||||
query_kinds: &'tcx [DepKindStruct<'tcx>],
|
||||
query_system: QuerySystem<'tcx>,
|
||||
hooks: crate::hooks::Providers,
|
||||
) -> GlobalCtxt<'tcx> {
|
||||
let data_layout = s.target.parse_data_layout().unwrap_or_else(|err| {
|
||||
s.emit_fatal(err);
|
||||
@ -721,6 +726,7 @@ impl<'tcx> TyCtxt<'tcx> {
|
||||
hir_arena,
|
||||
interners,
|
||||
dep_graph,
|
||||
hooks,
|
||||
prof: s.prof.clone(),
|
||||
types: common_types,
|
||||
lifetimes: common_lifetimes,
|
||||
@ -1378,7 +1384,6 @@ impl<'tcx> TyCtxt<'tcx> {
|
||||
Placeholder,
|
||||
Generator,
|
||||
GeneratorWitness,
|
||||
GeneratorWitnessMIR,
|
||||
Dynamic,
|
||||
Closure,
|
||||
Tuple,
|
||||
|
@ -242,8 +242,7 @@ impl<'tcx> Ty<'tcx> {
|
||||
ty::Dynamic(..) => "trait object".into(),
|
||||
ty::Closure(..) => "closure".into(),
|
||||
ty::Generator(def_id, ..) => tcx.generator_kind(def_id).unwrap().descr().into(),
|
||||
ty::GeneratorWitness(..) |
|
||||
ty::GeneratorWitnessMIR(..) => "generator witness".into(),
|
||||
ty::GeneratorWitness(..) => "generator witness".into(),
|
||||
ty::Infer(ty::TyVar(_)) => "inferred type".into(),
|
||||
ty::Infer(ty::IntVar(_)) => "integer".into(),
|
||||
ty::Infer(ty::FloatVar(_)) => "floating-point number".into(),
|
||||
@ -295,7 +294,7 @@ impl<'tcx> Ty<'tcx> {
|
||||
ty::Dynamic(..) => "trait object".into(),
|
||||
ty::Closure(..) => "closure".into(),
|
||||
ty::Generator(def_id, ..) => tcx.generator_kind(def_id).unwrap().descr().into(),
|
||||
ty::GeneratorWitness(..) | ty::GeneratorWitnessMIR(..) => "generator witness".into(),
|
||||
ty::GeneratorWitness(..) => "generator witness".into(),
|
||||
ty::Tuple(..) => "tuple".into(),
|
||||
ty::Placeholder(..) => "higher-ranked type".into(),
|
||||
ty::Bound(..) => "bound type variable".into(),
|
||||
|
@ -29,8 +29,7 @@ pub enum SimplifiedType {
|
||||
Trait(DefId),
|
||||
Closure(DefId),
|
||||
Generator(DefId),
|
||||
GeneratorWitness(usize),
|
||||
GeneratorWitnessMIR(DefId),
|
||||
GeneratorWitness(DefId),
|
||||
Function(usize),
|
||||
Placeholder,
|
||||
}
|
||||
@ -130,10 +129,7 @@ pub fn simplify_type<'tcx>(
|
||||
ty::Ref(_, _, mutbl) => Some(SimplifiedType::Ref(mutbl)),
|
||||
ty::FnDef(def_id, _) | ty::Closure(def_id, _) => Some(SimplifiedType::Closure(def_id)),
|
||||
ty::Generator(def_id, _, _) => Some(SimplifiedType::Generator(def_id)),
|
||||
ty::GeneratorWitness(tys) => {
|
||||
Some(SimplifiedType::GeneratorWitness(tys.skip_binder().len()))
|
||||
}
|
||||
ty::GeneratorWitnessMIR(def_id, _) => Some(SimplifiedType::GeneratorWitnessMIR(def_id)),
|
||||
ty::GeneratorWitness(def_id, _) => Some(SimplifiedType::GeneratorWitness(def_id)),
|
||||
ty::Never => Some(SimplifiedType::Never),
|
||||
ty::Tuple(tys) => Some(SimplifiedType::Tuple(tys.len())),
|
||||
ty::FnPtr(f) => Some(SimplifiedType::Function(f.skip_binder().inputs().len())),
|
||||
@ -169,7 +165,7 @@ impl SimplifiedType {
|
||||
| SimplifiedType::Trait(d)
|
||||
| SimplifiedType::Closure(d)
|
||||
| SimplifiedType::Generator(d)
|
||||
| SimplifiedType::GeneratorWitnessMIR(d) => Some(d),
|
||||
| SimplifiedType::GeneratorWitness(d) => Some(d),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
@ -240,7 +236,6 @@ impl DeepRejectCtxt {
|
||||
| ty::Closure(..)
|
||||
| ty::Generator(..)
|
||||
| ty::GeneratorWitness(..)
|
||||
| ty::GeneratorWitnessMIR(..)
|
||||
| ty::Placeholder(..)
|
||||
| ty::Bound(..)
|
||||
| ty::Infer(_) => bug!("unexpected impl_ty: {impl_ty}"),
|
||||
@ -342,7 +337,7 @@ impl DeepRejectCtxt {
|
||||
|
||||
ty::Error(_) => true,
|
||||
|
||||
ty::GeneratorWitness(..) | ty::GeneratorWitnessMIR(..) => {
|
||||
ty::GeneratorWitness(..) => {
|
||||
bug!("unexpected obligation type: {:?}", obligation_ty)
|
||||
}
|
||||
}
|
||||
|
@ -127,11 +127,7 @@ impl FlagComputation {
|
||||
self.add_ty(args.tupled_upvars_ty());
|
||||
}
|
||||
|
||||
&ty::GeneratorWitness(ts) => {
|
||||
self.bound_computation(ts, |flags, ts| flags.add_tys(ts));
|
||||
}
|
||||
|
||||
ty::GeneratorWitnessMIR(_, args) => {
|
||||
ty::GeneratorWitness(_, args) => {
|
||||
let should_remove_further_specializable =
|
||||
!self.flags.contains(TypeFlags::STILL_FURTHER_SPECIALIZABLE);
|
||||
self.add_args(args);
|
||||
|
@ -810,7 +810,6 @@ where
|
||||
| ty::Never
|
||||
| ty::FnDef(..)
|
||||
| ty::GeneratorWitness(..)
|
||||
| ty::GeneratorWitnessMIR(..)
|
||||
| ty::Foreign(..)
|
||||
| ty::Dynamic(_, _, ty::Dyn) => {
|
||||
bug!("TyAndLayout::field({:?}): not applicable", this)
|
||||
|
@ -106,9 +106,8 @@ pub use self::sty::{
|
||||
};
|
||||
pub use self::trait_def::TraitDef;
|
||||
pub use self::typeck_results::{
|
||||
CanonicalUserType, CanonicalUserTypeAnnotation, CanonicalUserTypeAnnotations,
|
||||
GeneratorDiagnosticData, GeneratorInteriorTypeCause, TypeckResults, UserType,
|
||||
UserTypeAnnotationIndex,
|
||||
CanonicalUserType, CanonicalUserTypeAnnotation, CanonicalUserTypeAnnotations, TypeckResults,
|
||||
UserType, UserTypeAnnotationIndex,
|
||||
};
|
||||
|
||||
pub mod _match;
|
||||
|
@ -157,9 +157,9 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for ReverseMapper<'tcx> {
|
||||
Ty::new_generator(self.tcx, def_id, args, movability)
|
||||
}
|
||||
|
||||
ty::GeneratorWitnessMIR(def_id, args) => {
|
||||
ty::GeneratorWitness(def_id, args) => {
|
||||
let args = self.fold_closure_args(def_id, args);
|
||||
Ty::new_generator_witness_mir(self.tcx, def_id, args)
|
||||
Ty::new_generator_witness(self.tcx, def_id, args)
|
||||
}
|
||||
|
||||
ty::Param(param) => {
|
||||
|
@ -132,5 +132,4 @@ parameterized_over_tcx! {
|
||||
ty::Predicate,
|
||||
ty::Clause,
|
||||
ty::ClauseKind,
|
||||
ty::GeneratorDiagnosticData,
|
||||
}
|
||||
|
@ -271,7 +271,7 @@ fn characteristic_def_id_of_type_cached<'a>(
|
||||
ty::FnDef(def_id, _)
|
||||
| ty::Closure(def_id, _)
|
||||
| ty::Generator(def_id, _, _)
|
||||
| ty::GeneratorWitnessMIR(def_id, _)
|
||||
| ty::GeneratorWitness(def_id, _)
|
||||
| ty::Foreign(def_id) => Some(def_id),
|
||||
|
||||
ty::Bool
|
||||
@ -286,7 +286,6 @@ fn characteristic_def_id_of_type_cached<'a>(
|
||||
| ty::Infer(_)
|
||||
| ty::Bound(..)
|
||||
| ty::Error(_)
|
||||
| ty::GeneratorWitness(..)
|
||||
| ty::Never
|
||||
| ty::Float(_) => None,
|
||||
}
|
||||
|
@ -838,10 +838,7 @@ pub trait PrettyPrinter<'tcx>:
|
||||
|
||||
p!("}}")
|
||||
}
|
||||
ty::GeneratorWitness(types) => {
|
||||
p!(in_binder(&types));
|
||||
}
|
||||
ty::GeneratorWitnessMIR(did, args) => {
|
||||
ty::GeneratorWitness(did, args) => {
|
||||
p!(write("{{"));
|
||||
if !self.tcx().sess.verbose() {
|
||||
p!("generator witness");
|
||||
|
@ -453,24 +453,14 @@ pub fn structurally_relate_tys<'tcx, R: TypeRelation<'tcx>>(
|
||||
Ok(Ty::new_generator(tcx, a_id, args, movability))
|
||||
}
|
||||
|
||||
(&ty::GeneratorWitness(a_types), &ty::GeneratorWitness(b_types)) => {
|
||||
// Wrap our types with a temporary GeneratorWitness struct
|
||||
// inside the binder so we can related them
|
||||
let a_types = a_types.map_bound(GeneratorWitness);
|
||||
let b_types = b_types.map_bound(GeneratorWitness);
|
||||
// Then remove the GeneratorWitness for the result
|
||||
let types = relation.relate(a_types, b_types)?.map_bound(|witness| witness.0);
|
||||
Ok(Ty::new_generator_witness(tcx, types))
|
||||
}
|
||||
|
||||
(&ty::GeneratorWitnessMIR(a_id, a_args), &ty::GeneratorWitnessMIR(b_id, b_args))
|
||||
(&ty::GeneratorWitness(a_id, a_args), &ty::GeneratorWitness(b_id, b_args))
|
||||
if a_id == b_id =>
|
||||
{
|
||||
// All GeneratorWitness types with the same id represent
|
||||
// the (anonymous) type of the same generator expression. So
|
||||
// all of their regions should be equated.
|
||||
let args = relation.relate(a_args, b_args)?;
|
||||
Ok(Ty::new_generator_witness_mir(tcx, a_id, args))
|
||||
Ok(Ty::new_generator_witness(tcx, a_id, args))
|
||||
}
|
||||
|
||||
(&ty::Closure(a_id, a_args), &ty::Closure(b_id, b_args)) if a_id == b_id => {
|
||||
|
@ -657,9 +657,8 @@ impl<'tcx> TypeSuperFoldable<TyCtxt<'tcx>> for Ty<'tcx> {
|
||||
ty::Generator(did, args, movability) => {
|
||||
ty::Generator(did, args.try_fold_with(folder)?, movability)
|
||||
}
|
||||
ty::GeneratorWitness(types) => ty::GeneratorWitness(types.try_fold_with(folder)?),
|
||||
ty::GeneratorWitnessMIR(did, args) => {
|
||||
ty::GeneratorWitnessMIR(did, args.try_fold_with(folder)?)
|
||||
ty::GeneratorWitness(did, args) => {
|
||||
ty::GeneratorWitness(did, args.try_fold_with(folder)?)
|
||||
}
|
||||
ty::Closure(did, args) => ty::Closure(did, args.try_fold_with(folder)?),
|
||||
ty::Alias(kind, data) => ty::Alias(kind, data.try_fold_with(folder)?),
|
||||
@ -708,8 +707,7 @@ impl<'tcx> TypeSuperVisitable<TyCtxt<'tcx>> for Ty<'tcx> {
|
||||
ty.visit_with(visitor)
|
||||
}
|
||||
ty::Generator(_did, ref args, _) => args.visit_with(visitor),
|
||||
ty::GeneratorWitness(ref types) => types.visit_with(visitor),
|
||||
ty::GeneratorWitnessMIR(_did, ref args) => args.visit_with(visitor),
|
||||
ty::GeneratorWitness(_did, ref args) => args.visit_with(visitor),
|
||||
ty::Closure(_did, ref args) => args.visit_with(visitor),
|
||||
ty::Alias(_, ref data) => data.visit_with(visitor),
|
||||
|
||||
|
@ -2164,19 +2164,11 @@ impl<'tcx> Ty<'tcx> {
|
||||
|
||||
#[inline]
|
||||
pub fn new_generator_witness(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
types: ty::Binder<'tcx, &'tcx List<Ty<'tcx>>>,
|
||||
) -> Ty<'tcx> {
|
||||
Ty::new(tcx, GeneratorWitness(types))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn new_generator_witness_mir(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
id: DefId,
|
||||
args: GenericArgsRef<'tcx>,
|
||||
) -> Ty<'tcx> {
|
||||
Ty::new(tcx, GeneratorWitnessMIR(id, args))
|
||||
Ty::new(tcx, GeneratorWitness(id, args))
|
||||
}
|
||||
|
||||
// misc
|
||||
@ -2706,7 +2698,6 @@ impl<'tcx> Ty<'tcx> {
|
||||
| ty::Dynamic(..)
|
||||
| ty::Closure(..)
|
||||
| ty::GeneratorWitness(..)
|
||||
| ty::GeneratorWitnessMIR(..)
|
||||
| ty::Never
|
||||
| ty::Tuple(_)
|
||||
| ty::Error(_)
|
||||
@ -2742,7 +2733,6 @@ impl<'tcx> Ty<'tcx> {
|
||||
| ty::Ref(..)
|
||||
| ty::Generator(..)
|
||||
| ty::GeneratorWitness(..)
|
||||
| ty::GeneratorWitnessMIR(..)
|
||||
| ty::Array(..)
|
||||
| ty::Closure(..)
|
||||
| ty::Never
|
||||
@ -2831,7 +2821,6 @@ impl<'tcx> Ty<'tcx> {
|
||||
| ty::Ref(..)
|
||||
| ty::Generator(..)
|
||||
| ty::GeneratorWitness(..)
|
||||
| ty::GeneratorWitnessMIR(..)
|
||||
| ty::Array(..)
|
||||
| ty::Closure(..)
|
||||
| ty::Never
|
||||
@ -2894,7 +2883,7 @@ impl<'tcx> Ty<'tcx> {
|
||||
// anything with custom metadata it might be more complicated.
|
||||
ty::Ref(_, _, hir::Mutability::Not) | ty::RawPtr(..) => false,
|
||||
|
||||
ty::Generator(..) | ty::GeneratorWitness(..) | ty::GeneratorWitnessMIR(..) => false,
|
||||
ty::Generator(..) | ty::GeneratorWitness(..) => false,
|
||||
|
||||
// Might be, but not "trivial" so just giving the safe answer.
|
||||
ty::Adt(..) | ty::Closure(..) => false,
|
||||
@ -2970,8 +2959,7 @@ impl<'tcx> Ty<'tcx> {
|
||||
| Dynamic(_, _, _)
|
||||
| Closure(_, _)
|
||||
| Generator(_, _, _)
|
||||
| GeneratorWitness(_)
|
||||
| GeneratorWitnessMIR(_, _)
|
||||
| GeneratorWitness(..)
|
||||
| Never
|
||||
| Tuple(_) => true,
|
||||
Error(_) | Infer(_) | Alias(_, _) | Param(_) | Bound(_, _) | Placeholder(_) => false,
|
||||
|
@ -189,10 +189,6 @@ pub struct TypeckResults<'tcx> {
|
||||
/// Details may be find in `rustc_hir_analysis::check::rvalue_scopes`.
|
||||
pub rvalue_scopes: RvalueScopes,
|
||||
|
||||
/// Stores the type, expression, span and optional scope span of all types
|
||||
/// that are live across the yield of this generator (if a generator).
|
||||
pub generator_interior_types: ty::Binder<'tcx, Vec<GeneratorInteriorTypeCause<'tcx>>>,
|
||||
|
||||
/// Stores the predicates that apply on generator witness types.
|
||||
/// formatting modified file tests/ui/generator/retain-resume-ref.rs
|
||||
pub generator_interior_predicates:
|
||||
@ -212,49 +208,6 @@ pub struct TypeckResults<'tcx> {
|
||||
offset_of_data: ItemLocalMap<(Ty<'tcx>, Vec<FieldIdx>)>,
|
||||
}
|
||||
|
||||
/// Whenever a value may be live across a generator yield, the type of that value winds up in the
|
||||
/// `GeneratorInteriorTypeCause` struct. This struct adds additional information about such
|
||||
/// captured types that can be useful for diagnostics. In particular, it stores the span that
|
||||
/// caused a given type to be recorded, along with the scope that enclosed the value (which can
|
||||
/// be used to find the await that the value is live across).
|
||||
///
|
||||
/// For example:
|
||||
///
|
||||
/// ```ignore (pseudo-Rust)
|
||||
/// async move {
|
||||
/// let x: T = expr;
|
||||
/// foo.await
|
||||
/// ...
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Here, we would store the type `T`, the span of the value `x`, the "scope-span" for
|
||||
/// the scope that contains `x`, the expr `T` evaluated from, and the span of `foo.await`.
|
||||
#[derive(TyEncodable, TyDecodable, Clone, Debug, Eq, Hash, PartialEq, HashStable)]
|
||||
#[derive(TypeFoldable, TypeVisitable)]
|
||||
pub struct GeneratorInteriorTypeCause<'tcx> {
|
||||
/// Type of the captured binding.
|
||||
pub ty: Ty<'tcx>,
|
||||
/// Span of the binding that was captured.
|
||||
pub span: Span,
|
||||
/// Span of the scope of the captured binding.
|
||||
pub scope_span: Option<Span>,
|
||||
/// Span of `.await` or `yield` expression.
|
||||
pub yield_span: Span,
|
||||
/// Expr which the type evaluated from.
|
||||
pub expr: Option<hir::HirId>,
|
||||
}
|
||||
|
||||
// This type holds diagnostic information on generators and async functions across crate boundaries
|
||||
// and is used to provide better error messages
|
||||
#[derive(TyEncodable, TyDecodable, Clone, Debug, HashStable)]
|
||||
pub struct GeneratorDiagnosticData<'tcx> {
|
||||
pub generator_interior_types: ty::Binder<'tcx, Vec<GeneratorInteriorTypeCause<'tcx>>>,
|
||||
pub hir_owner: DefId,
|
||||
pub nodes_types: ItemLocalMap<Ty<'tcx>>,
|
||||
pub adjustments: ItemLocalMap<Vec<ty::adjustment::Adjustment<'tcx>>>,
|
||||
}
|
||||
|
||||
impl<'tcx> TypeckResults<'tcx> {
|
||||
pub fn new(hir_owner: OwnerId) -> TypeckResults<'tcx> {
|
||||
TypeckResults {
|
||||
@ -278,7 +231,6 @@ impl<'tcx> TypeckResults<'tcx> {
|
||||
closure_min_captures: Default::default(),
|
||||
closure_fake_reads: Default::default(),
|
||||
rvalue_scopes: Default::default(),
|
||||
generator_interior_types: ty::Binder::dummy(Default::default()),
|
||||
generator_interior_predicates: Default::default(),
|
||||
treat_byte_string_as_slice: Default::default(),
|
||||
closure_size_eval: Default::default(),
|
||||
@ -351,28 +303,6 @@ impl<'tcx> TypeckResults<'tcx> {
|
||||
LocalTableInContextMut { hir_owner: self.hir_owner, data: &mut self.node_types }
|
||||
}
|
||||
|
||||
pub fn get_generator_diagnostic_data(&self) -> GeneratorDiagnosticData<'tcx> {
|
||||
let generator_interior_type = self.generator_interior_types.map_bound_ref(|vec| {
|
||||
vec.iter()
|
||||
.map(|item| {
|
||||
GeneratorInteriorTypeCause {
|
||||
ty: item.ty,
|
||||
span: item.span,
|
||||
scope_span: item.scope_span,
|
||||
yield_span: item.yield_span,
|
||||
expr: None, //FIXME: Passing expression over crate boundaries is impossible at the moment
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
});
|
||||
GeneratorDiagnosticData {
|
||||
generator_interior_types: generator_interior_type,
|
||||
hir_owner: self.hir_owner.to_def_id(),
|
||||
nodes_types: self.node_types.clone(),
|
||||
adjustments: self.adjustments.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn node_type(&self, id: hir::HirId) -> Ty<'tcx> {
|
||||
self.node_type_opt(id).unwrap_or_else(|| {
|
||||
bug!("node_type: no type for node {}", tls::with(|tcx| tcx.hir().node_to_string(id)))
|
||||
|
@ -855,7 +855,7 @@ impl<'tcx> OpaqueTypeExpander<'tcx> {
|
||||
let hidden_ty = bty.instantiate(self.tcx, args);
|
||||
self.fold_ty(hidden_ty);
|
||||
}
|
||||
let expanded_ty = Ty::new_generator_witness_mir(self.tcx, def_id, args);
|
||||
let expanded_ty = Ty::new_generator_witness(self.tcx, def_id, args);
|
||||
self.expanded_cache.insert((def_id, args), expanded_ty);
|
||||
expanded_ty
|
||||
}
|
||||
@ -888,7 +888,7 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for OpaqueTypeExpander<'tcx> {
|
||||
t
|
||||
};
|
||||
if self.expand_generators {
|
||||
if let ty::GeneratorWitnessMIR(def_id, args) = *t.kind() {
|
||||
if let ty::GeneratorWitness(def_id, args) = *t.kind() {
|
||||
t = self.expand_generator(def_id, args).unwrap_or(t);
|
||||
}
|
||||
}
|
||||
@ -1025,8 +1025,7 @@ impl<'tcx> Ty<'tcx> {
|
||||
| ty::Dynamic(..)
|
||||
| ty::Foreign(_)
|
||||
| ty::Generator(..)
|
||||
| ty::GeneratorWitness(_)
|
||||
| ty::GeneratorWitnessMIR(..)
|
||||
| ty::GeneratorWitness(..)
|
||||
| ty::Infer(_)
|
||||
| ty::Alias(..)
|
||||
| ty::Param(_)
|
||||
@ -1065,8 +1064,7 @@ impl<'tcx> Ty<'tcx> {
|
||||
| ty::Dynamic(..)
|
||||
| ty::Foreign(_)
|
||||
| ty::Generator(..)
|
||||
| ty::GeneratorWitness(_)
|
||||
| ty::GeneratorWitnessMIR(..)
|
||||
| ty::GeneratorWitness(..)
|
||||
| ty::Infer(_)
|
||||
| ty::Alias(..)
|
||||
| ty::Param(_)
|
||||
@ -1194,10 +1192,7 @@ impl<'tcx> Ty<'tcx> {
|
||||
false
|
||||
}
|
||||
|
||||
ty::Foreign(_)
|
||||
| ty::GeneratorWitness(..)
|
||||
| ty::GeneratorWitnessMIR(..)
|
||||
| ty::Error(_) => false,
|
||||
ty::Foreign(_) | ty::GeneratorWitness(..) | ty::Error(_) => false,
|
||||
}
|
||||
}
|
||||
|
||||
@ -1293,7 +1288,6 @@ pub fn needs_drop_components<'tcx>(
|
||||
| ty::FnPtr(_)
|
||||
| ty::Char
|
||||
| ty::GeneratorWitness(..)
|
||||
| ty::GeneratorWitnessMIR(..)
|
||||
| ty::RawPtr(_)
|
||||
| ty::Ref(..)
|
||||
| ty::Str => Ok(SmallVec::new()),
|
||||
@ -1364,11 +1358,7 @@ pub fn is_trivially_const_drop(ty: Ty<'_>) -> bool {
|
||||
|
||||
// Not trivial because they have components, and instead of looking inside,
|
||||
// we'll just perform trait selection.
|
||||
ty::Closure(..)
|
||||
| ty::Generator(..)
|
||||
| ty::GeneratorWitness(_)
|
||||
| ty::GeneratorWitnessMIR(..)
|
||||
| ty::Adt(..) => false,
|
||||
ty::Closure(..) | ty::Generator(..) | ty::GeneratorWitness(..) | ty::Adt(..) => false,
|
||||
|
||||
ty::Array(ty, _) | ty::Slice(ty) => is_trivially_const_drop(ty),
|
||||
|
||||
|
@ -190,14 +190,11 @@ fn push_inner<'tcx>(stack: &mut TypeWalkerStack<'tcx>, parent: GenericArg<'tcx>)
|
||||
ty::Adt(_, args)
|
||||
| ty::Closure(_, args)
|
||||
| ty::Generator(_, args, _)
|
||||
| ty::GeneratorWitnessMIR(_, args)
|
||||
| ty::GeneratorWitness(_, args)
|
||||
| ty::FnDef(_, args) => {
|
||||
stack.extend(args.iter().rev());
|
||||
}
|
||||
ty::Tuple(ts) => stack.extend(ts.iter().rev().map(GenericArg::from)),
|
||||
ty::GeneratorWitness(ts) => {
|
||||
stack.extend(ts.skip_binder().iter().rev().map(|ty| ty.into()));
|
||||
}
|
||||
ty::FnPtr(sig) => {
|
||||
stack.push(sig.skip_binder().output().into());
|
||||
stack.extend(sig.skip_binder().inputs().iter().copied().rev().map(|ty| ty.into()));
|
||||
|
@ -5,3 +5,27 @@ pub mod find_self_call;
|
||||
|
||||
pub use call_kind::{call_kind, CallDesugaringKind, CallKind};
|
||||
pub use find_self_call::find_self_call;
|
||||
|
||||
#[derive(Default, Copy, Clone)]
|
||||
pub struct Providers {
|
||||
pub queries: rustc_middle::query::Providers,
|
||||
pub extern_queries: rustc_middle::query::ExternProviders,
|
||||
pub hooks: rustc_middle::hooks::Providers,
|
||||
}
|
||||
|
||||
/// Backwards compatibility hack to keep the diff small. This
|
||||
/// gives direct access to the `queries` field's fields, which
|
||||
/// are what almost everything wants access to.
|
||||
impl std::ops::DerefMut for Providers {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.queries
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Deref for Providers {
|
||||
type Target = rustc_middle::query::Providers;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.queries
|
||||
}
|
||||
}
|
||||
|
@ -144,8 +144,7 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> {
|
||||
| ty::Dynamic(_, _, _)
|
||||
| ty::Closure(_, _)
|
||||
| ty::Generator(_, _, _)
|
||||
| ty::GeneratorWitness(_)
|
||||
| ty::GeneratorWitnessMIR(_, _)
|
||||
| ty::GeneratorWitness(..)
|
||||
| ty::Never
|
||||
| ty::Tuple(_)
|
||||
| ty::Alias(_, _)
|
||||
@ -184,8 +183,7 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> {
|
||||
| ty::FnDef(_, _)
|
||||
| ty::FnPtr(_)
|
||||
| ty::Dynamic(_, _, _)
|
||||
| ty::GeneratorWitness(_)
|
||||
| ty::GeneratorWitnessMIR(_, _)
|
||||
| ty::GeneratorWitness(..)
|
||||
| ty::Never
|
||||
| ty::Alias(_, _)
|
||||
| ty::Param(_)
|
||||
|
@ -4,7 +4,9 @@ use rustc_errors::{
|
||||
};
|
||||
use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic};
|
||||
use rustc_middle::mir::{AssertKind, UnsafetyViolationDetails};
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
use rustc_session::lint::{self, Lint};
|
||||
use rustc_span::def_id::DefId;
|
||||
use rustc_span::Span;
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
@ -237,20 +239,38 @@ pub(crate) struct FnItemRef {
|
||||
pub ident: String,
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(mir_transform_must_not_suspend)]
|
||||
pub(crate) struct MustNotSupend<'a> {
|
||||
#[label]
|
||||
pub(crate) struct MustNotSupend<'tcx, 'a> {
|
||||
pub tcx: TyCtxt<'tcx>,
|
||||
pub yield_sp: Span,
|
||||
#[subdiagnostic]
|
||||
pub reason: Option<MustNotSuspendReason>,
|
||||
#[help]
|
||||
pub src_sp: Span,
|
||||
pub pre: &'a str,
|
||||
pub def_path: String,
|
||||
pub def_id: DefId,
|
||||
pub post: &'a str,
|
||||
}
|
||||
|
||||
// Needed for def_path_str
|
||||
impl<'a> DecorateLint<'a, ()> for MustNotSupend<'_, '_> {
|
||||
fn decorate_lint<'b>(
|
||||
self,
|
||||
diag: &'b mut rustc_errors::DiagnosticBuilder<'a, ()>,
|
||||
) -> &'b mut rustc_errors::DiagnosticBuilder<'a, ()> {
|
||||
diag.span_label(self.yield_sp, crate::fluent_generated::_subdiag::label);
|
||||
if let Some(reason) = self.reason {
|
||||
diag.subdiagnostic(reason);
|
||||
}
|
||||
diag.span_help(self.src_sp, crate::fluent_generated::_subdiag::help);
|
||||
diag.set_arg("pre", self.pre);
|
||||
diag.set_arg("def_path", self.tcx.def_path_str(self.def_id));
|
||||
diag.set_arg("post", self.post);
|
||||
diag
|
||||
}
|
||||
|
||||
fn msg(&self) -> rustc_errors::DiagnosticMessage {
|
||||
crate::fluent_generated::mir_transform_must_not_suspend
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Subdiagnostic)]
|
||||
#[note(mir_transform_note)]
|
||||
pub(crate) struct MustNotSuspendReason {
|
||||
|
@ -853,60 +853,7 @@ impl StorageConflictVisitor<'_, '_, '_> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Validates the typeck view of the generator against the actual set of types saved between
|
||||
/// yield points.
|
||||
fn sanitize_witness<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
body: &Body<'tcx>,
|
||||
witness: Ty<'tcx>,
|
||||
upvars: &'tcx ty::List<Ty<'tcx>>,
|
||||
layout: &GeneratorLayout<'tcx>,
|
||||
) {
|
||||
let did = body.source.def_id();
|
||||
let param_env = tcx.param_env(did);
|
||||
|
||||
let allowed_upvars = tcx.normalize_erasing_regions(param_env, upvars);
|
||||
let allowed = match witness.kind() {
|
||||
&ty::GeneratorWitness(interior_tys) => {
|
||||
tcx.normalize_erasing_late_bound_regions(param_env, interior_tys)
|
||||
}
|
||||
_ => {
|
||||
tcx.sess.delay_span_bug(
|
||||
body.span,
|
||||
format!("unexpected generator witness type {:?}", witness.kind()),
|
||||
);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let mut mismatches = Vec::new();
|
||||
for fty in &layout.field_tys {
|
||||
if fty.ignore_for_traits {
|
||||
continue;
|
||||
}
|
||||
let decl_ty = tcx.normalize_erasing_regions(param_env, fty.ty);
|
||||
|
||||
// Sanity check that typeck knows about the type of locals which are
|
||||
// live across a suspension point
|
||||
if !allowed.contains(&decl_ty) && !allowed_upvars.contains(&decl_ty) {
|
||||
mismatches.push(decl_ty);
|
||||
}
|
||||
}
|
||||
|
||||
if !mismatches.is_empty() {
|
||||
span_bug!(
|
||||
body.span,
|
||||
"Broken MIR: generator contains type {:?} in MIR, \
|
||||
but typeck only knows about {} and {:?}",
|
||||
mismatches,
|
||||
allowed,
|
||||
allowed_upvars
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn compute_layout<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
liveness: LivenessInfo,
|
||||
body: &Body<'tcx>,
|
||||
) -> (
|
||||
@ -932,12 +879,11 @@ fn compute_layout<'tcx>(
|
||||
let decl = &body.local_decls[local];
|
||||
debug!(?decl);
|
||||
|
||||
let ignore_for_traits = if tcx.sess.opts.unstable_opts.drop_tracking_mir {
|
||||
// Do not `assert_crate_local` here, as post-borrowck cleanup may have already cleared
|
||||
// the information. This is alright, since `ignore_for_traits` is only relevant when
|
||||
// this code runs on pre-cleanup MIR, and `ignore_for_traits = false` is the safer
|
||||
// default.
|
||||
match decl.local_info {
|
||||
let ignore_for_traits = match decl.local_info {
|
||||
// Do not include raw pointers created from accessing `static` items, as those could
|
||||
// well be re-created by another access to the same static.
|
||||
ClearCrossCrate::Set(box LocalInfo::StaticRef { is_thread_local, .. }) => {
|
||||
@ -947,12 +893,6 @@ fn compute_layout<'tcx>(
|
||||
// post-analysis MIR.
|
||||
ClearCrossCrate::Set(box LocalInfo::FakeBorrow) => true,
|
||||
_ => false,
|
||||
}
|
||||
} else {
|
||||
// FIXME(#105084) HIR-based drop tracking does not account for all the temporaries that
|
||||
// MIR building may introduce. This leads to wrongly ignored types, but this is
|
||||
// necessary for internal consistency and to avoid ICEs.
|
||||
decl.internal
|
||||
};
|
||||
let decl =
|
||||
GeneratorSavedTy { ty: decl.ty, source_info: decl.source_info, ignore_for_traits };
|
||||
@ -1445,8 +1385,6 @@ pub(crate) fn mir_generator_witnesses<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
def_id: LocalDefId,
|
||||
) -> Option<GeneratorLayout<'tcx>> {
|
||||
assert!(tcx.sess.opts.unstable_opts.drop_tracking_mir);
|
||||
|
||||
let (body, _) = tcx.mir_promoted(def_id);
|
||||
let body = body.borrow();
|
||||
let body = &*body;
|
||||
@ -1469,7 +1407,7 @@ pub(crate) fn mir_generator_witnesses<'tcx>(
|
||||
// Extract locals which are live across suspension point into `layout`
|
||||
// `remap` gives a mapping from local indices onto generator struct indices
|
||||
// `storage_liveness` tells us which locals have live storage at suspension points
|
||||
let (_, generator_layout, _) = compute_layout(tcx, liveness_info, body);
|
||||
let (_, generator_layout, _) = compute_layout(liveness_info, body);
|
||||
|
||||
check_suspend_tys(tcx, &generator_layout, &body);
|
||||
|
||||
@ -1489,15 +1427,10 @@ impl<'tcx> MirPass<'tcx> for StateTransform {
|
||||
let gen_ty = body.local_decls.raw[1].ty;
|
||||
|
||||
// Get the discriminant type and args which typeck computed
|
||||
let (discr_ty, upvars, interior, movable) = match *gen_ty.kind() {
|
||||
let (discr_ty, movable) = match *gen_ty.kind() {
|
||||
ty::Generator(_, args, movability) => {
|
||||
let args = args.as_generator();
|
||||
(
|
||||
args.discr_ty(tcx),
|
||||
args.upvar_tys(),
|
||||
args.witness(),
|
||||
movability == hir::Movability::Movable,
|
||||
)
|
||||
(args.discr_ty(tcx), movability == hir::Movability::Movable)
|
||||
}
|
||||
_ => {
|
||||
tcx.sess.delay_span_bug(body.span, format!("unexpected generator type {gen_ty}"));
|
||||
@ -1574,13 +1507,7 @@ impl<'tcx> MirPass<'tcx> for StateTransform {
|
||||
// Extract locals which are live across suspension point into `layout`
|
||||
// `remap` gives a mapping from local indices onto generator struct indices
|
||||
// `storage_liveness` tells us which locals have live storage at suspension points
|
||||
let (remap, layout, storage_liveness) = compute_layout(tcx, liveness_info, body);
|
||||
|
||||
if tcx.sess.opts.unstable_opts.validate_mir
|
||||
&& !tcx.sess.opts.unstable_opts.drop_tracking_mir
|
||||
{
|
||||
sanitize_witness(tcx, body, interior, upvars, &layout);
|
||||
}
|
||||
let (remap, layout, storage_liveness) = compute_layout(liveness_info, body);
|
||||
|
||||
let can_return = can_return(tcx, body, tcx.param_env(body.source.def_id()));
|
||||
|
||||
@ -1954,11 +1881,12 @@ fn check_must_not_suspend_def(
|
||||
hir_id,
|
||||
data.source_span,
|
||||
errors::MustNotSupend {
|
||||
tcx,
|
||||
yield_sp: data.yield_span,
|
||||
reason,
|
||||
src_sp: data.source_span,
|
||||
pre: data.descr_pre,
|
||||
def_path: tcx.def_path_str(def_id),
|
||||
def_id,
|
||||
post: data.descr_post,
|
||||
},
|
||||
);
|
||||
|
@ -358,9 +358,7 @@ fn inner_mir_for_ctfe(tcx: TyCtxt<'_>, def: LocalDefId) -> Body<'_> {
|
||||
/// mir borrowck *before* doing so in order to ensure that borrowck can be run and doesn't
|
||||
/// end up missing the source MIR due to stealing happening.
|
||||
fn mir_drops_elaborated_and_const_checked(tcx: TyCtxt<'_>, def: LocalDefId) -> &Steal<Body<'_>> {
|
||||
if tcx.sess.opts.unstable_opts.drop_tracking_mir
|
||||
&& let DefKind::Generator = tcx.def_kind(def)
|
||||
{
|
||||
if let DefKind::Generator = tcx.def_kind(def) {
|
||||
tcx.ensure_with_value().mir_generator_witnesses(def);
|
||||
}
|
||||
let mir_borrowck = tcx.mir_borrowck(def);
|
||||
|
@ -2333,7 +2333,10 @@ impl CheckAttrVisitor<'_> {
|
||||
&mut diag,
|
||||
&cause,
|
||||
None,
|
||||
Some(ValuePairs::Sigs(ExpectedFound { expected: expected_sig, found: sig })),
|
||||
Some(ValuePairs::PolySigs(ExpectedFound {
|
||||
expected: ty::Binder::dummy(expected_sig),
|
||||
found: ty::Binder::dummy(sig),
|
||||
})),
|
||||
terr,
|
||||
false,
|
||||
false,
|
||||
|
@ -291,8 +291,7 @@ where
|
||||
| ty::Param(..)
|
||||
| ty::Bound(..)
|
||||
| ty::Error(_)
|
||||
| ty::GeneratorWitness(..)
|
||||
| ty::GeneratorWitnessMIR(..) => {}
|
||||
| ty::GeneratorWitness(..) => {}
|
||||
ty::Placeholder(..) | ty::Infer(..) => {
|
||||
bug!("unexpected type: {:?}", ty)
|
||||
}
|
||||
|
@ -214,6 +214,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
|
||||
module: None,
|
||||
}
|
||||
} else {
|
||||
let mut span_label = None;
|
||||
let item_span = path.last().unwrap().ident.span;
|
||||
let (mod_prefix, mod_str, module, suggestion) = if path.len() == 1 {
|
||||
debug!(?self.diagnostic_metadata.current_impl_items);
|
||||
@ -224,32 +225,41 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
|
||||
&& let FnKind::Fn(_, _, sig, ..) = fn_kind
|
||||
&& let Some(items) = self.diagnostic_metadata.current_impl_items
|
||||
&& let Some(item) = items.iter().find(|i| {
|
||||
if let AssocItemKind::Fn(..) | AssocItemKind::Const(..) = &i.kind
|
||||
&& i.ident.name == item_str.name
|
||||
// don't suggest if the item is in Fn signature arguments
|
||||
// issue #112590
|
||||
i.ident.name == item_str.name
|
||||
// Don't suggest if the item is in Fn signature arguments (#112590).
|
||||
&& !sig.span.contains(item_span)
|
||||
{
|
||||
debug!(?item_str.name);
|
||||
return true
|
||||
}
|
||||
false
|
||||
})
|
||||
{
|
||||
let self_sugg = match &item.kind {
|
||||
AssocItemKind::Fn(fn_) if fn_.sig.decl.has_self() => "self.",
|
||||
_ => "Self::",
|
||||
};
|
||||
|
||||
Some((
|
||||
item_span.shrink_to_lo(),
|
||||
let sp = item_span.shrink_to_lo();
|
||||
match &item.kind {
|
||||
AssocItemKind::Fn(..) => "consider using the associated function",
|
||||
AssocItemKind::Const(..) => "consider using the associated constant",
|
||||
_ => unreachable!("item kind was filtered above"),
|
||||
},
|
||||
self_sugg.to_string()
|
||||
))
|
||||
AssocItemKind::Fn(fn_)
|
||||
if !sig.decl.has_self() && fn_.sig.decl.has_self() => {
|
||||
// Ensure that we only suggest `self.` if `self` is available,
|
||||
// you can't call `fn foo(&self)` from `fn bar()` (#115992).
|
||||
// We also want to mention that the method exists.
|
||||
span_label = Some((
|
||||
item.ident.span,
|
||||
"a method by that name is available on `Self` here",
|
||||
));
|
||||
None
|
||||
}
|
||||
AssocItemKind::Fn(fn_) if fn_.sig.decl.has_self() => Some((
|
||||
sp,
|
||||
"consider using the method on `Self`",
|
||||
"self.".to_string(),
|
||||
)),
|
||||
AssocItemKind::Fn(_) => Some((
|
||||
sp,
|
||||
"consider using the associated function on `Self`",
|
||||
"Self::".to_string(),
|
||||
)),
|
||||
AssocItemKind::Const(..) => Some((
|
||||
sp,
|
||||
"consider using the associated constant on `Self`",
|
||||
"Self::".to_string(),
|
||||
)),
|
||||
_ => None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
@ -314,7 +324,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
|
||||
msg: format!("cannot find {expected} `{item_str}` in {mod_prefix}{mod_str}"),
|
||||
fallback_label,
|
||||
span: item_span,
|
||||
span_label: None,
|
||||
span_label,
|
||||
could_be_expr: false,
|
||||
suggestion,
|
||||
module,
|
||||
|
@ -1458,17 +1458,11 @@ options! {
|
||||
dont_buffer_diagnostics: bool = (false, parse_bool, [UNTRACKED],
|
||||
"emit diagnostics rather than buffering (breaks NLL error downgrading, sorting) \
|
||||
(default: no)"),
|
||||
drop_tracking: bool = (false, parse_bool, [TRACKED],
|
||||
"enables drop tracking in generators (default: no)"),
|
||||
drop_tracking_mir: bool = (false, parse_bool, [TRACKED],
|
||||
"enables drop tracking on MIR in generators (default: no)"),
|
||||
dual_proc_macros: bool = (false, parse_bool, [TRACKED],
|
||||
"load proc macros for both target and host, but only link to the target (default: no)"),
|
||||
dump_dep_graph: bool = (false, parse_bool, [UNTRACKED],
|
||||
"dump the dependency graph to $RUST_DEP_GRAPH (default: /tmp/dep_graph.gv) \
|
||||
(default: no)"),
|
||||
dump_drop_tracking_cfg: Option<String> = (None, parse_opt_string, [UNTRACKED],
|
||||
"dump drop-tracking control-flow graph as a `.dot` file (default: no)"),
|
||||
dump_mir: Option<String> = (None, parse_opt_string, [UNTRACKED],
|
||||
"dump MIR state to file.
|
||||
`val` is used to select which passes and functions to dump. For example:
|
||||
|
@ -1128,11 +1128,7 @@ impl<'tcx> Stable<'tcx> for Ty<'tcx> {
|
||||
ty::Bound(debruijn_idx, bound_ty) => {
|
||||
TyKind::Bound(debruijn_idx.as_usize(), bound_ty.stable(tables))
|
||||
}
|
||||
ty::Placeholder(..)
|
||||
| ty::GeneratorWitness(_)
|
||||
| ty::GeneratorWitnessMIR(_, _)
|
||||
| ty::Infer(_)
|
||||
| ty::Error(_) => {
|
||||
ty::Placeholder(..) | ty::GeneratorWitness(..) | ty::Infer(_) | ty::Error(_) => {
|
||||
unreachable!();
|
||||
}
|
||||
}
|
||||
|
@ -720,7 +720,6 @@ fn encode_ty<'tcx>(
|
||||
| ty::Bound(..)
|
||||
| ty::Error(..)
|
||||
| ty::GeneratorWitness(..)
|
||||
| ty::GeneratorWitnessMIR(..)
|
||||
| ty::Infer(..)
|
||||
| ty::Placeholder(..) => {
|
||||
bug!("encode_ty: unexpected `{:?}`", ty.kind());
|
||||
@ -973,12 +972,7 @@ fn transform_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, options: TransformTyOptio
|
||||
);
|
||||
}
|
||||
|
||||
ty::Bound(..)
|
||||
| ty::Error(..)
|
||||
| ty::GeneratorWitnessMIR(..)
|
||||
| ty::Infer(..)
|
||||
| ty::Param(..)
|
||||
| ty::Placeholder(..) => {
|
||||
ty::Bound(..) | ty::Error(..) | ty::Infer(..) | ty::Param(..) | ty::Placeholder(..) => {
|
||||
bug!("transform_ty: unexpected `{:?}`", ty.kind());
|
||||
}
|
||||
}
|
||||
|
@ -484,8 +484,7 @@ impl<'tcx> Printer<'tcx> for &mut SymbolMangler<'tcx> {
|
||||
|
||||
ty::Alias(ty::Inherent, _) => bug!("symbol_names: unexpected inherent projection"),
|
||||
ty::Alias(ty::Weak, _) => bug!("symbol_names: unexpected weak projection"),
|
||||
ty::GeneratorWitness(_) => bug!("symbol_names: unexpected `GeneratorWitness`"),
|
||||
ty::GeneratorWitnessMIR(..) => bug!("symbol_names: unexpected `GeneratorWitnessMIR`"),
|
||||
ty::GeneratorWitness(..) => bug!("symbol_names: unexpected `GeneratorWitness`"),
|
||||
}
|
||||
|
||||
// Only cache types that do not refer to an enclosing
|
||||
|
@ -11,7 +11,6 @@ use Arch::*;
|
||||
#[allow(non_camel_case_types)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum Arch {
|
||||
Armv7,
|
||||
Armv7k,
|
||||
Armv7s,
|
||||
Arm64,
|
||||
@ -29,7 +28,6 @@ pub enum Arch {
|
||||
impl Arch {
|
||||
pub fn target_name(self) -> &'static str {
|
||||
match self {
|
||||
Armv7 => "armv7",
|
||||
Armv7k => "armv7k",
|
||||
Armv7s => "armv7s",
|
||||
Arm64 | Arm64_macabi | Arm64_sim => "arm64",
|
||||
@ -43,7 +41,7 @@ impl Arch {
|
||||
|
||||
pub fn target_arch(self) -> Cow<'static, str> {
|
||||
Cow::Borrowed(match self {
|
||||
Armv7 | Armv7k | Armv7s => "arm",
|
||||
Armv7k | Armv7s => "arm",
|
||||
Arm64 | Arm64_32 | Arm64_macabi | Arm64_sim => "aarch64",
|
||||
I386 | I686 => "x86",
|
||||
X86_64 | X86_64_sim | X86_64_macabi | X86_64h => "x86_64",
|
||||
@ -52,7 +50,7 @@ impl Arch {
|
||||
|
||||
fn target_abi(self) -> &'static str {
|
||||
match self {
|
||||
Armv7 | Armv7k | Armv7s | Arm64 | Arm64_32 | I386 | I686 | X86_64 | X86_64h => "",
|
||||
Armv7k | Armv7s | Arm64 | Arm64_32 | I386 | I686 | X86_64 | X86_64h => "",
|
||||
X86_64_macabi | Arm64_macabi => "macabi",
|
||||
// x86_64-apple-ios is a simulator target, even though it isn't
|
||||
// declared that way in the target like the other ones...
|
||||
@ -62,18 +60,20 @@ impl Arch {
|
||||
|
||||
fn target_cpu(self) -> &'static str {
|
||||
match self {
|
||||
Armv7 => "cortex-a8", // iOS7 is supported on iPhone 4 and higher
|
||||
Armv7k => "cortex-a8",
|
||||
Armv7s => "cortex-a9",
|
||||
Armv7s => "swift", // iOS 10 is only supported on iPhone 5 or higher.
|
||||
Arm64 => "apple-a7",
|
||||
Arm64_32 => "apple-s4",
|
||||
I386 | I686 => "yonah",
|
||||
X86_64 | X86_64_sim => "core2",
|
||||
// Only macOS 10.12+ is supported, which means
|
||||
// all x86_64/x86 CPUs must be running at least penryn
|
||||
// https://github.com/llvm/llvm-project/blob/01f924d0e37a5deae51df0d77e10a15b63aa0c0f/clang/lib/Driver/ToolChains/Arch/X86.cpp#L79-L82
|
||||
I386 | I686 => "penryn",
|
||||
X86_64 | X86_64_sim => "penryn",
|
||||
X86_64_macabi => "penryn",
|
||||
// Note: `core-avx2` is slightly more advanced than `x86_64h`, see
|
||||
// comments (and disabled features) in `x86_64h_apple_darwin` for
|
||||
// details.
|
||||
// details. It is a higher baseline then `penryn` however.
|
||||
X86_64h => "core-avx2",
|
||||
X86_64_macabi => "core2",
|
||||
Arm64_macabi => "apple-a12",
|
||||
Arm64_sim => "apple-a12",
|
||||
}
|
||||
@ -115,21 +115,6 @@ fn pre_link_args(os: &'static str, arch: Arch, abi: &'static str) -> LinkArgs {
|
||||
}
|
||||
|
||||
pub fn opts(os: &'static str, arch: Arch) -> TargetOptions {
|
||||
// Static TLS is only available in macOS 10.7+. If you try to compile for 10.6
|
||||
// either the linker will complain if it is used or the binary will end up
|
||||
// segfaulting at runtime when run on 10.6. Rust by default supports macOS
|
||||
// 10.7+, but there is a standard environment variable,
|
||||
// MACOSX_DEPLOYMENT_TARGET, which is used to signal targeting older
|
||||
// versions of macOS. For example compiling on 10.10 with
|
||||
// MACOSX_DEPLOYMENT_TARGET set to 10.6 will cause the linker to generate
|
||||
// warnings about the usage of static TLS.
|
||||
//
|
||||
// Here we detect what version is being requested, defaulting to 10.7. Static
|
||||
// TLS is flagged as enabled if it looks to be supported. The architecture
|
||||
// only matters for default deployment target which is 11.0 for ARM64 and
|
||||
// 10.7 for everything else.
|
||||
let has_thread_local = os == "macos" && macos_deployment_target(Arch::X86_64) >= (10, 7);
|
||||
|
||||
let abi = arch.target_abi();
|
||||
|
||||
TargetOptions {
|
||||
@ -145,12 +130,17 @@ pub fn opts(os: &'static str, arch: Arch) -> TargetOptions {
|
||||
pre_link_args: pre_link_args(os, arch, abi),
|
||||
families: cvs!["unix"],
|
||||
is_like_osx: true,
|
||||
default_dwarf_version: 2,
|
||||
// LLVM notes that macOS 10.11+ and iOS 9+ default
|
||||
// to v4, so we do the same.
|
||||
// https://github.com/llvm/llvm-project/blob/378778a0d10c2f8d5df8ceff81f95b6002984a4b/clang/lib/Driver/ToolChains/Darwin.cpp#L1203
|
||||
default_dwarf_version: 4,
|
||||
frame_pointer: FramePointer::Always,
|
||||
has_rpath: true,
|
||||
dll_suffix: ".dylib".into(),
|
||||
archive_format: "darwin".into(),
|
||||
has_thread_local,
|
||||
// Thread locals became available with iOS 8 and macOS 10.7,
|
||||
// and both are far below our minimum.
|
||||
has_thread_local: true,
|
||||
abi_return_struct_as_int: true,
|
||||
emit_debug_gdb_scripts: false,
|
||||
eh_frame_header: false,
|
||||
@ -239,9 +229,7 @@ fn macos_default_deployment_target(arch: Arch) -> (u32, u32) {
|
||||
match arch {
|
||||
// Note: Arm64_sim is not included since macOS has no simulator.
|
||||
Arm64 | Arm64_macabi => (11, 0),
|
||||
// x86_64h-apple-darwin only supports macOS 10.8 and later
|
||||
X86_64h => (10, 8),
|
||||
_ => (10, 7),
|
||||
_ => (10, 12),
|
||||
}
|
||||
}
|
||||
|
||||
@ -292,8 +280,8 @@ fn link_env_remove(arch: Arch, os: &'static str) -> StaticCow<[StaticCow<str>]>
|
||||
// Otherwise if cross-compiling for a different OS/SDK, remove any part
|
||||
// of the linking environment that's wrong and reversed.
|
||||
match arch {
|
||||
Armv7 | Armv7k | Armv7s | Arm64 | Arm64_32 | I386 | I686 | X86_64 | X86_64_sim
|
||||
| X86_64h | Arm64_sim => {
|
||||
Armv7k | Armv7s | Arm64 | Arm64_32 | I386 | I686 | X86_64 | X86_64_sim | X86_64h
|
||||
| Arm64_sim => {
|
||||
cvs!["MACOSX_DEPLOYMENT_TARGET"]
|
||||
}
|
||||
X86_64_macabi | Arm64_macabi => cvs!["IPHONEOS_DEPLOYMENT_TARGET"],
|
||||
@ -303,7 +291,7 @@ fn link_env_remove(arch: Arch, os: &'static str) -> StaticCow<[StaticCow<str>]>
|
||||
|
||||
fn ios_deployment_target() -> (u32, u32) {
|
||||
// If you are looking for the default deployment target, prefer `rustc --print deployment-target`.
|
||||
from_set_deployment_target("IPHONEOS_DEPLOYMENT_TARGET").unwrap_or((7, 0))
|
||||
from_set_deployment_target("IPHONEOS_DEPLOYMENT_TARGET").unwrap_or((10, 0))
|
||||
}
|
||||
|
||||
fn mac_catalyst_deployment_target() -> (u32, u32) {
|
||||
@ -334,7 +322,7 @@ pub fn ios_sim_llvm_target(arch: Arch) -> String {
|
||||
|
||||
fn tvos_deployment_target() -> (u32, u32) {
|
||||
// If you are looking for the default deployment target, prefer `rustc --print deployment-target`.
|
||||
from_set_deployment_target("TVOS_DEPLOYMENT_TARGET").unwrap_or((7, 0))
|
||||
from_set_deployment_target("TVOS_DEPLOYMENT_TARGET").unwrap_or((10, 0))
|
||||
}
|
||||
|
||||
fn tvos_lld_platform_version() -> String {
|
||||
|
@ -1,21 +0,0 @@
|
||||
use super::apple_base::{ios_llvm_target, opts, Arch};
|
||||
use crate::spec::{Target, TargetOptions};
|
||||
|
||||
pub fn target() -> Target {
|
||||
let arch = Arch::Armv7;
|
||||
Target {
|
||||
// Clang automatically chooses a more specific target based on
|
||||
// IPHONEOS_DEPLOYMENT_TARGET.
|
||||
// This is required for the target to pick the right
|
||||
// MACH-O commands, so we do too.
|
||||
llvm_target: ios_llvm_target(arch).into(),
|
||||
pointer_width: 32,
|
||||
data_layout: "e-m:o-p:32:32-Fi8-f64:32:64-v64:32:64-v128:32:128-a:0:32-n32-S32".into(),
|
||||
arch: arch.target_arch(),
|
||||
options: TargetOptions {
|
||||
features: "+v7,+vfp3,+neon".into(),
|
||||
max_atomic_width: Some(64),
|
||||
..opts("ios", arch)
|
||||
},
|
||||
}
|
||||
}
|
@ -1,10 +1,10 @@
|
||||
use super::apple_base::{opts, Arch};
|
||||
use super::apple_base::{ios_llvm_target, opts, Arch};
|
||||
use crate::spec::{Target, TargetOptions};
|
||||
|
||||
pub fn target() -> Target {
|
||||
let arch = Arch::Armv7s;
|
||||
Target {
|
||||
llvm_target: "armv7s-apple-ios".into(),
|
||||
llvm_target: ios_llvm_target(arch).into(),
|
||||
pointer_width: 32,
|
||||
data_layout: "e-m:o-p:32:32-Fi8-f64:32:64-v64:32:64-v128:32:128-a:0:32-n32-S32".into(),
|
||||
arch: arch.target_arch(),
|
||||
|
@ -1394,7 +1394,6 @@ supported_targets! {
|
||||
("i386-apple-ios", i386_apple_ios),
|
||||
("x86_64-apple-ios", x86_64_apple_ios),
|
||||
("aarch64-apple-ios", aarch64_apple_ios),
|
||||
("armv7-apple-ios", armv7_apple_ios),
|
||||
("armv7s-apple-ios", armv7s_apple_ios),
|
||||
("x86_64-apple-ios-macabi", x86_64_apple_ios_macabi),
|
||||
("aarch64-apple-ios-macabi", aarch64_apple_ios_macabi),
|
||||
|
@ -5,7 +5,7 @@ use crate::spec::{StackProbeType, Target, TargetOptions};
|
||||
pub fn target() -> Target {
|
||||
let arch = Arch::X86_64;
|
||||
let mut base = opts("macos", arch);
|
||||
base.max_atomic_width = Some(128); // core2 supports cmpxchg16b
|
||||
base.max_atomic_width = Some(128); // penryn+ supports cmpxchg16b
|
||||
base.frame_pointer = FramePointer::Always;
|
||||
base.add_pre_link_args(LinkerFlavor::Darwin(Cc::Yes, Lld::No), &["-m64"]);
|
||||
base.stack_probes = StackProbeType::X86;
|
||||
|
@ -469,7 +469,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
||||
// FIXME: These should ideally not exist as a self type. It would be nice for
|
||||
// the builtin auto trait impls of generators to instead directly recurse
|
||||
// into the witness.
|
||||
ty::GeneratorWitness(_) | ty::GeneratorWitnessMIR(_, _) => (),
|
||||
ty::GeneratorWitness(..) => (),
|
||||
|
||||
// These variants should not exist as a self type.
|
||||
ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_))
|
||||
@ -621,8 +621,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
||||
| ty::Dynamic(..)
|
||||
| ty::Closure(..)
|
||||
| ty::Generator(..)
|
||||
| ty::GeneratorWitness(_)
|
||||
| ty::GeneratorWitnessMIR(..)
|
||||
| ty::GeneratorWitness(..)
|
||||
| ty::Never
|
||||
| ty::Tuple(_)
|
||||
| ty::Param(_)
|
||||
@ -778,8 +777,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
||||
| ty::Alias(..)
|
||||
| ty::Closure(..)
|
||||
| ty::Generator(..)
|
||||
| ty::GeneratorWitness(_)
|
||||
| ty::GeneratorWitnessMIR(..)
|
||||
| ty::GeneratorWitness(..)
|
||||
| ty::Never
|
||||
| ty::Tuple(_)
|
||||
| ty::Param(_)
|
||||
|
@ -61,9 +61,7 @@ pub(in crate::solve) fn instantiate_constituent_tys_for_auto_trait<'tcx>(
|
||||
Ok(vec![generator_args.tupled_upvars_ty(), generator_args.witness()])
|
||||
}
|
||||
|
||||
ty::GeneratorWitness(types) => Ok(ecx.instantiate_binder_with_placeholders(types).to_vec()),
|
||||
|
||||
ty::GeneratorWitnessMIR(def_id, args) => Ok(ecx
|
||||
ty::GeneratorWitness(def_id, args) => Ok(ecx
|
||||
.tcx()
|
||||
.generator_hidden_types(def_id)
|
||||
.map(|bty| {
|
||||
@ -127,7 +125,6 @@ pub(in crate::solve) fn instantiate_constituent_tys_for_sized_trait<'tcx>(
|
||||
| ty::Ref(..)
|
||||
| ty::Generator(..)
|
||||
| ty::GeneratorWitness(..)
|
||||
| ty::GeneratorWitnessMIR(..)
|
||||
| ty::Array(..)
|
||||
| ty::Closure(..)
|
||||
| ty::Never
|
||||
@ -204,9 +201,7 @@ pub(in crate::solve) fn instantiate_constituent_tys_for_copy_clone_trait<'tcx>(
|
||||
}
|
||||
}
|
||||
|
||||
ty::GeneratorWitness(types) => Ok(ecx.instantiate_binder_with_placeholders(types).to_vec()),
|
||||
|
||||
ty::GeneratorWitnessMIR(def_id, args) => Ok(ecx
|
||||
ty::GeneratorWitness(def_id, args) => Ok(ecx
|
||||
.tcx()
|
||||
.generator_hidden_types(def_id)
|
||||
.map(|bty| {
|
||||
@ -282,8 +277,7 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_callable<'tcx>(
|
||||
| ty::Ref(_, _, _)
|
||||
| ty::Dynamic(_, _, _)
|
||||
| ty::Generator(_, _, _)
|
||||
| ty::GeneratorWitness(_)
|
||||
| ty::GeneratorWitnessMIR(..)
|
||||
| ty::GeneratorWitness(..)
|
||||
| ty::Never
|
||||
| ty::Tuple(_)
|
||||
| ty::Alias(_, _)
|
||||
|
@ -330,8 +330,7 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for Canonicalizer<'_, 'tcx> {
|
||||
| ty::Dynamic(_, _, _)
|
||||
| ty::Closure(_, _)
|
||||
| ty::Generator(_, _, _)
|
||||
| ty::GeneratorWitness(_)
|
||||
| ty::GeneratorWitnessMIR(..)
|
||||
| ty::GeneratorWitness(..)
|
||||
| ty::Never
|
||||
| ty::Tuple(_)
|
||||
| ty::Alias(_, _)
|
||||
|
@ -388,7 +388,6 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
|
||||
| ty::Infer(ty::IntVar(..) | ty::FloatVar(..))
|
||||
| ty::Generator(..)
|
||||
| ty::GeneratorWitness(..)
|
||||
| ty::GeneratorWitnessMIR(..)
|
||||
| ty::Never
|
||||
| ty::Foreign(..) => tcx.types.unit,
|
||||
|
||||
@ -556,7 +555,6 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
|
||||
| ty::Infer(ty::IntVar(..) | ty::FloatVar(..))
|
||||
| ty::Generator(..)
|
||||
| ty::GeneratorWitness(..)
|
||||
| ty::GeneratorWitnessMIR(..)
|
||||
| ty::Never
|
||||
| ty::Foreign(..)
|
||||
| ty::Adt(_, _)
|
||||
|
@ -879,8 +879,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
||||
| ty::FnPtr(_)
|
||||
| ty::Closure(_, _)
|
||||
| ty::Generator(_, _, _)
|
||||
| ty::GeneratorWitness(_)
|
||||
| ty::GeneratorWitnessMIR(_, _)
|
||||
| ty::GeneratorWitness(..)
|
||||
| ty::Never
|
||||
| ty::Tuple(_)
|
||||
| ty::Adt(_, _)
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user