diff --git a/src/librustc/ich/impls_ty.rs b/src/librustc/ich/impls_ty.rs index 5f51579945e3..6a22147fedb5 100644 --- a/src/librustc/ich/impls_ty.rs +++ b/src/librustc/ich/impls_ty.rs @@ -374,6 +374,7 @@ for ty::RegionParameterDef { name, def_id, index, + issue_32330: _, pure_wrt_drop } = *self; diff --git a/src/librustc/infer/error_reporting/mod.rs b/src/librustc/infer/error_reporting/mod.rs index edf9ca89b339..68eb4639bce4 100644 --- a/src/librustc/infer/error_reporting/mod.rs +++ b/src/librustc/infer/error_reporting/mod.rs @@ -66,7 +66,8 @@ use hir::map as hir_map; use hir::def_id::DefId; use middle::region; use traits::{ObligationCause, ObligationCauseCode}; -use ty::{self, Region, TyCtxt, TypeFoldable}; +use ty::{self, TyCtxt, TypeFoldable}; +use ty::{Region, Issue32330}; use ty::error::TypeError; use syntax::ast::DUMMY_NODE_ID; use syntax_pos::{Pos, Span}; @@ -714,6 +715,35 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { 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(_, _, Some(box Issue32330 { + fn_def_id, region_name + })) | + TypeError::RegionsOverlyPolymorphic(_, _, Some(box 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>) @@ -733,6 +763,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { } }; self.note_type_err(&mut diag, &trace.cause, None, Some(trace.values), terr); + self.note_issue_32330(&mut diag, terr); diag } @@ -905,7 +936,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { format!(" for lifetime parameter {}in trait containing associated type `{}`", br_string(br), self.tcx.associated_item(def_id).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 0d02420457e6..760d443f0e56 100644 --- a/src/librustc/infer/higher_ranked/mod.rs +++ b/src/librustc/infer/higher_ranked/mod.rs @@ -13,7 +13,9 @@ use super::{CombinedSnapshot, InferCtxt, + LateBoundRegion, HigherRankedType, + RegionVariableOrigin, SubregionOrigin, SkolemizationMap}; use super::combine::CombineFields; @@ -27,6 +29,15 @@ use util::nodemap::{FxHashMap, FxHashSet}; pub struct HrMatchResult { pub value: U, + + /// Normally, when we do a higher-ranked match operation, we + /// expect all higher-ranked regions to be constrained as part of + /// the match operation. However, in the transition period for + /// #32330, it can happen that we sometimes have unconstrained + /// regions that get instantiated with fresh variables. In that + /// case, we collect the set of unconstrained bound regions here + /// and replace them with fresh variables. + pub unconstrained_regions: Vec, } impl<'a, 'gcx, 'tcx> CombineFields<'a, 'gcx, 'tcx> { @@ -97,6 +108,7 @@ impl<'a, 'gcx, 'tcx> CombineFields<'a, 'gcx, 'tcx> { /// that do not appear in `T`. If that happens, those regions are /// unconstrained, and this routine replaces them with `'static`. pub fn higher_ranked_match(&mut self, + span: Span, a_pair: &Binder<(T, U)>, b_match: &T, a_is_expected: bool) @@ -146,16 +158,28 @@ impl<'a, 'gcx, 'tcx> CombineFields<'a, 'gcx, 'tcx> { // be any region from the sets above, except for other members of // `skol_map`. There should always be a representative if things // are properly well-formed. + let mut unconstrained_regions = vec![]; let skol_representatives: FxHashMap<_, _> = skol_resolution_map .iter() - .map(|(&skol, &(_, ref regions))| { + .map(|(&skol, &(br, ref regions))| { let representative = regions.iter() .filter(|&&r| !skol_resolution_map.contains_key(r)) .cloned() .next() - .expect("no representative region"); + .unwrap_or_else(|| { // [1] + unconstrained_regions.push(br); + self.infcx.next_region_var( + LateBoundRegion(span, br, HigherRankedType)) + }); + + // [1] There should always be a representative, + // unless the higher-ranked region did not appear + // in the values being matched. We should reject + // as ill-formed cases that can lead to this, but + // right now we sometimes issue warnings (see + // #32330). (skol, representative) }) @@ -192,7 +216,10 @@ impl<'a, 'gcx, 'tcx> CombineFields<'a, 'gcx, 'tcx> { // We are now done with these skolemized variables. self.infcx.pop_skolemized(skol_map, snapshot); - Ok(HrMatchResult { value: a_value }) + Ok(HrMatchResult { + value: a_value, + unconstrained_regions, + }) }); } @@ -630,13 +657,28 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { skol_br, tainted_region); - return Err(if overly_polymorphic { + let issue_32330 = if let &ty::ReVar(vid) = tainted_region { + match self.region_vars.var_origin(vid) { + RegionVariableOrigin::EarlyBoundRegion(_, _, issue_32330) => { + issue_32330.map(Box::new) + } + _ => None + } + } else { + None + }; + + if overly_polymorphic { debug!("Overly polymorphic!"); - TypeError::RegionsOverlyPolymorphic(skol_br, tainted_region) + return Err(TypeError::RegionsOverlyPolymorphic(skol_br, + tainted_region, + issue_32330)); } else { debug!("Not as polymorphic!"); - TypeError::RegionsInsufficientlyPolymorphic(skol_br, tainted_region) - }) + return Err(TypeError::RegionsInsufficientlyPolymorphic(skol_br, + tainted_region, + issue_32330)); + } } } diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs index 56b5f6c45eb1..453a8777ad5a 100644 --- a/src/librustc/infer/mod.rs +++ b/src/librustc/infer/mod.rs @@ -299,7 +299,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 @@ -989,7 +989,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { span: Span, def: &ty::RegionParameterDef) -> ty::Region<'tcx> { - 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 @@ -1290,13 +1290,14 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { -> InferResult<'tcx, HrMatchResult>> { let match_pair = match_a.map_bound(|p| (p.projection_ty.trait_ref(self.tcx), p.ty)); + let span = cause.span; let trace = TypeTrace { cause, values: TraitRefs(ExpectedFound::new(true, match_pair.skip_binder().0, match_b)) }; let mut combine = self.combine_fields(trace, param_env); - let result = combine.higher_ranked_match(&match_pair, &match_b, true)?; + let result = combine.higher_ranked_match(span, &match_pair, &match_b, true)?; Ok(InferOk { value: result, obligations: combine.obligations }) } diff --git a/src/librustc/middle/resolve_lifetime.rs b/src/librustc/middle/resolve_lifetime.rs index a8e98e53db39..d346ac9dd9b0 100644 --- a/src/librustc/middle/resolve_lifetime.rs +++ b/src/librustc/middle/resolve_lifetime.rs @@ -153,6 +153,10 @@ pub struct NamedRegionMap { // (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. pub object_lifetime_defaults: NodeMap>, @@ -257,6 +261,7 @@ pub fn krate(sess: &Session, let mut map = NamedRegionMap { defs: NodeMap(), late_bound: NodeSet(), + issue_32330: NodeMap(), object_lifetime_defaults: compute_object_lifetime_defaults(sess, hir_map), }; sess.track_errors(|| { @@ -298,7 +303,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { fn visit_item(&mut self, item: &'tcx hir::Item) { match item.node { hir::ItemFn(ref decl, _, _, _, ref generics, _) => { - self.visit_early_late(None, decl, generics, |this| { + self.visit_early_late(item.id, None, decl, generics, |this| { intravisit::walk_item(this, item); }); } @@ -350,7 +355,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { fn visit_foreign_item(&mut self, item: &'tcx hir::ForeignItem) { match item.node { hir::ForeignItemFn(ref decl, _, ref generics) => { - self.visit_early_late(None, decl, generics, |this| { + self.visit_early_late(item.id, None, decl, generics, |this| { intravisit::walk_foreign_item(this, item); }) } @@ -401,6 +406,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { fn visit_trait_item(&mut self, trait_item: &'tcx hir::TraitItem) { if let hir::TraitItemKind::Method(ref sig, _) = trait_item.node { self.visit_early_late( + trait_item.id, Some(self.hir_map.get_parent(trait_item.id)), &sig.decl, &sig.generics, |this| intravisit::walk_trait_item(this, trait_item)) @@ -412,6 +418,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { fn visit_impl_item(&mut self, impl_item: &'tcx hir::ImplItem) { if let hir::ImplItemKind::Method(ref sig, _) = impl_item.node { self.visit_early_late( + impl_item.id, Some(self.hir_map.get_parent(impl_item.id)), &sig.decl, &sig.generics, |this| intravisit::walk_impl_item(this, impl_item)) @@ -804,13 +811,18 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { /// bound lifetimes are resolved by name and associated with a binder id (`binder_id`), so the /// ordering is not important there. fn visit_early_late(&mut self, + fn_id: ast::NodeId, parent_id: Option, decl: &'tcx hir::FnDecl, generics: &'tcx hir::Generics, walk: F) where F: for<'b, 'c> FnOnce(&'b mut LifetimeContext<'c, 'tcx>), { - insert_late_bound_lifetimes(self.map, decl, generics); + let fn_def_id = self.hir_map.local_def_id(fn_id); + insert_late_bound_lifetimes(self.map, + fn_def_id, + decl, + generics); // Find the start of nested early scopes, e.g. in methods. let mut index = 0; @@ -1522,6 +1534,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { /// not amongst the inputs to a projection. In other words, `<&'a /// T as Trait<''b>>::Foo` does not constrain `'a` or `'b`. fn insert_late_bound_lifetimes(map: &mut NamedRegionMap, + fn_def_id: DefId, decl: &hir::FnDecl, generics: &hir::Generics) { debug!("insert_late_bound_lifetimes(decl={:?}, generics={:?})", decl, generics); @@ -1579,9 +1592,22 @@ fn insert_late_bound_lifetimes(map: &mut NamedRegionMap, // any `impl Trait` in the return type? early-bound. if appears_in_output.impl_trait { continue; } - // does not appear in the inputs, but appears in the return type? early-bound. - if !constrained_by_input.regions.contains(&name) && - appears_in_output.regions.contains(&name) { + // does not appear in the inputs, but appears in the return + // type? eventually this will be early-bound, but for now we + // 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); + 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, + region_name: name, + }); continue; } diff --git a/src/librustc/traits/project.rs b/src/librustc/traits/project.rs index f5055b9765e7..3fb615e29cc1 100644 --- a/src/librustc/traits/project.rs +++ b/src/librustc/traits/project.rs @@ -505,7 +505,9 @@ fn opt_normalize_projection_type<'a, 'b, 'gcx, 'tcx>( let obligation = Obligation::with_depth(cause.clone(), depth, param_env, projection_ty); match project_type(selcx, &obligation) { - Ok(ProjectedTy::Progress(Progress { ty: projected_ty, mut obligations })) => { + Ok(ProjectedTy::Progress(Progress { ty: projected_ty, + mut obligations, + cacheable })) => { // if projection succeeded, then what we get out of this // is also non-normalized (consider: it was derived from // an impl, where-clause etc) and hence we must @@ -514,10 +516,12 @@ fn opt_normalize_projection_type<'a, 'b, 'gcx, 'tcx>( debug!("opt_normalize_projection_type: \ projected_ty={:?} \ depth={} \ - obligations={:?}", + obligations={:?} \ + cacheable={:?}", projected_ty, depth, - obligations); + obligations, + cacheable); let result = if projected_ty.has_projection_types() { let mut normalizer = AssociatedTypeNormalizer::new(selcx, @@ -544,7 +548,7 @@ fn opt_normalize_projection_type<'a, 'b, 'gcx, 'tcx>( }; let cache_value = prune_cache_value_obligations(infcx, &result); - infcx.projection_cache.borrow_mut().insert_ty(cache_key, cache_value); + infcx.projection_cache.borrow_mut().insert_ty(cache_key, cache_value, cacheable); Some(result) } @@ -556,7 +560,7 @@ fn opt_normalize_projection_type<'a, 'b, 'gcx, 'tcx>( value: projected_ty, obligations: vec![] }; - infcx.projection_cache.borrow_mut().insert_ty(cache_key, result.clone()); + infcx.projection_cache.borrow_mut().insert_ty(cache_key, result.clone(), true); Some(result) } Err(ProjectionTyError::TooManyCandidates) => { @@ -705,6 +709,7 @@ enum ProjectedTy<'tcx> { struct Progress<'tcx> { ty: Ty<'tcx>, obligations: Vec>, + cacheable: bool, } impl<'tcx> Progress<'tcx> { @@ -712,6 +717,7 @@ impl<'tcx> Progress<'tcx> { Progress { ty: tcx.types.err, obligations: vec![], + cacheable: true } } @@ -1325,6 +1331,7 @@ fn confirm_param_env_candidate<'cx, 'gcx, 'tcx>( Progress { ty: ty_match.value, obligations, + cacheable: ty_match.unconstrained_regions.is_empty(), } } Err(e) => { @@ -1368,6 +1375,7 @@ fn confirm_impl_candidate<'cx, 'gcx, 'tcx>( Progress { ty: ty.subst(tcx, substs), obligations: nested, + cacheable: true } } @@ -1529,11 +1537,20 @@ impl<'tcx> ProjectionCache<'tcx> { Ok(()) } - /// Indicates that `key` was normalized to `value`. - fn insert_ty(&mut self, key: ProjectionCacheKey<'tcx>, value: NormalizedTy<'tcx>) { + /// Indicates that `key` was normalized to `value`. If `cacheable` is false, + /// then this result is sadly not cacheable (this only occurs in weird + /// buggy cases, like #38714). + fn insert_ty(&mut self, + key: ProjectionCacheKey<'tcx>, + value: NormalizedTy<'tcx>, + cacheable: bool) { debug!("ProjectionCacheEntry::insert_ty: adding cache entry: key={:?}, value={:?}", key, value); - let fresh_key = self.map.insert(key, ProjectionCacheEntry::NormalizedTy(value)); + let fresh_key = if cacheable { + self.map.insert(key, ProjectionCacheEntry::NormalizedTy(value)) + } else { + !self.map.remove(key) + }; assert!(!fresh_key, "never started projecting `{:?}`", key); } diff --git a/src/librustc/ty/error.rs b/src/librustc/ty/error.rs index 86a4f6691896..3442cf0ef698 100644 --- a/src/librustc/ty/error.rs +++ b/src/librustc/ty/error.rs @@ -39,8 +39,8 @@ pub enum TypeError<'tcx> { RegionsDoesNotOutlive(Region<'tcx>, Region<'tcx>), RegionsNotSame(Region<'tcx>, Region<'tcx>), RegionsNoOverlap(Region<'tcx>, Region<'tcx>), - RegionsInsufficientlyPolymorphic(BoundRegion, Region<'tcx>), - RegionsOverlyPolymorphic(BoundRegion, Region<'tcx>), + RegionsInsufficientlyPolymorphic(BoundRegion, Region<'tcx>, Option>), + RegionsOverlyPolymorphic(BoundRegion, Region<'tcx>, Option>), Sorts(ExpectedFound>), IntMismatch(ExpectedFound), FloatMismatch(ExpectedFound), @@ -116,13 +116,13 @@ impl<'tcx> fmt::Display for TypeError<'tcx> { RegionsNoOverlap(..) => { write!(f, "lifetimes do not intersect") } - RegionsInsufficientlyPolymorphic(br, _) => { + RegionsInsufficientlyPolymorphic(br, _, _) => { write!(f, "expected bound lifetime parameter{}{}, found concrete lifetime", if br.is_named() { " " } else { "" }, br) } - RegionsOverlyPolymorphic(br, _) => { + RegionsOverlyPolymorphic(br, _, _) => { write!(f, "expected concrete lifetime, found bound lifetime parameter{}{}", if br.is_named() { " " } else { "" }, @@ -257,15 +257,15 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { self.note_and_explain_region(db, "...does not overlap ", region2, ""); } - RegionsInsufficientlyPolymorphic(_, conc_region) => { + RegionsInsufficientlyPolymorphic(_, conc_region, _) => { self.note_and_explain_region(db, "concrete lifetime that was found is ", conc_region, ""); } - RegionsOverlyPolymorphic(_, &ty::ReVar(_)) => { + RegionsOverlyPolymorphic(_, &ty::ReVar(_), _) => { // don't bother to print out the message below for // inference variables, it's not very illuminating. } - RegionsOverlyPolymorphic(_, conc_region) => { + RegionsOverlyPolymorphic(_, conc_region, _) => { self.note_and_explain_region(db, "expected concrete lifetime is ", conc_region, ""); } diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index c7b51af34133..f0a194752c4f 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -67,6 +67,7 @@ pub use self::sty::{ExistentialTraitRef, PolyExistentialTraitRef}; pub use self::sty::{ExistentialProjection, PolyExistentialProjection}; pub use self::sty::{BoundRegion, EarlyBoundRegion, FreeRegion, Region}; pub use self::sty::RegionKind; +pub use self::sty::Issue32330; pub use self::sty::{TyVid, IntVid, FloatVid, RegionVid, SkolemizedRegionVid}; pub use self::sty::BoundRegion::*; pub use self::sty::InferTy::*; @@ -681,6 +682,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` diff --git a/src/librustc/ty/structural_impls.rs b/src/librustc/ty/structural_impls.rs index e41eb079b378..34be80164da6 100644 --- a/src/librustc/ty/structural_impls.rs +++ b/src/librustc/ty/structural_impls.rs @@ -346,11 +346,13 @@ impl<'a, 'tcx> Lift<'tcx> for ty::error::TypeError<'a> { RegionsNoOverlap(a, b) => { return tcx.lift(&(a, b)).map(|(a, b)| RegionsNoOverlap(a, b)) } - RegionsInsufficientlyPolymorphic(a, b) => { - return tcx.lift(&b).map(|b| RegionsInsufficientlyPolymorphic(a, b)) + RegionsInsufficientlyPolymorphic(a, b, ref c) => { + let c = c.clone(); + return tcx.lift(&b).map(|b| RegionsInsufficientlyPolymorphic(a, b, c)) } - RegionsOverlyPolymorphic(a, b) => { - return tcx.lift(&b).map(|b| RegionsOverlyPolymorphic(a, b)) + RegionsOverlyPolymorphic(a, b, ref c) => { + let c = c.clone(); + return tcx.lift(&b).map(|b| RegionsOverlyPolymorphic(a, b, c)) } IntMismatch(x) => IntMismatch(x), FloatMismatch(x) => FloatMismatch(x), @@ -1002,11 +1004,13 @@ impl<'tcx> TypeFoldable<'tcx> for ty::error::TypeError<'tcx> { RegionsNoOverlap(a, b) => { RegionsNoOverlap(a.fold_with(folder), b.fold_with(folder)) }, - RegionsInsufficientlyPolymorphic(a, b) => { - RegionsInsufficientlyPolymorphic(a, b.fold_with(folder)) + RegionsInsufficientlyPolymorphic(a, b, ref c) => { + let c = c.clone(); + RegionsInsufficientlyPolymorphic(a, b.fold_with(folder), c) }, - RegionsOverlyPolymorphic(a, b) => { - RegionsOverlyPolymorphic(a, b.fold_with(folder)) + RegionsOverlyPolymorphic(a, b, ref c) => { + let c = c.clone(); + RegionsOverlyPolymorphic(a, b.fold_with(folder), c) }, IntMismatch(x) => IntMismatch(x), FloatMismatch(x) => FloatMismatch(x), @@ -1032,8 +1036,8 @@ impl<'tcx> TypeFoldable<'tcx> for ty::error::TypeError<'tcx> { RegionsNoOverlap(a, b) => { a.visit_with(visitor) || b.visit_with(visitor) }, - RegionsInsufficientlyPolymorphic(_, b) | - RegionsOverlyPolymorphic(_, b) => { + RegionsInsufficientlyPolymorphic(_, b, _) | + RegionsOverlyPolymorphic(_, b, _) => { b.visit_with(visitor) }, Sorts(x) => x.visit_with(visitor), diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs index 8d6b7b7ac9fd..2e4be870e915 100644 --- a/src/librustc/ty/sty.rs +++ b/src/librustc/ty/sty.rs @@ -77,6 +77,20 @@ impl BoundRegion { } } +/// 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 struct Issue32330 { + /// fn where is region declared + pub fn_def_id: DefId, + + /// 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 /// AST structure in libsyntax/ast.rs as well. #[derive(Clone, PartialEq, Eq, Hash, Debug, RustcEncodable, RustcDecodable)] diff --git a/src/librustc_typeck/astconv.rs b/src/librustc_typeck/astconv.rs index ee1e6bd950fa..b55d762bf0c8 100644 --- a/src/librustc_typeck/astconv.rs +++ b/src/librustc_typeck/astconv.rs @@ -1008,7 +1008,46 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { } hir::TyBareFn(ref bf) => { require_c_abi_if_variadic(tcx, &bf.decl, bf.abi, ast_ty.span); - tcx.mk_fn_ptr(self.ty_of_fn(bf.unsafety, bf.abi, &bf.decl)) + let bare_fn_ty = self.ty_of_fn(bf.unsafety, bf.abi, &bf.decl); + + // Find any late-bound regions declared in return type that do + // not appear in the arguments. These are not wellformed. + // + // Example: + // + // for<'a> fn() -> &'a str <-- 'a is bad + // for<'a> fn(&'a String) -> &'a str <-- 'a is ok + // + // Note that we do this check **here** and not in + // `ty_of_bare_fn` because the latter is also used to make + // the types for fn items, and we do not want to issue a + // warning then. (Once we fix #32330, the regions we are + // checking for here would be considered early bound + // anyway.) + let inputs = bare_fn_ty.inputs(); + let late_bound_in_args = tcx.collect_constrained_late_bound_regions( + &inputs.map_bound(|i| i.to_owned())); + let output = bare_fn_ty.output(); + 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, + _ => { + span_bug!( + bf.decl.output.span(), + "anonymous bound region {:?} in return but not args", + br); + } + }; + 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) } hir::TyTraitObject(ref bounds, ref lifetime) => { self.conv_object_ty_poly_trait_ref(ast_ty.span, bounds, lifetime) @@ -1128,56 +1167,23 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { -> ty::PolyFnSig<'tcx> { debug!("ty_of_fn"); - let tcx = self.tcx(); let input_tys: Vec = decl.inputs.iter().map(|a| self.ty_of_arg(a, None)).collect(); let output_ty = match decl.output { hir::Return(ref output) => self.ast_ty_to_ty(output), - hir::DefaultReturn(..) => tcx.mk_nil(), + hir::DefaultReturn(..) => self.tcx().mk_nil(), }; debug!("ty_of_fn: output_ty={:?}", output_ty); - let bare_fn_ty = ty::Binder(tcx.mk_fn_sig( + ty::Binder(self.tcx().mk_fn_sig( input_tys.into_iter(), output_ty, decl.variadic, unsafety, abi - )); - - // Find any late-bound regions declared in return type that do - // not appear in the arguments. These are not wellformed. - // - // Example: - // for<'a> fn() -> &'a str <-- 'a is bad - // for<'a> fn(&'a String) -> &'a str <-- 'a is ok - let inputs = bare_fn_ty.inputs(); - let late_bound_in_args = tcx.collect_constrained_late_bound_regions( - &inputs.map_bound(|i| i.to_owned())); - let output = bare_fn_ty.output(); - 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, - _ => { - span_bug!( - decl.output.span(), - "anonymous bound region {:?} in return but not args", - br); - } - }; - struct_span_err!(tcx.sess, - decl.output.span(), - E0581, - "return type references lifetime `{}`, \ - which does not appear in the fn input types", - br_name) - .emit(); - } - - bare_fn_ty + )) } pub fn ty_of_closure(&self, diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 955171203e13..516fae37b2c9 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -1623,7 +1623,7 @@ impl<'a, 'gcx, 'tcx> AstConv<'gcx, 'tcx> for FnCtxt<'a, 'gcx, 'tcx> { fn re_infer(&self, span: Span, def: Option<&ty::RegionParameterDef>) -> Option> { 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/collect.rs b/src/librustc_typeck/collect.rs index ea86c570c829..8f14e765dfb6 100644 --- a/src/librustc_typeck/collect.rs +++ b/src/librustc_typeck/collect.rs @@ -979,11 +979,13 @@ fn generics_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, let early_lifetimes = early_bound_lifetimes_from_generics(tcx, ast_generics); let regions = early_lifetimes.enumerate().map(|(i, l)| { + let issue_32330 = 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::>(); diff --git a/src/test/compile-fail/associated-types/cache/project-fn-ret-contravariant.rs b/src/test/compile-fail/associated-types/cache/project-fn-ret-contravariant.rs index 0e822aff01e8..c5557cee7cc1 100644 --- a/src/test/compile-fail/associated-types/cache/project-fn-ret-contravariant.rs +++ b/src/test/compile-fail/associated-types/cache/project-fn-ret-contravariant.rs @@ -43,19 +43,23 @@ fn baz<'a,'b>(x: &'a u32, y: &'b u32) -> (&'a u32, &'b u32) { (a, b) } -#[cfg(transmute)] // one instantiations: BAD -fn baz<'a,'b>(x: &'a u32) -> &'static u32 { - bar(foo, x) //[transmute]~ ERROR E0495 -} +// FIXME(#32330) +//#[cfg(transmute)] // one instantiations: BAD +//fn baz<'a,'b>(x: &'a u32) -> &'static u32 { +// bar(foo, x) //[transmute] ERROR E0495 +//} -#[cfg(krisskross)] // two instantiations, mixing and matching: BAD -fn transmute<'a,'b>(x: &'a u32, y: &'b u32) -> (&'a u32, &'b u32) { - let a = bar(foo, y); //[krisskross]~ ERROR E0495 - let b = bar(foo, x); //[krisskross]~ ERROR E0495 - (a, b) -} +// FIXME(#32330) +//#[cfg(krisskross)] // two instantiations, mixing and matching: BAD +//fn transmute<'a,'b>(x: &'a u32, y: &'b u32) -> (&'a u32, &'b u32) { +// let a = bar(foo, y); //[krisskross] ERROR E0495 +// let b = bar(foo, x); //[krisskross] ERROR E0495 +// (a, b) +//} #[rustc_error] fn main() { } //[ok]~^ ERROR compilation successful //[oneuse]~^^ ERROR compilation successful +//[transmute]~^^^ ERROR compilation successful +//[krisskross]~^^^^ ERROR compilation successful diff --git a/src/test/compile-fail/associated-types/cache/project-fn-ret-invariant.rs b/src/test/compile-fail/associated-types/cache/project-fn-ret-invariant.rs index 10fe612980d3..a15422e42d94 100644 --- a/src/test/compile-fail/associated-types/cache/project-fn-ret-invariant.rs +++ b/src/test/compile-fail/associated-types/cache/project-fn-ret-invariant.rs @@ -42,29 +42,35 @@ fn baz<'a,'b>(x: Type<'a>, y: Type<'b>) -> (Type<'a>, Type<'b>) { (a, b) } -#[cfg(oneuse)] // one instantiation: BAD -fn baz<'a,'b>(x: Type<'a>, y: Type<'b>) -> (Type<'a>, Type<'b>) { - let f = foo; // <-- No consistent type can be inferred for `f` here. - let a = bar(f, x); //[oneuse]~^ ERROR E0495 - let b = bar(f, y); - (a, b) -} +// FIXME(#32330) +//#[cfg(oneuse)] // one instantiation: BAD +//fn baz<'a,'b>(x: Type<'a>, y: Type<'b>) -> (Type<'a>, Type<'b>) { +// let f = foo; // <-- No consistent type can be inferred for `f` here. +// let a = bar(f, x); //[oneuse] ERROR E0495 +// let b = bar(f, y); +// (a, b) +//} -#[cfg(transmute)] // one instantiations: BAD -fn baz<'a,'b>(x: Type<'a>) -> Type<'static> { - // Cannot instantiate `foo` with any lifetime other than `'a`, - // since it is provided as input. +// FIXME(#32330) +//#[cfg(transmute)] // one instantiations: BAD +//fn baz<'a,'b>(x: Type<'a>) -> Type<'static> { +// // Cannot instantiate `foo` with any lifetime other than `'a`, +// // since it is provided as input. +// +// bar(foo, x) //[transmute] ERROR E0495 +//} - bar(foo, x) //[transmute]~ ERROR E0495 -} - -#[cfg(krisskross)] // two instantiations, mixing and matching: BAD -fn transmute<'a,'b>(x: Type<'a>, y: Type<'b>) -> (Type<'a>, Type<'b>) { - let a = bar(foo, y); //[krisskross]~ ERROR E0495 - let b = bar(foo, x); //[krisskross]~ ERROR E0495 - (a, b) -} +// FIXME(#32330) +//#[cfg(krisskross)] // two instantiations, mixing and matching: BAD +//fn transmute<'a,'b>(x: Type<'a>, y: Type<'b>) -> (Type<'a>, Type<'b>) { +// let a = bar(foo, y); //[krisskross] ERROR E0495 +// let b = bar(foo, x); //[krisskross] ERROR E0495 +// (a, b) +//} #[rustc_error] fn main() { } //[ok]~^ ERROR compilation successful +//[oneuse]~^^ ERROR compilation successful +//[transmute]~^^^ ERROR compilation successful +//[krisskross]~^^^^ ERROR compilation successful diff --git a/src/test/compile-fail/closure-expected-type/issue-38714.rs b/src/test/compile-fail/closure-expected-type/issue-38714.rs new file mode 100644 index 000000000000..cd0827663763 --- /dev/null +++ b/src/test/compile-fail/closure-expected-type/issue-38714.rs @@ -0,0 +1,26 @@ +// Copyright 2016 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. + +// Regression test for #38714. This code uses some creepy code paths +// that (as of the time of this writing) mix-and-match the bound +// regions from the expected/supplied types when deciding on a closure +// signature. Cleaning up those paths initially led to an ICE; +// reverting the relevant PR causes a proper error. + +fn foo(f: F) where F: for<'a> Fn(&'a str) -> &'a str {} +fn bar(f: F) where F: Fn(&str) -> &str {} + +fn main() { + foo(|a: &str| a); // Compiler panic + bar(|a: &str| a); // Works + + let local = |a: &str| a; + bar(local); //~ ERROR type mismatch +} diff --git a/src/test/compile-fail/hr-subtype.rs b/src/test/compile-fail/hr-subtype.rs index c88d74d53ce9..95e469ebcfd7 100644 --- a/src/test/compile-fail/hr-subtype.rs +++ b/src/test/compile-fail/hr-subtype.rs @@ -91,6 +91,9 @@ check! { free_inv_x_vs_free_inv_y: (fn(Inv<'x>), // - if we are covariant, then 'a and 'b can be set to the call-site // intersection; // - if we are contravariant, then 'a can be inferred to 'static. +// +// FIXME(#32330) this is true, but we are not currently impl'ing this +// full semantics check! { bound_a_b_vs_bound_a: (for<'a,'b> fn(&'a u32, &'b u32), for<'a> fn(&'a u32, &'a u32)) } check! { bound_co_a_b_vs_bound_co_a: (for<'a,'b> fn(Co<'a>, Co<'b>), diff --git a/src/test/ui/regions-fn-subtyping-return-static.stderr b/src/test/ui/regions-fn-subtyping-return-static.stderr index 1598a8a40d2f..0c7b44af949b 100644 --- a/src/test/ui/regions-fn-subtyping-return-static.stderr +++ b/src/test/ui/regions-fn-subtyping-return-static.stderr @@ -6,6 +6,8 @@ error[E0308]: mismatched types | = 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