From 6674db48872c1b84fe3ac3feb94b8d3e0ee82b24 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Tue, 20 Nov 2018 20:16:57 +1100 Subject: [PATCH 01/36] Reuse the `P` in `InvocationCollector::fold_{,opt_}expr`. This requires adding a new method, `P::filter_map`. This commit reduces instruction counts for various benchmarks by up to 0.7%. --- src/libsyntax/ext/expand.rs | 84 +++++++++++++++++++++---------------- src/libsyntax/ptr.rs | 28 +++++++++++-- 2 files changed, 73 insertions(+), 39 deletions(-) diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index cc8af70a050c9..68a96293891a0 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -1201,50 +1201,62 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { impl<'a, 'b> Folder for InvocationCollector<'a, 'b> { fn fold_expr(&mut self, expr: P) -> P { - let mut expr = self.cfg.configure_expr(expr).into_inner(); - expr.node = self.cfg.configure_expr_kind(expr.node); - - // ignore derives so they remain unused - let (attr, expr, after_derive) = self.classify_nonitem(expr); - - if attr.is_some() { - // collect the invoc regardless of whether or not attributes are permitted here - // expansion will eat the attribute so it won't error later - attr.as_ref().map(|a| self.cfg.maybe_emit_expr_attr_err(a)); - - // AstFragmentKind::Expr requires the macro to emit an expression - return self.collect_attr(attr, vec![], Annotatable::Expr(P(expr)), - AstFragmentKind::Expr, after_derive).make_expr(); - } + let expr = self.cfg.configure_expr(expr); + expr.map(|mut expr| { + expr.node = self.cfg.configure_expr_kind(expr.node); + + // ignore derives so they remain unused + let (attr, expr, after_derive) = self.classify_nonitem(expr); + + if attr.is_some() { + // Collect the invoc regardless of whether or not attributes are permitted here + // expansion will eat the attribute so it won't error later. + attr.as_ref().map(|a| self.cfg.maybe_emit_expr_attr_err(a)); + + // AstFragmentKind::Expr requires the macro to emit an expression. + return self.collect_attr(attr, vec![], Annotatable::Expr(P(expr)), + AstFragmentKind::Expr, after_derive) + .make_expr() + .into_inner() + } - if let ast::ExprKind::Mac(mac) = expr.node { - self.check_attributes(&expr.attrs); - self.collect_bang(mac, expr.span, AstFragmentKind::Expr).make_expr() - } else { - P(noop_fold_expr(expr, self)) - } + if let ast::ExprKind::Mac(mac) = expr.node { + self.check_attributes(&expr.attrs); + self.collect_bang(mac, expr.span, AstFragmentKind::Expr) + .make_expr() + .into_inner() + } else { + noop_fold_expr(expr, self) + } + }) } fn fold_opt_expr(&mut self, expr: P) -> Option> { - let mut expr = configure!(self, expr).into_inner(); - expr.node = self.cfg.configure_expr_kind(expr.node); + let expr = configure!(self, expr); + expr.filter_map(|mut expr| { + expr.node = self.cfg.configure_expr_kind(expr.node); - // ignore derives so they remain unused - let (attr, expr, after_derive) = self.classify_nonitem(expr); + // Ignore derives so they remain unused. + let (attr, expr, after_derive) = self.classify_nonitem(expr); - if attr.is_some() { - attr.as_ref().map(|a| self.cfg.maybe_emit_expr_attr_err(a)); + if attr.is_some() { + attr.as_ref().map(|a| self.cfg.maybe_emit_expr_attr_err(a)); - return self.collect_attr(attr, vec![], Annotatable::Expr(P(expr)), - AstFragmentKind::OptExpr, after_derive).make_opt_expr(); - } + return self.collect_attr(attr, vec![], Annotatable::Expr(P(expr)), + AstFragmentKind::OptExpr, after_derive) + .make_opt_expr() + .map(|expr| expr.into_inner()) + } - if let ast::ExprKind::Mac(mac) = expr.node { - self.check_attributes(&expr.attrs); - self.collect_bang(mac, expr.span, AstFragmentKind::OptExpr).make_opt_expr() - } else { - Some(P(noop_fold_expr(expr, self))) - } + if let ast::ExprKind::Mac(mac) = expr.node { + self.check_attributes(&expr.attrs); + self.collect_bang(mac, expr.span, AstFragmentKind::OptExpr) + .make_opt_expr() + .map(|expr| expr.into_inner()) + } else { + Some(noop_fold_expr(expr, self)) + } + }) } fn fold_pat(&mut self, pat: P) -> P { diff --git a/src/libsyntax/ptr.rs b/src/libsyntax/ptr.rs index bb47d9b535bef..9fbc64758da4d 100644 --- a/src/libsyntax/ptr.rs +++ b/src/libsyntax/ptr.rs @@ -72,7 +72,7 @@ impl P { *self.ptr } - /// Transform the inner value, consuming `self` and producing a new `P`. + /// Produce a new `P` from `self` without reallocating. pub fn map(mut self, f: F) -> P where F: FnOnce(T) -> T, { @@ -88,8 +88,30 @@ impl P { ptr::write(p, f(ptr::read(p))); // Recreate self from the raw pointer. - P { - ptr: Box::from_raw(p) + P { ptr: Box::from_raw(p) } + } + } + + /// Optionally produce a new `P` from `self` without reallocating. + pub fn filter_map(mut self, f: F) -> Option> where + F: FnOnce(T) -> Option, + { + let p: *mut T = &mut *self.ptr; + + // Leak self in case of panic. + // FIXME(eddyb) Use some sort of "free guard" that + // only deallocates, without dropping the pointee, + // in case the call the `f` below ends in a panic. + mem::forget(self); + + unsafe { + if let Some(v) = f(ptr::read(p)) { + ptr::write(p, v); + + // Recreate self from the raw pointer. + Some(P { ptr: Box::from_raw(p) }) + } else { + None } } } From 464c9da9c229111298d413882735ea707a30e077 Mon Sep 17 00:00:00 2001 From: ljedrz Date: Wed, 21 Nov 2018 10:07:33 +0100 Subject: [PATCH 02/36] serialize: preallocate VecDeque in Decodable::decode --- src/libserialize/collection_impls.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libserialize/collection_impls.rs b/src/libserialize/collection_impls.rs index 3e028d755c6d4..cbd642dd6ad91 100644 --- a/src/libserialize/collection_impls.rs +++ b/src/libserialize/collection_impls.rs @@ -86,7 +86,7 @@ impl Encodable for VecDeque { impl Decodable for VecDeque { fn decode(d: &mut D) -> Result, D::Error> { d.read_seq(|d, len| { - let mut deque: VecDeque = VecDeque::new(); + let mut deque: VecDeque = VecDeque::with_capacity(len); for i in 0..len { deque.push_back(d.read_seq_elt(i, |d| Decodable::decode(d))?); } From 591607d237665857880e16899788517b9b82d414 Mon Sep 17 00:00:00 2001 From: ljedrz Date: Wed, 21 Nov 2018 10:17:54 +0100 Subject: [PATCH 03/36] String: add a FIXME to from_utf16 --- src/liballoc/string.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/liballoc/string.rs b/src/liballoc/string.rs index 8d009101ce7da..0b25d911a299c 100644 --- a/src/liballoc/string.rs +++ b/src/liballoc/string.rs @@ -618,6 +618,8 @@ impl String { /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn from_utf16(v: &[u16]) -> Result { + // This isn't done via collect::>() for performance reasons. + // FIXME: the function can be simplified again when #48994 is closed. let mut ret = String::with_capacity(v.len()); for c in decode_utf16(v.iter().cloned()) { if let Ok(c) = c { From 057e6d3a35a54e8b88c2cef1e6a1b9e590066276 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Sun, 25 Nov 2018 17:33:16 +0100 Subject: [PATCH 04/36] Add TryFrom<&[T]> for [T; $N] where T: Copy `TryFrom<&[T]> for &[T; $N]` (note *reference* to an array) already exists, but not needing to dereference makes type inference easier for example when using `u32::from_be_bytes`. Also add doc examples doing just that. --- src/libcore/array.rs | 9 +++++ src/libcore/num/mod.rs | 78 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 87 insertions(+) diff --git a/src/libcore/array.rs b/src/libcore/array.rs index 3d24f8902bd83..26e7a79d35df6 100644 --- a/src/libcore/array.rs +++ b/src/libcore/array.rs @@ -148,6 +148,15 @@ macro_rules! array_impls { } } + #[unstable(feature = "try_from", issue = "33417")] + impl<'a, T> TryFrom<&'a [T]> for [T; $N] where T: Copy { + type Error = TryFromSliceError; + + fn try_from(slice: &[T]) -> Result<[T; $N], TryFromSliceError> { + <&Self>::try_from(slice).map(|r| *r) + } + } + #[unstable(feature = "try_from", issue = "33417")] impl<'a, T> TryFrom<&'a [T]> for &'a [T; $N] { type Error = TryFromSliceError; diff --git a/src/libcore/num/mod.rs b/src/libcore/num/mod.rs index 9deae12482976..f6f649bc06d01 100644 --- a/src/libcore/num/mod.rs +++ b/src/libcore/num/mod.rs @@ -1989,6 +1989,19 @@ big endian. ``` let value = ", stringify!($SelfT), "::from_be_bytes(", $be_bytes, "); assert_eq!(value, ", $swap_op, "); +``` + +When starting from a slice rather than an array, fallible conversion APIs can be used: + +``` +#![feature(try_from)] +use std::convert::TryInto; + +fn read_be_", stringify!($SelfT), "(input: &mut &[u8]) -> ", stringify!($SelfT), " { + let (int_bytes, rest) = input.split_at(std::mem::size_of::<", stringify!($SelfT), ">()); + *input = rest; + ", stringify!($SelfT), "::from_be_bytes(int_bytes.try_into().unwrap()) +} ```"), #[stable(feature = "int_to_from_bytes", since = "1.32.0")] #[rustc_const_unstable(feature = "const_int_conversion")] @@ -2008,6 +2021,19 @@ little endian. ``` let value = ", stringify!($SelfT), "::from_le_bytes(", $le_bytes, "); assert_eq!(value, ", $swap_op, "); +``` + +When starting from a slice rather than an array, fallible conversion APIs can be used: + +``` +#![feature(try_from)] +use std::convert::TryInto; + +fn read_be_", stringify!($SelfT), "(input: &mut &[u8]) -> ", stringify!($SelfT), " { + let (int_bytes, rest) = input.split_at(std::mem::size_of::<", stringify!($SelfT), ">()); + *input = rest; + ", stringify!($SelfT), "::from_be_bytes(int_bytes.try_into().unwrap()) +} ```"), #[stable(feature = "int_to_from_bytes", since = "1.32.0")] #[rustc_const_unstable(feature = "const_int_conversion")] @@ -2037,6 +2063,19 @@ let value = ", stringify!($SelfT), "::from_ne_bytes(if cfg!(target_endian = \"bi ", $le_bytes, " }); assert_eq!(value, ", $swap_op, "); +``` + +When starting from a slice rather than an array, fallible conversion APIs can be used: + +``` +#![feature(try_from)] +use std::convert::TryInto; + +fn read_be_", stringify!($SelfT), "(input: &mut &[u8]) -> ", stringify!($SelfT), " { + let (int_bytes, rest) = input.split_at(std::mem::size_of::<", stringify!($SelfT), ">()); + *input = rest; + ", stringify!($SelfT), "::from_be_bytes(int_bytes.try_into().unwrap()) +} ```"), #[stable(feature = "int_to_from_bytes", since = "1.32.0")] #[rustc_const_unstable(feature = "const_int_conversion")] @@ -3719,6 +3758,19 @@ big endian. ``` let value = ", stringify!($SelfT), "::from_be_bytes(", $be_bytes, "); assert_eq!(value, ", $swap_op, "); +``` + +When starting from a slice rather than an array, fallible conversion APIs can be used: + +``` +#![feature(try_from)] +use std::convert::TryInto; + +fn read_be_", stringify!($SelfT), "(input: &mut &[u8]) -> ", stringify!($SelfT), " { + let (int_bytes, rest) = input.split_at(std::mem::size_of::<", stringify!($SelfT), ">()); + *input = rest; + ", stringify!($SelfT), "::from_be_bytes(int_bytes.try_into().unwrap()) +} ```"), #[stable(feature = "int_to_from_bytes", since = "1.32.0")] #[rustc_const_unstable(feature = "const_int_conversion")] @@ -3738,6 +3790,19 @@ little endian. ``` let value = ", stringify!($SelfT), "::from_le_bytes(", $le_bytes, "); assert_eq!(value, ", $swap_op, "); +``` + +When starting from a slice rather than an array, fallible conversion APIs can be used: + +``` +#![feature(try_from)] +use std::convert::TryInto; + +fn read_be_", stringify!($SelfT), "(input: &mut &[u8]) -> ", stringify!($SelfT), " { + let (int_bytes, rest) = input.split_at(std::mem::size_of::<", stringify!($SelfT), ">()); + *input = rest; + ", stringify!($SelfT), "::from_be_bytes(int_bytes.try_into().unwrap()) +} ```"), #[stable(feature = "int_to_from_bytes", since = "1.32.0")] #[rustc_const_unstable(feature = "const_int_conversion")] @@ -3767,6 +3832,19 @@ let value = ", stringify!($SelfT), "::from_ne_bytes(if cfg!(target_endian = \"bi ", $le_bytes, " }); assert_eq!(value, ", $swap_op, "); +``` + +When starting from a slice rather than an array, fallible conversion APIs can be used: + +``` +#![feature(try_from)] +use std::convert::TryInto; + +fn read_be_", stringify!($SelfT), "(input: &mut &[u8]) -> ", stringify!($SelfT), " { + let (int_bytes, rest) = input.split_at(std::mem::size_of::<", stringify!($SelfT), ">()); + *input = rest; + ", stringify!($SelfT), "::from_be_bytes(int_bytes.try_into().unwrap()) +} ```"), #[stable(feature = "int_to_from_bytes", since = "1.32.0")] #[rustc_const_unstable(feature = "const_int_conversion")] From 769d7115fed078c93f66d8db24de2046a6ab6334 Mon Sep 17 00:00:00 2001 From: Andy Russell Date: Wed, 14 Nov 2018 14:51:28 -0500 Subject: [PATCH 05/36] add test for issue #21335 Fixes #21335. --- src/test/run-make/llvm-outputs/Makefile | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 src/test/run-make/llvm-outputs/Makefile diff --git a/src/test/run-make/llvm-outputs/Makefile b/src/test/run-make/llvm-outputs/Makefile new file mode 100644 index 0000000000000..d7f67577b04ce --- /dev/null +++ b/src/test/run-make/llvm-outputs/Makefile @@ -0,0 +1,5 @@ +-include ../../run-make-fulldeps/tools.mk + +all: + echo 'fn main() {}' | $(BARE_RUSTC) - --out-dir=$(TMPDIR)/random_directory_that_does_not_exist_ir/ --emit=llvm-ir + echo 'fn main() {}' | $(BARE_RUSTC) - --out-dir=$(TMPDIR)/random_directory_that_does_not_exist_bc/ --emit=llvm-bc From d4a6e739f3322c56e66fa8fef31569b7f16bc325 Mon Sep 17 00:00:00 2001 From: ljedrz Date: Fri, 9 Nov 2018 15:12:09 +0100 Subject: [PATCH 06/36] Use sort_by_cached_key when key the function is not trivial/free --- src/librustc/middle/resolve_lifetime.rs | 2 +- src/librustc/traits/object_safety.rs | 2 +- src/librustc_mir/monomorphize/partitioning.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/librustc/middle/resolve_lifetime.rs b/src/librustc/middle/resolve_lifetime.rs index 6ff450508d136..07054ee99af76 100644 --- a/src/librustc/middle/resolve_lifetime.rs +++ b/src/librustc/middle/resolve_lifetime.rs @@ -1573,7 +1573,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { .collect(); // ensure that we issue lints in a repeatable order - def_ids.sort_by_key(|&def_id| self.tcx.def_path_hash(def_id)); + def_ids.sort_by_cached_key(|&def_id| self.tcx.def_path_hash(def_id)); for def_id in def_ids { debug!( diff --git a/src/librustc/traits/object_safety.rs b/src/librustc/traits/object_safety.rs index c79fa3861234f..2909daf22b3ba 100644 --- a/src/librustc/traits/object_safety.rs +++ b/src/librustc/traits/object_safety.rs @@ -409,7 +409,7 @@ impl<'a, 'tcx> TyCtxt<'a, 'tcx, 'tcx> { .collect::>(); // existential predicates need to be in a specific order - associated_types.sort_by_key(|item| self.def_path_hash(item.def_id)); + associated_types.sort_by_cached_key(|item| self.def_path_hash(item.def_id)); let projection_predicates = associated_types.into_iter().map(|item| { ty::ExistentialPredicate::Projection(ty::ExistentialProjection { diff --git a/src/librustc_mir/monomorphize/partitioning.rs b/src/librustc_mir/monomorphize/partitioning.rs index 6dba020120f84..3a6ee6da42215 100644 --- a/src/librustc_mir/monomorphize/partitioning.rs +++ b/src/librustc_mir/monomorphize/partitioning.rs @@ -985,7 +985,7 @@ fn collect_and_partition_mono_items<'a, 'tcx>( output.push_str(" @@"); let mut empty = Vec::new(); let cgus = item_to_cgus.get_mut(i).unwrap_or(&mut empty); - cgus.as_mut_slice().sort_by_key(|&(ref name, _)| name.clone()); + cgus.as_mut_slice().sort_by_cached_key(|&(ref name, _)| name.clone()); cgus.dedup(); for &(ref cgu_name, (linkage, _)) in cgus.iter() { output.push_str(" "); From 6c80f7c4fcb83be65c0b3595eb30690c626a30e3 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Wed, 28 Nov 2018 16:45:58 +1100 Subject: [PATCH 07/36] Fix whitespace in `pp.rs`. This commit converts some 2-space indents to 4-space indents. --- src/libsyntax/print/pp.rs | 252 +++++++++++++++++++------------------- 1 file changed, 126 insertions(+), 126 deletions(-) diff --git a/src/libsyntax/print/pp.rs b/src/libsyntax/print/pp.rs index 086de35d531c7..00f22597a3fa3 100644 --- a/src/libsyntax/print/pp.rs +++ b/src/libsyntax/print/pp.rs @@ -316,75 +316,75 @@ impl<'a> Printer<'a> { pub fn pretty_print(&mut self, token: Token) -> io::Result<()> { debug!("pp Vec<{},{}>", self.left, self.right); match token { - Token::Eof => { - if !self.scan_stack.is_empty() { - self.check_stack(0); - self.advance_left()?; - } - self.indent(0); - Ok(()) - } - Token::Begin(b) => { - if self.scan_stack.is_empty() { - self.left_total = 1; - self.right_total = 1; - self.left = 0; - self.right = 0; - } else { - self.advance_right(); + Token::Eof => { + if !self.scan_stack.is_empty() { + self.check_stack(0); + self.advance_left()?; + } + self.indent(0); + Ok(()) } - debug!("pp Begin({})/buffer Vec<{},{}>", - b.offset, self.left, self.right); - self.buf[self.right] = BufEntry { token: token, size: -self.right_total }; - let right = self.right; - self.scan_push(right); - Ok(()) - } - Token::End => { - if self.scan_stack.is_empty() { - debug!("pp End/print Vec<{},{}>", self.left, self.right); - self.print(token, 0) - } else { - debug!("pp End/buffer Vec<{},{}>", self.left, self.right); - self.advance_right(); - self.buf[self.right] = BufEntry { token: token, size: -1 }; + Token::Begin(b) => { + if self.scan_stack.is_empty() { + self.left_total = 1; + self.right_total = 1; + self.left = 0; + self.right = 0; + } else { + self.advance_right(); + } + debug!("pp Begin({})/buffer Vec<{},{}>", + b.offset, self.left, self.right); + self.buf[self.right] = BufEntry { token: token, size: -self.right_total }; let right = self.right; self.scan_push(right); Ok(()) } - } - Token::Break(b) => { - if self.scan_stack.is_empty() { - self.left_total = 1; - self.right_total = 1; - self.left = 0; - self.right = 0; - } else { - self.advance_right(); + Token::End => { + if self.scan_stack.is_empty() { + debug!("pp End/print Vec<{},{}>", self.left, self.right); + self.print(token, 0) + } else { + debug!("pp End/buffer Vec<{},{}>", self.left, self.right); + self.advance_right(); + self.buf[self.right] = BufEntry { token: token, size: -1 }; + let right = self.right; + self.scan_push(right); + Ok(()) + } } - debug!("pp Break({})/buffer Vec<{},{}>", - b.offset, self.left, self.right); - self.check_stack(0); - let right = self.right; - self.scan_push(right); - self.buf[self.right] = BufEntry { token: token, size: -self.right_total }; - self.right_total += b.blank_space; - Ok(()) - } - Token::String(s, len) => { - if self.scan_stack.is_empty() { - debug!("pp String('{}')/print Vec<{},{}>", - s, self.left, self.right); - self.print(Token::String(s, len), len) - } else { - debug!("pp String('{}')/buffer Vec<{},{}>", - s, self.left, self.right); - self.advance_right(); - self.buf[self.right] = BufEntry { token: Token::String(s, len), size: len }; - self.right_total += len; - self.check_stream() + Token::Break(b) => { + if self.scan_stack.is_empty() { + self.left_total = 1; + self.right_total = 1; + self.left = 0; + self.right = 0; + } else { + self.advance_right(); + } + debug!("pp Break({})/buffer Vec<{},{}>", + b.offset, self.left, self.right); + self.check_stack(0); + let right = self.right; + self.scan_push(right); + self.buf[self.right] = BufEntry { token: token, size: -self.right_total }; + self.right_total += b.blank_space; + Ok(()) + } + Token::String(s, len) => { + if self.scan_stack.is_empty() { + debug!("pp String('{}')/print Vec<{},{}>", + s, self.left, self.right); + self.print(Token::String(s, len), len) + } else { + debug!("pp String('{}')/buffer Vec<{},{}>", + s, self.left, self.right); + self.advance_right(); + self.buf[self.right] = BufEntry { token: Token::String(s, len), size: len }; + self.right_total += len; + self.check_stream() + } } - } } } pub fn check_stream(&mut self) -> io::Result<()> { @@ -523,74 +523,74 @@ impl<'a> Printer<'a> { self.right, 6)); match token { - Token::Begin(b) => { - if l > self.space { - let col = self.margin - self.space + b.offset; - debug!("print Begin -> push broken block at col {}", col); - self.print_stack.push(PrintStackElem { - offset: col, - pbreak: PrintStackBreak::Broken(b.breaks) - }); - } else { - debug!("print Begin -> push fitting block"); - self.print_stack.push(PrintStackElem { - offset: 0, - pbreak: PrintStackBreak::Fits - }); - } - Ok(()) - } - Token::End => { - debug!("print End -> pop End"); - let print_stack = &mut self.print_stack; - assert!(!print_stack.is_empty()); - print_stack.pop().unwrap(); - Ok(()) - } - Token::Break(b) => { - let top = self.get_top(); - match top.pbreak { - PrintStackBreak::Fits => { - debug!("print Break({}) in fitting block", b.blank_space); - self.space -= b.blank_space; - self.indent(b.blank_space); - Ok(()) - } - PrintStackBreak::Broken(Breaks::Consistent) => { - debug!("print Break({}+{}) in consistent block", - top.offset, b.offset); - let ret = self.print_newline(top.offset + b.offset); - self.space = self.margin - (top.offset + b.offset); - ret - } - PrintStackBreak::Broken(Breaks::Inconsistent) => { + Token::Begin(b) => { if l > self.space { - debug!("print Break({}+{}) w/ newline in inconsistent", - top.offset, b.offset); - let ret = self.print_newline(top.offset + b.offset); - self.space = self.margin - (top.offset + b.offset); - ret + let col = self.margin - self.space + b.offset; + debug!("print Begin -> push broken block at col {}", col); + self.print_stack.push(PrintStackElem { + offset: col, + pbreak: PrintStackBreak::Broken(b.breaks) + }); } else { - debug!("print Break({}) w/o newline in inconsistent", - b.blank_space); - self.indent(b.blank_space); - self.space -= b.blank_space; - Ok(()) + debug!("print Begin -> push fitting block"); + self.print_stack.push(PrintStackElem { + offset: 0, + pbreak: PrintStackBreak::Fits + }); } - } + Ok(()) + } + Token::End => { + debug!("print End -> pop End"); + let print_stack = &mut self.print_stack; + assert!(!print_stack.is_empty()); + print_stack.pop().unwrap(); + Ok(()) + } + Token::Break(b) => { + let top = self.get_top(); + match top.pbreak { + PrintStackBreak::Fits => { + debug!("print Break({}) in fitting block", b.blank_space); + self.space -= b.blank_space; + self.indent(b.blank_space); + Ok(()) + } + PrintStackBreak::Broken(Breaks::Consistent) => { + debug!("print Break({}+{}) in consistent block", + top.offset, b.offset); + let ret = self.print_newline(top.offset + b.offset); + self.space = self.margin - (top.offset + b.offset); + ret + } + PrintStackBreak::Broken(Breaks::Inconsistent) => { + if l > self.space { + debug!("print Break({}+{}) w/ newline in inconsistent", + top.offset, b.offset); + let ret = self.print_newline(top.offset + b.offset); + self.space = self.margin - (top.offset + b.offset); + ret + } else { + debug!("print Break({}) w/o newline in inconsistent", + b.blank_space); + self.indent(b.blank_space); + self.space -= b.blank_space; + Ok(()) + } + } + } + } + Token::String(ref s, len) => { + debug!("print String({})", s); + assert_eq!(l, len); + // assert!(l <= space); + self.space -= len; + self.print_str(s) + } + Token::Eof => { + // Eof should never get here. + panic!(); } - } - Token::String(ref s, len) => { - debug!("print String({})", s); - assert_eq!(l, len); - // assert!(l <= space); - self.space -= len; - self.print_str(s) - } - Token::Eof => { - // Eof should never get here. - panic!(); - } } } From deb9195e5749c4f15e9a5ae0e7ee8e1802c716e4 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Wed, 28 Nov 2018 16:46:43 +1100 Subject: [PATCH 08/36] Remove `huge_word` and `zero_word`. They are unused. The commit also adds some blank lines between some methods. --- src/libsyntax/print/pp.rs | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/src/libsyntax/print/pp.rs b/src/libsyntax/print/pp.rs index 00f22597a3fa3..e01c4b01aaa66 100644 --- a/src/libsyntax/print/pp.rs +++ b/src/libsyntax/print/pp.rs @@ -309,10 +309,12 @@ impl<'a> Printer<'a> { pub fn last_token(&mut self) -> Token { self.buf[self.right].token.clone() } - /// be very careful with this! + + /// Be very careful with this! pub fn replace_last_token(&mut self, t: Token) { self.buf[self.right].token = t; } + pub fn pretty_print(&mut self, token: Token) -> io::Result<()> { debug!("pp Vec<{},{}>", self.left, self.right); match token { @@ -387,6 +389,7 @@ impl<'a> Printer<'a> { } } } + pub fn check_stream(&mut self) -> io::Result<()> { debug!("check_stream Vec<{}, {}> with left_total={}, right_total={}", self.left, self.right, self.left_total, self.right_total); @@ -405,19 +408,24 @@ impl<'a> Printer<'a> { } Ok(()) } + pub fn scan_push(&mut self, x: usize) { debug!("scan_push {}", x); self.scan_stack.push_front(x); } + pub fn scan_pop(&mut self) -> usize { self.scan_stack.pop_front().unwrap() } + pub fn scan_top(&mut self) -> usize { *self.scan_stack.front().unwrap() } + pub fn scan_pop_bottom(&mut self) -> usize { self.scan_stack.pop_back().unwrap() } + pub fn advance_right(&mut self) { self.right += 1; self.right %= self.buf_max_len; @@ -427,6 +435,7 @@ impl<'a> Printer<'a> { } assert_ne!(self.right, self.left); } + pub fn advance_left(&mut self) -> io::Result<()> { debug!("advance_left Vec<{},{}>, sizeof({})={}", self.left, self.right, self.left, self.buf[self.left].size); @@ -461,6 +470,7 @@ impl<'a> Printer<'a> { Ok(()) } + pub fn check_stack(&mut self, k: isize) { if !self.scan_stack.is_empty() { let x = self.scan_top(); @@ -488,6 +498,7 @@ impl<'a> Printer<'a> { } } } + pub fn print_newline(&mut self, amount: isize) -> io::Result<()> { debug!("NEWLINE {}", amount); let ret = write!(self.out, "\n"); @@ -495,10 +506,12 @@ impl<'a> Printer<'a> { self.indent(amount); ret } + pub fn indent(&mut self, amount: isize) { debug!("INDENT {}", amount); self.pending_indentation += amount; } + pub fn get_top(&mut self) -> PrintStackElem { match self.print_stack.last() { Some(el) => *el, @@ -508,6 +521,7 @@ impl<'a> Printer<'a> { } } } + pub fn print_str(&mut self, s: &str) -> io::Result<()> { while self.pending_indentation > 0 { write!(self.out, " ")?; @@ -515,6 +529,7 @@ impl<'a> Printer<'a> { } write!(self.out, "{}", s) } + pub fn print(&mut self, token: Token, l: isize) -> io::Result<()> { debug!("print {} {} (remaining line space={})", token, l, self.space); @@ -633,14 +648,6 @@ impl<'a> Printer<'a> { self.pretty_print(Token::String(wrd.to_string(), wrd.len() as isize)) } - pub fn huge_word(&mut self, wrd: &str) -> io::Result<()> { - self.pretty_print(Token::String(wrd.to_string(), SIZE_INFINITY)) - } - - pub fn zero_word(&mut self, wrd: &str) -> io::Result<()> { - self.pretty_print(Token::String(wrd.to_string(), 0)) - } - fn spaces(&mut self, n: usize) -> io::Result<()> { self.break_offset(n, 0) } From 787959c20d062d396b97a5566e0a766d963af022 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 29 Nov 2018 11:36:58 +1100 Subject: [PATCH 09/36] Use `Cow` in `Token::String`. `Printer::word` takes a `&str` and converts it into a `String`, which causes an allocation. But that allocation is rarely necessary, because `&str` is almost always a `&'static str` or a `String` that won't be used again. This commit changes `Token::String` so it holds a `Cow<'static, str>` instead of a `String`, which avoids a lot of allocations. --- src/librustc/hir/print.rs | 61 ++++++++-------- src/librustc_driver/pretty.rs | 2 +- src/librustc_typeck/check_unused.rs | 2 +- src/libsyntax/parse/parser.rs | 2 +- src/libsyntax/print/pp.rs | 12 +++- src/libsyntax/print/pprust.rs | 106 ++++++++++++++-------------- src/libsyntax_pos/symbol.rs | 4 ++ 7 files changed, 101 insertions(+), 88 deletions(-) diff --git a/src/librustc/hir/print.rs b/src/librustc/hir/print.rs index e69d32ad1deaf..9a0ceddcf1b4a 100644 --- a/src/librustc/hir/print.rs +++ b/src/librustc/hir/print.rs @@ -25,6 +25,7 @@ use hir; use hir::{PatKind, GenericBound, TraitBoundModifier, RangeEnd}; use hir::{GenericParam, GenericParamKind, GenericArg}; +use std::borrow::Cow; use std::cell::Cell; use std::io::{self, Write, Read}; use std::iter::Peekable; @@ -209,7 +210,7 @@ pub fn to_string(ann: &dyn PpAnn, f: F) -> String String::from_utf8(wr).unwrap() } -pub fn visibility_qualified(vis: &hir::Visibility, w: &str) -> String { +pub fn visibility_qualified>>(vis: &hir::Visibility, w: S) -> String { to_string(NO_ANN, |s| { s.print_visibility(vis)?; s.s.word(w) @@ -226,12 +227,13 @@ impl<'a> State<'a> { self.s.word(" ") } - pub fn word_nbsp(&mut self, w: &str) -> io::Result<()> { + pub fn word_nbsp>>(&mut self, w: S) -> io::Result<()> { self.s.word(w)?; self.nbsp() } - pub fn head(&mut self, w: &str) -> io::Result<()> { + pub fn head>>(&mut self, w: S) -> io::Result<()> { + let w = w.into(); // outer-box is consistent self.cbox(indent_unit)?; // head-box is inconsistent @@ -303,7 +305,7 @@ impl<'a> State<'a> { pub fn synth_comment(&mut self, text: String) -> io::Result<()> { self.s.word("/*")?; self.s.space()?; - self.s.word(&text[..])?; + self.s.word(text)?; self.s.space()?; self.s.word("*/") } @@ -468,7 +470,7 @@ impl<'a> State<'a> { self.end() // end the outer fn box } hir::ForeignItemKind::Static(ref t, m) => { - self.head(&visibility_qualified(&item.vis, "static"))?; + self.head(visibility_qualified(&item.vis, "static"))?; if m { self.word_space("mut")?; } @@ -480,7 +482,7 @@ impl<'a> State<'a> { self.end() // end the outer cbox } hir::ForeignItemKind::Type => { - self.head(&visibility_qualified(&item.vis, "type"))?; + self.head(visibility_qualified(&item.vis, "type"))?; self.print_name(item.name)?; self.s.word(";")?; self.end()?; // end the head-ibox @@ -495,7 +497,7 @@ impl<'a> State<'a> { default: Option, vis: &hir::Visibility) -> io::Result<()> { - self.s.word(&visibility_qualified(vis, ""))?; + self.s.word(visibility_qualified(vis, ""))?; self.word_space("const")?; self.print_ident(ident)?; self.word_space(":")?; @@ -534,7 +536,7 @@ impl<'a> State<'a> { self.ann.pre(self, AnnNode::Item(item))?; match item.node { hir::ItemKind::ExternCrate(orig_name) => { - self.head(&visibility_qualified(&item.vis, "extern crate"))?; + self.head(visibility_qualified(&item.vis, "extern crate"))?; if let Some(orig_name) = orig_name { self.print_name(orig_name)?; self.s.space()?; @@ -547,7 +549,7 @@ impl<'a> State<'a> { self.end()?; // end outer head-block } hir::ItemKind::Use(ref path, kind) => { - self.head(&visibility_qualified(&item.vis, "use"))?; + self.head(visibility_qualified(&item.vis, "use"))?; self.print_path(path, false)?; match kind { @@ -566,7 +568,7 @@ impl<'a> State<'a> { self.end()?; // end outer head-block } hir::ItemKind::Static(ref ty, m, expr) => { - self.head(&visibility_qualified(&item.vis, "static"))?; + self.head(visibility_qualified(&item.vis, "static"))?; if m == hir::MutMutable { self.word_space("mut")?; } @@ -582,7 +584,7 @@ impl<'a> State<'a> { self.end()?; // end the outer cbox } hir::ItemKind::Const(ref ty, expr) => { - self.head(&visibility_qualified(&item.vis, "const"))?; + self.head(visibility_qualified(&item.vis, "const"))?; self.print_name(item.name)?; self.word_space(":")?; self.print_type(&ty)?; @@ -609,7 +611,7 @@ impl<'a> State<'a> { self.ann.nested(self, Nested::Body(body))?; } hir::ItemKind::Mod(ref _mod) => { - self.head(&visibility_qualified(&item.vis, "mod"))?; + self.head(visibility_qualified(&item.vis, "mod"))?; self.print_name(item.name)?; self.nbsp()?; self.bopen()?; @@ -618,18 +620,18 @@ impl<'a> State<'a> { } hir::ItemKind::ForeignMod(ref nmod) => { self.head("extern")?; - self.word_nbsp(&nmod.abi.to_string())?; + self.word_nbsp(nmod.abi.to_string())?; self.bopen()?; self.print_foreign_mod(nmod, &item.attrs)?; self.bclose(item.span)?; } hir::ItemKind::GlobalAsm(ref ga) => { - self.head(&visibility_qualified(&item.vis, "global asm"))?; - self.s.word(&ga.asm.as_str())?; + self.head(visibility_qualified(&item.vis, "global asm"))?; + self.s.word(ga.asm.as_str().get())?; self.end()? } hir::ItemKind::Ty(ref ty, ref generics) => { - self.head(&visibility_qualified(&item.vis, "type"))?; + self.head(visibility_qualified(&item.vis, "type"))?; self.print_name(item.name)?; self.print_generic_params(&generics.params)?; self.end()?; // end the inner ibox @@ -642,7 +644,7 @@ impl<'a> State<'a> { self.end()?; // end the outer ibox } hir::ItemKind::Existential(ref exist) => { - self.head(&visibility_qualified(&item.vis, "existential type"))?; + self.head(visibility_qualified(&item.vis, "existential type"))?; self.print_name(item.name)?; self.print_generic_params(&exist.generics.params)?; self.end()?; // end the inner ibox @@ -668,11 +670,11 @@ impl<'a> State<'a> { self.print_enum_def(enum_definition, params, item.name, item.span, &item.vis)?; } hir::ItemKind::Struct(ref struct_def, ref generics) => { - self.head(&visibility_qualified(&item.vis, "struct"))?; + self.head(visibility_qualified(&item.vis, "struct"))?; self.print_struct(struct_def, generics, item.name, item.span, true)?; } hir::ItemKind::Union(ref struct_def, ref generics) => { - self.head(&visibility_qualified(&item.vis, "union"))?; + self.head(visibility_qualified(&item.vis, "union"))?; self.print_struct(struct_def, generics, item.name, item.span, true)?; } hir::ItemKind::Impl(unsafety, @@ -795,7 +797,7 @@ impl<'a> State<'a> { span: syntax_pos::Span, visibility: &hir::Visibility) -> io::Result<()> { - self.head(&visibility_qualified(visibility, "enum"))?; + self.head(visibility_qualified(visibility, "enum"))?; self.print_name(name)?; self.print_generic_params(&generics.params)?; self.print_where_clause(&generics.where_clause)?; @@ -1587,14 +1589,14 @@ impl<'a> State<'a> { } pub fn print_usize(&mut self, i: usize) -> io::Result<()> { - self.s.word(&i.to_string()) + self.s.word(i.to_string()) } pub fn print_ident(&mut self, ident: ast::Ident) -> io::Result<()> { if ident.is_raw_guess() { - self.s.word(&format!("r#{}", ident.name))?; + self.s.word(format!("r#{}", ident.name))?; } else { - self.s.word(&ident.as_str())?; + self.s.word(ident.as_str().get())?; } self.ann.post(self, AnnNode::Name(&ident.name)) } @@ -2010,7 +2012,7 @@ impl<'a> State<'a> { self.commasep(Inconsistent, &decl.inputs, |s, ty| { s.ibox(indent_unit)?; if let Some(arg_name) = arg_names.get(i) { - s.s.word(&arg_name.as_str())?; + s.s.word(arg_name.as_str().get())?; s.s.word(":")?; s.s.space()?; } else if let Some(body_id) = body_id { @@ -2073,7 +2075,8 @@ impl<'a> State<'a> { } } - pub fn print_bounds(&mut self, prefix: &str, bounds: &[hir::GenericBound]) -> io::Result<()> { + pub fn print_bounds(&mut self, prefix: &'static str, bounds: &[hir::GenericBound]) + -> io::Result<()> { if !bounds.is_empty() { self.s.word(prefix)?; let mut first = true; @@ -2322,7 +2325,7 @@ impl<'a> State<'a> { Some(Abi::Rust) => Ok(()), Some(abi) => { self.word_nbsp("extern")?; - self.word_nbsp(&abi.to_string()) + self.word_nbsp(abi.to_string()) } None => Ok(()), } @@ -2332,7 +2335,7 @@ impl<'a> State<'a> { match opt_abi { Some(abi) => { self.word_nbsp("extern")?; - self.word_nbsp(&abi.to_string()) + self.word_nbsp(abi.to_string()) } None => Ok(()), } @@ -2342,7 +2345,7 @@ impl<'a> State<'a> { header: hir::FnHeader, vis: &hir::Visibility) -> io::Result<()> { - self.s.word(&visibility_qualified(vis, ""))?; + self.s.word(visibility_qualified(vis, ""))?; match header.constness { hir::Constness::NotConst => {} @@ -2358,7 +2361,7 @@ impl<'a> State<'a> { if header.abi != Abi::Rust { self.word_nbsp("extern")?; - self.word_nbsp(&header.abi.to_string())?; + self.word_nbsp(header.abi.to_string())?; } self.s.word("fn") diff --git a/src/librustc_driver/pretty.rs b/src/librustc_driver/pretty.rs index c7ba31e339570..fb8093d1d77a7 100644 --- a/src/librustc_driver/pretty.rs +++ b/src/librustc_driver/pretty.rs @@ -530,7 +530,7 @@ impl<'a, 'tcx> pprust_hir::PpAnn for TypedAnnotation<'a, 'tcx> { s.s.space()?; s.s.word("as")?; s.s.space()?; - s.s.word(&self.tables.get().expr_ty(expr).to_string())?; + s.s.word(self.tables.get().expr_ty(expr).to_string())?; s.pclose() } _ => Ok(()), diff --git a/src/librustc_typeck/check_unused.rs b/src/librustc_typeck/check_unused.rs index 22a96d4e908ca..103331894ff1e 100644 --- a/src/librustc_typeck/check_unused.rs +++ b/src/librustc_typeck/check_unused.rs @@ -185,7 +185,7 @@ fn unused_crates_lint<'tcx>(tcx: TyCtxt<'_, 'tcx, 'tcx>) { Some(orig_name) => format!("use {} as {};", orig_name, item.name), None => format!("use {};", item.name), }; - let replacement = visibility_qualified(&item.vis, &base_replacement); + let replacement = visibility_qualified(&item.vis, base_replacement); tcx.struct_span_lint_node(lint, id, extern_crate.span, msg) .span_suggestion_short_with_applicability( extern_crate.span, diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index e2f09affd4fea..99a59fa8a75ca 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -2795,7 +2795,7 @@ impl<'a> Parser<'a> { s.print_usize(float.trunc() as usize)?; s.pclose()?; s.s.word(".")?; - s.s.word(fstr.splitn(2, ".").last().unwrap()) + s.s.word(fstr.splitn(2, ".").last().unwrap().to_string()) }); err.span_suggestion_with_applicability( lo.to(self.prev_span), diff --git a/src/libsyntax/print/pp.rs b/src/libsyntax/print/pp.rs index e01c4b01aaa66..b9addcaeb8edf 100644 --- a/src/libsyntax/print/pp.rs +++ b/src/libsyntax/print/pp.rs @@ -147,6 +147,7 @@ use std::collections::VecDeque; use std::fmt; use std::io; +use std::borrow::Cow; /// How to break. Described in more detail in the module docs. #[derive(Clone, Copy, PartialEq)] @@ -169,7 +170,10 @@ pub struct BeginToken { #[derive(Clone)] pub enum Token { - String(String, isize), + // In practice a string token contains either a `&'static str` or a + // `String`. `Cow` is overkill for this because we never modify the data, + // but it's more convenient than rolling our own more specialized type. + String(Cow<'static, str>, isize), Break(BreakToken), Begin(BeginToken), End, @@ -644,8 +648,10 @@ impl<'a> Printer<'a> { self.pretty_print(Token::Eof) } - pub fn word(&mut self, wrd: &str) -> io::Result<()> { - self.pretty_print(Token::String(wrd.to_string(), wrd.len() as isize)) + pub fn word>>(&mut self, wrd: S) -> io::Result<()> { + let s = wrd.into(); + let len = s.len() as isize; + self.pretty_print(Token::String(s, len)) } fn spaces(&mut self, n: usize) -> io::Result<()> { diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index ce7708cc42e56..14ad4b5c6f815 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -29,6 +29,7 @@ use syntax_pos::{DUMMY_SP, FileName}; use tokenstream::{self, TokenStream, TokenTree}; use std::ascii; +use std::borrow::Cow; use std::io::{self, Write, Read}; use std::iter::Peekable; use std::vec; @@ -444,7 +445,7 @@ pub trait PrintState<'a> { fn cur_lit(&mut self) -> Option<&comments::Literal>; fn bump_lit(&mut self) -> Option; - fn word_space(&mut self, w: &str) -> io::Result<()> { + fn word_space>>(&mut self, w: S) -> io::Result<()> { self.writer().word(w)?; self.writer().space() } @@ -539,7 +540,7 @@ pub trait PrintState<'a> { comments::Mixed => { assert_eq!(cmnt.lines.len(), 1); self.writer().zerobreak()?; - self.writer().word(&cmnt.lines[0])?; + self.writer().word(cmnt.lines[0].clone())?; self.writer().zerobreak() } comments::Isolated => { @@ -548,7 +549,7 @@ pub trait PrintState<'a> { // Don't print empty lines because they will end up as trailing // whitespace if !line.is_empty() { - self.writer().word(&line[..])?; + self.writer().word(line.clone())?; } self.writer().hardbreak()?; } @@ -559,13 +560,13 @@ pub trait PrintState<'a> { self.writer().word(" ")?; } if cmnt.lines.len() == 1 { - self.writer().word(&cmnt.lines[0])?; + self.writer().word(cmnt.lines[0].clone())?; self.writer().hardbreak() } else { self.ibox(0)?; for line in &cmnt.lines { if !line.is_empty() { - self.writer().word(&line[..])?; + self.writer().word(line.clone())?; } self.writer().hardbreak()?; } @@ -610,7 +611,7 @@ pub trait PrintState<'a> { fn print_literal(&mut self, lit: &ast::Lit) -> io::Result<()> { self.maybe_print_comment(lit.span.lo())?; if let Some(ltrl) = self.next_lit(lit.span.lo()) { - return self.writer().word(<rl.lit); + return self.writer().word(ltrl.lit.clone()); } match lit.node { ast::LitKind::Str(st, style) => self.print_string(&st.as_str(), style), @@ -618,31 +619,31 @@ pub trait PrintState<'a> { let mut res = String::from("b'"); res.extend(ascii::escape_default(byte).map(|c| c as char)); res.push('\''); - self.writer().word(&res[..]) + self.writer().word(res) } ast::LitKind::Char(ch) => { let mut res = String::from("'"); res.extend(ch.escape_default()); res.push('\''); - self.writer().word(&res[..]) + self.writer().word(res) } ast::LitKind::Int(i, t) => { match t { ast::LitIntType::Signed(st) => { - self.writer().word(&st.val_to_string(i as i128)) + self.writer().word(st.val_to_string(i as i128)) } ast::LitIntType::Unsigned(ut) => { - self.writer().word(&ut.val_to_string(i)) + self.writer().word(ut.val_to_string(i)) } ast::LitIntType::Unsuffixed => { - self.writer().word(&i.to_string()) + self.writer().word(i.to_string()) } } } ast::LitKind::Float(ref f, t) => { - self.writer().word(&format!("{}{}", &f, t.ty_to_string())) + self.writer().word(format!("{}{}", &f, t.ty_to_string())) } - ast::LitKind::FloatUnsuffixed(ref f) => self.writer().word(&f.as_str()), + ast::LitKind::FloatUnsuffixed(ref f) => self.writer().word(f.as_str().get()), ast::LitKind::Bool(val) => { if val { self.writer().word("true") } else { self.writer().word("false") } } @@ -652,7 +653,7 @@ pub trait PrintState<'a> { escaped.extend(ascii::escape_default(ch) .map(|c| c as char)); } - self.writer().word(&format!("b\"{}\"", escaped)) + self.writer().word(format!("b\"{}\"", escaped)) } } } @@ -669,7 +670,7 @@ pub trait PrintState<'a> { string=st)) } }; - self.writer().word(&st[..]) + self.writer().word(st) } fn print_inner_attributes(&mut self, @@ -727,7 +728,7 @@ pub trait PrintState<'a> { if segment.ident.name != keywords::CrateRoot.name() && segment.ident.name != keywords::DollarCrate.name() { - self.writer().word(&segment.ident.as_str())?; + self.writer().word(segment.ident.as_str().get())?; } else if segment.ident.name == keywords::DollarCrate.name() { self.print_dollar_crate(segment.ident.span.ctxt())?; } @@ -746,7 +747,7 @@ pub trait PrintState<'a> { } self.maybe_print_comment(attr.span.lo())?; if attr.is_sugared_doc { - self.writer().word(&attr.value_str().unwrap().as_str())?; + self.writer().word(attr.value_str().unwrap().as_str().get())?; self.writer().hardbreak() } else { match attr.style { @@ -807,7 +808,7 @@ pub trait PrintState<'a> { fn print_tt(&mut self, tt: tokenstream::TokenTree) -> io::Result<()> { match tt { TokenTree::Token(_, ref tk) => { - self.writer().word(&token_to_string(tk))?; + self.writer().word(token_to_string(tk))?; match *tk { parse::token::DocComment(..) => { self.writer().hardbreak() @@ -816,11 +817,11 @@ pub trait PrintState<'a> { } } TokenTree::Delimited(_, ref delimed) => { - self.writer().word(&token_to_string(&delimed.open_token()))?; + self.writer().word(token_to_string(&delimed.open_token()))?; self.writer().space()?; self.print_tts(delimed.stream())?; self.writer().space()?; - self.writer().word(&token_to_string(&delimed.close_token())) + self.writer().word(token_to_string(&delimed.close_token())) }, } } @@ -889,12 +890,13 @@ impl<'a> State<'a> { self.s.cbox(u) } - pub fn word_nbsp(&mut self, w: &str) -> io::Result<()> { + pub fn word_nbsp>>(&mut self, w: S) -> io::Result<()> { self.s.word(w)?; self.nbsp() } - pub fn head(&mut self, w: &str) -> io::Result<()> { + pub fn head>>(&mut self, w: S) -> io::Result<()> { + let w = w.into(); // outer-box is consistent self.cbox(INDENT_UNIT)?; // head-box is inconsistent @@ -956,7 +958,7 @@ impl<'a> State<'a> { pub fn synth_comment(&mut self, text: String) -> io::Result<()> { self.s.word("/*")?; self.s.space()?; - self.s.word(&text[..])?; + self.s.word(text)?; self.s.space()?; self.s.word("*/") } @@ -1129,7 +1131,7 @@ impl<'a> State<'a> { self.end() // end the outer fn box } ast::ForeignItemKind::Static(ref t, m) => { - self.head(&visibility_qualified(&item.vis, "static"))?; + self.head(visibility_qualified(&item.vis, "static"))?; if m { self.word_space("mut")?; } @@ -1141,7 +1143,7 @@ impl<'a> State<'a> { self.end() // end the outer cbox } ast::ForeignItemKind::Ty => { - self.head(&visibility_qualified(&item.vis, "type"))?; + self.head(visibility_qualified(&item.vis, "type"))?; self.print_ident(item.ident)?; self.s.word(";")?; self.end()?; // end the head-ibox @@ -1164,7 +1166,7 @@ impl<'a> State<'a> { vis: &ast::Visibility) -> io::Result<()> { - self.s.word(&visibility_qualified(vis, ""))?; + self.s.word(visibility_qualified(vis, ""))?; self.word_space("const")?; self.print_ident(ident)?; self.word_space(":")?; @@ -1203,7 +1205,7 @@ impl<'a> State<'a> { self.ann.pre(self, AnnNode::Item(item))?; match item.node { ast::ItemKind::ExternCrate(orig_name) => { - self.head(&visibility_qualified(&item.vis, "extern crate"))?; + self.head(visibility_qualified(&item.vis, "extern crate"))?; if let Some(orig_name) = orig_name { self.print_name(orig_name)?; self.s.space()?; @@ -1216,14 +1218,14 @@ impl<'a> State<'a> { self.end()?; // end outer head-block } ast::ItemKind::Use(ref tree) => { - self.head(&visibility_qualified(&item.vis, "use"))?; + self.head(visibility_qualified(&item.vis, "use"))?; self.print_use_tree(tree)?; self.s.word(";")?; self.end()?; // end inner head-block self.end()?; // end outer head-block } ast::ItemKind::Static(ref ty, m, ref expr) => { - self.head(&visibility_qualified(&item.vis, "static"))?; + self.head(visibility_qualified(&item.vis, "static"))?; if m == ast::Mutability::Mutable { self.word_space("mut")?; } @@ -1239,7 +1241,7 @@ impl<'a> State<'a> { self.end()?; // end the outer cbox } ast::ItemKind::Const(ref ty, ref expr) => { - self.head(&visibility_qualified(&item.vis, "const"))?; + self.head(visibility_qualified(&item.vis, "const"))?; self.print_ident(item.ident)?; self.word_space(":")?; self.print_type(ty)?; @@ -1264,7 +1266,7 @@ impl<'a> State<'a> { self.print_block_with_attrs(body, &item.attrs)?; } ast::ItemKind::Mod(ref _mod) => { - self.head(&visibility_qualified(&item.vis, "mod"))?; + self.head(visibility_qualified(&item.vis, "mod"))?; self.print_ident(item.ident)?; if _mod.inline || self.is_expanded { @@ -1281,18 +1283,18 @@ impl<'a> State<'a> { } ast::ItemKind::ForeignMod(ref nmod) => { self.head("extern")?; - self.word_nbsp(&nmod.abi.to_string())?; + self.word_nbsp(nmod.abi.to_string())?; self.bopen()?; self.print_foreign_mod(nmod, &item.attrs)?; self.bclose(item.span)?; } ast::ItemKind::GlobalAsm(ref ga) => { - self.head(&visibility_qualified(&item.vis, "global_asm!"))?; - self.s.word(&ga.asm.as_str())?; + self.head(visibility_qualified(&item.vis, "global_asm!"))?; + self.s.word(ga.asm.as_str().get())?; self.end()?; } ast::ItemKind::Ty(ref ty, ref generics) => { - self.head(&visibility_qualified(&item.vis, "type"))?; + self.head(visibility_qualified(&item.vis, "type"))?; self.print_ident(item.ident)?; self.print_generic_params(&generics.params)?; self.end()?; // end the inner ibox @@ -1305,7 +1307,7 @@ impl<'a> State<'a> { self.end()?; // end the outer ibox } ast::ItemKind::Existential(ref bounds, ref generics) => { - self.head(&visibility_qualified(&item.vis, "existential type"))?; + self.head(visibility_qualified(&item.vis, "existential type"))?; self.print_ident(item.ident)?; self.print_generic_params(&generics.params)?; self.end()?; // end the inner ibox @@ -1326,11 +1328,11 @@ impl<'a> State<'a> { )?; } ast::ItemKind::Struct(ref struct_def, ref generics) => { - self.head(&visibility_qualified(&item.vis, "struct"))?; + self.head(visibility_qualified(&item.vis, "struct"))?; self.print_struct(struct_def, generics, item.ident, item.span, true)?; } ast::ItemKind::Union(ref struct_def, ref generics) => { - self.head(&visibility_qualified(&item.vis, "union"))?; + self.head(visibility_qualified(&item.vis, "union"))?; self.print_struct(struct_def, generics, item.ident, item.span, true)?; } ast::ItemKind::Impl(unsafety, @@ -1479,7 +1481,7 @@ impl<'a> State<'a> { generics: &ast::Generics, ident: ast::Ident, span: syntax_pos::Span, visibility: &ast::Visibility) -> io::Result<()> { - self.head(&visibility_qualified(visibility, "enum"))?; + self.head(visibility_qualified(visibility, "enum"))?; self.print_ident(ident)?; self.print_generic_params(&generics.params)?; self.print_where_clause(&generics.where_clause)?; @@ -1514,9 +1516,9 @@ impl<'a> State<'a> { ast::VisibilityKind::Restricted { ref path, .. } => { let path = to_string(|s| s.print_path(path, false, 0)); if path == "self" || path == "super" { - self.word_nbsp(&format!("pub({})", path)) + self.word_nbsp(format!("pub({})", path)) } else { - self.word_nbsp(&format!("pub(in {})", path)) + self.word_nbsp(format!("pub(in {})", path)) } } ast::VisibilityKind::Inherited => Ok(()) @@ -2415,19 +2417,19 @@ impl<'a> State<'a> { pub fn print_ident(&mut self, ident: ast::Ident) -> io::Result<()> { if ident.is_raw_guess() { - self.s.word(&format!("r#{}", ident))?; + self.s.word(format!("r#{}", ident))?; } else { - self.s.word(&ident.as_str())?; + self.s.word(ident.as_str().get())?; } self.ann.post(self, AnnNode::Ident(&ident)) } pub fn print_usize(&mut self, i: usize) -> io::Result<()> { - self.s.word(&i.to_string()) + self.s.word(i.to_string()) } pub fn print_name(&mut self, name: ast::Name) -> io::Result<()> { - self.s.word(&name.as_str())?; + self.s.word(name.as_str().get())?; self.ann.post(self, AnnNode::Name(&name)) } @@ -2851,10 +2853,8 @@ impl<'a> State<'a> { } } - pub fn print_type_bounds(&mut self, - prefix: &str, - bounds: &[ast::GenericBound]) - -> io::Result<()> { + pub fn print_type_bounds(&mut self, prefix: &'static str, bounds: &[ast::GenericBound]) + -> io::Result<()> { if !bounds.is_empty() { self.s.word(prefix)?; let mut first = true; @@ -3146,7 +3146,7 @@ impl<'a> State<'a> { Some(Abi::Rust) => Ok(()), Some(abi) => { self.word_nbsp("extern")?; - self.word_nbsp(&abi.to_string()) + self.word_nbsp(abi.to_string()) } None => Ok(()) } @@ -3157,7 +3157,7 @@ impl<'a> State<'a> { match opt_abi { Some(abi) => { self.word_nbsp("extern")?; - self.word_nbsp(&abi.to_string()) + self.word_nbsp(abi.to_string()) } None => Ok(()) } @@ -3166,7 +3166,7 @@ impl<'a> State<'a> { pub fn print_fn_header_info(&mut self, header: ast::FnHeader, vis: &ast::Visibility) -> io::Result<()> { - self.s.word(&visibility_qualified(vis, ""))?; + self.s.word(visibility_qualified(vis, ""))?; match header.constness.node { ast::Constness::NotConst => {} @@ -3178,7 +3178,7 @@ impl<'a> State<'a> { if header.abi != Abi::Rust { self.word_nbsp("extern")?; - self.word_nbsp(&header.abi.to_string())?; + self.word_nbsp(header.abi.to_string())?; } self.s.word("fn") diff --git a/src/libsyntax_pos/symbol.rs b/src/libsyntax_pos/symbol.rs index 361353c82e25c..b891cdefc2a41 100644 --- a/src/libsyntax_pos/symbol.rs +++ b/src/libsyntax_pos/symbol.rs @@ -493,6 +493,10 @@ impl LocalInternedString { symbol: Symbol::intern(self.string) } } + + pub fn get(&self) -> &'static str { + self.string + } } impl ::std::convert::AsRef for LocalInternedString From 64cd645d14407d312c7ed3a10efd9b3a99271884 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 29 Nov 2018 13:58:58 +1100 Subject: [PATCH 10/36] Split up `pretty_print` and `print`. `pretty_print` takes a `Token` and `match`es on it. But the particular `Token` kind is known at each call site, so this commit splits it into five functions: `pretty_print_eof`, `pretty_print_begin`, etc. This commit also does likewise with `print`, though there is one callsite for `print` where the `Token` kind isn't known, so a generic `print` has to stay (but it now just calls out to the various `print_*` functions). --- src/libsyntax/print/pp.rs | 302 +++++++++++++++++++------------------- 1 file changed, 152 insertions(+), 150 deletions(-) diff --git a/src/libsyntax/print/pp.rs b/src/libsyntax/print/pp.rs index b9addcaeb8edf..aaed56da29d5f 100644 --- a/src/libsyntax/print/pp.rs +++ b/src/libsyntax/print/pp.rs @@ -140,9 +140,9 @@ //! calculation, SCAN will write "infinity" to the size and let PRINT consume //! it. //! -//! In this implementation (following the paper, again) the SCAN process is -//! the method called `Printer::pretty_print`, and the 'PRINT' process is the method -//! called `Printer::print`. +//! In this implementation (following the paper, again) the SCAN process is the +//! methods called `Printer::pretty_print_*`, and the 'PRINT' process is the +//! method called `Printer::print`. use std::collections::VecDeque; use std::fmt; @@ -319,78 +319,77 @@ impl<'a> Printer<'a> { self.buf[self.right].token = t; } - pub fn pretty_print(&mut self, token: Token) -> io::Result<()> { - debug!("pp Vec<{},{}>", self.left, self.right); - match token { - Token::Eof => { - if !self.scan_stack.is_empty() { - self.check_stack(0); - self.advance_left()?; - } - self.indent(0); - Ok(()) - } - Token::Begin(b) => { - if self.scan_stack.is_empty() { - self.left_total = 1; - self.right_total = 1; - self.left = 0; - self.right = 0; - } else { - self.advance_right(); - } - debug!("pp Begin({})/buffer Vec<{},{}>", - b.offset, self.left, self.right); - self.buf[self.right] = BufEntry { token: token, size: -self.right_total }; - let right = self.right; - self.scan_push(right); - Ok(()) - } - Token::End => { - if self.scan_stack.is_empty() { - debug!("pp End/print Vec<{},{}>", self.left, self.right); - self.print(token, 0) - } else { - debug!("pp End/buffer Vec<{},{}>", self.left, self.right); - self.advance_right(); - self.buf[self.right] = BufEntry { token: token, size: -1 }; - let right = self.right; - self.scan_push(right); - Ok(()) - } - } - Token::Break(b) => { - if self.scan_stack.is_empty() { - self.left_total = 1; - self.right_total = 1; - self.left = 0; - self.right = 0; - } else { - self.advance_right(); - } - debug!("pp Break({})/buffer Vec<{},{}>", - b.offset, self.left, self.right); - self.check_stack(0); - let right = self.right; - self.scan_push(right); - self.buf[self.right] = BufEntry { token: token, size: -self.right_total }; - self.right_total += b.blank_space; - Ok(()) - } - Token::String(s, len) => { - if self.scan_stack.is_empty() { - debug!("pp String('{}')/print Vec<{},{}>", - s, self.left, self.right); - self.print(Token::String(s, len), len) - } else { - debug!("pp String('{}')/buffer Vec<{},{}>", - s, self.left, self.right); - self.advance_right(); - self.buf[self.right] = BufEntry { token: Token::String(s, len), size: len }; - self.right_total += len; - self.check_stream() - } - } + fn pretty_print_eof(&mut self) -> io::Result<()> { + if !self.scan_stack.is_empty() { + self.check_stack(0); + self.advance_left()?; + } + self.indent(0); + Ok(()) + } + + fn pretty_print_begin(&mut self, b: BeginToken) -> io::Result<()> { + if self.scan_stack.is_empty() { + self.left_total = 1; + self.right_total = 1; + self.left = 0; + self.right = 0; + } else { + self.advance_right(); + } + debug!("pp Begin({})/buffer Vec<{},{}>", + b.offset, self.left, self.right); + self.buf[self.right] = BufEntry { token: Token::Begin(b), size: -self.right_total }; + let right = self.right; + self.scan_push(right); + Ok(()) + } + + fn pretty_print_end(&mut self) -> io::Result<()> { + if self.scan_stack.is_empty() { + debug!("pp End/print Vec<{},{}>", self.left, self.right); + self.print_end() + } else { + debug!("pp End/buffer Vec<{},{}>", self.left, self.right); + self.advance_right(); + self.buf[self.right] = BufEntry { token: Token::End, size: -1 }; + let right = self.right; + self.scan_push(right); + Ok(()) + } + } + + fn pretty_print_break(&mut self, b: BreakToken) -> io::Result<()> { + if self.scan_stack.is_empty() { + self.left_total = 1; + self.right_total = 1; + self.left = 0; + self.right = 0; + } else { + self.advance_right(); + } + debug!("pp Break({})/buffer Vec<{},{}>", + b.offset, self.left, self.right); + self.check_stack(0); + let right = self.right; + self.scan_push(right); + self.buf[self.right] = BufEntry { token: Token::Break(b), size: -self.right_total }; + self.right_total += b.blank_space; + Ok(()) + } + + fn pretty_print_string(&mut self, s: Cow<'static, str>, len: isize) -> io::Result<()> { + if self.scan_stack.is_empty() { + debug!("pp String('{}')/print Vec<{},{}>", + s, self.left, self.right); + self.print_string(s, len) + } else { + debug!("pp String('{}')/buffer Vec<{},{}>", + s, self.left, self.right); + self.advance_right(); + self.buf[self.right] = BufEntry { token: Token::String(s, len), size: len }; + self.right_total += len; + self.check_stream() } } @@ -526,7 +525,70 @@ impl<'a> Printer<'a> { } } - pub fn print_str(&mut self, s: &str) -> io::Result<()> { + pub fn print_begin(&mut self, b: BeginToken, l: isize) -> io::Result<()> { + if l > self.space { + let col = self.margin - self.space + b.offset; + debug!("print Begin -> push broken block at col {}", col); + self.print_stack.push(PrintStackElem { + offset: col, + pbreak: PrintStackBreak::Broken(b.breaks) + }); + } else { + debug!("print Begin -> push fitting block"); + self.print_stack.push(PrintStackElem { + offset: 0, + pbreak: PrintStackBreak::Fits + }); + } + Ok(()) + } + + pub fn print_end(&mut self) -> io::Result<()> { + debug!("print End -> pop End"); + let print_stack = &mut self.print_stack; + assert!(!print_stack.is_empty()); + print_stack.pop().unwrap(); + Ok(()) + } + + pub fn print_break(&mut self, b: BreakToken, l: isize) -> io::Result<()> { + let top = self.get_top(); + match top.pbreak { + PrintStackBreak::Fits => { + debug!("print Break({}) in fitting block", b.blank_space); + self.space -= b.blank_space; + self.indent(b.blank_space); + Ok(()) + } + PrintStackBreak::Broken(Breaks::Consistent) => { + debug!("print Break({}+{}) in consistent block", + top.offset, b.offset); + let ret = self.print_newline(top.offset + b.offset); + self.space = self.margin - (top.offset + b.offset); + ret + } + PrintStackBreak::Broken(Breaks::Inconsistent) => { + if l > self.space { + debug!("print Break({}+{}) w/ newline in inconsistent", + top.offset, b.offset); + let ret = self.print_newline(top.offset + b.offset); + self.space = self.margin - (top.offset + b.offset); + ret + } else { + debug!("print Break({}) w/o newline in inconsistent", + b.blank_space); + self.indent(b.blank_space); + self.space -= b.blank_space; + Ok(()) + } + } + } + } + + pub fn print_string(&mut self, s: Cow<'static, str>, len: isize) -> io::Result<()> { + debug!("print String({})", s); + // assert!(len <= space); + self.space -= len; while self.pending_indentation > 0 { write!(self.out, " ")?; self.pending_indentation -= 1; @@ -542,74 +604,14 @@ impl<'a> Printer<'a> { self.right, 6)); match token { - Token::Begin(b) => { - if l > self.space { - let col = self.margin - self.space + b.offset; - debug!("print Begin -> push broken block at col {}", col); - self.print_stack.push(PrintStackElem { - offset: col, - pbreak: PrintStackBreak::Broken(b.breaks) - }); - } else { - debug!("print Begin -> push fitting block"); - self.print_stack.push(PrintStackElem { - offset: 0, - pbreak: PrintStackBreak::Fits - }); - } - Ok(()) - } - Token::End => { - debug!("print End -> pop End"); - let print_stack = &mut self.print_stack; - assert!(!print_stack.is_empty()); - print_stack.pop().unwrap(); - Ok(()) - } - Token::Break(b) => { - let top = self.get_top(); - match top.pbreak { - PrintStackBreak::Fits => { - debug!("print Break({}) in fitting block", b.blank_space); - self.space -= b.blank_space; - self.indent(b.blank_space); - Ok(()) - } - PrintStackBreak::Broken(Breaks::Consistent) => { - debug!("print Break({}+{}) in consistent block", - top.offset, b.offset); - let ret = self.print_newline(top.offset + b.offset); - self.space = self.margin - (top.offset + b.offset); - ret - } - PrintStackBreak::Broken(Breaks::Inconsistent) => { - if l > self.space { - debug!("print Break({}+{}) w/ newline in inconsistent", - top.offset, b.offset); - let ret = self.print_newline(top.offset + b.offset); - self.space = self.margin - (top.offset + b.offset); - ret - } else { - debug!("print Break({}) w/o newline in inconsistent", - b.blank_space); - self.indent(b.blank_space); - self.space -= b.blank_space; - Ok(()) - } - } - } - } - Token::String(ref s, len) => { - debug!("print String({})", s); - assert_eq!(l, len); - // assert!(l <= space); - self.space -= len; - self.print_str(s) - } - Token::Eof => { - // Eof should never get here. - panic!(); + Token::Begin(b) => self.print_begin(b, l), + Token::End => self.print_end(), + Token::Break(b) => self.print_break(b, l), + Token::String(s, len) => { + assert_eq!(len, l); + self.print_string(s, len) } + Token::Eof => panic!(), // Eof should never get here. } } @@ -617,10 +619,10 @@ impl<'a> Printer<'a> { /// "raw box" pub fn rbox(&mut self, indent: usize, b: Breaks) -> io::Result<()> { - self.pretty_print(Token::Begin(BeginToken { + self.pretty_print_begin(BeginToken { offset: indent as isize, breaks: b - })) + }) } /// Inconsistent breaking box @@ -634,24 +636,24 @@ impl<'a> Printer<'a> { } pub fn break_offset(&mut self, n: usize, off: isize) -> io::Result<()> { - self.pretty_print(Token::Break(BreakToken { + self.pretty_print_break(BreakToken { offset: off, blank_space: n as isize - })) + }) } pub fn end(&mut self) -> io::Result<()> { - self.pretty_print(Token::End) + self.pretty_print_end() } pub fn eof(&mut self) -> io::Result<()> { - self.pretty_print(Token::Eof) + self.pretty_print_eof() } pub fn word>>(&mut self, wrd: S) -> io::Result<()> { let s = wrd.into(); let len = s.len() as isize; - self.pretty_print(Token::String(s, len)) + self.pretty_print_string(s, len) } fn spaces(&mut self, n: usize) -> io::Result<()> { From 71f643e5bf98427dc91886365ded0936b1f68b60 Mon Sep 17 00:00:00 2001 From: yui-knk Date: Thu, 29 Nov 2018 08:44:44 +0900 Subject: [PATCH 11/36] Remove not used option `mir_stats` has not been used since 2b32cb90c72d90c722d56324ca0ea9f748ebf4e1. --- src/librustc/session/config.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs index 3fd22793a08a0..480d4a8e48f0e 100644 --- a/src/librustc/session/config.rs +++ b/src/librustc/session/config.rs @@ -1286,8 +1286,6 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, "print some performance-related statistics"), hir_stats: bool = (false, parse_bool, [UNTRACKED], "print some statistics about AST and HIR"), - mir_stats: bool = (false, parse_bool, [UNTRACKED], - "print some statistics about MIR"), always_encode_mir: bool = (false, parse_bool, [TRACKED], "encode MIR of all functions into the crate metadata"), osx_rpath_install_name: bool = (false, parse_bool, [TRACKED], From 2a91bbac5d04273b217170f4ad5eebe02ffcb1af Mon Sep 17 00:00:00 2001 From: Corey Farwell Date: Thu, 29 Nov 2018 08:09:28 -0500 Subject: [PATCH 12/36] Rename conversion util; remove duplicate util in librustc_codegen_llvm. --- src/librustc_codegen_llvm/back/write.rs | 10 +++++----- src/librustc_codegen_llvm/debuginfo/metadata.rs | 4 ++-- src/librustc_codegen_llvm/llvm/archive_ro.rs | 16 ++-------------- src/librustc_codegen_llvm/metadata.rs | 4 ++-- src/librustc_fs_util/lib.rs | 6 +++--- 5 files changed, 14 insertions(+), 26 deletions(-) diff --git a/src/librustc_codegen_llvm/back/write.rs b/src/librustc_codegen_llvm/back/write.rs index 7945d381760a1..4eb920324bd00 100644 --- a/src/librustc_codegen_llvm/back/write.rs +++ b/src/librustc_codegen_llvm/back/write.rs @@ -23,7 +23,7 @@ use llvm_util; use ModuleLlvm; use rustc_codegen_ssa::{ModuleCodegen, CompiledModule}; use rustc::util::common::time_ext; -use rustc_fs_util::{path2cstr, link_or_copy}; +use rustc_fs_util::{path_to_c_string, link_or_copy}; use rustc_data_structures::small_c_str::SmallCStr; use errors::{self, Handler, FatalError}; use type_::Type; @@ -80,7 +80,7 @@ pub fn write_output_file( output: &Path, file_type: llvm::FileType) -> Result<(), FatalError> { unsafe { - let output_c = path2cstr(output); + let output_c = path_to_c_string(output); let result = llvm::LLVMRustWriteOutputFile(target, pm, m, output_c.as_ptr(), file_type); if result.into_result().is_err() { let msg = format!("could not write output to {}", output.display()); @@ -211,7 +211,7 @@ pub(crate) fn save_temp_bitcode( let ext = format!("{}.bc", name); let cgu = Some(&module.name[..]); let path = cgcx.output_filenames.temp_path_ext(&ext, cgu); - let cstr = path2cstr(&path); + let cstr = path_to_c_string(&path); let llmod = module.module_llvm.llmod(); llvm::LLVMWriteBitcodeToFile(llmod, cstr.as_ptr()); } @@ -324,7 +324,7 @@ pub(crate) unsafe fn optimize(cgcx: &CodegenContext, if config.emit_no_opt_bc { let out = cgcx.output_filenames.temp_path_ext("no-opt.bc", module_name); - let out = path2cstr(&out); + let out = path_to_c_string(&out); llvm::LLVMWriteBitcodeToFile(llmod, out.as_ptr()); } @@ -530,7 +530,7 @@ pub(crate) unsafe fn codegen(cgcx: &CodegenContext, || -> Result<(), FatalError> { if config.emit_ir { let out = cgcx.output_filenames.temp_path(OutputType::LlvmAssembly, module_name); - let out = path2cstr(&out); + let out = path_to_c_string(&out); extern "C" fn demangle_callback(input_ptr: *const c_char, input_len: size_t, diff --git a/src/librustc_codegen_llvm/debuginfo/metadata.rs b/src/librustc_codegen_llvm/debuginfo/metadata.rs index 81f2769800d2f..47e92fbe87d31 100644 --- a/src/librustc_codegen_llvm/debuginfo/metadata.rs +++ b/src/librustc_codegen_llvm/debuginfo/metadata.rs @@ -39,7 +39,7 @@ use rustc::ty::layout::{self, Align, HasDataLayout, Integer, IntegerExt, LayoutO PrimitiveExt, Size, TyLayout}; use rustc::session::config; use rustc::util::nodemap::FxHashMap; -use rustc_fs_util::path2cstr; +use rustc_fs_util::path_to_c_string; use rustc_data_structures::small_c_str::SmallCStr; use libc::{c_uint, c_longlong}; @@ -892,7 +892,7 @@ pub fn compile_unit_metadata(tcx: TyCtxt, }; fn path_to_mdstring(llcx: &'ll llvm::Context, path: &Path) -> &'ll Value { - let path_str = path2cstr(path); + let path_str = path_to_c_string(path); unsafe { llvm::LLVMMDStringInContext(llcx, path_str.as_ptr(), diff --git a/src/librustc_codegen_llvm/llvm/archive_ro.rs b/src/librustc_codegen_llvm/llvm/archive_ro.rs index 2a77f256e3a0c..d5c73fecf814a 100644 --- a/src/librustc_codegen_llvm/llvm/archive_ro.rs +++ b/src/librustc_codegen_llvm/llvm/archive_ro.rs @@ -10,10 +10,10 @@ //! A wrapper around LLVM's archive (.a) code -use std::ffi::CString; use std::path::Path; use std::slice; use std::str; +use rustc_fs_util::path_to_c_string; pub struct ArchiveRO { pub raw: &'static mut super::Archive, @@ -38,24 +38,12 @@ impl ArchiveRO { /// raised. pub fn open(dst: &Path) -> Result { return unsafe { - let s = path2cstr(dst); + let s = path_to_c_string(dst); let ar = super::LLVMRustOpenArchive(s.as_ptr()).ok_or_else(|| { super::last_error().unwrap_or_else(|| "failed to open archive".to_owned()) })?; Ok(ArchiveRO { raw: ar }) }; - - #[cfg(unix)] - fn path2cstr(p: &Path) -> CString { - use std::os::unix::prelude::*; - use std::ffi::OsStr; - let p: &OsStr = p.as_ref(); - CString::new(p.as_bytes()).unwrap() - } - #[cfg(windows)] - fn path2cstr(p: &Path) -> CString { - CString::new(p.to_str().unwrap()).unwrap() - } } pub fn iter(&self) -> Iter { diff --git a/src/librustc_codegen_llvm/metadata.rs b/src/librustc_codegen_llvm/metadata.rs index 7752465d885bb..5605f64c2e72c 100644 --- a/src/librustc_codegen_llvm/metadata.rs +++ b/src/librustc_codegen_llvm/metadata.rs @@ -18,7 +18,7 @@ use rustc_data_structures::owning_ref::OwningRef; use std::path::Path; use std::ptr; use std::slice; -use rustc_fs_util::path2cstr; +use rustc_fs_util::path_to_c_string; pub use rustc_data_structures::sync::MetadataRef; @@ -57,7 +57,7 @@ impl MetadataLoader for LlvmMetadataLoader { filename: &Path) -> Result { unsafe { - let buf = path2cstr(filename); + let buf = path_to_c_string(filename); let mb = llvm::LLVMRustCreateMemoryBufferWithContentsOfFile(buf.as_ptr()) .ok_or_else(|| format!("error reading library: '{}'", filename.display()))?; let of = ObjectFile::new(mb) diff --git a/src/librustc_fs_util/lib.rs b/src/librustc_fs_util/lib.rs index ffe420b109d3e..1b0ff4f861c7b 100644 --- a/src/librustc_fs_util/lib.rs +++ b/src/librustc_fs_util/lib.rs @@ -116,13 +116,13 @@ pub fn rename_or_copy_remove, Q: AsRef>(p: P, } #[cfg(unix)] -pub fn path2cstr(p: &Path) -> CString { - use std::os::unix::prelude::*; +pub fn path_to_c_string(p: &Path) -> CString { + use std::os::unix::ffi::OsStrExt; use std::ffi::OsStr; let p: &OsStr = p.as_ref(); CString::new(p.as_bytes()).unwrap() } #[cfg(windows)] -pub fn path2cstr(p: &Path) -> CString { +pub fn path_to_c_string(p: &Path) -> CString { CString::new(p.to_str().unwrap()).unwrap() } From e955dbca99ee13a9d4a459b238cf5e0d9a973ae4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20K=C3=A5re=20Alsaker?= Date: Thu, 17 May 2018 05:19:08 +0200 Subject: [PATCH 13/36] Use raw_entry for more efficient interning --- src/librustc/ty/context.rs | 185 +++++++++-------------- src/librustc_data_structures/interner.rs | 68 +++++++++ src/librustc_data_structures/lib.rs | 2 + 3 files changed, 144 insertions(+), 111 deletions(-) create mode 100644 src/librustc_data_structures/interner.rs diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs index 5780fbe55ad1f..4034abb468f59 100644 --- a/src/librustc/ty/context.rs +++ b/src/librustc/ty/context.rs @@ -53,6 +53,7 @@ use ty::CanonicalTy; use ty::CanonicalPolyFnSig; use util::nodemap::{DefIdMap, DefIdSet, ItemLocalMap}; use util::nodemap::{FxHashMap, FxHashSet}; +use rustc_data_structures::interner::HashInterner; use smallvec::SmallVec; use rustc_data_structures::stable_hasher::{HashStable, hash_stable_hashmap, StableHasher, StableHasherResult, @@ -113,7 +114,7 @@ pub struct GlobalArenas<'tcx> { const_allocs: TypedArena, } -type InternedSet<'tcx, T> = Lock>>; +type InternedSet<'tcx, T> = Lock, ()>>; pub struct CtxtInterners<'tcx> { /// The arena that types, regions, etc are allocated from @@ -166,51 +167,39 @@ impl<'gcx: 'tcx, 'tcx> CtxtInterners<'tcx> { // determine that all contents are in the global tcx. // See comments on Lift for why we can't use that. if flags.flags.intersects(ty::TypeFlags::KEEP_IN_LOCAL_TCX) { - let mut interner = local.type_.borrow_mut(); - if let Some(&Interned(ty)) = interner.get(&st) { - return ty; - } - - let ty_struct = TyS { - sty: st, - flags: flags.flags, - outer_exclusive_binder: flags.outer_exclusive_binder, - }; + local.type_.borrow_mut().intern(st, |st| { + let ty_struct = TyS { + sty: st, + flags: flags.flags, + outer_exclusive_binder: flags.outer_exclusive_binder, + }; - // Make sure we don't end up with inference - // types/regions in the global interner - if local as *const _ as usize == global as *const _ as usize { - bug!("Attempted to intern `{:?}` which contains \ - inference types/regions in the global type context", - &ty_struct); - } + // Make sure we don't end up with inference + // types/regions in the global interner + if local as *const _ as usize == global as *const _ as usize { + bug!("Attempted to intern `{:?}` which contains \ + inference types/regions in the global type context", + &ty_struct); + } - // Don't be &mut TyS. - let ty: Ty<'tcx> = local.arena.alloc(ty_struct); - interner.insert(Interned(ty)); - ty + Interned(local.arena.alloc(ty_struct)) + }).0 } else { - let mut interner = global.type_.borrow_mut(); - if let Some(&Interned(ty)) = interner.get(&st) { - return ty; - } - - let ty_struct = TyS { - sty: st, - flags: flags.flags, - outer_exclusive_binder: flags.outer_exclusive_binder, - }; + global.type_.borrow_mut().intern(st, |st| { + let ty_struct = TyS { + sty: st, + flags: flags.flags, + outer_exclusive_binder: flags.outer_exclusive_binder, + }; - // This is safe because all the types the ty_struct can point to - // already is in the global arena - let ty_struct: TyS<'gcx> = unsafe { - mem::transmute(ty_struct) - }; + // This is safe because all the types the ty_struct can point to + // already is in the global arena + let ty_struct: TyS<'gcx> = unsafe { + mem::transmute(ty_struct) + }; - // Don't be &mut TyS. - let ty: Ty<'gcx> = global.arena.alloc(ty_struct); - interner.insert(Interned(ty)); - ty + Interned(global.arena.alloc(ty_struct)) + }).0 } } } @@ -825,12 +814,9 @@ impl<'tcx> CommonTypes<'tcx> { fn new(interners: &CtxtInterners<'tcx>) -> CommonTypes<'tcx> { let mk = |sty| CtxtInterners::intern_ty(interners, interners, sty); let mk_region = |r| { - if let Some(r) = interners.region.borrow().get(&r) { - return r.0; - } - let r = interners.arena.alloc(r); - interners.region.borrow_mut().insert(Interned(r)); - &*r + interners.region.borrow_mut().intern(r, |r| { + Interned(interners.arena.alloc(r)) + }).0 }; CommonTypes { bool: mk(Bool), @@ -950,14 +936,14 @@ pub struct GlobalCtxt<'tcx> { /// Data layout specification for the current target. pub data_layout: TargetDataLayout, - stability_interner: Lock>, + stability_interner: Lock>, /// Stores the value of constants (and deduplicates the actual memory) - allocation_interner: Lock>, + allocation_interner: Lock>, pub alloc_map: Lock>, - layout_interner: Lock>, + layout_interner: Lock>, /// A general purpose channel to throw data out the back towards LLVM worker /// threads. @@ -1040,16 +1026,9 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { self, alloc: Allocation, ) -> &'gcx Allocation { - let allocs = &mut self.allocation_interner.borrow_mut(); - if let Some(alloc) = allocs.get(&alloc) { - return alloc; - } - - let interned = self.global_arenas.const_allocs.alloc(alloc); - if let Some(prev) = allocs.replace(interned) { // insert into interner - bug!("Tried to overwrite interned Allocation: {:#?}", prev) - } - interned + self.allocation_interner.borrow_mut().intern(alloc, |alloc| { + self.global_arenas.const_allocs.alloc(alloc) + }) } /// Allocates a byte or string literal for `mir::interpret`, read-only @@ -1061,29 +1040,15 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { } pub fn intern_stability(self, stab: attr::Stability) -> &'gcx attr::Stability { - let mut stability_interner = self.stability_interner.borrow_mut(); - if let Some(st) = stability_interner.get(&stab) { - return st; - } - - let interned = self.global_interners.arena.alloc(stab); - if let Some(prev) = stability_interner.replace(interned) { - bug!("Tried to overwrite interned Stability: {:?}", prev) - } - interned + self.stability_interner.borrow_mut().intern(stab, |stab| { + self.global_interners.arena.alloc(stab) + }) } pub fn intern_layout(self, layout: LayoutDetails) -> &'gcx LayoutDetails { - let mut layout_interner = self.layout_interner.borrow_mut(); - if let Some(layout) = layout_interner.get(&layout) { - return layout; - } - - let interned = self.global_arenas.layout.alloc(layout); - if let Some(prev) = layout_interner.replace(interned) { - bug!("Tried to overwrite interned Layout: {:?}", prev) - } - interned + self.layout_interner.borrow_mut().intern(layout, |layout| { + self.global_arenas.layout.alloc(layout) + }) } /// Returns a range of the start/end indices specified with the @@ -2193,7 +2158,7 @@ macro_rules! sty_debug_print { }; $(let mut $variant = total;)* - for &Interned(t) in tcx.interners.type_.borrow().iter() { + for &Interned(t) in tcx.interners.type_.borrow().keys() { let variant = match t.sty { ty::Bool | ty::Char | ty::Int(..) | ty::Uint(..) | ty::Float(..) | ty::Str | ty::Never => continue, @@ -2252,6 +2217,13 @@ impl<'a, 'tcx> TyCtxt<'a, 'tcx, 'tcx> { /// An entry in an interner. struct Interned<'tcx, T: 'tcx+?Sized>(&'tcx T); +impl<'tcx, T: 'tcx+?Sized> Clone for Interned<'tcx, T> { + fn clone(&self) -> Self { + Interned(self.0) + } +} +impl<'tcx, T: 'tcx+?Sized> Copy for Interned<'tcx, T> {} + // NB: An Interned compares and hashes as a sty. impl<'tcx> PartialEq for Interned<'tcx, TyS<'tcx>> { fn eq(&self, other: &Interned<'tcx, TyS<'tcx>>) -> bool { @@ -2372,37 +2344,28 @@ macro_rules! intern_method { // determine that all contents are in the global tcx. // See comments on Lift for why we can't use that. if ($keep_in_local_tcx)(&v) { - let mut interner = self.interners.$name.borrow_mut(); - if let Some(&Interned(v)) = interner.get(key) { - return v; - } - - // Make sure we don't end up with inference - // types/regions in the global tcx. - if self.is_global() { - bug!("Attempted to intern `{:?}` which contains \ - inference types/regions in the global type context", - v); - } - - let i = $alloc_method(&self.interners.arena, v); - interner.insert(Interned(i)); - i + self.interners.$name.borrow_mut().intern_ref(key, || { + // Make sure we don't end up with inference + // types/regions in the global tcx. + if self.is_global() { + bug!("Attempted to intern `{:?}` which contains \ + inference types/regions in the global type context", + v); + } + + Interned($alloc_method(&self.interners.arena, v)) + }).0 } else { - let mut interner = self.global_interners.$name.borrow_mut(); - if let Some(&Interned(v)) = interner.get(key) { - return v; - } - - // This transmutes $alloc<'tcx> to $alloc<'gcx> - let v = unsafe { - mem::transmute(v) - }; - let i: &$lt_tcx $ty = $alloc_method(&self.global_interners.arena, v); - // Cast to 'gcx - let i = unsafe { mem::transmute(i) }; - interner.insert(Interned(i)); - i + self.global_interners.$name.borrow_mut().intern_ref(key, || { + // This transmutes $alloc<'tcx> to $alloc<'gcx> + let v = unsafe { + mem::transmute(v) + }; + let i: &$lt_tcx $ty = $alloc_method(&self.global_interners.arena, v); + // Cast to 'gcx + let i = unsafe { mem::transmute(i) }; + Interned(i) + }).0 } } } diff --git a/src/librustc_data_structures/interner.rs b/src/librustc_data_structures/interner.rs new file mode 100644 index 0000000000000..29e5aefee7f0b --- /dev/null +++ b/src/librustc_data_structures/interner.rs @@ -0,0 +1,68 @@ +// 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. + +use std::hash::Hash; +use std::hash::BuildHasher; +use std::hash::Hasher; +use std::collections::HashMap; +use std::collections::hash_map::RawEntryMut; +use std::borrow::Borrow; + +pub trait HashInterner { + fn intern_ref K>(&mut self, value: &Q, make: F) -> K + where K: Borrow, + Q: Hash + Eq; + + fn intern K>(&mut self, value: Q, make: F) -> K + where K: Borrow, + Q: Hash + Eq; +} + +impl HashInterner for HashMap { + #[inline] + fn intern_ref K>(&mut self, value: &Q, make: F) -> K + where K: Borrow, + Q: Hash + Eq + { + let mut hasher = self.hasher().build_hasher(); + value.hash(&mut hasher); + let hash = hasher.finish(); + let entry = self.raw_entry_mut().from_key_hashed_nocheck(hash, value); + + match entry { + RawEntryMut::Occupied(e) => *e.key(), + RawEntryMut::Vacant(e) => { + let v = make(); + e.insert_hashed_nocheck(hash, v, ()); + v + } + } + } + + #[inline] + fn intern K>(&mut self, value: Q, make: F) -> K + where K: Borrow, + Q: Hash + Eq + { + let mut hasher = self.hasher().build_hasher(); + value.hash(&mut hasher); + let hash = hasher.finish(); + let entry = self.raw_entry_mut().from_key_hashed_nocheck(hash, &value); + + match entry { + RawEntryMut::Occupied(e) => *e.key(), + RawEntryMut::Vacant(e) => { + let v = make(value); + e.insert_hashed_nocheck(hash, v, ()); + v + } + } + } +} diff --git a/src/librustc_data_structures/lib.rs b/src/librustc_data_structures/lib.rs index 135abebdacb7a..96cb235a93362 100644 --- a/src/librustc_data_structures/lib.rs +++ b/src/librustc_data_structures/lib.rs @@ -29,6 +29,7 @@ #![feature(nll)] #![feature(allow_internal_unstable)] #![feature(vec_resize_with)] +#![feature(hash_raw_entry)] #![cfg_attr(unix, feature(libc))] #![cfg_attr(test, feature(test))] @@ -66,6 +67,7 @@ pub mod flock; pub mod fx; pub mod graph; pub mod indexed_vec; +pub mod interner; pub mod obligation_forest; pub mod owning_ref; pub mod ptr_key; From 0124341935787d1d6aa5eace209c9bb7968f35db Mon Sep 17 00:00:00 2001 From: David Wood Date: Thu, 29 Nov 2018 20:40:43 +0100 Subject: [PATCH 14/36] Only consider stem when extension is exe. This commit modifies linker flavor inference to only remove the extension to the linker when performing inference if that extension is a 'exe'. --- src/librustc_codegen_ssa/back/link.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/librustc_codegen_ssa/back/link.rs b/src/librustc_codegen_ssa/back/link.rs index b0575b841d5d5..24a70dc797771 100644 --- a/src/librustc_codegen_ssa/back/link.rs +++ b/src/librustc_codegen_ssa/back/link.rs @@ -161,7 +161,11 @@ pub fn linker_and_flavor(sess: &Session) -> (PathBuf, LinkerFlavor) { LinkerFlavor::Lld(_) => "lld", }), flavor)), (Some(linker), None) => { - let stem = linker.file_stem().and_then(|stem| stem.to_str()).unwrap_or_else(|| { + let stem = if linker.extension().and_then(|ext| ext.to_str()) == Some("exe") { + linker.file_stem().and_then(|stem| stem.to_str()) + } else { + linker.to_str() + }.unwrap_or_else(|| { sess.fatal("couldn't extract file stem from specified linker"); }).to_owned(); From 3b2cfc510b523f62b66c7106f04153f7625c8936 Mon Sep 17 00:00:00 2001 From: scalexm Date: Tue, 6 Nov 2018 20:55:50 +0100 Subject: [PATCH 15/36] Handle inference variables in `nll_relate` and use it for chalk --- src/librustc/infer/nll_relate/mod.rs | 327 +++++++++++++----- src/librustc/ty/relate.rs | 17 +- .../borrow_check/nll/type_check/relate_tys.rs | 17 +- src/librustc_traits/chalk_context/mod.rs | 51 ++- src/librustc_traits/chalk_context/unify.rs | 98 ++++++ 5 files changed, 393 insertions(+), 117 deletions(-) create mode 100644 src/librustc_traits/chalk_context/unify.rs diff --git a/src/librustc/infer/nll_relate/mod.rs b/src/librustc/infer/nll_relate/mod.rs index 9bdbf77fee0a9..ff79c38680f25 100644 --- a/src/librustc/infer/nll_relate/mod.rs +++ b/src/librustc/infer/nll_relate/mod.rs @@ -11,30 +11,41 @@ //! This code is kind of an alternate way of doing subtyping, //! supertyping, and type equating, distinct from the `combine.rs` //! code but very similar in its effect and design. Eventually the two -//! ought to be merged. This code is intended for use in NLL. +//! ought to be merged. This code is intended for use in NLL and chalk. //! //! Here are the key differences: //! -//! - This code generally assumes that there are no unbound type -//! inferences variables, because at NLL -//! time types are fully inferred up-to regions. -//! - Actually, to support user-given type annotations like -//! `Vec<_>`, we do have some measure of support for type -//! inference variables, but we impose some simplifying -//! assumptions on them that would not be suitable for the infer -//! code more generally. This could be fixed. +//! - This code may choose to bypass some checks (e.g. the occurs check) +//! in case we know that there are no unbound type inference variables. +//! This is the case for NLL, because at NLL time types are fully inferred +//! up-to regions. //! - This code uses "universes" to handle higher-ranked regions and //! not the leak-check. This is "more correct" than what rustc does //! and we are generally migrating in this direction, but NLL had to //! get there first. +//! +//! Also, this code assumes that there are no bound type vars at all, not even +//! free ones. This is ok because: +//! - we are not relating anything quantified over some type variable +//! - we will have instantiated all the bound type vars already (the one +//! thing we relate in chalk are basically domain goals and their +//! constituents) use crate::infer::InferCtxt; use crate::ty::fold::{TypeFoldable, TypeVisitor}; use crate::ty::relate::{self, Relate, RelateResult, TypeRelation}; use crate::ty::subst::Kind; use crate::ty::{self, Ty, TyCtxt}; +use crate::ty::error::TypeError; +use crate::traits::DomainGoal; use rustc_data_structures::fx::FxHashMap; +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub enum NormalizationStrategy { + Lazy, + Eager, +} + pub struct TypeRelating<'me, 'gcx: 'tcx, 'tcx: 'me, D> where D: TypeRelatingDelegate<'tcx>, @@ -75,6 +86,10 @@ pub trait TypeRelatingDelegate<'tcx> { /// delegate. fn push_outlives(&mut self, sup: ty::Region<'tcx>, sub: ty::Region<'tcx>); + /// Push a domain goal that will need to be proved for the two types to + /// be related. Used for lazy normalization. + fn push_domain_goal(&mut self, domain_goal: DomainGoal<'tcx>); + /// Creates a new universe index. Used when instantiating placeholders. fn create_next_universe(&mut self) -> ty::UniverseIndex; @@ -105,6 +120,13 @@ pub trait TypeRelatingDelegate<'tcx> { /// relate `Foo<'?0>` with `Foo<'a>` (and probably add an outlives /// relation stating that `'?0: 'a`). fn generalize_existential(&mut self, universe: ty::UniverseIndex) -> ty::Region<'tcx>; + + /// Define the normalization strategy to use, eager or lazy. + fn normalization() -> NormalizationStrategy; + + /// Enable some optimizations if we do not expect inference variables + /// in the RHS of the relation. + fn forbid_inference_vars() -> bool; } #[derive(Clone, Debug)] @@ -242,15 +264,79 @@ where self.delegate.push_outlives(sup, sub); } - /// When we encounter a canonical variable `var` in the output, - /// equate it with `kind`. If the variable has been previously - /// equated, then equate it again. - fn relate_var(&mut self, var_ty: Ty<'tcx>, value_ty: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> { - debug!("equate_var(var_ty={:?}, value_ty={:?})", var_ty, value_ty); + /// Relate a projection type and some value type lazily. This will always + /// succeed, but we are pushing an additional `ProjectionEq` goal depending + /// on the value type: + /// - if the value type is any type `T` which is not a projection, we push + /// `ProjectionEq(projection = T)`. + /// - if the value type is another projection `other_projection`, we create + /// a new inference variable `?U` and push the two goals + /// `ProjectionEq(projection = ?U)`, `ProjectionEq(other_projection = ?U)`. + fn relate_projection_ty( + &mut self, + projection_ty: ty::ProjectionTy<'tcx>, + value_ty: ty::Ty<'tcx> + ) -> Ty<'tcx> { + use crate::infer::type_variable::TypeVariableOrigin; + use crate::traits::WhereClause; + use syntax_pos::DUMMY_SP; + + match value_ty.sty { + ty::Projection(other_projection_ty) => { + let var = self.infcx.next_ty_var(TypeVariableOrigin::MiscVariable(DUMMY_SP)); + self.relate_projection_ty(projection_ty, var); + self.relate_projection_ty(other_projection_ty, var); + var + } + + _ => { + let projection = ty::ProjectionPredicate { + projection_ty, + ty: value_ty, + }; + self.delegate.push_domain_goal( + DomainGoal::Holds(WhereClause::ProjectionEq(projection)) + ); + value_ty + } + } + } + + /// Relate a type inference variable with a value type. + fn relate_ty_var( + &mut self, + vid: ty::TyVid, + value_ty: Ty<'tcx> + ) -> RelateResult<'tcx, Ty<'tcx>> { + debug!("relate_ty_var(vid={:?}, value_ty={:?})", vid, value_ty); + + match value_ty.sty { + ty::Infer(ty::TyVar(value_vid)) => { + // Two type variables: just equate them. + self.infcx.type_variables.borrow_mut().equate(vid, value_vid); + return Ok(value_ty); + } + + ty::Projection(projection_ty) + if D::normalization() == NormalizationStrategy::Lazy => + { + return Ok(self.relate_projection_ty(projection_ty, self.infcx.tcx.mk_var(vid))); + } + + _ => (), + } + + let generalized_ty = self.generalize_value(value_ty, vid)?; + debug!("relate_ty_var: generalized_ty = {:?}", generalized_ty); + + if D::forbid_inference_vars() { + // In NLL, we don't have type inference variables + // floating around, so we can do this rather imprecise + // variant of the occurs-check. + assert!(!generalized_ty.has_infer_types()); + } - let generalized_ty = self.generalize_value(value_ty); - self.infcx - .force_instantiate_unchecked(var_ty, generalized_ty); + self.infcx.type_variables.borrow_mut().instantiate(vid, generalized_ty); // The generalized values we extract from `canonical_var_values` have // been fully instantiated and hence the set of scopes we have @@ -264,22 +350,27 @@ where // Restore the old scopes now. self.a_scopes = old_a_scopes; - debug!("equate_var: complete, result = {:?}", result); + debug!("relate_ty_var: complete, result = {:?}", result); result } - fn generalize_value>(&mut self, value: T) -> T { - TypeGeneralizer { - tcx: self.infcx.tcx, + fn generalize_value>( + &mut self, + value: T, + for_vid: ty::TyVid + ) -> RelateResult<'tcx, T> { + let universe = self.infcx.probe_ty_var(for_vid).unwrap_err(); + + let mut generalizer = TypeGeneralizer { + infcx: self.infcx, delegate: &mut self.delegate, first_free_index: ty::INNERMOST, ambient_variance: self.ambient_variance, + for_vid_sub_root: self.infcx.type_variables.borrow_mut().sub_root_var(for_vid), + universe, + }; - // These always correspond to an `_` or `'_` written by - // user, and those are always in the root universe. - universe: ty::UniverseIndex::ROOT, - }.relate(&value, &value) - .unwrap() + generalizer.relate(&value, &value) } } @@ -327,11 +418,35 @@ where Ok(r) } - fn tys(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> { + fn tys(&mut self, a: Ty<'tcx>, mut b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> { let a = self.infcx.shallow_resolve(a); - match a.sty { - ty::Infer(ty::TyVar(_)) | ty::Infer(ty::IntVar(_)) | ty::Infer(ty::FloatVar(_)) => { - self.relate_var(a.into(), b.into()) + + if !D::forbid_inference_vars() { + b = self.infcx.shallow_resolve(b); + } + + match (&a.sty, &b.sty) { + (_, &ty::Infer(ty::TyVar(vid))) => { + if D::forbid_inference_vars() { + // Forbid inference variables in the RHS. + bug!("unexpected inference var {:?}", b) + } else { + self.relate_ty_var(vid, a) + } + } + + (&ty::Infer(ty::TyVar(vid)), _) => self.relate_ty_var(vid, b), + + (&ty::Projection(projection_ty), _) + if D::normalization() == NormalizationStrategy::Lazy => + { + Ok(self.relate_projection_ty(projection_ty, b)) + } + + (_, &ty::Projection(projection_ty)) + if D::normalization() == NormalizationStrategy::Lazy => + { + Ok(self.relate_projection_ty(projection_ty, a)) } _ => { @@ -340,7 +455,8 @@ where a, b, self.ambient_variance ); - relate::super_relate_tys(self, a, b) + // Will also handle unification of `IntVar` and `FloatVar`. + self.infcx.super_combine_tys(self, a, b) } } } @@ -551,7 +667,7 @@ struct TypeGeneralizer<'me, 'gcx: 'tcx, 'tcx: 'me, D> where D: TypeRelatingDelegate<'tcx> + 'me, { - tcx: TyCtxt<'me, 'gcx, 'tcx>, + infcx: &'me InferCtxt<'me, 'gcx, 'tcx>, delegate: &'me mut D, @@ -561,6 +677,14 @@ where first_free_index: ty::DebruijnIndex, + /// The vid of the type variable that is in the process of being + /// instantiated. If we find this within the value we are folding, + /// that means we would have created a cyclic value. + for_vid_sub_root: ty::TyVid, + + /// The universe of the type variable that is in the process of being + /// instantiated. If we find anything that this universe cannot name, + /// we reject the relation. universe: ty::UniverseIndex, } @@ -569,7 +693,7 @@ where D: TypeRelatingDelegate<'tcx>, { fn tcx(&self) -> TyCtxt<'me, 'gcx, 'tcx> { - self.tcx + self.infcx.tcx } fn tag(&self) -> &'static str { @@ -609,17 +733,89 @@ where } fn tys(&mut self, a: Ty<'tcx>, _: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> { + use crate::infer::type_variable::TypeVariableValue; + debug!("TypeGeneralizer::tys(a={:?})", a,); match a.sty { - ty::Infer(ty::TyVar(_)) | ty::Infer(ty::IntVar(_)) | ty::Infer(ty::FloatVar(_)) => { + ty::Infer(ty::TyVar(_)) | ty::Infer(ty::IntVar(_)) | ty::Infer(ty::FloatVar(_)) + if D::forbid_inference_vars() => + { bug!( "unexpected inference variable encountered in NLL generalization: {:?}", a ); } - _ => relate::super_relate_tys(self, a, a), + ty::Infer(ty::TyVar(vid)) => { + let mut variables = self.infcx.type_variables.borrow_mut(); + let vid = variables.root_var(vid); + let sub_vid = variables.sub_root_var(vid); + if sub_vid == self.for_vid_sub_root { + // If sub-roots are equal, then `for_vid` and + // `vid` are related via subtyping. + debug!("TypeGeneralizer::tys: occurs check failed"); + return Err(TypeError::Mismatch); + } else { + match variables.probe(vid) { + TypeVariableValue::Known { value: u } => { + drop(variables); + self.relate(&u, &u) + } + TypeVariableValue::Unknown { universe } => { + if self.universe.cannot_name(universe) { + debug!( + "TypeGeneralizer::tys: root universe {:?} cannot name\ + variable in universe {:?}", + self.universe, + universe + ); + return Err(TypeError::Mismatch); + } + + if self.ambient_variance == ty::Bivariant { + // FIXME: we may need a WF predicate (related to #54105). + } + + let origin = *variables.var_origin(vid); + let new_var_id = variables.new_var(self.universe, false, origin); + let u = self.tcx().mk_var(new_var_id); + debug!( + "generalize: replacing original vid={:?} with new={:?}", + vid, + u + ); + return Ok(u); + } + } + } + } + + ty::Infer(ty::IntVar(_)) | + ty::Infer(ty::FloatVar(_)) => { + // No matter what mode we are in, + // integer/floating-point types must be equal to be + // relatable. + Ok(a) + } + + ty::Placeholder(placeholder) => { + if self.universe.cannot_name(placeholder.universe) { + debug!( + "TypeGeneralizer::tys: root universe {:?} cannot name\ + placeholder in universe {:?}", + self.universe, + placeholder.universe + ); + Err(TypeError::Mismatch) + } else { + Ok(a) + } + } + + _ => { + relate::super_relate_tys(self, a, a) + } } } @@ -673,64 +869,3 @@ where Ok(ty::Binder::bind(result)) } } - -impl InferCtxt<'_, '_, 'tcx> { - /// A hacky sort of method used by the NLL type-relating code: - /// - /// - `var` must be some unbound type variable. - /// - `value` must be a suitable type to use as its value. - /// - /// `var` will then be equated with `value`. Note that this - /// sidesteps a number of important checks, such as the "occurs - /// check" that prevents cyclic types, so it is important not to - /// use this method during regular type-check. - fn force_instantiate_unchecked(&self, var: Ty<'tcx>, value: Ty<'tcx>) { - match (&var.sty, &value.sty) { - (&ty::Infer(ty::TyVar(vid)), _) => { - let mut type_variables = self.type_variables.borrow_mut(); - - // In NLL, we don't have type inference variables - // floating around, so we can do this rather imprecise - // variant of the occurs-check. - assert!(!value.has_infer_types()); - - type_variables.instantiate(vid, value); - } - - (&ty::Infer(ty::IntVar(vid)), &ty::Int(value)) => { - let mut int_unification_table = self.int_unification_table.borrow_mut(); - int_unification_table - .unify_var_value(vid, Some(ty::IntVarValue::IntType(value))) - .unwrap_or_else(|_| { - bug!("failed to unify int var `{:?}` with `{:?}`", vid, value); - }); - } - - (&ty::Infer(ty::IntVar(vid)), &ty::Uint(value)) => { - let mut int_unification_table = self.int_unification_table.borrow_mut(); - int_unification_table - .unify_var_value(vid, Some(ty::IntVarValue::UintType(value))) - .unwrap_or_else(|_| { - bug!("failed to unify int var `{:?}` with `{:?}`", vid, value); - }); - } - - (&ty::Infer(ty::FloatVar(vid)), &ty::Float(value)) => { - let mut float_unification_table = self.float_unification_table.borrow_mut(); - float_unification_table - .unify_var_value(vid, Some(ty::FloatVarValue(value))) - .unwrap_or_else(|_| { - bug!("failed to unify float var `{:?}` with `{:?}`", vid, value) - }); - } - - _ => { - bug!( - "force_instantiate_unchecked invoked with bad combination: var={:?} value={:?}", - var, - value, - ); - } - } - } -} diff --git a/src/librustc/ty/relate.rs b/src/librustc/ty/relate.rs index 082c1bd5fea44..60ad7413cc7ff 100644 --- a/src/librustc/ty/relate.rs +++ b/src/librustc/ty/relate.rs @@ -371,6 +371,10 @@ pub fn super_relate_tys<'a, 'gcx, 'tcx, R>(relation: &mut R, bug!("var types encountered in super_relate_tys") } + (ty::Bound(..), _) | (_, ty::Bound(..)) => { + bug!("bound types encountered in super_relate_tys") + } + (&ty::Error, _) | (_, &ty::Error) => { Ok(tcx.types.err) @@ -394,6 +398,10 @@ pub fn super_relate_tys<'a, 'gcx, 'tcx, R>(relation: &mut R, Ok(a) } + (ty::Placeholder(p1), ty::Placeholder(p2)) if p1 == p2 => { + Ok(a) + } + (&ty::Adt(a_def, a_substs), &ty::Adt(b_def, b_substs)) if a_def == b_def => { @@ -556,8 +564,13 @@ pub fn super_relate_tys<'a, 'gcx, 'tcx, R>(relation: &mut R, Ok(tcx.mk_fn_ptr(fty)) } - (&ty::Projection(ref a_data), &ty::Projection(ref b_data)) => - { + (ty::UnnormalizedProjection(a_data), ty::UnnormalizedProjection(b_data)) => { + let projection_ty = relation.relate(a_data, b_data)?; + Ok(tcx.mk_ty(ty::UnnormalizedProjection(projection_ty))) + } + + // these two are already handled downstream in case of lazy normalization + (ty::Projection(a_data), ty::Projection(b_data)) => { let projection_ty = relation.relate(a_data, b_data)?; Ok(tcx.mk_projection(projection_ty.item_def_id, projection_ty.substs)) } 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 cf4f913080783..9924b03387132 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 @@ -10,10 +10,11 @@ use borrow_check::nll::constraints::OutlivesConstraint; use borrow_check::nll::type_check::{BorrowCheckContext, Locations}; -use rustc::infer::nll_relate::{TypeRelating, TypeRelatingDelegate}; +use rustc::infer::nll_relate::{TypeRelating, TypeRelatingDelegate, NormalizationStrategy}; use rustc::infer::{InferCtxt, NLLRegionVariableOrigin}; use rustc::mir::ConstraintCategory; use rustc::traits::query::Fallible; +use rustc::traits::DomainGoal; use rustc::ty::relate::TypeRelation; use rustc::ty::{self, Ty}; @@ -38,7 +39,7 @@ pub(super) fn relate_types<'tcx>( TypeRelating::new( infcx, NllTypeRelatingDelegate::new(infcx, borrowck_context, locations, category), - v, + v ).relate(&a, &b)?; Ok(()) } @@ -115,4 +116,16 @@ impl TypeRelatingDelegate<'tcx> for NllTypeRelatingDelegate<'_, '_, '_, 'tcx> { }); } } + + fn push_domain_goal(&mut self, _: DomainGoal<'tcx>) { + // No-op + } + + fn normalization() -> NormalizationStrategy { + NormalizationStrategy::Eager + } + + fn forbid_inference_vars() -> bool { + true + } } diff --git a/src/librustc_traits/chalk_context/mod.rs b/src/librustc_traits/chalk_context/mod.rs index 25a6af284b572..e4d93374faa46 100644 --- a/src/librustc_traits/chalk_context/mod.rs +++ b/src/librustc_traits/chalk_context/mod.rs @@ -9,13 +9,14 @@ // except according to those terms. mod program_clauses; +mod unify; -use chalk_engine::fallible::Fallible as ChalkEngineFallible; +use chalk_engine::fallible::{Fallible, NoSolution}; use chalk_engine::{context, hh::HhGoal, DelayedLiteral, Literal, ExClause}; use rustc::infer::canonical::{ - Canonical, CanonicalVarValues, OriginalQueryValues, QueryRegionConstraint, QueryResponse, + Canonical, CanonicalVarValues, OriginalQueryValues, QueryResponse, }; -use rustc::infer::{InferCtxt, InferOk, LateBoundRegionConversionTime}; +use rustc::infer::{InferCtxt, LateBoundRegionConversionTime}; use rustc::traits::{ DomainGoal, ExClauseFold, @@ -30,11 +31,12 @@ use rustc::traits::{ use rustc::ty::fold::{TypeFoldable, TypeFolder, TypeVisitor}; use rustc::ty::subst::{Kind, UnpackedKind}; use rustc::ty::{self, TyCtxt}; +use syntax_pos::DUMMY_SP; use std::fmt::{self, Debug}; use std::marker::PhantomData; -use syntax_pos::DUMMY_SP; +use self::unify::*; #[derive(Copy, Clone, Debug)] crate struct ChalkArenas<'gcx> { @@ -55,10 +57,12 @@ crate struct ChalkInferenceContext<'cx, 'gcx: 'tcx, 'tcx: 'cx> { #[derive(Copy, Clone, Debug)] crate struct UniverseMap; +crate type RegionConstraint<'tcx> = ty::OutlivesPredicate, ty::Region<'tcx>>; + #[derive(Clone, Debug, PartialEq, Eq, Hash)] crate struct ConstrainedSubst<'tcx> { subst: CanonicalVarValues<'tcx>, - constraints: Vec>, + constraints: Vec>, } BraceStructTypeFoldableImpl! { @@ -86,7 +90,7 @@ impl context::Context for ChalkArenas<'tcx> { type GoalInEnvironment = InEnvironment<'tcx, Goal<'tcx>>; - type RegionConstraint = QueryRegionConstraint<'tcx>; + type RegionConstraint = RegionConstraint<'tcx>; type Substitution = CanonicalVarValues<'tcx>; @@ -104,7 +108,7 @@ impl context::Context for ChalkArenas<'tcx> { type ProgramClauses = Vec>; - type UnificationResult = InferOk<'tcx, ()>; + type UnificationResult = UnificationResult<'tcx>; fn goal_in_environment( env: &Environment<'tcx>, @@ -291,7 +295,7 @@ impl context::ResolventOps, ChalkArenas<'tcx>> _goal: &DomainGoal<'tcx>, _subst: &CanonicalVarValues<'tcx>, _clause: &Clause<'tcx>, - ) -> chalk_engine::fallible::Fallible>> { + ) -> Fallible>> { panic!() } @@ -301,7 +305,7 @@ impl context::ResolventOps, ChalkArenas<'tcx>> _selected_goal: &InEnvironment<'tcx, Goal<'tcx>>, _answer_table_goal: &Canonical<'gcx, InEnvironment<'gcx, Goal<'gcx>>>, _canonical_answer_subst: &Canonical<'gcx, ConstrainedSubst<'gcx>>, - ) -> chalk_engine::fallible::Fallible> { + ) -> Fallible> { panic!() } } @@ -376,7 +380,7 @@ impl context::UnificationOps, ChalkArenas<'tcx>> fn canonicalize_constrained_subst( &mut self, subst: CanonicalVarValues<'tcx>, - constraints: Vec>, + constraints: Vec>, ) -> Canonical<'gcx, ConstrainedSubst<'gcx>> { self.infcx.canonicalize_response(&ConstrainedSubst { subst, constraints }) } @@ -400,11 +404,13 @@ impl context::UnificationOps, ChalkArenas<'tcx>> fn unify_parameters( &mut self, - _environment: &Environment<'tcx>, - _a: &Kind<'tcx>, - _b: &Kind<'tcx>, - ) -> ChalkEngineFallible> { - panic!() + environment: &Environment<'tcx>, + a: &Kind<'tcx>, + b: &Kind<'tcx>, + ) -> Fallible> { + self.infcx.commit_if_ok(|_| { + unify(self.infcx, *environment, a, b).map_err(|_| NoSolution) + }) } fn sink_answer_subset( @@ -421,11 +427,22 @@ impl context::UnificationOps, ChalkArenas<'tcx>> panic!("lift") } - fn into_ex_clause(&mut self, _result: InferOk<'tcx, ()>, _ex_clause: &mut ChalkExClause<'tcx>) { - panic!("TBD") + fn into_ex_clause( + &mut self, + result: UnificationResult<'tcx>, + ex_clause: &mut ChalkExClause<'tcx> + ) { + into_ex_clause(result, ex_clause); } } +crate fn into_ex_clause(result: UnificationResult<'tcx>, ex_clause: &mut ChalkExClause<'tcx>) { + ex_clause.subgoals.extend( + result.goals.into_iter().map(Literal::Positive) + ); + ex_clause.constraints.extend(result.constraints); +} + type ChalkHhGoal<'tcx> = HhGoal>; type ChalkExClause<'tcx> = ExClause>; diff --git a/src/librustc_traits/chalk_context/unify.rs b/src/librustc_traits/chalk_context/unify.rs new file mode 100644 index 0000000000000..3a9c3918d137e --- /dev/null +++ b/src/librustc_traits/chalk_context/unify.rs @@ -0,0 +1,98 @@ +use rustc::infer::nll_relate::{TypeRelating, TypeRelatingDelegate, NormalizationStrategy}; +use rustc::infer::{InferCtxt, RegionVariableOrigin}; +use rustc::traits::{DomainGoal, Goal, Environment, InEnvironment}; +use rustc::ty::relate::{Relate, TypeRelation, RelateResult}; +use rustc::ty; +use syntax_pos::DUMMY_SP; + +crate struct UnificationResult<'tcx> { + crate goals: Vec>>, + crate constraints: Vec>, +} + +crate fn unify<'me, 'gcx, 'tcx, T: Relate<'tcx>>( + infcx: &'me InferCtxt<'me, 'gcx, 'tcx>, + environment: Environment<'tcx>, + a: &T, + b: &T +) -> RelateResult<'tcx, UnificationResult<'tcx>> { + let mut delegate = ChalkTypeRelatingDelegate::new( + infcx, + environment + ); + + TypeRelating::new( + infcx, + &mut delegate, + ty::Variance::Invariant + ).relate(a, b)?; + + Ok(UnificationResult { + goals: delegate.goals, + constraints: delegate.constraints, + }) +} + +struct ChalkTypeRelatingDelegate<'me, 'gcx: 'tcx, 'tcx: 'me> { + infcx: &'me InferCtxt<'me, 'gcx, 'tcx>, + environment: Environment<'tcx>, + goals: Vec>>, + constraints: Vec>, +} + +impl ChalkTypeRelatingDelegate<'me, 'gcx, 'tcx> { + fn new( + infcx: &'me InferCtxt<'me, 'gcx, 'tcx>, + environment: Environment<'tcx>, + ) -> Self { + Self { + infcx, + environment, + goals: Vec::new(), + constraints: Vec::new(), + } + } +} + +impl TypeRelatingDelegate<'tcx> for &mut ChalkTypeRelatingDelegate<'_, '_, 'tcx> { + fn create_next_universe(&mut self) -> ty::UniverseIndex { + self.infcx.create_next_universe() + } + + fn next_existential_region_var(&mut self) -> ty::Region<'tcx> { + self.infcx.next_region_var(RegionVariableOrigin::MiscVariable(DUMMY_SP)) + } + + fn next_placeholder_region( + &mut self, + placeholder: ty::PlaceholderRegion + ) -> ty::Region<'tcx> { + self.infcx.tcx.mk_region(ty::RePlaceholder(placeholder)) + } + + fn generalize_existential(&mut self, universe: ty::UniverseIndex) -> ty::Region<'tcx> { + self.infcx.next_region_var_in_universe( + RegionVariableOrigin::MiscVariable(DUMMY_SP), + universe + ) + } + + fn push_outlives(&mut self, sup: ty::Region<'tcx>, sub: ty::Region<'tcx>) { + self.constraints.push(ty::OutlivesPredicate(sup.into(), sub)); + } + + fn push_domain_goal(&mut self, domain_goal: DomainGoal<'tcx>) { + let goal = self.environment.with( + self.infcx.tcx.mk_goal(domain_goal.into_goal()) + ); + self.goals.push(goal); + } + + fn normalization() -> NormalizationStrategy { + NormalizationStrategy::Lazy + } + + fn forbid_inference_vars() -> bool { + false + } +} From 0169dc3f36aec63e5926f2d69e788a396fd10f82 Mon Sep 17 00:00:00 2001 From: scalexm Date: Fri, 9 Nov 2018 14:49:37 +0100 Subject: [PATCH 16/36] Implement `ResolventOps` --- src/librustc/ty/fold.rs | 44 ++- src/librustc/ty/relate.rs | 278 ++++++++++++++++++ src/librustc_traits/chalk_context/mod.rs | 44 +-- .../chalk_context/resolvent_ops.rs | 241 +++++++++++++++ 4 files changed, 572 insertions(+), 35 deletions(-) create mode 100644 src/librustc_traits/chalk_context/resolvent_ops.rs diff --git a/src/librustc/ty/fold.rs b/src/librustc/ty/fold.rs index 6f0e8d4f02680..5b9193ad74ac3 100644 --- a/src/librustc/ty/fold.rs +++ b/src/librustc/ty/fold.rs @@ -679,24 +679,31 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { // vars. See comment on `shift_vars_through_binders` method in // `subst.rs` for more details. +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +enum Direction { + In, + Out, +} + struct Shifter<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { tcx: TyCtxt<'a, 'gcx, 'tcx>, - current_index: ty::DebruijnIndex, amount: u32, + direction: Direction, } impl Shifter<'a, 'gcx, 'tcx> { - pub fn new(tcx: TyCtxt<'a, 'gcx, 'tcx>, amount: u32) -> Self { + pub fn new(tcx: TyCtxt<'a, 'gcx, 'tcx>, amount: u32, direction: Direction) -> Self { Shifter { tcx, current_index: ty::INNERMOST, amount, + direction, } } } -impl<'a, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for Shifter<'a, 'gcx, 'tcx> { +impl TypeFolder<'gcx, 'tcx> for Shifter<'a, 'gcx, 'tcx> { fn tcx<'b>(&'b self) -> TyCtxt<'b, 'gcx, 'tcx> { self.tcx } fn fold_binder>(&mut self, t: &ty::Binder) -> ty::Binder { @@ -712,7 +719,14 @@ impl<'a, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for Shifter<'a, 'gcx, 'tcx> { if self.amount == 0 || debruijn < self.current_index { r } else { - let shifted = ty::ReLateBound(debruijn.shifted_in(self.amount), br); + let debruijn = match self.direction { + Direction::In => debruijn.shifted_in(self.amount), + Direction::Out => { + assert!(debruijn.as_u32() >= self.amount); + debruijn.shifted_out(self.amount) + } + }; + let shifted = ty::ReLateBound(debruijn, br); self.tcx.mk_region(shifted) } } @@ -726,8 +740,15 @@ impl<'a, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for Shifter<'a, 'gcx, 'tcx> { if self.amount == 0 || debruijn < self.current_index { ty } else { + let debruijn = match self.direction { + Direction::In => debruijn.shifted_in(self.amount), + Direction::Out => { + assert!(debruijn.as_u32() >= self.amount); + debruijn.shifted_out(self.amount) + } + }; self.tcx.mk_ty( - ty::Bound(debruijn.shifted_in(self.amount), bound_ty) + ty::Bound(debruijn, bound_ty) ) } } @@ -760,7 +781,18 @@ pub fn shift_vars<'a, 'gcx, 'tcx, T>( debug!("shift_vars(value={:?}, amount={})", value, amount); - value.fold_with(&mut Shifter::new(tcx, amount)) + value.fold_with(&mut Shifter::new(tcx, amount, Direction::In)) +} + +pub fn shift_out_vars<'a, 'gcx, 'tcx, T>( + tcx: TyCtxt<'a, 'gcx, 'tcx>, + value: &T, + amount: u32 +) -> T where T: TypeFoldable<'tcx> { + debug!("shift_out_vars(value={:?}, amount={})", + value, amount); + + value.fold_with(&mut Shifter::new(tcx, amount, Direction::Out)) } /// An "escaping var" is a bound var whose binder is not part of `t`. A bound var can be a diff --git a/src/librustc/ty/relate.rs b/src/librustc/ty/relate.rs index 60ad7413cc7ff..1b64a686794c0 100644 --- a/src/librustc/ty/relate.rs +++ b/src/librustc/ty/relate.rs @@ -25,6 +25,7 @@ use std::rc::Rc; use std::iter; use rustc_target::spec::abi; use hir as ast; +use traits; pub type RelateResult<'tcx, T> = Result>; @@ -723,6 +724,283 @@ impl<'tcx> Relate<'tcx> for Kind<'tcx> { } } +impl<'tcx> Relate<'tcx> for ty::TraitPredicate<'tcx> { + fn relate<'a, 'gcx, R>( + relation: &mut R, + a: &ty::TraitPredicate<'tcx>, + b: &ty::TraitPredicate<'tcx> + ) -> RelateResult<'tcx, ty::TraitPredicate<'tcx>> + where R: TypeRelation<'a, 'gcx, 'tcx>, 'gcx: 'tcx, 'tcx: 'a + { + Ok(ty::TraitPredicate { + trait_ref: relation.relate(&a.trait_ref, &b.trait_ref)?, + }) + } +} + +impl<'tcx> Relate<'tcx> for ty::ProjectionPredicate<'tcx> { + fn relate<'a, 'gcx, R>( + relation: &mut R, + a: &ty::ProjectionPredicate<'tcx>, + b: &ty::ProjectionPredicate<'tcx>, + ) -> RelateResult<'tcx, ty::ProjectionPredicate<'tcx>> + where R: TypeRelation<'a, 'gcx, 'tcx>, 'gcx: 'tcx, 'tcx: 'a + { + Ok(ty::ProjectionPredicate { + projection_ty: relation.relate(&a.projection_ty, &b.projection_ty)?, + ty: relation.relate(&a.ty, &b.ty)?, + }) + } +} + +impl<'tcx> Relate<'tcx> for traits::WhereClause<'tcx> { + fn relate<'a, 'gcx, R>( + relation: &mut R, + a: &traits::WhereClause<'tcx>, + b: &traits::WhereClause<'tcx> + ) -> RelateResult<'tcx, traits::WhereClause<'tcx>> + where R: TypeRelation<'a, 'gcx, 'tcx>, 'gcx: 'tcx, 'tcx: 'a + { + use traits::WhereClause::*; + match (a, b) { + (Implemented(a_pred), Implemented(b_pred)) => { + Ok(Implemented(relation.relate(a_pred, b_pred)?)) + } + + (ProjectionEq(a_pred), ProjectionEq(b_pred)) => { + Ok(ProjectionEq(relation.relate(a_pred, b_pred)?)) + } + + (RegionOutlives(a_pred), RegionOutlives(b_pred)) => { + Ok(RegionOutlives(ty::OutlivesPredicate( + relation.relate(&a_pred.0, &b_pred.0)?, + relation.relate(&a_pred.1, &b_pred.1)?, + ))) + } + + (TypeOutlives(a_pred), TypeOutlives(b_pred)) => { + Ok(TypeOutlives(ty::OutlivesPredicate( + relation.relate(&a_pred.0, &b_pred.0)?, + relation.relate(&a_pred.1, &b_pred.1)?, + ))) + } + + _ => Err(TypeError::Mismatch), + } + } +} + +impl<'tcx> Relate<'tcx> for traits::WellFormed<'tcx> { + fn relate<'a, 'gcx, R>( + relation: &mut R, + a: &traits::WellFormed<'tcx>, + b: &traits::WellFormed<'tcx> + ) -> RelateResult<'tcx, traits::WellFormed<'tcx>> + where R: TypeRelation<'a, 'gcx, 'tcx>, 'gcx: 'tcx, 'tcx: 'a + { + use traits::WellFormed::*; + match (a, b) { + (Trait(a_pred), Trait(b_pred)) => Ok(Trait(relation.relate(a_pred, b_pred)?)), + (Ty(a_ty), Ty(b_ty)) => Ok(Ty(relation.relate(a_ty, b_ty)?)), + _ => Err(TypeError::Mismatch), + } + } +} + +impl<'tcx> Relate<'tcx> for traits::FromEnv<'tcx> { + fn relate<'a, 'gcx, R>( + relation: &mut R, + a: &traits::FromEnv<'tcx>, + b: &traits::FromEnv<'tcx> + ) -> RelateResult<'tcx, traits::FromEnv<'tcx>> + where R: TypeRelation<'a, 'gcx, 'tcx>, 'gcx: 'tcx, 'tcx: 'a + { + use traits::FromEnv::*; + match (a, b) { + (Trait(a_pred), Trait(b_pred)) => Ok(Trait(relation.relate(a_pred, b_pred)?)), + (Ty(a_ty), Ty(b_ty)) => Ok(Ty(relation.relate(a_ty, b_ty)?)), + _ => Err(TypeError::Mismatch), + } + } +} + +impl<'tcx> Relate<'tcx> for traits::DomainGoal<'tcx> { + fn relate<'a, 'gcx, R>( + relation: &mut R, + a: &traits::DomainGoal<'tcx>, + b: &traits::DomainGoal<'tcx> + ) -> RelateResult<'tcx, traits::DomainGoal<'tcx>> + where R: TypeRelation<'a, 'gcx, 'tcx>, 'gcx: 'tcx, 'tcx: 'a + { + use traits::DomainGoal::*; + match (a, b) { + (Holds(a_wc), Holds(b_wc)) => Ok(Holds(relation.relate(a_wc, b_wc)?)), + (WellFormed(a_wf), WellFormed(b_wf)) => Ok(WellFormed(relation.relate(a_wf, b_wf)?)), + (FromEnv(a_fe), FromEnv(b_fe)) => Ok(FromEnv(relation.relate(a_fe, b_fe)?)), + + (Normalize(a_pred), Normalize(b_pred)) => { + Ok(Normalize(relation.relate(a_pred, b_pred)?)) + } + + _ => Err(TypeError::Mismatch), + } + } +} + +impl<'tcx> Relate<'tcx> for traits::Goal<'tcx> { + fn relate<'a, 'gcx, R>( + relation: &mut R, + a: &traits::Goal<'tcx>, + b: &traits::Goal<'tcx> + ) -> RelateResult<'tcx, traits::Goal<'tcx>> + where R: TypeRelation<'a, 'gcx, 'tcx>, 'gcx: 'tcx, 'tcx: 'a + { + use traits::GoalKind::*; + match (a, b) { + (Implies(a_clauses, a_goal), Implies(b_clauses, b_goal)) => { + let clauses = relation.relate(a_clauses, b_clauses)?; + let goal = relation.relate(a_goal, b_goal)?; + Ok(relation.tcx().mk_goal(Implies(clauses, goal))) + } + + (And(a_left, a_right), And(b_left, b_right)) => { + let left = relation.relate(a_left, b_left)?; + let right = relation.relate(a_right, b_right)?; + Ok(relation.tcx().mk_goal(And(left, right))) + } + + (Not(a_goal), Not(b_goal)) => { + let goal = relation.relate(a_goal, b_goal)?; + Ok(relation.tcx().mk_goal(Not(goal))) + } + + (DomainGoal(a_goal), DomainGoal(b_goal)) => { + let goal = relation.relate(a_goal, b_goal)?; + Ok(relation.tcx().mk_goal(DomainGoal(goal))) + } + + (Quantified(a_qkind, a_goal), Quantified(b_qkind, b_goal)) + if a_qkind == b_qkind => + { + let goal = relation.relate(a_goal, b_goal)?; + Ok(relation.tcx().mk_goal(Quantified(*a_qkind, goal))) + } + + (CannotProve, CannotProve) => Ok(*a), + + _ => Err(TypeError::Mismatch), + } + } +} + +impl<'tcx> Relate<'tcx> for traits::Goals<'tcx> { + fn relate<'a, 'gcx, R>( + relation: &mut R, + a: &traits::Goals<'tcx>, + b: &traits::Goals<'tcx> + ) -> RelateResult<'tcx, traits::Goals<'tcx>> + where R: TypeRelation<'a, 'gcx, 'tcx>, 'gcx: 'tcx, 'tcx: 'a + { + if a.len() != b.len() { + return Err(TypeError::Mismatch); + } + + let tcx = relation.tcx(); + let goals = a.iter().zip(b.iter()).map(|(a, b)| relation.relate(a, b)); + Ok(tcx.mk_goals(goals)?) + } +} + +impl<'tcx> Relate<'tcx> for traits::Clause<'tcx> { + fn relate<'a, 'gcx, R>( + relation: &mut R, + a: &traits::Clause<'tcx>, + b: &traits::Clause<'tcx> + ) -> RelateResult<'tcx, traits::Clause<'tcx>> + where R: TypeRelation<'a, 'gcx, 'tcx>, 'gcx: 'tcx, 'tcx: 'a + { + use traits::Clause::*; + match (a, b) { + (Implies(a_clause), Implies(b_clause)) => { + let clause = relation.relate(a_clause, b_clause)?; + Ok(Implies(clause)) + } + + (ForAll(a_clause), ForAll(b_clause)) => { + let clause = relation.relate(a_clause, b_clause)?; + Ok(ForAll(clause)) + } + + _ => Err(TypeError::Mismatch), + } + } +} + +impl<'tcx> Relate<'tcx> for traits::Clauses<'tcx> { + fn relate<'a, 'gcx, R>( + relation: &mut R, + a: &traits::Clauses<'tcx>, + b: &traits::Clauses<'tcx> + ) -> RelateResult<'tcx, traits::Clauses<'tcx>> + where R: TypeRelation<'a, 'gcx, 'tcx>, 'gcx: 'tcx, 'tcx: 'a + { + if a.len() != b.len() { + return Err(TypeError::Mismatch); + } + + let tcx = relation.tcx(); + let clauses = a.iter().zip(b.iter()).map(|(a, b)| relation.relate(a, b)); + Ok(tcx.mk_clauses(clauses)?) + } +} + +impl<'tcx> Relate<'tcx> for traits::ProgramClause<'tcx> { + fn relate<'a, 'gcx, R>( + relation: &mut R, + a: &traits::ProgramClause<'tcx>, + b: &traits::ProgramClause<'tcx> + ) -> RelateResult<'tcx, traits::ProgramClause<'tcx>> + where R: TypeRelation<'a, 'gcx, 'tcx>, 'gcx: 'tcx, 'tcx: 'a + { + Ok(traits::ProgramClause { + goal: relation.relate(&a.goal, &b.goal)?, + hypotheses: relation.relate(&a.hypotheses, &b.hypotheses)?, + category: traits::ProgramClauseCategory::Other, + }) + } +} + +impl<'tcx> Relate<'tcx> for traits::Environment<'tcx> { + fn relate<'a, 'gcx, R>( + relation: &mut R, + a: &traits::Environment<'tcx>, + b: &traits::Environment<'tcx> + ) -> RelateResult<'tcx, traits::Environment<'tcx>> + where R: TypeRelation<'a, 'gcx, 'tcx>, 'gcx: 'tcx, 'tcx: 'a + { + Ok(traits::Environment { + clauses: relation.relate(&a.clauses, &b.clauses)?, + }) + } +} + +impl<'tcx, G> Relate<'tcx> for traits::InEnvironment<'tcx, G> + where G: Relate<'tcx> +{ + fn relate<'a, 'gcx, R>( + relation: &mut R, + a: &traits::InEnvironment<'tcx, G>, + b: &traits::InEnvironment<'tcx, G> + ) -> RelateResult<'tcx, traits::InEnvironment<'tcx, G>> + where R: TypeRelation<'a, 'gcx, 'tcx>, 'gcx: 'tcx, 'tcx: 'a + { + Ok(traits::InEnvironment { + environment: relation.relate(&a.environment, &b.environment)?, + goal: relation.relate(&a.goal, &b.goal)?, + }) + } +} + /////////////////////////////////////////////////////////////////////////// // Error handling diff --git a/src/librustc_traits/chalk_context/mod.rs b/src/librustc_traits/chalk_context/mod.rs index e4d93374faa46..bc3f2fd951b93 100644 --- a/src/librustc_traits/chalk_context/mod.rs +++ b/src/librustc_traits/chalk_context/mod.rs @@ -9,14 +9,24 @@ // except according to those terms. mod program_clauses; +mod resolvent_ops; mod unify; use chalk_engine::fallible::{Fallible, NoSolution}; -use chalk_engine::{context, hh::HhGoal, DelayedLiteral, Literal, ExClause}; -use rustc::infer::canonical::{ - Canonical, CanonicalVarValues, OriginalQueryValues, QueryResponse, +use chalk_engine::{ + context, + hh::HhGoal, + DelayedLiteral, + Literal, + ExClause }; use rustc::infer::{InferCtxt, LateBoundRegionConversionTime}; +use rustc::infer::canonical::{ + Canonical, + CanonicalVarValues, + OriginalQueryValues, + QueryResponse, +}; use rustc::traits::{ DomainGoal, ExClauseFold, @@ -28,9 +38,9 @@ use rustc::traits::{ Environment, InEnvironment, }; +use rustc::ty::{self, TyCtxt}; use rustc::ty::fold::{TypeFoldable, TypeFolder, TypeVisitor}; use rustc::ty::subst::{Kind, UnpackedKind}; -use rustc::ty::{self, TyCtxt}; use syntax_pos::DUMMY_SP; use std::fmt::{self, Debug}; @@ -201,7 +211,7 @@ impl context::ContextOps> for ChalkContext<'cx, 'gcx> { fn is_trivial_substitution( u_canon: &Canonical<'gcx, InEnvironment<'gcx, Goal<'gcx>>>, - canonical_subst: &Canonical<'tcx, ConstrainedSubst<'tcx>>, + canonical_subst: &Canonical<'gcx, ConstrainedSubst<'gcx>>, ) -> bool { let subst = &canonical_subst.value.subst; assert_eq!(u_canon.variables.len(), subst.var_values.len()); @@ -286,30 +296,6 @@ impl context::InferenceTable, ChalkArenas<'tcx>> } } -impl context::ResolventOps, ChalkArenas<'tcx>> - for ChalkInferenceContext<'cx, 'gcx, 'tcx> -{ - fn resolvent_clause( - &mut self, - _environment: &Environment<'tcx>, - _goal: &DomainGoal<'tcx>, - _subst: &CanonicalVarValues<'tcx>, - _clause: &Clause<'tcx>, - ) -> Fallible>> { - panic!() - } - - fn apply_answer_subst( - &mut self, - _ex_clause: ChalkExClause<'tcx>, - _selected_goal: &InEnvironment<'tcx, Goal<'tcx>>, - _answer_table_goal: &Canonical<'gcx, InEnvironment<'gcx, Goal<'gcx>>>, - _canonical_answer_subst: &Canonical<'gcx, ConstrainedSubst<'gcx>>, - ) -> Fallible> { - panic!() - } -} - impl context::TruncateOps, ChalkArenas<'tcx>> for ChalkInferenceContext<'cx, 'gcx, 'tcx> { diff --git a/src/librustc_traits/chalk_context/resolvent_ops.rs b/src/librustc_traits/chalk_context/resolvent_ops.rs new file mode 100644 index 0000000000000..df6458a766d4e --- /dev/null +++ b/src/librustc_traits/chalk_context/resolvent_ops.rs @@ -0,0 +1,241 @@ +use chalk_engine::fallible::{Fallible, NoSolution}; +use chalk_engine::{ + context, + Literal, + ExClause +}; +use rustc::infer::{InferCtxt, LateBoundRegionConversionTime}; +use rustc::infer::canonical::{Canonical, CanonicalVarValues}; +use rustc::traits::{ + DomainGoal, + Goal, + GoalKind, + Clause, + ProgramClause, + Environment, + InEnvironment, +}; +use rustc::ty::{self, Ty}; +use rustc::ty::subst::Kind; +use rustc::ty::relate::{Relate, RelateResult, TypeRelation}; +use syntax_pos::DUMMY_SP; + +use super::{ChalkInferenceContext, ChalkArenas, ChalkExClause, ConstrainedSubst}; +use super::unify::*; + +impl context::ResolventOps, ChalkArenas<'tcx>> + for ChalkInferenceContext<'cx, 'gcx, 'tcx> +{ + fn resolvent_clause( + &mut self, + environment: &Environment<'tcx>, + goal: &DomainGoal<'tcx>, + subst: &CanonicalVarValues<'tcx>, + clause: &Clause<'tcx>, + ) -> Fallible>> { + use chalk_engine::context::UnificationOps; + + self.infcx.probe(|_| { + let ProgramClause { + goal: consequence, + hypotheses, + .. + } = match clause { + Clause::Implies(program_clause) => *program_clause, + Clause::ForAll(program_clause) => self.infcx.replace_bound_vars_with_fresh_vars( + DUMMY_SP, + LateBoundRegionConversionTime::HigherRankedType, + program_clause + ).0, + }; + + let result = unify(self.infcx, *environment, goal, &consequence) + .map_err(|_| NoSolution)?; + + let mut ex_clause = ExClause { + subst: subst.clone(), + delayed_literals: vec![], + constraints: vec![], + subgoals: vec![], + }; + + self.into_ex_clause(result, &mut ex_clause); + + ex_clause.subgoals.extend( + hypotheses.iter().map(|g| match g { + GoalKind::Not(g) => Literal::Negative(environment.with(*g)), + g => Literal::Positive(environment.with(*g)), + }) + ); + + let canonical_ex_clause = self.canonicalize_ex_clause(&ex_clause); + Ok(canonical_ex_clause) + }) + } + + fn apply_answer_subst( + &mut self, + ex_clause: ChalkExClause<'tcx>, + selected_goal: &InEnvironment<'tcx, Goal<'tcx>>, + answer_table_goal: &Canonical<'gcx, InEnvironment<'gcx, Goal<'gcx>>>, + canonical_answer_subst: &Canonical<'gcx, ConstrainedSubst<'gcx>>, + ) -> Fallible> { + let (answer_subst, _) = self.infcx.instantiate_canonical_with_fresh_inference_vars( + DUMMY_SP, + canonical_answer_subst + ); + + let mut substitutor = AnswerSubstitutor { + infcx: self.infcx, + environment: selected_goal.environment, + answer_subst: answer_subst.subst, + binder_index: ty::INNERMOST, + ex_clause, + }; + + substitutor.relate(&answer_table_goal.value, &selected_goal) + .map_err(|_| NoSolution)?; + + let mut ex_clause = substitutor.ex_clause; + ex_clause.constraints.extend(answer_subst.constraints); + Ok(ex_clause) + } +} + +struct AnswerSubstitutor<'cx, 'gcx: 'tcx, 'tcx: 'cx> { + infcx: &'cx InferCtxt<'cx, 'gcx, 'tcx>, + environment: Environment<'tcx>, + answer_subst: CanonicalVarValues<'tcx>, + binder_index: ty::DebruijnIndex, + ex_clause: ChalkExClause<'tcx>, +} + +impl AnswerSubstitutor<'cx, 'gcx, 'tcx> { + fn unify_free_answer_var( + &mut self, + answer_var: ty::BoundVar, + pending: Kind<'tcx> + ) -> RelateResult<'tcx, ()> { + let answer_param = &self.answer_subst.var_values[answer_var]; + let pending = &ty::fold::shift_out_vars( + self.infcx.tcx, + &pending, + self.binder_index.as_u32() + ); + + super::into_ex_clause( + unify(self.infcx, self.environment, answer_param, pending)?, + &mut self.ex_clause + ); + + Ok(()) + } +} + +impl TypeRelation<'cx, 'gcx, 'tcx> for AnswerSubstitutor<'cx, 'gcx, 'tcx> { + fn tcx(&self) -> ty::TyCtxt<'cx, 'gcx, 'tcx> { + self.infcx.tcx + } + + fn tag(&self) -> &'static str { + "chalk_context::answer_substitutor" + } + + fn a_is_expected(&self) -> bool { + true + } + + fn relate_with_variance>( + &mut self, + _variance: ty::Variance, + a: &T, + b: &T, + ) -> RelateResult<'tcx, T> { + // We don't care about variance. + self.relate(a, b) + } + + fn binders>( + &mut self, + a: &ty::Binder, + b: &ty::Binder, + ) -> RelateResult<'tcx, ty::Binder> { + self.binder_index.shift_in(1); + let result = self.relate(a.skip_binder(), b.skip_binder())?; + self.binder_index.shift_out(1); + Ok(ty::Binder::bind(result)) + } + + fn tys(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> { + let b = self.infcx.shallow_resolve(b); + + if let &ty::Bound(debruijn, bound_ty) = &a.sty { + // Free bound var + if debruijn == self.binder_index { + self.unify_free_answer_var(bound_ty.var, b.into())?; + return Ok(b); + } + } + + match (&a.sty, &b.sty) { + (&ty::Bound(a_debruijn, a_bound), &ty::Bound(b_debruijn, b_bound)) => { + assert_eq!(a_debruijn, b_debruijn); + assert_eq!(a_bound.var, b_bound.var); + Ok(a) + } + + // Those should have been canonicalized away. + (ty::Placeholder(..), _) => { + bug!("unexpected placeholder ty in `AnswerSubstitutor`: {:?} ", a); + } + + // Everything else should just be a perfect match as well, + // and we forbid inference variables. + _ => match ty::relate::super_relate_tys(self, a, b) { + Ok(ty) => Ok(ty), + Err(err) => bug!("type mismatch in `AnswerSubstitutor`: {}", err), + } + } + } + + fn regions( + &mut self, + a: ty::Region<'tcx>, + b: ty::Region<'tcx>, + ) -> RelateResult<'tcx, ty::Region<'tcx>> { + let b = match b { + &ty::ReVar(vid) => self.infcx + .borrow_region_constraints() + .opportunistic_resolve_var(self.infcx.tcx, vid), + + other => other, + }; + + if let &ty::ReLateBound(debruijn, bound) = a { + // Free bound region + if debruijn == self.binder_index { + self.unify_free_answer_var(bound.assert_bound_var(), b.into())?; + return Ok(b); + } + } + + match (a, b) { + (&ty::ReLateBound(a_debruijn, a_bound), &ty::ReLateBound(b_debruijn, b_bound)) => { + assert_eq!(a_debruijn, b_debruijn); + assert_eq!(a_bound.assert_bound_var(), b_bound.assert_bound_var()); + } + + (ty::ReStatic, ty::ReStatic) | + (ty::ReErased, ty::ReErased) | + (ty::ReEmpty, ty::ReEmpty) => (), + + (&ty::ReFree(a_free), &ty::ReFree(b_free)) => { + assert_eq!(a_free, b_free); + } + + _ => bug!("unexpected regions in `AnswerSubstitutor`: {:?}, {:?}", a, b), + } + + Ok(a) + } +} From b2b82f790b0342fa7eb4e3b81bf1ae00681b4543 Mon Sep 17 00:00:00 2001 From: scalexm Date: Thu, 15 Nov 2018 20:05:36 +0100 Subject: [PATCH 17/36] Implement `AggregateOps` `make_solution` does not return any guidance for now --- src/librustc_traits/chalk_context/mod.rs | 30 ++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/src/librustc_traits/chalk_context/mod.rs b/src/librustc_traits/chalk_context/mod.rs index bc3f2fd951b93..58a8d2abd9962 100644 --- a/src/librustc_traits/chalk_context/mod.rs +++ b/src/librustc_traits/chalk_context/mod.rs @@ -26,6 +26,7 @@ use rustc::infer::canonical::{ CanonicalVarValues, OriginalQueryValues, QueryResponse, + Certainty, }; use rustc::traits::{ DomainGoal, @@ -132,9 +133,34 @@ impl context::AggregateOps> for ChalkContext<'cx, 'gcx> { fn make_solution( &self, _root_goal: &Canonical<'gcx, InEnvironment<'gcx, Goal<'gcx>>>, - _simplified_answers: impl context::AnswerStream>, + mut simplified_answers: impl context::AnswerStream>, ) -> Option>> { - unimplemented!() + use chalk_engine::SimplifiedAnswer; + + if simplified_answers.peek_answer().is_none() { + return None; + } + + let SimplifiedAnswer { subst, ambiguous } = simplified_answers + .next_answer() + .unwrap(); + + let ambiguous = simplified_answers.peek_answer().is_some() || ambiguous; + + Some(subst.unchecked_map(|subst| { + QueryResponse { + var_values: subst.subst, + region_constraints: subst.constraints + .into_iter() + .map(|c| ty::Binder::bind(c)) + .collect(), + certainty: match ambiguous { + true => Certainty::Ambiguous, + false => Certainty::Proven, + }, + value: (), + } + })) } } From c805e817a93a641ef92e58900bc5b0876bbf51f1 Mon Sep 17 00:00:00 2001 From: scalexm Date: Thu, 29 Nov 2018 20:29:50 +0100 Subject: [PATCH 18/36] Fix doc comments --- src/librustc/infer/nll_relate/mod.rs | 10 +++++----- .../borrow_check/nll/type_check/relate_tys.rs | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/librustc/infer/nll_relate/mod.rs b/src/librustc/infer/nll_relate/mod.rs index ff79c38680f25..ce5aee81ef9d7 100644 --- a/src/librustc/infer/nll_relate/mod.rs +++ b/src/librustc/infer/nll_relate/mod.rs @@ -16,15 +16,15 @@ //! Here are the key differences: //! //! - This code may choose to bypass some checks (e.g. the occurs check) -//! in case we know that there are no unbound type inference variables. -//! This is the case for NLL, because at NLL time types are fully inferred -//! up-to regions. +//! in the case where we know that there are no unbound type inference +//! variables. This is the case for NLL, because at NLL time types are fully +//! inferred up-to regions. //! - This code uses "universes" to handle higher-ranked regions and //! not the leak-check. This is "more correct" than what rustc does //! and we are generally migrating in this direction, but NLL had to //! get there first. //! -//! Also, this code assumes that there are no bound type vars at all, not even +//! Also, this code assumes that there are no bound types at all, not even //! free ones. This is ok because: //! - we are not relating anything quantified over some type variable //! - we will have instantiated all the bound type vars already (the one @@ -265,7 +265,7 @@ where } /// Relate a projection type and some value type lazily. This will always - /// succeed, but we are pushing an additional `ProjectionEq` goal depending + /// succeed, but we push an additional `ProjectionEq` goal depending /// on the value type: /// - if the value type is any type `T` which is not a projection, we push /// `ProjectionEq(projection = T)`. 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 9924b03387132..225e2841fb0ac 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 @@ -118,7 +118,7 @@ impl TypeRelatingDelegate<'tcx> for NllTypeRelatingDelegate<'_, '_, '_, 'tcx> { } fn push_domain_goal(&mut self, _: DomainGoal<'tcx>) { - // No-op + bug!("should never be invoked with eager normalization") } fn normalization() -> NormalizationStrategy { From 1fce415649784ecaf3bcb3a6916e10284c0affec Mon Sep 17 00:00:00 2001 From: scalexm Date: Thu, 29 Nov 2018 20:50:16 +0100 Subject: [PATCH 19/36] Correctly generalize inference variables in `nll_relate` --- src/librustc/infer/nll_relate/mod.rs | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/src/librustc/infer/nll_relate/mod.rs b/src/librustc/infer/nll_relate/mod.rs index ce5aee81ef9d7..972ba16f7e2c7 100644 --- a/src/librustc/infer/nll_relate/mod.rs +++ b/src/librustc/infer/nll_relate/mod.rs @@ -762,23 +762,18 @@ where drop(variables); self.relate(&u, &u) } - TypeVariableValue::Unknown { universe } => { - if self.universe.cannot_name(universe) { - debug!( - "TypeGeneralizer::tys: root universe {:?} cannot name\ - variable in universe {:?}", - self.universe, - universe - ); - return Err(TypeError::Mismatch); - } - + TypeVariableValue::Unknown { universe: _universe } => { if self.ambient_variance == ty::Bivariant { // FIXME: we may need a WF predicate (related to #54105). } let origin = *variables.var_origin(vid); + + // Replacing with a new variable in the universe `self.universe`, + // it will be unified later with the original type variable in + // the universe `_universe`. let new_var_id = variables.new_var(self.universe, false, origin); + let u = self.tcx().mk_var(new_var_id); debug!( "generalize: replacing original vid={:?} with new={:?}", From 4cce4ffdefd23b4113ca2f54128b88b70fd31dde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20K=C3=A5re=20Alsaker?= Date: Thu, 29 Nov 2018 21:13:04 +0100 Subject: [PATCH 20/36] Add inline attributes and add unit to CommonTypes --- src/libcore/num/mod.rs | 1 + src/librustc/infer/at.rs | 1 + src/librustc/infer/resolve.rs | 1 + src/librustc/traits/mod.rs | 1 + src/librustc/ty/context.rs | 39 ++++++++++++++++++++++++++++++++++- src/librustc/ty/fold.rs | 1 + src/librustc/ty/mod.rs | 5 +++++ src/librustc/ty/sty.rs | 8 +++++++ src/libsyntax_pos/lib.rs | 1 + 9 files changed, 57 insertions(+), 1 deletion(-) diff --git a/src/libcore/num/mod.rs b/src/libcore/num/mod.rs index 57b5903c9d39f..c87dcbe500cba 100644 --- a/src/libcore/num/mod.rs +++ b/src/libcore/num/mod.rs @@ -3614,6 +3614,7 @@ assert_eq!(3", stringify!($SelfT), ".checked_next_power_of_two(), Some(4)); assert_eq!(", stringify!($SelfT), "::max_value().checked_next_power_of_two(), None);", $EndFeature, " ```"), + #[inline] #[stable(feature = "rust1", since = "1.0.0")] pub fn checked_next_power_of_two(self) -> Option { self.one_less_than_next_power_of_two().checked_add(1) diff --git a/src/librustc/infer/at.rs b/src/librustc/infer/at.rs index 0e4c94aaaf394..70e922c6676e1 100644 --- a/src/librustc/infer/at.rs +++ b/src/librustc/infer/at.rs @@ -52,6 +52,7 @@ pub struct Trace<'a, 'gcx: 'tcx, 'tcx: 'a> { } impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { + #[inline] pub fn at(&'a self, cause: &'a ObligationCause<'tcx>, param_env: ty::ParamEnv<'tcx>) diff --git a/src/librustc/infer/resolve.rs b/src/librustc/infer/resolve.rs index a0c310ac2761e..7a1ee85acd42c 100644 --- a/src/librustc/infer/resolve.rs +++ b/src/librustc/infer/resolve.rs @@ -25,6 +25,7 @@ pub struct OpportunisticTypeResolver<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { } impl<'a, 'gcx, 'tcx> OpportunisticTypeResolver<'a, 'gcx, 'tcx> { + #[inline] pub fn new(infcx: &'a InferCtxt<'a, 'gcx, 'tcx>) -> Self { OpportunisticTypeResolver { infcx } } diff --git a/src/librustc/traits/mod.rs b/src/librustc/traits/mod.rs index e582a9020464d..ab2fa68ab5f89 100644 --- a/src/librustc/traits/mod.rs +++ b/src/librustc/traits/mod.rs @@ -1052,6 +1052,7 @@ impl<'tcx,O> Obligation<'tcx,O> { } impl<'tcx> ObligationCause<'tcx> { + #[inline] pub fn new(span: Span, body_id: ast::NodeId, code: ObligationCauseCode<'tcx>) diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs index 79a4638d6f7d9..ebcf83cb8ecad 100644 --- a/src/librustc/ty/context.rs +++ b/src/librustc/ty/context.rs @@ -155,6 +155,7 @@ impl<'gcx: 'tcx, 'tcx> CtxtInterners<'tcx> { } /// Intern a type + #[inline(never)] fn intern_ty( local: &CtxtInterners<'tcx>, global: &CtxtInterners<'gcx>, @@ -216,6 +217,7 @@ impl<'gcx: 'tcx, 'tcx> CtxtInterners<'tcx> { } pub struct CommonTypes<'tcx> { + pub unit: Ty<'tcx>, pub bool: Ty<'tcx>, pub char: Ty<'tcx>, pub isize: Ty<'tcx>, @@ -832,7 +834,9 @@ impl<'tcx> CommonTypes<'tcx> { interners.region.borrow_mut().insert(Interned(r)); &*r }; + CommonTypes { + unit: mk(Tuple(List::empty())), bool: mk(Bool), char: mk(Char), never: mk(Never), @@ -885,6 +889,7 @@ pub struct TyCtxt<'a, 'gcx: 'tcx, 'tcx: 'a> { impl<'a, 'gcx, 'tcx> Deref for TyCtxt<'a, 'gcx, 'tcx> { type Target = &'a GlobalCtxt<'gcx>; + #[inline(always)] fn deref(&self) -> &Self::Target { &self.gcx } @@ -2515,6 +2520,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { self.mk_fn_ptr(converted_sig) } + #[inline] pub fn mk_ty(&self, st: TyKind<'tcx>) -> Ty<'tcx> { CtxtInterners::intern_ty(&self.interners, &self.global_interners, st) } @@ -2548,19 +2554,23 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { } } + #[inline] pub fn mk_str(self) -> Ty<'tcx> { self.mk_ty(Str) } + #[inline] pub fn mk_static_str(self) -> Ty<'tcx> { self.mk_imm_ref(self.types.re_static, self.mk_str()) } + #[inline] pub fn mk_adt(self, def: &'tcx AdtDef, substs: &'tcx Substs<'tcx>) -> Ty<'tcx> { // take a copy of substs so that we own the vectors inside self.mk_ty(Adt(def, substs)) } + #[inline] pub fn mk_foreign(self, def_id: DefId) -> Ty<'tcx> { self.mk_ty(Foreign(def_id)) } @@ -2584,42 +2594,52 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { self.mk_ty(Adt(adt_def, substs)) } + #[inline] pub fn mk_ptr(self, tm: TypeAndMut<'tcx>) -> Ty<'tcx> { self.mk_ty(RawPtr(tm)) } + #[inline] pub fn mk_ref(self, r: Region<'tcx>, tm: TypeAndMut<'tcx>) -> Ty<'tcx> { self.mk_ty(Ref(r, tm.ty, tm.mutbl)) } + #[inline] pub fn mk_mut_ref(self, r: Region<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> { self.mk_ref(r, TypeAndMut {ty: ty, mutbl: hir::MutMutable}) } + #[inline] pub fn mk_imm_ref(self, r: Region<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> { self.mk_ref(r, TypeAndMut {ty: ty, mutbl: hir::MutImmutable}) } + #[inline] pub fn mk_mut_ptr(self, ty: Ty<'tcx>) -> Ty<'tcx> { self.mk_ptr(TypeAndMut {ty: ty, mutbl: hir::MutMutable}) } + #[inline] pub fn mk_imm_ptr(self, ty: Ty<'tcx>) -> Ty<'tcx> { self.mk_ptr(TypeAndMut {ty: ty, mutbl: hir::MutImmutable}) } + #[inline] pub fn mk_nil_ptr(self) -> Ty<'tcx> { self.mk_imm_ptr(self.mk_unit()) } + #[inline] pub fn mk_array(self, ty: Ty<'tcx>, n: u64) -> Ty<'tcx> { self.mk_ty(Array(ty, ty::Const::from_usize(self, n))) } + #[inline] pub fn mk_slice(self, ty: Ty<'tcx>) -> Ty<'tcx> { self.mk_ty(Slice(ty)) } + #[inline] pub fn intern_tup(self, ts: &[Ty<'tcx>]) -> Ty<'tcx> { self.mk_ty(Tuple(self.intern_type_list(ts))) } @@ -2628,10 +2648,12 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { iter.intern_with(|ts| self.mk_ty(Tuple(self.intern_type_list(ts)))) } + #[inline] pub fn mk_unit(self) -> Ty<'tcx> { - self.intern_tup(&[]) + self.types.unit } + #[inline] pub fn mk_diverging_default(self) -> Ty<'tcx> { if self.features().never_type { self.types.never @@ -2640,19 +2662,23 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { } } + #[inline] pub fn mk_bool(self) -> Ty<'tcx> { self.mk_ty(Bool) } + #[inline] pub fn mk_fn_def(self, def_id: DefId, substs: &'tcx Substs<'tcx>) -> Ty<'tcx> { self.mk_ty(FnDef(def_id, substs)) } + #[inline] pub fn mk_fn_ptr(self, fty: PolyFnSig<'tcx>) -> Ty<'tcx> { self.mk_ty(FnPtr(fty)) } + #[inline] pub fn mk_dynamic( self, obj: ty::Binder<&'tcx List>>, @@ -2661,6 +2687,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { self.mk_ty(Dynamic(obj, reg)) } + #[inline] pub fn mk_projection(self, item_def_id: DefId, substs: &'tcx Substs<'tcx>) @@ -2671,11 +2698,13 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { })) } + #[inline] pub fn mk_closure(self, closure_id: DefId, closure_substs: ClosureSubsts<'tcx>) -> Ty<'tcx> { self.mk_ty(Closure(closure_id, closure_substs)) } + #[inline] pub fn mk_generator(self, id: DefId, generator_substs: GeneratorSubsts<'tcx>, @@ -2684,32 +2713,39 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { self.mk_ty(Generator(id, generator_substs, movability)) } + #[inline] pub fn mk_generator_witness(self, types: ty::Binder<&'tcx List>>) -> Ty<'tcx> { self.mk_ty(GeneratorWitness(types)) } + #[inline] pub fn mk_var(self, v: TyVid) -> Ty<'tcx> { self.mk_infer(TyVar(v)) } + #[inline] pub fn mk_int_var(self, v: IntVid) -> Ty<'tcx> { self.mk_infer(IntVar(v)) } + #[inline] pub fn mk_float_var(self, v: FloatVid) -> Ty<'tcx> { self.mk_infer(FloatVar(v)) } + #[inline] pub fn mk_infer(self, it: InferTy) -> Ty<'tcx> { self.mk_ty(Infer(it)) } + #[inline] pub fn mk_ty_param(self, index: u32, name: InternedString) -> Ty<'tcx> { self.mk_ty(Param(ParamTy { idx: index, name: name })) } + #[inline] pub fn mk_self_type(self) -> Ty<'tcx> { self.mk_ty_param(0, keywords::SelfType.name().as_interned_str()) } @@ -2723,6 +2759,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { } } + #[inline] pub fn mk_opaque(self, def_id: DefId, substs: &'tcx Substs<'tcx>) -> Ty<'tcx> { self.mk_ty(Opaque(def_id, substs)) } diff --git a/src/librustc/ty/fold.rs b/src/librustc/ty/fold.rs index 6f0e8d4f02680..1bc7d64879267 100644 --- a/src/librustc/ty/fold.rs +++ b/src/librustc/ty/fold.rs @@ -374,6 +374,7 @@ pub struct RegionFolder<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { } impl<'a, 'gcx, 'tcx> RegionFolder<'a, 'gcx, 'tcx> { + #[inline] pub fn new( tcx: TyCtxt<'a, 'gcx, 'tcx>, skipped_regions: &'a mut bool, diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index b371f4532e5fa..4633ab1166347 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -1275,6 +1275,7 @@ impl<'tcx> PolyProjectionPredicate<'tcx> { self.skip_binder().projection_ty.item_def_id } + #[inline] pub fn to_poly_trait_ref(&self, tcx: TyCtxt<'_, '_, '_>) -> PolyTraitRef<'tcx> { // Note: unlike with `TraitRef::to_poly_trait_ref()`, // `self.0.trait_ref` is permitted to have escaping regions. @@ -1633,6 +1634,7 @@ impl<'tcx> ParamEnv<'tcx> { /// there are no where clauses in scope. Hidden types (like `impl /// Trait`) are left hidden, so this is suitable for ordinary /// type-checking. + #[inline] pub fn empty() -> Self { Self::new(List::empty(), Reveal::UserFacing) } @@ -1644,11 +1646,13 @@ impl<'tcx> ParamEnv<'tcx> { /// /// N.B. If you want to have predicates in scope, use `ParamEnv::new`, /// or invoke `param_env.with_reveal_all()`. + #[inline] pub fn reveal_all() -> Self { Self::new(List::empty(), Reveal::All) } /// Construct a trait environment with the given set of predicates. + #[inline] pub fn new(caller_bounds: &'tcx List>, reveal: Reveal) -> Self { @@ -2148,6 +2152,7 @@ impl<'a, 'gcx, 'tcx> AdtDef { } } + #[inline] pub fn variant_descr(&self) -> &'static str { match self.adt_kind() { AdtKind::Struct => "struct", diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs index a18e3a275467d..1416cb17feaed 100644 --- a/src/librustc/ty/sty.rs +++ b/src/librustc/ty/sty.rs @@ -667,6 +667,7 @@ impl<'tcx> TraitRef<'tcx> { } } + #[inline] pub fn self_ty(&self) -> Ty<'tcx> { self.substs.type_at(0) } @@ -978,15 +979,18 @@ impl<'tcx> FnSig<'tcx> { pub type PolyFnSig<'tcx> = Binder>; impl<'tcx> PolyFnSig<'tcx> { + #[inline] pub fn inputs(&self) -> Binder<&'tcx [Ty<'tcx>]> { self.map_bound_ref(|fn_sig| fn_sig.inputs()) } + #[inline] pub fn input(&self, index: usize) -> ty::Binder> { self.map_bound_ref(|fn_sig| fn_sig.inputs()[index]) } pub fn inputs_and_output(&self) -> ty::Binder<&'tcx List>> { self.map_bound_ref(|fn_sig| fn_sig.inputs_and_output) } + #[inline] pub fn output(&self) -> ty::Binder> { self.map_bound_ref(|fn_sig| fn_sig.output()) } @@ -1548,6 +1552,7 @@ impl<'a, 'gcx, 'tcx> TyS<'tcx> { } } + #[inline] pub fn is_ty_var(&self) -> bool { match self.sty { Infer(TyVar(_)) => true, @@ -1732,6 +1737,7 @@ impl<'a, 'gcx, 'tcx> TyS<'tcx> { } } + #[inline] pub fn is_integral(&self) -> bool { match self.sty { Infer(IntVar(_)) | Int(_) | Uint(_) => true, @@ -1762,6 +1768,7 @@ impl<'a, 'gcx, 'tcx> TyS<'tcx> { } } + #[inline] pub fn is_fp(&self) -> bool { match self.sty { Infer(FloatVar(_)) | Float(_) => true, @@ -1845,6 +1852,7 @@ impl<'a, 'gcx, 'tcx> TyS<'tcx> { } } + #[inline] pub fn ty_adt_def(&self) -> Option<&'tcx AdtDef> { match self.sty { Adt(adt, _) => Some(adt), diff --git a/src/libsyntax_pos/lib.rs b/src/libsyntax_pos/lib.rs index 65f6d27239b83..34fb71e4ddf68 100644 --- a/src/libsyntax_pos/lib.rs +++ b/src/libsyntax_pos/lib.rs @@ -587,6 +587,7 @@ impl fmt::Debug for SpanData { } impl MultiSpan { + #[inline] pub fn new() -> MultiSpan { MultiSpan { primary_spans: vec![], From fdef3848a01c4567abd326043b797f6bdc3f3e76 Mon Sep 17 00:00:00 2001 From: Vitaly _Vi Shukela Date: Fri, 30 Nov 2018 00:06:10 +0300 Subject: [PATCH 21/36] Add libstd and libcore Cargo features "panic_immediate_abort" It stop asserts and panics from libstd to automatically include string output and formatting code. Use case: developing static executables smaller than 50 kilobytes, where usual formatting code is excessive while keeping debuggability in debug mode. May resolve #54981. --- src/libcore/Cargo.toml | 4 ++++ src/libcore/panicking.rs | 24 +++++++++++++++++++++--- src/libstd/Cargo.toml | 3 +++ src/libstd/panicking.rs | 17 +++++++++++++++-- 4 files changed, 43 insertions(+), 5 deletions(-) diff --git a/src/libcore/Cargo.toml b/src/libcore/Cargo.toml index 0b01cfc488bba..7fd61f07d5e7f 100644 --- a/src/libcore/Cargo.toml +++ b/src/libcore/Cargo.toml @@ -21,3 +21,7 @@ path = "../libcore/benches/lib.rs" [dev-dependencies] rand = "0.5" + +[features] +# Make panics and failed asserts immediately abort without formatting any message +panic_immediate_abort = [] diff --git a/src/libcore/panicking.rs b/src/libcore/panicking.rs index 58407de9566e9..67c0b6ada9055 100644 --- a/src/libcore/panicking.rs +++ b/src/libcore/panicking.rs @@ -39,9 +39,15 @@ use fmt; use panic::{Location, PanicInfo}; -#[cold] #[inline(never)] // this is the slow path, always +#[cold] +// inline(never) is required even in panic_immediate_abort mode, lest linker error +#[inline(never)] #[lang = "panic"] pub fn panic(expr_file_line_col: &(&'static str, &'static str, u32, u32)) -> ! { + if cfg!(feature = "panic_immediate_abort") { + unsafe { super::intrinsics::abort() } + }; + // Use Arguments::new_v1 instead of format_args!("{}", expr) to potentially // reduce size overhead. The format_args! macro uses str's Display trait to // write expr, which calls Formatter::pad, which must accommodate string @@ -52,16 +58,28 @@ pub fn panic(expr_file_line_col: &(&'static str, &'static str, u32, u32)) -> ! { panic_fmt(fmt::Arguments::new_v1(&[expr], &[]), &(file, line, col)) } -#[cold] #[inline(never)] +#[cold] +// inline(never) is required even in panic_immediate_abort mode, lest linker error +#[inline(never)] #[lang = "panic_bounds_check"] fn panic_bounds_check(file_line_col: &(&'static str, u32, u32), index: usize, len: usize) -> ! { + if cfg!(feature = "panic_immediate_abort") { + unsafe { super::intrinsics::abort() } + }; + panic_fmt(format_args!("index out of bounds: the len is {} but the index is {}", len, index), file_line_col) } -#[cold] #[inline(never)] +#[cold] +#[cfg_attr(not(feature="panic_immediate_abort"),inline(never))] +#[cfg_attr( feature="panic_immediate_abort" ,inline)] pub fn panic_fmt(fmt: fmt::Arguments, file_line_col: &(&'static str, u32, u32)) -> ! { + if cfg!(feature = "panic_immediate_abort") { + unsafe { super::intrinsics::abort() } + }; + // NOTE This function never crosses the FFI boundary; it's a Rust-to-Rust call #[allow(improper_ctypes)] // PanicInfo contains a trait object which is not FFI safe extern "Rust" { diff --git a/src/libstd/Cargo.toml b/src/libstd/Cargo.toml index 2b1d515c83b75..c1446218367e4 100644 --- a/src/libstd/Cargo.toml +++ b/src/libstd/Cargo.toml @@ -47,6 +47,9 @@ backtrace = [] panic-unwind = ["panic_unwind"] profiler = ["profiler_builtins"] +# Make panics and failed asserts immediately abort without formatting any message +panic_immediate_abort = ["core/panic_immediate_abort"] + # An off-by-default feature which enables a linux-syscall-like ABI for libstd to # interoperate with the host environment. Currently not well documented and # requires rebuilding the standard library to use it. diff --git a/src/libstd/panicking.rs b/src/libstd/panicking.rs index a0590a800d3cc..29a0b3feefdfa 100644 --- a/src/libstd/panicking.rs +++ b/src/libstd/panicking.rs @@ -334,9 +334,15 @@ pub fn rust_begin_panic(info: &PanicInfo) -> ! { #[unstable(feature = "libstd_sys_internals", reason = "used by the panic! macro", issue = "0")] -#[inline(never)] #[cold] +#[cold] +#[cfg_attr(not(feature="panic_immediate_abort"),inline(never))] +#[cfg_attr( feature="panic_immediate_abort" ,inline)] pub fn begin_panic_fmt(msg: &fmt::Arguments, file_line_col: &(&'static str, u32, u32)) -> ! { + if cfg!(feature = "panic_immediate_abort") { + unsafe { intrinsics::abort() } + }; + let (file, line, col) = *file_line_col; let info = PanicInfo::internal_constructor( Some(msg), @@ -398,8 +404,15 @@ fn continue_panic_fmt(info: &PanicInfo) -> ! { reason = "used by the panic! macro", issue = "0")] #[cfg_attr(not(test), lang = "begin_panic")] -#[inline(never)] #[cold] // avoid code bloat at the call sites as much as possible +// avoid code bloat at the call sites as much as possible +// inline(never) is required even in panic_immediate_abort mode, lest linker error +#[inline(never)] +#[cold] pub fn begin_panic(msg: M, file_line_col: &(&'static str, u32, u32)) -> ! { + if cfg!(feature = "panic_immediate_abort") { + unsafe { intrinsics::abort() } + }; + // Note that this should be the only allocation performed in this code path. // Currently this means that panic!() on OOM will invoke this code path, // but then again we're not really ready for panic on OOM anyway. If From 225140ed216d7395530b2e4597fb224305e6375b Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 29 Nov 2018 15:02:06 -0800 Subject: [PATCH 22/36] Optimize local linkchecker program I noticed on a [recent build][1] that the linkchecker stage of CI took a whopping 15 minutes of CI time for something that should be near instantaneous. Some local profiling showed some very hot functions and clones which were pretty easy to remove, and now instead of running in minutes locally it runs in seconds. [1]: https://ci.appveyor.com/project/rust-lang/rust/build/job/kptifw1kb1nm4xuu --- src/tools/linkchecker/main.rs | 38 +++++++++++++++++------------------ 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/src/tools/linkchecker/main.rs b/src/tools/linkchecker/main.rs index ca7e0224dc328..11c83819eaa93 100644 --- a/src/tools/linkchecker/main.rs +++ b/src/tools/linkchecker/main.rs @@ -24,12 +24,12 @@ //! A few whitelisted exceptions are allowed as there's known bugs in rustdoc, //! but this should catch the majority of "broken link" cases. +use std::collections::hash_map::Entry; +use std::collections::{HashMap, HashSet}; use std::env; -use std::fs::File; -use std::io::prelude::*; +use std::fs; use std::path::{Path, PathBuf, Component}; -use std::collections::{HashMap, HashSet}; -use std::collections::hash_map::Entry; +use std::rc::Rc; use Redirect::*; @@ -63,7 +63,7 @@ enum Redirect { } struct FileEntry { - source: String, + source: Rc, ids: HashSet, } @@ -113,7 +113,7 @@ fn walk(cache: &mut Cache, root: &Path, dir: &Path, errors: &mut bool) { let entry = cache.get_mut(&pretty_path).unwrap(); // we don't need the source anymore, // so drop to reduce memory-usage - entry.source = String::new(); + entry.source = Rc::new(String::new()); } } } @@ -287,24 +287,24 @@ fn load_file(cache: &mut Cache, root: &Path, file: &Path, redirect: Redirect) - -> Result<(PathBuf, String), LoadError> { - let mut contents = String::new(); + -> Result<(PathBuf, Rc), LoadError> { let pretty_file = PathBuf::from(file.strip_prefix(root).unwrap_or(&file)); - let maybe_redirect = match cache.entry(pretty_file.clone()) { + let (maybe_redirect, contents) = match cache.entry(pretty_file.clone()) { Entry::Occupied(entry) => { - contents = entry.get().source.clone(); - None + (None, entry.get().source.clone()) } Entry::Vacant(entry) => { - let mut fp = File::open(file).map_err(|err| { - if let FromRedirect(true) = redirect { - LoadError::BrokenRedirect(file.to_path_buf(), err) - } else { - LoadError::IOError(err) + let contents = match fs::read_to_string(file) { + Ok(s) => Rc::new(s), + Err(err) => { + return Err(if let FromRedirect(true) = redirect { + LoadError::BrokenRedirect(file.to_path_buf(), err) + } else { + LoadError::IOError(err) + }) } - })?; - fp.read_to_string(&mut contents).map_err(|err| LoadError::IOError(err))?; + }; let maybe = maybe_redirect(&contents); if maybe.is_some() { @@ -317,7 +317,7 @@ fn load_file(cache: &mut Cache, ids: HashSet::new(), }); } - maybe + (maybe, contents) } }; match maybe_redirect.map(|url| file.parent().unwrap().join(url)) { From d3f9788e596f9b7f07c165e0f382dbe2cdb02b6e Mon Sep 17 00:00:00 2001 From: Vitaly _Vi Shukela Date: Fri, 30 Nov 2018 02:17:05 +0300 Subject: [PATCH 23/36] panic_immediate_abort: Fix issues from review --- src/libcore/panicking.rs | 13 ++++++------- src/libstd/panicking.rs | 11 ++++++----- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/libcore/panicking.rs b/src/libcore/panicking.rs index 67c0b6ada9055..834fcd246c532 100644 --- a/src/libcore/panicking.rs +++ b/src/libcore/panicking.rs @@ -40,13 +40,13 @@ use fmt; use panic::{Location, PanicInfo}; #[cold] -// inline(never) is required even in panic_immediate_abort mode, lest linker error -#[inline(never)] +// never inline unless panic_immediate_abort to avoid code bloat at the call sites as much as possible +#[cfg_attr(not(feature="panic_immediate_abort"),inline(never))] #[lang = "panic"] pub fn panic(expr_file_line_col: &(&'static str, &'static str, u32, u32)) -> ! { if cfg!(feature = "panic_immediate_abort") { unsafe { super::intrinsics::abort() } - }; + } // Use Arguments::new_v1 instead of format_args!("{}", expr) to potentially // reduce size overhead. The format_args! macro uses str's Display trait to @@ -59,14 +59,13 @@ pub fn panic(expr_file_line_col: &(&'static str, &'static str, u32, u32)) -> ! { } #[cold] -// inline(never) is required even in panic_immediate_abort mode, lest linker error -#[inline(never)] +#[cfg_attr(not(feature="panic_immediate_abort"),inline(never))] #[lang = "panic_bounds_check"] fn panic_bounds_check(file_line_col: &(&'static str, u32, u32), index: usize, len: usize) -> ! { if cfg!(feature = "panic_immediate_abort") { unsafe { super::intrinsics::abort() } - }; + } panic_fmt(format_args!("index out of bounds: the len is {} but the index is {}", len, index), file_line_col) @@ -78,7 +77,7 @@ fn panic_bounds_check(file_line_col: &(&'static str, u32, u32), pub fn panic_fmt(fmt: fmt::Arguments, file_line_col: &(&'static str, u32, u32)) -> ! { if cfg!(feature = "panic_immediate_abort") { unsafe { super::intrinsics::abort() } - }; + } // NOTE This function never crosses the FFI boundary; it's a Rust-to-Rust call #[allow(improper_ctypes)] // PanicInfo contains a trait object which is not FFI safe diff --git a/src/libstd/panicking.rs b/src/libstd/panicking.rs index 29a0b3feefdfa..82ceec62f357a 100644 --- a/src/libstd/panicking.rs +++ b/src/libstd/panicking.rs @@ -335,13 +335,15 @@ pub fn rust_begin_panic(info: &PanicInfo) -> ! { reason = "used by the panic! macro", issue = "0")] #[cold] +// If panic_immediate_abort, inline the abort call, +// otherwise avoid inlining because of it is cold path. #[cfg_attr(not(feature="panic_immediate_abort"),inline(never))] #[cfg_attr( feature="panic_immediate_abort" ,inline)] pub fn begin_panic_fmt(msg: &fmt::Arguments, file_line_col: &(&'static str, u32, u32)) -> ! { if cfg!(feature = "panic_immediate_abort") { unsafe { intrinsics::abort() } - }; + } let (file, line, col) = *file_line_col; let info = PanicInfo::internal_constructor( @@ -404,14 +406,13 @@ fn continue_panic_fmt(info: &PanicInfo) -> ! { reason = "used by the panic! macro", issue = "0")] #[cfg_attr(not(test), lang = "begin_panic")] -// avoid code bloat at the call sites as much as possible -// inline(never) is required even in panic_immediate_abort mode, lest linker error -#[inline(never)] +// never inline unless panic_immediate_abort to avoid code bloat at the call sites as much as possible +#[cfg_attr(not(feature="panic_immediate_abort"),inline(never))] #[cold] pub fn begin_panic(msg: M, file_line_col: &(&'static str, u32, u32)) -> ! { if cfg!(feature = "panic_immediate_abort") { unsafe { intrinsics::abort() } - }; + } // Note that this should be the only allocation performed in this code path. // Currently this means that panic!() on OOM will invoke this code path, From f18a8c6163fa3b1eef5aabd1baf1eef2b9789c46 Mon Sep 17 00:00:00 2001 From: Vitaly _Vi Shukela Date: Fri, 30 Nov 2018 02:37:04 +0300 Subject: [PATCH 24/36] Fix exceeding line width limit --- src/libcore/panicking.rs | 3 ++- src/libstd/panicking.rs | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/libcore/panicking.rs b/src/libcore/panicking.rs index 834fcd246c532..aa18a60fc0f6d 100644 --- a/src/libcore/panicking.rs +++ b/src/libcore/panicking.rs @@ -40,7 +40,8 @@ use fmt; use panic::{Location, PanicInfo}; #[cold] -// never inline unless panic_immediate_abort to avoid code bloat at the call sites as much as possible +// never inline unless panic_immediate_abort to avoid code +// bloat at the call sites as much as possible #[cfg_attr(not(feature="panic_immediate_abort"),inline(never))] #[lang = "panic"] pub fn panic(expr_file_line_col: &(&'static str, &'static str, u32, u32)) -> ! { diff --git a/src/libstd/panicking.rs b/src/libstd/panicking.rs index 82ceec62f357a..4930d35660814 100644 --- a/src/libstd/panicking.rs +++ b/src/libstd/panicking.rs @@ -406,7 +406,8 @@ fn continue_panic_fmt(info: &PanicInfo) -> ! { reason = "used by the panic! macro", issue = "0")] #[cfg_attr(not(test), lang = "begin_panic")] -// never inline unless panic_immediate_abort to avoid code bloat at the call sites as much as possible +// never inline unless panic_immediate_abort to avoid code +// bloat at the call sites as much as possible #[cfg_attr(not(feature="panic_immediate_abort"),inline(never))] #[cold] pub fn begin_panic(msg: M, file_line_col: &(&'static str, u32, u32)) -> ! { From a0fec9488ed6f1d7b751f7e45b9919f908b560a1 Mon Sep 17 00:00:00 2001 From: Dan Robertson Date: Fri, 30 Nov 2018 02:04:10 +0000 Subject: [PATCH 25/36] Fix panic with outlives in existential type Don't panic in determining the privacy of a type if a lifetime outlives generic exists in an existential type. --- src/librustc_privacy/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustc_privacy/lib.rs b/src/librustc_privacy/lib.rs index fbd6f6edd31c5..6d9abbf5af2ac 100644 --- a/src/librustc_privacy/lib.rs +++ b/src/librustc_privacy/lib.rs @@ -969,7 +969,7 @@ impl<'a, 'tcx> TypeVisitor<'tcx> for TypePrivacyVisitor<'a, 'tcx> { Some(poly_projection_predicate.skip_binder() .projection_ty.trait_ref(self.tcx)) } - ty::Predicate::TypeOutlives(..) => None, + ty::Predicate::TypeOutlives(..) | ty::Predicate::RegionOutlives(..) => None, _ => bug!("unexpected predicate: {:?}", predicate), }; if let Some(trait_ref) = trait_ref { From c144dc07e35c1ef4a25bc321dce07565952121aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Sat, 24 Nov 2018 17:12:28 -0800 Subject: [PATCH 26/36] Fix ICE with feature self_struct_ctor --- src/librustc/middle/reachable.rs | 1 + src/test/ui/issues/issue-56202.rs | 15 +++++++++++++++ src/test/ui/issues/issue-56202.stderr | 7 +++++++ 3 files changed, 23 insertions(+) create mode 100644 src/test/ui/issues/issue-56202.rs create mode 100644 src/test/ui/issues/issue-56202.stderr diff --git a/src/librustc/middle/reachable.rs b/src/librustc/middle/reachable.rs index 0009a517dd1db..cf81729bdaba8 100644 --- a/src/librustc/middle/reachable.rs +++ b/src/librustc/middle/reachable.rs @@ -116,6 +116,7 @@ impl<'a, 'tcx> Visitor<'tcx> for ReachableContext<'a, 'tcx> { Some(Def::Local(node_id)) | Some(Def::Upvar(node_id, ..)) => { self.reachable_symbols.insert(node_id); } + Some(Def::Err) => {} // #56202: calling `def.def_id()` would be an error Some(def) => { let def_id = def.def_id(); if let Some(node_id) = self.tcx.hir.as_local_node_id(def_id) { diff --git a/src/test/ui/issues/issue-56202.rs b/src/test/ui/issues/issue-56202.rs new file mode 100644 index 0000000000000..c80c372c9e2ea --- /dev/null +++ b/src/test/ui/issues/issue-56202.rs @@ -0,0 +1,15 @@ +#![feature(self_struct_ctor)] + +trait FooTrait {} + +trait BarTrait { + fn foo(_: T) -> Self; +} + +struct FooStruct(u32); + +impl BarTrait for FooStruct { + fn foo(_: T) -> Self { + Self(u32::default()) + } +} diff --git a/src/test/ui/issues/issue-56202.stderr b/src/test/ui/issues/issue-56202.stderr new file mode 100644 index 0000000000000..3007b08450298 --- /dev/null +++ b/src/test/ui/issues/issue-56202.stderr @@ -0,0 +1,7 @@ +error[E0601]: `main` function not found in crate `issue_56202` + | + = note: consider adding a `main` function to `$DIR/issue-56202.rs` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0601`. From 027bf8b4de0176a526b7e5adaa63e65a87c37951 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Sun, 25 Nov 2018 07:40:26 -0800 Subject: [PATCH 27/36] Use opt_def_id instead of having special branch --- src/librustc/middle/reachable.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/librustc/middle/reachable.rs b/src/librustc/middle/reachable.rs index cf81729bdaba8..ab0094df0e219 100644 --- a/src/librustc/middle/reachable.rs +++ b/src/librustc/middle/reachable.rs @@ -116,10 +116,10 @@ impl<'a, 'tcx> Visitor<'tcx> for ReachableContext<'a, 'tcx> { Some(Def::Local(node_id)) | Some(Def::Upvar(node_id, ..)) => { self.reachable_symbols.insert(node_id); } - Some(Def::Err) => {} // #56202: calling `def.def_id()` would be an error Some(def) => { - let def_id = def.def_id(); - if let Some(node_id) = self.tcx.hir.as_local_node_id(def_id) { + if let Some((node_id, def_id)) = def.opt_def_id().and_then(|def_id| { + self.tcx.hir.as_local_node_id(def_id).map(|node_id| (node_id, def_id)) + }) { if self.def_id_represents_local_inlined_item(def_id) { self.worklist.push(node_id); } else { From 5f19cdc4f87be3bf6391264ef41264bdd3d0d370 Mon Sep 17 00:00:00 2001 From: Alexander Regueiro Date: Fri, 30 Nov 2018 02:55:22 +0000 Subject: [PATCH 28/36] Changed test for issue 56202 to compile-pass. --- src/test/ui/issues/issue-56202.rs | 4 ++++ src/test/ui/issues/issue-56202.stderr | 7 ------- 2 files changed, 4 insertions(+), 7 deletions(-) delete mode 100644 src/test/ui/issues/issue-56202.stderr diff --git a/src/test/ui/issues/issue-56202.rs b/src/test/ui/issues/issue-56202.rs index c80c372c9e2ea..4b25f58c02ac8 100644 --- a/src/test/ui/issues/issue-56202.rs +++ b/src/test/ui/issues/issue-56202.rs @@ -1,3 +1,5 @@ +// compile-pass + #![feature(self_struct_ctor)] trait FooTrait {} @@ -13,3 +15,5 @@ impl BarTrait for FooStruct { Self(u32::default()) } } + +fn main() {} diff --git a/src/test/ui/issues/issue-56202.stderr b/src/test/ui/issues/issue-56202.stderr deleted file mode 100644 index 3007b08450298..0000000000000 --- a/src/test/ui/issues/issue-56202.stderr +++ /dev/null @@ -1,7 +0,0 @@ -error[E0601]: `main` function not found in crate `issue_56202` - | - = note: consider adding a `main` function to `$DIR/issue-56202.rs` - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0601`. From 946ea1453d5a611084566c190cea60d9ccd41697 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20K=C3=A5re=20Alsaker?= Date: Thu, 29 Nov 2018 14:33:36 +0100 Subject: [PATCH 29/36] Inline things --- src/libarena/lib.rs | 2 ++ src/libcore/num/mod.rs | 1 + src/libstd/collections/hash/map.rs | 7 +++++++ src/libstd/collections/hash/table.rs | 4 ++++ 4 files changed, 14 insertions(+) diff --git a/src/libarena/lib.rs b/src/libarena/lib.rs index dae05a368fa06..955cab1d93f76 100644 --- a/src/libarena/lib.rs +++ b/src/libarena/lib.rs @@ -298,6 +298,7 @@ pub struct DroplessArena { unsafe impl Send for DroplessArena {} impl Default for DroplessArena { + #[inline] fn default() -> DroplessArena { DroplessArena { ptr: Cell::new(0 as *mut u8), @@ -319,6 +320,7 @@ impl DroplessArena { false } + #[inline] fn align(&self, align: usize) { let final_address = ((self.ptr.get() as usize) + align - 1) & !(align - 1); self.ptr.set(final_address as *mut u8); diff --git a/src/libcore/num/mod.rs b/src/libcore/num/mod.rs index 57b5903c9d39f..c87dcbe500cba 100644 --- a/src/libcore/num/mod.rs +++ b/src/libcore/num/mod.rs @@ -3614,6 +3614,7 @@ assert_eq!(3", stringify!($SelfT), ".checked_next_power_of_two(), Some(4)); assert_eq!(", stringify!($SelfT), "::max_value().checked_next_power_of_two(), None);", $EndFeature, " ```"), + #[inline] #[stable(feature = "rust1", since = "1.0.0")] pub fn checked_next_power_of_two(self) -> Option { self.one_less_than_next_power_of_two().checked_add(1) diff --git a/src/libstd/collections/hash/map.rs b/src/libstd/collections/hash/map.rs index 7c717d832fa54..349aa029ba8cb 100644 --- a/src/libstd/collections/hash/map.rs +++ b/src/libstd/collections/hash/map.rs @@ -849,6 +849,7 @@ impl HashMap /// let mut map: HashMap<&str, i32> = HashMap::new(); /// map.reserve(10); /// ``` + #[inline] #[stable(feature = "rust1", since = "1.0.0")] pub fn reserve(&mut self, additional: usize) { match self.reserve_internal(additional, Infallible) { @@ -880,6 +881,7 @@ impl HashMap self.reserve_internal(additional, Fallible) } + #[inline] fn reserve_internal(&mut self, additional: usize, fallibility: Fallibility) -> Result<(), CollectionAllocErr> { @@ -1571,6 +1573,7 @@ impl HashMap /// so that the map now contains keys which compare equal, search may start /// acting erratically, with two keys randomly masking each other. Implementations /// are free to assume this doesn't happen (within the limits of memory-safety). + #[inline(always)] #[unstable(feature = "hash_raw_entry", issue = "56167")] pub fn raw_entry_mut(&mut self) -> RawEntryBuilderMut { self.reserve(1); @@ -1911,6 +1914,7 @@ impl<'a, K, V, S> RawEntryBuilderMut<'a, K, V, S> } /// Create a `RawEntryMut` from the given key and its hash. + #[inline] #[unstable(feature = "hash_raw_entry", issue = "56167")] pub fn from_key_hashed_nocheck(self, hash: u64, k: &Q) -> RawEntryMut<'a, K, V, S> where K: Borrow, @@ -1919,6 +1923,7 @@ impl<'a, K, V, S> RawEntryBuilderMut<'a, K, V, S> self.from_hash(hash, |q| q.borrow().eq(k)) } + #[inline] fn search(self, hash: u64, is_match: F, compare_hashes: bool) -> RawEntryMut<'a, K, V, S> where for<'b> F: FnMut(&'b K) -> bool, { @@ -1941,6 +1946,7 @@ impl<'a, K, V, S> RawEntryBuilderMut<'a, K, V, S> } } /// Create a `RawEntryMut` from the given hash. + #[inline] #[unstable(feature = "hash_raw_entry", issue = "56167")] pub fn from_hash(self, hash: u64, is_match: F) -> RawEntryMut<'a, K, V, S> where for<'b> F: FnMut(&'b K) -> bool, @@ -2215,6 +2221,7 @@ impl<'a, K, V, S> RawVacantEntryMut<'a, K, V, S> { /// Sets the value of the entry with the VacantEntry's key, /// and returns a mutable reference to it. + #[inline] #[unstable(feature = "hash_raw_entry", issue = "56167")] pub fn insert_hashed_nocheck(self, hash: u64, key: K, value: V) -> (&'a mut K, &'a mut V) { let hash = SafeHash::new(hash); diff --git a/src/libstd/collections/hash/table.rs b/src/libstd/collections/hash/table.rs index 547f97cc8acee..479e6dccb90dd 100644 --- a/src/libstd/collections/hash/table.rs +++ b/src/libstd/collections/hash/table.rs @@ -329,6 +329,7 @@ impl Put for FullBucket } impl>> Bucket { + #[inline] pub fn new(table: M, hash: SafeHash) -> Bucket { Bucket::at_index(table, hash.inspect() as usize) } @@ -342,6 +343,7 @@ impl>> Bucket { } } + #[inline] pub fn at_index(table: M, ib_index: usize) -> Bucket { // if capacity is 0, then the RawBucket will be populated with bogus pointers. // This is an uncommon case though, so avoid it in release builds. @@ -654,6 +656,7 @@ impl GapThenFull // Returns a Layout which describes the allocation required for a hash table, // and the offset of the array of (key, value) pairs in the allocation. +#[inline(always)] fn calculate_layout(capacity: usize) -> Result<(Layout, usize), LayoutErr> { let hashes = Layout::array::(capacity)?; let pairs = Layout::array::<(K, V)>(capacity)?; @@ -722,6 +725,7 @@ impl RawTable { } } + #[inline(always)] fn raw_bucket_at(&self, index: usize) -> RawBucket { let (_, pairs_offset) = calculate_layout::(self.capacity()) .unwrap_or_else(|_| unsafe { hint::unreachable_unchecked() }); From 9b847f0f9644325e8a6764f84244b977ecd9fa05 Mon Sep 17 00:00:00 2001 From: Philipp Hansch Date: Fri, 30 Nov 2018 08:43:50 +0100 Subject: [PATCH 30/36] Fix const_fn ICE with non-const function pointer --- src/librustc_mir/transform/qualify_consts.rs | 259 ++++++++++--------- src/test/ui/consts/issue-56164.rs | 13 + src/test/ui/consts/issue-56164.stderr | 15 ++ 3 files changed, 165 insertions(+), 122 deletions(-) create mode 100644 src/test/ui/consts/issue-56164.rs create mode 100644 src/test/ui/consts/issue-56164.stderr diff --git a/src/librustc_mir/transform/qualify_consts.rs b/src/librustc_mir/transform/qualify_consts.rs index 09fe7b14c7973..3ad0c41d795a7 100644 --- a/src/librustc_mir/transform/qualify_consts.rs +++ b/src/librustc_mir/transform/qualify_consts.rs @@ -895,145 +895,160 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { let mut is_shuffle = false; let mut is_const_fn = false; let mut is_promotable_const_fn = false; - if let ty::FnDef(def_id, _) = fn_ty.sty { - callee_def_id = Some(def_id); - match self.tcx.fn_sig(def_id).abi() { - Abi::RustIntrinsic | - Abi::PlatformIntrinsic => { - assert!(!self.tcx.is_const_fn(def_id)); - match &self.tcx.item_name(def_id).as_str()[..] { - | "size_of" - | "min_align_of" - | "needs_drop" - | "type_id" - | "bswap" - | "bitreverse" - | "ctpop" - | "cttz" - | "cttz_nonzero" - | "ctlz" - | "ctlz_nonzero" - | "overflowing_add" - | "overflowing_sub" - | "overflowing_mul" - | "unchecked_shl" - | "unchecked_shr" - | "rotate_left" - | "rotate_right" - | "add_with_overflow" - | "sub_with_overflow" - | "mul_with_overflow" - // no need to check feature gates, intrinsics are only callable from the - // libstd or with forever unstable feature gates - => is_const_fn = true, - // special intrinsic that can be called diretly without an intrinsic - // feature gate needs a language feature gate - "transmute" => { - // never promote transmute calls - if self.mode != Mode::Fn { - is_const_fn = true; - // const eval transmute calls only with the feature gate - if !self.tcx.features().const_transmute { - emit_feature_err( - &self.tcx.sess.parse_sess, "const_transmute", - self.span, GateIssue::Language, - &format!("The use of std::mem::transmute() \ - is gated in {}s", self.mode)); + match fn_ty.sty { + ty::FnDef(def_id, _) => { + callee_def_id = Some(def_id); + match self.tcx.fn_sig(def_id).abi() { + Abi::RustIntrinsic | + Abi::PlatformIntrinsic => { + assert!(!self.tcx.is_const_fn(def_id)); + match &self.tcx.item_name(def_id).as_str()[..] { + | "size_of" + | "min_align_of" + | "needs_drop" + | "type_id" + | "bswap" + | "bitreverse" + | "ctpop" + | "cttz" + | "cttz_nonzero" + | "ctlz" + | "ctlz_nonzero" + | "overflowing_add" + | "overflowing_sub" + | "overflowing_mul" + | "unchecked_shl" + | "unchecked_shr" + | "rotate_left" + | "rotate_right" + | "add_with_overflow" + | "sub_with_overflow" + | "mul_with_overflow" + // no need to check feature gates, intrinsics are only callable from the + // libstd or with forever unstable feature gates + => is_const_fn = true, + // special intrinsic that can be called diretly without an intrinsic + // feature gate needs a language feature gate + "transmute" => { + // never promote transmute calls + if self.mode != Mode::Fn { + is_const_fn = true; + // const eval transmute calls only with the feature gate + if !self.tcx.features().const_transmute { + emit_feature_err( + &self.tcx.sess.parse_sess, "const_transmute", + self.span, GateIssue::Language, + &format!("The use of std::mem::transmute() \ + is gated in {}s", self.mode)); + } } } - } - name if name.starts_with("simd_shuffle") => { - is_shuffle = true; - } + name if name.starts_with("simd_shuffle") => { + is_shuffle = true; + } - _ => {} - } - } - _ => { - // in normal functions we only care about promotion - if self.mode == Mode::Fn { - // never promote const fn calls of - // functions without #[rustc_promotable] - if self.tcx.is_promotable_const_fn(def_id) { - is_const_fn = true; - is_promotable_const_fn = true; - } else if self.tcx.is_const_fn(def_id) { - is_const_fn = true; + _ => {} } - } else { - // stable const fn or unstable const fns with their feature gate - // active - if self.tcx.is_const_fn(def_id) { - is_const_fn = true; - } else if self.is_const_panic_fn(def_id) { - // check the const_panic feature gate - // FIXME: cannot allow this inside `allow_internal_unstable` because - // that would make `panic!` insta stable in constants, since the - // macro is marked with the attr - if self.tcx.features().const_panic { + } + _ => { + // in normal functions we only care about promotion + if self.mode == Mode::Fn { + // never promote const fn calls of + // functions without #[rustc_promotable] + if self.tcx.is_promotable_const_fn(def_id) { is_const_fn = true; - } else { - // don't allow panics in constants without the feature gate - emit_feature_err( - &self.tcx.sess.parse_sess, - "const_panic", - self.span, - GateIssue::Language, - &format!("panicking in {}s is unstable", self.mode), - ); - } - } else if let Some(feature) = self.tcx.is_unstable_const_fn(def_id) { - // check `#[unstable]` const fns or `#[rustc_const_unstable]` - // functions without the feature gate active in this crate to report - // a better error message than the one below - if self.span.allows_unstable() { - // `allow_internal_unstable` can make such calls stable + is_promotable_const_fn = true; + } else if self.tcx.is_const_fn(def_id) { is_const_fn = true; - } else { - let mut err = self.tcx.sess.struct_span_err(self.span, - &format!("`{}` is not yet stable as a const fn", - self.tcx.item_path_str(def_id))); - help!(&mut err, - "in Nightly builds, add `#![feature({})]` \ - to the crate attributes to enable", - feature); - err.emit(); } } else { - // FIXME(#24111) Remove this check when const fn stabilizes - let (msg, note) = if let UnstableFeatures::Disallow = - self.tcx.sess.opts.unstable_features { - (format!("calls in {}s are limited to \ - tuple structs and tuple variants", - self.mode), - Some("a limited form of compile-time function \ - evaluation is available on a nightly \ - compiler via `const fn`")) + // stable const fn or unstable const fns with their feature gate + // active + if self.tcx.is_const_fn(def_id) { + is_const_fn = true; + } else if self.is_const_panic_fn(def_id) { + // check the const_panic feature gate + // FIXME: cannot allow this inside `allow_internal_unstable` because + // that would make `panic!` insta stable in constants, since the + // macro is marked with the attr + if self.tcx.features().const_panic { + is_const_fn = true; + } else { + // don't allow panics in constants without the feature gate + emit_feature_err( + &self.tcx.sess.parse_sess, + "const_panic", + self.span, + GateIssue::Language, + &format!("panicking in {}s is unstable", self.mode), + ); + } + } else if let Some(feature) = self.tcx.is_unstable_const_fn(def_id) { + // check `#[unstable]` const fns or `#[rustc_const_unstable]` + // functions without the feature gate active in this crate to report + // a better error message than the one below + if self.span.allows_unstable() { + // `allow_internal_unstable` can make such calls stable + is_const_fn = true; + } else { + let mut err = self.tcx.sess.struct_span_err(self.span, + &format!("`{}` is not yet stable as a const fn", + self.tcx.item_path_str(def_id))); + help!(&mut err, + "in Nightly builds, add `#![feature({})]` \ + to the crate attributes to enable", + feature); + err.emit(); + } } else { - (format!("calls in {}s are limited \ - to constant functions, \ - tuple structs and tuple variants", - self.mode), - None) - }; - let mut err = struct_span_err!( - self.tcx.sess, - self.span, - E0015, - "{}", - msg, - ); - if let Some(note) = note { - err.span_note(self.span, note); + // FIXME(#24111) Remove this check when const fn stabilizes + let (msg, note) = if let UnstableFeatures::Disallow = + self.tcx.sess.opts.unstable_features { + (format!("calls in {}s are limited to \ + tuple structs and tuple variants", + self.mode), + Some("a limited form of compile-time function \ + evaluation is available on a nightly \ + compiler via `const fn`")) + } else { + (format!("calls in {}s are limited \ + to constant functions, \ + tuple structs and tuple variants", + self.mode), + None) + }; + let mut err = struct_span_err!( + self.tcx.sess, + self.span, + E0015, + "{}", + msg, + ); + if let Some(note) = note { + err.span_note(self.span, note); + } + err.emit(); } - err.emit(); } } } + }, + ty::FnPtr(_) => { + if self.mode != Mode::Fn { + let mut err = self.tcx.sess.struct_span_err( + self.span, + &format!("function pointers are not allowed in const fn")); + err.emit(); + } + }, + _ => { + self.not_const(); + return } } + let constant_arguments = callee_def_id.and_then(|id| { args_required_const(self.tcx, id) }); diff --git a/src/test/ui/consts/issue-56164.rs b/src/test/ui/consts/issue-56164.rs new file mode 100644 index 0000000000000..9d1a8b59463e7 --- /dev/null +++ b/src/test/ui/consts/issue-56164.rs @@ -0,0 +1,13 @@ +#![feature(const_fn)] + +const fn foo() { (||{})() } +//~^ ERROR calls in constant functions are limited to constant functions, tuple structs and tuple +// variants + +const fn bad(input: fn()) { + input() + //~^ ERROR function pointers are not allowed in const fn +} + +fn main() { +} diff --git a/src/test/ui/consts/issue-56164.stderr b/src/test/ui/consts/issue-56164.stderr new file mode 100644 index 0000000000000..d3e9ce379aed7 --- /dev/null +++ b/src/test/ui/consts/issue-56164.stderr @@ -0,0 +1,15 @@ +error[E0015]: calls in constant functions are limited to constant functions, tuple structs and tuple variants + --> $DIR/issue-56164.rs:3:18 + | +LL | const fn foo() { (||{})() } + | ^^^^^^^^ + +error: function pointers are not allowed in const fn + --> $DIR/issue-56164.rs:8:5 + | +LL | input() + | ^^^^^^^ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0015`. From 247ab496689d6a43995ed1cfd56b7fb2bafad095 Mon Sep 17 00:00:00 2001 From: Philipp Hansch Date: Fri, 30 Nov 2018 08:45:15 +0100 Subject: [PATCH 31/36] Pacify tidy --- src/librustc_mir/transform/qualify_consts.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/librustc_mir/transform/qualify_consts.rs b/src/librustc_mir/transform/qualify_consts.rs index 3ad0c41d795a7..bcee6d75b5a4a 100644 --- a/src/librustc_mir/transform/qualify_consts.rs +++ b/src/librustc_mir/transform/qualify_consts.rs @@ -924,8 +924,8 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { | "add_with_overflow" | "sub_with_overflow" | "mul_with_overflow" - // no need to check feature gates, intrinsics are only callable from the - // libstd or with forever unstable feature gates + // no need to check feature gates, intrinsics are only callable + // from the libstd or with forever unstable feature gates => is_const_fn = true, // special intrinsic that can be called diretly without an intrinsic // feature gate needs a language feature gate @@ -969,9 +969,9 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { is_const_fn = true; } else if self.is_const_panic_fn(def_id) { // check the const_panic feature gate - // FIXME: cannot allow this inside `allow_internal_unstable` because - // that would make `panic!` insta stable in constants, since the - // macro is marked with the attr + // FIXME: cannot allow this inside `allow_internal_unstable` + // because that would make `panic!` insta stable in constants, + // since the macro is marked with the attr if self.tcx.features().const_panic { is_const_fn = true; } else { @@ -984,10 +984,10 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { &format!("panicking in {}s is unstable", self.mode), ); } - } else if let Some(feature) = self.tcx.is_unstable_const_fn(def_id) { + } else if let Some(feat) = self.tcx.is_unstable_const_fn(def_id) { // check `#[unstable]` const fns or `#[rustc_const_unstable]` - // functions without the feature gate active in this crate to report - // a better error message than the one below + // functions without the feature gate active in this crate to + // report a better error message than the one below if self.span.allows_unstable() { // `allow_internal_unstable` can make such calls stable is_const_fn = true; @@ -998,7 +998,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { help!(&mut err, "in Nightly builds, add `#![feature({})]` \ to the crate attributes to enable", - feature); + feat); err.emit(); } } else { From 4c34404940e616e1ccd5520e0bf758853c2511c3 Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Fri, 30 Nov 2018 09:29:36 -0500 Subject: [PATCH 32/36] update nomicon --- src/doc/nomicon | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/nomicon b/src/doc/nomicon index f8a4e96feb2e5..b7eb4a087207a 160000 --- a/src/doc/nomicon +++ b/src/doc/nomicon @@ -1 +1 @@ -Subproject commit f8a4e96feb2e5a6ed1ef170ad40e3509a7755cb4 +Subproject commit b7eb4a087207af2405c0669fa577f8545b894c66 From 934871aa794e87000b1255257a33b5d8b176aa40 Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Fri, 30 Nov 2018 09:36:49 -0500 Subject: [PATCH 33/36] Add the edition guide to doc.rust-lang.org --- .gitmodules | 3 +++ src/bootstrap/doc.rs | 1 + src/doc/edition-guide | 1 + 3 files changed, 5 insertions(+) create mode 160000 src/doc/edition-guide diff --git a/.gitmodules b/.gitmodules index 6b2714edd4d12..4a136cff1cdf2 100644 --- a/.gitmodules +++ b/.gitmodules @@ -65,3 +65,6 @@ [submodule "src/doc/rustc-guide"] path = src/doc/rustc-guide url = https://github.com/rust-lang/rustc-guide.git +[submodule "src/doc/edition-guide"] + path = src/doc/edition-guide + url = https://github.com/rust-lang-nursery/edition-guide diff --git a/src/bootstrap/doc.rs b/src/bootstrap/doc.rs index f9b19ffb10d69..18a2b950e5966 100644 --- a/src/bootstrap/doc.rs +++ b/src/bootstrap/doc.rs @@ -70,6 +70,7 @@ macro_rules! book { book!( Nomicon, "src/doc/nomicon", "nomicon"; Reference, "src/doc/reference", "reference"; + EditionGuide, "src/doc/edition-guide", "edition-guide"; RustdocBook, "src/doc/rustdoc", "rustdoc"; RustcBook, "src/doc/rustc", "rustc"; RustByExample, "src/doc/rust-by-example", "rust-by-example"; diff --git a/src/doc/edition-guide b/src/doc/edition-guide new file mode 160000 index 0000000000000..ad895867b6751 --- /dev/null +++ b/src/doc/edition-guide @@ -0,0 +1 @@ +Subproject commit ad895867b675199a7f597ce7045a56875a7e516a From d49a8d558f20a753b13c1c9f10c1f63775399f7d Mon Sep 17 00:00:00 2001 From: Alexander Regueiro Date: Fri, 30 Nov 2018 02:57:30 +0000 Subject: [PATCH 34/36] Removed feature gate. --- .../src/language-features/self-struct-ctor.md | 33 ------------------- src/librustc/hir/lowering.rs | 16 --------- src/libsyntax/feature_gate.rs | 7 ++-- .../rfcs/rfc-2302-self-struct-ctor.rs | 2 -- .../feature-gate-self-struct-ctor.rs | 22 ------------- .../feature-gate-self-struct-ctor.stderr | 19 ----------- src/test/ui/issues/issue-56202.rs | 2 -- .../ui/keyword/keyword-self-as-identifier.rs | 1 - .../keyword/keyword-self-as-identifier.stderr | 10 +----- src/test/ui/self/self_type_keyword-2.rs | 3 -- src/test/ui/self/self_type_keyword-2.stderr | 30 ++--------------- 11 files changed, 7 insertions(+), 138 deletions(-) delete mode 100644 src/doc/unstable-book/src/language-features/self-struct-ctor.md delete mode 100644 src/test/ui/feature-gates/feature-gate-self-struct-ctor.rs delete mode 100644 src/test/ui/feature-gates/feature-gate-self-struct-ctor.stderr diff --git a/src/doc/unstable-book/src/language-features/self-struct-ctor.md b/src/doc/unstable-book/src/language-features/self-struct-ctor.md deleted file mode 100644 index b4742c48a32ff..0000000000000 --- a/src/doc/unstable-book/src/language-features/self-struct-ctor.md +++ /dev/null @@ -1,33 +0,0 @@ -# `self_struct_ctor` - -The tracking issue for this feature is: [#51994] -[#51994]: https://github.com/rust-lang/rust/issues/51994 - ------------------------- - -The `self_struct_ctor` feature gate lets you use the special `Self` -identifier as a constructor and a pattern. - -A simple example is: - -```rust -#![feature(self_struct_ctor)] - -struct ST(i32, i32); - -impl ST { - fn new() -> Self { - ST(0, 1) - } - - fn ctor() -> Self { - Self(1,2) // constructed by `Self`, it is the same as `ST(1, 2)` - } - - fn pattern(self) { - match self { - Self(x, y) => println!("{} {}", x, y), // used as a pattern - } - } -} -``` diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs index b3ba2968c9f51..dc8baa112bb59 100644 --- a/src/librustc/hir/lowering.rs +++ b/src/librustc/hir/lowering.rs @@ -67,7 +67,6 @@ use syntax::ast; use syntax::ast::*; use syntax::errors; use syntax::ext::hygiene::{Mark, SyntaxContext}; -use syntax::feature_gate::{emit_feature_err, GateIssue}; use syntax::print::pprust; use syntax::ptr::P; use syntax::source_map::{self, respan, CompilerDesugaringKind, Spanned}; @@ -3628,7 +3627,6 @@ impl<'a> LoweringContext<'a> { ParamMode::Optional, ImplTraitContext::disallowed(), ); - self.check_self_struct_ctor_feature(&qpath); hir::PatKind::TupleStruct( qpath, pats.iter().map(|x| self.lower_pat(x)).collect(), @@ -3643,7 +3641,6 @@ impl<'a> LoweringContext<'a> { ParamMode::Optional, ImplTraitContext::disallowed(), ); - self.check_self_struct_ctor_feature(&qpath); hir::PatKind::Path(qpath) } PatKind::Struct(ref path, ref fields, etc) => { @@ -4039,7 +4036,6 @@ impl<'a> LoweringContext<'a> { ParamMode::Optional, ImplTraitContext::disallowed(), ); - self.check_self_struct_ctor_feature(&qpath); hir::ExprKind::Path(qpath) } ExprKind::Break(opt_label, ref opt_expr) => { @@ -5102,18 +5098,6 @@ impl<'a> LoweringContext<'a> { ThinVec::new())); P(self.expr_call(e.span, from_err, hir_vec![e])) } - - fn check_self_struct_ctor_feature(&self, qp: &hir::QPath) { - if let hir::QPath::Resolved(_, ref p) = qp { - if p.segments.len() == 1 && - p.segments[0].ident.name == keywords::SelfType.name() && - !self.sess.features_untracked().self_struct_ctor { - emit_feature_err(&self.sess.parse_sess, "self_struct_ctor", - p.span, GateIssue::Language, - "`Self` struct constructors are unstable"); - } - } - } } fn body_ids(bodies: &BTreeMap) -> Vec { diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index 3bc349170514c..0d8f45433e4c7 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -475,9 +475,6 @@ declare_features! ( // Non-builtin attributes in inner attribute position (active, custom_inner_attributes, "1.30.0", Some(54726), None), - // Self struct constructor (RFC 2302) - (active, self_struct_ctor, "1.30.0", Some(51994), None), - // allow mixing of bind-by-move in patterns and references to // those identifiers in guards, *if* we are using MIR-borrowck // (aka NLL). Essentially this means you need to be on @@ -688,9 +685,11 @@ declare_features! ( (accepted, macro_literal_matcher, "1.31.0", Some(35625), None), // Use `?` as the Kleene "at most one" operator (accepted, macro_at_most_once_rep, "1.32.0", Some(48075), None), + // Self struct constructor (RFC 2302) + (accepted, self_struct_ctor, "1.32.0", Some(51994), None), ); -// If you change this, please modify src/doc/unstable-book as well. You must +// If you change this, please modify `src/doc/unstable-book` as well. You must // move that documentation into the relevant place in the other docs, and // remove the chapter on the flag. diff --git a/src/test/run-pass/rfcs/rfc-2302-self-struct-ctor.rs b/src/test/run-pass/rfcs/rfc-2302-self-struct-ctor.rs index 156e240e44282..1ec20c50034bd 100644 --- a/src/test/run-pass/rfcs/rfc-2302-self-struct-ctor.rs +++ b/src/test/run-pass/rfcs/rfc-2302-self-struct-ctor.rs @@ -1,7 +1,5 @@ // run-pass -#![feature(self_struct_ctor)] - #![allow(dead_code)] use std::fmt::Display; diff --git a/src/test/ui/feature-gates/feature-gate-self-struct-ctor.rs b/src/test/ui/feature-gates/feature-gate-self-struct-ctor.rs deleted file mode 100644 index 98eab39491320..0000000000000 --- a/src/test/ui/feature-gates/feature-gate-self-struct-ctor.rs +++ /dev/null @@ -1,22 +0,0 @@ -struct ST1(i32, i32); - -impl ST1 { - fn ctor() -> Self { - Self(1,2) - //~^ ERROR: `Self` struct constructors are unstable (see issue #51994) [E0658] - } -} - -struct ST2; - -impl ST2 { - fn ctor() -> Self { - Self - //~^ ERROR: `Self` struct constructors are unstable (see issue #51994) [E0658] - } -} - -fn main() { - let _ = ST1::ctor(); - let _ = ST2::ctor(); -} diff --git a/src/test/ui/feature-gates/feature-gate-self-struct-ctor.stderr b/src/test/ui/feature-gates/feature-gate-self-struct-ctor.stderr deleted file mode 100644 index 6061a0db76ec5..0000000000000 --- a/src/test/ui/feature-gates/feature-gate-self-struct-ctor.stderr +++ /dev/null @@ -1,19 +0,0 @@ -error[E0658]: `Self` struct constructors are unstable (see issue #51994) - --> $DIR/feature-gate-self-struct-ctor.rs:5:9 - | -LL | Self(1,2) - | ^^^^ - | - = help: add #![feature(self_struct_ctor)] to the crate attributes to enable - -error[E0658]: `Self` struct constructors are unstable (see issue #51994) - --> $DIR/feature-gate-self-struct-ctor.rs:14:9 - | -LL | Self - | ^^^^ - | - = help: add #![feature(self_struct_ctor)] to the crate attributes to enable - -error: aborting due to 2 previous errors - -For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/issues/issue-56202.rs b/src/test/ui/issues/issue-56202.rs index 4b25f58c02ac8..bd222b7fe980e 100644 --- a/src/test/ui/issues/issue-56202.rs +++ b/src/test/ui/issues/issue-56202.rs @@ -1,7 +1,5 @@ // compile-pass -#![feature(self_struct_ctor)] - trait FooTrait {} trait BarTrait { diff --git a/src/test/ui/keyword/keyword-self-as-identifier.rs b/src/test/ui/keyword/keyword-self-as-identifier.rs index ad5b8fb64342d..b50fc68bed6be 100644 --- a/src/test/ui/keyword/keyword-self-as-identifier.rs +++ b/src/test/ui/keyword/keyword-self-as-identifier.rs @@ -10,5 +10,4 @@ fn main() { let Self = 22; //~ ERROR cannot find unit struct/variant or constant `Self` in this scope - //~^ ERROR `Self` struct constructors are unstable (see issue #51994) } diff --git a/src/test/ui/keyword/keyword-self-as-identifier.stderr b/src/test/ui/keyword/keyword-self-as-identifier.stderr index 296269819f836..dbb106e8d53a5 100644 --- a/src/test/ui/keyword/keyword-self-as-identifier.stderr +++ b/src/test/ui/keyword/keyword-self-as-identifier.stderr @@ -4,15 +4,7 @@ error[E0531]: cannot find unit struct/variant or constant `Self` in this scope LL | let Self = 22; //~ ERROR cannot find unit struct/variant or constant `Self` in this scope | ^^^^ not found in this scope -error[E0658]: `Self` struct constructors are unstable (see issue #51994) - --> $DIR/keyword-self-as-identifier.rs:12:9 - | -LL | let Self = 22; //~ ERROR cannot find unit struct/variant or constant `Self` in this scope - | ^^^^ - | - = help: add #![feature(self_struct_ctor)] to the crate attributes to enable - -error: aborting due to 2 previous errors +error: aborting due to 1 previous error Some errors occurred: E0531, E0658. For more information about an error, try `rustc --explain E0531`. diff --git a/src/test/ui/self/self_type_keyword-2.rs b/src/test/ui/self/self_type_keyword-2.rs index bbaf060ca87e3..8331ae0b307d9 100644 --- a/src/test/ui/self/self_type_keyword-2.rs +++ b/src/test/ui/self/self_type_keyword-2.rs @@ -13,14 +13,11 @@ use self::Self as Foo; //~ ERROR unresolved import `self::Self` pub fn main() { let Self = 5; //~^ ERROR cannot find unit struct/variant or constant `Self` in this scope - //~^^ ERROR `Self` struct constructors are unstable (see issue #51994) match 15 { Self => (), //~^ ERROR cannot find unit struct/variant or constant `Self` in this scope - //~^^ ERROR `Self` struct constructors are unstable (see issue #51994) Foo { x: Self } => (), //~^ ERROR cannot find unit struct/variant or constant `Self` in this scope - //~^^ ERROR `Self` struct constructors are unstable (see issue #51994) } } diff --git a/src/test/ui/self/self_type_keyword-2.stderr b/src/test/ui/self/self_type_keyword-2.stderr index 82529974d0e37..3a2e9bb9c9c83 100644 --- a/src/test/ui/self/self_type_keyword-2.stderr +++ b/src/test/ui/self/self_type_keyword-2.stderr @@ -11,42 +11,18 @@ LL | let Self = 5; | ^^^^ not found in this scope error[E0531]: cannot find unit struct/variant or constant `Self` in this scope - --> $DIR/self_type_keyword-2.rs:19:9 + --> $DIR/self_type_keyword-2.rs:18:9 | LL | Self => (), | ^^^^ not found in this scope error[E0531]: cannot find unit struct/variant or constant `Self` in this scope - --> $DIR/self_type_keyword-2.rs:22:18 + --> $DIR/self_type_keyword-2.rs:20:18 | LL | Foo { x: Self } => (), | ^^^^ not found in this scope -error[E0658]: `Self` struct constructors are unstable (see issue #51994) - --> $DIR/self_type_keyword-2.rs:14:9 - | -LL | let Self = 5; - | ^^^^ - | - = help: add #![feature(self_struct_ctor)] to the crate attributes to enable - -error[E0658]: `Self` struct constructors are unstable (see issue #51994) - --> $DIR/self_type_keyword-2.rs:19:9 - | -LL | Self => (), - | ^^^^ - | - = help: add #![feature(self_struct_ctor)] to the crate attributes to enable - -error[E0658]: `Self` struct constructors are unstable (see issue #51994) - --> $DIR/self_type_keyword-2.rs:22:18 - | -LL | Foo { x: Self } => (), - | ^^^^ - | - = help: add #![feature(self_struct_ctor)] to the crate attributes to enable - -error: aborting due to 7 previous errors +error: aborting due to 4 previous errors Some errors occurred: E0432, E0531, E0658. For more information about an error, try `rustc --explain E0432`. From 24717fdaa106ffe1cea7826bdaa8377e0268a183 Mon Sep 17 00:00:00 2001 From: Alexander Regueiro Date: Fri, 30 Nov 2018 15:42:57 +0000 Subject: [PATCH 35/36] Updated ui tests. --- src/test/ui/keyword/keyword-self-as-identifier.stderr | 5 ++--- src/test/ui/self/self_type_keyword-2.stderr | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/test/ui/keyword/keyword-self-as-identifier.stderr b/src/test/ui/keyword/keyword-self-as-identifier.stderr index dbb106e8d53a5..c47f4aeabefd8 100644 --- a/src/test/ui/keyword/keyword-self-as-identifier.stderr +++ b/src/test/ui/keyword/keyword-self-as-identifier.stderr @@ -4,7 +4,6 @@ error[E0531]: cannot find unit struct/variant or constant `Self` in this scope LL | let Self = 22; //~ ERROR cannot find unit struct/variant or constant `Self` in this scope | ^^^^ not found in this scope -error: aborting due to 1 previous error +error: aborting due to previous error -Some errors occurred: E0531, E0658. -For more information about an error, try `rustc --explain E0531`. +For more information about this error, try `rustc --explain E0531`. diff --git a/src/test/ui/self/self_type_keyword-2.stderr b/src/test/ui/self/self_type_keyword-2.stderr index 3a2e9bb9c9c83..972e5bdddc673 100644 --- a/src/test/ui/self/self_type_keyword-2.stderr +++ b/src/test/ui/self/self_type_keyword-2.stderr @@ -24,5 +24,5 @@ LL | Foo { x: Self } => (), error: aborting due to 4 previous errors -Some errors occurred: E0432, E0531, E0658. +Some errors occurred: E0432, E0531. For more information about an error, try `rustc --explain E0432`. From a8248976fd43d0d8661ef2839dff4ca320e11e30 Mon Sep 17 00:00:00 2001 From: Alexander Regueiro Date: Fri, 30 Nov 2018 15:48:01 +0000 Subject: [PATCH 36/36] Moved feature-gate tests to correct dir. --- .../ui/{ => feature-gates}/feature-gate-custom_test_frameworks.rs | 0 .../feature-gate-custom_test_frameworks.stderr | 0 .../ui/{ => feature-gates}/feature-gate-doc_cfg-cfg-rustdoc.rs | 0 .../{ => feature-gates}/feature-gate-doc_cfg-cfg-rustdoc.stderr | 0 .../ui/{ => feature-gates}/feature-gate-underscore_const_names.rs | 0 .../feature-gate-underscore_const_names.stderr | 0 src/test/ui/{ => feature-gates}/feature-gate-unsized_locals.rs | 0 .../ui/{ => feature-gates}/feature-gate-unsized_locals.stderr | 0 8 files changed, 0 insertions(+), 0 deletions(-) rename src/test/ui/{ => feature-gates}/feature-gate-custom_test_frameworks.rs (100%) rename src/test/ui/{ => feature-gates}/feature-gate-custom_test_frameworks.stderr (100%) rename src/test/ui/{ => feature-gates}/feature-gate-doc_cfg-cfg-rustdoc.rs (100%) rename src/test/ui/{ => feature-gates}/feature-gate-doc_cfg-cfg-rustdoc.stderr (100%) rename src/test/ui/{ => feature-gates}/feature-gate-underscore_const_names.rs (100%) rename src/test/ui/{ => feature-gates}/feature-gate-underscore_const_names.stderr (100%) rename src/test/ui/{ => feature-gates}/feature-gate-unsized_locals.rs (100%) rename src/test/ui/{ => feature-gates}/feature-gate-unsized_locals.stderr (100%) diff --git a/src/test/ui/feature-gate-custom_test_frameworks.rs b/src/test/ui/feature-gates/feature-gate-custom_test_frameworks.rs similarity index 100% rename from src/test/ui/feature-gate-custom_test_frameworks.rs rename to src/test/ui/feature-gates/feature-gate-custom_test_frameworks.rs diff --git a/src/test/ui/feature-gate-custom_test_frameworks.stderr b/src/test/ui/feature-gates/feature-gate-custom_test_frameworks.stderr similarity index 100% rename from src/test/ui/feature-gate-custom_test_frameworks.stderr rename to src/test/ui/feature-gates/feature-gate-custom_test_frameworks.stderr diff --git a/src/test/ui/feature-gate-doc_cfg-cfg-rustdoc.rs b/src/test/ui/feature-gates/feature-gate-doc_cfg-cfg-rustdoc.rs similarity index 100% rename from src/test/ui/feature-gate-doc_cfg-cfg-rustdoc.rs rename to src/test/ui/feature-gates/feature-gate-doc_cfg-cfg-rustdoc.rs diff --git a/src/test/ui/feature-gate-doc_cfg-cfg-rustdoc.stderr b/src/test/ui/feature-gates/feature-gate-doc_cfg-cfg-rustdoc.stderr similarity index 100% rename from src/test/ui/feature-gate-doc_cfg-cfg-rustdoc.stderr rename to src/test/ui/feature-gates/feature-gate-doc_cfg-cfg-rustdoc.stderr diff --git a/src/test/ui/feature-gate-underscore_const_names.rs b/src/test/ui/feature-gates/feature-gate-underscore_const_names.rs similarity index 100% rename from src/test/ui/feature-gate-underscore_const_names.rs rename to src/test/ui/feature-gates/feature-gate-underscore_const_names.rs diff --git a/src/test/ui/feature-gate-underscore_const_names.stderr b/src/test/ui/feature-gates/feature-gate-underscore_const_names.stderr similarity index 100% rename from src/test/ui/feature-gate-underscore_const_names.stderr rename to src/test/ui/feature-gates/feature-gate-underscore_const_names.stderr diff --git a/src/test/ui/feature-gate-unsized_locals.rs b/src/test/ui/feature-gates/feature-gate-unsized_locals.rs similarity index 100% rename from src/test/ui/feature-gate-unsized_locals.rs rename to src/test/ui/feature-gates/feature-gate-unsized_locals.rs diff --git a/src/test/ui/feature-gate-unsized_locals.stderr b/src/test/ui/feature-gates/feature-gate-unsized_locals.stderr similarity index 100% rename from src/test/ui/feature-gate-unsized_locals.stderr rename to src/test/ui/feature-gates/feature-gate-unsized_locals.stderr