From b26120db11d36fbf85742149221e474c885d2387 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 6 Jan 2017 14:35:23 -0500 Subject: [PATCH] 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 full explanation. --- src/librustc/infer/error_reporting.rs | 60 +++++++++------- src/librustc/infer/higher_ranked/mod.rs | 48 +------------ src/librustc/infer/mod.rs | 6 +- src/librustc/middle/resolve_lifetime.rs | 49 +++++++------ src/librustc/ty/mod.rs | 4 +- src/librustc/ty/sty.rs | 25 +++---- src/librustc/util/ppaux.rs | 13 ++-- src/librustc_typeck/astconv.rs | 57 ++++++---------- src/librustc_typeck/check/mod.rs | 2 +- src/librustc_typeck/check/writeback.rs | 2 +- src/librustc_typeck/collect.rs | 6 +- src/librustc_typeck/diagnostics.rs | 68 +++++++++++++++++++ src/librustdoc/clean/mod.rs | 2 +- .../cache/wasm-issue-32330.rs => E0582.rs} | 14 ++-- .../bound-lifetime-constrained.rs | 16 ++--- .../bound-lifetime-in-binding-only.rs | 9 --- .../bound-lifetime-in-return-only.rs | 4 -- .../regions-fn-subtyping-return-static.rs | 5 +- .../ui/regions-fn-subtyping-return-static.rs | 57 ++++++++++++++++ .../regions-fn-subtyping-return-static.stderr | 13 ++++ 20 files changed, 270 insertions(+), 190 deletions(-) rename src/test/compile-fail/{associated-types/cache/wasm-issue-32330.rs => E0582.rs} (79%) create mode 100644 src/test/ui/regions-fn-subtyping-return-static.rs create mode 100644 src/test/ui/regions-fn-subtyping-return-static.stderr diff --git a/src/librustc/infer/error_reporting.rs b/src/librustc/infer/error_reporting.rs index 2489a6a6c7a..939f214407e 100644 --- a/src/librustc/infer/error_reporting.rs +++ b/src/librustc/infer/error_reporting.rs @@ -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 \ + ")); + } + _ => { } + } + } + _ => { } + } + } + 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) } diff --git a/src/librustc/infer/higher_ranked/mod.rs b/src/librustc/infer/higher_ranked/mod.rs index 08e522f5fd6..e919f025409 100644 --- a/src/librustc/infer/higher_ranked/mod.rs +++ b/src/librustc/infer/higher_ranked/mod.rs @@ -622,7 +622,7 @@ pub fn skolemize_late_bound_regions(&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(()) } diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs index ef93a10808b..c3a6a62764d 100644 --- a/src/librustc/infer/mod.rs +++ b/src/librustc/infer/mod.rs @@ -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), // 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 diff --git a/src/librustc/middle/resolve_lifetime.rs b/src/librustc/middle/resolve_lifetime.rs index 88ef2c69a04..158b406deb8 100644 --- a/src/librustc/middle/resolve_lifetime.rs +++ b/src/librustc/middle/resolve_lifetime.rs @@ -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, - // 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, + // 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, // 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(&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; diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index c9ae3b3df02..4d9514b1473 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -606,6 +606,7 @@ pub struct RegionParameterDef { pub name: Name, pub def_id: DefId, pub index: u32, + pub issue_32330: Option, /// `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) } } diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs index 9bba4c6e37a..4ce1d7a9013 100644 --- a/src/librustc/ty/sty.rs +++ b/src/librustc/ty/sty.rs @@ -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 diff --git a/src/librustc/util/ppaux.rs b/src/librustc/util/ppaux.rs index 5d6ee1a277a..a45c43235eb 100644 --- a/src/librustc/util/ppaux.rs +++ b/src/librustc/util/ppaux.rs @@ -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), } diff --git a/src/librustc_typeck/astconv.rs b/src/librustc_typeck/astconv.rs index a3373f6da28..fb6d28448a0 100644 --- a/src/librustc_typeck/astconv.rs +++ b/src/librustc_typeck/astconv.rs @@ -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, 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) } diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index c4a366c2f17..8329d3eeed9 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -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)) diff --git a/src/librustc_typeck/check/writeback.rs b/src/librustc_typeck/check/writeback.rs index 9df0542f51f..28156dd616b 100644 --- a/src/librustc_typeck/check/writeback.rs +++ b/src/librustc_typeck/check/writeback.rs @@ -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, diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs index 266975994ec..1981e7c3a3d 100644 --- a/src/librustc_typeck/collect.rs +++ b/src/librustc_typeck/collect.rs @@ -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::>(); @@ -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() } diff --git a/src/librustc_typeck/diagnostics.rs b/src/librustc_typeck/diagnostics.rs index c41d40b41e4..5bfc3a934af 100644 --- a/src/librustc_typeck/diagnostics.rs +++ b/src/librustc_typeck/diagnostics.rs @@ -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(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(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! { diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index dc2aa1f138d..291fc8dfa96 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -810,7 +810,7 @@ impl Clean> for ty::Region { fn clean(&self, cx: &DocContext) -> Option { 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(..) | diff --git a/src/test/compile-fail/associated-types/cache/wasm-issue-32330.rs b/src/test/compile-fail/E0582.rs similarity index 79% rename from src/test/compile-fail/associated-types/cache/wasm-issue-32330.rs rename to src/test/compile-fail/E0582.rs index 6ba09acc0e7..5e6f65a18f4 100644 --- a/src/test/compile-fail/associated-types/cache/wasm-issue-32330.rs +++ b/src/test/compile-fail/E0582.rs @@ -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(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(t: F) + // No type can satisfy this requirement, since `'a` does not + // appear in any of the input types: + where F: for<'a> Iterator + //~^ ERROR E0582 { } diff --git a/src/test/compile-fail/associated-types/bound-lifetime-constrained.rs b/src/test/compile-fail/associated-types/bound-lifetime-constrained.rs index de6ce798d63..7d04372088b 100644 --- a/src/test/compile-fail/associated-types/bound-lifetime-constrained.rs +++ b/src/test/compile-fail/associated-types/bound-lifetime-constrained.rs @@ -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 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 Fn() -> <() as Foo<'a>>::Item>) { - //[object]~^ ERROR `Output` references lifetime `'a` - //[object]~| WARNING previously accepted + //[object]~^ ERROR E0582 } #[cfg(clause)] fn clause1() 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() where T: for<'a> Fn() -> <() as Foo<'a>>::Item { //[clause]~^ ERROR `Output` references lifetime `'a` - //[clause]~| WARNING previously accepted } #[rustc_error] diff --git a/src/test/compile-fail/associated-types/bound-lifetime-in-binding-only.rs b/src/test/compile-fail/associated-types/bound-lifetime-in-binding-only.rs index 020c9e5e1db..7cb0623315e 100644 --- a/src/test/compile-fail/associated-types/bound-lifetime-in-binding-only.rs +++ b/src/test/compile-fail/associated-types/bound-lifetime-in-binding-only.rs @@ -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 Foo>() { //[angle]~^ ERROR binding for associated type `Item` references lifetime `'a` - //[angle]~| WARNING previously accepted } #[cfg(angle)] fn angle1() where T: for<'a> Foo { //[angle]~^ ERROR binding for associated type `Item` references lifetime `'a` - //[angle]~| WARNING previously accepted } #[cfg(angle)] fn angle2() where for<'a> T: Foo { //[angle]~^ ERROR binding for associated type `Item` references lifetime `'a` - //[angle]~| WARNING previously accepted } #[cfg(angle)] fn angle3(_: &for<'a> Foo) { //[angle]~^ ERROR binding for associated type `Item` references lifetime `'a` - //[angle]~| WARNING previously accepted } #[cfg(paren)] fn paren Fn() -> &'a i32>() { //[paren]~^ ERROR binding for associated type `Output` references lifetime `'a` - //[paren]~| WARNING previously accepted } #[cfg(paren)] fn paren1() 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() 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)] diff --git a/src/test/compile-fail/associated-types/bound-lifetime-in-return-only.rs b/src/test/compile-fail/associated-types/bound-lifetime-in-return-only.rs index 0b4a9bf58a6..7c1fbfa53d9 100644 --- a/src/test/compile-fail/associated-types/bound-lifetime-in-return-only.rs +++ b/src/test/compile-fail/associated-types/bound-lifetime-in-return-only.rs @@ -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)] diff --git a/src/test/compile-fail/regions-fn-subtyping-return-static.rs b/src/test/compile-fail/regions-fn-subtyping-return-static.rs index c0116b21166..ac7dd022c7c 100644 --- a/src/test/compile-fail/regions-fn-subtyping-return-static.rs +++ b/src/test/compile-fail/regions-fn-subtyping-return-static.rs @@ -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); } diff --git a/src/test/ui/regions-fn-subtyping-return-static.rs b/src/test/ui/regions-fn-subtyping-return-static.rs new file mode 100644 index 00000000000..90985111867 --- /dev/null +++ b/src/test/ui/regions-fn-subtyping-return-static.rs @@ -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 or the MIT license +// , 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() { +} diff --git a/src/test/ui/regions-fn-subtyping-return-static.stderr b/src/test/ui/regions-fn-subtyping-return-static.stderr new file mode 100644 index 00000000000..0c7b44af949 --- /dev/null +++ b/src/test/ui/regions-fn-subtyping-return-static.stderr @@ -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 + +error: aborting due to previous error +