diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-30 18:31:36 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-30 18:31:36 +0000 |
commit | e02c5b5930c2c9ba3e5423fe12e2ef0155017297 (patch) | |
tree | fd60ebbbb5299e16e5fca8c773ddb74f764760db /compiler/rustc_hir_typeck | |
parent | Adding debian version 1.73.0+dfsg1-1. (diff) | |
download | rustc-e02c5b5930c2c9ba3e5423fe12e2ef0155017297.tar.xz rustc-e02c5b5930c2c9ba3e5423fe12e2ef0155017297.zip |
Merging upstream version 1.74.1+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'compiler/rustc_hir_typeck')
29 files changed, 1015 insertions, 2706 deletions
diff --git a/compiler/rustc_hir_typeck/messages.ftl b/compiler/rustc_hir_typeck/messages.ftl index 2281343e2..9950a2263 100644 --- a/compiler/rustc_hir_typeck/messages.ftl +++ b/compiler/rustc_hir_typeck/messages.ftl @@ -16,6 +16,21 @@ hir_typeck_candidate_trait_note = `{$trait_name}` defines an item `{$item_name}` *[other] , perhaps you need to restrict type parameter `{$action_or_ty}` with it } +hir_typeck_cannot_cast_to_bool = cannot cast `{$expr_ty}` as `bool` + .suggestion = compare with zero instead + .help = compare with zero instead + .label = unsupported cast + +hir_typeck_cast_enum_drop = cannot cast enum `{$expr_ty}` into integer `{$cast_ty}` because it implements `Drop` + +hir_typeck_cast_unknown_pointer = cannot cast {$to -> + [true] to + *[false] from + } a pointer of an unknown kind + .label_to = needs more type information + .note = the type information given here is insufficient to check whether the pointer cast is valid + .label_from = the type information given here is insufficient to check whether the pointer cast is valid + hir_typeck_const_select_must_be_const = this argument must be a `const fn` .help = consult the documentation on `const_eval_select` for more information @@ -29,10 +44,16 @@ hir_typeck_convert_using_method = try using `{$sugg}` to convert `{$found}` to ` hir_typeck_ctor_is_private = tuple struct constructor `{$def}` is private +hir_typeck_deref_is_empty = this expression `Deref`s to `{$deref_ty}` which implements `is_empty` + hir_typeck_expected_default_return_type = expected `()` because of default return type hir_typeck_expected_return_type = expected `{$expected}` because of return type +hir_typeck_explicit_destructor = explicit use of destructor method + .label = explicit destructor calls not allowed + .suggestion = consider using `drop` function + hir_typeck_field_multiply_specified_in_initializer = field `{$ident}` specified more than once .label = used more than once @@ -52,20 +73,32 @@ hir_typeck_functional_record_update_on_non_struct = hir_typeck_help_set_edition_cargo = set `edition = "{$edition}"` in `Cargo.toml` hir_typeck_help_set_edition_standalone = pass `--edition {$edition}` to `rustc` -hir_typeck_lang_start_expected_sig_note = the `start` lang item should have the signature `fn(fn() -> T, isize, *const *const u8, u8) -> isize` -hir_typeck_lang_start_incorrect_number_params = incorrect number of parameters for the `start` lang item -hir_typeck_lang_start_incorrect_number_params_note_expected_count = the `start` lang item should have four parameters, but found {$found_param_count} +hir_typeck_int_to_fat = cannot cast `{$expr_ty}` to a pointer that {$known_wide -> + [true] is + *[false] may be + } wide +hir_typeck_int_to_fat_label = creating a `{$cast_ty}` requires both an address and {$metadata} +hir_typeck_int_to_fat_label_nightly = consider casting this expression to `*const ()`, then using `core::ptr::from_raw_parts` + +hir_typeck_invalid_callee = expected function, found {$ty} -hir_typeck_lang_start_incorrect_param = parameter {$param_num} of the `start` lang item is incorrect - .suggestion = change the type from `{$found_ty}` to `{$expected_ty}` +hir_typeck_lossy_provenance_int2ptr = + strict provenance disallows casting integer `{$expr_ty}` to pointer `{$cast_ty}` + .suggestion = use `.with_addr()` to adjust a valid pointer in the same allocation, to this address + .help = if you can't comply with strict provenance and don't have a pointer with the correct provenance you can use `std::ptr::from_exposed_addr()` instead -hir_typeck_lang_start_incorrect_ret_ty = the return type of the `start` lang item is incorrect - .suggestion = change the type from `{$found_ty}` to `{$expected_ty}` +hir_typeck_lossy_provenance_ptr2int = + under strict provenance it is considered bad style to cast pointer `{$expr_ty}` to integer `{$cast_ty}` + .suggestion = use `.addr()` to obtain the address of a pointer + .help = if you can't comply with strict provenance and need to expose the pointer provenance you can use `.expose_addr()` instead hir_typeck_method_call_on_unknown_raw_pointee = cannot call a method on a raw pointer with an unknown pointee type +hir_typeck_missing_fn_lang_items = failed to find an overloaded call trait for closure call + .help = make sure the `fn`/`fn_mut`/`fn_once` lang items are defined and have correctly defined `call`/`call_mut`/`call_once` methods + hir_typeck_missing_parentheses_in_range = can't call method `{$method_name}` on type `{$ty_str}` hir_typeck_no_associated_item = no {$item_kind} named `{$item_name}` found for {$ty_prefix} `{$ty_str}`{$trait_missing_method -> @@ -81,11 +114,20 @@ hir_typeck_option_result_asref = use `{$def_path}::as_ref` to convert `{$expecte hir_typeck_option_result_cloned = use `{$def_path}::cloned` to clone the value inside the `{$def_path}` hir_typeck_option_result_copied = use `{$def_path}::copied` to copy the value inside the `{$def_path}` +hir_typeck_remove_semi_for_coerce = you might have meant to return the `match` expression +hir_typeck_remove_semi_for_coerce_expr = this could be implicitly returned but it is a statement, not a tail expression +hir_typeck_remove_semi_for_coerce_ret = the `match` arms can conform to this return type +hir_typeck_remove_semi_for_coerce_semi = the `match` is a statement because of this semicolon, consider removing it +hir_typeck_remove_semi_for_coerce_suggestion = remove this semicolon + hir_typeck_return_stmt_outside_of_fn_body = {$statement_kind} statement outside of function body .encl_body_label = the {$statement_kind} is part of this body... .encl_fn_label = ...not the enclosing function body +hir_typeck_rustcall_incorrect_args = + functions with the "rust-call" ABI must take a single non-self tuple argument + hir_typeck_struct_expr_non_exhaustive = cannot create non-exhaustive {$what} using struct expression @@ -95,8 +137,18 @@ hir_typeck_suggest_boxing_when_appropriate = store this in the heap by calling ` hir_typeck_suggest_ptr_null_mut = consider using `core::ptr::null_mut` instead +hir_typeck_trivial_cast = trivial {$numeric -> + [true] numeric cast + *[false] cast + }: `{$expr_ty}` as `{$cast_ty}` + .help = cast can be replaced by coercion; this might require a temporary variable + hir_typeck_union_pat_dotdot = `..` cannot be used in union patterns hir_typeck_union_pat_multiple_fields = union patterns should have exactly one field + +hir_typeck_use_is_empty = + consider using the `is_empty` method on `{$expr_ty}` to determine if it contains anything + hir_typeck_yield_expr_outside_of_generator = yield expression outside of generator literal diff --git a/compiler/rustc_hir_typeck/src/_match.rs b/compiler/rustc_hir_typeck/src/_match.rs index 7ad9f51ba..81fe0cc48 100644 --- a/compiler/rustc_hir_typeck/src/_match.rs +++ b/compiler/rustc_hir_typeck/src/_match.rs @@ -1,7 +1,8 @@ use crate::coercion::{AsCoercionSite, CoerceMany}; use crate::{Diverges, Expectation, FnCtxt, Needs}; -use rustc_errors::{Applicability, Diagnostic, MultiSpan}; +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}; @@ -225,24 +226,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return; } - let semi_span = expr.span.shrink_to_hi().with_hi(semi_span.hi()); - let mut ret_span: MultiSpan = semi_span.into(); - ret_span.push_span_label( - expr.span, - "this could be implicitly returned but it is a statement, not a tail expression", - ); - ret_span.push_span_label(ret, "the `match` arms can conform to this return type"); - ret_span.push_span_label( - semi_span, - "the `match` is a statement because of this semicolon, consider removing it", - ); - diag.span_note(ret_span, "you might have meant to return the `match` expression"); - diag.tool_only_span_suggestion( - semi_span, - "remove this semicolon", - "", - Applicability::MaybeIncorrect, - ); + let semi = expr.span.shrink_to_hi().with_hi(semi_span.hi()); + let sugg = crate::errors::RemoveSemiForCoerce { expr: expr.span, ret, semi }; + diag.subdiagnostic(sugg); } /// When the previously checked expression (the scrutinee) diverges, @@ -267,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( @@ -290,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)); @@ -304,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"))) }); } } diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs index 02371f85a..f2c58ee27 100644 --- a/compiler/rustc_hir_typeck/src/callee.rs +++ b/compiler/rustc_hir_typeck/src/callee.rs @@ -2,9 +2,9 @@ use super::method::probe::ProbeScope; use super::method::MethodCallee; use super::{Expectation, FnCtxt, TupleArgumentsFlag}; -use crate::type_error_struct; +use crate::errors; use rustc_ast::util::parser::PREC_POSTFIX; -use rustc_errors::{struct_span_err, Applicability, Diagnostic, ErrorGuaranteed, StashKey}; +use rustc_errors::{Applicability, Diagnostic, ErrorGuaranteed, StashKey}; use rustc_hir as hir; use rustc_hir::def::{self, CtorKind, DefKind, Namespace, Res}; use rustc_hir::def_id::DefId; @@ -44,23 +44,15 @@ pub fn check_legal_trait_for_method_call( trait_id: DefId, ) { if tcx.lang_items().drop_trait() == Some(trait_id) { - let mut err = struct_span_err!(tcx.sess, span, E0040, "explicit use of destructor method"); - err.span_label(span, "explicit destructor calls not allowed"); - - let (sp, suggestion) = receiver - .and_then(|s| tcx.sess.source_map().span_to_snippet(s).ok()) - .filter(|snippet| !snippet.is_empty()) - .map(|snippet| (expr_span, format!("drop({snippet})"))) - .unwrap_or_else(|| (span, "drop".to_string())); - - err.span_suggestion( - sp, - "consider using `drop` function", - suggestion, - Applicability::MaybeIncorrect, - ); - - err.emit(); + let sugg = if let Some(receiver) = receiver.filter(|s| !s.is_empty()) { + errors::ExplicitDestructorCallSugg::Snippet { + lo: expr_span.shrink_to_lo(), + hi: receiver.shrink_to_hi().to(expr_span.shrink_to_hi()), + } + } else { + errors::ExplicitDestructorCallSugg::Empty(span) + }; + tcx.sess.emit_err(errors::ExplicitDestructorCall { span, sugg }); } } @@ -387,6 +379,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Unit testing: function items annotated with // `#[rustc_evaluate_where_clauses]` trigger special output // to let us test the trait evaluation system. + // Untranslatable diagnostics are okay for rustc internals + #[allow(rustc::untranslatable_diagnostic)] + #[allow(rustc::diagnostic_outside_of_impl)] if self.tcx.has_attr(def_id, sym::rustc_evaluate_where_clauses) { let predicates = self.tcx.predicates_of(def_id); let predicates = predicates.instantiate(self.tcx, args); @@ -478,10 +473,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); self.require_type_is_sized(ty, sp, traits::RustCall); } else { - self.tcx.sess.span_err( - sp, - "functions with the \"rust-call\" ABI must take a single non-self tuple argument", - ); + self.tcx.sess.emit_err(errors::RustCallIncorrectArgs { span: sp }); } } @@ -610,17 +602,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } let callee_ty = self.resolve_vars_if_possible(callee_ty); - let mut err = type_error_struct!( - self.tcx.sess, - callee_expr.span, - callee_ty, - E0618, - "expected function, found {}", - match &unit_variant { + let mut err = self.tcx.sess.create_err(errors::InvalidCallee { + span: callee_expr.span, + ty: match &unit_variant { Some((_, kind, path)) => format!("{kind} `{path}`"), None => format!("`{callee_ty}`"), - } - ); + }, + }); + if callee_ty.references_error() { + err.downgrade_to_delayed_bug(); + } self.identify_bad_closure_def_and_call( &mut err, @@ -797,7 +788,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let effect = match const_context { _ if host_always_on => tcx.consts.true_, - Some(hir::ConstContext::Static(_) | hir::ConstContext::Const) => tcx.consts.false_, + Some(hir::ConstContext::Static(_) | hir::ConstContext::Const { .. }) => { + tcx.consts.false_ + } Some(hir::ConstContext::ConstFn) => { let args = ty::GenericArgs::identity_for_item(tcx, context); args.host_effect_param().expect("ConstContext::Maybe must have host effect param") @@ -891,15 +884,7 @@ impl<'a, 'tcx> DeferredCallResolution<'tcx> { None => { // This can happen if `#![no_core]` is used and the `fn/fn_mut/fn_once` // lang items are not defined (issue #86238). - let mut err = fcx.inh.tcx.sess.struct_span_err( - self.call_expr.span, - "failed to find an overloaded call trait for closure call", - ); - err.help( - "make sure the `fn`/`fn_mut`/`fn_once` lang items are defined \ - and have correctly defined `call`/`call_mut`/`call_once` methods", - ); - err.emit(); + fcx.inh.tcx.sess.emit_err(errors::MissingFnLangItems { span: self.call_expr.span }); } } } diff --git a/compiler/rustc_hir_typeck/src/cast.rs b/compiler/rustc_hir_typeck/src/cast.rs index 31a03fabe..2b1ac7f35 100644 --- a/compiler/rustc_hir_typeck/src/cast.rs +++ b/compiler/rustc_hir_typeck/src/cast.rs @@ -30,11 +30,10 @@ use super::FnCtxt; +use crate::errors; use crate::type_error_struct; use hir::ExprKind; -use rustc_errors::{ - struct_span_err, Applicability, DelayDm, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, -}; +use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed}; use rustc_hir as hir; use rustc_macros::{TypeFoldable, TypeVisitable}; use rustc_middle::mir::Mutability; @@ -130,7 +129,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { | ty::Float(_) | ty::Array(..) | ty::GeneratorWitness(..) - | ty::GeneratorWitnessMIR(..) | ty::RawPtr(_) | ty::Ref(..) | ty::FnDef(..) @@ -321,28 +319,15 @@ impl<'a, 'tcx> CastCheck<'tcx> { .emit(); } CastError::CastToBool => { - let mut err = - struct_span_err!(fcx.tcx.sess, self.span, E0054, "cannot cast as `bool`"); - - if self.expr_ty.is_numeric() { - match fcx.tcx.sess.source_map().span_to_snippet(self.expr_span) { - Ok(snippet) => { - err.span_suggestion( - self.span, - "compare with zero instead", - format!("{snippet} != 0"), - Applicability::MachineApplicable, - ); - } - Err(_) => { - err.span_help(self.span, "compare with zero instead"); - } - } + let expr_ty = fcx.resolve_vars_if_possible(self.expr_ty); + let help = if self.expr_ty.is_numeric() { + errors::CannotCastToBoolHelp::Numeric( + self.expr_span.shrink_to_hi().with_hi(self.span.hi()), + ) } else { - err.span_label(self.span, "unsupported cast"); - } - - err.emit(); + errors::CannotCastToBoolHelp::Unsupported(self.span) + }; + fcx.tcx.sess.emit_err(errors::CannotCastToBool { span: self.span, expr_ty, help }); } CastError::CastToChar => { let mut err = type_error_struct!( @@ -531,33 +516,20 @@ impl<'a, 'tcx> CastCheck<'tcx> { .emit(); } CastError::IntToFatCast(known_metadata) => { - let mut err = struct_span_err!( - fcx.tcx.sess, - self.cast_span, - E0606, - "cannot cast `{}` to a pointer that {} wide", - fcx.ty_to_string(self.expr_ty), - if known_metadata.is_some() { "is" } else { "may be" } - ); - - err.span_label( - self.cast_span, - format!( - "creating a `{}` requires both an address and {}", - self.cast_ty, - known_metadata.unwrap_or("type-specific metadata"), - ), - ); - - if fcx.tcx.sess.is_nightly_build() { - err.span_label( - self.expr_span, - "consider casting this expression to `*const ()`, \ - then using `core::ptr::from_raw_parts`", - ); - } - - err.emit(); + let expr_if_nightly = fcx.tcx.sess.is_nightly_build().then_some(self.expr_span); + let cast_ty = fcx.resolve_vars_if_possible(self.cast_ty); + let expr_ty = fcx.ty_to_string(self.expr_ty); + let metadata = known_metadata.unwrap_or("type-specific metadata"); + let known_wide = known_metadata.is_some(); + let span = self.cast_span; + fcx.tcx.sess.emit_err(errors::IntToWide { + span, + metadata, + expr_ty, + cast_ty, + expr_if_nightly, + known_wide, + }); } CastError::UnknownCastPtrKind | CastError::UnknownExprPtrKind => { let unknown_cast_to = match e { @@ -565,27 +537,16 @@ impl<'a, 'tcx> CastCheck<'tcx> { CastError::UnknownExprPtrKind => false, _ => bug!(), }; - let mut err = struct_span_err!( - fcx.tcx.sess, - if unknown_cast_to { self.cast_span } else { self.span }, - E0641, - "cannot cast {} a pointer of an unknown kind", - if unknown_cast_to { "to" } else { "from" } - ); - if unknown_cast_to { - err.span_label(self.cast_span, "needs more type information"); - err.note( - "the type information given here is insufficient to check whether \ - the pointer cast is valid", - ); + let (span, sub) = if unknown_cast_to { + (self.cast_span, errors::CastUnknownPointerSub::To(self.cast_span)) } else { - err.span_label( - self.span, - "the type information given here is insufficient to check whether \ - the pointer cast is valid", - ); - } - err.emit(); + (self.cast_span, errors::CastUnknownPointerSub::From(self.span)) + }; + fcx.tcx.sess.emit_err(errors::CastUnknownPointer { + span, + to: unknown_cast_to, + sub, + }); } CastError::ForeignNonExhaustiveAdt => { make_invalid_casting_error( @@ -669,31 +630,18 @@ impl<'a, 'tcx> CastCheck<'tcx> { } fn trivial_cast_lint(&self, fcx: &FnCtxt<'a, 'tcx>) { - let t_cast = self.cast_ty; - let t_expr = self.expr_ty; - let (adjective, lint) = if t_cast.is_numeric() && t_expr.is_numeric() { - ("numeric ", lint::builtin::TRIVIAL_NUMERIC_CASTS) + let (numeric, lint) = if self.cast_ty.is_numeric() && self.expr_ty.is_numeric() { + (true, lint::builtin::TRIVIAL_NUMERIC_CASTS) } else { - ("", lint::builtin::TRIVIAL_CASTS) + (false, lint::builtin::TRIVIAL_CASTS) }; - fcx.tcx.struct_span_lint_hir( + let expr_ty = fcx.resolve_vars_if_possible(self.expr_ty); + let cast_ty = fcx.resolve_vars_if_possible(self.cast_ty); + fcx.tcx.emit_spanned_lint( lint, self.expr.hir_id, self.span, - DelayDm(|| { - format!( - "trivial {}cast: `{}` as `{}`", - adjective, - fcx.ty_to_string(t_expr), - fcx.ty_to_string(t_cast) - ) - }), - |lint| { - lint.help( - "cast can be replaced by coercion; this might \ - require a temporary variable", - ) - }, + errors::TrivialCast { numeric, expr_ty, cast_ty }, ); } @@ -776,6 +724,9 @@ impl<'a, 'tcx> CastCheck<'tcx> { }, // array-ptr-cast Ptr(mt) => { + if !fcx.type_is_sized_modulo_regions(fcx.param_env, mt.ty) { + return Err(CastError::IllegalCast); + } self.check_ref_cast(fcx, TypeAndMut { mutbl, ty: inner_ty }, mt) } _ => Err(CastError::NonScalar), @@ -786,7 +737,6 @@ impl<'a, 'tcx> CastCheck<'tcx> { } _ => return Err(CastError::NonScalar), }; - if let ty::Adt(adt_def, _) = *self.expr_ty.kind() { if adt_def.did().krate != LOCAL_CRATE { if adt_def.variants().iter().any(VariantDef::is_field_list_non_exhaustive) { @@ -794,7 +744,6 @@ impl<'a, 'tcx> CastCheck<'tcx> { } } } - match (t_from, t_cast) { // These types have invariants! can't cast into them. (_, Int(CEnum) | FnPtr) => Err(CastError::NonScalar), @@ -986,93 +935,67 @@ impl<'a, 'tcx> CastCheck<'tcx> { if let ty::Adt(d, _) = self.expr_ty.kind() && d.has_dtor(fcx.tcx) { - fcx.tcx.struct_span_lint_hir( + let expr_ty = fcx.resolve_vars_if_possible(self.expr_ty); + let cast_ty = fcx.resolve_vars_if_possible(self.cast_ty); + + fcx.tcx.emit_spanned_lint( lint::builtin::CENUM_IMPL_DROP_CAST, self.expr.hir_id, self.span, - DelayDm(|| format!( - "cannot cast enum `{}` into integer `{}` because it implements `Drop`", - self.expr_ty, self.cast_ty - )), - |lint| { - lint - }, + errors::CastEnumDrop { + expr_ty, + cast_ty, + } ); } } fn lossy_provenance_ptr2int_lint(&self, fcx: &FnCtxt<'a, 'tcx>, t_c: ty::cast::IntTy) { - fcx.tcx.struct_span_lint_hir( + let expr_prec = self.expr.precedence().order(); + let needs_parens = expr_prec < rustc_ast::util::parser::PREC_POSTFIX; + + let needs_cast = !matches!(t_c, ty::cast::IntTy::U(ty::UintTy::Usize)); + let cast_span = self.expr_span.shrink_to_hi().to(self.cast_span); + let expr_ty = fcx.resolve_vars_if_possible(self.expr_ty); + let cast_ty = fcx.resolve_vars_if_possible(self.cast_ty); + let expr_span = self.expr_span.shrink_to_lo(); + let sugg = match (needs_parens, needs_cast) { + (true, true) => errors::LossyProvenancePtr2IntSuggestion::NeedsParensCast { + expr_span, + cast_span, + cast_ty, + }, + (true, false) => { + errors::LossyProvenancePtr2IntSuggestion::NeedsParens { expr_span, cast_span } + } + (false, true) => { + errors::LossyProvenancePtr2IntSuggestion::NeedsCast { cast_span, cast_ty } + } + (false, false) => errors::LossyProvenancePtr2IntSuggestion::Other { cast_span }, + }; + + let lint = errors::LossyProvenancePtr2Int { expr_ty, cast_ty, sugg }; + fcx.tcx.emit_spanned_lint( lint::builtin::LOSSY_PROVENANCE_CASTS, self.expr.hir_id, self.span, - DelayDm(|| format!( - "under strict provenance it is considered bad style to cast pointer `{}` to integer `{}`", - self.expr_ty, self.cast_ty - )), - |lint| { - let msg = "use `.addr()` to obtain the address of a pointer"; - - let expr_prec = self.expr.precedence().order(); - let needs_parens = expr_prec < rustc_ast::util::parser::PREC_POSTFIX; - - let scalar_cast = match t_c { - ty::cast::IntTy::U(ty::UintTy::Usize) => String::new(), - _ => format!(" as {}", self.cast_ty), - }; - - let cast_span = self.expr_span.shrink_to_hi().to(self.cast_span); - - if needs_parens { - let suggestions = vec![ - (self.expr_span.shrink_to_lo(), String::from("(")), - (cast_span, format!(").addr(){scalar_cast}")), - ]; - - lint.multipart_suggestion(msg, suggestions, Applicability::MaybeIncorrect); - } else { - lint.span_suggestion( - cast_span, - msg, - format!(".addr(){scalar_cast}"), - Applicability::MaybeIncorrect, - ); - } - - lint.help( - "if you can't comply with strict provenance and need to expose the pointer \ - provenance you can use `.expose_addr()` instead" - ); - - lint - }, + lint, ); } fn fuzzy_provenance_int2ptr_lint(&self, fcx: &FnCtxt<'a, 'tcx>) { - fcx.tcx.struct_span_lint_hir( + let sugg = errors::LossyProvenanceInt2PtrSuggestion { + lo: self.expr_span.shrink_to_lo(), + hi: self.expr_span.shrink_to_hi().to(self.cast_span), + }; + let expr_ty = fcx.resolve_vars_if_possible(self.expr_ty); + let cast_ty = fcx.resolve_vars_if_possible(self.cast_ty); + let lint = errors::LossyProvenanceInt2Ptr { expr_ty, cast_ty, sugg }; + fcx.tcx.emit_spanned_lint( lint::builtin::FUZZY_PROVENANCE_CASTS, self.expr.hir_id, self.span, - DelayDm(|| format!( - "strict provenance disallows casting integer `{}` to pointer `{}`", - self.expr_ty, self.cast_ty - )), - |lint| { - let msg = "use `.with_addr()` to adjust a valid pointer in the same allocation, to this address"; - let suggestions = vec![ - (self.expr_span.shrink_to_lo(), String::from("(...).with_addr(")), - (self.expr_span.shrink_to_hi().to(self.cast_span), String::from(")")), - ]; - - lint.multipart_suggestion(msg, suggestions, Applicability::MaybeIncorrect); - lint.help( - "if you can't comply with strict provenance and don't have a pointer with \ - the correct provenance you can use `std::ptr::from_exposed_addr()` instead" - ); - - lint - }, + lint, ); } @@ -1088,26 +1011,19 @@ impl<'a, 'tcx> CastCheck<'tcx> { if let Some((deref_ty, _)) = derefed { // Give a note about what the expr derefs to. if deref_ty != self.expr_ty.peel_refs() { - err.span_note( - self.expr_span, - format!( - "this expression `Deref`s to `{}` which implements `is_empty`", - fcx.ty_to_string(deref_ty) - ), - ); + err.subdiagnostic(errors::DerefImplsIsEmpty { + span: self.expr_span, + deref_ty: fcx.ty_to_string(deref_ty), + }); } // Create a multipart suggestion: add `!` and `.is_empty()` in // place of the cast. - let suggestion = vec![ - (self.expr_span.shrink_to_lo(), "!".to_string()), - (self.span.with_lo(self.expr_span.hi()), ".is_empty()".to_string()), - ]; - - err.multipart_suggestion_verbose(format!( - "consider using the `is_empty` method on `{}` to determine if it contains anything", - fcx.ty_to_string(self.expr_ty), - ), suggestion, Applicability::MaybeIncorrect); + err.subdiagnostic(errors::UseIsEmpty { + lo: self.expr_span.shrink_to_lo(), + hi: self.span.with_lo(self.expr_span.hi()), + expr_ty: fcx.ty_to_string(self.expr_ty), + }); } } } diff --git a/compiler/rustc_hir_typeck/src/check.rs b/compiler/rustc_hir_typeck/src/check.rs index 1fc1e5aca..1fa0ec173 100644 --- a/compiler/rustc_hir_typeck/src/check.rs +++ b/compiler/rustc_hir_typeck/src/check.rs @@ -1,7 +1,6 @@ +use std::cell::RefCell; + use crate::coercion::CoerceMany; -use crate::errors::{ - LangStartIncorrectNumberArgs, LangStartIncorrectParam, LangStartIncorrectRetTy, -}; use crate::gather_locals::GatherLocalsVisitor; use crate::FnCtxt; use crate::GeneratorTypes; @@ -9,14 +8,15 @@ use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::intravisit::Visitor; use rustc_hir::lang_items::LangItem; -use rustc_hir_analysis::check::fn_maybe_err; +use rustc_hir_analysis::check::{check_function_signature, fn_maybe_err}; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_infer::infer::RegionVariableOrigin; use rustc_middle::ty::{self, Binder, Ty, TyCtxt}; use rustc_span::def_id::LocalDefId; +use rustc_span::symbol::sym; use rustc_target::spec::abi::Abi; use rustc_trait_selection::traits; -use std::cell::RefCell; +use rustc_trait_selection::traits::{ObligationCause, ObligationCauseCode}; /// Helper used for fns and closures. Does the grungy work of checking a function /// body and returns the function context used for that purpose, since in the case of a fn item @@ -166,52 +166,17 @@ pub(super) fn check_fn<'a, 'tcx>( if let Some(panic_impl_did) = tcx.lang_items().panic_impl() && panic_impl_did == fn_def_id.to_def_id() { - check_panic_info_fn(tcx, panic_impl_did.expect_local(), fn_sig, decl, declared_ret_ty); + check_panic_info_fn(tcx, panic_impl_did.expect_local(), fn_sig); } if let Some(lang_start_defid) = tcx.lang_items().start_fn() && lang_start_defid == fn_def_id.to_def_id() { - check_lang_start_fn(tcx, fn_sig, decl, fn_def_id); + check_lang_start_fn(tcx, fn_sig, fn_def_id); } gen_ty } -fn check_panic_info_fn( - tcx: TyCtxt<'_>, - fn_id: LocalDefId, - fn_sig: ty::FnSig<'_>, - decl: &hir::FnDecl<'_>, - declared_ret_ty: Ty<'_>, -) { - let Some(panic_info_did) = tcx.lang_items().panic_info() else { - tcx.sess.err("language item required, but not found: `panic_info`"); - return; - }; - - if *declared_ret_ty.kind() != ty::Never { - tcx.sess.span_err(decl.output.span(), "return type should be `!`"); - } - - let inputs = fn_sig.inputs(); - if inputs.len() != 1 { - tcx.sess.span_err(tcx.def_span(fn_id), "function should have one argument"); - return; - } - - let arg_is_panic_info = match *inputs[0].kind() { - ty::Ref(region, ty, mutbl) => match *ty.kind() { - ty::Adt(ref adt, _) => { - adt.did() == panic_info_did && mutbl.is_not() && !region.is_static() - } - _ => false, - }, - _ => false, - }; - - if !arg_is_panic_info { - tcx.sess.span_err(decl.inputs[0].span, "argument should be `&PanicInfo`"); - } - +fn check_panic_info_fn(tcx: TyCtxt<'_>, fn_id: LocalDefId, fn_sig: ty::FnSig<'_>) { let DefKind::Fn = tcx.def_kind(fn_id) else { let span = tcx.def_span(fn_id); tcx.sess.span_err(span, "should be a function"); @@ -227,125 +192,87 @@ fn check_panic_info_fn( let span = tcx.def_span(fn_id); tcx.sess.span_err(span, "should have no const parameters"); } -} - -fn check_lang_start_fn<'tcx>( - tcx: TyCtxt<'tcx>, - fn_sig: ty::FnSig<'tcx>, - decl: &'tcx hir::FnDecl<'tcx>, - def_id: LocalDefId, -) { - let inputs = fn_sig.inputs(); - - let arg_count = inputs.len(); - if arg_count != 4 { - tcx.sess.emit_err(LangStartIncorrectNumberArgs { - params_span: tcx.def_span(def_id), - found_param_count: arg_count, - }); - } - // only check args if they should exist by checking the count - // note: this does not handle args being shifted or their order swapped very nicely - // but it's a lang item, users shouldn't frequently encounter this - - // first arg is `main: fn() -> T` - if let Some(&main_arg) = inputs.get(0) { - // make a Ty for the generic on the fn for diagnostics - // FIXME: make the lang item generic checks check for the right generic *kind* - // for example `start`'s generic should be a type parameter - let generics = tcx.generics_of(def_id); - let fn_generic = generics.param_at(0, tcx); - let generic_ty = Ty::new_param(tcx, fn_generic.index, fn_generic.name); - let expected_fn_sig = - tcx.mk_fn_sig([], generic_ty, false, hir::Unsafety::Normal, Abi::Rust); - let expected_ty = Ty::new_fn_ptr(tcx, Binder::dummy(expected_fn_sig)); - - // we emit the same error to suggest changing the arg no matter what's wrong with the arg - let emit_main_fn_arg_err = || { - tcx.sess.emit_err(LangStartIncorrectParam { - param_span: decl.inputs[0].span, - param_num: 1, - expected_ty: expected_ty, - found_ty: main_arg, - }); - }; - - if let ty::FnPtr(main_fn_sig) = main_arg.kind() { - let main_fn_inputs = main_fn_sig.inputs(); - if main_fn_inputs.iter().count() != 0 { - emit_main_fn_arg_err(); - } - - let output = main_fn_sig.output(); - output.map_bound(|ret_ty| { - // if the output ty is a generic, it's probably the right one - if !matches!(ret_ty.kind(), ty::Param(_)) { - emit_main_fn_arg_err(); - } - }); - } else { - emit_main_fn_arg_err(); - } - } - - // second arg is isize - if let Some(&argc_arg) = inputs.get(1) { - if argc_arg != tcx.types.isize { - tcx.sess.emit_err(LangStartIncorrectParam { - param_span: decl.inputs[1].span, - param_num: 2, - expected_ty: tcx.types.isize, - found_ty: argc_arg, - }); - } - } - - // third arg is `*const *const u8` - if let Some(&argv_arg) = inputs.get(2) { - let mut argv_is_okay = false; - if let ty::RawPtr(outer_ptr) = argv_arg.kind() { - if outer_ptr.mutbl.is_not() { - if let ty::RawPtr(inner_ptr) = outer_ptr.ty.kind() { - if inner_ptr.mutbl.is_not() && inner_ptr.ty == tcx.types.u8 { - argv_is_okay = true; - } - } - } - } - - if !argv_is_okay { - let inner_ptr_ty = - Ty::new_ptr(tcx, ty::TypeAndMut { mutbl: hir::Mutability::Not, ty: tcx.types.u8 }); - let expected_ty = - Ty::new_ptr(tcx, ty::TypeAndMut { mutbl: hir::Mutability::Not, ty: inner_ptr_ty }); - tcx.sess.emit_err(LangStartIncorrectParam { - param_span: decl.inputs[2].span, - param_num: 3, - expected_ty, - found_ty: argv_arg, - }); - } - } + let Some(panic_info_did) = tcx.lang_items().panic_info() else { + tcx.sess.err("language item required, but not found: `panic_info`"); + return; + }; - // fourth arg is `sigpipe: u8` - if let Some(&sigpipe_arg) = inputs.get(3) { - if sigpipe_arg != tcx.types.u8 { - tcx.sess.emit_err(LangStartIncorrectParam { - param_span: decl.inputs[3].span, - param_num: 4, - expected_ty: tcx.types.u8, - found_ty: sigpipe_arg, - }); - } - } + // build type `for<'a, 'b> fn(&'a PanicInfo<'b>) -> !` + let panic_info_ty = tcx.type_of(panic_info_did).instantiate( + tcx, + &[ty::GenericArg::from(ty::Region::new_late_bound( + tcx, + ty::INNERMOST, + ty::BoundRegion { var: ty::BoundVar::from_u32(1), kind: ty::BrAnon }, + ))], + ); + let panic_info_ref_ty = Ty::new_imm_ref( + tcx, + ty::Region::new_late_bound( + tcx, + ty::INNERMOST, + ty::BoundRegion { var: ty::BoundVar::from_u32(0), kind: ty::BrAnon }, + ), + panic_info_ty, + ); + + let bounds = tcx.mk_bound_variable_kinds(&[ + ty::BoundVariableKind::Region(ty::BrAnon), + ty::BoundVariableKind::Region(ty::BrAnon), + ]); + let expected_sig = ty::Binder::bind_with_vars( + tcx.mk_fn_sig([panic_info_ref_ty], tcx.types.never, false, fn_sig.unsafety, Abi::Rust), + bounds, + ); + + check_function_signature( + tcx, + ObligationCause::new( + tcx.def_span(fn_id), + fn_id, + ObligationCauseCode::LangFunctionType(sym::panic_impl), + ), + fn_id.into(), + expected_sig, + ); +} - // output type is isize - if fn_sig.output() != tcx.types.isize { - tcx.sess.emit_err(LangStartIncorrectRetTy { - ret_span: decl.output.span(), - expected_ty: tcx.types.isize, - found_ty: fn_sig.output(), - }); - } +fn check_lang_start_fn<'tcx>(tcx: TyCtxt<'tcx>, fn_sig: ty::FnSig<'tcx>, def_id: LocalDefId) { + // build type `fn(main: fn() -> T, argc: isize, argv: *const *const u8, sigpipe: u8)` + + // make a Ty for the generic on the fn for diagnostics + // FIXME: make the lang item generic checks check for the right generic *kind* + // for example `start`'s generic should be a type parameter + let generics = tcx.generics_of(def_id); + let fn_generic = generics.param_at(0, tcx); + let generic_ty = Ty::new_param(tcx, fn_generic.index, fn_generic.name); + let main_fn_ty = Ty::new_fn_ptr( + tcx, + Binder::dummy(tcx.mk_fn_sig([], generic_ty, false, hir::Unsafety::Normal, Abi::Rust)), + ); + + let expected_sig = ty::Binder::dummy(tcx.mk_fn_sig( + [ + main_fn_ty, + tcx.types.isize, + Ty::new_imm_ptr(tcx, Ty::new_imm_ptr(tcx, tcx.types.u8)), + tcx.types.u8, + ], + tcx.types.isize, + false, + fn_sig.unsafety, + Abi::Rust, + )); + + check_function_signature( + tcx, + ObligationCause::new( + tcx.def_span(def_id), + def_id, + ObligationCauseCode::LangFunctionType(sym::start), + ), + def_id.into(), + expected_sig, + ); } diff --git a/compiler/rustc_hir_typeck/src/closure.rs b/compiler/rustc_hir_typeck/src/closure.rs index b19fb6da6..e426b9375 100644 --- a/compiler/rustc_hir_typeck/src/closure.rs +++ b/compiler/rustc_hir_typeck/src/closure.rs @@ -231,7 +231,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let inferred_sig = self.normalize( span, self.deduce_sig_from_projection( - Some(span), + Some(span), bound_predicate.rebind(proj_predicate), ), ); diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs index 2c16f21b4..d97691369 100644 --- a/compiler/rustc_hir_typeck/src/demand.rs +++ b/compiler/rustc_hir_typeck/src/demand.rs @@ -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,136 @@ 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. + pub 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); + let mut parent; + 'outer: loop { + // Climb the HIR tree to see if the current `Expr` is part of a `break;` statement. + let Some( + hir::Node::Stmt(hir::Stmt { kind: hir::StmtKind::Semi(&ref p), .. }) + | hir::Node::Block(hir::Block { expr: Some(&ref p), .. }) + | hir::Node::Expr(&ref p), + ) = self.tcx.hir().find(parent_id) + else { + break; + }; + parent = p; + parent_id = self.tcx.hir().parent_id(parent_id); + let hir::ExprKind::Break(destination, _) = parent.kind else { + continue; + }; + let mut parent_id = parent_id; + let mut direct = false; + 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(_)) => { + parent_id = self.tcx.hir().parent_id(parent_id); + parent + } + _ => break, + }; + if let hir::ExprKind::Loop(..) = parent.kind { + // When you have `'a: loop { break; }`, the `break` corresponds to the labeled + // loop, so we need to account for that. + direct = !direct; + } + if let hir::ExprKind::Loop(block, label, _, span) = parent.kind + && (destination.label == label || direct) + { + 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 'outer; + } else { + // Locate all other `break` statements within the same `loop` that might + // have affected inference. + struct FindBreaks<'tcx> { + label: Option<rustc_ast::Label>, + uses: Vec<&'tcx hir::Expr<'tcx>>, + nest_depth: usize, + } + impl<'tcx> Visitor<'tcx> for FindBreaks<'tcx> { + fn visit_expr(&mut self, ex: &'tcx hir::Expr<'tcx>) { + let nest_depth = self.nest_depth; + if let hir::ExprKind::Loop(_, label, _, _) = ex.kind { + if label == self.label { + // Account for `'a: loop { 'a: loop {...} }`. + return; + } + self.nest_depth += 1; + } + if let hir::ExprKind::Break(destination, _) = ex.kind + && (self.label == destination.label + // Account for `loop { 'a: loop { loop { break; } } }`. + || destination.label.is_none() && self.nest_depth == 0) + { + self.uses.push(ex); + } + hir::intravisit::walk_expr(self, ex); + self.nest_depth = nest_depth; + } + } + let mut expr_finder = FindBreaks { label, uses: vec![], nest_depth: 0 }; + expr_finder.visit_block(block); + let mut exit = false; + for ex in expr_finder.uses { + let hir::ExprKind::Break(_, val) = ex.kind else { + continue; + }; + let ty = match val { + Some(val) => { + match self.typeck_results.borrow().expr_ty_adjusted_opt(val) { + None => continue, + Some(ty) => ty, + } + } + None => self.tcx.types.unit, + }; + if self.can_eq(self.param_env, ty, expected) { + err.span_label( + ex.span, + "expected because of this `break`", + ); + exit = true; + } + } + if exit { + break 'outer; + } + } + } + } + } + } + fn annotate_expected_due_to_let_ty( &self, err: &mut Diagnostic, diff --git a/compiler/rustc_hir_typeck/src/errors.rs b/compiler/rustc_hir_typeck/src/errors.rs index 054d23c71..1526988fb 100644 --- a/compiler/rustc_hir_typeck/src/errors.rs +++ b/compiler/rustc_hir_typeck/src/errors.rs @@ -6,7 +6,7 @@ use rustc_errors::{ AddToDiagnostic, Applicability, Diagnostic, DiagnosticArgValue, IntoDiagnosticArg, MultiSpan, SubdiagnosticMessage, }; -use rustc_macros::{Diagnostic, Subdiagnostic}; +use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; use rustc_middle::ty::Ty; use rustc_span::{ edition::{Edition, LATEST_STABLE_EDITION}, @@ -55,6 +55,13 @@ impl IntoDiagnosticArg for ReturnLikeStatementKind { } #[derive(Diagnostic)] +#[diag(hir_typeck_rustcall_incorrect_args)] +pub struct RustCallIncorrectArgs { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] #[diag(hir_typeck_yield_expr_outside_of_generator, code = "E0627")] pub struct YieldExprOutsideOfGenerator { #[primary_span] @@ -77,6 +84,14 @@ pub struct MethodCallOnUnknownRawPointee { } #[derive(Diagnostic)] +#[diag(hir_typeck_missing_fn_lang_items)] +#[help] +pub struct MissingFnLangItems { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] #[diag(hir_typeck_functional_record_update_on_non_struct, code = "E0436")] pub struct FunctionalRecordUpdateOnNonStruct { #[primary_span] @@ -130,6 +145,29 @@ pub enum ExpectedReturnTypeLabel<'tcx> { } #[derive(Diagnostic)] +#[diag(hir_typeck_explicit_destructor, code = "E0040")] +pub struct ExplicitDestructorCall { + #[primary_span] + #[label] + pub span: Span, + #[subdiagnostic] + pub sugg: ExplicitDestructorCallSugg, +} + +#[derive(Subdiagnostic)] +pub enum ExplicitDestructorCallSugg { + #[suggestion(hir_typeck_suggestion, code = "drop", applicability = "maybe-incorrect")] + Empty(#[primary_span] Span), + #[multipart_suggestion(hir_typeck_suggestion, style = "short")] + Snippet { + #[suggestion_part(code = "drop(")] + lo: Span, + #[suggestion_part(code = ")")] + hi: Span, + }, +} + +#[derive(Diagnostic)] #[diag(hir_typeck_missing_parentheses_in_range, code = "E0689")] pub struct MissingParenthesesInRange { #[primary_span] @@ -198,37 +236,67 @@ impl AddToDiagnostic for TypeMismatchFruTypo { } } -#[derive(Diagnostic)] -#[diag(hir_typeck_lang_start_incorrect_number_params)] -#[note(hir_typeck_lang_start_incorrect_number_params_note_expected_count)] -#[note(hir_typeck_lang_start_expected_sig_note)] -pub struct LangStartIncorrectNumberArgs { - #[primary_span] - pub params_span: Span, - pub found_param_count: usize, +#[derive(LintDiagnostic)] +#[diag(hir_typeck_lossy_provenance_int2ptr)] +#[help] +pub struct LossyProvenanceInt2Ptr<'tcx> { + pub expr_ty: Ty<'tcx>, + pub cast_ty: Ty<'tcx>, + #[subdiagnostic] + pub sugg: LossyProvenanceInt2PtrSuggestion, } -#[derive(Diagnostic)] -#[diag(hir_typeck_lang_start_incorrect_param)] -pub struct LangStartIncorrectParam<'tcx> { - #[primary_span] - #[suggestion(style = "short", code = "{expected_ty}", applicability = "machine-applicable")] - pub param_span: Span, - - pub param_num: usize, - pub expected_ty: Ty<'tcx>, - pub found_ty: Ty<'tcx>, +#[derive(Subdiagnostic)] +#[multipart_suggestion(hir_typeck_suggestion, applicability = "has-placeholders")] +pub struct LossyProvenanceInt2PtrSuggestion { + #[suggestion_part(code = "(...).with_addr(")] + pub lo: Span, + #[suggestion_part(code = ")")] + pub hi: Span, } -#[derive(Diagnostic)] -#[diag(hir_typeck_lang_start_incorrect_ret_ty)] -pub struct LangStartIncorrectRetTy<'tcx> { - #[primary_span] - #[suggestion(style = "short", code = "{expected_ty}", applicability = "machine-applicable")] - pub ret_span: Span, +#[derive(LintDiagnostic)] +#[diag(hir_typeck_lossy_provenance_ptr2int)] +#[help] +pub struct LossyProvenancePtr2Int<'tcx> { + pub expr_ty: Ty<'tcx>, + pub cast_ty: Ty<'tcx>, + #[subdiagnostic] + pub sugg: LossyProvenancePtr2IntSuggestion<'tcx>, +} - pub expected_ty: Ty<'tcx>, - pub found_ty: Ty<'tcx>, +#[derive(Subdiagnostic)] +pub enum LossyProvenancePtr2IntSuggestion<'tcx> { + #[multipart_suggestion(hir_typeck_suggestion, applicability = "maybe-incorrect")] + NeedsParensCast { + #[suggestion_part(code = "(")] + expr_span: Span, + #[suggestion_part(code = ").addr() as {cast_ty}")] + cast_span: Span, + cast_ty: Ty<'tcx>, + }, + #[multipart_suggestion(hir_typeck_suggestion, applicability = "maybe-incorrect")] + NeedsParens { + #[suggestion_part(code = "(")] + expr_span: Span, + #[suggestion_part(code = ").addr()")] + cast_span: Span, + }, + #[suggestion( + hir_typeck_suggestion, + code = ".addr() as {cast_ty}", + applicability = "maybe-incorrect" + )] + NeedsCast { + #[primary_span] + cast_span: Span, + cast_ty: Ty<'tcx>, + }, + #[suggestion(hir_typeck_suggestion, code = ".addr()", applicability = "maybe-incorrect")] + Other { + #[primary_span] + cast_span: Span, + }, } #[derive(Subdiagnostic)] @@ -252,6 +320,28 @@ impl HelpUseLatestEdition { } } +#[derive(Diagnostic)] +#[diag(hir_typeck_invalid_callee, code = "E0618")] +pub struct InvalidCallee { + #[primary_span] + pub span: Span, + pub ty: String, +} + +#[derive(Diagnostic)] +#[diag(hir_typeck_int_to_fat, code = "E0606")] +pub struct IntToWide<'tcx> { + #[primary_span] + #[label(hir_typeck_int_to_fat_label)] + pub span: Span, + pub metadata: &'tcx str, + pub expr_ty: String, + pub cast_ty: Ty<'tcx>, + #[label(hir_typeck_int_to_fat_label_nightly)] + pub expr_if_nightly: Option<Span>, + pub known_wide: bool, +} + #[derive(Subdiagnostic)] pub enum OptionResultRefMismatch { #[suggestion( @@ -292,6 +382,32 @@ pub enum OptionResultRefMismatch { // }, } +pub struct RemoveSemiForCoerce { + pub expr: Span, + pub ret: Span, + pub semi: Span, +} + +impl AddToDiagnostic for RemoveSemiForCoerce { + fn add_to_diagnostic_with<F>(self, diag: &mut Diagnostic, _: F) + where + F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage, + { + let mut multispan: MultiSpan = self.semi.into(); + multispan.push_span_label(self.expr, fluent::hir_typeck_remove_semi_for_coerce_expr); + multispan.push_span_label(self.ret, fluent::hir_typeck_remove_semi_for_coerce_ret); + multispan.push_span_label(self.semi, fluent::hir_typeck_remove_semi_for_coerce_semi); + diag.span_note(multispan, fluent::hir_typeck_remove_semi_for_coerce); + + diag.tool_only_span_suggestion( + self.semi, + fluent::hir_typeck_remove_semi_for_coerce_suggestion, + "", + Applicability::MaybeIncorrect, + ); + } +} + #[derive(Diagnostic)] #[diag(hir_typeck_const_select_must_be_const)] #[help] @@ -324,6 +440,20 @@ pub struct UnionPatDotDot { pub span: Span, } +#[derive(Subdiagnostic)] +#[multipart_suggestion( + hir_typeck_use_is_empty, + applicability = "maybe-incorrect", + style = "verbose" +)] +pub struct UseIsEmpty { + #[suggestion_part(code = "!")] + pub lo: Span, + #[suggestion_part(code = ".is_empty()")] + pub hi: Span, + pub expr_ty: String, +} + #[derive(Diagnostic)] #[diag(hir_typeck_arg_mismatch_indeterminate)] pub struct ArgMismatchIndeterminate { @@ -370,6 +500,15 @@ pub struct SuggestPtrNullMut { pub span: Span, } +#[derive(LintDiagnostic)] +#[diag(hir_typeck_trivial_cast)] +#[help] +pub struct TrivialCast<'tcx> { + pub numeric: bool, + pub expr_ty: Ty<'tcx>, + pub cast_ty: Ty<'tcx>, +} + #[derive(Diagnostic)] #[diag(hir_typeck_no_associated_item, code = "E0599")] pub struct NoAssociatedItem { @@ -393,6 +532,74 @@ pub struct CandidateTraitNote { } #[derive(Diagnostic)] +#[diag(hir_typeck_cannot_cast_to_bool, code = "E0054")] +pub struct CannotCastToBool<'tcx> { + #[primary_span] + pub span: Span, + pub expr_ty: Ty<'tcx>, + #[subdiagnostic] + pub help: CannotCastToBoolHelp, +} + +#[derive(LintDiagnostic)] +#[diag(hir_typeck_cast_enum_drop)] +pub struct CastEnumDrop<'tcx> { + pub expr_ty: Ty<'tcx>, + pub cast_ty: Ty<'tcx>, +} + +#[derive(Diagnostic)] +#[diag(hir_typeck_cast_unknown_pointer, code = "E0641")] +pub struct CastUnknownPointer { + #[primary_span] + pub span: Span, + pub to: bool, + #[subdiagnostic] + pub sub: CastUnknownPointerSub, +} + +pub enum CastUnknownPointerSub { + To(Span), + From(Span), +} + +impl rustc_errors::AddToDiagnostic for CastUnknownPointerSub { + fn add_to_diagnostic_with<F>(self, diag: &mut rustc_errors::Diagnostic, f: F) + where + F: Fn( + &mut Diagnostic, + rustc_errors::SubdiagnosticMessage, + ) -> rustc_errors::SubdiagnosticMessage, + { + match self { + CastUnknownPointerSub::To(span) => { + let msg = f(diag, crate::fluent_generated::hir_typeck_label_to.into()); + diag.span_label(span, msg); + let msg = f(diag, crate::fluent_generated::hir_typeck_note.into()); + diag.note(msg); + } + CastUnknownPointerSub::From(span) => { + let msg = f(diag, crate::fluent_generated::hir_typeck_label_from.into()); + diag.span_label(span, msg); + } + } + } +} + +#[derive(Subdiagnostic)] +pub enum CannotCastToBoolHelp { + #[suggestion( + hir_typeck_suggestion, + applicability = "machine-applicable", + code = " != 0", + style = "verbose" + )] + Numeric(#[primary_span] Span), + #[label(hir_typeck_label)] + Unsupported(#[primary_span] Span), +} + +#[derive(Diagnostic)] #[diag(hir_typeck_ctor_is_private, code = "E0603")] pub struct CtorIsPrivate { #[primary_span] @@ -401,6 +608,14 @@ pub struct CtorIsPrivate { } #[derive(Subdiagnostic)] +#[note(hir_typeck_deref_is_empty)] +pub struct DerefImplsIsEmpty { + #[primary_span] + pub span: Span, + pub deref_ty: String, +} + +#[derive(Subdiagnostic)] #[multipart_suggestion( hir_typeck_convert_using_method, applicability = "machine-applicable", diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 7cea40fdd..eead4da5e 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -43,7 +43,10 @@ use rustc_infer::traits::query::NoSolution; use rustc_infer::traits::ObligationCause; use rustc_middle::middle::stability; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AllowTwoPhase}; -use rustc_middle::ty::error::TypeError::FieldMisMatch; +use rustc_middle::ty::error::{ + ExpectedFound, + TypeError::{FieldMisMatch, Sorts}, +}; use rustc_middle::ty::GenericArgsRef; use rustc_middle::ty::{self, AdtKind, Ty, TypeVisitableExt}; use rustc_session::errors::ExprParenthesesNeeded; @@ -525,8 +528,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { _ => self.instantiate_value_path(segs, opt_ty, res, expr.span, expr.hir_id).0, }; - if let ty::FnDef(did, ..) = *ty.kind() { + if let ty::FnDef(did, callee_args) = *ty.kind() { let fn_sig = ty.fn_sig(tcx); + + // HACK: whenever we get a FnDef in a non-const context, enforce effects to get the + // default `host = true` to avoid inference errors later. + if tcx.hir().body_const_context(self.body_id).is_none() { + self.enforce_context_effects(expr.hir_id, qpath.span(), did, callee_args); + } if tcx.fn_sig(did).skip_binder().abi() == RustIntrinsic && tcx.item_name(did) == sym::transmute { @@ -658,15 +667,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.suggest_mismatched_types_on_tail( &mut err, expr, ty, e_ty, target_id, ); + let error = Some(Sorts(ExpectedFound { expected: ty, found: e_ty })); + self.annotate_loop_expected_due_to_inference(&mut err, expr, error); if let Some(val) = ty_kind_suggestion(ty) { - let label = destination - .label - .map(|l| format!(" {}", l.ident)) - .unwrap_or_else(String::new); - err.span_suggestion( - expr.span, + err.span_suggestion_verbose( + expr.span.shrink_to_hi(), "give it a value of the expected type", - format!("break{label} {val}"), + format!(" {val}"), Applicability::HasPlaceholders, ); } @@ -711,7 +718,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // ... except when we try to 'break rust;'. // ICE this expression in particular (see #43162). if let ExprKind::Path(QPath::Resolved(_, path)) = e.kind { - if path.segments.len() == 1 && path.segments[0].ident.name == sym::rust { + if let [segment] = path.segments && segment.ident.name == sym::rust { fatally_break_rust(self.tcx); } } @@ -1203,7 +1210,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // otherwise check exactly as a let statement self.check_decl(let_expr.into()); // but return a bool, for this is a boolean expression - self.tcx.types.bool + if let Some(error_guaranteed) = let_expr.is_recovered { + self.set_tainted_by_errors(error_guaranteed); + Ty::new_error(self.tcx, error_guaranteed) + } else { + self.tcx.types.bool + } } fn check_expr_loop( @@ -2310,13 +2322,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let guar = if field.name == kw::Empty { self.tcx.sess.delay_span_bug(field.span, "field name with no name") - } else if self.method_exists( - field, - base_ty, - expr.hir_id, - true, - expected.only_has_type(self), - ) { + } else if self.method_exists(field, base_ty, expr.hir_id, expected.only_has_type(self)) { self.ban_take_value_of_method(expr, base_ty, field) } else if !base_ty.is_primitive_ty() { self.ban_nonexisting_field(field, base, expr, base_ty) @@ -2501,7 +2507,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut err = self.private_field_err(field, base_did); // Also check if an accessible method exists, which is often what is meant. - if self.method_exists(field, expr_t, expr.hir_id, false, return_ty) + if self.method_exists(field, expr_t, expr.hir_id, return_ty) && !self.expr_in_place(expr.hir_id) { self.suggest_method_call( diff --git a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs index 840910732..8bc66ac55 100644 --- a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs +++ b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs @@ -664,10 +664,12 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { ); self.walk_pat(discr_place, arm.pat, arm.guard.is_some()); - if let Some(hir::Guard::If(e)) = arm.guard { - self.consume_expr(e) - } else if let Some(hir::Guard::IfLet(ref l)) = arm.guard { - self.consume_expr(l.init) + match arm.guard { + Some(hir::Guard::If(ref e)) => self.consume_expr(e), + Some(hir::Guard::IfLet(ref l)) => { + self.walk_local(l.init, l.pat, None, |t| t.borrow_expr(l.init, ty::ImmBorrow)) + } + None => {} } self.consume_expr(arm.body); diff --git a/compiler/rustc_hir_typeck/src/fallback.rs b/compiler/rustc_hir_typeck/src/fallback.rs index 5b5986a34..952b90d6a 100644 --- a/compiler/rustc_hir_typeck/src/fallback.rs +++ b/compiler/rustc_hir_typeck/src/fallback.rs @@ -4,6 +4,7 @@ use rustc_data_structures::{ graph::{iterate::DepthFirstSearch, vec_graph::VecGraph}, unord::{UnordBag, UnordMap, UnordSet}, }; +use rustc_infer::infer::{DefineOpaqueTypes, InferOk}; use rustc_middle::ty::{self, Ty}; impl<'tcx> FnCtxt<'_, 'tcx> { @@ -23,20 +24,10 @@ impl<'tcx> FnCtxt<'_, 'tcx> { self.fulfillment_cx.borrow_mut().pending_obligations() ); - // Check if we have any unsolved variables. If not, no need for fallback. - let unsolved_variables = self.unsolved_variables(); - if unsolved_variables.is_empty() { - return; - } + let fallback_occured = self.fallback_types() | self.fallback_effects(); - let diverging_fallback = self.calculate_diverging_fallback(&unsolved_variables); - - // We do fallback in two passes, to try to generate - // better error messages. - // The first time, we do *not* replace opaque types. - for ty in unsolved_variables { - debug!("unsolved_variable = {:?}", ty); - self.fallback_if_possible(ty, &diverging_fallback); + if !fallback_occured { + return; } // We now see if we can make progress. This might cause us to @@ -65,6 +56,53 @@ impl<'tcx> FnCtxt<'_, 'tcx> { self.select_obligations_where_possible(|_| {}); } + fn fallback_types(&self) -> bool { + // Check if we have any unsolved variables. If not, no need for fallback. + let unsolved_variables = self.unsolved_variables(); + + if unsolved_variables.is_empty() { + return false; + } + + let diverging_fallback = self.calculate_diverging_fallback(&unsolved_variables); + + // We do fallback in two passes, to try to generate + // better error messages. + // The first time, we do *not* replace opaque types. + for ty in unsolved_variables { + debug!("unsolved_variable = {:?}", ty); + self.fallback_if_possible(ty, &diverging_fallback); + } + + true + } + + fn fallback_effects(&self) -> bool { + let unsolved_effects = self.unsolved_effects(); + + if unsolved_effects.is_empty() { + return false; + } + + // not setting `fallback_has_occured` here because that field is only used for type fallback + // diagnostics. + + for effect in unsolved_effects { + let expected = self.tcx.consts.true_; + let cause = self.misc(rustc_span::DUMMY_SP); + match self.at(&cause, self.param_env).eq(DefineOpaqueTypes::Yes, expected, effect) { + Ok(InferOk { obligations, value: () }) => { + self.register_predicates(obligations); + } + Err(e) => { + bug!("cannot eq unsolved effect: {e:?}") + } + } + } + + true + } + // Tries to apply a fallback to `ty` if it is an unsolved variable. // // - Unconstrained ints are replaced with `i32`. diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index 28fe2e062..415920221 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -509,21 +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 (_, body_id, interior, kind) in generators.drain(..) { - crate::generator_interior::resolve_interior(self, 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. /// @@ -533,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. @@ -550,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; @@ -1295,17 +1280,25 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { (GenericParamDefKind::Type { .. }, GenericArg::Infer(inf)) => { self.fcx.ty_infer(Some(param), inf.span).into() } - (GenericParamDefKind::Const { .. }, GenericArg::Infer(inf)) => { + ( + &GenericParamDefKind::Const { has_default, is_host_effect }, + GenericArg::Infer(inf), + ) => { let tcx = self.fcx.tcx(); - self.fcx - .ct_infer( - tcx.type_of(param.def_id) - .no_bound_vars() - .expect("const parameter types cannot be generic"), - Some(param), - inf.span, - ) - .into() + + if has_default && is_host_effect { + self.fcx.var_for_effect(param) + } else { + self.fcx + .ct_infer( + tcx.type_of(param.def_id) + .no_bound_vars() + .expect("const parameter types cannot be generic"), + Some(param), + inf.span, + ) + .into() + } } _ => unreachable!(), } @@ -1324,7 +1317,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } GenericParamDefKind::Type { has_default, .. } => { if !infer_args && has_default { - // If we have a default, then we it doesn't matter that we're not + // If we have a default, then it doesn't matter that we're not // inferring the type arguments: we provide the default where any // is missing. tcx.type_of(param.def_id).instantiate(tcx, args.unwrap()).into() @@ -1336,17 +1329,28 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.fcx.var_for_def(self.span, param) } } - GenericParamDefKind::Const { has_default } => { - if !infer_args - && has_default - && !tcx.has_attr(param.def_id, sym::rustc_host) - { - tcx.const_param_default(param.def_id) - .instantiate(tcx, args.unwrap()) - .into() - } else { - self.fcx.var_for_def(self.span, param) + GenericParamDefKind::Const { has_default, is_host_effect } => { + if has_default { + // N.B. this is a bit of a hack. `infer_args` is passed depending on + // whether the user has provided generic args. E.g. for `Vec::new` + // we would have to infer the generic types. However, for `Vec::<T>::new` + // where the allocator param `A` has a default we will *not* infer. But + // for effect params this is a different story: if the user has not written + // anything explicit for the effect param, we always need to try to infer + // it before falling back to default, such that a `const fn` such as + // `needs_drop::<()>` can still be called in const contexts. (if we defaulted + // instead of inferred, typeck would error) + if is_host_effect { + return self.fcx.var_for_effect(param); + } else if !infer_args { + return tcx + .const_param_default(param.def_id) + .instantiate(tcx, args.unwrap()) + .into(); + } } + + self.fcx.var_for_def(self.span, param) } } } diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs index c44d12e61..43d4496dd 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs @@ -1,6 +1,6 @@ use crate::FnCtxt; use rustc_hir as hir; -use rustc_hir::def::Res; +use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_infer::{infer::type_variable::TypeVariableOriginKind, traits::ObligationCauseCode}; use rustc_middle::ty::{self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor}; @@ -20,10 +20,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { else { return false; }; - let hir = self.tcx.hir(); - let hir::Node::Expr(expr) = hir.get(hir_id) else { - return false; - }; let Some(unsubstituted_pred) = self .tcx @@ -37,15 +33,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; let generics = self.tcx.generics_of(def_id); - let predicate_args = match unsubstituted_pred.kind().skip_binder() { - ty::ClauseKind::Trait(pred) => pred.trait_ref.args.to_vec(), - ty::ClauseKind::Projection(pred) => pred.projection_ty.args.to_vec(), - ty::ClauseKind::ConstArgHasType(arg, ty) => { - vec![ty.into(), arg.into()] - } - ty::ClauseKind::ConstEvaluatable(e) => vec![e.into()], - _ => return false, - }; + let (predicate_args, predicate_self_type_to_point_at) = + match unsubstituted_pred.kind().skip_binder() { + ty::ClauseKind::Trait(pred) => { + (pred.trait_ref.args.to_vec(), Some(pred.self_ty().into())) + } + ty::ClauseKind::Projection(pred) => (pred.projection_ty.args.to_vec(), None), + ty::ClauseKind::ConstArgHasType(arg, ty) => (vec![ty.into(), arg.into()], None), + ty::ClauseKind::ConstEvaluatable(e) => (vec![e.into()], None), + _ => return false, + }; let find_param_matching = |matches: &dyn Fn(ty::ParamTerm) -> bool| { predicate_args.iter().find_map(|arg| { @@ -96,55 +93,92 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.find_ambiguous_parameter_in(def_id, error.root_obligation.predicate); } - if self.closure_span_overlaps_error(error, expr.span) { - return false; - } + let hir = self.tcx.hir(); + let (expr, qpath) = match hir.get(hir_id) { + hir::Node::Expr(expr) => { + if self.closure_span_overlaps_error(error, expr.span) { + return false; + } + let qpath = + if let hir::ExprKind::Path(qpath) = expr.kind { Some(qpath) } else { None }; - match &expr.kind { - hir::ExprKind::Path(qpath) => { - if let hir::Node::Expr(hir::Expr { - kind: hir::ExprKind::Call(callee, args), - hir_id: call_hir_id, - span: call_span, - .. - }) = hir.get_parent(expr.hir_id) - && callee.hir_id == expr.hir_id - { - if self.closure_span_overlaps_error(error, *call_span) { - return false; - } + (Some(&expr.kind), qpath) + } + hir::Node::Ty(hir::Ty { kind: hir::TyKind::Path(qpath), .. }) => (None, Some(*qpath)), + _ => return false, + }; - for param in - [param_to_point_at, fallback_param_to_point_at, self_param_to_point_at] - .into_iter() - .flatten() + if let Some(qpath) = qpath { + // Prefer pointing at the turbofished arg that corresponds to the + // self type of the failing predicate over anything else. + if let Some(param) = predicate_self_type_to_point_at + && self.point_at_path_if_possible(error, def_id, param, &qpath) + { + return true; + } + + if let hir::Node::Expr(hir::Expr { + kind: hir::ExprKind::Call(callee, args), + hir_id: call_hir_id, + span: call_span, + .. + }) = hir.get_parent(hir_id) + && callee.hir_id == hir_id + { + if self.closure_span_overlaps_error(error, *call_span) { + return false; + } + + for param in + [param_to_point_at, fallback_param_to_point_at, self_param_to_point_at] + .into_iter() + .flatten() + { + if self.blame_specific_arg_if_possible( + error, + def_id, + param, + *call_hir_id, + callee.span, + None, + args, + ) { - if self.blame_specific_arg_if_possible( - error, - def_id, - param, - *call_hir_id, - callee.span, - None, - args, - ) - { - return true; - } + return true; } } - // Notably, we only point to params that are local to the - // item we're checking, since those are the ones we are able - // to look in the final `hir::PathSegment` for. Everything else - // would require a deeper search into the `qpath` than I think - // is worthwhile. - if let Some(param_to_point_at) = param_to_point_at - && self.point_at_path_if_possible(error, def_id, param_to_point_at, qpath) - { + } + + for param in [param_to_point_at, fallback_param_to_point_at, self_param_to_point_at] + .into_iter() + .flatten() + { + if self.point_at_path_if_possible(error, def_id, param, &qpath) { return true; } } - hir::ExprKind::MethodCall(segment, receiver, args, ..) => { + } + + match expr { + Some(hir::ExprKind::MethodCall(segment, receiver, args, ..)) => { + if let Some(param) = predicate_self_type_to_point_at + && self.point_at_generic_if_possible(error, def_id, param, segment) + { + // HACK: This is not correct, since `predicate_self_type_to_point_at` might + // not actually correspond to the receiver of the method call. But we + // re-adjust the cause code here in order to prefer pointing at one of + // the method's turbofish segments but still use `FunctionArgumentObligation` + // elsewhere. Hopefully this doesn't break something. + error.obligation.cause.map_code(|parent_code| { + ObligationCauseCode::FunctionArgumentObligation { + arg_hir_id: receiver.hir_id, + call_hir_id: hir_id, + parent_code, + } + }); + return true; + } + for param in [param_to_point_at, fallback_param_to_point_at, self_param_to_point_at] .into_iter() .flatten() @@ -166,12 +200,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { { return true; } + // Handle `Self` param specifically, since it's separated in + // the method call representation + if self_param_to_point_at.is_some() { + error.obligation.cause.span = receiver + .span + .find_ancestor_in_same_ctxt(error.obligation.cause.span) + .unwrap_or(receiver.span); + return true; + } } - hir::ExprKind::Struct(qpath, fields, ..) => { - if let Res::Def( - hir::def::DefKind::Struct | hir::def::DefKind::Variant, - variant_def_id, - ) = self.typeck_results.borrow().qpath_res(qpath, hir_id) + Some(hir::ExprKind::Struct(qpath, fields, ..)) => { + if let Res::Def(DefKind::Struct | DefKind::Variant, variant_def_id) = + self.typeck_results.borrow().qpath_res(qpath, hir_id) { for param in [param_to_point_at, fallback_param_to_point_at, self_param_to_point_at] @@ -193,10 +234,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } } - if let Some(param_to_point_at) = param_to_point_at - && self.point_at_path_if_possible(error, def_id, param_to_point_at, qpath) + + for param in [ + predicate_self_type_to_point_at, + param_to_point_at, + fallback_param_to_point_at, + self_param_to_point_at, + ] + .into_iter() + .flatten() { - return true; + if self.point_at_path_if_possible(error, def_id, param, qpath) { + return true; + } } } _ => {} @@ -213,17 +263,43 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { qpath: &hir::QPath<'tcx>, ) -> bool { match qpath { - hir::QPath::Resolved(_, path) => { - if let Some(segment) = path.segments.last() - && self.point_at_generic_if_possible(error, def_id, param, segment) + hir::QPath::Resolved(self_ty, path) => { + for segment in path.segments.iter().rev() { + if let Res::Def(kind, def_id) = segment.res + && !matches!(kind, DefKind::Mod | DefKind::ForeignMod) + && self.point_at_generic_if_possible(error, def_id, param, segment) + { + return true; + } + } + // Handle `Self` param specifically, since it's separated in + // the path representation + if let Some(self_ty) = self_ty + && let ty::GenericArgKind::Type(ty) = param.unpack() + && ty == self.tcx.types.self_param { + error.obligation.cause.span = self_ty + .span + .find_ancestor_in_same_ctxt(error.obligation.cause.span) + .unwrap_or(self_ty.span); return true; } } - hir::QPath::TypeRelative(_, segment) => { + hir::QPath::TypeRelative(self_ty, segment) => { if self.point_at_generic_if_possible(error, def_id, param, segment) { return true; } + // Handle `Self` param specifically, since it's separated in + // the path representation + if let ty::GenericArgKind::Type(ty) = param.unpack() + && ty == self.tcx.types.self_param + { + error.obligation.cause.span = self_ty + .span + .find_ancestor_in_same_ctxt(error.obligation.cause.span) + .unwrap_or(self_ty.span); + return true; + } } _ => {} } @@ -398,7 +474,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } /** - * Recursively searches for the most-specific blamable expression. + * Recursively searches for the most-specific blameable expression. * For example, if you have a chain of constraints like: * - want `Vec<i32>: Copy` * - because `Option<Vec<i32>>: Copy` needs `Vec<i32>: Copy` because `impl <T: Copy> Copy for Option<T>` @@ -618,14 +694,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; let variant_def_id = match expr_struct_def_kind { - hir::def::DefKind::Struct => { + DefKind::Struct => { if in_ty_adt.did() != expr_struct_def_id { // FIXME: Deal with type aliases? return Err(expr); } expr_struct_def_id } - hir::def::DefKind::Variant => { + DefKind::Variant => { // If this is a variant, its parent is the type definition. if in_ty_adt.did() != self.tcx.parent(expr_struct_def_id) { // FIXME: Deal with type aliases? @@ -727,14 +803,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; let variant_def_id = match expr_struct_def_kind { - hir::def::DefKind::Ctor(hir::def::CtorOf::Struct, hir::def::CtorKind::Fn) => { + DefKind::Ctor(hir::def::CtorOf::Struct, hir::def::CtorKind::Fn) => { if in_ty_adt.did() != self.tcx.parent(expr_ctor_def_id) { // FIXME: Deal with type aliases? return Err(expr); } self.tcx.parent(expr_ctor_def_id) } - hir::def::DefKind::Ctor(hir::def::CtorOf::Variant, hir::def::CtorKind::Fn) => { + DefKind::Ctor(hir::def::CtorOf::Variant, hir::def::CtorKind::Fn) => { // For a typical enum like // `enum Blah<T> { Variant(T) }` // we get the following resolutions: diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index 4def78673..c0332a48b 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -273,11 +273,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // // This check is here because there is currently no way to express a trait bound for `FnDef` types only. if is_const_eval_select && (1..=2).contains(&idx) { - if let ty::FnDef(def_id, _) = checked_ty.kind() { - if idx == 1 && !self.tcx.is_const_fn_raw(*def_id) { - self.tcx - .sess - .emit_err(errors::ConstSelectMustBeConst { span: provided_arg.span }); + if let ty::FnDef(def_id, args) = *checked_ty.kind() { + if idx == 1 { + if !self.tcx.is_const_fn_raw(def_id) { + self.tcx.sess.emit_err(errors::ConstSelectMustBeConst { + span: provided_arg.span, + }); + } else { + self.enforce_context_effects( + provided_arg.hir_id, + provided_arg.span, + def_id, + args, + ) + } } } else { self.tcx.sess.emit_err(errors::ConstSelectMustBeFn { @@ -1361,10 +1370,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } _ => bug!("unexpected type: {:?}", ty.normalized), }, - Res::Def( - DefKind::Struct | DefKind::Union | DefKind::TyAlias { .. } | DefKind::AssocTy, - _, - ) + Res::Def(DefKind::Struct | DefKind::Union | DefKind::TyAlias | DefKind::AssocTy, _) | Res::SelfTyParam { .. } | Res::SelfTyAlias { .. } => match ty.normalized.ty_adt_def() { Some(adt) if !adt.is_enum() => { diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs index 6a82b0021..4a245d30c 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs @@ -266,7 +266,14 @@ impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> { param: Option<&ty::GenericParamDef>, span: Span, ) -> Const<'tcx> { + // FIXME ideally this shouldn't use unwrap match param { + Some( + param @ ty::GenericParamDef { + kind: ty::GenericParamDefKind::Const { is_host_effect: true, .. }, + .. + }, + ) => self.var_for_effect(param).as_const().unwrap(), Some(param) => self.var_for_def(span, param).as_const().unwrap(), None => self.next_const_var( ty, @@ -317,7 +324,21 @@ impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> { fn record_ty(&self, hir_id: hir::HirId, ty: Ty<'tcx>, span: Span) { // FIXME: normalization and escaping regions - let ty = if !ty.has_escaping_bound_vars() { self.normalize(span, ty) } else { ty }; + let ty = if !ty.has_escaping_bound_vars() { + // NOTE: These obligations are 100% redundant and are implied by + // WF obligations that are registered elsewhere, but they have a + // better cause code assigned to them in `add_required_obligations_for_hir`. + // This means that they should shadow obligations with worse spans. + if let ty::Alias(ty::Projection | ty::Weak, ty::AliasTy { args, def_id, .. }) = + ty.kind() + { + self.add_required_obligations_for_hir(span, *def_id, args, hir_id); + } + + self.normalize(span, ty) + } else { + ty + }; self.write_ty(hir_id, ty) } diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index d2a53ee8b..abb689892 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -65,6 +65,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let expr = expr.peel_drop_temps(); self.suggest_missing_semicolon(err, expr, expected, false); let mut pointing_at_return_type = false; + if let hir::ExprKind::Break(..) = expr.kind { + // `break` type mismatches provide better context for tail `loop` expressions. + return false; + } if let Some((fn_id, fn_decl, can_suggest)) = self.get_fn_decl(blk_id) { pointing_at_return_type = self.suggest_missing_return_type( err, @@ -987,10 +991,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let bound_vars = self.tcx.late_bound_vars(fn_id); let ty = self.tcx.erase_late_bound_regions(Binder::bind_with_vars(ty, bound_vars)); let ty = match self.tcx.asyncness(fn_id.owner) { - hir::IsAsync::Async => self.get_impl_future_output_ty(ty).unwrap_or_else(|| { + ty::Asyncness::Yes => self.get_impl_future_output_ty(ty).unwrap_or_else(|| { span_bug!(fn_decl.output.span(), "failed to get output type of async function") }), - hir::IsAsync::NotAsync => ty, + ty::Asyncness::No => ty, }; let ty = self.normalize(expr.span, ty); if self.can_coerce(found, ty) { diff --git a/compiler/rustc_hir_typeck/src/gather_locals.rs b/compiler/rustc_hir_typeck/src/gather_locals.rs index ed4c63f17..0ad2c1d92 100644 --- a/compiler/rustc_hir_typeck/src/gather_locals.rs +++ b/compiler/rustc_hir_typeck/src/gather_locals.rs @@ -50,7 +50,7 @@ impl<'a> From<&'a hir::Local<'a>> for Declaration<'a> { impl<'a> From<&'a hir::Let<'a>> for Declaration<'a> { fn from(let_expr: &'a hir::Let<'a>) -> Self { - let hir::Let { hir_id, pat, ty, span, init } = *let_expr; + let hir::Let { hir_id, pat, ty, span, init, is_recovered: _ } = *let_expr; Declaration { hir_id, pat, ty, span, init: Some(init), origin: DeclOrigin::LetExpr } } } diff --git a/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/cfg_build.rs b/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/cfg_build.rs deleted file mode 100644 index cfedcee99..000000000 --- a/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/cfg_build.rs +++ /dev/null @@ -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) - }); - } -} diff --git a/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/cfg_propagate.rs b/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/cfg_propagate.rs deleted file mode 100644 index 633b47889..000000000 --- a/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/cfg_propagate.rs +++ /dev/null @@ -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 - } -} diff --git a/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/cfg_visualize.rs b/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/cfg_visualize.rs deleted file mode 100644 index e8d31be79..000000000 --- a/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/cfg_visualize.rs +++ /dev/null @@ -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(), - ) - } -} diff --git a/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/mod.rs b/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/mod.rs deleted file mode 100644 index e563bd40b..000000000 --- a/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/mod.rs +++ /dev/null @@ -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), - } - } -} diff --git a/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/record_consumed_borrow.rs b/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/record_consumed_borrow.rs deleted file mode 100644 index 29413f080..000000000 --- a/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/record_consumed_borrow.rs +++ /dev/null @@ -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); - } -} diff --git a/compiler/rustc_hir_typeck/src/generator_interior/mod.rs b/compiler/rustc_hir_typeck/src/generator_interior/mod.rs deleted file mode 100644 index 6a8171224..000000000 --- a/compiler/rustc_hir_typeck/src/generator_interior/mod.rs +++ /dev/null @@ -1,714 +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; -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::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, - 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; - - // 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); - check_must_not_suspend_ty( - fcx, - ty, - hir_id, - SuspendCheckData { - descr_pre, - plural_len: len.try_eval_target_usize(fcx.tcx, fcx.param_env).unwrap_or(0) - as usize - + 1, - ..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 - } -} diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs index c4d3cbc9f..6873382c4 100644 --- a/compiler/rustc_hir_typeck/src/lib.rs +++ b/compiler/rustc_hir_typeck/src/lib.rs @@ -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; @@ -436,6 +435,12 @@ fn fatally_break_rust(tcx: TyCtxt<'_>) { tcx.sess.cfg_version, config::host_triple(), )); + if let Some((flags, excluded_cargo_defaults)) = rustc_session::utils::extra_compiler_flags() { + handler.note_without_error(format!("compiler flags: {}", flags.join(" "))); + if excluded_cargo_defaults { + handler.note_without_error("some of the compiler flags provided by cargo are hidden"); + } + } } fn has_expected_num_generic_args(tcx: TyCtxt<'_>, trait_did: DefId, expected: usize) -> bool { diff --git a/compiler/rustc_hir_typeck/src/mem_categorization.rs b/compiler/rustc_hir_typeck/src/mem_categorization.rs index 9574da021..337d12b2d 100644 --- a/compiler/rustc_hir_typeck/src/mem_categorization.rs +++ b/compiler/rustc_hir_typeck/src/mem_categorization.rs @@ -557,10 +557,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { Ok(adt_def.variant_index_with_ctor_id(variant_ctor_id)) } Res::Def(DefKind::Ctor(CtorOf::Struct, ..), _) - | Res::Def( - DefKind::Struct | DefKind::Union | DefKind::TyAlias { .. } | DefKind::AssocTy, - _, - ) + | Res::Def(DefKind::Struct | DefKind::Union | DefKind::TyAlias | DefKind::AssocTy, _) | Res::SelfCtor(..) | Res::SelfTyParam { .. } | Res::SelfTyAlias { .. } => { diff --git a/compiler/rustc_hir_typeck/src/method/mod.rs b/compiler/rustc_hir_typeck/src/method/mod.rs index 6dd131aa2..86a0e95de 100644 --- a/compiler/rustc_hir_typeck/src/method/mod.rs +++ b/compiler/rustc_hir_typeck/src/method/mod.rs @@ -89,14 +89,13 @@ pub enum CandidateSource { } impl<'a, 'tcx> FnCtxt<'a, 'tcx> { - /// Determines whether the type `self_ty` supports a method name `method_name` or not. + /// Determines whether the type `self_ty` supports a visible method named `method_name` or not. #[instrument(level = "debug", skip(self))] pub fn method_exists( &self, method_name: Ident, self_ty: Ty<'tcx>, call_expr_id: hir::HirId, - allow_private: bool, return_type: Option<Ty<'tcx>>, ) -> bool { match self.probe_for_name( @@ -118,7 +117,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } Err(NoMatch(..)) => false, Err(Ambiguity(..)) => true, - Err(PrivateMatch(..)) => allow_private, + Err(PrivateMatch(..)) => false, Err(IllegalSizedBound { .. }) => true, Err(BadReturnType) => false, } diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index 72a04a02b..07c48ec63 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -2361,8 +2361,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Some(output_ty) => self.resolve_vars_if_possible(output_ty), _ => return, }; - let method_exists = - self.method_exists(item_name, output_ty, call.hir_id, true, return_type); + let method_exists = self.method_exists(item_name, output_ty, call.hir_id, return_type); debug!("suggest_await_before_method: is_method_exist={}", method_exists); if method_exists { err.span_suggestion_verbose( diff --git a/compiler/rustc_hir_typeck/src/upvar.rs b/compiler/rustc_hir_typeck/src/upvar.rs index 1a41786d2..4d6413903 100644 --- a/compiler/rustc_hir_typeck/src/upvar.rs +++ b/compiler/rustc_hir_typeck/src/upvar.rs @@ -41,6 +41,7 @@ use rustc_hir::intravisit::{self, Visitor}; use rustc_infer::infer::UpvarRegion; use rustc_middle::hir::place::{Place, PlaceBase, PlaceWithHirId, Projection, ProjectionKind}; use rustc_middle::mir::FakeReadCause; +use rustc_middle::traits::ObligationCauseCode; use rustc_middle::ty::{ self, ClosureSizeProfileData, Ty, TyCtxt, TypeckResults, UpvarArgs, UpvarCapture, }; @@ -195,7 +196,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { assert_eq!(self.tcx.hir().body_owner_def_id(body.id()), closure_def_id); let mut delegate = InferBorrowKind { - fcx: self, closure_def_id, capture_information: Default::default(), fake_reads: Default::default(), @@ -296,6 +296,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let final_upvar_tys = self.final_upvar_tys(closure_def_id); debug!(?closure_hir_id, ?args, ?final_upvar_tys); + if self.tcx.features().unsized_locals || self.tcx.features().unsized_fn_params { + for capture in + self.typeck_results.borrow().closure_min_captures_flattened(closure_def_id) + { + if let UpvarCapture::ByValue = capture.info.capture_kind { + self.require_type_is_sized( + capture.place.ty(), + capture.get_path_span(self.tcx), + ObligationCauseCode::SizedClosureCapture(closure_def_id), + ); + } + } + } + // Build a tuple (U0..Un) of the final upvar types U0..Un // and unify the upvar tuple type in the closure with it: let final_tupled_upvars_type = Ty::new_tup(self.tcx, &final_upvar_tys); @@ -1607,34 +1621,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Truncate the capture so that the place being borrowed is in accordance with RFC 1240, /// which states that it's unsafe to take a reference into a struct marked `repr(packed)`. fn restrict_repr_packed_field_ref_capture<'tcx>( - tcx: TyCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, mut place: Place<'tcx>, mut curr_borrow_kind: ty::UpvarCapture, ) -> (Place<'tcx>, ty::UpvarCapture) { let pos = place.projections.iter().enumerate().position(|(i, p)| { let ty = place.ty_before_projection(i); - // Return true for fields of packed structs, unless those fields have alignment 1. + // Return true for fields of packed structs. match p.kind { ProjectionKind::Field(..) => match ty.kind() { ty::Adt(def, _) if def.repr().packed() => { - // We erase regions here because they cannot be hashed - match tcx.layout_of(param_env.and(tcx.erase_regions(p.ty))) { - Ok(layout) if layout.align.abi.bytes() == 1 => { - // if the alignment is 1, the type can't be further - // disaligned. - debug!( - "restrict_repr_packed_field_ref_capture: ({:?}) - align = 1", - place - ); - false - } - _ => { - debug!("restrict_repr_packed_field_ref_capture: ({:?}) - true", place); - true - } - } + // We stop here regardless of field alignment. Field alignment can change as + // types change, including the types of private fields in other crates, and that + // shouldn't affect how we compute our captures. + true } _ => false, @@ -1689,9 +1689,7 @@ fn drop_location_span(tcx: TyCtxt<'_>, hir_id: hir::HirId) -> Span { tcx.sess.source_map().end_point(owner_span) } -struct InferBorrowKind<'a, 'tcx> { - fcx: &'a FnCtxt<'a, 'tcx>, - +struct InferBorrowKind<'tcx> { // The def-id of the closure whose kind and upvar accesses are being inferred. closure_def_id: LocalDefId, @@ -1725,7 +1723,7 @@ struct InferBorrowKind<'a, 'tcx> { fake_reads: Vec<(Place<'tcx>, FakeReadCause, hir::HirId)>, } -impl<'a, 'tcx> euv::Delegate<'tcx> for InferBorrowKind<'a, 'tcx> { +impl<'tcx> euv::Delegate<'tcx> for InferBorrowKind<'tcx> { fn fake_read( &mut self, place: &PlaceWithHirId<'tcx>, @@ -1740,12 +1738,7 @@ impl<'a, 'tcx> euv::Delegate<'tcx> for InferBorrowKind<'a, 'tcx> { let (place, _) = restrict_capture_precision(place.place.clone(), dummy_capture_kind); - let (place, _) = restrict_repr_packed_field_ref_capture( - self.fcx.tcx, - self.fcx.param_env, - place, - dummy_capture_kind, - ); + let (place, _) = restrict_repr_packed_field_ref_capture(place, dummy_capture_kind); self.fake_reads.push((place, cause, diag_expr_id)); } @@ -1780,12 +1773,8 @@ impl<'a, 'tcx> euv::Delegate<'tcx> for InferBorrowKind<'a, 'tcx> { // We only want repr packed restriction to be applied to reading references into a packed // struct, and not when the data is being moved. Therefore we call this method here instead // of in `restrict_capture_precision`. - let (place, mut capture_kind) = restrict_repr_packed_field_ref_capture( - self.fcx.tcx, - self.fcx.param_env, - place_with_id.place.clone(), - capture_kind, - ); + let (place, mut capture_kind) = + restrict_repr_packed_field_ref_capture(place_with_id.place.clone(), capture_kind); // Raw pointers don't inherit mutability if place_with_id.place.deref_tys().any(Ty::is_unsafe_ptr) { diff --git a/compiler/rustc_hir_typeck/src/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs index 603681bbc..9c16b486d 100644 --- a/compiler/rustc_hir_typeck/src/writeback.rs +++ b/compiler/rustc_hir_typeck/src/writeback.rs @@ -46,7 +46,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } // Type only exists for constants and statics, not functions. match self.tcx.hir().body_owner_kind(item_def_id) { - hir::BodyOwnerKind::Const | hir::BodyOwnerKind::Static(_) => { + hir::BodyOwnerKind::Const { .. } | hir::BodyOwnerKind::Static(_) => { let item_hir_id = self.tcx.hir().local_def_id_to_hir_id(item_def_id); wbcx.visit_node_id(body.value.span, item_hir_id); } @@ -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() |