Auto merge of #38897 - nikomatsakis:issue-32330-followup, r=arielb1
make lifetimes that only appear in return type early-bound This is the full and proper fix for #32330. This also makes some effort to give a nice error message (as evidenced by the `ui` test), sending users over to the tracking issue for a fuller explanation and offering a `--explain` message in some cases. This needs a crater run before we land. r? @arielb1
This commit is contained in:
commit
a3da24bba9
@ -72,13 +72,12 @@
|
||||
use hir::map as hir_map;
|
||||
use hir;
|
||||
|
||||
use lint;
|
||||
use hir::def_id::DefId;
|
||||
use infer;
|
||||
use middle::region;
|
||||
use traits::{ObligationCause, ObligationCauseCode};
|
||||
use ty::{self, TyCtxt, TypeFoldable};
|
||||
use ty::{Region, ReFree};
|
||||
use ty::{Region, ReFree, Issue32330};
|
||||
use ty::error::TypeError;
|
||||
|
||||
use std::fmt;
|
||||
@ -610,6 +609,39 @@ pub fn note_type_err(&self,
|
||||
self.tcx.note_and_explain_type_err(diag, terr, span);
|
||||
}
|
||||
|
||||
pub fn note_issue_32330(&self,
|
||||
diag: &mut DiagnosticBuilder<'tcx>,
|
||||
terr: &TypeError<'tcx>)
|
||||
{
|
||||
debug!("note_issue_32330: terr={:?}", terr);
|
||||
match *terr {
|
||||
TypeError::RegionsInsufficientlyPolymorphic(_, &Region::ReVar(vid)) |
|
||||
TypeError::RegionsOverlyPolymorphic(_, &Region::ReVar(vid)) => {
|
||||
match self.region_vars.var_origin(vid) {
|
||||
RegionVariableOrigin::EarlyBoundRegion(_, _, Some(Issue32330 {
|
||||
fn_def_id,
|
||||
region_name
|
||||
})) => {
|
||||
diag.note(
|
||||
&format!("lifetime parameter `{0}` declared on fn `{1}` \
|
||||
appears only in the return type, \
|
||||
but here is required to be higher-ranked, \
|
||||
which means that `{0}` must appear in both \
|
||||
argument and return types",
|
||||
region_name,
|
||||
self.tcx.item_path_str(fn_def_id)));
|
||||
diag.note(
|
||||
&format!("this error is the result of a recent bug fix; \
|
||||
for more information, see issue #33685 \
|
||||
<https://github.com/rust-lang/rust/issues/33685>"));
|
||||
}
|
||||
_ => { }
|
||||
}
|
||||
}
|
||||
_ => { }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn report_and_explain_type_error(&self,
|
||||
trace: TypeTrace<'tcx>,
|
||||
terr: &TypeError<'tcx>)
|
||||
@ -629,6 +661,7 @@ pub fn report_and_explain_type_error(&self,
|
||||
}
|
||||
};
|
||||
self.note_type_err(&mut diag, &trace.cause, None, Some(trace.values), terr);
|
||||
self.note_issue_32330(&mut diag, terr);
|
||||
diag
|
||||
}
|
||||
|
||||
@ -1053,27 +1086,6 @@ fn report_processed_errors(&self,
|
||||
err.emit();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn issue_32330_warnings(&self, span: Span, issue32330s: &[ty::Issue32330]) {
|
||||
for issue32330 in issue32330s {
|
||||
match *issue32330 {
|
||||
ty::Issue32330::WontChange => { }
|
||||
ty::Issue32330::WillChange { fn_def_id, region_name } => {
|
||||
self.tcx.sess.add_lint(
|
||||
lint::builtin::HR_LIFETIME_IN_ASSOC_TYPE,
|
||||
ast::CRATE_NODE_ID,
|
||||
span,
|
||||
format!("lifetime parameter `{0}` declared on fn `{1}` \
|
||||
appears only in the return type, \
|
||||
but here is required to be higher-ranked, \
|
||||
which means that `{0}` must appear in both \
|
||||
argument and return types",
|
||||
region_name,
|
||||
self.tcx.item_path_str(fn_def_id)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
|
||||
@ -1104,7 +1116,7 @@ fn report_inference_failure(&self,
|
||||
format!(" for lifetime parameter {}in trait containing associated type `{}`",
|
||||
br_string(br), type_name)
|
||||
}
|
||||
infer::EarlyBoundRegion(_, name) => {
|
||||
infer::EarlyBoundRegion(_, name, _) => {
|
||||
format!(" for lifetime parameter `{}`",
|
||||
name)
|
||||
}
|
||||
|
@ -622,7 +622,7 @@ pub fn skolemize_late_bound_regions<T>(&self,
|
||||
/// hold. See `README.md` for more details.
|
||||
pub fn leak_check(&self,
|
||||
overly_polymorphic: bool,
|
||||
span: Span,
|
||||
_span: Span,
|
||||
skol_map: &SkolemizationMap<'tcx>,
|
||||
snapshot: &CombinedSnapshot)
|
||||
-> RelateResult<'tcx, ()>
|
||||
@ -630,43 +630,6 @@ pub fn leak_check(&self,
|
||||
debug!("leak_check: skol_map={:?}",
|
||||
skol_map);
|
||||
|
||||
// ## Issue #32330 warnings
|
||||
//
|
||||
// When Issue #32330 is fixed, a certain number of late-bound
|
||||
// regions (LBR) will become early-bound. We wish to issue
|
||||
// warnings when the result of `leak_check` relies on such LBR, as
|
||||
// that means that compilation will likely start to fail.
|
||||
//
|
||||
// Recall that when we do a "HR subtype" check, we replace all
|
||||
// late-bound regions (LBR) in the subtype with fresh variables,
|
||||
// and skolemize the late-bound regions in the supertype. If those
|
||||
// skolemized regions from the supertype wind up being
|
||||
// super-regions (directly or indirectly) of either
|
||||
//
|
||||
// - another skolemized region; or,
|
||||
// - some region that pre-exists the HR subtype check
|
||||
// - e.g., a region variable that is not one of those created
|
||||
// to represent bound regions in the subtype
|
||||
//
|
||||
// then leak-check (and hence the subtype check) fails.
|
||||
//
|
||||
// What will change when we fix #32330 is that some of the LBR in the
|
||||
// subtype may become early-bound. In that case, they would no longer be in
|
||||
// the "permitted set" of variables that can be related to a skolemized
|
||||
// type.
|
||||
//
|
||||
// So the foundation for this warning is to collect variables that we found
|
||||
// to be related to a skolemized type. For each of them, we have a
|
||||
// `BoundRegion` which carries a `Issue32330` flag. We check whether any of
|
||||
// those flags indicate that this variable was created from a lifetime
|
||||
// that will change from late- to early-bound. If so, we issue a warning
|
||||
// indicating that the results of compilation may change.
|
||||
//
|
||||
// This is imperfect, since there are other kinds of code that will not
|
||||
// compile once #32330 is fixed. However, it fixes the errors observed in
|
||||
// practice on crater runs.
|
||||
let mut warnings = vec![];
|
||||
|
||||
let new_vars = self.region_vars_confined_to_snapshot(snapshot);
|
||||
for (&skol_br, &skol) in skol_map {
|
||||
// The inputs to a skolemized variable can only
|
||||
@ -680,13 +643,6 @@ pub fn leak_check(&self,
|
||||
match *tainted_region {
|
||||
ty::ReVar(vid) => {
|
||||
if new_vars.contains(&vid) {
|
||||
warnings.extend(
|
||||
match self.region_vars.var_origin(vid) {
|
||||
LateBoundRegion(_,
|
||||
ty::BrNamed(.., wc),
|
||||
_) => Some(wc),
|
||||
_ => None,
|
||||
});
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@ -712,8 +668,6 @@ pub fn leak_check(&self,
|
||||
}
|
||||
}
|
||||
|
||||
self.issue_32330_warnings(span, &warnings);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -368,7 +368,7 @@ pub enum RegionVariableOrigin {
|
||||
Coercion(Span),
|
||||
|
||||
// Region variables created as the values for early-bound regions
|
||||
EarlyBoundRegion(Span, ast::Name),
|
||||
EarlyBoundRegion(Span, ast::Name, Option<ty::Issue32330>),
|
||||
|
||||
// Region variables created for bound regions
|
||||
// in a function or method that is called
|
||||
@ -1184,7 +1184,7 @@ pub fn region_var_for_def(&self,
|
||||
span: Span,
|
||||
def: &ty::RegionParameterDef)
|
||||
-> &'tcx ty::Region {
|
||||
self.next_region_var(EarlyBoundRegion(span, def.name))
|
||||
self.next_region_var(EarlyBoundRegion(span, def.name, def.issue_32330))
|
||||
}
|
||||
|
||||
/// Create a type inference variable for the given
|
||||
@ -1761,7 +1761,7 @@ pub fn span(&self) -> Span {
|
||||
AddrOfRegion(a) => a,
|
||||
Autoref(a) => a,
|
||||
Coercion(a) => a,
|
||||
EarlyBoundRegion(a, _) => a,
|
||||
EarlyBoundRegion(a, ..) => a,
|
||||
LateBoundRegion(a, ..) => a,
|
||||
BoundRegionInCoherence(_) => syntax_pos::DUMMY_SP,
|
||||
UpvarRegion(_, a) => a
|
||||
|
@ -31,7 +31,7 @@
|
||||
use syntax::symbol::keywords;
|
||||
use syntax_pos::Span;
|
||||
use errors::DiagnosticBuilder;
|
||||
use util::nodemap::{NodeMap, FxHashSet, FxHashMap, DefIdMap};
|
||||
use util::nodemap::{NodeMap, NodeSet, FxHashSet, FxHashMap, DefIdMap};
|
||||
use rustc_back::slice;
|
||||
|
||||
use hir;
|
||||
@ -150,10 +150,14 @@ pub struct NamedRegionMap {
|
||||
// `Region` describing how that region is bound
|
||||
pub defs: NodeMap<Region>,
|
||||
|
||||
// the set of lifetime def ids that are late-bound; late-bound ids
|
||||
// are named regions appearing in fn arguments that do not appear
|
||||
// in where-clauses
|
||||
pub late_bound: NodeMap<ty::Issue32330>,
|
||||
// the set of lifetime def ids that are late-bound; a region can
|
||||
// be late-bound if (a) it does NOT appear in a where-clause and
|
||||
// (b) it DOES appear in the arguments.
|
||||
pub late_bound: NodeSet,
|
||||
|
||||
// Contains the node-ids for lifetimes that were (incorrectly) categorized
|
||||
// as late-bound, until #32330 was fixed.
|
||||
pub issue_32330: NodeMap<ty::Issue32330>,
|
||||
|
||||
// For each type and trait definition, maps type parameters
|
||||
// to the trait object lifetime defaults computed from them.
|
||||
@ -261,7 +265,8 @@ pub fn krate(sess: &Session,
|
||||
let krate = hir_map.krate();
|
||||
let mut map = NamedRegionMap {
|
||||
defs: NodeMap(),
|
||||
late_bound: NodeMap(),
|
||||
late_bound: NodeSet(),
|
||||
issue_32330: NodeMap(),
|
||||
object_lifetime_defaults: compute_object_lifetime_defaults(sess, hir_map),
|
||||
};
|
||||
sess.track_errors(|| {
|
||||
@ -840,7 +845,7 @@ fn visit_early_late<F>(&mut self,
|
||||
}
|
||||
|
||||
let lifetimes = generics.lifetimes.iter().map(|def| {
|
||||
if self.map.late_bound.contains_key(&def.lifetime.id) {
|
||||
if self.map.late_bound.contains(&def.lifetime.id) {
|
||||
Region::late(def)
|
||||
} else {
|
||||
Region::early(&mut index, def)
|
||||
@ -1610,22 +1615,26 @@ fn insert_late_bound_lifetimes(map: &mut NamedRegionMap,
|
||||
// just mark it so we can issue warnings.
|
||||
let constrained_by_input = constrained_by_input.regions.contains(&name);
|
||||
let appears_in_output = appears_in_output.regions.contains(&name);
|
||||
let will_change = !constrained_by_input && appears_in_output;
|
||||
let issue_32330 = if will_change {
|
||||
ty::Issue32330::WillChange {
|
||||
fn_def_id: fn_def_id,
|
||||
region_name: name,
|
||||
}
|
||||
} else {
|
||||
ty::Issue32330::WontChange
|
||||
};
|
||||
if !constrained_by_input && appears_in_output {
|
||||
debug!("inserting issue_32330 entry for {:?}, {:?} on {:?}",
|
||||
lifetime.lifetime.id,
|
||||
name,
|
||||
fn_def_id);
|
||||
map.issue_32330.insert(
|
||||
lifetime.lifetime.id,
|
||||
ty::Issue32330 {
|
||||
fn_def_id: fn_def_id,
|
||||
region_name: name,
|
||||
});
|
||||
continue;
|
||||
}
|
||||
|
||||
debug!("insert_late_bound_lifetimes: \
|
||||
lifetime {:?} with id {:?} is late-bound ({:?}",
|
||||
lifetime.lifetime.name, lifetime.lifetime.id, issue_32330);
|
||||
lifetime {:?} with id {:?} is late-bound",
|
||||
lifetime.lifetime.name, lifetime.lifetime.id);
|
||||
|
||||
let prev = map.late_bound.insert(lifetime.lifetime.id, issue_32330);
|
||||
assert!(prev.is_none(), "visited lifetime {:?} twice", lifetime.lifetime.id);
|
||||
let inserted = map.late_bound.insert(lifetime.lifetime.id);
|
||||
assert!(inserted, "visited lifetime {:?} twice", lifetime.lifetime.id);
|
||||
}
|
||||
|
||||
return;
|
||||
|
@ -606,6 +606,7 @@ pub struct RegionParameterDef {
|
||||
pub name: Name,
|
||||
pub def_id: DefId,
|
||||
pub index: u32,
|
||||
pub issue_32330: Option<ty::Issue32330>,
|
||||
|
||||
/// `pure_wrt_drop`, set by the (unsafe) `#[may_dangle]` attribute
|
||||
/// on generic parameter `'a`, asserts data of lifetime `'a`
|
||||
@ -622,8 +623,7 @@ pub fn to_early_bound_region_data(&self) -> ty::EarlyBoundRegion {
|
||||
}
|
||||
|
||||
pub fn to_bound_region(&self) -> ty::BoundRegion {
|
||||
// this is an early bound region, so unaffected by #32330
|
||||
ty::BoundRegion::BrNamed(self.def_id, self.name, Issue32330::WontChange)
|
||||
ty::BoundRegion::BrNamed(self.def_id, self.name)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -58,7 +58,7 @@ pub enum BoundRegion {
|
||||
///
|
||||
/// The def-id is needed to distinguish free regions in
|
||||
/// the event of shadowing.
|
||||
BrNamed(DefId, Name, Issue32330),
|
||||
BrNamed(DefId, Name),
|
||||
|
||||
/// Fresh bound identifiers created during GLB computations.
|
||||
BrFresh(u32),
|
||||
@ -68,23 +68,18 @@ pub enum BoundRegion {
|
||||
BrEnv
|
||||
}
|
||||
|
||||
/// True if this late-bound region is unconstrained, and hence will
|
||||
/// become early-bound once #32330 is fixed.
|
||||
/// When a region changed from late-bound to early-bound when #32330
|
||||
/// was fixed, its `RegionParameterDef` will have one of these
|
||||
/// structures that we can use to give nicer errors.
|
||||
#[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Eq, Ord, Hash,
|
||||
RustcEncodable, RustcDecodable)]
|
||||
pub enum Issue32330 {
|
||||
WontChange,
|
||||
pub struct Issue32330 {
|
||||
/// fn where is region declared
|
||||
pub fn_def_id: DefId,
|
||||
|
||||
/// this region will change from late-bound to early-bound once
|
||||
/// #32330 is fixed.
|
||||
WillChange {
|
||||
/// fn where is region declared
|
||||
fn_def_id: DefId,
|
||||
|
||||
/// name of region; duplicates the info in BrNamed but convenient
|
||||
/// to have it here, and this code is only temporary
|
||||
region_name: ast::Name,
|
||||
}
|
||||
/// name of region; duplicates the info in BrNamed but convenient
|
||||
/// to have it here, and this code is only temporary
|
||||
pub region_name: ast::Name,
|
||||
}
|
||||
|
||||
// NB: If you change this, you'll probably want to change the corresponding
|
||||
|
@ -276,7 +276,7 @@ fn in_binder<'a, 'gcx, 'tcx, T, U>(f: &mut fmt::Formatter,
|
||||
let new_value = tcx.replace_late_bound_regions(&value, |br| {
|
||||
let _ = start_or_continue(f, "for<", ", ");
|
||||
let br = match br {
|
||||
ty::BrNamed(_, name, _) => {
|
||||
ty::BrNamed(_, name) => {
|
||||
let _ = write!(f, "{}", name);
|
||||
br
|
||||
}
|
||||
@ -286,8 +286,7 @@ fn in_binder<'a, 'gcx, 'tcx, T, U>(f: &mut fmt::Formatter,
|
||||
let name = Symbol::intern("'r");
|
||||
let _ = write!(f, "{}", name);
|
||||
ty::BrNamed(tcx.hir.local_def_id(CRATE_NODE_ID),
|
||||
name,
|
||||
ty::Issue32330::WontChange)
|
||||
name)
|
||||
}
|
||||
};
|
||||
tcx.mk_region(ty::ReLateBound(ty::DebruijnIndex::new(1), br))
|
||||
@ -435,7 +434,7 @@ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
}
|
||||
|
||||
match *self {
|
||||
BrNamed(_, name, _) => write!(f, "{}", name),
|
||||
BrNamed(_, name) => write!(f, "{}", name),
|
||||
BrAnon(_) | BrFresh(_) | BrEnv => Ok(())
|
||||
}
|
||||
}
|
||||
@ -446,9 +445,9 @@ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match *self {
|
||||
BrAnon(n) => write!(f, "BrAnon({:?})", n),
|
||||
BrFresh(n) => write!(f, "BrFresh({:?})", n),
|
||||
BrNamed(did, name, issue32330) => {
|
||||
write!(f, "BrNamed({:?}:{:?}, {:?}, {:?})",
|
||||
did.krate, did.index, name, issue32330)
|
||||
BrNamed(did, name) => {
|
||||
write!(f, "BrNamed({:?}:{:?}, {:?})",
|
||||
did.krate, did.index, name)
|
||||
}
|
||||
BrEnv => "BrEnv".fmt(f),
|
||||
}
|
||||
|
@ -29,7 +29,6 @@
|
||||
use hir::def::Def;
|
||||
use hir::def_id::DefId;
|
||||
use middle::resolve_lifetime as rl;
|
||||
use rustc::lint;
|
||||
use rustc::ty::subst::{Kind, Subst, Substs};
|
||||
use rustc::traits;
|
||||
use rustc::ty::{self, Ty, TyCtxt, ToPredicate, TypeFoldable};
|
||||
@ -148,20 +147,9 @@ pub fn ast_region_to_region(&self,
|
||||
}
|
||||
|
||||
Some(&rl::Region::LateBound(debruijn, id)) => {
|
||||
// If this region is declared on a function, it will have
|
||||
// an entry in `late_bound`, but if it comes from
|
||||
// `for<'a>` in some type or something, it won't
|
||||
// necessarily have one. In that case though, we won't be
|
||||
// changed from late to early bound, so we can just
|
||||
// substitute false.
|
||||
let issue_32330 = tcx.named_region_map
|
||||
.late_bound
|
||||
.get(&id)
|
||||
.cloned()
|
||||
.unwrap_or(ty::Issue32330::WontChange);
|
||||
let name = tcx.hir.name(id);
|
||||
tcx.mk_region(ty::ReLateBound(debruijn,
|
||||
ty::BrNamed(tcx.hir.local_def_id(id), name, issue_32330)))
|
||||
ty::BrNamed(tcx.hir.local_def_id(id), name)))
|
||||
}
|
||||
|
||||
Some(&rl::Region::LateBoundAnon(debruijn, index)) => {
|
||||
@ -177,17 +165,10 @@ pub fn ast_region_to_region(&self,
|
||||
}
|
||||
|
||||
Some(&rl::Region::Free(scope, id)) => {
|
||||
// As in Region::LateBound above, could be missing for some late-bound
|
||||
// regions, but also for early-bound regions.
|
||||
let issue_32330 = tcx.named_region_map
|
||||
.late_bound
|
||||
.get(&id)
|
||||
.cloned()
|
||||
.unwrap_or(ty::Issue32330::WontChange);
|
||||
let name = tcx.hir.name(id);
|
||||
tcx.mk_region(ty::ReFree(ty::FreeRegion {
|
||||
scope: scope.to_code_extent(&tcx.region_maps),
|
||||
bound_region: ty::BrNamed(tcx.hir.local_def_id(id), name, issue_32330)
|
||||
bound_region: ty::BrNamed(tcx.hir.local_def_id(id), name)
|
||||
}))
|
||||
|
||||
// (*) -- not late-bound, won't change
|
||||
@ -566,7 +547,7 @@ fn trait_defines_associated_type_named(&self,
|
||||
|
||||
fn ast_type_binding_to_poly_projection_predicate(
|
||||
&self,
|
||||
path_id: ast::NodeId,
|
||||
_path_id: ast::NodeId,
|
||||
trait_ref: ty::PolyTraitRef<'tcx>,
|
||||
binding: &ConvertedBinding<'tcx>)
|
||||
-> Result<ty::PolyProjectionPredicate<'tcx>, ErrorReported>
|
||||
@ -602,7 +583,7 @@ fn ast_type_binding_to_poly_projection_predicate(
|
||||
debug!("late_bound_in_ty = {:?}", late_bound_in_ty);
|
||||
for br in late_bound_in_ty.difference(&late_bound_in_trait_ref) {
|
||||
let br_name = match *br {
|
||||
ty::BrNamed(_, name, _) => name,
|
||||
ty::BrNamed(_, name) => name,
|
||||
_ => {
|
||||
span_bug!(
|
||||
binding.span,
|
||||
@ -610,13 +591,13 @@ fn ast_type_binding_to_poly_projection_predicate(
|
||||
br);
|
||||
}
|
||||
};
|
||||
tcx.sess.add_lint(
|
||||
lint::builtin::HR_LIFETIME_IN_ASSOC_TYPE,
|
||||
path_id,
|
||||
binding.span,
|
||||
format!("binding for associated type `{}` references lifetime `{}`, \
|
||||
which does not appear in the trait input types",
|
||||
binding.item_name, br_name));
|
||||
struct_span_err!(tcx.sess,
|
||||
binding.span,
|
||||
E0582,
|
||||
"binding for associated type `{}` references lifetime `{}`, \
|
||||
which does not appear in the trait input types",
|
||||
binding.item_name, br_name)
|
||||
.emit();
|
||||
}
|
||||
|
||||
// Simple case: X is defined in the current trait.
|
||||
@ -1197,7 +1178,7 @@ pub fn ast_ty_to_ty(&self, ast_ty: &hir::Ty) -> Ty<'tcx> {
|
||||
let late_bound_in_ret = tcx.collect_referenced_late_bound_regions(&output);
|
||||
for br in late_bound_in_ret.difference(&late_bound_in_args) {
|
||||
let br_name = match *br {
|
||||
ty::BrNamed(_, name, _) => name,
|
||||
ty::BrNamed(_, name) => name,
|
||||
_ => {
|
||||
span_bug!(
|
||||
bf.decl.output.span(),
|
||||
@ -1205,13 +1186,13 @@ pub fn ast_ty_to_ty(&self, ast_ty: &hir::Ty) -> Ty<'tcx> {
|
||||
br);
|
||||
}
|
||||
};
|
||||
tcx.sess.add_lint(
|
||||
lint::builtin::HR_LIFETIME_IN_ASSOC_TYPE,
|
||||
ast_ty.id,
|
||||
ast_ty.span,
|
||||
format!("return type references lifetime `{}`, \
|
||||
which does not appear in the trait input types",
|
||||
br_name));
|
||||
struct_span_err!(tcx.sess,
|
||||
ast_ty.span,
|
||||
E0581,
|
||||
"return type references lifetime `{}`, \
|
||||
which does not appear in the fn input types",
|
||||
br_name)
|
||||
.emit();
|
||||
}
|
||||
tcx.mk_fn_ptr(bare_fn_ty)
|
||||
}
|
||||
|
@ -1418,7 +1418,7 @@ fn get_type_parameter_bounds(&self,
|
||||
fn re_infer(&self, span: Span, def: Option<&ty::RegionParameterDef>)
|
||||
-> Option<&'tcx ty::Region> {
|
||||
let v = match def {
|
||||
Some(def) => infer::EarlyBoundRegion(span, def.name),
|
||||
Some(def) => infer::EarlyBoundRegion(span, def.name, def.issue_32330),
|
||||
None => infer::MiscVariable(span)
|
||||
};
|
||||
Some(self.next_region_var(v))
|
||||
|
@ -108,7 +108,7 @@ fn new(fcx: &'cx FnCtxt<'cx, 'gcx, 'tcx>) -> WritebackCx<'cx, 'gcx, 'tcx> {
|
||||
};
|
||||
match *r {
|
||||
ty::ReFree(ty::FreeRegion {
|
||||
bound_region: ty::BoundRegion::BrNamed(def_id, name, _), ..
|
||||
bound_region: ty::BoundRegion::BrNamed(def_id, name), ..
|
||||
}) => {
|
||||
let bound_region = gcx.mk_region(ty::ReEarlyBound(ty::EarlyBoundRegion {
|
||||
index: i as u32,
|
||||
|
@ -1442,11 +1442,15 @@ fn generics_of_def_id<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
|
||||
|
||||
let early_lifetimes = early_bound_lifetimes_from_generics(ccx, ast_generics);
|
||||
let regions = early_lifetimes.iter().enumerate().map(|(i, l)| {
|
||||
let issue_32330 = ccx.tcx.named_region_map.issue_32330
|
||||
.get(&l.lifetime.id)
|
||||
.cloned();
|
||||
ty::RegionParameterDef {
|
||||
name: l.lifetime.name,
|
||||
index: own_start + i as u32,
|
||||
def_id: tcx.hir.local_def_id(l.lifetime.id),
|
||||
pure_wrt_drop: l.pure_wrt_drop,
|
||||
issue_32330: issue_32330,
|
||||
}
|
||||
}).collect::<Vec<_>>();
|
||||
|
||||
@ -1675,7 +1679,7 @@ fn early_bound_lifetimes_from_generics<'a, 'tcx, 'hir>(
|
||||
ast_generics
|
||||
.lifetimes
|
||||
.iter()
|
||||
.filter(|l| !ccx.tcx.named_region_map.late_bound.contains_key(&l.lifetime.id))
|
||||
.filter(|l| !ccx.tcx.named_region_map.late_bound.contains(&l.lifetime.id))
|
||||
.collect()
|
||||
}
|
||||
|
||||
|
@ -4056,6 +4056,74 @@ fn main() {
|
||||
```
|
||||
"##,
|
||||
|
||||
E0581: r##"
|
||||
In a `fn` type, a lifetime appears only in the return type,
|
||||
and not in the arguments types.
|
||||
|
||||
Erroneous code example:
|
||||
|
||||
```compile_fail,E0581
|
||||
fn main() {
|
||||
// Here, `'a` appears only in the return type:
|
||||
let x: for<'a> fn() -> &'a i32;
|
||||
}
|
||||
```
|
||||
|
||||
To fix this issue, either use the lifetime in the arguments, or use
|
||||
`'static`. Example:
|
||||
|
||||
```
|
||||
fn main() {
|
||||
// Here, `'a` appears only in the return type:
|
||||
let x: for<'a> fn(&'a i32) -> &'a i32;
|
||||
let y: fn() -> &'static i32;
|
||||
}
|
||||
```
|
||||
|
||||
Note: The examples above used to be (erroneously) accepted by the
|
||||
compiler, but this was since corrected. See [issue #33685] for more
|
||||
details.
|
||||
|
||||
[issue #33685]: https://github.com/rust-lang/rust/issues/33685
|
||||
"##,
|
||||
|
||||
E0582: r##"
|
||||
A lifetime appears only in an associated-type binding,
|
||||
and not in the input types to the trait.
|
||||
|
||||
Erroneous code example:
|
||||
|
||||
```compile_fail,E0582
|
||||
fn bar<F>(t: F)
|
||||
// No type can satisfy this requirement, since `'a` does not
|
||||
// appear in any of the input types (here, `i32`):
|
||||
where F: for<'a> Fn(i32) -> Option<&'a i32>
|
||||
{
|
||||
}
|
||||
|
||||
fn main() { }
|
||||
```
|
||||
|
||||
To fix this issue, either use the lifetime in the inputs, or use
|
||||
`'static`. Example:
|
||||
|
||||
```
|
||||
fn bar<F, G>(t: F, u: G)
|
||||
where F: for<'a> Fn(&'a i32) -> Option<&'a i32>,
|
||||
G: Fn(i32) -> Option<&'static i32>,
|
||||
{
|
||||
}
|
||||
|
||||
fn main() { }
|
||||
```
|
||||
|
||||
Note: The examples above used to be (erroneously) accepted by the
|
||||
compiler, but this was since corrected. See [issue #33685] for more
|
||||
details.
|
||||
|
||||
[issue #33685]: https://github.com/rust-lang/rust/issues/33685
|
||||
"##,
|
||||
|
||||
}
|
||||
|
||||
register_diagnostics! {
|
||||
|
@ -810,7 +810,7 @@ impl Clean<Option<Lifetime>> for ty::Region {
|
||||
fn clean(&self, cx: &DocContext) -> Option<Lifetime> {
|
||||
match *self {
|
||||
ty::ReStatic => Some(Lifetime::statik()),
|
||||
ty::ReLateBound(_, ty::BrNamed(_, name, _)) => Some(Lifetime(name.to_string())),
|
||||
ty::ReLateBound(_, ty::BrNamed(_, name)) => Some(Lifetime(name.to_string())),
|
||||
ty::ReEarlyBound(ref data) => Some(Lifetime(data.name.clean(cx))),
|
||||
|
||||
ty::ReLateBound(..) |
|
||||
|
@ -12,7 +12,6 @@
|
||||
// stopped compiling when #32330 is fixed.
|
||||
|
||||
#![allow(dead_code, unused_variables)]
|
||||
#![deny(hr_lifetime_in_assoc_type)]
|
||||
|
||||
use std::str::Chars;
|
||||
|
||||
@ -31,16 +30,21 @@ fn mk_unexpected_char_err<'a>() -> Option<&'a i32> {
|
||||
|
||||
fn foo<'a>(data: &mut Chars<'a>) {
|
||||
bar(mk_unexpected_char_err)
|
||||
//~^ ERROR lifetime parameter `'a` declared on fn `mk_unexpected_char_err`
|
||||
//~| WARNING hard error in a future release
|
||||
}
|
||||
|
||||
fn bar<F>(t: F)
|
||||
// No type can satisfy this requirement, since `'a` does not
|
||||
// appear in any of the input types:
|
||||
where F: for<'a> Fn() -> Option<&'a i32>
|
||||
//~^ ERROR associated type `Output` references lifetime `'a`, which does not
|
||||
//~| WARNING hard error in a future release
|
||||
//~^ ERROR E0582
|
||||
{
|
||||
}
|
||||
|
||||
fn baz<F>(t: F)
|
||||
// No type can satisfy this requirement, since `'a` does not
|
||||
// appear in any of the input types:
|
||||
where F: for<'a> Iterator<Item=&'a i32>
|
||||
//~^ ERROR E0582
|
||||
{
|
||||
}
|
||||
|
@ -12,7 +12,7 @@
|
||||
|
||||
#![allow(dead_code)]
|
||||
#![feature(rustc_attrs)]
|
||||
#![deny(hr_lifetime_in_assoc_type)]
|
||||
#![allow(hr_lifetime_in_assoc_type)]
|
||||
|
||||
trait Foo<'a> {
|
||||
type Item;
|
||||
@ -25,40 +25,34 @@ impl<'a> Foo<'a> for() {
|
||||
// Check that appearing in a projection input in the argument is not enough:
|
||||
#[cfg(func)]
|
||||
fn func1(_: for<'a> fn(<() as Foo<'a>>::Item) -> &'a i32) {
|
||||
//[func]~^ ERROR return type references lifetime `'a`
|
||||
//[func]~| WARNING previously accepted
|
||||
//[func]~^ ERROR E0581
|
||||
}
|
||||
|
||||
// Check that appearing in a projection input in the return still
|
||||
// causes an error:
|
||||
#[cfg(func)]
|
||||
fn func2(_: for<'a> fn() -> <() as Foo<'a>>::Item) {
|
||||
//[func]~^ ERROR return type references lifetime `'a`
|
||||
//[func]~| WARNING previously accepted
|
||||
//[func]~^ ERROR E0581
|
||||
}
|
||||
|
||||
#[cfg(object)]
|
||||
fn object1(_: Box<for<'a> Fn(<() as Foo<'a>>::Item) -> &'a i32>) {
|
||||
//[object]~^ ERROR `Output` references lifetime `'a`
|
||||
//[object]~| WARNING previously accepted
|
||||
//[object]~^ ERROR E0582
|
||||
}
|
||||
|
||||
#[cfg(object)]
|
||||
fn object2(_: Box<for<'a> Fn() -> <() as Foo<'a>>::Item>) {
|
||||
//[object]~^ ERROR `Output` references lifetime `'a`
|
||||
//[object]~| WARNING previously accepted
|
||||
//[object]~^ ERROR E0582
|
||||
}
|
||||
|
||||
#[cfg(clause)]
|
||||
fn clause1<T>() where T: for<'a> Fn(<() as Foo<'a>>::Item) -> &'a i32 {
|
||||
//[clause]~^ ERROR `Output` references lifetime `'a`
|
||||
//[clause]~| WARNING previously accepted
|
||||
}
|
||||
|
||||
#[cfg(clause)]
|
||||
fn clause2<T>() where T: for<'a> Fn() -> <() as Foo<'a>>::Item {
|
||||
//[clause]~^ ERROR `Output` references lifetime `'a`
|
||||
//[clause]~| WARNING previously accepted
|
||||
}
|
||||
|
||||
#[rustc_error]
|
||||
|
@ -13,7 +13,6 @@
|
||||
#![allow(dead_code)]
|
||||
#![feature(rustc_attrs)]
|
||||
#![feature(unboxed_closures)]
|
||||
#![deny(hr_lifetime_in_assoc_type)]
|
||||
|
||||
trait Foo {
|
||||
type Item;
|
||||
@ -22,49 +21,41 @@ trait Foo {
|
||||
#[cfg(angle)]
|
||||
fn angle<T: for<'a> Foo<Item=&'a i32>>() {
|
||||
//[angle]~^ ERROR binding for associated type `Item` references lifetime `'a`
|
||||
//[angle]~| WARNING previously accepted
|
||||
}
|
||||
|
||||
#[cfg(angle)]
|
||||
fn angle1<T>() where T: for<'a> Foo<Item=&'a i32> {
|
||||
//[angle]~^ ERROR binding for associated type `Item` references lifetime `'a`
|
||||
//[angle]~| WARNING previously accepted
|
||||
}
|
||||
|
||||
#[cfg(angle)]
|
||||
fn angle2<T>() where for<'a> T: Foo<Item=&'a i32> {
|
||||
//[angle]~^ ERROR binding for associated type `Item` references lifetime `'a`
|
||||
//[angle]~| WARNING previously accepted
|
||||
}
|
||||
|
||||
#[cfg(angle)]
|
||||
fn angle3(_: &for<'a> Foo<Item=&'a i32>) {
|
||||
//[angle]~^ ERROR binding for associated type `Item` references lifetime `'a`
|
||||
//[angle]~| WARNING previously accepted
|
||||
}
|
||||
|
||||
#[cfg(paren)]
|
||||
fn paren<T: for<'a> Fn() -> &'a i32>() {
|
||||
//[paren]~^ ERROR binding for associated type `Output` references lifetime `'a`
|
||||
//[paren]~| WARNING previously accepted
|
||||
}
|
||||
|
||||
#[cfg(paren)]
|
||||
fn paren1<T>() where T: for<'a> Fn() -> &'a i32 {
|
||||
//[paren]~^ ERROR binding for associated type `Output` references lifetime `'a`
|
||||
//[paren]~| WARNING previously accepted
|
||||
}
|
||||
|
||||
#[cfg(paren)]
|
||||
fn paren2<T>() where for<'a> T: Fn() -> &'a i32 {
|
||||
//[paren]~^ ERROR binding for associated type `Output` references lifetime `'a`
|
||||
//[paren]~| WARNING previously accepted
|
||||
}
|
||||
|
||||
#[cfg(paren)]
|
||||
fn paren3(_: &for<'a> Fn() -> &'a i32) {
|
||||
//[paren]~^ ERROR binding for associated type `Output` references lifetime `'a`
|
||||
//[paren]~| WARNING previously accepted
|
||||
}
|
||||
|
||||
#[cfg(elision)]
|
||||
|
@ -22,27 +22,23 @@ trait Foo {
|
||||
#[cfg(sig)]
|
||||
fn sig1(_: for<'a> fn() -> &'a i32) {
|
||||
//[sig]~^ ERROR return type references lifetime `'a`
|
||||
//[sig]~| WARNING previously accepted
|
||||
}
|
||||
|
||||
#[cfg(sig)]
|
||||
fn sig2(_: for<'a, 'b> fn(&'b i32) -> &'a i32) {
|
||||
//[sig]~^ ERROR return type references lifetime `'a`
|
||||
//[sig]~| WARNING previously accepted
|
||||
}
|
||||
|
||||
#[cfg(local)]
|
||||
fn local1() {
|
||||
let _: for<'a> fn() -> &'a i32 = loop { };
|
||||
//[local]~^ ERROR return type references lifetime `'a`
|
||||
//[local]~| WARNING previously accepted
|
||||
}
|
||||
|
||||
#[cfg(structure)]
|
||||
struct Struct1 {
|
||||
x: for<'a> fn() -> &'a i32
|
||||
//[structure]~^ ERROR return type references lifetime `'a`
|
||||
//[structure]~| WARNING previously accepted
|
||||
}
|
||||
|
||||
#[cfg(elision)]
|
||||
|
@ -46,7 +46,10 @@ fn baz(x: &S) -> &S {
|
||||
|
||||
fn supply_F() {
|
||||
want_F(foo);
|
||||
want_F(bar);
|
||||
|
||||
// FIXME(#33684) -- this should be a subtype, but current alg. rejects it incorrectly
|
||||
want_F(bar); //~ ERROR E0308
|
||||
|
||||
want_F(baz);
|
||||
}
|
||||
|
||||
|
57
src/test/ui/regions-fn-subtyping-return-static.rs
Normal file
57
src/test/ui/regions-fn-subtyping-return-static.rs
Normal file
@ -0,0 +1,57 @@
|
||||
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// In this fn, the type `F` is a function that takes a reference to a
|
||||
// struct and returns another reference with the same lifetime.
|
||||
//
|
||||
// Meanwhile, the bare fn `foo` takes a reference to a struct with
|
||||
// *ANY* lifetime and returns a reference with the 'static lifetime.
|
||||
// This can safely be considered to be an instance of `F` because all
|
||||
// lifetimes are sublifetimes of 'static.
|
||||
|
||||
#![allow(dead_code)]
|
||||
#![allow(unused_variables)]
|
||||
|
||||
struct S;
|
||||
|
||||
// Given 'cx, return 'cx
|
||||
type F = for<'cx> fn(&'cx S) -> &'cx S;
|
||||
fn want_F(f: F) { }
|
||||
|
||||
// Given anything, return 'static
|
||||
type G = for<'cx> fn(&'cx S) -> &'static S;
|
||||
fn want_G(f: G) { }
|
||||
|
||||
// Should meet both.
|
||||
fn foo(x: &S) -> &'static S {
|
||||
panic!()
|
||||
}
|
||||
|
||||
// Should meet both.
|
||||
fn bar<'a,'b>(x: &'a S) -> &'b S {
|
||||
panic!()
|
||||
}
|
||||
|
||||
// Meets F, but not G.
|
||||
fn baz(x: &S) -> &S {
|
||||
panic!()
|
||||
}
|
||||
|
||||
fn supply_F() {
|
||||
want_F(foo);
|
||||
|
||||
// FIXME(#33684) -- this should be a subtype, but current alg. rejects it incorrectly
|
||||
want_F(bar); //~ ERROR E0308
|
||||
|
||||
want_F(baz);
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
}
|
13
src/test/ui/regions-fn-subtyping-return-static.stderr
Normal file
13
src/test/ui/regions-fn-subtyping-return-static.stderr
Normal file
@ -0,0 +1,13 @@
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/regions-fn-subtyping-return-static.rs:51:12
|
||||
|
|
||||
51 | want_F(bar); //~ ERROR E0308
|
||||
| ^^^ expected concrete lifetime, found bound lifetime parameter 'cx
|
||||
|
|
||||
= note: expected type `fn(&'cx S) -> &'cx S`
|
||||
found type `fn(&'a S) -> &S {bar::<'_>}`
|
||||
= note: lifetime parameter `'b` declared on fn `bar` appears only in the return type, but here is required to be higher-ranked, which means that `'b` must appear in both argument and return types
|
||||
= note: this error is the result of a recent bug fix; for more information, see issue #33685 <https://github.com/rust-lang/rust/issues/33685>
|
||||
|
||||
error: aborting due to previous error
|
||||
|
Loading…
Reference in New Issue
Block a user