From 6d40b7232eaa00ab5c060582011f350725703a1e Mon Sep 17 00:00:00 2001 From: Sebastian Geisler Date: Tue, 30 Oct 2018 22:24:33 -0700 Subject: [PATCH 01/88] Implement checked_add_duration for SystemTime Since SystemTime is opaque there is no way to check if the result of an addition will be in bounds. That makes the Add trait completely unusable with untrusted data. This is a big problem because adding a Duration to UNIX_EPOCH is the standard way of constructing a SystemTime from a unix timestamp. This commit implements checked_add_duration(&self, &Duration) -> Option for std::time::SystemTime and as a prerequisite also for all platform specific time structs. This also led to the refactoring of many add_duration(&self, &Duration) -> SystemTime functions to avoid redundancy (they now unwrap the result of checked_add_duration). Some basic unit tests for the newly introduced function were added too. --- src/libstd/sys/cloudabi/time.rs | 19 +++++++++++++------ src/libstd/sys/redox/time.rs | 25 +++++++++++++++++++------ src/libstd/sys/unix/time.rs | 29 +++++++++++++++++++++++------ src/libstd/sys/wasm/time.rs | 4 ++++ src/libstd/sys/windows/time.rs | 16 ++++++++++++---- src/libstd/time.rs | 21 +++++++++++++++++++++ 6 files changed, 92 insertions(+), 22 deletions(-) diff --git a/src/libstd/sys/cloudabi/time.rs b/src/libstd/sys/cloudabi/time.rs index ee12731619aac..191324e26a40f 100644 --- a/src/libstd/sys/cloudabi/time.rs +++ b/src/libstd/sys/cloudabi/time.rs @@ -19,10 +19,14 @@ pub struct Instant { t: abi::timestamp, } -pub fn dur2intervals(dur: &Duration) -> abi::timestamp { +fn checked_dur2intervals(dur: &Duration) -> Option { dur.as_secs() .checked_mul(NSEC_PER_SEC) .and_then(|nanos| nanos.checked_add(dur.subsec_nanos() as abi::timestamp)) +} + +pub fn dur2intervals(dur: &Duration) -> abi::timestamp { + checked_dur2intervals(dur) .expect("overflow converting duration to nanoseconds") } @@ -92,11 +96,14 @@ impl SystemTime { } pub fn add_duration(&self, other: &Duration) -> SystemTime { - SystemTime { - t: self.t - .checked_add(dur2intervals(other)) - .expect("overflow when adding duration to instant"), - } + self.checked_add_duration(other) + .expect("overflow when adding duration to instant") + } + + pub fn checked_add_duration(&self, other: &Duration) -> Option { + checked_dur2intervals(other) + .and_then(|d| self.t.checked_add(d)) + .map(|t| SystemTime {t}) } pub fn sub_duration(&self, other: &Duration) -> SystemTime { diff --git a/src/libstd/sys/redox/time.rs b/src/libstd/sys/redox/time.rs index 5c491115c5516..5f8799489aa6a 100644 --- a/src/libstd/sys/redox/time.rs +++ b/src/libstd/sys/redox/time.rs @@ -42,27 +42,36 @@ impl Timespec { } fn add_duration(&self, other: &Duration) -> Timespec { - let mut secs = other + self.checked_add_duration(other).expect("overflow when adding duration to time") + } + + fn checked_add_duration(&self, other: &Duration) -> Option { + let mut secs = match other .as_secs() .try_into() // <- target type would be `i64` .ok() .and_then(|secs| self.t.tv_sec.checked_add(secs)) - .expect("overflow when adding duration to time"); + { + Some(ts) => ts, + None => return None, + }; // Nano calculations can't overflow because nanos are <1B which fit // in a u32. let mut nsec = other.subsec_nanos() + self.t.tv_nsec as u32; if nsec >= NSEC_PER_SEC as u32 { nsec -= NSEC_PER_SEC as u32; - secs = secs.checked_add(1).expect("overflow when adding \ - duration to time"); + secs = match secs.checked_add(1) { + Some(ts) => ts, + None => return None, + } } - Timespec { + Some(Timespec { t: syscall::TimeSpec { tv_sec: secs, tv_nsec: nsec as i32, }, - } + }) } fn sub_duration(&self, other: &Duration) -> Timespec { @@ -180,6 +189,10 @@ impl SystemTime { SystemTime { t: self.t.add_duration(other) } } + pub fn checked_add_duration(&self, other: &Duration) -> Option { + self.t.checked_add_duration(other).map(|t| SystemTime { t }) + } + pub fn sub_duration(&self, other: &Duration) -> SystemTime { SystemTime { t: self.t.sub_duration(other) } } diff --git a/src/libstd/sys/unix/time.rs b/src/libstd/sys/unix/time.rs index 0b1fb726357e1..50c3c00382eb5 100644 --- a/src/libstd/sys/unix/time.rs +++ b/src/libstd/sys/unix/time.rs @@ -43,27 +43,36 @@ impl Timespec { } fn add_duration(&self, other: &Duration) -> Timespec { - let mut secs = other + self.checked_add_duration(other).expect("overflow when adding duration to time") + } + + fn checked_add_duration(&self, other: &Duration) -> Option { + let mut secs = match other .as_secs() .try_into() // <- target type would be `libc::time_t` .ok() .and_then(|secs| self.t.tv_sec.checked_add(secs)) - .expect("overflow when adding duration to time"); + { + Some(ts) => ts, + None => return None, + }; // Nano calculations can't overflow because nanos are <1B which fit // in a u32. let mut nsec = other.subsec_nanos() + self.t.tv_nsec as u32; if nsec >= NSEC_PER_SEC as u32 { nsec -= NSEC_PER_SEC as u32; - secs = secs.checked_add(1).expect("overflow when adding \ - duration to time"); + secs = match secs.checked_add(1) { + Some(ts) => ts, + None => return None, + } } - Timespec { + Some(Timespec { t: libc::timespec { tv_sec: secs, tv_nsec: nsec as _, }, - } + }) } fn sub_duration(&self, other: &Duration) -> Timespec { @@ -201,6 +210,10 @@ mod inner { SystemTime { t: self.t.add_duration(other) } } + pub fn checked_add_duration(&self, other: &Duration) -> Option { + self.t.checked_add_duration(other).map(|t| SystemTime { t }) + } + pub fn sub_duration(&self, other: &Duration) -> SystemTime { SystemTime { t: self.t.sub_duration(other) } } @@ -325,6 +338,10 @@ mod inner { SystemTime { t: self.t.add_duration(other) } } + pub fn checked_add_duration(&self, other: &Duration) -> Option { + self.t.checked_add_duration(other).map(|t| SystemTime { t }) + } + pub fn sub_duration(&self, other: &Duration) -> SystemTime { SystemTime { t: self.t.sub_duration(other) } } diff --git a/src/libstd/sys/wasm/time.rs b/src/libstd/sys/wasm/time.rs index e52435e63398f..991e8176edf6d 100644 --- a/src/libstd/sys/wasm/time.rs +++ b/src/libstd/sys/wasm/time.rs @@ -51,6 +51,10 @@ impl SystemTime { SystemTime(self.0 + *other) } + pub fn checked_add_duration(&self, other: &Duration) -> Option { + self.0.checked_add(*other).map(|d| SystemTime(d)) + } + pub fn sub_duration(&self, other: &Duration) -> SystemTime { SystemTime(self.0 - *other) } diff --git a/src/libstd/sys/windows/time.rs b/src/libstd/sys/windows/time.rs index 07e64d386a1c2..8eebe4a85fe16 100644 --- a/src/libstd/sys/windows/time.rs +++ b/src/libstd/sys/windows/time.rs @@ -128,9 +128,13 @@ impl SystemTime { } pub fn add_duration(&self, other: &Duration) -> SystemTime { - let intervals = self.intervals().checked_add(dur2intervals(other)) - .expect("overflow when adding duration to time"); - SystemTime::from_intervals(intervals) + self.checked_add_duration(other).expect("overflow when adding duration to time") + } + + pub fn checked_add_duration(&self, other: &Duration) -> Option { + checked_dur2intervals(other) + .and_then(|d| self.intervals().checked_add(d)) + .map(|i| SystemTime::from_intervals(i)) } pub fn sub_duration(&self, other: &Duration) -> SystemTime { @@ -180,11 +184,15 @@ impl Hash for SystemTime { } } -fn dur2intervals(d: &Duration) -> i64 { +fn checked_dur2intervals(d: &Duration) -> Option { d.as_secs() .checked_mul(INTERVALS_PER_SEC) .and_then(|i| i.checked_add(d.subsec_nanos() as u64 / 100)) .and_then(|i| i.try_into().ok()) +} + +fn dur2intervals(d: &Duration) -> i64 { + checked_dur2intervals(d) .expect("overflow when converting duration to intervals") } diff --git a/src/libstd/time.rs b/src/libstd/time.rs index 90ab349159915..94875d8993d33 100644 --- a/src/libstd/time.rs +++ b/src/libstd/time.rs @@ -357,6 +357,14 @@ impl SystemTime { pub fn elapsed(&self) -> Result { SystemTime::now().duration_since(*self) } + + /// Returns `Some(t)` where `t` is the time `self + duration` if `t` can be represented as + /// `SystemTime` (which means it's inside the bounds of the underlying data structure), `None` + /// otherwise. + #[stable(feature = "time_checked_add", since = "1.32.0")] + pub fn checked_add_duration(&self, duration: &Duration) -> Option { + self.0.checked_add_duration(duration).map(|t| SystemTime(t)) + } } #[stable(feature = "time2", since = "1.8.0")] @@ -557,6 +565,19 @@ mod tests { let one_second_from_epoch2 = UNIX_EPOCH + Duration::new(0, 500_000_000) + Duration::new(0, 500_000_000); assert_eq!(one_second_from_epoch, one_second_from_epoch2); + + // checked_add_duration will not panic on overflow + let mut maybe_t = Some(SystemTime::UNIX_EPOCH); + let max_duration = Duration::from_secs(u64::max_value()); + // in case `SystemTime` can store `>= UNIX_EPOCH + max_duration`. + for _ in 0..2 { + maybe_t = maybe_t.and_then(|t| t.checked_add_duration(&max_duration)); + } + assert_eq!(maybe_t, None); + + // checked_add_duration calculates the right time and will work for another year + let year = Duration::from_secs(60 * 60 * 24 * 365); + assert_eq!(a + year, a.checked_add_duration(&year).unwrap()); } #[test] From 86ef38b3b7a24959ca11a29c79cf921ed5986bc9 Mon Sep 17 00:00:00 2001 From: Sebastian Geisler Date: Sun, 4 Nov 2018 21:23:20 -0800 Subject: [PATCH 02/88] Rename checked_add_duration to checked_add and make it take the duration by value --- src/libstd/time.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/libstd/time.rs b/src/libstd/time.rs index 94875d8993d33..c905af442de1a 100644 --- a/src/libstd/time.rs +++ b/src/libstd/time.rs @@ -361,9 +361,9 @@ impl SystemTime { /// Returns `Some(t)` where `t` is the time `self + duration` if `t` can be represented as /// `SystemTime` (which means it's inside the bounds of the underlying data structure), `None` /// otherwise. - #[stable(feature = "time_checked_add", since = "1.32.0")] - pub fn checked_add_duration(&self, duration: &Duration) -> Option { - self.0.checked_add_duration(duration).map(|t| SystemTime(t)) + #[unstable(feature = "time_checked_add", issue = "55940")] + pub fn checked_add(&self, duration: Duration) -> Option { + self.0.checked_add_duration(&duration).map(|t| SystemTime(t)) } } @@ -571,13 +571,13 @@ mod tests { let max_duration = Duration::from_secs(u64::max_value()); // in case `SystemTime` can store `>= UNIX_EPOCH + max_duration`. for _ in 0..2 { - maybe_t = maybe_t.and_then(|t| t.checked_add_duration(&max_duration)); + maybe_t = maybe_t.and_then(|t| t.checked_add(max_duration)); } assert_eq!(maybe_t, None); // checked_add_duration calculates the right time and will work for another year let year = Duration::from_secs(60 * 60 * 24 * 365); - assert_eq!(a + year, a.checked_add_duration(&year).unwrap()); + assert_eq!(a + year, a.checked_add(year).unwrap()); } #[test] From f2106d0746cdbd04ddad44c35b4e13eeced2a546 Mon Sep 17 00:00:00 2001 From: Sebastian Geisler Date: Thu, 15 Nov 2018 22:56:07 -0800 Subject: [PATCH 03/88] use ? operator instead of match --- src/libstd/sys/redox/time.rs | 13 +++---------- src/libstd/sys/unix/time.rs | 13 +++---------- 2 files changed, 6 insertions(+), 20 deletions(-) diff --git a/src/libstd/sys/redox/time.rs b/src/libstd/sys/redox/time.rs index 5f8799489aa6a..514629282ac2b 100644 --- a/src/libstd/sys/redox/time.rs +++ b/src/libstd/sys/redox/time.rs @@ -46,25 +46,18 @@ impl Timespec { } fn checked_add_duration(&self, other: &Duration) -> Option { - let mut secs = match other + let mut secs = other .as_secs() .try_into() // <- target type would be `i64` .ok() - .and_then(|secs| self.t.tv_sec.checked_add(secs)) - { - Some(ts) => ts, - None => return None, - }; + .and_then(|secs| self.t.tv_sec.checked_add(secs))?; // Nano calculations can't overflow because nanos are <1B which fit // in a u32. let mut nsec = other.subsec_nanos() + self.t.tv_nsec as u32; if nsec >= NSEC_PER_SEC as u32 { nsec -= NSEC_PER_SEC as u32; - secs = match secs.checked_add(1) { - Some(ts) => ts, - None => return None, - } + secs = secs.checked_add(1)?; } Some(Timespec { t: syscall::TimeSpec { diff --git a/src/libstd/sys/unix/time.rs b/src/libstd/sys/unix/time.rs index 50c3c00382eb5..6c7ee3dd922df 100644 --- a/src/libstd/sys/unix/time.rs +++ b/src/libstd/sys/unix/time.rs @@ -47,25 +47,18 @@ impl Timespec { } fn checked_add_duration(&self, other: &Duration) -> Option { - let mut secs = match other + let mut secs = other .as_secs() .try_into() // <- target type would be `libc::time_t` .ok() - .and_then(|secs| self.t.tv_sec.checked_add(secs)) - { - Some(ts) => ts, - None => return None, - }; + .and_then(|secs| self.t.tv_sec.checked_add(secs))?; // Nano calculations can't overflow because nanos are <1B which fit // in a u32. let mut nsec = other.subsec_nanos() + self.t.tv_nsec as u32; if nsec >= NSEC_PER_SEC as u32 { nsec -= NSEC_PER_SEC as u32; - secs = match secs.checked_add(1) { - Some(ts) => ts, - None => return None, - } + secs = secs.checked_add(1)?; } Some(Timespec { t: libc::timespec { From 3d33d05c81f949f743b3441cc1e960f2dc2bd5ac Mon Sep 17 00:00:00 2001 From: Oliver Scherer Date: Sat, 17 Nov 2018 13:55:35 +0100 Subject: [PATCH 04/88] We're looking at the miri memory for constants instead of their initializers' MIR --- src/librustc_mir/monomorphize/collector.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/librustc_mir/monomorphize/collector.rs b/src/librustc_mir/monomorphize/collector.rs index 8e27635dee8c1..92318755ff521 100644 --- a/src/librustc_mir/monomorphize/collector.rs +++ b/src/librustc_mir/monomorphize/collector.rs @@ -178,10 +178,6 @@ //! Some things are not yet fully implemented in the current version of this //! module. //! -//! ### Initializers of Constants and Statics -//! Since no MIR is constructed yet for initializer expressions of constants and -//! statics we cannot inspect these properly. -//! //! ### Const Fns //! Ideally, no mono item should be generated for const fns unless there //! is a call to them that cannot be evaluated at compile time. At the moment From ef99b57b13f9ab04f44dd9c5c325be8f0b6b9dc1 Mon Sep 17 00:00:00 2001 From: Oliver Scherer Date: Sat, 17 Nov 2018 15:05:12 +0100 Subject: [PATCH 05/88] Refactor local monomorphization logic to be easier to comprehend --- src/librustc_mir/monomorphize/collector.rs | 43 +++++++++++----------- 1 file changed, 21 insertions(+), 22 deletions(-) diff --git a/src/librustc_mir/monomorphize/collector.rs b/src/librustc_mir/monomorphize/collector.rs index 92318755ff521..977285b27be38 100644 --- a/src/librustc_mir/monomorphize/collector.rs +++ b/src/librustc_mir/monomorphize/collector.rs @@ -187,7 +187,6 @@ use rustc::hir::{self, CodegenFnAttrFlags}; use rustc::hir::itemlikevisit::ItemLikeVisitor; -use rustc::hir::Node; use rustc::hir::def_id::DefId; use rustc::mir::interpret::{AllocId, ConstValue}; use rustc::middle::lang_items::{ExchangeMallocFnLangItem, StartFnLangItem}; @@ -737,27 +736,27 @@ fn should_monomorphize_locally<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, instance: ty::InstanceDef::CloneShim(..) => return true }; - return match tcx.hir.get_if_local(def_id) { - Some(Node::ForeignItem(..)) => { - false // foreign items are linked against, not codegened. - } - Some(_) => true, - None => { - if tcx.is_reachable_non_generic(def_id) || - tcx.is_foreign_item(def_id) || - is_available_upstream_generic(tcx, def_id, instance.substs) - { - // We can link to the item in question, no instance needed - // in this crate - false - } else { - if !tcx.is_mir_available(def_id) { - bug!("Cannot create local mono-item for {:?}", def_id) - } - true - } - } - }; + if tcx.is_foreign_item(def_id) { + // We can always link to foreign items + return false; + } + + if def_id.is_local() { + // local items cannot be referred to locally without monomorphizing them locally + return true; + } + + if tcx.is_reachable_non_generic(def_id) || + is_available_upstream_generic(tcx, def_id, instance.substs) { + // We can link to the item in question, no instance needed + // in this crate + return false; + } + + if !tcx.is_mir_available(def_id) { + bug!("Cannot create local mono-item for {:?}", def_id) + } + return true; fn is_available_upstream_generic<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId, From eb18ddd8f4fede5e12a97f724014a8406dd75881 Mon Sep 17 00:00:00 2001 From: Oliver Scherer Date: Sat, 17 Nov 2018 15:36:06 +0100 Subject: [PATCH 06/88] Don't auto-inline `const fn` --- src/librustc/ty/instance.rs | 5 +---- .../item-collection/unreferenced-const-fn.rs | 3 +-- src/test/ui/consts/auxiliary/const_fn_lib.rs | 20 ++++++++++++++++++- .../consts/const_fn_return_nested_fn_ptr.rs | 10 ++++++++++ 4 files changed, 31 insertions(+), 7 deletions(-) create mode 100644 src/test/ui/consts/const_fn_return_nested_fn_ptr.rs diff --git a/src/librustc/ty/instance.rs b/src/librustc/ty/instance.rs index b6691df39c120..411a6e7e6238e 100644 --- a/src/librustc/ty/instance.rs +++ b/src/librustc/ty/instance.rs @@ -173,10 +173,7 @@ impl<'tcx> InstanceDef<'tcx> { // available to normal end-users. return true } - let codegen_fn_attrs = tcx.codegen_fn_attrs(self.def_id()); - // need to use `is_const_fn_raw` since we don't really care if the user can use it as a - // const fn, just whether the function should be inlined - codegen_fn_attrs.requests_inline() || tcx.is_const_fn_raw(self.def_id()) + tcx.codegen_fn_attrs(self.def_id()).requests_inline() } } diff --git a/src/test/codegen-units/item-collection/unreferenced-const-fn.rs b/src/test/codegen-units/item-collection/unreferenced-const-fn.rs index c4a49fd4ec4d6..5e181870fed65 100644 --- a/src/test/codegen-units/item-collection/unreferenced-const-fn.rs +++ b/src/test/codegen-units/item-collection/unreferenced-const-fn.rs @@ -11,11 +11,10 @@ // ignore-tidy-linelength // compile-flags:-Zprint-mono-items=lazy -// NB: We do not expect *any* monomorphization to be generated here. - #![deny(dead_code)] #![crate_type = "rlib"] +//~ MONO_ITEM fn unreferenced_const_fn::foo[0] @@ unreferenced_const_fn-cgu.0[External] pub const fn foo(x: u32) -> u32 { x + 0xf00 } diff --git a/src/test/ui/consts/auxiliary/const_fn_lib.rs b/src/test/ui/consts/auxiliary/const_fn_lib.rs index 5063c8d1d1f54..72369aae97ce3 100644 --- a/src/test/ui/consts/auxiliary/const_fn_lib.rs +++ b/src/test/ui/consts/auxiliary/const_fn_lib.rs @@ -10,6 +10,24 @@ // Crate that exports a const fn. Used for testing cross-crate. +#![feature(const_fn)] #![crate_type="rlib"] -pub const fn foo() -> usize { 22 } //~ ERROR const fn is unstable +pub const fn foo() -> usize { 22 } + +pub const fn bar() -> fn() { + fn x() {} + x +} + +#[inline] +pub const fn bar_inlined() -> fn() { + fn x() {} + x +} + +#[inline(always)] +pub const fn bar_inlined_always() -> fn() { + fn x() {} + x +} diff --git a/src/test/ui/consts/const_fn_return_nested_fn_ptr.rs b/src/test/ui/consts/const_fn_return_nested_fn_ptr.rs new file mode 100644 index 0000000000000..c7617c9c7ad03 --- /dev/null +++ b/src/test/ui/consts/const_fn_return_nested_fn_ptr.rs @@ -0,0 +1,10 @@ +// compile-pass +// aux-build:const_fn_lib.rs + +extern crate const_fn_lib; + +fn main() { + const_fn_lib::bar()(); + const_fn_lib::bar_inlined()(); + const_fn_lib::bar_inlined_always()(); +} From b16985a3546844e14842b9c15ddc50aff9f1adda Mon Sep 17 00:00:00 2001 From: Matthew Jasper Date: Sat, 10 Nov 2018 23:02:13 +0000 Subject: [PATCH 07/88] Remove mir::StatementKind::EndRegion Since lexical MIR borrow check is gone, and validation no longer uses these, they can be removed. --- src/librustc/ich/impls_mir.rs | 3 - src/librustc/mir/mod.rs | 8 - src/librustc/mir/visit.rs | 1 - src/librustc/session/config.rs | 2 - src/librustc/ty/context.rs | 7 - src/librustc_codegen_ssa/mir/statement.rs | 1 - src/librustc_mir/borrow_check/mod.rs | 4 - .../borrow_check/nll/invalidation.rs | 2 - src/librustc_mir/borrow_check/nll/renumber.rs | 14 +- .../borrow_check/nll/type_check/mod.rs | 1 - src/librustc_mir/build/cfg.rs | 26 --- src/librustc_mir/build/scope.rs | 15 +- src/librustc_mir/dataflow/impls/borrows.rs | 6 +- .../dataflow/move_paths/builder.rs | 1 - src/librustc_mir/interpret/step.rs | 1 - src/librustc_mir/transform/check_unsafety.rs | 1 - .../transform/cleanup_post_borrowck.rs | 109 ++----------- src/librustc_mir/transform/erase_regions.rs | 6 +- src/librustc_mir/transform/mod.rs | 3 - src/librustc_mir/transform/qualify_consts.rs | 1 - .../transform/qualify_min_const_fn.rs | 1 - .../transform/remove_noop_landing_pads.rs | 5 +- src/librustc_mir/transform/rustc_peek.rs | 1 - src/librustc_passes/mir_stats.rs | 1 - src/test/mir-opt/end_region_1.rs | 42 ----- src/test/mir-opt/end_region_2.rs | 81 --------- src/test/mir-opt/end_region_3.rs | 81 --------- src/test/mir-opt/end_region_4.rs | 83 ---------- src/test/mir-opt/end_region_5.rs | 77 --------- src/test/mir-opt/end_region_6.rs | 83 ---------- src/test/mir-opt/end_region_7.rs | 91 ----------- src/test/mir-opt/end_region_8.rs | 83 ---------- src/test/mir-opt/end_region_9.rs | 97 ----------- src/test/mir-opt/end_region_cyclic.rs | 141 ---------------- .../end_region_destruction_extents_1.rs | 154 ------------------ src/test/mir-opt/issue-43457.rs | 55 ------- src/test/mir-opt/loop_test.rs | 2 +- src/test/run-pass/issues/issue-16671.rs | 2 +- src/test/ui/borrowck/immutable-arg.rs | 2 +- src/test/ui/issues/issue-45697-1.rs | 2 +- src/test/ui/issues/issue-45697.rs | 2 +- src/test/ui/issues/issue-46023.rs | 2 +- src/test/ui/issues/issue-46471-1.rs | 2 +- src/test/ui/issues/issue-46471.rs | 2 +- src/test/ui/issues/issue-46472.rs | 2 +- .../ui/moves/moves-based-on-type-tuple.rs | 2 +- .../maybe-initialized-drop-uninitialized.rs | 2 +- .../maybe-initialized-drop-with-fragment.rs | 2 +- ...lized-drop-with-uninitialized-fragments.rs | 2 +- src/test/ui/nll/maybe-initialized-drop.rs | 2 +- 50 files changed, 33 insertions(+), 1283 deletions(-) delete mode 100644 src/test/mir-opt/end_region_1.rs delete mode 100644 src/test/mir-opt/end_region_2.rs delete mode 100644 src/test/mir-opt/end_region_3.rs delete mode 100644 src/test/mir-opt/end_region_4.rs delete mode 100644 src/test/mir-opt/end_region_5.rs delete mode 100644 src/test/mir-opt/end_region_6.rs delete mode 100644 src/test/mir-opt/end_region_7.rs delete mode 100644 src/test/mir-opt/end_region_8.rs delete mode 100644 src/test/mir-opt/end_region_9.rs delete mode 100644 src/test/mir-opt/end_region_cyclic.rs delete mode 100644 src/test/mir-opt/end_region_destruction_extents_1.rs delete mode 100644 src/test/mir-opt/issue-43457.rs diff --git a/src/librustc/ich/impls_mir.rs b/src/librustc/ich/impls_mir.rs index d6ad74f16a92b..d98bb82aabad1 100644 --- a/src/librustc/ich/impls_mir.rs +++ b/src/librustc/ich/impls_mir.rs @@ -217,9 +217,6 @@ for mir::StatementKind<'gcx> { mir::StatementKind::StorageDead(ref place) => { place.hash_stable(hcx, hasher); } - mir::StatementKind::EndRegion(ref region_scope) => { - region_scope.hash_stable(hcx, hasher); - } mir::StatementKind::EscapeToRaw(ref place) => { place.hash_stable(hcx, hasher); } diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs index d8e45c881f555..a9ea1c9a10931 100644 --- a/src/librustc/mir/mod.rs +++ b/src/librustc/mir/mod.rs @@ -15,7 +15,6 @@ use hir::def::CtorKind; use hir::def_id::DefId; use hir::{self, HirId, InlineAsm}; -use middle::region; use mir::interpret::{ConstValue, EvalErrorKind, Scalar}; use mir::visit::MirVisitable; use rustc_apfloat::ieee::{Double, Single}; @@ -1789,10 +1788,6 @@ pub enum StatementKind<'tcx> { /// for more details. EscapeToRaw(Operand<'tcx>), - /// Mark one terminating point of a region scope (i.e. static region). - /// (The starting point(s) arise implicitly from borrows.) - EndRegion(region::Scope), - /// Encodes a user's type ascription. These need to be preserved /// intact so that NLL can respect them. For example: /// @@ -1846,8 +1841,6 @@ impl<'tcx> Debug for Statement<'tcx> { match self.kind { Assign(ref place, ref rv) => write!(fmt, "{:?} = {:?}", place, rv), FakeRead(ref cause, ref place) => write!(fmt, "FakeRead({:?}, {:?})", cause, place), - // (reuse lifetime rendering policy from ppaux.) - EndRegion(ref ce) => write!(fmt, "EndRegion({})", ty::ReScope(*ce)), Retag { fn_entry, ref place } => write!(fmt, "Retag({}{:?})", if fn_entry { "[fn entry] " } else { "" }, place), EscapeToRaw(ref place) => write!(fmt, "EscapeToRaw({:?})", place), @@ -3028,7 +3021,6 @@ EnumTypeFoldableImpl! { (StatementKind::InlineAsm) { asm, outputs, inputs }, (StatementKind::Retag) { fn_entry, place }, (StatementKind::EscapeToRaw)(place), - (StatementKind::EndRegion)(a), (StatementKind::AscribeUserType)(a, v, b), (StatementKind::Nop), } diff --git a/src/librustc/mir/visit.rs b/src/librustc/mir/visit.rs index 2a994ee0509c2..0c9b06a8d8c7d 100644 --- a/src/librustc/mir/visit.rs +++ b/src/librustc/mir/visit.rs @@ -377,7 +377,6 @@ macro_rules! make_mir_visitor { location ); } - StatementKind::EndRegion(_) => {} StatementKind::SetDiscriminant{ ref $($mutability)* place, .. } => { self.visit_place( place, diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs index ee6d970750adf..92a808bbda25a 100644 --- a/src/librustc/session/config.rs +++ b/src/librustc/session/config.rs @@ -1149,8 +1149,6 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, "when debug-printing compiler state, do not include spans"), // o/w tests have closure@path identify_regions: bool = (false, parse_bool, [UNTRACKED], "make unnamed regions display as '# (where # is some non-ident unique id)"), - emit_end_regions: bool = (false, parse_bool, [UNTRACKED], - "emit EndRegion as part of MIR; enable transforms that solely process EndRegion"), borrowck: Option = (None, parse_opt_string, [UNTRACKED], "select which borrowck is used (`ast`, `mir`, `migrate`, or `compare`)"), two_phase_borrows: bool = (false, parse_bool, [UNTRACKED], diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs index cdfe8f53b854b..1d81ab199619d 100644 --- a/src/librustc/ty/context.rs +++ b/src/librustc/ty/context.rs @@ -1540,13 +1540,6 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { } } - /// Should we emit EndRegion MIR statements? These are consumed by - /// MIR borrowck, but not when NLL is used. - pub fn emit_end_regions(self) -> bool { - self.sess.opts.debugging_opts.emit_end_regions || - self.use_mir_borrowck() - } - #[inline] pub fn local_crate_exports_generics(self) -> bool { debug_assert!(self.sess.opts.share_generics()); diff --git a/src/librustc_codegen_ssa/mir/statement.rs b/src/librustc_codegen_ssa/mir/statement.rs index a69474142ab58..0d058c85f333b 100644 --- a/src/librustc_codegen_ssa/mir/statement.rs +++ b/src/librustc_codegen_ssa/mir/statement.rs @@ -105,7 +105,6 @@ impl<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { bx } mir::StatementKind::FakeRead(..) | - mir::StatementKind::EndRegion(..) | mir::StatementKind::Retag { .. } | mir::StatementKind::EscapeToRaw { .. } | mir::StatementKind::AscribeUserType(..) | diff --git a/src/librustc_mir/borrow_check/mod.rs b/src/librustc_mir/borrow_check/mod.rs index f0d3b863f78b2..8819908de6ae1 100644 --- a/src/librustc_mir/borrow_check/mod.rs +++ b/src/librustc_mir/borrow_check/mod.rs @@ -592,10 +592,6 @@ impl<'cx, 'gcx, 'tcx> DataflowResultsConsumer<'cx, 'tcx> for MirBorrowckCtxt<'cx self.consume_operand(context, (input, span), flow_state); } } - StatementKind::EndRegion(ref _rgn) => { - // ignored when consuming results (update to - // flow_state already handled). - } StatementKind::Nop | StatementKind::AscribeUserType(..) | StatementKind::Retag { .. } diff --git a/src/librustc_mir/borrow_check/nll/invalidation.rs b/src/librustc_mir/borrow_check/nll/invalidation.rs index 576509c0fddda..8af23a8813a9e 100644 --- a/src/librustc_mir/borrow_check/nll/invalidation.rs +++ b/src/librustc_mir/borrow_check/nll/invalidation.rs @@ -132,8 +132,6 @@ impl<'cx, 'tcx, 'gcx> Visitor<'tcx> for InvalidationGenerator<'cx, 'tcx, 'gcx> { self.consume_operand(context, input); } } - // EndRegion matters to older NLL/MIR AST borrowck, not to alias NLL - StatementKind::EndRegion(..) | StatementKind::Nop | StatementKind::AscribeUserType(..) | StatementKind::Retag { .. } | diff --git a/src/librustc_mir/borrow_check/nll/renumber.rs b/src/librustc_mir/borrow_check/nll/renumber.rs index 363afb87ed909..e9f749ac092d2 100644 --- a/src/librustc_mir/borrow_check/nll/renumber.rs +++ b/src/librustc_mir/borrow_check/nll/renumber.rs @@ -10,7 +10,7 @@ use rustc::ty::subst::Substs; use rustc::ty::{self, ClosureSubsts, GeneratorSubsts, Ty, TypeFoldable}; -use rustc::mir::{BasicBlock, Location, Mir, Statement, StatementKind, UserTypeAnnotation}; +use rustc::mir::{Location, Mir, UserTypeAnnotation}; use rustc::mir::visit::{MutVisitor, TyContext}; use rustc::infer::{InferCtxt, NLLRegionVariableOrigin}; @@ -119,16 +119,4 @@ impl<'a, 'gcx, 'tcx> MutVisitor<'tcx> for NLLVisitor<'a, 'gcx, 'tcx> { debug!("visit_closure_substs: substs={:?}", substs); } - - fn visit_statement( - &mut self, - block: BasicBlock, - statement: &mut Statement<'tcx>, - location: Location, - ) { - if let StatementKind::EndRegion(_) = statement.kind { - statement.kind = StatementKind::Nop; - } - self.super_statement(block, statement, location); - } } diff --git a/src/librustc_mir/borrow_check/nll/type_check/mod.rs b/src/librustc_mir/borrow_check/nll/type_check/mod.rs index 06dfd4bc2cc1f..b978d8c9d0ae0 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs @@ -1314,7 +1314,6 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { | StatementKind::StorageLive(..) | StatementKind::StorageDead(..) | StatementKind::InlineAsm { .. } - | StatementKind::EndRegion(_) | StatementKind::Retag { .. } | StatementKind::EscapeToRaw { .. } | StatementKind::Nop => {} diff --git a/src/librustc_mir/build/cfg.rs b/src/librustc_mir/build/cfg.rs index 619ebb1675ca6..2efb75c232d2e 100644 --- a/src/librustc_mir/build/cfg.rs +++ b/src/librustc_mir/build/cfg.rs @@ -14,9 +14,7 @@ //! Routines for manipulating the control-flow graph. use build::CFG; -use rustc::middle::region; use rustc::mir::*; -use rustc::ty::TyCtxt; impl<'tcx> CFG<'tcx> { pub fn block_data(&self, blk: BasicBlock) -> &BasicBlockData<'tcx> { @@ -45,30 +43,6 @@ impl<'tcx> CFG<'tcx> { self.block_data_mut(block).statements.push(statement); } - pub fn push_end_region<'a, 'gcx:'a+'tcx>(&mut self, - tcx: TyCtxt<'a, 'gcx, 'tcx>, - block: BasicBlock, - source_info: SourceInfo, - region_scope: region::Scope) { - if tcx.emit_end_regions() { - if let region::ScopeData::CallSite = region_scope.data { - // The CallSite scope (aka the root scope) is sort of weird, in that it is - // supposed to "separate" the "interior" and "exterior" of a closure. Being - // that, it is not really a part of the region hierarchy, but for some - // reason it *is* considered a part of it. - // - // It should die a hopefully painful death with NLL, so let's leave this hack - // for now so that nobody can complain about soundness. - return - } - - self.push(block, Statement { - source_info, - kind: StatementKind::EndRegion(region_scope), - }); - } - } - pub fn push_assign(&mut self, block: BasicBlock, source_info: SourceInfo, diff --git a/src/librustc_mir/build/scope.rs b/src/librustc_mir/build/scope.rs index 99badd5a03fe6..1a8f8fbbb68db 100644 --- a/src/librustc_mir/build/scope.rs +++ b/src/librustc_mir/build/scope.rs @@ -90,7 +90,7 @@ should go to. use build::{BlockAnd, BlockAndExtension, Builder, CFG}; use hair::LintLevel; use rustc::middle::region; -use rustc::ty::{Ty, TyCtxt}; +use rustc::ty::Ty; use rustc::hir; use rustc::hir::def_id::LOCAL_CRATE; use rustc::mir::*; @@ -381,7 +381,6 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { let scope = self.scopes.pop().unwrap(); assert_eq!(scope.region_scope, region_scope.0); - self.cfg.push_end_region(self.hir.tcx(), block, region_scope.1, scope.region_scope); let resume_block = self.resume_block(); unpack!(block = build_scope_drops(&mut self.cfg, resume_block, @@ -439,9 +438,6 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { b }; - // End all regions for scopes out of which we are breaking. - self.cfg.push_end_region(self.hir.tcx(), block, region_scope.1, scope.region_scope); - unpack!(block = build_scope_drops(&mut self.cfg, resume_block, scope, @@ -491,9 +487,6 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { b }; - // End all regions for scopes out of which we are breaking. - self.cfg.push_end_region(self.hir.tcx(), block, src_info, scope.region_scope); - unpack!(block = build_scope_drops(&mut self.cfg, resume_block, scope, @@ -790,7 +783,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { if scopes.iter().any(|scope| scope.needs_cleanup) { for scope in scopes.iter_mut() { - target = build_diverge_scope(self.hir.tcx(), cfg, scope.region_scope_span, + target = build_diverge_scope(cfg, scope.region_scope_span, scope, target, generator_drop); } } @@ -950,8 +943,7 @@ fn build_scope_drops<'tcx>(cfg: &mut CFG<'tcx>, block.unit() } -fn build_diverge_scope<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>, - cfg: &mut CFG<'tcx>, +fn build_diverge_scope<'a, 'gcx, 'tcx>(cfg: &mut CFG<'tcx>, span: Span, scope: &mut Scope<'tcx>, mut target: BasicBlock, @@ -1018,7 +1010,6 @@ fn build_diverge_scope<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>, cached_block } else { let block = cfg.start_new_cleanup_block(); - cfg.push_end_region(tcx, block, source_info(span), scope.region_scope); cfg.terminate(block, source_info(span), TerminatorKind::Goto { target }); *cached_block = Some(block); block diff --git a/src/librustc_mir/dataflow/impls/borrows.rs b/src/librustc_mir/dataflow/impls/borrows.rs index 811da9e1acca7..5e84aa05d386c 100644 --- a/src/librustc_mir/dataflow/impls/borrows.rs +++ b/src/librustc_mir/dataflow/impls/borrows.rs @@ -190,8 +190,7 @@ impl<'a, 'gcx, 'tcx> Borrows<'a, 'gcx, 'tcx> { } /// Add all borrows to the kill set, if those borrows are out of scope at `location`. - /// That means either they went out of either a nonlexical scope, if we care about those - /// at the moment, or the location represents a lexical EndRegion + /// That means they went out of a nonlexical scope fn kill_loans_out_of_scope_at_location(&self, sets: &mut BlockSets, location: Location) { @@ -252,9 +251,6 @@ impl<'a, 'gcx, 'tcx> BitDenotation for Borrows<'a, 'gcx, 'tcx> { }); match stmt.kind { - mir::StatementKind::EndRegion(_) => { - } - mir::StatementKind::Assign(ref lhs, ref rhs) => { // Make sure there are no remaining borrows for variables // that are assigned over. diff --git a/src/librustc_mir/dataflow/move_paths/builder.rs b/src/librustc_mir/dataflow/move_paths/builder.rs index 0e2376d201fd6..3796b1cc4b0c8 100644 --- a/src/librustc_mir/dataflow/move_paths/builder.rs +++ b/src/librustc_mir/dataflow/move_paths/builder.rs @@ -301,7 +301,6 @@ impl<'b, 'a, 'gcx, 'tcx> Gatherer<'b, 'a, 'gcx, 'tcx> { span_bug!(stmt.source_info.span, "SetDiscriminant should not exist during borrowck"); } - StatementKind::EndRegion(..) | StatementKind::Retag { .. } | StatementKind::EscapeToRaw { .. } | StatementKind::AscribeUserType(..) | diff --git a/src/librustc_mir/interpret/step.rs b/src/librustc_mir/interpret/step.rs index ac13e5982dae3..8814118f65be4 100644 --- a/src/librustc_mir/interpret/step.rs +++ b/src/librustc_mir/interpret/step.rs @@ -129,7 +129,6 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> } // Statements we do not track. - EndRegion(..) => {} AscribeUserType(..) => {} // Defined to do nothing. These are added by optimization passes, to avoid changing the diff --git a/src/librustc_mir/transform/check_unsafety.rs b/src/librustc_mir/transform/check_unsafety.rs index 4ebeebca2273b..3404772f8255f 100644 --- a/src/librustc_mir/transform/check_unsafety.rs +++ b/src/librustc_mir/transform/check_unsafety.rs @@ -112,7 +112,6 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> { StatementKind::SetDiscriminant { .. } | StatementKind::StorageLive(..) | StatementKind::StorageDead(..) | - StatementKind::EndRegion(..) | StatementKind::Retag { .. } | StatementKind::EscapeToRaw { .. } | StatementKind::AscribeUserType(..) | diff --git a/src/librustc_mir/transform/cleanup_post_borrowck.rs b/src/librustc_mir/transform/cleanup_post_borrowck.rs index 98311444e2871..c0edd3926d32d 100644 --- a/src/librustc_mir/transform/cleanup_post_borrowck.rs +++ b/src/librustc_mir/transform/cleanup_post_borrowck.rs @@ -10,111 +10,26 @@ //! This module provides two passes: //! -//! - `CleanEndRegions`, that reduces the set of `EndRegion` statements -//! in the MIR. -//! - `CleanAscribeUserType`, that replaces all `AscribeUserType` statements -//! with `Nop`. +//! - [CleanAscribeUserType], that replaces all +//! [StatementKind::AscribeUserType] statements with [StatementKind::Nop]. +//! - [CleanFakeReadsAndBorrows], that replaces all [FakeRead] statements and +//! borrows that are read by [FakeReadCause::ForMatchGuard] fake reads with +//! [StatementKind::Nop]. //! -//! The `CleanEndRegions` "pass" is actually implemented as two +//! The [CleanFakeReadsAndBorrows] "pass" is actually implemented as two //! traversals (aka visits) of the input MIR. The first traversal, -//! `GatherBorrowedRegions`, finds all of the regions in the MIR -//! that are involved in a borrow. -//! -//! The second traversal, `DeleteTrivialEndRegions`, walks over the -//! MIR and removes any `EndRegion` that is applied to a region that -//! was not seen in the previous pass. -//! -//! The `CleanAscribeUserType` pass runs at a distinct time from the -//! `CleanEndRegions` pass. It is important that the `CleanAscribeUserType` -//! pass runs after the MIR borrowck so that the NLL type checker can -//! perform the type assertion when it encounters the `AscribeUserType` -//! statements. +//! [DeleteAndRecordFakeReads], deletes the fake reads and finds the temporaries +//! read by [ForMatchGuard] reads, and [DeleteFakeBorrows] deletes the +//! initialization of those temporaries. use rustc_data_structures::fx::FxHashSet; -use rustc::middle::region; use rustc::mir::{BasicBlock, FakeReadCause, Local, Location, Mir, Place}; -use rustc::mir::{Rvalue, Statement, StatementKind}; -use rustc::mir::visit::{MutVisitor, Visitor, TyContext}; -use rustc::ty::{Ty, RegionKind, TyCtxt}; -use smallvec::smallvec; +use rustc::mir::{Statement, StatementKind}; +use rustc::mir::visit::MutVisitor; +use rustc::ty::TyCtxt; use transform::{MirPass, MirSource}; -pub struct CleanEndRegions; - -#[derive(Default)] -struct GatherBorrowedRegions { - seen_regions: FxHashSet, -} - -struct DeleteTrivialEndRegions<'a> { - seen_regions: &'a FxHashSet, -} - -impl MirPass for CleanEndRegions { - fn run_pass<'a, 'tcx>(&self, - tcx: TyCtxt<'a, 'tcx, 'tcx>, - _source: MirSource, - mir: &mut Mir<'tcx>) { - if !tcx.emit_end_regions() { return; } - - let mut gather = GatherBorrowedRegions::default(); - gather.visit_mir(mir); - - let mut delete = DeleteTrivialEndRegions { seen_regions: &mut gather.seen_regions }; - delete.visit_mir(mir); - } -} - -impl<'tcx> Visitor<'tcx> for GatherBorrowedRegions { - fn visit_rvalue(&mut self, - rvalue: &Rvalue<'tcx>, - location: Location) { - // Gather regions that are used for borrows - if let Rvalue::Ref(r, _, _) = *rvalue { - if let RegionKind::ReScope(ce) = *r { - self.seen_regions.insert(ce); - } - } - self.super_rvalue(rvalue, location); - } - - fn visit_ty(&mut self, ty: &Ty<'tcx>, _: TyContext) { - // Gather regions that occur in types - let mut regions = smallvec![]; - for t in ty.walk() { - t.push_regions(&mut regions); - } - for re in regions { - match *re { - RegionKind::ReScope(ce) => { self.seen_regions.insert(ce); } - _ => {}, - } - } - self.super_ty(ty); - } -} - -impl<'a, 'tcx> MutVisitor<'tcx> for DeleteTrivialEndRegions<'a> { - fn visit_statement(&mut self, - block: BasicBlock, - statement: &mut Statement<'tcx>, - location: Location) { - let mut delete_it = false; - - if let StatementKind::EndRegion(ref region_scope) = statement.kind { - if !self.seen_regions.contains(region_scope) { - delete_it = true; - } - } - - if delete_it { - statement.make_nop(); - } - self.super_statement(block, statement, location); - } -} - pub struct CleanAscribeUserType; pub struct DeleteAscribeUserType; diff --git a/src/librustc_mir/transform/erase_regions.rs b/src/librustc_mir/transform/erase_regions.rs index 6351a6b40cb03..a5b5a7e86d21b 100644 --- a/src/librustc_mir/transform/erase_regions.rs +++ b/src/librustc_mir/transform/erase_regions.rs @@ -12,7 +12,7 @@ //! We want to do this once just before codegen, so codegen does not have to take //! care erasing regions all over the place. //! NOTE: We do NOT erase regions of statements that are relevant for -//! "types-as-contracts"-validation, namely, AcquireValid, ReleaseValid, and EndRegion. +//! "types-as-contracts"-validation, namely, AcquireValid, ReleaseValid use rustc::ty::subst::Substs; use rustc::ty::{self, Ty, TyCtxt}; @@ -54,10 +54,6 @@ impl<'a, 'tcx> MutVisitor<'tcx> for EraseRegionsVisitor<'a, 'tcx> { block: BasicBlock, statement: &mut Statement<'tcx>, location: Location) { - if let StatementKind::EndRegion(_) = statement.kind { - statement.kind = StatementKind::Nop; - } - self.super_statement(block, statement, location); } } diff --git a/src/librustc_mir/transform/mod.rs b/src/librustc_mir/transform/mod.rs index 92cfcb3fd56cb..0a83026fb004a 100644 --- a/src/librustc_mir/transform/mod.rs +++ b/src/librustc_mir/transform/mod.rs @@ -209,9 +209,6 @@ fn mir_const<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> &'tcx Stea let mut mir = tcx.mir_built(def_id).steal(); run_passes(tcx, &mut mir, def_id, MirPhase::Const, &[ - // Remove all `EndRegion` statements that are not involved in borrows. - &cleanup_post_borrowck::CleanEndRegions, - // What we need to do constant evaluation. &simplify::SimplifyCfg::new("initial"), &type_check::TypeckMir, diff --git a/src/librustc_mir/transform/qualify_consts.rs b/src/librustc_mir/transform/qualify_consts.rs index 00309b0a3e905..816e85948851e 100644 --- a/src/librustc_mir/transform/qualify_consts.rs +++ b/src/librustc_mir/transform/qualify_consts.rs @@ -1168,7 +1168,6 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { StatementKind::StorageLive(_) | StatementKind::StorageDead(_) | StatementKind::InlineAsm {..} | - StatementKind::EndRegion(_) | StatementKind::Retag { .. } | StatementKind::EscapeToRaw { .. } | StatementKind::AscribeUserType(..) | diff --git a/src/librustc_mir/transform/qualify_min_const_fn.rs b/src/librustc_mir/transform/qualify_min_const_fn.rs index ed13063cfdf17..e567a79d7f21e 100644 --- a/src/librustc_mir/transform/qualify_min_const_fn.rs +++ b/src/librustc_mir/transform/qualify_min_const_fn.rs @@ -243,7 +243,6 @@ fn check_statement( | StatementKind::StorageDead(_) | StatementKind::Retag { .. } | StatementKind::EscapeToRaw { .. } - | StatementKind::EndRegion(_) | StatementKind::AscribeUserType(..) | StatementKind::Nop => Ok(()), } diff --git a/src/librustc_mir/transform/remove_noop_landing_pads.rs b/src/librustc_mir/transform/remove_noop_landing_pads.rs index 445ffbbcf3407..a31d12baed2b4 100644 --- a/src/librustc_mir/transform/remove_noop_landing_pads.rs +++ b/src/librustc_mir/transform/remove_noop_landing_pads.rs @@ -52,12 +52,9 @@ impl RemoveNoopLandingPads { StatementKind::FakeRead(..) | StatementKind::StorageLive(_) | StatementKind::StorageDead(_) | - StatementKind::EndRegion(_) | StatementKind::AscribeUserType(..) | StatementKind::Nop => { - // These are all nops in a landing pad (there's some - // borrowck interaction between EndRegion and storage - // instructions, but this should all run after borrowck). + // These are all nops in a landing pad } StatementKind::Assign(Place::Local(_), box Rvalue::Use(_)) => { diff --git a/src/librustc_mir/transform/rustc_peek.rs b/src/librustc_mir/transform/rustc_peek.rs index 8f026c706fdf7..f852195b8351a 100644 --- a/src/librustc_mir/transform/rustc_peek.rs +++ b/src/librustc_mir/transform/rustc_peek.rs @@ -161,7 +161,6 @@ fn each_block<'a, 'tcx, O>(tcx: TyCtxt<'a, 'tcx, 'tcx>, mir::StatementKind::StorageLive(_) | mir::StatementKind::StorageDead(_) | mir::StatementKind::InlineAsm { .. } | - mir::StatementKind::EndRegion(_) | mir::StatementKind::Retag { .. } | mir::StatementKind::EscapeToRaw { .. } | mir::StatementKind::AscribeUserType(..) | diff --git a/src/librustc_passes/mir_stats.rs b/src/librustc_passes/mir_stats.rs index 68840ed4a4804..fb37f03a1cc41 100644 --- a/src/librustc_passes/mir_stats.rs +++ b/src/librustc_passes/mir_stats.rs @@ -83,7 +83,6 @@ impl<'a, 'tcx> mir_visit::Visitor<'tcx> for StatCollector<'a, 'tcx> { self.record(match statement.kind { StatementKind::Assign(..) => "StatementKind::Assign", StatementKind::FakeRead(..) => "StatementKind::FakeRead", - StatementKind::EndRegion(..) => "StatementKind::EndRegion", StatementKind::Retag { .. } => "StatementKind::Retag", StatementKind::EscapeToRaw { .. } => "StatementKind::EscapeToRaw", StatementKind::SetDiscriminant { .. } => "StatementKind::SetDiscriminant", diff --git a/src/test/mir-opt/end_region_1.rs b/src/test/mir-opt/end_region_1.rs deleted file mode 100644 index dd1c2bd51260b..0000000000000 --- a/src/test/mir-opt/end_region_1.rs +++ /dev/null @@ -1,42 +0,0 @@ -// 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: -Z identify_regions -Z emit-end-regions -// ignore-tidy-linelength - -// This is just about the simplest program that exhibits an EndRegion. - -fn main() { - let a = 3; - let b = &a; -} - -// END RUST SOURCE -// START rustc.main.SimplifyCfg-qualify-consts.after.mir -// let mut _0: (); -// ... -// let _2: &'11_1rs i32; -// ... -// let _1: i32; -// ... -// bb0: { -// StorageLive(_1); -// _1 = const 3i32; -// FakeRead(ForLet, _1); -// StorageLive(_2); -// _2 = &'11_1rs _1; -// FakeRead(ForLet, _2); -// _0 = (); -// EndRegion('11_1rs); -// StorageDead(_2); -// StorageDead(_1); -// return; -// } -// END rustc.main.SimplifyCfg-qualify-consts.after.mir diff --git a/src/test/mir-opt/end_region_2.rs b/src/test/mir-opt/end_region_2.rs deleted file mode 100644 index 6b0a28b811007..0000000000000 --- a/src/test/mir-opt/end_region_2.rs +++ /dev/null @@ -1,81 +0,0 @@ -// 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: -Z identify_regions -Z emit-end-regions -// ignore-tidy-linelength - -// We will EndRegion for borrows in a loop that occur before break but -// not those after break. - -fn main() { - loop { - let a = true; - let b = &a; - if a { break; } - let c = &a; - } -} - -// END RUST SOURCE -// START rustc.main.SimplifyCfg-qualify-consts.after.mir -// let mut _0: (); -// ... -// let _7: &'26_3rs bool; -// ... -// let _3: &'26_1rs bool; -// ... -// let _2: bool; -// ... -// let mut _4: (); -// let mut _5: bool; -// ... -// bb0: { -// goto -> bb1; -// } -// bb1: { -// falseUnwind -> [real: bb2, cleanup: bb3]; -// } -// bb2: { -// StorageLive(_2); -// _2 = const true; -// FakeRead(ForLet, _2); -// StorageLive(_3); -// _3 = &'26_1rs _2; -// FakeRead(ForLet, _3); -// StorageLive(_5); -// _5 = _2; -// switchInt(move _5) -> [false: bb5, otherwise: bb4]; -// } -// bb3: { -// ... -// } -// bb4: { -// _0 = (); -// StorageDead(_5); -// EndRegion('26_1rs); -// StorageDead(_3); -// StorageDead(_2); -// return; -// } -// bb5: { -// _4 = (); -// StorageDead(_5); -// StorageLive(_7); -// _7 = &'26_3rs _2; -// FakeRead(ForLet, _7); -// _1 = (); -// EndRegion('26_3rs); -// StorageDead(_7); -// EndRegion('26_1rs); -// StorageDead(_3); -// StorageDead(_2); -// goto -> bb1; -// } -// END rustc.main.SimplifyCfg-qualify-consts.after.mir diff --git a/src/test/mir-opt/end_region_3.rs b/src/test/mir-opt/end_region_3.rs deleted file mode 100644 index d8d48358e53fc..0000000000000 --- a/src/test/mir-opt/end_region_3.rs +++ /dev/null @@ -1,81 +0,0 @@ -// 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: -Z identify_regions -Z emit-end-regions -// ignore-tidy-linelength - -// Binding the borrow's subject outside the loop does not increase the -// scope of the borrow. - -fn main() { - let mut a; - loop { - a = true; - let b = &a; - if a { break; } - let c = &a; - } -} - -// END RUST SOURCE -// START rustc.main.SimplifyCfg-qualify-consts.after.mir -// let mut _0: (); -// ... -// let _7: &'30_3rs bool; -// ... -// let _3: &'30_1rs bool; -// ... -// let mut _1: bool; -// ... -// let mut _2: (); -// let mut _4: (); -// let mut _5: bool; -// let mut _6: !; -// bb0: { -// StorageLive(_1); -// goto -> bb1; -// } -// bb1: { -// falseUnwind -> [real: bb2, cleanup: bb3]; -// } -// bb2: { -// _1 = const true; -// StorageLive(_3); -// _3 = &'30_1rs _1; -// FakeRead(ForLet, _3); -// StorageLive(_5); -// _5 = _1; -// switchInt(move _5) -> [false: bb5, otherwise: bb4]; -// } -// bb3: { -// ... -// } -// bb4: { -// _0 = (); -// StorageDead(_5); -// EndRegion('30_1rs); -// StorageDead(_3); -// StorageDead(_1); -// return; -// } -// bb5: { -// _4 = (); -// StorageDead(_5); -// StorageLive(_7); -// _7 = &'30_3rs _1; -// FakeRead(ForLet, _7); -// _2 = (); -// EndRegion('30_3rs); -// StorageDead(_7); -// EndRegion('30_1rs); -// StorageDead(_3); -// goto -> bb1; -// } -// END rustc.main.SimplifyCfg-qualify-consts.after.mir diff --git a/src/test/mir-opt/end_region_4.rs b/src/test/mir-opt/end_region_4.rs deleted file mode 100644 index 3d15f20bd05f3..0000000000000 --- a/src/test/mir-opt/end_region_4.rs +++ /dev/null @@ -1,83 +0,0 @@ -// 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: -Z identify_regions -Z emit-end-regions -// ignore-tidy-linelength - -// Unwinding should EndRegion for in-scope borrows: Direct borrows. - -fn main() { - let d = D(0); - let a = 0; - let b = &a; - foo(*b); - let c = &a; -} - -struct D(i32); -impl Drop for D { fn drop(&mut self) { println!("dropping D({})", self.0); } } - -fn foo(i: i32) { - if i > 0 { panic!("im positive"); } -} - -// END RUST SOURCE -// START rustc.main.SimplifyCfg-qualify-consts.after.mir -// let mut _0: (); -// ... -// let _6: &'31_4rs i32; -// ... -// let _3: &'31_2rs i32; -// ... -// let _2: i32; -// ... -// let _1: D; -// ... -// let mut _4: (); -// let mut _5: i32; -// bb0: { -// StorageLive(_1); -// _1 = D(const 0i32,); -// FakeRead(ForLet, _1); -// StorageLive(_2); -// _2 = const 0i32; -// FakeRead(ForLet, _2); -// StorageLive(_3); -// _3 = &'31_2rs _2; -// FakeRead(ForLet, _3); -// StorageLive(_5); -// _5 = (*_3); -// _4 = const foo(move _5) -> [return: bb2, unwind: bb3]; -// } -// bb1: { -// resume; -// } -// bb2: { -// StorageDead(_5); -// StorageLive(_6); -// _6 = &'31_4rs _2; -// FakeRead(ForLet, _6); -// _0 = (); -// EndRegion('31_4rs); -// StorageDead(_6); -// EndRegion('31_2rs); -// StorageDead(_3); -// StorageDead(_2); -// drop(_1) -> [return: bb4, unwind: bb1]; -// } -// bb3: { -// EndRegion('31_2rs); -// drop(_1) -> bb1; -// } -// bb4: { -// StorageDead(_1); -// return; -// } -// END rustc.main.SimplifyCfg-qualify-consts.after.mir diff --git a/src/test/mir-opt/end_region_5.rs b/src/test/mir-opt/end_region_5.rs deleted file mode 100644 index 06d1fbabe1616..0000000000000 --- a/src/test/mir-opt/end_region_5.rs +++ /dev/null @@ -1,77 +0,0 @@ -// 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: -Z identify_regions -Z span_free_formats -Z emit-end-regions - -// Unwinding should EndRegion for in-scope borrows: Borrowing via by-ref closure. - -fn main() { - let d = D(0); - foo(|| -> i32 { d.0 }); -} - -struct D(i32); -impl Drop for D { fn drop(&mut self) { println!("dropping D({})", self.0); } } - -fn foo(f: F) where F: FnOnce() -> i32 { - if f() > 0 { panic!("im positive"); } -} - -// END RUST SOURCE -// START rustc.main.SimplifyCfg-qualify-consts.after.mir -// fn main() -> () { -// ... -// let mut _0: (); -// ... -// let _1: D; -// ... -// let mut _2: (); -// let mut _3: [closure@NodeId(28) d:&'18s D]; -// let mut _4: &'18s D; -// bb0: { -// StorageLive(_1); -// _1 = D(const 0i32,); -// FakeRead(ForLet, _1); -// StorageLive(_3); -// StorageLive(_4); -// _4 = &'18s _1; -// _3 = [closure@NodeId(28)] { d: move _4 }; -// StorageDead(_4); -// _2 = const foo(move _3) -> [return: bb2, unwind: bb3]; -// } -// bb1: { -// resume; -// } -// bb2: { -// EndRegion('18s); -// StorageDead(_3); -// _0 = (); -// drop(_1) -> [return: bb4, unwind: bb1]; -// } -// bb3: { -// EndRegion('18s); -// drop(_1) -> bb1; -// } -// bb4: { -// StorageDead(_1); -// return; -// } -// } -// END rustc.main.SimplifyCfg-qualify-consts.after.mir - -// START rustc.main-{{closure}}.SimplifyCfg-qualify-consts.after.mir -// fn main::{{closure}}(_1: [closure@NodeId(28) d:&'18s D]) -> i32 { -// let mut _0: i32; -// -// bb0: { -// _0 = ((*(_1.0: &'18s D)).0: i32); -// return; -// } -// END rustc.main-{{closure}}.SimplifyCfg-qualify-consts.after.mir diff --git a/src/test/mir-opt/end_region_6.rs b/src/test/mir-opt/end_region_6.rs deleted file mode 100644 index d0db23e6de0ee..0000000000000 --- a/src/test/mir-opt/end_region_6.rs +++ /dev/null @@ -1,83 +0,0 @@ -// 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: -Z identify_regions -Z span_free_formats -Z emit-end-regions -// ignore-tidy-linelength - -// Unwinding should EndRegion for in-scope borrows: 2nd borrow within by-ref closure. - -fn main() { - let d = D(0); - foo(|| -> i32 { let r = &d; r.0 }); -} - -struct D(i32); -impl Drop for D { fn drop(&mut self) { println!("dropping D({})", self.0); } } - -fn foo(f: F) where F: FnOnce() -> i32 { - if f() > 0 { panic!("im positive"); } -} - -// END RUST SOURCE -// START rustc.main.SimplifyCfg-qualify-consts.after.mir -// fn main() -> () { -// let mut _0: (); -// ... -// let _1: D; -// ... -// let mut _2: (); -// let mut _3: [closure@NodeId(33) d:&'24s D]; -// let mut _4: &'24s D; -// bb0: { -// StorageLive(_1); -// _1 = D(const 0i32,); -// FakeRead(ForLet, _1); -// StorageLive(_3); -// StorageLive(_4); -// _4 = &'24s _1; -// _3 = [closure@NodeId(33)] { d: move _4 }; -// StorageDead(_4); -// _2 = const foo(move _3) -> [return: bb2, unwind: bb3]; -// } -// bb1: { -// resume; -// } -// bb2: { -// EndRegion('24s); -// StorageDead(_3); -// _0 = (); -// drop(_1) -> [return: bb4, unwind: bb1]; -// } -// bb3: { -// EndRegion('24s); -// drop(_1) -> bb1; -// } -// bb4: { -// StorageDead(_1); -// return; -// } -// END rustc.main.SimplifyCfg-qualify-consts.after.mir - -// START rustc.main-{{closure}}.SimplifyCfg-qualify-consts.after.mir -// fn main::{{closure}}(_1: [closure@NodeId(33) d:&'24s D]) -> i32 { -// let mut _0: i32; -// ... -// let _2: &'21_0rs D; -// ... -// bb0: { -// StorageLive(_2); -// _2 = &'21_0rs (*(_1.0: &'24s D)); -// FakeRead(ForLet, _2); -// _0 = ((*_2).0: i32); -// EndRegion('21_0rs); -// StorageDead(_2); -// return; -// } -// END rustc.main-{{closure}}.SimplifyCfg-qualify-consts.after.mir diff --git a/src/test/mir-opt/end_region_7.rs b/src/test/mir-opt/end_region_7.rs deleted file mode 100644 index c7df440ebe2f3..0000000000000 --- a/src/test/mir-opt/end_region_7.rs +++ /dev/null @@ -1,91 +0,0 @@ -// 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: -Z identify_regions -Z span_free_formats -Z emit-end-regions -// ignore-tidy-linelength - -// Unwinding should EndRegion for in-scope borrows: Borrow of moved data. - -fn main() { - let d = D(0); - foo(move || -> i32 { let r = &d; r.0 }); -} - -struct D(i32); -impl Drop for D { fn drop(&mut self) { println!("dropping D({})", self.0); } } - -fn foo(f: F) where F: FnOnce() -> i32 { - if f() > 0 { panic!("im positive"); } -} - -// END RUST SOURCE -// START rustc.main.SimplifyCfg-qualify-consts.after.mir -// fn main() -> () { -// let mut _0: (); -// ... -// let _1: D; -// ... -// let mut _2: (); -// let mut _3: [closure@NodeId(33) d:D]; -// bb0: { -// StorageLive(_1); -// _1 = D(const 0i32,); -// FakeRead(ForLet, _1); -// StorageLive(_3); -// _3 = [closure@NodeId(33)] { d: move _1 }; -// _2 = const foo(move _3) -> [return: bb2, unwind: bb4]; -// } -// bb1: { -// resume; -// } -// bb2: { -// drop(_3) -> [return: bb5, unwind: bb3]; -// } -// bb3: { -// drop(_1) -> bb1; -// } -// bb4: { -// drop(_3) -> bb3; -// } -// bb5: { -// StorageDead(_3); -// _0 = (); -// drop(_1) -> [return: bb6, unwind: bb1]; -// } -// bb6: { -// StorageDead(_1); -// return; -// } -// } -// END rustc.main.SimplifyCfg-qualify-consts.after.mir - -// START rustc.main-{{closure}}.SimplifyCfg-qualify-consts.after.mir -// fn main::{{closure}}(_1: [closure@NodeId(33) d:D]) -> i32 { -// let mut _0: i32; -// ... -// let _2: &'21_0rs D; -// ... -// bb0: { -// StorageLive(_2); -// _2 = &'21_0rs (_1.0: D); -// FakeRead(ForLet, _2); -// _0 = ((*_2).0: i32); -// EndRegion('21_0rs); -// StorageDead(_2); -// drop(_1) -> [return: bb2, unwind: bb1]; -// } -// bb1: { -// resume; -// } -// bb2: { -// return; -// } -// } -// END rustc.main-{{closure}}.SimplifyCfg-qualify-consts.after.mir diff --git a/src/test/mir-opt/end_region_8.rs b/src/test/mir-opt/end_region_8.rs deleted file mode 100644 index 9f2a9c3b72e8b..0000000000000 --- a/src/test/mir-opt/end_region_8.rs +++ /dev/null @@ -1,83 +0,0 @@ -// 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: -Z identify_regions -Z span_free_formats -Z emit-end-regions -// ignore-tidy-linelength - -// Unwinding should EndRegion for in-scope borrows: Move of borrow into closure. - -fn main() { - let d = D(0); - let r = &d; - foo(move || -> i32 { r.0 }); -} - -struct D(i32); -impl Drop for D { fn drop(&mut self) { println!("dropping D({})", self.0); } } - -fn foo(f: F) where F: FnOnce() -> i32 { - if f() > 0 { panic!("im positive"); } -} - -// END RUST SOURCE -// START rustc.main.SimplifyCfg-qualify-consts.after.mir -// fn main() -> () { -// let mut _0: (); -// ... -// let _2: &'26_1rs D; -// ... -// let _1: D; -// ... -// let mut _3: (); -// let mut _4: [closure@NodeId(33) r:&'24s D]; -// bb0: { -// StorageLive(_1); -// _1 = D(const 0i32,); -// FakeRead(ForLet, _1); -// StorageLive(_2); -// _2 = &'26_1rs _1; -// FakeRead(ForLet, _2); -// StorageLive(_4); -// _4 = [closure@NodeId(33)] { r: _2 }; -// _3 = const foo(move _4) -> [return: bb2, unwind: bb3]; -// } -// bb1: { -// resume; -// } -// bb2: { -// EndRegion('24s); -// StorageDead(_4); -// _0 = (); -// EndRegion('26_1rs); -// StorageDead(_2); -// drop(_1) -> [return: bb4, unwind: bb1]; -// } -// bb3: { -// EndRegion('24s); -// EndRegion('26_1rs); -// drop(_1) -> bb1; -// } -// bb4: { -// StorageDead(_1); -// return; -// } -// } -// END rustc.main.SimplifyCfg-qualify-consts.after.mir - -// START rustc.main-{{closure}}.SimplifyCfg-qualify-consts.after.mir -// fn main::{{closure}}(_1: [closure@NodeId(33) r:&'24s D]) -> i32 { -// let mut _0: i32; -// -// bb0: { -// _0 = ((*(_1.0: &'26_1rs D)).0: i32); -// return; -// } -// } -// END rustc.main-{{closure}}.SimplifyCfg-qualify-consts.after.mir diff --git a/src/test/mir-opt/end_region_9.rs b/src/test/mir-opt/end_region_9.rs deleted file mode 100644 index ef2d949d3074e..0000000000000 --- a/src/test/mir-opt/end_region_9.rs +++ /dev/null @@ -1,97 +0,0 @@ -// 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: -Z identify_regions -Z span_free_formats -Z emit-end-regions -// ignore-tidy-linelength - -// This test models a scenario that arielb1 found during review. -// Namely, any filtering of EndRegions must ensure to continue to emit -// any necessary EndRegions that occur earlier in the source than the -// first borrow involving that region. -// -// It is tricky to actually construct examples of this, which is the -// main reason that I am keeping this test even though I have now -// removed the pre-filter that motivated the test in the first place. - -fn main() { - let mut second_iter = false; - let x = 3; - 'a: loop { - let mut y; - loop { - if second_iter { - break 'a; // want to generate `EndRegion('a)` here - } else { - y = &/*'a*/ x; - } - second_iter = true; - } - } -} - -// END RUST SOURCE -// START rustc.main.SimplifyCfg-qualify-consts.after.mir -// fn main() -> () { -// let mut _0: (); -// ... -// let mut _4: &'37_0rs i32; -// ... -// let _2: i32; -// ... -// let mut _1: bool; -// ... -// let mut _3: (); -// let mut _5: !; -// let mut _6: (); -// let mut _7: bool; -// let mut _8: !; -// bb0: { -// StorageLive(_1); -// _1 = const false; -// FakeRead(ForLet, _1); -// StorageLive(_2); -// _2 = const 3i32; -// FakeRead(ForLet, _2); -// falseUnwind -> [real: bb2, cleanup: bb1]; -// } -// bb1: { -// ... -// } -// bb2: { -// StorageLive(_4); -// goto -> bb3; -// } -// bb3: { -// falseUnwind -> [real: bb4, cleanup: bb1]; -// } -// bb4: { -// StorageLive(_7); -// _7 = _1; -// switchInt(move _7) -> [false: bb6, otherwise: bb5]; -// } -// bb5: { -// _0 = (); -// StorageDead(_7); -// EndRegion('37_0rs); -// StorageDead(_4); -// StorageDead(_2); -// StorageDead(_1); -// return; -// } -// bb6: { -// _4 = &'37_0rs _2; -// _6 = (); -// StorageDead(_7); -// _1 = const true; -// _3 = (); -// goto -> bb3; -// } -// } -// END rustc.main.SimplifyCfg-qualify-consts.after.mir diff --git a/src/test/mir-opt/end_region_cyclic.rs b/src/test/mir-opt/end_region_cyclic.rs deleted file mode 100644 index 3dbc73caf65d8..0000000000000 --- a/src/test/mir-opt/end_region_cyclic.rs +++ /dev/null @@ -1,141 +0,0 @@ -// 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: -Z identify_regions -Z span_free_formats -Z emit-end-regions -// ignore-tidy-linelength - -// This test models a scenario with a cyclic reference. Rust obviously -// needs to handle such cases. -// -// The interesting part about this test is that such case shows that -// one cannot generally force all references to be dead before you hit -// their EndRegion; at least, not without breaking the more important -// property that all borrowed storage locations have their regions -// ended strictly before their StorageDeads. (This test was inspired -// by discussion on Issue #43481.) - -use std::cell::Cell; - -struct S<'a> { - r: Cell>>, -} - -fn main() { - loop { - let x = S { r: Cell::new(None) }; - x.r.set(Some(&x)); - if query() { break; } - x.r.set(Some(&x)); - } -} - -fn query() -> bool { true } - -// END RUST SOURCE -// START rustc.main.SimplifyCfg-qualify-consts.after.mir -// fn main() -> (){ -// let mut _0: (); -// scope 1 { -// } -// scope 2 { -// let _2: S<'49_0rs>; -// } -// let mut _1: (); -// let mut _3: std::cell::Cell>>; -// let mut _4: std::option::Option<&'49_0rs S<'49_0rs>>; -// let mut _5: (); -// let mut _6: &'25s std::cell::Cell>>; -// let mut _7: std::option::Option<&'49_0rs S<'49_0rs>>; -// let mut _8: &'49_0rs S<'49_0rs>; -// let mut _9: &'49_0rs S<'49_0rs>; -// let mut _10: (); -// let mut _11: bool; -// let mut _12: !; -// let mut _13: (); -// let mut _14: &'47s std::cell::Cell>>; -// let mut _15: std::option::Option<&'49_0rs S<'49_0rs>>; -// let mut _16: &'49_0rs S<'49_0rs>; -// let mut _17: &'49_0rs S<'49_0rs>; -// bb0: { -// goto -> bb1; -// } -// bb1: { -// falseUnwind -> [real: bb2, cleanup: bb3]; -// } -// bb2: { -// StorageLive(_2); -// StorageLive(_3); -// StorageLive(_4); -// _4 = std::option::Option<&'49_0rs S<'49_0rs>>::None; -// _3 = const >::new(move _4) -> [return: bb4, unwind: bb3]; -// } -// bb3: { -// resume; -// } -// bb4: { -// StorageDead(_4); -// _2 = S<'49_0rs> { r: move _3 }; -// StorageDead(_3); -// FakeRead(ForLet, _2); -// StorageLive(_6); -// _6 = &'25s (_2.0: std::cell::Cell>>); -// StorageLive(_7); -// StorageLive(_8); -// StorageLive(_9); -// _9 = &'49_0rs _2; -// _8 = &'49_0rs (*_9); -// _7 = std::option::Option<&'49_0rs S<'49_0rs>>::Some(move _8,); -// StorageDead(_8); -// _5 = const >::set(move _6, move _7) -> [return: bb5, unwind: bb3]; -// } -// bb5: { -// EndRegion('25s); -// StorageDead(_7); -// StorageDead(_6); -// StorageDead(_9); -// StorageLive(_11); -// _11 = const query() -> [return: bb6, unwind: bb3]; -// } -// bb6: { -// switchInt(move _11) -> [false: bb8, otherwise: bb7]; -// } -// bb7: { -// _0 = (); -// StorageDead(_11); -// EndRegion('49_0rs); -// StorageDead(_2); -// return; -// } -// bb8: { -// _10 = (); -// StorageDead(_11); -// StorageLive(_14); -// _14 = &'47s (_2.0: std::cell::Cell>>); -// StorageLive(_15); -// StorageLive(_16); -// StorageLive(_17); -// _17 = &'49_0rs _2; -// _16 = &'49_0rs (*_17); -// _15 = std::option::Option<&'49_0rs S<'49_0rs>>::Some(move _16,); -// StorageDead(_16); -// _13 = const >::set(move _14, move _15) -> [return: bb9, unwind: bb3]; -// } -// bb9: { -// EndRegion('47s); -// StorageDead(_15); -// StorageDead(_14); -// StorageDead(_17); -// _1 = (); -// EndRegion('49_0rs); -// StorageDead(_2); -// goto -> bb1; -// } -// } -// END rustc.main.SimplifyCfg-qualify-consts.after.mir diff --git a/src/test/mir-opt/end_region_destruction_extents_1.rs b/src/test/mir-opt/end_region_destruction_extents_1.rs deleted file mode 100644 index eb381dfc5521f..0000000000000 --- a/src/test/mir-opt/end_region_destruction_extents_1.rs +++ /dev/null @@ -1,154 +0,0 @@ -// 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: -Z identify_regions -Z span_free_formats -Z emit-end-regions -// ignore-tidy-linelength - -// A scenario with significant destruction code extents (which have -// suffix "dce" in current `-Z identify_regions` rendering). - -#![feature(dropck_eyepatch)] - -fn main() { - // Since the second param to `D1` is may_dangle, it is legal for - // the region of that parameter to end before the drop code for D1 - // is executed. - (D1(&S1("ex1"), &S1("dang1"))).0; -} - -#[derive(Debug)] -struct S1(&'static str); - -#[derive(Debug)] -struct D1<'a, 'b>(&'a S1, &'b S1); - -// The `#[may_dangle]` means that references of type `&'b _` may be -// invalid during the execution of this destructor; i.e. in this case -// the destructor code is not allowed to read or write `*self.1`, while -// it can read/write `*self.0`. -unsafe impl<'a, #[may_dangle] 'b> Drop for D1<'a, 'b> { - fn drop(&mut self) { - println!("D1({:?}, _)", self.0); - } -} - -// Notes on the MIR output below: -// -// 1. The `EndRegion('13s)` is allowed to precede the `drop(_3)` -// solely because of the #[may_dangle] mentioned above. -// -// 2. Regarding the occurrence of `EndRegion('15ds)` *after* `StorageDead(_6)` -// (where we have borrows `&'15ds _6`): Eventually: -// -// i. this code should be rejected (by mir-borrowck), or -// -// ii. the MIR code generation should be changed so that the -// EndRegion('15ds)` precedes `StorageDead(_6)` in the -// control-flow. (Note: arielb1 views drop+storagedead as one -// unit, and does not see this option as a useful avenue to -// explore.), or -// -// iii. the presence of EndRegion should be made irrelevant by a -// transformation encoding the effects of rvalue-promotion. -// This may be the simplest and most-likely option; note in -// particular that `StorageDead(_6)` goes away below in -// rustc.main.QualifyAndPromoteConstants.after.mir - -// END RUST SOURCE - -// START rustc.main.QualifyAndPromoteConstants.before.mir -// fn main() -> () { -// let mut _0: (); -// let mut _1: &'15ds S1; -// let mut _2: D1<'15ds, '13s>; -// let mut _3: &'15ds S1; -// let mut _4: &'15ds S1; -// let _5: S1; -// let mut _6: &'13s S1; -// let mut _7: &'13s S1; -// let _8: S1; -// bb0: { -// StorageLive(_2); -// StorageLive(_3); -// StorageLive(_4); -// StorageLive(_5); -// _5 = S1(const "ex1",); -// _4 = &'15ds _5; -// _3 = &'15ds (*_4); -// StorageLive(_6); -// StorageLive(_7); -// StorageLive(_8); -// _8 = S1(const "dang1",); -// _7 = &'13s _8; -// _6 = &'13s (*_7); -// _2 = D1<'15ds, '13s>(move _3, move _6); -// EndRegion('13s); -// StorageDead(_6); -// StorageDead(_3); -// _1 = (_2.0: &'15ds S1); -// drop(_2) -> [return: bb2, unwind: bb1]; -// } -// bb1: { -// resume; -// } -// bb2: { -// StorageDead(_2); -// StorageDead(_7); -// StorageDead(_8); -// StorageDead(_4); -// StorageDead(_5); -// EndRegion('15ds); -// _0 = (); -// return; -// } -// } -// END rustc.main.QualifyAndPromoteConstants.before.mir - -// START rustc.main.QualifyAndPromoteConstants.after.mir -// fn main() -> (){ -// let mut _0: (); -// let mut _1: &'15ds S1; -// let mut _2: D1<'15ds, '13s>; -// let mut _3: &'15ds S1; -// let mut _4: &'15ds S1; -// let _5: S1; -// let mut _6: &'13s S1; -// let mut _7: &'13s S1; -// let _8: S1; -// bb0: { -// StorageLive(_2); -// StorageLive(_3); -// StorageLive(_4); -// _4 = &'15ds (promoted[1]: S1); -// _3 = &'15ds (*_4); -// StorageLive(_6); -// StorageLive(_7); -// _7 = &'13s (promoted[0]: S1); -// _6 = &'13s (*_7); -// _2 = D1<'15ds, '13s>(move _3, move _6); -// EndRegion('13s); -// StorageDead(_6); -// StorageDead(_3); -// _1 = (_2.0: &'15ds S1); -// drop(_2) -> [return: bb2, unwind: bb1]; -// } -// bb1: { -// resume; -// } -// bb2: { -// StorageDead(_2); -// StorageDead(_7); -// StorageDead(_4); -// EndRegion('15ds); -// _0 = (); -// return; -// } -// } -// END rustc.main.QualifyAndPromoteConstants.after.mir diff --git a/src/test/mir-opt/issue-43457.rs b/src/test/mir-opt/issue-43457.rs deleted file mode 100644 index 85cecc5070cd5..0000000000000 --- a/src/test/mir-opt/issue-43457.rs +++ /dev/null @@ -1,55 +0,0 @@ -// 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: -Z identify_regions -Z span_free_formats -Z emit-end-regions -// ignore-tidy-linelength - -// Regression test for #43457: an `EndRegion` was missing from output -// because compiler was using a faulty means for region map lookup. - -use std::cell::RefCell; - -fn rc_refcell_test(r: RefCell) { - r.borrow_mut(); -} - -fn main() { } - -// END RUST SOURCE -// START rustc.rc_refcell_test.SimplifyCfg-qualify-consts.after.mir -// -// fn rc_refcell_test(_1: std::cell::RefCell) -> () { -// let mut _0: (); -// scope 1 { -// let _2: std::cell::RefCell; -// } -// let mut _3: std::cell::RefMut<'17ds, i32>; -// let mut _4: &'17ds std::cell::RefCell; -// -// bb0: { -// StorageLive(_2); -// _2 = _1; -// StorageLive(_4); -// _4 = &'17ds _2; -// _3 = const >::borrow_mut(_4) -> bb1; -// } -// -// bb1: { -// drop(_3) -> bb2; -// } -// -// bb2: { -// StorageDead(_4); -// EndRegion('17ds); -// _0 = (); -// StorageDead(_2); -// return; -// } -// } diff --git a/src/test/mir-opt/loop_test.rs b/src/test/mir-opt/loop_test.rs index 2e526a221cc72..77616f1d39ad4 100644 --- a/src/test/mir-opt/loop_test.rs +++ b/src/test/mir-opt/loop_test.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// compile-flags: -Z identify_regions -Z emit-end-regions +// compile-flags: -Z identify_regions // Tests to make sure we correctly generate falseUnwind edges in loops diff --git a/src/test/run-pass/issues/issue-16671.rs b/src/test/run-pass/issues/issue-16671.rs index 5ff2bc97ae236..14bda56dd8fbe 100644 --- a/src/test/run-pass/issues/issue-16671.rs +++ b/src/test/run-pass/issues/issue-16671.rs @@ -9,7 +9,7 @@ // except according to those terms. // run-pass -//compile-flags: -Z borrowck=compare -Z emit-end-regions +//compile-flags: -Z borrowck=compare #![deny(warnings)] diff --git a/src/test/ui/borrowck/immutable-arg.rs b/src/test/ui/borrowck/immutable-arg.rs index 7c387ed080821..c5291ab3e1dbf 100644 --- a/src/test/ui/borrowck/immutable-arg.rs +++ b/src/test/ui/borrowck/immutable-arg.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -//compile-flags: -Z emit-end-regions -Z borrowck=compare +//compile-flags: -Z borrowck=compare fn foo(_x: u32) { _x = 4; diff --git a/src/test/ui/issues/issue-45697-1.rs b/src/test/ui/issues/issue-45697-1.rs index b8be209833a6e..e79bfb23b05af 100644 --- a/src/test/ui/issues/issue-45697-1.rs +++ b/src/test/ui/issues/issue-45697-1.rs @@ -11,7 +11,7 @@ // Test that assignments to an `&mut` pointer which is found in a // borrowed (but otherwise non-aliasable) location is illegal. -// compile-flags: -Z emit-end-regions -Z borrowck=compare -C overflow-checks=on +// compile-flags: -Z borrowck=compare -C overflow-checks=on struct S<'a> { pointer: &'a mut isize diff --git a/src/test/ui/issues/issue-45697.rs b/src/test/ui/issues/issue-45697.rs index 27acc2c89f75d..936e8a40fa83f 100644 --- a/src/test/ui/issues/issue-45697.rs +++ b/src/test/ui/issues/issue-45697.rs @@ -11,7 +11,7 @@ // Test that assignments to an `&mut` pointer which is found in a // borrowed (but otherwise non-aliasable) location is illegal. -// compile-flags: -Z emit-end-regions -Z borrowck=compare -C overflow-checks=off +// compile-flags: -Z borrowck=compare -C overflow-checks=off struct S<'a> { pointer: &'a mut isize diff --git a/src/test/ui/issues/issue-46023.rs b/src/test/ui/issues/issue-46023.rs index d5c8cd6d0f8fc..ea7e627eadf10 100644 --- a/src/test/ui/issues/issue-46023.rs +++ b/src/test/ui/issues/issue-46023.rs @@ -9,7 +9,7 @@ // except according to those terms. // revisions: ast mir -//[mir]compile-flags: -Z emit-end-regions -Z borrowck=mir +//[mir]compile-flags: -Z borrowck=mir fn main() { let x = 0; diff --git a/src/test/ui/issues/issue-46471-1.rs b/src/test/ui/issues/issue-46471-1.rs index 0dbcdea89f940..7fb77f9ce8bf6 100644 --- a/src/test/ui/issues/issue-46471-1.rs +++ b/src/test/ui/issues/issue-46471-1.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// compile-flags: -Z emit-end-regions -Z borrowck=compare +// compile-flags: -Z borrowck=compare fn main() { let y = { diff --git a/src/test/ui/issues/issue-46471.rs b/src/test/ui/issues/issue-46471.rs index 654a3d8f964d2..201b8e2de9a79 100644 --- a/src/test/ui/issues/issue-46471.rs +++ b/src/test/ui/issues/issue-46471.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// compile-flags: -Z emit-end-regions -Z borrowck=compare +// compile-flags: -Z borrowck=compare fn foo() -> &'static u32 { let x = 0; diff --git a/src/test/ui/issues/issue-46472.rs b/src/test/ui/issues/issue-46472.rs index 8137cd2dd8913..b6838a48b1b68 100644 --- a/src/test/ui/issues/issue-46472.rs +++ b/src/test/ui/issues/issue-46472.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// compile-flags: -Z emit-end-regions -Z borrowck=compare +// compile-flags: -Z borrowck=compare fn bar<'a>() -> &'a mut u32 { &mut 4 diff --git a/src/test/ui/moves/moves-based-on-type-tuple.rs b/src/test/ui/moves/moves-based-on-type-tuple.rs index 27903fee117b3..9dda275d95eaa 100644 --- a/src/test/ui/moves/moves-based-on-type-tuple.rs +++ b/src/test/ui/moves/moves-based-on-type-tuple.rs @@ -10,7 +10,7 @@ #![feature(box_syntax)] -// compile-flags: -Z emit-end-regions -Z borrowck=compare +// compile-flags: -Z borrowck=compare fn dup(x: Box) -> Box<(Box,Box)> { box (x, x) diff --git a/src/test/ui/nll/maybe-initialized-drop-uninitialized.rs b/src/test/ui/nll/maybe-initialized-drop-uninitialized.rs index ae815a5efe97c..f96e73ce09f5f 100644 --- a/src/test/ui/nll/maybe-initialized-drop-uninitialized.rs +++ b/src/test/ui/nll/maybe-initialized-drop-uninitialized.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// compile-flags: -Z emit-end-regions -Zborrowck=mir +// compile-flags: -Zborrowck=mir // compile-pass #![allow(warnings)] diff --git a/src/test/ui/nll/maybe-initialized-drop-with-fragment.rs b/src/test/ui/nll/maybe-initialized-drop-with-fragment.rs index 00d146e0f02d6..0e9cdf2f428ad 100644 --- a/src/test/ui/nll/maybe-initialized-drop-with-fragment.rs +++ b/src/test/ui/nll/maybe-initialized-drop-with-fragment.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -//compile-flags: -Z emit-end-regions -Zborrowck=mir +//compile-flags: -Zborrowck=mir #![allow(warnings)] diff --git a/src/test/ui/nll/maybe-initialized-drop-with-uninitialized-fragments.rs b/src/test/ui/nll/maybe-initialized-drop-with-uninitialized-fragments.rs index cd46014a7f5ca..080fdfe022801 100644 --- a/src/test/ui/nll/maybe-initialized-drop-with-uninitialized-fragments.rs +++ b/src/test/ui/nll/maybe-initialized-drop-with-uninitialized-fragments.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -//compile-flags: -Z emit-end-regions -Zborrowck=mir +//compile-flags: -Zborrowck=mir #![allow(warnings)] diff --git a/src/test/ui/nll/maybe-initialized-drop.rs b/src/test/ui/nll/maybe-initialized-drop.rs index 9a3aca346208d..db680ef8bea17 100644 --- a/src/test/ui/nll/maybe-initialized-drop.rs +++ b/src/test/ui/nll/maybe-initialized-drop.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -//compile-flags: -Z emit-end-regions -Zborrowck=mir +//compile-flags: -Zborrowck=mir #![allow(warnings)] From 6027e0810ff9f60103d2ff4c24791e40db491316 Mon Sep 17 00:00:00 2001 From: Matthew Jasper Date: Sat, 10 Nov 2018 23:52:20 +0000 Subject: [PATCH 08/88] Only handle ReVar regions in NLL borrowck Now that lexical MIR borrowck is gone, there's no need to store Regions unnecessarily. --- src/librustc_mir/borrow_check/borrow_set.rs | 28 +++--- src/librustc_mir/borrow_check/mod.rs | 7 +- .../borrow_check/nll/explain_borrow/mod.rs | 2 +- src/librustc_mir/dataflow/impls/borrows.rs | 91 ++----------------- 4 files changed, 23 insertions(+), 105 deletions(-) diff --git a/src/librustc_mir/borrow_check/borrow_set.rs b/src/librustc_mir/borrow_check/borrow_set.rs index c432826dca865..fd7dc7fc4bd3a 100644 --- a/src/librustc_mir/borrow_check/borrow_set.rs +++ b/src/librustc_mir/borrow_check/borrow_set.rs @@ -9,6 +9,7 @@ // except according to those terms. use borrow_check::place_ext::PlaceExt; +use borrow_check::nll::ToRegionVid; use dataflow::indexes::BorrowIndex; use dataflow::move_paths::MoveData; use rustc::mir::traversal; @@ -16,7 +17,7 @@ use rustc::mir::visit::{ PlaceContext, Visitor, NonUseContext, MutatingUseContext, NonMutatingUseContext }; use rustc::mir::{self, Location, Mir, Place, Local}; -use rustc::ty::{Region, TyCtxt}; +use rustc::ty::{RegionVid, TyCtxt}; use rustc::util::nodemap::{FxHashMap, FxHashSet}; use rustc_data_structures::indexed_vec::IndexVec; use rustc_data_structures::bit_set::BitSet; @@ -42,7 +43,7 @@ crate struct BorrowSet<'tcx> { /// Every borrow has a region; this maps each such regions back to /// its borrow-indexes. - crate region_map: FxHashMap, FxHashSet>, + crate region_map: FxHashMap>, /// Map from local to all the borrows on that local crate local_map: FxHashMap>, @@ -77,7 +78,7 @@ crate struct BorrowData<'tcx> { /// What kind of borrow this is crate kind: mir::BorrowKind, /// The region for which this borrow is live - crate region: Region<'tcx>, + crate region: RegionVid, /// Place from which we are borrowing crate borrowed_place: mir::Place<'tcx>, /// Place to which the borrow was stored @@ -92,13 +93,7 @@ impl<'tcx> fmt::Display for BorrowData<'tcx> { mir::BorrowKind::Unique => "uniq ", mir::BorrowKind::Mut { .. } => "mut ", }; - let region = self.region.to_string(); - let separator = if !region.is_empty() { - " " - } else { - "" - }; - write!(w, "&{}{}{}{:?}", region, separator, kind, self.borrowed_place) + write!(w, "&{:?} {}{:?}", self.region, kind, self.borrowed_place) } } @@ -189,7 +184,7 @@ struct GatherBorrows<'a, 'gcx: 'tcx, 'tcx: 'a> { idx_vec: IndexVec>, location_map: FxHashMap, activation_map: FxHashMap>, - region_map: FxHashMap, FxHashSet>, + region_map: FxHashMap>, local_map: FxHashMap>, /// When we encounter a 2-phase borrow statement, it will always @@ -219,6 +214,8 @@ impl<'a, 'gcx, 'tcx> Visitor<'tcx> for GatherBorrows<'a, 'gcx, 'tcx> { return; } + let region = region.to_region_vid(); + let borrow = BorrowData { kind, region, @@ -230,7 +227,7 @@ impl<'a, 'gcx, 'tcx> Visitor<'tcx> for GatherBorrows<'a, 'gcx, 'tcx> { let idx = self.idx_vec.push(borrow); self.location_map.insert(location, idx); - self.insert_as_pending_if_two_phase(location, &assigned_place, region, kind, idx); + self.insert_as_pending_if_two_phase(location, &assigned_place, kind, idx); self.region_map.entry(region).or_default().insert(idx); if let Some(local) = borrowed_place.root_local() { @@ -314,7 +311,7 @@ impl<'a, 'gcx, 'tcx> Visitor<'tcx> for GatherBorrows<'a, 'gcx, 'tcx> { let borrow_data = &self.idx_vec[borrow_index]; assert_eq!(borrow_data.reserve_location, location); assert_eq!(borrow_data.kind, kind); - assert_eq!(borrow_data.region, region); + assert_eq!(borrow_data.region, region.to_region_vid()); assert_eq!(borrow_data.borrowed_place, *place); } @@ -347,13 +344,12 @@ impl<'a, 'gcx, 'tcx> GatherBorrows<'a, 'gcx, 'tcx> { &mut self, start_location: Location, assigned_place: &mir::Place<'tcx>, - region: Region<'tcx>, kind: mir::BorrowKind, borrow_index: BorrowIndex, ) { debug!( - "Borrows::insert_as_pending_if_two_phase({:?}, {:?}, {:?}, {:?})", - start_location, assigned_place, region, borrow_index, + "Borrows::insert_as_pending_if_two_phase({:?}, {:?}, {:?})", + start_location, assigned_place, borrow_index, ); if !self.allow_two_phase_borrow(kind) { diff --git a/src/librustc_mir/borrow_check/mod.rs b/src/librustc_mir/borrow_check/mod.rs index 8819908de6ae1..76ba6ae5de6ee 100644 --- a/src/librustc_mir/borrow_check/mod.rs +++ b/src/librustc_mir/borrow_check/mod.rs @@ -14,7 +14,6 @@ use borrow_check::nll::region_infer::RegionInferenceContext; use rustc::hir; use rustc::hir::Node; use rustc::hir::def_id::DefId; -use rustc::hir::map::definitions::DefPathData; use rustc::infer::InferCtxt; use rustc::lint::builtin::UNUSED_MUT; use rustc::middle::borrowck::SignalledError; @@ -162,10 +161,6 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>( move_data: move_data, param_env: param_env, }; - let body_id = match tcx.def_key(def_id).disambiguated_data.data { - DefPathData::StructCtor | DefPathData::EnumVariant(_) => None, - _ => Some(tcx.hir.body_owned_by(id)), - }; let dead_unwinds = BitSet::new_empty(mir.basic_blocks().len()); let mut flow_inits = FlowAtLocation::new(do_dataflow( @@ -212,7 +207,7 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>( id, &attributes, &dead_unwinds, - Borrows::new(tcx, mir, regioncx.clone(), def_id, body_id, &borrow_set), + Borrows::new(tcx, mir, regioncx.clone(), &borrow_set), |rs, i| DebugFormatted::new(&rs.location(i)), )); let flow_uninits = FlowAtLocation::new(do_dataflow( diff --git a/src/librustc_mir/borrow_check/nll/explain_borrow/mod.rs b/src/librustc_mir/borrow_check/nll/explain_borrow/mod.rs index 2bf531d1d3e56..bb9a29b055b7f 100644 --- a/src/librustc_mir/borrow_check/nll/explain_borrow/mod.rs +++ b/src/librustc_mir/borrow_check/nll/explain_borrow/mod.rs @@ -206,7 +206,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { let mir = self.mir; let tcx = self.infcx.tcx; - let borrow_region_vid = regioncx.to_region_vid(borrow.region); + let borrow_region_vid = borrow.region; debug!( "explain_why_borrow_contains_point: borrow_region_vid={:?}", borrow_region_vid diff --git a/src/librustc_mir/dataflow/impls/borrows.rs b/src/librustc_mir/dataflow/impls/borrows.rs index 5e84aa05d386c..27bc28ac81d84 100644 --- a/src/librustc_mir/dataflow/impls/borrows.rs +++ b/src/librustc_mir/dataflow/impls/borrows.rs @@ -12,18 +12,13 @@ use borrow_check::borrow_set::{BorrowSet, BorrowData}; use borrow_check::place_ext::PlaceExt; use rustc; -use rustc::hir; -use rustc::hir::def_id::DefId; -use rustc::middle::region; use rustc::mir::{self, Location, Place, Mir}; use rustc::ty::TyCtxt; -use rustc::ty::{RegionKind, RegionVid}; -use rustc::ty::RegionKind::ReScope; +use rustc::ty::RegionVid; use rustc_data_structures::bit_set::{BitSet, BitSetOperator}; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::indexed_vec::{Idx, IndexVec}; -use rustc_data_structures::sync::Lrc; use dataflow::{BitDenotation, BlockSets, InitialFlow}; pub use dataflow::indexes::BorrowIndex; @@ -42,8 +37,6 @@ use std::rc::Rc; pub struct Borrows<'a, 'gcx: 'tcx, 'tcx: 'a> { tcx: TyCtxt<'a, 'gcx, 'tcx>, mir: &'a Mir<'tcx>, - scope_tree: Lrc, - root_scope: Option, borrow_set: Rc>, borrows_out_of_scope_at_location: FxHashMap>, @@ -150,18 +143,8 @@ impl<'a, 'gcx, 'tcx> Borrows<'a, 'gcx, 'tcx> { tcx: TyCtxt<'a, 'gcx, 'tcx>, mir: &'a Mir<'tcx>, nonlexical_regioncx: Rc>, - def_id: DefId, - body_id: Option, borrow_set: &Rc>, ) -> Self { - let scope_tree = tcx.region_scope_tree(def_id); - let root_scope = body_id.map(|body_id| { - region::Scope { - id: tcx.hir.body(body_id).value.hir_id.local_id, - data: region::ScopeData::CallSite - } - }); - let mut borrows_out_of_scope_at_location = FxHashMap::default(); for (borrow_index, borrow_data) in borrow_set.borrows.iter_enumerated() { let borrow_region = borrow_data.region.to_region_vid(); @@ -177,8 +160,6 @@ impl<'a, 'gcx, 'tcx> Borrows<'a, 'gcx, 'tcx> { mir: mir, borrow_set: borrow_set.clone(), borrows_out_of_scope_at_location, - scope_tree, - root_scope, _nonlexical_regioncx: nonlexical_regioncx, } } @@ -277,22 +258,13 @@ impl<'a, 'gcx, 'tcx> BitDenotation for Borrows<'a, 'gcx, 'tcx> { panic!("could not find BorrowIndex for location {:?}", location); }); - if let RegionKind::ReEmpty = region { - // If the borrowed value dies before the borrow is used, the region for - // the borrow can be empty. Don't track the borrow in that case. - debug!("Borrows::statement_effect_on_borrows \ - location: {:?} stmt: {:?} has empty region, killing {:?}", - location, stmt.kind, index); - sets.kill(*index); - return - } else { - debug!("Borrows::statement_effect_on_borrows location: {:?} stmt: {:?}", - location, stmt.kind); - } - - assert!(self.borrow_set.region_map.get(region).unwrap_or_else(|| { - panic!("could not find BorrowIndexs for region {:?}", region); - }).contains(&index)); + assert!(self.borrow_set.region_map + .get(®ion.to_region_vid()) + .unwrap_or_else(|| { + panic!("could not find BorrowIndexs for RegionVid {:?}", region); + }) + .contains(&index) + ); sets.gen(*index); // Issue #46746: Two-phase borrows handles @@ -349,52 +321,7 @@ impl<'a, 'gcx, 'tcx> BitDenotation for Borrows<'a, 'gcx, 'tcx> { self.kill_loans_out_of_scope_at_location(sets, location); } - fn terminator_effect(&self, sets: &mut BlockSets, location: Location) { - debug!("Borrows::terminator_effect sets: {:?} location: {:?}", sets, location); - - let block = &self.mir.basic_blocks().get(location.block).unwrap_or_else(|| { - panic!("could not find block at location {:?}", location); - }); - - let term = block.terminator(); - match term.kind { - mir::TerminatorKind::Resume | - mir::TerminatorKind::Return | - mir::TerminatorKind::GeneratorDrop => { - // When we return from the function, then all `ReScope`-style regions - // are guaranteed to have ended. - // Normally, there would be `EndRegion` statements that come before, - // and hence most of these loans will already be dead -- but, in some cases - // like unwind paths, we do not always emit `EndRegion` statements, so we - // add some kills here as a "backup" and to avoid spurious error messages. - for (borrow_index, borrow_data) in self.borrow_set.borrows.iter_enumerated() { - if let ReScope(scope) = borrow_data.region { - // Check that the scope is not actually a scope from a function that is - // a parent of our closure. Note that the CallSite scope itself is - // *outside* of the closure, for some weird reason. - if let Some(root_scope) = self.root_scope { - if *scope != root_scope && - self.scope_tree.is_subscope_of(*scope, root_scope) - { - sets.kill(borrow_index); - } - } - } - } - } - mir::TerminatorKind::Abort | - mir::TerminatorKind::SwitchInt {..} | - mir::TerminatorKind::Drop {..} | - mir::TerminatorKind::DropAndReplace {..} | - mir::TerminatorKind::Call {..} | - mir::TerminatorKind::Assert {..} | - mir::TerminatorKind::Yield {..} | - mir::TerminatorKind::Goto {..} | - mir::TerminatorKind::FalseEdges {..} | - mir::TerminatorKind::FalseUnwind {..} | - mir::TerminatorKind::Unreachable => {} - } - } + fn terminator_effect(&self, _: &mut BlockSets, _: Location) {} fn propagate_call_return(&self, _in_out: &mut BitSet, From 1d7fc0ca500d8e532e7b1019397baec353560c26 Mon Sep 17 00:00:00 2001 From: Matthew Jasper Date: Sat, 17 Nov 2018 21:07:17 +0000 Subject: [PATCH 09/88] Simplify MIR drop generation Now that EndRegion is gone, we don't need to create as many gotos. --- src/librustc_mir/build/scope.rs | 288 ++++++++++++++++---------------- src/test/mir-opt/issue-49232.rs | 41 +---- 2 files changed, 154 insertions(+), 175 deletions(-) diff --git a/src/librustc_mir/build/scope.rs b/src/librustc_mir/build/scope.rs index 1a8f8fbbb68db..2a11f24095b06 100644 --- a/src/librustc_mir/build/scope.rs +++ b/src/librustc_mir/build/scope.rs @@ -96,6 +96,7 @@ use rustc::hir::def_id::LOCAL_CRATE; use rustc::mir::*; use syntax_pos::{Span}; use rustc_data_structures::fx::FxHashMap; +use std::collections::hash_map::Entry; #[derive(Debug)] pub struct Scope<'tcx> { @@ -224,7 +225,7 @@ impl<'tcx> Scope<'tcx> { /// Should always be run for all inner scopes when a drop is pushed into some scope enclosing a /// larger extent of code. /// - /// `storage_only` controls whether to invalidate only drop paths run `StorageDead`. + /// `storage_only` controls whether to invalidate only drop paths that run `StorageDead`. /// `this_scope_only` controls whether to invalidate only drop paths that refer to the current /// top-of-scope (as opposed to dependent scopes). fn invalidate_cache(&mut self, storage_only: bool, this_scope_only: bool) { @@ -242,8 +243,8 @@ impl<'tcx> Scope<'tcx> { } if !storage_only && !this_scope_only { - for dropdata in &mut self.drops { - if let DropKind::Value { ref mut cached_block } = dropdata.kind { + for drop_data in &mut self.drops { + if let DropKind::Value { ref mut cached_block } = drop_data.kind { cached_block.invalidate(); } } @@ -323,7 +324,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { let parent_hir_id = tcx.hir.definitions().node_to_hir_id( self.source_scope_local_data[source_scope].lint_root - ); + ); let current_hir_id = tcx.hir.definitions().node_to_hir_id(node_id); sets.lint_level_set(parent_hir_id) == @@ -333,7 +334,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { if !same_lint_scopes { self.source_scope = self.new_source_scope(region_scope.1.span, lint_level, - None); + None); } } self.push_scope(region_scope); @@ -381,14 +382,18 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { let scope = self.scopes.pop().unwrap(); assert_eq!(scope.region_scope, region_scope.0); - let resume_block = self.resume_block(); - unpack!(block = build_scope_drops(&mut self.cfg, - resume_block, - &scope, - &self.scopes, - block, - self.arg_count, - false)); + let unwind_to = self.scopes.last().and_then(|next_scope| { + next_scope.cached_unwind.get(false) + }).unwrap_or_else(|| self.resume_block()); + + unpack!(block = build_scope_drops( + &mut self.cfg, + &scope, + block, + unwind_to, + self.arg_count, + false, + )); block.unit() } @@ -396,8 +401,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { /// Branch out of `block` to `target`, exiting all scopes up to /// and including `region_scope`. This will insert whatever drops are - /// needed, as well as tracking this exit for the SEME region. See - /// module comment for details. + /// needed. See module comment for details. pub fn exit_scope(&mut self, span: Span, region_scope: (region::Scope, SourceInfo), @@ -415,38 +419,51 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { // If we are emitting a `drop` statement, we need to have the cached // diverge cleanup pads ready in case that drop panics. - let may_panic = self.scopes[(len - scope_count)..].iter() - .any(|s| s.drops.iter().any(|s| s.kind.may_panic())); + let may_panic = self.scopes[(len - scope_count)..].iter().any(|s| s.needs_cleanup); if may_panic { self.diverge_cleanup(); } - { - let resume_block = self.resume_block(); - let mut rest = &mut self.scopes[(len - scope_count)..]; - while let Some((scope, rest_)) = {rest}.split_last_mut() { - rest = rest_; - block = if let Some(&e) = scope.cached_exits.get(&(target, region_scope.0)) { - self.cfg.terminate(block, scope.source_info(span), - TerminatorKind::Goto { target: e }); - return; - } else { - let b = self.cfg.start_new_block(); - self.cfg.terminate(block, scope.source_info(span), - TerminatorKind::Goto { target: b }); - scope.cached_exits.insert((target, region_scope.0), b); - b + let mut scopes = self.scopes[(len - scope_count - 1)..].iter_mut().rev(); + let mut scope = scopes.next().unwrap(); + for next_scope in scopes { + if scope.drops.is_empty() { + scope = next_scope; + continue; + } + let source_info = scope.source_info(span); + block = match scope.cached_exits.entry((target, region_scope.0)) { + Entry::Occupied(e) => { + self.cfg.terminate(block, source_info, + TerminatorKind::Goto { target: *e.get() }); + return; + } + Entry::Vacant(v) => { + let b = self.cfg.start_new_block(); + self.cfg.terminate(block, source_info, + TerminatorKind::Goto { target: b }); + v.insert(b); + b + } }; - unpack!(block = build_scope_drops(&mut self.cfg, - resume_block, - scope, - rest, - block, - self.arg_count, - false)); - } + let unwind_to = next_scope.cached_unwind.get(false).unwrap_or_else(|| { + debug_assert!(!may_panic, "cached block not present?"); + START_BLOCK + }); + + unpack!(block = build_scope_drops( + &mut self.cfg, + scope, + block, + unwind_to, + self.arg_count, + false, + )); + + scope = next_scope; } + let scope = &self.scopes[len - scope_count]; self.cfg.terminate(block, scope.source_info(span), TerminatorKind::Goto { target }); @@ -461,20 +478,20 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { return None; } - // Fill in the cache + // Fill in the cache for unwinds self.diverge_cleanup_gen(true); let src_info = self.scopes[0].source_info(self.fn_span); + let resume_block = self.resume_block(); + let mut scopes = self.scopes.iter_mut().rev().peekable(); let mut block = self.cfg.start_new_block(); let result = block; - let resume_block = self.resume_block(); - let mut rest = &mut self.scopes[..]; - while let Some((scope, rest_)) = {rest}.split_last_mut() { - rest = rest_; + while let Some(scope) = scopes.next() { if !scope.needs_cleanup { continue; } + block = if let Some(b) = scope.cached_generator_drop { self.cfg.terminate(block, src_info, TerminatorKind::Goto { target: b }); @@ -487,13 +504,20 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { b }; - unpack!(block = build_scope_drops(&mut self.cfg, - resume_block, - scope, - rest, - block, - self.arg_count, - true)); + let unwind_to = scopes.peek().as_ref().map(|scope| { + scope.cached_unwind.get(true).unwrap_or_else(|| { + span_bug!(src_info.span, "cached block not present?") + }) + }).unwrap_or(resume_block); + + unpack!(block = build_scope_drops( + &mut self.cfg, + scope, + block, + unwind_to, + self.arg_count, + true, + )); } self.cfg.terminate(block, src_info, TerminatorKind::GeneratorDrop); @@ -503,9 +527,9 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { /// Creates a new source scope, nested in the current one. pub fn new_source_scope(&mut self, - span: Span, - lint_level: LintLevel, - safety: Option) -> SourceScope { + span: Span, + lint_level: LintLevel, + safety: Option) -> SourceScope { let parent = self.source_scope; debug!("new_source_scope({:?}, {:?}, {:?}) - parent({:?})={:?}", span, lint_level, safety, @@ -742,8 +766,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { /// Creates a path that performs all required cleanup for unwinding. /// /// This path terminates in Resume. Returns the start of the path. - /// See module comment for more details. None indicates there’s no - /// cleanup to do at this point. + /// See module comment for more details. pub fn diverge_cleanup(&mut self) -> BasicBlock { self.diverge_cleanup_gen(false) } @@ -765,11 +788,6 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { } fn diverge_cleanup_gen(&mut self, generator_drop: bool) -> BasicBlock { - // To start, create the resume terminator. - let mut target = self.resume_block(); - - let Builder { ref mut cfg, ref mut scopes, .. } = *self; - // Build up the drops in **reverse** order. The end result will // look like: // @@ -781,11 +799,17 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { // store caches. If everything is cached, we'll just walk right // to left reading the cached results but never created anything. - if scopes.iter().any(|scope| scope.needs_cleanup) { - for scope in scopes.iter_mut() { - target = build_diverge_scope(cfg, scope.region_scope_span, - scope, target, generator_drop); - } + // Find the last cached block + let (mut target, first_uncached) = if let Some(cached_index) = self.scopes.iter() + .rposition(|scope| scope.cached_unwind.get(generator_drop).is_some()) { + (self.scopes[cached_index].cached_unwind.get(generator_drop).unwrap(), cached_index + 1) + } else { + (self.resume_block(), 0) + }; + + for scope in self.scopes[first_uncached..].iter_mut() { + target = build_diverge_scope(&mut self.cfg, scope.region_scope_span, + scope, target, generator_drop); } target @@ -859,64 +883,62 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { } /// Builds drops for pop_scope and exit_scope. -fn build_scope_drops<'tcx>(cfg: &mut CFG<'tcx>, - resume_block: BasicBlock, - scope: &Scope<'tcx>, - earlier_scopes: &[Scope<'tcx>], - mut block: BasicBlock, - arg_count: usize, - generator_drop: bool) - -> BlockAnd<()> { - debug!("build_scope_drops({:?} -> {:?})", block, scope); - let mut iter = scope.drops.iter().rev(); - while let Some(drop_data) = iter.next() { +fn build_scope_drops<'tcx>( + cfg: &mut CFG<'tcx>, + scope: &Scope<'tcx>, + mut block: BasicBlock, + last_unwind_to: BasicBlock, + arg_count: usize, + generator_drop: bool, +) -> BlockAnd<()> { + debug!("build_scope_drops({:?} -> {:?}", block, scope); + + // Build up the drops in evaluation order. The end result will + // look like: + // + // [SDs, drops[n]] --..> [SDs, drop[1]] -> [SDs, drop[0]] -> [[SDs]] + // | | | + // : | | + // V V + // [drop[n]] -...-> [drop[1]] ------> [drop[0]] ------> [last_unwind_to] + // + // The horizontal arrows represent the execution path when the drops return + // successfully. The downwards arrows represent the execution path when the + // drops panic (panicking while unwinding will abort, so there's no need for + // another set of arrows). The drops for the unwind path should have already + // been generated by `diverge_cleanup_gen`. + // + // The code in this function reads from right to left. + // Storage dead drops have to be done left to right (since we can only push + // to the end of a Vec). So, we find the next drop and then call + // push_storage_deads which will iterate backwards through them so that + // they are added in the correct order. + + let mut unwind_blocks = scope.drops.iter().rev().filter_map(|drop_data| { + if let DropKind::Value { cached_block } = drop_data.kind { + Some(cached_block.get(generator_drop).unwrap_or_else(|| { + span_bug!(drop_data.span, "cached block not present?") + })) + } else { + None + } + }); + + // When we unwind from a drop, we start cleaning up from the next one, so + // we don't need this block. + unwind_blocks.next(); + + for drop_data in scope.drops.iter().rev() { let source_info = scope.source_info(drop_data.span); match drop_data.kind { DropKind::Value { .. } => { - // Try to find the next block with its cached block for us to - // diverge into, either a previous block in this current scope or - // the top of the previous scope. - // - // If it wasn't for EndRegion, we could just chain all the DropData - // together and pick the first DropKind::Value. Please do that - // when we replace EndRegion with NLL. - let on_diverge = iter.clone().filter_map(|dd| { - match dd.kind { - DropKind::Value { cached_block } => Some(cached_block), - DropKind::Storage => None - } - }).next().or_else(|| { - if earlier_scopes.iter().any(|scope| scope.needs_cleanup) { - // If *any* scope requires cleanup code to be run, - // we must use the cached unwind from the *topmost* - // scope, to ensure all EndRegions from surrounding - // scopes are executed before the drop code runs. - Some(earlier_scopes.last().unwrap().cached_unwind) - } else { - // We don't need any further cleanup, so return None - // to avoid creating a landing pad. We can skip - // EndRegions because all local regions end anyway - // when the function unwinds. - // - // This is an important optimization because LLVM is - // terrible at optimizing landing pads. FIXME: I think - // it would be cleaner and better to do this optimization - // in SimplifyCfg instead of here. - None - } - }); - - let on_diverge = on_diverge.map(|cached_block| { - cached_block.get(generator_drop).unwrap_or_else(|| { - span_bug!(drop_data.span, "cached block not present?") - }) - }); + let unwind_to = unwind_blocks.next().unwrap_or(last_unwind_to); let next = cfg.start_new_block(); cfg.terminate(block, source_info, TerminatorKind::Drop { location: drop_data.location.clone(), target: next, - unwind: Some(on_diverge.unwrap_or(resume_block)) + unwind: Some(unwind_to) }); block = next; } @@ -943,20 +965,17 @@ fn build_scope_drops<'tcx>(cfg: &mut CFG<'tcx>, block.unit() } -fn build_diverge_scope<'a, 'gcx, 'tcx>(cfg: &mut CFG<'tcx>, - span: Span, - scope: &mut Scope<'tcx>, - mut target: BasicBlock, - generator_drop: bool) - -> BasicBlock +fn build_diverge_scope<'tcx>(cfg: &mut CFG<'tcx>, + span: Span, + scope: &mut Scope<'tcx>, + mut target: BasicBlock, + generator_drop: bool) + -> BasicBlock { // Build up the drops in **reverse** order. The end result will // look like: // - // [EndRegion Block] -> [drops[n]] -...-> [drops[0]] -> [Free] -> [target] - // | | - // +---------------------------------------------------------+ - // code for scope + // [drops[n]] -...-> [drops[0]] -> [target] // // The code in this function reads from right to left. At each // point, we check for cached blocks representing the @@ -1001,20 +1020,7 @@ fn build_diverge_scope<'a, 'gcx, 'tcx>(cfg: &mut CFG<'tcx>, }; } - // Finally, push the EndRegion block, used by mir-borrowck, and set - // `cached_unwind` to point to it (Block becomes trivial goto after - // pass that removes all EndRegions). - target = { - let cached_block = scope.cached_unwind.ref_mut(generator_drop); - if let Some(cached_block) = *cached_block { - cached_block - } else { - let block = cfg.start_new_cleanup_block(); - cfg.terminate(block, source_info(span), TerminatorKind::Goto { target }); - *cached_block = Some(block); - block - } - }; + *scope.cached_unwind.ref_mut(generator_drop) = Some(target); debug!("build_diverge_scope({:?}, {:?}) = {:?}", scope, span, target); diff --git a/src/test/mir-opt/issue-49232.rs b/src/test/mir-opt/issue-49232.rs index f9024b6706334..d8365c8c9cc23 100644 --- a/src/test/mir-opt/issue-49232.rs +++ b/src/test/mir-opt/issue-49232.rs @@ -44,7 +44,7 @@ fn main() { // falseUnwind -> [real: bb3, cleanup: bb4]; // } // bb2: { -// goto -> bb29; +// goto -> bb20; // } // bb3: { // StorageLive(_2); @@ -90,58 +90,31 @@ fn main() { // StorageDead(_3); // StorageLive(_6); // _6 = &_2; -// _5 = const std::mem::drop(move _6) -> [return: bb28, unwind: bb4]; +// _5 = const std::mem::drop(move _6) -> [return: bb19, unwind: bb4]; // } // bb15: { +// StorageDead(_3); // goto -> bb16; // } // bb16: { -// goto -> bb17; -// } -// bb17: { -// goto -> bb18; -// } -// bb18: { -// goto -> bb19; -// } -// bb19: { -// goto -> bb20; -// } -// bb20: { -// StorageDead(_3); -// goto -> bb21; -// } -// bb21: { -// goto -> bb22; -// } -// bb22: { // StorageDead(_2); -// goto -> bb23; -// } -// bb23: { -// goto -> bb24; -// } -// bb24: { -// goto -> bb25; -// } -// bb25: { // goto -> bb2; // } -// bb26: { +// bb17: { // _4 = (); // unreachable; // } -// bb27: { +// bb18: { // StorageDead(_4); // goto -> bb14; // } -// bb28: { +// bb19: { // StorageDead(_6); // _1 = (); // StorageDead(_2); // goto -> bb1; // } -// bb29: { +// bb20: { // return; // } // } From 089a50411f3bceb233dd5da8057252ff7b6b47e1 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 19 Nov 2018 12:05:21 -0800 Subject: [PATCH 10/88] Encode a custom "producers" section in wasm files This commit implements WebAssembly/tool-conventions#65 for wasm files produced by the Rust compiler. This adds a bit of metadata to wasm modules to indicate that the file's language includes Rust and the file's "processed-by" tools includes rustc. The thinking with this section is to eventually have telemetry in browsers tracking all this. --- src/librustc_codegen_llvm/back/link.rs | 5 ++ src/librustc_codegen_llvm/back/wasm.rs | 107 +++++++++++++++++++++++++ 2 files changed, 112 insertions(+) diff --git a/src/librustc_codegen_llvm/back/link.rs b/src/librustc_codegen_llvm/back/link.rs index 20f05d110877a..fa9a852ad0135 100644 --- a/src/librustc_codegen_llvm/back/link.rs +++ b/src/librustc_codegen_llvm/back/link.rs @@ -692,6 +692,11 @@ fn link_natively(sess: &Session, if sess.opts.target_triple.triple() == "wasm32-unknown-unknown" { wasm::rewrite_imports(&out_filename, &codegen_results.crate_info.wasm_imports); + wasm::add_producer_section( + &out_filename, + &sess.edition().to_string(), + option_env!("CFG_VERSION").unwrap_or("unknown"), + ); } } diff --git a/src/librustc_codegen_llvm/back/wasm.rs b/src/librustc_codegen_llvm/back/wasm.rs index 7101255173caf..1a5c65f3c4397 100644 --- a/src/librustc_codegen_llvm/back/wasm.rs +++ b/src/librustc_codegen_llvm/back/wasm.rs @@ -17,6 +17,7 @@ use serialize::leb128; // https://webassembly.github.io/spec/core/binary/modules.html#binary-importsec const WASM_IMPORT_SECTION_ID: u8 = 2; +const WASM_CUSTOM_SECTION_ID: u8 = 0; const WASM_EXTERNAL_KIND_FUNCTION: u8 = 0; const WASM_EXTERNAL_KIND_TABLE: u8 = 1; @@ -121,6 +122,112 @@ pub fn rewrite_imports(path: &Path, import_map: &FxHashMap) { } } +/// Add or augment the existing `producers` section to encode information about +/// the Rust compiler used to produce the wasm file. +pub fn add_producer_section( + path: &Path, + rust_version: &str, + rustc_version: &str, +) { + struct Field<'a> { + name: &'a str, + values: Vec>, + } + + #[derive(Copy, Clone)] + struct FieldValue<'a> { + name: &'a str, + version: &'a str, + } + + let wasm = fs::read(path).expect("failed to read wasm output"); + let mut ret = WasmEncoder::new(); + ret.data.extend(&wasm[..8]); + + // skip the 8 byte wasm/version header + let rustc_value = FieldValue { + name: "rustc", + version: rustc_version, + }; + let rust_value = FieldValue { + name: "Rust", + version: rust_version, + }; + let mut fields = Vec::new(); + let mut wrote_rustc = false; + let mut wrote_rust = false; + + // Move all sections from the original wasm file to our output, skipping + // everything except the producers section + for (id, raw) in WasmSections(WasmDecoder::new(&wasm[8..])) { + if id != WASM_CUSTOM_SECTION_ID { + ret.byte(id); + ret.bytes(raw); + continue + } + let mut decoder = WasmDecoder::new(raw); + if decoder.str() != "producers" { + ret.byte(id); + ret.bytes(raw); + continue + } + + // Read off the producers section into our fields outside the loop, + // we'll re-encode the producers section when we're done (to handle an + // entirely missing producers section as well). + info!("rewriting existing producers section"); + + for _ in 0..decoder.u32() { + let name = decoder.str(); + let mut values = Vec::new(); + for _ in 0..decoder.u32() { + let name = decoder.str(); + let version = decoder.str(); + values.push(FieldValue { name, version }); + } + + if name == "language" { + values.push(rust_value); + wrote_rust = true; + } else if name == "processed-by" { + values.push(rustc_value); + wrote_rustc = true; + } + fields.push(Field { name, values }); + } + } + + if !wrote_rust { + fields.push(Field { + name: "language", + values: vec![rust_value], + }); + } + if !wrote_rustc { + fields.push(Field { + name: "processed-by", + values: vec![rustc_value], + }); + } + + // Append the producers section to the end of the wasm file. + let mut section = WasmEncoder::new(); + section.str("producers"); + section.u32(fields.len() as u32); + for field in fields { + section.str(field.name); + section.u32(field.values.len() as u32); + for value in field.values { + section.str(value.name); + section.str(value.version); + } + } + ret.byte(WASM_CUSTOM_SECTION_ID); + ret.bytes(§ion.data); + + fs::write(path, &ret.data).expect("failed to write wasm output"); +} + struct WasmSections<'a>(WasmDecoder<'a>); impl<'a> Iterator for WasmSections<'a> { From 6eeedbcd70855b45cf41ef14bc91a15f0ee67c7c Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 20 Nov 2018 15:08:26 +0100 Subject: [PATCH 11/88] generator fields are not necessarily initialized --- src/librustc_mir/interpret/visitor.rs | 33 ++++++++++++++++++--------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/src/librustc_mir/interpret/visitor.rs b/src/librustc_mir/interpret/visitor.rs index f0a71242599bf..505195763d062 100644 --- a/src/librustc_mir/interpret/visitor.rs +++ b/src/librustc_mir/interpret/visitor.rs @@ -142,6 +142,7 @@ macro_rules! make_value_visitor { self.walk_value(v) } /// Visit the given value as a union. No automatic recursion can happen here. + /// Also called for the fields of a generator, which may or may not be initialized. #[inline(always)] fn visit_union(&mut self, _v: Self::V) -> EvalResult<'tcx> { @@ -291,17 +292,28 @@ macro_rules! make_value_visitor { // use that as an unambiguous signal for detecting primitives. Make sure // we did not miss any primitive. debug_assert!(fields > 0); - self.visit_union(v)?; + self.visit_union(v) }, layout::FieldPlacement::Arbitrary { ref offsets, .. } => { - // FIXME: We collect in a vec because otherwise there are lifetime errors: - // Projecting to a field needs (mutable!) access to `ecx`. - let fields: Vec> = - (0..offsets.len()).map(|i| { - v.project_field(self.ecx(), i as u64) - }) - .collect(); - self.visit_aggregate(v, fields.into_iter())?; + // Special handling needed for generators: All but the first field + // (which is the state) are actually implicitly `MaybeUninit`, i.e., + // they may or may not be initialized, so we cannot visit them. + match v.layout().ty.sty { + ty::Generator(..) => { + let field = v.project_field(self.ecx(), 0)?; + self.visit_aggregate(v, std::iter::once(Ok(field))) + } + _ => { + // FIXME: We collect in a vec because otherwise there are lifetime + // errors: Projecting to a field needs access to `ecx`. + let fields: Vec> = + (0..offsets.len()).map(|i| { + v.project_field(self.ecx(), i as u64) + }) + .collect(); + self.visit_aggregate(v, fields.into_iter()) + } + } }, layout::FieldPlacement::Array { .. } => { // Let's get an mplace first. @@ -317,10 +329,9 @@ macro_rules! make_value_visitor { .map(|f| f.and_then(|f| { Ok(Value::from_mem_place(f)) })); - self.visit_aggregate(v, iter)?; + self.visit_aggregate(v, iter) } } - Ok(()) } } } From 033cbfec4d3bb23948a99379f8d63b7cfe5eed45 Mon Sep 17 00:00:00 2001 From: Corey Farwell Date: Tue, 20 Nov 2018 09:34:15 -0500 Subject: [PATCH 12/88] Incorporate `dyn` into more comments and docs. --- src/liballoc/boxed.rs | 16 ++++++++-------- src/liballoc/rc.rs | 4 ++-- src/libcore/raw.rs | 2 +- src/librustc/mir/mod.rs | 2 +- src/librustc/ty/adjustment.rs | 2 +- src/librustc/ty/util.rs | 2 +- src/librustc_codegen_ssa/meth.rs | 2 +- src/libstd/fs.rs | 4 ++-- src/test/run-pass/string-box-error.rs | 3 ++- 9 files changed, 19 insertions(+), 18 deletions(-) diff --git a/src/liballoc/boxed.rs b/src/liballoc/boxed.rs index 63b262d1f3d98..c3a84bf778d03 100644 --- a/src/liballoc/boxed.rs +++ b/src/liballoc/boxed.rs @@ -489,7 +489,7 @@ impl Box { /// ``` /// use std::any::Any; /// - /// fn print_if_string(value: Box) { + /// fn print_if_string(value: Box) { /// if let Ok(string) = value.downcast::() { /// println!("String ({}): {}", string.len(), string); /// } @@ -523,7 +523,7 @@ impl Box { /// ``` /// use std::any::Any; /// - /// fn print_if_string(value: Box) { + /// fn print_if_string(value: Box) { /// if let Ok(string) = value.downcast::() { /// println!("String ({}): {}", string.len(), string); /// } @@ -618,10 +618,10 @@ impl FusedIterator for Box {} /// `FnBox` is a version of the `FnOnce` intended for use with boxed /// closure objects. The idea is that where one would normally store a -/// `Box` in a data structure, you should use -/// `Box`. The two traits behave essentially the same, except +/// `Box` in a data structure, you should use +/// `Box`. The two traits behave essentially the same, except /// that a `FnBox` closure can only be called if it is boxed. (Note -/// that `FnBox` may be deprecated in the future if `Box` +/// that `FnBox` may be deprecated in the future if `Box` /// closures become directly usable.) /// /// # Examples @@ -629,7 +629,7 @@ impl FusedIterator for Box {} /// Here is a snippet of code which creates a hashmap full of boxed /// once closures and then removes them one by one, calling each /// closure as it is removed. Note that the type of the closures -/// stored in the map is `Box i32>` and not `Box i32>` and not `Box i32>`. /// /// ``` @@ -638,8 +638,8 @@ impl FusedIterator for Box {} /// use std::boxed::FnBox; /// use std::collections::HashMap; /// -/// fn make_map() -> HashMap i32>> { -/// let mut map: HashMap i32>> = HashMap::new(); +/// fn make_map() -> HashMap i32>> { +/// let mut map: HashMap i32>> = HashMap::new(); /// map.insert(1, Box::new(|| 22)); /// map.insert(2, Box::new(|| 44)); /// map diff --git a/src/liballoc/rc.rs b/src/liballoc/rc.rs index bb52d7990ff57..12c19912662fc 100644 --- a/src/liballoc/rc.rs +++ b/src/liballoc/rc.rs @@ -634,7 +634,7 @@ impl Rc { impl Rc { #[inline] #[stable(feature = "rc_downcast", since = "1.29.0")] - /// Attempt to downcast the `Rc` to a concrete type. + /// Attempt to downcast the `Rc` to a concrete type. /// /// # Examples /// @@ -642,7 +642,7 @@ impl Rc { /// use std::any::Any; /// use std::rc::Rc; /// - /// fn print_if_string(value: Rc) { + /// fn print_if_string(value: Rc) { /// if let Ok(string) = value.downcast::() { /// println!("String ({}): {}", string.len(), string); /// } diff --git a/src/libcore/raw.rs b/src/libcore/raw.rs index a95f05227fb8b..b7597795b5e62 100644 --- a/src/libcore/raw.rs +++ b/src/libcore/raw.rs @@ -21,7 +21,7 @@ /// The representation of a trait object like `&SomeTrait`. /// /// This struct has the same layout as types like `&SomeTrait` and -/// `Box`. The [Trait Objects chapter of the +/// `Box`. The [Trait Objects chapter of the /// Book][moreinfo] contains more details about the precise nature of /// these internals. /// diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs index d8e45c881f555..acc03bd4a48b1 100644 --- a/src/librustc/mir/mod.rs +++ b/src/librustc/mir/mod.rs @@ -2207,7 +2207,7 @@ pub enum CastKind { /// "Unsize" -- convert a thin-or-fat pointer to a fat pointer. /// codegen must figure out the details once full monomorphization /// is known. For example, this could be used to cast from a - /// `&[i32;N]` to a `&[i32]`, or a `Box` to a `Box` + /// `&[i32;N]` to a `&[i32]`, or a `Box` to a `Box` /// (presuming `T: Trait`). Unsize, } diff --git a/src/librustc/ty/adjustment.rs b/src/librustc/ty/adjustment.rs index 3263da8fda365..83521c5f72406 100644 --- a/src/librustc/ty/adjustment.rs +++ b/src/librustc/ty/adjustment.rs @@ -48,7 +48,7 @@ use ty::subst::Substs; /// stored in `unsize` is `Foo<[i32]>`, we don't store any further detail about /// the underlying conversions from `[i32; 4]` to `[i32]`. /// -/// 3. Coercing a `Box` to `Box` is an interesting special case. In +/// 3. Coercing a `Box` to `Box` is an interesting special case. In /// that case, we have the pointer we need coming in, so there are no /// autoderefs, and no autoref. Instead we just do the `Unsize` transformation. /// At some point, of course, `Box` should move out of the compiler, in which diff --git a/src/librustc/ty/util.rs b/src/librustc/ty/util.rs index 3d0c54d6b0a5b..0d756aa078195 100644 --- a/src/librustc/ty/util.rs +++ b/src/librustc/ty/util.rs @@ -303,7 +303,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { /// Same as applying struct_tail on `source` and `target`, but only /// keeps going as long as the two types are instances of the same /// structure definitions. - /// For `(Foo>, Foo)`, the result will be `(Foo, Trait)`, + /// For `(Foo>, Foo)`, the result will be `(Foo, Trait)`, /// whereas struct_tail produces `T`, and `Trait`, respectively. pub fn struct_lockstep_tails(self, source: Ty<'tcx>, diff --git a/src/librustc_codegen_ssa/meth.rs b/src/librustc_codegen_ssa/meth.rs index 06c4f7a87d880..24be932141c7e 100644 --- a/src/librustc_codegen_ssa/meth.rs +++ b/src/librustc_codegen_ssa/meth.rs @@ -74,7 +74,7 @@ impl<'a, 'tcx: 'a> VirtualIndex { /// The vtables are cached instead of created on every call. /// /// The `trait_ref` encodes the erased self type. Hence if we are -/// making an object `Foo` from a value of type `Foo`, then +/// making an object `Foo` from a value of type `Foo`, then /// `trait_ref` would map `T:Trait`. pub fn get_vtable<'tcx, Cx: CodegenMethods<'tcx>>( cx: &Cx, diff --git a/src/libstd/fs.rs b/src/libstd/fs.rs index f4703dec187b8..92678dd5cede0 100644 --- a/src/libstd/fs.rs +++ b/src/libstd/fs.rs @@ -256,7 +256,7 @@ fn initial_buffer_size(file: &File) -> usize { /// use std::fs; /// use std::net::SocketAddr; /// -/// fn main() -> Result<(), Box> { +/// fn main() -> Result<(), Box> { /// let foo: SocketAddr = String::from_utf8_lossy(&fs::read("address.txt")?).parse()?; /// Ok(()) /// } @@ -298,7 +298,7 @@ pub fn read>(path: P) -> io::Result> { /// use std::fs; /// use std::net::SocketAddr; /// -/// fn main() -> Result<(), Box> { +/// fn main() -> Result<(), Box> { /// let foo: SocketAddr = fs::read_to_string("address.txt")?.parse()?; /// Ok(()) /// } diff --git a/src/test/run-pass/string-box-error.rs b/src/test/run-pass/string-box-error.rs index a80d9a0593566..5d8a664b93dad 100644 --- a/src/test/run-pass/string-box-error.rs +++ b/src/test/run-pass/string-box-error.rs @@ -8,7 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// Ensure that both `Box` and `Box` can be obtained from `String`. +// Ensure that both `Box` and `Box` can be +// obtained from `String`. use std::error::Error; From 7f1077700c019b2dc8d651528ecad106c9267858 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 20 Nov 2018 15:55:23 +0100 Subject: [PATCH 13/88] fix comment --- src/librustc_mir/interpret/visitor.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/librustc_mir/interpret/visitor.rs b/src/librustc_mir/interpret/visitor.rs index 505195763d062..a645e06732269 100644 --- a/src/librustc_mir/interpret/visitor.rs +++ b/src/librustc_mir/interpret/visitor.rs @@ -142,7 +142,6 @@ macro_rules! make_value_visitor { self.walk_value(v) } /// Visit the given value as a union. No automatic recursion can happen here. - /// Also called for the fields of a generator, which may or may not be initialized. #[inline(always)] fn visit_union(&mut self, _v: Self::V) -> EvalResult<'tcx> { From 6befe6784f37d953884ef05982b41c3a11932170 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 20 Nov 2018 16:19:24 +0100 Subject: [PATCH 14/88] treat generator fields like unions --- src/librustc_mir/interpret/visitor.rs | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/src/librustc_mir/interpret/visitor.rs b/src/librustc_mir/interpret/visitor.rs index a645e06732269..81e56f3115d26 100644 --- a/src/librustc_mir/interpret/visitor.rs +++ b/src/librustc_mir/interpret/visitor.rs @@ -158,7 +158,9 @@ macro_rules! make_value_visitor { ) -> EvalResult<'tcx> { self.walk_aggregate(v, fields) } - /// Called each time we recurse down to a field, passing in old and new value. + + /// Called each time we recurse down to a field of a "product-like" aggregate + /// (structs, tuples, arrays and the like, but not enums), passing in old and new value. /// This gives the visitor the chance to track the stack of nested fields that /// we are descending through. #[inline(always)] @@ -171,6 +173,19 @@ macro_rules! make_value_visitor { self.visit_value(new_val) } + /// Called for recursing into the field of a generator. These are not known to be + /// initialized, so we treat them like unions. + #[inline(always)] + fn visit_generator_field( + &mut self, + _old_val: Self::V, + _field: usize, + new_val: Self::V, + ) -> EvalResult<'tcx> { + self.visit_union(new_val) + } + + /// Called when recursing into an enum variant. #[inline(always)] fn visit_variant( &mut self, @@ -300,7 +315,12 @@ macro_rules! make_value_visitor { match v.layout().ty.sty { ty::Generator(..) => { let field = v.project_field(self.ecx(), 0)?; - self.visit_aggregate(v, std::iter::once(Ok(field))) + self.visit_aggregate(v, std::iter::once(Ok(field)))?; + for i in 1..offsets.len() { + let field = v.project_field(self.ecx(), i as u64)?; + self.visit_generator_field(v, i, field)?; + } + Ok(()) } _ => { // FIXME: We collect in a vec because otherwise there are lifetime From 682b33a1103695de3c6520d55204b3c3d45f68ec Mon Sep 17 00:00:00 2001 From: Masaki Hara Date: Mon, 19 Nov 2018 00:15:41 +0900 Subject: [PATCH 15/88] Add require_type_is_sized_deferred. --- src/librustc_typeck/check/mod.rs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index aabef5c323483..b882d982c1fe5 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -208,6 +208,10 @@ pub struct Inherited<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { fulfillment_cx: RefCell>>, + // Some additional `Sized` obligations badly affect type inference. + // These obligations are added in a later stage of typeck. + deferred_sized_obligations: RefCell, Span, traits::ObligationCauseCode<'tcx>)>>, + // When we process a call like `c()` where `c` is a closure type, // we may not have decided yet whether `c` is a `Fn`, `FnMut`, or // `FnOnce` closure. In that case, we defer full resolution of the @@ -644,6 +648,7 @@ impl<'a, 'gcx, 'tcx> Inherited<'a, 'gcx, 'tcx> { infcx, fulfillment_cx: RefCell::new(TraitEngine::new(tcx)), locals: RefCell::new(Default::default()), + deferred_sized_obligations: RefCell::new(Vec::new()), deferred_call_resolutions: RefCell::new(Default::default()), deferred_cast_checks: RefCell::new(Vec::new()), deferred_generator_interiors: RefCell::new(Vec::new()), @@ -907,6 +912,10 @@ fn typeck_tables_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, fcx.closure_analyze(body); assert!(fcx.deferred_call_resolutions.borrow().is_empty()); fcx.resolve_generator_interiors(def_id); + + for (ty, span, code) in fcx.deferred_sized_obligations.borrow_mut().drain(..) { + fcx.require_type_is_sized(ty, span, code); + } fcx.select_all_obligations_or_error(); if fn_decl.is_some() { @@ -2345,6 +2354,14 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { self.require_type_meets(ty, span, code, lang_item); } + pub fn require_type_is_sized_deferred(&self, + ty: Ty<'tcx>, + span: Span, + code: traits::ObligationCauseCode<'tcx>) + { + self.deferred_sized_obligations.borrow_mut().push((ty, span, code)); + } + pub fn register_bound(&self, ty: Ty<'tcx>, def_id: DefId, From 8b426232eef0629265bbfd0bc81fab75e113762b Mon Sep 17 00:00:00 2001 From: Masaki Hara Date: Mon, 19 Nov 2018 00:18:13 +0900 Subject: [PATCH 16/88] Check arg/ret sizedness at ExprKind::Path. --- src/librustc_typeck/check/mod.rs | 25 +++++++++++++++++++ src/test/ui/issues/issue-30355.nll.stderr | 22 ---------------- src/test/ui/issues/issue-30355.rs | 4 +-- src/test/ui/issues/issue-30355.stderr | 24 ++++++------------ src/test/ui/unsized-locals/unsized-exprs.rs | 2 ++ .../ui/unsized-locals/unsized-exprs.stderr | 13 +++++++++- src/test/ui/unsized-locals/unsized-exprs2.rs | 2 -- 7 files changed, 48 insertions(+), 44 deletions(-) delete mode 100644 src/test/ui/issues/issue-30355.nll.stderr diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index b882d982c1fe5..572979d063d2f 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -3956,6 +3956,31 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { tcx.types.err }; + if let ty::FnDef(..) = ty.sty { + let fn_sig = ty.fn_sig(tcx); + if !tcx.features().unsized_locals { + // We want to remove some Sized bounds from std functions, + // but don't want to expose the removal to stable Rust. + // i.e. we don't want to allow + // + // ```rust + // drop as fn(str); + // ``` + // + // to work in stable even if the Sized bound on `drop` is relaxed. + for i in 0..fn_sig.inputs().skip_binder().len() { + let input = tcx.erase_late_bound_regions(&fn_sig.input(i)); + self.require_type_is_sized_deferred(input, expr.span, + traits::SizedArgumentType); + } + } + // Here we want to prevent struct constructors from returning unsized types. + // There were two cases this happened: fn pointer coercion in stable + // and usual function call in presense of unsized_locals. + let output = tcx.erase_late_bound_regions(&fn_sig.output()); + self.require_type_is_sized_deferred(output, expr.span, traits::SizedReturnType); + } + // We always require that the type provided as the value for // a type parameter outlives the moment of instantiation. let substs = self.tables.borrow().node_substs(expr.hir_id); diff --git a/src/test/ui/issues/issue-30355.nll.stderr b/src/test/ui/issues/issue-30355.nll.stderr deleted file mode 100644 index fdf8157dcf833..0000000000000 --- a/src/test/ui/issues/issue-30355.nll.stderr +++ /dev/null @@ -1,22 +0,0 @@ -error[E0161]: cannot move a value of type X: the size of X cannot be statically determined - --> $DIR/issue-30355.rs:15:6 - | -LL | &X(*Y) - | ^^^^^ - -error[E0161]: cannot move a value of type [u8]: the size of [u8] cannot be statically determined - --> $DIR/issue-30355.rs:15:8 - | -LL | &X(*Y) - | ^^ - -error[E0508]: cannot move out of type `[u8]`, a non-copy slice - --> $DIR/issue-30355.rs:15:8 - | -LL | &X(*Y) - | ^^ cannot move out of here - -error: aborting due to 3 previous errors - -Some errors occurred: E0161, E0508. -For more information about an error, try `rustc --explain E0161`. diff --git a/src/test/ui/issues/issue-30355.rs b/src/test/ui/issues/issue-30355.rs index ee19d04031893..8d5eac06c4379 100644 --- a/src/test/ui/issues/issue-30355.rs +++ b/src/test/ui/issues/issue-30355.rs @@ -13,9 +13,7 @@ pub struct X([u8]); pub static Y: &'static X = { const Y: &'static [u8] = b""; &X(*Y) - //~^ ERROR cannot move out - //~^^ ERROR cannot move a - //~^^^ ERROR cannot move a + //~^ ERROR E0277 }; fn main() {} diff --git a/src/test/ui/issues/issue-30355.stderr b/src/test/ui/issues/issue-30355.stderr index 7e843688035af..1b55f20e6b431 100644 --- a/src/test/ui/issues/issue-30355.stderr +++ b/src/test/ui/issues/issue-30355.stderr @@ -1,22 +1,14 @@ -error[E0161]: cannot move a value of type X: the size of X cannot be statically determined +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time --> $DIR/issue-30355.rs:15:6 | LL | &X(*Y) - | ^^^^^ - -error[E0161]: cannot move a value of type [u8]: the size of [u8] cannot be statically determined - --> $DIR/issue-30355.rs:15:8 + | ^ doesn't have a size known at compile-time | -LL | &X(*Y) - | ^^ - -error[E0507]: cannot move out of borrowed content - --> $DIR/issue-30355.rs:15:8 - | -LL | &X(*Y) - | ^^ cannot move out of borrowed content + = help: the trait `std::marker::Sized` is not implemented for `[u8]` + = note: to learn more, visit + = note: all function arguments must have a statically known size + = help: unsized locals are gated as an unstable feature -error: aborting due to 3 previous errors +error: aborting due to previous error -Some errors occurred: E0161, E0507. -For more information about an error, try `rustc --explain E0161`. +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/unsized-locals/unsized-exprs.rs b/src/test/ui/unsized-locals/unsized-exprs.rs index 0cf93c78c4abe..8ca88edcb6add 100644 --- a/src/test/ui/unsized-locals/unsized-exprs.rs +++ b/src/test/ui/unsized-locals/unsized-exprs.rs @@ -23,4 +23,6 @@ fn main() { //~^ERROR E0277 udrop::>(A { 0: *foo() }); //~^ERROR E0277 + udrop::>(A(*foo())); + //~^ERROR E0277 } diff --git a/src/test/ui/unsized-locals/unsized-exprs.stderr b/src/test/ui/unsized-locals/unsized-exprs.stderr index eb2016941770c..0ca60e8dea0d9 100644 --- a/src/test/ui/unsized-locals/unsized-exprs.stderr +++ b/src/test/ui/unsized-locals/unsized-exprs.stderr @@ -20,6 +20,17 @@ LL | udrop::>(A { 0: *foo() }); = note: required because it appears within the type `A<[u8]>` = note: structs must have a statically known size to be initialized -error: aborting due to 2 previous errors +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> $DIR/unsized-exprs.rs:26:22 + | +LL | udrop::>(A(*foo())); + | ^ doesn't have a size known at compile-time + | + = help: within `A<[u8]>`, the trait `std::marker::Sized` is not implemented for `[u8]` + = note: to learn more, visit + = note: required because it appears within the type `A<[u8]>` + = note: the return type of a function must have a statically known size + +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/unsized-locals/unsized-exprs2.rs b/src/test/ui/unsized-locals/unsized-exprs2.rs index ae69893a83577..3fb5a002e0e3c 100644 --- a/src/test/ui/unsized-locals/unsized-exprs2.rs +++ b/src/test/ui/unsized-locals/unsized-exprs2.rs @@ -21,6 +21,4 @@ impl std::ops::Add for A<[u8]> { fn main() { udrop::<[u8]>(foo()[..]); //~^ERROR cannot move out of indexed content - // FIXME: should be error - udrop::>(A(*foo())); } From 8ab5be13a31261317c0e4b52bd4743da03e5bf74 Mon Sep 17 00:00:00 2001 From: Masaki Hara Date: Mon, 19 Nov 2018 00:19:14 +0900 Subject: [PATCH 17/88] Add tests verifying #50940. --- .../ui/unsized-locals/issue-50940-with-feature.rs | 7 +++++++ .../unsized-locals/issue-50940-with-feature.stderr | 14 ++++++++++++++ src/test/ui/unsized-locals/issue-50940.rs | 5 +++++ src/test/ui/unsized-locals/issue-50940.stderr | 14 ++++++++++++++ 4 files changed, 40 insertions(+) create mode 100644 src/test/ui/unsized-locals/issue-50940-with-feature.rs create mode 100644 src/test/ui/unsized-locals/issue-50940-with-feature.stderr create mode 100644 src/test/ui/unsized-locals/issue-50940.rs create mode 100644 src/test/ui/unsized-locals/issue-50940.stderr diff --git a/src/test/ui/unsized-locals/issue-50940-with-feature.rs b/src/test/ui/unsized-locals/issue-50940-with-feature.rs new file mode 100644 index 0000000000000..3e5d39ab31150 --- /dev/null +++ b/src/test/ui/unsized-locals/issue-50940-with-feature.rs @@ -0,0 +1,7 @@ +#![feature(unsized_locals)] + +fn main() { + struct A(X); + A as fn(str) -> A; + //~^ERROR the size for values of type `str` cannot be known at compilation time +} diff --git a/src/test/ui/unsized-locals/issue-50940-with-feature.stderr b/src/test/ui/unsized-locals/issue-50940-with-feature.stderr new file mode 100644 index 0000000000000..f4f015fa19065 --- /dev/null +++ b/src/test/ui/unsized-locals/issue-50940-with-feature.stderr @@ -0,0 +1,14 @@ +error[E0277]: the size for values of type `str` cannot be known at compilation time + --> $DIR/issue-50940-with-feature.rs:5:5 + | +LL | A as fn(str) -> A; + | ^ doesn't have a size known at compile-time + | + = help: within `main::A`, the trait `std::marker::Sized` is not implemented for `str` + = note: to learn more, visit + = note: required because it appears within the type `main::A` + = note: the return type of a function must have a statically known size + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/unsized-locals/issue-50940.rs b/src/test/ui/unsized-locals/issue-50940.rs new file mode 100644 index 0000000000000..7ba809b7e83e3 --- /dev/null +++ b/src/test/ui/unsized-locals/issue-50940.rs @@ -0,0 +1,5 @@ +fn main() { + struct A(X); + A as fn(str) -> A; + //~^ERROR the size for values of type `str` cannot be known at compilation time +} diff --git a/src/test/ui/unsized-locals/issue-50940.stderr b/src/test/ui/unsized-locals/issue-50940.stderr new file mode 100644 index 0000000000000..9f3669ccf1f15 --- /dev/null +++ b/src/test/ui/unsized-locals/issue-50940.stderr @@ -0,0 +1,14 @@ +error[E0277]: the size for values of type `str` cannot be known at compilation time + --> $DIR/issue-50940.rs:3:5 + | +LL | A as fn(str) -> A; + | ^ doesn't have a size known at compile-time + | + = help: the trait `std::marker::Sized` is not implemented for `str` + = note: to learn more, visit + = note: all function arguments must have a statically known size + = help: unsized locals are gated as an unstable feature + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`. From 2ff6ffc872645bf6f5bc7dda4a817a1fc7789684 Mon Sep 17 00:00:00 2001 From: Masaki Hara Date: Mon, 19 Nov 2018 00:26:05 +0900 Subject: [PATCH 18/88] Add tests for unsized-locals functions stability. --- src/test/run-pass/unsized-locals/unsized-exprs.rs | 1 + src/test/ui/unsized-locals/auxiliary/ufuncs.rs | 3 +++ src/test/ui/unsized-locals/unsized-exprs3.rs | 10 ++++++++++ src/test/ui/unsized-locals/unsized-exprs3.stderr | 14 ++++++++++++++ 4 files changed, 28 insertions(+) create mode 100644 src/test/ui/unsized-locals/auxiliary/ufuncs.rs create mode 100644 src/test/ui/unsized-locals/unsized-exprs3.rs create mode 100644 src/test/ui/unsized-locals/unsized-exprs3.stderr diff --git a/src/test/run-pass/unsized-locals/unsized-exprs.rs b/src/test/run-pass/unsized-locals/unsized-exprs.rs index 4b988f1e72d5a..bc64fcdec2e39 100644 --- a/src/test/run-pass/unsized-locals/unsized-exprs.rs +++ b/src/test/run-pass/unsized-locals/unsized-exprs.rs @@ -34,4 +34,5 @@ fn main() { udrop::<[u8]>((*foo())); udrop::<[u8]>((*tfoo()).1); *afoo() + 42; + udrop as fn([u8]); } diff --git a/src/test/ui/unsized-locals/auxiliary/ufuncs.rs b/src/test/ui/unsized-locals/auxiliary/ufuncs.rs new file mode 100644 index 0000000000000..065563d45a472 --- /dev/null +++ b/src/test/ui/unsized-locals/auxiliary/ufuncs.rs @@ -0,0 +1,3 @@ +#![feature(unsized_locals)] + +pub fn udrop(_x: T) {} diff --git a/src/test/ui/unsized-locals/unsized-exprs3.rs b/src/test/ui/unsized-locals/unsized-exprs3.rs new file mode 100644 index 0000000000000..2133b01e09480 --- /dev/null +++ b/src/test/ui/unsized-locals/unsized-exprs3.rs @@ -0,0 +1,10 @@ +// aux-build:ufuncs.rs + +extern crate ufuncs; + +use ufuncs::udrop; + +fn main() { + udrop as fn([u8]); + //~^ERROR E0277 +} diff --git a/src/test/ui/unsized-locals/unsized-exprs3.stderr b/src/test/ui/unsized-locals/unsized-exprs3.stderr new file mode 100644 index 0000000000000..42f91a946a851 --- /dev/null +++ b/src/test/ui/unsized-locals/unsized-exprs3.stderr @@ -0,0 +1,14 @@ +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> $DIR/unsized-exprs3.rs:8:5 + | +LL | udrop as fn([u8]); + | ^^^^^ doesn't have a size known at compile-time + | + = help: the trait `std::marker::Sized` is not implemented for `[u8]` + = note: to learn more, visit + = note: all function arguments must have a statically known size + = help: unsized locals are gated as an unstable feature + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`. From c6a803a286faae901a6ebd35ec222901f691c7ec Mon Sep 17 00:00:00 2001 From: Masaki Hara Date: Mon, 19 Nov 2018 00:27:16 +0900 Subject: [PATCH 19/88] Modify doc to reflect the unsized-locals improvement. --- src/doc/unstable-book/src/language-features/unsized-locals.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/doc/unstable-book/src/language-features/unsized-locals.md b/src/doc/unstable-book/src/language-features/unsized-locals.md index 6f7cf754ae083..1165ab93a1469 100644 --- a/src/doc/unstable-book/src/language-features/unsized-locals.md +++ b/src/doc/unstable-book/src/language-features/unsized-locals.md @@ -80,8 +80,6 @@ fn main() { } ``` -However, the current implementation allows `MyTupleStruct(..)` to be unsized. This will be fixed in the future. - ## By-value trait objects With this feature, you can have by-value `self` arguments without `Self: Sized` bounds. From fc284c1eee0180903fe11463dcee06e414202cf7 Mon Sep 17 00:00:00 2001 From: Dan Aloni Date: Mon, 19 Nov 2018 16:32:18 +0200 Subject: [PATCH 20/88] Stabilize macro_literal_matcher --- .../macro-literal-matcher.md | 17 ---------- src/libsyntax/ext/tt/macro_rules.rs | 31 ++++++++----------- src/libsyntax/feature_gate.rs | 8 ++--- src/test/run-pass/issues/issue-52169.rs | 2 +- src/test/run-pass/macros/macro-literal.rs | 2 +- .../feature-gate-macro-literal-matcher.rs | 19 ------------ .../feature-gate-macro-literal-matcher.stderr | 11 ------- 7 files changed, 17 insertions(+), 73 deletions(-) delete mode 100644 src/doc/unstable-book/src/language-features/macro-literal-matcher.md delete mode 100644 src/test/ui/feature-gates/feature-gate-macro-literal-matcher.rs delete mode 100644 src/test/ui/feature-gates/feature-gate-macro-literal-matcher.stderr diff --git a/src/doc/unstable-book/src/language-features/macro-literal-matcher.md b/src/doc/unstable-book/src/language-features/macro-literal-matcher.md deleted file mode 100644 index 870158200dee9..0000000000000 --- a/src/doc/unstable-book/src/language-features/macro-literal-matcher.md +++ /dev/null @@ -1,17 +0,0 @@ -# `macro_literal_matcher` - -The tracking issue for this feature is: [#35625] - -The RFC is: [rfc#1576]. - -With this feature gate enabled, the [list of designators] gains one more entry: - -* `literal`: a literal. Examples: 2, "string", 'c' - -A `literal` may be followed by anything, similarly to the `ident` specifier. - -[rfc#1576]: http://rust-lang.github.io/rfcs/1576-macros-literal-matcher.html -[#35625]: https://github.com/rust-lang/rust/issues/35625 -[list of designators]: ../reference/macros-by-example.html - ------------------------- diff --git a/src/libsyntax/ext/tt/macro_rules.rs b/src/libsyntax/ext/tt/macro_rules.rs index 6bba891278aca..55c1904bd274a 100644 --- a/src/libsyntax/ext/tt/macro_rules.rs +++ b/src/libsyntax/ext/tt/macro_rules.rs @@ -19,7 +19,7 @@ use ext::tt::macro_parser::{MatchedSeq, MatchedNonterminal}; use ext::tt::macro_parser::{parse, parse_failure_msg}; use ext::tt::quoted; use ext::tt::transcribe::transcribe; -use feature_gate::{self, emit_feature_err, Features, GateIssue}; +use feature_gate::Features; use parse::{Directory, ParseSess}; use parse::parser::Parser; use parse::token::{self, NtTT}; @@ -1027,26 +1027,21 @@ fn has_legal_fragment_specifier(sess: &ParseSess, Ok(()) } -fn is_legal_fragment_specifier(sess: &ParseSess, - features: &Features, - attrs: &[ast::Attribute], +fn is_legal_fragment_specifier(_sess: &ParseSess, + _features: &Features, + _attrs: &[ast::Attribute], frag_name: &str, - frag_span: Span) -> bool { + _frag_span: Span) -> bool { + /* + * If new fragmnet specifiers are invented in nightly, `_sess`, + * `_features`, `_attrs`, and `_frag_span` will be useful for + * here for checking against feature gates. See past versions of + * this function. + */ match frag_name { "item" | "block" | "stmt" | "expr" | "pat" | "lifetime" | - "path" | "ty" | "ident" | "meta" | "tt" | "vis" | "" => true, - "literal" => { - if !features.macro_literal_matcher && - !attr::contains_name(attrs, "allow_internal_unstable") { - let explain = feature_gate::EXPLAIN_LITERAL_MATCHER; - emit_feature_err(sess, - "macro_literal_matcher", - frag_span, - GateIssue::Language, - explain); - } - true - }, + "path" | "ty" | "ident" | "meta" | "tt" | "vis" | "literal" | + "" => true, _ => false, } } diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index 2f5db9bd08155..73567765a04c4 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -436,9 +436,6 @@ declare_features! ( // Allows irrefutable patterns in if-let and while-let statements (RFC 2086) (active, irrefutable_let_patterns, "1.27.0", Some(44495), None), - // Allows use of the :literal macro fragment specifier (RFC 1576) - (active, macro_literal_matcher, "1.27.0", Some(35625), None), - // inconsistent bounds in where clauses (active, trivial_bounds, "1.28.0", Some(48214), None), @@ -690,6 +687,8 @@ declare_features! ( (accepted, impl_header_lifetime_elision, "1.31.0", Some(15872), None), // `extern crate foo as bar;` puts `bar` into extern prelude. (accepted, extern_crate_item_prelude, "1.31.0", Some(55599), None), + // Allows use of the :literal macro fragment specifier (RFC 1576) + (accepted, macro_literal_matcher, "1.31.0", Some(35625), None), ); // If you change this, please modify src/doc/unstable-book as well. You must @@ -1425,9 +1424,6 @@ pub const EXPLAIN_DEPR_CUSTOM_DERIVE: &'static str = pub const EXPLAIN_DERIVE_UNDERSCORE: &'static str = "attributes of the form `#[derive_*]` are reserved for the compiler"; -pub const EXPLAIN_LITERAL_MATCHER: &'static str = - ":literal fragment specifier is experimental and subject to change"; - pub const EXPLAIN_UNSIZED_TUPLE_COERCION: &'static str = "unsized tuple coercion is not stable enough for use and is subject to change"; diff --git a/src/test/run-pass/issues/issue-52169.rs b/src/test/run-pass/issues/issue-52169.rs index 19c0f51d235bc..9421dfc7897d0 100644 --- a/src/test/run-pass/issues/issue-52169.rs +++ b/src/test/run-pass/issues/issue-52169.rs @@ -9,7 +9,7 @@ // except according to those terms. // run-pass -#![feature(macro_literal_matcher)] +#![cfg_attr(stage0, feature(macro_literal_matcher))] macro_rules! a { ($i:literal) => { "right" }; diff --git a/src/test/run-pass/macros/macro-literal.rs b/src/test/run-pass/macros/macro-literal.rs index ecbb47757d11e..07e27d9f516cb 100644 --- a/src/test/run-pass/macros/macro-literal.rs +++ b/src/test/run-pass/macros/macro-literal.rs @@ -9,7 +9,7 @@ // except according to those terms. // run-pass -#![feature(macro_literal_matcher)] +#![cfg_attr(stage0, feature(macro_literal_matcher))] macro_rules! mtester { ($l:literal) => { diff --git a/src/test/ui/feature-gates/feature-gate-macro-literal-matcher.rs b/src/test/ui/feature-gates/feature-gate-macro-literal-matcher.rs deleted file mode 100644 index db5cca193ab4e..0000000000000 --- a/src/test/ui/feature-gates/feature-gate-macro-literal-matcher.rs +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright 2018 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. - -// Test that the :lifetime macro fragment cannot be used when macro_lifetime_matcher -// feature gate is not used. - -macro_rules! m { ($lt:literal) => {} } -//~^ ERROR :literal fragment specifier is experimental and subject to change - -fn main() { - m!("some string literal"); -} diff --git a/src/test/ui/feature-gates/feature-gate-macro-literal-matcher.stderr b/src/test/ui/feature-gates/feature-gate-macro-literal-matcher.stderr deleted file mode 100644 index f714b916966a1..0000000000000 --- a/src/test/ui/feature-gates/feature-gate-macro-literal-matcher.stderr +++ /dev/null @@ -1,11 +0,0 @@ -error[E0658]: :literal fragment specifier is experimental and subject to change (see issue #35625) - --> $DIR/feature-gate-macro-literal-matcher.rs:14:19 - | -LL | macro_rules! m { ($lt:literal) => {} } - | ^^^^^^^^^^^ - | - = help: add #![feature(macro_literal_matcher)] to the crate attributes to enable - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0658`. From b8ae7b801bf1f3d298493b2e7e1328a1a7ecace7 Mon Sep 17 00:00:00 2001 From: Dan Aloni Date: Tue, 20 Nov 2018 09:11:12 +0200 Subject: [PATCH 21/88] macro_literal_matcher: fixes per petrochenkov's review --- src/libsyntax/ext/tt/macro_rules.rs | 6 +++--- src/test/run-pass/issues/issue-52169.rs | 1 - src/test/run-pass/macros/macro-literal.rs | 1 - 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/libsyntax/ext/tt/macro_rules.rs b/src/libsyntax/ext/tt/macro_rules.rs index 55c1904bd274a..d526e464ba4f5 100644 --- a/src/libsyntax/ext/tt/macro_rules.rs +++ b/src/libsyntax/ext/tt/macro_rules.rs @@ -1033,9 +1033,9 @@ fn is_legal_fragment_specifier(_sess: &ParseSess, frag_name: &str, _frag_span: Span) -> bool { /* - * If new fragmnet specifiers are invented in nightly, `_sess`, - * `_features`, `_attrs`, and `_frag_span` will be useful for - * here for checking against feature gates. See past versions of + * If new fragment specifiers are invented in nightly, `_sess`, + * `_features`, `_attrs`, and `_frag_span` will be useful here + * for checking against feature gates. See past versions of * this function. */ match frag_name { diff --git a/src/test/run-pass/issues/issue-52169.rs b/src/test/run-pass/issues/issue-52169.rs index 9421dfc7897d0..c4ed534cc20ec 100644 --- a/src/test/run-pass/issues/issue-52169.rs +++ b/src/test/run-pass/issues/issue-52169.rs @@ -9,7 +9,6 @@ // except according to those terms. // run-pass -#![cfg_attr(stage0, feature(macro_literal_matcher))] macro_rules! a { ($i:literal) => { "right" }; diff --git a/src/test/run-pass/macros/macro-literal.rs b/src/test/run-pass/macros/macro-literal.rs index 07e27d9f516cb..de268e3388a5e 100644 --- a/src/test/run-pass/macros/macro-literal.rs +++ b/src/test/run-pass/macros/macro-literal.rs @@ -9,7 +9,6 @@ // except according to those terms. // run-pass -#![cfg_attr(stage0, feature(macro_literal_matcher))] macro_rules! mtester { ($l:literal) => { From d4ee1c93ff25a7fa6f5e35dc774a648a7e6d578e Mon Sep 17 00:00:00 2001 From: Tom Tromey Date: Wed, 21 Nov 2018 11:35:49 -0700 Subject: [PATCH 22/88] Fix BTreeSet and BTreeMap gdb pretty-printers The BTreeSet and BTreeMap gdb pretty-printers did not take the node structure into account, and consequently only worked for shallow sets. This fixes the problem by iterating over child nodes when needed. This patch avoids the current approach of implementing some of the value manipulations in debugger-indepdendent code. This was done for convenience: a type lookup was needed for the first time, and there currently are no lldb formatters for these types. Closes #55771 --- src/etc/debugger_pretty_printers_common.py | 26 -------- src/etc/gdb_rust_pretty_printing.py | 70 ++++++++++++-------- src/test/debuginfo/pretty-std-collections.rs | 17 ++--- 3 files changed, 50 insertions(+), 63 deletions(-) diff --git a/src/etc/debugger_pretty_printers_common.py b/src/etc/debugger_pretty_printers_common.py index 1797f6708ac5b..b99e401929e62 100644 --- a/src/etc/debugger_pretty_printers_common.py +++ b/src/etc/debugger_pretty_printers_common.py @@ -375,32 +375,6 @@ def extract_tail_head_ptr_and_cap_from_std_vecdeque(vec_val): assert data_ptr.type.get_dwarf_type_kind() == DWARF_TYPE_CODE_PTR return (tail, head, data_ptr, capacity) - -def extract_length_and_ptr_from_std_btreeset(vec_val): - assert vec_val.type.get_type_kind() == TYPE_KIND_STD_BTREESET - map = vec_val.get_child_at_index(0) - root = map.get_child_at_index(0) - length = map.get_child_at_index(1).as_integer() - node = root.get_child_at_index(0) - ptr = node.get_child_at_index(0) - unique_ptr_val = ptr.get_child_at_index(0) - data_ptr = unique_ptr_val.get_child_at_index(0) - assert data_ptr.type.get_dwarf_type_kind() == DWARF_TYPE_CODE_PTR - return (length, data_ptr) - - -def extract_length_and_ptr_from_std_btreemap(vec_val): - assert vec_val.type.get_type_kind() == TYPE_KIND_STD_BTREEMAP - root = vec_val.get_child_at_index(0) - length = vec_val.get_child_at_index(1).as_integer() - node = root.get_child_at_index(0) - ptr = node.get_child_at_index(0) - unique_ptr_val = ptr.get_child_at_index(0) - data_ptr = unique_ptr_val.get_child_at_index(0) - assert data_ptr.type.get_dwarf_type_kind() == DWARF_TYPE_CODE_PTR - return (length, data_ptr) - - def extract_length_and_ptr_from_slice(slice_val): assert (slice_val.type.get_type_kind() == TYPE_KIND_SLICE or slice_val.type.get_type_kind() == TYPE_KIND_STR_SLICE) diff --git a/src/etc/gdb_rust_pretty_printing.py b/src/etc/gdb_rust_pretty_printing.py index e6d5ef1a23ff7..11e46e8c54cc9 100755 --- a/src/etc/gdb_rust_pretty_printing.py +++ b/src/etc/gdb_rust_pretty_printing.py @@ -304,6 +304,32 @@ def children(self): yield (str(index), (gdb_ptr + index).dereference()) +# Yield each key (and optionally value) from a BoxedNode. +def children_of_node(boxed_node, height, want_values): + ptr = boxed_node['ptr']['pointer'] + # This is written oddly because we don't want to rely on the field name being `__0`. + node_ptr = ptr[ptr.type.fields()[0]] + if height > 0: + type_name = str(node_ptr.type.target()).replace('LeafNode', 'InternalNode') + node_type = gdb.lookup_type(type_name) + node_ptr = node_ptr.cast(node_type.pointer()) + leaf = node_ptr['data'] + else: + leaf = node_ptr.dereference() + keys = leaf['keys']['value']['value'] + if want_values: + values = leaf['vals']['value']['value'] + length = int(leaf['len']) + for i in xrange(0, length + 1): + if height > 0: + for child in children_of_node(node_ptr['edges'][i], height - 1, want_values): + yield child + if i < length: + if want_values: + yield (keys[i], values[i]) + else: + yield keys[i] + class RustStdBTreeSetPrinter(object): def __init__(self, val): self.__val = val @@ -313,21 +339,16 @@ def display_hint(): return "array" def to_string(self): - (length, data_ptr) = \ - rustpp.extract_length_and_ptr_from_std_btreeset(self.__val) return (self.__val.type.get_unqualified_type_name() + - ("(len: %i)" % length)) + ("(len: %i)" % self.__val.get_wrapped_value()['map']['length'])) def children(self): - (length, data_ptr) = \ - rustpp.extract_length_and_ptr_from_std_btreeset(self.__val) - leaf_node = GdbValue(data_ptr.get_wrapped_value().dereference()) - maybe_uninit_keys = leaf_node.get_child_at_index(3) - manually_drop_keys = maybe_uninit_keys.get_child_at_index(1) - keys = manually_drop_keys.get_child_at_index(0) - gdb_ptr = keys.get_wrapped_value() - for index in xrange(length): - yield (str(index), gdb_ptr[index]) + root = self.__val.get_wrapped_value()['map']['root'] + node_ptr = root['node'] + i = 0 + for child in children_of_node(node_ptr, root['height'], False): + yield (str(i), child) + i = i + 1 class RustStdBTreeMapPrinter(object): @@ -339,26 +360,17 @@ def display_hint(): return "map" def to_string(self): - (length, data_ptr) = \ - rustpp.extract_length_and_ptr_from_std_btreemap(self.__val) return (self.__val.type.get_unqualified_type_name() + - ("(len: %i)" % length)) + ("(len: %i)" % self.__val.get_wrapped_value()['length'])) def children(self): - (length, data_ptr) = \ - rustpp.extract_length_and_ptr_from_std_btreemap(self.__val) - leaf_node = GdbValue(data_ptr.get_wrapped_value().dereference()) - maybe_uninit_keys = leaf_node.get_child_at_index(3) - manually_drop_keys = maybe_uninit_keys.get_child_at_index(1) - keys = manually_drop_keys.get_child_at_index(0) - keys_ptr = keys.get_wrapped_value() - maybe_uninit_vals = leaf_node.get_child_at_index(4) - manually_drop_vals = maybe_uninit_vals.get_child_at_index(1) - vals = manually_drop_vals.get_child_at_index(0) - vals_ptr = vals.get_wrapped_value() - for index in xrange(length): - yield (str(index), keys_ptr[index]) - yield (str(index), vals_ptr[index]) + root = self.__val.get_wrapped_value()['root'] + node_ptr = root['node'] + i = 0 + for child in children_of_node(node_ptr, root['height'], True): + yield (str(i), child[0]) + yield (str(i), child[1]) + i = i + 1 class RustStdStringPrinter(object): diff --git a/src/test/debuginfo/pretty-std-collections.rs b/src/test/debuginfo/pretty-std-collections.rs index 8e37a884b34bb..55e20d67276cd 100644 --- a/src/test/debuginfo/pretty-std-collections.rs +++ b/src/test/debuginfo/pretty-std-collections.rs @@ -8,6 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// ignore-tidy-linelength // ignore-windows failing on win32 bot // ignore-freebsd: gdb package too new // ignore-android: FIXME(#10381) @@ -20,10 +21,10 @@ // gdb-command: run // gdb-command: print btree_set -// gdb-check:$1 = BTreeSet(len: 3) = {3, 5, 7} +// gdb-check:$1 = BTreeSet(len: 15) = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14} // gdb-command: print btree_map -// gdb-check:$2 = BTreeMap(len: 3) = {[3] = 3, [5] = 7, [7] = 4} +// gdb-check:$2 = BTreeMap(len: 15) = {[0] = 0, [1] = 1, [2] = 2, [3] = 3, [4] = 4, [5] = 5, [6] = 6, [7] = 7, [8] = 8, [9] = 9, [10] = 10, [11] = 11, [12] = 12, [13] = 13, [14] = 14} // gdb-command: print vec_deque // gdb-check:$3 = VecDeque(len: 3, cap: 8) = {5, 3, 7} @@ -38,15 +39,15 @@ fn main() { // BTreeSet let mut btree_set = BTreeSet::new(); - btree_set.insert(5); - btree_set.insert(3); - btree_set.insert(7); + for i in 0..15 { + btree_set.insert(i); + } // BTreeMap let mut btree_map = BTreeMap::new(); - btree_map.insert(5, 7); - btree_map.insert(3, 3); - btree_map.insert(7, 4); + for i in 0..15 { + btree_map.insert(i, i); + } // VecDeque let mut vec_deque = VecDeque::new(); From d1cd4e8d0d383842acfe2d6ea75eed1e0c0909ac Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 21 Nov 2018 21:56:23 -0800 Subject: [PATCH 23/88] Move a flaky process test out of libstd This test ensures that everything in `env::vars()` is inherited but that's not actually true because other tests may add env vars after we spawn the process, causing the test to be flaky! This commit moves the test to a run-pass test where it can execute in isolation. Along the way this removes a lot of the platform specificity of the test, using iteslf to print the environment instead of a foreign process. --- src/libstd/process.rs | 36 -------------------------------- src/test/run-pass/inherit-env.rs | 25 ++++++++++++++++++++++ 2 files changed, 25 insertions(+), 36 deletions(-) create mode 100644 src/test/run-pass/inherit-env.rs diff --git a/src/libstd/process.rs b/src/libstd/process.rs index 51481e129df8d..2d0848252be6d 100644 --- a/src/libstd/process.rs +++ b/src/libstd/process.rs @@ -1889,42 +1889,6 @@ mod tests { cmd } - #[test] - fn test_inherit_env() { - use env; - - let result = env_cmd().output().unwrap(); - let output = String::from_utf8(result.stdout).unwrap(); - - for (ref k, ref v) in env::vars() { - // Don't check android RANDOM variable which seems to change - // whenever the shell runs, and our `env_cmd` is indeed running a - // shell which means it'll get a different RANDOM than we probably - // have. - // - // Also skip env vars with `-` in the name on android because, well, - // I'm not sure. It appears though that the `set` command above does - // not print env vars with `-` in the name, so we just skip them - // here as we won't find them in the output. Note that most env vars - // use `_` instead of `-`, but our build system sets a few env vars - // with `-` in the name. - if cfg!(target_os = "android") && - (*k == "RANDOM" || k.contains("-")) { - continue - } - - // Windows has hidden environment variables whose names start with - // equals signs (`=`). Those do not show up in the output of the - // `set` command. - assert!((cfg!(windows) && k.starts_with("=")) || - k.starts_with("DYLD") || - output.contains(&format!("{}={}", *k, *v)) || - output.contains(&format!("{}='{}'", *k, *v)), - "output doesn't contain `{}={}`\n{}", - k, v, output); - } - } - #[test] fn test_override_env() { use env; diff --git a/src/test/run-pass/inherit-env.rs b/src/test/run-pass/inherit-env.rs new file mode 100644 index 0000000000000..856d3a5f72d34 --- /dev/null +++ b/src/test/run-pass/inherit-env.rs @@ -0,0 +1,25 @@ +// ignore-emscripten +// ignore-wasm32 + +use std::env; +use std::process::Command; + +fn main() { + if env::args().nth(1).map(|s| s == "print").unwrap_or(false) { + for (k, v) in env::vars() { + println!("{}={}", k, v); + } + return + } + + let me = env::current_exe().unwrap(); + let result = Command::new(me).arg("print").output().unwrap(); + let output = String::from_utf8(result.stdout).unwrap(); + + for (k, v) in env::vars() { + assert!(output.contains(&format!("{}={}", k, v)), + "output doesn't contain `{}={}`\n{}", + k, v, output); + } +} + From b319715456c08097f2cd936da309ed6e8aec2843 Mon Sep 17 00:00:00 2001 From: Wesley Wiser Date: Thu, 22 Nov 2018 12:56:15 -0500 Subject: [PATCH 24/88] Disable the self-profiler unless the `-Z self-profile` flag is set Related to #51648 --- src/librustc/session/mod.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/librustc/session/mod.rs b/src/librustc/session/mod.rs index d688d93b80842..1187c53305d1c 100644 --- a/src/librustc/session/mod.rs +++ b/src/librustc/session/mod.rs @@ -826,8 +826,10 @@ impl Session { } pub fn profiler ()>(&self, f: F) { - let mut profiler = self.self_profiling.borrow_mut(); - f(&mut profiler); + if self.opts.debugging_opts.self_profile { + let mut profiler = self.self_profiling.borrow_mut(); + f(&mut profiler); + } } pub fn print_profiler_results(&self) { From 4381772695dfb8ced845fd47e7eada74f9f6bd21 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Thu, 22 Nov 2018 22:17:23 +0100 Subject: [PATCH 25/88] Fix invalid panic setup message --- src/librustc/session/config.rs | 2 +- src/test/ui/panic-runtime/bad-panic-flag1.rs | 2 +- src/test/ui/panic-runtime/bad-panic-flag1.stderr | 2 +- src/test/ui/panic-runtime/bad-panic-flag2.rs | 2 +- src/test/ui/panic-runtime/bad-panic-flag2.stderr | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs index 78aabf86e300d..07f095fc6139c 100644 --- a/src/librustc/session/config.rs +++ b/src/librustc/session/config.rs @@ -802,7 +802,7 @@ macro_rules! options { pub const parse_opt_uint: Option<&'static str> = Some("a number"); pub const parse_panic_strategy: Option<&'static str> = - Some("either `panic` or `abort`"); + Some("either `unwind` or `abort`"); pub const parse_relro_level: Option<&'static str> = Some("one of: `full`, `partial`, or `off`"); pub const parse_sanitizer: Option<&'static str> = diff --git a/src/test/ui/panic-runtime/bad-panic-flag1.rs b/src/test/ui/panic-runtime/bad-panic-flag1.rs index f067b6b8349b6..4e553c4df2fd4 100644 --- a/src/test/ui/panic-runtime/bad-panic-flag1.rs +++ b/src/test/ui/panic-runtime/bad-panic-flag1.rs @@ -9,6 +9,6 @@ // except according to those terms. // compile-flags:-C panic=foo -// error-pattern:either `panic` or `abort` was expected +// error-pattern:either `unwind` or `abort` was expected fn main() {} diff --git a/src/test/ui/panic-runtime/bad-panic-flag1.stderr b/src/test/ui/panic-runtime/bad-panic-flag1.stderr index 3a65419c98fb7..013373c6f9313 100644 --- a/src/test/ui/panic-runtime/bad-panic-flag1.stderr +++ b/src/test/ui/panic-runtime/bad-panic-flag1.stderr @@ -1,2 +1,2 @@ -error: incorrect value `foo` for codegen option `panic` - either `panic` or `abort` was expected +error: incorrect value `foo` for codegen option `panic` - either `unwind` or `abort` was expected diff --git a/src/test/ui/panic-runtime/bad-panic-flag2.rs b/src/test/ui/panic-runtime/bad-panic-flag2.rs index 0ecf65f080fa9..f560e7f4eb2de 100644 --- a/src/test/ui/panic-runtime/bad-panic-flag2.rs +++ b/src/test/ui/panic-runtime/bad-panic-flag2.rs @@ -9,6 +9,6 @@ // except according to those terms. // compile-flags:-C panic -// error-pattern:requires either `panic` or `abort` +// error-pattern:requires either `unwind` or `abort` fn main() {} diff --git a/src/test/ui/panic-runtime/bad-panic-flag2.stderr b/src/test/ui/panic-runtime/bad-panic-flag2.stderr index 8d919e55c9068..6ab94ea704d30 100644 --- a/src/test/ui/panic-runtime/bad-panic-flag2.stderr +++ b/src/test/ui/panic-runtime/bad-panic-flag2.stderr @@ -1,2 +1,2 @@ -error: codegen option `panic` requires either `panic` or `abort` (C panic=) +error: codegen option `panic` requires either `unwind` or `abort` (C panic=) From 7401e3def59452e795468d4d5e4f30c7ef100fec Mon Sep 17 00:00:00 2001 From: scalexm Date: Fri, 2 Nov 2018 15:08:51 +0100 Subject: [PATCH 26/88] Distinguish between placeholder kinds --- src/librustc/infer/canonical/mod.rs | 6 +++--- src/librustc/infer/higher_ranked/mod.rs | 2 +- src/librustc/infer/mod.rs | 2 +- src/librustc/infer/nll_relate/mod.rs | 4 ++-- src/librustc/ty/mod.rs | 21 ++++++++++++++++--- src/librustc/ty/sty.rs | 2 +- src/librustc/util/ppaux.rs | 2 +- .../borrow_check/error_reporting.rs | 4 ++-- .../borrow_check/nll/region_infer/mod.rs | 2 +- .../borrow_check/nll/region_infer/values.rs | 18 ++++++++-------- .../borrow_check/nll/type_check/mod.rs | 2 +- .../borrow_check/nll/type_check/relate_tys.rs | 5 ++++- 12 files changed, 44 insertions(+), 26 deletions(-) diff --git a/src/librustc/infer/canonical/mod.rs b/src/librustc/infer/canonical/mod.rs index f7eb7118f412f..41839d61d3260 100644 --- a/src/librustc/infer/canonical/mod.rs +++ b/src/librustc/infer/canonical/mod.rs @@ -142,7 +142,7 @@ pub enum CanonicalVarKind { /// A "placeholder" that represents "any region". Created when you /// are solving a goal like `for<'a> T: Foo<'a>` to represent the /// bound region `'a`. - PlaceholderRegion(ty::Placeholder), + PlaceholderRegion(ty::PlaceholderRegion), } impl CanonicalVarKind { @@ -374,9 +374,9 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { universe_map(ui), ).into(), - CanonicalVarKind::PlaceholderRegion(ty::Placeholder { universe, name }) => { + CanonicalVarKind::PlaceholderRegion(ty::PlaceholderRegion { universe, name }) => { let universe_mapped = universe_map(universe); - let placeholder_mapped = ty::Placeholder { + let placeholder_mapped = ty::PlaceholderRegion { universe: universe_mapped, name, }; diff --git a/src/librustc/infer/higher_ranked/mod.rs b/src/librustc/infer/higher_ranked/mod.rs index 8172f620c3646..ddf46b18ef730 100644 --- a/src/librustc/infer/higher_ranked/mod.rs +++ b/src/librustc/infer/higher_ranked/mod.rs @@ -340,7 +340,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { let next_universe = self.create_next_universe(); let (result, map) = self.tcx.replace_late_bound_regions(binder, |br| { - self.tcx.mk_region(ty::RePlaceholder(ty::Placeholder { + self.tcx.mk_region(ty::RePlaceholder(ty::PlaceholderRegion { universe: next_universe, name: br, })) diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs index 87e32be1a1759..dfe6aa160b3ad 100644 --- a/src/librustc/infer/mod.rs +++ b/src/librustc/infer/mod.rs @@ -411,7 +411,7 @@ pub enum NLLRegionVariableOrigin { /// "Universal" instantiation of a higher-ranked region (e.g., /// from a `for<'a> T` binder). Meant to represent "any region". - Placeholder(ty::Placeholder), + Placeholder(ty::PlaceholderRegion), Existential, } diff --git a/src/librustc/infer/nll_relate/mod.rs b/src/librustc/infer/nll_relate/mod.rs index e003c1989e096..9bdbf77fee0a9 100644 --- a/src/librustc/infer/nll_relate/mod.rs +++ b/src/librustc/infer/nll_relate/mod.rs @@ -95,7 +95,7 @@ pub trait TypeRelatingDelegate<'tcx> { /// So e.g. if you have `for<'a> fn(..) <: for<'b> fn(..)`, then /// we will invoke this method to instantiate `'b` with a /// placeholder region. - fn next_placeholder_region(&mut self, placeholder: ty::Placeholder) -> ty::Region<'tcx>; + fn next_placeholder_region(&mut self, placeholder: ty::PlaceholderRegion) -> ty::Region<'tcx>; /// Creates a new existential region in the given universe. This /// is used when handling subtyping and type variables -- if we @@ -176,7 +176,7 @@ where universe }); - let placeholder = ty::Placeholder { universe, name: br }; + let placeholder = ty::PlaceholderRegion { universe, name: br }; delegate.next_placeholder_region(placeholder) } else { delegate.next_existential_region_var() diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index ad200449f8907..4372beafa6488 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -1587,12 +1587,27 @@ impl UniverseIndex { /// universe are just two regions with an unknown relationship to one /// another. #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable, PartialOrd, Ord)] -pub struct Placeholder { +pub struct Placeholder { pub universe: UniverseIndex, - pub name: BoundRegion, + pub name: T, } -impl_stable_hash_for!(struct Placeholder { universe, name }); +impl<'a, 'gcx, T> HashStable> for Placeholder + where T: HashStable> +{ + fn hash_stable( + &self, + hcx: &mut StableHashingContext<'a>, + hasher: &mut StableHasher + ) { + self.universe.hash_stable(hcx, hasher); + self.name.hash_stable(hcx, hasher); + } +} + +pub type PlaceholderRegion = Placeholder; + +pub type PlaceholderType = Placeholder; /// When type checking, we use the `ParamEnv` to track /// details about the set of where-clauses that are in scope at this diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs index bd3a34cae90f4..5c01f1cc3e4d7 100644 --- a/src/librustc/ty/sty.rs +++ b/src/librustc/ty/sty.rs @@ -1165,7 +1165,7 @@ pub enum RegionKind { /// A placeholder region - basically the higher-ranked version of ReFree. /// Should not exist after typeck. - RePlaceholder(ty::Placeholder), + RePlaceholder(ty::PlaceholderRegion), /// Empty lifetime is for data that is never accessed. /// Bottom in the region lattice. We treat ReEmpty somewhat diff --git a/src/librustc/util/ppaux.rs b/src/librustc/util/ppaux.rs index d53370d242bd9..613a0dbc61f42 100644 --- a/src/librustc/util/ppaux.rs +++ b/src/librustc/util/ppaux.rs @@ -792,7 +792,7 @@ define_print! { } ty::ReLateBound(_, br) | ty::ReFree(ty::FreeRegion { bound_region: br, .. }) | - ty::RePlaceholder(ty::Placeholder { name: br, .. }) => { + ty::RePlaceholder(ty::PlaceholderRegion { name: br, .. }) => { write!(f, "{}", br) } ty::ReScope(scope) if cx.identify_regions => { diff --git a/src/librustc_mir/borrow_check/error_reporting.rs b/src/librustc_mir/borrow_check/error_reporting.rs index 3c4d8e09fc166..4ccd26bee8b88 100644 --- a/src/librustc_mir/borrow_check/error_reporting.rs +++ b/src/librustc_mir/borrow_check/error_reporting.rs @@ -2193,7 +2193,7 @@ impl<'tcx> AnnotatedBorrowFnSignature<'tcx> { match ty.sty { ty::TyKind::Ref(ty::RegionKind::ReLateBound(_, br), _, _) | ty::TyKind::Ref( - ty::RegionKind::RePlaceholder(ty::Placeholder { name: br, .. }), + ty::RegionKind::RePlaceholder(ty::PlaceholderRegion { name: br, .. }), _, _, ) => with_highlight_region_for_bound_region(*br, counter, || ty.to_string()), @@ -2207,7 +2207,7 @@ impl<'tcx> AnnotatedBorrowFnSignature<'tcx> { match ty.sty { ty::TyKind::Ref(region, _, _) => match region { ty::RegionKind::ReLateBound(_, br) - | ty::RegionKind::RePlaceholder(ty::Placeholder { name: br, .. }) => { + | ty::RegionKind::RePlaceholder(ty::PlaceholderRegion { name: br, .. }) => { with_highlight_region_for_bound_region(*br, counter, || region.to_string()) } _ => region.to_string(), diff --git a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs index 376f445924270..6a1dc50c67a09 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs @@ -1230,7 +1230,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { mir: &Mir<'tcx>, _mir_def_id: DefId, longer_fr: RegionVid, - placeholder: ty::Placeholder, + placeholder: ty::PlaceholderRegion, ) { debug!( "check_bound_universal_region(fr={:?}, placeholder={:?})", diff --git a/src/librustc_mir/borrow_check/nll/region_infer/values.rs b/src/librustc_mir/borrow_check/nll/region_infer/values.rs index 2b7ef38d3edf9..69e2c896d33e5 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/values.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/values.rs @@ -150,7 +150,7 @@ crate enum RegionElement { /// A placeholder (e.g., instantiated from a `for<'a> fn(&'a u32)` /// type). - PlaceholderRegion(ty::Placeholder), + PlaceholderRegion(ty::PlaceholderRegion), } /// When we initially compute liveness, we use a bit matrix storing @@ -219,17 +219,17 @@ impl LivenessValues { } } -/// Maps from `ty::Placeholder` values that are used in the rest of +/// Maps from `ty::PlaceholderRegion` values that are used in the rest of /// rustc to the internal `PlaceholderIndex` values that are used in /// NLL. #[derive(Default)] crate struct PlaceholderIndices { - to_index: FxHashMap, - from_index: IndexVec, + to_index: FxHashMap, + from_index: IndexVec, } impl PlaceholderIndices { - crate fn insert(&mut self, placeholder: ty::Placeholder) -> PlaceholderIndex { + crate fn insert(&mut self, placeholder: ty::PlaceholderRegion) -> PlaceholderIndex { let PlaceholderIndices { to_index, from_index, @@ -239,11 +239,11 @@ impl PlaceholderIndices { .or_insert_with(|| from_index.push(placeholder)) } - crate fn lookup_index(&self, placeholder: ty::Placeholder) -> PlaceholderIndex { + crate fn lookup_index(&self, placeholder: ty::PlaceholderRegion) -> PlaceholderIndex { self.to_index[&placeholder] } - crate fn lookup_placeholder(&self, placeholder: PlaceholderIndex) -> ty::Placeholder { + crate fn lookup_placeholder(&self, placeholder: PlaceholderIndex) -> ty::PlaceholderRegion { self.from_index[placeholder] } @@ -375,7 +375,7 @@ impl RegionValues { crate fn placeholders_contained_in<'a>( &'a self, r: N, - ) -> impl Iterator + 'a { + ) -> impl Iterator + 'a { self.placeholders .row(r) .into_iter() @@ -432,7 +432,7 @@ impl ToElementIndex for RegionVid { } } -impl ToElementIndex for ty::Placeholder { +impl ToElementIndex for ty::PlaceholderRegion { fn add_to_row(self, values: &mut RegionValues, row: N) -> bool { let index = values.placeholder_indices.lookup_index(self); values.placeholders.insert(row, index) diff --git a/src/librustc_mir/borrow_check/nll/type_check/mod.rs b/src/librustc_mir/borrow_check/nll/type_check/mod.rs index 06dfd4bc2cc1f..6f4d8c2bef934 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs @@ -777,7 +777,7 @@ impl MirTypeckRegionConstraints<'tcx> { fn placeholder_region( &mut self, infcx: &InferCtxt<'_, '_, 'tcx>, - placeholder: ty::Placeholder, + placeholder: ty::PlaceholderRegion, ) -> ty::Region<'tcx> { let placeholder_index = self.placeholder_indices.insert(placeholder); match self.placeholder_index_to_region.get(placeholder_index) { diff --git a/src/librustc_mir/borrow_check/nll/type_check/relate_tys.rs b/src/librustc_mir/borrow_check/nll/type_check/relate_tys.rs index b82efb29f6e56..cf4f913080783 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/relate_tys.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/relate_tys.rs @@ -84,7 +84,10 @@ impl TypeRelatingDelegate<'tcx> for NllTypeRelatingDelegate<'_, '_, '_, 'tcx> { } } - fn next_placeholder_region(&mut self, placeholder: ty::Placeholder) -> ty::Region<'tcx> { + fn next_placeholder_region( + &mut self, + placeholder: ty::PlaceholderRegion + ) -> ty::Region<'tcx> { if let Some(borrowck_context) = &mut self.borrowck_context { borrowck_context.constraints.placeholder_region(self.infcx, placeholder) } else { From 05995a85221cde573b81ab918b0f3686452dca3b Mon Sep 17 00:00:00 2001 From: scalexm Date: Fri, 2 Nov 2018 18:48:24 +0100 Subject: [PATCH 27/88] Introduce `TyKind::Placeholder` variant --- src/librustc/ich/impls_ty.rs | 3 +++ src/librustc/infer/canonical/canonicalizer.rs | 1 + src/librustc/infer/freshen.rs | 6 +++--- src/librustc/traits/coherence.rs | 2 +- src/librustc/traits/error_reporting.rs | 2 +- src/librustc/traits/query/dropck_outlives.rs | 1 + src/librustc/traits/select.rs | 3 +++ src/librustc/ty/context.rs | 2 +- src/librustc/ty/error.rs | 1 + src/librustc/ty/fast_reject.rs | 2 +- src/librustc/ty/flags.rs | 1 + src/librustc/ty/item_path.rs | 1 + src/librustc/ty/layout.rs | 4 +++- src/librustc/ty/mod.rs | 1 + src/librustc/ty/outlives.rs | 1 + src/librustc/ty/structural_impls.rs | 2 ++ src/librustc/ty/sty.rs | 5 +++++ src/librustc/ty/util.rs | 2 +- src/librustc/ty/walk.rs | 2 +- src/librustc/ty/wf.rs | 1 + src/librustc/util/ppaux.rs | 5 ++++- src/librustc_codegen_llvm/debuginfo/type_names.rs | 1 + src/librustc_lint/types.rs | 1 + src/librustc_mir/monomorphize/item.rs | 1 + src/librustc_traits/chalk_context/program_clauses.rs | 1 + src/librustc_traits/dropck_outlives.rs | 2 +- src/librustc_traits/lowering/environment.rs | 1 + src/librustc_typeck/check/cast.rs | 2 +- src/librustc_typeck/variance/constraints.rs | 1 + src/librustdoc/clean/mod.rs | 1 + 30 files changed, 46 insertions(+), 13 deletions(-) diff --git a/src/librustc/ich/impls_ty.rs b/src/librustc/ich/impls_ty.rs index 679107160a6fc..4b465c7ad54a9 100644 --- a/src/librustc/ich/impls_ty.rs +++ b/src/librustc/ich/impls_ty.rs @@ -687,6 +687,9 @@ for ty::TyKind<'gcx> Bound(bound_ty) => { bound_ty.hash_stable(hcx, hasher); } + ty::Placeholder(placeholder_ty) => { + placeholder_ty.hash_stable(hcx, hasher); + } Foreign(def_id) => { def_id.hash_stable(hcx, hasher); } diff --git a/src/librustc/infer/canonical/canonicalizer.rs b/src/librustc/infer/canonical/canonicalizer.rs index a787eeae663fd..a3cbae2ff71c1 100644 --- a/src/librustc/infer/canonical/canonicalizer.rs +++ b/src/librustc/infer/canonical/canonicalizer.rs @@ -380,6 +380,7 @@ impl<'cx, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for Canonicalizer<'cx, 'gcx, 'tcx> | ty::Never | ty::Tuple(..) | ty::Projection(..) + | ty::Placeholder(..) | ty::UnnormalizedProjection(..) | ty::Foreign(..) | ty::Param(..) diff --git a/src/librustc/infer/freshen.rs b/src/librustc/infer/freshen.rs index b53444992fa21..d17cf0c7b47a4 100644 --- a/src/librustc/infer/freshen.rs +++ b/src/librustc/infer/freshen.rs @@ -170,9 +170,6 @@ impl<'a, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for TypeFreshener<'a, 'gcx, 'tcx> { t } - ty::Bound(..) => - bug!("encountered bound ty during freshening"), - ty::Generator(..) | ty::Bool | ty::Char | @@ -200,6 +197,9 @@ impl<'a, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for TypeFreshener<'a, 'gcx, 'tcx> { ty::Opaque(..) => { t.super_fold_with(self) } + + ty::Placeholder(..) | + ty::Bound(..) => bug!("unexpected type {:?}", t), } } } diff --git a/src/librustc/traits/coherence.rs b/src/librustc/traits/coherence.rs index 71b77909b82a8..b7a84c99308f8 100644 --- a/src/librustc/traits/coherence.rs +++ b/src/librustc/traits/coherence.rs @@ -455,7 +455,7 @@ fn ty_is_local_constructor(ty: Ty<'_>, in_crate: InCrate) -> bool { false } - ty::Bound(..) | ty::Infer(..) => match in_crate { + ty::Placeholder(..) | ty::Bound(..) | ty::Infer(..) => match in_crate { InCrate::Local => false, // The inference variable might be unified with a local // type in that remote crate. diff --git a/src/librustc/traits/error_reporting.rs b/src/librustc/traits/error_reporting.rs index 48b2b25d6adf9..7e97dc3c84a79 100644 --- a/src/librustc/traits/error_reporting.rs +++ b/src/librustc/traits/error_reporting.rs @@ -281,7 +281,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { ty::Generator(..) => Some(18), ty::Foreign(..) => Some(19), ty::GeneratorWitness(..) => Some(20), - ty::Bound(..) | ty::Infer(..) | ty::Error => None, + ty::Placeholder(..) | ty::Bound(..) | ty::Infer(..) | ty::Error => None, ty::UnnormalizedProjection(..) => bug!("only used with chalk-engine"), } } diff --git a/src/librustc/traits/query/dropck_outlives.rs b/src/librustc/traits/query/dropck_outlives.rs index 99dc099d57738..b8bf0fcc15307 100644 --- a/src/librustc/traits/query/dropck_outlives.rs +++ b/src/librustc/traits/query/dropck_outlives.rs @@ -251,6 +251,7 @@ pub fn trivial_dropck_outlives<'tcx>(tcx: TyCtxt<'_, '_, 'tcx>, ty: Ty<'tcx>) -> | ty::Projection(..) | ty::Param(_) | ty::Opaque(..) + | ty::Placeholder(..) | ty::Infer(_) | ty::Bound(..) | ty::Generator(..) => false, diff --git a/src/librustc/traits/select.rs b/src/librustc/traits/select.rs index 550c27ca0ab8b..6a91ad59d98e4 100644 --- a/src/librustc/traits/select.rs +++ b/src/librustc/traits/select.rs @@ -2470,6 +2470,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> { ty::Infer(ty::TyVar(_)) => Ambiguous, ty::UnnormalizedProjection(..) + | ty::Placeholder(..) | ty::Bound(_) | ty::Infer(ty::FreshTy(_)) | ty::Infer(ty::FreshIntTy(_)) @@ -2555,6 +2556,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> { } ty::UnnormalizedProjection(..) + | ty::Placeholder(..) | ty::Bound(_) | ty::Infer(ty::FreshTy(_)) | ty::Infer(ty::FreshIntTy(_)) @@ -2594,6 +2596,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> { | ty::Char => Vec::new(), ty::UnnormalizedProjection(..) + | ty::Placeholder(..) | ty::Dynamic(..) | ty::Param(..) | ty::Foreign(..) diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs index a8ce52a8e156c..d9d1b81fd8fe0 100644 --- a/src/librustc/ty/context.rs +++ b/src/librustc/ty/context.rs @@ -2250,7 +2250,7 @@ impl<'a, 'tcx> TyCtxt<'a, 'tcx, 'tcx> { pub fn print_debug_stats(self) { sty_debug_print!( self, - Adt, Array, Slice, RawPtr, Ref, FnDef, FnPtr, + Adt, Array, Slice, RawPtr, Ref, FnDef, FnPtr, Placeholder, Generator, GeneratorWitness, Dynamic, Closure, Tuple, Bound, Param, Infer, UnnormalizedProjection, Projection, Opaque, Foreign); diff --git a/src/librustc/ty/error.rs b/src/librustc/ty/error.rs index 4737c72b1ef0f..e78759e3e7961 100644 --- a/src/librustc/ty/error.rs +++ b/src/librustc/ty/error.rs @@ -212,6 +212,7 @@ impl<'a, 'gcx, 'lcx, 'tcx> ty::TyS<'tcx> { ty::Infer(ty::TyVar(_)) => "inferred type".into(), ty::Infer(ty::IntVar(_)) => "integral variable".into(), ty::Infer(ty::FloatVar(_)) => "floating-point variable".into(), + ty::Placeholder(..) => "placeholder type".into(), ty::Bound(_) | ty::Infer(ty::FreshTy(_)) => "fresh type".into(), ty::Infer(ty::FreshIntTy(_)) => "fresh integral type".into(), diff --git a/src/librustc/ty/fast_reject.rs b/src/librustc/ty/fast_reject.rs index 380f95993f8fb..8304e36381572 100644 --- a/src/librustc/ty/fast_reject.rs +++ b/src/librustc/ty/fast_reject.rs @@ -122,7 +122,7 @@ pub fn simplify_type<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>, ty::Foreign(def_id) => { Some(ForeignSimplifiedType(def_id)) } - ty::Bound(..) | ty::Infer(_) | ty::Error => None, + ty::Placeholder(..) | ty::Bound(..) | ty::Infer(_) | ty::Error => None, } } diff --git a/src/librustc/ty/flags.rs b/src/librustc/ty/flags.rs index 0764f363250dd..9cdeb300393b9 100644 --- a/src/librustc/ty/flags.rs +++ b/src/librustc/ty/flags.rs @@ -74,6 +74,7 @@ impl FlagComputation { &ty::Uint(_) | &ty::Never | &ty::Str | + &ty::Placeholder(..) | &ty::Foreign(..) => { } diff --git a/src/librustc/ty/item_path.rs b/src/librustc/ty/item_path.rs index d44ba03084159..f6c90ab0a1ad2 100644 --- a/src/librustc/ty/item_path.rs +++ b/src/librustc/ty/item_path.rs @@ -515,6 +515,7 @@ pub fn characteristic_def_id_of_type(ty: Ty<'_>) -> Option { ty::Str | ty::FnPtr(_) | ty::Projection(_) | + ty::Placeholder(..) | ty::UnnormalizedProjection(..) | ty::Param(_) | ty::Opaque(..) | diff --git a/src/librustc/ty/layout.rs b/src/librustc/ty/layout.rs index da0a9acede20e..5406495226d79 100644 --- a/src/librustc/ty/layout.rs +++ b/src/librustc/ty/layout.rs @@ -1159,6 +1159,7 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> { } ty::Bound(..) | + ty::Placeholder(..) | ty::UnnormalizedProjection(..) | ty::GeneratorWitness(..) | ty::Infer(_) => { @@ -1743,7 +1744,8 @@ impl<'a, 'tcx, C> TyLayoutMethods<'tcx, C> for Ty<'tcx> } ty::Projection(_) | ty::UnnormalizedProjection(..) | ty::Bound(..) | - ty::Opaque(..) | ty::Param(_) | ty::Infer(_) | ty::Error => { + ty::Placeholder(..) | ty::Opaque(..) | ty::Param(_) | ty::Infer(_) | + ty::Error => { bug!("TyLayout::field_type: unexpected type `{}`", this.ty) } }) diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index 4372beafa6488..baedbc8214976 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -2445,6 +2445,7 @@ impl<'a, 'gcx, 'tcx> AdtDef { } } + Placeholder(..) | Bound(..) | Infer(..) => { bug!("unexpected type `{:?}` in sized_constraint_for_ty", diff --git a/src/librustc/ty/outlives.rs b/src/librustc/ty/outlives.rs index 7fac88a3d78f1..0e3fc62e4ca9a 100644 --- a/src/librustc/ty/outlives.rs +++ b/src/librustc/ty/outlives.rs @@ -155,6 +155,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { ty::FnDef(..) | // OutlivesFunction (*) ty::FnPtr(_) | // OutlivesFunction (*) ty::Dynamic(..) | // OutlivesObject, OutlivesFragment (*) + ty::Placeholder(..) | ty::Bound(..) | ty::Error => { // (*) Bare functions and traits are both binders. In the diff --git a/src/librustc/ty/structural_impls.rs b/src/librustc/ty/structural_impls.rs index e92f92dce63d0..d6aeb288b5cdc 100644 --- a/src/librustc/ty/structural_impls.rs +++ b/src/librustc/ty/structural_impls.rs @@ -746,6 +746,7 @@ impl<'tcx> TypeFoldable<'tcx> for Ty<'tcx> { ty::Infer(_) | ty::Param(..) | ty::Bound(..) | + ty::Placeholder(..) | ty::Never | ty::Foreign(..) => return self }; @@ -792,6 +793,7 @@ impl<'tcx> TypeFoldable<'tcx> for Ty<'tcx> { ty::Error | ty::Infer(_) | ty::Bound(..) | + ty::Placeholder(..) | ty::Param(..) | ty::Never | ty::Foreign(..) => false, diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs index 5c01f1cc3e4d7..7e6673a3dcdc1 100644 --- a/src/librustc/ty/sty.rs +++ b/src/librustc/ty/sty.rs @@ -203,6 +203,9 @@ pub enum TyKind<'tcx> { /// Bound type variable, used only when preparing a trait query. Bound(BoundTy), + /// A placeholder type - universally quantified higher-ranked type. + Placeholder(ty::PlaceholderType), + /// A type variable used during type checking. Infer(InferTy), @@ -1890,6 +1893,7 @@ impl<'a, 'gcx, 'tcx> TyS<'tcx> { Foreign(..) | Param(_) | Bound(..) | + Placeholder(..) | Infer(_) | Error => {} } @@ -1954,6 +1958,7 @@ impl<'a, 'gcx, 'tcx> TyS<'tcx> { ty::Infer(ty::TyVar(_)) => false, ty::Bound(_) | + ty::Placeholder(..) | ty::Infer(ty::FreshTy(_)) | ty::Infer(ty::FreshIntTy(_)) | ty::Infer(ty::FreshFloatTy(_)) => diff --git a/src/librustc/ty/util.rs b/src/librustc/ty/util.rs index 3d0c54d6b0a5b..f0885f960516d 100644 --- a/src/librustc/ty/util.rs +++ b/src/librustc/ty/util.rs @@ -952,7 +952,7 @@ fn needs_drop_raw<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, // Can refer to a type which may drop. // FIXME(eddyb) check this against a ParamEnv. ty::Dynamic(..) | ty::Projection(..) | ty::Param(_) | ty::Bound(..) | - ty::Opaque(..) | ty::Infer(_) | ty::Error => true, + ty::Placeholder(..) | ty::Opaque(..) | ty::Infer(_) | ty::Error => true, ty::UnnormalizedProjection(..) => bug!("only used with chalk-engine"), diff --git a/src/librustc/ty/walk.rs b/src/librustc/ty/walk.rs index 284c595ee2d96..82b95b9df6031 100644 --- a/src/librustc/ty/walk.rs +++ b/src/librustc/ty/walk.rs @@ -82,7 +82,7 @@ fn push_subtypes<'tcx>(stack: &mut TypeWalkerStack<'tcx>, parent_ty: Ty<'tcx>) { match parent_ty.sty { ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Str | ty::Infer(_) | ty::Param(_) | ty::Never | ty::Error | - ty::Bound(..) | ty::Foreign(..) => { + ty::Placeholder(..) | ty::Bound(..) | ty::Foreign(..) => { } ty::Array(ty, len) => { push_const(stack, len); diff --git a/src/librustc/ty/wf.rs b/src/librustc/ty/wf.rs index 1336eac63f880..6ae0793d92471 100644 --- a/src/librustc/ty/wf.rs +++ b/src/librustc/ty/wf.rs @@ -259,6 +259,7 @@ impl<'a, 'gcx, 'tcx> WfPredicates<'a, 'gcx, 'tcx> { ty::Never | ty::Param(_) | ty::Bound(..) | + ty::Placeholder(..) | ty::Foreign(..) => { // WfScalar, WfParameter, etc } diff --git a/src/librustc/util/ppaux.rs b/src/librustc/util/ppaux.rs index 613a0dbc61f42..0c424b344159d 100644 --- a/src/librustc/util/ppaux.rs +++ b/src/librustc/util/ppaux.rs @@ -18,7 +18,7 @@ use ty::{Bool, Char, Adt}; use ty::{Error, Str, Array, Slice, Float, FnDef, FnPtr}; use ty::{Param, Bound, RawPtr, Ref, Never, Tuple}; use ty::{Closure, Generator, GeneratorWitness, Foreign, Projection, Opaque}; -use ty::{UnnormalizedProjection, Dynamic, Int, Uint, Infer}; +use ty::{Placeholder, UnnormalizedProjection, Dynamic, Int, Uint, Infer}; use ty::{self, RegionVid, Ty, TyCtxt, TypeFoldable, GenericParamCount, GenericParamDefKind}; use util::nodemap::FxHashSet; @@ -1144,6 +1144,9 @@ define_print! { data.print(f, cx)?; write!(f, ")") } + Placeholder(placeholder) => { + write!(f, "Placeholder({:?})", placeholder) + } Opaque(def_id, substs) => { if cx.is_verbose { return write!(f, "Opaque({:?}, {:?})", def_id, substs); diff --git a/src/librustc_codegen_llvm/debuginfo/type_names.rs b/src/librustc_codegen_llvm/debuginfo/type_names.rs index c3a15ccca0a9d..60545f9e193d1 100644 --- a/src/librustc_codegen_llvm/debuginfo/type_names.rs +++ b/src/librustc_codegen_llvm/debuginfo/type_names.rs @@ -172,6 +172,7 @@ pub fn push_debuginfo_type_name<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, } ty::Error | ty::Infer(_) | + ty::Placeholder(..) | ty::UnnormalizedProjection(..) | ty::Projection(..) | ty::Bound(..) | diff --git a/src/librustc_lint/types.rs b/src/librustc_lint/types.rs index 0e2d041b1ffdb..82ace620c8ab2 100644 --- a/src/librustc_lint/types.rs +++ b/src/librustc_lint/types.rs @@ -727,6 +727,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { ty::Closure(..) | ty::Generator(..) | ty::GeneratorWitness(..) | + ty::Placeholder(..) | ty::UnnormalizedProjection(..) | ty::Projection(..) | ty::Opaque(..) | diff --git a/src/librustc_mir/monomorphize/item.rs b/src/librustc_mir/monomorphize/item.rs index 9c90e5ffd3c78..24de92e79f0e4 100644 --- a/src/librustc_mir/monomorphize/item.rs +++ b/src/librustc_mir/monomorphize/item.rs @@ -378,6 +378,7 @@ impl<'a, 'tcx> DefPathBasedNames<'a, 'tcx> { ty::Error | ty::Bound(..) | ty::Infer(_) | + ty::Placeholder(..) | ty::UnnormalizedProjection(..) | ty::Projection(..) | ty::Param(_) | diff --git a/src/librustc_traits/chalk_context/program_clauses.rs b/src/librustc_traits/chalk_context/program_clauses.rs index 31f97b72e1927..5592d2a583cfb 100644 --- a/src/librustc_traits/chalk_context/program_clauses.rs +++ b/src/librustc_traits/chalk_context/program_clauses.rs @@ -418,6 +418,7 @@ impl ChalkInferenceContext<'cx, 'gcx, 'tcx> { } ty::GeneratorWitness(..) | + ty::Placeholder(..) | ty::UnnormalizedProjection(..) | ty::Infer(..) | ty::Bound(..) | diff --git a/src/librustc_traits/dropck_outlives.rs b/src/librustc_traits/dropck_outlives.rs index af64522f18398..9ab86daf65449 100644 --- a/src/librustc_traits/dropck_outlives.rs +++ b/src/librustc_traits/dropck_outlives.rs @@ -274,7 +274,7 @@ fn dtorck_constraint_for_ty<'a, 'gcx, 'tcx>( ty::UnnormalizedProjection(..) => bug!("only used with chalk-engine"), - ty::Bound(..) | ty::Infer(..) | ty::Error => { + ty::Placeholder(..) | ty::Bound(..) | ty::Infer(..) | ty::Error => { // By the time this code runs, all type variables ought to // be fully resolved. Err(NoSolution) diff --git a/src/librustc_traits/lowering/environment.rs b/src/librustc_traits/lowering/environment.rs index 54f0c6e8da78a..c3573f47cebfe 100644 --- a/src/librustc_traits/lowering/environment.rs +++ b/src/librustc_traits/lowering/environment.rs @@ -114,6 +114,7 @@ impl ClauseVisitor<'set, 'a, 'tcx> { ty::Tuple(..) | ty::Never | ty::Infer(..) | + ty::Placeholder(..) | ty::Bound(..) => (), ty::GeneratorWitness(..) | diff --git a/src/librustc_typeck/check/cast.rs b/src/librustc_typeck/check/cast.rs index 3f0a353124442..c35aee7883f49 100644 --- a/src/librustc_typeck/check/cast.rs +++ b/src/librustc_typeck/check/cast.rs @@ -128,7 +128,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { ty::Opaque(def_id, substs) => Some(PointerKind::OfOpaque(def_id, substs)), ty::Param(ref p) => Some(PointerKind::OfParam(p)), // Insufficient type information. - ty::Bound(..) | ty::Infer(_) => None, + ty::Placeholder(..) | ty::Bound(..) | ty::Infer(_) => None, ty::Bool | ty::Char | ty::Int(..) | ty::Uint(..) | ty::Float(_) | ty::Array(..) | ty::GeneratorWitness(..) | diff --git a/src/librustc_typeck/variance/constraints.rs b/src/librustc_typeck/variance/constraints.rs index 47d34c909961e..ed32e5a8d9b38 100644 --- a/src/librustc_typeck/variance/constraints.rs +++ b/src/librustc_typeck/variance/constraints.rs @@ -336,6 +336,7 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> { // types, where we use Error as the Self type } + ty::Placeholder(..) | ty::UnnormalizedProjection(..) | ty::GeneratorWitness(..) | ty::Bound(..) | diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 0518d73e1e30f..fd8f70b19e7ec 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -2744,6 +2744,7 @@ impl<'tcx> Clean for Ty<'tcx> { ty::Closure(..) | ty::Generator(..) => Tuple(vec![]), // FIXME(pcwalton) ty::Bound(..) => panic!("Bound"), + ty::Placeholder(..) => panic!("Placeholder"), ty::UnnormalizedProjection(..) => panic!("UnnormalizedProjection"), ty::GeneratorWitness(..) => panic!("GeneratorWitness"), ty::Infer(..) => panic!("Infer"), From da3def3ebffaefc4e8abbdba99981141ab74e4e4 Mon Sep 17 00:00:00 2001 From: scalexm Date: Fri, 2 Nov 2018 18:56:30 +0100 Subject: [PATCH 28/88] Rename some occurences of `skol` to `placeholder` --- src/librustc/traits/project.rs | 4 ++-- src/librustc/traits/query/normalize.rs | 2 +- src/librustc/ty/fold.rs | 8 ++++---- src/librustc/ty/mod.rs | 6 +++--- src/librustc/ty/sty.rs | 2 +- src/librustc_typeck/check/method/probe.rs | 2 +- src/librustc_typeck/check/writeback.rs | 4 ++-- 7 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/librustc/traits/project.rs b/src/librustc/traits/project.rs index b59bd0e238873..0aabb7ad713f1 100644 --- a/src/librustc/traits/project.rs +++ b/src/librustc/traits/project.rs @@ -424,7 +424,7 @@ impl<'a, 'b, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for AssociatedTypeNormalizer<'a, if let ConstValue::Unevaluated(def_id, substs) = constant.val { let tcx = self.selcx.tcx().global_tcx(); if let Some(param_env) = self.tcx().lift_to_global(&self.param_env) { - if substs.needs_infer() || substs.has_skol() { + if substs.needs_infer() || substs.has_placeholders() { let identity_substs = Substs::identity_for_item(tcx, def_id); let instance = ty::Instance::resolve(tcx, param_env, def_id, identity_substs); if let Some(instance) = instance { @@ -1656,7 +1656,7 @@ impl<'tcx> ProjectionCache<'tcx> { } pub fn rollback_placeholder(&mut self, snapshot: &ProjectionCacheSnapshot) { - self.map.partial_rollback(&snapshot.snapshot, &|k| k.ty.has_re_skol()); + self.map.partial_rollback(&snapshot.snapshot, &|k| k.ty.has_re_placeholders()); } pub fn commit(&mut self, snapshot: &ProjectionCacheSnapshot) { diff --git a/src/librustc/traits/query/normalize.rs b/src/librustc/traits/query/normalize.rs index 59b086e35de31..91b2ba301c312 100644 --- a/src/librustc/traits/query/normalize.rs +++ b/src/librustc/traits/query/normalize.rs @@ -202,7 +202,7 @@ impl<'cx, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for QueryNormalizer<'cx, 'gcx, 'tcx if let ConstValue::Unevaluated(def_id, substs) = constant.val { let tcx = self.infcx.tcx.global_tcx(); if let Some(param_env) = self.tcx().lift_to_global(&self.param_env) { - if substs.needs_infer() || substs.has_skol() { + if substs.needs_infer() || substs.has_placeholders() { let identity_substs = Substs::identity_for_item(tcx, def_id); let instance = ty::Instance::resolve(tcx, param_env, def_id, identity_substs); if let Some(instance) = instance { diff --git a/src/librustc/ty/fold.rs b/src/librustc/ty/fold.rs index 2c7814831450c..9abc84e2a258a 100644 --- a/src/librustc/ty/fold.rs +++ b/src/librustc/ty/fold.rs @@ -102,14 +102,14 @@ pub trait TypeFoldable<'tcx>: fmt::Debug + Clone { fn needs_infer(&self) -> bool { self.has_type_flags(TypeFlags::HAS_TY_INFER | TypeFlags::HAS_RE_INFER) } - fn has_skol(&self) -> bool { - self.has_type_flags(TypeFlags::HAS_RE_SKOL) + fn has_placeholders(&self) -> bool { + self.has_type_flags(TypeFlags::HAS_RE_PLACEHOLDER) } fn needs_subst(&self) -> bool { self.has_type_flags(TypeFlags::NEEDS_SUBST) } - fn has_re_skol(&self) -> bool { - self.has_type_flags(TypeFlags::HAS_RE_SKOL) + fn has_re_placeholders(&self) -> bool { + self.has_type_flags(TypeFlags::HAS_RE_PLACEHOLDER) } fn has_closure_types(&self) -> bool { self.has_type_flags(TypeFlags::HAS_TY_CLOSURE) diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index baedbc8214976..183aaadf6e6be 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -432,7 +432,7 @@ bitflags! { const HAS_SELF = 1 << 1; const HAS_TY_INFER = 1 << 2; const HAS_RE_INFER = 1 << 3; - const HAS_RE_SKOL = 1 << 4; + const HAS_RE_PLACEHOLDER = 1 << 4; /// Does this have any `ReEarlyBound` regions? Used to /// determine whether substitition is required, since those @@ -478,7 +478,7 @@ bitflags! { TypeFlags::HAS_SELF.bits | TypeFlags::HAS_TY_INFER.bits | TypeFlags::HAS_RE_INFER.bits | - TypeFlags::HAS_RE_SKOL.bits | + TypeFlags::HAS_RE_PLACEHOLDER.bits | TypeFlags::HAS_RE_EARLY_BOUND.bits | TypeFlags::HAS_FREE_REGIONS.bits | TypeFlags::HAS_TY_ERR.bits | @@ -1689,7 +1689,7 @@ impl<'tcx> ParamEnv<'tcx> { } Reveal::All => { - if value.has_skol() + if value.has_placeholders() || value.needs_infer() || value.has_param_types() || value.has_self_ty() diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs index 7e6673a3dcdc1..cd69d31a00458 100644 --- a/src/librustc/ty/sty.rs +++ b/src/librustc/ty/sty.rs @@ -1465,7 +1465,7 @@ impl RegionKind { } ty::RePlaceholder(..) => { flags = flags | TypeFlags::HAS_FREE_REGIONS; - flags = flags | TypeFlags::HAS_RE_SKOL; + flags = flags | TypeFlags::HAS_RE_PLACEHOLDER; } ty::ReLateBound(..) => { flags = flags | TypeFlags::HAS_RE_LATE_BOUND; diff --git a/src/librustc_typeck/check/method/probe.rs b/src/librustc_typeck/check/method/probe.rs index 0373bf4e7522d..5b67116cb511e 100644 --- a/src/librustc_typeck/check/method/probe.rs +++ b/src/librustc_typeck/check/method/probe.rs @@ -1494,7 +1494,7 @@ impl<'tcx> Candidate<'tcx> { // `WhereClausePick`. assert!( !trait_ref.skip_binder().substs.needs_infer() - && !trait_ref.skip_binder().substs.has_skol() + && !trait_ref.skip_binder().substs.has_placeholders() ); WhereClausePick(trait_ref.clone()) diff --git a/src/librustc_typeck/check/writeback.rs b/src/librustc_typeck/check/writeback.rs index 50f54bba3fd20..669f2bcb77c4d 100644 --- a/src/librustc_typeck/check/writeback.rs +++ b/src/librustc_typeck/check/writeback.rs @@ -115,7 +115,7 @@ impl<'cx, 'gcx, 'tcx> WritebackCx<'cx, 'gcx, 'tcx> { fn write_ty_to_tables(&mut self, hir_id: hir::HirId, ty: Ty<'gcx>) { debug!("write_ty_to_tables({:?}, {:?})", hir_id, ty); - assert!(!ty.needs_infer() && !ty.has_skol()); + assert!(!ty.needs_infer() && !ty.has_placeholders()); self.tables.node_types_mut().insert(hir_id, ty); } @@ -580,7 +580,7 @@ impl<'cx, 'gcx, 'tcx> WritebackCx<'cx, 'gcx, 'tcx> { if let Some(substs) = self.fcx.tables.borrow().node_substs_opt(hir_id) { let substs = self.resolve(&substs, &span); debug!("write_substs_to_tcx({:?}, {:?})", hir_id, substs); - assert!(!substs.needs_infer() && !substs.has_skol()); + assert!(!substs.needs_infer() && !substs.has_placeholders()); self.tables.node_substs_mut().insert(hir_id, substs); } From 91623ca6405b5e3c7736bcacb244718906d4aa69 Mon Sep 17 00:00:00 2001 From: scalexm Date: Fri, 2 Nov 2018 19:25:20 +0100 Subject: [PATCH 29/88] Add `HAS_TY_PLACEHOLDER` flag --- src/librustc/infer/canonical/canonicalizer.rs | 8 ++++++-- src/librustc/ty/flags.rs | 5 ++++- src/librustc/ty/fold.rs | 2 +- src/librustc/ty/mod.rs | 5 ++++- 4 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/librustc/infer/canonical/canonicalizer.rs b/src/librustc/infer/canonical/canonicalizer.rs index a3cbae2ff71c1..a9c6c3b50853d 100644 --- a/src/librustc/infer/canonical/canonicalizer.rs +++ b/src/librustc/infer/canonical/canonicalizer.rs @@ -409,9 +409,13 @@ impl<'cx, 'gcx, 'tcx> Canonicalizer<'cx, 'gcx, 'tcx> { V: TypeFoldable<'tcx> + Lift<'gcx>, { let needs_canonical_flags = if canonicalize_region_mode.any() { - TypeFlags::HAS_FREE_REGIONS | TypeFlags::KEEP_IN_LOCAL_TCX + TypeFlags::KEEP_IN_LOCAL_TCX | + TypeFlags::HAS_FREE_REGIONS | // `HAS_RE_PLACEHOLDER` implies `HAS_FREE_REGIONS` + TypeFlags::HAS_TY_PLACEHOLDER } else { - TypeFlags::KEEP_IN_LOCAL_TCX + TypeFlags::KEEP_IN_LOCAL_TCX | + TypeFlags::HAS_RE_PLACEHOLDER | + TypeFlags::HAS_TY_PLACEHOLDER }; let gcx = tcx.global_tcx(); diff --git a/src/librustc/ty/flags.rs b/src/librustc/ty/flags.rs index 9cdeb300393b9..3b84e7b54bdf3 100644 --- a/src/librustc/ty/flags.rs +++ b/src/librustc/ty/flags.rs @@ -74,7 +74,6 @@ impl FlagComputation { &ty::Uint(_) | &ty::Never | &ty::Str | - &ty::Placeholder(..) | &ty::Foreign(..) => { } @@ -120,6 +119,10 @@ impl FlagComputation { self.add_binder(bound_ty.index); } + &ty::Placeholder(..) => { + self.add_flags(TypeFlags::HAS_TY_PLACEHOLDER); + } + &ty::Infer(infer) => { self.add_flags(TypeFlags::HAS_FREE_LOCAL_NAMES); // it might, right? self.add_flags(TypeFlags::HAS_TY_INFER); diff --git a/src/librustc/ty/fold.rs b/src/librustc/ty/fold.rs index 9abc84e2a258a..16de146cf4e4f 100644 --- a/src/librustc/ty/fold.rs +++ b/src/librustc/ty/fold.rs @@ -103,7 +103,7 @@ pub trait TypeFoldable<'tcx>: fmt::Debug + Clone { self.has_type_flags(TypeFlags::HAS_TY_INFER | TypeFlags::HAS_RE_INFER) } fn has_placeholders(&self) -> bool { - self.has_type_flags(TypeFlags::HAS_RE_PLACEHOLDER) + self.has_type_flags(TypeFlags::HAS_RE_PLACEHOLDER | TypeFlags::HAS_TY_PLACEHOLDER) } fn needs_subst(&self) -> bool { self.has_type_flags(TypeFlags::NEEDS_SUBST) diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index 183aaadf6e6be..b371f4532e5fa 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -467,6 +467,8 @@ bitflags! { /// if a global bound is safe to evaluate. const HAS_RE_LATE_BOUND = 1 << 13; + const HAS_TY_PLACEHOLDER = 1 << 14; + const NEEDS_SUBST = TypeFlags::HAS_PARAMS.bits | TypeFlags::HAS_SELF.bits | TypeFlags::HAS_RE_EARLY_BOUND.bits; @@ -486,7 +488,8 @@ bitflags! { TypeFlags::HAS_TY_CLOSURE.bits | TypeFlags::HAS_FREE_LOCAL_NAMES.bits | TypeFlags::KEEP_IN_LOCAL_TCX.bits | - TypeFlags::HAS_RE_LATE_BOUND.bits; + TypeFlags::HAS_RE_LATE_BOUND.bits | + TypeFlags::HAS_TY_PLACEHOLDER.bits; } } From cdb96be11e178a82ac6b16f37ef2a9c2b7189d6b Mon Sep 17 00:00:00 2001 From: scalexm Date: Fri, 2 Nov 2018 19:46:30 +0100 Subject: [PATCH 30/88] Handle placeholder types in canonicalization --- src/librustc/ich/impls_ty.rs | 3 +- src/librustc/infer/canonical/canonicalizer.rs | 43 +++++++++++++++---- src/librustc/infer/canonical/mod.rs | 36 +++++++++++----- src/librustc/infer/mod.rs | 22 ++++++++++ src/librustc/infer/type_variable.rs | 2 +- 5 files changed, 85 insertions(+), 21 deletions(-) diff --git a/src/librustc/ich/impls_ty.rs b/src/librustc/ich/impls_ty.rs index 4b465c7ad54a9..e9fac5b923961 100644 --- a/src/librustc/ich/impls_ty.rs +++ b/src/librustc/ich/impls_ty.rs @@ -1099,12 +1099,13 @@ impl_stable_hash_for!(struct infer::canonical::CanonicalVarInfo { impl_stable_hash_for!(enum infer::canonical::CanonicalVarKind { Ty(k), + PlaceholderTy(placeholder), Region(ui), PlaceholderRegion(placeholder), }); impl_stable_hash_for!(enum infer::canonical::CanonicalTyVarKind { - General, + General(ui), Int, Float }); diff --git a/src/librustc/infer/canonical/canonicalizer.rs b/src/librustc/infer/canonical/canonicalizer.rs index a9c6c3b50853d..7a9527573c7d6 100644 --- a/src/librustc/infer/canonical/canonicalizer.rs +++ b/src/librustc/infer/canonical/canonicalizer.rs @@ -339,11 +339,35 @@ impl<'cx, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for Canonicalizer<'cx, 'gcx, 'tcx> fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { match t.sty { - ty::Infer(ty::TyVar(_)) => self.canonicalize_ty_var(CanonicalTyVarKind::General, t), + ty::Infer(ty::TyVar(vid)) => { + match self.infcx.unwrap().probe_ty_var(vid) { + // `t` could be a float / int variable: canonicalize that instead + Ok(t) => self.fold_ty(t), + + // `TyVar(vid)` is unresolved, track its universe index in the canonicalized + // result + Err(ui) => self.canonicalize_ty_var( + CanonicalVarInfo { + kind: CanonicalVarKind::Ty(CanonicalTyVarKind::General(ui)) + }, + t + ) + } + } - ty::Infer(ty::IntVar(_)) => self.canonicalize_ty_var(CanonicalTyVarKind::Int, t), + ty::Infer(ty::IntVar(_)) => self.canonicalize_ty_var( + CanonicalVarInfo { + kind: CanonicalVarKind::Ty(CanonicalTyVarKind::Int) + }, + t + ), - ty::Infer(ty::FloatVar(_)) => self.canonicalize_ty_var(CanonicalTyVarKind::Float, t), + ty::Infer(ty::FloatVar(_)) => self.canonicalize_ty_var( + CanonicalVarInfo { + kind: CanonicalVarKind::Ty(CanonicalTyVarKind::Float) + }, + t + ), ty::Infer(ty::FreshTy(_)) | ty::Infer(ty::FreshIntTy(_)) @@ -351,6 +375,13 @@ impl<'cx, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for Canonicalizer<'cx, 'gcx, 'tcx> bug!("encountered a fresh type during canonicalization") } + ty::Placeholder(placeholder) => self.canonicalize_ty_var( + CanonicalVarInfo { + kind: CanonicalVarKind::PlaceholderTy(placeholder) + }, + t + ), + ty::Bound(bound_ty) => { if bound_ty.index >= self.binder_index { bug!("escaping bound type during canonicalization") @@ -380,7 +411,6 @@ impl<'cx, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for Canonicalizer<'cx, 'gcx, 'tcx> | ty::Never | ty::Tuple(..) | ty::Projection(..) - | ty::Placeholder(..) | ty::UnnormalizedProjection(..) | ty::Foreign(..) | ty::Param(..) @@ -579,15 +609,12 @@ impl<'cx, 'gcx, 'tcx> Canonicalizer<'cx, 'gcx, 'tcx> { /// if `ty_var` is bound to anything; if so, canonicalize /// *that*. Otherwise, create a new canonical variable for /// `ty_var`. - fn canonicalize_ty_var(&mut self, ty_kind: CanonicalTyVarKind, ty_var: Ty<'tcx>) -> Ty<'tcx> { + fn canonicalize_ty_var(&mut self, info: CanonicalVarInfo, ty_var: Ty<'tcx>) -> Ty<'tcx> { let infcx = self.infcx.expect("encountered ty-var without infcx"); let bound_to = infcx.shallow_resolve(ty_var); if bound_to != ty_var { self.fold_ty(bound_to) } else { - let info = CanonicalVarInfo { - kind: CanonicalVarKind::Ty(ty_kind), - }; let var = self.canonical_var(info, ty_var.into()); self.tcx().mk_ty(ty::Bound(BoundTy::new(self.binder_index, var))) } diff --git a/src/librustc/infer/canonical/mod.rs b/src/librustc/infer/canonical/mod.rs index 41839d61d3260..230f8958b3385 100644 --- a/src/librustc/infer/canonical/mod.rs +++ b/src/librustc/infer/canonical/mod.rs @@ -122,6 +122,7 @@ impl CanonicalVarInfo { pub fn is_existential(&self) -> bool { match self.kind { CanonicalVarKind::Ty(_) => true, + CanonicalVarKind::PlaceholderTy(_) => false, CanonicalVarKind::Region(_) => true, CanonicalVarKind::PlaceholderRegion(..) => false, } @@ -136,6 +137,9 @@ pub enum CanonicalVarKind { /// Some kind of type inference variable. Ty(CanonicalTyVarKind), + /// A "placeholder" that represents "any type". + PlaceholderTy(ty::PlaceholderType), + /// Region variable `'?R`. Region(ty::UniverseIndex), @@ -148,12 +152,12 @@ pub enum CanonicalVarKind { impl CanonicalVarKind { pub fn universe(self) -> ty::UniverseIndex { match self { - // At present, we don't support higher-ranked - // quantification over types, so all type variables are in - // the root universe. - CanonicalVarKind::Ty(_) => ty::UniverseIndex::ROOT, + CanonicalVarKind::Ty(kind) => match kind { + CanonicalTyVarKind::General(ui) => ui, + CanonicalTyVarKind::Float | CanonicalTyVarKind::Int => ty::UniverseIndex::ROOT, + } - // Region variables can be created in sub-universes. + CanonicalVarKind::PlaceholderTy(placeholder) => placeholder.universe, CanonicalVarKind::Region(ui) => ui, CanonicalVarKind::PlaceholderRegion(placeholder) => placeholder.universe, } @@ -168,7 +172,7 @@ impl CanonicalVarKind { #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, RustcDecodable, RustcEncodable)] pub enum CanonicalTyVarKind { /// General type variable `?T` that can be unified with arbitrary types. - General, + General(ty::UniverseIndex), /// Integral type variable `?I` (that can only be unified with integral types). Int, @@ -358,8 +362,11 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { match cv_info.kind { CanonicalVarKind::Ty(ty_kind) => { let ty = match ty_kind { - CanonicalTyVarKind::General => { - self.next_ty_var(TypeVariableOrigin::MiscVariable(span)) + CanonicalTyVarKind::General(ui) => { + self.next_ty_var_in_universe( + TypeVariableOrigin::MiscVariable(span), + universe_map(ui) + ) } CanonicalTyVarKind::Int => self.tcx.mk_int_var(self.next_int_var_id()), @@ -369,6 +376,15 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { ty.into() } + CanonicalVarKind::PlaceholderTy(ty::PlaceholderType { universe, name }) => { + let universe_mapped = universe_map(universe); + let placeholder_mapped = ty::PlaceholderType { + universe: universe_mapped, + name, + }; + self.tcx.mk_ty(ty::Placeholder(placeholder_mapped)).into() + } + CanonicalVarKind::Region(ui) => self.next_region_var_in_universe( RegionVariableOrigin::MiscVariable(span), universe_map(ui), @@ -380,9 +396,7 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { universe: universe_mapped, name, }; - self.tcx - .mk_region(ty::RePlaceholder(placeholder_mapped)) - .into() + self.tcx.mk_region(ty::RePlaceholder(placeholder_mapped)).into() } } } diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs index dfe6aa160b3ad..6f041fdfb0ac4 100644 --- a/src/librustc/infer/mod.rs +++ b/src/librustc/infer/mod.rs @@ -972,6 +972,17 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { self.tcx.mk_var(self.next_ty_var_id(false, origin)) } + pub fn next_ty_var_in_universe( + &self, + origin: TypeVariableOrigin, + universe: ty::UniverseIndex + ) -> Ty<'tcx> { + let vid = self.type_variables + .borrow_mut() + .new_var(universe, false, origin); + self.tcx.mk_var(vid) + } + pub fn next_diverging_ty_var(&self, origin: TypeVariableOrigin) -> Ty<'tcx> { self.tcx.mk_var(self.next_ty_var_id(true, origin)) } @@ -1227,6 +1238,17 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { } } + /// If `TyVar(vid)` resolves to a type, return that type. Else, return the + /// universe index of `TyVar(vid)`. + pub fn probe_ty_var(&self, vid: TyVid) -> Result, ty::UniverseIndex> { + use self::type_variable::TypeVariableValue; + + match self.type_variables.borrow_mut().probe(vid) { + TypeVariableValue::Known { value } => Ok(value), + TypeVariableValue::Unknown { universe } => Err(universe), + } + } + pub fn shallow_resolve(&self, typ: Ty<'tcx>) -> Ty<'tcx> { self.inlined_shallow_resolve(typ) } diff --git a/src/librustc/infer/type_variable.rs b/src/librustc/infer/type_variable.rs index bec19ba9099dc..5624961ea6e67 100644 --- a/src/librustc/infer/type_variable.rs +++ b/src/librustc/infer/type_variable.rs @@ -72,7 +72,7 @@ pub type TypeVariableMap = FxHashMap; struct TypeVariableData { origin: TypeVariableOrigin, - diverging: bool + diverging: bool, } #[derive(Copy, Clone, Debug)] From 6bf17d249b1dc515eeafcdd04e30723120cb7899 Mon Sep 17 00:00:00 2001 From: scalexm Date: Sat, 3 Nov 2018 14:52:37 +0100 Subject: [PATCH 31/88] Instantiate all bound vars universally --- src/librustc/infer/higher_ranked/mod.rs | 43 ++++++++++++++++--------- src/librustc/infer/mod.rs | 6 ++-- src/librustc/traits/project.rs | 2 +- src/librustc/traits/select.rs | 10 +++--- 4 files changed, 36 insertions(+), 25 deletions(-) diff --git a/src/librustc/infer/higher_ranked/mod.rs b/src/librustc/infer/higher_ranked/mod.rs index ddf46b18ef730..5218aa36facc1 100644 --- a/src/librustc/infer/higher_ranked/mod.rs +++ b/src/librustc/infer/higher_ranked/mod.rs @@ -53,7 +53,7 @@ impl<'a, 'gcx, 'tcx> CombineFields<'a, 'gcx, 'tcx> { // First, we instantiate each bound region in the supertype with a // fresh placeholder region. let (b_prime, placeholder_map) = - self.infcx.replace_late_bound_regions_with_placeholders(b); + self.infcx.replace_bound_vars_with_placeholders(b); // Next, we instantiate each bound region in the subtype // with a fresh region variable. These region variables -- @@ -115,7 +115,7 @@ impl<'a, 'gcx, 'tcx> CombineFields<'a, 'gcx, 'tcx> { // First, we instantiate each bound region in the matcher // with a placeholder region. let ((a_match, a_value), placeholder_map) = - self.infcx.replace_late_bound_regions_with_placeholders(a_pair); + self.infcx.replace_bound_vars_with_placeholders(a_pair); debug!("higher_ranked_match: a_match={:?}", a_match); debug!("higher_ranked_match: placeholder_map={:?}", placeholder_map); @@ -314,10 +314,10 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { region_vars } - /// Replace all regions bound by `binder` with placeholder regions and - /// return a map indicating which bound-region was replaced with what - /// placeholder region. This is the first step of checking subtyping - /// when higher-ranked things are involved. + /// Replace all regions (resp. types) bound by `binder` with placeholder + /// regions (resp. types) and return a map indicating which bound-region + /// was replaced with what placeholder region. This is the first step of + /// checking subtyping when higher-ranked things are involved. /// /// **Important:** you must call this function from within a snapshot. /// Moreover, before committing the snapshot, you must eventually call @@ -330,26 +330,37 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { /// the [rustc guide]. /// /// [rustc guide]: https://rust-lang-nursery.github.io/rustc-guide/traits/hrtb.html - pub fn replace_late_bound_regions_with_placeholders( + pub fn replace_bound_vars_with_placeholders( &self, - binder: &ty::Binder, + binder: &ty::Binder ) -> (T, PlaceholderMap<'tcx>) where - T : TypeFoldable<'tcx>, + T: TypeFoldable<'tcx> { let next_universe = self.create_next_universe(); - let (result, map) = self.tcx.replace_late_bound_regions(binder, |br| { + let fld_r = |br| { self.tcx.mk_region(ty::RePlaceholder(ty::PlaceholderRegion { universe: next_universe, name: br, })) - }); + }; + + let fld_t = |bound_ty: ty::BoundTy| { + self.tcx.mk_ty(ty::Placeholder(ty::PlaceholderType { + universe: next_universe, + name: bound_ty.var, + })) + }; + + let (result, map) = self.tcx.replace_bound_vars(binder, fld_r, fld_t); - debug!("replace_late_bound_regions_with_placeholders(binder={:?}, result={:?}, map={:?})", - binder, - result, - map); + debug!( + "replace_bound_vars_with_placeholders(binder={:?}, result={:?}, map={:?})", + binder, + result, + map + ); (result, map) } @@ -530,7 +541,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { /// Pops the placeholder regions found in `placeholder_map` from the region /// inference context. Whenever you create placeholder regions via - /// `replace_late_bound_regions_with_placeholders`, they must be popped before you + /// `replace_bound_vars_with_placeholders`, they must be popped before you /// commit the enclosing snapshot (if you do not commit, e.g. within a /// probe or as a result of an error, then this is not necessary, as /// popping happens as part of the rollback). diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs index 6f041fdfb0ac4..a29c85bd2b1c5 100644 --- a/src/librustc/infer/mod.rs +++ b/src/librustc/infer/mod.rs @@ -227,7 +227,7 @@ pub struct InferCtxt<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> { universe: Cell, } -/// A map returned by `replace_late_bound_regions_with_placeholders()` +/// A map returned by `replace_bound_vars_with_placeholders()` /// indicating the placeholder region that each late-bound region was /// replaced with. pub type PlaceholderMap<'tcx> = BTreeMap>; @@ -935,7 +935,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { b, }, placeholder_map, - ) = self.replace_late_bound_regions_with_placeholders(predicate); + ) = self.replace_bound_vars_with_placeholders(predicate); let cause_span = cause.span; let ok = self.at(cause, param_env).sub_exp(a_is_expected, a, b)?; @@ -952,7 +952,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { ) -> UnitResult<'tcx> { self.commit_if_ok(|snapshot| { let (ty::OutlivesPredicate(r_a, r_b), placeholder_map) = - self.replace_late_bound_regions_with_placeholders(predicate); + self.replace_bound_vars_with_placeholders(predicate); let origin = SubregionOrigin::from_obligation_cause(cause, || { RelateRegionParamBound(cause.span) }); diff --git a/src/librustc/traits/project.rs b/src/librustc/traits/project.rs index 0aabb7ad713f1..e7b5fc3d1ffd7 100644 --- a/src/librustc/traits/project.rs +++ b/src/librustc/traits/project.rs @@ -204,7 +204,7 @@ pub fn poly_project_and_unify_type<'cx, 'gcx, 'tcx>( let infcx = selcx.infcx(); infcx.commit_if_ok(|snapshot| { let (placeholder_predicate, placeholder_map) = - infcx.replace_late_bound_regions_with_placeholders(&obligation.predicate); + infcx.replace_bound_vars_with_placeholders(&obligation.predicate); let skol_obligation = obligation.with(placeholder_predicate); let r = match project_and_unify_type(selcx, &skol_obligation) { diff --git a/src/librustc/traits/select.rs b/src/librustc/traits/select.rs index 6a91ad59d98e4..0cb071e3196ae 100644 --- a/src/librustc/traits/select.rs +++ b/src/librustc/traits/select.rs @@ -1726,7 +1726,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> { let poly_trait_predicate = self.infcx() .resolve_type_vars_if_possible(&obligation.predicate); let (skol_trait_predicate, placeholder_map) = self.infcx() - .replace_late_bound_regions_with_placeholders(&poly_trait_predicate); + .replace_bound_vars_with_placeholders(&poly_trait_predicate); debug!( "match_projection_obligation_against_definition_bounds: \ skol_trait_predicate={:?} placeholder_map={:?}", @@ -2685,7 +2685,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> { self.in_snapshot(|this, snapshot| { let (skol_ty, placeholder_map) = this.infcx() - .replace_late_bound_regions_with_placeholders(&ty); + .replace_bound_vars_with_placeholders(&ty); let Normalized { value: normalized_ty, mut obligations, @@ -2919,7 +2919,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> { let trait_obligations: Vec> = self.in_snapshot(|this, snapshot| { let poly_trait_ref = obligation.predicate.to_poly_trait_ref(); let (trait_ref, placeholder_map) = this.infcx() - .replace_late_bound_regions_with_placeholders(&poly_trait_ref); + .replace_bound_vars_with_placeholders(&poly_trait_ref); let cause = obligation.derived_cause(ImplDerivedObligation); this.impl_or_trait_obligations( cause, @@ -3122,7 +3122,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> { self.in_snapshot(|this, snapshot| { let (predicate, placeholder_map) = this.infcx() - .replace_late_bound_regions_with_placeholders(&obligation.predicate); + .replace_bound_vars_with_placeholders(&obligation.predicate); let trait_ref = predicate.trait_ref; let trait_def_id = trait_ref.def_id; let substs = trait_ref.substs; @@ -3585,7 +3585,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> { } let (skol_obligation, placeholder_map) = self.infcx() - .replace_late_bound_regions_with_placeholders(&obligation.predicate); + .replace_bound_vars_with_placeholders(&obligation.predicate); let skol_obligation_trait_ref = skol_obligation.trait_ref; let impl_substs = self.infcx From 95861b159072c404ad5ef2951d1f8c323a3878ca Mon Sep 17 00:00:00 2001 From: scalexm Date: Sat, 3 Nov 2018 15:15:33 +0100 Subject: [PATCH 32/88] Move `BoundTy` debruijn index to the `TyKind` enum variant --- src/librustc/ich/impls_ty.rs | 3 ++- src/librustc/infer/canonical/canonicalizer.rs | 8 ++++---- .../infer/canonical/query_response.rs | 8 ++++---- src/librustc/traits/select.rs | 6 +++--- src/librustc/traits/structural_impls.rs | 2 +- src/librustc/ty/error.rs | 2 +- src/librustc/ty/flags.rs | 4 ++-- src/librustc/ty/fold.rs | 19 ++++++++----------- src/librustc/ty/sty.rs | 12 +++++------- src/librustc/ty/subst.rs | 19 ++++++++++--------- src/librustc/util/ppaux.rs | 6 +++--- .../chalk_context/program_clauses.rs | 19 +++++++++++-------- src/librustc_traits/lowering/environment.rs | 2 +- src/librustc_traits/lowering/mod.rs | 3 ++- 14 files changed, 57 insertions(+), 56 deletions(-) diff --git a/src/librustc/ich/impls_ty.rs b/src/librustc/ich/impls_ty.rs index e9fac5b923961..ad317a96590b3 100644 --- a/src/librustc/ich/impls_ty.rs +++ b/src/librustc/ich/impls_ty.rs @@ -684,7 +684,8 @@ for ty::TyKind<'gcx> Param(param_ty) => { param_ty.hash_stable(hcx, hasher); } - Bound(bound_ty) => { + Bound(debruijn, bound_ty) => { + debruijn.hash_stable(hcx, hasher); bound_ty.hash_stable(hcx, hasher); } ty::Placeholder(placeholder_ty) => { diff --git a/src/librustc/infer/canonical/canonicalizer.rs b/src/librustc/infer/canonical/canonicalizer.rs index 7a9527573c7d6..ddb520775da0a 100644 --- a/src/librustc/infer/canonical/canonicalizer.rs +++ b/src/librustc/infer/canonical/canonicalizer.rs @@ -23,7 +23,7 @@ use infer::InferCtxt; use std::sync::atomic::Ordering; use ty::fold::{TypeFoldable, TypeFolder}; use ty::subst::Kind; -use ty::{self, BoundTy, BoundVar, Lift, List, Ty, TyCtxt, TypeFlags}; +use ty::{self, BoundVar, Lift, List, Ty, TyCtxt, TypeFlags}; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::indexed_vec::Idx; @@ -382,8 +382,8 @@ impl<'cx, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for Canonicalizer<'cx, 'gcx, 'tcx> t ), - ty::Bound(bound_ty) => { - if bound_ty.index >= self.binder_index { + ty::Bound(debruijn, _) => { + if debruijn >= self.binder_index { bug!("escaping bound type during canonicalization") } else { t @@ -616,7 +616,7 @@ impl<'cx, 'gcx, 'tcx> Canonicalizer<'cx, 'gcx, 'tcx> { self.fold_ty(bound_to) } else { let var = self.canonical_var(info, ty_var.into()); - self.tcx().mk_ty(ty::Bound(BoundTy::new(self.binder_index, var))) + self.tcx().mk_ty(ty::Bound(self.binder_index, var.into())) } } } diff --git a/src/librustc/infer/canonical/query_response.rs b/src/librustc/infer/canonical/query_response.rs index 6f3d10268351a..d32594ebdf366 100644 --- a/src/librustc/infer/canonical/query_response.rs +++ b/src/librustc/infer/canonical/query_response.rs @@ -435,21 +435,21 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { match result_value.unpack() { UnpackedKind::Type(result_value) => { // e.g., here `result_value` might be `?0` in the example above... - if let ty::Bound(b) = result_value.sty { + if let ty::Bound(debruijn, b) = result_value.sty { // ...in which case we would set `canonical_vars[0]` to `Some(?U)`. // We only allow a `ty::INNERMOST` index in substitutions. - assert_eq!(b.index, ty::INNERMOST); + assert_eq!(debruijn, ty::INNERMOST); opt_values[b.var] = Some(*original_value); } } UnpackedKind::Lifetime(result_value) => { // e.g., here `result_value` might be `'?1` in the example above... - if let &ty::RegionKind::ReLateBound(index, br) = result_value { + if let &ty::RegionKind::ReLateBound(debruijn, br) = result_value { // ... in which case we would set `canonical_vars[0]` to `Some('static)`. // We only allow a `ty::INNERMOST` index in substitutions. - assert_eq!(index, ty::INNERMOST); + assert_eq!(debruijn, ty::INNERMOST); opt_values[br.assert_bound_var()] = Some(*original_value); } } diff --git a/src/librustc/traits/select.rs b/src/librustc/traits/select.rs index 0cb071e3196ae..0f59f478cb415 100644 --- a/src/librustc/traits/select.rs +++ b/src/librustc/traits/select.rs @@ -2471,7 +2471,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> { ty::UnnormalizedProjection(..) | ty::Placeholder(..) - | ty::Bound(_) + | ty::Bound(..) | ty::Infer(ty::FreshTy(_)) | ty::Infer(ty::FreshIntTy(_)) | ty::Infer(ty::FreshFloatTy(_)) => { @@ -2557,7 +2557,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> { ty::UnnormalizedProjection(..) | ty::Placeholder(..) - | ty::Bound(_) + | ty::Bound(..) | ty::Infer(ty::FreshTy(_)) | ty::Infer(ty::FreshIntTy(_)) | ty::Infer(ty::FreshFloatTy(_)) => { @@ -2601,7 +2601,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> { | ty::Param(..) | ty::Foreign(..) | ty::Projection(..) - | ty::Bound(_) + | ty::Bound(..) | ty::Infer(ty::TyVar(_)) | ty::Infer(ty::FreshTy(_)) | ty::Infer(ty::FreshIntTy(_)) diff --git a/src/librustc/traits/structural_impls.rs b/src/librustc/traits/structural_impls.rs index 3e417f10c4494..36538ac0889de 100644 --- a/src/librustc/traits/structural_impls.rs +++ b/src/librustc/traits/structural_impls.rs @@ -324,7 +324,7 @@ impl<'tcx> TypeVisitor<'tcx> for BoundNamesCollector { use syntax::symbol::Symbol; match t.sty { - ty::Bound(bound_ty) if bound_ty.index == self.binder_index => { + ty::Bound(debruijn, bound_ty) if debruijn == self.binder_index => { self.types.insert( bound_ty.var.as_u32(), match bound_ty.kind { diff --git a/src/librustc/ty/error.rs b/src/librustc/ty/error.rs index e78759e3e7961..90022a770c114 100644 --- a/src/librustc/ty/error.rs +++ b/src/librustc/ty/error.rs @@ -213,7 +213,7 @@ impl<'a, 'gcx, 'lcx, 'tcx> ty::TyS<'tcx> { ty::Infer(ty::IntVar(_)) => "integral variable".into(), ty::Infer(ty::FloatVar(_)) => "floating-point variable".into(), ty::Placeholder(..) => "placeholder type".into(), - ty::Bound(_) | + ty::Bound(..) => "bound type".into(), ty::Infer(ty::FreshTy(_)) => "fresh type".into(), ty::Infer(ty::FreshIntTy(_)) => "fresh integral type".into(), ty::Infer(ty::FreshFloatTy(_)) => "fresh floating-point type".into(), diff --git a/src/librustc/ty/flags.rs b/src/librustc/ty/flags.rs index 3b84e7b54bdf3..1ea7e27c0dcdb 100644 --- a/src/librustc/ty/flags.rs +++ b/src/librustc/ty/flags.rs @@ -115,8 +115,8 @@ impl FlagComputation { self.add_substs(&substs.substs); } - &ty::Bound(bound_ty) => { - self.add_binder(bound_ty.index); + &ty::Bound(debruijn, _) => { + self.add_binder(debruijn); } &ty::Placeholder(..) => { diff --git a/src/librustc/ty/fold.rs b/src/librustc/ty/fold.rs index 16de146cf4e4f..6f0e8d4f02680 100644 --- a/src/librustc/ty/fold.rs +++ b/src/librustc/ty/fold.rs @@ -460,8 +460,8 @@ impl<'a, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for BoundVarReplacer<'a, 'gcx, 'tcx> fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { match t.sty { - ty::Bound(bound_ty) => { - if bound_ty.index == self.current_index { + ty::Bound(debruijn, bound_ty) => { + if debruijn == self.current_index { let fld_t = &mut self.fld_t; let ty = fld_t(bound_ty); ty::fold::shift_vars( @@ -526,7 +526,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { T: TypeFoldable<'tcx> { // identity for bound types - let fld_t = |bound_ty| self.mk_ty(ty::Bound(bound_ty)); + let fld_t = |bound_ty| self.mk_ty(ty::Bound(ty::INNERMOST, bound_ty)); self.replace_escaping_bound_vars(value.skip_binder(), fld_r, fld_t) } @@ -722,16 +722,13 @@ impl<'a, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for Shifter<'a, 'gcx, 'tcx> { fn fold_ty(&mut self, ty: ty::Ty<'tcx>) -> ty::Ty<'tcx> { match ty.sty { - ty::Bound(bound_ty) => { - if self.amount == 0 || bound_ty.index < self.current_index { + ty::Bound(debruijn, bound_ty) => { + if self.amount == 0 || debruijn < self.current_index { ty } else { - let shifted = ty::BoundTy { - index: bound_ty.index.shifted_in(self.amount), - var: bound_ty.var, - kind: bound_ty.kind, - }; - self.tcx.mk_ty(ty::Bound(shifted)) + self.tcx.mk_ty( + ty::Bound(debruijn.shifted_in(self.amount), bound_ty) + ) } } diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs index cd69d31a00458..3056abba95626 100644 --- a/src/librustc/ty/sty.rs +++ b/src/librustc/ty/sty.rs @@ -201,7 +201,7 @@ pub enum TyKind<'tcx> { Param(ParamTy), /// Bound type variable, used only when preparing a trait query. - Bound(BoundTy), + Bound(ty::DebruijnIndex, BoundTy), /// A placeholder type - universally quantified higher-ranked type. Placeholder(ty::PlaceholderType), @@ -1245,7 +1245,6 @@ newtype_index! { #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, RustcEncodable, RustcDecodable)] pub struct BoundTy { - pub index: DebruijnIndex, pub var: BoundVar, pub kind: BoundTyKind, } @@ -1256,13 +1255,12 @@ pub enum BoundTyKind { Param(InternedString), } -impl_stable_hash_for!(struct BoundTy { index, var, kind }); +impl_stable_hash_for!(struct BoundTy { var, kind }); impl_stable_hash_for!(enum self::BoundTyKind { Anon, Param(a) }); -impl BoundTy { - pub fn new(index: DebruijnIndex, var: BoundVar) -> Self { +impl From for BoundTy { + fn from(var: BoundVar) -> Self { BoundTy { - index, var, kind: BoundTyKind::Anon, } @@ -1957,7 +1955,7 @@ impl<'a, 'gcx, 'tcx> TyS<'tcx> { ty::Infer(ty::TyVar(_)) => false, - ty::Bound(_) | + ty::Bound(..) | ty::Placeholder(..) | ty::Infer(ty::FreshTy(_)) | ty::Infer(ty::FreshIntTy(_)) | diff --git a/src/librustc/ty/subst.rs b/src/librustc/ty/subst.rs index b7f1731ba44e5..3425203989891 100644 --- a/src/librustc/ty/subst.rs +++ b/src/librustc/ty/subst.rs @@ -190,11 +190,12 @@ impl<'a, 'gcx, 'tcx> Substs<'tcx> { Substs::for_item(tcx, def_id, |param, _| { match param.kind { ty::GenericParamDefKind::Type { .. } => { - tcx.mk_ty(ty::Bound(ty::BoundTy { - index: ty::INNERMOST, - var: ty::BoundVar::from(param.index), - kind: ty::BoundTyKind::Param(param.name), - })).into() + tcx.mk_ty( + ty::Bound(ty::INNERMOST, ty::BoundTy { + var: ty::BoundVar::from(param.index), + kind: ty::BoundTyKind::Param(param.name), + }) + ).into() } ty::GenericParamDefKind::Lifetime => { @@ -584,18 +585,18 @@ impl CanonicalUserSubsts<'tcx> { self.value.substs.iter().zip(BoundVar::new(0)..).all(|(kind, cvar)| { match kind.unpack() { UnpackedKind::Type(ty) => match ty.sty { - ty::Bound(b) => { + ty::Bound(debruijn, b) => { // We only allow a `ty::INNERMOST` index in substitutions. - assert_eq!(b.index, ty::INNERMOST); + assert_eq!(debruijn, ty::INNERMOST); cvar == b.var } _ => false, }, UnpackedKind::Lifetime(r) => match r { - ty::ReLateBound(index, br) => { + ty::ReLateBound(debruijn, br) => { // We only allow a `ty::INNERMOST` index in substitutions. - assert_eq!(*index, ty::INNERMOST); + assert_eq!(*debruijn, ty::INNERMOST); cvar == br.assert_bound_var() } _ => false, diff --git a/src/librustc/util/ppaux.rs b/src/librustc/util/ppaux.rs index 0c424b344159d..eea3b54919d52 100644 --- a/src/librustc/util/ppaux.rs +++ b/src/librustc/util/ppaux.rs @@ -1110,13 +1110,13 @@ define_print! { Infer(infer_ty) => write!(f, "{}", infer_ty), Error => write!(f, "[type error]"), Param(ref param_ty) => write!(f, "{}", param_ty), - Bound(bound_ty) => { + Bound(debruijn, bound_ty) => { match bound_ty.kind { ty::BoundTyKind::Anon => { - if bound_ty.index == ty::INNERMOST { + if debruijn == ty::INNERMOST { write!(f, "^{}", bound_ty.var.index()) } else { - write!(f, "^{}_{}", bound_ty.index.index(), bound_ty.var.index()) + write!(f, "^{}_{}", debruijn.index(), bound_ty.var.index()) } } diff --git a/src/librustc_traits/chalk_context/program_clauses.rs b/src/librustc_traits/chalk_context/program_clauses.rs index 5592d2a583cfb..b8670e5e91436 100644 --- a/src/librustc_traits/chalk_context/program_clauses.rs +++ b/src/librustc_traits/chalk_context/program_clauses.rs @@ -59,7 +59,8 @@ fn assemble_clauses_from_assoc_ty_values<'tcx>( fn program_clauses_for_raw_ptr<'tcx>(tcx: ty::TyCtxt<'_, '_, 'tcx>) -> Clauses<'tcx> { let ty = ty::Bound( - ty::BoundTy::new(ty::INNERMOST, ty::BoundVar::from_u32(0)) + ty::INNERMOST, + ty::BoundVar::from_u32(0).into() ); let ty = tcx.mk_ty(ty); @@ -88,9 +89,9 @@ fn program_clauses_for_fn_ptr<'tcx>( ) -> Clauses<'tcx> { let inputs_and_output = tcx.mk_type_list( (0..arity_and_output).into_iter() + .map(|i| ty::BoundVar::from(i)) // DebruijnIndex(1) because we are going to inject these in a `PolyFnSig` - .map(|i| ty::BoundTy::new(ty::DebruijnIndex::from(1usize), ty::BoundVar::from(i))) - .map(|t| tcx.mk_ty(ty::Bound(t))) + .map(|var| tcx.mk_ty(ty::Bound(ty::DebruijnIndex::from(1usize), var.into()))) ); let fn_sig = ty::Binder::bind(ty::FnSig { @@ -115,7 +116,8 @@ fn program_clauses_for_fn_ptr<'tcx>( fn program_clauses_for_slice<'tcx>(tcx: ty::TyCtxt<'_, '_, 'tcx>) -> Clauses<'tcx> { let ty = ty::Bound( - ty::BoundTy::new(ty::INNERMOST, ty::BoundVar::from_u32(0)) + ty::INNERMOST, + ty::BoundVar::from_u32(0).into() ); let ty = tcx.mk_ty(ty); @@ -151,7 +153,8 @@ fn program_clauses_for_array<'tcx>( length: &'tcx ty::Const<'tcx> ) -> Clauses<'tcx> { let ty = ty::Bound( - ty::BoundTy::new(ty::INNERMOST, ty::BoundVar::from_u32(0)) + ty::INNERMOST, + ty::BoundVar::from_u32(0).into() ); let ty = tcx.mk_ty(ty); @@ -188,8 +191,8 @@ fn program_clauses_for_tuple<'tcx>( ) -> Clauses<'tcx> { let type_list = tcx.mk_type_list( (0..arity).into_iter() - .map(|i| ty::BoundTy::new(ty::INNERMOST, ty::BoundVar::from(i))) - .map(|t| tcx.mk_ty(ty::Bound(t))) + .map(|i| ty::BoundVar::from(i)) + .map(|var| tcx.mk_ty(ty::Bound(ty::INNERMOST, var.into()))) ); let tuple_ty = tcx.mk_ty(ty::Tuple(type_list)); @@ -233,7 +236,7 @@ fn program_clauses_for_ref<'tcx>(tcx: ty::TyCtxt<'_, '_, 'tcx>) -> Clauses<'tcx> ty::ReLateBound(ty::INNERMOST, ty::BoundRegion::BrAnon(0)) ); let ty = tcx.mk_ty( - ty::Bound(ty::BoundTy::new(ty::INNERMOST, ty::BoundVar::from_u32(1))) + ty::Bound(ty::INNERMOST, ty::BoundVar::from_u32(1).into()) ); let ref_ty = tcx.mk_ref(region, ty::TypeAndMut { diff --git a/src/librustc_traits/lowering/environment.rs b/src/librustc_traits/lowering/environment.rs index c3573f47cebfe..519b0ac61058e 100644 --- a/src/librustc_traits/lowering/environment.rs +++ b/src/librustc_traits/lowering/environment.rs @@ -55,7 +55,7 @@ impl ClauseVisitor<'set, 'a, 'tcx> { ty::ReLateBound(ty::INNERMOST, ty::BoundRegion::BrAnon(0)) ); let ty = self.tcx.mk_ty( - ty::Bound(ty::BoundTy::new(ty::INNERMOST, ty::BoundVar::from_u32(1))) + ty::Bound(ty::INNERMOST, ty::BoundVar::from_u32(1).into()) ); let ref_ty = self.tcx.mk_ref(region, ty::TypeAndMut { diff --git a/src/librustc_traits/lowering/mod.rs b/src/librustc_traits/lowering/mod.rs index cf1bc04dd4e01..2d8e5b48aac7b 100644 --- a/src/librustc_traits/lowering/mod.rs +++ b/src/librustc_traits/lowering/mod.rs @@ -515,7 +515,8 @@ pub fn program_clauses_for_associated_type_def<'a, 'tcx>( .unwrap_or(0); // Add a new type param after the existing ones (`U` in the comment above). let ty_var = ty::Bound( - ty::BoundTy::new(ty::INNERMOST, ty::BoundVar::from_u32(offset + 1)) + ty::INNERMOST, + ty::BoundVar::from_u32(offset + 1).into() ); // `ProjectionEq(>::AssocType = U)` From 5b2baa8336ab85f958ac6a8e78e71b490a43e474 Mon Sep 17 00:00:00 2001 From: scalexm Date: Sat, 3 Nov 2018 16:08:50 +0100 Subject: [PATCH 33/88] Implement some instantiate / canonical routines --- src/librustc_traits/chalk_context/mod.rs | 165 ++++++++++++++++------- 1 file changed, 113 insertions(+), 52 deletions(-) diff --git a/src/librustc_traits/chalk_context/mod.rs b/src/librustc_traits/chalk_context/mod.rs index 0fd9f607a5462..14d4be2b1787d 100644 --- a/src/librustc_traits/chalk_context/mod.rs +++ b/src/librustc_traits/chalk_context/mod.rs @@ -11,7 +11,7 @@ mod program_clauses; use chalk_engine::fallible::Fallible as ChalkEngineFallible; -use chalk_engine::{context, hh::HhGoal, DelayedLiteral, ExClause}; +use chalk_engine::{context, hh::HhGoal, DelayedLiteral, Literal, ExClause}; use rustc::infer::canonical::{ Canonical, CanonicalVarValues, OriginalQueryValues, QueryRegionConstraint, QueryResponse, }; @@ -28,7 +28,7 @@ use rustc::traits::{ InEnvironment, }; use rustc::ty::fold::{TypeFoldable, TypeFolder, TypeVisitor}; -use rustc::ty::subst::Kind; +use rustc::ty::subst::{Kind, UnpackedKind}; use rustc::ty::{self, TyCtxt}; use std::fmt::{self, Debug}; @@ -44,7 +44,7 @@ crate struct ChalkArenas<'gcx> { #[derive(Copy, Clone)] crate struct ChalkContext<'cx, 'gcx: 'cx> { _arenas: ChalkArenas<'gcx>, - _tcx: TyCtxt<'cx, 'gcx, 'gcx>, + tcx: TyCtxt<'cx, 'gcx, 'gcx>, } #[derive(Copy, Clone)] @@ -68,7 +68,7 @@ BraceStructTypeFoldableImpl! { } impl context::Context for ChalkArenas<'tcx> { - type CanonicalExClause = Canonical<'tcx, ExClause>; + type CanonicalExClause = Canonical<'tcx, ChalkExClause<'tcx>>; type CanonicalGoalInEnvironment = Canonical<'tcx, InEnvironment<'tcx, Goal<'tcx>>>; @@ -147,19 +147,29 @@ impl context::ContextOps> for ChalkContext<'cx, 'gcx> { /// - the environment and goal found by substitution `S` into `arg` fn instantiate_ucanonical_goal( &self, - _arg: &Canonical<'gcx, InEnvironment<'gcx, Goal<'gcx>>>, - _op: impl context::WithInstantiatedUCanonicalGoal, Output = R>, + arg: &Canonical<'gcx, InEnvironment<'gcx, Goal<'gcx>>>, + op: impl context::WithInstantiatedUCanonicalGoal, Output = R>, ) -> R { - unimplemented!() + self.tcx.infer_ctxt().enter_with_canonical(DUMMY_SP, arg, |ref infcx, arg, subst| { + let chalk_infcx = &mut ChalkInferenceContext { + infcx, + }; + op.with(chalk_infcx, subst, arg.environment, arg.goal) + }) } fn instantiate_ex_clause( &self, _num_universes: usize, - _canonical_ex_clause: &Canonical<'gcx, ChalkExClause<'gcx>>, - _op: impl context::WithInstantiatedExClause, Output = R>, + arg: &Canonical<'gcx, ChalkExClause<'gcx>>, + op: impl context::WithInstantiatedExClause, Output = R>, ) -> R { - unimplemented!() + self.tcx.infer_ctxt().enter_with_canonical(DUMMY_SP, &arg.upcast(), |ref infcx, arg, _| { + let chalk_infcx = &mut ChalkInferenceContext { + infcx, + }; + op.with(chalk_infcx,arg) + }) } /// True if this solution has no region constraints. @@ -186,14 +196,33 @@ impl context::ContextOps> for ChalkContext<'cx, 'gcx> { } fn is_trivial_substitution( - _u_canon: &Canonical<'gcx, InEnvironment<'gcx, Goal<'gcx>>>, - _canonical_subst: &Canonical<'gcx, ConstrainedSubst<'gcx>>, + u_canon: &Canonical<'gcx, InEnvironment<'gcx, Goal<'gcx>>>, + canonical_subst: &Canonical<'tcx, ConstrainedSubst<'tcx>>, ) -> bool { - unimplemented!() - } - - fn num_universes(_: &Canonical<'gcx, InEnvironment<'gcx, Goal<'gcx>>>) -> usize { - 0 // FIXME + let subst = &canonical_subst.value.subst; + assert_eq!(u_canon.variables.len(), subst.var_values.len()); + subst.var_values + .iter_enumerated() + .all(|(cvar, kind)| match kind.unpack() { + UnpackedKind::Lifetime(r) => match r { + &ty::ReLateBound(debruijn, br) => { + debug_assert_eq!(debruijn, ty::INNERMOST); + cvar == br.assert_bound_var() + } + _ => false, + }, + UnpackedKind::Type(ty) => match ty.sty { + ty::Bound(debruijn, bound_ty) => { + debug_assert_eq!(debruijn, ty::INNERMOST); + cvar == bound_ty.var + } + _ => false, + }, + }) + } + + fn num_universes(canon: &Canonical<'gcx, InEnvironment<'gcx, Goal<'gcx>>>) -> usize { + canon.max_universe.index() + 1 } /// Convert a goal G *from* the canonical universes *into* our @@ -214,39 +243,6 @@ impl context::ContextOps> for ChalkContext<'cx, 'gcx> { } } -//impl context::UCanonicalGoalInEnvironment> -// for Canonical<'gcx, ty::ParamEnvAnd<'gcx, Goal<'gcx>>> -//{ -// fn canonical(&self) -> &Canonical<'gcx, ty::ParamEnvAnd<'gcx, Goal<'gcx>>> { -// self -// } -// -// fn is_trivial_substitution( -// &self, -// canonical_subst: &Canonical<'tcx, ConstrainedSubst<'tcx>>, -// ) -> bool { -// let subst = &canonical_subst.value.subst; -// assert_eq!(self.canonical.variables.len(), subst.var_values.len()); -// subst -// .var_values -// .iter_enumerated() -// .all(|(cvar, kind)| match kind.unpack() { -// Kind::Lifetime(r) => match r { -// ty::ReCanonical(cvar1) => cvar == cvar1, -// _ => false, -// }, -// Kind::Type(ty) => match ty.sty { -// ty::Infer(ty::InferTy::CanonicalTy(cvar1)) => cvar == cvar1, -// _ => false, -// }, -// }) -// } -// -// fn num_universes(&self) -> usize { -// 0 // FIXME -// } -//} - impl context::InferenceTable, ChalkArenas<'tcx>> for ChalkInferenceContext<'cx, 'gcx, 'tcx> { @@ -338,9 +334,9 @@ impl context::UnificationOps, ChalkArenas<'tcx>> fn instantiate_binders_universally( &mut self, - _arg: &ty::Binder>, + arg: &ty::Binder>, ) -> Goal<'tcx> { - panic!("FIXME -- universal instantiation needs sgrif's branch") + self.infcx.replace_bound_vars_with_placeholders(arg).0 } fn instantiate_binders_existentially( @@ -491,3 +487,68 @@ BraceStructLiftImpl! { subst, constraints } } + +trait Upcast<'tcx, 'gcx: 'tcx>: 'gcx { + type Upcasted: 'tcx; + + fn upcast(&self) -> Self::Upcasted; +} + +impl<'tcx, 'gcx: 'tcx> Upcast<'tcx, 'gcx> for DelayedLiteral> { + type Upcasted = DelayedLiteral>; + + fn upcast(&self) -> Self::Upcasted { + match self { + &DelayedLiteral::CannotProve(..) => DelayedLiteral::CannotProve(()), + &DelayedLiteral::Negative(index) => DelayedLiteral::Negative(index), + DelayedLiteral::Positive(index, subst) => DelayedLiteral::Positive( + *index, + subst.clone() + ), + } + } +} + +impl<'tcx, 'gcx: 'tcx> Upcast<'tcx, 'gcx> for Literal> { + type Upcasted = Literal>; + + fn upcast(&self) -> Self::Upcasted { + match self { + &Literal::Negative(goal) => Literal::Negative(goal), + &Literal::Positive(goal) => Literal::Positive(goal), + } + } +} + +impl<'tcx, 'gcx: 'tcx> Upcast<'tcx, 'gcx> for ExClause> { + type Upcasted = ExClause>; + + fn upcast(&self) -> Self::Upcasted { + ExClause { + subst: self.subst.clone(), + delayed_literals: self.delayed_literals + .iter() + .map(|l| l.upcast()) + .collect(), + constraints: self.constraints.clone(), + subgoals: self.subgoals + .iter() + .map(|g| g.upcast()) + .collect(), + } + } +} + +impl<'tcx, 'gcx: 'tcx, T> Upcast<'tcx, 'gcx> for Canonical<'gcx, T> + where T: Upcast<'tcx, 'gcx> +{ + type Upcasted = Canonical<'tcx, T::Upcasted>; + + fn upcast(&self) -> Self::Upcasted { + Canonical { + max_universe: self.max_universe, + value: self.value.upcast(), + variables: self.variables, + } + } +} From c0f98e83906453e0f563a6f925f5c6e14f93a0ba Mon Sep 17 00:00:00 2001 From: scalexm Date: Mon, 5 Nov 2018 18:08:47 +0100 Subject: [PATCH 34/88] Fix `ChalkInferenceContext::into_hh_goal` --- src/librustc_traits/chalk_context/mod.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/librustc_traits/chalk_context/mod.rs b/src/librustc_traits/chalk_context/mod.rs index 14d4be2b1787d..25a6af284b572 100644 --- a/src/librustc_traits/chalk_context/mod.rs +++ b/src/librustc_traits/chalk_context/mod.rs @@ -256,7 +256,10 @@ impl context::InferenceTable, ChalkArenas<'tcx>> fn into_hh_goal(&mut self, goal: Goal<'tcx>) -> ChalkHhGoal<'tcx> { match *goal { - GoalKind::Implies(..) => panic!("FIXME rust-lang-nursery/chalk#94"), + GoalKind::Implies(hypotheses, goal) => HhGoal::Implies( + hypotheses.iter().cloned().collect(), + goal + ), GoalKind::And(left, right) => HhGoal::And(left, right), GoalKind::Not(subgoal) => HhGoal::Not(subgoal), GoalKind::DomainGoal(d) => HhGoal::DomainGoal(d), From 4478dced472dec4f516cc4ef553a1476e40c09b8 Mon Sep 17 00:00:00 2001 From: scalexm Date: Wed, 14 Nov 2018 12:50:26 +0100 Subject: [PATCH 35/88] Fix NLL ui test --- src/test/ui/nll/user-annotations/dump-fn-method.stderr | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/ui/nll/user-annotations/dump-fn-method.stderr b/src/test/ui/nll/user-annotations/dump-fn-method.stderr index 359423d0cfbea..c963625c961e8 100644 --- a/src/test/ui/nll/user-annotations/dump-fn-method.stderr +++ b/src/test/ui/nll/user-annotations/dump-fn-method.stderr @@ -4,7 +4,7 @@ error: user substs: Canonical { max_universe: U0, variables: [], value: UserSubs LL | let x = foo::; //~ ERROR [u32] | ^^^^^^^^^^ -error: user substs: Canonical { max_universe: U0, variables: [CanonicalVarInfo { kind: Ty(General) }, CanonicalVarInfo { kind: Ty(General) }], value: UserSubsts { substs: [^0, u32, ^1], user_self_ty: None } } +error: user substs: Canonical { max_universe: U0, variables: [CanonicalVarInfo { kind: Ty(General(U0)) }, CanonicalVarInfo { kind: Ty(General(U0)) }], value: UserSubsts { substs: [^0, u32, ^1], user_self_ty: None } } --> $DIR/dump-fn-method.rs:42:13 | LL | let x = <_ as Bazoom>::method::<_>; //~ ERROR [^0, u32, ^1] @@ -16,7 +16,7 @@ error: user substs: Canonical { max_universe: U0, variables: [], value: UserSubs LL | let x = >::method::; //~ ERROR [u8, u16, u32] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: user substs: Canonical { max_universe: U0, variables: [CanonicalVarInfo { kind: Ty(General) }, CanonicalVarInfo { kind: Ty(General) }], value: UserSubsts { substs: [^0, ^1, u32], user_self_ty: None } } +error: user substs: Canonical { max_universe: U1, variables: [CanonicalVarInfo { kind: Ty(General(U1)) }, CanonicalVarInfo { kind: Ty(General(U1)) }], value: UserSubsts { substs: [^0, ^1, u32], user_self_ty: None } } --> $DIR/dump-fn-method.rs:54:5 | LL | y.method::(44, 66); //~ ERROR [^0, ^1, u32] From 98cd2ad4ea84408ecd9d064f4bd90fe406fb7ddd Mon Sep 17 00:00:00 2001 From: Oliver Scherer Date: Mon, 12 Nov 2018 08:30:52 +0100 Subject: [PATCH 36/88] Move some methods from `Memory` to `Allocation` --- src/librustc/mir/interpret/allocation.rs | 84 ++++++++++++++++++++++++ src/librustc_mir/interpret/memory.rs | 84 ------------------------ 2 files changed, 84 insertions(+), 84 deletions(-) diff --git a/src/librustc/mir/interpret/allocation.rs b/src/librustc/mir/interpret/allocation.rs index 3250ea266a587..1a88a959b4eb0 100644 --- a/src/librustc/mir/interpret/allocation.rs +++ b/src/librustc/mir/interpret/allocation.rs @@ -49,6 +49,90 @@ pub struct Allocation { pub extra: Extra, } +/// Byte accessors +impl<'tcx, Tag, Extra> Allocation { + /// The last argument controls whether we error out when there are undefined + /// or pointer bytes. You should never call this, call `get_bytes` or + /// `get_bytes_with_undef_and_ptr` instead, + /// + /// This function also guarantees that the resulting pointer will remain stable + /// even when new allocations are pushed to the `HashMap`. `copy_repeatedly` relies + /// on that. + fn get_bytes_internal( + &self, + ptr: Pointer, + size: Size, + align: Align, + check_defined_and_ptr: bool, + ) -> EvalResult<'tcx, &[u8]> { + assert_ne!(size.bytes(), 0, "0-sized accesses should never even get a `Pointer`"); + self.check_align(ptr.into(), align)?; + self.check_bounds(ptr, size, InboundsCheck::Live)?; + + if check_defined_and_ptr { + self.check_defined(ptr, size)?; + self.check_relocations(ptr, size)?; + } else { + // We still don't want relocations on the *edges* + self.check_relocation_edges(ptr, size)?; + } + + let alloc = self.get(ptr.alloc_id)?; + AllocationExtra::memory_read(alloc, ptr, size)?; + + assert_eq!(ptr.offset.bytes() as usize as u64, ptr.offset.bytes()); + assert_eq!(size.bytes() as usize as u64, size.bytes()); + let offset = ptr.offset.bytes() as usize; + Ok(&alloc.bytes[offset..offset + size.bytes() as usize]) + } + + #[inline] + fn get_bytes( + &self, + ptr: Pointer, + size: Size, + align: Align + ) -> EvalResult<'tcx, &[u8]> { + self.get_bytes_internal(ptr, size, align, true) + } + + /// It is the caller's responsibility to handle undefined and pointer bytes. + /// However, this still checks that there are no relocations on the *edges*. + #[inline] + fn get_bytes_with_undef_and_ptr( + &self, + ptr: Pointer, + size: Size, + align: Align + ) -> EvalResult<'tcx, &[u8]> { + self.get_bytes_internal(ptr, size, align, false) + } + + /// Just calling this already marks everything as defined and removes relocations, + /// so be sure to actually put data there! + fn get_bytes_mut( + &mut self, + ptr: Pointer, + size: Size, + align: Align, + ) -> EvalResult<'tcx, &mut [u8]> { + assert_ne!(size.bytes(), 0, "0-sized accesses should never even get a `Pointer`"); + self.check_align(ptr.into(), align)?; + self.check_bounds(ptr, size, InboundsCheck::Live)?; + + self.mark_definedness(ptr, size, true)?; + self.clear_relocations(ptr, size)?; + + let alloc = self.get_mut(ptr.alloc_id)?; + AllocationExtra::memory_written(alloc, ptr, size)?; + + assert_eq!(ptr.offset.bytes() as usize as u64, ptr.offset.bytes()); + assert_eq!(size.bytes() as usize as u64, size.bytes()); + let offset = ptr.offset.bytes() as usize; + Ok(&mut alloc.bytes[offset..offset + size.bytes() as usize]) + } +} + pub trait AllocationExtra: ::std::fmt::Debug + Default + Clone { /// Hook for performing extra checks on a memory read access. /// diff --git a/src/librustc_mir/interpret/memory.rs b/src/librustc_mir/interpret/memory.rs index 898600d8322d2..759892451bd94 100644 --- a/src/librustc_mir/interpret/memory.rs +++ b/src/librustc_mir/interpret/memory.rs @@ -609,90 +609,6 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> { } } -/// Byte accessors -impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> { - /// The last argument controls whether we error out when there are undefined - /// or pointer bytes. You should never call this, call `get_bytes` or - /// `get_bytes_with_undef_and_ptr` instead, - /// - /// This function also guarantees that the resulting pointer will remain stable - /// even when new allocations are pushed to the `HashMap`. `copy_repeatedly` relies - /// on that. - fn get_bytes_internal( - &self, - ptr: Pointer, - size: Size, - align: Align, - check_defined_and_ptr: bool, - ) -> EvalResult<'tcx, &[u8]> { - assert_ne!(size.bytes(), 0, "0-sized accesses should never even get a `Pointer`"); - self.check_align(ptr.into(), align)?; - self.check_bounds(ptr, size, InboundsCheck::Live)?; - - if check_defined_and_ptr { - self.check_defined(ptr, size)?; - self.check_relocations(ptr, size)?; - } else { - // We still don't want relocations on the *edges* - self.check_relocation_edges(ptr, size)?; - } - - let alloc = self.get(ptr.alloc_id)?; - AllocationExtra::memory_read(alloc, ptr, size)?; - - assert_eq!(ptr.offset.bytes() as usize as u64, ptr.offset.bytes()); - assert_eq!(size.bytes() as usize as u64, size.bytes()); - let offset = ptr.offset.bytes() as usize; - Ok(&alloc.bytes[offset..offset + size.bytes() as usize]) - } - - #[inline] - fn get_bytes( - &self, - ptr: Pointer, - size: Size, - align: Align - ) -> EvalResult<'tcx, &[u8]> { - self.get_bytes_internal(ptr, size, align, true) - } - - /// It is the caller's responsibility to handle undefined and pointer bytes. - /// However, this still checks that there are no relocations on the *edges*. - #[inline] - fn get_bytes_with_undef_and_ptr( - &self, - ptr: Pointer, - size: Size, - align: Align - ) -> EvalResult<'tcx, &[u8]> { - self.get_bytes_internal(ptr, size, align, false) - } - - /// Just calling this already marks everything as defined and removes relocations, - /// so be sure to actually put data there! - fn get_bytes_mut( - &mut self, - ptr: Pointer, - size: Size, - align: Align, - ) -> EvalResult<'tcx, &mut [u8]> { - assert_ne!(size.bytes(), 0, "0-sized accesses should never even get a `Pointer`"); - self.check_align(ptr.into(), align)?; - self.check_bounds(ptr, size, InboundsCheck::Live)?; - - self.mark_definedness(ptr, size, true)?; - self.clear_relocations(ptr, size)?; - - let alloc = self.get_mut(ptr.alloc_id)?; - AllocationExtra::memory_written(alloc, ptr, size)?; - - assert_eq!(ptr.offset.bytes() as usize as u64, ptr.offset.bytes()); - assert_eq!(size.bytes() as usize as u64, size.bytes()); - let offset = ptr.offset.bytes() as usize; - Ok(&mut alloc.bytes[offset..offset + size.bytes() as usize]) - } -} - /// Interning (for CTFE) impl<'a, 'mir, 'tcx, M> Memory<'a, 'mir, 'tcx, M> where From d40a7713d3f044dc6f590acfc55f74f44f8295e8 Mon Sep 17 00:00:00 2001 From: Oliver Scherer Date: Mon, 12 Nov 2018 08:32:30 +0100 Subject: [PATCH 37/88] Preliminary code adjustment to let the compiler complain about missing methods --- src/librustc/mir/interpret/allocation.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/librustc/mir/interpret/allocation.rs b/src/librustc/mir/interpret/allocation.rs index 1a88a959b4eb0..669302852d451 100644 --- a/src/librustc/mir/interpret/allocation.rs +++ b/src/librustc/mir/interpret/allocation.rs @@ -60,7 +60,7 @@ impl<'tcx, Tag, Extra> Allocation { /// on that. fn get_bytes_internal( &self, - ptr: Pointer, + ptr: Pointer, size: Size, align: Align, check_defined_and_ptr: bool, @@ -89,7 +89,7 @@ impl<'tcx, Tag, Extra> Allocation { #[inline] fn get_bytes( &self, - ptr: Pointer, + ptr: Pointer, size: Size, align: Align ) -> EvalResult<'tcx, &[u8]> { @@ -101,7 +101,7 @@ impl<'tcx, Tag, Extra> Allocation { #[inline] fn get_bytes_with_undef_and_ptr( &self, - ptr: Pointer, + ptr: Pointer, size: Size, align: Align ) -> EvalResult<'tcx, &[u8]> { @@ -112,7 +112,7 @@ impl<'tcx, Tag, Extra> Allocation { /// so be sure to actually put data there! fn get_bytes_mut( &mut self, - ptr: Pointer, + ptr: Pointer, size: Size, align: Align, ) -> EvalResult<'tcx, &mut [u8]> { From eb30ce8acb6617fbe6182a3b5b9078dea4027a90 Mon Sep 17 00:00:00 2001 From: Oliver Scherer Date: Mon, 12 Nov 2018 08:34:04 +0100 Subject: [PATCH 38/88] Move relocation methods from `Memory` to `Allocation` --- src/librustc/mir/interpret/allocation.rs | 73 ++++++++++++++++++++++++ src/librustc_mir/interpret/memory.rs | 73 ------------------------ 2 files changed, 73 insertions(+), 73 deletions(-) diff --git a/src/librustc/mir/interpret/allocation.rs b/src/librustc/mir/interpret/allocation.rs index 669302852d451..2334ca2f91a9a 100644 --- a/src/librustc/mir/interpret/allocation.rs +++ b/src/librustc/mir/interpret/allocation.rs @@ -133,6 +133,79 @@ impl<'tcx, Tag, Extra> Allocation { } } +/// Relocations +impl<'tcx, Tag, Extra> Allocation { + /// Return all relocations overlapping with the given ptr-offset pair. + fn relocations( + &self, + ptr: Pointer, + size: Size, + ) -> EvalResult<'tcx, &[(Size, (M::PointerTag, AllocId))]> { + // We have to go back `pointer_size - 1` bytes, as that one would still overlap with + // the beginning of this range. + let start = ptr.offset.bytes().saturating_sub(self.pointer_size().bytes() - 1); + let end = ptr.offset + size; // this does overflow checking + Ok(self.get(ptr.alloc_id)?.relocations.range(Size::from_bytes(start)..end)) + } + + /// Check that there ar eno relocations overlapping with the given range. + #[inline(always)] + fn check_relocations(&self, ptr: Pointer, size: Size) -> EvalResult<'tcx> { + if self.relocations(ptr, size)?.len() != 0 { + err!(ReadPointerAsBytes) + } else { + Ok(()) + } + } + + /// Remove all relocations inside the given range. + /// If there are relocations overlapping with the edges, they + /// are removed as well *and* the bytes they cover are marked as + /// uninitialized. This is a somewhat odd "spooky action at a distance", + /// but it allows strictly more code to run than if we would just error + /// immediately in that case. + fn clear_relocations(&mut self, ptr: Pointer, size: Size) -> EvalResult<'tcx> { + // Find the start and end of the given range and its outermost relocations. + let (first, last) = { + // Find all relocations overlapping the given range. + let relocations = self.relocations(ptr, size)?; + if relocations.is_empty() { + return Ok(()); + } + + (relocations.first().unwrap().0, + relocations.last().unwrap().0 + self.pointer_size()) + }; + let start = ptr.offset; + let end = start + size; + + let alloc = self.get_mut(ptr.alloc_id)?; + + // Mark parts of the outermost relocations as undefined if they partially fall outside the + // given range. + if first < start { + alloc.undef_mask.set_range(first, start, false); + } + if last > end { + alloc.undef_mask.set_range(end, last, false); + } + + // Forget all the relocations. + alloc.relocations.remove_range(first..last); + + Ok(()) + } + + /// Error if there are relocations overlapping with the edges of the + /// given memory range. + #[inline] + fn check_relocation_edges(&self, ptr: Pointer, size: Size) -> EvalResult<'tcx> { + self.check_relocations(ptr, Size::ZERO)?; + self.check_relocations(ptr.offset(size, self)?, Size::ZERO)?; + Ok(()) + } +} + pub trait AllocationExtra: ::std::fmt::Debug + Default + Clone { /// Hook for performing extra checks on a memory read access. /// diff --git a/src/librustc_mir/interpret/memory.rs b/src/librustc_mir/interpret/memory.rs index 759892451bd94..cb52e595e02be 100644 --- a/src/librustc_mir/interpret/memory.rs +++ b/src/librustc_mir/interpret/memory.rs @@ -955,79 +955,6 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> { } } -/// Relocations -impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> { - /// Return all relocations overlapping with the given ptr-offset pair. - fn relocations( - &self, - ptr: Pointer, - size: Size, - ) -> EvalResult<'tcx, &[(Size, (M::PointerTag, AllocId))]> { - // We have to go back `pointer_size - 1` bytes, as that one would still overlap with - // the beginning of this range. - let start = ptr.offset.bytes().saturating_sub(self.pointer_size().bytes() - 1); - let end = ptr.offset + size; // this does overflow checking - Ok(self.get(ptr.alloc_id)?.relocations.range(Size::from_bytes(start)..end)) - } - - /// Check that there ar eno relocations overlapping with the given range. - #[inline(always)] - fn check_relocations(&self, ptr: Pointer, size: Size) -> EvalResult<'tcx> { - if self.relocations(ptr, size)?.len() != 0 { - err!(ReadPointerAsBytes) - } else { - Ok(()) - } - } - - /// Remove all relocations inside the given range. - /// If there are relocations overlapping with the edges, they - /// are removed as well *and* the bytes they cover are marked as - /// uninitialized. This is a somewhat odd "spooky action at a distance", - /// but it allows strictly more code to run than if we would just error - /// immediately in that case. - fn clear_relocations(&mut self, ptr: Pointer, size: Size) -> EvalResult<'tcx> { - // Find the start and end of the given range and its outermost relocations. - let (first, last) = { - // Find all relocations overlapping the given range. - let relocations = self.relocations(ptr, size)?; - if relocations.is_empty() { - return Ok(()); - } - - (relocations.first().unwrap().0, - relocations.last().unwrap().0 + self.pointer_size()) - }; - let start = ptr.offset; - let end = start + size; - - let alloc = self.get_mut(ptr.alloc_id)?; - - // Mark parts of the outermost relocations as undefined if they partially fall outside the - // given range. - if first < start { - alloc.undef_mask.set_range(first, start, false); - } - if last > end { - alloc.undef_mask.set_range(end, last, false); - } - - // Forget all the relocations. - alloc.relocations.remove_range(first..last); - - Ok(()) - } - - /// Error if there are relocations overlapping with the edges of the - /// given memory range. - #[inline] - fn check_relocation_edges(&self, ptr: Pointer, size: Size) -> EvalResult<'tcx> { - self.check_relocations(ptr, Size::ZERO)?; - self.check_relocations(ptr.offset(size, self)?, Size::ZERO)?; - Ok(()) - } -} - /// Undefined bytes impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> { // FIXME: Add a fast version for the common, nonoverlapping case From d98c46ce57d562ebcfb01ec814ff8d90d47ff7ea Mon Sep 17 00:00:00 2001 From: Oliver Scherer Date: Mon, 12 Nov 2018 08:35:32 +0100 Subject: [PATCH 39/88] Move undef mask methods from `Memory` to `Allocation` --- src/librustc/mir/interpret/allocation.rs | 33 ++++++++++++++++++++++++ src/librustc_mir/interpret/memory.rs | 29 --------------------- 2 files changed, 33 insertions(+), 29 deletions(-) diff --git a/src/librustc/mir/interpret/allocation.rs b/src/librustc/mir/interpret/allocation.rs index 2334ca2f91a9a..ad4bf415b8de0 100644 --- a/src/librustc/mir/interpret/allocation.rs +++ b/src/librustc/mir/interpret/allocation.rs @@ -206,6 +206,39 @@ impl<'tcx, Tag, Extra> Allocation { } } + +/// Undefined bytes +impl<'tcx, Tag, Extra> Allocation { + /// Checks that a range of bytes is defined. If not, returns the `ReadUndefBytes` + /// error which will report the first byte which is undefined. + #[inline] + fn check_defined(&self, ptr: Pointer, size: Size) -> EvalResult<'tcx> { + let alloc = self.get(ptr.alloc_id)?; + alloc.undef_mask.is_range_defined( + ptr.offset, + ptr.offset + size, + ).or_else(|idx| err!(ReadUndefBytes(idx))) + } + + pub fn mark_definedness( + &mut self, + ptr: Pointer, + size: Size, + new_state: bool, + ) -> EvalResult<'tcx> { + if size.bytes() == 0 { + return Ok(()); + } + let alloc = self.get_mut(ptr.alloc_id)?; + alloc.undef_mask.set_range( + ptr.offset, + ptr.offset + size, + new_state, + ); + Ok(()) + } +} + pub trait AllocationExtra: ::std::fmt::Debug + Default + Clone { /// Hook for performing extra checks on a memory read access. /// diff --git a/src/librustc_mir/interpret/memory.rs b/src/librustc_mir/interpret/memory.rs index cb52e595e02be..d8ae107a22b56 100644 --- a/src/librustc_mir/interpret/memory.rs +++ b/src/librustc_mir/interpret/memory.rs @@ -984,33 +984,4 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> { Ok(()) } - - /// Checks that a range of bytes is defined. If not, returns the `ReadUndefBytes` - /// error which will report the first byte which is undefined. - #[inline] - fn check_defined(&self, ptr: Pointer, size: Size) -> EvalResult<'tcx> { - let alloc = self.get(ptr.alloc_id)?; - alloc.undef_mask.is_range_defined( - ptr.offset, - ptr.offset + size, - ).or_else(|idx| err!(ReadUndefBytes(idx))) - } - - pub fn mark_definedness( - &mut self, - ptr: Pointer, - size: Size, - new_state: bool, - ) -> EvalResult<'tcx> { - if size.bytes() == 0 { - return Ok(()); - } - let alloc = self.get_mut(ptr.alloc_id)?; - alloc.undef_mask.set_range( - ptr.offset, - ptr.offset + size, - new_state, - ); - Ok(()) - } } From 7c9d786e5007004917d73749fad32bc3bff94cce Mon Sep 17 00:00:00 2001 From: Oliver Scherer Date: Mon, 12 Nov 2018 08:37:54 +0100 Subject: [PATCH 40/88] Move alignment and bounds check from `Memory` to `Allocation` --- src/librustc/mir/interpret/allocation.rs | 45 ++++++++++++++++++++++++ src/librustc_mir/interpret/memory.rs | 42 ---------------------- 2 files changed, 45 insertions(+), 42 deletions(-) diff --git a/src/librustc/mir/interpret/allocation.rs b/src/librustc/mir/interpret/allocation.rs index ad4bf415b8de0..b2737ae203fba 100644 --- a/src/librustc/mir/interpret/allocation.rs +++ b/src/librustc/mir/interpret/allocation.rs @@ -49,6 +49,51 @@ pub struct Allocation { pub extra: Extra, } +/// Alignment and bounds checks +impl<'tcx, Tag, Extra> Allocation { + /// Check if the pointer is "in-bounds". Notice that a pointer pointing at the end + /// of an allocation (i.e., at the first *inaccessible* location) *is* considered + /// in-bounds! This follows C's/LLVM's rules. `check` indicates whether we + /// additionally require the pointer to be pointing to a *live* (still allocated) + /// allocation. + /// If you want to check bounds before doing a memory access, better use `check_bounds`. + pub fn check_bounds_ptr( + &self, + ptr: Pointer, + check: InboundsCheck, + ) -> EvalResult<'tcx> { + let allocation_size = match check { + InboundsCheck::Live => { + let alloc = self.get(ptr.alloc_id)?; + alloc.bytes.len() as u64 + } + InboundsCheck::MaybeDead => { + self.get_size_and_align(ptr.alloc_id).0.bytes() + } + }; + if ptr.offset.bytes() > allocation_size { + return err!(PointerOutOfBounds { + ptr: ptr.erase_tag(), + check, + allocation_size: Size::from_bytes(allocation_size), + }); + } + Ok(()) + } + + /// Check if the memory range beginning at `ptr` and of size `Size` is "in-bounds". + #[inline(always)] + pub fn check_bounds( + &self, + ptr: Pointer, + size: Size, + check: InboundsCheck, + ) -> EvalResult<'tcx> { + // if ptr.offset is in bounds, then so is ptr (because offset checks for overflow) + self.check_bounds_ptr(ptr.offset(size, &*self)?, check) + } +} + /// Byte accessors impl<'tcx, Tag, Extra> Allocation { /// The last argument controls whether we error out when there are undefined diff --git a/src/librustc_mir/interpret/memory.rs b/src/librustc_mir/interpret/memory.rs index d8ae107a22b56..9f8e8fc921af8 100644 --- a/src/librustc_mir/interpret/memory.rs +++ b/src/librustc_mir/interpret/memory.rs @@ -284,48 +284,6 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> { }) } } - - /// Check if the pointer is "in-bounds". Notice that a pointer pointing at the end - /// of an allocation (i.e., at the first *inaccessible* location) *is* considered - /// in-bounds! This follows C's/LLVM's rules. `check` indicates whether we - /// additionally require the pointer to be pointing to a *live* (still allocated) - /// allocation. - /// If you want to check bounds before doing a memory access, better use `check_bounds`. - pub fn check_bounds_ptr( - &self, - ptr: Pointer, - check: InboundsCheck, - ) -> EvalResult<'tcx> { - let allocation_size = match check { - InboundsCheck::Live => { - let alloc = self.get(ptr.alloc_id)?; - alloc.bytes.len() as u64 - } - InboundsCheck::MaybeDead => { - self.get_size_and_align(ptr.alloc_id).0.bytes() - } - }; - if ptr.offset.bytes() > allocation_size { - return err!(PointerOutOfBounds { - ptr: ptr.erase_tag(), - check, - allocation_size: Size::from_bytes(allocation_size), - }); - } - Ok(()) - } - - /// Check if the memory range beginning at `ptr` and of size `Size` is "in-bounds". - #[inline(always)] - pub fn check_bounds( - &self, - ptr: Pointer, - size: Size, - check: InboundsCheck, - ) -> EvalResult<'tcx> { - // if ptr.offset is in bounds, then so is ptr (because offset checks for overflow) - self.check_bounds_ptr(ptr.offset(size, &*self)?, check) - } } /// Allocation accessors From c392fbbbf16981dc6368de7ea6a8797892221283 Mon Sep 17 00:00:00 2001 From: Oliver Scherer Date: Mon, 12 Nov 2018 08:38:35 +0100 Subject: [PATCH 41/88] Adjust generics to `Allocation` parameters --- src/librustc/mir/interpret/allocation.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/librustc/mir/interpret/allocation.rs b/src/librustc/mir/interpret/allocation.rs index b2737ae203fba..05f9f482d5327 100644 --- a/src/librustc/mir/interpret/allocation.rs +++ b/src/librustc/mir/interpret/allocation.rs @@ -59,7 +59,7 @@ impl<'tcx, Tag, Extra> Allocation { /// If you want to check bounds before doing a memory access, better use `check_bounds`. pub fn check_bounds_ptr( &self, - ptr: Pointer, + ptr: Pointer, check: InboundsCheck, ) -> EvalResult<'tcx> { let allocation_size = match check { @@ -85,7 +85,7 @@ impl<'tcx, Tag, Extra> Allocation { #[inline(always)] pub fn check_bounds( &self, - ptr: Pointer, + ptr: Pointer, size: Size, check: InboundsCheck, ) -> EvalResult<'tcx> { @@ -183,9 +183,9 @@ impl<'tcx, Tag, Extra> Allocation { /// Return all relocations overlapping with the given ptr-offset pair. fn relocations( &self, - ptr: Pointer, + ptr: Pointer, size: Size, - ) -> EvalResult<'tcx, &[(Size, (M::PointerTag, AllocId))]> { + ) -> EvalResult<'tcx, &[(Size, (Tag, AllocId))]> { // We have to go back `pointer_size - 1` bytes, as that one would still overlap with // the beginning of this range. let start = ptr.offset.bytes().saturating_sub(self.pointer_size().bytes() - 1); @@ -195,7 +195,7 @@ impl<'tcx, Tag, Extra> Allocation { /// Check that there ar eno relocations overlapping with the given range. #[inline(always)] - fn check_relocations(&self, ptr: Pointer, size: Size) -> EvalResult<'tcx> { + fn check_relocations(&self, ptr: Pointer, size: Size) -> EvalResult<'tcx> { if self.relocations(ptr, size)?.len() != 0 { err!(ReadPointerAsBytes) } else { @@ -209,7 +209,7 @@ impl<'tcx, Tag, Extra> Allocation { /// uninitialized. This is a somewhat odd "spooky action at a distance", /// but it allows strictly more code to run than if we would just error /// immediately in that case. - fn clear_relocations(&mut self, ptr: Pointer, size: Size) -> EvalResult<'tcx> { + fn clear_relocations(&mut self, ptr: Pointer, size: Size) -> EvalResult<'tcx> { // Find the start and end of the given range and its outermost relocations. let (first, last) = { // Find all relocations overlapping the given range. @@ -244,7 +244,7 @@ impl<'tcx, Tag, Extra> Allocation { /// Error if there are relocations overlapping with the edges of the /// given memory range. #[inline] - fn check_relocation_edges(&self, ptr: Pointer, size: Size) -> EvalResult<'tcx> { + fn check_relocation_edges(&self, ptr: Pointer, size: Size) -> EvalResult<'tcx> { self.check_relocations(ptr, Size::ZERO)?; self.check_relocations(ptr.offset(size, self)?, Size::ZERO)?; Ok(()) @@ -257,7 +257,7 @@ impl<'tcx, Tag, Extra> Allocation { /// Checks that a range of bytes is defined. If not, returns the `ReadUndefBytes` /// error which will report the first byte which is undefined. #[inline] - fn check_defined(&self, ptr: Pointer, size: Size) -> EvalResult<'tcx> { + fn check_defined(&self, ptr: Pointer, size: Size) -> EvalResult<'tcx> { let alloc = self.get(ptr.alloc_id)?; alloc.undef_mask.is_range_defined( ptr.offset, @@ -267,7 +267,7 @@ impl<'tcx, Tag, Extra> Allocation { pub fn mark_definedness( &mut self, - ptr: Pointer, + ptr: Pointer, size: Size, new_state: bool, ) -> EvalResult<'tcx> { From 04210f3e169b31613f70e5de9550a4f46039e7bc Mon Sep 17 00:00:00 2001 From: Oliver Scherer Date: Mon, 12 Nov 2018 08:39:04 +0100 Subject: [PATCH 42/88] Access `self` instead of `alloc` --- src/librustc/mir/interpret/allocation.rs | 44 ++++++++---------------- 1 file changed, 14 insertions(+), 30 deletions(-) diff --git a/src/librustc/mir/interpret/allocation.rs b/src/librustc/mir/interpret/allocation.rs index 05f9f482d5327..aba0da32d4544 100644 --- a/src/librustc/mir/interpret/allocation.rs +++ b/src/librustc/mir/interpret/allocation.rs @@ -60,21 +60,12 @@ impl<'tcx, Tag, Extra> Allocation { pub fn check_bounds_ptr( &self, ptr: Pointer, - check: InboundsCheck, ) -> EvalResult<'tcx> { - let allocation_size = match check { - InboundsCheck::Live => { - let alloc = self.get(ptr.alloc_id)?; - alloc.bytes.len() as u64 - } - InboundsCheck::MaybeDead => { - self.get_size_and_align(ptr.alloc_id).0.bytes() - } - }; + let allocation_size = self.bytes.len() as u64; if ptr.offset.bytes() > allocation_size { return err!(PointerOutOfBounds { ptr: ptr.erase_tag(), - check, + check: InboundsCheck::Live, allocation_size: Size::from_bytes(allocation_size), }); } @@ -87,15 +78,14 @@ impl<'tcx, Tag, Extra> Allocation { &self, ptr: Pointer, size: Size, - check: InboundsCheck, ) -> EvalResult<'tcx> { // if ptr.offset is in bounds, then so is ptr (because offset checks for overflow) - self.check_bounds_ptr(ptr.offset(size, &*self)?, check) + self.check_bounds_ptr(ptr.offset(size, &*self)?) } } /// Byte accessors -impl<'tcx, Tag, Extra> Allocation { +impl<'tcx, Tag, Extra: AllocationExtra> Allocation { /// The last argument controls whether we error out when there are undefined /// or pointer bytes. You should never call this, call `get_bytes` or /// `get_bytes_with_undef_and_ptr` instead, @@ -122,13 +112,12 @@ impl<'tcx, Tag, Extra> Allocation { self.check_relocation_edges(ptr, size)?; } - let alloc = self.get(ptr.alloc_id)?; - AllocationExtra::memory_read(alloc, ptr, size)?; + AllocationExtra::memory_read(self, ptr, size)?; assert_eq!(ptr.offset.bytes() as usize as u64, ptr.offset.bytes()); assert_eq!(size.bytes() as usize as u64, size.bytes()); let offset = ptr.offset.bytes() as usize; - Ok(&alloc.bytes[offset..offset + size.bytes() as usize]) + Ok(&self.bytes[offset..offset + size.bytes() as usize]) } #[inline] @@ -168,13 +157,12 @@ impl<'tcx, Tag, Extra> Allocation { self.mark_definedness(ptr, size, true)?; self.clear_relocations(ptr, size)?; - let alloc = self.get_mut(ptr.alloc_id)?; - AllocationExtra::memory_written(alloc, ptr, size)?; + AllocationExtra::memory_written(self, ptr, size)?; assert_eq!(ptr.offset.bytes() as usize as u64, ptr.offset.bytes()); assert_eq!(size.bytes() as usize as u64, size.bytes()); let offset = ptr.offset.bytes() as usize; - Ok(&mut alloc.bytes[offset..offset + size.bytes() as usize]) + Ok(&mut self.bytes[offset..offset + size.bytes() as usize]) } } @@ -190,7 +178,7 @@ impl<'tcx, Tag, Extra> Allocation { // the beginning of this range. let start = ptr.offset.bytes().saturating_sub(self.pointer_size().bytes() - 1); let end = ptr.offset + size; // this does overflow checking - Ok(self.get(ptr.alloc_id)?.relocations.range(Size::from_bytes(start)..end)) + Ok(self.relocations.range(Size::from_bytes(start)..end)) } /// Check that there ar eno relocations overlapping with the given range. @@ -224,19 +212,17 @@ impl<'tcx, Tag, Extra> Allocation { let start = ptr.offset; let end = start + size; - let alloc = self.get_mut(ptr.alloc_id)?; - // Mark parts of the outermost relocations as undefined if they partially fall outside the // given range. if first < start { - alloc.undef_mask.set_range(first, start, false); + self.undef_mask.set_range(first, start, false); } if last > end { - alloc.undef_mask.set_range(end, last, false); + self.undef_mask.set_range(end, last, false); } // Forget all the relocations. - alloc.relocations.remove_range(first..last); + self.relocations.remove_range(first..last); Ok(()) } @@ -258,8 +244,7 @@ impl<'tcx, Tag, Extra> Allocation { /// error which will report the first byte which is undefined. #[inline] fn check_defined(&self, ptr: Pointer, size: Size) -> EvalResult<'tcx> { - let alloc = self.get(ptr.alloc_id)?; - alloc.undef_mask.is_range_defined( + self.undef_mask.is_range_defined( ptr.offset, ptr.offset + size, ).or_else(|idx| err!(ReadUndefBytes(idx))) @@ -274,8 +259,7 @@ impl<'tcx, Tag, Extra> Allocation { if size.bytes() == 0 { return Ok(()); } - let alloc = self.get_mut(ptr.alloc_id)?; - alloc.undef_mask.set_range( + self.undef_mask.set_range( ptr.offset, ptr.offset + size, new_state, From ad11856431ea3c0808951eed9fcbefbd82590ef2 Mon Sep 17 00:00:00 2001 From: Oliver Scherer Date: Mon, 12 Nov 2018 08:56:41 +0100 Subject: [PATCH 43/88] Fiddle a `HasDataLayout` through the allocation methods --- src/librustc/mir/interpret/allocation.rs | 60 ++++++++++++++++-------- 1 file changed, 41 insertions(+), 19 deletions(-) diff --git a/src/librustc/mir/interpret/allocation.rs b/src/librustc/mir/interpret/allocation.rs index aba0da32d4544..1856232573c2a 100644 --- a/src/librustc/mir/interpret/allocation.rs +++ b/src/librustc/mir/interpret/allocation.rs @@ -18,6 +18,7 @@ use std::iter; use mir; use std::ops::{Deref, DerefMut}; use rustc_data_structures::sorted_map::SortedMap; +use rustc_target::abi::HasDataLayout; /// Used by `check_bounds` to indicate whether the pointer needs to be just inbounds /// or also inbounds of a *live* allocation. @@ -76,16 +77,17 @@ impl<'tcx, Tag, Extra> Allocation { #[inline(always)] pub fn check_bounds( &self, + cx: &impl HasDataLayout, ptr: Pointer, size: Size, ) -> EvalResult<'tcx> { // if ptr.offset is in bounds, then so is ptr (because offset checks for overflow) - self.check_bounds_ptr(ptr.offset(size, &*self)?) + self.check_bounds_ptr(ptr.offset(size, cx)?) } } /// Byte accessors -impl<'tcx, Tag, Extra: AllocationExtra> Allocation { +impl<'tcx, Tag: Copy, Extra: AllocationExtra> Allocation { /// The last argument controls whether we error out when there are undefined /// or pointer bytes. You should never call this, call `get_bytes` or /// `get_bytes_with_undef_and_ptr` instead, @@ -95,6 +97,7 @@ impl<'tcx, Tag, Extra: AllocationExtra> Allocation { /// on that. fn get_bytes_internal( &self, + cx: &impl HasDataLayout, ptr: Pointer, size: Size, align: Align, @@ -102,14 +105,14 @@ impl<'tcx, Tag, Extra: AllocationExtra> Allocation { ) -> EvalResult<'tcx, &[u8]> { assert_ne!(size.bytes(), 0, "0-sized accesses should never even get a `Pointer`"); self.check_align(ptr.into(), align)?; - self.check_bounds(ptr, size, InboundsCheck::Live)?; + self.check_bounds(cx, ptr, size)?; if check_defined_and_ptr { self.check_defined(ptr, size)?; - self.check_relocations(ptr, size)?; + self.check_relocations(cx, ptr, size)?; } else { // We still don't want relocations on the *edges* - self.check_relocation_edges(ptr, size)?; + self.check_relocation_edges(cx, ptr, size)?; } AllocationExtra::memory_read(self, ptr, size)?; @@ -123,11 +126,12 @@ impl<'tcx, Tag, Extra: AllocationExtra> Allocation { #[inline] fn get_bytes( &self, + cx: &impl HasDataLayout, ptr: Pointer, size: Size, align: Align ) -> EvalResult<'tcx, &[u8]> { - self.get_bytes_internal(ptr, size, align, true) + self.get_bytes_internal(cx, ptr, size, align, true) } /// It is the caller's responsibility to handle undefined and pointer bytes. @@ -135,27 +139,29 @@ impl<'tcx, Tag, Extra: AllocationExtra> Allocation { #[inline] fn get_bytes_with_undef_and_ptr( &self, + cx: &impl HasDataLayout, ptr: Pointer, size: Size, align: Align ) -> EvalResult<'tcx, &[u8]> { - self.get_bytes_internal(ptr, size, align, false) + self.get_bytes_internal(cx, ptr, size, align, false) } /// Just calling this already marks everything as defined and removes relocations, /// so be sure to actually put data there! fn get_bytes_mut( &mut self, + cx: &impl HasDataLayout, ptr: Pointer, size: Size, align: Align, ) -> EvalResult<'tcx, &mut [u8]> { assert_ne!(size.bytes(), 0, "0-sized accesses should never even get a `Pointer`"); self.check_align(ptr.into(), align)?; - self.check_bounds(ptr, size, InboundsCheck::Live)?; + self.check_bounds(cx, ptr, size)?; self.mark_definedness(ptr, size, true)?; - self.clear_relocations(ptr, size)?; + self.clear_relocations(cx, ptr, size)?; AllocationExtra::memory_written(self, ptr, size)?; @@ -167,24 +173,30 @@ impl<'tcx, Tag, Extra: AllocationExtra> Allocation { } /// Relocations -impl<'tcx, Tag, Extra> Allocation { +impl<'tcx, Tag: Copy, Extra> Allocation { /// Return all relocations overlapping with the given ptr-offset pair. fn relocations( &self, + cx: &impl HasDataLayout, ptr: Pointer, size: Size, ) -> EvalResult<'tcx, &[(Size, (Tag, AllocId))]> { // We have to go back `pointer_size - 1` bytes, as that one would still overlap with // the beginning of this range. - let start = ptr.offset.bytes().saturating_sub(self.pointer_size().bytes() - 1); + let start = ptr.offset.bytes().saturating_sub(cx.data_layout().pointer_size.bytes() - 1); let end = ptr.offset + size; // this does overflow checking Ok(self.relocations.range(Size::from_bytes(start)..end)) } /// Check that there ar eno relocations overlapping with the given range. #[inline(always)] - fn check_relocations(&self, ptr: Pointer, size: Size) -> EvalResult<'tcx> { - if self.relocations(ptr, size)?.len() != 0 { + fn check_relocations( + &self, + cx: &impl HasDataLayout, + ptr: Pointer, + size: Size, + ) -> EvalResult<'tcx> { + if self.relocations(cx, ptr, size)?.len() != 0 { err!(ReadPointerAsBytes) } else { Ok(()) @@ -197,17 +209,22 @@ impl<'tcx, Tag, Extra> Allocation { /// uninitialized. This is a somewhat odd "spooky action at a distance", /// but it allows strictly more code to run than if we would just error /// immediately in that case. - fn clear_relocations(&mut self, ptr: Pointer, size: Size) -> EvalResult<'tcx> { + fn clear_relocations( + &mut self, + cx: &impl HasDataLayout, + ptr: Pointer, + size: Size, + ) -> EvalResult<'tcx> { // Find the start and end of the given range and its outermost relocations. let (first, last) = { // Find all relocations overlapping the given range. - let relocations = self.relocations(ptr, size)?; + let relocations = self.relocations(cx, ptr, size)?; if relocations.is_empty() { return Ok(()); } (relocations.first().unwrap().0, - relocations.last().unwrap().0 + self.pointer_size()) + relocations.last().unwrap().0 + cx.data_layout().pointer_size) }; let start = ptr.offset; let end = start + size; @@ -230,9 +247,14 @@ impl<'tcx, Tag, Extra> Allocation { /// Error if there are relocations overlapping with the edges of the /// given memory range. #[inline] - fn check_relocation_edges(&self, ptr: Pointer, size: Size) -> EvalResult<'tcx> { - self.check_relocations(ptr, Size::ZERO)?; - self.check_relocations(ptr.offset(size, self)?, Size::ZERO)?; + fn check_relocation_edges( + &self, + cx: &impl HasDataLayout, + ptr: Pointer, + size: Size, + ) -> EvalResult<'tcx> { + self.check_relocations(cx, ptr, Size::ZERO)?; + self.check_relocations(cx, ptr.offset(size, cx)?, Size::ZERO)?; Ok(()) } } From 9ecde5712ec5b393c7a0bb0074c957446ed9886b Mon Sep 17 00:00:00 2001 From: Oliver Scherer Date: Mon, 12 Nov 2018 09:00:41 +0100 Subject: [PATCH 44/88] Move some byte and scalar accessors from `Memory` to `Allocation` --- src/librustc/mir/interpret/allocation.rs | 200 +++++++++++++++++++++++ src/librustc_mir/interpret/memory.rs | 197 ---------------------- 2 files changed, 200 insertions(+), 197 deletions(-) diff --git a/src/librustc/mir/interpret/allocation.rs b/src/librustc/mir/interpret/allocation.rs index 1856232573c2a..cba51981021a9 100644 --- a/src/librustc/mir/interpret/allocation.rs +++ b/src/librustc/mir/interpret/allocation.rs @@ -86,6 +86,206 @@ impl<'tcx, Tag, Extra> Allocation { } } +/// Reading and writing +impl<'tcx, Tag: Copy, Extra: AllocationExtra> Allocation { + pub fn read_c_str(&self, ptr: Pointer) -> EvalResult<'tcx, &[u8]> { + let alloc = self.get(ptr.alloc_id)?; + assert_eq!(ptr.offset.bytes() as usize as u64, ptr.offset.bytes()); + let offset = ptr.offset.bytes() as usize; + match alloc.bytes[offset..].iter().position(|&c| c == 0) { + Some(size) => { + let p1 = Size::from_bytes((size + 1) as u64); + self.check_relocations(ptr, p1)?; + self.check_defined(ptr, p1)?; + Ok(&alloc.bytes[offset..offset + size]) + } + None => err!(UnterminatedCString(ptr.erase_tag())), + } + } + + pub fn check_bytes( + &self, + ptr: Scalar, + size: Size, + allow_ptr_and_undef: bool, + ) -> EvalResult<'tcx> { + // Empty accesses don't need to be valid pointers, but they should still be non-NULL + let align = Align::from_bytes(1).unwrap(); + if size.bytes() == 0 { + self.check_align(ptr, align)?; + return Ok(()); + } + let ptr = ptr.to_ptr()?; + // Check bounds, align and relocations on the edges + self.get_bytes_with_undef_and_ptr(ptr, size, align)?; + // Check undef and ptr + if !allow_ptr_and_undef { + self.check_defined(ptr, size)?; + self.check_relocations(ptr, size)?; + } + Ok(()) + } + + pub fn read_bytes(&self, ptr: Scalar, size: Size) -> EvalResult<'tcx, &[u8]> { + // Empty accesses don't need to be valid pointers, but they should still be non-NULL + let align = Align::from_bytes(1).unwrap(); + if size.bytes() == 0 { + self.check_align(ptr, align)?; + return Ok(&[]); + } + self.get_bytes(ptr.to_ptr()?, size, align) + } + + pub fn write_bytes(&mut self, ptr: Scalar, src: &[u8]) -> EvalResult<'tcx> { + // Empty accesses don't need to be valid pointers, but they should still be non-NULL + let align = Align::from_bytes(1).unwrap(); + if src.is_empty() { + self.check_align(ptr, align)?; + return Ok(()); + } + let bytes = self.get_bytes_mut(ptr.to_ptr()?, Size::from_bytes(src.len() as u64), align)?; + bytes.clone_from_slice(src); + Ok(()) + } + + pub fn write_repeat( + &mut self, + ptr: Scalar, + val: u8, + count: Size + ) -> EvalResult<'tcx> { + // Empty accesses don't need to be valid pointers, but they should still be non-NULL + let align = Align::from_bytes(1).unwrap(); + if count.bytes() == 0 { + self.check_align(ptr, align)?; + return Ok(()); + } + let bytes = self.get_bytes_mut(ptr.to_ptr()?, count, align)?; + for b in bytes { + *b = val; + } + Ok(()) + } + + /// Read a *non-ZST* scalar + pub fn read_scalar( + &self, + ptr: Pointer, + ptr_align: Align, + size: Size + ) -> EvalResult<'tcx, ScalarMaybeUndef> { + // get_bytes_unchecked tests alignment and relocation edges + let bytes = self.get_bytes_with_undef_and_ptr( + ptr, size, ptr_align.min(self.int_align(size)) + )?; + // Undef check happens *after* we established that the alignment is correct. + // We must not return Ok() for unaligned pointers! + if self.check_defined(ptr, size).is_err() { + // this inflates undefined bytes to the entire scalar, even if only a few + // bytes are undefined + return Ok(ScalarMaybeUndef::Undef); + } + // Now we do the actual reading + let bits = read_target_uint(self.tcx.data_layout.endian, bytes).unwrap(); + // See if we got a pointer + if size != self.pointer_size() { + // *Now* better make sure that the inside also is free of relocations. + self.check_relocations(ptr, size)?; + } else { + let alloc = self.get(ptr.alloc_id)?; + match alloc.relocations.get(&ptr.offset) { + Some(&(tag, alloc_id)) => { + let ptr = Pointer::new_with_tag(alloc_id, Size::from_bytes(bits as u64), tag); + return Ok(ScalarMaybeUndef::Scalar(ptr.into())) + } + None => {}, + } + } + // We don't. Just return the bits. + Ok(ScalarMaybeUndef::Scalar(Scalar::from_uint(bits, size))) + } + + pub fn read_ptr_sized( + &self, + ptr: Pointer, + ptr_align: Align + ) -> EvalResult<'tcx, ScalarMaybeUndef> { + self.read_scalar(ptr, ptr_align, self.pointer_size()) + } + + /// Write a *non-ZST* scalar + pub fn write_scalar( + &mut self, + ptr: Pointer, + ptr_align: Align, + val: ScalarMaybeUndef, + type_size: Size, + ) -> EvalResult<'tcx> { + let val = match val { + ScalarMaybeUndef::Scalar(scalar) => scalar, + ScalarMaybeUndef::Undef => return self.mark_definedness(ptr, type_size, false), + }; + + let bytes = match val { + Scalar::Ptr(val) => { + assert_eq!(type_size, self.pointer_size()); + val.offset.bytes() as u128 + } + + Scalar::Bits { bits, size } => { + assert_eq!(size as u64, type_size.bytes()); + debug_assert_eq!(truncate(bits, Size::from_bytes(size.into())), bits, + "Unexpected value of size {} when writing to memory", size); + bits + }, + }; + + { + // get_bytes_mut checks alignment + let endian = self.tcx.data_layout.endian; + let dst = self.get_bytes_mut(ptr, type_size, ptr_align)?; + write_target_uint(endian, dst, bytes).unwrap(); + } + + // See if we have to also write a relocation + match val { + Scalar::Ptr(val) => { + self.get_mut(ptr.alloc_id)?.relocations.insert( + ptr.offset, + (val.tag, val.alloc_id), + ); + } + _ => {} + } + + Ok(()) + } + + pub fn write_ptr_sized( + &mut self, + ptr: Pointer, + ptr_align: Align, + val: ScalarMaybeUndef + ) -> EvalResult<'tcx> { + let ptr_size = self.pointer_size(); + self.write_scalar(ptr.into(), ptr_align, val, ptr_size) + } + + fn int_align(&self, size: Size) -> Align { + // We assume pointer-sized integers have the same alignment as pointers. + // We also assume signed and unsigned integers of the same size have the same alignment. + let ity = match size.bytes() { + 1 => layout::I8, + 2 => layout::I16, + 4 => layout::I32, + 8 => layout::I64, + 16 => layout::I128, + _ => bug!("bad integer size: {}", size.bytes()), + }; + ity.align(self).abi + } +} + /// Byte accessors impl<'tcx, Tag: Copy, Extra: AllocationExtra> Allocation { /// The last argument controls whether we error out when there are undefined diff --git a/src/librustc_mir/interpret/memory.rs b/src/librustc_mir/interpret/memory.rs index 9f8e8fc921af8..69789637927d9 100644 --- a/src/librustc_mir/interpret/memory.rs +++ b/src/librustc_mir/interpret/memory.rs @@ -714,203 +714,6 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> { Ok(()) } - - pub fn read_c_str(&self, ptr: Pointer) -> EvalResult<'tcx, &[u8]> { - let alloc = self.get(ptr.alloc_id)?; - assert_eq!(ptr.offset.bytes() as usize as u64, ptr.offset.bytes()); - let offset = ptr.offset.bytes() as usize; - match alloc.bytes[offset..].iter().position(|&c| c == 0) { - Some(size) => { - let p1 = Size::from_bytes((size + 1) as u64); - self.check_relocations(ptr, p1)?; - self.check_defined(ptr, p1)?; - Ok(&alloc.bytes[offset..offset + size]) - } - None => err!(UnterminatedCString(ptr.erase_tag())), - } - } - - pub fn check_bytes( - &self, - ptr: Scalar, - size: Size, - allow_ptr_and_undef: bool, - ) -> EvalResult<'tcx> { - // Empty accesses don't need to be valid pointers, but they should still be non-NULL - let align = Align::from_bytes(1).unwrap(); - if size.bytes() == 0 { - self.check_align(ptr, align)?; - return Ok(()); - } - let ptr = ptr.to_ptr()?; - // Check bounds, align and relocations on the edges - self.get_bytes_with_undef_and_ptr(ptr, size, align)?; - // Check undef and ptr - if !allow_ptr_and_undef { - self.check_defined(ptr, size)?; - self.check_relocations(ptr, size)?; - } - Ok(()) - } - - pub fn read_bytes(&self, ptr: Scalar, size: Size) -> EvalResult<'tcx, &[u8]> { - // Empty accesses don't need to be valid pointers, but they should still be non-NULL - let align = Align::from_bytes(1).unwrap(); - if size.bytes() == 0 { - self.check_align(ptr, align)?; - return Ok(&[]); - } - self.get_bytes(ptr.to_ptr()?, size, align) - } - - pub fn write_bytes(&mut self, ptr: Scalar, src: &[u8]) -> EvalResult<'tcx> { - // Empty accesses don't need to be valid pointers, but they should still be non-NULL - let align = Align::from_bytes(1).unwrap(); - if src.is_empty() { - self.check_align(ptr, align)?; - return Ok(()); - } - let bytes = self.get_bytes_mut(ptr.to_ptr()?, Size::from_bytes(src.len() as u64), align)?; - bytes.clone_from_slice(src); - Ok(()) - } - - pub fn write_repeat( - &mut self, - ptr: Scalar, - val: u8, - count: Size - ) -> EvalResult<'tcx> { - // Empty accesses don't need to be valid pointers, but they should still be non-NULL - let align = Align::from_bytes(1).unwrap(); - if count.bytes() == 0 { - self.check_align(ptr, align)?; - return Ok(()); - } - let bytes = self.get_bytes_mut(ptr.to_ptr()?, count, align)?; - for b in bytes { - *b = val; - } - Ok(()) - } - - /// Read a *non-ZST* scalar - pub fn read_scalar( - &self, - ptr: Pointer, - ptr_align: Align, - size: Size - ) -> EvalResult<'tcx, ScalarMaybeUndef> { - // get_bytes_unchecked tests alignment and relocation edges - let bytes = self.get_bytes_with_undef_and_ptr( - ptr, size, ptr_align.min(self.int_align(size)) - )?; - // Undef check happens *after* we established that the alignment is correct. - // We must not return Ok() for unaligned pointers! - if self.check_defined(ptr, size).is_err() { - // this inflates undefined bytes to the entire scalar, even if only a few - // bytes are undefined - return Ok(ScalarMaybeUndef::Undef); - } - // Now we do the actual reading - let bits = read_target_uint(self.tcx.data_layout.endian, bytes).unwrap(); - // See if we got a pointer - if size != self.pointer_size() { - // *Now* better make sure that the inside also is free of relocations. - self.check_relocations(ptr, size)?; - } else { - let alloc = self.get(ptr.alloc_id)?; - match alloc.relocations.get(&ptr.offset) { - Some(&(tag, alloc_id)) => { - let ptr = Pointer::new_with_tag(alloc_id, Size::from_bytes(bits as u64), tag); - return Ok(ScalarMaybeUndef::Scalar(ptr.into())) - } - None => {}, - } - } - // We don't. Just return the bits. - Ok(ScalarMaybeUndef::Scalar(Scalar::from_uint(bits, size))) - } - - pub fn read_ptr_sized( - &self, - ptr: Pointer, - ptr_align: Align - ) -> EvalResult<'tcx, ScalarMaybeUndef> { - self.read_scalar(ptr, ptr_align, self.pointer_size()) - } - - /// Write a *non-ZST* scalar - pub fn write_scalar( - &mut self, - ptr: Pointer, - ptr_align: Align, - val: ScalarMaybeUndef, - type_size: Size, - ) -> EvalResult<'tcx> { - let val = match val { - ScalarMaybeUndef::Scalar(scalar) => scalar, - ScalarMaybeUndef::Undef => return self.mark_definedness(ptr, type_size, false), - }; - - let bytes = match val { - Scalar::Ptr(val) => { - assert_eq!(type_size, self.pointer_size()); - val.offset.bytes() as u128 - } - - Scalar::Bits { bits, size } => { - assert_eq!(size as u64, type_size.bytes()); - debug_assert_eq!(truncate(bits, Size::from_bytes(size.into())), bits, - "Unexpected value of size {} when writing to memory", size); - bits - }, - }; - - { - // get_bytes_mut checks alignment - let endian = self.tcx.data_layout.endian; - let dst = self.get_bytes_mut(ptr, type_size, ptr_align)?; - write_target_uint(endian, dst, bytes).unwrap(); - } - - // See if we have to also write a relocation - match val { - Scalar::Ptr(val) => { - self.get_mut(ptr.alloc_id)?.relocations.insert( - ptr.offset, - (val.tag, val.alloc_id), - ); - } - _ => {} - } - - Ok(()) - } - - pub fn write_ptr_sized( - &mut self, - ptr: Pointer, - ptr_align: Align, - val: ScalarMaybeUndef - ) -> EvalResult<'tcx> { - let ptr_size = self.pointer_size(); - self.write_scalar(ptr.into(), ptr_align, val, ptr_size) - } - - fn int_align(&self, size: Size) -> Align { - // We assume pointer-sized integers have the same alignment as pointers. - // We also assume signed and unsigned integers of the same size have the same alignment. - let ity = match size.bytes() { - 1 => layout::I8, - 2 => layout::I16, - 4 => layout::I32, - 8 => layout::I64, - 16 => layout::I128, - _ => bug!("bad integer size: {}", size.bytes()), - }; - ity.align(self).abi - } } /// Undefined bytes From 07e7804110c77d572012f913a341523bdbaac4dd Mon Sep 17 00:00:00 2001 From: Oliver Scherer Date: Mon, 12 Nov 2018 13:26:53 +0100 Subject: [PATCH 45/88] Adjust rustc_mir::interpret to changes in `Allocation`/`Memory` methods --- src/librustc/mir/interpret/allocation.rs | 112 ++++++++++++++--------- src/librustc_mir/interpret/memory.rs | 16 +++- src/librustc_mir/interpret/operand.rs | 17 +++- src/librustc_mir/interpret/place.rs | 15 ++- src/librustc_mir/interpret/terminator.rs | 3 +- src/librustc_mir/interpret/traits.rs | 31 +++++-- src/librustc_mir/interpret/validity.rs | 28 ++++-- 7 files changed, 146 insertions(+), 76 deletions(-) diff --git a/src/librustc/mir/interpret/allocation.rs b/src/librustc/mir/interpret/allocation.rs index cba51981021a9..6ef7a5a266d37 100644 --- a/src/librustc/mir/interpret/allocation.rs +++ b/src/librustc/mir/interpret/allocation.rs @@ -10,9 +10,12 @@ //! The virtual memory representation of the MIR interpreter -use super::{Pointer, EvalResult, AllocId}; +use super::{ + Pointer, EvalResult, AllocId, ScalarMaybeUndef, write_target_uint, read_target_uint, Scalar, + truncate, +}; -use ty::layout::{Size, Align}; +use ty::layout::{self, Size, Align}; use syntax::ast::Mutability; use std::iter; use mir; @@ -88,16 +91,19 @@ impl<'tcx, Tag, Extra> Allocation { /// Reading and writing impl<'tcx, Tag: Copy, Extra: AllocationExtra> Allocation { - pub fn read_c_str(&self, ptr: Pointer) -> EvalResult<'tcx, &[u8]> { - let alloc = self.get(ptr.alloc_id)?; + pub fn read_c_str( + &self, + cx: &impl HasDataLayout, + ptr: Pointer, + ) -> EvalResult<'tcx, &[u8]> { assert_eq!(ptr.offset.bytes() as usize as u64, ptr.offset.bytes()); let offset = ptr.offset.bytes() as usize; - match alloc.bytes[offset..].iter().position(|&c| c == 0) { + match self.bytes[offset..].iter().position(|&c| c == 0) { Some(size) => { let p1 = Size::from_bytes((size + 1) as u64); - self.check_relocations(ptr, p1)?; + self.check_relocations(cx, ptr, p1)?; self.check_defined(ptr, p1)?; - Ok(&alloc.bytes[offset..offset + size]) + Ok(&self.bytes[offset..offset + size]) } None => err!(UnterminatedCString(ptr.erase_tag())), } @@ -105,7 +111,8 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra> Allocation { pub fn check_bytes( &self, - ptr: Scalar, + cx: &impl HasDataLayout, + ptr: Pointer, size: Size, allow_ptr_and_undef: bool, ) -> EvalResult<'tcx> { @@ -115,42 +122,54 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra> Allocation { self.check_align(ptr, align)?; return Ok(()); } - let ptr = ptr.to_ptr()?; // Check bounds, align and relocations on the edges - self.get_bytes_with_undef_and_ptr(ptr, size, align)?; + self.get_bytes_with_undef_and_ptr(cx, ptr, size, align)?; // Check undef and ptr if !allow_ptr_and_undef { self.check_defined(ptr, size)?; - self.check_relocations(ptr, size)?; + self.check_relocations(cx, ptr, size)?; } Ok(()) } - pub fn read_bytes(&self, ptr: Scalar, size: Size) -> EvalResult<'tcx, &[u8]> { + pub fn read_bytes( + &self, + cx: &impl HasDataLayout, + ptr: Pointer, + size: Size, + ) -> EvalResult<'tcx, &[u8]> { // Empty accesses don't need to be valid pointers, but they should still be non-NULL let align = Align::from_bytes(1).unwrap(); if size.bytes() == 0 { self.check_align(ptr, align)?; return Ok(&[]); } - self.get_bytes(ptr.to_ptr()?, size, align) + self.get_bytes(cx, ptr, size, align) } - pub fn write_bytes(&mut self, ptr: Scalar, src: &[u8]) -> EvalResult<'tcx> { + pub fn write_bytes( + &mut self, + cx: &impl HasDataLayout, + ptr: Pointer, + src: &[u8], + ) -> EvalResult<'tcx> { // Empty accesses don't need to be valid pointers, but they should still be non-NULL let align = Align::from_bytes(1).unwrap(); if src.is_empty() { self.check_align(ptr, align)?; return Ok(()); } - let bytes = self.get_bytes_mut(ptr.to_ptr()?, Size::from_bytes(src.len() as u64), align)?; + let bytes = self.get_bytes_mut( + cx, ptr, Size::from_bytes(src.len() as u64), align, + )?; bytes.clone_from_slice(src); Ok(()) } pub fn write_repeat( &mut self, - ptr: Scalar, + cx: &impl HasDataLayout, + ptr: Pointer, val: u8, count: Size ) -> EvalResult<'tcx> { @@ -160,7 +179,7 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra> Allocation { self.check_align(ptr, align)?; return Ok(()); } - let bytes = self.get_bytes_mut(ptr.to_ptr()?, count, align)?; + let bytes = self.get_bytes_mut(cx, ptr, count, align)?; for b in bytes { *b = val; } @@ -170,13 +189,14 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra> Allocation { /// Read a *non-ZST* scalar pub fn read_scalar( &self, - ptr: Pointer, + cx: &impl HasDataLayout, + ptr: Pointer, ptr_align: Align, size: Size - ) -> EvalResult<'tcx, ScalarMaybeUndef> { + ) -> EvalResult<'tcx, ScalarMaybeUndef> { // get_bytes_unchecked tests alignment and relocation edges let bytes = self.get_bytes_with_undef_and_ptr( - ptr, size, ptr_align.min(self.int_align(size)) + cx, ptr, size, ptr_align.min(self.int_align(cx, size)) )?; // Undef check happens *after* we established that the alignment is correct. // We must not return Ok() for unaligned pointers! @@ -186,14 +206,13 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra> Allocation { return Ok(ScalarMaybeUndef::Undef); } // Now we do the actual reading - let bits = read_target_uint(self.tcx.data_layout.endian, bytes).unwrap(); + let bits = read_target_uint(cx.data_layout().endian, bytes).unwrap(); // See if we got a pointer - if size != self.pointer_size() { + if size != cx.data_layout().pointer_size { // *Now* better make sure that the inside also is free of relocations. - self.check_relocations(ptr, size)?; + self.check_relocations(cx, ptr, size)?; } else { - let alloc = self.get(ptr.alloc_id)?; - match alloc.relocations.get(&ptr.offset) { + match self.relocations.get(&ptr.offset) { Some(&(tag, alloc_id)) => { let ptr = Pointer::new_with_tag(alloc_id, Size::from_bytes(bits as u64), tag); return Ok(ScalarMaybeUndef::Scalar(ptr.into())) @@ -207,18 +226,20 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra> Allocation { pub fn read_ptr_sized( &self, - ptr: Pointer, + cx: &impl HasDataLayout, + ptr: Pointer, ptr_align: Align - ) -> EvalResult<'tcx, ScalarMaybeUndef> { - self.read_scalar(ptr, ptr_align, self.pointer_size()) + ) -> EvalResult<'tcx, ScalarMaybeUndef> { + self.read_scalar(cx, ptr, ptr_align, cx.data_layout().pointer_size) } /// Write a *non-ZST* scalar pub fn write_scalar( &mut self, - ptr: Pointer, + cx: &impl HasDataLayout, + ptr: Pointer, ptr_align: Align, - val: ScalarMaybeUndef, + val: ScalarMaybeUndef, type_size: Size, ) -> EvalResult<'tcx> { let val = match val { @@ -228,7 +249,7 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra> Allocation { let bytes = match val { Scalar::Ptr(val) => { - assert_eq!(type_size, self.pointer_size()); + assert_eq!(type_size, cx.data_layout().pointer_size); val.offset.bytes() as u128 } @@ -242,15 +263,15 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra> Allocation { { // get_bytes_mut checks alignment - let endian = self.tcx.data_layout.endian; - let dst = self.get_bytes_mut(ptr, type_size, ptr_align)?; + let endian = cx.data_layout().endian; + let dst = self.get_bytes_mut(cx, ptr, type_size, ptr_align)?; write_target_uint(endian, dst, bytes).unwrap(); } // See if we have to also write a relocation match val { Scalar::Ptr(val) => { - self.get_mut(ptr.alloc_id)?.relocations.insert( + self.relocations.insert( ptr.offset, (val.tag, val.alloc_id), ); @@ -263,15 +284,20 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra> Allocation { pub fn write_ptr_sized( &mut self, - ptr: Pointer, + cx: &impl HasDataLayout, + ptr: Pointer, ptr_align: Align, - val: ScalarMaybeUndef + val: ScalarMaybeUndef ) -> EvalResult<'tcx> { - let ptr_size = self.pointer_size(); - self.write_scalar(ptr.into(), ptr_align, val, ptr_size) + let ptr_size = cx.data_layout().pointer_size; + self.write_scalar(cx, ptr.into(), ptr_align, val, ptr_size) } - fn int_align(&self, size: Size) -> Align { + fn int_align( + &self, + cx: &impl HasDataLayout, + size: Size, + ) -> Align { // We assume pointer-sized integers have the same alignment as pointers. // We also assume signed and unsigned integers of the same size have the same alignment. let ity = match size.bytes() { @@ -282,7 +308,7 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra> Allocation { 16 => layout::I128, _ => bug!("bad integer size: {}", size.bytes()), }; - ity.align(self).abi + ity.align(cx).abi } } @@ -337,7 +363,7 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra> Allocation { /// It is the caller's responsibility to handle undefined and pointer bytes. /// However, this still checks that there are no relocations on the *edges*. #[inline] - fn get_bytes_with_undef_and_ptr( + pub fn get_bytes_with_undef_and_ptr( &self, cx: &impl HasDataLayout, ptr: Pointer, @@ -349,7 +375,7 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra> Allocation { /// Just calling this already marks everything as defined and removes relocations, /// so be sure to actually put data there! - fn get_bytes_mut( + pub fn get_bytes_mut( &mut self, cx: &impl HasDataLayout, ptr: Pointer, @@ -375,7 +401,7 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra> Allocation { /// Relocations impl<'tcx, Tag: Copy, Extra> Allocation { /// Return all relocations overlapping with the given ptr-offset pair. - fn relocations( + pub fn relocations( &self, cx: &impl HasDataLayout, ptr: Pointer, diff --git a/src/librustc_mir/interpret/memory.rs b/src/librustc_mir/interpret/memory.rs index 69789637927d9..3119d9ed0ffa3 100644 --- a/src/librustc_mir/interpret/memory.rs +++ b/src/librustc_mir/interpret/memory.rs @@ -21,7 +21,7 @@ use std::ptr; use std::borrow::Cow; use rustc::ty::{self, Instance, ParamEnv, query::TyCtxtAt}; -use rustc::ty::layout::{self, Align, TargetDataLayout, Size, HasDataLayout}; +use rustc::ty::layout::{Align, TargetDataLayout, Size, HasDataLayout}; pub use rustc::mir::interpret::{truncate, write_target_uint, read_target_uint}; use rustc_data_structures::fx::{FxHashSet, FxHashMap}; @@ -30,7 +30,7 @@ use syntax::ast::Mutability; use super::{ Pointer, AllocId, Allocation, GlobalId, AllocationExtra, InboundsCheck, EvalResult, Scalar, EvalErrorKind, AllocType, PointerArithmetic, - Machine, AllocMap, MayLeak, ScalarMaybeUndef, ErrorHandled, + Machine, AllocMap, MayLeak, ErrorHandled, }; #[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)] @@ -655,7 +655,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> { // (`get_bytes_with_undef_and_ptr` below checks that there are no // relocations overlapping the edges; those would not be handled correctly). let relocations = { - let relocations = self.relocations(src, size)?; + let relocations = self.get(src.alloc_id)?.relocations(self, src, size)?; let mut new_relocations = Vec::with_capacity(relocations.len() * (length as usize)); for i in 0..length { new_relocations.extend( @@ -671,9 +671,15 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> { new_relocations }; + let tcx = self.tcx.tcx; + // This also checks alignment, and relocation edges on the src. - let src_bytes = self.get_bytes_with_undef_and_ptr(src, size, src_align)?.as_ptr(); - let dest_bytes = self.get_bytes_mut(dest, size * length, dest_align)?.as_mut_ptr(); + let src_bytes = self.get(src.alloc_id)? + .get_bytes_with_undef_and_ptr(&tcx, src, size, src_align)? + .as_ptr(); + let dest_bytes = self.get_mut(dest.alloc_id)? + .get_bytes_mut(&tcx, dest, size * length, dest_align)? + .as_mut_ptr(); // SAFE: The above indexing would have panicked if there weren't at least `size` bytes // behind `src` and `dest`. Also, we use the overlapping-safe `ptr::copy` if `src` and diff --git a/src/librustc_mir/interpret/operand.rs b/src/librustc_mir/interpret/operand.rs index 8238d580022a8..3f5f0ebed72d0 100644 --- a/src/librustc_mir/interpret/operand.rs +++ b/src/librustc_mir/interpret/operand.rs @@ -278,7 +278,9 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> let ptr = ptr.to_ptr()?; match mplace.layout.abi { layout::Abi::Scalar(..) => { - let scalar = self.memory.read_scalar(ptr, ptr_align, mplace.layout.size)?; + let scalar = self.memory + .get(ptr.alloc_id)? + .read_scalar(self, ptr, ptr_align, mplace.layout.size)?; Ok(Some(Immediate::Scalar(scalar))) } layout::Abi::ScalarPair(ref a, ref b) => { @@ -288,8 +290,12 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> let b_offset = a_size.align_to(b.align(self).abi); assert!(b_offset.bytes() > 0); // we later use the offset to test which field to use let b_ptr = ptr.offset(b_offset, self)?.into(); - let a_val = self.memory.read_scalar(a_ptr, ptr_align, a_size)?; - let b_val = self.memory.read_scalar(b_ptr, ptr_align, b_size)?; + let a_val = self.memory + .get(ptr.alloc_id)? + .read_scalar(self, a_ptr, ptr_align, a_size)?; + let b_val = self.memory + .get(ptr.alloc_id)? + .read_scalar(self, b_ptr, ptr_align, b_size)?; Ok(Some(Immediate::ScalarPair(a_val, b_val))) } _ => Ok(None), @@ -345,7 +351,10 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> mplace: MPlaceTy<'tcx, M::PointerTag>, ) -> EvalResult<'tcx, &str> { let len = mplace.len(self)?; - let bytes = self.memory.read_bytes(mplace.ptr, Size::from_bytes(len as u64))?; + let ptr = mplace.ptr.to_ptr()?; + let bytes = self.memory + .get(ptr.alloc_id)? + .read_bytes(self, ptr, Size::from_bytes(len as u64))?; let str = ::std::str::from_utf8(bytes) .map_err(|err| EvalErrorKind::ValidationFailure(err.to_string()))?; Ok(str) diff --git a/src/librustc_mir/interpret/place.rs b/src/librustc_mir/interpret/place.rs index 7ef3dd5f7201e..e2b6c00ba382c 100644 --- a/src/librustc_mir/interpret/place.rs +++ b/src/librustc_mir/interpret/place.rs @@ -718,6 +718,7 @@ where } let ptr = ptr.to_ptr()?; + let tcx = &*self.tcx; // FIXME: We should check that there are dest.layout.size many bytes available in // memory. The code below is not sufficient, with enough padding it might not // cover all the bytes! @@ -729,8 +730,8 @@ where dest.layout) } - self.memory.write_scalar( - ptr, ptr_align.min(dest.layout.align.abi), scalar, dest.layout.size + self.memory.get_mut(ptr.alloc_id)?.write_scalar( + tcx, ptr, ptr_align.min(dest.layout.align.abi), scalar, dest.layout.size ) } Immediate::ScalarPair(a_val, b_val) => { @@ -742,14 +743,18 @@ where let (a_size, b_size) = (a.size(self), b.size(self)); let (a_align, b_align) = (a.align(self).abi, b.align(self).abi); let b_offset = a_size.align_to(b_align); - let b_ptr = ptr.offset(b_offset, self)?.into(); + let b_ptr = ptr.offset(b_offset, self)?; // It is tempting to verify `b_offset` against `layout.fields.offset(1)`, // but that does not work: We could be a newtype around a pair, then the // fields do not match the `ScalarPair` components. - self.memory.write_scalar(ptr, ptr_align.min(a_align), a_val, a_size)?; - self.memory.write_scalar(b_ptr, ptr_align.min(b_align), b_val, b_size) + self.memory + .get_mut(ptr.alloc_id)? + .write_scalar(tcx, ptr, ptr_align.min(a_align), a_val, a_size)?; + self.memory + .get_mut(b_ptr.alloc_id)? + .write_scalar(tcx, b_ptr, ptr_align.min(b_align), b_val, b_size) } } } diff --git a/src/librustc_mir/interpret/terminator.rs b/src/librustc_mir/interpret/terminator.rs index fd17a4a71295b..9e59611125d92 100644 --- a/src/librustc_mir/interpret/terminator.rs +++ b/src/librustc_mir/interpret/terminator.rs @@ -404,7 +404,8 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> let ptr_align = self.tcx.data_layout.pointer_align.abi; let ptr = self.deref_operand(args[0])?; let vtable = ptr.vtable()?; - let fn_ptr = self.memory.read_ptr_sized( + let fn_ptr = self.memory.get(vtable.alloc_id)?.read_ptr_sized( + self, vtable.offset(ptr_size * (idx as u64 + 3), self)?, ptr_align )?.to_ptr()?; diff --git a/src/librustc_mir/interpret/traits.rs b/src/librustc_mir/interpret/traits.rs index f11fd45b753fc..8e39574c01e98 100644 --- a/src/librustc_mir/interpret/traits.rs +++ b/src/librustc_mir/interpret/traits.rs @@ -55,23 +55,31 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> ptr_align, MemoryKind::Vtable, )?.with_default_tag(); + let tcx = &*self.tcx; - let drop = ::monomorphize::resolve_drop_in_place(*self.tcx, ty); + let drop = ::monomorphize::resolve_drop_in_place(*tcx, ty); let drop = self.memory.create_fn_alloc(drop).with_default_tag(); - self.memory.write_ptr_sized(vtable, ptr_align, Scalar::Ptr(drop).into())?; + self.memory + .get_mut(vtable.alloc_id)? + .write_ptr_sized(tcx, vtable, ptr_align, Scalar::Ptr(drop).into())?; let size_ptr = vtable.offset(ptr_size, self)?; - self.memory.write_ptr_sized(size_ptr, ptr_align, Scalar::from_uint(size, ptr_size).into())?; + self.memory + .get_mut(size_ptr.alloc_id)? + .write_ptr_sized(tcx, size_ptr, ptr_align, Scalar::from_uint(size, ptr_size).into())?; let align_ptr = vtable.offset(ptr_size * 2, self)?; - self.memory.write_ptr_sized(align_ptr, ptr_align, - Scalar::from_uint(align, ptr_size).into())?; + self.memory + .get_mut(align_ptr.alloc_id)? + .write_ptr_sized(tcx, align_ptr, ptr_align, Scalar::from_uint(align, ptr_size).into())?; for (i, method) in methods.iter().enumerate() { if let Some((def_id, substs)) = *method { let instance = self.resolve(def_id, substs)?; let fn_ptr = self.memory.create_fn_alloc(instance).with_default_tag(); let method_ptr = vtable.offset(ptr_size * (3 + i as u64), self)?; - self.memory.write_ptr_sized(method_ptr, ptr_align, Scalar::Ptr(fn_ptr).into())?; + self.memory + .get_mut(method_ptr.alloc_id)? + .write_ptr_sized(tcx, method_ptr, ptr_align, Scalar::Ptr(fn_ptr).into())?; } } @@ -88,7 +96,10 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> ) -> EvalResult<'tcx, (ty::Instance<'tcx>, ty::Ty<'tcx>)> { // we don't care about the pointee type, we just want a pointer let pointer_align = self.tcx.data_layout.pointer_align.abi; - let drop_fn = self.memory.read_ptr_sized(vtable, pointer_align)?.to_ptr()?; + let drop_fn = self.memory + .get(vtable.alloc_id)? + .read_ptr_sized(self, vtable, pointer_align)? + .to_ptr()?; let drop_instance = self.memory.get_fn(drop_fn)?; trace!("Found drop fn: {:?}", drop_instance); let fn_sig = drop_instance.ty(*self.tcx).fn_sig(*self.tcx); @@ -104,9 +115,11 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> ) -> EvalResult<'tcx, (Size, Align)> { let pointer_size = self.pointer_size(); let pointer_align = self.tcx.data_layout.pointer_align.abi; - let size = self.memory.read_ptr_sized(vtable.offset(pointer_size, self)?,pointer_align)? + let alloc = self.memory.get(vtable.alloc_id)?; + let size = alloc.read_ptr_sized(self, vtable.offset(pointer_size, self)?, pointer_align)? .to_bits(pointer_size)? as u64; - let align = self.memory.read_ptr_sized( + let align = alloc.read_ptr_sized( + self, vtable.offset(pointer_size * 2, self)?, pointer_align )?.to_bits(pointer_size)? as u64; diff --git a/src/librustc_mir/interpret/validity.rs b/src/librustc_mir/interpret/validity.rs index 6d1cacfa1479c..b3a82cd70232a 100644 --- a/src/librustc_mir/interpret/validity.rs +++ b/src/librustc_mir/interpret/validity.rs @@ -21,7 +21,7 @@ use rustc::mir::interpret::{ }; use super::{ - OpTy, MPlaceTy, Machine, EvalContext, ValueVisitor + OpTy, MPlaceTy, Machine, EvalContext, ValueVisitor, Operand, }; macro_rules! validation_failure { @@ -396,7 +396,9 @@ impl<'rt, 'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> // Maintain the invariant that the place we are checking is // already verified to be in-bounds. try_validation!( - self.ecx.memory.check_bounds(ptr, size, InboundsCheck::Live), + self.ecx.memory + .get(ptr.alloc_id)? + .check_bounds(self.ecx, ptr, size), "dangling (not entirely in bounds) reference", self.path); } // Check if we have encountered this pointer+layout combination @@ -520,12 +522,14 @@ impl<'rt, 'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> _ => false, } } => { - let mplace = if op.layout.is_zst() { + let mplace = match *op { // it's a ZST, the memory content cannot matter - MPlaceTy::dangling(op.layout, self.ecx) - } else { - // non-ZST array/slice/str cannot be immediate - op.to_mem_place() + Operand::Immediate(_) if op.layout.is_zst() => + // invent an aligned mplace + MPlaceTy::dangling(op.layout, self.ecx), + // FIXME: what about single element arrays? They can be Scalar layout I think + Operand::Immediate(_) => bug!("non-ZST array/slice cannot be immediate"), + Operand::Indirect(_) => op.to_mem_place(), }; // This is the length of the array/slice. let len = mplace.len(self.ecx)?; @@ -534,6 +538,11 @@ impl<'rt, 'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> // This is the size in bytes of the whole array. let size = ty_size * len; + if op.layout.is_zst() { + return self.ecx.memory.check_align(mplace.ptr, op.layout.align); + } + let ptr = mplace.ptr.to_ptr()?; + // NOTE: Keep this in sync with the handling of integer and float // types above, in `visit_primitive`. // In run-time mode, we accept pointers in here. This is actually more @@ -543,8 +552,9 @@ impl<'rt, 'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> // to reject those pointers, we just do not have the machinery to // talk about parts of a pointer. // We also accept undef, for consistency with the type-based checks. - match self.ecx.memory.check_bytes( - mplace.ptr, + match self.ecx.memory.get(ptr.alloc_id)?.check_bytes( + self.ecx, + ptr, size, /*allow_ptr_and_undef*/!self.const_mode, ) { From 3a0e8254b06c8a9c8e4b775a18524797f1e3d44c Mon Sep 17 00:00:00 2001 From: Oliver Scherer Date: Tue, 13 Nov 2018 09:44:59 +0100 Subject: [PATCH 46/88] Remove unnecessary `Result` (function always returned `Ok`) --- src/librustc/mir/interpret/allocation.rs | 12 ++++++------ src/librustc_mir/interpret/memory.rs | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/librustc/mir/interpret/allocation.rs b/src/librustc/mir/interpret/allocation.rs index 6ef7a5a266d37..42bcd3a902760 100644 --- a/src/librustc/mir/interpret/allocation.rs +++ b/src/librustc/mir/interpret/allocation.rs @@ -406,12 +406,12 @@ impl<'tcx, Tag: Copy, Extra> Allocation { cx: &impl HasDataLayout, ptr: Pointer, size: Size, - ) -> EvalResult<'tcx, &[(Size, (Tag, AllocId))]> { + ) -> &[(Size, (Tag, AllocId))] { // We have to go back `pointer_size - 1` bytes, as that one would still overlap with // the beginning of this range. let start = ptr.offset.bytes().saturating_sub(cx.data_layout().pointer_size.bytes() - 1); let end = ptr.offset + size; // this does overflow checking - Ok(self.relocations.range(Size::from_bytes(start)..end)) + self.relocations.range(Size::from_bytes(start)..end) } /// Check that there ar eno relocations overlapping with the given range. @@ -422,10 +422,10 @@ impl<'tcx, Tag: Copy, Extra> Allocation { ptr: Pointer, size: Size, ) -> EvalResult<'tcx> { - if self.relocations(cx, ptr, size)?.len() != 0 { - err!(ReadPointerAsBytes) - } else { + if self.relocations(cx, ptr, size).is_empty() { Ok(()) + } else { + err!(ReadPointerAsBytes) } } @@ -444,7 +444,7 @@ impl<'tcx, Tag: Copy, Extra> Allocation { // Find the start and end of the given range and its outermost relocations. let (first, last) = { // Find all relocations overlapping the given range. - let relocations = self.relocations(cx, ptr, size)?; + let relocations = self.relocations(cx, ptr, size); if relocations.is_empty() { return Ok(()); } diff --git a/src/librustc_mir/interpret/memory.rs b/src/librustc_mir/interpret/memory.rs index 3119d9ed0ffa3..896a7a25b5d95 100644 --- a/src/librustc_mir/interpret/memory.rs +++ b/src/librustc_mir/interpret/memory.rs @@ -655,7 +655,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> { // (`get_bytes_with_undef_and_ptr` below checks that there are no // relocations overlapping the edges; those would not be handled correctly). let relocations = { - let relocations = self.get(src.alloc_id)?.relocations(self, src, size)?; + let relocations = self.get(src.alloc_id)?.relocations(self, src, size); let mut new_relocations = Vec::with_capacity(relocations.len() * (length as usize)); for i in 0..length { new_relocations.extend( From a835555474c87def84099df412816d5edfa2b9cb Mon Sep 17 00:00:00 2001 From: Oliver Scherer Date: Tue, 13 Nov 2018 10:19:12 +0100 Subject: [PATCH 47/88] Make zst accesses in allocations take the regular path. Speeding up zst accesses should be done on a higher level. --- src/librustc/mir/interpret/allocation.rs | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/src/librustc/mir/interpret/allocation.rs b/src/librustc/mir/interpret/allocation.rs index 42bcd3a902760..ba2755b29f0b8 100644 --- a/src/librustc/mir/interpret/allocation.rs +++ b/src/librustc/mir/interpret/allocation.rs @@ -116,12 +116,7 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra> Allocation { size: Size, allow_ptr_and_undef: bool, ) -> EvalResult<'tcx> { - // Empty accesses don't need to be valid pointers, but they should still be non-NULL let align = Align::from_bytes(1).unwrap(); - if size.bytes() == 0 { - self.check_align(ptr, align)?; - return Ok(()); - } // Check bounds, align and relocations on the edges self.get_bytes_with_undef_and_ptr(cx, ptr, size, align)?; // Check undef and ptr @@ -138,12 +133,7 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra> Allocation { ptr: Pointer, size: Size, ) -> EvalResult<'tcx, &[u8]> { - // Empty accesses don't need to be valid pointers, but they should still be non-NULL let align = Align::from_bytes(1).unwrap(); - if size.bytes() == 0 { - self.check_align(ptr, align)?; - return Ok(&[]); - } self.get_bytes(cx, ptr, size, align) } @@ -153,12 +143,7 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra> Allocation { ptr: Pointer, src: &[u8], ) -> EvalResult<'tcx> { - // Empty accesses don't need to be valid pointers, but they should still be non-NULL let align = Align::from_bytes(1).unwrap(); - if src.is_empty() { - self.check_align(ptr, align)?; - return Ok(()); - } let bytes = self.get_bytes_mut( cx, ptr, Size::from_bytes(src.len() as u64), align, )?; @@ -173,12 +158,7 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra> Allocation { val: u8, count: Size ) -> EvalResult<'tcx> { - // Empty accesses don't need to be valid pointers, but they should still be non-NULL let align = Align::from_bytes(1).unwrap(); - if count.bytes() == 0 { - self.check_align(ptr, align)?; - return Ok(()); - } let bytes = self.get_bytes_mut(cx, ptr, count, align)?; for b in bytes { *b = val; @@ -329,7 +309,6 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra> Allocation { align: Align, check_defined_and_ptr: bool, ) -> EvalResult<'tcx, &[u8]> { - assert_ne!(size.bytes(), 0, "0-sized accesses should never even get a `Pointer`"); self.check_align(ptr.into(), align)?; self.check_bounds(cx, ptr, size)?; From df1ed0c2a68d0a38d98434b734ed042f7b976e5e Mon Sep 17 00:00:00 2001 From: Oliver Scherer Date: Tue, 13 Nov 2018 14:32:39 +0100 Subject: [PATCH 48/88] Make a method that doesn't need `Self` a free function instead --- src/librustc/mir/interpret/allocation.rs | 35 ++++++++++++------------ 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/src/librustc/mir/interpret/allocation.rs b/src/librustc/mir/interpret/allocation.rs index ba2755b29f0b8..bafa32df8486d 100644 --- a/src/librustc/mir/interpret/allocation.rs +++ b/src/librustc/mir/interpret/allocation.rs @@ -176,7 +176,7 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra> Allocation { ) -> EvalResult<'tcx, ScalarMaybeUndef> { // get_bytes_unchecked tests alignment and relocation edges let bytes = self.get_bytes_with_undef_and_ptr( - cx, ptr, size, ptr_align.min(self.int_align(cx, size)) + cx, ptr, size, ptr_align.min(int_align(cx, size)) )?; // Undef check happens *after* we established that the alignment is correct. // We must not return Ok() for unaligned pointers! @@ -272,24 +272,23 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra> Allocation { let ptr_size = cx.data_layout().pointer_size; self.write_scalar(cx, ptr.into(), ptr_align, val, ptr_size) } +} - fn int_align( - &self, - cx: &impl HasDataLayout, - size: Size, - ) -> Align { - // We assume pointer-sized integers have the same alignment as pointers. - // We also assume signed and unsigned integers of the same size have the same alignment. - let ity = match size.bytes() { - 1 => layout::I8, - 2 => layout::I16, - 4 => layout::I32, - 8 => layout::I64, - 16 => layout::I128, - _ => bug!("bad integer size: {}", size.bytes()), - }; - ity.align(cx).abi - } +fn int_align( + cx: &impl HasDataLayout, + size: Size, +) -> Align { + // We assume pointer-sized integers have the same alignment as pointers. + // We also assume signed and unsigned integers of the same size have the same alignment. + let ity = match size.bytes() { + 1 => layout::I8, + 2 => layout::I16, + 4 => layout::I32, + 8 => layout::I64, + 16 => layout::I128, + _ => bug!("bad integer size: {}", size.bytes()), + }; + ity.align(cx).abi } /// Byte accessors From 20dee47a66a802c0e7f0b8884632592ad9311357 Mon Sep 17 00:00:00 2001 From: Oliver Scherer Date: Tue, 13 Nov 2018 14:54:00 +0100 Subject: [PATCH 49/88] Add regression test for integral pointers in zst str slice fat pointers --- src/test/ui/consts/int_ptr_for_zst_slices.rs | 3 +++ .../ui/consts/int_ptr_for_zst_slices.stderr | 23 +++++++++++++++++++ 2 files changed, 26 insertions(+) create mode 100644 src/test/ui/consts/int_ptr_for_zst_slices.rs create mode 100644 src/test/ui/consts/int_ptr_for_zst_slices.stderr diff --git a/src/test/ui/consts/int_ptr_for_zst_slices.rs b/src/test/ui/consts/int_ptr_for_zst_slices.rs new file mode 100644 index 0000000000000..ece8d3d1505c5 --- /dev/null +++ b/src/test/ui/consts/int_ptr_for_zst_slices.rs @@ -0,0 +1,3 @@ +const FOO: &str = unsafe { &*(1_usize as *const [u8; 0] as *const [u8] as *const [str]) }; + +fn main() {} \ No newline at end of file diff --git a/src/test/ui/consts/int_ptr_for_zst_slices.stderr b/src/test/ui/consts/int_ptr_for_zst_slices.stderr new file mode 100644 index 0000000000000..c7b78d98cc880 --- /dev/null +++ b/src/test/ui/consts/int_ptr_for_zst_slices.stderr @@ -0,0 +1,23 @@ +error[E0308]: mismatched types + --> $DIR/int_ptr_for_zst_slices.rs:1:28 + | +LL | const FOO: &str = unsafe { &*(1_usize as *const [u8; 0] as *const [u8] as *const [str]) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected str, found slice + | + = note: expected type `&'static str` + found type `&[str]` + +error[E0277]: the size for values of type `str` cannot be known at compilation time + --> $DIR/int_ptr_for_zst_slices.rs:1:75 + | +LL | const FOO: &str = unsafe { &*(1_usize as *const [u8; 0] as *const [u8] as *const [str]) }; + | ^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `std::marker::Sized` is not implemented for `str` + = note: to learn more, visit + = note: slice and array elements must have `Sized` type + +error: aborting due to 2 previous errors + +Some errors occurred: E0277, E0308. +For more information about an error, try `rustc --explain E0277`. From cc2f46e55a3fe52affe0dd4aee2ececd75c78031 Mon Sep 17 00:00:00 2001 From: Oliver Scherer Date: Tue, 13 Nov 2018 14:55:18 +0100 Subject: [PATCH 50/88] Reorder methods in `allocation.rs` --- src/librustc/mir/interpret/allocation.rs | 170 +++++++++++------------ 1 file changed, 85 insertions(+), 85 deletions(-) diff --git a/src/librustc/mir/interpret/allocation.rs b/src/librustc/mir/interpret/allocation.rs index bafa32df8486d..2e08baa6f5171 100644 --- a/src/librustc/mir/interpret/allocation.rs +++ b/src/librustc/mir/interpret/allocation.rs @@ -89,6 +89,91 @@ impl<'tcx, Tag, Extra> Allocation { } } +/// Byte accessors +impl<'tcx, Tag: Copy, Extra: AllocationExtra> Allocation { + /// The last argument controls whether we error out when there are undefined + /// or pointer bytes. You should never call this, call `get_bytes` or + /// `get_bytes_with_undef_and_ptr` instead, + /// + /// This function also guarantees that the resulting pointer will remain stable + /// even when new allocations are pushed to the `HashMap`. `copy_repeatedly` relies + /// on that. + fn get_bytes_internal( + &self, + cx: &impl HasDataLayout, + ptr: Pointer, + size: Size, + align: Align, + check_defined_and_ptr: bool, + ) -> EvalResult<'tcx, &[u8]> { + self.check_align(ptr.into(), align)?; + self.check_bounds(cx, ptr, size)?; + + if check_defined_and_ptr { + self.check_defined(ptr, size)?; + self.check_relocations(cx, ptr, size)?; + } else { + // We still don't want relocations on the *edges* + self.check_relocation_edges(cx, ptr, size)?; + } + + AllocationExtra::memory_read(self, ptr, size)?; + + assert_eq!(ptr.offset.bytes() as usize as u64, ptr.offset.bytes()); + assert_eq!(size.bytes() as usize as u64, size.bytes()); + let offset = ptr.offset.bytes() as usize; + Ok(&self.bytes[offset..offset + size.bytes() as usize]) + } + + #[inline] + fn get_bytes( + &self, + cx: &impl HasDataLayout, + ptr: Pointer, + size: Size, + align: Align + ) -> EvalResult<'tcx, &[u8]> { + self.get_bytes_internal(cx, ptr, size, align, true) + } + + /// It is the caller's responsibility to handle undefined and pointer bytes. + /// However, this still checks that there are no relocations on the *edges*. + #[inline] + pub fn get_bytes_with_undef_and_ptr( + &self, + cx: &impl HasDataLayout, + ptr: Pointer, + size: Size, + align: Align + ) -> EvalResult<'tcx, &[u8]> { + self.get_bytes_internal(cx, ptr, size, align, false) + } + + /// Just calling this already marks everything as defined and removes relocations, + /// so be sure to actually put data there! + pub fn get_bytes_mut( + &mut self, + cx: &impl HasDataLayout, + ptr: Pointer, + size: Size, + align: Align, + ) -> EvalResult<'tcx, &mut [u8]> { + assert_ne!(size.bytes(), 0, "0-sized accesses should never even get a `Pointer`"); + self.check_align(ptr.into(), align)?; + self.check_bounds(cx, ptr, size)?; + + self.mark_definedness(ptr, size, true)?; + self.clear_relocations(cx, ptr, size)?; + + AllocationExtra::memory_written(self, ptr, size)?; + + assert_eq!(ptr.offset.bytes() as usize as u64, ptr.offset.bytes()); + assert_eq!(size.bytes() as usize as u64, size.bytes()); + let offset = ptr.offset.bytes() as usize; + Ok(&mut self.bytes[offset..offset + size.bytes() as usize]) + } +} + /// Reading and writing impl<'tcx, Tag: Copy, Extra: AllocationExtra> Allocation { pub fn read_c_str( @@ -291,91 +376,6 @@ fn int_align( ity.align(cx).abi } -/// Byte accessors -impl<'tcx, Tag: Copy, Extra: AllocationExtra> Allocation { - /// The last argument controls whether we error out when there are undefined - /// or pointer bytes. You should never call this, call `get_bytes` or - /// `get_bytes_with_undef_and_ptr` instead, - /// - /// This function also guarantees that the resulting pointer will remain stable - /// even when new allocations are pushed to the `HashMap`. `copy_repeatedly` relies - /// on that. - fn get_bytes_internal( - &self, - cx: &impl HasDataLayout, - ptr: Pointer, - size: Size, - align: Align, - check_defined_and_ptr: bool, - ) -> EvalResult<'tcx, &[u8]> { - self.check_align(ptr.into(), align)?; - self.check_bounds(cx, ptr, size)?; - - if check_defined_and_ptr { - self.check_defined(ptr, size)?; - self.check_relocations(cx, ptr, size)?; - } else { - // We still don't want relocations on the *edges* - self.check_relocation_edges(cx, ptr, size)?; - } - - AllocationExtra::memory_read(self, ptr, size)?; - - assert_eq!(ptr.offset.bytes() as usize as u64, ptr.offset.bytes()); - assert_eq!(size.bytes() as usize as u64, size.bytes()); - let offset = ptr.offset.bytes() as usize; - Ok(&self.bytes[offset..offset + size.bytes() as usize]) - } - - #[inline] - fn get_bytes( - &self, - cx: &impl HasDataLayout, - ptr: Pointer, - size: Size, - align: Align - ) -> EvalResult<'tcx, &[u8]> { - self.get_bytes_internal(cx, ptr, size, align, true) - } - - /// It is the caller's responsibility to handle undefined and pointer bytes. - /// However, this still checks that there are no relocations on the *edges*. - #[inline] - pub fn get_bytes_with_undef_and_ptr( - &self, - cx: &impl HasDataLayout, - ptr: Pointer, - size: Size, - align: Align - ) -> EvalResult<'tcx, &[u8]> { - self.get_bytes_internal(cx, ptr, size, align, false) - } - - /// Just calling this already marks everything as defined and removes relocations, - /// so be sure to actually put data there! - pub fn get_bytes_mut( - &mut self, - cx: &impl HasDataLayout, - ptr: Pointer, - size: Size, - align: Align, - ) -> EvalResult<'tcx, &mut [u8]> { - assert_ne!(size.bytes(), 0, "0-sized accesses should never even get a `Pointer`"); - self.check_align(ptr.into(), align)?; - self.check_bounds(cx, ptr, size)?; - - self.mark_definedness(ptr, size, true)?; - self.clear_relocations(cx, ptr, size)?; - - AllocationExtra::memory_written(self, ptr, size)?; - - assert_eq!(ptr.offset.bytes() as usize as u64, ptr.offset.bytes()); - assert_eq!(size.bytes() as usize as u64, size.bytes()); - let offset = ptr.offset.bytes() as usize; - Ok(&mut self.bytes[offset..offset + size.bytes() as usize]) - } -} - /// Relocations impl<'tcx, Tag: Copy, Extra> Allocation { /// Return all relocations overlapping with the given ptr-offset pair. From ebf03363f2d2385bc2248549cd93fca42779699a Mon Sep 17 00:00:00 2001 From: Oliver Scherer Date: Tue, 13 Nov 2018 15:36:05 +0100 Subject: [PATCH 51/88] Properly test for int pointers in fat pointers to str slices of zero chars --- src/test/ui/consts/int_ptr_for_zst_slices.rs | 7 +++-- .../ui/consts/int_ptr_for_zst_slices.stderr | 26 +++++-------------- 2 files changed, 12 insertions(+), 21 deletions(-) diff --git a/src/test/ui/consts/int_ptr_for_zst_slices.rs b/src/test/ui/consts/int_ptr_for_zst_slices.rs index ece8d3d1505c5..809cc15be0c3f 100644 --- a/src/test/ui/consts/int_ptr_for_zst_slices.rs +++ b/src/test/ui/consts/int_ptr_for_zst_slices.rs @@ -1,3 +1,6 @@ -const FOO: &str = unsafe { &*(1_usize as *const [u8; 0] as *const [u8] as *const [str]) }; +#![feature(const_raw_ptr_deref)] -fn main() {} \ No newline at end of file +const FOO: &str = unsafe { &*(1_usize as *const [u8; 0] as *const [u8] as *const str) }; +//~^ ERROR it is undefined behaviour to use this value + +fn main() {} diff --git a/src/test/ui/consts/int_ptr_for_zst_slices.stderr b/src/test/ui/consts/int_ptr_for_zst_slices.stderr index c7b78d98cc880..437e6952e740a 100644 --- a/src/test/ui/consts/int_ptr_for_zst_slices.stderr +++ b/src/test/ui/consts/int_ptr_for_zst_slices.stderr @@ -1,23 +1,11 @@ -error[E0308]: mismatched types - --> $DIR/int_ptr_for_zst_slices.rs:1:28 +error[E0080]: it is undefined behavior to use this value + --> $DIR/int_ptr_for_zst_slices.rs:3:1 | -LL | const FOO: &str = unsafe { &*(1_usize as *const [u8; 0] as *const [u8] as *const [str]) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected str, found slice +LL | const FOO: &str = unsafe { &*(1_usize as *const [u8; 0] as *const [u8] as *const str) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered uninitialized or non-UTF-8 data in str at . | - = note: expected type `&'static str` - found type `&[str]` + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rust compiler repository if you believe it should not be considered undefined behavior -error[E0277]: the size for values of type `str` cannot be known at compilation time - --> $DIR/int_ptr_for_zst_slices.rs:1:75 - | -LL | const FOO: &str = unsafe { &*(1_usize as *const [u8; 0] as *const [u8] as *const [str]) }; - | ^^^^^^^^^^^^ doesn't have a size known at compile-time - | - = help: the trait `std::marker::Sized` is not implemented for `str` - = note: to learn more, visit - = note: slice and array elements must have `Sized` type - -error: aborting due to 2 previous errors +error: aborting due to previous error -Some errors occurred: E0277, E0308. -For more information about an error, try `rustc --explain E0277`. +For more information about this error, try `rustc --explain E0080`. From ef332959dc42f25da567ae9a345ad395a19234a1 Mon Sep 17 00:00:00 2001 From: Oliver Scherer Date: Tue, 13 Nov 2018 16:13:51 +0100 Subject: [PATCH 52/88] Reintroduce zst-slice reading `read_bytes` method on `Memory` --- src/librustc_mir/interpret/memory.rs | 16 ++++++++++++++++ src/librustc_mir/interpret/operand.rs | 5 +---- src/test/ui/consts/int_ptr_for_zst_slices.rs | 3 ++- 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/src/librustc_mir/interpret/memory.rs b/src/librustc_mir/interpret/memory.rs index 896a7a25b5d95..b468943cbf0b1 100644 --- a/src/librustc_mir/interpret/memory.rs +++ b/src/librustc_mir/interpret/memory.rs @@ -567,6 +567,22 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> { } } +/// Byte Accessors +impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> { + pub fn read_bytes( + &self, + ptr: Scalar, + size: Size, + ) -> EvalResult<'tcx, &[u8]> { + if size.bytes() == 0 { + Ok(&[]) + } else { + let ptr = ptr.to_ptr()?; + self.get(ptr.alloc_id)?.read_bytes(self, ptr, size) + } + } +} + /// Interning (for CTFE) impl<'a, 'mir, 'tcx, M> Memory<'a, 'mir, 'tcx, M> where diff --git a/src/librustc_mir/interpret/operand.rs b/src/librustc_mir/interpret/operand.rs index 3f5f0ebed72d0..0fb5b59e4420b 100644 --- a/src/librustc_mir/interpret/operand.rs +++ b/src/librustc_mir/interpret/operand.rs @@ -351,10 +351,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> mplace: MPlaceTy<'tcx, M::PointerTag>, ) -> EvalResult<'tcx, &str> { let len = mplace.len(self)?; - let ptr = mplace.ptr.to_ptr()?; - let bytes = self.memory - .get(ptr.alloc_id)? - .read_bytes(self, ptr, Size::from_bytes(len as u64))?; + let bytes = self.memory.read_bytes(mplace.ptr, Size::from_bytes(len as u64))?; let str = ::std::str::from_utf8(bytes) .map_err(|err| EvalErrorKind::ValidationFailure(err.to_string()))?; Ok(str) diff --git a/src/test/ui/consts/int_ptr_for_zst_slices.rs b/src/test/ui/consts/int_ptr_for_zst_slices.rs index 809cc15be0c3f..afa2c6a5b9e1c 100644 --- a/src/test/ui/consts/int_ptr_for_zst_slices.rs +++ b/src/test/ui/consts/int_ptr_for_zst_slices.rs @@ -1,6 +1,7 @@ +// compile-pass + #![feature(const_raw_ptr_deref)] const FOO: &str = unsafe { &*(1_usize as *const [u8; 0] as *const [u8] as *const str) }; -//~^ ERROR it is undefined behaviour to use this value fn main() {} From 87bd5d13d8a175a0be82fe2b1db5588036c4bdb6 Mon Sep 17 00:00:00 2001 From: Oliver Scherer Date: Tue, 13 Nov 2018 17:23:26 +0100 Subject: [PATCH 53/88] Remove stderr file, because the test passes now --- src/test/ui/consts/int_ptr_for_zst_slices.stderr | 11 ----------- 1 file changed, 11 deletions(-) delete mode 100644 src/test/ui/consts/int_ptr_for_zst_slices.stderr diff --git a/src/test/ui/consts/int_ptr_for_zst_slices.stderr b/src/test/ui/consts/int_ptr_for_zst_slices.stderr deleted file mode 100644 index 437e6952e740a..0000000000000 --- a/src/test/ui/consts/int_ptr_for_zst_slices.stderr +++ /dev/null @@ -1,11 +0,0 @@ -error[E0080]: it is undefined behavior to use this value - --> $DIR/int_ptr_for_zst_slices.rs:3:1 - | -LL | const FOO: &str = unsafe { &*(1_usize as *const [u8; 0] as *const [u8] as *const str) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered uninitialized or non-UTF-8 data in str at . - | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rust compiler repository if you believe it should not be considered undefined behavior - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0080`. From 65b702c6b108dfa5e2c3fa891d4f96755293ddb3 Mon Sep 17 00:00:00 2001 From: Oliver Scherer Date: Wed, 14 Nov 2018 10:56:09 +0100 Subject: [PATCH 54/88] Update miri submodule --- Cargo.lock | 8 ++++---- src/tools/miri | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f7d5a1a507bab..dc9296b81e223 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -306,7 +306,7 @@ dependencies = [ "clippy-mini-macro-test 0.2.0", "clippy_dev 0.0.1", "clippy_lints 0.0.212", - "compiletest_rs 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)", + "compiletest_rs 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)", "derive-new 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "regex 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", @@ -423,7 +423,7 @@ dependencies = [ [[package]] name = "compiletest_rs" -version = "0.3.16" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "diff 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1315,7 +1315,7 @@ dependencies = [ "byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "cargo_metadata 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", "colored 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)", - "compiletest_rs 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)", + "compiletest_rs 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.5.12 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", "vergen 3.0.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3274,7 +3274,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum colored 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b0aa3473e85a3161b59845d6096b289bb577874cafeaf75ea1b1beaa6572c7fc" "checksum commoncrypto 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d056a8586ba25a1e4d61cb090900e495952c7886786fc55f909ab2f819b69007" "checksum commoncrypto-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1fed34f46747aa73dfaa578069fd8279d2818ade2b55f38f22a9401c7f4083e2" -"checksum compiletest_rs 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)" = "75e809f56d6aa9575b67924b0af686c4f4c1380314f47947e235e9ff7fa94bed" +"checksum compiletest_rs 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)" = "89747fe073b7838343bd2c2445e7a7c2e0d415598f8925f0fa9205b9cdfc48cb" "checksum core-foundation 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cc3532ec724375c7cb7ff0a097b714fde180bb1f6ed2ab27cfcd99ffca873cd2" "checksum core-foundation-sys 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a3fb15cdbdd9cf8b82d97d0296bb5cd3631bba58d6e31650a002a8e7fb5721f9" "checksum crossbeam 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "24ce9782d4d5c53674646a6a4c1863a21a8fc0cb649b3c94dfc16e45071dea19" diff --git a/src/tools/miri b/src/tools/miri index bbb1d80703f27..06758c48bd7a7 160000 --- a/src/tools/miri +++ b/src/tools/miri @@ -1 +1 @@ -Subproject commit bbb1d80703f272a5592ceeb3832a489776512251 +Subproject commit 06758c48bd7a77bb5aa43fc50cf344540ba5afef From b820cc79a93a8867b1fe51ab628663671843a4ed Mon Sep 17 00:00:00 2001 From: Oliver Scherer Date: Wed, 14 Nov 2018 11:45:10 +0100 Subject: [PATCH 55/88] Clean up array/slice of primitive validation --- src/librustc_mir/interpret/validity.rs | 23 ++++++++----------- src/test/ui/consts/validate_never_arrays.rs | 5 ++++ .../ui/consts/validate_never_arrays.stderr | 11 +++++++++ 3 files changed, 26 insertions(+), 13 deletions(-) create mode 100644 src/test/ui/consts/validate_never_arrays.rs create mode 100644 src/test/ui/consts/validate_never_arrays.stderr diff --git a/src/librustc_mir/interpret/validity.rs b/src/librustc_mir/interpret/validity.rs index b3a82cd70232a..4b9ded4c17ee9 100644 --- a/src/librustc_mir/interpret/validity.rs +++ b/src/librustc_mir/interpret/validity.rs @@ -21,7 +21,7 @@ use rustc::mir::interpret::{ }; use super::{ - OpTy, MPlaceTy, Machine, EvalContext, ValueVisitor, Operand, + OpTy, Machine, EvalContext, ValueVisitor, }; macro_rules! validation_failure { @@ -522,25 +522,22 @@ impl<'rt, 'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> _ => false, } } => { - let mplace = match *op { - // it's a ZST, the memory content cannot matter - Operand::Immediate(_) if op.layout.is_zst() => - // invent an aligned mplace - MPlaceTy::dangling(op.layout, self.ecx), - // FIXME: what about single element arrays? They can be Scalar layout I think - Operand::Immediate(_) => bug!("non-ZST array/slice cannot be immediate"), - Operand::Indirect(_) => op.to_mem_place(), - }; + if op.layout.is_zst() { + return Ok(()); + } + // non-ZST array cannot be immediate, slices are never immediate + let mplace = op.to_mem_place(); // This is the length of the array/slice. let len = mplace.len(self.ecx)?; + // zero length slices have nothing to be checked + if len == 0 { + return Ok(()); + } // This is the element type size. let ty_size = self.ecx.layout_of(tys)?.size; // This is the size in bytes of the whole array. let size = ty_size * len; - if op.layout.is_zst() { - return self.ecx.memory.check_align(mplace.ptr, op.layout.align); - } let ptr = mplace.ptr.to_ptr()?; // NOTE: Keep this in sync with the handling of integer and float diff --git a/src/test/ui/consts/validate_never_arrays.rs b/src/test/ui/consts/validate_never_arrays.rs new file mode 100644 index 0000000000000..9610b7b22f161 --- /dev/null +++ b/src/test/ui/consts/validate_never_arrays.rs @@ -0,0 +1,5 @@ +#![feature(const_raw_ptr_deref, never_type)] + +const FOO: &[!; 1] = unsafe { &*(1_usize as *const [!; 1]) }; //~ ERROR undefined behavior + +fn main() {} diff --git a/src/test/ui/consts/validate_never_arrays.stderr b/src/test/ui/consts/validate_never_arrays.stderr new file mode 100644 index 0000000000000..0b639240bb348 --- /dev/null +++ b/src/test/ui/consts/validate_never_arrays.stderr @@ -0,0 +1,11 @@ +error[E0080]: it is undefined behavior to use this value + --> $DIR/validate_never_arrays.rs:3:1 + | +LL | const FOO: &[!; 1] = unsafe { &*(1_usize as *const [!; 1]) }; //~ ERROR undefined behavior + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a value of an uninhabited type at .[0] + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rust compiler repository if you believe it should not be considered undefined behavior + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0080`. From d3139b9c4157957aa40f212f5d423db4359001a9 Mon Sep 17 00:00:00 2001 From: Oliver Scherer Date: Wed, 14 Nov 2018 12:19:56 +0100 Subject: [PATCH 56/88] Rebase fallout --- src/librustc_mir/interpret/memory.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustc_mir/interpret/memory.rs b/src/librustc_mir/interpret/memory.rs index b468943cbf0b1..c7c6dd1284646 100644 --- a/src/librustc_mir/interpret/memory.rs +++ b/src/librustc_mir/interpret/memory.rs @@ -30,7 +30,7 @@ use syntax::ast::Mutability; use super::{ Pointer, AllocId, Allocation, GlobalId, AllocationExtra, InboundsCheck, EvalResult, Scalar, EvalErrorKind, AllocType, PointerArithmetic, - Machine, AllocMap, MayLeak, ErrorHandled, + Machine, AllocMap, MayLeak, ErrorHandled, AllocationExtra, }; #[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)] From 1c08ced99589114164f057bc4cd020a6bad48af9 Mon Sep 17 00:00:00 2001 From: Oliver Scherer Date: Wed, 14 Nov 2018 13:28:38 +0100 Subject: [PATCH 57/88] Explain early abort legality --- src/librustc_mir/interpret/validity.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/librustc_mir/interpret/validity.rs b/src/librustc_mir/interpret/validity.rs index 4b9ded4c17ee9..8ce5a0365cf65 100644 --- a/src/librustc_mir/interpret/validity.rs +++ b/src/librustc_mir/interpret/validity.rs @@ -522,6 +522,7 @@ impl<'rt, 'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> _ => false, } } => { + // bailing out for zsts is ok, since the array element type can only be int/float if op.layout.is_zst() { return Ok(()); } From 8b04b098695951f561ef4eedf33c6fbc63728caa Mon Sep 17 00:00:00 2001 From: Oliver Scherer Date: Thu, 15 Nov 2018 14:43:58 +0100 Subject: [PATCH 58/88] Move alignment checks out of `Allocation` --- src/librustc/mir/interpret/allocation.rs | 69 +++++------------------- src/librustc_mir/interpret/memory.rs | 12 ++--- src/librustc_mir/interpret/operand.rs | 11 ++-- src/librustc_mir/interpret/place.rs | 16 +++--- src/librustc_mir/interpret/terminator.rs | 3 +- src/librustc_mir/interpret/traits.rs | 17 +++--- 6 files changed, 43 insertions(+), 85 deletions(-) diff --git a/src/librustc/mir/interpret/allocation.rs b/src/librustc/mir/interpret/allocation.rs index 2e08baa6f5171..b2c4454880575 100644 --- a/src/librustc/mir/interpret/allocation.rs +++ b/src/librustc/mir/interpret/allocation.rs @@ -15,7 +15,7 @@ use super::{ truncate, }; -use ty::layout::{self, Size, Align}; +use ty::layout::{Size, Align}; use syntax::ast::Mutability; use std::iter; use mir; @@ -103,10 +103,8 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra> Allocation { cx: &impl HasDataLayout, ptr: Pointer, size: Size, - align: Align, check_defined_and_ptr: bool, ) -> EvalResult<'tcx, &[u8]> { - self.check_align(ptr.into(), align)?; self.check_bounds(cx, ptr, size)?; if check_defined_and_ptr { @@ -126,14 +124,13 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra> Allocation { } #[inline] - fn get_bytes( + pub fn get_bytes( &self, cx: &impl HasDataLayout, ptr: Pointer, size: Size, - align: Align ) -> EvalResult<'tcx, &[u8]> { - self.get_bytes_internal(cx, ptr, size, align, true) + self.get_bytes_internal(cx, ptr, size, true) } /// It is the caller's responsibility to handle undefined and pointer bytes. @@ -144,9 +141,8 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra> Allocation { cx: &impl HasDataLayout, ptr: Pointer, size: Size, - align: Align ) -> EvalResult<'tcx, &[u8]> { - self.get_bytes_internal(cx, ptr, size, align, false) + self.get_bytes_internal(cx, ptr, size, false) } /// Just calling this already marks everything as defined and removes relocations, @@ -156,10 +152,8 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra> Allocation { cx: &impl HasDataLayout, ptr: Pointer, size: Size, - align: Align, ) -> EvalResult<'tcx, &mut [u8]> { assert_ne!(size.bytes(), 0, "0-sized accesses should never even get a `Pointer`"); - self.check_align(ptr.into(), align)?; self.check_bounds(cx, ptr, size)?; self.mark_definedness(ptr, size, true)?; @@ -201,9 +195,8 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra> Allocation { size: Size, allow_ptr_and_undef: bool, ) -> EvalResult<'tcx> { - let align = Align::from_bytes(1).unwrap(); // Check bounds, align and relocations on the edges - self.get_bytes_with_undef_and_ptr(cx, ptr, size, align)?; + self.get_bytes_with_undef_and_ptr(cx, ptr, size)?; // Check undef and ptr if !allow_ptr_and_undef { self.check_defined(ptr, size)?; @@ -212,26 +205,13 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra> Allocation { Ok(()) } - pub fn read_bytes( - &self, - cx: &impl HasDataLayout, - ptr: Pointer, - size: Size, - ) -> EvalResult<'tcx, &[u8]> { - let align = Align::from_bytes(1).unwrap(); - self.get_bytes(cx, ptr, size, align) - } - pub fn write_bytes( &mut self, cx: &impl HasDataLayout, ptr: Pointer, src: &[u8], ) -> EvalResult<'tcx> { - let align = Align::from_bytes(1).unwrap(); - let bytes = self.get_bytes_mut( - cx, ptr, Size::from_bytes(src.len() as u64), align, - )?; + let bytes = self.get_bytes_mut(cx, ptr, Size::from_bytes(src.len() as u64))?; bytes.clone_from_slice(src); Ok(()) } @@ -243,8 +223,7 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra> Allocation { val: u8, count: Size ) -> EvalResult<'tcx> { - let align = Align::from_bytes(1).unwrap(); - let bytes = self.get_bytes_mut(cx, ptr, count, align)?; + let bytes = self.get_bytes_mut(cx, ptr, count)?; for b in bytes { *b = val; } @@ -256,13 +235,10 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra> Allocation { &self, cx: &impl HasDataLayout, ptr: Pointer, - ptr_align: Align, size: Size ) -> EvalResult<'tcx, ScalarMaybeUndef> { - // get_bytes_unchecked tests alignment and relocation edges - let bytes = self.get_bytes_with_undef_and_ptr( - cx, ptr, size, ptr_align.min(int_align(cx, size)) - )?; + // get_bytes_unchecked tests relocation edges + let bytes = self.get_bytes_with_undef_and_ptr(cx, ptr, size)?; // Undef check happens *after* we established that the alignment is correct. // We must not return Ok() for unaligned pointers! if self.check_defined(ptr, size).is_err() { @@ -293,9 +269,8 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra> Allocation { &self, cx: &impl HasDataLayout, ptr: Pointer, - ptr_align: Align ) -> EvalResult<'tcx, ScalarMaybeUndef> { - self.read_scalar(cx, ptr, ptr_align, cx.data_layout().pointer_size) + self.read_scalar(cx, ptr, cx.data_layout().pointer_size) } /// Write a *non-ZST* scalar @@ -303,7 +278,6 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra> Allocation { &mut self, cx: &impl HasDataLayout, ptr: Pointer, - ptr_align: Align, val: ScalarMaybeUndef, type_size: Size, ) -> EvalResult<'tcx> { @@ -327,9 +301,8 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra> Allocation { }; { - // get_bytes_mut checks alignment let endian = cx.data_layout().endian; - let dst = self.get_bytes_mut(cx, ptr, type_size, ptr_align)?; + let dst = self.get_bytes_mut(cx, ptr, type_size)?; write_target_uint(endian, dst, bytes).unwrap(); } @@ -351,31 +324,13 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra> Allocation { &mut self, cx: &impl HasDataLayout, ptr: Pointer, - ptr_align: Align, val: ScalarMaybeUndef ) -> EvalResult<'tcx> { let ptr_size = cx.data_layout().pointer_size; - self.write_scalar(cx, ptr.into(), ptr_align, val, ptr_size) + self.write_scalar(cx, ptr.into(), val, ptr_size) } } -fn int_align( - cx: &impl HasDataLayout, - size: Size, -) -> Align { - // We assume pointer-sized integers have the same alignment as pointers. - // We also assume signed and unsigned integers of the same size have the same alignment. - let ity = match size.bytes() { - 1 => layout::I8, - 2 => layout::I16, - 4 => layout::I32, - 8 => layout::I64, - 16 => layout::I128, - _ => bug!("bad integer size: {}", size.bytes()), - }; - ity.align(cx).abi -} - /// Relocations impl<'tcx, Tag: Copy, Extra> Allocation { /// Return all relocations overlapping with the given ptr-offset pair. diff --git a/src/librustc_mir/interpret/memory.rs b/src/librustc_mir/interpret/memory.rs index c7c6dd1284646..2e47d7e69d9a5 100644 --- a/src/librustc_mir/interpret/memory.rs +++ b/src/librustc_mir/interpret/memory.rs @@ -578,7 +578,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> { Ok(&[]) } else { let ptr = ptr.to_ptr()?; - self.get(ptr.alloc_id)?.read_bytes(self, ptr, size) + self.get(ptr.alloc_id)?.get_bytes(self, ptr, size) } } } @@ -656,10 +656,10 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> { length: u64, nonoverlapping: bool, ) -> EvalResult<'tcx> { + self.check_align(src, src_align)?; + self.check_align(dest, dest_align)?; if size.bytes() == 0 { // Nothing to do for ZST, other than checking alignment and non-NULLness. - self.check_align(src, src_align)?; - self.check_align(dest, dest_align)?; return Ok(()); } let src = src.to_ptr()?; @@ -689,12 +689,12 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> { let tcx = self.tcx.tcx; - // This also checks alignment, and relocation edges on the src. + // This checks relocation edges on the src. let src_bytes = self.get(src.alloc_id)? - .get_bytes_with_undef_and_ptr(&tcx, src, size, src_align)? + .get_bytes_with_undef_and_ptr(&tcx, src, size)? .as_ptr(); let dest_bytes = self.get_mut(dest.alloc_id)? - .get_bytes_mut(&tcx, dest, size * length, dest_align)? + .get_bytes_mut(&tcx, dest, size * length)? .as_mut_ptr(); // SAFE: The above indexing would have panicked if there weren't at least `size` bytes diff --git a/src/librustc_mir/interpret/operand.rs b/src/librustc_mir/interpret/operand.rs index 0fb5b59e4420b..ba995afddc813 100644 --- a/src/librustc_mir/interpret/operand.rs +++ b/src/librustc_mir/interpret/operand.rs @@ -275,12 +275,14 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> return Ok(Some(Immediate::Scalar(Scalar::zst().into()))); } + // check for integer pointers before alignment to report better errors let ptr = ptr.to_ptr()?; + self.memory.check_align(ptr.into(), ptr_align)?; match mplace.layout.abi { layout::Abi::Scalar(..) => { let scalar = self.memory .get(ptr.alloc_id)? - .read_scalar(self, ptr, ptr_align, mplace.layout.size)?; + .read_scalar(self, ptr, mplace.layout.size)?; Ok(Some(Immediate::Scalar(scalar))) } layout::Abi::ScalarPair(ref a, ref b) => { @@ -289,13 +291,14 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> let a_ptr = ptr; let b_offset = a_size.align_to(b.align(self).abi); assert!(b_offset.bytes() > 0); // we later use the offset to test which field to use - let b_ptr = ptr.offset(b_offset, self)?.into(); + let b_ptr = ptr.offset(b_offset, self)?; let a_val = self.memory .get(ptr.alloc_id)? - .read_scalar(self, a_ptr, ptr_align, a_size)?; + .read_scalar(self, a_ptr, a_size)?; + self.memory.check_align(b_ptr.into(), b.align(self))?; let b_val = self.memory .get(ptr.alloc_id)? - .read_scalar(self, b_ptr, ptr_align, b_size)?; + .read_scalar(self, b_ptr, b_size)?; Ok(Some(Immediate::ScalarPair(a_val, b_val))) } _ => Ok(None), diff --git a/src/librustc_mir/interpret/place.rs b/src/librustc_mir/interpret/place.rs index e2b6c00ba382c..6317cfb94d27f 100644 --- a/src/librustc_mir/interpret/place.rs +++ b/src/librustc_mir/interpret/place.rs @@ -713,11 +713,12 @@ where // Nothing to do for ZSTs, other than checking alignment if dest.layout.is_zst() { - self.memory.check_align(ptr, ptr_align)?; - return Ok(()); + return self.memory.check_align(ptr, ptr_align); } + // check for integer pointers before alignment to report better errors let ptr = ptr.to_ptr()?; + self.memory.check_align(ptr.into(), ptr_align)?; let tcx = &*self.tcx; // FIXME: We should check that there are dest.layout.size many bytes available in // memory. The code below is not sufficient, with enough padding it might not @@ -729,9 +730,8 @@ where _ => bug!("write_immediate_to_mplace: invalid Scalar layout: {:#?}", dest.layout) } - self.memory.get_mut(ptr.alloc_id)?.write_scalar( - tcx, ptr, ptr_align.min(dest.layout.align.abi), scalar, dest.layout.size + tcx, ptr, scalar, dest.layout.size ) } Immediate::ScalarPair(a_val, b_val) => { @@ -741,20 +741,22 @@ where dest.layout) }; let (a_size, b_size) = (a.size(self), b.size(self)); - let (a_align, b_align) = (a.align(self).abi, b.align(self).abi); + let b_align = b.align(self).abi; let b_offset = a_size.align_to(b_align); let b_ptr = ptr.offset(b_offset, self)?; + self.memory.check_align(b_ptr.into(), ptr_align.min(b_align))?; + // It is tempting to verify `b_offset` against `layout.fields.offset(1)`, // but that does not work: We could be a newtype around a pair, then the // fields do not match the `ScalarPair` components. self.memory .get_mut(ptr.alloc_id)? - .write_scalar(tcx, ptr, ptr_align.min(a_align), a_val, a_size)?; + .write_scalar(tcx, ptr, a_val, a_size)?; self.memory .get_mut(b_ptr.alloc_id)? - .write_scalar(tcx, b_ptr, ptr_align.min(b_align), b_val, b_size) + .write_scalar(tcx, b_ptr, b_val, b_size) } } } diff --git a/src/librustc_mir/interpret/terminator.rs b/src/librustc_mir/interpret/terminator.rs index 9e59611125d92..617d204fe1042 100644 --- a/src/librustc_mir/interpret/terminator.rs +++ b/src/librustc_mir/interpret/terminator.rs @@ -401,13 +401,12 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> // cannot use the shim here, because that will only result in infinite recursion ty::InstanceDef::Virtual(_, idx) => { let ptr_size = self.pointer_size(); - let ptr_align = self.tcx.data_layout.pointer_align.abi; let ptr = self.deref_operand(args[0])?; let vtable = ptr.vtable()?; + self.memory.check_align(vtable.into(), self.tcx.data_layout.pointer_align.abi)?; let fn_ptr = self.memory.get(vtable.alloc_id)?.read_ptr_sized( self, vtable.offset(ptr_size * (idx as u64 + 3), self)?, - ptr_align )?.to_ptr()?; let instance = self.memory.get_fn(fn_ptr)?; diff --git a/src/librustc_mir/interpret/traits.rs b/src/librustc_mir/interpret/traits.rs index 8e39574c01e98..37979c8ee664c 100644 --- a/src/librustc_mir/interpret/traits.rs +++ b/src/librustc_mir/interpret/traits.rs @@ -61,16 +61,16 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> let drop = self.memory.create_fn_alloc(drop).with_default_tag(); self.memory .get_mut(vtable.alloc_id)? - .write_ptr_sized(tcx, vtable, ptr_align, Scalar::Ptr(drop).into())?; + .write_ptr_sized(tcx, vtable, Scalar::Ptr(drop).into())?; let size_ptr = vtable.offset(ptr_size, self)?; self.memory .get_mut(size_ptr.alloc_id)? - .write_ptr_sized(tcx, size_ptr, ptr_align, Scalar::from_uint(size, ptr_size).into())?; + .write_ptr_sized(tcx, size_ptr, Scalar::from_uint(size, ptr_size).into())?; let align_ptr = vtable.offset(ptr_size * 2, self)?; self.memory .get_mut(align_ptr.alloc_id)? - .write_ptr_sized(tcx, align_ptr, ptr_align, Scalar::from_uint(align, ptr_size).into())?; + .write_ptr_sized(tcx, align_ptr, Scalar::from_uint(align, ptr_size).into())?; for (i, method) in methods.iter().enumerate() { if let Some((def_id, substs)) = *method { @@ -79,7 +79,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> let method_ptr = vtable.offset(ptr_size * (3 + i as u64), self)?; self.memory .get_mut(method_ptr.alloc_id)? - .write_ptr_sized(tcx, method_ptr, ptr_align, Scalar::Ptr(fn_ptr).into())?; + .write_ptr_sized(tcx, method_ptr, Scalar::Ptr(fn_ptr).into())?; } } @@ -95,10 +95,10 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> vtable: Pointer, ) -> EvalResult<'tcx, (ty::Instance<'tcx>, ty::Ty<'tcx>)> { // we don't care about the pointee type, we just want a pointer - let pointer_align = self.tcx.data_layout.pointer_align.abi; + self.memory.check_align(vtable.into(), self.tcx.data_layout.pointer_align.abi)?; let drop_fn = self.memory .get(vtable.alloc_id)? - .read_ptr_sized(self, vtable, pointer_align)? + .read_ptr_sized(self, vtable)? .to_ptr()?; let drop_instance = self.memory.get_fn(drop_fn)?; trace!("Found drop fn: {:?}", drop_instance); @@ -114,14 +114,13 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> vtable: Pointer, ) -> EvalResult<'tcx, (Size, Align)> { let pointer_size = self.pointer_size(); - let pointer_align = self.tcx.data_layout.pointer_align.abi; + self.memory.check_align(vtable.into(), self.tcx.data_layout.pointer_align.abi)?; let alloc = self.memory.get(vtable.alloc_id)?; - let size = alloc.read_ptr_sized(self, vtable.offset(pointer_size, self)?, pointer_align)? + let size = alloc.read_ptr_sized(self, vtable.offset(pointer_size, self)?)? .to_bits(pointer_size)? as u64; let align = alloc.read_ptr_sized( self, vtable.offset(pointer_size * 2, self)?, - pointer_align )?.to_bits(pointer_size)? as u64; Ok((Size::from_bytes(size), Align::from_bytes(align).unwrap())) } From 9d57adf2ba57420903b4c9b4dbb492c3fcf691b8 Mon Sep 17 00:00:00 2001 From: Oliver Scherer Date: Thu, 15 Nov 2018 14:48:34 +0100 Subject: [PATCH 59/88] Explain {read,write}_scalar failure to cope with zsts --- src/librustc/mir/interpret/allocation.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/librustc/mir/interpret/allocation.rs b/src/librustc/mir/interpret/allocation.rs index b2c4454880575..2ecd5eb5a6269 100644 --- a/src/librustc/mir/interpret/allocation.rs +++ b/src/librustc/mir/interpret/allocation.rs @@ -231,6 +231,11 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra> Allocation { } /// Read a *non-ZST* scalar + /// + /// zsts can't be read out of two reasons: + /// * byteorder cannot work with zero element buffers + /// * in oder to obtain a `Pointer` we need to check for ZSTness anyway due to integer pointers + /// being valid for ZSTs pub fn read_scalar( &self, cx: &impl HasDataLayout, @@ -274,6 +279,11 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra> Allocation { } /// Write a *non-ZST* scalar + /// + /// zsts can't be read out of two reasons: + /// * byteorder cannot work with zero element buffers + /// * in oder to obtain a `Pointer` we need to check for ZSTness anyway due to integer pointers + /// being valid for ZSTs pub fn write_scalar( &mut self, cx: &impl HasDataLayout, From 927c5aab47c624fe9f4cf02289a2acccd425432c Mon Sep 17 00:00:00 2001 From: Oliver Scherer Date: Thu, 15 Nov 2018 17:47:11 +0100 Subject: [PATCH 60/88] Use correct alignment for fat pointer extra part --- src/librustc_mir/interpret/operand.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustc_mir/interpret/operand.rs b/src/librustc_mir/interpret/operand.rs index ba995afddc813..61c3c6f24ffb8 100644 --- a/src/librustc_mir/interpret/operand.rs +++ b/src/librustc_mir/interpret/operand.rs @@ -295,7 +295,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> let a_val = self.memory .get(ptr.alloc_id)? .read_scalar(self, a_ptr, a_size)?; - self.memory.check_align(b_ptr.into(), b.align(self))?; + self.memory.check_align(b_ptr.into(), b.align(self).min(ptr_align))?; let b_val = self.memory .get(ptr.alloc_id)? .read_scalar(self, b_ptr, b_size)?; From 9b8e82ad242ba438b0db983248c59281b4355891 Mon Sep 17 00:00:00 2001 From: Oliver Scherer Date: Thu, 15 Nov 2018 18:05:15 +0100 Subject: [PATCH 61/88] Use correct alignment checks for scalars and zsts, too --- src/librustc_mir/interpret/operand.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/librustc_mir/interpret/operand.rs b/src/librustc_mir/interpret/operand.rs index 61c3c6f24ffb8..4ec01b6ca1032 100644 --- a/src/librustc_mir/interpret/operand.rs +++ b/src/librustc_mir/interpret/operand.rs @@ -271,13 +271,13 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> if mplace.layout.is_zst() { // Not all ZSTs have a layout we would handle below, so just short-circuit them // all here. - self.memory.check_align(ptr, ptr_align)?; + self.memory.check_align(ptr, ptr_align.min(mplace.layout.align))?; return Ok(Some(Immediate::Scalar(Scalar::zst().into()))); } // check for integer pointers before alignment to report better errors let ptr = ptr.to_ptr()?; - self.memory.check_align(ptr.into(), ptr_align)?; + self.memory.check_align(ptr.into(), ptr_align.min(mplace.layout.align))?; match mplace.layout.abi { layout::Abi::Scalar(..) => { let scalar = self.memory From 10102d1f0aa0e7e612ea2a51d5329af928b07f03 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 16 Nov 2018 11:16:34 +0100 Subject: [PATCH 62/88] comment nit Co-Authored-By: oli-obk --- src/librustc_mir/interpret/memory.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustc_mir/interpret/memory.rs b/src/librustc_mir/interpret/memory.rs index 2e47d7e69d9a5..1cb9543b36631 100644 --- a/src/librustc_mir/interpret/memory.rs +++ b/src/librustc_mir/interpret/memory.rs @@ -659,7 +659,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> { self.check_align(src, src_align)?; self.check_align(dest, dest_align)?; if size.bytes() == 0 { - // Nothing to do for ZST, other than checking alignment and non-NULLness. + // Nothing to do for ZST, other than checking alignment and non-NULLness which already happened. return Ok(()); } let src = src.to_ptr()?; From a5ef2d1b54c833783a64a98d5d585bb44218497a Mon Sep 17 00:00:00 2001 From: Oliver Scherer Date: Fri, 16 Nov 2018 11:46:58 +0100 Subject: [PATCH 63/88] Array and slice projections need to update the place alignment --- src/librustc_mir/interpret/operand.rs | 7 ++++--- src/librustc_mir/interpret/place.rs | 12 +++++++----- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/librustc_mir/interpret/operand.rs b/src/librustc_mir/interpret/operand.rs index 4ec01b6ca1032..9588a931c4a35 100644 --- a/src/librustc_mir/interpret/operand.rs +++ b/src/librustc_mir/interpret/operand.rs @@ -271,13 +271,13 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> if mplace.layout.is_zst() { // Not all ZSTs have a layout we would handle below, so just short-circuit them // all here. - self.memory.check_align(ptr, ptr_align.min(mplace.layout.align))?; + self.memory.check_align(ptr, ptr_align)?; return Ok(Some(Immediate::Scalar(Scalar::zst().into()))); } // check for integer pointers before alignment to report better errors let ptr = ptr.to_ptr()?; - self.memory.check_align(ptr.into(), ptr_align.min(mplace.layout.align))?; + self.memory.check_align(ptr.into(), ptr_align)?; match mplace.layout.abi { layout::Abi::Scalar(..) => { let scalar = self.memory @@ -295,7 +295,8 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> let a_val = self.memory .get(ptr.alloc_id)? .read_scalar(self, a_ptr, a_size)?; - self.memory.check_align(b_ptr.into(), b.align(self).min(ptr_align))?; + let b_align = ptr_align.restrict_for_offset(b_offset); + self.memory.check_align(b_ptr.into(), b_align)?; let b_val = self.memory .get(ptr.alloc_id)? .read_scalar(self, b_ptr, b_size)?; diff --git a/src/librustc_mir/interpret/place.rs b/src/librustc_mir/interpret/place.rs index 6317cfb94d27f..d93aca7f4e1d9 100644 --- a/src/librustc_mir/interpret/place.rs +++ b/src/librustc_mir/interpret/place.rs @@ -393,8 +393,9 @@ where let dl = &self.tcx.data_layout; Ok((0..len).map(move |i| { let ptr = base.ptr.ptr_offset(i * stride, dl)?; + let align = base.align.restrict_for_offset(i * stride); Ok(MPlaceTy { - mplace: MemPlace { ptr, align: base.align, meta: None }, + mplace: MemPlace { ptr, align, meta: None }, layout }) })) @@ -417,6 +418,7 @@ where _ => bug!("Unexpected layout of index access: {:#?}", base.layout), }; let ptr = base.ptr.ptr_offset(from_offset, self)?; + let align = base.align.restrict_for_offset(from_offset); // Compute meta and new layout let inner_len = len - to - from; @@ -435,7 +437,7 @@ where let layout = self.layout_of(ty)?; Ok(MPlaceTy { - mplace: MemPlace { ptr, align: base.align, meta }, + mplace: MemPlace { ptr, align, meta }, layout }) } @@ -741,11 +743,11 @@ where dest.layout) }; let (a_size, b_size) = (a.size(self), b.size(self)); - let b_align = b.align(self).abi; - let b_offset = a_size.align_to(b_align); + let b_offset = a_size.align_to(b.align(self).abi); + let b_align = ptr_align.restrict_for_offset(b_offset); let b_ptr = ptr.offset(b_offset, self)?; - self.memory.check_align(b_ptr.into(), ptr_align.min(b_align))?; + self.memory.check_align(b_ptr.into(), b_align)?; // It is tempting to verify `b_offset` against `layout.fields.offset(1)`, // but that does not work: We could be a newtype around a pair, then the From cb8fa335721285809c5930cd0fded35899a05064 Mon Sep 17 00:00:00 2001 From: Oliver Scherer Date: Fri, 16 Nov 2018 11:48:48 +0100 Subject: [PATCH 64/88] tidy --- src/librustc_mir/interpret/memory.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/librustc_mir/interpret/memory.rs b/src/librustc_mir/interpret/memory.rs index 1cb9543b36631..4250023764555 100644 --- a/src/librustc_mir/interpret/memory.rs +++ b/src/librustc_mir/interpret/memory.rs @@ -659,7 +659,8 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> { self.check_align(src, src_align)?; self.check_align(dest, dest_align)?; if size.bytes() == 0 { - // Nothing to do for ZST, other than checking alignment and non-NULLness which already happened. + // Nothing to do for ZST, other than checking alignment and + // non-NULLness which already happened. return Ok(()); } let src = src.to_ptr()?; From 972d798881cb470aa7a75b9ed7fa6c37117492e0 Mon Sep 17 00:00:00 2001 From: Oliver Scherer Date: Fri, 16 Nov 2018 16:25:15 +0100 Subject: [PATCH 65/88] Document `Allocation` --- src/librustc/mir/interpret/allocation.rs | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/librustc/mir/interpret/allocation.rs b/src/librustc/mir/interpret/allocation.rs index 2ecd5eb5a6269..296c2e2dd6b3d 100644 --- a/src/librustc/mir/interpret/allocation.rs +++ b/src/librustc/mir/interpret/allocation.rs @@ -170,6 +170,8 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra> Allocation { /// Reading and writing impl<'tcx, Tag: Copy, Extra: AllocationExtra> Allocation { + /// Reads bytes until a `0` is encountered. Will error if the end of the allocation is reached + /// before a `0` is found. pub fn read_c_str( &self, cx: &impl HasDataLayout, @@ -188,6 +190,9 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra> Allocation { } } + /// Validates that `ptr.offset` and `ptr.offset + size` do not point to the middle of a + /// relocation. If `allow_ptr_and_undef` is `false`, also enforces that the memory in the + /// given range contains neither relocations nor undef bytes. pub fn check_bytes( &self, cx: &impl HasDataLayout, @@ -195,7 +200,7 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra> Allocation { size: Size, allow_ptr_and_undef: bool, ) -> EvalResult<'tcx> { - // Check bounds, align and relocations on the edges + // Check bounds and relocations on the edges self.get_bytes_with_undef_and_ptr(cx, ptr, size)?; // Check undef and ptr if !allow_ptr_and_undef { @@ -205,6 +210,9 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra> Allocation { Ok(()) } + /// Writes `src` to the memory starting at `ptr.offset`. + /// + /// Will do bounds checks on the allocation. pub fn write_bytes( &mut self, cx: &impl HasDataLayout, @@ -216,6 +224,7 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra> Allocation { Ok(()) } + /// Sets `count` bytes starting at `ptr.offset` with `val`. Basically `memset`. pub fn write_repeat( &mut self, cx: &impl HasDataLayout, @@ -236,6 +245,8 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra> Allocation { /// * byteorder cannot work with zero element buffers /// * in oder to obtain a `Pointer` we need to check for ZSTness anyway due to integer pointers /// being valid for ZSTs + /// + /// Note: This function does not do *any* alignment checks, you need to do these before calling pub fn read_scalar( &self, cx: &impl HasDataLayout, @@ -270,6 +281,7 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra> Allocation { Ok(ScalarMaybeUndef::Scalar(Scalar::from_uint(bits, size))) } + /// Note: This function does not do *any* alignment checks, you need to do these before calling pub fn read_ptr_sized( &self, cx: &impl HasDataLayout, @@ -284,6 +296,8 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra> Allocation { /// * byteorder cannot work with zero element buffers /// * in oder to obtain a `Pointer` we need to check for ZSTness anyway due to integer pointers /// being valid for ZSTs + /// + /// Note: This function does not do *any* alignment checks, you need to do these before calling pub fn write_scalar( &mut self, cx: &impl HasDataLayout, @@ -330,6 +344,7 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra> Allocation { Ok(()) } + /// Note: This function does not do *any* alignment checks, you need to do these before calling pub fn write_ptr_sized( &mut self, cx: &impl HasDataLayout, @@ -357,7 +372,7 @@ impl<'tcx, Tag: Copy, Extra> Allocation { self.relocations.range(Size::from_bytes(start)..end) } - /// Check that there ar eno relocations overlapping with the given range. + /// Check that there are no relocations overlapping with the given range. #[inline(always)] fn check_relocations( &self, From 22872196f55d93d7af6b3c96e34bef3579f0c3fc Mon Sep 17 00:00:00 2001 From: Oliver Scherer Date: Sat, 17 Nov 2018 13:36:55 +0100 Subject: [PATCH 66/88] Factor out mplace offsetting into its own method --- src/librustc_mir/interpret/place.rs | 53 +++++++++++++++++------------ 1 file changed, 31 insertions(+), 22 deletions(-) diff --git a/src/librustc_mir/interpret/place.rs b/src/librustc_mir/interpret/place.rs index d93aca7f4e1d9..0fd1a993cbd90 100644 --- a/src/librustc_mir/interpret/place.rs +++ b/src/librustc_mir/interpret/place.rs @@ -159,6 +159,19 @@ impl MemPlace { Some(meta) => Immediate::ScalarPair(self.ptr.into(), meta.into()), } } + + pub fn offset( + self, + offset: Size, + meta: Option>, + cx: &impl HasDataLayout, + ) -> EvalResult<'tcx, Self> { + Ok(MemPlace { + ptr: self.ptr.ptr_offset(offset, cx)?, + align: self.align.restrict_for_offset(offset), + meta, + }) + } } impl<'tcx, Tag> MPlaceTy<'tcx, Tag> { @@ -174,6 +187,19 @@ impl<'tcx, Tag> MPlaceTy<'tcx, Tag> { } } + pub fn offset( + self, + offset: Size, + meta: Option>, + layout: TyLayout<'tcx>, + cx: &impl HasDataLayout, + ) -> EvalResult<'tcx, Self> { + Ok(MPlaceTy { + mplace: self.mplace.offset(offset, meta, cx)?, + layout, + }) + } + #[inline] fn from_aligned_ptr(ptr: Pointer, layout: TyLayout<'tcx>) -> Self { MPlaceTy { mplace: MemPlace::from_ptr(ptr, layout.align.abi), layout } @@ -367,13 +393,9 @@ where (None, offset) }; - let ptr = base.ptr.ptr_offset(offset, self)?; - let align = base.align - // We do not look at `base.layout.align` nor `field_layout.align`, unlike - // codegen -- mostly to see if we can get away with that - .restrict_for_offset(offset); // must be last thing that happens - - Ok(MPlaceTy { mplace: MemPlace { ptr, align, meta }, layout: field_layout }) + // We do not look at `base.layout.align` nor `field_layout.align`, unlike + // codegen -- mostly to see if we can get away with that + base.offset(offset, meta, field_layout, self) } // Iterates over all fields of an array. Much more efficient than doing the @@ -391,14 +413,7 @@ where }; let layout = base.layout.field(self, 0)?; let dl = &self.tcx.data_layout; - Ok((0..len).map(move |i| { - let ptr = base.ptr.ptr_offset(i * stride, dl)?; - let align = base.align.restrict_for_offset(i * stride); - Ok(MPlaceTy { - mplace: MemPlace { ptr, align, meta: None }, - layout - }) - })) + Ok((0..len).map(move |i| base.offset(i * stride, None, layout, dl))) } pub fn mplace_subslice( @@ -417,8 +432,6 @@ where stride * from, _ => bug!("Unexpected layout of index access: {:#?}", base.layout), }; - let ptr = base.ptr.ptr_offset(from_offset, self)?; - let align = base.align.restrict_for_offset(from_offset); // Compute meta and new layout let inner_len = len - to - from; @@ -435,11 +448,7 @@ where bug!("cannot subslice non-array type: `{:?}`", base.layout.ty), }; let layout = self.layout_of(ty)?; - - Ok(MPlaceTy { - mplace: MemPlace { ptr, align, meta }, - layout - }) + base.offset(from_offset, meta, layout, self) } pub fn mplace_downcast( From 3220c0ce1afcb7b47671d6efe8fc38ab12d3d806 Mon Sep 17 00:00:00 2001 From: Oliver Scherer Date: Sat, 17 Nov 2018 16:18:05 +0100 Subject: [PATCH 67/88] Explain why vtable generation needs no alignment checks --- src/librustc_mir/interpret/traits.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/librustc_mir/interpret/traits.rs b/src/librustc_mir/interpret/traits.rs index 37979c8ee664c..bda585b8eda34 100644 --- a/src/librustc_mir/interpret/traits.rs +++ b/src/librustc_mir/interpret/traits.rs @@ -59,6 +59,9 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> let drop = ::monomorphize::resolve_drop_in_place(*tcx, ty); let drop = self.memory.create_fn_alloc(drop).with_default_tag(); + // no need to do any alignment checks on the memory accesses below, because we know the + // allocation is correctly aligned as we created it above. Also we're only offsetting by + // multiples of `ptr_align`, which means that it will stay aligned to `ptr_align`. self.memory .get_mut(vtable.alloc_id)? .write_ptr_sized(tcx, vtable, Scalar::Ptr(drop).into())?; From 360f9888bc143f6d7b2c09f723e255121bf49f8d Mon Sep 17 00:00:00 2001 From: Oliver Scherer Date: Sat, 17 Nov 2018 17:52:25 +0100 Subject: [PATCH 68/88] update miri submodule --- src/tools/miri | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri b/src/tools/miri index 06758c48bd7a7..dd7f545a69e4b 160000 --- a/src/tools/miri +++ b/src/tools/miri @@ -1 +1 @@ -Subproject commit 06758c48bd7a77bb5aa43fc50cf344540ba5afef +Subproject commit dd7f545a69e4b720407e458bf4ade0b207bbf9ee From b853252bcdb2ded2b049d833c51a993fe0ed40f8 Mon Sep 17 00:00:00 2001 From: Oliver Scherer Date: Tue, 20 Nov 2018 11:04:07 +0100 Subject: [PATCH 69/88] Rebase fallout --- src/librustc/mir/interpret/allocation.rs | 13 ++---------- src/librustc/mir/interpret/pointer.rs | 19 +++++++++++++++++- src/librustc_mir/interpret/memory.rs | 25 +++++++++++++++++++----- src/librustc_mir/interpret/operand.rs | 4 ++-- src/librustc_mir/interpret/validity.rs | 2 +- src/tools/miri | 2 +- 6 files changed, 44 insertions(+), 21 deletions(-) diff --git a/src/librustc/mir/interpret/allocation.rs b/src/librustc/mir/interpret/allocation.rs index 296c2e2dd6b3d..c612d6ad1bb24 100644 --- a/src/librustc/mir/interpret/allocation.rs +++ b/src/librustc/mir/interpret/allocation.rs @@ -57,23 +57,14 @@ pub struct Allocation { impl<'tcx, Tag, Extra> Allocation { /// Check if the pointer is "in-bounds". Notice that a pointer pointing at the end /// of an allocation (i.e., at the first *inaccessible* location) *is* considered - /// in-bounds! This follows C's/LLVM's rules. `check` indicates whether we - /// additionally require the pointer to be pointing to a *live* (still allocated) - /// allocation. + /// in-bounds! This follows C's/LLVM's rules. /// If you want to check bounds before doing a memory access, better use `check_bounds`. pub fn check_bounds_ptr( &self, ptr: Pointer, ) -> EvalResult<'tcx> { let allocation_size = self.bytes.len() as u64; - if ptr.offset.bytes() > allocation_size { - return err!(PointerOutOfBounds { - ptr: ptr.erase_tag(), - check: InboundsCheck::Live, - allocation_size: Size::from_bytes(allocation_size), - }); - } - Ok(()) + ptr.check_in_alloc(Size::from_bytes(allocation_size), InboundsCheck::Live) } /// Check if the memory range beginning at `ptr` and of size `Size` is "in-bounds". diff --git a/src/librustc/mir/interpret/pointer.rs b/src/librustc/mir/interpret/pointer.rs index 969f2c0e8376a..a046825f088bb 100644 --- a/src/librustc/mir/interpret/pointer.rs +++ b/src/librustc/mir/interpret/pointer.rs @@ -2,7 +2,7 @@ use mir; use ty::layout::{self, HasDataLayout, Size}; use super::{ - AllocId, EvalResult, + AllocId, EvalResult, InboundsCheck, }; //////////////////////////////////////////////////////////////////////////////// @@ -148,4 +148,21 @@ impl<'tcx, Tag> Pointer { pub fn erase_tag(self) -> Pointer { Pointer { alloc_id: self.alloc_id, offset: self.offset, tag: () } } + + #[inline(always)] + pub fn check_in_alloc( + self, + allocation_size: Size, + check: InboundsCheck, + ) -> EvalResult<'tcx, ()> { + if self.offset > allocation_size { + err!(PointerOutOfBounds { + ptr: self.erase_tag(), + check, + allocation_size, + }) + } else { + Ok(()) + } + } } diff --git a/src/librustc_mir/interpret/memory.rs b/src/librustc_mir/interpret/memory.rs index 4250023764555..c673b57a66f5f 100644 --- a/src/librustc_mir/interpret/memory.rs +++ b/src/librustc_mir/interpret/memory.rs @@ -28,9 +28,9 @@ use rustc_data_structures::fx::{FxHashSet, FxHashMap}; use syntax::ast::Mutability; use super::{ - Pointer, AllocId, Allocation, GlobalId, AllocationExtra, InboundsCheck, + Pointer, AllocId, Allocation, GlobalId, AllocationExtra, EvalResult, Scalar, EvalErrorKind, AllocType, PointerArithmetic, - Machine, AllocMap, MayLeak, ErrorHandled, AllocationExtra, + Machine, AllocMap, MayLeak, ErrorHandled, InboundsCheck, }; #[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)] @@ -251,9 +251,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> { Scalar::Ptr(ptr) => { // check this is not NULL -- which we can ensure only if this is in-bounds // of some (potentially dead) allocation. - self.check_bounds_ptr(ptr, InboundsCheck::MaybeDead)?; - // data required for alignment check - let (_, align) = self.get_size_and_align(ptr.alloc_id); + let align = self.check_bounds_ptr_maybe_dead(ptr)?; (ptr.offset.bytes(), align) } Scalar::Bits { bits, size } => { @@ -284,6 +282,23 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> { }) } } + + /// Check if the pointer is "in-bounds". Notice that a pointer pointing at the end + /// of an allocation (i.e., at the first *inaccessible* location) *is* considered + /// in-bounds! This follows C's/LLVM's rules. + /// This function also works for deallocated allocations. + /// Use `.get(ptr.alloc_id)?.check_bounds_ptr(ptr)` if you want to force the allocation + /// to still be live. + /// If you want to check bounds before doing a memory access, better first obtain + /// an `Allocation` and call `check_bounds`. + pub fn check_bounds_ptr_maybe_dead( + &self, + ptr: Pointer, + ) -> EvalResult<'tcx, Align> { + let (allocation_size, align) = self.get_size_and_align(ptr.alloc_id); + ptr.check_in_alloc(allocation_size, InboundsCheck::MaybeDead)?; + Ok(align) + } } /// Allocation accessors diff --git a/src/librustc_mir/interpret/operand.rs b/src/librustc_mir/interpret/operand.rs index 9588a931c4a35..539bc6d965fd6 100644 --- a/src/librustc_mir/interpret/operand.rs +++ b/src/librustc_mir/interpret/operand.rs @@ -19,7 +19,7 @@ use rustc::ty::layout::{self, Size, LayoutOf, TyLayout, HasDataLayout, IntegerEx use rustc::mir::interpret::{ GlobalId, AllocId, ConstValue, Pointer, Scalar, - EvalResult, EvalErrorKind, InboundsCheck, + EvalResult, EvalErrorKind, }; use super::{EvalContext, Machine, MemPlace, MPlaceTy, MemoryKind}; pub use rustc::mir::interpret::ScalarMaybeUndef; @@ -647,7 +647,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> ScalarMaybeUndef::Scalar(Scalar::Ptr(ptr)) => { // The niche must be just 0 (which an inbounds pointer value never is) let ptr_valid = niche_start == 0 && variants_start == variants_end && - self.memory.check_bounds_ptr(ptr, InboundsCheck::MaybeDead).is_ok(); + self.memory.check_bounds_ptr_maybe_dead(ptr).is_ok(); if !ptr_valid { return err!(InvalidDiscriminant(raw_discr.erase_tag())); } diff --git a/src/librustc_mir/interpret/validity.rs b/src/librustc_mir/interpret/validity.rs index 8ce5a0365cf65..ed4cb65ea74b1 100644 --- a/src/librustc_mir/interpret/validity.rs +++ b/src/librustc_mir/interpret/validity.rs @@ -17,7 +17,7 @@ use rustc::ty::layout::{self, Size, Align, TyLayout, LayoutOf, VariantIdx}; use rustc::ty; use rustc_data_structures::fx::FxHashSet; use rustc::mir::interpret::{ - Scalar, AllocType, EvalResult, EvalErrorKind, InboundsCheck, + Scalar, AllocType, EvalResult, EvalErrorKind, }; use super::{ diff --git a/src/tools/miri b/src/tools/miri index dd7f545a69e4b..32e93ed7762e5 160000 --- a/src/tools/miri +++ b/src/tools/miri @@ -1 +1 @@ -Subproject commit dd7f545a69e4b720407e458bf4ade0b207bbf9ee +Subproject commit 32e93ed7762e5aa1a721636096848fc3c7bc7218 From b8a30f04cdd129ab89b85d0eda11f1c65058767a Mon Sep 17 00:00:00 2001 From: scalexm Date: Sat, 24 Nov 2018 23:34:44 +0100 Subject: [PATCH 70/88] Try to work around #53332 in `src/test/run-pass/rustc-rust-log.rs` --- src/test/run-pass/rustc-rust-log.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/test/run-pass/rustc-rust-log.rs b/src/test/run-pass/rustc-rust-log.rs index 8c3e4b391e995..10e91e71fe284 100644 --- a/src/test/run-pass/rustc-rust-log.rs +++ b/src/test/run-pass/rustc-rust-log.rs @@ -16,6 +16,7 @@ // // dont-check-compiler-stdout // dont-check-compiler-stderr +// compile-flags: --error-format human // rustc-env:RUST_LOG=debug From 6b338e034adfc1bffed942c084c892d34f1cd9b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Sat, 24 Nov 2018 16:23:11 -0800 Subject: [PATCH 71/88] Suggest correct enum variant on typo --- src/librustc_typeck/astconv.rs | 29 ++++++++++++++++++- src/test/ui/issues/issue-34209.rs | 4 +-- src/test/ui/issues/issue-34209.stderr | 7 ++--- src/test/ui/suggestions/suggest-variants.rs | 15 ++++++++++ .../ui/suggestions/suggest-variants.stderr | 20 +++++++++++++ 5 files changed, 68 insertions(+), 7 deletions(-) create mode 100644 src/test/ui/suggestions/suggest-variants.rs create mode 100644 src/test/ui/suggestions/suggest-variants.stderr diff --git a/src/librustc_typeck/astconv.rs b/src/librustc_typeck/astconv.rs index a8164c85e821d..4fbbe58445254 100644 --- a/src/librustc_typeck/astconv.rs +++ b/src/librustc_typeck/astconv.rs @@ -36,8 +36,9 @@ use lint; use std::iter; use syntax::ast; -use syntax::ptr::P; use syntax::feature_gate::{GateIssue, emit_feature_err}; +use syntax::ptr::P; +use syntax::util::lev_distance::find_best_match_for_name; use syntax_pos::{DUMMY_SP, Span, MultiSpan}; pub trait AstConv<'gcx, 'tcx> { @@ -1303,6 +1304,32 @@ impl<'o, 'gcx: 'tcx, 'tcx> dyn AstConv<'gcx, 'tcx>+'o { Err(ErrorReported) => return (tcx.types.err, Def::Err), } } + (&ty::Adt(adt_def, _substs), Def::Enum(_did)) => { + let ty_str = ty.to_string(); + // Incorrect enum variant + let mut err = tcx.sess.struct_span_err( + span, + &format!("no variant `{}` on enum `{}`", &assoc_name.as_str(), ty_str), + ); + // Check if it was a typo + let input = adt_def.variants.iter().map(|variant| &variant.name); + if let Some(suggested_name) = find_best_match_for_name( + input, + &assoc_name.as_str(), + None, + ) { + err.span_suggestion_with_applicability( + span, + "did you mean", + format!("{}::{}", ty_str, suggested_name.to_string()), + Applicability::MaybeIncorrect, + ); + } else { + err.span_label(span, "unknown variant"); + } + err.emit(); + return (tcx.types.err, Def::Err); + } _ => { // Don't print TyErr to the user. if !ty.references_error() { diff --git a/src/test/ui/issues/issue-34209.rs b/src/test/ui/issues/issue-34209.rs index b3cb7d4cc3053..86eb624edcac1 100644 --- a/src/test/ui/issues/issue-34209.rs +++ b/src/test/ui/issues/issue-34209.rs @@ -14,8 +14,8 @@ enum S { fn bug(l: S) { match l { - S::B{ } => { }, - //~^ ERROR ambiguous associated type + S::B { } => { }, + //~^ ERROR no variant `B` on enum `S` } } diff --git a/src/test/ui/issues/issue-34209.stderr b/src/test/ui/issues/issue-34209.stderr index 0dfdd8b588634..d5a5647422f41 100644 --- a/src/test/ui/issues/issue-34209.stderr +++ b/src/test/ui/issues/issue-34209.stderr @@ -1,9 +1,8 @@ -error[E0223]: ambiguous associated type +error: no variant `B` on enum `S` --> $DIR/issue-34209.rs:17:9 | -LL | S::B{ } => { }, - | ^^^^ help: use fully-qualified syntax: `::B` +LL | S::B { } => { }, + | ^^^^ help: did you mean: `S::A` error: aborting due to previous error -For more information about this error, try `rustc --explain E0223`. diff --git a/src/test/ui/suggestions/suggest-variants.rs b/src/test/ui/suggestions/suggest-variants.rs new file mode 100644 index 0000000000000..4a131ed837b87 --- /dev/null +++ b/src/test/ui/suggestions/suggest-variants.rs @@ -0,0 +1,15 @@ +#[derive(Debug)] +enum Shape { + Square { size: i32 }, + Circle { radius: i32 }, +} + +struct S { + x: usize, +} + +fn main() { + println!("My shape is {:?}", Shape::Squareee { size: 5}); + println!("My shape is {:?}", Shape::Circl { size: 5}); + println!("My shape is {:?}", Shape::Rombus{ size: 5}); +} diff --git a/src/test/ui/suggestions/suggest-variants.stderr b/src/test/ui/suggestions/suggest-variants.stderr new file mode 100644 index 0000000000000..08ae68ea71302 --- /dev/null +++ b/src/test/ui/suggestions/suggest-variants.stderr @@ -0,0 +1,20 @@ +error: no variant `Squareee` on enum `Shape` + --> $DIR/suggest-variants.rs:12:34 + | +LL | println!("My shape is {:?}", Shape::Squareee { size: 5}); + | ^^^^^^^^^^^^^^^ help: did you mean: `Shape::Square` + +error: no variant `Circl` on enum `Shape` + --> $DIR/suggest-variants.rs:13:34 + | +LL | println!("My shape is {:?}", Shape::Circl { size: 5}); + | ^^^^^^^^^^^^ help: did you mean: `Shape::Circle` + +error: no variant `Rombus` on enum `Shape` + --> $DIR/suggest-variants.rs:14:34 + | +LL | println!("My shape is {:?}", Shape::Rombus{ size: 5}); + | ^^^^^^^^^^^^^ unknown variant + +error: aborting due to 3 previous errors + From dce1c4530e2707c338fe56b26a36797377f11514 Mon Sep 17 00:00:00 2001 From: Wesley Wiser Date: Thu, 22 Nov 2018 13:05:25 -0500 Subject: [PATCH 72/88] [Windows] Work around non-monotonic clocks in the self-profiler On Windows, the high-resolution timestamp api doesn't seem to always be monotonic. This can cause panics when the self-profiler uses the `Instant` api to find elapsed time. Work around this by detecting the case where now is less than the start time and just use 0 elapsed ticks as the measurement. Fixes #51648 --- src/librustc/util/profiling.rs | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/librustc/util/profiling.rs b/src/librustc/util/profiling.rs index 37073b6e82080..1f05043976491 100644 --- a/src/librustc/util/profiling.rs +++ b/src/librustc/util/profiling.rs @@ -12,7 +12,7 @@ use session::config::Options; use std::fs; use std::io::{self, StdoutLock, Write}; -use std::time::Instant; +use std::time::{Duration, Instant}; macro_rules! define_categories { ($($name:ident,)*) => { @@ -197,7 +197,20 @@ impl SelfProfiler { } fn stop_timer(&mut self) -> u64 { - let elapsed = self.current_timer.elapsed(); + let elapsed = if cfg!(windows) { + // On Windows, timers don't always appear to be monotonic (see #51648) + // which can lead to panics when calculating elapsed time. + // Work around this by testing to see if the current time is less than + // our recorded time, and if it is, just returning 0. + let now = Instant::now(); + if self.current_timer >= now { + Duration::new(0, 0) + } else { + self.current_timer.elapsed() + } + } else { + self.current_timer.elapsed() + }; self.current_timer = Instant::now(); From 1e34dfce6f49b5bef0da31a8abe34e753c3af513 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 2 Nov 2018 19:46:59 +1100 Subject: [PATCH 73/88] Update to `ena` 0.11.0. This version has some significant speed-ups relating to snapshotting. --- Cargo.lock | 6 +++--- src/librustc_data_structures/Cargo.toml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index dc9296b81e223..a7b83f87b191b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -661,7 +661,7 @@ dependencies = [ [[package]] name = "ena" -version = "0.10.1" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2230,7 +2230,7 @@ name = "rustc_data_structures" version = "0.0.0" dependencies = [ "cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "ena 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", + "ena 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "graphviz 0.0.0", "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3295,7 +3295,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum difference 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198" "checksum either 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3be565ca5c557d7f59e7cfcf1844f9e3033650c929c6566f511e8005f205c1d0" "checksum elasticlunr-rs 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "4837d77a1e157489a3933b743fd774ae75074e0e390b2b7f071530048a0d87ee" -"checksum ena 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "25b4e5febb25f08c49f1b07dc33a182729a6b21edfb562b5aef95f78e0dbe5bb" +"checksum ena 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f56c93cc076508c549d9bb747f79aa9b4eb098be7b8cad8830c3137ef52d1e00" "checksum ena 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "88dc8393b3c7352f94092497f6b52019643e493b6b890eb417cdb7c46117e621" "checksum env_logger 0.5.12 (registry+https://github.com/rust-lang/crates.io-index)" = "f4d7e69c283751083d53d01eac767407343b8b69c4bd70058e08adc2637cb257" "checksum env_logger 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "afb070faf94c85d17d50ca44f6ad076bce18ae92f0037d350947240a36e9d42e" diff --git a/src/librustc_data_structures/Cargo.toml b/src/librustc_data_structures/Cargo.toml index 5b3dd38adf23f..188919d063351 100644 --- a/src/librustc_data_structures/Cargo.toml +++ b/src/librustc_data_structures/Cargo.toml @@ -9,7 +9,7 @@ path = "lib.rs" crate-type = ["dylib"] [dependencies] -ena = "0.10.1" +ena = "0.11" log = "0.4" rustc_cratesio_shim = { path = "../librustc_cratesio_shim" } serialize = { path = "../libserialize" } From 9847b5cfcb16876630064ed766fbbb7545c67368 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 5 Nov 2018 09:28:12 +1100 Subject: [PATCH 74/88] Remove `insert_noop`. Because it's as useless as its name suggests. This commit also renames `UndoLog::Noop` as `UndoLog::Purged`, because (a) that's a more descriptive name and (b) it matches the name used in similar code in `librustc/infer/region_constraints/mod.rs`. --- src/librustc/traits/project.rs | 8 ++------ src/librustc_data_structures/snapshot_map/mod.rs | 14 ++++---------- 2 files changed, 6 insertions(+), 16 deletions(-) diff --git a/src/librustc/traits/project.rs b/src/librustc/traits/project.rs index b59bd0e238873..e44bf2661c55c 100644 --- a/src/librustc/traits/project.rs +++ b/src/librustc/traits/project.rs @@ -1714,12 +1714,8 @@ impl<'tcx> ProjectionCache<'tcx> { /// to be a NormalizedTy. pub fn complete_normalized(&mut self, key: ProjectionCacheKey<'tcx>, ty: &NormalizedTy<'tcx>) { // We want to insert `ty` with no obligations. If the existing value - // already has no obligations (as is common) we can use `insert_noop` - // to do a minimal amount of work -- the HashMap insertion is skipped, - // and minimal changes are made to the undo log. - if ty.obligations.is_empty() { - self.map.insert_noop(); - } else { + // already has no obligations (as is common) we don't insert anything. + if !ty.obligations.is_empty() { self.map.insert(key, ProjectionCacheEntry::NormalizedTy(Normalized { value: ty.value, obligations: vec![] diff --git a/src/librustc_data_structures/snapshot_map/mod.rs b/src/librustc_data_structures/snapshot_map/mod.rs index 0b42cb1edddec..2c36a549baca1 100644 --- a/src/librustc_data_structures/snapshot_map/mod.rs +++ b/src/librustc_data_structures/snapshot_map/mod.rs @@ -44,7 +44,7 @@ enum UndoLog { CommittedSnapshot, Inserted(K), Overwrite(K, V), - Noop, + Purged, } impl SnapshotMap @@ -72,12 +72,6 @@ impl SnapshotMap } } - pub fn insert_noop(&mut self) { - if !self.undo_log.is_empty() { - self.undo_log.push(UndoLog::Noop); - } - } - pub fn remove(&mut self, key: K) -> bool { match self.map.remove(&key) { Some(old_value) => { @@ -128,13 +122,13 @@ impl SnapshotMap let reverse = match self.undo_log[i] { UndoLog::OpenSnapshot => false, UndoLog::CommittedSnapshot => false, - UndoLog::Noop => false, + UndoLog::Purged => false, UndoLog::Inserted(ref k) => should_revert_key(k), UndoLog::Overwrite(ref k, _) => should_revert_key(k), }; if reverse { - let entry = mem::replace(&mut self.undo_log[i], UndoLog::Noop); + let entry = mem::replace(&mut self.undo_log[i], UndoLog::Purged); self.reverse(entry); } } @@ -171,7 +165,7 @@ impl SnapshotMap self.map.insert(key, old_value); } - UndoLog::Noop => {} + UndoLog::Purged => {} } } } From c86bbd4830410f19812ddbe4286ebb83ae043a4b Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 5 Nov 2018 09:34:37 +1100 Subject: [PATCH 75/88] Rename `UndoLogEntry` as `UndoLog`. So that it matches `librustc_data_structures/snapshot_map/mod.rs` and the `ena` crate. --- src/librustc/infer/region_constraints/mod.rs | 10 +++++----- src/librustc/infer/region_constraints/taint.rs | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/librustc/infer/region_constraints/mod.rs b/src/librustc/infer/region_constraints/mod.rs index 391bfc428c3bb..eb4c0d08f9fa1 100644 --- a/src/librustc/infer/region_constraints/mod.rs +++ b/src/librustc/infer/region_constraints/mod.rs @@ -11,7 +11,7 @@ //! See README.md use self::CombineMapType::*; -use self::UndoLogEntry::*; +use self::UndoLog::*; use super::unify_key; use super::{MiscVariable, RegionVariableOrigin, SubregionOrigin}; @@ -59,7 +59,7 @@ pub struct RegionConstraintCollector<'tcx> { /// otherwise we end up adding entries for things like the lower /// bound on a variable and so forth, which can never be rolled /// back. - undo_log: Vec>, + undo_log: Vec>, /// When we add a R1 == R2 constriant, we currently add (a) edges /// R1 <= R2 and R2 <= R1 and (b) we unify the two regions in this @@ -254,7 +254,7 @@ struct TwoRegions<'tcx> { } #[derive(Copy, Clone, PartialEq)] -enum UndoLogEntry<'tcx> { +enum UndoLog<'tcx> { /// Pushed when we start a snapshot. OpenSnapshot, @@ -456,7 +456,7 @@ impl<'tcx> RegionConstraintCollector<'tcx> { self.any_unifications = snapshot.any_unifications; } - fn rollback_undo_entry(&mut self, undo_entry: UndoLogEntry<'tcx>) { + fn rollback_undo_entry(&mut self, undo_entry: UndoLog<'tcx>) { match undo_entry { OpenSnapshot => { panic!("Failure to observe stack discipline"); @@ -548,7 +548,7 @@ impl<'tcx> RegionConstraintCollector<'tcx> { fn kill_constraint<'tcx>( placeholders: &FxHashSet>, - undo_entry: &UndoLogEntry<'tcx>, + undo_entry: &UndoLog<'tcx>, ) -> bool { match undo_entry { &AddConstraint(Constraint::VarSubVar(..)) => false, diff --git a/src/librustc/infer/region_constraints/taint.rs b/src/librustc/infer/region_constraints/taint.rs index ef7365276f6d2..9f08fdcad7eea 100644 --- a/src/librustc/infer/region_constraints/taint.rs +++ b/src/librustc/infer/region_constraints/taint.rs @@ -29,7 +29,7 @@ impl<'tcx> TaintSet<'tcx> { pub(super) fn fixed_point( &mut self, tcx: TyCtxt<'_, '_, 'tcx>, - undo_log: &[UndoLogEntry<'tcx>], + undo_log: &[UndoLog<'tcx>], verifys: &[Verify<'tcx>], ) { let mut prev_len = 0; From 7fe09a6551b72133e68911f3125f28642ec243ac Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 5 Nov 2018 09:35:28 +1100 Subject: [PATCH 76/88] Replace a `.truncate(0)` call with `.clear()`. --- src/librustc_data_structures/snapshot_map/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustc_data_structures/snapshot_map/mod.rs b/src/librustc_data_structures/snapshot_map/mod.rs index 2c36a549baca1..6ad886625d8b5 100644 --- a/src/librustc_data_structures/snapshot_map/mod.rs +++ b/src/librustc_data_structures/snapshot_map/mod.rs @@ -106,7 +106,7 @@ impl SnapshotMap self.assert_open_snapshot(snapshot); if snapshot.len == 0 { // The root snapshot. - self.undo_log.truncate(0); + self.undo_log.clear(); } else { self.undo_log[snapshot.len] = UndoLog::CommittedSnapshot; } From f5624e41e819bb85408eba7b3abd550f61a2857a Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 5 Nov 2018 10:37:23 +1100 Subject: [PATCH 77/88] Make `commit` and `rollback_to` methods take ownership of the snapshots. Because they shouldn't be reused. This provides consistency with the `ena` crate. --- src/librustc/infer/mod.rs | 2 +- src/librustc/traits/project.rs | 6 +++--- src/librustc_data_structures/snapshot_map/mod.rs | 8 ++++---- src/librustc_data_structures/snapshot_map/test.rs | 8 ++++---- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs index 87e32be1a1759..9e51a4c16e5cb 100644 --- a/src/librustc/infer/mod.rs +++ b/src/librustc/infer/mod.rs @@ -790,7 +790,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { self.projection_cache .borrow_mut() - .commit(&projection_cache_snapshot); + .commit(projection_cache_snapshot); self.type_variables.borrow_mut().commit(type_snapshot); self.int_unification_table.borrow_mut().commit(int_snapshot); self.float_unification_table diff --git a/src/librustc/traits/project.rs b/src/librustc/traits/project.rs index e44bf2661c55c..8f165863b3bab 100644 --- a/src/librustc/traits/project.rs +++ b/src/librustc/traits/project.rs @@ -1652,15 +1652,15 @@ impl<'tcx> ProjectionCache<'tcx> { } pub fn rollback_to(&mut self, snapshot: ProjectionCacheSnapshot) { - self.map.rollback_to(&snapshot.snapshot); + self.map.rollback_to(snapshot.snapshot); } pub fn rollback_placeholder(&mut self, snapshot: &ProjectionCacheSnapshot) { self.map.partial_rollback(&snapshot.snapshot, &|k| k.ty.has_re_skol()); } - pub fn commit(&mut self, snapshot: &ProjectionCacheSnapshot) { - self.map.commit(&snapshot.snapshot); + pub fn commit(&mut self, snapshot: ProjectionCacheSnapshot) { + self.map.commit(snapshot.snapshot); } /// Try to start normalize `key`; returns an error if diff --git a/src/librustc_data_structures/snapshot_map/mod.rs b/src/librustc_data_structures/snapshot_map/mod.rs index 6ad886625d8b5..8b761bdc8c622 100644 --- a/src/librustc_data_structures/snapshot_map/mod.rs +++ b/src/librustc_data_structures/snapshot_map/mod.rs @@ -102,8 +102,8 @@ impl SnapshotMap }); } - pub fn commit(&mut self, snapshot: &Snapshot) { - self.assert_open_snapshot(snapshot); + pub fn commit(&mut self, snapshot: Snapshot) { + self.assert_open_snapshot(&snapshot); if snapshot.len == 0 { // The root snapshot. self.undo_log.clear(); @@ -134,8 +134,8 @@ impl SnapshotMap } } - pub fn rollback_to(&mut self, snapshot: &Snapshot) { - self.assert_open_snapshot(snapshot); + pub fn rollback_to(&mut self, snapshot: Snapshot) { + self.assert_open_snapshot(&snapshot); while self.undo_log.len() > snapshot.len + 1 { let entry = self.undo_log.pop().unwrap(); self.reverse(entry); diff --git a/src/librustc_data_structures/snapshot_map/test.rs b/src/librustc_data_structures/snapshot_map/test.rs index 700f9c95e3b57..67be806261b74 100644 --- a/src/librustc_data_structures/snapshot_map/test.rs +++ b/src/librustc_data_structures/snapshot_map/test.rs @@ -20,7 +20,7 @@ fn basic() { map.insert(44, "fourty-four"); assert_eq!(map[&44], "fourty-four"); assert_eq!(map.get(&33), None); - map.rollback_to(&snapshot); + map.rollback_to(snapshot); assert_eq!(map[&22], "twenty-two"); assert_eq!(map.get(&33), None); assert_eq!(map.get(&44), None); @@ -33,7 +33,7 @@ fn out_of_order() { map.insert(22, "twenty-two"); let snapshot1 = map.snapshot(); let _snapshot2 = map.snapshot(); - map.rollback_to(&snapshot1); + map.rollback_to(snapshot1); } #[test] @@ -43,8 +43,8 @@ fn nested_commit_then_rollback() { let snapshot1 = map.snapshot(); let snapshot2 = map.snapshot(); map.insert(22, "thirty-three"); - map.commit(&snapshot2); + map.commit(snapshot2); assert_eq!(map[&22], "thirty-three"); - map.rollback_to(&snapshot1); + map.rollback_to(snapshot1); assert_eq!(map[&22], "twenty-two"); } From f23c969492fe5aeb2e33316032387175020d2672 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 5 Nov 2018 10:44:52 +1100 Subject: [PATCH 78/88] Introduce `in_snapshot` and `assert_open_snapshot` methods. This makes the two snapshot implementations more consistent with each other and with crate `ena`. --- src/librustc/infer/region_constraints/mod.rs | 11 +++++++---- src/librustc_data_structures/snapshot_map/mod.rs | 10 +++++++--- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/src/librustc/infer/region_constraints/mod.rs b/src/librustc/infer/region_constraints/mod.rs index eb4c0d08f9fa1..231eff9ff9fd7 100644 --- a/src/librustc/infer/region_constraints/mod.rs +++ b/src/librustc/infer/region_constraints/mod.rs @@ -429,10 +429,14 @@ impl<'tcx> RegionConstraintCollector<'tcx> { } } - pub fn commit(&mut self, snapshot: RegionSnapshot) { - debug!("RegionConstraintCollector: commit({})", snapshot.length); + fn assert_open_snapshot(&self, snapshot: &RegionSnapshot) { assert!(self.undo_log.len() > snapshot.length); assert!(self.undo_log[snapshot.length] == OpenSnapshot); + } + + pub fn commit(&mut self, snapshot: RegionSnapshot) { + debug!("RegionConstraintCollector: commit({})", snapshot.length); + self.assert_open_snapshot(&snapshot); if snapshot.length == 0 { self.undo_log.clear(); @@ -444,8 +448,7 @@ impl<'tcx> RegionConstraintCollector<'tcx> { pub fn rollback_to(&mut self, snapshot: RegionSnapshot) { debug!("RegionConstraintCollector: rollback_to({:?})", snapshot); - assert!(self.undo_log.len() > snapshot.length); - assert!(self.undo_log[snapshot.length] == OpenSnapshot); + self.assert_open_snapshot(&snapshot); while self.undo_log.len() > snapshot.length + 1 { let undo_entry = self.undo_log.pop().unwrap(); self.rollback_undo_entry(undo_entry); diff --git a/src/librustc_data_structures/snapshot_map/mod.rs b/src/librustc_data_structures/snapshot_map/mod.rs index 8b761bdc8c622..a480499a30c3c 100644 --- a/src/librustc_data_structures/snapshot_map/mod.rs +++ b/src/librustc_data_structures/snapshot_map/mod.rs @@ -55,16 +55,20 @@ impl SnapshotMap self.undo_log.clear(); } + fn in_snapshot(&self) -> bool { + !self.undo_log.is_empty() + } + pub fn insert(&mut self, key: K, value: V) -> bool { match self.map.insert(key.clone(), value) { None => { - if !self.undo_log.is_empty() { + if self.in_snapshot() { self.undo_log.push(UndoLog::Inserted(key)); } true } Some(old_value) => { - if !self.undo_log.is_empty() { + if self.in_snapshot() { self.undo_log.push(UndoLog::Overwrite(key, old_value)); } false @@ -75,7 +79,7 @@ impl SnapshotMap pub fn remove(&mut self, key: K) -> bool { match self.map.remove(&key) { Some(old_value) => { - if !self.undo_log.is_empty() { + if self.in_snapshot() { self.undo_log.push(UndoLog::Overwrite(key, old_value)); } true From 2d68fa07bff2b417094024597790d30acd501ab3 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 5 Nov 2018 10:59:33 +1100 Subject: [PATCH 79/88] Remove `OpenSnapshot` and `CommittedSnapshot` markers from `SnapshotMap`. They're not strictly necessary, and they result in the `Vec` being allocated even for the trivial (and common) case where a `start_snapshot` is immediately followed by a `commit` or `rollback_to`. --- .../snapshot_map/mod.rs | 48 +++++++------------ .../snapshot_map/test.rs | 11 +++-- 2 files changed, 25 insertions(+), 34 deletions(-) diff --git a/src/librustc_data_structures/snapshot_map/mod.rs b/src/librustc_data_structures/snapshot_map/mod.rs index a480499a30c3c..c256506a19d42 100644 --- a/src/librustc_data_structures/snapshot_map/mod.rs +++ b/src/librustc_data_structures/snapshot_map/mod.rs @@ -21,6 +21,7 @@ pub struct SnapshotMap { map: FxHashMap, undo_log: Vec>, + num_open_snapshots: usize, } // HACK(eddyb) manual impl avoids `Default` bounds on `K` and `V`. @@ -31,6 +32,7 @@ impl Default for SnapshotMap SnapshotMap { map: Default::default(), undo_log: Default::default(), + num_open_snapshots: 0, } } } @@ -40,8 +42,6 @@ pub struct Snapshot { } enum UndoLog { - OpenSnapshot, - CommittedSnapshot, Inserted(K), Overwrite(K, V), Purged, @@ -53,10 +53,11 @@ impl SnapshotMap pub fn clear(&mut self) { self.map.clear(); self.undo_log.clear(); + self.num_open_snapshots = 0; } fn in_snapshot(&self) -> bool { - !self.undo_log.is_empty() + self.num_open_snapshots > 0 } pub fn insert(&mut self, key: K, value: V) -> bool { @@ -93,27 +94,27 @@ impl SnapshotMap } pub fn snapshot(&mut self) -> Snapshot { - self.undo_log.push(UndoLog::OpenSnapshot); - let len = self.undo_log.len() - 1; + let len = self.undo_log.len(); + self.num_open_snapshots += 1; Snapshot { len } } fn assert_open_snapshot(&self, snapshot: &Snapshot) { - assert!(snapshot.len < self.undo_log.len()); - assert!(match self.undo_log[snapshot.len] { - UndoLog::OpenSnapshot => true, - _ => false, - }); + assert!(self.undo_log.len() >= snapshot.len); + assert!(self.num_open_snapshots > 0); } pub fn commit(&mut self, snapshot: Snapshot) { self.assert_open_snapshot(&snapshot); - if snapshot.len == 0 { - // The root snapshot. + if self.num_open_snapshots == 1 { + // The root snapshot. It's safe to clear the undo log because + // there's no snapshot further out that we might need to roll back + // to. + assert!(snapshot.len == 0); self.undo_log.clear(); - } else { - self.undo_log[snapshot.len] = UndoLog::CommittedSnapshot; } + + self.num_open_snapshots -= 1; } pub fn partial_rollback(&mut self, @@ -122,10 +123,8 @@ impl SnapshotMap where F: Fn(&K) -> bool { self.assert_open_snapshot(snapshot); - for i in (snapshot.len + 1..self.undo_log.len()).rev() { + for i in (snapshot.len .. self.undo_log.len()).rev() { let reverse = match self.undo_log[i] { - UndoLog::OpenSnapshot => false, - UndoLog::CommittedSnapshot => false, UndoLog::Purged => false, UndoLog::Inserted(ref k) => should_revert_key(k), UndoLog::Overwrite(ref k, _) => should_revert_key(k), @@ -140,27 +139,16 @@ impl SnapshotMap pub fn rollback_to(&mut self, snapshot: Snapshot) { self.assert_open_snapshot(&snapshot); - while self.undo_log.len() > snapshot.len + 1 { + while self.undo_log.len() > snapshot.len { let entry = self.undo_log.pop().unwrap(); self.reverse(entry); } - let v = self.undo_log.pop().unwrap(); - assert!(match v { - UndoLog::OpenSnapshot => true, - _ => false, - }); - assert!(self.undo_log.len() == snapshot.len); + self.num_open_snapshots -= 1; } fn reverse(&mut self, entry: UndoLog) { match entry { - UndoLog::OpenSnapshot => { - panic!("cannot rollback an uncommitted snapshot"); - } - - UndoLog::CommittedSnapshot => {} - UndoLog::Inserted(key) => { self.map.remove(&key); } diff --git a/src/librustc_data_structures/snapshot_map/test.rs b/src/librustc_data_structures/snapshot_map/test.rs index 67be806261b74..b4ecb85fc4302 100644 --- a/src/librustc_data_structures/snapshot_map/test.rs +++ b/src/librustc_data_structures/snapshot_map/test.rs @@ -17,8 +17,8 @@ fn basic() { let snapshot = map.snapshot(); map.insert(22, "thirty-three"); assert_eq!(map[&22], "thirty-three"); - map.insert(44, "fourty-four"); - assert_eq!(map[&44], "fourty-four"); + map.insert(44, "forty-four"); + assert_eq!(map[&44], "forty-four"); assert_eq!(map.get(&33), None); map.rollback_to(snapshot); assert_eq!(map[&22], "twenty-two"); @@ -32,8 +32,11 @@ fn out_of_order() { let mut map = SnapshotMap::default(); map.insert(22, "twenty-two"); let snapshot1 = map.snapshot(); - let _snapshot2 = map.snapshot(); - map.rollback_to(snapshot1); + map.insert(33, "thirty-three"); + let snapshot2 = map.snapshot(); + map.insert(44, "forty-four"); + map.rollback_to(snapshot1); // bogus, but accepted + map.rollback_to(snapshot2); // asserts } #[test] From 94967ae8c1129d63df4446240df4fc45a57c8164 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 5 Nov 2018 12:21:54 +1100 Subject: [PATCH 80/88] Remove `OpenSnapshot` and `CommittedSnapshot` markers from `RegionConstraintCollector`. They're not strictly necessary, and they result in the `Vec` being allocated even for the trivial (and common) case where a `start_snapshot` is immediately followed by a `commit` or `rollback_to`. The commit also removes a now-unnecessary argument of `pop_placeholders()`. --- src/librustc/infer/higher_ranked/mod.rs | 6 +- src/librustc/infer/region_constraints/mod.rs | 65 +++++++++---------- .../infer/region_constraints/taint.rs | 3 +- 3 files changed, 32 insertions(+), 42 deletions(-) diff --git a/src/librustc/infer/higher_ranked/mod.rs b/src/librustc/infer/higher_ranked/mod.rs index 8172f620c3646..4e203b986dfb0 100644 --- a/src/librustc/infer/higher_ranked/mod.rs +++ b/src/librustc/infer/higher_ranked/mod.rs @@ -543,11 +543,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { ) { debug!("pop_placeholders({:?})", placeholder_map); let placeholder_regions: FxHashSet<_> = placeholder_map.values().cloned().collect(); - self.borrow_region_constraints() - .pop_placeholders( - &placeholder_regions, - &snapshot.region_constraints_snapshot, - ); + self.borrow_region_constraints().pop_placeholders(&placeholder_regions); self.universe.set(snapshot.universe); if !placeholder_map.is_empty() { self.projection_cache.borrow_mut().rollback_placeholder( diff --git a/src/librustc/infer/region_constraints/mod.rs b/src/librustc/infer/region_constraints/mod.rs index 231eff9ff9fd7..af1b6964b8189 100644 --- a/src/librustc/infer/region_constraints/mod.rs +++ b/src/librustc/infer/region_constraints/mod.rs @@ -52,15 +52,18 @@ pub struct RegionConstraintCollector<'tcx> { /// The undo log records actions that might later be undone. /// - /// Note: when the undo_log is empty, we are not actively + /// Note: `num_open_snapshots` is used to track if we are actively /// snapshotting. When the `start_snapshot()` method is called, we - /// push an OpenSnapshot entry onto the list to indicate that we - /// are now actively snapshotting. The reason for this is that - /// otherwise we end up adding entries for things like the lower - /// bound on a variable and so forth, which can never be rolled - /// back. + /// increment `num_open_snapshots` to indicate that we are now actively + /// snapshotting. The reason for this is that otherwise we end up adding + /// entries for things like the lower bound on a variable and so forth, + /// which can never be rolled back. undo_log: Vec>, + /// The number of open snapshots, i.e. those that haven't been committed or + /// rolled back. + num_open_snapshots: usize, + /// When we add a R1 == R2 constriant, we currently add (a) edges /// R1 <= R2 and R2 <= R1 and (b) we unify the two regions in this /// table. You can then call `opportunistic_resolve_var` early @@ -255,14 +258,6 @@ struct TwoRegions<'tcx> { #[derive(Copy, Clone, PartialEq)] enum UndoLog<'tcx> { - /// Pushed when we start a snapshot. - OpenSnapshot, - - /// Replaces an `OpenSnapshot` when a snapshot is committed, but - /// that snapshot is not the root. If the root snapshot is - /// unrolled, all nested snapshots must be committed. - CommitedSnapshot, - /// We added `RegionVid` AddVar(RegionVid), @@ -387,6 +382,7 @@ impl<'tcx> RegionConstraintCollector<'tcx> { glbs, bound_count: _, undo_log: _, + num_open_snapshots: _, unification_table, any_unifications, } = self; @@ -415,13 +411,13 @@ impl<'tcx> RegionConstraintCollector<'tcx> { } fn in_snapshot(&self) -> bool { - !self.undo_log.is_empty() + self.num_open_snapshots > 0 } pub fn start_snapshot(&mut self) -> RegionSnapshot { let length = self.undo_log.len(); debug!("RegionConstraintCollector: start_snapshot({})", length); - self.undo_log.push(OpenSnapshot); + self.num_open_snapshots += 1; RegionSnapshot { length, region_snapshot: self.unification_table.snapshot(), @@ -430,41 +426,45 @@ impl<'tcx> RegionConstraintCollector<'tcx> { } fn assert_open_snapshot(&self, snapshot: &RegionSnapshot) { - assert!(self.undo_log.len() > snapshot.length); - assert!(self.undo_log[snapshot.length] == OpenSnapshot); + assert!(self.undo_log.len() >= snapshot.length); + assert!(self.num_open_snapshots > 0); } pub fn commit(&mut self, snapshot: RegionSnapshot) { debug!("RegionConstraintCollector: commit({})", snapshot.length); self.assert_open_snapshot(&snapshot); - if snapshot.length == 0 { + if self.num_open_snapshots == 1 { + // The root snapshot. It's safe to clear the undo log because + // there's no snapshot further out that we might need to roll back + // to. + assert!(snapshot.length == 0); self.undo_log.clear(); - } else { - (*self.undo_log)[snapshot.length] = CommitedSnapshot; } + + self.num_open_snapshots -= 1; + self.unification_table.commit(snapshot.region_snapshot); } pub fn rollback_to(&mut self, snapshot: RegionSnapshot) { debug!("RegionConstraintCollector: rollback_to({:?})", snapshot); self.assert_open_snapshot(&snapshot); - while self.undo_log.len() > snapshot.length + 1 { + + while self.undo_log.len() > snapshot.length { let undo_entry = self.undo_log.pop().unwrap(); self.rollback_undo_entry(undo_entry); } - let c = self.undo_log.pop().unwrap(); - assert!(c == OpenSnapshot); + + self.num_open_snapshots -= 1; + self.unification_table.rollback_to(snapshot.region_snapshot); self.any_unifications = snapshot.any_unifications; } fn rollback_undo_entry(&mut self, undo_entry: UndoLog<'tcx>) { match undo_entry { - OpenSnapshot => { - panic!("Failure to observe stack discipline"); - } - Purged | CommitedSnapshot => { + Purged => { // nothing to do here } AddVar(vid) => { @@ -524,15 +524,10 @@ impl<'tcx> RegionConstraintCollector<'tcx> { /// in `skols`. This is used after a higher-ranked operation /// completes to remove all trace of the placeholder regions /// created in that time. - pub fn pop_placeholders( - &mut self, - placeholders: &FxHashSet>, - snapshot: &RegionSnapshot, - ) { + pub fn pop_placeholders(&mut self, placeholders: &FxHashSet>) { debug!("pop_placeholders(placeholders={:?})", placeholders); assert!(self.in_snapshot()); - assert!(self.undo_log[snapshot.length] == OpenSnapshot); let constraints_to_kill: Vec = self.undo_log .iter() @@ -565,7 +560,7 @@ impl<'tcx> RegionConstraintCollector<'tcx> { &AddCombination(_, ref two_regions) => { placeholders.contains(&two_regions.a) || placeholders.contains(&two_regions.b) } - &AddVar(..) | &OpenSnapshot | &Purged | &CommitedSnapshot => false, + &AddVar(..) | &Purged => false, } } } diff --git a/src/librustc/infer/region_constraints/taint.rs b/src/librustc/infer/region_constraints/taint.rs index 9f08fdcad7eea..27ce7f106030a 100644 --- a/src/librustc/infer/region_constraints/taint.rs +++ b/src/librustc/infer/region_constraints/taint.rs @@ -65,8 +65,7 @@ impl<'tcx> TaintSet<'tcx> { "we never add verifications while doing higher-ranked things", ) } - &Purged | &AddCombination(..) | &AddVar(..) | &OpenSnapshot - | &CommitedSnapshot => {} + &Purged | &AddCombination(..) | &AddVar(..) => {} } } } From 68a26ec647147d70bcd7f0e7f56a0bf9fedb5f06 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Sun, 25 Nov 2018 08:26:22 +0100 Subject: [PATCH 81/88] Stabilize the int_to_from_bytes feature Fixes #52963 --- src/libcore/num/mod.rs | 48 ++++++---------------- src/test/run-pass/const-int-conversion.rs | 2 +- src/test/ui/consts/const-int-conversion.rs | 2 +- 3 files changed, 14 insertions(+), 38 deletions(-) diff --git a/src/libcore/num/mod.rs b/src/libcore/num/mod.rs index 30b7b45468412..9deae12482976 100644 --- a/src/libcore/num/mod.rs +++ b/src/libcore/num/mod.rs @@ -1921,12 +1921,10 @@ big-endian (network) byte order. # Examples ``` -#![feature(int_to_from_bytes)] - let bytes = ", $swap_op, stringify!($SelfT), ".to_be_bytes(); assert_eq!(bytes, ", $be_bytes, "); ```"), - #[unstable(feature = "int_to_from_bytes", issue = "52963")] + #[stable(feature = "int_to_from_bytes", since = "1.32.0")] #[rustc_const_unstable(feature = "const_int_conversion")] #[inline] pub const fn to_be_bytes(self) -> [u8; mem::size_of::()] { @@ -1941,12 +1939,10 @@ little-endian byte order. # Examples ``` -#![feature(int_to_from_bytes)] - let bytes = ", $swap_op, stringify!($SelfT), ".to_le_bytes(); assert_eq!(bytes, ", $le_bytes, "); ```"), - #[unstable(feature = "int_to_from_bytes", issue = "52963")] + #[stable(feature = "int_to_from_bytes", since = "1.32.0")] #[rustc_const_unstable(feature = "const_int_conversion")] #[inline] pub const fn to_le_bytes(self) -> [u8; mem::size_of::()] { @@ -1969,8 +1965,6 @@ instead. # Examples ``` -#![feature(int_to_from_bytes)] - let bytes = ", $swap_op, stringify!($SelfT), ".to_ne_bytes(); assert_eq!(bytes, if cfg!(target_endian = \"big\") { ", $be_bytes, " @@ -1978,7 +1972,7 @@ assert_eq!(bytes, if cfg!(target_endian = \"big\") { ", $le_bytes, " }); ```"), - #[unstable(feature = "int_to_from_bytes", issue = "52963")] + #[stable(feature = "int_to_from_bytes", since = "1.32.0")] #[rustc_const_unstable(feature = "const_int_conversion")] #[inline] pub const fn to_ne_bytes(self) -> [u8; mem::size_of::()] { @@ -1993,12 +1987,10 @@ big endian. # Examples ``` -#![feature(int_to_from_bytes)] - let value = ", stringify!($SelfT), "::from_be_bytes(", $be_bytes, "); assert_eq!(value, ", $swap_op, "); ```"), - #[unstable(feature = "int_to_from_bytes", issue = "52963")] + #[stable(feature = "int_to_from_bytes", since = "1.32.0")] #[rustc_const_unstable(feature = "const_int_conversion")] #[inline] pub const fn from_be_bytes(bytes: [u8; mem::size_of::()]) -> Self { @@ -2014,12 +2006,10 @@ little endian. # Examples ``` -#![feature(int_to_from_bytes)] - let value = ", stringify!($SelfT), "::from_le_bytes(", $le_bytes, "); assert_eq!(value, ", $swap_op, "); ```"), - #[unstable(feature = "int_to_from_bytes", issue = "52963")] + #[stable(feature = "int_to_from_bytes", since = "1.32.0")] #[rustc_const_unstable(feature = "const_int_conversion")] #[inline] pub const fn from_le_bytes(bytes: [u8; mem::size_of::()]) -> Self { @@ -2041,8 +2031,6 @@ appropriate instead. # Examples ``` -#![feature(int_to_from_bytes)] - let value = ", stringify!($SelfT), "::from_ne_bytes(if cfg!(target_endian = \"big\") { ", $be_bytes, " } else { @@ -2050,7 +2038,7 @@ let value = ", stringify!($SelfT), "::from_ne_bytes(if cfg!(target_endian = \"bi }); assert_eq!(value, ", $swap_op, "); ```"), - #[unstable(feature = "int_to_from_bytes", issue = "52963")] + #[stable(feature = "int_to_from_bytes", since = "1.32.0")] #[rustc_const_unstable(feature = "const_int_conversion")] #[inline] pub const fn from_ne_bytes(bytes: [u8; mem::size_of::()]) -> Self { @@ -3663,12 +3651,10 @@ big-endian (network) byte order. # Examples ``` -#![feature(int_to_from_bytes)] - let bytes = ", $swap_op, stringify!($SelfT), ".to_be_bytes(); assert_eq!(bytes, ", $be_bytes, "); ```"), - #[unstable(feature = "int_to_from_bytes", issue = "52963")] + #[stable(feature = "int_to_from_bytes", since = "1.32.0")] #[rustc_const_unstable(feature = "const_int_conversion")] #[inline] pub const fn to_be_bytes(self) -> [u8; mem::size_of::()] { @@ -3683,12 +3669,10 @@ little-endian byte order. # Examples ``` -#![feature(int_to_from_bytes)] - let bytes = ", $swap_op, stringify!($SelfT), ".to_le_bytes(); assert_eq!(bytes, ", $le_bytes, "); ```"), - #[unstable(feature = "int_to_from_bytes", issue = "52963")] + #[stable(feature = "int_to_from_bytes", since = "1.32.0")] #[rustc_const_unstable(feature = "const_int_conversion")] #[inline] pub const fn to_le_bytes(self) -> [u8; mem::size_of::()] { @@ -3711,8 +3695,6 @@ instead. # Examples ``` -#![feature(int_to_from_bytes)] - let bytes = ", $swap_op, stringify!($SelfT), ".to_ne_bytes(); assert_eq!(bytes, if cfg!(target_endian = \"big\") { ", $be_bytes, " @@ -3720,7 +3702,7 @@ assert_eq!(bytes, if cfg!(target_endian = \"big\") { ", $le_bytes, " }); ```"), - #[unstable(feature = "int_to_from_bytes", issue = "52963")] + #[stable(feature = "int_to_from_bytes", since = "1.32.0")] #[rustc_const_unstable(feature = "const_int_conversion")] #[inline] pub const fn to_ne_bytes(self) -> [u8; mem::size_of::()] { @@ -3735,12 +3717,10 @@ big endian. # Examples ``` -#![feature(int_to_from_bytes)] - let value = ", stringify!($SelfT), "::from_be_bytes(", $be_bytes, "); assert_eq!(value, ", $swap_op, "); ```"), - #[unstable(feature = "int_to_from_bytes", issue = "52963")] + #[stable(feature = "int_to_from_bytes", since = "1.32.0")] #[rustc_const_unstable(feature = "const_int_conversion")] #[inline] pub const fn from_be_bytes(bytes: [u8; mem::size_of::()]) -> Self { @@ -3756,12 +3736,10 @@ little endian. # Examples ``` -#![feature(int_to_from_bytes)] - let value = ", stringify!($SelfT), "::from_le_bytes(", $le_bytes, "); assert_eq!(value, ", $swap_op, "); ```"), - #[unstable(feature = "int_to_from_bytes", issue = "52963")] + #[stable(feature = "int_to_from_bytes", since = "1.32.0")] #[rustc_const_unstable(feature = "const_int_conversion")] #[inline] pub const fn from_le_bytes(bytes: [u8; mem::size_of::()]) -> Self { @@ -3783,8 +3761,6 @@ appropriate instead. # Examples ``` -#![feature(int_to_from_bytes)] - let value = ", stringify!($SelfT), "::from_ne_bytes(if cfg!(target_endian = \"big\") { ", $be_bytes, " } else { @@ -3792,7 +3768,7 @@ let value = ", stringify!($SelfT), "::from_ne_bytes(if cfg!(target_endian = \"bi }); assert_eq!(value, ", $swap_op, "); ```"), - #[unstable(feature = "int_to_from_bytes", issue = "52963")] + #[stable(feature = "int_to_from_bytes", since = "1.32.0")] #[rustc_const_unstable(feature = "const_int_conversion")] #[inline] pub const fn from_ne_bytes(bytes: [u8; mem::size_of::()]) -> Self { diff --git a/src/test/run-pass/const-int-conversion.rs b/src/test/run-pass/const-int-conversion.rs index 790c62288d38b..e199c43858521 100644 --- a/src/test/run-pass/const-int-conversion.rs +++ b/src/test/run-pass/const-int-conversion.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(const_int_conversion, const_int_ops, reverse_bits, int_to_from_bytes)] +#![feature(const_int_conversion, const_int_ops, reverse_bits)] const REVERSE: u32 = 0x12345678_u32.reverse_bits(); const FROM_BE_BYTES: i32 = i32::from_be_bytes([0x12, 0x34, 0x56, 0x78]); diff --git a/src/test/ui/consts/const-int-conversion.rs b/src/test/ui/consts/const-int-conversion.rs index 0abe6b4a1e49f..2a20f0df15ca2 100644 --- a/src/test/ui/consts/const-int-conversion.rs +++ b/src/test/ui/consts/const-int-conversion.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(reverse_bits, int_to_from_bytes)] +#![feature(reverse_bits)] fn main() { let x: &'static i32 = &(5_i32.reverse_bits()); From af54eb2916b9f5707c7bfa577f5db29918870ce2 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 25 Nov 2018 10:56:10 +0100 Subject: [PATCH 82/88] read_c_str should call the AllocationExtra hooks --- src/librustc/mir/interpret/allocation.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/librustc/mir/interpret/allocation.rs b/src/librustc/mir/interpret/allocation.rs index c612d6ad1bb24..406d41d343684 100644 --- a/src/librustc/mir/interpret/allocation.rs +++ b/src/librustc/mir/interpret/allocation.rs @@ -172,10 +172,9 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra> Allocation { let offset = ptr.offset.bytes() as usize; match self.bytes[offset..].iter().position(|&c| c == 0) { Some(size) => { - let p1 = Size::from_bytes((size + 1) as u64); - self.check_relocations(cx, ptr, p1)?; - self.check_defined(ptr, p1)?; - Ok(&self.bytes[offset..offset + size]) + let size = Size::from_bytes((size + 1) as u64); + // Go through `get_bytes` for checks and AllocationExtra hooks + self.get_bytes(cx, ptr, size) } None => err!(UnterminatedCString(ptr.erase_tag())), } From 0fac350f9982b78ef5c74ac7db98933262d361b5 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 25 Nov 2018 11:23:21 +0100 Subject: [PATCH 83/88] yay for NLL --- src/librustc/mir/interpret/allocation.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/librustc/mir/interpret/allocation.rs b/src/librustc/mir/interpret/allocation.rs index 406d41d343684..3ff0e9f177ddc 100644 --- a/src/librustc/mir/interpret/allocation.rs +++ b/src/librustc/mir/interpret/allocation.rs @@ -314,11 +314,9 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra> Allocation { }, }; - { - let endian = cx.data_layout().endian; - let dst = self.get_bytes_mut(cx, ptr, type_size)?; - write_target_uint(endian, dst, bytes).unwrap(); - } + let endian = cx.data_layout().endian; + let dst = self.get_bytes_mut(cx, ptr, type_size)?; + write_target_uint(endian, dst, bytes).unwrap(); // See if we have to also write a relocation match val { From a6ea01f2396a55c5245b93b8f9c6edd3c5a0e204 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 25 Nov 2018 12:07:20 +0100 Subject: [PATCH 84/88] fix length of slice returned from read_c_str --- src/librustc/mir/interpret/allocation.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/librustc/mir/interpret/allocation.rs b/src/librustc/mir/interpret/allocation.rs index 3ff0e9f177ddc..0ecec75339861 100644 --- a/src/librustc/mir/interpret/allocation.rs +++ b/src/librustc/mir/interpret/allocation.rs @@ -172,9 +172,11 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra> Allocation { let offset = ptr.offset.bytes() as usize; match self.bytes[offset..].iter().position(|&c| c == 0) { Some(size) => { - let size = Size::from_bytes((size + 1) as u64); - // Go through `get_bytes` for checks and AllocationExtra hooks - self.get_bytes(cx, ptr, size) + let size_with_null = Size::from_bytes((size + 1) as u64); + // Go through `get_bytes` for checks and AllocationExtra hooks. + // We read the null, so we include it in the requestm, but we want it removed + // from the result! + Ok(&self.get_bytes(cx, ptr, size_with_null)?[..size]) } None => err!(UnterminatedCString(ptr.erase_tag())), } From 2472e832503995a024a6fbf533b504a0d0bf9e9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20S=CC=B6c=CC=B6h=CC=B6n=CC=B6e=CC=B6i=CC=B6d=CC=B6?= =?UTF-8?q?e=CC=B6r=20Scherer?= Date: Sun, 25 Nov 2018 14:21:34 +0100 Subject: [PATCH 85/88] Typo Co-Authored-By: RalfJung --- src/librustc/mir/interpret/allocation.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustc/mir/interpret/allocation.rs b/src/librustc/mir/interpret/allocation.rs index 0ecec75339861..ab63e882c4a55 100644 --- a/src/librustc/mir/interpret/allocation.rs +++ b/src/librustc/mir/interpret/allocation.rs @@ -174,7 +174,7 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra> Allocation { Some(size) => { let size_with_null = Size::from_bytes((size + 1) as u64); // Go through `get_bytes` for checks and AllocationExtra hooks. - // We read the null, so we include it in the requestm, but we want it removed + // We read the null, so we include it in the request, but we want it removed // from the result! Ok(&self.get_bytes(cx, ptr, size_with_null)?[..size]) } From d4a78da543fd8959edf386602537bece057a3918 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Wed, 31 Oct 2018 00:22:19 +0300 Subject: [PATCH 86/88] resolve: Prohibit relative paths in visibilities on 2018 edition --- src/librustc_resolve/lib.rs | 13 ++++++++++++- src/test/ui/privacy/restricted/relative-2018.rs | 13 +++++++++++++ .../ui/privacy/restricted/relative-2018.stderr | 16 ++++++++++++++++ 3 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 src/test/ui/privacy/restricted/relative-2018.rs create mode 100644 src/test/ui/privacy/restricted/relative-2018.stderr diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs index 12dabd2a31da1..a392ab717c06c 100644 --- a/src/librustc_resolve/lib.rs +++ b/src/librustc_resolve/lib.rs @@ -4710,7 +4710,18 @@ impl<'a, 'crateloader: 'a> Resolver<'a, 'crateloader> { ty::Visibility::Restricted(self.current_module.normal_ancestor_id) } ast::VisibilityKind::Restricted { ref path, id, .. } => { - // Visibilities are resolved as global by default, add starting root segment. + // For visibilities we are not ready to provide correct implementation of "uniform + // paths" right now, so on 2018 edition we only allow module-relative paths for now. + let first_ident = path.segments[0].ident; + if self.session.rust_2018() && !first_ident.is_path_segment_keyword() { + let msg = "relative paths are not supported in visibilities on 2018 edition"; + self.session.struct_span_err(first_ident.span, msg) + .span_suggestion(path.span, "try", format!("crate::{}", path)) + .emit(); + return ty::Visibility::Public; + } + // On 2015 visibilities are resolved as crate-relative by default, + // add starting root segment if necessary. let segments = path.make_root().iter().chain(path.segments.iter()) .map(|seg| Segment { ident: seg.ident, id: Some(seg.id) }) .collect::>(); diff --git a/src/test/ui/privacy/restricted/relative-2018.rs b/src/test/ui/privacy/restricted/relative-2018.rs new file mode 100644 index 0000000000000..69b7c1e4d4f3c --- /dev/null +++ b/src/test/ui/privacy/restricted/relative-2018.rs @@ -0,0 +1,13 @@ +// edition:2018 + +mod m { + pub(in crate) struct S1; // OK + pub(in super) struct S2; // OK + pub(in self) struct S3; // OK + pub(in ::core) struct S4; + //~^ ERROR visibilities can only be restricted to ancestor modules + pub(in a::b) struct S5; + //~^ ERROR relative paths are not supported in visibilities on 2018 edition +} + +fn main() {} diff --git a/src/test/ui/privacy/restricted/relative-2018.stderr b/src/test/ui/privacy/restricted/relative-2018.stderr new file mode 100644 index 0000000000000..61effc463e98f --- /dev/null +++ b/src/test/ui/privacy/restricted/relative-2018.stderr @@ -0,0 +1,16 @@ +error: visibilities can only be restricted to ancestor modules + --> $DIR/relative-2018.rs:7:12 + | +LL | pub(in ::core) struct S4; + | ^^^^^^ + +error: relative paths are not supported in visibilities on 2018 edition + --> $DIR/relative-2018.rs:9:12 + | +LL | pub(in a::b) struct S5; + | ^--- + | | + | help: try: `crate::a::b` + +error: aborting due to 2 previous errors + From e593431bc7f3ad89aea8e792384d6719bf60f3f3 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Sun, 25 Nov 2018 04:25:59 +0300 Subject: [PATCH 87/88] resolve: Fix bad span arithmetics in import conflict diagnostics --- src/librustc_resolve/lib.rs | 10 +++++----- src/test/ui/issues/issue-45829/import-self.rs | 3 +++ .../ui/issues/issue-45829/import-self.stderr | 20 ++++++++++++++++--- 3 files changed, 25 insertions(+), 8 deletions(-) diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs index a392ab717c06c..443b1ccdef836 100644 --- a/src/librustc_resolve/lib.rs +++ b/src/librustc_resolve/lib.rs @@ -4999,10 +4999,10 @@ impl<'a, 'crateloader: 'a> Resolver<'a, 'crateloader> { err.span_suggestion_with_applicability( binding.span, &rename_msg, - match (&directive.subclass, snippet.as_ref()) { - (ImportDirectiveSubclass::SingleImport { .. }, "self") => + match directive.subclass { + ImportDirectiveSubclass::SingleImport { type_ns_only: true, .. } => format!("self as {}", suggested_name), - (ImportDirectiveSubclass::SingleImport { source, .. }, _) => + ImportDirectiveSubclass::SingleImport { source, .. } => format!( "{} as {}{}", &snippet[..((source.span.hi().0 - binding.span.lo().0) as usize)], @@ -5013,13 +5013,13 @@ impl<'a, 'crateloader: 'a> Resolver<'a, 'crateloader> { "" } ), - (ImportDirectiveSubclass::ExternCrate { source, target, .. }, _) => + ImportDirectiveSubclass::ExternCrate { source, target, .. } => format!( "extern crate {} as {};", source.unwrap_or(target.name), suggested_name, ), - (_, _) => unreachable!(), + _ => unreachable!(), }, Applicability::MaybeIncorrect, ); diff --git a/src/test/ui/issues/issue-45829/import-self.rs b/src/test/ui/issues/issue-45829/import-self.rs index 8b13ffd0076d5..eb5fb458d8279 100644 --- a/src/test/ui/issues/issue-45829/import-self.rs +++ b/src/test/ui/issues/issue-45829/import-self.rs @@ -19,4 +19,7 @@ use foo as self; use foo::self; +use foo::A; +use foo::{self as A}; + fn main() {} diff --git a/src/test/ui/issues/issue-45829/import-self.stderr b/src/test/ui/issues/issue-45829/import-self.stderr index 985dc4e7131cf..55e51952a8804 100644 --- a/src/test/ui/issues/issue-45829/import-self.stderr +++ b/src/test/ui/issues/issue-45829/import-self.stderr @@ -25,7 +25,21 @@ help: you can use `as` to change the binding name of the import LL | use foo::{self as other_foo}; | ^^^^^^^^^^^^^^^^^ -error: aborting due to 3 previous errors +error[E0252]: the name `A` is defined multiple times + --> $DIR/import-self.rs:23:11 + | +LL | use foo::A; + | ------ previous import of the type `A` here +LL | use foo::{self as A}; + | ^^^^^^^^^ `A` reimported here + | + = note: `A` must be defined only once in the type namespace of this module +help: you can use `as` to change the binding name of the import + | +LL | use foo::{self as OtherA}; + | ^^^^^^^^^^^^^^ + +error: aborting due to 4 previous errors -Some errors occurred: E0255, E0429. -For more information about an error, try `rustc --explain E0255`. +Some errors occurred: E0252, E0255, E0429. +For more information about an error, try `rustc --explain E0252`. From fe548e311a8f3a2e193989dc959841874738423f Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Sun, 25 Nov 2018 05:45:43 +0300 Subject: [PATCH 88/88] resolve: Fix some more asserts in import validation --- src/librustc_resolve/resolve_imports.rs | 3 +- src/test/ui/imports/auxiliary/issue-56125.rs | 2 + src/test/ui/imports/issue-56125.rs | 25 +++++++-- src/test/ui/imports/issue-56125.stderr | 59 +++++++++++++------- 4 files changed, 63 insertions(+), 26 deletions(-) diff --git a/src/librustc_resolve/resolve_imports.rs b/src/librustc_resolve/resolve_imports.rs index 616cc9d2fc51b..52e3e54b9f931 100644 --- a/src/librustc_resolve/resolve_imports.rs +++ b/src/librustc_resolve/resolve_imports.rs @@ -864,7 +864,8 @@ impl<'a, 'b:'a, 'c: 'b> ImportResolver<'a, 'b, 'c> { } PathResult::NonModule(path_res) if path_res.base_def() == Def::Err => { // The error was already reported earlier. - assert!(directive.imported_module.get().is_none()); + assert!(!self.ambiguity_errors.is_empty() || + directive.imported_module.get().is_none()); return None; } PathResult::Indeterminate | PathResult::NonModule(..) => unreachable!(), diff --git a/src/test/ui/imports/auxiliary/issue-56125.rs b/src/test/ui/imports/auxiliary/issue-56125.rs index 0ff407756b3ae..8e0797582970d 100644 --- a/src/test/ui/imports/auxiliary/issue-56125.rs +++ b/src/test/ui/imports/auxiliary/issue-56125.rs @@ -1,3 +1,5 @@ +pub mod issue_56125 {} + pub mod last_segment { pub mod issue_56125 {} } diff --git a/src/test/ui/imports/issue-56125.rs b/src/test/ui/imports/issue-56125.rs index 4baeb8a34dd76..843b52f18435e 100644 --- a/src/test/ui/imports/issue-56125.rs +++ b/src/test/ui/imports/issue-56125.rs @@ -2,11 +2,24 @@ // compile-flags:--extern issue_56125 // aux-build:issue-56125.rs -use issue_56125::last_segment::*; -//~^ ERROR `issue_56125` is ambiguous -//~| ERROR unresolved import `issue_56125::last_segment` -use issue_56125::non_last_segment::non_last_segment::*; -//~^ ERROR `issue_56125` is ambiguous -//~| ERROR failed to resolve: could not find `non_last_segment` in `issue_56125` +#![feature(uniform_paths)] + +mod m1 { + use issue_56125::last_segment::*; + //~^ ERROR `issue_56125` is ambiguous + //~| ERROR unresolved import `issue_56125::last_segment` +} + +mod m2 { + use issue_56125::non_last_segment::non_last_segment::*; + //~^ ERROR `issue_56125` is ambiguous + //~| ERROR failed to resolve: could not find `non_last_segment` in `issue_56125` +} + +mod m3 { + mod empty {} + use empty::issue_56125; //~ ERROR unresolved import `empty::issue_56125` + use issue_56125::*; //~ ERROR `issue_56125` is ambiguous +} fn main() {} diff --git a/src/test/ui/imports/issue-56125.stderr b/src/test/ui/imports/issue-56125.stderr index 096d5be97f0e0..b1292ef8f783e 100644 --- a/src/test/ui/imports/issue-56125.stderr +++ b/src/test/ui/imports/issue-56125.stderr @@ -1,46 +1,67 @@ error[E0433]: failed to resolve: could not find `non_last_segment` in `issue_56125` - --> $DIR/issue-56125.rs:8:18 + --> $DIR/issue-56125.rs:14:22 | -LL | use issue_56125::non_last_segment::non_last_segment::*; - | ^^^^^^^^^^^^^^^^ could not find `non_last_segment` in `issue_56125` +LL | use issue_56125::non_last_segment::non_last_segment::*; + | ^^^^^^^^^^^^^^^^ could not find `non_last_segment` in `issue_56125` error[E0432]: unresolved import `issue_56125::last_segment` - --> $DIR/issue-56125.rs:5:18 + --> $DIR/issue-56125.rs:8:22 | -LL | use issue_56125::last_segment::*; - | ^^^^^^^^^^^^ could not find `last_segment` in `issue_56125` +LL | use issue_56125::last_segment::*; + | ^^^^^^^^^^^^ could not find `last_segment` in `issue_56125` + +error[E0432]: unresolved import `empty::issue_56125` + --> $DIR/issue-56125.rs:21:9 + | +LL | use empty::issue_56125; //~ ERROR unresolved import `empty::issue_56125` + | ^^^^^^^^^^^^^^^^^^ no `issue_56125` in `m3::empty` error[E0659]: `issue_56125` is ambiguous (name vs any other name during import resolution) - --> $DIR/issue-56125.rs:5:5 + --> $DIR/issue-56125.rs:8:9 | -LL | use issue_56125::last_segment::*; - | ^^^^^^^^^^^ ambiguous name +LL | use issue_56125::last_segment::*; + | ^^^^^^^^^^^ ambiguous name | = note: `issue_56125` could refer to an extern crate passed with `--extern` = help: use `::issue_56125` to refer to this extern crate unambiguously note: `issue_56125` could also refer to the module imported here - --> $DIR/issue-56125.rs:5:5 + --> $DIR/issue-56125.rs:8:9 | -LL | use issue_56125::last_segment::*; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | use issue_56125::last_segment::*; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = help: use `self::issue_56125` to refer to this module unambiguously error[E0659]: `issue_56125` is ambiguous (name vs any other name during import resolution) - --> $DIR/issue-56125.rs:8:5 + --> $DIR/issue-56125.rs:14:9 | -LL | use issue_56125::non_last_segment::non_last_segment::*; - | ^^^^^^^^^^^ ambiguous name +LL | use issue_56125::non_last_segment::non_last_segment::*; + | ^^^^^^^^^^^ ambiguous name | = note: `issue_56125` could refer to an extern crate passed with `--extern` = help: use `::issue_56125` to refer to this extern crate unambiguously note: `issue_56125` could also refer to the module imported here - --> $DIR/issue-56125.rs:5:5 + --> $DIR/issue-56125.rs:14:9 | -LL | use issue_56125::last_segment::*; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | use issue_56125::non_last_segment::non_last_segment::*; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = help: use `self::issue_56125` to refer to this module unambiguously -error: aborting due to 4 previous errors +error[E0659]: `issue_56125` is ambiguous (name vs any other name during import resolution) + --> $DIR/issue-56125.rs:22:9 + | +LL | use issue_56125::*; //~ ERROR `issue_56125` is ambiguous + | ^^^^^^^^^^^ ambiguous name + | + = note: `issue_56125` could refer to an extern crate passed with `--extern` + = help: use `::issue_56125` to refer to this extern crate unambiguously +note: `issue_56125` could also refer to the unresolved item imported here + --> $DIR/issue-56125.rs:21:9 + | +LL | use empty::issue_56125; //~ ERROR unresolved import `empty::issue_56125` + | ^^^^^^^^^^^^^^^^^^ + = help: use `self::issue_56125` to refer to this unresolved item unambiguously + +error: aborting due to 6 previous errors Some errors occurred: E0432, E0433, E0659. For more information about an error, try `rustc --explain E0432`.