From 658134a8a235a456c4a7acfd51c0e2c922cabfc1 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 24 Sep 2017 23:57:42 -0700 Subject: [PATCH 01/15] Allow unused extern crate again This is a partial revert of #42588. There is a usability concern reported in #44294 that was not considered in the discussion of the PR, so I would like to back this out of 1.21. As is, I think users would have a worse and more confusing experience with this lint enabled by default. We can re-enabled once there are better diagnostics or the case in #44294 does not trigger the lint. --- src/librustc/lint/builtin.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustc/lint/builtin.rs b/src/librustc/lint/builtin.rs index 811bf9776101d..cbe642a9a76a6 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" } From 7e08df507db8036e55445417d002145317f07760 Mon Sep 17 00:00:00 2001 From: Jeffrey Seyfried Date: Thu, 21 Sep 2017 00:20:56 -0700 Subject: [PATCH 02/15] Fix bug in collecting trait and impl items with derives. --- src/libsyntax/ext/expand.rs | 7 +------ src/test/compile-fail/issue-43023.rs | 28 ++++++++++++++++++++++++++++ 2 files changed, 29 insertions(+), 6 deletions(-) create mode 100644 src/test/compile-fail/issue-43023.rs diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index d1172b1b2ce94..171b0a22e9fc2 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/test/compile-fail/issue-43023.rs b/src/test/compile-fail/issue-43023.rs new file mode 100644 index 0000000000000..6a5f7a1136aaa --- /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, enums and unions + fn f() { + file!(); + } +} + +trait Tr1 { + #[derive(Debug)] //~ ERROR `derive` may only be applied to structs, enums and unions + fn f(); +} + +trait Tr2 { + #[derive(Debug)] //~ ERROR `derive` may only be applied to structs, enums and unions + type F; +} From 957846c3a2afdbc04493b07378d8dfe83cd6d061 Mon Sep 17 00:00:00 2001 From: "Zack M. Davis" Date: Sat, 16 Sep 2017 00:13:07 -0700 Subject: [PATCH 03/15] `--cap-lints allow` switches off `can_emit_warnings` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This boolean field on the error `Handler` is toggled to silence warnings when `-A warnings` is passed. (This is actually a separate mechanism from the global lint level—whether there's some redundancy to be factored away here is an important question, but not one we concern ourselves with in this commit.) But the same rationale applies for `--cap-lints allow`. In particular, this makes the "soft" feature-gate warning introduced in 8492ad24 (which is not a lint, but just calls `struct_span_warn`) not pollute the builds of dependent crates. Thanks to @kennytm for pointing out the potential of `can_emit_warnings` for this purpose. Resolves #44213. --- src/librustc/session/mod.rs | 10 ++++++--- ...eature-gate-fn_must_use-cap-lints-allow.rs | 22 +++++++++++++++++++ 2 files changed, 29 insertions(+), 3 deletions(-) create mode 100644 src/test/compile-fail/feature-gate-fn_must_use-cap-lints-allow.rs diff --git a/src/librustc/session/mod.rs b/src/librustc/session/mod.rs index 823a637c7e0d4..7ff9d202c1808 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/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 0000000000000..1c04199c05f7c --- /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 From 69bd53a11a6fa71ae0243186882588c4b1db60dd Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 17 Jul 2017 09:24:05 -0700 Subject: [PATCH 04/15] Update the libc submodule Brings in a few fixes for wasm/asmjs --- src/liblibc | 2 +- src/libstd/os/raw.rs | 6 ++--- src/libstd/sys/unix/fd.rs | 28 ++++++++++++++++----- src/libstd/sys/unix/fs.rs | 2 ++ src/libstd/sys/unix/process/process_unix.rs | 5 ++-- 5 files changed, 30 insertions(+), 13 deletions(-) diff --git a/src/liblibc b/src/liblibc index 2a5b50b7f7f53..d64716407e3ee 160000 --- a/src/liblibc +++ b/src/liblibc @@ -1 +1 @@ -Subproject commit 2a5b50b7f7f539a0fd201331d6c1e0534aa332f5 +Subproject commit d64716407e3ee430fce7a008cc7d19a3072dca6c diff --git a/src/libstd/os/raw.rs b/src/libstd/os/raw.rs index c34491941d690..fe0427d4e5f9c 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 138087f165142..f50b093acc848 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 cb0f687e0721c..f94af4913324f 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 edd322ca6fa07..ae24021fb6c3a 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 From 423251a4aee0da5df8e8a13a4f0991aaec2bae42 Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Sun, 20 Aug 2017 19:16:36 +0300 Subject: [PATCH 05/15] clear out projection subobligations after they are processed After a projection was processed, its derived subobligations no longer need any processing when encountered, and can be removed. This improves the status of #43787. This is actually complementary to #43938 - that PR fixes selection caching (and @remram44's example, which "accidentally" worked because of the buggy projection caching) while this PR fixes projection caching --- src/librustc/traits/fulfill.rs | 3 + src/librustc/traits/project.rs | 116 ++++++++++++++++++++++++++++----- src/librustc/traits/select.rs | 12 +++- 3 files changed, 112 insertions(+), 19 deletions(-) diff --git a/src/librustc/traits/fulfill.rs b/src/librustc/traits/fulfill.rs index 78e47693caaf1..fbc393cbd96f2 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 e70258007e463..dbf04d72439e0 100644 --- a/src/librustc/traits/project.rs +++ b/src/librustc/traits/project.rs @@ -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,7 @@ 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) { + match infcx.projection_cache.borrow_mut().try_start(cache_key) { Ok(()) => { } Err(ProjectionCacheEntry::Ambiguous) => { // If we found ambiguity the last time, that generally @@ -522,7 +526,7 @@ fn opt_normalize_projection_type<'a, 'b, 'gcx, 'tcx>( obligations, } }; - infcx.projection_cache.borrow_mut().complete(projection_ty, &result); + infcx.projection_cache.borrow_mut().insert_ty(cache_key, &result); Some(result) } Ok(ProjectedTy::NoProgress(projected_ty)) => { @@ -533,14 +537,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); 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,7 +555,7 @@ 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)) } } @@ -1323,8 +1327,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 +1395,7 @@ enum ProjectionCacheEntry<'tcx> { // NB: intentionally not Clone pub struct ProjectionCacheSnapshot { - snapshot: Snapshot + snapshot: Snapshot, } impl<'tcx> ProjectionCache<'tcx> { @@ -1356,7 +1414,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 +1424,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()); @@ -1377,25 +1435,51 @@ impl<'tcx> ProjectionCache<'tcx> { } /// 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={:?}", + fn insert_ty(&mut self, key: ProjectionCacheKey<'tcx>, value: &NormalizedTy<'tcx>) { + debug!("ProjectionCacheEntry::insert_ty: adding cache entry: key={:?}, value={:?}", key, value); let fresh_key = self.map.insert(key, ProjectionCacheEntry::NormalizedTy(value.clone())); 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 f723b7dc425b1..551bfb5db47ad 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 From 596c9a78e4fe08d22b104d035890d2418b4b6bbc Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sat, 2 Sep 2017 08:35:07 -0400 Subject: [PATCH 06/15] limit and clear cache obligations opportunistically Keep **all** the obligations for every projection is wasteful of memory and compilation time. We only really care about those subobligations that may inform the result of the projection (i.e., may help to resolve any inference variables that appear within). Therefore, we can clear the subobligations from the cache that don't potentially affect the result of the projection. On every cache hit, we also take the opportunity to check if the type variables have been resolved *yet* and, if so, clear out the pending obligations. Fixes #43613 --- src/librustc/infer/mod.rs | 12 +++++++ src/librustc/infer/resolve.rs | 39 ++++++++++++++++++++- src/librustc/traits/project.rs | 64 ++++++++++++++++++++++++++++++---- src/librustc/ty/mod.rs | 4 +++ 4 files changed, 111 insertions(+), 8 deletions(-) diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs index 6c9b9d853f403..56b5f6c45eb15 100644 --- a/src/librustc/infer/mod.rs +++ b/src/librustc/infer/mod.rs @@ -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> { diff --git a/src/librustc/infer/resolve.rs b/src/librustc/infer/resolve.rs index 639a330dc6e67..10899e42afb81 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/traits/project.rs b/src/librustc/traits/project.rs index dbf04d72439e0..553a43cd15133 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; @@ -415,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(cache_key) { + 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 @@ -465,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 @@ -478,6 +479,14 @@ 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![]; + } + return Some(ty); } Err(ProjectionCacheEntry::Error) => { @@ -526,7 +535,10 @@ fn opt_normalize_projection_type<'a, 'b, 'gcx, 'tcx>( obligations, } }; - infcx.projection_cache.borrow_mut().insert_ty(cache_key, &result); + + let cache_value = prune_cache_value_obligations(infcx, &result); + infcx.projection_cache.borrow_mut().insert_ty(cache_key, cache_value); + Some(result) } Ok(ProjectedTy::NoProgress(projected_ty)) => { @@ -537,7 +549,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); + infcx.projection_cache.borrow_mut().insert_ty(cache_key, result.clone()); Some(result) } Err(ProjectionTyError::TooManyCandidates) => { @@ -561,6 +573,44 @@ fn opt_normalize_projection_type<'a, 'b, 'gcx, 'tcx>( } } +/// 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 } +} + /// 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 @@ -1435,10 +1485,10 @@ impl<'tcx> ProjectionCache<'tcx> { } /// Indicates that `key` was normalized to `value`. - fn insert_ty(&mut self, key: ProjectionCacheKey<'tcx>, value: &NormalizedTy<'tcx>) { + fn insert_ty(&mut self, key: ProjectionCacheKey<'tcx>, value: NormalizedTy<'tcx>) { 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 = self.map.insert(key, ProjectionCacheEntry::NormalizedTy(value)); assert!(!fresh_key, "never started projecting `{:?}`", key); } diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index 6597dccf25816..c7b51af341336 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -1015,6 +1015,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> { From 02a49f85824ab478e4bdc29280436a67471d6c98 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 6 Sep 2017 17:54:37 -0400 Subject: [PATCH 07/15] add in a "paranoid" trait bound --- src/librustc/traits/project.rs | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/src/librustc/traits/project.rs b/src/librustc/traits/project.rs index 553a43cd15133..5533e0effddd1 100644 --- a/src/librustc/traits/project.rs +++ b/src/librustc/traits/project.rs @@ -487,6 +487,13 @@ fn opt_normalize_projection_type<'a, 'b, 'gcx, 'tcx>( ty.obligations = vec![]; } + push_paranoid_cache_value_obligation(infcx, + param_env, + projection_ty, + cause, + depth, + &mut ty); + return Some(ty); } Err(ProjectionCacheEntry::Error) => { @@ -611,6 +618,33 @@ fn prune_cache_value_obligations<'a, 'gcx, 'tcx>(infcx: &'a InferCtxt<'a, 'gcx, 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 is a vague concern +/// that perhaps someone would not have proven those, but also not +/// have used a snapshot, in which case the cache could remain +/// populated even though `T: Trait` has not been shown. Returning +/// this "paranoid" obligation ensures that, no matter what has come +/// before, if you prove the subobligations, we at least know that `T: +/// Trait` is implemented. +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 From 89247fbb7a5f85923fb5ec257be01dea842ac8aa Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Thu, 7 Sep 2017 12:38:33 -0400 Subject: [PATCH 08/15] update comment --- src/librustc/traits/project.rs | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/src/librustc/traits/project.rs b/src/librustc/traits/project.rs index 5533e0effddd1..bd511f1acb173 100644 --- a/src/librustc/traits/project.rs +++ b/src/librustc/traits/project.rs @@ -623,13 +623,24 @@ fn prune_cache_value_obligations<'a, 'gcx, 'tcx>(infcx: &'a InferCtxt<'a, 'gcx, /// 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 is a vague concern -/// that perhaps someone would not have proven those, but also not -/// have used a snapshot, in which case the cache could remain -/// populated even though `T: Trait` has not been shown. Returning -/// this "paranoid" obligation ensures that, no matter what has come -/// before, if you prove the subobligations, we at least know that `T: -/// Trait` is implemented. +/// 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>, From 41d7051ee9d31146526f3920912be54e8e1dba4c Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Thu, 28 Sep 2017 16:57:48 -0400 Subject: [PATCH 09/15] update prerelease version --- src/bootstrap/channel.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bootstrap/channel.rs b/src/bootstrap/channel.rs index 0c22cc2f6c439..b79c7de34314d 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, From 7b54e83ff2fab5b0464dd0421b6d16d0a7a2ccce Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Fri, 6 Oct 2017 17:12:24 +0300 Subject: [PATCH 10/15] fix logic error in #44269's `prune_cache_value_obligations` We want to retain obligations that *contain* inference variables, not obligations that *don't contain* them, in order to fix #43132. Because of surrounding changes to inference, the ICE doesn't occur in its original case, but I believe it could still be made to occur on master. Maybe I should try to write a new test case? Certainly not right now (I'm mainly trying to get us a beta that we can ship) but maybe before we land this PR on nightly? This seems to cause a 10% performance regression in my imprecise attempt to benchmark item-body checking for #43613, but it's better to be slow and right than fast and wrong. If we want to recover that, I think we can change the constrained-type-parameter code to actually give a list of projections that are important for resolving inference variables and filter everything else out. --- src/librustc/traits/project.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustc/traits/project.rs b/src/librustc/traits/project.rs index bd511f1acb173..f5055b9765e7f 100644 --- a/src/librustc/traits/project.rs +++ b/src/librustc/traits/project.rs @@ -604,7 +604,7 @@ fn prune_cache_value_obligations<'a, 'gcx, 'tcx>(infcx: &'a InferCtxt<'a, 'gcx, // but we have `T: Foo` and `?1: Bar`). ty::Predicate::Projection(ref data) => - !infcx.any_unresolved_type_vars(&data.ty()), + infcx.any_unresolved_type_vars(&data.ty()), // We are only interested in `T: Foo` predicates, whre // `U` references one of `unresolved_type_vars`. =) From 08071c28f86acca99b515f45de3387bc968d3107 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 6 Oct 2017 12:00:51 -0400 Subject: [PATCH 11/15] Revert "Cleanup some remains of `hr_lifetime_in_assoc_type` compatibility lint" This reverts commit 80cf3f99f4a3377fd9b544d18017ef29b8713dfd. --- src/librustc/ich/impls_ty.rs | 1 + src/librustc/infer/error_reporting/mod.rs | 35 +++++++- src/librustc/infer/higher_ranked/mod.rs | 56 +++++++++++-- src/librustc/infer/mod.rs | 7 +- src/librustc/middle/resolve_lifetime.rs | 38 +++++++-- src/librustc/traits/project.rs | 33 ++++++-- src/librustc/ty/error.rs | 14 ++-- src/librustc/ty/mod.rs | 2 + src/librustc/ty/structural_impls.rs | 24 +++--- src/librustc/ty/sty.rs | 14 ++++ src/librustc_typeck/astconv.rs | 80 ++++++++++--------- src/librustc_typeck/check/mod.rs | 2 +- src/librustc_typeck/collect.rs | 2 + .../cache/project-fn-ret-contravariant.rs | 24 +++--- .../cache/project-fn-ret-invariant.rs | 46 ++++++----- .../closure-expected-type/issue-38714.rs | 26 ++++++ src/test/compile-fail/hr-subtype.rs | 3 + .../regions-fn-subtyping-return-static.stderr | 2 + 18 files changed, 298 insertions(+), 111 deletions(-) create mode 100644 src/test/compile-fail/closure-expected-type/issue-38714.rs diff --git a/src/librustc/ich/impls_ty.rs b/src/librustc/ich/impls_ty.rs index 5f51579945e33..6a22147fedb5f 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 edf9ca89b3394..68eb4639bce4a 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 0d02420457e6b..760d443f0e56a 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 56b5f6c45eb15..453a8777ad5a6 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 a8e98e53db394..d346ac9dd9b05 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 f5055b9765e7f..3fb615e29cc15 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 86a4f66918965..3442cf0ef698a 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 c7b51af341336..f0a194752c4f8 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 e41eb079b3782..34be80164da60 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 8d6b7b7ac9fdd..2e4be870e915c 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 ee1e6bd950fa1..b55d762bf0c8a 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 955171203e13a..516fae37b2c93 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 ea86c570c8296..8f14e765dfb6a 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 0e822aff01e87..c5557cee7cc1d 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 10fe612980d34..a15422e42d94a 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 0000000000000..cd08276637636 --- /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 c88d74d53ce94..95e469ebcfd72 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 1598a8a40d2f0..0c7b44af949b6 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 From 2eabf4f09625413f2346192fdd61ae2408a5bbb2 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 6 Oct 2017 12:42:32 -0400 Subject: [PATCH 12/15] tweak test case error message for #42023 --- src/test/compile-fail/issue-43023.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/test/compile-fail/issue-43023.rs b/src/test/compile-fail/issue-43023.rs index 6a5f7a1136aaa..55a3b2f8ce3a1 100644 --- a/src/test/compile-fail/issue-43023.rs +++ b/src/test/compile-fail/issue-43023.rs @@ -11,18 +11,18 @@ struct S; impl S { - #[derive(Debug)] //~ ERROR `derive` may only be applied to structs, enums and unions + #[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, enums and unions + #[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, enums and unions + #[derive(Debug)] //~ ERROR `derive` may only be applied to structs and enums type F; } From c257aa6c6845d5a973fbdc68b4f6c63a8e7d3736 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 6 Oct 2017 11:59:35 -0700 Subject: [PATCH 13/15] Update liblibc submodule again --- src/liblibc | 2 +- src/rustc/libc_shim/Cargo.toml | 5 ++++- src/rustc/libc_shim/build.rs | 18 ------------------ 3 files changed, 5 insertions(+), 20 deletions(-) delete mode 100644 src/rustc/libc_shim/build.rs diff --git a/src/liblibc b/src/liblibc index d64716407e3ee..4d1b322cff63c 160000 --- a/src/liblibc +++ b/src/liblibc @@ -1 +1 @@ -Subproject commit d64716407e3ee430fce7a008cc7d19a3072dca6c +Subproject commit 4d1b322cff63c47e7ed649fae048e680cfeda077 diff --git a/src/rustc/libc_shim/Cargo.toml b/src/rustc/libc_shim/Cargo.toml index 39df3528be369..38a3aefa23bb4 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 546f60482e7bc..0000000000000 --- 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"); -} From a32ac51c4eea7ff87faf9e1f63f6b74a204271c6 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 6 Oct 2017 12:58:16 -0700 Subject: [PATCH 14/15] Update cargo to rust-1.21.0 branch --- src/tools/cargo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/cargo b/src/tools/cargo index 3d3f2c05d742e..3423351a5d75a 160000 --- a/src/tools/cargo +++ b/src/tools/cargo @@ -1 +1 @@ -Subproject commit 3d3f2c05d742e5f907e951aa8849b03f0bc1a895 +Subproject commit 3423351a5d75ac7377bb15987842aadcfd068ad2 From a4923d9676dff457fdabdedf9ab73906a97d8b79 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Sat, 7 Oct 2017 08:00:12 -0700 Subject: [PATCH 15/15] Remove the fuchsia builder I believe everything was renamed anyway, so beta is broken regardless --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index ca28aabc41fde..e6ae0af9124e6 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