diff --git a/.travis.yml b/.travis.yml index ca28aabc41fd..e6ae0af9124e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -97,7 +97,6 @@ matrix: - env: IMAGE=dist-arm-linux DEPLOY=1 - env: IMAGE=dist-armhf-linux DEPLOY=1 - env: IMAGE=dist-armv7-linux DEPLOY=1 - - env: IMAGE=dist-fuchsia DEPLOY=1 - env: IMAGE=dist-i586-gnu-i686-musl DEPLOY=1 - env: IMAGE=dist-i686-freebsd DEPLOY=1 - env: IMAGE=dist-i686-linux DEPLOY=1 diff --git a/src/bootstrap/channel.rs b/src/bootstrap/channel.rs index 0c22cc2f6c43..b79c7de34314 100644 --- a/src/bootstrap/channel.rs +++ b/src/bootstrap/channel.rs @@ -29,7 +29,7 @@ pub const CFG_RELEASE_NUM: &str = "1.21.0"; // An optional number to put after the label, e.g. '.2' -> '-beta.2' // Be sure to make this starts with a dot to conform to semver pre-release // versions (section 9) -pub const CFG_PRERELEASE_VERSION: &str = ".3"; +pub const CFG_PRERELEASE_VERSION: &str = ".4"; pub struct GitInfo { inner: Option, diff --git a/src/liblibc b/src/liblibc index 2a5b50b7f7f5..4d1b322cff63 160000 --- a/src/liblibc +++ b/src/liblibc @@ -1 +1 @@ -Subproject commit 2a5b50b7f7f539a0fd201331d6c1e0534aa332f5 +Subproject commit 4d1b322cff63c47e7ed649fae048e680cfeda077 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 6c9b9d853f40..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 @@ -1160,6 +1160,18 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { value.fold_with(&mut r) } + /// Returns true if `T` contains unresolved type variables. In the + /// process of visiting `T`, this will resolve (where possible) + /// type variables in `T`, but it never constructs the final, + /// resolved type, so it's more efficient than + /// `resolve_type_vars_if_possible()`. + pub fn any_unresolved_type_vars(&self, value: &T) -> bool + where T: TypeFoldable<'tcx> + { + let mut r = resolve::UnresolvedTypeFinder::new(self); + value.visit_with(&mut r) + } + pub fn resolve_type_and_region_vars_if_possible(&self, value: &T) -> T where T: TypeFoldable<'tcx> { @@ -1278,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/infer/resolve.rs b/src/librustc/infer/resolve.rs index 639a330dc6e6..10899e42afb8 100644 --- a/src/librustc/infer/resolve.rs +++ b/src/librustc/infer/resolve.rs @@ -10,7 +10,7 @@ use super::{InferCtxt, FixupError, FixupResult}; use ty::{self, Ty, TyCtxt, TypeFoldable}; -use ty::fold::TypeFolder; +use ty::fold::{TypeFolder, TypeVisitor}; /////////////////////////////////////////////////////////////////////////// // OPPORTUNISTIC TYPE RESOLVER @@ -80,6 +80,43 @@ impl<'a, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for OpportunisticTypeAndRegionResolv } } +/////////////////////////////////////////////////////////////////////////// +// UNRESOLVED TYPE FINDER + +/// The unresolved type **finder** walks your type and searches for +/// type variables that don't yet have a value. They get pushed into a +/// vector. It does not construct the fully resolved type (which might +/// involve some hashing and so forth). +pub struct UnresolvedTypeFinder<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { + infcx: &'a InferCtxt<'a, 'gcx, 'tcx>, +} + +impl<'a, 'gcx, 'tcx> UnresolvedTypeFinder<'a, 'gcx, 'tcx> { + pub fn new(infcx: &'a InferCtxt<'a, 'gcx, 'tcx>) -> Self { + UnresolvedTypeFinder { infcx } + } +} + +impl<'a, 'gcx, 'tcx> TypeVisitor<'tcx> for UnresolvedTypeFinder<'a, 'gcx, 'tcx> { + fn visit_ty(&mut self, t: Ty<'tcx>) -> bool { + let t = self.infcx.shallow_resolve(t); + if t.has_infer_types() { + if let ty::TyInfer(_) = t.sty { + // Since we called `shallow_resolve` above, this must + // be an (as yet...) unresolved inference variable. + true + } else { + // Otherwise, visit its contents. + t.super_visit_with(self) + } + } else { + // Micro-optimize: no inference types at all Can't have unresolved type + // variables, no need to visit the contents. + false + } + } +} + /////////////////////////////////////////////////////////////////////////// // FULL TYPE RESOLUTION diff --git a/src/librustc/lint/builtin.rs b/src/librustc/lint/builtin.rs index 811bf9776101..cbe642a9a76a 100644 --- a/src/librustc/lint/builtin.rs +++ b/src/librustc/lint/builtin.rs @@ -30,7 +30,7 @@ declare_lint! { declare_lint! { pub UNUSED_EXTERN_CRATES, - Warn, + Allow, "extern crates that are never used" } 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/session/mod.rs b/src/librustc/session/mod.rs index 823a637c7e0d..7ff9d202c180 100644 --- a/src/librustc/session/mod.rs +++ b/src/librustc/session/mod.rs @@ -642,12 +642,16 @@ pub fn build_session_with_codemap(sopts: config::Options, // FIXME: This is not general enough to make the warning lint completely override // normal diagnostic warnings, since the warning lint can also be denied and changed // later via the source code. - let can_print_warnings = sopts.lint_opts + let warnings_allow = sopts.lint_opts .iter() .filter(|&&(ref key, _)| *key == "warnings") - .map(|&(_, ref level)| *level != lint::Allow) + .map(|&(_, ref level)| *level == lint::Allow) .last() - .unwrap_or(true); + .unwrap_or(false); + let cap_lints_allow = sopts.lint_cap.map_or(false, |cap| cap == lint::Allow); + + let can_print_warnings = !(warnings_allow || cap_lints_allow); + let treat_err_as_bug = sopts.debugging_opts.treat_err_as_bug; let emitter: Box = match (sopts.error_format, emitter_dest) { diff --git a/src/librustc/traits/fulfill.rs b/src/librustc/traits/fulfill.rs index 78e47693caaf..fbc393cbd96f 100644 --- a/src/librustc/traits/fulfill.rs +++ b/src/librustc/traits/fulfill.rs @@ -251,6 +251,9 @@ impl<'a, 'gcx, 'tcx> FulfillmentContext<'tcx> { }); debug!("select: outcome={:?}", outcome); + // FIXME: if we kept the original cache key, we could mark projection + // obligations as complete for the projection cache here. + errors.extend( outcome.errors.into_iter() .map(|e| to_fulfillment_error(e))); diff --git a/src/librustc/traits/project.rs b/src/librustc/traits/project.rs index e70258007e46..3fb615e29cc1 100644 --- a/src/librustc/traits/project.rs +++ b/src/librustc/traits/project.rs @@ -24,7 +24,7 @@ use super::VtableImplData; use super::util; use hir::def_id::DefId; -use infer::InferOk; +use infer::{InferCtxt, InferOk}; use infer::type_variable::TypeVariableOrigin; use rustc_data_structures::snapshot_map::{Snapshot, SnapshotMap}; use syntax::ast; @@ -121,11 +121,13 @@ struct ProjectionTyCandidateSet<'tcx> { /// /// for<...> ::U == V /// -/// If successful, this may result in additional obligations. +/// If successful, this may result in additional obligations. Also returns +/// the projection cache key used to track these additional obligations. pub fn poly_project_and_unify_type<'cx, 'gcx, 'tcx>( selcx: &mut SelectionContext<'cx, 'gcx, 'tcx>, obligation: &PolyProjectionObligation<'tcx>) - -> Result>>, MismatchedProjectionTypes<'tcx>> + -> Result>>, + MismatchedProjectionTypes<'tcx>> { debug!("poly_project_and_unify_type(obligation={:?})", obligation); @@ -161,7 +163,8 @@ pub fn poly_project_and_unify_type<'cx, 'gcx, 'tcx>( fn project_and_unify_type<'cx, 'gcx, 'tcx>( selcx: &mut SelectionContext<'cx, 'gcx, 'tcx>, obligation: &ProjectionObligation<'tcx>) - -> Result>>, MismatchedProjectionTypes<'tcx>> + -> Result>>, + MismatchedProjectionTypes<'tcx>> { debug!("project_and_unify_type(obligation={:?})", obligation); @@ -396,6 +399,7 @@ fn opt_normalize_projection_type<'a, 'b, 'gcx, 'tcx>( let infcx = selcx.infcx(); let projection_ty = infcx.resolve_type_vars_if_possible(&projection_ty); + let cache_key = ProjectionCacheKey { ty: projection_ty }; debug!("opt_normalize_projection_type(\ projection_ty={:?}, \ @@ -411,7 +415,8 @@ fn opt_normalize_projection_type<'a, 'b, 'gcx, 'tcx>( // bounds. It might be the case that we want two distinct caches, // or else another kind of cache entry. - match infcx.projection_cache.borrow_mut().try_start(projection_ty) { + let cache_result = infcx.projection_cache.borrow_mut().try_start(cache_key); + match cache_result { Ok(()) => { } Err(ProjectionCacheEntry::Ambiguous) => { // If we found ambiguity the last time, that generally @@ -461,7 +466,7 @@ fn opt_normalize_projection_type<'a, 'b, 'gcx, 'tcx>( projection_ty); selcx.infcx().report_overflow_error(&obligation, false); } - Err(ProjectionCacheEntry::NormalizedTy(ty)) => { + Err(ProjectionCacheEntry::NormalizedTy(mut ty)) => { // If we find the value in the cache, then return it along // with the obligations that went along with it. Note // that, when using a fulfillment context, these @@ -474,6 +479,21 @@ fn opt_normalize_projection_type<'a, 'b, 'gcx, 'tcx>( debug!("opt_normalize_projection_type: \ found normalized ty `{:?}`", ty); + + // Once we have inferred everything we need to know, we + // can ignore the `obligations` from that point on. + if !infcx.any_unresolved_type_vars(&ty.value) { + infcx.projection_cache.borrow_mut().complete(cache_key); + ty.obligations = vec![]; + } + + push_paranoid_cache_value_obligation(infcx, + param_env, + projection_ty, + cause, + depth, + &mut ty); + return Some(ty); } Err(ProjectionCacheEntry::Error) => { @@ -485,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 @@ -494,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, @@ -522,7 +546,10 @@ fn opt_normalize_projection_type<'a, 'b, 'gcx, 'tcx>( obligations, } }; - infcx.projection_cache.borrow_mut().complete(projection_ty, &result); + + let cache_value = prune_cache_value_obligations(infcx, &result); + infcx.projection_cache.borrow_mut().insert_ty(cache_key, cache_value, cacheable); + Some(result) } Ok(ProjectedTy::NoProgress(projected_ty)) => { @@ -533,14 +560,14 @@ fn opt_normalize_projection_type<'a, 'b, 'gcx, 'tcx>( value: projected_ty, obligations: vec![] }; - infcx.projection_cache.borrow_mut().complete(projection_ty, &result); + infcx.projection_cache.borrow_mut().insert_ty(cache_key, result.clone(), true); Some(result) } Err(ProjectionTyError::TooManyCandidates) => { debug!("opt_normalize_projection_type: \ too many candidates"); infcx.projection_cache.borrow_mut() - .ambiguous(projection_ty); + .ambiguous(cache_key); None } Err(ProjectionTyError::TraitSelectionError(_)) => { @@ -551,12 +578,88 @@ fn opt_normalize_projection_type<'a, 'b, 'gcx, 'tcx>( // reported later infcx.projection_cache.borrow_mut() - .error(projection_ty); + .error(cache_key); Some(normalize_to_error(selcx, param_env, projection_ty, cause, depth)) } } } +/// If there are unresolved type variables, then we need to include +/// any subobligations that bind them, at least until those type +/// variables are fully resolved. +fn prune_cache_value_obligations<'a, 'gcx, 'tcx>(infcx: &'a InferCtxt<'a, 'gcx, 'tcx>, + result: &NormalizedTy<'tcx>) + -> NormalizedTy<'tcx> { + if !infcx.any_unresolved_type_vars(&result.value) { + return NormalizedTy { value: result.value, obligations: vec![] }; + } + + let mut obligations: Vec<_> = + result.obligations + .iter() + .filter(|obligation| match obligation.predicate { + // We found a `T: Foo` predicate, let's check + // if `U` references any unresolved type + // variables. In principle, we only care if this + // projection can help resolve any of the type + // variables found in `result.value` -- but we just + // check for any type variables here, for fear of + // indirect obligations (e.g., we project to `?0`, + // but we have `T: Foo` and `?1: Bar`). + ty::Predicate::Projection(ref data) => + infcx.any_unresolved_type_vars(&data.ty()), + + // We are only interested in `T: Foo` predicates, whre + // `U` references one of `unresolved_type_vars`. =) + _ => false, + }) + .cloned() + .collect(); + + obligations.shrink_to_fit(); + + NormalizedTy { value: result.value, obligations } +} + +/// Whenever we give back a cache result for a projection like `::Item ==> X`, we *always* include the obligation to prove +/// that `T: Trait` (we may also include some other obligations). This +/// may or may not be necessary -- in principle, all the obligations +/// that must be proven to show that `T: Trait` were also returned +/// when the cache was first populated. But there are some vague concerns, +/// and so we take the precatuionary measure of including `T: Trait` in +/// the result: +/// +/// Concern #1. The current setup is fragile. Perhaps someone could +/// have failed to prove the concerns from when the cache was +/// populated, but also not have used a snapshot, in which case the +/// cache could remain populated even though `T: Trait` has not been +/// shown. In this case, the "other code" is at fault -- when you +/// project something, you are supposed to either have a snapshot or +/// else prove all the resulting obligations -- but it's still easy to +/// get wrong. +/// +/// Concern #2. Even within the snapshot, if those original +/// obligations are not yet proven, then we are able to do projections +/// that may yet turn out to be wrong. This *may* lead to some sort +/// of trouble, though we don't have a concrete example of how that +/// can occur yet. But it seems risky at best. +fn push_paranoid_cache_value_obligation<'a, 'gcx, 'tcx>(infcx: &'a InferCtxt<'a, 'gcx, 'tcx>, + param_env: ty::ParamEnv<'tcx>, + projection_ty: ty::ProjectionTy<'tcx>, + cause: ObligationCause<'tcx>, + depth: usize, + result: &mut NormalizedTy<'tcx>) +{ + let trait_ref = projection_ty.trait_ref(infcx.tcx).to_poly_trait_ref(); + let trait_obligation = Obligation { cause, + recursion_depth: depth, + param_env, + predicate: trait_ref.to_predicate() }; + result.obligations.push(trait_obligation); +} + /// If we are projecting `::Item`, but `T: Trait` does not /// hold. In various error cases, we cannot generate a valid /// normalized projection. Therefore, we create an inference variable @@ -606,6 +709,7 @@ enum ProjectedTy<'tcx> { struct Progress<'tcx> { ty: Ty<'tcx>, obligations: Vec>, + cacheable: bool, } impl<'tcx> Progress<'tcx> { @@ -613,6 +717,7 @@ impl<'tcx> Progress<'tcx> { Progress { ty: tcx.types.err, obligations: vec![], + cacheable: true } } @@ -1226,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) => { @@ -1269,6 +1375,7 @@ fn confirm_impl_candidate<'cx, 'gcx, 'tcx>( Progress { ty: ty.subst(tcx, substs), obligations: nested, + cacheable: true } } @@ -1323,8 +1430,62 @@ fn assoc_ty_def<'cx, 'gcx, 'tcx>( // # Cache +/// The projection cache. Unlike the standard caches, this can +/// include infcx-dependent type variables - therefore, we have to roll +/// the cache back each time we roll a snapshot back, to avoid assumptions +/// on yet-unresolved inference variables. Types with skolemized regions +/// also have to be removed when the respective snapshot ends. +/// +/// Because of that, projection cache entries can be "stranded" and left +/// inaccessible when type variables inside the key are resolved. We make no +/// attempt to recover or remove "stranded" entries, but rather let them be +/// (for the lifetime of the infcx). +/// +/// Entries in the projection cache might contain inference variables +/// that will be resolved by obligations on the projection cache entry - e.g. +/// when a type parameter in the associated type is constrained through +/// an "RFC 447" projection on the impl. +/// +/// When working with a fulfillment context, the derived obligations of each +/// projection cache entry will be registered on the fulfillcx, so any users +/// that can wait for a fulfillcx fixed point need not care about this. However, +/// users that don't wait for a fixed point (e.g. trait evaluation) have to +/// resolve the obligations themselves to make sure the projected result is +/// ok and avoid issues like #43132. +/// +/// If that is done, after evaluation the obligations, it is a good idea to +/// call `ProjectionCache::complete` to make sure the obligations won't be +/// re-evaluated and avoid an exponential worst-case. +/// +/// FIXME: we probably also want some sort of cross-infcx cache here to +/// reduce the amount of duplication. Let's see what we get with the Chalk +/// reforms. pub struct ProjectionCache<'tcx> { - map: SnapshotMap, ProjectionCacheEntry<'tcx>>, + map: SnapshotMap, ProjectionCacheEntry<'tcx>>, +} + +#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] +pub struct ProjectionCacheKey<'tcx> { + ty: ty::ProjectionTy<'tcx> +} + +impl<'cx, 'gcx, 'tcx> ProjectionCacheKey<'tcx> { + pub fn from_poly_projection_predicate(selcx: &mut SelectionContext<'cx, 'gcx, 'tcx>, + predicate: &ty::PolyProjectionPredicate<'tcx>) + -> Option + { + let infcx = selcx.infcx(); + // We don't do cross-snapshot caching of obligations with escaping regions, + // so there's no cache key to use + infcx.tcx.no_late_bound_regions(&predicate) + .map(|predicate| ProjectionCacheKey { + // We don't attempt to match up with a specific type-variable state + // from a specific call to `opt_normalize_projection_type` - if + // there's no precise match, the original cache entry is "stranded" + // anyway. + ty: infcx.resolve_type_vars_if_possible(&predicate.projection_ty) + }) + } } #[derive(Clone, Debug)] @@ -1337,7 +1498,7 @@ enum ProjectionCacheEntry<'tcx> { // NB: intentionally not Clone pub struct ProjectionCacheSnapshot { - snapshot: Snapshot + snapshot: Snapshot, } impl<'tcx> ProjectionCache<'tcx> { @@ -1356,7 +1517,7 @@ impl<'tcx> ProjectionCache<'tcx> { } pub fn rollback_skolemized(&mut self, snapshot: &ProjectionCacheSnapshot) { - self.map.partial_rollback(&snapshot.snapshot, &|k| k.has_re_skol()); + self.map.partial_rollback(&snapshot.snapshot, &|k| k.ty.has_re_skol()); } pub fn commit(&mut self, snapshot: ProjectionCacheSnapshot) { @@ -1366,7 +1527,7 @@ impl<'tcx> ProjectionCache<'tcx> { /// Try to start normalize `key`; returns an error if /// normalization already occurred (this error corresponds to a /// cache hit, so it's actually a good thing). - fn try_start(&mut self, key: ty::ProjectionTy<'tcx>) + fn try_start(&mut self, key: ProjectionCacheKey<'tcx>) -> Result<(), ProjectionCacheEntry<'tcx>> { if let Some(entry) = self.map.get(&key) { return Err(entry.clone()); @@ -1376,26 +1537,61 @@ impl<'tcx> ProjectionCache<'tcx> { Ok(()) } - /// Indicates that `key` was normalized to `value`. - fn complete(&mut self, key: ty::ProjectionTy<'tcx>, value: &NormalizedTy<'tcx>) { - debug!("ProjectionCacheEntry::complete: adding cache entry: key={:?}, value={:?}", + /// 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.clone())); + let fresh_key = if cacheable { + self.map.insert(key, ProjectionCacheEntry::NormalizedTy(value)) + } else { + !self.map.remove(key) + }; assert!(!fresh_key, "never started projecting `{:?}`", key); } + /// Mark the relevant projection cache key as having its derived obligations + /// complete, so they won't have to be re-computed (this is OK to do in a + /// snapshot - if the snapshot is rolled back, the obligations will be + /// marked as incomplete again). + pub fn complete(&mut self, key: ProjectionCacheKey<'tcx>) { + let ty = match self.map.get(&key) { + Some(&ProjectionCacheEntry::NormalizedTy(ref ty)) => { + debug!("ProjectionCacheEntry::complete({:?}) - completing {:?}", + key, ty); + ty.value + } + ref value => { + // Type inference could "strand behind" old cache entries. Leave + // them alone for now. + debug!("ProjectionCacheEntry::complete({:?}) - ignoring {:?}", + key, value); + return + } + }; + + self.map.insert(key, ProjectionCacheEntry::NormalizedTy(Normalized { + value: ty, + obligations: vec![] + })); + } + /// Indicates that trying to normalize `key` resulted in /// ambiguity. No point in trying it again then until we gain more /// type information (in which case, the "fully resolved" key will /// be different). - fn ambiguous(&mut self, key: ty::ProjectionTy<'tcx>) { + fn ambiguous(&mut self, key: ProjectionCacheKey<'tcx>) { let fresh = self.map.insert(key, ProjectionCacheEntry::Ambiguous); assert!(!fresh, "never started projecting `{:?}`", key); } /// Indicates that trying to normalize `key` resulted in /// error. - fn error(&mut self, key: ty::ProjectionTy<'tcx>) { + fn error(&mut self, key: ProjectionCacheKey<'tcx>) { let fresh = self.map.insert(key, ProjectionCacheEntry::Error); assert!(!fresh, "never started projecting `{:?}`", key); } diff --git a/src/librustc/traits/select.rs b/src/librustc/traits/select.rs index f723b7dc425b..551bfb5db47a 100644 --- a/src/librustc/traits/select.rs +++ b/src/librustc/traits/select.rs @@ -16,7 +16,7 @@ use self::EvaluationResult::*; use super::coherence; use super::DerivedObligationCause; use super::project; -use super::project::{normalize_with_depth, Normalized}; +use super::project::{normalize_with_depth, Normalized, ProjectionCacheKey}; use super::{PredicateObligation, TraitObligation, ObligationCause}; use super::{ObligationCauseCode, BuiltinDerivedObligation, ImplDerivedObligation}; use super::{SelectionError, Unimplemented, OutputTypeParameterMismatch}; @@ -655,8 +655,14 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> { let project_obligation = obligation.with(data.clone()); match project::poly_project_and_unify_type(self, &project_obligation) { Ok(Some(subobligations)) => { - self.evaluate_predicates_recursively(previous_stack, - subobligations.iter()) + let result = self.evaluate_predicates_recursively(previous_stack, + subobligations.iter()); + if let Some(key) = + ProjectionCacheKey::from_poly_projection_predicate(self, data) + { + self.infcx.projection_cache.borrow_mut().complete(key); + } + result } Ok(None) => { EvaluatedToAmbig 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 6597dccf2581..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` @@ -1015,6 +1017,10 @@ impl<'tcx> PolyProjectionPredicate<'tcx> { // levels. ty::Binder(self.0.projection_ty.trait_ref(tcx)) } + + pub fn ty(&self) -> Binder> { + Binder(self.skip_binder().ty) // preserves binding levels + } } pub trait ToPolyTraitRef<'tcx> { 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/libstd/os/raw.rs b/src/libstd/os/raw.rs index c34491941d69..fe0427d4e5f9 100644 --- a/src/libstd/os/raw.rs +++ b/src/libstd/os/raw.rs @@ -14,8 +14,7 @@ use fmt; -#[cfg(any(target_os = "emscripten", - all(target_os = "linux", any(target_arch = "aarch64", +#[cfg(any(all(target_os = "linux", any(target_arch = "aarch64", target_arch = "arm", target_arch = "powerpc", target_arch = "powerpc64", @@ -24,8 +23,7 @@ use fmt; target_arch = "arm")), all(target_os = "fuchsia", target_arch = "aarch64")))] #[stable(feature = "raw_os", since = "1.1.0")] pub type c_char = u8; -#[cfg(not(any(target_os = "emscripten", - all(target_os = "linux", any(target_arch = "aarch64", +#[cfg(not(any(all(target_os = "linux", any(target_arch = "aarch64", target_arch = "arm", target_arch = "powerpc", target_arch = "powerpc64", diff --git a/src/libstd/sys/unix/fd.rs b/src/libstd/sys/unix/fd.rs index 138087f16514..f50b093acc84 100644 --- a/src/libstd/sys/unix/fd.rs +++ b/src/libstd/sys/unix/fd.rs @@ -71,13 +71,21 @@ impl FileDesc { #[cfg(target_os = "android")] use super::android::cvt_pread64; - #[cfg(not(target_os = "android"))] + #[cfg(target_os = "emscripten")] unsafe fn cvt_pread64(fd: c_int, buf: *mut c_void, count: usize, offset: i64) -> io::Result { - #[cfg(any(target_os = "linux", target_os = "emscripten"))] use libc::pread64; - #[cfg(not(any(target_os = "linux", target_os = "emscripten")))] + cvt(pread64(fd, buf, count, offset as i32)) + } + + #[cfg(not(any(target_os = "android", target_os = "emscripten")))] + unsafe fn cvt_pread64(fd: c_int, buf: *mut c_void, count: usize, offset: i64) + -> io::Result + { + #[cfg(target_os = "linux")] + use libc::pread64; + #[cfg(not(target_os = "linux"))] use libc::pread as pread64; cvt(pread64(fd, buf, count, offset)) } @@ -104,13 +112,21 @@ impl FileDesc { #[cfg(target_os = "android")] use super::android::cvt_pwrite64; - #[cfg(not(target_os = "android"))] + #[cfg(target_os = "emscripten")] + unsafe fn cvt_pwrite64(fd: c_int, buf: *const c_void, count: usize, offset: i64) + -> io::Result + { + use libc::pwrite64; + cvt(pwrite64(fd, buf, count, offset as i32)) + } + + #[cfg(not(any(target_os = "android", target_os = "emscripten")))] unsafe fn cvt_pwrite64(fd: c_int, buf: *const c_void, count: usize, offset: i64) -> io::Result { - #[cfg(any(target_os = "linux", target_os = "emscripten"))] + #[cfg(target_os = "linux")] use libc::pwrite64; - #[cfg(not(any(target_os = "linux", target_os = "emscripten")))] + #[cfg(not(target_os = "linux"))] use libc::pwrite as pwrite64; cvt(pwrite64(fd, buf, count, offset)) } diff --git a/src/libstd/sys/unix/fs.rs b/src/libstd/sys/unix/fs.rs index cb0f687e0721..f94af4913324 100644 --- a/src/libstd/sys/unix/fs.rs +++ b/src/libstd/sys/unix/fs.rs @@ -514,6 +514,8 @@ impl File { SeekFrom::End(off) => (libc::SEEK_END, off), SeekFrom::Current(off) => (libc::SEEK_CUR, off), }; + #[cfg(target_os = "emscripten")] + let pos = pos as i32; let n = cvt(unsafe { lseek64(self.0.raw(), pos, whence) })?; Ok(n as u64) } diff --git a/src/libstd/sys/unix/process/process_unix.rs b/src/libstd/sys/unix/process/process_unix.rs index edd322ca6fa0..ae24021fb6c3 100644 --- a/src/libstd/sys/unix/process/process_unix.rs +++ b/src/libstd/sys/unix/process/process_unix.rs @@ -10,7 +10,6 @@ use io::{self, Error, ErrorKind}; use libc::{self, c_int, gid_t, pid_t, uid_t}; -use mem; use ptr; use sys::cvt; @@ -184,7 +183,9 @@ impl Command { } // NaCl has no signal support. - if cfg!(not(any(target_os = "nacl", target_os = "emscripten"))) { + #[cfg(not(any(target_os = "nacl", target_os = "emscripten")))] + { + use mem; // Reset signal handling so the child process starts in a // standardized state. libstd ignores SIGPIPE, and signal-handling // libraries often set a mask. Child processes inherit ignored diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index d1172b1b2ce9..171b0a22e9fc 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -736,12 +736,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { item: Annotatable, kind: ExpansionKind) -> Expansion { - if !traits.is_empty() && - (kind == ExpansionKind::TraitItems || kind == ExpansionKind::ImplItems) { - self.cx.span_err(traits[0].span, "`derive` can be only be applied to items"); - return kind.expect_from_annotatables(::std::iter::once(item)); - } - self.collect(kind, InvocationKind::Attr { attr: attr, traits: traits, item: item }) + self.collect(kind, InvocationKind::Attr { attr, traits, item }) } // If `item` is an attr invocation, remove and return the macro attribute. diff --git a/src/rustc/libc_shim/Cargo.toml b/src/rustc/libc_shim/Cargo.toml index 39df3528be36..38a3aefa23bb 100644 --- a/src/rustc/libc_shim/Cargo.toml +++ b/src/rustc/libc_shim/Cargo.toml @@ -10,7 +10,6 @@ name = "libc" version = "0.0.0" authors = ["The Rust Project Developers"] -build = "build.rs" [lib] name = "libc" @@ -21,3 +20,7 @@ doc = false [dependencies] core = { path = "../../libcore" } + +[features] +stdbuild = [] +default = ["stdbuild"] diff --git a/src/rustc/libc_shim/build.rs b/src/rustc/libc_shim/build.rs deleted file mode 100644 index 546f60482e7b..000000000000 --- a/src/rustc/libc_shim/build.rs +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright 2015 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. - -#![deny(warnings)] - -// See comments in Cargo.toml for why this exists - -fn main() { - println!("cargo:rustc-cfg=stdbuild"); - println!("cargo:rerun-if-changed=build.rs"); -} 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) -} - -#[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(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) +//} - bar(foo, x) //[transmute]~ ERROR E0495 -} +// 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 +//} -#[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/feature-gate-fn_must_use-cap-lints-allow.rs b/src/test/compile-fail/feature-gate-fn_must_use-cap-lints-allow.rs new file mode 100644 index 000000000000..1c04199c05f7 --- /dev/null +++ b/src/test/compile-fail/feature-gate-fn_must_use-cap-lints-allow.rs @@ -0,0 +1,22 @@ +// Copyright 2017 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. + +// compile-flags: --cap-lints allow + +// This tests that the fn_must_use feature-gate warning respects the lint +// cap. (See discussion in Issue #44213.) + +#![feature(rustc_attrs)] + +#[must_use] // (no feature-gate warning because of the lint cap!) +fn need_to_use_it() -> bool { true } + +#[rustc_error] +fn main() {} //~ ERROR compilation successful 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/compile-fail/issue-43023.rs b/src/test/compile-fail/issue-43023.rs new file mode 100644 index 000000000000..55a3b2f8ce3a --- /dev/null +++ b/src/test/compile-fail/issue-43023.rs @@ -0,0 +1,28 @@ +// Copyright 2017 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. + +struct S; + +impl S { + #[derive(Debug)] //~ ERROR `derive` may only be applied to structs and enums + fn f() { + file!(); + } +} + +trait Tr1 { + #[derive(Debug)] //~ ERROR `derive` may only be applied to structs and enums + fn f(); +} + +trait Tr2 { + #[derive(Debug)] //~ ERROR `derive` may only be applied to structs and enums + type F; +} 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 diff --git a/src/tools/cargo b/src/tools/cargo index 5068cb606aad..3423351a5d75 160000 --- a/src/tools/cargo +++ b/src/tools/cargo @@ -1 +1 @@ -Subproject commit 5068cb606aadb5e70238a3a52e52749f21b11055 +Subproject commit 3423351a5d75ac7377bb15987842aadcfd068ad2