Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

on region_errors.rs #100900

Merged
merged 1 commit into from
Aug 27, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 41 additions & 52 deletions compiler/rustc_borrowck/src/diagnostics/region_errors.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#![deny(rustc::untranslatable_diagnostic)]
#![deny(rustc::diagnostic_outside_of_impl)]
//! Error reporting machinery for lifetime errors.

use rustc_data_structures::fx::FxHashSet;
Expand All @@ -23,7 +25,10 @@ use rustc_span::symbol::{kw, sym, Ident};
use rustc_span::Span;

use crate::borrowck_errors;
use crate::session_diagnostics::GenericDoesNotLiveLongEnough;
use crate::session_diagnostics::{
FnMutError, FnMutReturnTypeErr, GenericDoesNotLiveLongEnough, LifetimeOutliveErr,
LifetimeReturnCategoryErr, RequireStaticErr, VarHereDenote,
};

use super::{OutlivesSuggestionBuilder, RegionName};
use crate::region_infer::BlameConstraint;
Expand Down Expand Up @@ -488,32 +493,27 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
let ErrorConstraintInfo { outlived_fr, span, .. } = errci;

let mut diag = self
.infcx
.tcx
.sess
.struct_span_err(*span, "captured variable cannot escape `FnMut` closure body");

let mut output_ty = self.regioncx.universal_regions().unnormalized_output_ty;
if let ty::Opaque(def_id, _) = *output_ty.kind() {
output_ty = self.infcx.tcx.type_of(def_id)
};

debug!("report_fnmut_error: output_ty={:?}", output_ty);

let message = match output_ty.kind() {
ty::Closure(_, _) => {
"returns a closure that contains a reference to a captured variable, which then \
escapes the closure body"
}
ty::Adt(def, _) if self.infcx.tcx.is_diagnostic_item(sym::gen_future, def.did()) => {
"returns an `async` block that contains a reference to a captured variable, which then \
escapes the closure body"
}
_ => "returns a reference to a captured variable which escapes the closure body",
let err = FnMutError {
span: *span,
ty_err: match output_ty.kind() {
ty::Closure(_, _) => FnMutReturnTypeErr::ReturnClosure { span: *span },
ty::Adt(def, _)
if self.infcx.tcx.is_diagnostic_item(sym::gen_future, def.did()) =>
{
FnMutReturnTypeErr::ReturnAsyncBlock { span: *span }
}
_ => FnMutReturnTypeErr::ReturnRef { span: *span },
},
};

diag.span_label(*span, message);
let mut diag = self.infcx.tcx.sess.create_err(err);

if let ReturnConstraint::ClosureUpvar(upvar_field) = kind {
let def_id = match self.regioncx.universal_regions().defining_ty {
Expand All @@ -532,20 +532,15 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
let upvars_map = self.infcx.tcx.upvars_mentioned(def_id).unwrap();
let upvar_def_span = self.infcx.tcx.hir().span(def_hir);
let upvar_span = upvars_map.get(&def_hir).unwrap().span;
diag.span_label(upvar_def_span, "variable defined here");
diag.span_label(upvar_span, "variable captured here");
diag.subdiagnostic(VarHereDenote::Defined { span: upvar_def_span });
diag.subdiagnostic(VarHereDenote::Captured { span: upvar_span });
}
}

if let Some(fr_span) = self.give_region_a_name(*outlived_fr).unwrap().span() {
diag.span_label(fr_span, "inferred to be a `FnMut` closure");
diag.subdiagnostic(VarHereDenote::FnMutInferred { span: fr_span });
}

diag.note(
"`FnMut` closures only have access to their captured variables while they are \
executing...",
);
diag.note("...therefore, they cannot allow references to captured variables to escape");
self.suggest_move_on_borrowing_closure(&mut diag);

diag
Expand Down Expand Up @@ -681,39 +676,33 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
..
} = errci;

let mut diag =
self.infcx.tcx.sess.struct_span_err(*span, "lifetime may not live long enough");

let (_, mir_def_name) =
self.infcx.tcx.article_and_description(self.mir_def_id().to_def_id());

let err = LifetimeOutliveErr { span: *span };
let mut diag = self.infcx.tcx.sess.create_err(err);

let fr_name = self.give_region_a_name(*fr).unwrap();
fr_name.highlight_region_name(&mut diag);
let outlived_fr_name = self.give_region_a_name(*outlived_fr).unwrap();
outlived_fr_name.highlight_region_name(&mut diag);

match (category, outlived_fr_is_local, fr_is_local) {
(ConstraintCategory::Return(_), true, _) => {
diag.span_label(
*span,
format!(
"{mir_def_name} was supposed to return data with lifetime `{outlived_fr_name}` but it is returning \
data with lifetime `{fr_name}`",
),
);
}
_ => {
diag.span_label(
*span,
format!(
"{}requires that `{}` must outlive `{}`",
category.description(),
fr_name,
outlived_fr_name,
),
);
}
}
let err_category = match (category, outlived_fr_is_local, fr_is_local) {
(ConstraintCategory::Return(_), true, _) => LifetimeReturnCategoryErr::WrongReturn {
span: *span,
mir_def_name,
outlived_fr_name,
fr_name: &fr_name,
},
_ => LifetimeReturnCategoryErr::ShortReturn {
span: *span,
category_desc: category.description(),
free_region_name: &fr_name,
outlived_fr_name,
},
};

diag.subdiagnostic(err_category);

self.add_static_impl_trait_suggestion(&mut diag, *fr, fr_name, *outlived_fr);
self.suggest_adding_lifetime_params(&mut diag, *fr, *outlived_fr);
Expand Down Expand Up @@ -862,7 +851,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
ident.span,
"calling this method introduces the `impl`'s 'static` requirement",
);
err.span_note(multi_span, "the used `impl` has a `'static` requirement");
err.subdiagnostic(RequireStaticErr::UsedImpl { multi_span });
err.span_suggestion_verbose(
span.shrink_to_hi(),
"consider relaxing the implicit `'static` requirement",
Expand Down
18 changes: 6 additions & 12 deletions compiler/rustc_borrowck/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ extern crate tracing;

use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_data_structures::graph::dominators::Dominators;
use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed};
use rustc_errors::{Diagnostic, DiagnosticBuilder, ErrorGuaranteed};
use rustc_hir as hir;
use rustc_hir::def_id::LocalDefId;
use rustc_index::bit_set::ChunkedBitSet;
Expand Down Expand Up @@ -50,6 +50,8 @@ use rustc_mir_dataflow::move_paths::{InitLocation, LookupResult, MoveData, MoveE
use rustc_mir_dataflow::Analysis;
use rustc_mir_dataflow::MoveDataParamEnv;

use crate::session_diagnostics::VarNeedNotMut;

use self::diagnostics::{AccessKind, RegionName};
use self::location::LocationTable;
use self::prefixes::PrefixSet;
Expand Down Expand Up @@ -424,17 +426,9 @@ fn do_mir_borrowck<'a, 'tcx>(
continue;
}

tcx.struct_span_lint_hir(UNUSED_MUT, lint_root, span, |lint| {
let mut_span = tcx.sess.source_map().span_until_non_whitespace(span);
lint.build("variable does not need to be mutable")
.span_suggestion_short(
mut_span,
"remove this `mut`",
"",
Applicability::MachineApplicable,
)
.emit();
})
let mut_span = tcx.sess.source_map().span_until_non_whitespace(span);

tcx.emit_spanned_lint(UNUSED_MUT, lint_root, span, VarNeedNotMut { span: mut_span })
}

let tainted_by_errors = mbcx.emit_errors();
Expand Down
17 changes: 6 additions & 11 deletions compiler/rustc_borrowck/src/region_infer/opaque_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ use rustc_span::Span;
use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _;
use rustc_trait_selection::traits::TraitEngineExt as _;

use crate::session_diagnostics::ConstNotUsedTraitAlias;

use super::RegionInferenceContext;

impl<'tcx> RegionInferenceContext<'tcx> {
Expand Down Expand Up @@ -639,17 +641,10 @@ impl<'tcx> TypeFolder<'tcx> for ReverseMapper<'tcx> {
Some(GenericArgKind::Const(c1)) => c1,
Some(u) => panic!("const mapped to unexpected kind: {:?}", u),
None => {
self.tcx
.sess
.struct_span_err(
self.span,
&format!(
"const parameter `{}` is part of concrete type but not \
used in parameter list for the `impl Trait` type alias",
ct
),
)
.emit();
self.tcx.sess.emit_err(ConstNotUsedTraitAlias {
ct: ct.to_string(),
span: self.span,
});

self.tcx().const_error(ct.ty())
}
Expand Down
117 changes: 116 additions & 1 deletion compiler/rustc_borrowck/src/session_diagnostics.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
use rustc_macros::{SessionDiagnostic, SessionSubdiagnostic};
use rustc_errors::{IntoDiagnosticArg, MultiSpan};
use rustc_macros::{LintDiagnostic, SessionDiagnostic, SessionSubdiagnostic};
use rustc_middle::ty::Ty;
use rustc_span::Span;

use crate::diagnostics::RegionName;

#[derive(SessionDiagnostic)]
#[diag(borrowck::move_unsized, code = "E0161")]
pub(crate) struct MoveUnsized<'tcx> {
Expand Down Expand Up @@ -42,3 +45,115 @@ pub(crate) struct GenericDoesNotLiveLongEnough {
#[primary_span]
pub span: Span,
}

#[derive(LintDiagnostic)]
#[diag(borrowck::var_does_not_need_mut)]
pub(crate) struct VarNeedNotMut {
#[suggestion_short(applicability = "machine-applicable", code = "")]
pub span: Span,
}

#[derive(SessionDiagnostic)]
#[diag(borrowck::const_not_used_in_type_alias)]
pub(crate) struct ConstNotUsedTraitAlias {
pub ct: String,
#[primary_span]
pub span: Span,
}

#[derive(SessionDiagnostic)]
#[diag(borrowck::var_cannot_escape_closure)]
#[note]
#[note(borrowck::cannot_escape)]
pub(crate) struct FnMutError {
#[primary_span]
pub span: Span,
#[subdiagnostic]
pub ty_err: FnMutReturnTypeErr,
}

#[derive(SessionSubdiagnostic)]
pub(crate) enum VarHereDenote {
#[label(borrowck::var_here_captured)]
Captured {
#[primary_span]
span: Span,
},
#[label(borrowck::var_here_defined)]
Defined {
#[primary_span]
span: Span,
},
#[label(borrowck::closure_inferred_mut)]
FnMutInferred {
#[primary_span]
span: Span,
},
}

#[derive(SessionSubdiagnostic)]
pub(crate) enum FnMutReturnTypeErr {
#[label(borrowck::returned_closure_escaped)]
ReturnClosure {
#[primary_span]
span: Span,
},
#[label(borrowck::returned_async_block_escaped)]
ReturnAsyncBlock {
#[primary_span]
span: Span,
},
#[label(borrowck::returned_ref_escaped)]
ReturnRef {
#[primary_span]
span: Span,
},
}

#[derive(SessionDiagnostic)]
#[diag(borrowck::lifetime_constraints_error)]
pub(crate) struct LifetimeOutliveErr {
#[primary_span]
pub span: Span,
}

#[derive(SessionSubdiagnostic)]
pub(crate) enum LifetimeReturnCategoryErr<'a> {
#[label(borrowck::returned_lifetime_wrong)]
WrongReturn {
#[primary_span]
span: Span,
mir_def_name: &'a str,
outlived_fr_name: RegionName,
fr_name: &'a RegionName,
},
#[label(borrowck::returned_lifetime_short)]
ShortReturn {
#[primary_span]
span: Span,
category_desc: &'static str,
free_region_name: &'a RegionName,
outlived_fr_name: RegionName,
},
}

impl IntoDiagnosticArg for &RegionName {
fn into_diagnostic_arg(self) -> rustc_errors::DiagnosticArgValue<'static> {
format!("{}", self).into_diagnostic_arg()
}
}

impl IntoDiagnosticArg for RegionName {
fn into_diagnostic_arg(self) -> rustc_errors::DiagnosticArgValue<'static> {
format!("{}", self).into_diagnostic_arg()
}
}

#[derive(SessionSubdiagnostic)]
pub(crate) enum RequireStaticErr {
#[note(borrowck::used_impl_require_static)]
UsedImpl {
#[primary_span]
multi_span: MultiSpan,
},
}
42 changes: 42 additions & 0 deletions compiler/rustc_error_messages/locales/en-US/borrowck.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,45 @@ borrowck_higher_ranked_subtype_error =

borrowck_generic_does_not_live_long_enough =
`{$kind}` does not live long enough

borrowck_move_borrowed =
cannot move out of `{$desc}` beacause it is borrowed

borrowck_var_does_not_need_mut =
variable does not need to be mutable
.suggestion = remove this `mut`

borrowck_const_not_used_in_type_alias =
const parameter `{$ct}` is part of concrete type but not used in parameter list for the `impl Trait` type alias

borrowck_var_cannot_escape_closure =
captured variable cannot escape `FnMut` closure body
.note = `FnMut` closures only have access to their captured variables while they are executing...
.cannot_escape = ...therefore, they cannot allow references to captured variables to escape

borrowck_var_here_defined = variable defined here

borrowck_var_here_captured = variable captured here

borrowck_closure_inferred_mut = inferred to be a `FnMut` closure

borrowck_returned_closure_escaped =
returns a closure that contains a reference to a captured variable, which then escapes the closure body

borrowck_returned_async_block_escaped =
returns an `async` block that contains a reference to a captured variable, which then escapes the closure body

borrowck_returned_ref_escaped =
returns a reference to a captured variable which escapes the closure body

borrowck_lifetime_constraints_error =
lifetime may not live long enough

borrowck_returned_lifetime_wrong =
{$mir_def_name} was supposed to return data with lifetime `{$outlived_fr_name}` but it is returning data with lifetime `{$fr_name}`

borrowck_returned_lifetime_short =
{$category_desc}requires that `{$free_region_name}` must outlive `{$outlived_fr_name}`

borrowck_used_impl_require_static =
the used `impl` has a `'static` requirement