summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_hir_typeck
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-30 18:31:36 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-30 18:31:36 +0000
commite02c5b5930c2c9ba3e5423fe12e2ef0155017297 (patch)
treefd60ebbbb5299e16e5fca8c773ddb74f764760db /compiler/rustc_hir_typeck
parentAdding debian version 1.73.0+dfsg1-1. (diff)
downloadrustc-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')
-rw-r--r--compiler/rustc_hir_typeck/messages.ftl66
-rw-r--r--compiler/rustc_hir_typeck/src/_match.rs46
-rw-r--r--compiler/rustc_hir_typeck/src/callee.rs69
-rw-r--r--compiler/rustc_hir_typeck/src/cast.rs274
-rw-r--r--compiler/rustc_hir_typeck/src/check.rs249
-rw-r--r--compiler/rustc_hir_typeck/src/closure.rs2
-rw-r--r--compiler/rustc_hir_typeck/src/demand.rs131
-rw-r--r--compiler/rustc_hir_typeck/src/errors.rs269
-rw-r--r--compiler/rustc_hir_typeck/src/expr.rs44
-rw-r--r--compiler/rustc_hir_typeck/src/expr_use_visitor.rs10
-rw-r--r--compiler/rustc_hir_typeck/src/fallback.rs64
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs80
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs220
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs24
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs23
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs8
-rw-r--r--compiler/rustc_hir_typeck/src/gather_locals.rs2
-rw-r--r--compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/cfg_build.rs601
-rw-r--r--compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/cfg_propagate.rs92
-rw-r--r--compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/cfg_visualize.rs96
-rw-r--r--compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/mod.rs306
-rw-r--r--compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/record_consumed_borrow.rs242
-rw-r--r--compiler/rustc_hir_typeck/src/generator_interior/mod.rs714
-rw-r--r--compiler/rustc_hir_typeck/src/lib.rs7
-rw-r--r--compiler/rustc_hir_typeck/src/mem_categorization.rs5
-rw-r--r--compiler/rustc_hir_typeck/src/method/mod.rs5
-rw-r--r--compiler/rustc_hir_typeck/src/method/suggest.rs3
-rw-r--r--compiler/rustc_hir_typeck/src/upvar.rs61
-rw-r--r--compiler/rustc_hir_typeck/src/writeback.rs8
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()