From e2fe7a083ed63b1a739f5e0d0417cfd8a7da6510 Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Tue, 4 Nov 2014 02:29:23 -0500 Subject: [PATCH 01/83] Lifetime guide -> ownership guide --- configure | 2 +- mk/docs.mk | 2 +- src/doc/{guide-lifetimes.md => guide-ownership.md} | 0 src/doc/guide-pointers.md | 8 ++++---- src/doc/guide-unsafe.md | 2 +- src/doc/index.md | 2 +- src/doc/po4a.conf | 2 +- 7 files changed, 9 insertions(+), 9 deletions(-) rename src/doc/{guide-lifetimes.md => guide-ownership.md} (100%) diff --git a/configure b/configure index 5ac398220592d..ed8c4ea1661d9 100755 --- a/configure +++ b/configure @@ -1046,7 +1046,7 @@ do make_dir $h/test/doc-guide-ffi make_dir $h/test/doc-guide-runtime make_dir $h/test/doc-guide-macros - make_dir $h/test/doc-guide-lifetimes + make_dir $h/test/doc-guide-ownership make_dir $h/test/doc-guide-pointers make_dir $h/test/doc-guide-container make_dir $h/test/doc-guide-tasks diff --git a/mk/docs.mk b/mk/docs.mk index 48eb9e81c20a4..b1f5c10fd3d02 100644 --- a/mk/docs.mk +++ b/mk/docs.mk @@ -25,7 +25,7 @@ # L10N_LANGS are the languages for which the docs have been # translated. ###################################################################### -DOCS := index intro tutorial guide guide-ffi guide-macros guide-lifetimes \ +DOCS := index intro tutorial guide guide-ffi guide-macros guide-ownership \ guide-tasks guide-container guide-pointers guide-testing \ guide-plugin guide-crates complement-bugreport guide-error-handling \ complement-lang-faq complement-design-faq complement-project-faq \ diff --git a/src/doc/guide-lifetimes.md b/src/doc/guide-ownership.md similarity index 100% rename from src/doc/guide-lifetimes.md rename to src/doc/guide-ownership.md diff --git a/src/doc/guide-pointers.md b/src/doc/guide-pointers.md index cf7ecd7e51ff7..10f2fdb5063df 100644 --- a/src/doc/guide-pointers.md +++ b/src/doc/guide-pointers.md @@ -408,8 +408,8 @@ test.rs:4 let y = &x; ``` As you might guess, this kind of analysis is complex for a human, and therefore -hard for a computer, too! There is an entire [guide devoted to references -and lifetimes](guide-lifetimes.html) that goes into lifetimes in +hard for a computer, too! There is an entire [guide devoted to references, ownership, +and lifetimes](guide-ownership.html) that goes into this topic in great detail, so if you want the full details, check that out. ## Best practices @@ -526,7 +526,7 @@ with some improvements: 4. Rust enforces that no other writeable pointers alias to this heap memory, which means writing to an invalid pointer is not possible. -See the section on references or the [lifetimes guide](guide-lifetimes.html) +See the section on references or the [ownership guide](guide-ownership.html) for more detail on how lifetimes work. Using boxes and references together is very common. For example: @@ -759,5 +759,5 @@ Here's a quick rundown of Rust's pointer types: # Related resources * [API documentation for Box](std/boxed/index.html) -* [Lifetimes guide](guide-lifetimes.html) +* [Ownership guide](guide-ownership.html) * [Cyclone paper on regions](http://www.cs.umd.edu/projects/cyclone/papers/cyclone-regions.pdf), which inspired Rust's lifetime system diff --git a/src/doc/guide-unsafe.md b/src/doc/guide-unsafe.md index 4d6dde7f57fb9..5b248126c80f0 100644 --- a/src/doc/guide-unsafe.md +++ b/src/doc/guide-unsafe.md @@ -37,7 +37,7 @@ build safe interfaces. ## References One of Rust's biggest features is memory safety. This is achieved in -part via [the lifetime system](guide-lifetimes.html), which is how the +part via [the ownership system](guide-ownership.html), which is how the compiler can guarantee that every `&` reference is always valid, and, for example, never pointing to freed memory. diff --git a/src/doc/index.md b/src/doc/index.md index 7d4d48e80a383..779099558220a 100644 --- a/src/doc/index.md +++ b/src/doc/index.md @@ -54,9 +54,9 @@ Rust Guides are in-depth looks at a particular topic that's relevant to Rust development. If you're trying to figure out how to do something, there may be a guide that can help you out: +* [Ownership](guide-ownership.html) * [Strings](guide-strings.html) * [Pointers](guide-pointers.html) -* [References and Lifetimes](guide-lifetimes.html) * [Crates and modules](guide-crates.html) * [Tasks and Communication](guide-tasks.html) * [Error Handling](guide-error-handling.html) diff --git a/src/doc/po4a.conf b/src/doc/po4a.conf index 4fbb3c210165a..1726afa51e479 100644 --- a/src/doc/po4a.conf +++ b/src/doc/po4a.conf @@ -11,7 +11,7 @@ [type: text] src/doc/complement-project-faq.md $lang:doc/l10n/$lang/complement-project-faq.md [type: text] src/doc/guide-container.md $lang:doc/l10n/$lang/guide-container.md [type: text] src/doc/guide-ffi.md $lang:doc/l10n/$lang/guide-ffi.md -[type: text] src/doc/guide-lifetimes.md $lang:doc/l10n/$lang/guide-lifetimes.md +[type: text] src/doc/guide-ownership.md $lang:doc/l10n/$lang/guide-ownership.md [type: text] src/doc/guide-macros.md $lang:doc/l10n/$lang/guide-macros.md [type: text] src/doc/guide-plugin.md $lang:doc/l10n/$lang/guide-plugin.md [type: text] src/doc/guide-pointers.md $lang:doc/l10n/$lang/guide-pointers.md From ddb77185d36985aeac85094cb4742da93a7e821d Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Thu, 27 Nov 2014 09:04:20 -0500 Subject: [PATCH 02/83] impl Clone for Cow --- src/libcore/borrow.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/libcore/borrow.rs b/src/libcore/borrow.rs index 06fda8d60928f..0bfa3dac1b0ee 100644 --- a/src/libcore/borrow.rs +++ b/src/libcore/borrow.rs @@ -137,6 +137,18 @@ pub enum Cow<'a, T, Sized? B: 'a> where B: ToOwned { Owned(T) } +impl<'a, T, Sized? B> Clone for Cow<'a, T, B> where B: ToOwned { + fn clone(&self) -> Cow<'a, T, B> { + match *self { + Borrowed(b) => Borrowed(b), + Owned(ref o) => { + let b: &B = BorrowFrom::borrow_from(o); + Owned(b.to_owned()) + }, + } + } +} + impl<'a, T, Sized? B> Cow<'a, T, B> where B: ToOwned { /// Acquire a mutable reference to the owned form of the data. /// From 44abe92d66bdfe3a2154834d6a593c7a93c4ce32 Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Thu, 27 Nov 2014 14:31:54 -0500 Subject: [PATCH 03/83] small doc fixes We don't need this &mut, and vec could use []s --- src/libstd/io/mem.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libstd/io/mem.rs b/src/libstd/io/mem.rs index f27951f263da2..4f18f09d3526c 100644 --- a/src/libstd/io/mem.rs +++ b/src/libstd/io/mem.rs @@ -280,10 +280,10 @@ impl<'a> Seek for BufWriter<'a> { /// # #![allow(unused_must_use)] /// use std::io::BufReader; /// -/// let mut buf = [0, 1, 2, 3]; -/// let mut r = BufReader::new(&mut buf); +/// let buf = [0, 1, 2, 3]; +/// let mut r = BufReader::new(&buf); /// -/// assert_eq!(r.read_to_end().unwrap(), vec!(0, 1, 2, 3)); +/// assert_eq!(r.read_to_end().unwrap(), vec![0, 1, 2, 3]); /// ``` pub struct BufReader<'a> { buf: &'a [u8], From a74b492763a212399276133287b053616dad0306 Mon Sep 17 00:00:00 2001 From: Jauhien Piatlicki Date: Sat, 29 Nov 2014 01:36:34 +0100 Subject: [PATCH 04/83] fix expand_quote_ty function as parse_ty was changed and needs no arguments now --- src/libsyntax/ext/quote.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/libsyntax/ext/quote.rs b/src/libsyntax/ext/quote.rs index 3fca110a881c8..45752499ad592 100644 --- a/src/libsyntax/ext/quote.rs +++ b/src/libsyntax/ext/quote.rs @@ -450,9 +450,8 @@ pub fn expand_quote_ty(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree]) -> Box { - let e_param_colons = cx.expr_lit(sp, ast::LitBool(false)); let expanded = expand_parse_call(cx, sp, "parse_ty", - vec!(e_param_colons), tts); + vec![], tts); base::MacExpr::new(expanded) } From 9460d1744f812dd0c162ad9299959cae009671d2 Mon Sep 17 00:00:00 2001 From: kulakowski Date: Sat, 29 Nov 2014 01:38:32 -0600 Subject: [PATCH 05/83] Fix typo in reference.md --- src/doc/reference.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/reference.md b/src/doc/reference.md index 1d27ac096df8a..47c25548af0ce 100644 --- a/src/doc/reference.md +++ b/src/doc/reference.md @@ -522,7 +522,7 @@ The two values of the boolean type are written `true` and `false`. ### Symbols ```{.ebnf .gram} -symbol : "::" "->" +symbol : "::" | "->" | '#' | '[' | ']' | '(' | ')' | '{' | '}' | ',' | ';' ; ``` From f5715f7867ab7e13fd3304d85861b1dcb1375a89 Mon Sep 17 00:00:00 2001 From: P1start Date: Sun, 30 Nov 2014 17:39:50 +1300 Subject: [PATCH 06/83] Allow trailing commas in array patterns and attributes --- src/libsyntax/parse/attr.rs | 2 +- src/libsyntax/parse/common.rs | 7 +------ src/libsyntax/parse/parser.rs | 5 +++++ .../compile-fail/trailing-comma-array-repeat.rs | 13 +++++++++++++ src/test/run-pass/trailing-comma.rs | 6 ++++++ 5 files changed, 26 insertions(+), 7 deletions(-) create mode 100644 src/test/compile-fail/trailing-comma-array-repeat.rs diff --git a/src/libsyntax/parse/attr.rs b/src/libsyntax/parse/attr.rs index 0c919daa8ed11..40703049cc3ac 100644 --- a/src/libsyntax/parse/attr.rs +++ b/src/libsyntax/parse/attr.rs @@ -212,7 +212,7 @@ impl<'a> ParserAttr for Parser<'a> { fn parse_meta_seq(&mut self) -> Vec> { self.parse_seq(&token::OpenDelim(token::Paren), &token::CloseDelim(token::Paren), - seq_sep_trailing_disallowed(token::Comma), + seq_sep_trailing_allowed(token::Comma), |p| p.parse_meta_item()).node } diff --git a/src/libsyntax/parse/common.rs b/src/libsyntax/parse/common.rs index 3842170d67777..a96bf1ce10b79 100644 --- a/src/libsyntax/parse/common.rs +++ b/src/libsyntax/parse/common.rs @@ -19,18 +19,13 @@ pub struct SeqSep { pub trailing_sep_allowed: bool } -pub fn seq_sep_trailing_disallowed(t: token::Token) -> SeqSep { - SeqSep { - sep: Some(t), - trailing_sep_allowed: false, - } -} pub fn seq_sep_trailing_allowed(t: token::Token) -> SeqSep { SeqSep { sep: Some(t), trailing_sep_allowed: true, } } + pub fn seq_sep_none() -> SeqSep { SeqSep { sep: None, diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 447f2a376e15a..9623a1b75b56a 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -3129,6 +3129,11 @@ impl<'a> Parser<'a> { first = false; } else { self.expect(&token::Comma); + + if self.token == token::CloseDelim(token::Bracket) + && (before_slice || after.len() != 0) { + break + } } if before_slice { diff --git a/src/test/compile-fail/trailing-comma-array-repeat.rs b/src/test/compile-fail/trailing-comma-array-repeat.rs new file mode 100644 index 0000000000000..dadd657158384 --- /dev/null +++ b/src/test/compile-fail/trailing-comma-array-repeat.rs @@ -0,0 +1,13 @@ +// Copyright 2014 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. + +fn main() { + let [_, ..,] = [(), ()]; //~ ERROR unexpected token: `]` +} diff --git a/src/test/run-pass/trailing-comma.rs b/src/test/run-pass/trailing-comma.rs index 5e93f8eedb7eb..00e050640805b 100644 --- a/src/test/run-pass/trailing-comma.rs +++ b/src/test/run-pass/trailing-comma.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#![feature(advanced_slice_patterns,)] + fn f(_: T,) {} struct Foo; @@ -24,9 +26,13 @@ enum Baz { Qux(int,), } +#[allow(unused,)] pub fn main() { f::(0i,); let (_, _,) = (1i, 1i,); + let [_, _,] = [1i, 1,]; + let [_, _, .., _,] = [1i, 1, 1, 1,]; + let [_, _, _.., _,] = [1i, 1, 1, 1,]; let x: Foo = Foo::; From 5048953debe9eb44c672f3c453e894bd64a1301a Mon Sep 17 00:00:00 2001 From: Tobias Bucher Date: Sat, 29 Nov 2014 01:20:14 +0100 Subject: [PATCH 07/83] Simplify `RefCell` code a bit, make `deref` a no-op. --- src/libcore/cell.rs | 118 ++++++++++++++++++++++++++++---------------- 1 file changed, 75 insertions(+), 43 deletions(-) diff --git a/src/libcore/cell.rs b/src/libcore/cell.rs index 587bb4cb110e1..ed4df10120272 100644 --- a/src/libcore/cell.rs +++ b/src/libcore/cell.rs @@ -277,12 +277,9 @@ impl RefCell { /// Returns `None` if the value is currently mutably borrowed. #[unstable = "may be renamed, depending on global conventions"] pub fn try_borrow<'a>(&'a self) -> Option> { - match self.borrow.get() { - WRITING => None, - borrow => { - self.borrow.set(borrow + 1); - Some(Ref { _parent: self }) - } + match BorrowRef::new(&self.borrow) { + Some(b) => Some(Ref { _value: unsafe { &*self.value.get() }, _borrow: b }), + None => None, } } @@ -310,12 +307,9 @@ impl RefCell { /// Returns `None` if the value is currently borrowed. #[unstable = "may be renamed, depending on global conventions"] pub fn try_borrow_mut<'a>(&'a self) -> Option> { - match self.borrow.get() { - UNUSED => { - self.borrow.set(WRITING); - Some(RefMut { _parent: self }) - }, - _ => None + match BorrowRefMut::new(&self.borrow) { + Some(b) => Some(RefMut { _value: unsafe { &mut *self.value.get() }, _borrow: b }), + None => None, } } @@ -368,29 +362,56 @@ impl PartialEq for RefCell { } } -/// Wraps a borrowed reference to a value in a `RefCell` box. -#[unstable] -pub struct Ref<'b, T:'b> { - // FIXME #12808: strange name to try to avoid interfering with - // field accesses of the contained type via Deref - _parent: &'b RefCell +struct BorrowRef<'b> { + _borrow: &'b Cell, +} + +impl<'b> BorrowRef<'b> { + fn new(borrow: &'b Cell) -> Option> { + match borrow.get() { + WRITING => None, + b => { + borrow.set(b + 1); + Some(BorrowRef { _borrow: borrow }) + }, + } + } } #[unsafe_destructor] -#[unstable] -impl<'b, T> Drop for Ref<'b, T> { +impl<'b> Drop for BorrowRef<'b> { fn drop(&mut self) { - let borrow = self._parent.borrow.get(); + let borrow = self._borrow.get(); debug_assert!(borrow != WRITING && borrow != UNUSED); - self._parent.borrow.set(borrow - 1); + self._borrow.set(borrow - 1); } } +impl<'b> Clone for BorrowRef<'b> { + fn clone(&self) -> BorrowRef<'b> { + // Since this Ref exists, we know the borrow flag + // is not set to WRITING. + let borrow = self._borrow.get(); + debug_assert!(borrow != WRITING && borrow != UNUSED); + self._borrow.set(borrow + 1); + BorrowRef { _borrow: self._borrow } + } +} + +/// Wraps a borrowed reference to a value in a `RefCell` box. +#[unstable] +pub struct Ref<'b, T:'b> { + // FIXME #12808: strange name to try to avoid interfering with + // field accesses of the contained type via Deref + _value: &'b T, + _borrow: BorrowRef<'b>, +} + #[unstable = "waiting for `Deref` to become stable"] impl<'b, T> Deref for Ref<'b, T> { #[inline] fn deref<'a>(&'a self) -> &'a T { - unsafe { &*self._parent.value.get() } + self._value } } @@ -401,41 +422,52 @@ impl<'b, T> Deref for Ref<'b, T> { /// A `Clone` implementation would interfere with the widespread /// use of `r.borrow().clone()` to clone the contents of a `RefCell`. #[experimental = "likely to be moved to a method, pending language changes"] -pub fn clone_ref<'b, T>(orig: &Ref<'b, T>) -> Ref<'b, T> { - // Since this Ref exists, we know the borrow flag - // is not set to WRITING. - let borrow = orig._parent.borrow.get(); - debug_assert!(borrow != WRITING && borrow != UNUSED); - orig._parent.borrow.set(borrow + 1); - +pub fn clone_ref<'b, T:Clone>(orig: &Ref<'b, T>) -> Ref<'b, T> { Ref { - _parent: orig._parent, + _value: orig._value, + _borrow: orig._borrow.clone(), } } -/// Wraps a mutable borrowed reference to a value in a `RefCell` box. -#[unstable] -pub struct RefMut<'b, T:'b> { - // FIXME #12808: strange name to try to avoid interfering with - // field accesses of the contained type via Deref - _parent: &'b RefCell +struct BorrowRefMut<'b> { + _borrow: &'b Cell, } #[unsafe_destructor] -#[unstable] -impl<'b, T> Drop for RefMut<'b, T> { +impl<'b> Drop for BorrowRefMut<'b> { fn drop(&mut self) { - let borrow = self._parent.borrow.get(); + let borrow = self._borrow.get(); debug_assert!(borrow == WRITING); - self._parent.borrow.set(UNUSED); + self._borrow.set(UNUSED); } } +impl<'b> BorrowRefMut<'b> { + fn new(borrow: &'b Cell) -> Option> { + match borrow.get() { + UNUSED => { + borrow.set(WRITING); + Some(BorrowRefMut { _borrow: borrow }) + }, + _ => None, + } + } +} + +/// Wraps a mutable borrowed reference to a value in a `RefCell` box. +#[unstable] +pub struct RefMut<'b, T:'b> { + // FIXME #12808: strange name to try to avoid interfering with + // field accesses of the contained type via Deref + _value: &'b mut T, + _borrow: BorrowRefMut<'b>, +} + #[unstable = "waiting for `Deref` to become stable"] impl<'b, T> Deref for RefMut<'b, T> { #[inline] fn deref<'a>(&'a self) -> &'a T { - unsafe { &*self._parent.value.get() } + self._value } } @@ -443,7 +475,7 @@ impl<'b, T> Deref for RefMut<'b, T> { impl<'b, T> DerefMut for RefMut<'b, T> { #[inline] fn deref_mut<'a>(&'a mut self) -> &'a mut T { - unsafe { &mut *self._parent.value.get() } + self._value } } From 6687b2a6e1c7e9ece2ab9bf47e0048561ca8b578 Mon Sep 17 00:00:00 2001 From: Erick Tryzelaar Date: Fri, 14 Nov 2014 22:50:47 -0800 Subject: [PATCH 08/83] std: add tests for the Vec Writer impl --- src/libstd/io/mem.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/libstd/io/mem.rs b/src/libstd/io/mem.rs index f27951f263da2..e084b585394e8 100644 --- a/src/libstd/io/mem.rs +++ b/src/libstd/io/mem.rs @@ -362,6 +362,16 @@ mod test { use self::test::Bencher; use str::StrPrelude; + #[test] + fn test_vec_writer() { + let mut writer = Vec::new(); + writer.write(&[0]).unwrap(); + writer.write(&[1, 2, 3]).unwrap(); + writer.write(&[4, 5, 6, 7]).unwrap(); + let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7]; + assert_eq!(writer.as_slice(), b); + } + #[test] fn test_mem_writer() { let mut writer = MemWriter::new(); @@ -385,6 +395,8 @@ mod test { assert_eq!(writer.tell(), Ok(8)); writer.write(&[]).unwrap(); assert_eq!(writer.tell(), Ok(8)); + + assert!(writer.write(&[1]).is_err()); } let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7]; assert_eq!(buf.as_slice(), b); From 0d24780793f798ad9b2b17695f1079719d3b9854 Mon Sep 17 00:00:00 2001 From: Erick Tryzelaar Date: Sun, 30 Nov 2014 16:55:53 -0800 Subject: [PATCH 09/83] std: add Reader impl for &[u8] --- src/libcore/result.rs | 7 ++--- src/libgraphviz/lib.rs | 8 ++--- src/libstd/io/buffered.rs | 6 ++-- src/libstd/io/mem.rs | 61 +++++++++++++++++++++++++++++++++++++++ src/libstd/io/util.rs | 5 ++-- src/libtime/lib.rs | 9 +++--- 6 files changed, 76 insertions(+), 20 deletions(-) diff --git a/src/libcore/result.rs b/src/libcore/result.rs index 202ac46449754..2368739a66f00 100644 --- a/src/libcore/result.rs +++ b/src/libcore/result.rs @@ -446,13 +446,12 @@ impl Result { /// ``` /// use std::io::{BufReader, IoResult}; /// - /// let buffer = "1\n2\n3\n4\n"; - /// let mut reader = BufReader::new(buffer.as_bytes()); + /// let mut buffer = "1\n2\n3\n4\n"; /// /// let mut sum = 0; /// - /// while !reader.eof() { - /// let line: IoResult = reader.read_line(); + /// while !buffer.is_empty() { + /// let line: IoResult = buffer.read_line(); /// // Convert the string line to a number using `map` and `from_str` /// let val: IoResult = line.map(|line| { /// from_str::(line.as_slice().trim_right()).unwrap_or(0) diff --git a/src/libgraphviz/lib.rs b/src/libgraphviz/lib.rs index af1dbc2b7fca4..0a5081d07be00 100644 --- a/src/libgraphviz/lib.rs +++ b/src/libgraphviz/lib.rs @@ -547,7 +547,7 @@ mod tests { use self::NodeLabels::*; use super::{Id, LabelText, LabelStr, EscStr, Labeller}; use super::{Nodes, Edges, GraphWalk, render}; - use std::io::{BufReader, IoResult}; + use std::io::IoResult; use std::str; /// each node is an index in a vector in the graph. @@ -698,8 +698,7 @@ mod tests { fn test_input(g: LabelledGraph) -> IoResult { let mut writer = Vec::new(); render(&g, &mut writer).unwrap(); - let mut r = BufReader::new(writer[]); - r.read_to_string() + (&mut writer.as_slice()).read_to_string() } // All of the tests use raw-strings as the format for the expected outputs, @@ -811,8 +810,7 @@ r#"digraph hasse_diagram { edge(1, 3, ";"), edge(2, 3, ";" ))); render(&g, &mut writer).unwrap(); - let mut r = BufReader::new(writer[]); - let r = r.read_to_string(); + let r = (&mut writer.as_slice()).read_to_string(); assert_eq!(r.unwrap().as_slice(), r#"digraph syntax_tree { diff --git a/src/libstd/io/buffered.rs b/src/libstd/io/buffered.rs index e92bad592d126..fba9e4f2e25e1 100644 --- a/src/libstd/io/buffered.rs +++ b/src/libstd/io/buffered.rs @@ -406,7 +406,7 @@ mod test { use prelude::*; use super::*; use super::super::{IoResult, EndOfFile}; - use super::super::mem::{MemReader, BufReader}; + use super::super::mem::MemReader; use self::test::Bencher; use str::StrPrelude; @@ -626,14 +626,14 @@ mod test { #[test] fn read_char_buffered() { let buf = [195u8, 159u8]; - let mut reader = BufferedReader::with_capacity(1, BufReader::new(&buf)); + let mut reader = BufferedReader::with_capacity(1, buf[]); assert_eq!(reader.read_char(), Ok('ß')); } #[test] fn test_chars() { let buf = [195u8, 159u8, b'a']; - let mut reader = BufferedReader::with_capacity(1, BufReader::new(&buf)); + let mut reader = BufferedReader::with_capacity(1, buf[]); let mut it = reader.chars(); assert_eq!(it.next(), Some(Ok('ß'))); assert_eq!(it.next(), Some(Ok('a'))); diff --git a/src/libstd/io/mem.rs b/src/libstd/io/mem.rs index e084b585394e8..6a9b10d2a7bd0 100644 --- a/src/libstd/io/mem.rs +++ b/src/libstd/io/mem.rs @@ -206,6 +206,41 @@ impl Buffer for MemReader { fn consume(&mut self, amt: uint) { self.pos += amt; } } +impl<'a> Reader for &'a [u8] { + #[inline] + fn read(&mut self, buf: &mut [u8]) -> IoResult { + if self.is_empty() { return Err(io::standard_error(io::EndOfFile)); } + + let write_len = min(buf.len(), self.len()); + { + let input = self[..write_len]; + let output = buf[mut ..write_len]; + slice::bytes::copy_memory(output, input); + } + + *self = self.slice_from(write_len); + + Ok(write_len) + } +} + +impl<'a> Buffer for &'a [u8] { + #[inline] + fn fill_buf<'a>(&'a mut self) -> IoResult<&'a [u8]> { + if self.is_empty() { + Err(io::standard_error(io::EndOfFile)) + } else { + Ok(*self) + } + } + + #[inline] + fn consume(&mut self, amt: uint) { + *self = self[amt..]; + } +} + + /// Writes to a fixed-size byte slice /// /// If a write will not fit in the buffer, it returns an error and does not @@ -469,6 +504,32 @@ mod test { assert!(reader.read(&mut buf).is_err()); } + #[test] + fn test_slice_reader() { + let in_buf = vec![0, 1, 2, 3, 4, 5, 6, 7]; + let mut reader = &mut in_buf.as_slice(); + let mut buf = []; + assert_eq!(reader.read(&mut buf), Ok(0)); + let mut buf = [0]; + assert_eq!(reader.read(&mut buf), Ok(1)); + assert_eq!(reader.len(), 7); + let b: &[_] = &[0]; + assert_eq!(buf.as_slice(), b); + let mut buf = [0, ..4]; + assert_eq!(reader.read(&mut buf), Ok(4)); + assert_eq!(reader.len(), 3); + let b: &[_] = &[1, 2, 3, 4]; + assert_eq!(buf.as_slice(), b); + assert_eq!(reader.read(&mut buf), Ok(3)); + let b: &[_] = &[5, 6, 7]; + assert_eq!(buf[0..3], b); + assert!(reader.read(&mut buf).is_err()); + let mut reader = &mut in_buf.as_slice(); + assert_eq!(reader.read_until(3).unwrap(), vec!(0, 1, 2, 3)); + assert_eq!(reader.read_until(3).unwrap(), vec!(4, 5, 6, 7)); + assert!(reader.read(&mut buf).is_err()); + } + #[test] fn test_buf_reader() { let in_buf = vec![0, 1, 2, 3, 4, 5, 6, 7]; diff --git a/src/libstd/io/util.rs b/src/libstd/io/util.rs index 393283ff64c5b..e78bd1dd33f32 100644 --- a/src/libstd/io/util.rs +++ b/src/libstd/io/util.rs @@ -273,7 +273,7 @@ impl> Reader for IterReader { #[cfg(test)] mod test { - use io::{MemReader, BufReader, ByRefReader}; + use io::{MemReader, ByRefReader}; use io; use boxed::Box; use super::*; @@ -395,8 +395,7 @@ mod test { #[test] fn limit_reader_buffer() { - let data = "0123456789\n0123456789\n"; - let mut r = BufReader::new(data.as_bytes()); + let r = &mut b"0123456789\n0123456789\n"; { let mut r = LimitReader::new(r.by_ref(), 3); assert_eq!(r.read_line(), Ok("012".to_string())); diff --git a/src/libtime/lib.rs b/src/libtime/lib.rs index 1d5268b975481..a77a6276a8b4e 100644 --- a/src/libtime/lib.rs +++ b/src/libtime/lib.rs @@ -32,7 +32,6 @@ use self::Fmt::*; use std::fmt::Show; use std::fmt; -use std::io::BufReader; use std::num::SignedInt; use std::string::String; use std::time::Duration; @@ -1187,7 +1186,7 @@ pub fn strptime(s: &str, format: &str) -> Result { } } - let mut rdr = BufReader::new(format.as_bytes()); + let mut rdr: &[u8] = format.as_bytes(); let mut tm = Tm { tm_sec: 0_i32, tm_min: 0_i32, @@ -1211,13 +1210,13 @@ pub fn strptime(s: &str, format: &str) -> Result { let next = range.next; let mut buf = [0]; - let c = match rdr.read(&mut buf) { + let c = match (&mut rdr).read(&mut buf) { Ok(..) => buf[0] as char, Err(..) => break }; match c { '%' => { - let ch = match rdr.read(&mut buf) { + let ch = match (&mut rdr).read(&mut buf) { Ok(..) => buf[0] as char, Err(..) => break }; @@ -1233,7 +1232,7 @@ pub fn strptime(s: &str, format: &str) -> Result { } } - if pos == len && rdr.tell().unwrap() == format.len() as u64 { + if pos == len && (&mut rdr).is_empty() { Ok(Tm { tm_sec: tm.tm_sec, tm_min: tm.tm_min, From e407472f90247bd23485191b9ecaab89a90326c8 Mon Sep 17 00:00:00 2001 From: Paul Collier Date: Mon, 1 Dec 2014 20:43:50 -0800 Subject: [PATCH 10/83] rustdoc: Check for href when prepending rootPath Fixes #18354. --- src/librustdoc/html/static/main.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/librustdoc/html/static/main.js b/src/librustdoc/html/static/main.js index 7c6f7ed3fe230..8943d942cefbc 100644 --- a/src/librustdoc/html/static/main.js +++ b/src/librustdoc/html/static/main.js @@ -707,8 +707,8 @@ var code = $('').append(structs[j]); $.each(code.find('a'), function(idx, a) { var href = $(a).attr('href'); - if (!href.startsWith('http')) { - $(a).attr('href', rootPath + $(a).attr('href')); + if (href && !href.startsWith('http')) { + $(a).attr('href', rootPath + href); } }); var li = $('
  • ').append(code); From 5bbe5d6d931519e424aee21664fd37d33152817e Mon Sep 17 00:00:00 2001 From: Matej Lach Date: Tue, 2 Dec 2014 13:40:18 +0000 Subject: [PATCH 11/83] better wording --- src/doc/guide.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/doc/guide.md b/src/doc/guide.md index c2d43a20ec46c..ce5730ad9cedd 100644 --- a/src/doc/guide.md +++ b/src/doc/guide.md @@ -2064,8 +2064,8 @@ Great! Next up: let's compare our guess to the secret guess. ## Comparing guesses If you remember, earlier in the guide, we made a `cmp` function that compared -two numbers. Let's add that in, along with a `match` statement to compare the -guess to the secret guess: +two numbers. Let's add that in, along with a `match` statement to compare our +guess to the secret number: ```{rust,ignore} use std::io; From ebf22cbf6a1821a808ca3b11708cf2f4dc60c0a8 Mon Sep 17 00:00:00 2001 From: Matej Lach Date: Tue, 2 Dec 2014 16:10:40 +0000 Subject: [PATCH 12/83] replace 'but' with 'and' --- src/doc/guide.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/guide.md b/src/doc/guide.md index c2d43a20ec46c..afc0811300d4c 100644 --- a/src/doc/guide.md +++ b/src/doc/guide.md @@ -2861,7 +2861,7 @@ parts of your library. The six levels are: * experimental: This item was only recently introduced or is otherwise in a state of flux. It may change significantly, or even be removed. No guarantee of backwards-compatibility. -* unstable: This item is still under development, but requires more testing to +* unstable: This item is still under development and requires more testing to be considered stable. No guarantee of backwards-compatibility. * stable: This item is considered stable, and will not change significantly. Guarantee of backwards-compatibility. From 6182084e58bf3fde2e700424bf7d6ea708641f74 Mon Sep 17 00:00:00 2001 From: Victor van den Elzen Date: Tue, 2 Dec 2014 20:42:09 +0100 Subject: [PATCH 13/83] Improve documentation of checked_* functions --- src/libcore/num/mod.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libcore/num/mod.rs b/src/libcore/num/mod.rs index ce61bd97e1323..748e8942a3fea 100644 --- a/src/libcore/num/mod.rs +++ b/src/libcore/num/mod.rs @@ -284,7 +284,7 @@ pub trait Int /// ``` fn checked_add(self, other: Self) -> Option; - /// Checked integer subtraction. Computes `self + other`, returning `None` + /// Checked integer subtraction. Computes `self - other`, returning `None` /// if underflow occurred. /// /// # Example @@ -297,7 +297,7 @@ pub trait Int /// ``` fn checked_sub(self, other: Self) -> Option; - /// Checked integer multiplication. Computes `self + other`, returning + /// Checked integer multiplication. Computes `self * other`, returning /// `None` if underflow or overflow occurred. /// /// # Example @@ -310,8 +310,8 @@ pub trait Int /// ``` fn checked_mul(self, other: Self) -> Option; - /// Checked integer division. Computes `self + other` returning `None` if - /// `self == 0` or the operation results in underflow or overflow. + /// Checked integer division. Computes `self / other`, returning `None` if + /// `other == 0` or the operation results in underflow or overflow. /// /// # Example /// From d8c5269dd2eb14e3d1fefa4a2a26f3ff6a9f1ba8 Mon Sep 17 00:00:00 2001 From: Victor van den Elzen Date: Tue, 2 Dec 2014 20:43:47 +0100 Subject: [PATCH 14/83] Update comment to current file path for intrinsics --- src/libcore/intrinsics.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libcore/intrinsics.rs b/src/libcore/intrinsics.rs index 78c74075d4867..11a1073343ea4 100644 --- a/src/libcore/intrinsics.rs +++ b/src/libcore/intrinsics.rs @@ -10,7 +10,7 @@ //! rustc compiler intrinsics. //! -//! The corresponding definitions are in librustc/middle/trans/foreign.rs. +//! The corresponding definitions are in librustc_trans/trans/intrinsic.rs. //! //! # Volatiles //! From 886ff4f3c3d05d4dda13390f045a6eb577f1e509 Mon Sep 17 00:00:00 2001 From: Luqman Aden Date: Tue, 2 Dec 2014 17:33:52 -0500 Subject: [PATCH 15/83] lldb: Fix pretty printer for nullable-opt enums with fat pointers. --- src/etc/lldb_rust_formatters.py | 9 +++++++-- src/test/debuginfo/option-like-enum.rs | 9 +++++++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/etc/lldb_rust_formatters.py b/src/etc/lldb_rust_formatters.py index 7924d63c8e0d5..f4f1a5121d195 100644 --- a/src/etc/lldb_rust_formatters.py +++ b/src/etc/lldb_rust_formatters.py @@ -138,9 +138,14 @@ def print_enum_val(val, internal_dict): return "" % first_variant_name # Read the discriminant - disr_val = val.GetChildAtIndex(0).GetChildAtIndex(disr_field_index).GetValueAsUnsigned() + disr_val = val.GetChildAtIndex(0).GetChildAtIndex(disr_field_index) - if disr_val == 0: + # If the discriminant field is a fat pointer we have to consider the + # first word as the true discriminant + if disr_val.GetType().GetTypeClass() == lldb.eTypeClassStruct: + disr_val = disr_val.GetChildAtIndex(0) + + if disr_val.GetValueAsUnsigned() == 0: # Null case: Print the name of the null-variant null_variant_name = first_variant_name[last_separator_index + 1:] return null_variant_name diff --git a/src/test/debuginfo/option-like-enum.rs b/src/test/debuginfo/option-like-enum.rs index 11c594bac599a..333a430e35111 100644 --- a/src/test/debuginfo/option-like-enum.rs +++ b/src/test/debuginfo/option-like-enum.rs @@ -61,6 +61,12 @@ // lldb-command:print void_droid // lldb-check:[...]$5 = Void +// lldb-command:print some_str +// lldb-check:[...]$6 = Some(&str { data_ptr: [...], length: 3 }) + +// lldb-command:print none_str +// lldb-check:[...]$7 = None + // If a struct has exactly two variants, one of them is empty, and the other one // contains a non-nullable pointer, then this value is used as the discriminator. @@ -96,6 +102,9 @@ struct NamedFieldsRepr<'a> { fn main() { + let some_str: Option<&'static str> = Some("abc"); + let none_str: Option<&'static str> = None; + let some: Option<&u32> = Some(unsafe { std::mem::transmute(0x12345678u) }); let none: Option<&u32> = None; From 89d09953733e72d61876c633dc0658180aefc4d6 Mon Sep 17 00:00:00 2001 From: Luqman Aden Date: Tue, 2 Dec 2014 18:23:35 -0500 Subject: [PATCH 16/83] gdb: Fix pretty printer for nullable-opt enums with fat pointers. --- src/etc/gdb_rust_pretty_printing.py | 16 +++++++++++----- .../debuginfo/gdb-pretty-struct-and-enums.rs | 12 ++++++++++-- 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/src/etc/gdb_rust_pretty_printing.py b/src/etc/gdb_rust_pretty_printing.py index 1af649f073176..7e5918ea39e1e 100644 --- a/src/etc/gdb_rust_pretty_printing.py +++ b/src/etc/gdb_rust_pretty_printing.py @@ -54,13 +54,14 @@ def rust_pretty_printer_lookup_function(val): return RustStructPrinter(val, false) if enum_member_count == 1: - if enum_members[0].name == None: + first_variant_name = enum_members[0].name + if first_variant_name == None: # This is a singleton enum return rust_pretty_printer_lookup_function(val[enum_members[0]]) else: - assert enum_members[0].name.startswith("RUST$ENCODED$ENUM$") + assert first_variant_name.startswith("RUST$ENCODED$ENUM$") # This is a space-optimized enum - last_separator_index = enum_members[0].name.rfind("$") + last_separator_index = first_variant_name.rfind("$") second_last_separator_index = first_variant_name.rfind("$", 0, last_separator_index) disr_field_index = first_variant_name[second_last_separator_index + 1 : last_separator_index] @@ -68,7 +69,12 @@ def rust_pretty_printer_lookup_function(val): sole_variant_val = val[enum_members[0]] disr_field = get_field_at_index(sole_variant_val, disr_field_index) - discriminant = int(sole_variant_val[disr_field]) + discriminant = sole_variant_val[disr_field] + + # If the discriminant field is a fat pointer we have to consider the + # first word as the true discriminant + if discriminant.type.code == gdb.TYPE_CODE_STRUCT: + discriminant = discriminant[get_field_at_index(discriminant, 0)] if discriminant == 0: null_variant_name = first_variant_name[last_separator_index + 1:] @@ -173,7 +179,7 @@ def to_string(self): class IdentityPrinter: def __init__(self, string): - self.string + self.string = string def to_string(self): return self.string diff --git a/src/test/debuginfo/gdb-pretty-struct-and-enums.rs b/src/test/debuginfo/gdb-pretty-struct-and-enums.rs index 9a42cd92fdc87..76cf3c1149dd5 100644 --- a/src/test/debuginfo/gdb-pretty-struct-and-enums.rs +++ b/src/test/debuginfo/gdb-pretty-struct-and-enums.rs @@ -58,11 +58,17 @@ // gdb-command: print none // gdb-check:$12 = None +// gdb-command: print some_fat +// gdb-check:$13 = Some = {"abc"} + +// gdb-command: print none_fat +// gdb-check:$14 = None + // gdb-command: print nested_variant1 -// gdb-check:$13 = NestedVariant1 = {NestedStruct = {regular_struct = RegularStruct = {the_first_field = 111, the_second_field = 112.5, the_third_field = true, the_fourth_field = "NestedStructString1"}, tuple_struct = TupleStruct = {113.5, 114}, empty_struct = EmptyStruct, c_style_enum = CStyleEnumVar2, mixed_enum = MixedEnumTupleVar = {115, 116, false}}} +// gdb-check:$15 = NestedVariant1 = {NestedStruct = {regular_struct = RegularStruct = {the_first_field = 111, the_second_field = 112.5, the_third_field = true, the_fourth_field = "NestedStructString1"}, tuple_struct = TupleStruct = {113.5, 114}, empty_struct = EmptyStruct, c_style_enum = CStyleEnumVar2, mixed_enum = MixedEnumTupleVar = {115, 116, false}}} // gdb-command: print nested_variant2 -// gdb-check:$14 = NestedVariant2 = {abc = NestedStruct = {regular_struct = RegularStruct = {the_first_field = 117, the_second_field = 118.5, the_third_field = false, the_fourth_field = "NestedStructString10"}, tuple_struct = TupleStruct = {119.5, 120}, empty_struct = EmptyStruct, c_style_enum = CStyleEnumVar3, mixed_enum = MixedEnumStructVar = {field1 = 121.5, field2 = -122}}} +// gdb-check:$16 = NestedVariant2 = {abc = NestedStruct = {regular_struct = RegularStruct = {the_first_field = 117, the_second_field = 118.5, the_third_field = false, the_fourth_field = "NestedStructString10"}, tuple_struct = TupleStruct = {119.5, 120}, empty_struct = EmptyStruct, c_style_enum = CStyleEnumVar3, mixed_enum = MixedEnumStructVar = {field1 = 121.5, field2 = -122}}} use self::CStyleEnum::{CStyleEnumVar1, CStyleEnumVar2, CStyleEnumVar3}; use self::MixedEnum::{MixedEnumCStyleVar, MixedEnumTupleVar, MixedEnumStructVar}; @@ -129,6 +135,8 @@ fn main() { let some = Some(110u); let none: Option = None; + let some_fat = Some("abc"); + let none_fat: Option<&'static str> = None; let nested_variant1 = NestedVariant1( NestedStruct { From b258de7bda2963a0e1940fc419011560937b2b08 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Thu, 20 Nov 2014 18:17:37 -0500 Subject: [PATCH 17/83] libcore: add `Rhs` input parameter to comparison traits --- src/libcore/cmp.rs | 24 ++++++++++++------------ src/libcore/lib.rs | 1 + 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/src/libcore/cmp.rs b/src/libcore/cmp.rs index 51122d0a17023..11878dc76d989 100644 --- a/src/libcore/cmp.rs +++ b/src/libcore/cmp.rs @@ -61,13 +61,13 @@ use option::{Option, Some, None}; /// `Eq`. #[lang="eq"] #[unstable = "Definition may change slightly after trait reform"] -pub trait PartialEq for Sized? { +pub trait PartialEq for Sized? { /// This method tests for `self` and `other` values to be equal, and is used by `==`. - fn eq(&self, other: &Self) -> bool; + fn eq(&self, other: &Rhs) -> bool; /// This method tests for `!=`. #[inline] - fn ne(&self, other: &Self) -> bool { !self.eq(other) } + fn ne(&self, other: &Rhs) -> bool { !self.eq(other) } } /// Trait for equality comparisons which are [equivalence relations]( @@ -80,7 +80,7 @@ pub trait PartialEq for Sized? { /// - symmetric: `a == b` implies `b == a`; and /// - transitive: `a == b` and `b == c` implies `a == c`. #[unstable = "Definition may change slightly after trait reform"] -pub trait Eq for Sized?: PartialEq { +pub trait Eq for Sized?: PartialEq { // FIXME #13101: this method is used solely by #[deriving] to // assert that every component of a type implements #[deriving] // itself, the current deriving infrastructure means doing this @@ -150,7 +150,7 @@ impl Ordering { /// - transitive, `a < b` and `b < c` implies `a < c`. The same must hold for /// both `==` and `>`. #[unstable = "Definition may change slightly after trait reform"] -pub trait Ord for Sized?: Eq + PartialOrd { +pub trait Ord for Sized?: Eq + PartialOrd { /// This method returns an ordering between `self` and `other` values. /// /// By convention, `self.cmp(&other)` returns the ordering matching @@ -161,7 +161,7 @@ pub trait Ord for Sized?: Eq + PartialOrd { /// assert_eq!(10u.cmp(&5), Greater); // because 10 > 5 /// assert_eq!( 5u.cmp(&5), Equal); // because 5 == 5 /// ``` - fn cmp(&self, other: &Self) -> Ordering; + fn cmp(&self, other: &Rhs) -> Ordering; } #[unstable = "Trait is unstable."] @@ -194,14 +194,14 @@ impl PartialOrd for Ordering { /// 5.11). #[lang="ord"] #[unstable = "Definition may change slightly after trait reform"] -pub trait PartialOrd for Sized?: PartialEq { +pub trait PartialOrd for Sized?: PartialEq { /// This method returns an ordering between `self` and `other` values /// if one exists. - fn partial_cmp(&self, other: &Self) -> Option; + fn partial_cmp(&self, other: &Rhs) -> Option; /// This method tests less than (for `self` and `other`) and is used by the `<` operator. #[inline] - fn lt(&self, other: &Self) -> bool { + fn lt(&self, other: &Rhs) -> bool { match self.partial_cmp(other) { Some(Less) => true, _ => false, @@ -210,7 +210,7 @@ pub trait PartialOrd for Sized?: PartialEq { /// This method tests less than or equal to (`<=`). #[inline] - fn le(&self, other: &Self) -> bool { + fn le(&self, other: &Rhs) -> bool { match self.partial_cmp(other) { Some(Less) | Some(Equal) => true, _ => false, @@ -219,7 +219,7 @@ pub trait PartialOrd for Sized?: PartialEq { /// This method tests greater than (`>`). #[inline] - fn gt(&self, other: &Self) -> bool { + fn gt(&self, other: &Rhs) -> bool { match self.partial_cmp(other) { Some(Greater) => true, _ => false, @@ -228,7 +228,7 @@ pub trait PartialOrd for Sized?: PartialEq { /// This method tests greater than or equal to (`>=`). #[inline] - fn ge(&self, other: &Self) -> bool { + fn ge(&self, other: &Rhs) -> bool { match self.partial_cmp(other) { Some(Greater) | Some(Equal) => true, _ => false, diff --git a/src/libcore/lib.rs b/src/libcore/lib.rs index 56a8677306079..5ad9462daf274 100644 --- a/src/libcore/lib.rs +++ b/src/libcore/lib.rs @@ -59,6 +59,7 @@ #![allow(unknown_features)] #![feature(globs, intrinsics, lang_items, macro_rules, phase)] #![feature(simd, unsafe_destructor, slicing_syntax)] +#![feature(default_type_params)] #![deny(missing_docs)] mod macros; From 2578de9d6090210d9e94fd013190f387c8a88048 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Thu, 20 Nov 2014 19:32:04 -0500 Subject: [PATCH 18/83] Test PartialEq multidispatch --- src/test/run-pass/eq-multidispatch.rs | 37 +++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 src/test/run-pass/eq-multidispatch.rs diff --git a/src/test/run-pass/eq-multidispatch.rs b/src/test/run-pass/eq-multidispatch.rs new file mode 100644 index 0000000000000..018e8cddc71c6 --- /dev/null +++ b/src/test/run-pass/eq-multidispatch.rs @@ -0,0 +1,37 @@ +// Copyright 2014 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. + +#![feature(default_type_params)] + +#[deriving(PartialEq)] +struct Bar; +struct Baz; +struct Foo; +struct Fu; + +impl PartialEq for Baz { fn eq(&self, _: &Baz) -> bool { true } } + +impl PartialEq for Foo { fn eq(&self, _: &Fu) -> bool { true } } +impl PartialEq for Fu { fn eq(&self, _: &Foo) -> bool { true } } + +impl PartialEq for Foo { fn eq(&self, _: &Bar) -> bool { false } } +impl PartialEq for Bar { fn eq(&self, _: &Foo) -> bool { false } } + +fn main() { + assert!(Bar != Foo); + assert!(Foo != Bar); + + assert!(Bar == Bar); + + assert!(Baz == Baz); + + assert!(Foo == Fu); + assert!(Fu == Foo); +} From 851c7b5e0f76e32e5a1316befc7465bdc573ac43 Mon Sep 17 00:00:00 2001 From: Clark Gaebel Date: Tue, 2 Dec 2014 17:32:37 -0800 Subject: [PATCH 19/83] Fixed out of date comment on `copy_memory` --- src/libcore/slice.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/libcore/slice.rs b/src/libcore/slice.rs index 950f04a5d97e3..906cbd72104e9 100644 --- a/src/libcore/slice.rs +++ b/src/libcore/slice.rs @@ -1781,12 +1781,13 @@ pub mod bytes { /// Copies data from `src` to `dst` /// - /// `src` and `dst` must not overlap. Panics if the length of `dst` - /// is less than the length of `src`. + /// Panics if the length of `dst` is less than the length of `src`. #[inline] pub fn copy_memory(dst: &mut [u8], src: &[u8]) { let len_src = src.len(); assert!(dst.len() >= len_src); + // `dst` is unaliasable, so we know statically it doesn't overlap + // with `src`. unsafe { ptr::copy_nonoverlapping_memory(dst.as_mut_ptr(), src.as_ptr(), From ea6f628709a468dc70344cc8c59efb7aa1f70821 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 3 Dec 2014 09:16:00 +0100 Subject: [PATCH 20/83] libtest: get rid of dependency to ToJson deriving encodable + using json::PrettyEncoder removes the only ToJson trait implementation in the rust repository outside of libserialize --- src/libtest/lib.rs | 24 +++--------------------- 1 file changed, 3 insertions(+), 21 deletions(-) diff --git a/src/libtest/lib.rs b/src/libtest/lib.rs index fbc60c9b34257..049f2cc5603b6 100644 --- a/src/libtest/lib.rs +++ b/src/libtest/lib.rs @@ -51,8 +51,7 @@ use std::collections::TreeMap; use stats::Stats; use getopts::{OptGroup, optflag, optopt}; use regex::Regex; -use serialize::{json, Decodable}; -use serialize::json::{Json, ToJson}; +use serialize::{json, Decodable, Encodable}; use term::Terminal; use term::color::{Color, RED, YELLOW, GREEN, CYAN}; @@ -1100,17 +1099,6 @@ fn calc_result(desc: &TestDesc, task_succeeded: bool) -> TestResult { } } - -impl ToJson for Metric { - fn to_json(&self) -> json::Json { - let mut map = TreeMap::new(); - map.insert("value".to_string(), json::Json::F64(self.value)); - map.insert("noise".to_string(), json::Json::F64(self.noise)); - json::Json::Object(map) - } -} - - impl MetricMap { pub fn new() -> MetricMap { @@ -1138,14 +1126,8 @@ impl MetricMap { pub fn save(&self, p: &Path) -> io::IoResult<()> { let mut file = try!(File::create(p)); let MetricMap(ref map) = *self; - - // FIXME(pcwalton): Yuck. - let mut new_map = TreeMap::new(); - for (ref key, ref value) in map.iter() { - new_map.insert(key.to_string(), (*value).clone()); - } - - new_map.to_json().to_pretty_writer(&mut file) + let mut enc = json::PrettyEncoder::new(&mut file); + map.encode(&mut enc) } /// Compare against another MetricMap. Optionally compare all From 721370481264784eeda3d5c5fe092e03ed78ab1e Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Tue, 4 Nov 2014 02:52:36 -0500 Subject: [PATCH 21/83] New Guide: Ownership This replaces the previous "Lifetimes guide," since we are discussing things from an owernship perspective now. --- src/doc/guide-ownership.md | 973 ++++++++++++++++--------------------- 1 file changed, 431 insertions(+), 542 deletions(-) diff --git a/src/doc/guide-ownership.md b/src/doc/guide-ownership.md index 7a5c535827c25..c1180f7e6a93a 100644 --- a/src/doc/guide-ownership.md +++ b/src/doc/guide-ownership.md @@ -1,565 +1,454 @@ -% The Rust References and Lifetimes Guide - -# Introduction - -References are one of the more flexible and powerful tools available in -Rust. They can point anywhere: into the heap, stack, and even into the -interior of another data structure. A reference is as flexible as a C pointer -or C++ reference. - -Unlike C and C++ compilers, the Rust compiler includes special static -checks that ensure that programs use references safely. - -Despite their complete safety, a reference's representation at runtime -is the same as that of an ordinary pointer in a C program. They introduce zero -overhead. The compiler does all safety checks at compile time. - -Although references have rather elaborate theoretical underpinnings -(e.g. region pointers), the core concepts will be familiar to anyone -who has worked with C or C++. The best way to explain how they are -used—and their limitations—is probably just to work through several examples. - -# By example - -References, sometimes known as *borrowed pointers*, are only valid for -a limited duration. References never claim any kind of ownership -over the data that they point to. Instead, they are used for cases -where you would like to use data for a short time. - -Consider a simple struct type `Point`: - -~~~ -struct Point {x: f64, y: f64} -~~~ - -We can use this simple definition to allocate points in many different ways. For -example, in this code, each of these local variables contains a point, -but allocated in a different place: - -~~~ -# struct Point {x: f64, y: f64} -let on_the_stack : Point = Point {x: 3.0, y: 4.0}; -let on_the_heap : Box = box Point {x: 7.0, y: 9.0}; -~~~ - -Suppose we wanted to write a procedure that computed the distance between any -two points, no matter where they were stored. One option is to define a function -that takes two arguments of type `Point`—that is, it takes the points by value. -But if we define it this way, calling the function will cause the points to be -copied. For points, this is probably not so bad, but often copies are -expensive. So we'd like to define a function that takes the points just as -a reference. - -~~~ -# use std::num::Float; -# struct Point {x: f64, y: f64} -# fn sqrt(f: f64) -> f64 { 0.0 } -fn compute_distance(p1: &Point, p2: &Point) -> f64 { - let x_d = p1.x - p2.x; - let y_d = p1.y - p2.y; - (x_d * x_d + y_d * y_d).sqrt() +% The Rust Ownership Guide + +This guide presents Rust's ownership system. This is one of Rust's most unique +and compelling features, with which Rust developers should become quite +acquainted. Ownership is how Rust achieves its largest goal, memory safety. +The ownership system has a few distinct concepts: **ownership**, **borrowing**, +and **lifetimes**. We'll talk about each one in turn. + +# Meta + +Before we get to the details, two important notes about the ownership system. + +Rust has a focus on safety and speed. It accomplishes these goals through many +"zero cost abstractions," which means that in Rust, abstractions cost as little +as possible in order to make them work. The ownership system is a prime example +of a zero cost abstraction. All of the analysis we'll talk about in this guide +is _done at compile time_. You do not pay any run-time cost for any of these +features. + +However, this system does have a certain cost: learning curve. Many new users +to Rust experience something we like to call "fighting with the borrow +checker," where the Rust compiler refuses to compile a program that the author +thinks is valid. This often happens because the programmer's mental model of +how ownership should work doesn't match the actual rules that Rust implements. +You probably will experience similar things at first. There is good news, +however: more experienced Rust developers report that once they work with the +rules of the ownership system for a period of time, they fight the borrow +checker less and less. + +With that in mind, let's learn about ownership. + +# Ownership + +At its core, ownership is about 'resources.' For the purposes of the vast +majority of this guide, we will talk about a specific resource: memory. The +concept generalizes to any kind of resource, like a file handle, but to make it +more concrete, we'll focus on memory. + +When your program allocates some memory, it needs some way to deallocate that +memory. Imagine a function `foo` that allocates four bytes of memory, and then +never deallocates that memory. We call this problem 'leaking' memory, because +each time we call `foo`, we're allocating another four bytes. Eventually, with +enough calls to `foo`, we will run our system out of memory. That's no good. So +we need some way for `foo` to deallocate those four bytes. It's also important +that we don't deallocate too many times, either. Without getting into the +details, attempting to deallocate memory multiple times can lead to problems. +In other words, any time some memory is allocated, we need to make sure that we +deallocate that memory once and only once. Too many times is bad, not enough +times is bad. The counts must match. + +There's one other important detail with regards to allocating memory. Whenever +we request some amount of memory, what we are given is a handle to that memory. +This handle (often called a 'pointer', when we're referring to memory) is how +we interact with the allocated memory. As long as we have that handle, we can +do something with the memory. Once we're done with the handle, we're also done +with the memory, as we can't do anything useful without a handle to it. + +Historically, systems programming languages require you to track these +allocations, deallocations, and handles yourself. For example, if we want some +memory from the heap in a language like C, we do this: + +```c +{ + int *x = malloc(sizeof(int)); + + // we can now do stuff with our handle x + *x = 5; + + free(x); } -~~~ - -Now we can call `compute_distance()`: - -~~~ -# struct Point {x: f64, y: f64} -# let on_the_stack : Point = Point{x: 3.0, y: 4.0}; -# let on_the_heap : Box = box Point{x: 7.0, y: 9.0}; -# fn compute_distance(p1: &Point, p2: &Point) -> f64 { 0.0 } -compute_distance(&on_the_stack, &*on_the_heap); -~~~ - -Here, the `&` operator takes the address of the variable -`on_the_stack`; this is because `on_the_stack` has the type `Point` -(that is, a struct value) and we have to take its address to get a -value. We also call this _borrowing_ the local variable -`on_the_stack`, because we have created an alias: that is, another -name for the same data. - -Likewise, in the case of `on_the_heap`, -the `&` operator is used in conjunction with the `*` operator -to take a reference to the contents of the box. - -Whenever a caller lends data to a callee, there are some limitations on what -the caller can do with the original. For example, if the contents of a -variable have been lent out, you cannot send that variable to another task. In -addition, the compiler will reject any code that might cause the borrowed -value to be freed or overwrite its component fields with values of different -types (I'll get into what kinds of actions those are shortly). This rule -should make intuitive sense: you must wait for a borrower to return the value -that you lent it (that is, wait for the reference to go out of scope) -before you can make full use of it again. - -# Other uses for the & operator - -In the previous example, the value `on_the_stack` was defined like so: - -~~~ -# struct Point {x: f64, y: f64} -let on_the_stack: Point = Point {x: 3.0, y: 4.0}; -~~~ - -This declaration means that code can only pass `Point` by value to other -functions. As a consequence, we had to explicitly take the address of -`on_the_stack` to get a reference. Sometimes however it is more -convenient to move the & operator into the definition of `on_the_stack`: - -~~~ -# struct Point {x: f64, y: f64} -let on_the_stack2: &Point = &Point {x: 3.0, y: 4.0}; -~~~ - -Applying `&` to an rvalue (non-assignable location) is just a convenient -shorthand for creating a temporary and taking its address. A more verbose -way to write the same code is: - -~~~ -# struct Point {x: f64, y: f64} -let tmp = Point {x: 3.0, y: 4.0}; -let on_the_stack2 : &Point = &tmp; -~~~ - -# Taking the address of fields - -The `&` operator is not limited to taking the address of -local variables. It can also take the address of fields or -individual array elements. For example, consider this type definition -for `Rectangle`: - -~~~ -struct Point {x: f64, y: f64} // as before -struct Size {w: f64, h: f64} // as before -struct Rectangle {origin: Point, size: Size} -~~~ - -Now, as before, we can define rectangles in a few different ways: - -~~~ -# struct Point {x: f64, y: f64} -# struct Size {w: f64, h: f64} // as before -# struct Rectangle {origin: Point, size: Size} -let rect_stack = &Rectangle {origin: Point {x: 1.0, y: 2.0}, - size: Size {w: 3.0, h: 4.0}}; -let rect_heap = box Rectangle {origin: Point {x: 5.0, y: 6.0}, - size: Size {w: 3.0, h: 4.0}}; -~~~ - -In each case, we can extract out individual subcomponents with the `&` -operator. For example, I could write: - -~~~ -# struct Point {x: f64, y: f64} // as before -# struct Size {w: f64, h: f64} // as before -# struct Rectangle {origin: Point, size: Size} -# let rect_stack = &Rectangle {origin: Point {x: 1.0, y: 2.0}, size: Size {w: 3.0, h: 4.0}}; -# let rect_heap = box Rectangle {origin: Point {x: 5.0, y: 6.0}, size: Size {w: 3.0, h: 4.0}}; -# fn compute_distance(p1: &Point, p2: &Point) -> f64 { 0.0 } -compute_distance(&rect_stack.origin, &rect_heap.origin); -~~~ - -which would borrow the field `origin` from the rectangle on the stack -as well as from the owned box, and then compute the distance between them. +``` -# Lifetimes +The call to `malloc` allocates some memory. The call to `free` deallocates the +memory. There's also bookkeeping about allocating the correct amount of memory. + +Rust combines these two aspects of allocating memory (and other resources) into +a concept called 'ownership.' Whenever we request some memory, that handle we +receive is called the 'owning handle.' Whenever that handle goes out of scope, +Rust knows that you cannot do anything with the memory anymore, and so +therefore deallocates the memory for you. Here's the equivalent example in +Rust: -We’ve seen a few examples of borrowing data. To this point, we’ve glossed -over issues of safety. As stated in the introduction, at runtime a reference -is simply a pointer, nothing more. Therefore, avoiding C's problems with -dangling pointers requires a compile-time safety check. - -The basis for the check is the notion of _lifetimes_. A lifetime is a -static approximation of the span of execution during which the pointer -is valid: it always corresponds to some expression or block within the -program. - -The compiler will only allow a borrow *if it can guarantee that the data will -not be reassigned or moved for the lifetime of the pointer*. This does not -necessarily mean that the data is stored in immutable memory. For example, -the following function is legal: - -~~~ -# fn some_condition() -> bool { true } -# struct Foo { f: int } -fn example3() -> int { - let mut x = box Foo {f: 3}; - if some_condition() { - let y = &x.f; // -+ L - return *y; // | - } // -+ - x = box Foo {f: 4}; - // ... -# return 0; +```rust +{ + let x = box 5i; } -~~~ - -Here, the interior of the variable `x` is being borrowed -and `x` is declared as mutable. However, the compiler can prove that -`x` is not assigned anywhere in the lifetime L of the variable -`y`. Therefore, it accepts the function, even though `x` is mutable -and in fact is mutated later in the function. - -It may not be clear why we are so concerned about mutating a borrowed -variable. The reason is that the runtime system frees any box -_as soon as its owning reference changes or goes out of -scope_. Therefore, a program like this is illegal (and would be -rejected by the compiler): - -~~~ {.ignore} -fn example3() -> int { - let mut x = box X {f: 3}; - let y = &x.f; - x = box X {f: 4}; // Error reported here. - *y +``` + +The `box` keyword creates a `Box` (specifically `Box` in this case) by +allocating a small segment of memory on the heap with enough space to fit an +`int`. But where in the code is the box deallocated? We said before that we +must have a deallocation for each allocation. Rust handles this for you. It +knows that our handle, `x`, is the owning reference to our box. Rust knows that +`x` will go out of scope at the end of the block, and so it inserts a call to +deallocate the memory at the end of the scope. Because the compiler does this +for us, it's impossible to forget. We always exaclty one deallocations paired +with each of our allocations. + +This is pretty straightforward, but what happens when we want to pass our box +to a function? Let's look at some code: + +```rust +fn main() { + let x = box 5i; + + add_one(x); } -~~~ - -To make this clearer, consider this diagram showing the state of -memory immediately before the re-assignment of `x`: - -~~~ {.text} - Stack Exchange Heap - - x +-------------+ - | box {f:int} | ----+ - y +-------------+ | - | &int | ----+ - +-------------+ | +---------+ - +--> | f: 3 | - +---------+ -~~~ - -Once the reassignment occurs, the memory will look like this: - -~~~ {.text} - Stack Exchange Heap - - x +-------------+ +---------+ - | box {f:int} | -------> | f: 4 | - y +-------------+ +---------+ - | &int | ----+ - +-------------+ | +---------+ - +--> | (freed) | - +---------+ -~~~ - -Here you can see that the variable `y` still points at the old `f` -property of Foo, which has been freed. - -In fact, the compiler can apply the same kind of reasoning to any -memory that is (uniquely) owned by the stack frame. So we could -modify the previous example to introduce additional owned pointers -and structs, and the compiler will still be able to detect possible -mutations. This time, we'll use an analogy to illustrate the concept. - -~~~ {.ignore} -fn example3() -> int { - struct House { owner: Box } - struct Person { age: int } - - let mut house = box House { - owner: box Person {age: 30} - }; - - let owner_age = &house.owner.age; - house = box House {owner: box Person {age: 40}}; // Error reported here. - house.owner = box Person {age: 50}; // Error reported here. - *owner_age + +fn add_one(mut num: Box) { + *num += 1; } -~~~ - -In this case, two errors are reported, one when the variable `house` is -modified and another when `house.owner` is modified. Either modification would -invalidate the pointer `owner_age`. - -# Borrowing and enums - -The previous example showed that the type system forbids any mutations -of owned boxed values while they are being borrowed. In general, the type -system also forbids borrowing a value as mutable if it is already being -borrowed - either as a mutable reference or an immutable one. This restriction -prevents pointers from pointing into freed memory. There is one other -case where the compiler must be very careful to ensure that pointers -remain valid: pointers into the interior of an `enum`. - -Let’s look at the following `shape` type that can represent both rectangles -and circles: - -~~~ -struct Point {x: f64, y: f64}; // as before -struct Size {w: f64, h: f64}; // as before -enum Shape { - Circle(Point, f64), // origin, radius - Rectangle(Point, Size) // upper-left, dimensions +``` + +This code works, but it's not ideal. For example, let's add one more line of +code, where we print out the value of `x`: + +```{rust,ignore} +fn main() { + let x = box 5i; + + add_one(x); + + println!("{}", x); } -~~~ - -Now we might write a function to compute the area of a shape. This -function takes a reference to a shape, to avoid the need for -copying. - -~~~ -# struct Point {x: f64, y: f64}; // as before -# struct Size {w: f64, h: f64}; // as before -# enum Shape { -# Circle(Point, f64), // origin, radius -# Rectangle(Point, Size) // upper-left, dimensions -# } -fn compute_area(shape: &Shape) -> f64 { - match *shape { - Shape::Circle(_, radius) => std::f64::consts::PI * radius * radius, - Shape::Rectangle(_, ref size) => size.w * size.h - } + +fn add_one(mut num: Box) { + *num += 1; } -~~~ - -The first case matches against circles. Here, the pattern extracts the -radius from the shape variant and the action uses it to compute the -area of the circle. - -The second match is more interesting. Here we match against a -rectangle and extract its size: but rather than copy the `size` -struct, we use a by-reference binding to create a pointer to it. In -other words, a pattern binding like `ref size` binds the name `size` -to a pointer of type `&size` into the _interior of the enum_. - -To make this more clear, let's look at a diagram of memory layout in -the case where `shape` points at a rectangle: - -~~~ {.text} -Stack Memory - -+-------+ +---------------+ -| shape | ------> | rectangle( | -+-------+ | {x: f64, | -| size | -+ | y: f64}, | -+-------+ +----> | {w: f64, | - | h: f64}) | - +---------------+ -~~~ - -Here you can see that rectangular shapes are composed of five words of -memory. The first is a tag indicating which variant this enum is -(`rectangle`, in this case). The next two words are the `x` and `y` -fields for the point and the remaining two are the `w` and `h` fields -for the size. The binding `size` is then a pointer into the inside of -the shape. - -Perhaps you can see where the danger lies: if the shape were somehow -to be reassigned, perhaps to a circle, then although the memory used -to store that shape value would still be valid, _it would have a -different type_! The following diagram shows what memory would look -like if code overwrote `shape` with a circle: - -~~~ {.text} -Stack Memory - -+-------+ +---------------+ -| shape | ------> | circle( | -+-------+ | {x: f64, | -| size | -+ | y: f64}, | -+-------+ +----> | f64) | - | | - +---------------+ -~~~ - -As you can see, the `size` pointer would be pointing at a `f64` -instead of a struct. This is not good: dereferencing the second field -of a `f64` as if it were a struct with two fields would be a memory -safety violation. - -So, in fact, for every `ref` binding, the compiler will impose the -same rules as the ones we saw for borrowing the interior of an owned -box: it must be able to guarantee that the `enum` will not be -overwritten for the duration of the borrow. In fact, the compiler -would accept the example we gave earlier. The example is safe because -the shape pointer has type `&Shape`, which means "reference to -immutable memory containing a `shape`". If, however, the type of that -pointer were `&mut Shape`, then the ref binding would be ill-typed. -Just as with owned boxes, the compiler will permit `ref` bindings -into data owned by the stack frame even if the data are mutable, -but otherwise it requires that the data reside in immutable memory. - -# Returning references - -So far, all of the examples we have looked at, use references in a -“downward” direction. That is, a method or code block creates a -reference, then uses it within the same scope. It is also -possible to return references as the result of a function, but -as we'll see, doing so requires some explicit annotation. - -We could write a subroutine like this: - -~~~ -struct Point {x: f64, y: f64} -fn get_x<'r>(p: &'r Point) -> &'r f64 { &p.x } -~~~ - -Here, the function `get_x()` returns a pointer into the structure it -was given. The type of the parameter (`&'r Point`) and return type -(`&'r f64`) both use a new syntactic form that we have not seen so -far. Here the identifier `r` names the lifetime of the pointer -explicitly. So in effect, this function declares that it takes a -pointer with lifetime `r` and returns a pointer with that same -lifetime. - -In general, it is only possible to return references if they -are derived from a parameter to the procedure. In that case, the -pointer result will always have the same lifetime as one of the -parameters; named lifetimes indicate which parameter that -is. - -In the previous code samples, function parameter types did not include a -lifetime name. The compiler simply creates a fresh name for the lifetime -automatically: that is, the lifetime name is guaranteed to refer to a distinct -lifetime from the lifetimes of all other parameters. - -Named lifetimes that appear in function signatures are conceptually -the same as the other lifetimes we have seen before, but they are a bit -abstract: they don’t refer to a specific expression within `get_x()`, -but rather to some expression within the *caller of `get_x()`*. The -lifetime `r` is actually a kind of *lifetime parameter*: it is defined -by the caller to `get_x()`, just as the value for the parameter `p` is -defined by that caller. - -In any case, whatever the lifetime of `r` is, the pointer produced by -`&p.x` always has the same lifetime as `p` itself: a pointer to a -field of a struct is valid as long as the struct is valid. Therefore, -the compiler accepts the function `get_x()`. - -In general, if you borrow a struct or box to create a -reference, it will only be valid within the function -and cannot be returned. This is why the typical way to return references -is to take references as input (the only other case in -which it can be legal to return a reference is if it -points at a static constant). - -# Named lifetimes - -Lifetimes can be named and referenced. For example, the special lifetime -`'static`, which does not go out of scope, can be used to create global -variables and communicate between tasks (see the manual for use cases). - -## Parameter Lifetimes - -Named lifetimes allow for grouping of parameters by lifetime. -For example, consider this function: - -~~~ -# struct Point {x: f64, y: f64}; // as before -# struct Size {w: f64, h: f64}; // as before -# enum Shape { -# Circle(Point, f64), // origin, radius -# Rectangle(Point, Size) // upper-left, dimensions -# } -# fn compute_area(shape: &Shape) -> f64 { 0.0 } -fn select<'r, T>(shape: &'r Shape, threshold: f64, - a: &'r T, b: &'r T) -> &'r T { - if compute_area(shape) > threshold {a} else {b} +``` + +This does not compile, and gives us an error: + +```{notrust,ignore} +error: use of moved value: `x` + println!("{}", x); + ^ +``` + +Remember, we need one deallocation for every allocation. When we try to pass +our box to `add_one`, we would have two handles to the memory: `x` in `main`, +and `num` in `add_one`. If we deallocated the memory when each handle went out +of scope, we would have two deallocations and one allocation, and that's wrong. +So when we call `add_one`, Rust defines `num` as the owner of the handle. And +so, now that we've given ownership to `num`, `x` is invalid. `x`'s value has +"moved" from `x` to `num`. Hence the error: use of moved value `x`. + +To fix this, we can have `add_one` give ownership back when it's done with the +box: + +```rust +fn main() { + let x = box 5i; + + let y = add_one(x); + + println!("{}", y); } -~~~ - -This function takes three references and assigns each the same -lifetime `r`. In practice, this means that, in the caller, the -lifetime `r` will be the *intersection of the lifetime of the three -region parameters*. This may be overly conservative, as in this -example: - -~~~ -# struct Point {x: f64, y: f64}; // as before -# struct Size {w: f64, h: f64}; // as before -# enum Shape { -# Circle(Point, f64), // origin, radius -# Rectangle(Point, Size) // upper-left, dimensions -# } -# fn compute_area(shape: &Shape) -> f64 { 0.0 } -# fn select<'r, T>(shape: &Shape, threshold: f64, -# a: &'r T, b: &'r T) -> &'r T { -# if compute_area(shape) > threshold {a} else {b} + +fn add_one(mut num: Box) -> Box { + *num += 1; + + num +} +``` + +This code will compile and run just fine. Now, we return a `box`, and so the +ownership is transferred back to `y` in `main`. We only have ownership for the +duration of our function before giving it back. This pattern is very common, +and so Rust introduces a concept to describe a handle which temporarily refers +to something another handle owns. It's called "borrowing," and it's done with +"references", designated by the `&` symbol. + +# Borrowing + +Here's the current state of our `add_one` function: + +```rust +fn add_one(mut num: Box) -> Box { + *num += 1; + + num +} +``` + +This function takes ownership, because it takes a `Box`, which owns its +contents. But then we give ownership right back. + +In the physical world, you can give one of your possessions to someone for a +short period of time. You still own your posession, you're just letting someone +else use it for a while. We call that 'lending' something to someone, and that +person is said to be 'borrowing' that something from you. + +Rust's ownershp system also allows an owner to lend out a handle for a limited +period. This is also called 'borrowing.' Here's a version of `add_one` which +borrows its argument rather than taking ownership: + +```rust +fn add_one(num: &mut int) { + *num += 1; +} +``` + +This function borrows an `int` from its caller, and then increments it. When +the function is over, and `num` goes out of scope, the borrow is over. + +# Lifetimes + +Lending out a reference to a resource that someone else owns can be +complicated, however. For example, imagine this set of operations: + +1. I aquire a handle to some kind of resource. +2. I lend you a reference to the resource. +3. I decide I'm done with the resource, and deallocate it, while you still have + your reference. +4. You decide to use the resource. + +Uh oh! Your reference is pointing to an invalid resource. This is called a +"dangling pointer" or "use after free," when the resource is memory. + +To fix this, we have to make sure that step four never happens after step +three. The ownership system in Rust does this through a concept called +"lifetimes," which describe the scope that a reference is valid for. + +Let's look at that function which borrows an `int` again: + +```rust +fn add_one(num: &int) -> int { + *num + 1 +} +``` + +Rust has a feature called 'lifetime elision,' which allows you to not write +lifetime annotations in certain circumstances. This is one of them. Without +eliding the liftimes, `add_one` looks like this: + +```rust +fn add_one<'a>(num: &'a int) -> int { + *num + 1 +} +``` + +The `'a` is called a **lifetime**. Most lifetimes are used in places where +short names like `'a`, `'b` and `'c` are clearest, but it's often useful to +have more descriptive names. Let's dig into the syntax in a bit more detail: + +```{rust,ignore} +fn add_one<'a>(...) +``` + +This part _declares_ our lifetimes. This says that `add_one` has one lifetime, +`'a`. If we had two, it would look like this: + +```{rust,ignore} +fn add_two<'a, 'b>(...) +``` + +Then in our parameter list, we use the liftimes we've named: + +```{rust,ignore} +...(num: &'a int) -> ... +``` + +If you compare `&int` to `&'a int`, they're the same, it's just that the +lifetime `'a` has snuck in between the `&` and the `int`. We read `&int` as "a +reference to an int" and `&'a int` as "a reference to an int with the lifetime 'a.'" + +Why do lifetimes matter? Well, for example, here's some code: + +```rust +struct Foo<'a> { + x: &'a int, +} + +fn main() { + let y = &5i; // this is the same as `let _y = 5; let y = &_y; + let f = Foo { x: y }; + + println!("{}", f.x); +} +``` + +As you can see, `struct`s can also have liftimes. In a similar way to functions, + +```{rust} +struct Foo<'a> { +# x: &'a int, # } - // -+ r -fn select_based_on_unit_circle<'r, T>( // |-+ B - threshold: f64, a: &'r T, b: &'r T) -> &'r T { // | | - // | | - let shape = Shape::Circle(Point {x: 0., y: 0.}, 1.); // | | - select(&shape, threshold, a, b) // | | -} // |-+ - // -+ -~~~ - -In this call to `select()`, the lifetime of the first parameter shape -is B, the function body. Both of the second two parameters `a` and `b` -share the same lifetime, `r`, which is a lifetime parameter of -`select_based_on_unit_circle()`. The caller will infer the -intersection of these two lifetimes as the lifetime of the returned -value, and hence the return value of `select()` will be assigned a -lifetime of B. This will in turn lead to a compilation error, because -`select_based_on_unit_circle()` is supposed to return a value with the -lifetime `r`. - -To address this, we can modify the definition of `select()` to -distinguish the lifetime of the first parameter from the lifetime of -the latter two. After all, the first parameter is not being -returned. Here is how the new `select()` might look: - -~~~ -# struct Point {x: f64, y: f64}; // as before -# struct Size {w: f64, h: f64}; // as before -# enum Shape { -# Circle(Point, f64), // origin, radius -# Rectangle(Point, Size) // upper-left, dimensions +``` + +declares a lifetime, and + +```rust +# struct Foo<'a> { +x: &'a int, # } -# fn compute_area(shape: &Shape) -> f64 { 0.0 } -fn select<'r, 'tmp, T>(shape: &'tmp Shape, threshold: f64, - a: &'r T, b: &'r T) -> &'r T { - if compute_area(shape) > threshold {a} else {b} +``` + +uses it. So why do we need a liftime here? We need to ensure that any reference +to a `Foo` cannot outlive the reference to an `int` it contains. + +## Thinking in scopes + +A way to think about lifetimes is to visualize the scope that a reference is +valid for. For example: + +```rust +fn main() { + let y = &5i; // -+ y goes into scope + // | + // stuff // | + // | +} // -+ y goes out of scope +``` + +Adding in our `Foo`: + +```rust +struct Foo<'a> { + x: &'a int, } -~~~ - -Here you can see that `shape`'s lifetime is now named `tmp`. The -parameters `a`, `b`, and the return value all have the lifetime `r`. -However, since the lifetime `tmp` is not returned, it would be more -concise to just omit the named lifetime for `shape` altogether: - -~~~ -# struct Point {x: f64, y: f64}; // as before -# struct Size {w: f64, h: f64}; // as before -# enum Shape { -# Circle(Point, f64), // origin, radius -# Rectangle(Point, Size) // upper-left, dimensions -# } -# fn compute_area(shape: &Shape) -> f64 { 0.0 } -fn select<'r, T>(shape: &Shape, threshold: f64, - a: &'r T, b: &'r T) -> &'r T { - if compute_area(shape) > threshold {a} else {b} + +fn main() { + let y = &5i; // -+ y goes into scope + let f = Foo { x: y }; // -+ f goes into scope + // stuff // | + // | +} // -+ f & y go out of scope +``` + +Our `f` lives within the scope of `y`, so everything works. What if it didn't? +This code won't work: + +```{rust,ignore} +struct Foo<'a> { + x: &'a int, +} + +fn main() { + let x; // -+ x goes into scope + // | + { // | + let y = &5i; // ---+ y goes into scope + let f = Foo { x: y }; // ---+ f goes into scope + x = &f.x; // | | error here + } // ---+ f & y go out of scope + // | + println!("{}", x); // | +} // -+ x goes out of scope +``` + +Whew! As you can see here, the scopes of `f` and `y` are smaller than the scope +of `x`. But when we do `x = &f.x`, we make `x` a reference to something that's +about to go out of scope. + +Named lifetimes are a way of giving these scopes a name. Giving something a +name is the first step towards being able to talk about it. + +## 'static + +The lifetime named 'static' is a special lifetime. It signals that something +has the lifetime of the entire program. Most Rust programmers first come across +`'static` when dealing with strings: + +```rust +let x: &'static str = "Hello, world."; +``` + +String literals have the type `&'static str` because the reference is always +alive: they are baked into the data segment of the final binary. Another +example are globals: + +```rust +static FOO: int = 5i; +let x: &'static int = &FOO; +``` + +This adds an `int` to the data segment of the binary, and FOO is a reference to +it. + +# Shared Ownership + +In all the examples we've considered so far, we've assumed that each handle has +a singular owner. But sometimes, this doesn't work. Consider a car. Cars have +four wheels. We would want a wheel to know which car it was attached to. But +this won't work: + +```{rust,ignore} +struct Car { + name: String, +} + +struct Wheel { + size: int, + owner: Car, } -~~~ -This is equivalent to the previous definition. +fn main() { + let car = Car { name: "DeLorian".to_string() }; + + for _ in range(0u, 4) { + Wheel { size: 360, owner: car }; + } +} +``` + +We try to make four `Wheel`s, each with a `Car` that it's attached to. But the +compiler knows that on the second iteration of the loop, there's a problem: + +```{notrust,ignore} +error: use of moved value: `car` + Wheel { size: 360, owner: car }; + ^~~ +note: `car` moved here because it has type `Car`, which is non-copyable + Wheel { size: 360, owner: car }; + ^~~ +``` + +We need our `Car` to be pointed to by multiple `Wheel`s. We can't do that with +`Box`, because it has a single owner. We can do t with `Rc` instead: + +```rust +use std::rc::Rc; + +struct Car { + name: String, +} + +struct Wheel { + size: int, + owner: Rc, +} -## Labeled Control Structures +fn main() { + let car = Car { name: "DeLorian".to_string() }; -Named lifetime notation can also be used to control the flow of execution: + let car_owner = Rc::new(car); -~~~ -'h: for i in range(0u, 10) { - 'g: loop { - if i % 2 == 0 { continue 'h; } - if i == 9 { break 'h; } - break 'g; + for _ in range(0u, 4) { + Wheel { size: 360, owner: car_owner.clone() }; } } -~~~ +``` -> *Note:* Labelled breaks are not currently supported within `while` loops. +We wrap our `Car` in an `Rc`, getting an `Rc`, and then use the +`clone()` method to make new references. We've also changed our `Wheel` to have +an `Rc` rather than just a `Car`. -Named labels are hygienic and can be used safely within macros. -See the macros guide section on hygiene for more details. +This is the simplest kind of multiple ownership possible. For example, there's +also `Arc`, which uses more expensive atomic instructions to be the +thread-safe counterpart of `Rc`. -# Conclusion +# Related Resources -So there you have it: a (relatively) brief tour of the lifetime -system. For more details, we refer to the (yet to be written) reference -document on references, which will explain the full notation -and give more examples. +Coming Soon. From 861e11ceebf862f15917c4c36a0d2a43c88680d3 Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Wed, 3 Dec 2014 03:29:57 -0500 Subject: [PATCH 22/83] Remove outdated comment. https://github.com/rust-lang/rust/pull/19472#issuecomment-65370278 --- src/libsyntax/feature_gate.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index 7453da6374e00..5d984678b7f7f 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -32,9 +32,7 @@ use parse::token; use std::slice; -/// This is a list of all known features since the beginning of time. This list -/// can never shrink, it may only be expanded (in order to prevent old programs -/// from failing to compile). The status of each feature may change, however. +// if you change this list without updating src/doc/reference.md, @cmr will be sad static KNOWN_FEATURES: &'static [(&'static str, Status)] = &[ ("globs", Active), ("macro_rules", Active), @@ -73,8 +71,6 @@ static KNOWN_FEATURES: &'static [(&'static str, Status)] = &[ ("if_let", Active), ("while_let", Active), - // if you change this list without updating src/doc/reference.md, cmr will be sad - // A temporary feature gate used to enable parser extensions needed // to bootstrap fix for #5723. ("issue_5723_bootstrap", Accepted), From 2840d58dab0144c5589b60322c4f681bd8052aba Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Fri, 21 Nov 2014 00:14:05 -0500 Subject: [PATCH 23/83] Overload the `==` operator - String == &str == CowString - Vec == &[T] == &mut [T] == [T, ..N] == CowVec - InternedString == &str --- src/libcollections/string.rs | 45 +++++++++++++++++++++++++- src/libcollections/vec.rs | 63 +++++++++++++++++++++++++++++++++--- src/libcore/array.rs | 27 ++++++++++++++-- src/libcore/borrow.rs | 7 ++-- src/libcore/cmp.rs | 26 +++++++++++---- src/libcore/iter.rs | 12 +++++-- src/libcore/slice.rs | 6 ++-- src/libsyntax/parse/token.rs | 22 +++++++++++++ 8 files changed, 187 insertions(+), 21 deletions(-) diff --git a/src/libcollections/string.rs b/src/libcollections/string.rs index 38b67fbd74451..180978b3d77b6 100644 --- a/src/libcollections/string.rs +++ b/src/libcollections/string.rs @@ -30,7 +30,7 @@ use str::{CharRange, CowString, FromStr, StrAllocating, Owned}; use vec::{DerefVec, Vec, as_vec}; /// A growable string stored as a UTF-8 encoded buffer. -#[deriving(Clone, PartialEq, PartialOrd, Eq, Ord)] +#[deriving(Clone, PartialOrd, Eq, Ord)] #[stable] pub struct String { vec: Vec, @@ -738,6 +738,49 @@ impl Extend for String { } } +impl PartialEq for String { + #[inline] + fn eq(&self, other: &String) -> bool { PartialEq::eq(&**self, &**other) } + #[inline] + fn ne(&self, other: &String) -> bool { PartialEq::ne(&**self, &**other) } +} + +macro_rules! impl_eq { + ($lhs:ty, $rhs: ty) => { + impl<'a> PartialEq<$rhs> for $lhs { + #[inline] + fn eq(&self, other: &$rhs) -> bool { PartialEq::eq(&**self, &**other) } + #[inline] + fn ne(&self, other: &$rhs) -> bool { PartialEq::ne(&**self, &**other) } + } + + impl<'a> PartialEq<$lhs> for $rhs { + #[inline] + fn eq(&self, other: &$lhs) -> bool { PartialEq::eq(&**self, &**other) } + #[inline] + fn ne(&self, other: &$lhs) -> bool { PartialEq::ne(&**self, &**other) } + } + + } +} + +impl_eq!(String, &'a str) +impl_eq!(CowString<'a>, String) + +impl<'a, 'b> PartialEq<&'b str> for CowString<'a> { + #[inline] + fn eq(&self, other: &&'b str) -> bool { PartialEq::eq(&**self, &**other) } + #[inline] + fn ne(&self, other: &&'b str) -> bool { PartialEq::ne(&**self, &**other) } +} + +impl<'a, 'b> PartialEq> for &'b str { + #[inline] + fn eq(&self, other: &CowString<'a>) -> bool { PartialEq::eq(&**self, &**other) } + #[inline] + fn ne(&self, other: &CowString<'a>) -> bool { PartialEq::ne(&**self, &**other) } +} + #[experimental = "waiting on Str stabilization"] impl Str for String { #[inline] diff --git a/src/libcollections/vec.rs b/src/libcollections/vec.rs index 5edd3d0b780be..8d98df8b20062 100644 --- a/src/libcollections/vec.rs +++ b/src/libcollections/vec.rs @@ -535,14 +535,69 @@ impl Extend for Vec { } } -#[unstable = "waiting on PartialEq stability"] -impl PartialEq for Vec { +impl PartialEq> for Vec where A: PartialEq { #[inline] - fn eq(&self, other: &Vec) -> bool { - self.as_slice() == other.as_slice() + fn eq(&self, other: &Vec) -> bool { PartialEq::eq(&**self, &**other) } + #[inline] + fn ne(&self, other: &Vec) -> bool { PartialEq::ne(&**self, &**other) } +} + +macro_rules! impl_eq { + ($lhs:ty, $rhs:ty) => { + impl<'b, A, B> PartialEq<$rhs> for $lhs where A: PartialEq { + #[inline] + fn eq(&self, other: &$rhs) -> bool { PartialEq::eq(&**self, &**other) } + #[inline] + fn ne(&self, other: &$rhs) -> bool { PartialEq::ne(&**self, &**other) } + } + + impl<'b, A, B> PartialEq<$lhs> for $rhs where B: PartialEq { + #[inline] + fn eq(&self, other: &$lhs) -> bool { PartialEq::eq(&**self, &**other) } + #[inline] + fn ne(&self, other: &$lhs) -> bool { PartialEq::ne(&**self, &**other) } + } + } +} + +impl_eq!(Vec, &'b [B]) +impl_eq!(Vec, &'b mut [B]) + +impl<'a, A, B> PartialEq> for CowVec<'a, A> where A: PartialEq + Clone { + #[inline] + fn eq(&self, other: &Vec) -> bool { PartialEq::eq(&**self, &**other) } + #[inline] + fn ne(&self, other: &Vec) -> bool { PartialEq::ne(&**self, &**other) } +} + +impl<'a, A, B> PartialEq> for Vec where A: Clone, B: PartialEq { + #[inline] + fn eq(&self, other: &CowVec<'a, A>) -> bool { PartialEq::eq(&**self, &**other) } + #[inline] + fn ne(&self, other: &CowVec<'a, A>) -> bool { PartialEq::ne(&**self, &**other) } +} + +macro_rules! impl_eq_for_cowvec { + ($rhs:ty) => { + impl<'a, 'b, A, B> PartialEq<$rhs> for CowVec<'a, A> where A: PartialEq + Clone { + #[inline] + fn eq(&self, other: &$rhs) -> bool { PartialEq::eq(&**self, &**other) } + #[inline] + fn ne(&self, other: &$rhs) -> bool { PartialEq::ne(&**self, &**other) } + } + + impl<'a, 'b, A, B> PartialEq> for $rhs where A: Clone, B: PartialEq { + #[inline] + fn eq(&self, other: &CowVec<'a, A>) -> bool { PartialEq::eq(&**self, &**other) } + #[inline] + fn ne(&self, other: &CowVec<'a, A>) -> bool { PartialEq::ne(&**self, &**other) } + } } } +impl_eq_for_cowvec!(&'b [B]) +impl_eq_for_cowvec!(&'b mut [B]) + #[unstable = "waiting on PartialOrd stability"] impl PartialOrd for Vec { #[inline] diff --git a/src/libcore/array.rs b/src/libcore/array.rs index 60765e82cb476..ffaf35414ea0c 100644 --- a/src/libcore/array.rs +++ b/src/libcore/array.rs @@ -18,6 +18,7 @@ use clone::Clone; use cmp::{PartialEq, Eq, PartialOrd, Ord, Ordering}; use fmt; use kinds::Copy; +use ops::Deref; use option::Option; // macro for implementing n-ary tuple functions and operations @@ -39,17 +40,37 @@ macro_rules! array_impls { } #[unstable = "waiting for PartialEq to stabilize"] - impl PartialEq for [T, ..$N] { + impl PartialEq<[B, ..$N]> for [A, ..$N] where A: PartialEq { #[inline] - fn eq(&self, other: &[T, ..$N]) -> bool { + fn eq(&self, other: &[B, ..$N]) -> bool { self[] == other[] } #[inline] - fn ne(&self, other: &[T, ..$N]) -> bool { + fn ne(&self, other: &[B, ..$N]) -> bool { self[] != other[] } } + impl<'a, A, B, Rhs> PartialEq for [A, ..$N] where + A: PartialEq, + Rhs: Deref<[B]>, + { + #[inline(always)] + fn eq(&self, other: &Rhs) -> bool { PartialEq::eq(self[], &**other) } + #[inline(always)] + fn ne(&self, other: &Rhs) -> bool { PartialEq::ne(self[], &**other) } + } + + impl<'a, A, B, Lhs> PartialEq<[B, ..$N]> for Lhs where + A: PartialEq, + Lhs: Deref<[A]> + { + #[inline(always)] + fn eq(&self, other: &[B, ..$N]) -> bool { PartialEq::eq(&**self, other[]) } + #[inline(always)] + fn ne(&self, other: &[B, ..$N]) -> bool { PartialEq::ne(&**self, other[]) } + } + #[unstable = "waiting for Eq to stabilize"] impl Eq for [T, ..$N] { } diff --git a/src/libcore/borrow.rs b/src/libcore/borrow.rs index 06fda8d60928f..b88fb914dd8b0 100644 --- a/src/libcore/borrow.rs +++ b/src/libcore/borrow.rs @@ -196,9 +196,12 @@ impl<'a, T, Sized? B> Ord for Cow<'a, T, B> where B: Ord + ToOwned { } } -impl<'a, T, Sized? B> PartialEq for Cow<'a, T, B> where B: PartialEq + ToOwned { +impl<'a, 'b, T, U, Sized? B, Sized? C> PartialEq> for Cow<'a, T, B> where + B: PartialEq + ToOwned, + C: ToOwned, +{ #[inline] - fn eq(&self, other: &Cow<'a, T, B>) -> bool { + fn eq(&self, other: &Cow<'b, U, C>) -> bool { PartialEq::eq(&**self, &**other) } } diff --git a/src/libcore/cmp.rs b/src/libcore/cmp.rs index 11878dc76d989..d5001a08b1dc5 100644 --- a/src/libcore/cmp.rs +++ b/src/libcore/cmp.rs @@ -400,11 +400,11 @@ mod impls { // & pointers #[unstable = "Trait is unstable."] - impl<'a, Sized? T: PartialEq> PartialEq for &'a T { + impl<'a, 'b, Sized? A, Sized? B> PartialEq<&'b B> for &'a A where A: PartialEq { #[inline] - fn eq(&self, other: & &'a T) -> bool { PartialEq::eq(*self, *other) } + fn eq(&self, other: & &'b B) -> bool { PartialEq::eq(*self, *other) } #[inline] - fn ne(&self, other: & &'a T) -> bool { PartialEq::ne(*self, *other) } + fn ne(&self, other: & &'b B) -> bool { PartialEq::ne(*self, *other) } } #[unstable = "Trait is unstable."] impl<'a, Sized? T: PartialOrd> PartialOrd for &'a T { @@ -432,11 +432,11 @@ mod impls { // &mut pointers #[unstable = "Trait is unstable."] - impl<'a, Sized? T: PartialEq> PartialEq for &'a mut T { + impl<'a, 'b, Sized? A, Sized? B> PartialEq<&'b mut B> for &'a mut A where A: PartialEq { #[inline] - fn eq(&self, other: &&'a mut T) -> bool { PartialEq::eq(*self, *other) } + fn eq(&self, other: &&'b mut B) -> bool { PartialEq::eq(*self, *other) } #[inline] - fn ne(&self, other: &&'a mut T) -> bool { PartialEq::ne(*self, *other) } + fn ne(&self, other: &&'b mut B) -> bool { PartialEq::ne(*self, *other) } } #[unstable = "Trait is unstable."] impl<'a, Sized? T: PartialOrd> PartialOrd for &'a mut T { @@ -460,4 +460,18 @@ mod impls { } #[unstable = "Trait is unstable."] impl<'a, Sized? T: Eq> Eq for &'a mut T {} + + impl<'a, 'b, Sized? A, Sized? B> PartialEq<&'b mut B> for &'a A where A: PartialEq { + #[inline] + fn eq(&self, other: &&'b mut B) -> bool { PartialEq::eq(*self, *other) } + #[inline] + fn ne(&self, other: &&'b mut B) -> bool { PartialEq::ne(*self, *other) } + } + + impl<'a, 'b, Sized? A, Sized? B> PartialEq<&'b B> for &'a mut A where A: PartialEq { + #[inline] + fn eq(&self, other: &&'b B) -> bool { PartialEq::eq(*self, *other) } + #[inline] + fn ne(&self, other: &&'b B) -> bool { PartialEq::ne(*self, *other) } + } } diff --git a/src/libcore/iter.rs b/src/libcore/iter.rs index 2d488a4b15563..f9595f0663d57 100644 --- a/src/libcore/iter.rs +++ b/src/libcore/iter.rs @@ -2473,7 +2473,11 @@ pub mod order { } /// Compare `a` and `b` for equality (Using partial equality, `PartialEq`) - pub fn eq, S: Iterator>(mut a: T, mut b: S) -> bool { + pub fn eq(mut a: L, mut b: R) -> bool where + A: PartialEq, + L: Iterator, + R: Iterator, + { loop { match (a.next(), b.next()) { (None, None) => return true, @@ -2484,7 +2488,11 @@ pub mod order { } /// Compare `a` and `b` for nonequality (Using partial equality, `PartialEq`) - pub fn ne, S: Iterator>(mut a: T, mut b: S) -> bool { + pub fn ne(mut a: L, mut b: R) -> bool where + A: PartialEq, + L: Iterator, + R: Iterator, + { loop { match (a.next(), b.next()) { (None, None) => return false, diff --git a/src/libcore/slice.rs b/src/libcore/slice.rs index 950f04a5d97e3..b445dba611797 100644 --- a/src/libcore/slice.rs +++ b/src/libcore/slice.rs @@ -1802,12 +1802,12 @@ pub mod bytes { // #[unstable = "waiting for DST"] -impl PartialEq for [T] { - fn eq(&self, other: &[T]) -> bool { +impl PartialEq<[B]> for [A] where A: PartialEq { + fn eq(&self, other: &[B]) -> bool { self.len() == other.len() && order::eq(self.iter(), other.iter()) } - fn ne(&self, other: &[T]) -> bool { + fn ne(&self, other: &[B]) -> bool { self.len() != other.len() || order::ne(self.iter(), other.iter()) } diff --git a/src/libsyntax/parse/token.rs b/src/libsyntax/parse/token.rs index 37df2bf14c2e1..1376f59d79fcf 100644 --- a/src/libsyntax/parse/token.rs +++ b/src/libsyntax/parse/token.rs @@ -629,6 +629,28 @@ impl<'a> Equiv<&'a str> for InternedString { } } +impl<'a> PartialEq<&'a str> for InternedString { + #[inline(always)] + fn eq(&self, other: & &'a str) -> bool { + PartialEq::eq(self.string.as_slice(), *other) + } + #[inline(always)] + fn ne(&self, other: & &'a str) -> bool { + PartialEq::ne(self.string.as_slice(), *other) + } +} + +impl<'a> PartialEq for &'a str { + #[inline(always)] + fn eq(&self, other: &InternedString) -> bool { + PartialEq::eq(*self, other.string.as_slice()) + } + #[inline(always)] + fn ne(&self, other: &InternedString) -> bool { + PartialEq::ne(*self, other.string.as_slice()) + } +} + impl, E> Decodable for InternedString { fn decode(d: &mut D) -> Result { Ok(get_name(get_ident_interner().intern( From b32b24d13a8cbf5db44ac73b44ae48ad639a47c7 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Thu, 20 Nov 2014 20:25:27 -0500 Subject: [PATCH 24/83] Replace `equiv` method calls with `==` operator sugar --- src/librustc/lint/builtin.rs | 2 +- src/librustc/metadata/creader.rs | 18 +++++++++--------- src/librustc/session/config.rs | 2 +- src/librustc_trans/driver/driver.rs | 10 +++++----- src/libsyntax/attr.rs | 6 +++--- src/libsyntax/ext/asm.rs | 8 ++++---- src/libsyntax/feature_gate.rs | 6 +++--- src/libsyntax/parse/obsolete.rs | 2 +- 8 files changed, 27 insertions(+), 27 deletions(-) diff --git a/src/librustc/lint/builtin.rs b/src/librustc/lint/builtin.rs index aa99fe996f02a..9a214d531d157 100644 --- a/src/librustc/lint/builtin.rs +++ b/src/librustc/lint/builtin.rs @@ -1377,7 +1377,7 @@ impl MissingDoc { let has_doc = attrs.iter().any(|a| { match a.node.value.node { - ast::MetaNameValue(ref name, _) if name.equiv(&("doc")) => true, + ast::MetaNameValue(ref name, _) if *name == "doc" => true, _ => false } }); diff --git a/src/librustc/metadata/creader.rs b/src/librustc/metadata/creader.rs index 7f0941db6b2e4..5a8d60fbecd6c 100644 --- a/src/librustc/metadata/creader.rs +++ b/src/librustc/metadata/creader.rs @@ -105,7 +105,7 @@ fn warn_if_multiple_versions(diag: &SpanHandler, cstore: &CStore) { } fn visit_crate(e: &Env, c: &ast::Crate) { - for a in c.attrs.iter().filter(|m| m.name().equiv(&("link_args"))) { + for a in c.attrs.iter().filter(|m| m.name() == "link_args") { match a.value_str() { Some(ref linkarg) => e.sess.cstore.add_used_link_args(linkarg.get()), None => { /* fallthrough */ } @@ -205,7 +205,7 @@ fn visit_item(e: &Env, i: &ast::Item) { // First, add all of the custom link_args attributes let link_args = i.attrs.iter() - .filter_map(|at| if at.name().equiv(&("link_args")) { + .filter_map(|at| if at.name() == "link_args" { Some(at) } else { None @@ -220,7 +220,7 @@ fn visit_item(e: &Env, i: &ast::Item) { // Next, process all of the #[link(..)]-style arguments let link_args = i.attrs.iter() - .filter_map(|at| if at.name().equiv(&("link")) { + .filter_map(|at| if at.name() == "link" { Some(at) } else { None @@ -230,18 +230,18 @@ fn visit_item(e: &Env, i: &ast::Item) { match m.meta_item_list() { Some(items) => { let kind = items.iter().find(|k| { - k.name().equiv(&("kind")) + k.name() == "kind" }).and_then(|a| a.value_str()); let kind = match kind { Some(k) => { - if k.equiv(&("static")) { + if k == "static" { cstore::NativeStatic } else if e.sess.target.target.options.is_like_osx - && k.equiv(&("framework")) { + && k == "framework" { cstore::NativeFramework - } else if k.equiv(&("framework")) { + } else if k == "framework" { cstore::NativeFramework - } else if k.equiv(&("dylib")) { + } else if k == "dylib" { cstore::NativeUnknown } else { e.sess.span_err(m.span, @@ -253,7 +253,7 @@ fn visit_item(e: &Env, i: &ast::Item) { None => cstore::NativeUnknown }; let n = items.iter().find(|n| { - n.name().equiv(&("name")) + n.name() == "name" }).and_then(|a| a.value_str()); let n = match n { Some(n) => n, diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs index 3c2dbae665fa9..33de2c9abe928 100644 --- a/src/librustc/session/config.rs +++ b/src/librustc/session/config.rs @@ -944,7 +944,7 @@ mod test { let sessopts = build_session_options(matches); let sess = build_session(sessopts, None, registry); let cfg = build_configuration(&sess); - let mut test_items = cfg.iter().filter(|m| m.name().equiv(&("test"))); + let mut test_items = cfg.iter().filter(|m| m.name() == "test"); assert!(test_items.next().is_some()); assert!(test_items.next().is_none()); } diff --git a/src/librustc_trans/driver/driver.rs b/src/librustc_trans/driver/driver.rs index aeef16276e5b5..d3281ae1c19fc 100644 --- a/src/librustc_trans/driver/driver.rs +++ b/src/librustc_trans/driver/driver.rs @@ -679,19 +679,19 @@ pub fn collect_crate_types(session: &Session, let attr_types: Vec = attrs.iter().filter_map(|a| { if a.check_name("crate_type") { match a.value_str() { - Some(ref n) if n.equiv(&("rlib")) => { + Some(ref n) if *n == "rlib" => { Some(config::CrateTypeRlib) } - Some(ref n) if n.equiv(&("dylib")) => { + Some(ref n) if *n == "dylib" => { Some(config::CrateTypeDylib) } - Some(ref n) if n.equiv(&("lib")) => { + Some(ref n) if *n == "lib" => { Some(config::default_lib_output()) } - Some(ref n) if n.equiv(&("staticlib")) => { + Some(ref n) if *n == "staticlib" => { Some(config::CrateTypeStaticlib) } - Some(ref n) if n.equiv(&("bin")) => Some(config::CrateTypeExecutable), + Some(ref n) if *n == "bin" => Some(config::CrateTypeExecutable), Some(_) => { session.add_lint(lint::builtin::UNKNOWN_CRATE_TYPES, ast::CRATE_NODE_ID, diff --git a/src/libsyntax/attr.rs b/src/libsyntax/attr.rs index fdfa275549a2c..a2811681efd37 100644 --- a/src/libsyntax/attr.rs +++ b/src/libsyntax/attr.rs @@ -287,11 +287,11 @@ pub fn find_inline_attr(attrs: &[Attribute]) -> InlineAttr { // FIXME (#2809)---validate the usage of #[inline] and #[inline] attrs.iter().fold(InlineNone, |ia,attr| { match attr.node.value.node { - MetaWord(ref n) if n.equiv(&("inline")) => { + MetaWord(ref n) if *n == "inline" => { mark_used(attr); InlineHint } - MetaList(ref n, ref items) if n.equiv(&("inline")) => { + MetaList(ref n, ref items) if *n == "inline" => { mark_used(attr); if contains_name(items.as_slice(), "always") { InlineAlways @@ -409,7 +409,7 @@ pub fn require_unique_names(diagnostic: &SpanHandler, metas: &[P]) { pub fn find_repr_attrs(diagnostic: &SpanHandler, attr: &Attribute) -> Vec { let mut acc = Vec::new(); match attr.node.value.node { - ast::MetaList(ref s, ref items) if s.equiv(&("repr")) => { + ast::MetaList(ref s, ref items) if *s == "repr" => { mark_used(attr); for item in items.iter() { match item.node { diff --git a/src/libsyntax/ext/asm.rs b/src/libsyntax/ext/asm.rs index a0999d9eee96e..b138811187ba9 100644 --- a/src/libsyntax/ext/asm.rs +++ b/src/libsyntax/ext/asm.rs @@ -148,7 +148,7 @@ pub fn expand_asm<'cx>(cx: &'cx mut ExtCtxt, sp: Span, tts: &[ast::TokenTree]) let (s, _str_style) = p.parse_str(); - if OPTIONS.iter().any(|opt| s.equiv(opt)) { + if OPTIONS.iter().any(|&opt| s == opt) { cx.span_warn(p.last_span, "expected a clobber, found an option"); } clobs.push(s); @@ -157,13 +157,13 @@ pub fn expand_asm<'cx>(cx: &'cx mut ExtCtxt, sp: Span, tts: &[ast::TokenTree]) Options => { let (option, _str_style) = p.parse_str(); - if option.equiv(&("volatile")) { + if option == "volatile" { // Indicates that the inline assembly has side effects // and must not be optimized out along with its outputs. volatile = true; - } else if option.equiv(&("alignstack")) { + } else if option == "alignstack" { alignstack = true; - } else if option.equiv(&("intel")) { + } else if option == "intel" { dialect = ast::AsmIntel; } else { cx.span_warn(p.last_span, "unrecognized option"); diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index 7453da6374e00..c7aef0020b2c4 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -172,12 +172,12 @@ impl<'a, 'v> Visitor<'v> for Context<'a> { fn visit_item(&mut self, i: &ast::Item) { for attr in i.attrs.iter() { - if attr.name().equiv(&("thread_local")) { + if attr.name() == "thread_local" { self.gate_feature("thread_local", i.span, "`#[thread_local]` is an experimental feature, and does not \ currently handle destructors. There is no corresponding \ `#[task_local]` mapping to the task model"); - } else if attr.name().equiv(&("linkage")) { + } else if attr.name() == "linkage" { self.gate_feature("linkage", i.span, "the `linkage` attribute is experimental \ and not portable across platforms") @@ -429,7 +429,7 @@ pub fn check_crate(span_handler: &SpanHandler, krate: &ast::Crate) -> (Features, } }; match KNOWN_FEATURES.iter() - .find(|& &(n, _)| name.equiv(&n)) { + .find(|& &(n, _)| name == n) { Some(&(name, Active)) => { cx.features.push(name); } Some(&(_, Removed)) => { span_handler.span_err(mi.span, "feature has been removed"); diff --git a/src/libsyntax/parse/obsolete.rs b/src/libsyntax/parse/obsolete.rs index 86a96fc521642..650f8295d01a6 100644 --- a/src/libsyntax/parse/obsolete.rs +++ b/src/libsyntax/parse/obsolete.rs @@ -117,7 +117,7 @@ impl<'a> ParserObsoleteMethods for parser::Parser<'a> { fn is_obsolete_ident(&mut self, ident: &str) -> bool { match self.token { token::Ident(sid, _) => { - token::get_ident(sid).equiv(&ident) + token::get_ident(sid) == ident } _ => false } From eac635de01d083e0423dcf9e548cf522ec41015d Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Sat, 22 Nov 2014 19:45:35 -0500 Subject: [PATCH 25/83] Remove unused transmutes from tests --- src/libstd/path/posix.rs | 93 +++++++++++++++++--------------------- src/libstd/path/windows.rs | 85 +++++++++++++++------------------- 2 files changed, 78 insertions(+), 100 deletions(-) diff --git a/src/libstd/path/posix.rs b/src/libstd/path/posix.rs index cc8fcccf14a06..f6778588addb9 100644 --- a/src/libstd/path/posix.rs +++ b/src/libstd/path/posix.rs @@ -443,7 +443,6 @@ static dot_dot_static: &'static [u8] = b".."; mod tests { use prelude::*; use super::*; - use mem; use str; use str::StrPrelude; @@ -601,10 +600,8 @@ mod tests { macro_rules! t( (s: $path:expr, $op:ident, $exp:expr) => ( { - unsafe { - let path = Path::new($path); - assert!(path.$op() == mem::transmute(($exp).as_bytes())); - } + let path = Path::new($path); + assert!(path.$op() == ($exp).as_bytes()); } ); (s: $path:expr, $op:ident, $exp:expr, opt) => ( @@ -616,11 +613,9 @@ mod tests { ); (v: $path:expr, $op:ident, $exp:expr) => ( { - unsafe { - let arg = $path; - let path = Path::new(arg); - assert!(path.$op() == mem::transmute($exp)); - } + let arg = $path; + let path = Path::new(arg); + assert!(path.$op() == $exp); } ); ) @@ -668,9 +663,8 @@ mod tests { t!(v: b"hi/there.txt", extension, Some(b"txt")); t!(v: b"hi/there\x80.txt", extension, Some(b"txt")); t!(v: b"hi/there.t\x80xt", extension, Some(b"t\x80xt")); - let no: Option<&'static [u8]> = None; - t!(v: b"hi/there", extension, no); - t!(v: b"hi/there\x80", extension, no); + t!(v: b"hi/there", extension, None); + t!(v: b"hi/there\x80", extension, None); t!(s: "hi/there.txt", extension, Some("txt"), opt); t!(s: "hi/there", extension, None, opt); t!(s: "there.txt", extension, Some("txt"), opt); @@ -959,62 +953,57 @@ mod tests { macro_rules! t( (s: $path:expr, $filename:expr, $dirname:expr, $filestem:expr, $ext:expr) => ( { - unsafe { - let path = $path; - let filename = $filename; - assert!(path.filename_str() == filename, - "{}.filename_str(): Expected `{}`, found {}", - path.as_str().unwrap(), filename, path.filename_str()); - let dirname = $dirname; - assert!(path.dirname_str() == dirname, - "`{}`.dirname_str(): Expected `{}`, found `{}`", - path.as_str().unwrap(), dirname, path.dirname_str()); - let filestem = $filestem; - assert!(path.filestem_str() == filestem, - "`{}`.filestem_str(): Expected `{}`, found `{}`", - path.as_str().unwrap(), filestem, path.filestem_str()); - let ext = $ext; - assert!(path.extension_str() == mem::transmute(ext), - "`{}`.extension_str(): Expected `{}`, found `{}`", - path.as_str().unwrap(), ext, path.extension_str()); - } + let path = $path; + let filename = $filename; + assert!(path.filename_str() == filename, + "{}.filename_str(): Expected `{}`, found {}", + path.as_str().unwrap(), filename, path.filename_str()); + let dirname = $dirname; + assert!(path.dirname_str() == dirname, + "`{}`.dirname_str(): Expected `{}`, found `{}`", + path.as_str().unwrap(), dirname, path.dirname_str()); + let filestem = $filestem; + assert!(path.filestem_str() == filestem, + "`{}`.filestem_str(): Expected `{}`, found `{}`", + path.as_str().unwrap(), filestem, path.filestem_str()); + let ext = $ext; + assert!(path.extension_str() == ext, + "`{}`.extension_str(): Expected `{}`, found `{}`", + path.as_str().unwrap(), ext, path.extension_str()); } ); (v: $path:expr, $filename:expr, $dirname:expr, $filestem:expr, $ext:expr) => ( { - unsafe { - let path = $path; - assert!(path.filename() == mem::transmute($filename)); - assert!(path.dirname() == mem::transmute($dirname)); - assert!(path.filestem() == mem::transmute($filestem)); - assert!(path.extension() == mem::transmute($ext)); - } + let path = $path; + assert!(path.filename() == $filename); + assert!(path.dirname() == $dirname); + assert!(path.filestem() == $filestem); + assert!(path.extension() == $ext); } ) ) - let no: Option<&'static str> = None; - t!(v: Path::new(b"a/b/c"), Some(b"c"), b"a/b", Some(b"c"), no); - t!(v: Path::new(b"a/b/\xFF"), Some(b"\xFF"), b"a/b", Some(b"\xFF"), no); + t!(v: Path::new(b"a/b/c"), Some(b"c"), b"a/b", Some(b"c"), None); + t!(v: Path::new(b"a/b/\xFF"), Some(b"\xFF"), b"a/b", Some(b"\xFF"), None); t!(v: Path::new(b"hi/there.\xFF"), Some(b"there.\xFF"), b"hi", Some(b"there"), Some(b"\xFF")); - t!(s: Path::new("a/b/c"), Some("c"), Some("a/b"), Some("c"), no); - t!(s: Path::new("."), None, Some("."), None, no); - t!(s: Path::new("/"), None, Some("/"), None, no); - t!(s: Path::new(".."), None, Some(".."), None, no); - t!(s: Path::new("../.."), None, Some("../.."), None, no); + t!(s: Path::new("a/b/c"), Some("c"), Some("a/b"), Some("c"), None); + t!(s: Path::new("."), None, Some("."), None, None); + t!(s: Path::new("/"), None, Some("/"), None, None); + t!(s: Path::new(".."), None, Some(".."), None, None); + t!(s: Path::new("../.."), None, Some("../.."), None, None); t!(s: Path::new("hi/there.txt"), Some("there.txt"), Some("hi"), Some("there"), Some("txt")); - t!(s: Path::new("hi/there"), Some("there"), Some("hi"), Some("there"), no); + t!(s: Path::new("hi/there"), Some("there"), Some("hi"), Some("there"), None); t!(s: Path::new("hi/there."), Some("there."), Some("hi"), Some("there"), Some("")); - t!(s: Path::new("hi/.there"), Some(".there"), Some("hi"), Some(".there"), no); + t!(s: Path::new("hi/.there"), Some(".there"), Some("hi"), Some(".there"), None); t!(s: Path::new("hi/..there"), Some("..there"), Some("hi"), Some("."), Some("there")); - t!(s: Path::new(b"a/b/\xFF"), None, Some("a/b"), None, no); + t!(s: Path::new(b"a/b/\xFF"), None, Some("a/b"), None, None); t!(s: Path::new(b"a/b/\xFF.txt"), None, Some("a/b"), None, Some("txt")); - t!(s: Path::new(b"a/b/c.\x80"), None, Some("a/b"), Some("c"), no); - t!(s: Path::new(b"\xFF/b"), Some("b"), None, Some("b"), no); + t!(s: Path::new(b"a/b/c.\x80"), None, Some("a/b"), Some("c"), None); + t!(s: Path::new(b"\xFF/b"), Some("b"), None, Some("b"), None); } #[test] diff --git a/src/libstd/path/windows.rs b/src/libstd/path/windows.rs index 6a6551af4999b..13891a6333014 100644 --- a/src/libstd/path/windows.rs +++ b/src/libstd/path/windows.rs @@ -1115,7 +1115,6 @@ fn prefix_len(p: Option) -> uint { #[cfg(test)] mod tests { - use mem; use prelude::*; use super::*; use super::parse_prefix; @@ -1358,11 +1357,9 @@ mod tests { macro_rules! t( (s: $path:expr, $op:ident, $exp:expr) => ( { - unsafe { - let path = $path; - let path = Path::new(path); - assert!(path.$op() == Some(mem::transmute($exp))); - } + let path = $path; + let path = Path::new(path); + assert!(path.$op() == Some($exp)); } ); (s: $path:expr, $op:ident, $exp:expr, opt) => ( @@ -1375,11 +1372,9 @@ mod tests { ); (v: $path:expr, $op:ident, $exp:expr) => ( { - unsafe { - let path = $path; - let path = Path::new(path); - assert!(path.$op() == mem::transmute($exp)); - } + let path = $path; + let path = Path::new(path); + assert!(path.$op() == $exp); } ) ) @@ -1464,8 +1459,7 @@ mod tests { // filestem is based on filename, so we don't need the full set of prefix tests t!(v: b"hi\\there.txt", extension, Some(b"txt")); - let no: Option<&'static [u8]> = None; - t!(v: b"hi\\there", extension, no); + t!(v: b"hi\\there", extension, None); t!(s: "hi\\there.txt", extension_str, Some("txt"), opt); t!(s: "hi\\there", extension_str, None, opt); t!(s: "there.txt", extension_str, Some("txt"), opt); @@ -1872,53 +1866,48 @@ mod tests { macro_rules! t( (s: $path:expr, $filename:expr, $dirname:expr, $filestem:expr, $ext:expr) => ( { - unsafe { - let path = $path; - let filename = $filename; - assert!(path.filename_str() == filename, - "`{}`.filename_str(): Expected `{}`, found `{}`", - path.as_str().unwrap(), filename, path.filename_str()); - let dirname = $dirname; - assert!(path.dirname_str() == dirname, - "`{}`.dirname_str(): Expected `{}`, found `{}`", - path.as_str().unwrap(), dirname, path.dirname_str()); - let filestem = $filestem; - assert!(path.filestem_str() == filestem, - "`{}`.filestem_str(): Expected `{}`, found `{}`", - path.as_str().unwrap(), filestem, path.filestem_str()); - let ext = $ext; - assert!(path.extension_str() == mem::transmute(ext), - "`{}`.extension_str(): Expected `{}`, found `{}`", - path.as_str().unwrap(), ext, path.extension_str()); - } + let path = $path; + let filename = $filename; + assert!(path.filename_str() == filename, + "`{}`.filename_str(): Expected `{}`, found `{}`", + path.as_str().unwrap(), filename, path.filename_str()); + let dirname = $dirname; + assert!(path.dirname_str() == dirname, + "`{}`.dirname_str(): Expected `{}`, found `{}`", + path.as_str().unwrap(), dirname, path.dirname_str()); + let filestem = $filestem; + assert!(path.filestem_str() == filestem, + "`{}`.filestem_str(): Expected `{}`, found `{}`", + path.as_str().unwrap(), filestem, path.filestem_str()); + let ext = $ext; + assert!(path.extension_str() == ext, + "`{}`.extension_str(): Expected `{}`, found `{}`", + path.as_str().unwrap(), ext, path.extension_str()); } ); (v: $path:expr, $filename:expr, $dirname:expr, $filestem:expr, $ext:expr) => ( { - unsafe { - let path = $path; - assert!(path.filename() == mem::transmute($filename)); - assert!(path.dirname() == mem::transmute($dirname)); - assert!(path.filestem() == mem::transmute($filestem)); - assert!(path.extension() == mem::transmute($ext)); - } + let path = $path; + assert!(path.filename() == $filename); + assert!(path.dirname() == $dirname); + assert!(path.filestem() == $filestem); + assert!(path.extension() == $ext); } ) ) - let no: Option<&'static str> = None; - t!(v: Path::new(b"a\\b\\c"), Some(b"c"), b"a\\b", Some(b"c"), no); - t!(s: Path::new("a\\b\\c"), Some("c"), Some("a\\b"), Some("c"), no); - t!(s: Path::new("."), None, Some("."), None, no); - t!(s: Path::new("\\"), None, Some("\\"), None, no); - t!(s: Path::new(".."), None, Some(".."), None, no); - t!(s: Path::new("..\\.."), None, Some("..\\.."), None, no); + t!(v: Path::new(b"a\\b\\c"), Some(b"c"), b"a\\b", Some(b"c"), None); + t!(s: Path::new("a\\b\\c"), Some("c"), Some("a\\b"), Some("c"), None); + t!(s: Path::new("."), None, Some("."), None, None); + t!(s: Path::new("\\"), None, Some("\\"), None, None); + t!(s: Path::new(".."), None, Some(".."), None, None); + t!(s: Path::new("..\\.."), None, Some("..\\.."), None, None); t!(s: Path::new("hi\\there.txt"), Some("there.txt"), Some("hi"), Some("there"), Some("txt")); - t!(s: Path::new("hi\\there"), Some("there"), Some("hi"), Some("there"), no); + t!(s: Path::new("hi\\there"), Some("there"), Some("hi"), Some("there"), None); t!(s: Path::new("hi\\there."), Some("there."), Some("hi"), Some("there"), Some("")); - t!(s: Path::new("hi\\.there"), Some(".there"), Some("hi"), Some(".there"), no); + t!(s: Path::new("hi\\.there"), Some(".there"), Some("hi"), Some(".there"), None); t!(s: Path::new("hi\\..there"), Some("..there"), Some("hi"), Some("."), Some("there")); From 09707d70a48b7bb1a180f44e233dfe36b196ad46 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Fri, 21 Nov 2014 01:20:04 -0500 Subject: [PATCH 26/83] Fix fallout --- src/libcollections/dlist.rs | 4 ++-- src/libcollections/enum_set.rs | 24 ++++++++++++------------ src/libcollections/string.rs | 6 ++++-- src/libcollections/vec.rs | 8 ++++---- src/libcore/intrinsics.rs | 2 +- src/libcore/option.rs | 6 +++--- src/libcore/result.rs | 6 +++--- src/libcore/slice.rs | 12 ++++++------ src/libcoretest/mem.rs | 2 +- src/librustc/middle/cfg/graphviz.rs | 2 +- src/librustc_back/rpath.rs | 4 ++-- src/librustc_trans/trans/debuginfo.rs | 2 +- src/librustdoc/lib.rs | 2 +- src/libstd/io/fs.rs | 2 +- src/libstd/os.rs | 4 ++-- src/libsyntax/util/small_vector.rs | 4 ++-- src/libterm/terminfo/parm.rs | 22 +++++++++++----------- src/test/run-pass/issue-15080.rs | 2 +- src/test/run-pass/issue-7784.rs | 2 +- 19 files changed, 59 insertions(+), 57 deletions(-) diff --git a/src/libcollections/dlist.rs b/src/libcollections/dlist.rs index 3f95bda663e15..39cdf0c456413 100644 --- a/src/libcollections/dlist.rs +++ b/src/libcollections/dlist.rs @@ -945,7 +945,7 @@ mod tests { let mut m = list_from(v.as_slice()); m.rotate_backward(); check_links(&m); m.rotate_forward(); check_links(&m); - assert_eq!(v.iter().collect::>(), m.iter().collect()); + assert_eq!(v.iter().collect::>(), m.iter().collect::>()); m.rotate_forward(); check_links(&m); m.rotate_forward(); check_links(&m); m.pop_front(); check_links(&m); @@ -953,7 +953,7 @@ mod tests { m.rotate_backward(); check_links(&m); m.push_front(9); check_links(&m); m.rotate_forward(); check_links(&m); - assert_eq!(vec![3i,9,5,1,2], m.into_iter().collect()); + assert_eq!(vec![3i,9,5,1,2], m.into_iter().collect::>()); } #[test] diff --git a/src/libcollections/enum_set.rs b/src/libcollections/enum_set.rs index d21465c822f47..2cbde0168a2e8 100644 --- a/src/libcollections/enum_set.rs +++ b/src/libcollections/enum_set.rs @@ -397,23 +397,23 @@ mod test { fn test_iterator() { let mut e1: EnumSet = EnumSet::new(); - let elems: Vec = e1.iter().collect(); + let elems: ::vec::Vec = e1.iter().collect(); assert!(elems.is_empty()) e1.insert(A); - let elems = e1.iter().collect(); + let elems: ::vec::Vec<_> = e1.iter().collect(); assert_eq!(vec![A], elems) e1.insert(C); - let elems = e1.iter().collect(); + let elems: ::vec::Vec<_> = e1.iter().collect(); assert_eq!(vec![A,C], elems) e1.insert(C); - let elems = e1.iter().collect(); + let elems: ::vec::Vec<_> = e1.iter().collect(); assert_eq!(vec![A,C], elems) e1.insert(B); - let elems = e1.iter().collect(); + let elems: ::vec::Vec<_> = e1.iter().collect(); assert_eq!(vec![A,B,C], elems) } @@ -431,35 +431,35 @@ mod test { e2.insert(C); let e_union = e1 | e2; - let elems = e_union.iter().collect(); + let elems: ::vec::Vec<_> = e_union.iter().collect(); assert_eq!(vec![A,B,C], elems) let e_intersection = e1 & e2; - let elems = e_intersection.iter().collect(); + let elems: ::vec::Vec<_> = e_intersection.iter().collect(); assert_eq!(vec![C], elems) // Another way to express intersection let e_intersection = e1 - (e1 - e2); - let elems = e_intersection.iter().collect(); + let elems: ::vec::Vec<_> = e_intersection.iter().collect(); assert_eq!(vec![C], elems) let e_subtract = e1 - e2; - let elems = e_subtract.iter().collect(); + let elems: ::vec::Vec<_> = e_subtract.iter().collect(); assert_eq!(vec![A], elems) // Bitwise XOR of two sets, aka symmetric difference let e_symmetric_diff = e1 ^ e2; - let elems = e_symmetric_diff.iter().collect(); + let elems: ::vec::Vec<_> = e_symmetric_diff.iter().collect(); assert_eq!(vec![A,B], elems) // Another way to express symmetric difference let e_symmetric_diff = (e1 - e2) | (e2 - e1); - let elems = e_symmetric_diff.iter().collect(); + let elems: ::vec::Vec<_> = e_symmetric_diff.iter().collect(); assert_eq!(vec![A,B], elems) // Yet another way to express symmetric difference let e_symmetric_diff = (e1 | e2) - (e1 & e2); - let elems = e_symmetric_diff.iter().collect(); + let elems: ::vec::Vec<_> = e_symmetric_diff.iter().collect(); assert_eq!(vec![A,B], elems) } diff --git a/src/libcollections/string.rs b/src/libcollections/string.rs index 180978b3d77b6..8917afa34b149 100644 --- a/src/libcollections/string.rs +++ b/src/libcollections/string.rs @@ -1010,10 +1010,12 @@ mod tests { #[test] fn test_from_utf8_lossy() { let xs = b"hello"; - assert_eq!(String::from_utf8_lossy(xs), "hello".into_cow()); + let ys: str::CowString = "hello".into_cow(); + assert_eq!(String::from_utf8_lossy(xs), ys); let xs = "ศไทย中华Việt Nam".as_bytes(); - assert_eq!(String::from_utf8_lossy(xs), "ศไทย中华Việt Nam".into_cow()); + let ys: str::CowString = "ศไทย中华Việt Nam".into_cow(); + assert_eq!(String::from_utf8_lossy(xs), ys); let xs = b"Hello\xC2 There\xFF Goodbye"; assert_eq!(String::from_utf8_lossy(xs), diff --git a/src/libcollections/vec.rs b/src/libcollections/vec.rs index 8d98df8b20062..c40f0c19c09f8 100644 --- a/src/libcollections/vec.rs +++ b/src/libcollections/vec.rs @@ -1868,13 +1868,13 @@ mod tests { let mut values = vec![1u8,2,3,4,5]; { let slice = values.slice_from_mut(2); - assert!(slice == &mut [3, 4, 5]); + assert!(slice == [3, 4, 5]); for p in slice.iter_mut() { *p += 2; } } - assert!(values.as_slice() == &[1, 2, 5, 6, 7]); + assert!(values.as_slice() == [1, 2, 5, 6, 7]); } #[test] @@ -1882,13 +1882,13 @@ mod tests { let mut values = vec![1u8,2,3,4,5]; { let slice = values.slice_to_mut(2); - assert!(slice == &mut [1, 2]); + assert!(slice == [1, 2]); for p in slice.iter_mut() { *p += 1; } } - assert!(values.as_slice() == &[2, 3, 3, 4, 5]); + assert!(values.as_slice() == [2, 3, 3, 4, 5]); } #[test] diff --git a/src/libcore/intrinsics.rs b/src/libcore/intrinsics.rs index 78c74075d4867..347777b587aa5 100644 --- a/src/libcore/intrinsics.rs +++ b/src/libcore/intrinsics.rs @@ -226,7 +226,7 @@ extern "rust-intrinsic" { /// use std::mem; /// /// let v: &[u8] = unsafe { mem::transmute("L") }; - /// assert!(v == &[76u8]); + /// assert!(v == [76u8]); /// ``` pub fn transmute(e: T) -> U; diff --git a/src/libcore/option.rs b/src/libcore/option.rs index 7d7b41bf7bfd8..ef895a1d7fbcc 100644 --- a/src/libcore/option.rs +++ b/src/libcore/option.rs @@ -274,9 +274,9 @@ impl Option { /// let mut x = Some("Diamonds"); /// { /// let v = x.as_mut_slice(); - /// assert!(v == &mut ["Diamonds"]); + /// assert!(v == ["Diamonds"]); /// v[0] = "Dirt"; - /// assert!(v == &mut ["Dirt"]); + /// assert!(v == ["Dirt"]); /// } /// assert_eq!(x, Some("Dirt")); /// ``` @@ -554,7 +554,7 @@ impl Option { /// /// let x = None; /// let v: Vec<&str> = x.into_iter().collect(); - /// assert_eq!(v, vec![]); + /// assert!(v.is_empty()); /// ``` #[inline] #[unstable = "waiting for iterator conventions"] diff --git a/src/libcore/result.rs b/src/libcore/result.rs index 202ac46449754..07bb6f15c94ff 100644 --- a/src/libcore/result.rs +++ b/src/libcore/result.rs @@ -407,14 +407,14 @@ impl Result { /// let mut x: Result<&str, uint> = Ok("Gold"); /// { /// let v = x.as_mut_slice(); - /// assert!(v == &mut ["Gold"]); + /// assert!(v == ["Gold"]); /// v[0] = "Silver"; - /// assert!(v == &mut ["Silver"]); + /// assert!(v == ["Silver"]); /// } /// assert_eq!(x, Ok("Silver")); /// /// let mut x: Result<&str, uint> = Err(45); - /// assert!(x.as_mut_slice() == &mut []); + /// assert!(x.as_mut_slice().is_empty()); /// ``` #[inline] #[unstable = "waiting for mut conventions"] diff --git a/src/libcore/slice.rs b/src/libcore/slice.rs index b445dba611797..12cd20584a180 100644 --- a/src/libcore/slice.rs +++ b/src/libcore/slice.rs @@ -374,20 +374,20 @@ pub trait SlicePrelude for Sized? { /// // scoped to restrict the lifetime of the borrows /// { /// let (left, right) = v.split_at_mut(0); - /// assert!(left == &mut []); - /// assert!(right == &mut [1i, 2, 3, 4, 5, 6]); + /// assert!(left == []); + /// assert!(right == [1i, 2, 3, 4, 5, 6]); /// } /// /// { /// let (left, right) = v.split_at_mut(2); - /// assert!(left == &mut [1i, 2]); - /// assert!(right == &mut [3i, 4, 5, 6]); + /// assert!(left == [1i, 2]); + /// assert!(right == [3i, 4, 5, 6]); /// } /// /// { /// let (left, right) = v.split_at_mut(6); - /// assert!(left == &mut [1i, 2, 3, 4, 5, 6]); - /// assert!(right == &mut []); + /// assert!(left == [1i, 2, 3, 4, 5, 6]); + /// assert!(right == []); /// } /// ``` #[unstable = "waiting on final error conventions"] diff --git a/src/libcoretest/mem.rs b/src/libcoretest/mem.rs index e4dde7c641e1e..75ddfd5413b31 100644 --- a/src/libcoretest/mem.rs +++ b/src/libcoretest/mem.rs @@ -109,7 +109,7 @@ fn test_transmute() { } unsafe { - assert!(vec![76u8] == transmute("L".to_string())); + assert!(vec![76u8] == transmute::<_, Vec>("L".to_string())); } } diff --git a/src/librustc/middle/cfg/graphviz.rs b/src/librustc/middle/cfg/graphviz.rs index 8e0e8ee1c5ee0..92c87aacc7dc5 100644 --- a/src/librustc/middle/cfg/graphviz.rs +++ b/src/librustc/middle/cfg/graphviz.rs @@ -40,7 +40,7 @@ fn replace_newline_with_backslash_l(s: String) -> String { let mut last_two: Vec<_> = s.as_slice().chars().rev().take(2).collect(); last_two.reverse(); - if last_two.as_slice() != &['\\', 'l'] { + if last_two != ['\\', 'l'] { s.push_str("\\l"); } s diff --git a/src/librustc_back/rpath.rs b/src/librustc_back/rpath.rs index 26cc859434f2c..9c94823f86758 100644 --- a/src/librustc_back/rpath.rs +++ b/src/librustc_back/rpath.rs @@ -156,7 +156,7 @@ mod test { "rpath2".to_string(), "rpath1".to_string() ]); - assert!(res.as_slice() == &[ + assert!(res.as_slice() == [ "rpath1".to_string(), "rpath2".to_string() ]); @@ -176,7 +176,7 @@ mod test { "4a".to_string(), "3".to_string() ]); - assert!(res.as_slice() == &[ + assert!(res.as_slice() == [ "1a".to_string(), "2".to_string(), "4a".to_string(), diff --git a/src/librustc_trans/trans/debuginfo.rs b/src/librustc_trans/trans/debuginfo.rs index e798dd4dc945f..555cb0004892f 100644 --- a/src/librustc_trans/trans/debuginfo.rs +++ b/src/librustc_trans/trans/debuginfo.rs @@ -1545,7 +1545,7 @@ fn compile_unit_metadata(cx: &CrateContext) { Some(ref p) if p.is_relative() => { // prepend "./" if necessary let dotdot = b".."; - let prefix = &[dotdot[0], ::std::path::SEP_BYTE]; + let prefix = [dotdot[0], ::std::path::SEP_BYTE]; let mut path_bytes = p.as_vec().to_vec(); if path_bytes.slice_to(2) != prefix && diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index c592474b057ff..cc946a6ca4a96 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -172,7 +172,7 @@ pub fn main_args(args: &[String]) -> int { } } - if matches.opt_strs("passes").as_slice() == &["list".to_string()] { + if matches.opt_strs("passes") == ["list"] { println!("Available passes for running rustdoc:"); for &(name, _, description) in PASSES.iter() { println!("{:>20} - {}", name, description); diff --git a/src/libstd/io/fs.rs b/src/libstd/io/fs.rs index bd334f52628fe..fd6b57d096ae6 100644 --- a/src/libstd/io/fs.rs +++ b/src/libstd/io/fs.rs @@ -1528,7 +1528,7 @@ mod test { check!(File::create(&tmpdir.join("test")).write(&bytes)); let actual = check!(File::open(&tmpdir.join("test")).read_to_end()); - assert!(actual.as_slice() == &bytes); + assert!(actual == bytes.as_slice()); } #[test] diff --git a/src/libstd/os.rs b/src/libstd/os.rs index 90203709627ff..0abd030a16347 100644 --- a/src/libstd/os.rs +++ b/src/libstd/os.rs @@ -2034,7 +2034,7 @@ mod tests { fn split_paths_windows() { fn check_parse(unparsed: &str, parsed: &[&str]) -> bool { split_paths(unparsed) == - parsed.iter().map(|s| Path::new(*s)).collect() + parsed.iter().map(|s| Path::new(*s)).collect::>() } assert!(check_parse("", &mut [""])); @@ -2054,7 +2054,7 @@ mod tests { fn split_paths_unix() { fn check_parse(unparsed: &str, parsed: &[&str]) -> bool { split_paths(unparsed) == - parsed.iter().map(|s| Path::new(*s)).collect() + parsed.iter().map(|s| Path::new(*s)).collect::>() } assert!(check_parse("", &mut [""])); diff --git a/src/libsyntax/util/small_vector.rs b/src/libsyntax/util/small_vector.rs index c4b3288d44db8..d56e4f704499e 100644 --- a/src/libsyntax/util/small_vector.rs +++ b/src/libsyntax/util/small_vector.rs @@ -224,10 +224,10 @@ mod test { assert_eq!(Vec::new(), v); let v = SmallVector::one(1i); - assert_eq!(vec!(1i), v.into_iter().collect()); + assert_eq!(vec!(1i), v.into_iter().collect::>()); let v = SmallVector::many(vec!(1i, 2i, 3i)); - assert_eq!(vec!(1i, 2i, 3i), v.into_iter().collect()); + assert_eq!(vec!(1i, 2i, 3i), v.into_iter().collect::>()); } #[test] diff --git a/src/libterm/terminfo/parm.rs b/src/libterm/terminfo/parm.rs index 62a49c5d90270..d644542db0732 100644 --- a/src/libterm/terminfo/parm.rs +++ b/src/libterm/terminfo/parm.rs @@ -582,13 +582,13 @@ mod test { fn test_basic_setabf() { let s = b"\\E[48;5;%p1%dm"; assert_eq!(expand(s, &[Number(1)], &mut Variables::new()).unwrap(), - "\\E[48;5;1m".bytes().collect()); + "\\E[48;5;1m".bytes().collect::>()); } #[test] fn test_multiple_int_constants() { assert_eq!(expand(b"%{1}%{2}%d%d", &[], &mut Variables::new()).unwrap(), - "21".bytes().collect()); + "21".bytes().collect::>()); } #[test] @@ -596,9 +596,9 @@ mod test { let mut vars = Variables::new(); assert_eq!(expand(b"%p1%d%p2%d%p3%d%i%p1%d%p2%d%p3%d", &[Number(1),Number(2),Number(3)], &mut vars), - Ok("123233".bytes().collect())); + Ok("123233".bytes().collect::>())); assert_eq!(expand(b"%p1%d%p2%d%i%p1%d%p2%d", &[], &mut vars), - Ok("0011".bytes().collect())); + Ok("0011".bytes().collect::>())); } #[test] @@ -672,15 +672,15 @@ mod test { let res = expand(s, &[Number(1)], &mut vars); assert!(res.is_ok(), res.unwrap_err()); assert_eq!(res.unwrap(), - "\\E[31m".bytes().collect()); + "\\E[31m".bytes().collect::>()); let res = expand(s, &[Number(8)], &mut vars); assert!(res.is_ok(), res.unwrap_err()); assert_eq!(res.unwrap(), - "\\E[90m".bytes().collect()); + "\\E[90m".bytes().collect::>()); let res = expand(s, &[Number(42)], &mut vars); assert!(res.is_ok(), res.unwrap_err()); assert_eq!(res.unwrap(), - "\\E[38;5;42m".bytes().collect()); + "\\E[38;5;42m".bytes().collect::>()); } #[test] @@ -692,13 +692,13 @@ mod test { Words("foo".to_string()), Words("f".to_string()), Words("foo".to_string())], vars), - Ok("foofoo ffo".bytes().collect())); + Ok("foofoo ffo".bytes().collect::>())); assert_eq!(expand(b"%p1%:-4.2s", &[Words("foo".to_string())], vars), - Ok("fo ".bytes().collect())); + Ok("fo ".bytes().collect::>())); assert_eq!(expand(b"%p1%d%p1%.3d%p1%5d%p1%:+d", &[Number(1)], vars), - Ok("1001 1+1".bytes().collect())); + Ok("1001 1+1".bytes().collect::>())); assert_eq!(expand(b"%p1%o%p1%#o%p2%6.4x%p2%#6.4X", &[Number(15), Number(27)], vars), - Ok("17017 001b0X001B".bytes().collect())); + Ok("17017 001b0X001B".bytes().collect::>())); } } diff --git a/src/test/run-pass/issue-15080.rs b/src/test/run-pass/issue-15080.rs index c2c370ae504c7..76b8463a41701 100644 --- a/src/test/run-pass/issue-15080.rs +++ b/src/test/run-pass/issue-15080.rs @@ -26,5 +26,5 @@ fn main() { break } } - assert!(result.as_slice() == &[2, 4]); + assert!(result == [2, 4]); } diff --git a/src/test/run-pass/issue-7784.rs b/src/test/run-pass/issue-7784.rs index f0310cd8df3c8..666847517efde 100644 --- a/src/test/run-pass/issue-7784.rs +++ b/src/test/run-pass/issue-7784.rs @@ -33,6 +33,6 @@ fn main() { let out = bar("baz", "foo"); let [a, xs.., d] = out; assert_eq!(a, "baz"); - assert!(xs == &["foo", "foo"]); + assert!(xs == ["foo", "foo"]); assert_eq!(d, "baz"); } From 5cfac942018b3f8e0b384c49d6564fcff25634e6 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Wed, 26 Nov 2014 23:50:12 -0500 Subject: [PATCH 27/83] Deprecate Equiv --- src/libcollections/str.rs | 1 + src/libcollections/string.rs | 3 ++- src/libcollections/vec.rs | 3 ++- src/libcore/cmp.rs | 2 +- src/libcore/ptr.rs | 4 ++++ src/libcore/slice.rs | 6 ++++-- src/libcore/str.rs | 2 ++ src/libgraphviz/maybe_owned_vec.rs | 1 + src/libstd/collections/hash/map.rs | 2 ++ src/libsyntax/parse/token.rs | 1 + 10 files changed, 20 insertions(+), 5 deletions(-) diff --git a/src/libcollections/str.rs b/src/libcollections/str.rs index c6fa1332186ec..ad0a5e7617646 100644 --- a/src/libcollections/str.rs +++ b/src/libcollections/str.rs @@ -563,6 +563,7 @@ impl<'a> Ord for MaybeOwned<'a> { } } +#[allow(deprecated)] #[deprecated = "use std::str::CowString"] impl<'a, S: Str> Equiv for MaybeOwned<'a> { #[inline] diff --git a/src/libcollections/string.rs b/src/libcollections/string.rs index 8917afa34b149..fbb0bb5c4ce86 100644 --- a/src/libcollections/string.rs +++ b/src/libcollections/string.rs @@ -822,7 +822,8 @@ impl hash::Hash for String { } } -#[experimental = "waiting on Equiv stabilization"] +#[allow(deprecated)] +#[deprecated = "Use overloaded `core::cmp::PartialEq`"] impl<'a, S: Str> Equiv for String { #[inline] fn equiv(&self, other: &S) -> bool { diff --git a/src/libcollections/vec.rs b/src/libcollections/vec.rs index c40f0c19c09f8..2396cf8cec67c 100644 --- a/src/libcollections/vec.rs +++ b/src/libcollections/vec.rs @@ -609,7 +609,8 @@ impl PartialOrd for Vec { #[unstable = "waiting on Eq stability"] impl Eq for Vec {} -#[experimental] +#[allow(deprecated)] +#[deprecated = "Use overloaded `core::cmp::PartialEq`"] impl> Equiv for Vec { #[inline] fn equiv(&self, other: &V) -> bool { self.as_slice() == other.as_slice() } diff --git a/src/libcore/cmp.rs b/src/libcore/cmp.rs index d5001a08b1dc5..df19256471ede 100644 --- a/src/libcore/cmp.rs +++ b/src/libcore/cmp.rs @@ -240,7 +240,7 @@ pub trait PartialOrd for Sized?: PartialEq { /// of different types. The most common use case for this relation is /// container types; e.g. it is often desirable to be able to use `&str` /// values to look up entries in a container with `String` keys. -#[experimental = "Better solutions may be discovered."] +#[deprecated = "Use overloaded core::cmp::PartialEq"] pub trait Equiv for Sized? { /// Implement this function to decide equivalent values. fn equiv(&self, other: &T) -> bool; diff --git a/src/libcore/ptr.rs b/src/libcore/ptr.rs index 5e2f5529e8d49..416bc4588b43c 100644 --- a/src/libcore/ptr.rs +++ b/src/libcore/ptr.rs @@ -321,12 +321,16 @@ impl PartialEq for *mut T { impl Eq for *mut T {} // Equivalence for pointers +#[allow(deprecated)] +#[deprecated = "Use overloaded `core::cmp::PartialEq`"] impl Equiv<*mut T> for *const T { fn equiv(&self, other: &*mut T) -> bool { self.to_uint() == other.to_uint() } } +#[allow(deprecated)] +#[deprecated = "Use overloaded `core::cmp::PartialEq`"] impl Equiv<*const T> for *mut T { fn equiv(&self, other: &*const T) -> bool { self.to_uint() == other.to_uint() diff --git a/src/libcore/slice.rs b/src/libcore/slice.rs index 12cd20584a180..85bd6adf8b8a6 100644 --- a/src/libcore/slice.rs +++ b/src/libcore/slice.rs @@ -1816,13 +1816,15 @@ impl PartialEq<[B]> for [A] where A: PartialEq { #[unstable = "waiting for DST"] impl Eq for [T] {} -#[unstable = "waiting for DST"] +#[allow(deprecated)] +#[deprecated = "Use overloaded `core::cmp::PartialEq`"] impl> Equiv for [T] { #[inline] fn equiv(&self, other: &V) -> bool { self.as_slice() == other.as_slice() } } -#[unstable = "waiting for DST"] +#[allow(deprecated)] +#[deprecated = "Use overloaded `core::cmp::PartialEq`"] impl<'a,T:PartialEq, Sized? V: AsSlice> Equiv for &'a mut [T] { #[inline] fn equiv(&self, other: &V) -> bool { self.as_slice() == other.as_slice() } diff --git a/src/libcore/str.rs b/src/libcore/str.rs index b9586399aec5d..4be628f0ac3b3 100644 --- a/src/libcore/str.rs +++ b/src/libcore/str.rs @@ -1248,6 +1248,8 @@ pub mod traits { } } + #[allow(deprecated)] + #[deprecated = "Use overloaded `core::cmp::PartialEq`"] impl Equiv for str { #[inline] fn equiv(&self, other: &S) -> bool { eq_slice(self, other.as_slice()) } diff --git a/src/libgraphviz/maybe_owned_vec.rs b/src/libgraphviz/maybe_owned_vec.rs index 6482a514115aa..05932db6632ff 100644 --- a/src/libgraphviz/maybe_owned_vec.rs +++ b/src/libgraphviz/maybe_owned_vec.rs @@ -96,6 +96,7 @@ impl<'a, T: Ord> Ord for MaybeOwnedVector<'a, T> { } } +#[allow(deprecated)] impl<'a, T: PartialEq, Sized? V: AsSlice> Equiv for MaybeOwnedVector<'a, T> { fn equiv(&self, other: &V) -> bool { self.as_slice() == other.as_slice() diff --git a/src/libstd/collections/hash/map.rs b/src/libstd/collections/hash/map.rs index 50a00714ea06c..bd07dbf5c9184 100644 --- a/src/libstd/collections/hash/map.rs +++ b/src/libstd/collections/hash/map.rs @@ -425,12 +425,14 @@ impl, V, S, H: Hasher> HashMap { table::make_hash(&self.hasher, x) } + #[allow(deprecated)] fn search_equiv<'a, Sized? Q: Hash + Equiv>(&'a self, q: &Q) -> Option> { let hash = self.make_hash(q); search_hashed(&self.table, &hash, |k| q.equiv(k)).into_option() } + #[allow(deprecated)] fn search_equiv_mut<'a, Sized? Q: Hash + Equiv>(&'a mut self, q: &Q) -> Option> { let hash = self.make_hash(q); diff --git a/src/libsyntax/parse/token.rs b/src/libsyntax/parse/token.rs index 1376f59d79fcf..52b54bc7f2d6d 100644 --- a/src/libsyntax/parse/token.rs +++ b/src/libsyntax/parse/token.rs @@ -623,6 +623,7 @@ impl fmt::Show for InternedString { } } +#[allow(deprecated)] impl<'a> Equiv<&'a str> for InternedString { fn equiv(&self, other: & &'a str) -> bool { (*other) == self.string.as_slice() From 131d4ed018f43a07324aebdd68a997b731945b0b Mon Sep 17 00:00:00 2001 From: Kang Seonghoon Date: Thu, 4 Dec 2014 00:56:45 +0900 Subject: [PATCH 28/83] rustdoc: Fixed a missing rendering of ForeignStaticItems. --- src/librustdoc/html/render.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs index 2be703e2458bb..8d7522af027b2 100644 --- a/src/librustdoc/html/render.rs +++ b/src/librustdoc/html/render.rs @@ -1435,7 +1435,8 @@ impl<'a> fmt::Show for Item<'a> { clean::TypedefItem(ref t) => item_typedef(fmt, self.item, t), clean::MacroItem(ref m) => item_macro(fmt, self.item, m), clean::PrimitiveItem(ref p) => item_primitive(fmt, self.item, p), - clean::StaticItem(ref i) => item_static(fmt, self.item, i), + clean::StaticItem(ref i) | clean::ForeignStaticItem(ref i) => + item_static(fmt, self.item, i), clean::ConstantItem(ref c) => item_constant(fmt, self.item, c), _ => Ok(()) } From a3bb8585e89f1eade039111733a2f1efa331e6ff Mon Sep 17 00:00:00 2001 From: Kang Seonghoon Date: Wed, 3 Dec 2014 23:57:45 +0900 Subject: [PATCH 29/83] rustdoc: Refactored various uses of ItemType. In particular, ItemType variants are no longer reexported. Since we already do namespace them via `item_type` mod, it's fine. --- src/librustdoc/html/format.rs | 3 +- src/librustdoc/html/item_type.rs | 100 +++++++++++---------- src/librustdoc/html/render.rs | 146 +++++++++++++++---------------- 3 files changed, 129 insertions(+), 120 deletions(-) diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index 6e0b76d441b93..9861d18ce51f0 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -23,7 +23,6 @@ use syntax::ast_util; use clean; use stability_summary::ModuleSummary; -use html::item_type; use html::item_type::ItemType; use html::render; use html::render::{cache, CURRENT_LOCATION_KEY}; @@ -283,7 +282,7 @@ fn path(w: &mut fmt::Formatter, path: &clean::Path, print_all: bool, url.push_str("/"); } match shortty { - item_type::Module => { + ItemType::Module => { url.push_str(fqp.last().unwrap().as_slice()); url.push_str("/index.html"); } diff --git a/src/librustdoc/html/item_type.rs b/src/librustdoc/html/item_type.rs index cb3ad9d063f3a..1917184c5a990 100644 --- a/src/librustdoc/html/item_type.rs +++ b/src/librustdoc/html/item_type.rs @@ -9,7 +9,6 @@ // except according to those terms. //! Item types. -pub use self::ItemType::*; use std::fmt; use clean; @@ -44,27 +43,64 @@ pub enum ItemType { } impl ItemType { + pub fn from_item(item: &clean::Item) -> ItemType { + match item.inner { + clean::ModuleItem(..) => ItemType::Module, + clean::StructItem(..) => ItemType::Struct, + clean::EnumItem(..) => ItemType::Enum, + clean::FunctionItem(..) => ItemType::Function, + clean::TypedefItem(..) => ItemType::Typedef, + clean::StaticItem(..) => ItemType::Static, + clean::ConstantItem(..) => ItemType::Constant, + clean::TraitItem(..) => ItemType::Trait, + clean::ImplItem(..) => ItemType::Impl, + clean::ViewItemItem(..) => ItemType::ViewItem, + clean::TyMethodItem(..) => ItemType::TyMethod, + clean::MethodItem(..) => ItemType::Method, + clean::StructFieldItem(..) => ItemType::StructField, + clean::VariantItem(..) => ItemType::Variant, + clean::ForeignFunctionItem(..) => ItemType::ForeignFunction, + clean::ForeignStaticItem(..) => ItemType::ForeignStatic, + clean::MacroItem(..) => ItemType::Macro, + clean::PrimitiveItem(..) => ItemType::Primitive, + clean::AssociatedTypeItem(..) => ItemType::AssociatedType, + } + } + + pub fn from_type_kind(kind: clean::TypeKind) -> ItemType { + match kind { + clean::TypeStruct => ItemType::Struct, + clean::TypeEnum => ItemType::Enum, + clean::TypeFunction => ItemType::Function, + clean::TypeTrait => ItemType::Trait, + clean::TypeModule => ItemType::Module, + clean::TypeStatic => ItemType::Static, + clean::TypeVariant => ItemType::Variant, + clean::TypeTypedef => ItemType::Typedef, + } + } + pub fn to_static_str(&self) -> &'static str { match *self { - Module => "mod", - Struct => "struct", - Enum => "enum", - Function => "fn", - Typedef => "type", - Static => "static", - Trait => "trait", - Impl => "impl", - ViewItem => "viewitem", - TyMethod => "tymethod", - Method => "method", - StructField => "structfield", - Variant => "variant", - ForeignFunction => "ffi", - ForeignStatic => "ffs", - Macro => "macro", - Primitive => "primitive", - AssociatedType => "associatedtype", - Constant => "constant", + ItemType::Module => "mod", + ItemType::Struct => "struct", + ItemType::Enum => "enum", + ItemType::Function => "fn", + ItemType::Typedef => "type", + ItemType::Static => "static", + ItemType::Trait => "trait", + ItemType::Impl => "impl", + ItemType::ViewItem => "viewitem", + ItemType::TyMethod => "tymethod", + ItemType::Method => "method", + ItemType::StructField => "structfield", + ItemType::Variant => "variant", + ItemType::ForeignFunction => "ffi", + ItemType::ForeignStatic => "ffs", + ItemType::Macro => "macro", + ItemType::Primitive => "primitive", + ItemType::AssociatedType => "associatedtype", + ItemType::Constant => "constant", } } } @@ -75,27 +111,3 @@ impl fmt::Show for ItemType { } } -pub fn shortty(item: &clean::Item) -> ItemType { - match item.inner { - clean::ModuleItem(..) => Module, - clean::StructItem(..) => Struct, - clean::EnumItem(..) => Enum, - clean::FunctionItem(..) => Function, - clean::TypedefItem(..) => Typedef, - clean::StaticItem(..) => Static, - clean::ConstantItem(..) => Constant, - clean::TraitItem(..) => Trait, - clean::ImplItem(..) => Impl, - clean::ViewItemItem(..) => ViewItem, - clean::TyMethodItem(..) => TyMethod, - clean::MethodItem(..) => Method, - clean::StructFieldItem(..) => StructField, - clean::VariantItem(..) => Variant, - clean::ForeignFunctionItem(..) => ForeignFunction, - clean::ForeignStaticItem(..) => ForeignStatic, - clean::MacroItem(..) => Macro, - clean::PrimitiveItem(..) => Primitive, - clean::AssociatedTypeItem(..) => AssociatedType, - } -} - diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs index 8d7522af027b2..8c2be47d9c0f4 100644 --- a/src/librustdoc/html/render.rs +++ b/src/librustdoc/html/render.rs @@ -61,8 +61,7 @@ use fold::DocFolder; use html::format::{VisSpace, Method, FnStyleSpace, MutableSpace, Stability}; use html::format::{ConciseStability, TyParamBounds, WhereClause}; use html::highlight; -use html::item_type::{ItemType, shortty}; -use html::item_type; +use html::item_type::ItemType; use html::layout; use html::markdown::Markdown; use html::markdown; @@ -314,19 +313,8 @@ pub fn run(mut krate: clean::Crate, let paths: HashMap, ItemType)> = analysis.as_ref().map(|a| { let paths = a.external_paths.borrow_mut().take().unwrap(); - paths.into_iter().map(|(k, (v, t))| { - (k, (v, match t { - clean::TypeStruct => item_type::Struct, - clean::TypeEnum => item_type::Enum, - clean::TypeFunction => item_type::Function, - clean::TypeTrait => item_type::Trait, - clean::TypeModule => item_type::Module, - clean::TypeStatic => item_type::Static, - clean::TypeVariant => item_type::Variant, - clean::TypeTypedef => item_type::Typedef, - })) - }).collect() - }).unwrap_or(HashMap::new()); + paths.into_iter().map(|(k, (v, t))| (k, (v, ItemType::from_type_kind(t)))).collect() + }).unwrap_or(HashMap::new()); let mut cache = Cache { impls: HashMap::new(), external_paths: paths.iter().map(|(&k, v)| (k, v.ref0().clone())) @@ -359,7 +347,7 @@ pub fn run(mut krate: clean::Crate, for &(n, ref e) in krate.externs.iter() { cache.extern_locations.insert(n, extern_location(e, &cx.dst)); let did = ast::DefId { krate: n, node: ast::CRATE_NODE_ID }; - cache.paths.insert(did, (vec![e.name.to_string()], item_type::Module)); + cache.paths.insert(did, (vec![e.name.to_string()], ItemType::Module)); } // Cache where all known primitives have their documentation located. @@ -642,6 +630,11 @@ fn mkdir(path: &Path) -> io::IoResult<()> { } } +/// Returns a documentation-level item type from the item. +fn shortty(item: &clean::Item) -> ItemType { + ItemType::from_item(item) +} + /// Takes a path to a source file and cleans the path to it. This canonicalizes /// things like ".." to components which preserve the "top down" hierarchy of a /// static HTML tree. @@ -855,13 +848,13 @@ impl DocFolder for Cache { let last = self.parent_stack.last().unwrap(); let did = *last; let path = match self.paths.get(&did) { - Some(&(_, item_type::Trait)) => + Some(&(_, ItemType::Trait)) => Some(self.stack[..self.stack.len() - 1]), // The current stack not necessarily has correlation for // where the type was defined. On the other hand, // `paths` always has the right information if present. - Some(&(ref fqp, item_type::Struct)) | - Some(&(ref fqp, item_type::Enum)) => + Some(&(ref fqp, ItemType::Struct)) | + Some(&(ref fqp, ItemType::Enum)) => Some(fqp[..fqp.len() - 1]), Some(..) => Some(self.stack.as_slice()), None => None @@ -929,7 +922,7 @@ impl DocFolder for Cache { clean::VariantItem(..) if !self.privmod => { let mut stack = self.stack.clone(); stack.pop(); - self.paths.insert(item.def_id, (stack, item_type::Enum)); + self.paths.insert(item.def_id, (stack, ItemType::Enum)); } clean::PrimitiveItem(..) if item.visibility.is_some() => { @@ -1491,45 +1484,50 @@ fn item_module(w: &mut fmt::Formatter, cx: &Context, !cx.ignore_private_item(&items[*i]) }).collect::>(); + // the order of item types in the listing + fn reorder(ty: ItemType) -> u8 { + match ty { + ItemType::ViewItem => 0, + ItemType::Primitive => 1, + ItemType::Module => 2, + ItemType::Macro => 3, + ItemType::Struct => 4, + ItemType::Enum => 5, + ItemType::Constant => 6, + ItemType::Static => 7, + ItemType::ForeignFunction => 8, + ItemType::ForeignStatic => 9, + ItemType::Trait => 10, + ItemType::Function => 11, + ItemType::Typedef => 12, + _ => 13 + ty as u8, + } + } + fn cmp(i1: &clean::Item, i2: &clean::Item, idx1: uint, idx2: uint) -> Ordering { - if shortty(i1) == shortty(i2) { + let ty1 = shortty(i1); + let ty2 = shortty(i2); + if ty1 == ty2 { return i1.name.cmp(&i2.name); } - match (&i1.inner, &i2.inner) { - (&clean::ViewItemItem(ref a), &clean::ViewItemItem(ref b)) => { - match (&a.inner, &b.inner) { - (&clean::ExternCrate(..), _) => Less, - (_, &clean::ExternCrate(..)) => Greater, - _ => idx1.cmp(&idx2), + + let tycmp = reorder(ty1).cmp(&reorder(ty2)); + if let Equal = tycmp { + // for reexports, `extern crate` takes precedence. + match (&i1.inner, &i2.inner) { + (&clean::ViewItemItem(ref a), &clean::ViewItemItem(ref b)) => { + match (&a.inner, &b.inner) { + (&clean::ExternCrate(..), _) => return Less, + (_, &clean::ExternCrate(..)) => return Greater, + _ => {} + } } + (_, _) => {} } - (&clean::ViewItemItem(..), _) => Less, - (_, &clean::ViewItemItem(..)) => Greater, - (&clean::PrimitiveItem(..), _) => Less, - (_, &clean::PrimitiveItem(..)) => Greater, - (&clean::ModuleItem(..), _) => Less, - (_, &clean::ModuleItem(..)) => Greater, - (&clean::MacroItem(..), _) => Less, - (_, &clean::MacroItem(..)) => Greater, - (&clean::StructItem(..), _) => Less, - (_, &clean::StructItem(..)) => Greater, - (&clean::EnumItem(..), _) => Less, - (_, &clean::EnumItem(..)) => Greater, - (&clean::ConstantItem(..), _) => Less, - (_, &clean::ConstantItem(..)) => Greater, - (&clean::StaticItem(..), _) => Less, - (_, &clean::StaticItem(..)) => Greater, - (&clean::ForeignFunctionItem(..), _) => Less, - (_, &clean::ForeignFunctionItem(..)) => Greater, - (&clean::ForeignStaticItem(..), _) => Less, - (_, &clean::ForeignStaticItem(..)) => Greater, - (&clean::TraitItem(..), _) => Less, - (_, &clean::TraitItem(..)) => Greater, - (&clean::FunctionItem(..), _) => Less, - (_, &clean::FunctionItem(..)) => Greater, - (&clean::TypedefItem(..), _) => Less, - (_, &clean::TypedefItem(..)) => Greater, - _ => idx1.cmp(&idx2), + + idx1.cmp(&idx2) + } else { + tycmp } } @@ -1546,26 +1544,26 @@ fn item_module(w: &mut fmt::Formatter, cx: &Context, try!(write!(w, "")); } curty = myty; - let (short, name) = match myitem.inner { - clean::ModuleItem(..) => ("modules", "Modules"), - clean::StructItem(..) => ("structs", "Structs"), - clean::EnumItem(..) => ("enums", "Enums"), - clean::FunctionItem(..) => ("functions", "Functions"), - clean::TypedefItem(..) => ("types", "Type Definitions"), - clean::StaticItem(..) => ("statics", "Statics"), - clean::ConstantItem(..) => ("constants", "Constants"), - clean::TraitItem(..) => ("traits", "Traits"), - clean::ImplItem(..) => ("impls", "Implementations"), - clean::ViewItemItem(..) => ("reexports", "Reexports"), - clean::TyMethodItem(..) => ("tymethods", "Type Methods"), - clean::MethodItem(..) => ("methods", "Methods"), - clean::StructFieldItem(..) => ("fields", "Struct Fields"), - clean::VariantItem(..) => ("variants", "Variants"), - clean::ForeignFunctionItem(..) => ("ffi-fns", "Foreign Functions"), - clean::ForeignStaticItem(..) => ("ffi-statics", "Foreign Statics"), - clean::MacroItem(..) => ("macros", "Macros"), - clean::PrimitiveItem(..) => ("primitives", "Primitive Types"), - clean::AssociatedTypeItem(..) => ("associated-types", "Associated Types"), + let (short, name) = match myty.unwrap() { + ItemType::Module => ("modules", "Modules"), + ItemType::Struct => ("structs", "Structs"), + ItemType::Enum => ("enums", "Enums"), + ItemType::Function => ("functions", "Functions"), + ItemType::Typedef => ("types", "Type Definitions"), + ItemType::Static => ("statics", "Statics"), + ItemType::Constant => ("constants", "Constants"), + ItemType::Trait => ("traits", "Traits"), + ItemType::Impl => ("impls", "Implementations"), + ItemType::ViewItem => ("reexports", "Reexports"), + ItemType::TyMethod => ("tymethods", "Type Methods"), + ItemType::Method => ("methods", "Methods"), + ItemType::StructField => ("fields", "Struct Fields"), + ItemType::Variant => ("variants", "Variants"), + ItemType::ForeignFunction => ("ffi-fns", "Foreign Functions"), + ItemType::ForeignStatic => ("ffi-statics", "Foreign Statics"), + ItemType::Macro => ("macros", "Macros"), + ItemType::Primitive => ("primitives", "Primitive Types"), + ItemType::AssociatedType => ("associated-types", "Associated Types"), }; try!(write!(w, "

    \ From 1cb1f00d40f000ac7633b62a603db4fcea835ca6 Mon Sep 17 00:00:00 2001 From: Kang Seonghoon Date: Thu, 4 Dec 2014 00:29:21 +0900 Subject: [PATCH 30/83] rustdoc: Removed Foreign{Function,Static} item types. They are just (unsafe) functions and static items to most users and even compilers! The metadata doesn't distinguish them, so Rustdoc ended up producing broken links (generated `ffi.*.html`, links to `fn.*.html`). It would be best to avoid this pitfall at all. --- src/librustdoc/html/item_type.rs | 9 +++------ src/librustdoc/html/render.rs | 12 ++++-------- src/librustdoc/html/static/main.js | 4 ++-- 3 files changed, 9 insertions(+), 16 deletions(-) diff --git a/src/librustdoc/html/item_type.rs b/src/librustdoc/html/item_type.rs index 1917184c5a990..0ad12b957ba8f 100644 --- a/src/librustdoc/html/item_type.rs +++ b/src/librustdoc/html/item_type.rs @@ -34,8 +34,7 @@ pub enum ItemType { Method = 10, StructField = 11, Variant = 12, - ForeignFunction = 13, - ForeignStatic = 14, + // we used to have ForeignFunction and ForeignStatic. they are retired now. Macro = 15, Primitive = 16, AssociatedType = 17, @@ -59,8 +58,8 @@ impl ItemType { clean::MethodItem(..) => ItemType::Method, clean::StructFieldItem(..) => ItemType::StructField, clean::VariantItem(..) => ItemType::Variant, - clean::ForeignFunctionItem(..) => ItemType::ForeignFunction, - clean::ForeignStaticItem(..) => ItemType::ForeignStatic, + clean::ForeignFunctionItem(..) => ItemType::Function, // no ForeignFunction + clean::ForeignStaticItem(..) => ItemType::Static, // no ForeignStatic clean::MacroItem(..) => ItemType::Macro, clean::PrimitiveItem(..) => ItemType::Primitive, clean::AssociatedTypeItem(..) => ItemType::AssociatedType, @@ -95,8 +94,6 @@ impl ItemType { ItemType::Method => "method", ItemType::StructField => "structfield", ItemType::Variant => "variant", - ItemType::ForeignFunction => "ffi", - ItemType::ForeignStatic => "ffs", ItemType::Macro => "macro", ItemType::Primitive => "primitive", ItemType::AssociatedType => "associatedtype", diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs index 8c2be47d9c0f4..36ce2796faf0d 100644 --- a/src/librustdoc/html/render.rs +++ b/src/librustdoc/html/render.rs @@ -1495,12 +1495,10 @@ fn item_module(w: &mut fmt::Formatter, cx: &Context, ItemType::Enum => 5, ItemType::Constant => 6, ItemType::Static => 7, - ItemType::ForeignFunction => 8, - ItemType::ForeignStatic => 9, - ItemType::Trait => 10, - ItemType::Function => 11, - ItemType::Typedef => 12, - _ => 13 + ty as u8, + ItemType::Trait => 8, + ItemType::Function => 9, + ItemType::Typedef => 10, + _ => 11 + ty as u8, } } @@ -1559,8 +1557,6 @@ fn item_module(w: &mut fmt::Formatter, cx: &Context, ItemType::Method => ("methods", "Methods"), ItemType::StructField => ("fields", "Struct Fields"), ItemType::Variant => ("variants", "Variants"), - ItemType::ForeignFunction => ("ffi-fns", "Foreign Functions"), - ItemType::ForeignStatic => ("ffi-statics", "Foreign Statics"), ItemType::Macro => ("macros", "Macros"), ItemType::Primitive => ("primitives", "Primitive Types"), ItemType::AssociatedType => ("associated-types", "Associated Types"), diff --git a/src/librustdoc/html/static/main.js b/src/librustdoc/html/static/main.js index 7c6f7ed3fe230..0fc6677cd9f71 100644 --- a/src/librustdoc/html/static/main.js +++ b/src/librustdoc/html/static/main.js @@ -566,8 +566,8 @@ "method", "structfield", "variant", - "ffi", - "ffs", + "ffi", // retained for backward compatibility + "ffs", // retained for backward compatibility "macro", "primitive", "associatedtype", From 1068855925581d7f6d4a177c5f55ac3d5da8afd7 Mon Sep 17 00:00:00 2001 From: Kang Seonghoon Date: Thu, 4 Dec 2014 00:51:19 +0900 Subject: [PATCH 31/83] rustdoc: Avoid rendering foreign items to the sidebar. Otherwise the generated documentation is 30% larger. The sidebar renders an entry for each item to all items, so large modules have O(n^2) items rendered in the sidebars. Not a correct solution, but at least it works. --- src/librustdoc/html/render.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs index 36ce2796faf0d..9eee8e04f0c2b 100644 --- a/src/librustdoc/html/render.rs +++ b/src/librustdoc/html/render.rs @@ -1244,6 +1244,10 @@ impl Context { for item in m.items.iter() { if self.ignore_private_item(item) { continue } + // avoid putting foreign items to the sidebar. + if let &clean::ForeignFunctionItem(..) = &item.inner { continue } + if let &clean::ForeignStaticItem(..) = &item.inner { continue } + let short = shortty(item).to_static_str(); let myname = match item.name { None => continue, From c200ae5a8a1ab70cfea39ef184e1fd0e87b33181 Mon Sep 17 00:00:00 2001 From: Nick Cameron Date: Tue, 2 Dec 2014 14:13:12 -0800 Subject: [PATCH 32/83] Remove feature gates for `if let`, `while let`, and tuple indexing Closes #19469 --- src/libsyntax/feature_gate.rs | 19 +++---------------- src/test/compile-fail/borrow-tuple-fields.rs | 2 -- src/test/compile-fail/if-let.rs | 2 +- src/test/compile-fail/issue-18566.rs | 2 -- src/test/compile-fail/issue-19244-1.rs | 2 -- .../compile-fail/lint-unnecessary-parens.rs | 1 - src/test/compile-fail/move-fragments-1.rs | 2 -- .../compile-fail/move-out-of-tuple-field.rs | 2 -- .../compile-fail/tuple-index-not-tuple.rs | 2 -- .../compile-fail/tuple-index-out-of-bounds.rs | 2 -- src/test/compile-fail/while-let.rs | 2 +- 11 files changed, 5 insertions(+), 33 deletions(-) diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index 7453da6374e00..c798b70551f16 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -65,13 +65,13 @@ static KNOWN_FEATURES: &'static [(&'static str, Status)] = &[ ("unboxed_closures", Active), ("import_shadowing", Active), ("advanced_slice_patterns", Active), - ("tuple_indexing", Active), + ("tuple_indexing", Accepted), ("associated_types", Active), ("visible_private_types", Active), ("slicing_syntax", Active), - ("if_let", Active), - ("while_let", Active), + ("if_let", Accepted), + ("while_let", Accepted), // if you change this list without updating src/doc/reference.md, cmr will be sad @@ -309,24 +309,11 @@ impl<'a, 'v> Visitor<'v> for Context<'a> { "unboxed closures are a work-in-progress \ feature with known bugs"); } - ast::ExprTupField(..) => { - self.gate_feature("tuple_indexing", - e.span, - "tuple indexing is experimental"); - } - ast::ExprIfLet(..) => { - self.gate_feature("if_let", e.span, - "`if let` syntax is experimental"); - } ast::ExprSlice(..) => { self.gate_feature("slicing_syntax", e.span, "slicing syntax is experimental"); } - ast::ExprWhileLet(..) => { - self.gate_feature("while_let", e.span, - "`while let` syntax is experimental"); - } _ => {} } visit::walk_expr(self, e); diff --git a/src/test/compile-fail/borrow-tuple-fields.rs b/src/test/compile-fail/borrow-tuple-fields.rs index 519bad4e627b7..1d09143c24d1d 100644 --- a/src/test/compile-fail/borrow-tuple-fields.rs +++ b/src/test/compile-fail/borrow-tuple-fields.rs @@ -8,8 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(tuple_indexing)] - struct Foo(Box, int); struct Bar(int, int); diff --git a/src/test/compile-fail/if-let.rs b/src/test/compile-fail/if-let.rs index b82fb7a94c95f..88b6854bb1d2c 100644 --- a/src/test/compile-fail/if-let.rs +++ b/src/test/compile-fail/if-let.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(macro_rules,if_let)] +#![feature(macro_rules)] fn macros() { macro_rules! foo{ diff --git a/src/test/compile-fail/issue-18566.rs b/src/test/compile-fail/issue-18566.rs index 89b1d8540d8c5..f64d8fee2d8b3 100644 --- a/src/test/compile-fail/issue-18566.rs +++ b/src/test/compile-fail/issue-18566.rs @@ -8,8 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(tuple_indexing)] - struct MyPtr<'a>(&'a mut uint); impl<'a> Deref for MyPtr<'a> { fn deref<'b>(&'b self) -> &'b uint { self.0 } diff --git a/src/test/compile-fail/issue-19244-1.rs b/src/test/compile-fail/issue-19244-1.rs index 4fcbb87889054..7ca83f21305f1 100644 --- a/src/test/compile-fail/issue-19244-1.rs +++ b/src/test/compile-fail/issue-19244-1.rs @@ -8,8 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(tuple_indexing)] - const TUP: (uint,) = (42,); fn main() { diff --git a/src/test/compile-fail/lint-unnecessary-parens.rs b/src/test/compile-fail/lint-unnecessary-parens.rs index 826a4ea5a8080..b71effa6f861b 100644 --- a/src/test/compile-fail/lint-unnecessary-parens.rs +++ b/src/test/compile-fail/lint-unnecessary-parens.rs @@ -9,7 +9,6 @@ // except according to those terms. #![deny(unused_parens)] -#![feature(if_let,while_let)] #[deriving(Eq, PartialEq)] struct X { y: bool } diff --git a/src/test/compile-fail/move-fragments-1.rs b/src/test/compile-fail/move-fragments-1.rs index ccf12cf79e1d6..e45862a7fc6e4 100644 --- a/src/test/compile-fail/move-fragments-1.rs +++ b/src/test/compile-fail/move-fragments-1.rs @@ -8,8 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(tuple_indexing)] - // Test that we correctly compute the move fragments for a fn. // // Note that the code below is not actually incorrect; the diff --git a/src/test/compile-fail/move-out-of-tuple-field.rs b/src/test/compile-fail/move-out-of-tuple-field.rs index 7f55a78e8b784..7fcb54e04672d 100644 --- a/src/test/compile-fail/move-out-of-tuple-field.rs +++ b/src/test/compile-fail/move-out-of-tuple-field.rs @@ -8,8 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(tuple_indexing)] - struct Foo(Box); fn main() { diff --git a/src/test/compile-fail/tuple-index-not-tuple.rs b/src/test/compile-fail/tuple-index-not-tuple.rs index d4ef0e20b266d..33aeebb369166 100644 --- a/src/test/compile-fail/tuple-index-not-tuple.rs +++ b/src/test/compile-fail/tuple-index-not-tuple.rs @@ -8,8 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(tuple_indexing)] - struct Point { x: int, y: int } struct Empty; diff --git a/src/test/compile-fail/tuple-index-out-of-bounds.rs b/src/test/compile-fail/tuple-index-out-of-bounds.rs index f9bf2746794ac..609e34f2274b7 100644 --- a/src/test/compile-fail/tuple-index-out-of-bounds.rs +++ b/src/test/compile-fail/tuple-index-out-of-bounds.rs @@ -8,8 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(tuple_indexing)] - struct Point(int, int); fn main() { diff --git a/src/test/compile-fail/while-let.rs b/src/test/compile-fail/while-let.rs index 0dd442ec3f66a..ccf3d2dd75076 100644 --- a/src/test/compile-fail/while-let.rs +++ b/src/test/compile-fail/while-let.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(macro_rules,while_let)] +#![feature(macro_rules)] fn macros() { macro_rules! foo{ From e621116b867c7e4b2d7082aa9460f635443cd1b8 Mon Sep 17 00:00:00 2001 From: Alexander Light Date: Sun, 30 Nov 2014 08:19:29 -0500 Subject: [PATCH 33/83] make fmt_macros and rustdoc have standard doc attributes --- src/libfmt_macros/lib.rs | 5 +++++ src/librustdoc/lib.rs | 4 ++++ 2 files changed, 9 insertions(+) diff --git a/src/libfmt_macros/lib.rs b/src/libfmt_macros/lib.rs index 6b8fafbed5d77..03b65b3f71c84 100644 --- a/src/libfmt_macros/lib.rs +++ b/src/libfmt_macros/lib.rs @@ -18,6 +18,11 @@ #![experimental] #![crate_type = "rlib"] #![crate_type = "dylib"] +#![doc(html_logo_url = "http://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png", + html_favicon_url = "http://www.rust-lang.org/favicon.ico", + html_root_url = "http://doc.rust-lang.org/nightly/", + html_playground_url = "http://play.rust-lang.org/")] + #![feature(macro_rules, globs, import_shadowing)] pub use self::Piece::*; pub use self::Position::*; diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index c592474b057ff..bc02d8c4c300d 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -12,6 +12,10 @@ #![experimental] #![crate_type = "dylib"] #![crate_type = "rlib"] +#![doc(html_logo_url = "http://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png", + html_favicon_url = "http://www.rust-lang.org/favicon.ico", + html_root_url = "http://doc.rust-lang.org/nightly/", + html_playground_url = "http://play.rust-lang.org/")] #![allow(unknown_features)] #![feature(globs, if_let, macro_rules, phase, slicing_syntax, tuple_indexing)] From c7ae4a9664802582c4bc14be107d28b8257aeb91 Mon Sep 17 00:00:00 2001 From: Erick Tryzelaar Date: Sun, 30 Nov 2014 19:39:26 -0800 Subject: [PATCH 34/83] whitespace cleanup --- src/etc/rustup.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/etc/rustup.sh b/src/etc/rustup.sh index 4829e15fb0ffa..f73a0248c8516 100644 --- a/src/etc/rustup.sh +++ b/src/etc/rustup.sh @@ -188,7 +188,7 @@ flag() { fi } -validate_opt () { +validate_opt() { for arg in $CFG_ARGS do isArgValid=0 From 3b4ad726f1d9982a91693e36bfce51b1013f16c4 Mon Sep 17 00:00:00 2001 From: Erick Tryzelaar Date: Sun, 30 Nov 2014 21:17:08 -0800 Subject: [PATCH 35/83] rustup: make rustup executable --- src/etc/rustup.sh | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 src/etc/rustup.sh diff --git a/src/etc/rustup.sh b/src/etc/rustup.sh old mode 100644 new mode 100755 From 98f01f1f5481f12796d2c7b57f36e998b1e644f0 Mon Sep 17 00:00:00 2001 From: Erick Tryzelaar Date: Sun, 30 Nov 2014 19:51:52 -0800 Subject: [PATCH 36/83] rustup: probe for the existance of tar --- src/etc/rustup.sh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/etc/rustup.sh b/src/etc/rustup.sh index f73a0248c8516..52489412071ca 100755 --- a/src/etc/rustup.sh +++ b/src/etc/rustup.sh @@ -230,6 +230,7 @@ validate_opt() { } probe_need CFG_CURL curl +probe_need CFG_TAR tar CFG_SRC_DIR="$(cd $(dirname $0) && pwd)/" CFG_SELF="$0" @@ -430,7 +431,7 @@ if [ -z "${CFG_DISABLE_CARGO}" ]; then fi -(cd "${TMP_DIR}" && tar xzf "${TARBALL_NAME}") +(cd "${TMP_DIR}" && ${CFG_TAR} xzf "${TARBALL_NAME}") if [ $? -ne 0 ] then rm -Rf "${TMP_DIR}" @@ -457,7 +458,7 @@ then fi if [ -z "${CFG_DISABLE_CARGO}" ]; then - (cd "${TMP_DIR}" && tar xzf "${CARGO_TARBALL_NAME}") + (cd "${TMP_DIR}" && ${CFG_TAR} xzf "${CARGO_TARBALL_NAME}") if [ $? -ne 0 ] then rm -Rf "${TMP_DIR}" From e688eb3b4fcf4c5a22c4e69a2216d8d48a4109aa Mon Sep 17 00:00:00 2001 From: Erick Tryzelaar Date: Sun, 30 Nov 2014 20:06:24 -0800 Subject: [PATCH 37/83] rustup: add a RUST_ prefix to the rust-specific variables --- src/etc/rustup.sh | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/etc/rustup.sh b/src/etc/rustup.sh index 52489412071ca..03eb518272c51 100755 --- a/src/etc/rustup.sh +++ b/src/etc/rustup.sh @@ -389,14 +389,15 @@ esac msg "host triple: ${HOST_TRIPLE}" -PACKAGE_NAME=rust-nightly -PACKAGE_NAME_AND_TRIPLE="${PACKAGE_NAME}-${HOST_TRIPLE}" -TARBALL_NAME="${PACKAGE_NAME_AND_TRIPLE}.tar.gz" -REMOTE_TARBALL="https://static.rust-lang.org/dist/${TARBALL_NAME}" TMP_DIR="./rustup-tmp-install" -LOCAL_TARBALL="${TMP_DIR}/${TARBALL_NAME}" -LOCAL_INSTALL_DIR="${TMP_DIR}/${PACKAGE_NAME_AND_TRIPLE}" -LOCAL_INSTALL_SCRIPT="${LOCAL_INSTALL_DIR}/install.sh" + +RUST_PACKAGE_NAME=rust-nightly +RUST_PACKAGE_NAME_AND_TRIPLE="${RUST_PACKAGE_NAME}-${HOST_TRIPLE}" +RUST_TARBALL_NAME="${RUST_PACKAGE_NAME_AND_TRIPLE}.tar.gz" +RUST_REMOTE_TARBALL="https://static.rust-lang.org/dist/${RUST_TARBALL_NAME}" +RUST_LOCAL_TARBALL="${TMP_DIR}/${RUST_TARBALL_NAME}" +RUST_LOCAL_INSTALL_DIR="${TMP_DIR}/${RUST_PACKAGE_NAME_AND_TRIPLE}" +RUST_LOCAL_INSTALL_SCRIPT="${RUST_LOCAL_INSTALL_DIR}/install.sh" CARGO_PACKAGE_NAME=cargo-nightly CARGO_PACKAGE_NAME_AND_TRIPLE="${CARGO_PACKAGE_NAME}-${HOST_TRIPLE}" @@ -413,7 +414,7 @@ mkdir -p "${TMP_DIR}" need_ok "failed to create create temporary installation directory" msg "downloading rust installer" -"${CFG_CURL}" "${REMOTE_TARBALL}" > "${LOCAL_TARBALL}" +"${CFG_CURL}" "${RUST_REMOTE_TARBALL}" > "${RUST_LOCAL_TARBALL}" if [ $? -ne 0 ] then rm -Rf "${TMP_DIR}" @@ -431,7 +432,7 @@ if [ -z "${CFG_DISABLE_CARGO}" ]; then fi -(cd "${TMP_DIR}" && ${CFG_TAR} xzf "${TARBALL_NAME}") +(cd "${TMP_DIR}" && ${CFG_TAR} xzf "${RUST_TARBALL_NAME}") if [ $? -ne 0 ] then rm -Rf "${TMP_DIR}" @@ -450,7 +451,7 @@ then MAYBE_PREFIX="--prefix=${CFG_PREFIX}" fi -sh "${LOCAL_INSTALL_SCRIPT}" "${MAYBE_UNINSTALL}" "${MAYBE_PREFIX}" +sh "${RUST_LOCAL_INSTALL_SCRIPT}" "${MAYBE_UNINSTALL}" "${MAYBE_PREFIX}" if [ $? -ne 0 ] then rm -Rf "${TMP_DIR}" From fcb31e834716be12b98390eab921d5b7d39098de Mon Sep 17 00:00:00 2001 From: Erick Tryzelaar Date: Sun, 30 Nov 2014 20:08:06 -0800 Subject: [PATCH 38/83] rustup: rename TMP_DIR to CFG_TMP_DIR --- src/etc/rustup.sh | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/etc/rustup.sh b/src/etc/rustup.sh index 03eb518272c51..a0436f23e4077 100755 --- a/src/etc/rustup.sh +++ b/src/etc/rustup.sh @@ -389,35 +389,35 @@ esac msg "host triple: ${HOST_TRIPLE}" -TMP_DIR="./rustup-tmp-install" +CFG_TMP_DIR="./rustup-tmp-install" RUST_PACKAGE_NAME=rust-nightly RUST_PACKAGE_NAME_AND_TRIPLE="${RUST_PACKAGE_NAME}-${HOST_TRIPLE}" RUST_TARBALL_NAME="${RUST_PACKAGE_NAME_AND_TRIPLE}.tar.gz" RUST_REMOTE_TARBALL="https://static.rust-lang.org/dist/${RUST_TARBALL_NAME}" -RUST_LOCAL_TARBALL="${TMP_DIR}/${RUST_TARBALL_NAME}" -RUST_LOCAL_INSTALL_DIR="${TMP_DIR}/${RUST_PACKAGE_NAME_AND_TRIPLE}" +RUST_LOCAL_TARBALL="${CFG_TMP_DIR}/${RUST_TARBALL_NAME}" +RUST_LOCAL_INSTALL_DIR="${CFG_TMP_DIR}/${RUST_PACKAGE_NAME_AND_TRIPLE}" RUST_LOCAL_INSTALL_SCRIPT="${RUST_LOCAL_INSTALL_DIR}/install.sh" CARGO_PACKAGE_NAME=cargo-nightly CARGO_PACKAGE_NAME_AND_TRIPLE="${CARGO_PACKAGE_NAME}-${HOST_TRIPLE}" CARGO_TARBALL_NAME="${CARGO_PACKAGE_NAME_AND_TRIPLE}.tar.gz" CARGO_REMOTE_TARBALL="https://static.rust-lang.org/cargo-dist/${CARGO_TARBALL_NAME}" -CARGO_LOCAL_TARBALL="${TMP_DIR}/${CARGO_TARBALL_NAME}" -CARGO_LOCAL_INSTALL_DIR="${TMP_DIR}/${CARGO_PACKAGE_NAME_AND_TRIPLE}" +CARGO_LOCAL_TARBALL="${CFG_TMP_DIR}/${CARGO_TARBALL_NAME}" +CARGO_LOCAL_INSTALL_DIR="${CFG_TMP_DIR}/${CARGO_PACKAGE_NAME_AND_TRIPLE}" CARGO_LOCAL_INSTALL_SCRIPT="${CARGO_LOCAL_INSTALL_DIR}/install.sh" -rm -Rf "${TMP_DIR}" +rm -Rf "${CFG_TMP_DIR}" need_ok "failed to remove temporary installation directory" -mkdir -p "${TMP_DIR}" +mkdir -p "${CFG_TMP_DIR}" need_ok "failed to create create temporary installation directory" msg "downloading rust installer" "${CFG_CURL}" "${RUST_REMOTE_TARBALL}" > "${RUST_LOCAL_TARBALL}" if [ $? -ne 0 ] then - rm -Rf "${TMP_DIR}" + rm -Rf "${CFG_TMP_DIR}" err "failed to download installer" fi @@ -426,16 +426,16 @@ if [ -z "${CFG_DISABLE_CARGO}" ]; then "${CFG_CURL}" "${CARGO_REMOTE_TARBALL}" > "${CARGO_LOCAL_TARBALL}" if [ $? -ne 0 ] then - rm -Rf "${TMP_DIR}" + rm -Rf "${CFG_TMP_DIR}" err "failed to download cargo installer" fi fi -(cd "${TMP_DIR}" && ${CFG_TAR} xzf "${RUST_TARBALL_NAME}") +(cd "${CFG_TMP_DIR}" && ${CFG_TAR} xzf "${RUST_TARBALL_NAME}") if [ $? -ne 0 ] then - rm -Rf "${TMP_DIR}" + rm -Rf "${CFG_TMP_DIR}" err "failed to unpack installer" fi @@ -454,25 +454,25 @@ fi sh "${RUST_LOCAL_INSTALL_SCRIPT}" "${MAYBE_UNINSTALL}" "${MAYBE_PREFIX}" if [ $? -ne 0 ] then - rm -Rf "${TMP_DIR}" + rm -Rf "${CFG_TMP_DIR}" err "failed to install Rust" fi if [ -z "${CFG_DISABLE_CARGO}" ]; then - (cd "${TMP_DIR}" && ${CFG_TAR} xzf "${CARGO_TARBALL_NAME}") + (cd "${CFG_TMP_DIR}" && ${CFG_TAR} xzf "${CARGO_TARBALL_NAME}") if [ $? -ne 0 ] then - rm -Rf "${TMP_DIR}" + rm -Rf "${CFG_TMP_DIR}" err "failed to unpack cargo installer" fi sh "${CARGO_LOCAL_INSTALL_SCRIPT}" "${MAYBE_UNINSTALL}" "${MAYBE_PREFIX}" if [ $? -ne 0 ] then - rm -Rf "${TMP_DIR}" + rm -Rf "${CFG_TMP_DIR}" err "failed to install Cargo" fi fi -rm -Rf "${TMP_DIR}" +rm -Rf "${CFG_TMP_DIR}" need_ok "couldn't rm temporary installation directory" From 4f65d97bf8b4ec68ecba87ce75df1d6a6096a4e7 Mon Sep 17 00:00:00 2001 From: Erick Tryzelaar Date: Sun, 30 Nov 2014 20:13:31 -0800 Subject: [PATCH 39/83] rustup: factor out the install flags into a CFG_INSTALL_FLAGS variable --- src/etc/rustup.sh | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/src/etc/rustup.sh b/src/etc/rustup.sh index a0436f23e4077..88bcc1916e775 100755 --- a/src/etc/rustup.sh +++ b/src/etc/rustup.sh @@ -389,6 +389,17 @@ esac msg "host triple: ${HOST_TRIPLE}" +CFG_INSTALL_FLAGS="" +if [ -n "${CFG_UNINSTALL}" ] +then + CFG_INSTALL_FLAGS="${CFG_INSTALL_FLAGS} --uninstall" +fi + +if [ -n "${CFG_PREFIX}" ] +then + CFG_INSTALL_FLAGS="${CFG_INSTALL_FLAGS} --prefix=${CFG_PREFIX}" +fi + CFG_TMP_DIR="./rustup-tmp-install" RUST_PACKAGE_NAME=rust-nightly @@ -439,19 +450,7 @@ then err "failed to unpack installer" fi -MAYBE_UNINSTALL= -if [ -n "${CFG_UNINSTALL}" ] -then - MAYBE_UNINSTALL="--uninstall" -fi - -MAYBE_PREFIX= -if [ -n "${CFG_PREFIX}" ] -then - MAYBE_PREFIX="--prefix=${CFG_PREFIX}" -fi - -sh "${RUST_LOCAL_INSTALL_SCRIPT}" "${MAYBE_UNINSTALL}" "${MAYBE_PREFIX}" +sh "${RUST_LOCAL_INSTALL_SCRIPT}" "${CFG_INSTALL_FLAGS}" if [ $? -ne 0 ] then rm -Rf "${CFG_TMP_DIR}" @@ -466,7 +465,7 @@ if [ -z "${CFG_DISABLE_CARGO}" ]; then err "failed to unpack cargo installer" fi - sh "${CARGO_LOCAL_INSTALL_SCRIPT}" "${MAYBE_UNINSTALL}" "${MAYBE_PREFIX}" + sh "${CARGO_LOCAL_INSTALL_SCRIPT}" "${CFG_INSTALL_FLAGS}" if [ $? -ne 0 ] then rm -Rf "${CFG_TMP_DIR}" From 2e1a50121ef265214c5e2a7d82fe40b4928575ab Mon Sep 17 00:00:00 2001 From: Corey Richardson Date: Tue, 2 Dec 2014 16:48:48 -0800 Subject: [PATCH 40/83] syntax: support ES6-style unicode escapes First half of bootstrapping https://github.com/rust-lang/rfcs/pull/446 --- src/libsyntax/parse/lexer/mod.rs | 81 ++++++++++++++++++- src/libsyntax/parse/mod.rs | 22 +++-- .../compile-fail/new-unicode-escapes-1.rs | 13 +++ .../compile-fail/new-unicode-escapes-2.rs | 13 +++ .../compile-fail/new-unicode-escapes-3.rs | 13 +++ .../compile-fail/new-unicode-escapes-4.rs | 13 +++ src/test/run-pass/new-unicode-escapes.rs | 22 +++++ 7 files changed, 169 insertions(+), 8 deletions(-) create mode 100644 src/test/compile-fail/new-unicode-escapes-1.rs create mode 100644 src/test/compile-fail/new-unicode-escapes-2.rs create mode 100644 src/test/compile-fail/new-unicode-escapes-3.rs create mode 100644 src/test/compile-fail/new-unicode-escapes-4.rs create mode 100644 src/test/run-pass/new-unicode-escapes.rs diff --git a/src/libsyntax/parse/lexer/mod.rs b/src/libsyntax/parse/lexer/mod.rs index 57983a6dee6be..27b65e0f52798 100644 --- a/src/libsyntax/parse/lexer/mod.rs +++ b/src/libsyntax/parse/lexer/mod.rs @@ -764,6 +764,15 @@ impl<'a> StringReader<'a> { } } + // SNAP c9f6d69 + #[allow(unused)] + fn old_escape_warning(&mut self, sp: Span) { + self.span_diagnostic + .span_warn(sp, "\\U00ABCD12 and \\uABCD escapes are deprecated"); + self.span_diagnostic + .span_help(sp, "use \\u{ABCD12} escapes instead"); + } + /// Scan for a single (possibly escaped) byte or char /// in a byte, (non-raw) byte string, char, or (non-raw) string literal. /// `start` is the position of `first_source_char`, which is already consumed. @@ -782,12 +791,24 @@ impl<'a> StringReader<'a> { Some(e) => { return match e { 'n' | 'r' | 't' | '\\' | '\'' | '"' | '0' => true, - 'x' => self.scan_hex_digits(2u, delim, !ascii_only), + 'x' => self.scan_byte_escape(delim, !ascii_only), 'u' if !ascii_only => { - self.scan_hex_digits(4u, delim, false) + if self.curr == Some('{') { + self.scan_unicode_escape(delim) + } else { + let res = self.scan_hex_digits(4u, delim, false); + // SNAP c9f6d69 + //let sp = codemap::mk_sp(escaped_pos, self.last_pos); + //self.old_escape_warning(sp); + res + } } 'U' if !ascii_only => { - self.scan_hex_digits(8u, delim, false) + let res = self.scan_hex_digits(8u, delim, false); + // SNAP c9f6d69 + //let sp = codemap::mk_sp(escaped_pos, self.last_pos); + //self.old_escape_warning(sp); + res } '\n' if delim == '"' => { self.consume_whitespace(); @@ -848,6 +869,56 @@ impl<'a> StringReader<'a> { true } + /// Scan over a \u{...} escape + /// + /// At this point, we have already seen the \ and the u, the { is the current character. We + /// will read at least one digit, and up to 6, and pass over the }. + fn scan_unicode_escape(&mut self, delim: char) -> bool { + self.bump(); // past the { + let start_bpos = self.last_pos; + let mut count: uint = 0; + let mut accum_int = 0; + + while !self.curr_is('}') && count <= 6 { + let c = match self.curr { + Some(c) => c, + None => { + self.fatal_span_(start_bpos, self.last_pos, + "unterminated unicode escape (found EOF)"); + } + }; + accum_int *= 16; + accum_int += c.to_digit(16).unwrap_or_else(|| { + if c == delim { + self.fatal_span_(self.last_pos, self.pos, + "unterminated unicode escape (needed a `}`)"); + } else { + self.fatal_span_char(self.last_pos, self.pos, + "illegal character in unicode escape", c); + } + }) as u32; + self.bump(); + count += 1; + } + + if count > 6 { + self.fatal_span_(start_bpos, self.last_pos, + "overlong unicode escape (can have at most 6 hex digits)"); + } + + self.bump(); // past the ending } + + let mut valid = count >= 1 && count <= 6; + if char::from_u32(accum_int).is_none() { + valid = false; + } + + if !valid { + self.fatal_span_(start_bpos, self.last_pos, "illegal unicode character escape"); + } + valid + } + /// Scan over a float exponent. fn scan_float_exponent(&mut self) { if self.curr_is('e') || self.curr_is('E') { @@ -1273,6 +1344,10 @@ impl<'a> StringReader<'a> { return token::Byte(id); } + fn scan_byte_escape(&mut self, delim: char, below_0x7f_only: bool) -> bool { + self.scan_hex_digits(2, delim, below_0x7f_only) + } + fn scan_byte_string(&mut self) -> token::Lit { self.bump(); let start = self.last_pos; diff --git a/src/libsyntax/parse/mod.rs b/src/libsyntax/parse/mod.rs index b46f7cdfe22ad..8d0c2de048a56 100644 --- a/src/libsyntax/parse/mod.rs +++ b/src/libsyntax/parse/mod.rs @@ -393,16 +393,28 @@ pub fn char_lit(lit: &str) -> (char, int) { let msg = format!("lexer should have rejected a bad character escape {}", lit); let msg2 = msg.as_slice(); - let esc: |uint| -> Option<(char, int)> = |len| + fn esc(len: uint, lit: &str) -> Option<(char, int)> { num::from_str_radix(lit.slice(2, len), 16) .and_then(char::from_u32) - .map(|x| (x, len as int)); + .map(|x| (x, len as int)) + } + + let unicode_escape: || -> Option<(char, int)> = || + if lit.as_bytes()[2] == b'{' { + let idx = lit.find('}').expect(msg2); + let subslice = lit.slice(3, idx); + num::from_str_radix(subslice, 16) + .and_then(char::from_u32) + .map(|x| (x, subslice.char_len() as int + 4)) + } else { + esc(6, lit) + }; // Unicode escapes return match lit.as_bytes()[1] as char { - 'x' | 'X' => esc(4), - 'u' => esc(6), - 'U' => esc(10), + 'x' | 'X' => esc(4, lit), + 'u' => unicode_escape(), + 'U' => esc(10, lit), _ => None, }.expect(msg2); } diff --git a/src/test/compile-fail/new-unicode-escapes-1.rs b/src/test/compile-fail/new-unicode-escapes-1.rs new file mode 100644 index 0000000000000..f2422830a21cc --- /dev/null +++ b/src/test/compile-fail/new-unicode-escapes-1.rs @@ -0,0 +1,13 @@ +// Copyright 2014 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. + +pub fn main() { + let s = "\u{2603"; //~ ERROR unterminated unicode escape (needed a `}`) +} diff --git a/src/test/compile-fail/new-unicode-escapes-2.rs b/src/test/compile-fail/new-unicode-escapes-2.rs new file mode 100644 index 0000000000000..5da8674c37ea5 --- /dev/null +++ b/src/test/compile-fail/new-unicode-escapes-2.rs @@ -0,0 +1,13 @@ +// Copyright 2014 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. + +pub fn main() { + let s = "\u{260311111111}"; //~ ERROR overlong unicode escape (can have at most 6 hex digits) +} diff --git a/src/test/compile-fail/new-unicode-escapes-3.rs b/src/test/compile-fail/new-unicode-escapes-3.rs new file mode 100644 index 0000000000000..7c64d02efd746 --- /dev/null +++ b/src/test/compile-fail/new-unicode-escapes-3.rs @@ -0,0 +1,13 @@ +// Copyright 2014 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. + +pub fn main() { + let s = "\u{d805}"; //~ ERROR illegal unicode character escape +} diff --git a/src/test/compile-fail/new-unicode-escapes-4.rs b/src/test/compile-fail/new-unicode-escapes-4.rs new file mode 100644 index 0000000000000..ffc2b11e0c13c --- /dev/null +++ b/src/test/compile-fail/new-unicode-escapes-4.rs @@ -0,0 +1,13 @@ +// Copyright 2014 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. + +pub fn main() { + let s = "\u{lol}"; //~ ERROR illegal character in unicode escape +} diff --git a/src/test/run-pass/new-unicode-escapes.rs b/src/test/run-pass/new-unicode-escapes.rs new file mode 100644 index 0000000000000..2888389bcceab --- /dev/null +++ b/src/test/run-pass/new-unicode-escapes.rs @@ -0,0 +1,22 @@ +// Copyright 2014 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. + +pub fn main() { + let s = "\u{2603}"; + assert_eq!(s, "☃"); + + let s = "\u{2a10}\u{2A01}\u{2Aa0}"; + assert_eq!(s, "⨐⨁⪠"); + + let s = "\\{20}"; + let mut correct_s = String::from_str("\\"); + correct_s.push_str("{20}"); + assert_eq!(s, correct_s.as_slice()); +} From b388dc61a54de4b8d62fa069dfde0c67a469841a Mon Sep 17 00:00:00 2001 From: Erick Tryzelaar Date: Wed, 3 Dec 2014 15:17:32 -0800 Subject: [PATCH 41/83] rustup: factor out downloading and extracting the snapshot tarballs --- src/etc/rustup.sh | 53 +++++++++++++++++++++++++++++++---------------- 1 file changed, 35 insertions(+), 18 deletions(-) diff --git a/src/etc/rustup.sh b/src/etc/rustup.sh index 88bcc1916e775..8ca2390dda553 100755 --- a/src/etc/rustup.sh +++ b/src/etc/rustup.sh @@ -402,44 +402,61 @@ fi CFG_TMP_DIR="./rustup-tmp-install" +RUST_URL="https://static.rust-lang.org/dist" RUST_PACKAGE_NAME=rust-nightly RUST_PACKAGE_NAME_AND_TRIPLE="${RUST_PACKAGE_NAME}-${HOST_TRIPLE}" RUST_TARBALL_NAME="${RUST_PACKAGE_NAME_AND_TRIPLE}.tar.gz" -RUST_REMOTE_TARBALL="https://static.rust-lang.org/dist/${RUST_TARBALL_NAME}" -RUST_LOCAL_TARBALL="${CFG_TMP_DIR}/${RUST_TARBALL_NAME}" RUST_LOCAL_INSTALL_DIR="${CFG_TMP_DIR}/${RUST_PACKAGE_NAME_AND_TRIPLE}" RUST_LOCAL_INSTALL_SCRIPT="${RUST_LOCAL_INSTALL_DIR}/install.sh" +CARGO_URL="https://static.rust-lang.org/cargo-dist" CARGO_PACKAGE_NAME=cargo-nightly CARGO_PACKAGE_NAME_AND_TRIPLE="${CARGO_PACKAGE_NAME}-${HOST_TRIPLE}" CARGO_TARBALL_NAME="${CARGO_PACKAGE_NAME_AND_TRIPLE}.tar.gz" -CARGO_REMOTE_TARBALL="https://static.rust-lang.org/cargo-dist/${CARGO_TARBALL_NAME}" -CARGO_LOCAL_TARBALL="${CFG_TMP_DIR}/${CARGO_TARBALL_NAME}" CARGO_LOCAL_INSTALL_DIR="${CFG_TMP_DIR}/${CARGO_PACKAGE_NAME_AND_TRIPLE}" CARGO_LOCAL_INSTALL_SCRIPT="${CARGO_LOCAL_INSTALL_DIR}/install.sh" +# Fetch the package and extract it. +download_and_extract_package() { + remote_url="$1" + tarball_name="$2" + remote_tarball="${remote_url}/${tarball_name}" + local_tarball="${CFG_TMP_DIR}/${tarball_name}" + + msg "Downloading ${remote_tarball} to ${local_tarball}" + + mkdir -p "${CFG_TMP_DIR}" + need_ok "failed to create create download directory" + + "${CFG_CURL}" -f -o "${local_tarball}" "${remote_tarball}" + if [ $? -ne 0 ] + then + rm -Rf "${CFG_TMP_DIR}" + err "failed to download installer" + fi + + msg "Extracting ${tarball_name}" + (cd "${CFG_TMP_DIR}" && "${CFG_TAR}" -xvf "${tarball_name}") + if [ $? -ne 0 ]; then + rm -Rf "${CFG_TMP_DIR}" + err "failed to unpack installer" + fi +} + rm -Rf "${CFG_TMP_DIR}" need_ok "failed to remove temporary installation directory" mkdir -p "${CFG_TMP_DIR}" need_ok "failed to create create temporary installation directory" -msg "downloading rust installer" -"${CFG_CURL}" "${RUST_REMOTE_TARBALL}" > "${RUST_LOCAL_TARBALL}" -if [ $? -ne 0 ] -then - rm -Rf "${CFG_TMP_DIR}" - err "failed to download installer" -fi +download_and_extract_package \ + "${RUST_URL}" \ + "${RUST_TARBALL_NAME}" if [ -z "${CFG_DISABLE_CARGO}" ]; then - msg "downloading cargo installer" - "${CFG_CURL}" "${CARGO_REMOTE_TARBALL}" > "${CARGO_LOCAL_TARBALL}" - if [ $? -ne 0 ] - then - rm -Rf "${CFG_TMP_DIR}" - err "failed to download cargo installer" - fi + download_and_extract_package \ + "${CARGO_URL}" \ + "${CARGO_TARBALL_NAME}" fi From 8ca8e6fa4d3a7dc1a40e958cfa4ca62096bfa509 Mon Sep 17 00:00:00 2001 From: Erick Tryzelaar Date: Sun, 30 Nov 2014 20:44:03 -0800 Subject: [PATCH 42/83] rustup: factor out installing packages into a function --- src/etc/rustup.sh | 41 ++++++++++++++--------------------------- 1 file changed, 14 insertions(+), 27 deletions(-) diff --git a/src/etc/rustup.sh b/src/etc/rustup.sh index 8ca2390dda553..311c1c3f15ef7 100755 --- a/src/etc/rustup.sh +++ b/src/etc/rustup.sh @@ -443,6 +443,18 @@ download_and_extract_package() { fi } +# Wrap all the commands needed to install a package. +install_package() { + install_script="$1" + + sh "${install_script}" "${CFG_INSTALL_FLAGS}" + if [ $? -ne 0 ] + then + rm -Rf "${CFG_TMP_DIR}" + err "failed to install Rust" + fi +} + rm -Rf "${CFG_TMP_DIR}" need_ok "failed to remove temporary installation directory" @@ -459,35 +471,10 @@ if [ -z "${CFG_DISABLE_CARGO}" ]; then "${CARGO_TARBALL_NAME}" fi - -(cd "${CFG_TMP_DIR}" && ${CFG_TAR} xzf "${RUST_TARBALL_NAME}") -if [ $? -ne 0 ] -then - rm -Rf "${CFG_TMP_DIR}" - err "failed to unpack installer" -fi - -sh "${RUST_LOCAL_INSTALL_SCRIPT}" "${CFG_INSTALL_FLAGS}" -if [ $? -ne 0 ] -then - rm -Rf "${CFG_TMP_DIR}" - err "failed to install Rust" -fi +install_package "${RUST_LOCAL_INSTALL_SCRIPT}" if [ -z "${CFG_DISABLE_CARGO}" ]; then - (cd "${CFG_TMP_DIR}" && ${CFG_TAR} xzf "${CARGO_TARBALL_NAME}") - if [ $? -ne 0 ] - then - rm -Rf "${CFG_TMP_DIR}" - err "failed to unpack cargo installer" - fi - - sh "${CARGO_LOCAL_INSTALL_SCRIPT}" "${CFG_INSTALL_FLAGS}" - if [ $? -ne 0 ] - then - rm -Rf "${CFG_TMP_DIR}" - err "failed to install Cargo" - fi + install_package "${CARGO_LOCAL_INSTALL_SCRIPT}" fi rm -Rf "${CFG_TMP_DIR}" From bd8dac8f758fc76ff3c4553e187eba627728cf71 Mon Sep 17 00:00:00 2001 From: Erick Tryzelaar Date: Sun, 30 Nov 2014 23:34:19 -0800 Subject: [PATCH 43/83] rustup: rewrite to protect against truncation This closes #19168. It's possible that if the downloading of `rustup.sh` is interrupted, bad things could happen, such as running a naked "rm -rf /" instead of "rm -rf /path/to/tmpdir". This wraps rustup.sh's functionality in a function that gets called at the last time that should protect us from these truncation errors. --- src/etc/rustup.sh | 46 ++++++++++++++++++++++++++++------------------ 1 file changed, 28 insertions(+), 18 deletions(-) diff --git a/src/etc/rustup.sh b/src/etc/rustup.sh index 311c1c3f15ef7..85fe829c25a6d 100755 --- a/src/etc/rustup.sh +++ b/src/etc/rustup.sh @@ -455,27 +455,37 @@ install_package() { fi } -rm -Rf "${CFG_TMP_DIR}" -need_ok "failed to remove temporary installation directory" +# It's possible that curl could be interrupted partway though downloading +# `rustup.sh`, truncating the file. This could be especially bad if we were in +# the middle of a line that would run "rm -rf ". To protect against this, we +# wrap up the `rustup.sh` destructive functionality in this helper function, +# which we call as the last thing we do. This means we will not do anything +# unless we have the entire file downloaded. +install_packages() { + rm -Rf "${CFG_TMP_DIR}" + need_ok "failed to remove temporary installation directory" -mkdir -p "${CFG_TMP_DIR}" -need_ok "failed to create create temporary installation directory" - -download_and_extract_package \ - "${RUST_URL}" \ - "${RUST_TARBALL_NAME}" + mkdir -p "${CFG_TMP_DIR}" + need_ok "failed to create create temporary installation directory" -if [ -z "${CFG_DISABLE_CARGO}" ]; then download_and_extract_package \ - "${CARGO_URL}" \ - "${CARGO_TARBALL_NAME}" -fi + "${RUST_URL}" \ + "${RUST_TARBALL_NAME}" -install_package "${RUST_LOCAL_INSTALL_SCRIPT}" + if [ -z "${CFG_DISABLE_CARGO}" ]; then + download_and_extract_package \ + "${CARGO_URL}" \ + "${CARGO_TARBALL_NAME}" + fi -if [ -z "${CFG_DISABLE_CARGO}" ]; then - install_package "${CARGO_LOCAL_INSTALL_SCRIPT}" -fi + install_package "${RUST_LOCAL_INSTALL_SCRIPT}" + + if [ -z "${CFG_DISABLE_CARGO}" ]; then + install_package "${CARGO_LOCAL_INSTALL_SCRIPT}" + fi + + rm -Rf "${CFG_TMP_DIR}" + need_ok "couldn't rm temporary installation directory" +} -rm -Rf "${CFG_TMP_DIR}" -need_ok "couldn't rm temporary installation directory" +install_packages From 694500b07d185f94dbaa47949ac664f55fd2e48b Mon Sep 17 00:00:00 2001 From: Erick Tryzelaar Date: Mon, 1 Dec 2014 00:00:43 -0800 Subject: [PATCH 44/83] rustup: extract the tarballs as part of installation --- src/etc/rustup.sh | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/src/etc/rustup.sh b/src/etc/rustup.sh index 85fe829c25a6d..869031e03c303 100755 --- a/src/etc/rustup.sh +++ b/src/etc/rustup.sh @@ -416,8 +416,8 @@ CARGO_TARBALL_NAME="${CARGO_PACKAGE_NAME_AND_TRIPLE}.tar.gz" CARGO_LOCAL_INSTALL_DIR="${CFG_TMP_DIR}/${CARGO_PACKAGE_NAME_AND_TRIPLE}" CARGO_LOCAL_INSTALL_SCRIPT="${CARGO_LOCAL_INSTALL_DIR}/install.sh" -# Fetch the package and extract it. -download_and_extract_package() { +# Fetch the package. +download_package() { remote_url="$1" tarball_name="$2" remote_tarball="${remote_url}/${tarball_name}" @@ -434,6 +434,12 @@ download_and_extract_package() { rm -Rf "${CFG_TMP_DIR}" err "failed to download installer" fi +} + +# Wrap all the commands needed to install a package. +install_package() { + tarball_name="$1" + install_script="$2" msg "Extracting ${tarball_name}" (cd "${CFG_TMP_DIR}" && "${CFG_TAR}" -xvf "${tarball_name}") @@ -441,11 +447,6 @@ download_and_extract_package() { rm -Rf "${CFG_TMP_DIR}" err "failed to unpack installer" fi -} - -# Wrap all the commands needed to install a package. -install_package() { - install_script="$1" sh "${install_script}" "${CFG_INSTALL_FLAGS}" if [ $? -ne 0 ] @@ -468,20 +469,24 @@ install_packages() { mkdir -p "${CFG_TMP_DIR}" need_ok "failed to create create temporary installation directory" - download_and_extract_package \ + download_package \ "${RUST_URL}" \ "${RUST_TARBALL_NAME}" if [ -z "${CFG_DISABLE_CARGO}" ]; then - download_and_extract_package \ + download_package \ "${CARGO_URL}" \ "${CARGO_TARBALL_NAME}" fi - install_package "${RUST_LOCAL_INSTALL_SCRIPT}" + install_package \ + "${RUST_TARBALL_NAME}" \ + "${RUST_LOCAL_INSTALL_SCRIPT}" if [ -z "${CFG_DISABLE_CARGO}" ]; then - install_package "${CARGO_LOCAL_INSTALL_SCRIPT}" + install_package \ + "${CARGO_TARBALL_NAME}" \ + "${CARGO_LOCAL_INSTALL_SCRIPT}" fi rm -Rf "${CFG_TMP_DIR}" From f86737973ac65a5c9d6c60d74acccfcfe0175fb4 Mon Sep 17 00:00:00 2001 From: Erick Tryzelaar Date: Wed, 3 Dec 2014 15:14:30 -0800 Subject: [PATCH 45/83] rustup: simplify downloading packages --- src/etc/rustup.sh | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/etc/rustup.sh b/src/etc/rustup.sh index 869031e03c303..b610bc65b09c6 100755 --- a/src/etc/rustup.sh +++ b/src/etc/rustup.sh @@ -418,10 +418,8 @@ CARGO_LOCAL_INSTALL_SCRIPT="${CARGO_LOCAL_INSTALL_DIR}/install.sh" # Fetch the package. download_package() { - remote_url="$1" - tarball_name="$2" - remote_tarball="${remote_url}/${tarball_name}" - local_tarball="${CFG_TMP_DIR}/${tarball_name}" + remote_tarball="$1" + local_tarball="$2" msg "Downloading ${remote_tarball} to ${local_tarball}" @@ -469,14 +467,17 @@ install_packages() { mkdir -p "${CFG_TMP_DIR}" need_ok "failed to create create temporary installation directory" + RUST_LOCAL_TARBALL="${CFG_TMP_DIR}/${RUST_TARBALL_NAME}" + CARGO_LOCAL_TARBALL="${CFG_TMP_DIR}/${CARGO_TARBALL_NAME}" + download_package \ - "${RUST_URL}" \ - "${RUST_TARBALL_NAME}" + "${RUST_URL}/${RUST_TARBALL_NAME}" \ + "${RUST_LOCAL_TARBALL}" if [ -z "${CFG_DISABLE_CARGO}" ]; then download_package \ - "${CARGO_URL}" \ - "${CARGO_TARBALL_NAME}" + "${CARGO_URL}/${CARGO_TARBALL_NAME}" \ + "${CARGO_LOCAL_TARBALL}" fi install_package \ From 108bca53f04342a4626b34ac1d5b8236d170a12a Mon Sep 17 00:00:00 2001 From: P1start Date: Wed, 3 Dec 2014 22:47:53 +1300 Subject: [PATCH 46/83] =?UTF-8?q?Make=20the=20parser=E2=80=99s=20=E2=80=98?= =?UTF-8?q?expected=20,=20found=20=E2=80=99=20errors=20more=20ac?= =?UTF-8?q?curate?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As an example of what this changes, the following code: let x: [int ..4]; Currently spits out ‘expected `]`, found `..`’. However, a comma would also be valid there, as would a number of other tokens. This change adjusts the parser to produce more accurate errors, so that that example now produces ‘expected one of `(`, `+`, `,`, `::`, or `]`, found `..`’. --- src/libsyntax/parse/parser.rs | 183 +++++++++++------- src/test/compile-fail/better-expected.rs | 13 ++ .../compile-fail/column-offset-1-based.rs | 2 +- src/test/compile-fail/empty-impl-semicolon.rs | 2 +- src/test/compile-fail/issue-1655.rs | 2 +- src/test/compile-fail/issue-19096.rs | 2 +- src/test/compile-fail/issue-3036.rs | 2 +- src/test/compile-fail/match-vec-invalid.rs | 2 +- src/test/compile-fail/multitrait.rs | 2 +- src/test/compile-fail/mut-patterns.rs | 2 +- .../compile-fail/omitted-arg-in-item-fn.rs | 2 +- src/test/compile-fail/pat-range-bad-dots.rs | 2 +- src/test/compile-fail/raw-str-unbalanced.rs | 2 +- .../removed-syntax-closure-lifetime.rs | 2 +- .../removed-syntax-enum-newtype.rs | 2 +- .../compile-fail/removed-syntax-fixed-vec.rs | 2 +- .../removed-syntax-larrow-init.rs | 2 +- .../removed-syntax-larrow-move.rs | 2 +- .../removed-syntax-mut-vec-expr.rs | 2 +- .../compile-fail/removed-syntax-mut-vec-ty.rs | 2 +- .../removed-syntax-ptr-lifetime.rs | 2 +- .../compile-fail/removed-syntax-record.rs | 2 +- .../removed-syntax-uniq-mut-expr.rs | 2 +- .../removed-syntax-uniq-mut-ty.rs | 2 +- .../compile-fail/removed-syntax-with-1.rs | 2 +- .../compile-fail/struct-literal-in-for.rs | 2 +- src/test/compile-fail/struct-literal-in-if.rs | 2 +- .../struct-literal-in-match-discriminant.rs | 2 +- .../compile-fail/struct-literal-in-while.rs | 2 +- 29 files changed, 155 insertions(+), 95 deletions(-) create mode 100644 src/test/compile-fail/better-expected.rs diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 920bcc3a951ae..c9d78eccdc70a 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -87,6 +87,7 @@ use std::mem; use std::num::Float; use std::rc::Rc; use std::iter; +use std::slice; bitflags! { flags Restrictions: u8 { @@ -303,6 +304,22 @@ pub struct Parser<'a> { /// name is not known. This does not change while the parser is descending /// into modules, and sub-parsers have new values for this name. pub root_module_name: Option, + pub expected_tokens: Vec, +} + +#[deriving(PartialEq, Eq, Clone)] +pub enum TokenType { + Token(token::Token), + Operator, +} + +impl TokenType { + fn to_string(&self) -> String { + match *self { + TokenType::Token(ref t) => format!("`{}`", Parser::token_to_string(t)), + TokenType::Operator => "an operator".into_string(), + } + } } fn is_plain_ident_or_underscore(t: &token::Token) -> bool { @@ -347,6 +364,7 @@ impl<'a> Parser<'a> { open_braces: Vec::new(), owns_directory: true, root_module_name: None, + expected_tokens: Vec::new(), } } @@ -375,14 +393,18 @@ impl<'a> Parser<'a> { /// Expect and consume the token t. Signal an error if /// the next token is not t. pub fn expect(&mut self, t: &token::Token) { - if self.token == *t { - self.bump(); + if self.expected_tokens.is_empty() { + if self.token == *t { + self.bump(); + } else { + let token_str = Parser::token_to_string(t); + let this_token_str = self.this_token_to_string(); + self.fatal(format!("expected `{}`, found `{}`", + token_str, + this_token_str).as_slice()) + } } else { - let token_str = Parser::token_to_string(t); - let this_token_str = self.this_token_to_string(); - self.fatal(format!("expected `{}`, found `{}`", - token_str, - this_token_str).as_slice()) + self.expect_one_of(slice::ref_slice(t), &[]); } } @@ -392,15 +414,20 @@ impl<'a> Parser<'a> { pub fn expect_one_of(&mut self, edible: &[token::Token], inedible: &[token::Token]) { - fn tokens_to_string(tokens: &[token::Token]) -> String { + fn tokens_to_string(tokens: &[TokenType]) -> String { let mut i = tokens.iter(); // This might be a sign we need a connect method on Iterator. let b = i.next() - .map_or("".to_string(), |t| Parser::token_to_string(t)); - i.fold(b, |b,a| { - let mut b = b; - b.push_str("`, `"); - b.push_str(Parser::token_to_string(a).as_slice()); + .map_or("".into_string(), |t| t.to_string()); + i.enumerate().fold(b, |mut b, (i, ref a)| { + if tokens.len() > 2 && i == tokens.len() - 2 { + b.push_str(", or "); + } else if tokens.len() == 2 && i == tokens.len() - 2 { + b.push_str(" or "); + } else { + b.push_str(", "); + } + b.push_str(&*a.to_string()); b }) } @@ -409,17 +436,21 @@ impl<'a> Parser<'a> { } else if inedible.contains(&self.token) { // leave it in the input } else { - let mut expected = edible.iter().map(|x| x.clone()).collect::>(); - expected.push_all(inedible); + let mut expected = edible.iter().map(|x| TokenType::Token(x.clone())) + .collect::>(); + expected.extend(inedible.iter().map(|x| TokenType::Token(x.clone()))); + expected.push_all(&*self.expected_tokens); + expected.sort_by(|a, b| a.to_string().cmp(&b.to_string())); + expected.dedup(); let expect = tokens_to_string(expected.as_slice()); let actual = self.this_token_to_string(); self.fatal( (if expected.len() != 1 { - (format!("expected one of `{}`, found `{}`", + (format!("expected one of {}, found `{}`", expect, actual)) } else { - (format!("expected `{}`, found `{}`", + (format!("expected {}, found `{}`", expect, actual)) }).as_slice() @@ -514,10 +545,20 @@ impl<'a> Parser<'a> { spanned(lo, hi, node) } + /// Check if the next token is `tok`, and return `true` if so. + /// + /// This method is will automatically add `tok` to `expected_tokens` if `tok` is not + /// encountered. + pub fn check(&mut self, tok: &token::Token) -> bool { + let is_present = self.token == *tok; + if !is_present { self.expected_tokens.push(TokenType::Token(tok.clone())); } + is_present + } + /// Consume token 'tok' if it exists. Returns true if the given /// token was present, false otherwise. pub fn eat(&mut self, tok: &token::Token) -> bool { - let is_present = self.token == *tok; + let is_present = self.check(tok); if is_present { self.bump() } is_present } @@ -739,7 +780,7 @@ impl<'a> Parser<'a> { // commas in generic parameters, because it can stop either after // parsing a type or after parsing a comma. for i in iter::count(0u, 1) { - if self.token == token::Gt + if self.check(&token::Gt) || self.token == token::BinOp(token::Shr) || self.token == token::Ge || self.token == token::BinOpEq(token::Shr) { @@ -798,7 +839,7 @@ impl<'a> Parser<'a> { } _ => () } - if sep.trailing_sep_allowed && self.token == *ket { break; } + if sep.trailing_sep_allowed && self.check(ket) { break; } v.push(f(self)); } return v; @@ -881,6 +922,7 @@ impl<'a> Parser<'a> { self.span = next.sp; self.token = next.tok; self.tokens_consumed += 1u; + self.expected_tokens.clear(); } /// Advance the parser by one token and return the bumped token. @@ -999,7 +1041,7 @@ impl<'a> Parser<'a> { self.parse_proc_type(lifetime_defs) } else if self.token_is_bare_fn_keyword() || self.token_is_closure_keyword() { self.parse_ty_bare_fn_or_ty_closure(lifetime_defs) - } else if self.token == token::ModSep || + } else if self.check(&token::ModSep) || self.token.is_ident() || self.token.is_path() { @@ -1101,7 +1143,7 @@ impl<'a> Parser<'a> { /// Parses an optional unboxed closure kind (`&:`, `&mut:`, or `:`). pub fn parse_optional_unboxed_closure_kind(&mut self) -> Option { - if self.token == token::BinOp(token::And) && + if self.check(&token::BinOp(token::And)) && self.look_ahead(1, |t| t.is_keyword(keywords::Mut)) && self.look_ahead(2, |t| *t == token::Colon) { self.bump(); @@ -1211,7 +1253,8 @@ impl<'a> Parser<'a> { lifetime_defs: Vec) -> Vec { - if self.eat(&token::Lt) { + if self.token == token::Lt { + self.bump(); if lifetime_defs.is_empty() { self.warn("deprecated syntax; use the `for` keyword now \ (e.g. change `fn<'a>` to `for<'a> fn`)"); @@ -1430,7 +1473,7 @@ impl<'a> Parser<'a> { let lo = self.span.lo; - let t = if self.token == token::OpenDelim(token::Paren) { + let t = if self.check(&token::OpenDelim(token::Paren)) { self.bump(); // (t) is a parenthesized ty @@ -1440,7 +1483,7 @@ impl<'a> Parser<'a> { let mut last_comma = false; while self.token != token::CloseDelim(token::Paren) { ts.push(self.parse_ty_sum()); - if self.token == token::Comma { + if self.check(&token::Comma) { last_comma = true; self.bump(); } else { @@ -1464,11 +1507,11 @@ impl<'a> Parser<'a> { _ => self.obsolete(last_span, ObsoleteOwnedType) } TyTup(vec![self.parse_ty()]) - } else if self.token == token::BinOp(token::Star) { + } else if self.check(&token::BinOp(token::Star)) { // STAR POINTER (bare pointer?) self.bump(); TyPtr(self.parse_ptr()) - } else if self.token == token::OpenDelim(token::Bracket) { + } else if self.check(&token::OpenDelim(token::Bracket)) { // VECTOR self.expect(&token::OpenDelim(token::Bracket)); let t = self.parse_ty_sum(); @@ -1481,7 +1524,7 @@ impl<'a> Parser<'a> { }; self.expect(&token::CloseDelim(token::Bracket)); t - } else if self.token == token::BinOp(token::And) || + } else if self.check(&token::BinOp(token::And)) || self.token == token::AndAnd { // BORROWED POINTER self.expect_and(); @@ -1492,7 +1535,7 @@ impl<'a> Parser<'a> { self.token_is_closure_keyword() { // BARE FUNCTION OR CLOSURE self.parse_ty_bare_fn_or_ty_closure(Vec::new()) - } else if self.token == token::BinOp(token::Or) || + } else if self.check(&token::BinOp(token::Or)) || self.token == token::OrOr || (self.token == token::Lt && self.look_ahead(1, |t| { @@ -1509,7 +1552,7 @@ impl<'a> Parser<'a> { TyTypeof(e) } else if self.eat_keyword(keywords::Proc) { self.parse_proc_type(Vec::new()) - } else if self.token == token::Lt { + } else if self.check(&token::Lt) { // QUALIFIED PATH `::item` self.bump(); let self_type = self.parse_ty_sum(); @@ -1523,7 +1566,7 @@ impl<'a> Parser<'a> { trait_ref: P(trait_ref), item_name: item_name, })) - } else if self.token == token::ModSep || + } else if self.check(&token::ModSep) || self.token.is_ident() || self.token.is_path() { // NAMED TYPE @@ -1532,7 +1575,8 @@ impl<'a> Parser<'a> { // TYPE TO BE INFERRED TyInfer } else { - let msg = format!("expected type, found token {}", self.token); + let this_token_str = self.this_token_to_string(); + let msg = format!("expected type, found `{}`", this_token_str); self.fatal(msg.as_slice()); }; @@ -1635,7 +1679,7 @@ impl<'a> Parser<'a> { } pub fn maybe_parse_fixed_vstore(&mut self) -> Option> { - if self.token == token::Comma && + if self.check(&token::Comma) && self.look_ahead(1, |t| *t == token::DotDot) { self.bump(); self.bump(); @@ -1959,9 +2003,10 @@ impl<'a> Parser<'a> { token::Gt => { return res; } token::BinOp(token::Shr) => { return res; } _ => { + let this_token_str = self.this_token_to_string(); let msg = format!("expected `,` or `>` after lifetime \ - name, got: {}", - self.token); + name, found `{}`", + this_token_str); self.fatal(msg.as_slice()); } } @@ -2126,7 +2171,7 @@ impl<'a> Parser<'a> { es.push(self.parse_expr()); self.commit_expr(&**es.last().unwrap(), &[], &[token::Comma, token::CloseDelim(token::Paren)]); - if self.token == token::Comma { + if self.check(&token::Comma) { trailing_comma = true; self.bump(); @@ -2167,14 +2212,14 @@ impl<'a> Parser<'a> { token::OpenDelim(token::Bracket) => { self.bump(); - if self.token == token::CloseDelim(token::Bracket) { + if self.check(&token::CloseDelim(token::Bracket)) { // Empty vector. self.bump(); ex = ExprVec(Vec::new()); } else { // Nonempty vector. let first_expr = self.parse_expr(); - if self.token == token::Comma && + if self.check(&token::Comma) && self.look_ahead(1, |t| *t == token::DotDot) { // Repeating vector syntax: [ 0, ..512 ] self.bump(); @@ -2182,7 +2227,7 @@ impl<'a> Parser<'a> { let count = self.parse_expr(); self.expect(&token::CloseDelim(token::Bracket)); ex = ExprRepeat(first_expr, count); - } else if self.token == token::Comma { + } else if self.check(&token::Comma) { // Vector with two or more elements. self.bump(); let remaining_exprs = self.parse_seq_to_end( @@ -2284,7 +2329,7 @@ impl<'a> Parser<'a> { ex = ExprBreak(None); } hi = self.span.hi; - } else if self.token == token::ModSep || + } else if self.check(&token::ModSep) || self.token.is_ident() && !self.token.is_keyword(keywords::True) && !self.token.is_keyword(keywords::False) { @@ -2292,7 +2337,7 @@ impl<'a> Parser<'a> { self.parse_path(LifetimeAndTypesWithColons); // `!`, as an operator, is prefix, so we know this isn't that - if self.token == token::Not { + if self.check(&token::Not) { // MACRO INVOCATION expression self.bump(); @@ -2309,7 +2354,7 @@ impl<'a> Parser<'a> { tts, EMPTY_CTXT)); } - if self.token == token::OpenDelim(token::Brace) { + if self.check(&token::OpenDelim(token::Brace)) { // This is a struct literal, unless we're prohibited // from parsing struct literals here. if !self.restrictions.contains(RESTRICTION_NO_STRUCT_LITERAL) { @@ -2840,6 +2885,7 @@ impl<'a> Parser<'a> { self.restrictions.contains(RESTRICTION_NO_BAR_OP) { return lhs; } + self.expected_tokens.push(TokenType::Operator); let cur_opt = self.token.to_binop(); match cur_opt { @@ -3079,7 +3125,7 @@ impl<'a> Parser<'a> { /// Parse the RHS of a local variable declaration (e.g. '= 14;') fn parse_initializer(&mut self) -> Option> { - if self.token == token::Eq { + if self.check(&token::Eq) { self.bump(); Some(self.parse_expr()) } else { @@ -3092,7 +3138,7 @@ impl<'a> Parser<'a> { let mut pats = Vec::new(); loop { pats.push(self.parse_pat()); - if self.token == token::BinOp(token::Or) { self.bump(); } + if self.check(&token::BinOp(token::Or)) { self.bump(); } else { return pats; } }; } @@ -3114,11 +3160,11 @@ impl<'a> Parser<'a> { } if before_slice { - if self.token == token::DotDot { + if self.check(&token::DotDot) { self.bump(); - if self.token == token::Comma || - self.token == token::CloseDelim(token::Bracket) { + if self.check(&token::Comma) || + self.check(&token::CloseDelim(token::Bracket)) { slice = Some(P(ast::Pat { id: ast::DUMMY_NODE_ID, node: PatWild(PatWildMulti), @@ -3135,7 +3181,7 @@ impl<'a> Parser<'a> { } let subpat = self.parse_pat(); - if before_slice && self.token == token::DotDot { + if before_slice && self.check(&token::DotDot) { self.bump(); slice = Some(subpat); before_slice = false; @@ -3160,13 +3206,13 @@ impl<'a> Parser<'a> { } else { self.expect(&token::Comma); // accept trailing commas - if self.token == token::CloseDelim(token::Brace) { break } + if self.check(&token::CloseDelim(token::Brace)) { break } } let lo = self.span.lo; let hi; - if self.token == token::DotDot { + if self.check(&token::DotDot) { self.bump(); if self.token != token::CloseDelim(token::Brace) { let token_str = self.this_token_to_string(); @@ -3187,7 +3233,7 @@ impl<'a> Parser<'a> { let fieldname = self.parse_ident(); - let (subpat, is_shorthand) = if self.token == token::Colon { + let (subpat, is_shorthand) = if self.check(&token::Colon) { match bind_type { BindByRef(..) | BindByValue(MutMutable) => { let token_str = self.this_token_to_string(); @@ -3267,15 +3313,15 @@ impl<'a> Parser<'a> { token::OpenDelim(token::Paren) => { // parse (pat,pat,pat,...) as tuple self.bump(); - if self.token == token::CloseDelim(token::Paren) { + if self.check(&token::CloseDelim(token::Paren)) { self.bump(); pat = PatTup(vec![]); } else { let mut fields = vec!(self.parse_pat()); if self.look_ahead(1, |t| *t != token::CloseDelim(token::Paren)) { - while self.token == token::Comma { + while self.check(&token::Comma) { self.bump(); - if self.token == token::CloseDelim(token::Paren) { break; } + if self.check(&token::CloseDelim(token::Paren)) { break; } fields.push(self.parse_pat()); } } @@ -3318,7 +3364,7 @@ impl<'a> Parser<'a> { // These expressions are limited to literals (possibly // preceded by unary-minus) or identifiers. let val = self.parse_literal_maybe_minus(); - if (self.token == token::DotDotDot) && + if (self.check(&token::DotDotDot)) && self.look_ahead(1, |t| { *t != token::Comma && *t != token::CloseDelim(token::Bracket) }) { @@ -3621,7 +3667,7 @@ impl<'a> Parser<'a> { let hi = self.span.hi; if id.name == token::special_idents::invalid.name { - if self.token == token::Dot { + if self.check(&token::Dot) { let span = self.span; let token_string = self.this_token_to_string(); self.span_err(span, @@ -3934,7 +3980,7 @@ impl<'a> Parser<'a> { let bounds = self.parse_colon_then_ty_param_bounds(); - let default = if self.token == token::Eq { + let default = if self.check(&token::Eq) { self.bump(); Some(self.parse_ty_sum()) } @@ -4334,7 +4380,7 @@ impl<'a> Parser<'a> { (optional_unboxed_closure_kind, args) } }; - let output = if self.token == token::RArrow { + let output = if self.check(&token::RArrow) { self.parse_ret_ty() } else { Return(P(Ty { @@ -4359,7 +4405,7 @@ impl<'a> Parser<'a> { seq_sep_trailing_allowed(token::Comma), |p| p.parse_fn_block_arg()); - let output = if self.token == token::RArrow { + let output = if self.check(&token::RArrow) { self.parse_ret_ty() } else { Return(P(Ty { @@ -4616,7 +4662,7 @@ impl<'a> Parser<'a> { token::get_ident(class_name)).as_slice()); } self.bump(); - } else if self.token == token::OpenDelim(token::Paren) { + } else if self.check(&token::OpenDelim(token::Paren)) { // It's a tuple-like struct. is_tuple_like = true; fields = self.parse_unspanned_seq( @@ -4801,7 +4847,7 @@ impl<'a> Parser<'a> { fn parse_item_mod(&mut self, outer_attrs: &[Attribute]) -> ItemInfo { let id_span = self.span; let id = self.parse_ident(); - if self.token == token::Semi { + if self.check(&token::Semi) { self.bump(); // This mod is in an external file. Let's go get it! let (m, attrs) = self.eval_src_mod(id, outer_attrs, id_span); @@ -5044,7 +5090,8 @@ impl<'a> Parser<'a> { let (maybe_path, ident) = match self.token { token::Ident(..) => { let the_ident = self.parse_ident(); - let path = if self.eat(&token::Eq) { + let path = if self.token == token::Eq { + self.bump(); let path = self.parse_str(); let span = self.span; self.obsolete(span, ObsoleteExternCrateRenaming); @@ -5184,7 +5231,7 @@ impl<'a> Parser<'a> { token::get_ident(ident)).as_slice()); } kind = StructVariantKind(struct_def); - } else if self.token == token::OpenDelim(token::Paren) { + } else if self.check(&token::OpenDelim(token::Paren)) { all_nullary = false; let arg_tys = self.parse_enum_variant_seq( &token::OpenDelim(token::Paren), @@ -5348,7 +5395,7 @@ impl<'a> Parser<'a> { visibility, maybe_append(attrs, extra_attrs)); return IoviItem(item); - } else if self.token == token::OpenDelim(token::Brace) { + } else if self.check(&token::OpenDelim(token::Brace)) { return self.parse_item_foreign_mod(lo, opt_abi, visibility, attrs); } @@ -5629,7 +5676,7 @@ impl<'a> Parser<'a> { fn parse_view_path(&mut self) -> P { let lo = self.span.lo; - if self.token == token::OpenDelim(token::Brace) { + if self.check(&token::OpenDelim(token::Brace)) { // use {foo,bar} let idents = self.parse_unspanned_seq( &token::OpenDelim(token::Brace), @@ -5653,7 +5700,7 @@ impl<'a> Parser<'a> { self.bump(); let path_lo = self.span.lo; path = vec!(self.parse_ident()); - while self.token == token::ModSep { + while self.check(&token::ModSep) { self.bump(); let id = self.parse_ident(); path.push(id); @@ -5677,7 +5724,7 @@ impl<'a> Parser<'a> { token::ModSep => { // foo::bar or foo::{a,b,c} or foo::* - while self.token == token::ModSep { + while self.check(&token::ModSep) { self.bump(); match self.token { @@ -5846,7 +5893,7 @@ impl<'a> Parser<'a> { loop { match self.parse_foreign_item(attrs, macros_allowed) { IoviNone(returned_attrs) => { - if self.token == token::CloseDelim(token::Brace) { + if self.check(&token::CloseDelim(token::Brace)) { attrs = returned_attrs; break } diff --git a/src/test/compile-fail/better-expected.rs b/src/test/compile-fail/better-expected.rs new file mode 100644 index 0000000000000..489f892726a3b --- /dev/null +++ b/src/test/compile-fail/better-expected.rs @@ -0,0 +1,13 @@ +// Copyright 2014 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. + +fn main() { + let x: [int ..3]; //~ ERROR expected one of `(`, `+`, `,`, `::`, or `]`, found `..` +} diff --git a/src/test/compile-fail/column-offset-1-based.rs b/src/test/compile-fail/column-offset-1-based.rs index a00ded61758c2..621b480fe77d3 100644 --- a/src/test/compile-fail/column-offset-1-based.rs +++ b/src/test/compile-fail/column-offset-1-based.rs @@ -8,4 +8,4 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -# //~ ERROR 11:1: 11:2 error: expected `[`, found `` +# //~ ERROR 11:1: 11:2 error: expected one of `!` or `[`, found `` diff --git a/src/test/compile-fail/empty-impl-semicolon.rs b/src/test/compile-fail/empty-impl-semicolon.rs index b5f17eef88685..a598252f1b65e 100644 --- a/src/test/compile-fail/empty-impl-semicolon.rs +++ b/src/test/compile-fail/empty-impl-semicolon.rs @@ -8,4 +8,4 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -impl Foo; //~ ERROR expected `{`, found `;` +impl Foo; //~ ERROR expected one of `(`, `+`, `::`, or `{`, found `;` diff --git a/src/test/compile-fail/issue-1655.rs b/src/test/compile-fail/issue-1655.rs index 6bdcf5c5edced..a8704f7545f06 100644 --- a/src/test/compile-fail/issue-1655.rs +++ b/src/test/compile-fail/issue-1655.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// error-pattern:expected `[`, found `vec` +// error-pattern:expected one of `!` or `[`, found `vec` mod blade_runner { #vec[doc( brief = "Blade Runner is probably the best movie ever", diff --git a/src/test/compile-fail/issue-19096.rs b/src/test/compile-fail/issue-19096.rs index 7f42abb3acca4..6b67814aab33f 100644 --- a/src/test/compile-fail/issue-19096.rs +++ b/src/test/compile-fail/issue-19096.rs @@ -12,5 +12,5 @@ fn main() { let t = (42i, 42i); - t.0::; //~ ERROR expected one of `;`, `}`, found `::` + t.0::; //~ ERROR expected one of `.`, `;`, `}`, or an operator, found `::` } diff --git a/src/test/compile-fail/issue-3036.rs b/src/test/compile-fail/issue-3036.rs index 5f56f6b8b6b99..16834f491659b 100644 --- a/src/test/compile-fail/issue-3036.rs +++ b/src/test/compile-fail/issue-3036.rs @@ -13,4 +13,4 @@ fn main() { let x = 3 -} //~ ERROR: expected `;`, found `}` +} //~ ERROR: expected one of `.`, `;`, or an operator, found `}` diff --git a/src/test/compile-fail/match-vec-invalid.rs b/src/test/compile-fail/match-vec-invalid.rs index 51e83c14aa008..3e073d34f3261 100644 --- a/src/test/compile-fail/match-vec-invalid.rs +++ b/src/test/compile-fail/match-vec-invalid.rs @@ -11,7 +11,7 @@ fn main() { let a = Vec::new(); match a { - [1, tail.., tail..] => {}, //~ ERROR: expected `,`, found `..` + [1, tail.., tail..] => {}, //~ ERROR: expected one of `!`, `,`, or `@`, found `..` _ => () } } diff --git a/src/test/compile-fail/multitrait.rs b/src/test/compile-fail/multitrait.rs index 795e3807d5ec6..7add747fbfa53 100644 --- a/src/test/compile-fail/multitrait.rs +++ b/src/test/compile-fail/multitrait.rs @@ -12,7 +12,7 @@ struct S { y: int } -impl Cmp, ToString for S { //~ ERROR: expected `{`, found `,` +impl Cmp, ToString for S { //~ ERROR: expected one of `(`, `+`, `::`, or `{`, found `,` fn eq(&&other: S) { false } fn to_string(&self) -> String { "hi".to_string() } } diff --git a/src/test/compile-fail/mut-patterns.rs b/src/test/compile-fail/mut-patterns.rs index a33a603f7f564..a78e82bb73ca4 100644 --- a/src/test/compile-fail/mut-patterns.rs +++ b/src/test/compile-fail/mut-patterns.rs @@ -12,5 +12,5 @@ pub fn main() { struct Foo { x: int } - let mut Foo { x: x } = Foo { x: 3 }; //~ ERROR: expected `;`, found `{` + let mut Foo { x: x } = Foo { x: 3 }; //~ ERROR: expected one of `:`, `;`, `=`, or `@`, found `{` } diff --git a/src/test/compile-fail/omitted-arg-in-item-fn.rs b/src/test/compile-fail/omitted-arg-in-item-fn.rs index c5ff885997b72..729b45df8b430 100644 --- a/src/test/compile-fail/omitted-arg-in-item-fn.rs +++ b/src/test/compile-fail/omitted-arg-in-item-fn.rs @@ -8,5 +8,5 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -fn foo(x) { //~ ERROR expected `:`, found `)` +fn foo(x) { //~ ERROR expected one of `!`, `:`, or `@`, found `)` } diff --git a/src/test/compile-fail/pat-range-bad-dots.rs b/src/test/compile-fail/pat-range-bad-dots.rs index 5605caaeeeda6..7fe073a4c3d69 100644 --- a/src/test/compile-fail/pat-range-bad-dots.rs +++ b/src/test/compile-fail/pat-range-bad-dots.rs @@ -10,7 +10,7 @@ pub fn main() { match 22i { - 0 .. 3 => {} //~ ERROR expected `=>`, found `..` + 0 .. 3 => {} //~ ERROR expected one of `...`, `=>`, or `|`, found `..` _ => {} } } diff --git a/src/test/compile-fail/raw-str-unbalanced.rs b/src/test/compile-fail/raw-str-unbalanced.rs index 4f3fb7d5b8ab2..3403b28fdc9c0 100644 --- a/src/test/compile-fail/raw-str-unbalanced.rs +++ b/src/test/compile-fail/raw-str-unbalanced.rs @@ -10,5 +10,5 @@ static s: &'static str = r#" - "## //~ ERROR expected `;`, found `#` + "## //~ ERROR expected one of `.`, `;`, or an operator, found `#` ; diff --git a/src/test/compile-fail/removed-syntax-closure-lifetime.rs b/src/test/compile-fail/removed-syntax-closure-lifetime.rs index a726e30b1de59..a07832d5bb761 100644 --- a/src/test/compile-fail/removed-syntax-closure-lifetime.rs +++ b/src/test/compile-fail/removed-syntax-closure-lifetime.rs @@ -8,4 +8,4 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -type closure = Box; //~ ERROR expected `,`, found `/` +type closure = Box; //~ ERROR expected one of `(`, `+`, `,`, `::`, or `>`, found `/` diff --git a/src/test/compile-fail/removed-syntax-enum-newtype.rs b/src/test/compile-fail/removed-syntax-enum-newtype.rs index b9c9c5f0a537b..ba1b5a616df9d 100644 --- a/src/test/compile-fail/removed-syntax-enum-newtype.rs +++ b/src/test/compile-fail/removed-syntax-enum-newtype.rs @@ -8,4 +8,4 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -enum e = int; //~ ERROR expected `{`, found `=` +enum e = int; //~ ERROR expected one of `<` or `{`, found `=` diff --git a/src/test/compile-fail/removed-syntax-fixed-vec.rs b/src/test/compile-fail/removed-syntax-fixed-vec.rs index 917b4e03ad0ed..fe49d1f4a8d85 100644 --- a/src/test/compile-fail/removed-syntax-fixed-vec.rs +++ b/src/test/compile-fail/removed-syntax-fixed-vec.rs @@ -8,4 +8,4 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -type v = [int * 3]; //~ ERROR expected `]`, found `*` +type v = [int * 3]; //~ ERROR expected one of `(`, `+`, `,`, `::`, or `]`, found `*` diff --git a/src/test/compile-fail/removed-syntax-larrow-init.rs b/src/test/compile-fail/removed-syntax-larrow-init.rs index b2e856750df01..1474cc9dd396d 100644 --- a/src/test/compile-fail/removed-syntax-larrow-init.rs +++ b/src/test/compile-fail/removed-syntax-larrow-init.rs @@ -11,5 +11,5 @@ fn removed_moves() { let mut x = 0; let y <- x; - //~^ ERROR expected `;`, found `<-` + //~^ ERROR expected one of `!`, `:`, `;`, `=`, or `@`, found `<-` } diff --git a/src/test/compile-fail/removed-syntax-larrow-move.rs b/src/test/compile-fail/removed-syntax-larrow-move.rs index e39fbe0f950ec..552c9f2efa2de 100644 --- a/src/test/compile-fail/removed-syntax-larrow-move.rs +++ b/src/test/compile-fail/removed-syntax-larrow-move.rs @@ -12,5 +12,5 @@ fn removed_moves() { let mut x = 0; let y = 0; y <- x; - //~^ ERROR expected one of `;`, `}`, found `<-` + //~^ ERROR expected one of `!`, `.`, `::`, `;`, `{`, `}`, or an operator, found `<-` } diff --git a/src/test/compile-fail/removed-syntax-mut-vec-expr.rs b/src/test/compile-fail/removed-syntax-mut-vec-expr.rs index b20da6346f775..437f871f8eabd 100644 --- a/src/test/compile-fail/removed-syntax-mut-vec-expr.rs +++ b/src/test/compile-fail/removed-syntax-mut-vec-expr.rs @@ -11,5 +11,5 @@ fn f() { let v = [mut 1, 2, 3, 4]; //~^ ERROR expected identifier, found keyword `mut` - //~^^ ERROR expected `]`, found `1` + //~^^ ERROR expected one of `!`, `,`, `.`, `::`, `]`, `{`, or an operator, found `1` } diff --git a/src/test/compile-fail/removed-syntax-mut-vec-ty.rs b/src/test/compile-fail/removed-syntax-mut-vec-ty.rs index c5eec2ef6e199..af469fadf986d 100644 --- a/src/test/compile-fail/removed-syntax-mut-vec-ty.rs +++ b/src/test/compile-fail/removed-syntax-mut-vec-ty.rs @@ -10,4 +10,4 @@ type v = [mut int]; //~^ ERROR expected identifier, found keyword `mut` - //~^^ ERROR expected `]`, found `int` + //~^^ ERROR expected one of `(`, `+`, `,`, `::`, or `]`, found `int` diff --git a/src/test/compile-fail/removed-syntax-ptr-lifetime.rs b/src/test/compile-fail/removed-syntax-ptr-lifetime.rs index 0468ddd389a7c..1a1c4c9b40a15 100644 --- a/src/test/compile-fail/removed-syntax-ptr-lifetime.rs +++ b/src/test/compile-fail/removed-syntax-ptr-lifetime.rs @@ -8,4 +8,4 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -type bptr = &lifetime/int; //~ ERROR expected `;`, found `/` +type bptr = &lifetime/int; //~ ERROR expected one of `(`, `+`, `::`, or `;`, found `/` diff --git a/src/test/compile-fail/removed-syntax-record.rs b/src/test/compile-fail/removed-syntax-record.rs index b31e2538ab97b..ae5a68575f72f 100644 --- a/src/test/compile-fail/removed-syntax-record.rs +++ b/src/test/compile-fail/removed-syntax-record.rs @@ -8,4 +8,4 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -type t = { f: () }; //~ ERROR expected type, found token OpenDelim(Brace) +type t = { f: () }; //~ ERROR expected type, found `{` diff --git a/src/test/compile-fail/removed-syntax-uniq-mut-expr.rs b/src/test/compile-fail/removed-syntax-uniq-mut-expr.rs index 124b3738fab5f..c5559c4ea9621 100644 --- a/src/test/compile-fail/removed-syntax-uniq-mut-expr.rs +++ b/src/test/compile-fail/removed-syntax-uniq-mut-expr.rs @@ -11,5 +11,5 @@ fn f() { let a_box = box mut 42; //~^ ERROR expected identifier, found keyword `mut` - //~^^ ERROR expected `;`, found `42` + //~^^ ERROR expected one of `!`, `.`, `::`, `;`, `{`, or an operator, found `42` } diff --git a/src/test/compile-fail/removed-syntax-uniq-mut-ty.rs b/src/test/compile-fail/removed-syntax-uniq-mut-ty.rs index 579bfed1331ed..8c3db89bad236 100644 --- a/src/test/compile-fail/removed-syntax-uniq-mut-ty.rs +++ b/src/test/compile-fail/removed-syntax-uniq-mut-ty.rs @@ -10,4 +10,4 @@ type mut_box = Box; //~^ ERROR expected identifier, found keyword `mut` - //~^^ ERROR expected `,`, found `int` + //~^^ ERROR expected one of `(`, `+`, `,`, `::`, or `>`, found `int` diff --git a/src/test/compile-fail/removed-syntax-with-1.rs b/src/test/compile-fail/removed-syntax-with-1.rs index fd8cdb7b10edf..c7f31045cb6a2 100644 --- a/src/test/compile-fail/removed-syntax-with-1.rs +++ b/src/test/compile-fail/removed-syntax-with-1.rs @@ -16,5 +16,5 @@ fn removed_with() { let a = S { foo: (), bar: () }; let b = S { foo: () with a }; - //~^ ERROR expected one of `,`, `}`, found `with` + //~^ ERROR expected one of `,`, `.`, `}`, or an operator, found `with` } diff --git a/src/test/compile-fail/struct-literal-in-for.rs b/src/test/compile-fail/struct-literal-in-for.rs index ccd711d83758d..a37197b889de8 100644 --- a/src/test/compile-fail/struct-literal-in-for.rs +++ b/src/test/compile-fail/struct-literal-in-for.rs @@ -20,7 +20,7 @@ impl Foo { fn main() { for x in Foo { - x: 3 //~ ERROR expected one of `;`, `}` + x: 3 //~ ERROR expected one of `!`, `.`, `::`, `;`, `{`, `}`, or an operator, found `:` }.hi() { println!("yo"); } diff --git a/src/test/compile-fail/struct-literal-in-if.rs b/src/test/compile-fail/struct-literal-in-if.rs index d63c216c3bee4..9759e4f7bdaa9 100644 --- a/src/test/compile-fail/struct-literal-in-if.rs +++ b/src/test/compile-fail/struct-literal-in-if.rs @@ -20,7 +20,7 @@ impl Foo { fn main() { if Foo { - x: 3 //~ ERROR expected one of `;`, `}` + x: 3 //~ ERROR expected one of `!`, `.`, `::`, `;`, `{`, `}`, or an operator, found `:` }.hi() { println!("yo"); } diff --git a/src/test/compile-fail/struct-literal-in-match-discriminant.rs b/src/test/compile-fail/struct-literal-in-match-discriminant.rs index c740ba020629d..297d3f7347f48 100644 --- a/src/test/compile-fail/struct-literal-in-match-discriminant.rs +++ b/src/test/compile-fail/struct-literal-in-match-discriminant.rs @@ -14,7 +14,7 @@ struct Foo { fn main() { match Foo { - x: 3 //~ ERROR expected `=>` + x: 3 //~ ERROR expected one of `!`, `=>`, `@`, or `|`, found `:` } { Foo { x: x diff --git a/src/test/compile-fail/struct-literal-in-while.rs b/src/test/compile-fail/struct-literal-in-while.rs index 7b2c11e2597a2..5b1679cf9a142 100644 --- a/src/test/compile-fail/struct-literal-in-while.rs +++ b/src/test/compile-fail/struct-literal-in-while.rs @@ -20,7 +20,7 @@ impl Foo { fn main() { while Foo { - x: 3 //~ ERROR expected one of `;`, `}` + x: 3 //~ ERROR expected one of `!`, `.`, `::`, `;`, `{`, `}`, or an operator, found `:` }.hi() { println!("yo"); } From 805a06ca6a4f0999e13508e6271e3589f2c4c1b2 Mon Sep 17 00:00:00 2001 From: "NODA, Kai" Date: Tue, 25 Nov 2014 22:12:24 +0800 Subject: [PATCH 47/83] libstd: io::fs::File::stat() need not to take &mut self. The same goes for sys::fs::FileDesc::fstat() on Windows. Signed-off-by: NODA, Kai --- src/libstd/io/fs.rs | 2 +- src/libstd/sys/windows/fs.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libstd/io/fs.rs b/src/libstd/io/fs.rs index bd334f52628fe..d5f23694fc8ad 100644 --- a/src/libstd/io/fs.rs +++ b/src/libstd/io/fs.rs @@ -237,7 +237,7 @@ impl File { } /// Queries information about the underlying file. - pub fn stat(&mut self) -> IoResult { + pub fn stat(&self) -> IoResult { self.fd.fstat() .update_err("couldn't fstat file", |e| format!("{}; path={}", e, self.path.display())) diff --git a/src/libstd/sys/windows/fs.rs b/src/libstd/sys/windows/fs.rs index 9c4ffb926b5ae..9402c63dcf558 100644 --- a/src/libstd/sys/windows/fs.rs +++ b/src/libstd/sys/windows/fs.rs @@ -131,7 +131,7 @@ impl FileDesc { return ret; } - pub fn fstat(&mut self) -> IoResult { + pub fn fstat(&self) -> IoResult { let mut stat: libc::stat = unsafe { mem::zeroed() }; match unsafe { libc::fstat(self.fd(), &mut stat) } { 0 => Ok(mkstat(&stat)), From 3980cdecd073789fb5ff7256e2ca40685a289b01 Mon Sep 17 00:00:00 2001 From: "NODA, Kai" Date: Wed, 3 Dec 2014 06:06:59 +0800 Subject: [PATCH 48/83] libstd: explicitly disallow io::fs::File to open a directory. On *BSD systems, we can open(2) a directory and directly read(2) from it due to an old tradition. We should avoid doing so by explicitly calling fstat(2) to check the type of the opened file. Opening a directory as a module file can't always be avoided. Even when there's no "path" attribute trick involved, there can always be a *directory* named "my_module.rs". Fix #12460 Signed-off-by: NODA, Kai --- src/libstd/io/fs.rs | 30 +++++++++++++++++++++-------- src/test/compile-fail/issue-5806.rs | 4 +--- 2 files changed, 23 insertions(+), 11 deletions(-) diff --git a/src/libstd/io/fs.rs b/src/libstd/io/fs.rs index d5f23694fc8ad..c66540e639e3c 100644 --- a/src/libstd/io/fs.rs +++ b/src/libstd/io/fs.rs @@ -53,7 +53,8 @@ use clone::Clone; use io::standard_error; use io::{FilePermission, Write, Open, FileAccess, FileMode, FileType}; -use io::{IoResult, IoError, FileStat, SeekStyle, Seek, Writer, Reader}; +use io::{IoResult, IoError, InvalidInput}; +use io::{FileStat, SeekStyle, Seek, Writer, Reader}; use io::{Read, Truncate, ReadWrite, Append}; use io::UpdateIoError; use io; @@ -134,13 +135,26 @@ impl File { pub fn open_mode(path: &Path, mode: FileMode, access: FileAccess) -> IoResult { - fs_imp::open(path, mode, access).map(|fd| { - File { - path: path.clone(), - fd: fd, - last_nread: -1 + fs_imp::open(path, mode, access).and_then(|fd| { + // On *BSD systems, we can open a directory as a file and read from it: + // fd=open("/tmp", O_RDONLY); read(fd, buf, N); + // due to an old tradition before the introduction of opendir(3). + // We explicitly reject it because there are few use cases. + if cfg!(not(any(windows, target_os = "linux", target_os = "android"))) && + try!(fd.fstat()).kind == FileType::Directory { + Err(IoError { + kind: InvalidInput, + desc: "is a directory", + detail: None + }) + } else { + Ok(File { + path: path.clone(), + fd: fd, + last_nread: -1 + }) } - }).update_err("couldn't open file", |e| { + }).update_err("couldn't open path as file", |e| { format!("{}; path={}; mode={}; access={}", e, path.display(), mode_string(mode), access_string(access)) }) @@ -886,7 +900,7 @@ mod test { let filename = &tmpdir.join("file_that_does_not_exist.txt"); let result = File::open_mode(filename, Open, Read); - error!(result, "couldn't open file"); + error!(result, "couldn't open path as file"); if cfg!(unix) { error!(result, "no such file or directory"); } diff --git a/src/test/compile-fail/issue-5806.rs b/src/test/compile-fail/issue-5806.rs index 702f02c721d86..597366a1b35df 100644 --- a/src/test/compile-fail/issue-5806.rs +++ b/src/test/compile-fail/issue-5806.rs @@ -18,9 +18,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// ignore-freebsd FIXME #12460 - #[path = "../compile-fail"] -mod foo; //~ ERROR: illegal operation on a directory +mod foo; //~ ERROR: a directory fn main() {} From 4b271f3f64ce334767b8ff10d19abc402a1838b3 Mon Sep 17 00:00:00 2001 From: Kang Seonghoon Date: Thu, 4 Dec 2014 12:48:16 +0900 Subject: [PATCH 49/83] rustdoc: Preserve query/fragment in redirects whenever possible. We heavily rely on queries and fragments in the URL structure, so it is desired to preserve them even in the redirects. The generated redirect pages try to preserve them with scripts, which take precedence over the original `Refresh` metadata. Non-scripting browsers would continue to work (with no queries and fragments). --- src/librustdoc/html/layout.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/librustdoc/html/layout.rs b/src/librustdoc/html/layout.rs index 896d070c155ee..23f31580619ee 100644 --- a/src/librustdoc/html/layout.rs +++ b/src/librustdoc/html/layout.rs @@ -160,6 +160,7 @@ r##" } pub fn redirect(dst: &mut io::Writer, url: &str) -> io::IoResult<()> { + // "##, url = url, From a12b83996ee48ea149e69cb8600d61998d205c1b Mon Sep 17 00:00:00 2001 From: Kang Seonghoon Date: Thu, 4 Dec 2014 13:52:23 +0900 Subject: [PATCH 50/83] rustdoc: Do not deduplicate items when their parents differ. Fixes #17332. --- src/librustdoc/html/static/main.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/librustdoc/html/static/main.js b/src/librustdoc/html/static/main.js index 7c6f7ed3fe230..069cc100a962a 100644 --- a/src/librustdoc/html/static/main.js +++ b/src/librustdoc/html/static/main.js @@ -313,7 +313,8 @@ for (var i = results.length - 1; i > 0; i -= 1) { if (results[i].word === results[i - 1].word && results[i].item.ty === results[i - 1].item.ty && - results[i].item.path === results[i - 1].item.path) + results[i].item.path === results[i - 1].item.path && + (results[i].item.parent || {}).name === (results[i - 1].item.parent || {}).name) { results[i].id = -1; } From 39221a013fdbfcc05d44fc7a62650f62c7b833e9 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 1 Dec 2014 09:23:40 -0500 Subject: [PATCH 51/83] Implement the `Fn` trait for bare fn pointers in the compiler rather than doing it using hard-coded impls. This means that it works also for more complex fn types involving bound regions. Fixes #19126. --- src/libcore/ops.rs | 80 +++++----- src/librustc/middle/traits/mod.rs | 18 ++- src/librustc/middle/traits/select.rs | 143 +++++++++++++++--- src/librustc/middle/traits/util.rs | 4 + src/librustc/middle/ty_fold.rs | 3 + src/librustc_trans/trans/base.rs | 8 +- src/librustc_trans/trans/callee.rs | 106 +++++++++++++ src/librustc_trans/trans/meth.rs | 16 +- src/test/compile-fail/issue-15965.rs | 4 +- src/test/compile-fail/issue-18532.rs | 2 - .../unboxed-closures-unsafe-extern-fn.rs | 28 ++++ .../unboxed-closures-wrong-abi.rs | 28 ++++ ...boxed-closures-wrong-arg-type-extern-fn.rs | 29 ++++ .../run-pass/unboxed-closures-extern-fn-hr.rs | 45 ++++++ .../run-pass/unboxed-closures-extern-fn.rs | 2 +- 15 files changed, 442 insertions(+), 74 deletions(-) create mode 100644 src/test/compile-fail/unboxed-closures-unsafe-extern-fn.rs create mode 100644 src/test/compile-fail/unboxed-closures-wrong-abi.rs create mode 100644 src/test/compile-fail/unboxed-closures-wrong-arg-type-extern-fn.rs create mode 100644 src/test/run-pass/unboxed-closures-extern-fn-hr.rs diff --git a/src/libcore/ops.rs b/src/libcore/ops.rs index d85481098e4ff..4f4ec4867972e 100644 --- a/src/libcore/ops.rs +++ b/src/libcore/ops.rs @@ -833,48 +833,52 @@ impl FnOnce for F } } - -impl Fn<(),Result> for extern "Rust" fn() -> Result { - #[allow(non_snake_case)] - extern "rust-call" fn call(&self, _args: ()) -> Result { - (*self)() +#[cfg(stage0)] +mod fn_impls { + use super::Fn; + + impl Fn<(),Result> for extern "Rust" fn() -> Result { + #[allow(non_snake_case)] + extern "rust-call" fn call(&self, _args: ()) -> Result { + (*self)() + } } -} -impl Fn<(A0,),Result> for extern "Rust" fn(A0) -> Result { - #[allow(non_snake_case)] - extern "rust-call" fn call(&self, args: (A0,)) -> Result { - let (a0,) = args; - (*self)(a0) + impl Fn<(A0,),Result> for extern "Rust" fn(A0) -> Result { + #[allow(non_snake_case)] + extern "rust-call" fn call(&self, args: (A0,)) -> Result { + let (a0,) = args; + (*self)(a0) + } } -} -macro_rules! def_fn( - ($($args:ident)*) => ( - impl - Fn<($($args,)*),Result> - for extern "Rust" fn($($args: $args,)*) -> Result { - #[allow(non_snake_case)] - extern "rust-call" fn call(&self, args: ($($args,)*)) -> Result { - let ($($args,)*) = args; - (*self)($($args,)*) + macro_rules! def_fn( + ($($args:ident)*) => ( + impl + Fn<($($args,)*),Result> + for extern "Rust" fn($($args: $args,)*) -> Result { + #[allow(non_snake_case)] + extern "rust-call" fn call(&self, args: ($($args,)*)) -> Result { + let ($($args,)*) = args; + (*self)($($args,)*) + } } - } + ) ) -) -def_fn!(A0 A1) -def_fn!(A0 A1 A2) -def_fn!(A0 A1 A2 A3) -def_fn!(A0 A1 A2 A3 A4) -def_fn!(A0 A1 A2 A3 A4 A5) -def_fn!(A0 A1 A2 A3 A4 A5 A6) -def_fn!(A0 A1 A2 A3 A4 A5 A6 A7) -def_fn!(A0 A1 A2 A3 A4 A5 A6 A7 A8) -def_fn!(A0 A1 A2 A3 A4 A5 A6 A7 A8 A9) -def_fn!(A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 A10) -def_fn!(A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 A10 A11) -def_fn!(A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 A10 A11 A12) -def_fn!(A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 A10 A11 A12 A13) -def_fn!(A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 A10 A11 A12 A13 A14) -def_fn!(A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 A10 A11 A12 A13 A14 A15) + def_fn!(A0 A1) + def_fn!(A0 A1 A2) + def_fn!(A0 A1 A2 A3) + def_fn!(A0 A1 A2 A3 A4) + def_fn!(A0 A1 A2 A3 A4 A5) + def_fn!(A0 A1 A2 A3 A4 A5 A6) + def_fn!(A0 A1 A2 A3 A4 A5 A6 A7) + def_fn!(A0 A1 A2 A3 A4 A5 A6 A7 A8) + def_fn!(A0 A1 A2 A3 A4 A5 A6 A7 A8 A9) + def_fn!(A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 A10) + def_fn!(A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 A10 A11) + def_fn!(A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 A10 A11 A12) + def_fn!(A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 A10 A11 A12 A13) + def_fn!(A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 A10 A11 A12 A13 A14) + def_fn!(A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 A10 A11 A12 A13 A14 A15) +} diff --git a/src/librustc/middle/traits/mod.rs b/src/librustc/middle/traits/mod.rs index 34278c25cfa0b..b8d915c06e0b7 100644 --- a/src/librustc/middle/traits/mod.rs +++ b/src/librustc/middle/traits/mod.rs @@ -167,18 +167,21 @@ pub enum Vtable<'tcx, N> { /// Vtable identifying a particular impl. VtableImpl(VtableImplData<'tcx, N>), - /// Vtable automatically generated for an unboxed closure. The def - /// ID is the ID of the closure expression. This is a `VtableImpl` - /// in spirit, but the impl is generated by the compiler and does - /// not appear in the source. - VtableUnboxedClosure(ast::DefId, subst::Substs<'tcx>), - /// Successful resolution to an obligation provided by the caller /// for some type parameter. VtableParam(VtableParamData<'tcx>), /// Successful resolution for a builtin trait. VtableBuiltin(VtableBuiltinData), + + /// Vtable automatically generated for an unboxed closure. The def + /// ID is the ID of the closure expression. This is a `VtableImpl` + /// in spirit, but the impl is generated by the compiler and does + /// not appear in the source. + VtableUnboxedClosure(ast::DefId, subst::Substs<'tcx>), + + /// Same as above, but for a fn pointer type with the given signature. + VtableFnPointer(ty::Ty<'tcx>), } /// Identifies a particular impl in the source, along with a set of @@ -322,6 +325,7 @@ impl<'tcx, N> Vtable<'tcx, N> { pub fn iter_nested(&self) -> Items { match *self { VtableImpl(ref i) => i.iter_nested(), + VtableFnPointer(..) => (&[]).iter(), VtableUnboxedClosure(..) => (&[]).iter(), VtableParam(_) => (&[]).iter(), VtableBuiltin(ref i) => i.iter_nested(), @@ -331,6 +335,7 @@ impl<'tcx, N> Vtable<'tcx, N> { pub fn map_nested(&self, op: |&N| -> M) -> Vtable<'tcx, M> { match *self { VtableImpl(ref i) => VtableImpl(i.map_nested(op)), + VtableFnPointer(ref sig) => VtableFnPointer((*sig).clone()), VtableUnboxedClosure(d, ref s) => VtableUnboxedClosure(d, s.clone()), VtableParam(ref p) => VtableParam((*p).clone()), VtableBuiltin(ref i) => VtableBuiltin(i.map_nested(op)), @@ -340,6 +345,7 @@ impl<'tcx, N> Vtable<'tcx, N> { pub fn map_move_nested(self, op: |N| -> M) -> Vtable<'tcx, M> { match self { VtableImpl(i) => VtableImpl(i.map_move_nested(op)), + VtableFnPointer(sig) => VtableFnPointer(sig), VtableUnboxedClosure(d, s) => VtableUnboxedClosure(d, s), VtableParam(p) => VtableParam(p), VtableBuiltin(i) => VtableBuiltin(i.map_move_nested(op)), diff --git a/src/librustc/middle/traits/select.rs b/src/librustc/middle/traits/select.rs index 71a183b475c6f..9dbcf455f4b58 100644 --- a/src/librustc/middle/traits/select.rs +++ b/src/librustc/middle/traits/select.rs @@ -22,7 +22,7 @@ use super::{SelectionError, Unimplemented, Overflow, OutputTypeParameterMismatch}; use super::{Selection}; use super::{SelectionResult}; -use super::{VtableBuiltin, VtableImpl, VtableParam, VtableUnboxedClosure}; +use super::{VtableBuiltin, VtableImpl, VtableParam, VtableUnboxedClosure, VtableFnPointer}; use super::{VtableImplData, VtableParamData, VtableBuiltinData}; use super::{util}; @@ -36,7 +36,7 @@ use middle::ty_fold::TypeFoldable; use std::cell::RefCell; use std::collections::hash_map::HashMap; use std::rc::Rc; -use syntax::ast; +use syntax::{abi, ast}; use util::common::ErrorReported; use util::ppaux::Repr; @@ -131,7 +131,15 @@ enum Candidate<'tcx> { BuiltinCandidate(ty::BuiltinBound), ParamCandidate(VtableParamData<'tcx>), ImplCandidate(ast::DefId), + + /// Implementation of a `Fn`-family trait by one of the + /// anonymous types generated for a `||` expression. UnboxedClosureCandidate(/* closure */ ast::DefId, Substs<'tcx>), + + /// Implementation of a `Fn`-family trait by one of the anonymous + /// types generated for a fn pointer type (e.g., `fn(int)->int`) + FnPointerCandidate, + ErrorCandidate, } @@ -917,7 +925,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { None => { // For the time being, we ignore user-defined impls for builtin-bounds. // (And unboxed candidates only apply to the Fn/FnMut/etc traits.) - try!(self.assemble_unboxed_candidates(obligation, &mut candidates)); + try!(self.assemble_unboxed_closure_candidates(obligation, &mut candidates)); + try!(self.assemble_fn_pointer_candidates(obligation, &mut candidates)); try!(self.assemble_candidates_from_impls(obligation, &mut candidates)); } } @@ -968,20 +977,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { /// Note: the type parameters on an unboxed closure candidate are modeled as *output* type /// parameters and hence do not affect whether this trait is a match or not. They will be /// unified during the confirmation step. - fn assemble_unboxed_candidates(&mut self, - obligation: &Obligation<'tcx>, - candidates: &mut CandidateSet<'tcx>) - -> Result<(),SelectionError<'tcx>> + fn assemble_unboxed_closure_candidates(&mut self, + obligation: &Obligation<'tcx>, + candidates: &mut CandidateSet<'tcx>) + -> Result<(),SelectionError<'tcx>> { - let tcx = self.tcx(); - let kind = if Some(obligation.trait_ref.def_id) == tcx.lang_items.fn_trait() { - ty::FnUnboxedClosureKind - } else if Some(obligation.trait_ref.def_id) == tcx.lang_items.fn_mut_trait() { - ty::FnMutUnboxedClosureKind - } else if Some(obligation.trait_ref.def_id) == tcx.lang_items.fn_once_trait() { - ty::FnOnceUnboxedClosureKind - } else { - return Ok(()); // not a fn trait, ignore + let kind = match self.fn_family_trait_kind(obligation.trait_ref.def_id) { + Some(k) => k, + None => { return Ok(()); } }; let self_ty = self.infcx.shallow_resolve(obligation.self_ty()); @@ -1015,6 +1018,42 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { Ok(()) } + /// Implement one of the `Fn()` family for a fn pointer. + fn assemble_fn_pointer_candidates(&mut self, + obligation: &Obligation<'tcx>, + candidates: &mut CandidateSet<'tcx>) + -> Result<(),SelectionError<'tcx>> + { + // We provide a `Fn` impl for fn pointers (but not e.g. `FnMut`). + if Some(obligation.trait_ref.def_id) != self.tcx().lang_items.fn_trait() { + return Ok(()); + } + + let self_ty = self.infcx.shallow_resolve(obligation.self_ty()); + match self_ty.sty { + ty::ty_infer(..) => { + candidates.ambiguous = true; // could wind up being a fn() type + } + + // provide an impl, but only for suitable `fn` pointers + ty::ty_bare_fn(ty::BareFnTy { + fn_style: ast::NormalFn, + abi: abi::Rust, + sig: ty::FnSig { + inputs: _, + output: ty::FnConverging(_), + variadic: false + } + }) => { + candidates.vec.push(FnPointerCandidate); + } + + _ => { } + } + + Ok(()) + } + /// Search for impls that might apply to `obligation`. fn assemble_candidates_from_impls(&mut self, obligation: &Obligation<'tcx>, @@ -1551,6 +1590,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { try!(self.confirm_unboxed_closure_candidate(obligation, closure_def_id, &substs)); Ok(VtableUnboxedClosure(closure_def_id, substs)) } + + FnPointerCandidate => { + let fn_type = + try!(self.confirm_fn_pointer_candidate(obligation)); + Ok(VtableFnPointer(fn_type)) + } } } @@ -1646,6 +1691,51 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { nested: impl_obligations } } + fn confirm_fn_pointer_candidate(&mut self, + obligation: &Obligation<'tcx>) + -> Result,SelectionError<'tcx>> + { + debug!("confirm_fn_pointer_candidate({})", + obligation.repr(self.tcx())); + + let self_ty = self.infcx.shallow_resolve(obligation.self_ty()); + let sig = match self_ty.sty { + ty::ty_bare_fn(ty::BareFnTy { + fn_style: ast::NormalFn, + abi: abi::Rust, + ref sig + }) => { + (*sig).clone() + } + _ => { + self.tcx().sess.span_bug( + obligation.cause.span, + format!("Fn pointer candidate for inappropriate self type: {}", + self_ty.repr(self.tcx())).as_slice()); + } + }; + + let arguments_tuple = ty::mk_tup(self.tcx(), sig.inputs.to_vec()); + let output_type = sig.output.unwrap(); + let substs = + Substs::new_trait( + vec![arguments_tuple, output_type], + vec![], + vec![], + self_ty); + let trait_ref = Rc::new(ty::TraitRef { + def_id: obligation.trait_ref.def_id, + substs: substs, + }); + + let () = + try!(self.confirm(obligation.cause, + obligation.trait_ref.clone(), + trait_ref)); + + Ok(self_ty) + } + fn confirm_unboxed_closure_candidate(&mut self, obligation: &Obligation<'tcx>, closure_def_id: ast::DefId, @@ -1964,6 +2054,22 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { util::obligations_for_generics(self.tcx(), cause, recursion_depth, &bounds, &impl_substs.types) } + + fn fn_family_trait_kind(&self, + trait_def_id: ast::DefId) + -> Option + { + let tcx = self.tcx(); + if Some(trait_def_id) == tcx.lang_items.fn_trait() { + Some(ty::FnUnboxedClosureKind) + } else if Some(trait_def_id) == tcx.lang_items.fn_mut_trait() { + Some(ty::FnMutUnboxedClosureKind) + } else if Some(trait_def_id) == tcx.lang_items.fn_once_trait() { + Some(ty::FnOnceUnboxedClosureKind) + } else { + None + } + } } impl<'tcx> Repr<'tcx> for Candidate<'tcx> { @@ -1972,7 +2078,10 @@ impl<'tcx> Repr<'tcx> for Candidate<'tcx> { ErrorCandidate => format!("ErrorCandidate"), BuiltinCandidate(b) => format!("BuiltinCandidate({})", b), UnboxedClosureCandidate(c, ref s) => { - format!("MatchedUnboxedClosureCandidate({},{})", c, s.repr(tcx)) + format!("UnboxedClosureCandidate({},{})", c, s.repr(tcx)) + } + FnPointerCandidate => { + format!("FnPointerCandidate") } ParamCandidate(ref a) => format!("ParamCandidate({})", a.repr(tcx)), ImplCandidate(a) => format!("ImplCandidate({})", a.repr(tcx)), diff --git a/src/librustc/middle/traits/util.rs b/src/librustc/middle/traits/util.rs index e8b292aac6d32..1084807ef4a0a 100644 --- a/src/librustc/middle/traits/util.rs +++ b/src/librustc/middle/traits/util.rs @@ -302,6 +302,10 @@ impl<'tcx, N:Repr<'tcx>> Repr<'tcx> for super::Vtable<'tcx, N> { d.repr(tcx), s.repr(tcx)), + super::VtableFnPointer(ref d) => + format!("VtableFnPointer({})", + d.repr(tcx)), + super::VtableParam(ref v) => format!("VtableParam({})", v.repr(tcx)), diff --git a/src/librustc/middle/ty_fold.rs b/src/librustc/middle/ty_fold.rs index 08dcbffc9287b..de9eb42649845 100644 --- a/src/librustc/middle/ty_fold.rs +++ b/src/librustc/middle/ty_fold.rs @@ -466,6 +466,9 @@ impl<'tcx, N: TypeFoldable<'tcx>> TypeFoldable<'tcx> for traits::Vtable<'tcx, N> traits::VtableUnboxedClosure(d, ref s) => { traits::VtableUnboxedClosure(d, s.fold_with(folder)) } + traits::VtableFnPointer(ref d) => { + traits::VtableFnPointer(d.fold_with(folder)) + } traits::VtableParam(ref p) => traits::VtableParam(p.fold_with(folder)), traits::VtableBuiltin(ref d) => traits::VtableBuiltin(d.fold_with(folder)), } diff --git a/src/librustc_trans/trans/base.rs b/src/librustc_trans/trans/base.rs index 23072dee3b6b1..84a6b59934f6e 100644 --- a/src/librustc_trans/trans/base.rs +++ b/src/librustc_trans/trans/base.rs @@ -995,9 +995,9 @@ pub fn invoke<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, } if need_invoke(bcx) { - debug!("invoking {} at {}", llfn, bcx.llbb); + debug!("invoking {} at {}", bcx.val_to_string(llfn), bcx.llbb); for &llarg in llargs.iter() { - debug!("arg: {}", llarg); + debug!("arg: {}", bcx.val_to_string(llarg)); } let normal_bcx = bcx.fcx.new_temp_block("normal-return"); let landing_pad = bcx.fcx.get_landing_pad(); @@ -1015,9 +1015,9 @@ pub fn invoke<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, Some(attributes)); return (llresult, normal_bcx); } else { - debug!("calling {} at {}", llfn, bcx.llbb); + debug!("calling {} at {}", bcx.val_to_string(llfn), bcx.llbb); for &llarg in llargs.iter() { - debug!("arg: {}", llarg); + debug!("arg: {}", bcx.val_to_string(llarg)); } match call_info { diff --git a/src/librustc_trans/trans/callee.rs b/src/librustc_trans/trans/callee.rs index 50d2f885afa17..4bcb7b09495d9 100644 --- a/src/librustc_trans/trans/callee.rs +++ b/src/librustc_trans/trans/callee.rs @@ -242,6 +242,112 @@ fn trans_fn_ref_with_substs_to_callee<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, } } +/// Translates an adapter that implements the `Fn` trait for a fn +/// pointer. This is basically the equivalent of something like: +/// +/// ```rust +/// impl<'a> Fn(&'a int) -> &'a int for fn(&int) -> &int { +/// extern "rust-abi" fn call(&self, args: (&'a int,)) -> &'a int { +/// (*self)(args.0) +/// } +/// } +/// ``` +/// +/// but for the bare function type given. +pub fn trans_fn_pointer_shim<'a, 'tcx>( + ccx: &'a CrateContext<'a, 'tcx>, + bare_fn_ty: Ty<'tcx>) + -> ValueRef +{ + let _icx = push_ctxt("trans_fn_pointer_shim"); + let tcx = ccx.tcx(); + + debug!("trans_fn_pointer_shim(bare_fn_ty={})", + bare_fn_ty.repr(tcx)); + + // This is an impl of `Fn` trait, so receiver is `&self`. + let bare_fn_ty_ref = ty::mk_imm_rptr(tcx, ty::ReStatic, bare_fn_ty); + + // Construct the "tuply" version of `bare_fn_ty`. It takes two arguments: `self`, + // which is the fn pointer, and `args`, which is the arguments tuple. + let (input_tys, output_ty) = + match bare_fn_ty.sty { + ty::ty_bare_fn(ty::BareFnTy { fn_style: ast::NormalFn, + abi: synabi::Rust, + sig: ty::FnSig { inputs: ref input_tys, + output: output_ty, + variadic: false }}) => + { + (input_tys, output_ty) + } + + _ => { + tcx.sess.bug(format!("trans_fn_pointer_shim invoked on invalid type: {}", + bare_fn_ty.repr(tcx)).as_slice()); + } + }; + let tuple_input_ty = ty::mk_tup(tcx, input_tys.to_vec()); + let tuple_fn_ty = ty::mk_bare_fn(tcx, + ty::BareFnTy { fn_style: ast::NormalFn, + abi: synabi::RustCall, + sig: ty::FnSig { + inputs: vec![bare_fn_ty_ref, + tuple_input_ty], + output: output_ty, + variadic: false + }}); + debug!("tuple_fn_ty: {}", tuple_fn_ty.repr(tcx)); + + // + let function_name = + link::mangle_internal_name_by_type_and_seq(ccx, bare_fn_ty, + "fn_pointer_shim"); + let llfn = + decl_internal_rust_fn(ccx, + tuple_fn_ty, + function_name.as_slice()); + + // + let block_arena = TypedArena::new(); + let empty_substs = Substs::trans_empty(); + let fcx = new_fn_ctxt(ccx, + llfn, + ast::DUMMY_NODE_ID, + false, + output_ty, + &empty_substs, + None, + &block_arena); + let mut bcx = init_function(&fcx, false, output_ty); + + // the first argument (`self`) will be ptr to the the fn pointer + let llfnpointer = + Load(bcx, get_param(fcx.llfn, fcx.arg_pos(0) as u32)); + + // the remaining arguments will be the untupled values + let llargs: Vec<_> = + input_tys.iter() + .enumerate() + .map(|(i, _)| get_param(fcx.llfn, fcx.arg_pos(i+1) as u32)) + .collect(); + assert!(!fcx.needs_ret_allocas); + + let dest = fcx.llretslotptr.get().map(|_| + expr::SaveIn(fcx.get_ret_slot(bcx, output_ty, "ret_slot")) + ); + + bcx = trans_call_inner(bcx, + None, + bare_fn_ty, + |bcx, _| Callee { bcx: bcx, data: Fn(llfnpointer) }, + ArgVals(llargs.as_slice()), + dest).bcx; + + finish_fn(&fcx, bcx, output_ty); + + llfn +} + /// Translates the adapter that deconstructs a `Box` object into /// `Trait` so that a by-value self method can be called. pub fn trans_unboxing_shim<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, diff --git a/src/librustc_trans/trans/meth.rs b/src/librustc_trans/trans/meth.rs index a49b7b21627f8..ef431243b31e9 100644 --- a/src/librustc_trans/trans/meth.rs +++ b/src/librustc_trans/trans/meth.rs @@ -368,9 +368,15 @@ fn trans_monomorphized_callee<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, data: Fn(llfn), } } - _ => { - bcx.tcx().sess.bug( - "vtable_param left in monomorphized function's vtable substs"); + traits::VtableFnPointer(fn_ty) => { + let llfn = trans_fn_pointer_shim(bcx.ccx(), fn_ty); + Callee { bcx: bcx, data: Fn(llfn) } + } + traits::VtableBuiltin(..) | + traits::VtableParam(..) => { + bcx.sess().bug( + format!("resolved vtable bad vtable {} in trans", + vtable.repr(bcx.tcx())).as_slice()); } } } @@ -609,6 +615,10 @@ pub fn get_vtable<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, (vec!(llfn)).into_iter() } + traits::VtableFnPointer(bare_fn_ty) => { + let llfn = vec![trans_fn_pointer_shim(bcx.ccx(), bare_fn_ty)]; + llfn.into_iter() + } traits::VtableParam(..) => { bcx.sess().bug( format!("resolved vtable for {} to bad vtable {} in trans", diff --git a/src/test/compile-fail/issue-15965.rs b/src/test/compile-fail/issue-15965.rs index 856cd1b0f3f26..935e67706589e 100644 --- a/src/test/compile-fail/issue-15965.rs +++ b/src/test/compile-fail/issue-15965.rs @@ -11,8 +11,6 @@ fn main() { return { return () } //~ ERROR the type of this value must be known in this context - () //~^ ERROR the type of this value must be known in this context -//~^^ ERROR notation; the first type parameter for the function trait is neither a tuple nor unit -//~^^^ ERROR overloaded calls are experimental + () ; } diff --git a/src/test/compile-fail/issue-18532.rs b/src/test/compile-fail/issue-18532.rs index 943d326182f89..9cf922ae99002 100644 --- a/src/test/compile-fail/issue-18532.rs +++ b/src/test/compile-fail/issue-18532.rs @@ -17,6 +17,4 @@ fn main() { (return)((),()); //~^ ERROR the type of this value must be known - //~^^ ERROR the type of this value must be known - //~^^^ ERROR cannot use call notation } diff --git a/src/test/compile-fail/unboxed-closures-unsafe-extern-fn.rs b/src/test/compile-fail/unboxed-closures-unsafe-extern-fn.rs new file mode 100644 index 0000000000000..a82689b16491e --- /dev/null +++ b/src/test/compile-fail/unboxed-closures-unsafe-extern-fn.rs @@ -0,0 +1,28 @@ +// Copyright 2014 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. + +// Tests that unsafe extern fn pointers do not implement any Fn traits. + +#![feature(unboxed_closures)] + +use std::ops::{Fn,FnMut,FnOnce}; + +unsafe fn square(x: &int) -> int { (*x) * (*x) } + +fn call_itint>(_: &F, _: int) -> int { 0 } +fn call_it_mutint>(_: &mut F, _: int) -> int { 0 } +fn call_it_onceint>(_: F, _: int) -> int { 0 } + +fn main() { + let x = call_it(&square, 22); //~ ERROR not implemented + let y = call_it_mut(&mut square, 22); //~ ERROR not implemented + let z = call_it_once(square, 22); //~ ERROR not implemented +} + diff --git a/src/test/compile-fail/unboxed-closures-wrong-abi.rs b/src/test/compile-fail/unboxed-closures-wrong-abi.rs new file mode 100644 index 0000000000000..920e91958ee9e --- /dev/null +++ b/src/test/compile-fail/unboxed-closures-wrong-abi.rs @@ -0,0 +1,28 @@ +// Copyright 2014 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. + +// Tests that unsafe extern fn pointers do not implement any Fn traits. + +#![feature(unboxed_closures)] + +use std::ops::{Fn,FnMut,FnOnce}; + +extern "C" fn square(x: &int) -> int { (*x) * (*x) } + +fn call_itint>(_: &F, _: int) -> int { 0 } +fn call_it_mutint>(_: &mut F, _: int) -> int { 0 } +fn call_it_onceint>(_: F, _: int) -> int { 0 } + +fn main() { + let x = call_it(&square, 22); //~ ERROR not implemented + let y = call_it_mut(&mut square, 22); //~ ERROR not implemented + let z = call_it_once(square, 22); //~ ERROR not implemented +} + diff --git a/src/test/compile-fail/unboxed-closures-wrong-arg-type-extern-fn.rs b/src/test/compile-fail/unboxed-closures-wrong-arg-type-extern-fn.rs new file mode 100644 index 0000000000000..a7a7b1c676267 --- /dev/null +++ b/src/test/compile-fail/unboxed-closures-wrong-arg-type-extern-fn.rs @@ -0,0 +1,29 @@ +// Copyright 2014 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. + +// Tests that unsafe extern fn pointers do not implement any Fn traits. + +#![feature(unboxed_closures)] + +use std::ops::{Fn,FnMut,FnOnce}; + +unsafe fn square(x: int) -> int { x * x } +// note: argument type here is `int`, not `&int` + +fn call_itint>(_: &F, _: int) -> int { 0 } +fn call_it_mutint>(_: &mut F, _: int) -> int { 0 } +fn call_it_onceint>(_: F, _: int) -> int { 0 } + +fn main() { + let x = call_it(&square, 22); //~ ERROR not implemented + let y = call_it_mut(&mut square, 22); //~ ERROR not implemented + let z = call_it_once(square, 22); //~ ERROR not implemented +} + diff --git a/src/test/run-pass/unboxed-closures-extern-fn-hr.rs b/src/test/run-pass/unboxed-closures-extern-fn-hr.rs new file mode 100644 index 0000000000000..df753f0f33eb6 --- /dev/null +++ b/src/test/run-pass/unboxed-closures-extern-fn-hr.rs @@ -0,0 +1,45 @@ +// Copyright 2014 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. + +// Checks that higher-ranked extern fn pointers implement the full range of Fn traits. + +#![feature(unboxed_closures)] + +use std::ops::{Fn,FnMut,FnOnce}; + +fn square(x: &int) -> int { (*x) * (*x) } + +fn call_itint>(f: &F, x: int) -> int { + (*f)(&x) +} + +fn call_it_boxed(f: &Fn(&int) -> int, x: int) -> int { + f.call((&x,)) +} + +fn call_it_mutint>(f: &mut F, x: int) -> int { + (*f)(&x) +} + +fn call_it_onceint>(f: F, x: int) -> int { + f(&x) +} + +fn main() { + let x = call_it(&square, 22); + let x1 = call_it_boxed(&square, 22); + let y = call_it_mut(&mut square, 22); + let z = call_it_once(square, 22); + assert_eq!(x, square(&22)); + assert_eq!(x1, square(&22)); + assert_eq!(y, square(&22)); + assert_eq!(z, square(&22)); +} + diff --git a/src/test/run-pass/unboxed-closures-extern-fn.rs b/src/test/run-pass/unboxed-closures-extern-fn.rs index 2628bd90eef0e..58657c2b71880 100644 --- a/src/test/run-pass/unboxed-closures-extern-fn.rs +++ b/src/test/run-pass/unboxed-closures-extern-fn.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// Checks that extern fn points implement the full range of Fn traits. +// Checks that extern fn pointers implement the full range of Fn traits. #![feature(unboxed_closures)] #![feature(unboxed_closures)] From 64bf5a8687c78cf7dc605cc8c8b63748a2746943 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 3 Dec 2014 20:23:15 -0500 Subject: [PATCH 52/83] Add a cache so we don't create so many shims. --- src/librustc_trans/trans/callee.rs | 8 ++++++++ src/librustc_trans/trans/context.rs | 5 +++++ 2 files changed, 13 insertions(+) diff --git a/src/librustc_trans/trans/callee.rs b/src/librustc_trans/trans/callee.rs index 4bcb7b09495d9..176fe7096e751 100644 --- a/src/librustc_trans/trans/callee.rs +++ b/src/librustc_trans/trans/callee.rs @@ -262,6 +262,12 @@ pub fn trans_fn_pointer_shim<'a, 'tcx>( let _icx = push_ctxt("trans_fn_pointer_shim"); let tcx = ccx.tcx(); + let bare_fn_ty = ty::normalize_ty(tcx, bare_fn_ty); + match ccx.fn_pointer_shims().borrow().get(&bare_fn_ty) { + Some(&llval) => { return llval; } + None => { } + } + debug!("trans_fn_pointer_shim(bare_fn_ty={})", bare_fn_ty.repr(tcx)); @@ -345,6 +351,8 @@ pub fn trans_fn_pointer_shim<'a, 'tcx>( finish_fn(&fcx, bcx, output_ty); + ccx.fn_pointer_shims().borrow_mut().insert(bare_fn_ty, llfn); + llfn } diff --git a/src/librustc_trans/trans/context.rs b/src/librustc_trans/trans/context.rs index a0b7eb02f02e4..eedfd8df2c0ab 100644 --- a/src/librustc_trans/trans/context.rs +++ b/src/librustc_trans/trans/context.rs @@ -84,6 +84,7 @@ pub struct LocalCrateContext<'tcx> { tn: TypeNames, externs: RefCell, item_vals: RefCell>, + fn_pointer_shims: RefCell, ValueRef>>, drop_glues: RefCell, ValueRef>>, tydescs: RefCell, Rc>>>, /// Set when running emit_tydescs to enforce that no more tydescs are @@ -573,6 +574,10 @@ impl<'b, 'tcx> CrateContext<'b, 'tcx> { &self.shared.link_meta } + pub fn fn_pointer_shims(&self) -> &RefCell, ValueRef>> { + &self.local.fn_pointer_shims + } + pub fn drop_glues<'a>(&'a self) -> &'a RefCell, ValueRef>> { &self.local.drop_glues } From f2731ffb52a5873800df4ef2dfa28da9c4302976 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Thu, 4 Dec 2014 00:57:38 -0500 Subject: [PATCH 53/83] Adjust nits from pcwalton. --- src/librustc/middle/traits/select.rs | 6 ++++-- src/librustc_trans/trans/context.rs | 1 + 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/librustc/middle/traits/select.rs b/src/librustc/middle/traits/select.rs index 9dbcf455f4b58..2604204d9e63f 100644 --- a/src/librustc/middle/traits/select.rs +++ b/src/librustc/middle/traits/select.rs @@ -1024,7 +1024,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { candidates: &mut CandidateSet<'tcx>) -> Result<(),SelectionError<'tcx>> { - // We provide a `Fn` impl for fn pointers (but not e.g. `FnMut`). + // We provide a `Fn` impl for fn pointers. There is no need to provide + // the other traits (e.g. `FnMut`) since those are provided by blanket + // impls. if Some(obligation.trait_ref.def_id) != self.tcx().lang_items.fn_trait() { return Ok(()); } @@ -1705,7 +1707,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { abi: abi::Rust, ref sig }) => { - (*sig).clone() + sig } _ => { self.tcx().sess.span_bug( diff --git a/src/librustc_trans/trans/context.rs b/src/librustc_trans/trans/context.rs index eedfd8df2c0ab..8220645cec78b 100644 --- a/src/librustc_trans/trans/context.rs +++ b/src/librustc_trans/trans/context.rs @@ -395,6 +395,7 @@ impl<'tcx> LocalCrateContext<'tcx> { tn: TypeNames::new(), externs: RefCell::new(FnvHashMap::new()), item_vals: RefCell::new(NodeMap::new()), + fn_pointer_shims: RefCell::new(FnvHashMap::new()), drop_glues: RefCell::new(FnvHashMap::new()), tydescs: RefCell::new(FnvHashMap::new()), finished_tydescs: Cell::new(false), From e7c1f57d6c8781cfb3e746eac5f13f760fcde2b4 Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Sat, 29 Nov 2014 23:07:43 -0800 Subject: [PATCH 54/83] Back io::stdin with a global singleton BufferedReader io::stdin returns a new `BufferedReader` each time it's called, which results in some very confusing behavior with disappearing output. It now returns a `StdinReader`, which wraps a global singleton `Arc>`. `Reader` is implemented directly on `StdinReader`. However, `Buffer` is not, as the `fill_buf` method is fundamentaly un-thread safe. A `lock` method is defined on `StdinReader` which returns a smart pointer wrapping the underlying `BufferedReader` while guaranteeing mutual exclusion. Code that treats the return value of io::stdin as implementing `Buffer` will break. Add a call to `lock`: ```rust io::stdin().lines() // => io::stdin().lock().lines() ``` Closes #14434 [breaking-change] --- src/libstd/io/mod.rs | 8 +- src/libstd/io/stdio.rs | 152 +++++++++++++++--- src/test/bench/shootout-k-nucleotide.rs | 2 +- src/test/bench/sudoku.rs | 4 +- .../cannot-mutate-captured-non-mut-var.rs | 2 +- src/test/run-pass/issue-13304.rs | 2 +- src/test/run-pass/issue-14456.rs | 2 +- src/test/run-pass/issue-16671.rs | 2 +- 8 files changed, 143 insertions(+), 31 deletions(-) diff --git a/src/libstd/io/mod.rs b/src/libstd/io/mod.rs index 5ed10eab15b80..b2e4fc75cf238 100644 --- a/src/libstd/io/mod.rs +++ b/src/libstd/io/mod.rs @@ -32,7 +32,7 @@ //! ```rust //! use std::io; //! -//! for line in io::stdin().lines() { +//! for line in io::stdin().lock().lines() { //! print!("{}", line.unwrap()); //! } //! ``` @@ -1413,10 +1413,10 @@ pub trait Buffer: Reader { /// # Example /// /// ```rust - /// use std::io; + /// use std::io::BufReader; /// - /// let mut reader = io::stdin(); - /// let input = reader.read_line().ok().unwrap_or("nothing".to_string()); + /// let mut reader = BufReader::new(b"hello\nworld"); + /// assert_eq!("hello\n", &*reader.read_line().unwrap()); /// ``` /// /// # Error diff --git a/src/libstd/io/stdio.rs b/src/libstd/io/stdio.rs index 665000eae8837..ad5dcf71df737 100644 --- a/src/libstd/io/stdio.rs +++ b/src/libstd/io/stdio.rs @@ -29,22 +29,27 @@ use self::StdSource::*; use boxed::Box; use cell::RefCell; +use clone::Clone; use failure::LOCAL_STDERR; use fmt; -use io::{Reader, Writer, IoResult, IoError, OtherIoError, +use io::{Reader, Writer, IoResult, IoError, OtherIoError, Buffer, standard_error, EndOfFile, LineBufferedWriter, BufferedReader}; use kinds::Send; use libc; use mem; use option::{Option, Some, None}; +use ops::{Deref, DerefMut}; use result::{Ok, Err}; use rustrt; use rustrt::local::Local; use rustrt::task::Task; use slice::SlicePrelude; use str::StrPrelude; +use string::String; use sys::{fs, tty}; +use sync::{Arc, Mutex, MutexGuard, Once, ONCE_INIT}; use uint; +use vec::Vec; // And so begins the tale of acquiring a uv handle to a stdio stream on all // platforms in all situations. Our story begins by splitting the world into two @@ -90,28 +95,135 @@ thread_local!(static LOCAL_STDOUT: RefCell>> = { RefCell::new(None) }) -/// Creates a new non-blocking handle to the stdin of the current process. -/// -/// The returned handled is buffered by default with a `BufferedReader`. If -/// buffered access is not desired, the `stdin_raw` function is provided to -/// provided unbuffered access to stdin. +/// A synchronized wrapper around a buffered reader from stdin +#[deriving(Clone)] +pub struct StdinReader { + inner: Arc>>, +} + +/// A guard for exlusive access to `StdinReader`'s internal `BufferedReader`. +pub struct StdinReaderGuard<'a> { + inner: MutexGuard<'a, BufferedReader>, +} + +impl<'a> Deref> for StdinReaderGuard<'a> { + fn deref(&self) -> &BufferedReader { + &*self.inner + } +} + +impl<'a> DerefMut> for StdinReaderGuard<'a> { + fn deref_mut(&mut self) -> &mut BufferedReader { + &mut *self.inner + } +} + +impl StdinReader { + /// Locks the `StdinReader`, granting the calling thread exclusive access + /// to the underlying `BufferedReader`. + /// + /// This provides access to methods like `chars` and `lines`. + /// + /// ## Example + /// + /// ```rust + /// use std::io; + /// + /// for line in io::stdin().lock().lines() { + /// println!("{}", line.unwrap()); + /// } + /// ``` + pub fn lock<'a>(&'a mut self) -> StdinReaderGuard<'a> { + StdinReaderGuard { + inner: self.inner.lock() + } + } + + /// Like `Buffer::read_line`. + /// + /// The read is performed atomically - concurrent read calls in other + /// threads will not interleave with this one. + pub fn read_line(&mut self) -> IoResult { + self.inner.lock().read_line() + } + + /// Like `Buffer::read_until`. + /// + /// The read is performed atomically - concurrent read calls in other + /// threads will not interleave with this one. + pub fn read_until(&mut self, byte: u8) -> IoResult> { + self.inner.lock().read_until(byte) + } + + /// Like `Buffer::read_char`. + /// + /// The read is performed atomically - concurrent read calls in other + /// threads will not interleave with this one. + pub fn read_char(&mut self) -> IoResult { + self.inner.lock().read_char() + } +} + +impl Reader for StdinReader { + fn read(&mut self, buf: &mut [u8]) -> IoResult { + self.inner.lock().read(buf) + } + + // We have to manually delegate all of these because the default impls call + // read more than once and we don't want those calls to interleave (or + // incur the costs of repeated locking). + + fn read_at_least(&mut self, min: uint, buf: &mut [u8]) -> IoResult { + self.inner.lock().read_at_least(min, buf) + } + + fn push_at_least(&mut self, min: uint, len: uint, buf: &mut Vec) -> IoResult { + self.inner.lock().push_at_least(min, len, buf) + } + + fn read_to_end(&mut self) -> IoResult> { + self.inner.lock().read_to_end() + } + + fn read_le_uint_n(&mut self, nbytes: uint) -> IoResult { + self.inner.lock().read_le_uint_n(nbytes) + } + + fn read_be_uint_n(&mut self, nbytes: uint) -> IoResult { + self.inner.lock().read_be_uint_n(nbytes) + } +} + +/// Creates a new handle to the stdin of the current process. /// -/// Care should be taken when creating multiple handles to the stdin of a -/// process. Because this is a buffered reader by default, it's possible for -/// pending input to be unconsumed in one reader and unavailable to other -/// readers. It is recommended that only one handle at a time is created for the -/// stdin of a process. +/// The returned handle is a wrapper around a global `BufferedReader` shared +/// by all threads. If buffered access is not desired, the `stdin_raw` function +/// is provided to provided unbuffered access to stdin. /// /// See `stdout()` for more notes about this function. -pub fn stdin() -> BufferedReader { - // The default buffer capacity is 64k, but apparently windows doesn't like - // 64k reads on stdin. See #13304 for details, but the idea is that on - // windows we use a slightly smaller buffer that's been seen to be - // acceptable. - if cfg!(windows) { - BufferedReader::with_capacity(8 * 1024, stdin_raw()) - } else { - BufferedReader::new(stdin_raw()) +pub fn stdin() -> StdinReader { + // We're following the same strategy as kimundi's lazy_static library + static mut STDIN: *const StdinReader = 0 as *const StdinReader; + static ONCE: Once = ONCE_INIT; + + unsafe { + ONCE.doit(|| { + // The default buffer capacity is 64k, but apparently windows doesn't like + // 64k reads on stdin. See #13304 for details, but the idea is that on + // windows we use a slightly smaller buffer that's been seen to be + // acceptable. + let stdin = if cfg!(windows) { + BufferedReader::with_capacity(8 * 1024, stdin_raw()) + } else { + BufferedReader::new(stdin_raw()) + }; + let stdin = StdinReader { + inner: Arc::new(Mutex::new(stdin)) + }; + STDIN = mem::transmute(box stdin); + }); + + (*STDIN).clone() } } diff --git a/src/test/bench/shootout-k-nucleotide.rs b/src/test/bench/shootout-k-nucleotide.rs index b030e7bb93e87..8ed041513c47c 100644 --- a/src/test/bench/shootout-k-nucleotide.rs +++ b/src/test/bench/shootout-k-nucleotide.rs @@ -295,7 +295,7 @@ fn main() { let fd = std::io::File::open(&Path::new("shootout-k-nucleotide.data")); get_sequence(&mut std::io::BufferedReader::new(fd), ">THREE") } else { - get_sequence(&mut std::io::stdin(), ">THREE") + get_sequence(&mut *std::io::stdin().lock(), ">THREE") }; let input = Arc::new(input); diff --git a/src/test/bench/sudoku.rs b/src/test/bench/sudoku.rs index 6664eeecd5d85..c55f85f40e8b6 100644 --- a/src/test/bench/sudoku.rs +++ b/src/test/bench/sudoku.rs @@ -65,7 +65,7 @@ impl Sudoku { return true; } - pub fn read(mut reader: BufferedReader) -> Sudoku { + pub fn read(mut reader: &mut BufferedReader) -> Sudoku { /* assert first line is exactly "9,9" */ assert!(reader.read_line().unwrap() == "9,9".to_string()); @@ -284,7 +284,7 @@ fn main() { let mut sudoku = if use_default { Sudoku::from_vec(&DEFAULT_SUDOKU) } else { - Sudoku::read(io::stdin()) + Sudoku::read(&mut *io::stdin().lock()) }; sudoku.solve(); sudoku.write(&mut io::stdout()); diff --git a/src/test/compile-fail/cannot-mutate-captured-non-mut-var.rs b/src/test/compile-fail/cannot-mutate-captured-non-mut-var.rs index fcb09c200000b..daad1afedaaea 100644 --- a/src/test/compile-fail/cannot-mutate-captured-non-mut-var.rs +++ b/src/test/compile-fail/cannot-mutate-captured-non-mut-var.rs @@ -14,6 +14,6 @@ fn main() { //~^ ERROR: cannot assign to immutable captured outer variable in a proc `x` let s = std::io::stdin(); - proc() { s.lines(); }; + proc() { s.read_to_end(); }; //~^ ERROR: cannot borrow immutable captured outer variable in a proc `s` as mutable } diff --git a/src/test/run-pass/issue-13304.rs b/src/test/run-pass/issue-13304.rs index 047ff74035b0f..11003c6fc524d 100644 --- a/src/test/run-pass/issue-13304.rs +++ b/src/test/run-pass/issue-13304.rs @@ -37,7 +37,7 @@ fn parent() { } fn child() { - for line in io::stdin().lines() { + for line in io::stdin().lock().lines() { println!("{}", line.unwrap()); } } diff --git a/src/test/run-pass/issue-14456.rs b/src/test/run-pass/issue-14456.rs index 2339e3f63029a..f5fdf8704ed25 100644 --- a/src/test/run-pass/issue-14456.rs +++ b/src/test/run-pass/issue-14456.rs @@ -27,7 +27,7 @@ fn main() { fn child() { io::stdout().write_line("foo").unwrap(); io::stderr().write_line("bar").unwrap(); - assert_eq!(io::stdin().read_line().err().unwrap().kind, io::EndOfFile); + assert_eq!(io::stdin().lock().read_line().err().unwrap().kind, io::EndOfFile); } fn test() { diff --git a/src/test/run-pass/issue-16671.rs b/src/test/run-pass/issue-16671.rs index a0d384418f972..27a97e1f172e3 100644 --- a/src/test/run-pass/issue-16671.rs +++ b/src/test/run-pass/issue-16671.rs @@ -19,6 +19,6 @@ pub fn main() { let mut stdin = std::io::stdin(); spawn(proc() { - let _ = stdin.lines(); + let _ = stdin.read_to_end(); }); } From cddbb6a75bd4618e6e136e7ec5140b95899ad70b Mon Sep 17 00:00:00 2001 From: Chase Southwood Date: Thu, 4 Dec 2014 03:10:58 -0600 Subject: [PATCH 55/83] `DerefMut` should be `for Sized?` --- src/libcore/ops.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libcore/ops.rs b/src/libcore/ops.rs index d85481098e4ff..7de89e2bc508a 100644 --- a/src/libcore/ops.rs +++ b/src/libcore/ops.rs @@ -787,7 +787,7 @@ impl<'a, Sized? T> Deref for &'a mut T { /// } /// ``` #[lang="deref_mut"] -pub trait DerefMut: Deref { +pub trait DerefMut for Sized? : Deref { /// The method called to mutably dereference a value fn deref_mut<'a>(&'a mut self) -> &'a mut Result; } From 2e1911b47ae5a4fd8cf4c4a98e739666f99daf82 Mon Sep 17 00:00:00 2001 From: Austin Bonander Date: Wed, 3 Dec 2014 16:31:21 -0800 Subject: [PATCH 56/83] core::iter::Unfold: reword docs and add example Remove note about core --- src/libcore/iter.rs | 37 ++++++++++++++++++++++++++++++++++--- 1 file changed, 34 insertions(+), 3 deletions(-) diff --git a/src/libcore/iter.rs b/src/libcore/iter.rs index 2d488a4b15563..6c43d4dbc30ca 100644 --- a/src/libcore/iter.rs +++ b/src/libcore/iter.rs @@ -2036,18 +2036,49 @@ for Inspect<'a, A, T> { } } -/// An iterator which just modifies the contained state throughout iteration. +/// An iterator which passes mutable state to a closure and yields the result. +/// +/// # Example: The Fibonacci Sequence +/// +/// An iterator that yields sequential Fibonacci numbers, and stops on overflow. +/// +/// ```rust +/// use std::iter::Unfold; +/// use std::num::Int; // For `.checked_add()` +/// +/// // This iterator will yield up to the last Fibonacci number before the max value of `u32`. +/// // You can simply change `u32` to `u64` in this line if you want higher values than that. +/// let mut fibonacci = Unfold::new((Some(0u32), Some(1u32)), |&(ref mut x2, ref mut x1)| { +/// // Attempt to get the next Fibonacci number +/// // `x1` will be `None` if previously overflowed. +/// let next = match (*x2, *x1) { +/// (Some(x2), Some(x1)) => x2.checked_add(x1), +/// _ => None, +/// }; +/// +/// // Shift left: ret <- x2 <- x1 <- next +/// let ret = *x2; +/// *x2 = *x1; +/// *x1 = next; +/// +/// ret +/// }); +/// +/// for i in fibonacci { +/// println!("{}", i); +/// } +/// ``` #[experimental] pub struct Unfold<'a, A, St> { f: |&mut St|: 'a -> Option, - /// Internal state that will be yielded on the next iteration + /// Internal state that will be passed to the closure on the next iteration pub state: St, } #[experimental] impl<'a, A, St> Unfold<'a, A, St> { /// Creates a new iterator with the specified closure as the "iterator - /// function" and an initial state to eventually pass to the iterator + /// function" and an initial state to eventually pass to the closure #[inline] pub fn new<'a>(initial_state: St, f: |&mut St|: 'a -> Option) -> Unfold<'a, A, St> { From 7c44561ad6f4651047255d0c630a0c3f80c3fdd4 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 25 Nov 2014 14:21:20 -0500 Subject: [PATCH 57/83] Move various data structures out of typeck and into ty. --- src/librustc/lint/builtin.rs | 12 +- src/librustc/metadata/csearch.rs | 3 +- src/librustc/metadata/decoder.rs | 3 +- src/librustc/middle/astencode.rs | 79 ++++--- src/librustc/middle/cfg/construct.rs | 5 +- src/librustc/middle/check_const.rs | 3 +- src/librustc/middle/dead.rs | 12 +- src/librustc/middle/effect.rs | 2 +- src/librustc/middle/expr_use_visitor.rs | 9 +- src/librustc/middle/liveness.rs | 4 +- src/librustc/middle/mem_categorization.rs | 15 +- src/librustc/middle/privacy.rs | 4 +- src/librustc/middle/reachable.rs | 5 +- src/librustc/middle/ty.rs | 198 +++++++++++++++++- src/librustc/middle/ty_fold.rs | 41 ++-- .../middle/typeck/check/method/confirm.rs | 4 +- .../middle/typeck/check/method/mod.rs | 2 - .../middle/typeck/check/method/probe.rs | 2 +- src/librustc/middle/typeck/check/mod.rs | 11 +- src/librustc/middle/typeck/check/regionck.rs | 3 +- src/librustc/middle/typeck/check/writeback.rs | 3 +- src/librustc/middle/typeck/mod.rs | 188 ----------------- src/librustc/util/ppaux.rs | 18 +- src/librustc_trans/save/mod.rs | 10 +- src/librustc_trans/trans/callee.rs | 2 +- src/librustc_trans/trans/common.rs | 7 +- src/librustc_trans/trans/controlflow.rs | 2 +- src/librustc_trans/trans/expr.rs | 17 +- src/librustc_trans/trans/meth.rs | 11 +- 29 files changed, 331 insertions(+), 344 deletions(-) diff --git a/src/librustc/lint/builtin.rs b/src/librustc/lint/builtin.rs index 9a214d531d157..cb617b169a060 100644 --- a/src/librustc/lint/builtin.rs +++ b/src/librustc/lint/builtin.rs @@ -30,7 +30,7 @@ use metadata::csearch; use middle::def::*; use middle::ty::{mod, Ty}; use middle::typeck::astconv::ast_ty_to_ty; -use middle::typeck::{mod, infer}; +use middle::typeck::infer; use middle::{def, pat_util, stability}; use middle::const_eval::{eval_const_expr_partial, const_int, const_uint}; use util::ppaux::{ty_to_string}; @@ -1589,22 +1589,22 @@ impl LintPass for Stability { } ast::ExprMethodCall(i, _, _) => { span = i.span; - let method_call = typeck::MethodCall::expr(e.id); + let method_call = ty::MethodCall::expr(e.id); match cx.tcx.method_map.borrow().get(&method_call) { Some(method) => { match method.origin { - typeck::MethodStatic(def_id) => { + ty::MethodStatic(def_id) => { def_id } - typeck::MethodStaticUnboxedClosure(def_id) => { + ty::MethodStaticUnboxedClosure(def_id) => { def_id } - typeck::MethodTypeParam(typeck::MethodParam { + ty::MethodTypeParam(ty::MethodParam { ref trait_ref, method_num: index, .. }) | - typeck::MethodTraitObject(typeck::MethodObject { + ty::MethodTraitObject(ty::MethodObject { ref trait_ref, method_num: index, .. diff --git a/src/librustc/metadata/csearch.rs b/src/librustc/metadata/csearch.rs index 20e3f27f2ae18..ebf5cca6a31a3 100644 --- a/src/librustc/metadata/csearch.rs +++ b/src/librustc/metadata/csearch.rs @@ -21,7 +21,6 @@ use middle::def; use middle::lang_items; use middle::resolve; use middle::ty; -use middle::typeck; use middle::subst::VecPerParamSpace; use rbml; @@ -268,7 +267,7 @@ pub fn get_impl_trait<'tcx>(tcx: &ty::ctxt<'tcx>, // Given a def_id for an impl, return information about its vtables pub fn get_impl_vtables<'tcx>(tcx: &ty::ctxt<'tcx>, def: ast::DefId) - -> typeck::vtable_res<'tcx> { + -> ty::vtable_res<'tcx> { let cstore = &tcx.sess.cstore; let cdata = cstore.get_crate_data(def.krate); decoder::get_impl_vtables(&*cdata, def.node, tcx) diff --git a/src/librustc/metadata/decoder.rs b/src/librustc/metadata/decoder.rs index ec812cea3728d..f352a28df6972 100644 --- a/src/librustc/metadata/decoder.rs +++ b/src/librustc/metadata/decoder.rs @@ -30,7 +30,6 @@ use middle::resolve::{TraitItemKind, TypeTraitItemKind}; use middle::subst; use middle::ty::{ImplContainer, TraitContainer}; use middle::ty::{mod, Ty}; -use middle::typeck; use middle::astencode::vtable_decoder_helpers; use std::hash::Hash; @@ -422,7 +421,7 @@ pub fn get_impl_trait<'tcx>(cdata: Cmd, pub fn get_impl_vtables<'tcx>(cdata: Cmd, id: ast::NodeId, tcx: &ty::ctxt<'tcx>) - -> typeck::vtable_res<'tcx> + -> ty::vtable_res<'tcx> { let item_doc = lookup_item(id, cdata.data()); let vtables_doc = reader::get_doc(item_doc, tag_item_impl_vtables); diff --git a/src/librustc/middle/astencode.rs b/src/librustc/middle/astencode.rs index 523e997a8deec..113d127503f02 100644 --- a/src/librustc/middle/astencode.rs +++ b/src/librustc/middle/astencode.rs @@ -26,8 +26,7 @@ use metadata::tyencode; use middle::mem_categorization::Typer; use middle::subst; use middle::subst::VecPerParamSpace; -use middle::typeck::{mod, MethodCall, MethodCallee, MethodOrigin}; -use middle::ty::{mod, Ty}; +use middle::ty::{mod, Ty, MethodCall, MethodCallee, MethodOrigin}; use util::ppaux::ty_to_string; use syntax::{ast, ast_map, ast_util, codemap, fold}; @@ -576,12 +575,12 @@ impl tr for ty::UpvarBorrow { trait read_method_callee_helper<'tcx> { fn read_method_callee<'a, 'b>(&mut self, dcx: &DecodeContext<'a, 'b, 'tcx>) - -> (typeck::ExprAdjustment, MethodCallee<'tcx>); + -> (ty::ExprAdjustment, MethodCallee<'tcx>); } fn encode_method_callee<'a, 'tcx>(ecx: &e::EncodeContext<'a, 'tcx>, rbml_w: &mut Encoder, - adjustment: typeck::ExprAdjustment, + adjustment: ty::ExprAdjustment, method: &MethodCallee<'tcx>) { use serialize::Encoder; @@ -603,7 +602,7 @@ fn encode_method_callee<'a, 'tcx>(ecx: &e::EncodeContext<'a, 'tcx>, impl<'a, 'tcx> read_method_callee_helper<'tcx> for reader::Decoder<'a> { fn read_method_callee<'a, 'b>(&mut self, dcx: &DecodeContext<'a, 'b, 'tcx>) - -> (typeck::ExprAdjustment, MethodCallee<'tcx>) { + -> (ty::ExprAdjustment, MethodCallee<'tcx>) { self.read_struct("MethodCallee", 4, |this| { let adjustment = this.read_struct_field("adjustment", 0, |this| { @@ -627,22 +626,22 @@ impl<'a, 'tcx> read_method_callee_helper<'tcx> for reader::Decoder<'a> { impl<'tcx> tr for MethodOrigin<'tcx> { fn tr(&self, dcx: &DecodeContext) -> MethodOrigin<'tcx> { match *self { - typeck::MethodStatic(did) => typeck::MethodStatic(did.tr(dcx)), - typeck::MethodStaticUnboxedClosure(did) => { - typeck::MethodStaticUnboxedClosure(did.tr(dcx)) + ty::MethodStatic(did) => ty::MethodStatic(did.tr(dcx)), + ty::MethodStaticUnboxedClosure(did) => { + ty::MethodStaticUnboxedClosure(did.tr(dcx)) } - typeck::MethodTypeParam(ref mp) => { - typeck::MethodTypeParam( - typeck::MethodParam { + ty::MethodTypeParam(ref mp) => { + ty::MethodTypeParam( + ty::MethodParam { // def-id is already translated when we read it out trait_ref: mp.trait_ref.clone(), method_num: mp.method_num, } ) } - typeck::MethodTraitObject(ref mo) => { - typeck::MethodTraitObject( - typeck::MethodObject { + ty::MethodTraitObject(ref mo) => { + ty::MethodTraitObject( + ty::MethodObject { trait_ref: mo.trait_ref.clone(), .. *mo } @@ -687,16 +686,16 @@ pub trait vtable_decoder_helpers<'tcx> { fn read_vtable_res_with_key(&mut self, tcx: &ty::ctxt<'tcx>, cdata: &cstore::crate_metadata) - -> (typeck::ExprAdjustment, typeck::vtable_res<'tcx>); + -> (ty::ExprAdjustment, ty::vtable_res<'tcx>); fn read_vtable_res(&mut self, tcx: &ty::ctxt<'tcx>, cdata: &cstore::crate_metadata) - -> typeck::vtable_res<'tcx>; + -> ty::vtable_res<'tcx>; fn read_vtable_param_res(&mut self, tcx: &ty::ctxt<'tcx>, cdata: &cstore::crate_metadata) - -> typeck::vtable_param_res<'tcx>; + -> ty::vtable_param_res<'tcx>; fn read_vtable_origin(&mut self, tcx: &ty::ctxt<'tcx>, cdata: &cstore::crate_metadata) - -> typeck::vtable_origin<'tcx>; + -> ty::vtable_origin<'tcx>; } impl<'tcx, 'a> vtable_decoder_helpers<'tcx> for reader::Decoder<'a> { @@ -714,7 +713,7 @@ impl<'tcx, 'a> vtable_decoder_helpers<'tcx> for reader::Decoder<'a> { fn read_vtable_res_with_key(&mut self, tcx: &ty::ctxt<'tcx>, cdata: &cstore::crate_metadata) - -> (typeck::ExprAdjustment, typeck::vtable_res<'tcx>) { + -> (ty::ExprAdjustment, ty::vtable_res<'tcx>) { self.read_struct("VtableWithKey", 2, |this| { let adjustment = this.read_struct_field("adjustment", 0, |this| { Decodable::decode(this) @@ -728,7 +727,7 @@ impl<'tcx, 'a> vtable_decoder_helpers<'tcx> for reader::Decoder<'a> { fn read_vtable_res(&mut self, tcx: &ty::ctxt<'tcx>, cdata: &cstore::crate_metadata) - -> typeck::vtable_res<'tcx> + -> ty::vtable_res<'tcx> { self.read_vec_per_param_space( |this| this.read_vtable_param_res(tcx, cdata)) @@ -736,14 +735,14 @@ impl<'tcx, 'a> vtable_decoder_helpers<'tcx> for reader::Decoder<'a> { fn read_vtable_param_res(&mut self, tcx: &ty::ctxt<'tcx>, cdata: &cstore::crate_metadata) - -> typeck::vtable_param_res<'tcx> { + -> ty::vtable_param_res<'tcx> { self.read_to_vec(|this| Ok(this.read_vtable_origin(tcx, cdata))) .unwrap().into_iter().collect() } fn read_vtable_origin(&mut self, tcx: &ty::ctxt<'tcx>, cdata: &cstore::crate_metadata) - -> typeck::vtable_origin<'tcx> { + -> ty::vtable_origin<'tcx> { self.read_enum("vtable_origin", |this| { this.read_enum_variant(&["vtable_static", "vtable_param", @@ -752,7 +751,7 @@ impl<'tcx, 'a> vtable_decoder_helpers<'tcx> for reader::Decoder<'a> { |this, i| { Ok(match i { 0 => { - typeck::vtable_static( + ty::vtable_static( this.read_enum_variant_arg(0u, |this| { Ok(this.read_def_id_nodcx(cdata)) }).unwrap(), @@ -765,7 +764,7 @@ impl<'tcx, 'a> vtable_decoder_helpers<'tcx> for reader::Decoder<'a> { ) } 1 => { - typeck::vtable_param( + ty::vtable_param( this.read_enum_variant_arg(0u, |this| { Decodable::decode(this) }).unwrap(), @@ -775,14 +774,14 @@ impl<'tcx, 'a> vtable_decoder_helpers<'tcx> for reader::Decoder<'a> { ) } 2 => { - typeck::vtable_unboxed_closure( + ty::vtable_unboxed_closure( this.read_enum_variant_arg(0u, |this| { Ok(this.read_def_id_nodcx(cdata)) }).unwrap() ) } 3 => { - typeck::vtable_error + ty::vtable_error } _ => panic!("bad enum variant") }) @@ -826,7 +825,7 @@ trait rbml_writer_helpers<'tcx> { closure_type: &ty::ClosureTy<'tcx>); fn emit_method_origin<'a>(&mut self, ecx: &e::EncodeContext<'a, 'tcx>, - method_origin: &typeck::MethodOrigin<'tcx>); + method_origin: &ty::MethodOrigin<'tcx>); fn emit_ty<'a>(&mut self, ecx: &e::EncodeContext<'a, 'tcx>, ty: Ty<'tcx>); fn emit_tys<'a>(&mut self, ecx: &e::EncodeContext<'a, 'tcx>, tys: &[Ty<'tcx>]); fn emit_type_param_def<'a>(&mut self, ecx: &e::EncodeContext<'a, 'tcx>, @@ -860,25 +859,25 @@ impl<'a, 'tcx> rbml_writer_helpers<'tcx> for Encoder<'a> { fn emit_method_origin<'a>(&mut self, ecx: &e::EncodeContext<'a, 'tcx>, - method_origin: &typeck::MethodOrigin<'tcx>) + method_origin: &ty::MethodOrigin<'tcx>) { use serialize::Encoder; self.emit_enum("MethodOrigin", |this| { match *method_origin { - typeck::MethodStatic(def_id) => { + ty::MethodStatic(def_id) => { this.emit_enum_variant("MethodStatic", 0, 1, |this| { Ok(this.emit_def_id(def_id)) }) } - typeck::MethodStaticUnboxedClosure(def_id) => { + ty::MethodStaticUnboxedClosure(def_id) => { this.emit_enum_variant("MethodStaticUnboxedClosure", 1, 1, |this| { Ok(this.emit_def_id(def_id)) }) } - typeck::MethodTypeParam(ref p) => { + ty::MethodTypeParam(ref p) => { this.emit_enum_variant("MethodTypeParam", 2, 1, |this| { this.emit_struct("MethodParam", 2, |this| { try!(this.emit_struct_field("trait_ref", 0, |this| { @@ -892,7 +891,7 @@ impl<'a, 'tcx> rbml_writer_helpers<'tcx> for Encoder<'a> { }) } - typeck::MethodTraitObject(ref o) => { + ty::MethodTraitObject(ref o) => { this.emit_enum_variant("MethodTraitObject", 3, 1, |this| { this.emit_struct("MethodObject", 2, |this| { try!(this.emit_struct_field("trait_ref", 0, |this| { @@ -1330,7 +1329,7 @@ impl<'a> doc_decoder_helpers for rbml::Doc<'a> { trait rbml_decoder_decoder_helpers<'tcx> { fn read_method_origin<'a, 'b>(&mut self, dcx: &DecodeContext<'a, 'b, 'tcx>) - -> typeck::MethodOrigin<'tcx>; + -> ty::MethodOrigin<'tcx>; fn read_ty<'a, 'b>(&mut self, dcx: &DecodeContext<'a, 'b, 'tcx>) -> Ty<'tcx>; fn read_tys<'a, 'b>(&mut self, dcx: &DecodeContext<'a, 'b, 'tcx>) -> Vec>; fn read_trait_ref<'a, 'b>(&mut self, dcx: &DecodeContext<'a, 'b, 'tcx>) @@ -1409,7 +1408,7 @@ impl<'a, 'tcx> rbml_decoder_decoder_helpers<'tcx> for reader::Decoder<'a> { } fn read_method_origin<'a, 'b>(&mut self, dcx: &DecodeContext<'a, 'b, 'tcx>) - -> typeck::MethodOrigin<'tcx> + -> ty::MethodOrigin<'tcx> { self.read_enum("MethodOrigin", |this| { let variants = &["MethodStatic", "MethodStaticUnboxedClosure", @@ -1418,18 +1417,18 @@ impl<'a, 'tcx> rbml_decoder_decoder_helpers<'tcx> for reader::Decoder<'a> { Ok(match i { 0 => { let def_id = this.read_def_id(dcx); - typeck::MethodStatic(def_id) + ty::MethodStatic(def_id) } 1 => { let def_id = this.read_def_id(dcx); - typeck::MethodStaticUnboxedClosure(def_id) + ty::MethodStaticUnboxedClosure(def_id) } 2 => { this.read_struct("MethodTypeParam", 2, |this| { - Ok(typeck::MethodTypeParam( - typeck::MethodParam { + Ok(ty::MethodTypeParam( + ty::MethodParam { trait_ref: { this.read_struct_field("trait_ref", 0, |this| { Ok(this.read_trait_ref(dcx)) @@ -1446,8 +1445,8 @@ impl<'a, 'tcx> rbml_decoder_decoder_helpers<'tcx> for reader::Decoder<'a> { 3 => { this.read_struct("MethodTraitObject", 2, |this| { - Ok(typeck::MethodTraitObject( - typeck::MethodObject { + Ok(ty::MethodTraitObject( + ty::MethodObject { trait_ref: { this.read_struct_field("trait_ref", 0, |this| { Ok(this.read_trait_ref(dcx)) diff --git a/src/librustc/middle/cfg/construct.rs b/src/librustc/middle/cfg/construct.rs index b42fb8ccc41f4..90919609e2e44 100644 --- a/src/librustc/middle/cfg/construct.rs +++ b/src/librustc/middle/cfg/construct.rs @@ -12,7 +12,6 @@ use middle::cfg::*; use middle::def; use middle::graph; use middle::region::CodeExtent; -use middle::typeck; use middle::ty; use syntax::ast; use syntax::ast_util; @@ -510,7 +509,7 @@ impl<'a, 'tcx> CFGBuilder<'a, 'tcx> { pred: CFGIndex, func_or_rcvr: &ast::Expr, args: I) -> CFGIndex { - let method_call = typeck::MethodCall::expr(call_expr.id); + let method_call = ty::MethodCall::expr(call_expr.id); let return_ty = ty::ty_fn_ret(match self.tcx.method_map.borrow().get(&method_call) { Some(method) => method.ty, None => ty::expr_ty(self.tcx, func_or_rcvr) @@ -635,7 +634,7 @@ impl<'a, 'tcx> CFGBuilder<'a, 'tcx> { } fn is_method_call(&self, expr: &ast::Expr) -> bool { - let method_call = typeck::MethodCall::expr(expr.id); + let method_call = ty::MethodCall::expr(expr.id); self.tcx.method_map.borrow().contains_key(&method_call) } } diff --git a/src/librustc/middle/check_const.rs b/src/librustc/middle/check_const.rs index 8cb63fcd82741..de140fd5c306c 100644 --- a/src/librustc/middle/check_const.rs +++ b/src/librustc/middle/check_const.rs @@ -11,7 +11,6 @@ use middle::def::*; use middle::ty; -use middle::typeck; use util::ppaux; use syntax::ast; @@ -111,7 +110,7 @@ fn check_expr(v: &mut CheckCrateVisitor, e: &ast::Expr) -> bool { } ast::ExprLit(ref lit) if ast_util::lit_is_str(&**lit) => {} ast::ExprBinary(..) | ast::ExprUnary(..) => { - let method_call = typeck::MethodCall::expr(e.id); + let method_call = ty::MethodCall::expr(e.id); if v.tcx.method_map.borrow().contains_key(&method_call) { span_err!(v.tcx.sess, e.span, E0011, "user-defined operators are not allowed in constant \ diff --git a/src/librustc/middle/dead.rs b/src/librustc/middle/dead.rs index cf2e9a65859cd..03fe878242159 100644 --- a/src/librustc/middle/dead.rs +++ b/src/librustc/middle/dead.rs @@ -12,7 +12,7 @@ // closely. The idea is that all reachable symbols are live, codes called // from live codes are live, and everything else is dead. -use middle::{def, pat_util, privacy, ty, typeck}; +use middle::{def, pat_util, privacy, ty}; use lint; use util::nodemap::NodeSet; @@ -90,23 +90,23 @@ impl<'a, 'tcx> MarkSymbolVisitor<'a, 'tcx> { fn lookup_and_handle_method(&mut self, id: ast::NodeId, span: codemap::Span) { - let method_call = typeck::MethodCall::expr(id); + let method_call = ty::MethodCall::expr(id); match self.tcx.method_map.borrow().get(&method_call) { Some(method) => { match method.origin { - typeck::MethodStatic(def_id) => { + ty::MethodStatic(def_id) => { match ty::provided_source(self.tcx, def_id) { Some(p_did) => self.check_def_id(p_did), None => self.check_def_id(def_id) } } - typeck::MethodStaticUnboxedClosure(_) => {} - typeck::MethodTypeParam(typeck::MethodParam { + ty::MethodStaticUnboxedClosure(_) => {} + ty::MethodTypeParam(ty::MethodParam { ref trait_ref, method_num: index, .. }) | - typeck::MethodTraitObject(typeck::MethodObject { + ty::MethodTraitObject(ty::MethodObject { ref trait_ref, method_num: index, .. diff --git a/src/librustc/middle/effect.rs b/src/librustc/middle/effect.rs index e67df0332dce6..dbec69f420503 100644 --- a/src/librustc/middle/effect.rs +++ b/src/librustc/middle/effect.rs @@ -14,7 +14,7 @@ use self::UnsafeContext::*; use middle::def; use middle::ty::{mod, Ty}; -use middle::typeck::MethodCall; +use middle::ty::MethodCall; use util::ppaux; use syntax::ast; diff --git a/src/librustc/middle/expr_use_visitor.rs b/src/librustc/middle/expr_use_visitor.rs index cbf36aeff512c..7d2bb7458acd5 100644 --- a/src/librustc/middle/expr_use_visitor.rs +++ b/src/librustc/middle/expr_use_visitor.rs @@ -24,10 +24,9 @@ use middle::{def, region, pat_util}; use middle::mem_categorization as mc; use middle::mem_categorization::Typer; use middle::ty::{mod, Ty}; -use middle::typeck::{MethodCall, MethodObject, MethodTraitObject}; -use middle::typeck::{MethodOrigin, MethodParam, MethodTypeParam}; -use middle::typeck::{MethodStatic, MethodStaticUnboxedClosure}; -use middle::typeck; +use middle::ty::{MethodCall, MethodObject, MethodTraitObject}; +use middle::ty::{MethodOrigin, MethodParam, MethodTypeParam}; +use middle::ty::{MethodStatic, MethodStaticUnboxedClosure}; use util::ppaux::Repr; use syntax::ast; @@ -825,7 +824,7 @@ impl<'d,'t,'tcx,TYPER:mc::Typer<'tcx>> ExprUseVisitor<'d,'t,'tcx,TYPER> { debug!("walk_autoderefs expr={} autoderefs={}", expr.repr(self.tcx()), autoderefs); for i in range(0, autoderefs) { - let deref_id = typeck::MethodCall::autoderef(expr.id, i); + let deref_id = ty::MethodCall::autoderef(expr.id, i); match self.typer.node_method_ty(deref_id) { None => {} Some(method_ty) => { diff --git a/src/librustc/middle/liveness.rs b/src/librustc/middle/liveness.rs index fcc23d8ac5548..523c9f3330968 100644 --- a/src/librustc/middle/liveness.rs +++ b/src/librustc/middle/liveness.rs @@ -111,7 +111,7 @@ use self::VarKind::*; use middle::def::*; use middle::mem_categorization::Typer; -use middle::{pat_util, typeck, ty}; +use middle::{pat_util, ty}; use lint; use util::nodemap::NodeMap; @@ -1156,7 +1156,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { } ast::ExprMethodCall(_, _, ref args) => { - let method_call = typeck::MethodCall::expr(expr.id); + let method_call = ty::MethodCall::expr(expr.id); let method_ty = self.ir.tcx.method_map.borrow().get(&method_call).unwrap().ty; let diverges = ty::ty_fn_ret(method_ty) == ty::FnDiverging; let succ = if diverges { diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs index ce166fc5de6bb..cd70d8e2b487a 100644 --- a/src/librustc/middle/mem_categorization.rs +++ b/src/librustc/middle/mem_categorization.rs @@ -74,7 +74,6 @@ pub use self::categorization::*; use middle::def; use middle::region; use middle::ty::{mod, Ty}; -use middle::typeck; use util::nodemap::{DefIdMap, NodeMap}; use util::ppaux::{ty_to_string, Repr}; @@ -283,7 +282,7 @@ pub type McResult = Result; pub trait Typer<'tcx> { fn tcx<'a>(&'a self) -> &'a ty::ctxt<'tcx>; fn node_ty(&self, id: ast::NodeId) -> McResult>; - fn node_method_ty(&self, method_call: typeck::MethodCall) -> Option>; + fn node_method_ty(&self, method_call: ty::MethodCall) -> Option>; fn adjustments<'a>(&'a self) -> &'a RefCell>>; fn is_method_call(&self, id: ast::NodeId) -> bool; fn temporary_scope(&self, rvalue_id: ast::NodeId) -> Option; @@ -509,7 +508,7 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> { } ast::ExprIndex(ref base, _) => { - let method_call = typeck::MethodCall::expr(expr.id()); + let method_call = ty::MethodCall::expr(expr.id()); match self.typer.node_method_ty(method_call) { Some(method_ty) => { // If this is an index implemented by a method call, then it will @@ -890,12 +889,12 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> { implicit: bool) -> cmt<'tcx> { let adjustment = match self.typer.adjustments().borrow().get(&node.id()) { - Some(adj) if ty::adjust_is_object(adj) => typeck::AutoObject, - _ if deref_cnt != 0 => typeck::AutoDeref(deref_cnt), - _ => typeck::NoAdjustment + Some(adj) if ty::adjust_is_object(adj) => ty::AutoObject, + _ if deref_cnt != 0 => ty::AutoDeref(deref_cnt), + _ => ty::NoAdjustment }; - let method_call = typeck::MethodCall { + let method_call = ty::MethodCall { expr_id: node.id(), adjustment: adjustment }; @@ -980,7 +979,7 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> { //! - `elt`: the AST node being indexed //! - `base_cmt`: the cmt of `elt` - let method_call = typeck::MethodCall::expr(elt.id()); + let method_call = ty::MethodCall::expr(elt.id()); let method_ty = self.typer.node_method_ty(method_call); let element_ty = match method_ty { diff --git a/src/librustc/middle/privacy.rs b/src/librustc/middle/privacy.rs index 5e182ba8337cb..5770b601a69ce 100644 --- a/src/librustc/middle/privacy.rs +++ b/src/librustc/middle/privacy.rs @@ -19,8 +19,8 @@ use std::mem::replace; use metadata::csearch; use middle::{def, resolve}; use middle::ty::{mod, Ty}; -use middle::typeck::{MethodCall, MethodMap, MethodOrigin, MethodParam, MethodTypeParam}; -use middle::typeck::{MethodStatic, MethodStaticUnboxedClosure, MethodObject, MethodTraitObject}; +use middle::ty::{MethodCall, MethodMap, MethodOrigin, MethodParam, MethodTypeParam}; +use middle::ty::{MethodStatic, MethodStaticUnboxedClosure, MethodObject, MethodTraitObject}; use util::nodemap::{NodeMap, NodeSet}; use syntax::{ast, ast_map}; diff --git a/src/librustc/middle/reachable.rs b/src/librustc/middle/reachable.rs index 96e1aacb0cef5..fa02c940aa754 100644 --- a/src/librustc/middle/reachable.rs +++ b/src/librustc/middle/reachable.rs @@ -17,7 +17,6 @@ use middle::def; use middle::ty; -use middle::typeck; use middle::privacy; use session::config; use util::nodemap::NodeSet; @@ -137,9 +136,9 @@ impl<'a, 'tcx, 'v> Visitor<'v> for ReachableContext<'a, 'tcx> { } } ast::ExprMethodCall(..) => { - let method_call = typeck::MethodCall::expr(expr.id); + let method_call = ty::MethodCall::expr(expr.id); match (*self.tcx.method_map.borrow())[method_call].origin { - typeck::MethodStatic(def_id) => { + ty::MethodStatic(def_id) => { if is_local(def_id) { if self.def_id_represents_local_inlined_item(def_id) { self.worklist.push(def_id.node) diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index 35aed356303d8..fe8121375ad05 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -35,6 +35,9 @@ pub use self::ImplOrTraitItem::*; pub use self::BoundRegion::*; pub use self::sty::*; pub use self::IntVarValue::*; +pub use self::ExprAdjustment::*; +pub use self::vtable_origin::*; +pub use self::MethodOrigin::*; use back::svh::Svh; use session::Session; @@ -53,7 +56,6 @@ use middle::stability; use middle::subst::{mod, Subst, Substs, VecPerParamSpace}; use middle::traits; use middle::ty; -use middle::typeck; use middle::ty_fold::{mod, TypeFoldable, TypeFolder, HigherRankedFoldable}; use middle; use util::ppaux::{note_and_explain_region, bound_region_ptr_to_string}; @@ -412,7 +414,161 @@ pub fn type_of_adjust<'tcx>(cx: &ctxt<'tcx>, adj: &AutoAdjustment<'tcx>) -> Opti } } +#[deriving(Clone, Encodable, Decodable, PartialEq, PartialOrd, Show)] +pub struct param_index { + pub space: subst::ParamSpace, + pub index: uint +} + +#[deriving(Clone, Show)] +pub enum MethodOrigin<'tcx> { + // fully statically resolved method + MethodStatic(ast::DefId), + + // fully statically resolved unboxed closure invocation + MethodStaticUnboxedClosure(ast::DefId), + + // method invoked on a type parameter with a bounded trait + MethodTypeParam(MethodParam<'tcx>), + + // method invoked on a trait instance + MethodTraitObject(MethodObject<'tcx>), + +} + +// details for a method invoked with a receiver whose type is a type parameter +// with a bounded trait. +#[deriving(Clone, Show)] +pub struct MethodParam<'tcx> { + // the precise trait reference that occurs as a bound -- this may + // be a supertrait of what the user actually typed. + pub trait_ref: Rc>, + + // index of uint in the list of methods for the trait + pub method_num: uint, +} + +// details for a method invoked with a receiver whose type is an object +#[deriving(Clone, Show)] +pub struct MethodObject<'tcx> { + // the (super)trait containing the method to be invoked + pub trait_ref: Rc>, + + // the actual base trait id of the object + pub object_trait_id: ast::DefId, + + // index of the method to be invoked amongst the trait's methods + pub method_num: uint, + + // index into the actual runtime vtable. + // the vtable is formed by concatenating together the method lists of + // the base object trait and all supertraits; this is the index into + // that vtable + pub real_index: uint, +} + +#[deriving(Clone)] +pub struct MethodCallee<'tcx> { + pub origin: MethodOrigin<'tcx>, + pub ty: Ty<'tcx>, + pub substs: subst::Substs<'tcx> +} + +/// With method calls, we store some extra information in +/// side tables (i.e method_map). We use +/// MethodCall as a key to index into these tables instead of +/// just directly using the expression's NodeId. The reason +/// for this being that we may apply adjustments (coercions) +/// with the resulting expression also needing to use the +/// side tables. The problem with this is that we don't +/// assign a separate NodeId to this new expression +/// and so it would clash with the base expression if both +/// needed to add to the side tables. Thus to disambiguate +/// we also keep track of whether there's an adjustment in +/// our key. +#[deriving(Clone, PartialEq, Eq, Hash, Show)] +pub struct MethodCall { + pub expr_id: ast::NodeId, + pub adjustment: ExprAdjustment +} + +#[deriving(Clone, PartialEq, Eq, Hash, Show, Encodable, Decodable)] +pub enum ExprAdjustment { + NoAdjustment, + AutoDeref(uint), + AutoObject +} + +impl MethodCall { + pub fn expr(id: ast::NodeId) -> MethodCall { + MethodCall { + expr_id: id, + adjustment: NoAdjustment + } + } + + pub fn autoobject(id: ast::NodeId) -> MethodCall { + MethodCall { + expr_id: id, + adjustment: AutoObject + } + } + + pub fn autoderef(expr_id: ast::NodeId, autoderef: uint) -> MethodCall { + MethodCall { + expr_id: expr_id, + adjustment: AutoDeref(1 + autoderef) + } + } +} + +// maps from an expression id that corresponds to a method call to the details +// of the method to be invoked +pub type MethodMap<'tcx> = RefCell>>; + +pub type vtable_param_res<'tcx> = Vec>; +// Resolutions for bounds of all parameters, left to right, for a given path. +pub type vtable_res<'tcx> = VecPerParamSpace>; + +#[deriving(Clone)] +pub enum vtable_origin<'tcx> { + /* + Statically known vtable. def_id gives the impl item + from whence comes the vtable, and tys are the type substs. + vtable_res is the vtable itself. + */ + vtable_static(ast::DefId, subst::Substs<'tcx>, vtable_res<'tcx>), + + /* + Dynamic vtable, comes from a parameter that has a bound on it: + fn foo(a: T) -- a's vtable would have a + vtable_param origin + + The first argument is the param index (identifying T in the example), + and the second is the bound number (identifying baz) + */ + vtable_param(param_index, uint), + + /* + Vtable automatically generated for an unboxed closure. The def ID is the + ID of the closure expression. + */ + vtable_unboxed_closure(ast::DefId), + + /* + Asked to determine the vtable for ty_err. This is the value used + for the vtables of `Self` in a virtual call like `foo.bar()` + where `foo` is of object type. The same value is also used when + type errors occur. + */ + vtable_error, +} + + +// For every explicit cast into an object type, maps from the cast +// expr to the associated trait ref. +pub type ObjectCastMap<'tcx> = RefCell>>>; /// A restriction that certain types must be the same size. The use of /// `transmute` gives rise to these restrictions. @@ -473,7 +629,7 @@ pub struct ctxt<'tcx> { /// Maps from node-id of a trait object cast (like `foo as /// Box`) to the trait reference. - pub object_cast_map: typeck::ObjectCastMap<'tcx>, + pub object_cast_map: ObjectCastMap<'tcx>, pub map: ast_map::Map<'tcx>, pub intrinsic_defs: RefCell>>, @@ -548,7 +704,7 @@ pub struct ctxt<'tcx> { pub extern_const_statics: RefCell>, pub extern_const_variants: RefCell>, - pub method_map: typeck::MethodMap<'tcx>, + pub method_map: MethodMap<'tcx>, pub dependency_formats: RefCell, @@ -3658,7 +3814,7 @@ pub fn adjust_ty<'tcx>(cx: &ctxt<'tcx>, expr_id: ast::NodeId, unadjusted_ty: Ty<'tcx>, adjustment: Option<&AutoAdjustment<'tcx>>, - method_type: |typeck::MethodCall| -> Option>) + method_type: |MethodCall| -> Option>) -> Ty<'tcx> { if let ty_err = unadjusted_ty.sty { @@ -3699,7 +3855,7 @@ pub fn adjust_ty<'tcx>(cx: &ctxt<'tcx>, if !ty::type_is_error(adjusted_ty) { for i in range(0, adj.autoderefs) { - let method_call = typeck::MethodCall::autoderef(expr_id, i); + let method_call = MethodCall::autoderef(expr_id, i); match method_type(method_call) { Some(method_ty) => { if let ty::FnConverging(result_type) = ty_fn_ret(method_ty) { @@ -3830,7 +3986,7 @@ pub enum ExprKind { } pub fn expr_kind(tcx: &ctxt, expr: &ast::Expr) -> ExprKind { - if tcx.method_map.borrow().contains_key(&typeck::MethodCall::expr(expr.id)) { + if tcx.method_map.borrow().contains_key(&MethodCall::expr(expr.id)) { // Overloaded operations are generally calls, and hence they are // generated via DPS, but there are a few exceptions: return match expr.node { @@ -5747,7 +5903,7 @@ impl<'tcx> mc::Typer<'tcx> for ty::ctxt<'tcx> { Ok(ty::node_id_to_type(self, id)) } - fn node_method_ty(&self, method_call: typeck::MethodCall) -> Option> { + fn node_method_ty(&self, method_call: MethodCall) -> Option> { self.method_map.borrow().get(&method_call).map(|method| method.ty) } @@ -5756,7 +5912,7 @@ impl<'tcx> mc::Typer<'tcx> for ty::ctxt<'tcx> { } fn is_method_call(&self, id: ast::NodeId) -> bool { - self.method_map.borrow().contains_key(&typeck::MethodCall::expr(id)) + self.method_map.borrow().contains_key(&MethodCall::expr(id)) } fn temporary_scope(&self, rvalue_id: ast::NodeId) -> Option { @@ -6010,3 +6166,29 @@ impl<'tcx> Repr<'tcx> for TyTrait<'tcx> { self.bounds.repr(tcx)) } } + +impl<'tcx> Repr<'tcx> for vtable_origin<'tcx> { + fn repr(&self, tcx: &ty::ctxt<'tcx>) -> String { + match *self { + vtable_static(def_id, ref tys, ref vtable_res) => { + format!("vtable_static({}:{}, {}, {})", + def_id, + ty::item_path_str(tcx, def_id), + tys.repr(tcx), + vtable_res.repr(tcx)) + } + + vtable_param(x, y) => { + format!("vtable_param({}, {})", x, y) + } + + vtable_unboxed_closure(def_id) => { + format!("vtable_unboxed_closure({})", def_id) + } + + vtable_error => { + format!("vtable_error") + } + } + } +} diff --git a/src/librustc/middle/ty_fold.rs b/src/librustc/middle/ty_fold.rs index de9eb42649845..77092025349e7 100644 --- a/src/librustc/middle/ty_fold.rs +++ b/src/librustc/middle/ty_fold.rs @@ -38,7 +38,6 @@ use middle::subst; use middle::subst::VecPerParamSpace; use middle::ty::{mod, Ty}; use middle::traits; -use middle::typeck; use std::rc::Rc; use syntax::owned_slice::OwnedSlice; use util::ppaux::Repr; @@ -304,23 +303,23 @@ impl<'tcx> TypeFoldable<'tcx> for ty::AutoRef<'tcx> { } } -impl<'tcx> TypeFoldable<'tcx> for typeck::MethodOrigin<'tcx> { - fn fold_with>(&self, folder: &mut F) -> typeck::MethodOrigin<'tcx> { +impl<'tcx> TypeFoldable<'tcx> for ty::MethodOrigin<'tcx> { + fn fold_with>(&self, folder: &mut F) -> ty::MethodOrigin<'tcx> { match *self { - typeck::MethodStatic(def_id) => { - typeck::MethodStatic(def_id) + ty::MethodStatic(def_id) => { + ty::MethodStatic(def_id) } - typeck::MethodStaticUnboxedClosure(def_id) => { - typeck::MethodStaticUnboxedClosure(def_id) + ty::MethodStaticUnboxedClosure(def_id) => { + ty::MethodStaticUnboxedClosure(def_id) } - typeck::MethodTypeParam(ref param) => { - typeck::MethodTypeParam(typeck::MethodParam { + ty::MethodTypeParam(ref param) => { + ty::MethodTypeParam(ty::MethodParam { trait_ref: param.trait_ref.fold_with(folder), method_num: param.method_num }) } - typeck::MethodTraitObject(ref object) => { - typeck::MethodTraitObject(typeck::MethodObject { + ty::MethodTraitObject(ref object) => { + ty::MethodTraitObject(ty::MethodObject { trait_ref: object.trait_ref.fold_with(folder), object_trait_id: object.object_trait_id, method_num: object.method_num, @@ -331,22 +330,22 @@ impl<'tcx> TypeFoldable<'tcx> for typeck::MethodOrigin<'tcx> { } } -impl<'tcx> TypeFoldable<'tcx> for typeck::vtable_origin<'tcx> { - fn fold_with>(&self, folder: &mut F) -> typeck::vtable_origin<'tcx> { +impl<'tcx> TypeFoldable<'tcx> for ty::vtable_origin<'tcx> { + fn fold_with>(&self, folder: &mut F) -> ty::vtable_origin<'tcx> { match *self { - typeck::vtable_static(def_id, ref substs, ref origins) => { + ty::vtable_static(def_id, ref substs, ref origins) => { let r_substs = substs.fold_with(folder); let r_origins = origins.fold_with(folder); - typeck::vtable_static(def_id, r_substs, r_origins) + ty::vtable_static(def_id, r_substs, r_origins) } - typeck::vtable_param(n, b) => { - typeck::vtable_param(n, b) + ty::vtable_param(n, b) => { + ty::vtable_param(n, b) } - typeck::vtable_unboxed_closure(def_id) => { - typeck::vtable_unboxed_closure(def_id) + ty::vtable_unboxed_closure(def_id) => { + ty::vtable_unboxed_closure(def_id) } - typeck::vtable_error => { - typeck::vtable_error + ty::vtable_error => { + ty::vtable_error } } } diff --git a/src/librustc/middle/typeck/check/method/confirm.rs b/src/librustc/middle/typeck/check/method/confirm.rs index e866627be3d29..4e541f120ab8e 100644 --- a/src/librustc/middle/typeck/check/method/confirm.rs +++ b/src/librustc/middle/typeck/check/method/confirm.rs @@ -13,9 +13,9 @@ use super::probe; use middle::subst::{mod, Subst}; use middle::traits; use middle::ty::{mod, Ty}; +use middle::ty::{MethodCall, MethodCallee, MethodObject, MethodOrigin, + MethodParam, MethodStatic, MethodTraitObject, MethodTypeParam}; use middle::typeck::check::{mod, FnCtxt, NoPreference, PreferMutLvalue}; -use middle::typeck::{MethodCall, MethodCallee, MethodObject, MethodOrigin, - MethodParam, MethodStatic, MethodTraitObject, MethodTypeParam}; use middle::typeck::infer::{mod, InferCtxt}; use middle::ty_fold::HigherRankedFoldable; use syntax::ast; diff --git a/src/librustc/middle/typeck/check/method/mod.rs b/src/librustc/middle/typeck/check/method/mod.rs index 34c3292f8cd69..001f5be69a13d 100644 --- a/src/librustc/middle/typeck/check/method/mod.rs +++ b/src/librustc/middle/typeck/check/method/mod.rs @@ -21,8 +21,6 @@ use middle::typeck::check::{impl_self_ty}; use middle::typeck::check::vtable; use middle::typeck::check::vtable::select_new_fcx_obligations; use middle::typeck::infer; -use middle::typeck::{MethodCallee}; -use middle::typeck::{MethodParam, MethodTypeParam}; use util::ppaux::{Repr, UserString}; use std::rc::Rc; diff --git a/src/librustc/middle/typeck/check/method/probe.rs b/src/librustc/middle/typeck/check/method/probe.rs index 484d72130e61d..689071b77e496 100644 --- a/src/librustc/middle/typeck/check/method/probe.rs +++ b/src/librustc/middle/typeck/check/method/probe.rs @@ -17,10 +17,10 @@ use middle::subst; use middle::subst::Subst; use middle::traits; use middle::ty::{mod, Ty}; +use middle::ty::{MethodObject}; use middle::ty_fold::HigherRankedFoldable; use middle::typeck::check; use middle::typeck::check::{FnCtxt, NoPreference}; -use middle::typeck::{MethodObject}; use middle::typeck::infer; use middle::typeck::infer::InferCtxt; use syntax::ast; diff --git a/src/librustc/middle/typeck/check/mod.rs b/src/librustc/middle/typeck/check/mod.rs index 641cbd11d64e0..0033613d9c6ba 100644 --- a/src/librustc/middle/typeck/check/mod.rs +++ b/src/librustc/middle/typeck/check/mod.rs @@ -93,12 +93,13 @@ use middle::ty::{FnSig, VariantInfo, Polytype}; use middle::ty::{Disr, ParamTy, ParameterEnvironment}; use middle::ty::{mod, Ty}; use middle::ty::liberate_late_bound_regions; +use middle::ty::{MethodCall, MethodCallee, MethodMap, ObjectCastMap}; use middle::ty_fold::TypeFolder; use middle::typeck::astconv::{mod, ast_region_to_region, ast_ty_to_ty, AstConv}; use middle::typeck::check::_match::pat_ctxt; use middle::typeck::rscope::RegionScope; -use middle::typeck::{mod, CrateCtxt, infer, lookup_def_ccx, no_params, require_same_types}; -use middle::typeck::{MethodCall, MethodCallee, MethodMap, ObjectCastMap, TypeAndSubsts}; +use middle::typeck::{CrateCtxt, infer, lookup_def_ccx, no_params, require_same_types}; +use middle::typeck::TypeAndSubsts; use middle::lang_items::TypeIdLangItem; use lint; use util::common::{block_query, indenter, loop_query}; @@ -279,7 +280,7 @@ impl<'a, 'tcx> mem_categorization::Typer<'tcx> for FnCtxt<'a, 'tcx> { fn node_ty(&self, id: ast::NodeId) -> McResult> { Ok(self.node_ty(id)) } - fn node_method_ty(&self, method_call: typeck::MethodCall) + fn node_method_ty(&self, method_call: ty::MethodCall) -> Option> { self.inh.method_map.borrow().get(&method_call).map(|m| m.ty) } @@ -287,7 +288,7 @@ impl<'a, 'tcx> mem_categorization::Typer<'tcx> for FnCtxt<'a, 'tcx> { &self.inh.adjustments } fn is_method_call(&self, id: ast::NodeId) -> bool { - self.inh.method_map.borrow().contains_key(&typeck::MethodCall::expr(id)) + self.inh.method_map.borrow().contains_key(&ty::MethodCall::expr(id)) } fn temporary_scope(&self, rvalue_id: ast::NodeId) -> Option { self.tcx().temporary_scope(rvalue_id) @@ -3260,7 +3261,7 @@ fn check_expr_with_unifier<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, Some(method) => { let method_ty = method.ty; // HACK(eddyb) Fully qualified path to work around a resolve bug. - let method_call = ::middle::typeck::MethodCall::expr(op_ex.id); + let method_call = ::middle::ty::MethodCall::expr(op_ex.id); fcx.inh.method_map.borrow_mut().insert(method_call, method); match check_method_argument_types(fcx, op_ex.span, diff --git a/src/librustc/middle/typeck/check/regionck.rs b/src/librustc/middle/typeck/check/regionck.rs index 08f7f9cf5e37f..1c938bc1e1f30 100644 --- a/src/librustc/middle/typeck/check/regionck.rs +++ b/src/librustc/middle/typeck/check/regionck.rs @@ -119,7 +119,7 @@ use middle::mem_categorization as mc; use middle::region::CodeExtent; use middle::traits; use middle::ty::{ReScope}; -use middle::ty::{mod, Ty}; +use middle::ty::{mod, Ty, MethodCall}; use middle::typeck::astconv::AstConv; use middle::typeck::check::FnCtxt; use middle::typeck::check::regionmanip; @@ -127,7 +127,6 @@ use middle::typeck::check::vtable; use middle::typeck::infer::resolve_and_force_all_but_regions; use middle::typeck::infer::resolve_type; use middle::typeck::infer; -use middle::typeck::MethodCall; use middle::pat_util; use util::nodemap::{DefIdMap, NodeMap, FnvHashMap}; use util::ppaux::{ty_to_string, Repr}; diff --git a/src/librustc/middle/typeck/check/writeback.rs b/src/librustc/middle/typeck/check/writeback.rs index 23af30b44d935..8205e83fb052b 100644 --- a/src/librustc/middle/typeck/check/writeback.rs +++ b/src/librustc/middle/typeck/check/writeback.rs @@ -15,14 +15,13 @@ use self::ResolveReason::*; use middle::def; use middle::pat_util; -use middle::ty::{mod, Ty}; +use middle::ty::{mod, Ty, MethodCall, MethodCallee}; use middle::ty_fold::{TypeFolder,TypeFoldable}; use middle::typeck::astconv::AstConv; use middle::typeck::check::FnCtxt; use middle::typeck::infer::{force_all, resolve_all, resolve_region}; use middle::typeck::infer::resolve_type; use middle::typeck::infer; -use middle::typeck::{MethodCall, MethodCallee}; use middle::typeck::write_substs_to_tcx; use middle::typeck::write_ty_to_tcx; use util::ppaux::Repr; diff --git a/src/librustc/middle/typeck/mod.rs b/src/librustc/middle/typeck/mod.rs index 501dfcb2e2d9e..a95849e083d10 100644 --- a/src/librustc/middle/typeck/mod.rs +++ b/src/librustc/middle/typeck/mod.rs @@ -61,10 +61,6 @@ independently: #![allow(non_camel_case_types)] -pub use self::ExprAdjustment::*; -pub use self::vtable_origin::*; -pub use self::MethodOrigin::*; - use middle::def; use middle::resolve; use middle::subst; @@ -74,10 +70,7 @@ use session::config; use util::common::time; use util::ppaux::Repr; use util::ppaux; -use util::nodemap::{NodeMap, FnvHashMap}; -use std::cell::RefCell; -use std::rc::Rc; use syntax::codemap::Span; use syntax::print::pprust::*; use syntax::{ast, ast_map, abi}; @@ -90,192 +83,11 @@ pub mod collect; pub mod coherence; pub mod variance; -#[deriving(Clone, Encodable, Decodable, PartialEq, PartialOrd, Show)] -pub struct param_index { - pub space: subst::ParamSpace, - pub index: uint -} - -#[deriving(Clone, Show)] -pub enum MethodOrigin<'tcx> { - // fully statically resolved method - MethodStatic(ast::DefId), - - // fully statically resolved unboxed closure invocation - MethodStaticUnboxedClosure(ast::DefId), - - // method invoked on a type parameter with a bounded trait - MethodTypeParam(MethodParam<'tcx>), - - // method invoked on a trait instance - MethodTraitObject(MethodObject<'tcx>), - -} - -// details for a method invoked with a receiver whose type is a type parameter -// with a bounded trait. -#[deriving(Clone, Show)] -pub struct MethodParam<'tcx> { - // the precise trait reference that occurs as a bound -- this may - // be a supertrait of what the user actually typed. - pub trait_ref: Rc>, - - // index of uint in the list of methods for the trait - pub method_num: uint, -} - -// details for a method invoked with a receiver whose type is an object -#[deriving(Clone, Show)] -pub struct MethodObject<'tcx> { - // the (super)trait containing the method to be invoked - pub trait_ref: Rc>, - - // the actual base trait id of the object - pub object_trait_id: ast::DefId, - - // index of the method to be invoked amongst the trait's methods - pub method_num: uint, - - // index into the actual runtime vtable. - // the vtable is formed by concatenating together the method lists of - // the base object trait and all supertraits; this is the index into - // that vtable - pub real_index: uint, -} - -#[deriving(Clone)] -pub struct MethodCallee<'tcx> { - pub origin: MethodOrigin<'tcx>, - pub ty: Ty<'tcx>, - pub substs: subst::Substs<'tcx> -} - -/// With method calls, we store some extra information in -/// side tables (i.e method_map). We use -/// MethodCall as a key to index into these tables instead of -/// just directly using the expression's NodeId. The reason -/// for this being that we may apply adjustments (coercions) -/// with the resulting expression also needing to use the -/// side tables. The problem with this is that we don't -/// assign a separate NodeId to this new expression -/// and so it would clash with the base expression if both -/// needed to add to the side tables. Thus to disambiguate -/// we also keep track of whether there's an adjustment in -/// our key. -#[deriving(Clone, PartialEq, Eq, Hash, Show)] -pub struct MethodCall { - pub expr_id: ast::NodeId, - pub adjustment: ExprAdjustment -} - -#[deriving(Clone, PartialEq, Eq, Hash, Show, Encodable, Decodable)] -pub enum ExprAdjustment { - NoAdjustment, - AutoDeref(uint), - AutoObject -} - pub struct TypeAndSubsts<'tcx> { pub substs: subst::Substs<'tcx>, pub ty: Ty<'tcx>, } -impl MethodCall { - pub fn expr(id: ast::NodeId) -> MethodCall { - MethodCall { - expr_id: id, - adjustment: NoAdjustment - } - } - - pub fn autoobject(id: ast::NodeId) -> MethodCall { - MethodCall { - expr_id: id, - adjustment: AutoObject - } - } - - pub fn autoderef(expr_id: ast::NodeId, autoderef: uint) -> MethodCall { - MethodCall { - expr_id: expr_id, - adjustment: AutoDeref(1 + autoderef) - } - } -} - -// maps from an expression id that corresponds to a method call to the details -// of the method to be invoked -pub type MethodMap<'tcx> = RefCell>>; - -pub type vtable_param_res<'tcx> = Vec>; - -// Resolutions for bounds of all parameters, left to right, for a given path. -pub type vtable_res<'tcx> = VecPerParamSpace>; - -#[deriving(Clone)] -pub enum vtable_origin<'tcx> { - /* - Statically known vtable. def_id gives the impl item - from whence comes the vtable, and tys are the type substs. - vtable_res is the vtable itself. - */ - vtable_static(ast::DefId, subst::Substs<'tcx>, vtable_res<'tcx>), - - /* - Dynamic vtable, comes from a parameter that has a bound on it: - fn foo(a: T) -- a's vtable would have a - vtable_param origin - - The first argument is the param index (identifying T in the example), - and the second is the bound number (identifying baz) - */ - vtable_param(param_index, uint), - - /* - Vtable automatically generated for an unboxed closure. The def ID is the - ID of the closure expression. - */ - vtable_unboxed_closure(ast::DefId), - - /* - Asked to determine the vtable for ty_err. This is the value used - for the vtables of `Self` in a virtual call like `foo.bar()` - where `foo` is of object type. The same value is also used when - type errors occur. - */ - vtable_error, -} - -impl<'tcx> Repr<'tcx> for vtable_origin<'tcx> { - fn repr(&self, tcx: &ty::ctxt<'tcx>) -> String { - match *self { - vtable_static(def_id, ref tys, ref vtable_res) => { - format!("vtable_static({}:{}, {}, {})", - def_id, - ty::item_path_str(tcx, def_id), - tys.repr(tcx), - vtable_res.repr(tcx)) - } - - vtable_param(x, y) => { - format!("vtable_param({}, {})", x, y) - } - - vtable_unboxed_closure(def_id) => { - format!("vtable_unboxed_closure({})", def_id) - } - - vtable_error => { - format!("vtable_error") - } - } - } -} - -// For every explicit cast into an object type, maps from the cast -// expr to the associated trait ref. -pub type ObjectCastMap<'tcx> = RefCell>>>; - pub struct CrateCtxt<'a, 'tcx: 'a> { // A mapping from method call sites to traits that have that method. trait_map: resolve::TraitMap, diff --git a/src/librustc/util/ppaux.rs b/src/librustc/util/ppaux.rs index b739a97f734be..1dfedd4c85c06 100644 --- a/src/librustc/util/ppaux.rs +++ b/src/librustc/util/ppaux.rs @@ -23,7 +23,6 @@ use middle::ty::{ty_param, ty_ptr, ty_rptr, ty_tup, ty_open}; use middle::ty::{ty_unboxed_closure}; use middle::ty::{ty_uniq, ty_trait, ty_int, ty_uint, ty_infer}; use middle::ty; -use middle::typeck; use middle::typeck::check::regionmanip; use std::rc::Rc; @@ -1018,7 +1017,7 @@ impl<'tcx> Repr<'tcx> for ty::FnOutput<'tcx> { } } -impl<'tcx> Repr<'tcx> for typeck::MethodCallee<'tcx> { +impl<'tcx> Repr<'tcx> for ty::MethodCallee<'tcx> { fn repr(&self, tcx: &ctxt<'tcx>) -> String { format!("MethodCallee {{origin: {}, ty: {}, {}}}", self.origin.repr(tcx), @@ -1027,26 +1026,26 @@ impl<'tcx> Repr<'tcx> for typeck::MethodCallee<'tcx> { } } -impl<'tcx> Repr<'tcx> for typeck::MethodOrigin<'tcx> { +impl<'tcx> Repr<'tcx> for ty::MethodOrigin<'tcx> { fn repr(&self, tcx: &ctxt<'tcx>) -> String { match self { - &typeck::MethodStatic(def_id) => { + &ty::MethodStatic(def_id) => { format!("MethodStatic({})", def_id.repr(tcx)) } - &typeck::MethodStaticUnboxedClosure(def_id) => { + &ty::MethodStaticUnboxedClosure(def_id) => { format!("MethodStaticUnboxedClosure({})", def_id.repr(tcx)) } - &typeck::MethodTypeParam(ref p) => { + &ty::MethodTypeParam(ref p) => { p.repr(tcx) } - &typeck::MethodTraitObject(ref p) => { + &ty::MethodTraitObject(ref p) => { p.repr(tcx) } } } } -impl<'tcx> Repr<'tcx> for typeck::MethodParam<'tcx> { +impl<'tcx> Repr<'tcx> for ty::MethodParam<'tcx> { fn repr(&self, tcx: &ctxt<'tcx>) -> String { format!("MethodParam({},{})", self.trait_ref.repr(tcx), @@ -1054,7 +1053,7 @@ impl<'tcx> Repr<'tcx> for typeck::MethodParam<'tcx> { } } -impl<'tcx> Repr<'tcx> for typeck::MethodObject<'tcx> { +impl<'tcx> Repr<'tcx> for ty::MethodObject<'tcx> { fn repr(&self, tcx: &ctxt<'tcx>) -> String { format!("MethodObject({},{},{})", self.trait_ref.repr(tcx), @@ -1293,7 +1292,6 @@ impl<'tcx> Repr<'tcx> for ty::ExplicitSelfCategory { } } - impl<'tcx> Repr<'tcx> for regionmanip::WfConstraint<'tcx> { fn repr(&self, tcx: &ctxt) -> String { match *self { diff --git a/src/librustc_trans/save/mod.rs b/src/librustc_trans/save/mod.rs index 7a41be1dbe46f..481ee679dbd62 100644 --- a/src/librustc_trans/save/mod.rs +++ b/src/librustc_trans/save/mod.rs @@ -912,10 +912,10 @@ impl <'l, 'tcx> DxrVisitor<'l, 'tcx> { ex: &ast::Expr, args: &Vec>) { let method_map = self.analysis.ty_cx.method_map.borrow(); - let method_callee = &(*method_map)[typeck::MethodCall::expr(ex.id)]; + let method_callee = &(*method_map)[ty::MethodCall::expr(ex.id)]; let (def_id, decl_id) = match method_callee.origin { - typeck::MethodStatic(def_id) | - typeck::MethodStaticUnboxedClosure(def_id) => { + ty::MethodStatic(def_id) | + ty::MethodStaticUnboxedClosure(def_id) => { // method invoked on an object with a concrete type (not a static method) let decl_id = match ty::trait_item_of_item(&self.analysis.ty_cx, @@ -936,14 +936,14 @@ impl <'l, 'tcx> DxrVisitor<'l, 'tcx> { }; (Some(def_id), decl_id) } - typeck::MethodTypeParam(ref mp) => { + ty::MethodTypeParam(ref mp) => { // method invoked on a type parameter let trait_item = ty::trait_item(&self.analysis.ty_cx, mp.trait_ref.def_id, mp.method_num); (None, Some(trait_item.def_id())) } - typeck::MethodTraitObject(ref mo) => { + ty::MethodTraitObject(ref mo) => { // method invoked on a trait instance let trait_item = ty::trait_item(&self.analysis.ty_cx, mo.trait_ref.def_id, diff --git a/src/librustc_trans/trans/callee.rs b/src/librustc_trans/trans/callee.rs index 176fe7096e751..1a273f1e2b7c2 100644 --- a/src/librustc_trans/trans/callee.rs +++ b/src/librustc_trans/trans/callee.rs @@ -49,8 +49,8 @@ use trans::monomorphize; use trans::type_::Type; use trans::type_of; use middle::ty::{mod, Ty}; +use middle::ty::MethodCall; use middle::typeck::coherence::make_substs_for_receiver_types; -use middle::typeck::MethodCall; use util::ppaux::Repr; use util::ppaux::ty_to_string; diff --git a/src/librustc_trans/trans/common.rs b/src/librustc_trans/trans/common.rs index febb33f6c54af..6bd86cf5aa4a2 100644 --- a/src/librustc_trans/trans/common.rs +++ b/src/librustc_trans/trans/common.rs @@ -36,7 +36,6 @@ use middle::traits; use middle::ty::{mod, Ty}; use middle::ty_fold; use middle::ty_fold::TypeFoldable; -use middle::typeck; use middle::typeck::infer; use util::ppaux::Repr; use util::nodemap::{DefIdMap, FnvHashMap, NodeMap}; @@ -468,7 +467,7 @@ impl<'blk, 'tcx> mc::Typer<'tcx> for BlockS<'blk, 'tcx> { Ok(node_id_type(self, id)) } - fn node_method_ty(&self, method_call: typeck::MethodCall) -> Option> { + fn node_method_ty(&self, method_call: ty::MethodCall) -> Option> { self.tcx() .method_map .borrow() @@ -481,7 +480,7 @@ impl<'blk, 'tcx> mc::Typer<'tcx> for BlockS<'blk, 'tcx> { } fn is_method_call(&self, id: ast::NodeId) -> bool { - self.tcx().method_map.borrow().contains_key(&typeck::MethodCall::expr(id)) + self.tcx().method_map.borrow().contains_key(&ty::MethodCall::expr(id)) } fn temporary_scope(&self, rvalue_id: ast::NodeId) -> Option { @@ -870,7 +869,7 @@ pub enum ExprOrMethodCall { ExprId(ast::NodeId), // Type parameters for a method call like `a.foo::()` - MethodCall(typeck::MethodCall) + MethodCall(ty::MethodCall) } pub fn node_id_substs<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, diff --git a/src/librustc_trans/trans/controlflow.rs b/src/librustc_trans/trans/controlflow.rs index 3d32f6045a771..a1574aa2f0e43 100644 --- a/src/librustc_trans/trans/controlflow.rs +++ b/src/librustc_trans/trans/controlflow.rs @@ -27,7 +27,7 @@ use trans::meth; use trans::type_::Type; use trans; use middle::ty; -use middle::typeck::MethodCall; +use middle::ty::MethodCall; use session::config::FullDebugInfo; use util::ppaux::Repr; use util::ppaux; diff --git a/src/librustc_trans/trans/expr.rs b/src/librustc_trans/trans/expr.rs index 646ee601a4ca4..f538a73329b5d 100644 --- a/src/librustc_trans/trans/expr.rs +++ b/src/librustc_trans/trans/expr.rs @@ -47,9 +47,18 @@ use trans::build::*; use trans::cleanup::{mod, CleanupMethods}; use trans::common::*; use trans::datum::*; -use middle::ty::{mod, struct_fields, tup_fields}; -use middle::ty::{AdjustDerefRef, AdjustAddEnv, AutoUnsafe, AutoPtr, Ty}; -use middle::typeck::{mod, MethodCall}; +use trans::debuginfo; +use trans::glue; +use trans::machine; +use trans::meth; +use trans::inline; +use trans::tvec; +use trans::type_of; +use middle::ty::{struct_fields, tup_fields}; +use middle::ty::{AdjustDerefRef, AdjustAddEnv, AutoUnsafe}; +use middle::ty::{AutoPtr}; +use middle::ty::{mod, Ty}; +use middle::ty::MethodCall; use util::common::indenter; use util::ppaux::Repr; use trans::machine::{llsize_of, llsize_of_alloc}; @@ -2091,7 +2100,7 @@ fn deref_once<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, // path (below) to dereference that `&T`. let datum = match method_call.adjustment { // Always perform an AutoPtr when applying an overloaded auto-deref - typeck::AutoDeref(_) => unpack_datum!(bcx, auto_ref(bcx, datum, expr)), + ty::AutoDeref(_) => unpack_datum!(bcx, auto_ref(bcx, datum, expr)), _ => datum }; diff --git a/src/librustc_trans/trans/meth.rs b/src/librustc_trans/trans/meth.rs index ef431243b31e9..94ff526debd1e 100644 --- a/src/librustc_trans/trans/meth.rs +++ b/src/librustc_trans/trans/meth.rs @@ -31,8 +31,7 @@ use trans::machine; use trans::type_::Type; use trans::type_of::*; use middle::ty::{mod, Ty}; -use middle::typeck; -use middle::typeck::MethodCall; +use middle::ty::MethodCall; use util::ppaux::Repr; use std::c_str::ToCStr; @@ -119,8 +118,8 @@ pub fn trans_method_callee<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, .unwrap(); match origin { - typeck::MethodStatic(did) | - typeck::MethodStaticUnboxedClosure(did) => { + ty::MethodStatic(did) | + ty::MethodStaticUnboxedClosure(did) => { Callee { bcx: bcx, data: Fn(callee::trans_fn_ref(bcx, @@ -129,7 +128,7 @@ pub fn trans_method_callee<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, } } - typeck::MethodTypeParam(typeck::MethodParam { + ty::MethodTypeParam(ty::MethodParam { ref trait_ref, method_num }) => { @@ -147,7 +146,7 @@ pub fn trans_method_callee<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, method_num, origin) } - typeck::MethodTraitObject(ref mt) => { + ty::MethodTraitObject(ref mt) => { let self_expr = match self_expr { Some(self_expr) => self_expr, None => { From db75f8aa91a8757725a84db2345236f887b2ffec Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 25 Nov 2014 16:59:02 -0500 Subject: [PATCH 58/83] Move infer out of `middle::typeck` and into just `middle`. --- src/librustc/lib.rs | 1 + src/librustc/lint/builtin.rs | 2 +- src/librustc/lint/context.rs | 2 +- src/librustc/middle/check_static.rs | 4 +-- .../middle/{typeck => }/infer/coercion.rs | 9 ++++--- .../middle/{typeck => }/infer/combine.rs | 17 ++++++------ src/librustc/middle/{typeck => }/infer/doc.rs | 0 .../middle/{typeck => }/infer/equate.rs | 16 +++++------ .../{typeck => }/infer/error_reporting.rs | 27 ++++++++++--------- src/librustc/middle/{typeck => }/infer/glb.rs | 16 +++++------ .../{typeck => }/infer/higher_ranked/doc.rs | 0 .../{typeck => }/infer/higher_ranked/mod.rs | 17 ++++++------ .../middle/{typeck => }/infer/lattice.rs | 9 ++++--- src/librustc/middle/{typeck => }/infer/lub.rs | 17 ++++++------ src/librustc/middle/{typeck => }/infer/mod.rs | 0 .../infer/region_inference/doc.rs | 0 .../infer/region_inference/mod.rs | 8 +++--- .../middle/{typeck => }/infer/resolve.rs | 5 ++-- .../middle/{typeck => }/infer/skolemize.rs | 0 src/librustc/middle/{typeck => }/infer/sub.rs | 18 ++++++------- .../{typeck => }/infer/type_variable.rs | 0 .../middle/{typeck => }/infer/unify.rs | 4 +-- src/librustc/middle/traits/coherence.rs | 2 +- src/librustc/middle/traits/fulfill.rs | 2 +- src/librustc/middle/traits/mod.rs | 2 +- src/librustc/middle/traits/select.rs | 4 +-- src/librustc/middle/traits/util.rs | 2 +- src/librustc/middle/typeck/check/_match.rs | 2 +- src/librustc/middle/typeck/check/closure.rs | 2 +- src/librustc/middle/typeck/check/demand.rs | 6 ++--- .../middle/typeck/check/method/confirm.rs | 3 ++- .../middle/typeck/check/method/mod.rs | 2 +- .../middle/typeck/check/method/probe.rs | 4 +-- src/librustc/middle/typeck/check/mod.rs | 3 ++- src/librustc/middle/typeck/check/regionck.rs | 6 ++--- src/librustc/middle/typeck/check/vtable.rs | 2 +- src/librustc/middle/typeck/check/writeback.rs | 6 ++--- src/librustc/middle/typeck/coherence/mod.rs | 6 ++--- .../middle/typeck/coherence/overlap.rs | 3 +-- src/librustc/middle/typeck/collect.rs | 2 +- src/librustc/middle/typeck/mod.rs | 2 +- src/librustc_trans/save/mod.rs | 2 +- src/librustc_trans/test.rs | 8 +++--- src/librustc_trans/trans/common.rs | 2 +- src/librustc_trans/trans/expr.rs | 1 - 45 files changed, 126 insertions(+), 120 deletions(-) rename src/librustc/middle/{typeck => }/infer/coercion.rs (99%) rename src/librustc/middle/{typeck => }/infer/combine.rs (98%) rename src/librustc/middle/{typeck => }/infer/doc.rs (100%) rename src/librustc/middle/{typeck => }/infer/equate.rs (93%) rename src/librustc/middle/{typeck => }/infer/error_reporting.rs (99%) rename src/librustc/middle/{typeck => }/infer/glb.rs (92%) rename src/librustc/middle/{typeck => }/infer/higher_ranked/doc.rs (100%) rename src/librustc/middle/{typeck => }/infer/higher_ranked/mod.rs (97%) rename src/librustc/middle/{typeck => }/infer/lattice.rs (96%) rename src/librustc/middle/{typeck => }/infer/lub.rs (91%) rename src/librustc/middle/{typeck => }/infer/mod.rs (100%) rename src/librustc/middle/{typeck => }/infer/region_inference/doc.rs (100%) rename src/librustc/middle/{typeck => }/infer/region_inference/mod.rs (99%) rename src/librustc/middle/{typeck => }/infer/resolve.rs (98%) rename src/librustc/middle/{typeck => }/infer/skolemize.rs (100%) rename src/librustc/middle/{typeck => }/infer/sub.rs (92%) rename src/librustc/middle/{typeck => }/infer/type_variable.rs (100%) rename src/librustc/middle/{typeck => }/infer/unify.rs (99%) diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs index a83f8afd39617..d8da4df08bcc0 100644 --- a/src/librustc/lib.rs +++ b/src/librustc/lib.rs @@ -79,6 +79,7 @@ pub mod middle { pub mod fast_reject; pub mod graph; pub mod intrinsicck; + pub mod infer; pub mod lang_items; pub mod liveness; pub mod mem_categorization; diff --git a/src/librustc/lint/builtin.rs b/src/librustc/lint/builtin.rs index cb617b169a060..8469f477edb34 100644 --- a/src/librustc/lint/builtin.rs +++ b/src/librustc/lint/builtin.rs @@ -28,9 +28,9 @@ use self::MethodContext::*; use metadata::csearch; use middle::def::*; +use middle::infer; use middle::ty::{mod, Ty}; use middle::typeck::astconv::ast_ty_to_ty; -use middle::typeck::infer; use middle::{def, pat_util, stability}; use middle::const_eval::{eval_const_expr_partial, const_int, const_uint}; use util::ppaux::{ty_to_string}; diff --git a/src/librustc/lint/context.rs b/src/librustc/lint/context.rs index c7bed838eb919..8014673f759ac 100644 --- a/src/librustc/lint/context.rs +++ b/src/librustc/lint/context.rs @@ -25,11 +25,11 @@ //! for all lint attributes. use self::TargetLint::*; +use middle::infer; use middle::privacy::ExportedItems; use middle::subst; use middle::ty::{mod, Ty}; use middle::typeck::astconv::AstConv; -use middle::typeck::infer; use session::{early_error, Session}; use lint::{Level, LevelSource, Lint, LintId, LintArray, LintPass, LintPassObject}; use lint::{Default, CommandLine, Node, Allow, Warn, Deny, Forbid}; diff --git a/src/librustc/middle/check_static.rs b/src/librustc/middle/check_static.rs index d3c7ccf65dd72..2fc85afd3935d 100644 --- a/src/librustc/middle/check_static.rs +++ b/src/librustc/middle/check_static.rs @@ -27,7 +27,7 @@ use self::Mode::*; use middle::ty; use middle::def; -use middle::typeck; +use middle::infer; use middle::traits; use middle::mem_categorization as mc; use middle::expr_use_visitor as euv; @@ -113,7 +113,7 @@ impl<'a, 'tcx> CheckStaticVisitor<'a, 'tcx> { fn check_static_type(&self, e: &ast::Expr) { let ty = ty::node_id_to_type(self.tcx, e.id); - let infcx = typeck::infer::new_infer_ctxt(self.tcx); + let infcx = infer::new_infer_ctxt(self.tcx); let mut fulfill_cx = traits::FulfillmentContext::new(); let cause = traits::ObligationCause::misc(DUMMY_SP); let obligation = traits::obligation_for_builtin_bound(self.tcx, cause, ty, diff --git a/src/librustc/middle/typeck/infer/coercion.rs b/src/librustc/middle/infer/coercion.rs similarity index 99% rename from src/librustc/middle/typeck/infer/coercion.rs rename to src/librustc/middle/infer/coercion.rs index 51f8668692ea7..f04c519badc8c 100644 --- a/src/librustc/middle/typeck/infer/coercion.rs +++ b/src/librustc/middle/infer/coercion.rs @@ -60,14 +60,15 @@ //! sort of a minor point so I've opted to leave it for later---after all //! we may want to adjust precisely when coercions occur. +use super::{CoerceResult, resolve_type, Coercion}; +use super::combine::{CombineFields, Combine}; +use super::sub::Sub; +use super::resolve::try_resolve_tvar_shallow; + use middle::subst; use middle::ty::{AutoPtr, AutoDerefRef, AdjustDerefRef, AutoUnsize, AutoUnsafe}; use middle::ty::{mt}; use middle::ty::{mod, Ty}; -use middle::typeck::infer::{CoerceResult, resolve_type, Coercion}; -use middle::typeck::infer::combine::{CombineFields, Combine}; -use middle::typeck::infer::sub::Sub; -use middle::typeck::infer::resolve::try_resolve_tvar_shallow; use util::ppaux; use util::ppaux::Repr; diff --git a/src/librustc/middle/typeck/infer/combine.rs b/src/librustc/middle/infer/combine.rs similarity index 98% rename from src/librustc/middle/typeck/infer/combine.rs rename to src/librustc/middle/infer/combine.rs index ba6ae00b6671f..ab9c5b86aeb62 100644 --- a/src/librustc/middle/typeck/infer/combine.rs +++ b/src/librustc/middle/infer/combine.rs @@ -32,6 +32,14 @@ // is also useful to track which value is the "expected" value in // terms of error reporting. +use super::equate::Equate; +use super::glb::Glb; +use super::lub::Lub; +use super::sub::Sub; +use super::unify::InferCtxtMethodsForSimplyUnifiableTypes; +use super::{InferCtxt, cres}; +use super::{MiscVariable, TypeTrace}; +use super::type_variable::{RelationDir, EqTo, SubtypeOf, SupertypeOf}; use middle::subst; use middle::subst::{ErasedRegions, NonerasedRegions, Substs}; @@ -40,15 +48,6 @@ use middle::ty::{IntType, UintType}; use middle::ty::{BuiltinBounds}; use middle::ty::{mod, Ty}; use middle::ty_fold; -use middle::typeck::infer::equate::Equate; -use middle::typeck::infer::glb::Glb; -use middle::typeck::infer::lub::Lub; -use middle::typeck::infer::sub::Sub; -use middle::typeck::infer::unify::InferCtxtMethodsForSimplyUnifiableTypes; -use middle::typeck::infer::{InferCtxt, cres}; -use middle::typeck::infer::{MiscVariable, TypeTrace}; -use middle::typeck::infer::type_variable::{RelationDir, EqTo, - SubtypeOf, SupertypeOf}; use middle::ty_fold::{TypeFoldable}; use util::ppaux::Repr; diff --git a/src/librustc/middle/typeck/infer/doc.rs b/src/librustc/middle/infer/doc.rs similarity index 100% rename from src/librustc/middle/typeck/infer/doc.rs rename to src/librustc/middle/infer/doc.rs diff --git a/src/librustc/middle/typeck/infer/equate.rs b/src/librustc/middle/infer/equate.rs similarity index 93% rename from src/librustc/middle/typeck/infer/equate.rs rename to src/librustc/middle/infer/equate.rs index 356081c199afa..a79a50b1781eb 100644 --- a/src/librustc/middle/typeck/infer/equate.rs +++ b/src/librustc/middle/infer/equate.rs @@ -11,14 +11,14 @@ use middle::ty::{BuiltinBounds}; use middle::ty::{mod, Ty}; use middle::ty::TyVar; -use middle::typeck::infer::combine::*; -use middle::typeck::infer::{cres}; -use middle::typeck::infer::glb::Glb; -use middle::typeck::infer::InferCtxt; -use middle::typeck::infer::lub::Lub; -use middle::typeck::infer::sub::Sub; -use middle::typeck::infer::{TypeTrace, Subtype}; -use middle::typeck::infer::type_variable::{EqTo}; +use middle::infer::combine::*; +use middle::infer::{cres}; +use middle::infer::glb::Glb; +use middle::infer::InferCtxt; +use middle::infer::lub::Lub; +use middle::infer::sub::Sub; +use middle::infer::{TypeTrace, Subtype}; +use middle::infer::type_variable::{EqTo}; use util::ppaux::{Repr}; use syntax::ast::{Onceness, FnStyle}; diff --git a/src/librustc/middle/typeck/infer/error_reporting.rs b/src/librustc/middle/infer/error_reporting.rs similarity index 99% rename from src/librustc/middle/typeck/infer/error_reporting.rs rename to src/librustc/middle/infer/error_reporting.rs index 0607ccdc595a2..657ee088758d1 100644 --- a/src/librustc/middle/typeck/infer/error_reporting.rs +++ b/src/librustc/middle/infer/error_reporting.rs @@ -57,24 +57,25 @@ use self::FreshOrKept::*; +use super::InferCtxt; +use super::TypeTrace; +use super::SubregionOrigin; +use super::RegionVariableOrigin; +use super::ValuePairs; +use super::region_inference::RegionResolutionError; +use super::region_inference::ConcreteFailure; +use super::region_inference::SubSupConflict; +use super::region_inference::SupSupConflict; +use super::region_inference::ParamBoundFailure; +use super::region_inference::ProcessedErrors; +use super::region_inference::SameRegions; + use std::collections::HashSet; use middle::def; +use middle::infer; use middle::subst; use middle::ty::{mod, Ty}; use middle::ty::{Region, ReFree}; -use middle::typeck::infer; -use middle::typeck::infer::InferCtxt; -use middle::typeck::infer::TypeTrace; -use middle::typeck::infer::SubregionOrigin; -use middle::typeck::infer::RegionVariableOrigin; -use middle::typeck::infer::ValuePairs; -use middle::typeck::infer::region_inference::RegionResolutionError; -use middle::typeck::infer::region_inference::ConcreteFailure; -use middle::typeck::infer::region_inference::SubSupConflict; -use middle::typeck::infer::region_inference::SupSupConflict; -use middle::typeck::infer::region_inference::ParamBoundFailure; -use middle::typeck::infer::region_inference::ProcessedErrors; -use middle::typeck::infer::region_inference::SameRegions; use std::cell::{Cell, RefCell}; use std::char::from_u32; use std::rc::Rc; diff --git a/src/librustc/middle/typeck/infer/glb.rs b/src/librustc/middle/infer/glb.rs similarity index 92% rename from src/librustc/middle/typeck/infer/glb.rs rename to src/librustc/middle/infer/glb.rs index 671d2e3837c79..4237a7af32fc1 100644 --- a/src/librustc/middle/typeck/infer/glb.rs +++ b/src/librustc/middle/infer/glb.rs @@ -8,17 +8,17 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use super::combine::*; +use super::lattice::*; +use super::equate::Equate; +use super::higher_ranked::HigherRankedRelations; +use super::lub::Lub; +use super::sub::Sub; +use super::{cres, InferCtxt}; +use super::{TypeTrace, Subtype}; use middle::ty::{BuiltinBounds}; use middle::ty::{mod, Ty}; -use middle::typeck::infer::combine::*; -use middle::typeck::infer::lattice::*; -use middle::typeck::infer::equate::Equate; -use middle::typeck::infer::higher_ranked::HigherRankedRelations; -use middle::typeck::infer::lub::Lub; -use middle::typeck::infer::sub::Sub; -use middle::typeck::infer::{cres, InferCtxt}; -use middle::typeck::infer::{TypeTrace, Subtype}; use syntax::ast::{Many, Once, MutImmutable, MutMutable}; use syntax::ast::{NormalFn, UnsafeFn}; use syntax::ast::{Onceness, FnStyle}; diff --git a/src/librustc/middle/typeck/infer/higher_ranked/doc.rs b/src/librustc/middle/infer/higher_ranked/doc.rs similarity index 100% rename from src/librustc/middle/typeck/infer/higher_ranked/doc.rs rename to src/librustc/middle/infer/higher_ranked/doc.rs diff --git a/src/librustc/middle/typeck/infer/higher_ranked/mod.rs b/src/librustc/middle/infer/higher_ranked/mod.rs similarity index 97% rename from src/librustc/middle/typeck/infer/higher_ranked/mod.rs rename to src/librustc/middle/infer/higher_ranked/mod.rs index 2f80a574bb18b..95805ef8b944d 100644 --- a/src/librustc/middle/typeck/infer/higher_ranked/mod.rs +++ b/src/librustc/middle/infer/higher_ranked/mod.rs @@ -11,10 +11,11 @@ //! Helper routines for higher-ranked things. See the `doc` module at //! the end of the file for details. +use super::{combine, cres, InferCtxt, HigherRankedType}; +use super::combine::Combine; +use super::region_inference::{RegionMark}; + use middle::ty::{mod, Ty, replace_late_bound_regions}; -use middle::typeck::infer::{mod, combine, cres, InferCtxt}; -use middle::typeck::infer::combine::Combine; -use middle::typeck::infer::region_inference::{RegionMark}; use middle::ty_fold::{mod, HigherRankedFoldable, TypeFoldable}; use syntax::codemap::Span; use util::nodemap::FnvHashMap; @@ -62,7 +63,7 @@ impl<'tcx,C> HigherRankedRelations<'tcx> for C let (a_prime, _) = self.infcx().replace_late_bound_regions_with_fresh_var( self.trace().origin.span(), - infer::HigherRankedType, + HigherRankedType, a); // Second, we instantiate each bound region in the supertype with a @@ -131,10 +132,10 @@ impl<'tcx,C> HigherRankedRelations<'tcx> for C let span = self.trace().origin.span(); let (a_with_fresh, a_map) = self.infcx().replace_late_bound_regions_with_fresh_var( - span, infer::HigherRankedType, a); + span, HigherRankedType, a); let (b_with_fresh, _) = self.infcx().replace_late_bound_regions_with_fresh_var( - span, infer::HigherRankedType, b); + span, HigherRankedType, b); // Collect constraints. let result0 = @@ -221,10 +222,10 @@ impl<'tcx,C> HigherRankedRelations<'tcx> for C // Instantiate each bound region with a fresh region variable. let (a_with_fresh, a_map) = self.infcx().replace_late_bound_regions_with_fresh_var( - self.trace().origin.span(), infer::HigherRankedType, a); + self.trace().origin.span(), HigherRankedType, a); let (b_with_fresh, b_map) = self.infcx().replace_late_bound_regions_with_fresh_var( - self.trace().origin.span(), infer::HigherRankedType, b); + self.trace().origin.span(), HigherRankedType, b); let a_vars = var_ids(self, &a_map); let b_vars = var_ids(self, &b_map); diff --git a/src/librustc/middle/typeck/infer/lattice.rs b/src/librustc/middle/infer/lattice.rs similarity index 96% rename from src/librustc/middle/typeck/infer/lattice.rs rename to src/librustc/middle/infer/lattice.rs index daec959d11cd3..dd514ebee524a 100644 --- a/src/librustc/middle/typeck/infer/lattice.rs +++ b/src/librustc/middle/infer/lattice.rs @@ -29,12 +29,13 @@ //! over a `LatticeValue`, which is a value defined with respect to //! a lattice. +use super::*; +use super::combine::*; +use super::glb::Glb; +use super::lub::Lub; + use middle::ty::{TyVar}; use middle::ty::{mod, Ty}; -use middle::typeck::infer::*; -use middle::typeck::infer::combine::*; -use middle::typeck::infer::glb::Glb; -use middle::typeck::infer::lub::Lub; use util::ppaux::Repr; pub trait LatticeDir<'tcx> { diff --git a/src/librustc/middle/typeck/infer/lub.rs b/src/librustc/middle/infer/lub.rs similarity index 91% rename from src/librustc/middle/typeck/infer/lub.rs rename to src/librustc/middle/infer/lub.rs index e7bd1f3716c12..f53ba571062b2 100644 --- a/src/librustc/middle/typeck/infer/lub.rs +++ b/src/librustc/middle/infer/lub.rs @@ -8,16 +8,17 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use super::combine::*; +use super::equate::Equate; +use super::glb::Glb; +use super::higher_ranked::HigherRankedRelations; +use super::lattice::*; +use super::sub::Sub; +use super::{cres, InferCtxt}; +use super::{TypeTrace, Subtype}; + use middle::ty::{BuiltinBounds}; use middle::ty::{mod, Ty}; -use middle::typeck::infer::combine::*; -use middle::typeck::infer::equate::Equate; -use middle::typeck::infer::glb::Glb; -use middle::typeck::infer::higher_ranked::HigherRankedRelations; -use middle::typeck::infer::lattice::*; -use middle::typeck::infer::sub::Sub; -use middle::typeck::infer::{cres, InferCtxt}; -use middle::typeck::infer::{TypeTrace, Subtype}; use syntax::ast::{Many, Once}; use syntax::ast::{NormalFn, UnsafeFn}; use syntax::ast::{Onceness, FnStyle}; diff --git a/src/librustc/middle/typeck/infer/mod.rs b/src/librustc/middle/infer/mod.rs similarity index 100% rename from src/librustc/middle/typeck/infer/mod.rs rename to src/librustc/middle/infer/mod.rs diff --git a/src/librustc/middle/typeck/infer/region_inference/doc.rs b/src/librustc/middle/infer/region_inference/doc.rs similarity index 100% rename from src/librustc/middle/typeck/infer/region_inference/doc.rs rename to src/librustc/middle/infer/region_inference/doc.rs diff --git a/src/librustc/middle/typeck/infer/region_inference/mod.rs b/src/librustc/middle/infer/region_inference/mod.rs similarity index 99% rename from src/librustc/middle/typeck/infer/region_inference/mod.rs rename to src/librustc/middle/infer/region_inference/mod.rs index e39fbe105dcfc..9155c18cb3b5a 100644 --- a/src/librustc/middle/typeck/infer/region_inference/mod.rs +++ b/src/librustc/middle/infer/region_inference/mod.rs @@ -18,14 +18,14 @@ pub use self::RegionResolutionError::*; pub use self::VarValue::*; use self::Classification::*; +use super::cres; +use super::{RegionVariableOrigin, SubregionOrigin, TypeTrace, MiscVariable}; + use middle::region; use middle::ty; use middle::ty::{BoundRegion, FreeRegion, Region, RegionVid}; use middle::ty::{ReEmpty, ReStatic, ReInfer, ReFree, ReEarlyBound}; use middle::ty::{ReLateBound, ReScope, ReVar, ReSkolemized, BrFresh}; -use middle::typeck::infer::cres; -use middle::typeck::infer::{RegionVariableOrigin, SubregionOrigin, TypeTrace}; -use middle::typeck::infer; use middle::graph; use middle::graph::{Direction, NodeIndex}; use util::common::indenter; @@ -573,7 +573,7 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> { } None => {} } - let c = self.new_region_var(infer::MiscVariable(origin.span())); + let c = self.new_region_var(MiscVariable(origin.span())); self.combine_map(t).borrow_mut().insert(vars, c); if self.in_snapshot() { self.undo_log.borrow_mut().push(AddCombination(t, vars)); diff --git a/src/librustc/middle/typeck/infer/resolve.rs b/src/librustc/middle/infer/resolve.rs similarity index 98% rename from src/librustc/middle/typeck/infer/resolve.rs rename to src/librustc/middle/infer/resolve.rs index cf5efd188ed96..eaf363ffc74c2 100644 --- a/src/librustc/middle/typeck/infer/resolve.rs +++ b/src/librustc/middle/infer/resolve.rs @@ -48,12 +48,13 @@ #![allow(non_upper_case_globals)] +use super::{fixup_err, fres, InferCtxt}; +use super::{unresolved_int_ty,unresolved_float_ty,unresolved_ty}; + use middle::ty::{FloatVar, FloatVid, IntVar, IntVid, RegionVid, TyVar, TyVid}; use middle::ty::{IntType, UintType}; use middle::ty::{mod, Ty}; use middle::ty_fold; -use middle::typeck::infer::{fixup_err, fres, InferCtxt}; -use middle::typeck::infer::{unresolved_int_ty,unresolved_float_ty,unresolved_ty}; use syntax::codemap::Span; use util::ppaux::{Repr, ty_to_string}; diff --git a/src/librustc/middle/typeck/infer/skolemize.rs b/src/librustc/middle/infer/skolemize.rs similarity index 100% rename from src/librustc/middle/typeck/infer/skolemize.rs rename to src/librustc/middle/infer/skolemize.rs diff --git a/src/librustc/middle/typeck/infer/sub.rs b/src/librustc/middle/infer/sub.rs similarity index 92% rename from src/librustc/middle/typeck/infer/sub.rs rename to src/librustc/middle/infer/sub.rs index 65d2a5133936c..c470b2488273a 100644 --- a/src/librustc/middle/typeck/infer/sub.rs +++ b/src/librustc/middle/infer/sub.rs @@ -8,19 +8,19 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use super::combine::*; +use super::{cres, CresCompare}; +use super::equate::Equate; +use super::glb::Glb; +use super::higher_ranked::HigherRankedRelations; +use super::InferCtxt; +use super::lub::Lub; +use super::{TypeTrace, Subtype}; +use super::type_variable::{SubtypeOf, SupertypeOf}; use middle::ty::{BuiltinBounds}; use middle::ty::{mod, Ty}; use middle::ty::TyVar; -use middle::typeck::infer::combine::*; -use middle::typeck::infer::{cres, CresCompare}; -use middle::typeck::infer::equate::Equate; -use middle::typeck::infer::glb::Glb; -use middle::typeck::infer::higher_ranked::HigherRankedRelations; -use middle::typeck::infer::InferCtxt; -use middle::typeck::infer::lub::Lub; -use middle::typeck::infer::{TypeTrace, Subtype}; -use middle::typeck::infer::type_variable::{SubtypeOf, SupertypeOf}; use util::ppaux::{Repr}; use syntax::ast::{Onceness, FnStyle, MutImmutable, MutMutable}; diff --git a/src/librustc/middle/typeck/infer/type_variable.rs b/src/librustc/middle/infer/type_variable.rs similarity index 100% rename from src/librustc/middle/typeck/infer/type_variable.rs rename to src/librustc/middle/infer/type_variable.rs diff --git a/src/librustc/middle/typeck/infer/unify.rs b/src/librustc/middle/infer/unify.rs similarity index 99% rename from src/librustc/middle/typeck/infer/unify.rs rename to src/librustc/middle/infer/unify.rs index 1b3413bfb0104..6f6adb84a75f5 100644 --- a/src/librustc/middle/typeck/infer/unify.rs +++ b/src/librustc/middle/infer/unify.rs @@ -14,8 +14,8 @@ use std::kinds::marker; use middle::ty::{expected_found, IntVarValue}; use middle::ty::{mod, Ty}; -use middle::typeck::infer::{uok, ures}; -use middle::typeck::infer::InferCtxt; +use middle::infer::{uok, ures}; +use middle::infer::InferCtxt; use std::cell::RefCell; use std::fmt::Show; use syntax::ast; diff --git a/src/librustc/middle/traits/coherence.rs b/src/librustc/middle/traits/coherence.rs index 048f394224cf0..1bce353cb0cfe 100644 --- a/src/librustc/middle/traits/coherence.rs +++ b/src/librustc/middle/traits/coherence.rs @@ -17,7 +17,7 @@ use super::util; use middle::subst; use middle::subst::Subst; use middle::ty::{mod, Ty}; -use middle::typeck::infer::{mod, InferCtxt}; +use middle::infer::{mod, InferCtxt}; use syntax::ast; use syntax::codemap::DUMMY_SP; use util::ppaux::Repr; diff --git a/src/librustc/middle/traits/fulfill.rs b/src/librustc/middle/traits/fulfill.rs index 25c86be993f70..653c686ab197c 100644 --- a/src/librustc/middle/traits/fulfill.rs +++ b/src/librustc/middle/traits/fulfill.rs @@ -10,7 +10,7 @@ use middle::mem_categorization::Typer; use middle::ty; -use middle::typeck::infer::InferCtxt; +use middle::infer::InferCtxt; use std::collections::HashSet; use std::rc::Rc; use util::ppaux::Repr; diff --git a/src/librustc/middle/traits/mod.rs b/src/librustc/middle/traits/mod.rs index b8d915c06e0b7..e12ec44ad87ce 100644 --- a/src/librustc/middle/traits/mod.rs +++ b/src/librustc/middle/traits/mod.rs @@ -18,7 +18,7 @@ pub use self::ObligationCauseCode::*; use middle::mem_categorization::Typer; use middle::subst; use middle::ty::{mod, Ty}; -use middle::typeck::infer::InferCtxt; +use middle::infer::InferCtxt; use std::rc::Rc; use std::slice::Items; use syntax::ast; diff --git a/src/librustc/middle/traits/select.rs b/src/librustc/middle/traits/select.rs index 2604204d9e63f..0e6a0c19f7061 100644 --- a/src/librustc/middle/traits/select.rs +++ b/src/librustc/middle/traits/select.rs @@ -30,8 +30,8 @@ use middle::fast_reject; use middle::mem_categorization::Typer; use middle::subst::{Subst, Substs, VecPerParamSpace}; use middle::ty::{mod, Ty}; -use middle::typeck::infer; -use middle::typeck::infer::{InferCtxt, TypeSkolemizer}; +use middle::infer; +use middle::infer::{InferCtxt, TypeSkolemizer}; use middle::ty_fold::TypeFoldable; use std::cell::RefCell; use std::collections::hash_map::HashMap; diff --git a/src/librustc/middle/traits/util.rs b/src/librustc/middle/traits/util.rs index 1084807ef4a0a..1b7998a92638c 100644 --- a/src/librustc/middle/traits/util.rs +++ b/src/librustc/middle/traits/util.rs @@ -11,7 +11,7 @@ use middle::subst; use middle::subst::{ParamSpace, Substs, VecPerParamSpace}; -use middle::typeck::infer::InferCtxt; +use middle::infer::InferCtxt; use middle::ty::{mod, Ty}; use std::collections::HashSet; use std::fmt; diff --git a/src/librustc/middle/typeck/check/_match.rs b/src/librustc/middle/typeck/check/_match.rs index cdfd607d06710..6af36225364fb 100644 --- a/src/librustc/middle/typeck/check/_match.rs +++ b/src/librustc/middle/typeck/check/_match.rs @@ -14,7 +14,7 @@ use middle::subst::{Subst, Substs}; use middle::ty::{mod, Ty}; use middle::typeck::check::{check_expr, check_expr_has_type, demand, FnCtxt}; use middle::typeck::check::{instantiate_path, structurally_resolved_type, valid_range_bounds}; -use middle::typeck::infer::{mod, resolve}; +use middle::infer::{mod, resolve}; use middle::typeck::require_same_types; use util::nodemap::FnvHashMap; diff --git a/src/librustc/middle/typeck/check/closure.rs b/src/librustc/middle/typeck/check/closure.rs index 0a93b3a5ec7dc..252d0b09c959b 100644 --- a/src/librustc/middle/typeck/check/closure.rs +++ b/src/librustc/middle/typeck/check/closure.rs @@ -14,10 +14,10 @@ use super::check_fn; use super::{Expectation, ExpectCastableToType, ExpectHasType, NoExpectation}; use super::FnCtxt; +use middle::infer; use middle::subst; use middle::ty::{mod, Ty}; use middle::typeck::astconv; -use middle::typeck::infer; use middle::typeck::rscope::RegionScope; use syntax::abi; use syntax::ast; diff --git a/src/librustc/middle/typeck/check/demand.rs b/src/librustc/middle/typeck/check/demand.rs index 1e45d059b849b..df5b4a6c66ab9 100644 --- a/src/librustc/middle/typeck/check/demand.rs +++ b/src/librustc/middle/typeck/check/demand.rs @@ -11,9 +11,9 @@ use middle::ty::{mod, Ty}; use middle::typeck::check::FnCtxt; -use middle::typeck::infer; -use middle::typeck::infer::resolve_type; -use middle::typeck::infer::resolve::try_resolve_tvar_shallow; +use middle::infer; +use middle::infer::resolve_type; +use middle::infer::resolve::try_resolve_tvar_shallow; use std::result::{Err, Ok}; use std::result; diff --git a/src/librustc/middle/typeck/check/method/confirm.rs b/src/librustc/middle/typeck/check/method/confirm.rs index 4e541f120ab8e..291b023121348 100644 --- a/src/librustc/middle/typeck/check/method/confirm.rs +++ b/src/librustc/middle/typeck/check/method/confirm.rs @@ -16,7 +16,8 @@ use middle::ty::{mod, Ty}; use middle::ty::{MethodCall, MethodCallee, MethodObject, MethodOrigin, MethodParam, MethodStatic, MethodTraitObject, MethodTypeParam}; use middle::typeck::check::{mod, FnCtxt, NoPreference, PreferMutLvalue}; -use middle::typeck::infer::{mod, InferCtxt}; +use middle::infer; +use middle::infer::InferCtxt; use middle::ty_fold::HigherRankedFoldable; use syntax::ast; use syntax::codemap::Span; diff --git a/src/librustc/middle/typeck/check/method/mod.rs b/src/librustc/middle/typeck/check/method/mod.rs index 001f5be69a13d..df0df7f47a5da 100644 --- a/src/librustc/middle/typeck/check/method/mod.rs +++ b/src/librustc/middle/typeck/check/method/mod.rs @@ -20,7 +20,7 @@ use middle::typeck::check::{FnCtxt}; use middle::typeck::check::{impl_self_ty}; use middle::typeck::check::vtable; use middle::typeck::check::vtable::select_new_fcx_obligations; -use middle::typeck::infer; +use middle::infer; use util::ppaux::{Repr, UserString}; use std::rc::Rc; diff --git a/src/librustc/middle/typeck/check/method/probe.rs b/src/librustc/middle/typeck/check/method/probe.rs index 689071b77e496..6abc3a7ca57fc 100644 --- a/src/librustc/middle/typeck/check/method/probe.rs +++ b/src/librustc/middle/typeck/check/method/probe.rs @@ -21,8 +21,8 @@ use middle::ty::{MethodObject}; use middle::ty_fold::HigherRankedFoldable; use middle::typeck::check; use middle::typeck::check::{FnCtxt, NoPreference}; -use middle::typeck::infer; -use middle::typeck::infer::InferCtxt; +use middle::infer; +use middle::infer::InferCtxt; use syntax::ast; use syntax::codemap::{Span, DUMMY_SP}; use std::collections::HashSet; diff --git a/src/librustc/middle/typeck/check/mod.rs b/src/librustc/middle/typeck/check/mod.rs index 0033613d9c6ba..02106f25304a6 100644 --- a/src/librustc/middle/typeck/check/mod.rs +++ b/src/librustc/middle/typeck/check/mod.rs @@ -84,6 +84,7 @@ use self::TupleArgumentsFlag::*; use session::Session; use middle::{const_eval, def, traits}; +use middle::infer; use middle::lang_items::IteratorItem; use middle::mem_categorization::{mod, McResult}; use middle::pat_util::{mod, pat_id_map}; @@ -98,7 +99,7 @@ use middle::ty_fold::TypeFolder; use middle::typeck::astconv::{mod, ast_region_to_region, ast_ty_to_ty, AstConv}; use middle::typeck::check::_match::pat_ctxt; use middle::typeck::rscope::RegionScope; -use middle::typeck::{CrateCtxt, infer, lookup_def_ccx, no_params, require_same_types}; +use middle::typeck::{CrateCtxt, lookup_def_ccx, no_params, require_same_types}; use middle::typeck::TypeAndSubsts; use middle::lang_items::TypeIdLangItem; use lint; diff --git a/src/librustc/middle/typeck/check/regionck.rs b/src/librustc/middle/typeck/check/regionck.rs index 1c938bc1e1f30..b466e2589456a 100644 --- a/src/librustc/middle/typeck/check/regionck.rs +++ b/src/librustc/middle/typeck/check/regionck.rs @@ -124,9 +124,9 @@ use middle::typeck::astconv::AstConv; use middle::typeck::check::FnCtxt; use middle::typeck::check::regionmanip; use middle::typeck::check::vtable; -use middle::typeck::infer::resolve_and_force_all_but_regions; -use middle::typeck::infer::resolve_type; -use middle::typeck::infer; +use middle::infer::resolve_and_force_all_but_regions; +use middle::infer::resolve_type; +use middle::infer; use middle::pat_util; use util::nodemap::{DefIdMap, NodeMap, FnvHashMap}; use util::ppaux::{ty_to_string, Repr}; diff --git a/src/librustc/middle/typeck/check/vtable.rs b/src/librustc/middle/typeck/check/vtable.rs index 84cb74b4de248..5d6093c21e6a4 100644 --- a/src/librustc/middle/typeck/check/vtable.rs +++ b/src/librustc/middle/typeck/check/vtable.rs @@ -17,7 +17,7 @@ use middle::traits::{ObligationCause}; use middle::ty::{mod, Ty}; use middle::typeck::check::{FnCtxt, structurally_resolved_type}; -use middle::typeck::infer; +use middle::infer; use std::rc::Rc; use syntax::ast; use syntax::codemap::Span; diff --git a/src/librustc/middle/typeck/check/writeback.rs b/src/librustc/middle/typeck/check/writeback.rs index 8205e83fb052b..e2eee66387fe6 100644 --- a/src/librustc/middle/typeck/check/writeback.rs +++ b/src/librustc/middle/typeck/check/writeback.rs @@ -19,9 +19,9 @@ use middle::ty::{mod, Ty, MethodCall, MethodCallee}; use middle::ty_fold::{TypeFolder,TypeFoldable}; use middle::typeck::astconv::AstConv; use middle::typeck::check::FnCtxt; -use middle::typeck::infer::{force_all, resolve_all, resolve_region}; -use middle::typeck::infer::resolve_type; -use middle::typeck::infer; +use middle::infer::{force_all, resolve_all, resolve_region}; +use middle::infer::resolve_type; +use middle::infer; use middle::typeck::write_substs_to_tcx; use middle::typeck::write_ty_to_tcx; use util::ppaux::Repr; diff --git a/src/librustc/middle/typeck/coherence/mod.rs b/src/librustc/middle/typeck/coherence/mod.rs index 758608b79c2cb..4e1bcec5eead2 100644 --- a/src/librustc/middle/typeck/coherence/mod.rs +++ b/src/librustc/middle/typeck/coherence/mod.rs @@ -32,9 +32,9 @@ use middle::ty::type_is_ty_var; use middle::subst::Subst; use middle::ty; use middle::typeck::CrateCtxt; -use middle::typeck::infer::combine::Combine; -use middle::typeck::infer::InferCtxt; -use middle::typeck::infer::{new_infer_ctxt, resolve_ivar, resolve_type}; +use middle::infer::combine::Combine; +use middle::infer::InferCtxt; +use middle::infer::{new_infer_ctxt, resolve_ivar, resolve_type}; use std::collections::{HashSet}; use std::cell::RefCell; use std::rc::Rc; diff --git a/src/librustc/middle/typeck/coherence/overlap.rs b/src/librustc/middle/typeck/coherence/overlap.rs index 9f10a58f45852..0e74d4578d95b 100644 --- a/src/librustc/middle/typeck/coherence/overlap.rs +++ b/src/librustc/middle/typeck/coherence/overlap.rs @@ -13,8 +13,7 @@ use middle::traits; use middle::ty; -use middle::typeck::infer::{new_infer_ctxt}; -use middle::typeck::infer; +use middle::infer::{mod, new_infer_ctxt}; use syntax::ast::{DefId}; use syntax::ast::{LOCAL_CRATE}; use syntax::ast; diff --git a/src/librustc/middle/typeck/collect.rs b/src/librustc/middle/typeck/collect.rs index 6e989dd73dbef..5e1dbe16e3c0f 100644 --- a/src/librustc/middle/typeck/collect.rs +++ b/src/librustc/middle/typeck/collect.rs @@ -46,7 +46,7 @@ use middle::ty_fold::TypeFolder; use middle::typeck::astconv::{AstConv, ty_of_arg}; use middle::typeck::astconv::{ast_ty_to_ty, ast_region_to_region}; use middle::typeck::astconv; -use middle::typeck::infer; +use middle::infer; use middle::typeck::rscope::*; use middle::typeck::{CrateCtxt, lookup_def_tcx, no_params, write_ty_to_tcx}; use middle::typeck; diff --git a/src/librustc/middle/typeck/mod.rs b/src/librustc/middle/typeck/mod.rs index a95849e083d10..dbec92d02dfbd 100644 --- a/src/librustc/middle/typeck/mod.rs +++ b/src/librustc/middle/typeck/mod.rs @@ -63,6 +63,7 @@ independently: use middle::def; use middle::resolve; +use middle::infer; use middle::subst; use middle::subst::VecPerParamSpace; use middle::ty::{mod, Ty}; @@ -78,7 +79,6 @@ use syntax::{ast, ast_map, abi}; pub mod check; pub mod rscope; pub mod astconv; -pub mod infer; pub mod collect; pub mod coherence; pub mod variance; diff --git a/src/librustc_trans/save/mod.rs b/src/librustc_trans/save/mod.rs index 481ee679dbd62..59bbeb2dbc4f2 100644 --- a/src/librustc_trans/save/mod.rs +++ b/src/librustc_trans/save/mod.rs @@ -30,7 +30,7 @@ use driver::driver::CrateAnalysis; use session::Session; -use middle::{def, typeck}; +use middle::def; use middle::ty::{mod, Ty}; use std::cell::Cell; diff --git a/src/librustc_trans/test.rs b/src/librustc_trans/test.rs index 41fbe85576933..9244e6909e8a4 100644 --- a/src/librustc_trans/test.rs +++ b/src/librustc_trans/test.rs @@ -21,10 +21,10 @@ use middle::stability; use middle::subst; use middle::subst::Subst; use middle::ty::{mod, Ty}; -use middle::typeck::infer::combine::Combine; -use middle::typeck::infer; -use middle::typeck::infer::lub::Lub; -use middle::typeck::infer::glb::Glb; +use middle::infer::combine::Combine; +use middle::infer; +use middle::infer::lub::Lub; +use middle::infer::glb::Glb; use session::{mod,config}; use syntax::{abi, ast, ast_map, ast_util}; use syntax::codemap; diff --git a/src/librustc_trans/trans/common.rs b/src/librustc_trans/trans/common.rs index 6bd86cf5aa4a2..be3780facaa10 100644 --- a/src/librustc_trans/trans/common.rs +++ b/src/librustc_trans/trans/common.rs @@ -19,6 +19,7 @@ use llvm; use llvm::{ValueRef, BasicBlockRef, BuilderRef, ContextRef}; use llvm::{True, False, Bool}; use middle::def; +use middle::infer; use middle::lang_items::LangItem; use middle::mem_categorization as mc; use middle::region; @@ -36,7 +37,6 @@ use middle::traits; use middle::ty::{mod, Ty}; use middle::ty_fold; use middle::ty_fold::TypeFoldable; -use middle::typeck::infer; use util::ppaux::Repr; use util::nodemap::{DefIdMap, FnvHashMap, NodeMap}; diff --git a/src/librustc_trans/trans/expr.rs b/src/librustc_trans/trans/expr.rs index f538a73329b5d..d130dc0a55b1c 100644 --- a/src/librustc_trans/trans/expr.rs +++ b/src/librustc_trans/trans/expr.rs @@ -41,7 +41,6 @@ use middle::def; use middle::mem_categorization::Typer; use middle::subst::{mod, Subst}; use trans::{_match, adt, asm, base, callee, closure, consts, controlflow}; -use trans::{debuginfo, glue, machine, meth, inline, tvec, type_of}; use trans::base::*; use trans::build::*; use trans::cleanup::{mod, CleanupMethods}; From adda9c152096459bd9925114e7c742619939ac49 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 25 Nov 2014 19:48:49 -0500 Subject: [PATCH 59/83] Remove dependency on typeck from lint. --- src/librustc/lint/builtin.rs | 4 +--- src/librustc/lint/context.rs | 40 ------------------------------------ 2 files changed, 1 insertion(+), 43 deletions(-) diff --git a/src/librustc/lint/builtin.rs b/src/librustc/lint/builtin.rs index 8469f477edb34..10c0ae6d37417 100644 --- a/src/librustc/lint/builtin.rs +++ b/src/librustc/lint/builtin.rs @@ -28,9 +28,7 @@ use self::MethodContext::*; use metadata::csearch; use middle::def::*; -use middle::infer; use middle::ty::{mod, Ty}; -use middle::typeck::astconv::ast_ty_to_ty; use middle::{def, pat_util, stability}; use middle::const_eval::{eval_const_expr_partial, const_int, const_uint}; use util::ppaux::{ty_to_string}; @@ -84,7 +82,7 @@ impl LintPass for UnusedCasts { fn check_expr(&mut self, cx: &Context, e: &ast::Expr) { if let ast::ExprCast(ref expr, ref ty) = e.node { - let t_t = ast_ty_to_ty(cx, &infer::new_infer_ctxt(cx.tcx), &**ty); + let t_t = ty::expr_ty(cx.tcx, e); if ty::expr_ty(cx.tcx, &**expr) == t_t { cx.span_lint(UNUSED_TYPECASTS, ty.span, "unnecessary type cast"); } diff --git a/src/librustc/lint/context.rs b/src/librustc/lint/context.rs index 8014673f759ac..442d3aab92dba 100644 --- a/src/librustc/lint/context.rs +++ b/src/librustc/lint/context.rs @@ -25,18 +25,14 @@ //! for all lint attributes. use self::TargetLint::*; -use middle::infer; use middle::privacy::ExportedItems; -use middle::subst; use middle::ty::{mod, Ty}; -use middle::typeck::astconv::AstConv; use session::{early_error, Session}; use lint::{Level, LevelSource, Lint, LintId, LintArray, LintPass, LintPassObject}; use lint::{Default, CommandLine, Node, Allow, Warn, Deny, Forbid}; use lint::builtin; use util::nodemap::FnvHashMap; -use std::rc::Rc; use std::cell::RefCell; use std::tuple::Tuple2; use std::mem; @@ -541,42 +537,6 @@ impl<'a, 'tcx> Context<'a, 'tcx> { } } -impl<'a, 'tcx> AstConv<'tcx> for Context<'a, 'tcx>{ - fn tcx<'a>(&'a self) -> &'a ty::ctxt<'tcx> { self.tcx } - - fn get_item_ty(&self, id: ast::DefId) -> ty::Polytype<'tcx> { - ty::lookup_item_type(self.tcx, id) - } - - fn get_trait_def(&self, id: ast::DefId) -> Rc> { - ty::lookup_trait_def(self.tcx, id) - } - - fn ty_infer(&self, _span: Span) -> Ty<'tcx> { - infer::new_infer_ctxt(self.tcx).next_ty_var() - } - - fn associated_types_of_trait_are_valid(&self, _: Ty<'tcx>, _: ast::DefId) - -> bool { - // FIXME(pcwalton): This is wrong. - true - } - - fn associated_type_binding(&self, - _: Span, - _: Option>, - trait_id: ast::DefId, - associated_type_id: ast::DefId) - -> Ty<'tcx> { - // FIXME(pcwalton): This is wrong. - let trait_def = self.get_trait_def(trait_id); - let index = ty::associated_type_parameter_index(self.tcx, - &*trait_def, - associated_type_id); - ty::mk_param(self.tcx, subst::TypeSpace, index, associated_type_id) - } -} - impl<'a, 'tcx, 'v> Visitor<'v> for Context<'a, 'tcx> { fn visit_item(&mut self, it: &ast::Item) { self.with_lint_attrs(it.attrs.as_slice(), |cx| { From 9aeaaab3347657375e4d20855f8faebb2ab128da Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 25 Nov 2014 20:15:58 -0500 Subject: [PATCH 60/83] Remove dependence on typeck from ppaux. --- .../middle/infer/region_inference/doc.rs | 2 +- .../middle/typeck/check/regionmanip.rs | 19 +++++++++++++++++++ src/librustc/util/ppaux.rs | 19 ------------------- 3 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/librustc/middle/infer/region_inference/doc.rs b/src/librustc/middle/infer/region_inference/doc.rs index b4eac4c002677..686174b73060b 100644 --- a/src/librustc/middle/infer/region_inference/doc.rs +++ b/src/librustc/middle/infer/region_inference/doc.rs @@ -371,4 +371,4 @@ //! ### Skolemization //! //! For a discussion on skolemization and higher-ranked subtyping, please -//! see the module `middle::typeck::infer::higher_ranked::doc`. +//! see the module `middle::infer::higher_ranked::doc`. diff --git a/src/librustc/middle/typeck/check/regionmanip.rs b/src/librustc/middle/typeck/check/regionmanip.rs index 55214618aa90b..92dfd8b5f56d2 100644 --- a/src/librustc/middle/typeck/check/regionmanip.rs +++ b/src/librustc/middle/typeck/check/regionmanip.rs @@ -380,3 +380,22 @@ impl<'a, 'tcx> Wf<'a, 'tcx> { } } } + +impl<'tcx> Repr<'tcx> for WfConstraint<'tcx> { + fn repr(&self, tcx: &ty::ctxt) -> String { + match *self { + RegionSubRegionConstraint(_, r_a, r_b) => { + format!("RegionSubRegionConstraint({}, {})", + r_a.repr(tcx), + r_b.repr(tcx)) + } + + RegionSubParamConstraint(_, r, p) => { + format!("RegionSubParamConstraint({}, {})", + r.repr(tcx), + p.repr(tcx)) + } + } + } +} + diff --git a/src/librustc/util/ppaux.rs b/src/librustc/util/ppaux.rs index 1dfedd4c85c06..1283e89c29d0c 100644 --- a/src/librustc/util/ppaux.rs +++ b/src/librustc/util/ppaux.rs @@ -23,7 +23,6 @@ use middle::ty::{ty_param, ty_ptr, ty_rptr, ty_tup, ty_open}; use middle::ty::{ty_unboxed_closure}; use middle::ty::{ty_uniq, ty_trait, ty_int, ty_uint, ty_infer}; use middle::ty; -use middle::typeck::check::regionmanip; use std::rc::Rc; use syntax::abi; @@ -1292,24 +1291,6 @@ impl<'tcx> Repr<'tcx> for ty::ExplicitSelfCategory { } } -impl<'tcx> Repr<'tcx> for regionmanip::WfConstraint<'tcx> { - fn repr(&self, tcx: &ctxt) -> String { - match *self { - regionmanip::RegionSubRegionConstraint(_, r_a, r_b) => { - format!("RegionSubRegionConstraint({}, {})", - r_a.repr(tcx), - r_b.repr(tcx)) - } - - regionmanip::RegionSubParamConstraint(_, r, p) => { - format!("RegionSubParamConstraint({}, {})", - r.repr(tcx), - p.repr(tcx)) - } - } - } -} - impl<'tcx> UserString<'tcx> for ParamTy { fn user_string(&self, tcx: &ctxt) -> String { let id = self.idx; From 00ca861f9dfcbb5bc9f97d8463885fb04ef4812d Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 25 Nov 2014 20:16:28 -0500 Subject: [PATCH 61/83] Remove "dependence" on typeck from comment in substs. --- src/librustc/middle/subst.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/librustc/middle/subst.rs b/src/librustc/middle/subst.rs index 0a3e6c20316be..21f57a9d57388 100644 --- a/src/librustc/middle/subst.rs +++ b/src/librustc/middle/subst.rs @@ -589,8 +589,7 @@ impl<'a, 'tcx> TypeFolder<'tcx> for SubstFolder<'a, 'tcx> { // type declarations and other outer declarations, not those // bound in *fn types*. Region substitution of the bound // regions that appear in a function signature is done using - // the specialized routine - // `middle::typeck::check::regionmanip::replace_late_regions_in_fn_sig()`. + // the specialized routine `ty::replace_late_regions()`. match r { ty::ReEarlyBound(_, space, i, region_name) => { match self.substs.regions { From 55470abe720114bd93e55bc7fa79d454186e9f02 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 26 Nov 2014 04:52:02 -0500 Subject: [PATCH 62/83] Remove one dependence on typeck from const_eval. --- src/librustc/lib.rs | 1 + src/librustc/middle/astconv_util.rs | 89 +++++++++++++++++++++++++ src/librustc/middle/const_eval.rs | 14 +--- src/librustc/middle/typeck/astconv.rs | 70 +------------------ src/librustc/middle/typeck/check/mod.rs | 44 ++++++------ 5 files changed, 119 insertions(+), 99 deletions(-) create mode 100644 src/librustc/middle/astconv_util.rs diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs index d8da4df08bcc0..5637f5d175533 100644 --- a/src/librustc/lib.rs +++ b/src/librustc/lib.rs @@ -59,6 +59,7 @@ pub mod back { } pub mod middle { + pub mod astconv_util; pub mod astencode; pub mod borrowck; pub mod cfg; diff --git a/src/librustc/middle/astconv_util.rs b/src/librustc/middle/astconv_util.rs new file mode 100644 index 0000000000000..6b90bcd60e753 --- /dev/null +++ b/src/librustc/middle/astconv_util.rs @@ -0,0 +1,89 @@ +// Copyright 2012-2014 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. + +/*! + * This module contains a simple utility routine + * used by both `typeck` and `const_eval`. + * Almost certainly this could (and should) be refactored out of existence. + */ + +use middle::def; +use middle::ty::{mod, Ty}; +use syntax::ast; +use util::ppaux::Repr; + +pub const NO_REGIONS: uint = 1; +pub const NO_TPS: uint = 2; + +pub fn check_path_args(tcx: &ty::ctxt, + path: &ast::Path, + flags: uint) { + if (flags & NO_TPS) != 0u { + if path.segments.iter().any(|s| s.parameters.has_types()) { + span_err!(tcx.sess, path.span, E0109, + "type parameters are not allowed on this type"); + } + } + + if (flags & NO_REGIONS) != 0u { + if path.segments.iter().any(|s| s.parameters.has_lifetimes()) { + span_err!(tcx.sess, path.span, E0110, + "region parameters are not allowed on this type"); + } + } +} + +pub fn ast_ty_to_prim_ty<'tcx>(tcx: &ty::ctxt<'tcx>, ast_ty: &ast::Ty) + -> Option> { + match ast_ty.node { + ast::TyPath(ref path, id) => { + let a_def = match tcx.def_map.borrow().get(&id) { + None => { + tcx.sess.span_bug(ast_ty.span, + format!("unbound path {}", + path.repr(tcx)).as_slice()) + } + Some(&d) => d + }; + match a_def { + def::DefPrimTy(nty) => { + match nty { + ast::TyBool => { + check_path_args(tcx, path, NO_TPS | NO_REGIONS); + Some(ty::mk_bool()) + } + ast::TyChar => { + check_path_args(tcx, path, NO_TPS | NO_REGIONS); + Some(ty::mk_char()) + } + ast::TyInt(it) => { + check_path_args(tcx, path, NO_TPS | NO_REGIONS); + Some(ty::mk_mach_int(it)) + } + ast::TyUint(uit) => { + check_path_args(tcx, path, NO_TPS | NO_REGIONS); + Some(ty::mk_mach_uint(uit)) + } + ast::TyFloat(ft) => { + check_path_args(tcx, path, NO_TPS | NO_REGIONS); + Some(ty::mk_mach_float(ft)) + } + ast::TyStr => { + Some(ty::mk_str(tcx)) + } + } + } + _ => None + } + } + _ => None + } +} + diff --git a/src/librustc/middle/const_eval.rs b/src/librustc/middle/const_eval.rs index d5a292b9f09c6..43726f55bb989 100644 --- a/src/librustc/middle/const_eval.rs +++ b/src/librustc/middle/const_eval.rs @@ -17,8 +17,8 @@ pub use self::constness::*; use metadata::csearch; use middle::{astencode, def}; use middle::pat_util::def_to_path; -use middle::ty::{mod, Ty}; -use middle::typeck::{astconv, check}; +use middle::ty::{mod}; +use middle::astconv_util::{ast_ty_to_prim_ty}; use util::nodemap::DefIdMap; use syntax::ast::{mod, Expr}; @@ -277,14 +277,6 @@ impl<'a, 'tcx> ConstEvalVisitor<'a, 'tcx> { } impl<'a, 'tcx, 'v> Visitor<'v> for ConstEvalVisitor<'a, 'tcx> { - fn visit_ty(&mut self, t: &ast::Ty) { - if let ast::TyFixedLengthVec(_, ref expr) = t.node { - check::check_const_in_type(self.tcx, &**expr, ty::mk_uint()); - } - - visit::walk_ty(self, t); - } - fn visit_expr_post(&mut self, e: &Expr) { self.classify(e); } @@ -504,7 +496,7 @@ pub fn eval_const_expr_partial(tcx: &ty::ctxt, e: &Expr) -> Result( } } -pub const NO_REGIONS: uint = 1; -pub const NO_TPS: uint = 2; - -fn check_path_args(tcx: &ty::ctxt, - path: &ast::Path, - flags: uint) { - if (flags & NO_TPS) != 0u { - if path.segments.iter().any(|s| s.parameters.has_types()) { - span_err!(tcx.sess, path.span, E0109, - "type parameters are not allowed on this type"); - } - } - - if (flags & NO_REGIONS) != 0u { - if path.segments.iter().any(|s| s.parameters.has_lifetimes()) { - span_err!(tcx.sess, path.span, E0110, - "region parameters are not allowed on this type"); - } - } -} - -pub fn ast_ty_to_prim_ty<'tcx>(tcx: &ty::ctxt<'tcx>, ast_ty: &ast::Ty) - -> Option> { - match ast_ty.node { - ast::TyPath(ref path, id) => { - let a_def = match tcx.def_map.borrow().get(&id) { - None => { - tcx.sess.span_bug(ast_ty.span, - format!("unbound path {}", - path.repr(tcx)).as_slice()) - } - Some(&d) => d - }; - match a_def { - def::DefPrimTy(nty) => { - match nty { - ast::TyBool => { - check_path_args(tcx, path, NO_TPS | NO_REGIONS); - Some(ty::mk_bool()) - } - ast::TyChar => { - check_path_args(tcx, path, NO_TPS | NO_REGIONS); - Some(ty::mk_char()) - } - ast::TyInt(it) => { - check_path_args(tcx, path, NO_TPS | NO_REGIONS); - Some(ty::mk_mach_int(it)) - } - ast::TyUint(uit) => { - check_path_args(tcx, path, NO_TPS | NO_REGIONS); - Some(ty::mk_mach_uint(uit)) - } - ast::TyFloat(ft) => { - check_path_args(tcx, path, NO_TPS | NO_REGIONS); - Some(ty::mk_mach_float(ft)) - } - ast::TyStr => { - Some(ty::mk_str(tcx)) - } - } - } - _ => None - } - } - _ => None - } -} - /// Converts the given AST type to a built-in type. A "built-in type" is, at /// present, either a core numeric type, a string, or `Box`. pub fn ast_ty_to_builtin_ty<'tcx, AC: AstConv<'tcx>, RS: RegionScope>( diff --git a/src/librustc/middle/typeck/check/mod.rs b/src/librustc/middle/typeck/check/mod.rs index 02106f25304a6..cacccbda53ca7 100644 --- a/src/librustc/middle/typeck/check/mod.rs +++ b/src/librustc/middle/typeck/check/mod.rs @@ -361,6 +361,17 @@ impl<'a, 'tcx, 'v> Visitor<'v> for CheckItemTypesVisitor<'a, 'tcx> { check_item(self.ccx, i); visit::walk_item(self, i); } + + fn visit_ty(&mut self, t: &ast::Ty) { + match t.node { + ast::TyFixedLengthVec(_, ref expr) => { + check_const_in_type(self.ccx, &**expr, ty::mk_uint()); + } + _ => {} + } + + visit::walk_ty(self, t); + } } pub fn check_item_types(ccx: &CrateCtxt) { @@ -4672,25 +4683,18 @@ fn check_block_with_expected<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, /// Checks a constant appearing in a type. At the moment this is just the /// length expression in a fixed-length vector, but someday it might be /// extended to type-level numeric literals. -pub fn check_const_in_type<'tcx>(tcx: &ty::ctxt<'tcx>, - expr: &ast::Expr, - expected_type: Ty<'tcx>) { - // Synthesize a crate context. The trait map is not needed here (though I - // imagine it will be if we have associated statics --pcwalton), so we - // leave it blank. - let ccx = CrateCtxt { - trait_map: NodeMap::new(), - tcx: tcx, - }; - let inh = static_inherited_fields(&ccx); - let fcx = blank_fn_ctxt(&ccx, &inh, ty::FnConverging(expected_type), expr.id); +fn check_const_in_type<'a,'tcx>(ccx: &'a CrateCtxt<'a,'tcx>, + expr: &ast::Expr, + expected_type: Ty<'tcx>) { + let inh = static_inherited_fields(ccx); + let fcx = blank_fn_ctxt(ccx, &inh, ty::FnConverging(expected_type), expr.id); check_const_with_ty(&fcx, expr.span, expr, expected_type); } -pub fn check_const(ccx: &CrateCtxt, - sp: Span, - e: &ast::Expr, - id: ast::NodeId) { +fn check_const(ccx: &CrateCtxt, + sp: Span, + e: &ast::Expr, + id: ast::NodeId) { let inh = static_inherited_fields(ccx); let rty = ty::node_id_to_type(ccx.tcx, id); let fcx = blank_fn_ctxt(ccx, &inh, ty::FnConverging(rty), e.id); @@ -4698,10 +4702,10 @@ pub fn check_const(ccx: &CrateCtxt, check_const_with_ty(&fcx, sp, e, declty); } -pub fn check_const_with_ty<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, - _: Span, - e: &ast::Expr, - declty: Ty<'tcx>) { +fn check_const_with_ty<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, + _: Span, + e: &ast::Expr, + declty: Ty<'tcx>) { // Gather locals in statics (because of block expressions). // This is technically unnecessary because locals in static items are forbidden, // but prevents type checking from blowing up before const checking can properly From 1e112e94c3cd71f792e59318f7f7197999e30cf6 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 26 Nov 2014 05:48:57 -0500 Subject: [PATCH 63/83] Move `typeck` logically in the module tree out to the root and clamp down on its exports. Remove some dead code that is revealed. --- src/librustc/lib.rs | 4 ++- src/librustc/middle/ty.rs | 26 ++++++++++++++++ src/librustc/middle/typeck/astconv.rs | 9 +++--- src/librustc/middle/typeck/check/_match.rs | 6 ++-- src/librustc/middle/typeck/check/closure.rs | 4 +-- src/librustc/middle/typeck/check/demand.rs | 8 +---- .../middle/typeck/check/method/confirm.rs | 2 +- .../middle/typeck/check/method/mod.rs | 10 +++---- .../middle/typeck/check/method/probe.rs | 4 +-- src/librustc/middle/typeck/check/mod.rs | 27 ++++------------- src/librustc/middle/typeck/check/regionck.rs | 8 ++--- src/librustc/middle/typeck/check/vtable.rs | 2 +- src/librustc/middle/typeck/check/wf.rs | 6 ++-- src/librustc/middle/typeck/check/writeback.rs | 8 ++--- src/librustc/middle/typeck/coherence/mod.rs | 26 ++-------------- src/librustc/middle/typeck/collect.rs | 12 ++++---- src/librustc/middle/typeck/mod.rs | 30 +++++++++---------- src/librustc_trans/driver/driver.rs | 3 +- src/librustc_trans/trans/callee.rs | 3 +- 19 files changed, 90 insertions(+), 108 deletions(-) diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs index 5637f5d175533..aa31144182dc2 100644 --- a/src/librustc/lib.rs +++ b/src/librustc/lib.rs @@ -95,10 +95,12 @@ pub mod middle { pub mod traits; pub mod ty; pub mod ty_fold; - pub mod typeck; pub mod weak_lang_items; } +#[path="middle/typeck/mod.rs"] +pub mod typeck; + pub mod metadata; pub mod session; diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index fe8121375ad05..2327bc957e617 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -6192,3 +6192,29 @@ impl<'tcx> Repr<'tcx> for vtable_origin<'tcx> { } } } + +pub fn make_substs_for_receiver_types<'tcx>(tcx: &ty::ctxt<'tcx>, + trait_ref: &ty::TraitRef<'tcx>, + method: &ty::Method<'tcx>) + -> subst::Substs<'tcx> +{ + /*! + * Substitutes the values for the receiver's type parameters + * that are found in method, leaving the method's type parameters + * intact. + */ + + let meth_tps: Vec = + method.generics.types.get_slice(subst::FnSpace) + .iter() + .map(|def| ty::mk_param_from_def(tcx, def)) + .collect(); + let meth_regions: Vec = + method.generics.regions.get_slice(subst::FnSpace) + .iter() + .map(|def| ty::ReEarlyBound(def.def_id.node, def.space, + def.index, def.name)) + .collect(); + trait_ref.substs.clone().with_method(meth_tps, meth_regions) +} + diff --git a/src/librustc/middle/typeck/astconv.rs b/src/librustc/middle/typeck/astconv.rs index abdf66eeb3b16..f35b4d6602298 100644 --- a/src/librustc/middle/typeck/astconv.rs +++ b/src/librustc/middle/typeck/astconv.rs @@ -54,11 +54,10 @@ use middle::resolve_lifetime as rl; use middle::subst::{FnSpace, TypeSpace, AssocSpace, SelfSpace, Subst, Substs}; use middle::subst::{VecPerParamSpace}; use middle::ty::{mod, Ty}; -use middle::typeck::lookup_def_tcx; -use middle::typeck::rscope::{UnelidableRscope, RegionScope, SpecificRscope, - ShiftedRscope, BindingRscope}; -use middle::typeck::rscope; -use middle::typeck::TypeAndSubsts; +use typeck::lookup_def_tcx; +use typeck::rscope::{mod, UnelidableRscope, RegionScope, SpecificRscope, + ShiftedRscope, BindingRscope}; +use typeck::TypeAndSubsts; use util::common::ErrorReported; use util::nodemap::DefIdMap; use util::ppaux::{mod, Repr, UserString}; diff --git a/src/librustc/middle/typeck/check/_match.rs b/src/librustc/middle/typeck/check/_match.rs index 6af36225364fb..acc06cbd1cc87 100644 --- a/src/librustc/middle/typeck/check/_match.rs +++ b/src/librustc/middle/typeck/check/_match.rs @@ -12,10 +12,10 @@ use middle::def; use middle::pat_util::{PatIdMap, pat_id_map, pat_is_binding, pat_is_const}; use middle::subst::{Subst, Substs}; use middle::ty::{mod, Ty}; -use middle::typeck::check::{check_expr, check_expr_has_type, demand, FnCtxt}; -use middle::typeck::check::{instantiate_path, structurally_resolved_type, valid_range_bounds}; +use typeck::check::{check_expr, check_expr_has_type, demand, FnCtxt}; +use typeck::check::{instantiate_path, structurally_resolved_type, valid_range_bounds}; use middle::infer::{mod, resolve}; -use middle::typeck::require_same_types; +use typeck::require_same_types; use util::nodemap::FnvHashMap; use std::cmp; diff --git a/src/librustc/middle/typeck/check/closure.rs b/src/librustc/middle/typeck/check/closure.rs index 252d0b09c959b..37059dec30f05 100644 --- a/src/librustc/middle/typeck/check/closure.rs +++ b/src/librustc/middle/typeck/check/closure.rs @@ -17,8 +17,8 @@ use super::FnCtxt; use middle::infer; use middle::subst; use middle::ty::{mod, Ty}; -use middle::typeck::astconv; -use middle::typeck::rscope::RegionScope; +use typeck::astconv; +use typeck::rscope::RegionScope; use syntax::abi; use syntax::ast; use syntax::ast_util; diff --git a/src/librustc/middle/typeck/check/demand.rs b/src/librustc/middle/typeck/check/demand.rs index df5b4a6c66ab9..694ed890d76e1 100644 --- a/src/librustc/middle/typeck/check/demand.rs +++ b/src/librustc/middle/typeck/check/demand.rs @@ -10,7 +10,7 @@ use middle::ty::{mod, Ty}; -use middle::typeck::check::FnCtxt; +use typeck::check::FnCtxt; use middle::infer; use middle::infer::resolve_type; use middle::infer::resolve::try_resolve_tvar_shallow; @@ -29,12 +29,6 @@ pub fn suptype<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, sp: Span, |sp, e, a, s| { fcx.report_mismatched_types(sp, e, a, s) }) } -pub fn subtype<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, sp: Span, - expected: Ty<'tcx>, actual: Ty<'tcx>) { - suptype_with_fn(fcx, sp, true, actual, expected, - |sp, a, e, s| { fcx.report_mismatched_types(sp, e, a, s) }) -} - pub fn suptype_with_fn<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, sp: Span, b_is_expected: bool, diff --git a/src/librustc/middle/typeck/check/method/confirm.rs b/src/librustc/middle/typeck/check/method/confirm.rs index 291b023121348..078f4b64ac42b 100644 --- a/src/librustc/middle/typeck/check/method/confirm.rs +++ b/src/librustc/middle/typeck/check/method/confirm.rs @@ -15,7 +15,7 @@ use middle::traits; use middle::ty::{mod, Ty}; use middle::ty::{MethodCall, MethodCallee, MethodObject, MethodOrigin, MethodParam, MethodStatic, MethodTraitObject, MethodTypeParam}; -use middle::typeck::check::{mod, FnCtxt, NoPreference, PreferMutLvalue}; +use typeck::check::{mod, FnCtxt, NoPreference, PreferMutLvalue}; use middle::infer; use middle::infer::InferCtxt; use middle::ty_fold::HigherRankedFoldable; diff --git a/src/librustc/middle/typeck/check/method/mod.rs b/src/librustc/middle/typeck/check/method/mod.rs index df0df7f47a5da..117eb385eff48 100644 --- a/src/librustc/middle/typeck/check/method/mod.rs +++ b/src/librustc/middle/typeck/check/method/mod.rs @@ -15,11 +15,11 @@ use middle::subst::{Subst}; use middle::traits; use middle::ty::*; use middle::ty; -use middle::typeck::astconv::AstConv; -use middle::typeck::check::{FnCtxt}; -use middle::typeck::check::{impl_self_ty}; -use middle::typeck::check::vtable; -use middle::typeck::check::vtable::select_new_fcx_obligations; +use typeck::astconv::AstConv; +use typeck::check::{FnCtxt}; +use typeck::check::{impl_self_ty}; +use typeck::check::vtable; +use typeck::check::vtable::select_new_fcx_obligations; use middle::infer; use util::ppaux::{Repr, UserString}; diff --git a/src/librustc/middle/typeck/check/method/probe.rs b/src/librustc/middle/typeck/check/method/probe.rs index 6abc3a7ca57fc..73e7c5c663500 100644 --- a/src/librustc/middle/typeck/check/method/probe.rs +++ b/src/librustc/middle/typeck/check/method/probe.rs @@ -19,8 +19,8 @@ use middle::traits; use middle::ty::{mod, Ty}; use middle::ty::{MethodObject}; use middle::ty_fold::HigherRankedFoldable; -use middle::typeck::check; -use middle::typeck::check::{FnCtxt, NoPreference}; +use typeck::check; +use typeck::check::{FnCtxt, NoPreference}; use middle::infer; use middle::infer::InferCtxt; use syntax::ast; diff --git a/src/librustc/middle/typeck/check/mod.rs b/src/librustc/middle/typeck/check/mod.rs index cacccbda53ca7..e2ec9ba1ccb42 100644 --- a/src/librustc/middle/typeck/check/mod.rs +++ b/src/librustc/middle/typeck/check/mod.rs @@ -96,11 +96,11 @@ use middle::ty::{mod, Ty}; use middle::ty::liberate_late_bound_regions; use middle::ty::{MethodCall, MethodCallee, MethodMap, ObjectCastMap}; use middle::ty_fold::TypeFolder; -use middle::typeck::astconv::{mod, ast_region_to_region, ast_ty_to_ty, AstConv}; -use middle::typeck::check::_match::pat_ctxt; -use middle::typeck::rscope::RegionScope; -use middle::typeck::{CrateCtxt, lookup_def_ccx, no_params, require_same_types}; -use middle::typeck::TypeAndSubsts; +use typeck::astconv::{mod, ast_region_to_region, ast_ty_to_ty, AstConv}; +use typeck::check::_match::pat_ctxt; +use typeck::rscope::RegionScope; +use typeck::{CrateCtxt, lookup_def_ccx, no_params, require_same_types}; +use typeck::TypeAndSubsts; use middle::lang_items::TypeIdLangItem; use lint; use util::common::{block_query, indenter, loop_query}; @@ -1873,13 +1873,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - /// Fetch type of `expr` after applying adjustments that have been recorded in the fcx. - pub fn expr_ty_adjusted(&self, expr: &ast::Expr) -> Ty<'tcx> { - let adjustments = self.inh.adjustments.borrow(); - let adjustment = adjustments.get(&expr.id); - self.adjust_expr_ty(expr, adjustment) - } - /// Apply `adjustment` to the type of `expr` pub fn adjust_expr_ty(&self, expr: &ast::Expr, @@ -1932,16 +1925,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { infer::mk_subty(self.infcx(), a_is_expected, origin, sub, sup) } - pub fn can_mk_subty(&self, sub: Ty<'tcx>, sup: Ty<'tcx>) - -> Result<(), ty::type_err<'tcx>> { - infer::can_mk_subty(self.infcx(), sub, sup) - } - - pub fn can_mk_eqty(&self, sub: Ty<'tcx>, sup: Ty<'tcx>) - -> Result<(), ty::type_err<'tcx>> { - infer::can_mk_eqty(self.infcx(), sub, sup) - } - pub fn mk_assignty(&self, expr: &ast::Expr, sub: Ty<'tcx>, diff --git a/src/librustc/middle/typeck/check/regionck.rs b/src/librustc/middle/typeck/check/regionck.rs index b466e2589456a..bb5f6a4cf4872 100644 --- a/src/librustc/middle/typeck/check/regionck.rs +++ b/src/librustc/middle/typeck/check/regionck.rs @@ -120,10 +120,10 @@ use middle::region::CodeExtent; use middle::traits; use middle::ty::{ReScope}; use middle::ty::{mod, Ty, MethodCall}; -use middle::typeck::astconv::AstConv; -use middle::typeck::check::FnCtxt; -use middle::typeck::check::regionmanip; -use middle::typeck::check::vtable; +use typeck::astconv::AstConv; +use typeck::check::FnCtxt; +use typeck::check::regionmanip; +use typeck::check::vtable; use middle::infer::resolve_and_force_all_but_regions; use middle::infer::resolve_type; use middle::infer; diff --git a/src/librustc/middle/typeck/check/vtable.rs b/src/librustc/middle/typeck/check/vtable.rs index 5d6093c21e6a4..46b21825854de 100644 --- a/src/librustc/middle/typeck/check/vtable.rs +++ b/src/librustc/middle/typeck/check/vtable.rs @@ -15,7 +15,7 @@ use middle::traits::{Obligation, obligation_for_builtin_bound}; use middle::traits::{FulfillmentError, CodeSelectionError, CodeAmbiguity}; use middle::traits::{ObligationCause}; use middle::ty::{mod, Ty}; -use middle::typeck::check::{FnCtxt, +use typeck::check::{FnCtxt, structurally_resolved_type}; use middle::infer; use std::rc::Rc; diff --git a/src/librustc/middle/typeck/check/wf.rs b/src/librustc/middle/typeck/check/wf.rs index 8535ec4fa6e80..290658650ab3d 100644 --- a/src/librustc/middle/typeck/check/wf.rs +++ b/src/librustc/middle/typeck/check/wf.rs @@ -15,9 +15,9 @@ use middle::traits; use middle::ty::{mod, Ty}; use middle::ty::liberate_late_bound_regions; use middle::ty_fold::{TypeFolder, TypeFoldable}; -use middle::typeck::astconv::AstConv; -use middle::typeck::check::{FnCtxt, Inherited, blank_fn_ctxt, vtable, regionck}; -use middle::typeck::CrateCtxt; +use typeck::astconv::AstConv; +use typeck::check::{FnCtxt, Inherited, blank_fn_ctxt, vtable, regionck}; +use typeck::CrateCtxt; use util::ppaux::Repr; use std::collections::HashSet; diff --git a/src/librustc/middle/typeck/check/writeback.rs b/src/librustc/middle/typeck/check/writeback.rs index e2eee66387fe6..318e4a8fa6b6b 100644 --- a/src/librustc/middle/typeck/check/writeback.rs +++ b/src/librustc/middle/typeck/check/writeback.rs @@ -17,13 +17,13 @@ use middle::def; use middle::pat_util; use middle::ty::{mod, Ty, MethodCall, MethodCallee}; use middle::ty_fold::{TypeFolder,TypeFoldable}; -use middle::typeck::astconv::AstConv; -use middle::typeck::check::FnCtxt; +use typeck::astconv::AstConv; +use typeck::check::FnCtxt; use middle::infer::{force_all, resolve_all, resolve_region}; use middle::infer::resolve_type; use middle::infer; -use middle::typeck::write_substs_to_tcx; -use middle::typeck::write_ty_to_tcx; +use typeck::write_substs_to_tcx; +use typeck::write_ty_to_tcx; use util::ppaux::Repr; use std::cell::Cell; diff --git a/src/librustc/middle/typeck/coherence/mod.rs b/src/librustc/middle/typeck/coherence/mod.rs index 4e1bcec5eead2..d353ccf0b5827 100644 --- a/src/librustc/middle/typeck/coherence/mod.rs +++ b/src/librustc/middle/typeck/coherence/mod.rs @@ -19,7 +19,6 @@ use metadata::csearch::{each_impl, get_impl_trait}; use metadata::csearch; use middle::subst; -use middle::subst::{Substs}; use middle::ty::{ImplContainer, ImplOrTraitItemId, MethodTraitItemId}; use middle::ty::{TypeTraitItemId, lookup_item_type}; use middle::ty::{Ty, ty_bool, ty_char, ty_enum, ty_err}; @@ -31,7 +30,7 @@ use middle::ty::{ty_closure}; use middle::ty::type_is_ty_var; use middle::subst::Subst; use middle::ty; -use middle::typeck::CrateCtxt; +use typeck::CrateCtxt; use middle::infer::combine::Combine; use middle::infer::InferCtxt; use middle::infer::{new_infer_ctxt, resolve_ivar, resolve_type}; @@ -477,27 +476,6 @@ impl<'a, 'tcx> CoherenceChecker<'a, 'tcx> { } } -/// Substitutes the values for the receiver's type parameters that are found in method, leaving the -/// method's type parameters intact. -pub fn make_substs_for_receiver_types<'tcx>(tcx: &ty::ctxt<'tcx>, - trait_ref: &ty::TraitRef<'tcx>, - method: &ty::Method<'tcx>) - -> subst::Substs<'tcx> -{ - let meth_tps: Vec = - method.generics.types.get_slice(subst::FnSpace) - .iter() - .map(|def| ty::mk_param_from_def(tcx, def)) - .collect(); - let meth_regions: Vec = - method.generics.regions.get_slice(subst::FnSpace) - .iter() - .map(|def| ty::ReEarlyBound(def.def_id.node, def.space, - def.index, def.name)) - .collect(); - trait_ref.substs.clone().with_method(meth_tps, meth_regions) -} - fn subst_receiver_types_in_method_ty<'tcx>(tcx: &ty::ctxt<'tcx>, impl_id: ast::DefId, impl_poly_type: &ty::Polytype<'tcx>, @@ -507,7 +485,7 @@ fn subst_receiver_types_in_method_ty<'tcx>(tcx: &ty::ctxt<'tcx>, provided_source: Option) -> ty::Method<'tcx> { - let combined_substs = make_substs_for_receiver_types(tcx, trait_ref, method); + let combined_substs = ty::make_substs_for_receiver_types(tcx, trait_ref, method); debug!("subst_receiver_types_in_method_ty: combined_substs={}", combined_substs.repr(tcx)); diff --git a/src/librustc/middle/typeck/collect.rs b/src/librustc/middle/typeck/collect.rs index 5e1dbe16e3c0f..6b4df1f1dc9ad 100644 --- a/src/librustc/middle/typeck/collect.rs +++ b/src/librustc/middle/typeck/collect.rs @@ -43,13 +43,13 @@ use middle::ty::{ImplContainer, ImplOrTraitItemContainer, TraitContainer}; use middle::ty::{Polytype}; use middle::ty::{mod, Ty}; use middle::ty_fold::TypeFolder; -use middle::typeck::astconv::{AstConv, ty_of_arg}; -use middle::typeck::astconv::{ast_ty_to_ty, ast_region_to_region}; -use middle::typeck::astconv; +use typeck::astconv::{AstConv, ty_of_arg}; +use typeck::astconv::{ast_ty_to_ty, ast_region_to_region}; +use typeck::astconv; use middle::infer; -use middle::typeck::rscope::*; -use middle::typeck::{CrateCtxt, lookup_def_tcx, no_params, write_ty_to_tcx}; -use middle::typeck; +use typeck::rscope::*; +use typeck::{CrateCtxt, lookup_def_tcx, no_params, write_ty_to_tcx}; +use typeck; use util::nodemap::{FnvHashMap, FnvHashSet}; use util::ppaux; use util::ppaux::{Repr,UserString}; diff --git a/src/librustc/middle/typeck/mod.rs b/src/librustc/middle/typeck/mod.rs index dbec92d02dfbd..c2b3fe1f00894 100644 --- a/src/librustc/middle/typeck/mod.rs +++ b/src/librustc/middle/typeck/mod.rs @@ -76,32 +76,32 @@ use syntax::codemap::Span; use syntax::print::pprust::*; use syntax::{ast, ast_map, abi}; -pub mod check; -pub mod rscope; -pub mod astconv; -pub mod collect; -pub mod coherence; -pub mod variance; - -pub struct TypeAndSubsts<'tcx> { +mod check; +mod rscope; +mod astconv; +mod collect; +mod coherence; +mod variance; + +struct TypeAndSubsts<'tcx> { pub substs: subst::Substs<'tcx>, pub ty: Ty<'tcx>, } -pub struct CrateCtxt<'a, 'tcx: 'a> { +struct CrateCtxt<'a, 'tcx: 'a> { // A mapping from method call sites to traits that have that method. trait_map: resolve::TraitMap, tcx: &'a ty::ctxt<'tcx> } // Functions that write types into the node type table -pub fn write_ty_to_tcx<'tcx>(tcx: &ty::ctxt<'tcx>, node_id: ast::NodeId, ty: Ty<'tcx>) { +fn write_ty_to_tcx<'tcx>(tcx: &ty::ctxt<'tcx>, node_id: ast::NodeId, ty: Ty<'tcx>) { debug!("write_ty_to_tcx({}, {})", node_id, ppaux::ty_to_string(tcx, ty)); assert!(!ty::type_needs_infer(ty)); tcx.node_types.borrow_mut().insert(node_id, ty); } -pub fn write_substs_to_tcx<'tcx>(tcx: &ty::ctxt<'tcx>, +fn write_substs_to_tcx<'tcx>(tcx: &ty::ctxt<'tcx>, node_id: ast::NodeId, item_substs: ty::ItemSubsts<'tcx>) { if !item_substs.is_noop() { @@ -114,7 +114,7 @@ pub fn write_substs_to_tcx<'tcx>(tcx: &ty::ctxt<'tcx>, tcx.item_substs.borrow_mut().insert(node_id, item_substs); } } -pub fn lookup_def_tcx(tcx:&ty::ctxt, sp: Span, id: ast::NodeId) -> def::Def { +fn lookup_def_tcx(tcx:&ty::ctxt, sp: Span, id: ast::NodeId) -> def::Def { match tcx.def_map.borrow().get(&id) { Some(x) => x.clone(), _ => { @@ -123,12 +123,12 @@ pub fn lookup_def_tcx(tcx:&ty::ctxt, sp: Span, id: ast::NodeId) -> def::Def { } } -pub fn lookup_def_ccx(ccx: &CrateCtxt, sp: Span, id: ast::NodeId) +fn lookup_def_ccx(ccx: &CrateCtxt, sp: Span, id: ast::NodeId) -> def::Def { lookup_def_tcx(ccx.tcx, sp, id) } -pub fn no_params<'tcx>(t: Ty<'tcx>) -> ty::Polytype<'tcx> { +fn no_params<'tcx>(t: Ty<'tcx>) -> ty::Polytype<'tcx> { ty::Polytype { generics: ty::Generics {types: VecPerParamSpace::empty(), regions: VecPerParamSpace::empty()}, @@ -136,7 +136,7 @@ pub fn no_params<'tcx>(t: Ty<'tcx>) -> ty::Polytype<'tcx> { } } -pub fn require_same_types<'a, 'tcx>(tcx: &ty::ctxt<'tcx>, +fn require_same_types<'a, 'tcx>(tcx: &ty::ctxt<'tcx>, maybe_infcx: Option<&infer::InferCtxt<'a, 'tcx>>, t1_is_expected: bool, span: Span, diff --git a/src/librustc_trans/driver/driver.rs b/src/librustc_trans/driver/driver.rs index d3281ae1c19fc..3888d5b0f53a9 100644 --- a/src/librustc_trans/driver/driver.rs +++ b/src/librustc_trans/driver/driver.rs @@ -18,12 +18,13 @@ use lint; use llvm::{ContextRef, ModuleRef}; use metadata::common::LinkMeta; use metadata::creader; -use middle::{stability, ty, typeck, reachable}; +use middle::{stability, ty, reachable}; use middle::dependency_format; use middle; use plugin::load::Plugins; use plugin::registry::Registry; use plugin; +use rustc::typeck; use trans; use util::common::time; diff --git a/src/librustc_trans/trans/callee.rs b/src/librustc_trans/trans/callee.rs index 1a273f1e2b7c2..746109ef11346 100644 --- a/src/librustc_trans/trans/callee.rs +++ b/src/librustc_trans/trans/callee.rs @@ -50,7 +50,6 @@ use trans::type_::Type; use trans::type_of; use middle::ty::{mod, Ty}; use middle::ty::MethodCall; -use middle::typeck::coherence::make_substs_for_receiver_types; use util::ppaux::Repr; use util::ppaux::ty_to_string; @@ -573,7 +572,7 @@ pub fn trans_fn_ref_with_substs<'blk, 'tcx>( // Compute the first substitution let first_subst = - make_substs_for_receiver_types(tcx, &*trait_ref, &*method) + ty::make_substs_for_receiver_types(tcx, &*trait_ref, &*method) .erase_regions(); // And compose them From cc32f867d8337cfb97fbddf832169dc79556e31d Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 26 Nov 2014 20:49:36 -0500 Subject: [PATCH 64/83] Modify libsyntax/diagnostics to not be so persnickety. The scheme doesn't work in a multi-crate context. We'll need to come up with something better. --- src/librustc_typeck/diagnostics.rs | 149 ++++++++++++++++++++++++++++ src/libsyntax/diagnostics/plugin.rs | 37 ++----- 2 files changed, 160 insertions(+), 26 deletions(-) create mode 100644 src/librustc_typeck/diagnostics.rs diff --git a/src/librustc_typeck/diagnostics.rs b/src/librustc_typeck/diagnostics.rs new file mode 100644 index 0000000000000..afbb18faa0b9f --- /dev/null +++ b/src/librustc_typeck/diagnostics.rs @@ -0,0 +1,149 @@ +// Copyright 2014 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. + +#![allow(non_snake_case)] + +register_diagnostic!(E0001, r##" + This error suggests that the expression arm corresponding to the noted pattern + will never be reached as for all possible values of the expression being matched, + one of the preceeding patterns will match. + + This means that perhaps some of the preceeding patterns are too general, this + one is too specific or the ordering is incorrect. +"##) + +register_diagnostics!( + E0002, + E0003, + E0004, + E0005, + E0006, + E0007, + E0008, + E0009, + E0010, + E0011, + E0012, + E0013, + E0014, + E0015, + E0016, + E0017, + E0018, + E0019, + E0020, + E0022, + E0023, + E0024, + E0025, + E0026, + E0027, + E0029, + E0030, + E0031, + E0033, + E0034, + E0035, + E0036, + E0038, + E0040, + E0044, + E0045, + E0046, + E0049, + E0050, + E0051, + E0052, + E0053, + E0054, + E0055, + E0056, + E0057, + E0059, + E0060, + E0061, + E0062, + E0063, + E0066, + E0067, + E0068, + E0069, + E0070, + E0071, + E0072, + E0073, + E0074, + E0075, + E0076, + E0077, + E0079, + E0080, + E0081, + E0082, + E0083, + E0084, + E0085, + E0086, + E0087, + E0088, + E0089, + E0090, + E0091, + E0092, + E0093, + E0094, + E0100, + E0101, + E0102, + E0103, + E0104, + E0106, + E0107, + E0108, + E0109, + E0110, + E0116, + E0117, + E0118, + E0119, + E0120, + E0121, + E0122, + E0124, + E0127, + E0128, + E0129, + E0130, + E0131, + E0132, + E0133, + E0134, + E0135, + E0136, + E0137, + E0138, + E0139, + E0140, + E0141, + E0152, + E0153, + E0157, + E0158, + E0159, + E0161, + E0162, + E0163, + E0164, + E0165, + E0166, + E0167, + E0168, + E0169 +) diff --git a/src/libsyntax/diagnostics/plugin.rs b/src/libsyntax/diagnostics/plugin.rs index 5f4e675aad5d5..2be11a236d3b7 100644 --- a/src/libsyntax/diagnostics/plugin.rs +++ b/src/libsyntax/diagnostics/plugin.rs @@ -45,15 +45,6 @@ pub fn expand_diagnostic_used<'cx>(ecx: &'cx mut ExtCtxt, [ast::TtToken(_, token::Ident(code, _))] => code, _ => unreachable!() }; - with_registered_diagnostics(|diagnostics| { - if !diagnostics.contains_key(&code.name) { - ecx.span_err(span, format!( - "unknown diagnostic code {}; add to librustc/diagnostics.rs", - token::get_ident(code).get() - ).as_slice()); - } - () - }); with_used_diagnostics(|diagnostics| { match diagnostics.insert(code.name, span) { Some(previous_span) => { @@ -106,25 +97,19 @@ pub fn expand_build_diagnostic_array<'cx>(ecx: &'cx mut ExtCtxt, _ => unreachable!() }; - let (count, expr) = with_used_diagnostics(|diagnostics_in_use| { + let (count, expr) = with_registered_diagnostics(|diagnostics| { - let descriptions: Vec> = diagnostics - .iter().filter_map(|(code, description)| { - if !diagnostics_in_use.contains_key(code) { - ecx.span_warn(span, format!( - "diagnostic code {} never used", token::get_name(*code).get() - ).as_slice()); - } - description.map(|description| { - ecx.expr_tuple(span, vec![ - ecx.expr_str(span, token::get_name(*code)), - ecx.expr_str(span, token::get_name(description)) - ]) - }) - }).collect(); + let descriptions: Vec> = + diagnostics.iter().filter_map(|(code, description)| { + description.map(|description| { + ecx.expr_tuple(span, vec![ + ecx.expr_str(span, token::get_name(*code)), + ecx.expr_str(span, token::get_name(description))]) + }) + }).collect(); (descriptions.len(), ecx.expr_vec(span, descriptions)) - }) - }); + }); + MacItems::new(vec![quote_item!(ecx, pub static $name: [(&'static str, &'static str), ..$count] = $expr; ).unwrap()].into_iter()) From e135fa5b49b215e03802ea6fc99405fab8cbe8e9 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Thu, 27 Nov 2014 07:21:26 -0500 Subject: [PATCH 65/83] Remove dependencies on driver from trans et al. by moving various structs out from driver and into other places. --- src/librustc/middle/ty.rs | 11 +++ src/librustc/session/config.rs | 53 +++++++++++++ src/librustc_trans/back/link.rs | 8 +- src/librustc_trans/back/write.rs | 4 +- src/librustc_trans/driver/driver.rs | 114 ++++------------------------ src/librustc_trans/driver/mod.rs | 14 ++-- src/librustc_trans/driver/pretty.rs | 11 +-- src/librustc_trans/save/mod.rs | 5 +- src/librustc_trans/trans/base.rs | 20 ++--- src/librustc_trans/trans/common.rs | 5 -- src/librustc_trans/trans/context.rs | 4 - src/librustc_trans/trans/datum.rs | 7 -- src/librustc_trans/trans/mod.rs | 98 +++++++++++++++--------- src/librustdoc/clean/mod.rs | 4 +- src/librustdoc/core.rs | 4 +- src/librustdoc/test.rs | 4 +- 16 files changed, 171 insertions(+), 195 deletions(-) diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index 2327bc957e617..994f0c2090a57 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -92,6 +92,17 @@ pub const INITIAL_DISCRIMINANT_VALUE: Disr = 0; // Data types +/// The complete set of all analyses described in this module. This is +/// produced by the driver and fed to trans and later passes. +pub struct CrateAnalysis<'tcx> { + pub exp_map2: middle::resolve::ExportMap2, + pub exported_items: middle::privacy::ExportedItems, + pub public_items: middle::privacy::PublicItems, + pub ty_cx: ty::ctxt<'tcx>, + pub reachable: NodeSet, + pub name: String, +} + #[deriving(PartialEq, Eq, Hash)] pub struct field<'tcx> { pub name: ast::Name, diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs index 33de2c9abe928..cbc9dd9145bfb 100644 --- a/src/librustc/session/config.rs +++ b/src/librustc/session/config.rs @@ -114,6 +114,59 @@ pub struct Options { pub alt_std_name: Option } +pub enum Input { + /// Load source from file + File(Path), + /// The string is the source + Str(String) +} + +impl Input { + pub fn filestem(&self) -> String { + match *self { + Input::File(ref ifile) => ifile.filestem_str().unwrap().to_string(), + Input::Str(_) => "rust_out".to_string(), + } + } +} + +#[deriving(Clone)] +pub struct OutputFilenames { + pub out_directory: Path, + pub out_filestem: String, + pub single_output_file: Option, + pub extra: String, +} + +impl OutputFilenames { + pub fn path(&self, flavor: OutputType) -> Path { + match self.single_output_file { + Some(ref path) => return path.clone(), + None => {} + } + self.temp_path(flavor) + } + + pub fn temp_path(&self, flavor: OutputType) -> Path { + let base = self.out_directory.join(self.filestem()); + match flavor { + OutputTypeBitcode => base.with_extension("bc"), + OutputTypeAssembly => base.with_extension("s"), + OutputTypeLlvmAssembly => base.with_extension("ll"), + OutputTypeObject => base.with_extension("o"), + OutputTypeExe => base, + } + } + + pub fn with_extension(&self, extension: &str) -> Path { + self.out_directory.join(self.filestem()).with_extension(extension) + } + + pub fn filestem(&self) -> String { + format!("{}{}", self.out_filestem, self.extra) + } +} + pub fn host_triple() -> &'static str { // Get the host triple out of the build environment. This ensures that our // idea of the host triple is the same as for the set of libraries we've diff --git a/src/librustc_trans/back/link.rs b/src/librustc_trans/back/link.rs index d8cdffe210057..6057f9d908190 100644 --- a/src/librustc_trans/back/link.rs +++ b/src/librustc_trans/back/link.rs @@ -13,15 +13,13 @@ use super::archive; use super::rpath; use super::rpath::RPathConfig; use super::svh::Svh; -use driver::driver::{CrateTranslation, OutputFilenames, Input, FileInput}; use session::config; use session::config::NoDebugInfo; -use session::config::{OutputTypeBitcode, OutputTypeExe, OutputTypeObject}; +use session::config::{OutputFilenames, Input, OutputTypeBitcode, OutputTypeExe, OutputTypeObject}; use session::Session; use metadata::common::LinkMeta; use metadata::{encoder, cstore, filesearch, csearch, creader}; -use trans::context::CrateContext; -use trans::common::gensym_name; +use trans::{CrateContext, CrateTranslation, gensym_name}; use middle::ty::{mod, Ty}; use util::common::time; use util::ppaux; @@ -156,7 +154,7 @@ pub fn find_crate_name(sess: Option<&Session>, if let Some((attr, s)) = attr_crate_name { return validate(s.get().to_string(), Some(attr.span)); } - if let FileInput(ref path) = *input { + if let Input::File(ref path) = *input { if let Some(s) = path.filestem_str() { return validate(s.to_string(), None); } diff --git a/src/librustc_trans/back/write.rs b/src/librustc_trans/back/write.rs index b923bb076c301..e5ffe2675d6f4 100644 --- a/src/librustc_trans/back/write.rs +++ b/src/librustc_trans/back/write.rs @@ -10,13 +10,13 @@ use back::lto; use back::link::{get_cc_prog, remove}; -use driver::driver::{CrateTranslation, ModuleTranslation, OutputFilenames}; -use session::config::{NoDebugInfo, Passes, SomePasses, AllPasses}; +use session::config::{OutputFilenames, NoDebugInfo, Passes, SomePasses, AllPasses}; use session::Session; use session::config; use llvm; use llvm::{ModuleRef, TargetMachineRef, PassManagerRef, DiagnosticInfoRef, ContextRef}; use llvm::SMDiagnosticRef; +use trans::{CrateTranslation, ModuleTranslation}; use util::common::time; use syntax::codemap; use syntax::diagnostic; diff --git a/src/librustc_trans/driver/driver.rs b/src/librustc_trans/driver/driver.rs index 3888d5b0f53a9..47ba048a21b35 100644 --- a/src/librustc_trans/driver/driver.rs +++ b/src/librustc_trans/driver/driver.rs @@ -8,15 +8,11 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -pub use self::Input::*; - use back::link; use back::write; use session::Session; -use session::config; +use session::config::{mod, Input, OutputFilenames}; use lint; -use llvm::{ContextRef, ModuleRef}; -use metadata::common::LinkMeta; use metadata::creader; use middle::{stability, ty, reachable}; use middle::dependency_format; @@ -28,7 +24,6 @@ use rustc::typeck; use trans; use util::common::time; -use util::nodemap::{NodeSet}; use serialize::{json, Encodable}; @@ -114,36 +109,19 @@ pub fn anon_src() -> String { pub fn source_name(input: &Input) -> String { match *input { // FIXME (#9639): This needs to handle non-utf8 paths - FileInput(ref ifile) => ifile.as_str().unwrap().to_string(), - StrInput(_) => anon_src() - } -} - -pub enum Input { - /// Load source from file - FileInput(Path), - /// The string is the source - StrInput(String) -} - -impl Input { - fn filestem(&self) -> String { - match *self { - FileInput(ref ifile) => ifile.filestem_str().unwrap().to_string(), - StrInput(_) => "rust_out".to_string(), - } + Input::File(ref ifile) => ifile.as_str().unwrap().to_string(), + Input::Str(_) => anon_src() } } - pub fn phase_1_parse_input(sess: &Session, cfg: ast::CrateConfig, input: &Input) -> ast::Crate { let krate = time(sess.time_passes(), "parsing", (), |_| { match *input { - FileInput(ref file) => { + Input::File(ref file) => { parse::parse_crate_from_file(&(*file), cfg.clone(), &sess.parse_sess) } - StrInput(ref src) => { + Input::Str(ref src) => { parse::parse_crate_from_source_str(anon_src().to_string(), src.to_string(), cfg.clone(), @@ -343,23 +321,13 @@ pub fn assign_node_ids_and_map<'ast>(sess: &Session, map } -pub struct CrateAnalysis<'tcx> { - pub exp_map2: middle::resolve::ExportMap2, - pub exported_items: middle::privacy::ExportedItems, - pub public_items: middle::privacy::PublicItems, - pub ty_cx: ty::ctxt<'tcx>, - pub reachable: NodeSet, - pub name: String, -} - - /// Run the resolution, typechecking, region checking and other /// miscellaneous analysis passes on the crate. Return various /// structures carrying the results of the analysis. pub fn phase_3_run_analysis_passes<'tcx>(sess: Session, ast_map: ast_map::Map<'tcx>, type_arena: &'tcx TypedArena>, - name: String) -> CrateAnalysis<'tcx> { + name: String) -> ty::CrateAnalysis<'tcx> { let time_passes = sess.time_passes(); let krate = ast_map.krate(); @@ -474,7 +442,7 @@ pub fn phase_3_run_analysis_passes<'tcx>(sess: Session, time(time_passes, "lint checking", (), |_| lint::check_crate(&ty_cx, &exported_items)); - CrateAnalysis { + ty::CrateAnalysis { exp_map2: exp_map2, ty_cx: ty_cx, exported_items: exported_items, @@ -486,7 +454,7 @@ pub fn phase_3_run_analysis_passes<'tcx>(sess: Session, pub fn phase_save_analysis(sess: &Session, krate: &ast::Crate, - analysis: &CrateAnalysis, + analysis: &ty::CrateAnalysis, odir: &Option) { if (sess.opts.debugging_opts & config::SAVE_ANALYSIS) == 0 { return; @@ -495,25 +463,10 @@ pub fn phase_save_analysis(sess: &Session, save::process_crate(sess, krate, analysis, odir)); } -pub struct ModuleTranslation { - pub llcx: ContextRef, - pub llmod: ModuleRef, -} - -pub struct CrateTranslation { - pub modules: Vec, - pub metadata_module: ModuleTranslation, - pub link: LinkMeta, - pub metadata: Vec, - pub reachable: Vec, - pub crate_formats: dependency_format::Dependencies, - pub no_builtins: bool, -} - /// Run the translation phase to LLVM, after which the AST and analysis can /// be discarded. -pub fn phase_4_translate_to_llvm<'tcx>(analysis: CrateAnalysis<'tcx>) - -> (ty::ctxt<'tcx>, CrateTranslation) { +pub fn phase_4_translate_to_llvm<'tcx>(analysis: ty::CrateAnalysis<'tcx>) + -> (ty::ctxt<'tcx>, trans::CrateTranslation) { let time_passes = analysis.ty_cx.sess.time_passes(); time(time_passes, "resolving dependency formats", (), |_| @@ -521,13 +474,13 @@ pub fn phase_4_translate_to_llvm<'tcx>(analysis: CrateAnalysis<'tcx>) // Option dance to work around the lack of stack once closures. time(time_passes, "translation", analysis, |analysis| - trans::base::trans_crate(analysis)) + trans::trans_crate(analysis)) } /// Run LLVM itself, producing a bitcode file, assembly file or object file /// as a side effect. pub fn phase_5_run_llvm_passes(sess: &Session, - trans: &CrateTranslation, + trans: &trans::CrateTranslation, outputs: &OutputFilenames) { if sess.opts.cg.no_integrated_as { let output_type = config::OutputTypeAssembly; @@ -555,7 +508,7 @@ pub fn phase_5_run_llvm_passes(sess: &Session, /// Run the linker on any artifacts that resulted from the LLVM run. /// This should produce either a finished executable or library. pub fn phase_6_link_output(sess: &Session, - trans: &CrateTranslation, + trans: &trans::CrateTranslation, outputs: &OutputFilenames) { let old_path = os::getenv("PATH").unwrap_or_else(||String::new()); let mut new_path = sess.host_filesearch().get_tools_search_paths(); @@ -640,8 +593,8 @@ fn write_out_deps(sess: &Session, // Use default filename: crate source filename with extension replaced // by ".d" (true, None) => match *input { - FileInput(..) => outputs.with_extension("d"), - StrInput(..) => { + Input::File(..) => outputs.with_extension("d"), + Input::Str(..) => { sess.warn("can not write --dep-info without a filename \ when compiling stdin."); return @@ -752,43 +705,6 @@ pub fn collect_crate_metadata(session: &Session, session.opts.cg.metadata.clone() } -#[deriving(Clone)] -pub struct OutputFilenames { - pub out_directory: Path, - pub out_filestem: String, - pub single_output_file: Option, - extra: String, -} - -impl OutputFilenames { - pub fn path(&self, flavor: config::OutputType) -> Path { - match self.single_output_file { - Some(ref path) => return path.clone(), - None => {} - } - self.temp_path(flavor) - } - - pub fn temp_path(&self, flavor: config::OutputType) -> Path { - let base = self.out_directory.join(self.filestem()); - match flavor { - config::OutputTypeBitcode => base.with_extension("bc"), - config::OutputTypeAssembly => base.with_extension("s"), - config::OutputTypeLlvmAssembly => base.with_extension("ll"), - config::OutputTypeObject => base.with_extension("o"), - config::OutputTypeExe => base, - } - } - - pub fn with_extension(&self, extension: &str) -> Path { - self.out_directory.join(self.filestem()).with_extension(extension) - } - - fn filestem(&self) -> String { - format!("{}{}", self.out_filestem, self.extra) - } -} - pub fn build_output_filenames(input: &Input, odir: &Option, ofile: &Option, diff --git a/src/librustc_trans/driver/mod.rs b/src/librustc_trans/driver/mod.rs index 658be9169afdd..dc450ac5f3814 100644 --- a/src/librustc_trans/driver/mod.rs +++ b/src/librustc_trans/driver/mod.rs @@ -11,8 +11,8 @@ pub use syntax::diagnostic; use back::link; -use driver::driver::{Input, FileInput, StrInput}; use session::{config, Session, build_session}; +use session::config::Input; use lint::Lint; use lint; use metadata; @@ -89,9 +89,9 @@ fn run_compiler(args: &[String]) { if ifile == "-" { let contents = io::stdin().read_to_end().unwrap(); let src = String::from_utf8(contents).unwrap(); - (StrInput(src), None) + (Input::Str(src), None) } else { - (FileInput(Path::new(ifile)), Some(Path::new(ifile))) + (Input::File(Path::new(ifile)), Some(Path::new(ifile))) } } _ => early_error("multiple input filenames provided") @@ -116,11 +116,11 @@ fn run_compiler(args: &[String]) { let r = matches.opt_strs("Z"); if r.contains(&("ls".to_string())) { match input { - FileInput(ref ifile) => { + Input::File(ref ifile) => { let mut stdout = io::stdout(); list_metadata(&sess, &(*ifile), &mut stdout).unwrap(); } - StrInput(_) => { + Input::Str(_) => { early_error("can not list metadata for stdin"); } } @@ -411,12 +411,12 @@ fn print_crate_info(sess: &Session, fn parse_crate_attrs(sess: &Session, input: &Input) -> Vec { let result = match *input { - FileInput(ref ifile) => { + Input::File(ref ifile) => { parse::parse_crate_attrs_from_file(ifile, Vec::new(), &sess.parse_sess) } - StrInput(ref src) => { + Input::Str(ref src) => { parse::parse_crate_attrs_from_source_str( driver::anon_src().to_string(), src.to_string(), diff --git a/src/librustc_trans/driver/pretty.rs b/src/librustc_trans/driver/pretty.rs index 7bb83d7c2a819..ad110cfeafcbc 100644 --- a/src/librustc_trans/driver/pretty.rs +++ b/src/librustc_trans/driver/pretty.rs @@ -17,8 +17,9 @@ use self::NodesMatchingUII::*; use back::link; -use session::{config, Session}; -use driver::driver::{mod, CrateAnalysis}; +use session::Session; +use session::config::{mod, Input}; +use driver::driver::{mod}; use middle::ty; use middle::borrowck::{mod, FnPartsWithCFG}; @@ -242,7 +243,7 @@ impl<'ast> pprust::PpAnn for HygieneAnnotation<'ast> { struct TypedAnnotation<'tcx> { - analysis: CrateAnalysis<'tcx>, + analysis: ty::CrateAnalysis<'tcx>, } impl<'tcx> PrinterSupport<'tcx> for TypedAnnotation<'tcx> { @@ -409,7 +410,7 @@ fn needs_expansion(ppm: &PpMode) -> bool { pub fn pretty_print_input(sess: Session, cfg: ast::CrateConfig, - input: &driver::Input, + input: &Input, ppm: PpMode, opt_uii: Option, ofile: Option) { @@ -536,7 +537,7 @@ pub fn pretty_print_input(sess: Session, } fn print_flowgraph(variants: Vec, - analysis: CrateAnalysis, + analysis: ty::CrateAnalysis, code: blocks::Code, mut out: W) -> io::IoResult<()> { let ty_cx = &analysis.ty_cx; diff --git a/src/librustc_trans/save/mod.rs b/src/librustc_trans/save/mod.rs index 59bbeb2dbc4f2..1482422b8d03e 100644 --- a/src/librustc_trans/save/mod.rs +++ b/src/librustc_trans/save/mod.rs @@ -27,7 +27,6 @@ //! the format of the output away from extracting it from the compiler. //! DxrVisitor walks the AST and processes it. -use driver::driver::CrateAnalysis; use session::Session; use middle::def; @@ -68,7 +67,7 @@ fn generated_code(span: Span) -> bool { struct DxrVisitor<'l, 'tcx: 'l> { sess: &'l Session, - analysis: &'l CrateAnalysis<'tcx>, + analysis: &'l ty::CrateAnalysis<'tcx>, collected_paths: Vec<(NodeId, ast::Path, bool, recorder::Row)>, collecting: bool, @@ -1473,7 +1472,7 @@ impl<'l, 'tcx, 'v> Visitor<'v> for DxrVisitor<'l, 'tcx> { pub fn process_crate(sess: &Session, krate: &ast::Crate, - analysis: &CrateAnalysis, + analysis: &ty::CrateAnalysis, odir: &Option) { if generated_code(krate.span) { return; diff --git a/src/librustc_trans/trans/base.rs b/src/librustc_trans/trans/base.rs index 84a6b59934f6e..9d0e096c71d64 100644 --- a/src/librustc_trans/trans/base.rs +++ b/src/librustc_trans/trans/base.rs @@ -28,9 +28,11 @@ pub use self::ValueOrigin::*; pub use self::scalar_type::*; +use super::CrateTranslation; +use super::ModuleTranslation; + use back::link::{mangle_exported_name}; use back::{link, abi}; -use driver::driver::{CrateAnalysis, CrateTranslation, ModuleTranslation}; use lint; use llvm::{BasicBlockRef, Linkage, ValueRef, Vector, get_param}; use llvm; @@ -1078,12 +1080,6 @@ pub fn store_ty(cx: Block, v: ValueRef, dst: ValueRef, t: Ty) { }; } -pub fn ignore_lhs(_bcx: Block, local: &ast::Local) -> bool { - match local.pat.node { - ast::PatWild(ast::PatWildSingle) => true, _ => false - } -} - pub fn init_local<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, local: &ast::Local) -> Block<'blk, 'tcx> { debug!("init_local(bcx={}, local.id={})", bcx.to_str(), local.id); @@ -2916,12 +2912,6 @@ fn register_method(ccx: &CrateContext, id: ast::NodeId, llfn } -pub fn p2i(ccx: &CrateContext, v: ValueRef) -> ValueRef { - unsafe { - return llvm::LLVMConstPtrToInt(v, ccx.int_type().to_ref()); - } -} - pub fn crate_ctxt_to_encode_parms<'a, 'tcx>(cx: &'a SharedCrateContext<'tcx>, ie: encoder::EncodeInlinedItem<'a>) -> encoder::EncodeParams<'a, 'tcx> { @@ -3055,9 +3045,9 @@ fn internalize_symbols(cx: &SharedCrateContext, reachable: &HashSet) { } } -pub fn trans_crate<'tcx>(analysis: CrateAnalysis<'tcx>) +pub fn trans_crate<'tcx>(analysis: ty::CrateAnalysis<'tcx>) -> (ty::ctxt<'tcx>, CrateTranslation) { - let CrateAnalysis { ty_cx: tcx, exp_map2, reachable, name, .. } = analysis; + let ty::CrateAnalysis { ty_cx: tcx, exp_map2, reachable, name, .. } = analysis; let krate = tcx.map.krate(); // Before we touch LLVM, make sure that multithreading is enabled. diff --git a/src/librustc_trans/trans/common.rs b/src/librustc_trans/trans/common.rs index be3780facaa10..a8256176c2658 100644 --- a/src/librustc_trans/trans/common.rs +++ b/src/librustc_trans/trans/common.rs @@ -272,11 +272,6 @@ impl<'a, 'tcx> FunctionContext<'a, 'tcx> { } } - pub fn out_arg_pos(&self) -> uint { - assert!(self.caller_expects_out_pointer); - 0u - } - pub fn env_arg_pos(&self) -> uint { if self.caller_expects_out_pointer { 1u diff --git a/src/librustc_trans/trans/context.rs b/src/librustc_trans/trans/context.rs index 8220645cec78b..fd9d6b8f2c3b2 100644 --- a/src/librustc_trans/trans/context.rs +++ b/src/librustc_trans/trans/context.rs @@ -347,10 +347,6 @@ impl<'tcx> SharedCrateContext<'tcx> { &self.link_meta } - pub fn symbol_hasher<'a>(&'a self) -> &'a RefCell { - &self.symbol_hasher - } - pub fn tcx<'a>(&'a self) -> &'a ty::ctxt<'tcx> { &self.tcx } diff --git a/src/librustc_trans/trans/datum.rs b/src/librustc_trans/trans/datum.rs index f0fd94958ee97..532ef69081866 100644 --- a/src/librustc_trans/trans/datum.rs +++ b/src/librustc_trans/trans/datum.rs @@ -352,13 +352,6 @@ impl<'tcx> Datum<'tcx, Expr> { |_| bcx.sess().bug("assert_lvalue given rvalue")) } - /// Asserts that this datum *is* an lvalue and returns it. - pub fn assert_rvalue(self, bcx: Block) -> Datum<'tcx, Rvalue> { - self.match_kind( - |_| bcx.sess().bug("assert_rvalue given lvalue"), - |r| r) - } - pub fn store_to_dest<'blk>(self, bcx: Block<'blk, 'tcx>, dest: expr::Dest, diff --git a/src/librustc_trans/trans/mod.rs b/src/librustc_trans/trans/mod.rs index fe7697447acda..c00c477f4b8d2 100644 --- a/src/librustc_trans/trans/mod.rs +++ b/src/librustc_trans/trans/mod.rs @@ -8,40 +8,64 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -pub mod doc; -pub mod macros; -pub mod inline; -pub mod monomorphize; -pub mod controlflow; -pub mod glue; -pub mod datum; -pub mod callee; -pub mod expr; -pub mod common; -pub mod context; -pub mod consts; -pub mod type_of; -pub mod build; -pub mod builder; -pub mod base; -pub mod _match; -pub mod closure; -pub mod tvec; -pub mod meth; -pub mod cabi; -pub mod cabi_x86; -pub mod cabi_x86_64; -pub mod cabi_x86_win64; -pub mod cabi_arm; -pub mod cabi_mips; -pub mod foreign; -pub mod intrinsic; -pub mod debuginfo; -pub mod machine; -pub mod adt; -pub mod asm; -pub mod type_; -pub mod value; -pub mod basic_block; -pub mod llrepr; -pub mod cleanup; +use llvm::{ContextRef, ModuleRef}; +use metadata::common::LinkMeta; +use middle::dependency_format; + +pub use self::base::trans_crate; +pub use self::context::CrateContext; +pub use self::common::gensym_name; + +mod doc; +mod macros; +mod inline; +mod monomorphize; +mod controlflow; +mod glue; +mod datum; +mod callee; +mod expr; +mod common; +mod context; +mod consts; +mod type_of; +mod build; +mod builder; +mod base; +mod _match; +mod closure; +mod tvec; +mod meth; +mod cabi; +mod cabi_x86; +mod cabi_x86_64; +mod cabi_x86_win64; +mod cabi_arm; +mod cabi_mips; +mod foreign; +mod intrinsic; +mod debuginfo; +mod machine; +mod adt; +mod asm; +mod type_; +mod value; +mod basic_block; +mod llrepr; +mod cleanup; + +pub struct ModuleTranslation { + pub llcx: ContextRef, + pub llmod: ModuleRef, +} + +pub struct CrateTranslation { + pub modules: Vec, + pub metadata_module: ModuleTranslation, + pub link: LinkMeta, + pub metadata: Vec, + pub reachable: Vec, + pub crate_formats: dependency_format::Dependencies, + pub no_builtins: bool, +} + diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 6f1ddaff3605f..7e02891160ad2 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -39,7 +39,6 @@ use syntax::parse::token; use syntax::ptr::P; use rustc_trans::back::link; -use rustc_trans::driver::driver; use rustc::metadata::cstore; use rustc::metadata::csearch; use rustc::metadata::decoder; @@ -48,6 +47,7 @@ use rustc::middle::subst; use rustc::middle::subst::VecPerParamSpace; use rustc::middle::ty; use rustc::middle::stability; +use rustc::session::config; use std::rc::Rc; use std::u32; @@ -131,7 +131,7 @@ impl<'a, 'tcx> Clean for visit_ast::RustdocVisitor<'a, 'tcx> { externs.sort_by(|&(a, _), &(b, _)| a.cmp(&b)); // Figure out the name of this crate - let input = driver::FileInput(cx.src.clone()); + let input = config::Input::File(cx.src.clone()); let name = link::find_crate_name(None, self.attrs.as_slice(), &input); // Clean the crate, translating the entire libsyntax AST to one that is diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index b040a4bfd2a09..1eb6ba65860a1 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -83,7 +83,7 @@ pub fn run_core(libs: Vec, cfgs: Vec, externs: Externs, // Parse, resolve, and typecheck the given crate. - let input = driver::FileInput(cpath.clone()); + let input = config::Input::File(cpath.clone()); let warning_lint = lint::builtin::WARNINGS.name_lower(); @@ -122,7 +122,7 @@ pub fn run_core(libs: Vec, cfgs: Vec, externs: Externs, let ast_map = driver::assign_node_ids_and_map(&sess, &mut forest); let type_arena = TypedArena::new(); - let driver::CrateAnalysis { + let ty::CrateAnalysis { exported_items, public_items, ty_cx, .. } = driver::phase_3_run_analysis_passes(sess, ast_map, &type_arena, name); diff --git a/src/librustdoc/test.rs b/src/librustdoc/test.rs index 2a5972bb3d90b..8a18c75beddc3 100644 --- a/src/librustdoc/test.rs +++ b/src/librustdoc/test.rs @@ -42,7 +42,7 @@ pub fn run(input: &str, crate_name: Option) -> int { let input_path = Path::new(input); - let input = driver::FileInput(input_path.clone()); + let input = config::Input::File(input_path.clone()); let sessopts = config::Options { maybe_sysroot: Some(os::self_exe_path().unwrap().dir_path()), @@ -110,7 +110,7 @@ fn runtest(test: &str, cratename: &str, libs: Vec, externs: core::Externs, // the test harness wants its own `main` & top level functions, so // never wrap the test in `fn main() { ... }` let test = maketest(test, Some(cratename), true, as_test_harness); - let input = driver::StrInput(test.to_string()); + let input = config::Input::Str(test.to_string()); let sessopts = config::Options { maybe_sysroot: Some(os::self_exe_path().unwrap().dir_path()), From 93eb4333a085008a55e98259e454c5c592a03d45 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 26 Nov 2014 06:11:29 -0500 Subject: [PATCH 66/83] Move typeck into its own crate. --- mk/crates.mk | 13 ++++--- src/librustc/lib.rs | 3 -- src/librustc_trans/driver/driver.rs | 2 +- src/librustc_trans/lib.rs | 1 + .../typeck => librustc_typeck}/astconv.rs | 15 ++++---- .../check/_match.rs | 8 ++--- .../check/closure.rs | 4 +-- .../check/demand.rs | 2 +- .../check/method/confirm.rs | 2 +- .../check/method/doc.rs | 0 .../check/method/mod.rs | 10 +++--- .../check/method/probe.rs | 4 +-- .../typeck => librustc_typeck}/check/mod.rs | 14 ++++---- .../check/regionck.rs | 8 ++--- .../check/regionmanip.rs | 0 .../check/vtable.rs | 3 +- .../typeck => librustc_typeck}/check/wf.rs | 6 ++-- .../check/writeback.rs | 8 ++--- .../coherence/mod.rs | 2 +- .../coherence/orphan.rs | 0 .../coherence/overlap.rs | 0 .../typeck => librustc_typeck}/collect.rs | 25 +++++++------- src/librustc_typeck/diagnostics.rs | 4 ++- .../typeck/mod.rs => librustc_typeck/lib.rs} | 34 +++++++++++++++++-- .../typeck => librustc_typeck}/rscope.rs | 0 .../typeck => librustc_typeck}/variance.rs | 0 26 files changed, 100 insertions(+), 68 deletions(-) rename src/{librustc/middle/typeck => librustc_typeck}/astconv.rs (99%) rename src/{librustc/middle/typeck => librustc_typeck}/check/_match.rs (99%) rename src/{librustc/middle/typeck => librustc_typeck}/check/closure.rs (99%) rename src/{librustc/middle/typeck => librustc_typeck}/check/demand.rs (99%) rename src/{librustc/middle/typeck => librustc_typeck}/check/method/confirm.rs (99%) rename src/{librustc/middle/typeck => librustc_typeck}/check/method/doc.rs (100%) rename src/{librustc/middle/typeck => librustc_typeck}/check/method/mod.rs (98%) rename src/{librustc/middle/typeck => librustc_typeck}/check/method/probe.rs (99%) rename src/{librustc/middle/typeck => librustc_typeck}/check/mod.rs (99%) rename src/{librustc/middle/typeck => librustc_typeck}/check/regionck.rs (99%) rename src/{librustc/middle/typeck => librustc_typeck}/check/regionmanip.rs (100%) rename src/{librustc/middle/typeck => librustc_typeck}/check/vtable.rs (99%) rename src/{librustc/middle/typeck => librustc_typeck}/check/wf.rs (99%) rename src/{librustc/middle/typeck => librustc_typeck}/check/writeback.rs (99%) rename src/{librustc/middle/typeck => librustc_typeck}/coherence/mod.rs (99%) rename src/{librustc/middle/typeck => librustc_typeck}/coherence/orphan.rs (100%) rename src/{librustc/middle/typeck => librustc_typeck}/coherence/overlap.rs (100%) rename src/{librustc/middle/typeck => librustc_typeck}/collect.rs (99%) rename src/{librustc/middle/typeck/mod.rs => librustc_typeck/lib.rs} (91%) rename src/{librustc/middle/typeck => librustc_typeck}/rscope.rs (100%) rename src/{librustc/middle/typeck => librustc_typeck}/variance.rs (100%) diff --git a/mk/crates.mk b/mk/crates.mk index 77cea02f43b73..839631f02ab63 100644 --- a/mk/crates.mk +++ b/mk/crates.mk @@ -53,7 +53,7 @@ TARGET_CRATES := libc std flate arena term \ serialize getopts collections test time rand \ log regex graphviz core rbml alloc rustrt \ unicode -HOST_CRATES := syntax rustc rustc_trans rustdoc regex_macros fmt_macros \ +HOST_CRATES := syntax rustc rustc_typeck rustc_trans rustdoc regex_macros fmt_macros \ rustc_llvm rustc_back CRATES := $(TARGET_CRATES) $(HOST_CRATES) TOOLS := compiletest rustdoc rustc @@ -67,7 +67,9 @@ DEPS_std := core libc rand alloc collections rustrt unicode \ native:rust_builtin native:backtrace DEPS_graphviz := std DEPS_syntax := std term serialize log fmt_macros arena libc -DEPS_rustc_trans := rustc rustc_back rustc_llvm libc +DEPS_rustc_trans := arena flate getopts graphviz libc rustc rustc_back \ + rustc_typeck log syntax serialize rustc_llvm +DEPS_rustc_typeck := rustc syntax DEPS_rustc := syntax flate arena serialize getopts rbml \ time log graphviz rustc_llvm rustc_back DEPS_rustc_llvm := native:rustllvm libc std @@ -110,8 +112,11 @@ ONLY_RLIB_unicode := 1 # You should not need to edit below this line ################################################################################ -DOC_CRATES := $(filter-out rustc, $(filter-out rustc_trans, $(filter-out syntax, $(CRATES)))) -COMPILER_DOC_CRATES := rustc rustc_trans syntax +DOC_CRATES := $(filter-out rustc, \ + $(filter-out rustc_trans, \ + $(filter-out rustc_typeck, \ + $(filter-out syntax, $(CRATES))))) +COMPILER_DOC_CRATES := rustc rustc_trans rustc_typeck syntax # This macro creates some simple definitions for each crate being built, just # some munging of all of the parameters above. diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs index aa31144182dc2..a964609e4e634 100644 --- a/src/librustc/lib.rs +++ b/src/librustc/lib.rs @@ -98,9 +98,6 @@ pub mod middle { pub mod weak_lang_items; } -#[path="middle/typeck/mod.rs"] -pub mod typeck; - pub mod metadata; pub mod session; diff --git a/src/librustc_trans/driver/driver.rs b/src/librustc_trans/driver/driver.rs index 47ba048a21b35..f7cdc7de56088 100644 --- a/src/librustc_trans/driver/driver.rs +++ b/src/librustc_trans/driver/driver.rs @@ -20,7 +20,7 @@ use middle; use plugin::load::Plugins; use plugin::registry::Registry; use plugin; -use rustc::typeck; +use rustc_typeck as typeck; use trans; use util::common::time; diff --git a/src/librustc_trans/lib.rs b/src/librustc_trans/lib.rs index 4dc8c4d173633..8070b319f1850 100644 --- a/src/librustc_trans/lib.rs +++ b/src/librustc_trans/lib.rs @@ -32,6 +32,7 @@ extern crate getopts; extern crate graphviz; extern crate libc; extern crate rustc; +extern crate rustc_typeck; extern crate rustc_back; #[phase(plugin, link)] extern crate log; #[phase(plugin, link)] extern crate syntax; diff --git a/src/librustc/middle/typeck/astconv.rs b/src/librustc_typeck/astconv.rs similarity index 99% rename from src/librustc/middle/typeck/astconv.rs rename to src/librustc_typeck/astconv.rs index f35b4d6602298..d95ad9a11c87f 100644 --- a/src/librustc/middle/typeck/astconv.rs +++ b/src/librustc_typeck/astconv.rs @@ -54,10 +54,9 @@ use middle::resolve_lifetime as rl; use middle::subst::{FnSpace, TypeSpace, AssocSpace, SelfSpace, Subst, Substs}; use middle::subst::{VecPerParamSpace}; use middle::ty::{mod, Ty}; -use typeck::lookup_def_tcx; -use typeck::rscope::{mod, UnelidableRscope, RegionScope, SpecificRscope, - ShiftedRscope, BindingRscope}; -use typeck::TypeAndSubsts; +use rscope::{mod, UnelidableRscope, RegionScope, SpecificRscope, + ShiftedRscope, BindingRscope}; +use TypeAndSubsts; use util::common::ErrorReported; use util::nodemap::DefIdMap; use util::ppaux::{mod, Repr, UserString}; @@ -429,9 +428,9 @@ pub fn instantiate_trait_ref<'tcx,AC,RS>(this: &AC, where AC: AstConv<'tcx>, RS: RegionScope { - match lookup_def_tcx(this.tcx(), - ast_trait_ref.path.span, - ast_trait_ref.ref_id) { + match ::lookup_def_tcx(this.tcx(), + ast_trait_ref.path.span, + ast_trait_ref.ref_id) { def::DefTrait(trait_def_id) => { let trait_ref = Rc::new(ast_path_to_trait_ref(this, rscope, trait_def_id, self_ty, &ast_trait_ref.path)); @@ -1477,7 +1476,7 @@ pub fn partition_bounds<'a>(tcx: &ty::ctxt, for &ast_bound in ast_bounds.iter() { match *ast_bound { ast::TraitTyParamBound(ref b) => { - match lookup_def_tcx(tcx, b.trait_ref.path.span, b.trait_ref.ref_id) { + match ::lookup_def_tcx(tcx, b.trait_ref.path.span, b.trait_ref.ref_id) { def::DefTrait(trait_did) => { match trait_def_ids.get(&trait_did) { // Already seen this trait. We forbid diff --git a/src/librustc/middle/typeck/check/_match.rs b/src/librustc_typeck/check/_match.rs similarity index 99% rename from src/librustc/middle/typeck/check/_match.rs rename to src/librustc_typeck/check/_match.rs index acc06cbd1cc87..7dcf0aa3e2189 100644 --- a/src/librustc/middle/typeck/check/_match.rs +++ b/src/librustc_typeck/check/_match.rs @@ -9,13 +9,13 @@ // except according to those terms. use middle::def; +use middle::infer::{mod, resolve}; use middle::pat_util::{PatIdMap, pat_id_map, pat_is_binding, pat_is_const}; use middle::subst::{Subst, Substs}; use middle::ty::{mod, Ty}; -use typeck::check::{check_expr, check_expr_has_type, demand, FnCtxt}; -use typeck::check::{instantiate_path, structurally_resolved_type, valid_range_bounds}; -use middle::infer::{mod, resolve}; -use typeck::require_same_types; +use check::{check_expr, check_expr_has_type, demand, FnCtxt}; +use check::{instantiate_path, structurally_resolved_type, valid_range_bounds}; +use require_same_types; use util::nodemap::FnvHashMap; use std::cmp; diff --git a/src/librustc/middle/typeck/check/closure.rs b/src/librustc_typeck/check/closure.rs similarity index 99% rename from src/librustc/middle/typeck/check/closure.rs rename to src/librustc_typeck/check/closure.rs index 37059dec30f05..34030ae4493a2 100644 --- a/src/librustc/middle/typeck/check/closure.rs +++ b/src/librustc_typeck/check/closure.rs @@ -14,11 +14,11 @@ use super::check_fn; use super::{Expectation, ExpectCastableToType, ExpectHasType, NoExpectation}; use super::FnCtxt; +use astconv; use middle::infer; use middle::subst; use middle::ty::{mod, Ty}; -use typeck::astconv; -use typeck::rscope::RegionScope; +use rscope::RegionScope; use syntax::abi; use syntax::ast; use syntax::ast_util; diff --git a/src/librustc/middle/typeck/check/demand.rs b/src/librustc_typeck/check/demand.rs similarity index 99% rename from src/librustc/middle/typeck/check/demand.rs rename to src/librustc_typeck/check/demand.rs index 694ed890d76e1..2b8a52f050d6f 100644 --- a/src/librustc/middle/typeck/check/demand.rs +++ b/src/librustc_typeck/check/demand.rs @@ -9,8 +9,8 @@ // except according to those terms. +use check::FnCtxt; use middle::ty::{mod, Ty}; -use typeck::check::FnCtxt; use middle::infer; use middle::infer::resolve_type; use middle::infer::resolve::try_resolve_tvar_shallow; diff --git a/src/librustc/middle/typeck/check/method/confirm.rs b/src/librustc_typeck/check/method/confirm.rs similarity index 99% rename from src/librustc/middle/typeck/check/method/confirm.rs rename to src/librustc_typeck/check/method/confirm.rs index 078f4b64ac42b..1fe73f0478d56 100644 --- a/src/librustc/middle/typeck/check/method/confirm.rs +++ b/src/librustc_typeck/check/method/confirm.rs @@ -10,12 +10,12 @@ use super::probe; +use check::{mod, FnCtxt, NoPreference, PreferMutLvalue}; use middle::subst::{mod, Subst}; use middle::traits; use middle::ty::{mod, Ty}; use middle::ty::{MethodCall, MethodCallee, MethodObject, MethodOrigin, MethodParam, MethodStatic, MethodTraitObject, MethodTypeParam}; -use typeck::check::{mod, FnCtxt, NoPreference, PreferMutLvalue}; use middle::infer; use middle::infer::InferCtxt; use middle::ty_fold::HigherRankedFoldable; diff --git a/src/librustc/middle/typeck/check/method/doc.rs b/src/librustc_typeck/check/method/doc.rs similarity index 100% rename from src/librustc/middle/typeck/check/method/doc.rs rename to src/librustc_typeck/check/method/doc.rs diff --git a/src/librustc/middle/typeck/check/method/mod.rs b/src/librustc_typeck/check/method/mod.rs similarity index 98% rename from src/librustc/middle/typeck/check/method/mod.rs rename to src/librustc_typeck/check/method/mod.rs index 117eb385eff48..f87a4c9294bab 100644 --- a/src/librustc/middle/typeck/check/method/mod.rs +++ b/src/librustc_typeck/check/method/mod.rs @@ -10,16 +10,16 @@ //! Method lookup: the secret sauce of Rust. See `doc.rs`. +use astconv::AstConv; +use check::{FnCtxt}; +use check::{impl_self_ty}; +use check::vtable; +use check::vtable::select_new_fcx_obligations; use middle::subst; use middle::subst::{Subst}; use middle::traits; use middle::ty::*; use middle::ty; -use typeck::astconv::AstConv; -use typeck::check::{FnCtxt}; -use typeck::check::{impl_self_ty}; -use typeck::check::vtable; -use typeck::check::vtable::select_new_fcx_obligations; use middle::infer; use util::ppaux::{Repr, UserString}; diff --git a/src/librustc/middle/typeck/check/method/probe.rs b/src/librustc_typeck/check/method/probe.rs similarity index 99% rename from src/librustc/middle/typeck/check/method/probe.rs rename to src/librustc_typeck/check/method/probe.rs index 73e7c5c663500..6ff276edbce7e 100644 --- a/src/librustc/middle/typeck/check/method/probe.rs +++ b/src/librustc_typeck/check/method/probe.rs @@ -12,6 +12,8 @@ use super::{MethodError,Ambiguity,NoMatch}; use super::MethodIndex; use super::{CandidateSource,ImplSource,TraitSource}; +use check; +use check::{FnCtxt, NoPreference}; use middle::fast_reject; use middle::subst; use middle::subst::Subst; @@ -19,8 +21,6 @@ use middle::traits; use middle::ty::{mod, Ty}; use middle::ty::{MethodObject}; use middle::ty_fold::HigherRankedFoldable; -use typeck::check; -use typeck::check::{FnCtxt, NoPreference}; use middle::infer; use middle::infer::InferCtxt; use syntax::ast; diff --git a/src/librustc/middle/typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs similarity index 99% rename from src/librustc/middle/typeck/check/mod.rs rename to src/librustc_typeck/check/mod.rs index e2ec9ba1ccb42..a0f3f2734d976 100644 --- a/src/librustc/middle/typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -31,7 +31,7 @@ can be broken down into several distinct phases: In the process of checking, various constraints will be placed on these type variables through the subtyping relationships requested - through the `demand` module. The `typeck::infer` module is in charge + through the `demand` module. The `infer` module is in charge of resolving those constraints. - regionck: after main is complete, the regionck pass goes over all @@ -82,7 +82,8 @@ pub use self::Expectation::*; use self::IsBinopAssignment::*; use self::TupleArgumentsFlag::*; -use session::Session; +use astconv::{mod, ast_region_to_region, ast_ty_to_ty, AstConv}; +use check::_match::pat_ctxt; use middle::{const_eval, def, traits}; use middle::infer; use middle::lang_items::IteratorItem; @@ -96,11 +97,10 @@ use middle::ty::{mod, Ty}; use middle::ty::liberate_late_bound_regions; use middle::ty::{MethodCall, MethodCallee, MethodMap, ObjectCastMap}; use middle::ty_fold::TypeFolder; -use typeck::astconv::{mod, ast_region_to_region, ast_ty_to_ty, AstConv}; -use typeck::check::_match::pat_ctxt; -use typeck::rscope::RegionScope; -use typeck::{CrateCtxt, lookup_def_ccx, no_params, require_same_types}; -use typeck::TypeAndSubsts; +use rscope::RegionScope; +use session::Session; +use {CrateCtxt, lookup_def_ccx, no_params, require_same_types}; +use TypeAndSubsts; use middle::lang_items::TypeIdLangItem; use lint; use util::common::{block_query, indenter, loop_query}; diff --git a/src/librustc/middle/typeck/check/regionck.rs b/src/librustc_typeck/check/regionck.rs similarity index 99% rename from src/librustc/middle/typeck/check/regionck.rs rename to src/librustc_typeck/check/regionck.rs index bb5f6a4cf4872..2aec4393de942 100644 --- a/src/librustc/middle/typeck/check/regionck.rs +++ b/src/librustc_typeck/check/regionck.rs @@ -114,16 +114,16 @@ //! then mean that all later passes would have to check for these figments //! and report an error, and it just seems like more mess in the end.) +use astconv::AstConv; +use check::FnCtxt; +use check::regionmanip; +use check::vtable; use middle::def; use middle::mem_categorization as mc; use middle::region::CodeExtent; use middle::traits; use middle::ty::{ReScope}; use middle::ty::{mod, Ty, MethodCall}; -use typeck::astconv::AstConv; -use typeck::check::FnCtxt; -use typeck::check::regionmanip; -use typeck::check::vtable; use middle::infer::resolve_and_force_all_but_regions; use middle::infer::resolve_type; use middle::infer; diff --git a/src/librustc/middle/typeck/check/regionmanip.rs b/src/librustc_typeck/check/regionmanip.rs similarity index 100% rename from src/librustc/middle/typeck/check/regionmanip.rs rename to src/librustc_typeck/check/regionmanip.rs diff --git a/src/librustc/middle/typeck/check/vtable.rs b/src/librustc_typeck/check/vtable.rs similarity index 99% rename from src/librustc/middle/typeck/check/vtable.rs rename to src/librustc_typeck/check/vtable.rs index 46b21825854de..c2b263885bd73 100644 --- a/src/librustc/middle/typeck/check/vtable.rs +++ b/src/librustc_typeck/check/vtable.rs @@ -8,6 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use check::{FnCtxt, structurally_resolved_type}; use middle::subst::{SelfSpace, FnSpace}; use middle::traits; use middle::traits::{SelectionError, OutputTypeParameterMismatch, Overflow, Unimplemented}; @@ -15,8 +16,6 @@ use middle::traits::{Obligation, obligation_for_builtin_bound}; use middle::traits::{FulfillmentError, CodeSelectionError, CodeAmbiguity}; use middle::traits::{ObligationCause}; use middle::ty::{mod, Ty}; -use typeck::check::{FnCtxt, - structurally_resolved_type}; use middle::infer; use std::rc::Rc; use syntax::ast; diff --git a/src/librustc/middle/typeck/check/wf.rs b/src/librustc_typeck/check/wf.rs similarity index 99% rename from src/librustc/middle/typeck/check/wf.rs rename to src/librustc_typeck/check/wf.rs index 290658650ab3d..1769c588ec1de 100644 --- a/src/librustc/middle/typeck/check/wf.rs +++ b/src/librustc_typeck/check/wf.rs @@ -8,6 +8,9 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use astconv::AstConv; +use check::{FnCtxt, Inherited, blank_fn_ctxt, vtable, regionck}; +use CrateCtxt; use middle::region; use middle::subst; use middle::subst::{Subst}; @@ -15,9 +18,6 @@ use middle::traits; use middle::ty::{mod, Ty}; use middle::ty::liberate_late_bound_regions; use middle::ty_fold::{TypeFolder, TypeFoldable}; -use typeck::astconv::AstConv; -use typeck::check::{FnCtxt, Inherited, blank_fn_ctxt, vtable, regionck}; -use typeck::CrateCtxt; use util::ppaux::Repr; use std::collections::HashSet; diff --git a/src/librustc/middle/typeck/check/writeback.rs b/src/librustc_typeck/check/writeback.rs similarity index 99% rename from src/librustc/middle/typeck/check/writeback.rs rename to src/librustc_typeck/check/writeback.rs index 318e4a8fa6b6b..777f354bec126 100644 --- a/src/librustc/middle/typeck/check/writeback.rs +++ b/src/librustc_typeck/check/writeback.rs @@ -13,17 +13,17 @@ // substitutions. use self::ResolveReason::*; +use astconv::AstConv; +use check::FnCtxt; use middle::def; use middle::pat_util; use middle::ty::{mod, Ty, MethodCall, MethodCallee}; use middle::ty_fold::{TypeFolder,TypeFoldable}; -use typeck::astconv::AstConv; -use typeck::check::FnCtxt; use middle::infer::{force_all, resolve_all, resolve_region}; use middle::infer::resolve_type; use middle::infer; -use typeck::write_substs_to_tcx; -use typeck::write_ty_to_tcx; +use write_substs_to_tcx; +use write_ty_to_tcx; use util::ppaux::Repr; use std::cell::Cell; diff --git a/src/librustc/middle/typeck/coherence/mod.rs b/src/librustc_typeck/coherence/mod.rs similarity index 99% rename from src/librustc/middle/typeck/coherence/mod.rs rename to src/librustc_typeck/coherence/mod.rs index d353ccf0b5827..b8642ddde4082 100644 --- a/src/librustc/middle/typeck/coherence/mod.rs +++ b/src/librustc_typeck/coherence/mod.rs @@ -30,7 +30,7 @@ use middle::ty::{ty_closure}; use middle::ty::type_is_ty_var; use middle::subst::Subst; use middle::ty; -use typeck::CrateCtxt; +use CrateCtxt; use middle::infer::combine::Combine; use middle::infer::InferCtxt; use middle::infer::{new_infer_ctxt, resolve_ivar, resolve_type}; diff --git a/src/librustc/middle/typeck/coherence/orphan.rs b/src/librustc_typeck/coherence/orphan.rs similarity index 100% rename from src/librustc/middle/typeck/coherence/orphan.rs rename to src/librustc_typeck/coherence/orphan.rs diff --git a/src/librustc/middle/typeck/coherence/overlap.rs b/src/librustc_typeck/coherence/overlap.rs similarity index 100% rename from src/librustc/middle/typeck/coherence/overlap.rs rename to src/librustc_typeck/coherence/overlap.rs diff --git a/src/librustc/middle/typeck/collect.rs b/src/librustc_typeck/collect.rs similarity index 99% rename from src/librustc/middle/typeck/collect.rs rename to src/librustc_typeck/collect.rs index 6b4df1f1dc9ad..717e886029a3d 100644 --- a/src/librustc/middle/typeck/collect.rs +++ b/src/librustc_typeck/collect.rs @@ -32,6 +32,9 @@ as `ty_param()` instances. use self::ConvertMethodContext::*; use self::CreateTypeParametersForAssociatedTypesFlag::*; +use astconv::{AstConv, ty_of_arg}; +use astconv::{ast_ty_to_ty, ast_region_to_region}; +use astconv; use metadata::csearch; use middle::def; use middle::lang_items::SizedTraitLangItem; @@ -43,13 +46,9 @@ use middle::ty::{ImplContainer, ImplOrTraitItemContainer, TraitContainer}; use middle::ty::{Polytype}; use middle::ty::{mod, Ty}; use middle::ty_fold::TypeFolder; -use typeck::astconv::{AstConv, ty_of_arg}; -use typeck::astconv::{ast_ty_to_ty, ast_region_to_region}; -use typeck::astconv; use middle::infer; -use typeck::rscope::*; -use typeck::{CrateCtxt, lookup_def_tcx, no_params, write_ty_to_tcx}; -use typeck; +use rscope::*; +use {CrateCtxt, lookup_def_tcx, no_params, write_ty_to_tcx}; use util::nodemap::{FnvHashMap, FnvHashSet}; use util::ppaux; use util::ppaux::{Repr,UserString}; @@ -2159,13 +2158,13 @@ fn check_method_self_type<'a, 'tcx, RS:RegionScope>( base_type.repr(crate_context.tcx), base_type_free.repr(crate_context.tcx)); let infcx = infer::new_infer_ctxt(crate_context.tcx); - drop(typeck::require_same_types(crate_context.tcx, - Some(&infcx), - false, - explicit_self.span, - base_type_free, - required_type_free, - || { + drop(::require_same_types(crate_context.tcx, + Some(&infcx), + false, + explicit_self.span, + base_type_free, + required_type_free, + || { format!("mismatched self type: expected `{}`", ppaux::ty_to_string(crate_context.tcx, required_type)) })); diff --git a/src/librustc_typeck/diagnostics.rs b/src/librustc_typeck/diagnostics.rs index afbb18faa0b9f..36e81f18103b8 100644 --- a/src/librustc_typeck/diagnostics.rs +++ b/src/librustc_typeck/diagnostics.rs @@ -145,5 +145,7 @@ register_diagnostics!( E0166, E0167, E0168, - E0169 + E0169, + E0171, + E0172 ) diff --git a/src/librustc/middle/typeck/mod.rs b/src/librustc_typeck/lib.rs similarity index 91% rename from src/librustc/middle/typeck/mod.rs rename to src/librustc_typeck/lib.rs index c2b3fe1f00894..2f5b473567faa 100644 --- a/src/librustc/middle/typeck/mod.rs +++ b/src/librustc_typeck/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -/* +/*! typeck.rs, an introduction @@ -57,10 +57,37 @@ independently: all subtyping and assignment constraints are met. In essence, the check module specifies the constraints, and the infer module solves them. +# Note + +This API is completely unstable and subject to change. + */ +#![crate_name = "rustc_typeck"] +#![experimental] +#![crate_type = "dylib"] +#![crate_type = "rlib"] +#![doc(html_logo_url = "http://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png", + html_favicon_url = "http://www.rust-lang.org/favicon.ico", + html_root_url = "http://doc.rust-lang.org/nightly/")] + +#![feature(default_type_params, globs, if_let, import_shadowing, macro_rules, phase, quote)] +#![feature(slicing_syntax, tuple_indexing, unsafe_destructor)] +#![feature(rustc_diagnostic_macros)] #![allow(non_camel_case_types)] +#[phase(plugin, link)] extern crate log; +#[phase(plugin, link)] extern crate syntax; + +extern crate arena; +extern crate rustc; + +pub use rustc::lint; +pub use rustc::metadata; +pub use rustc::middle; +pub use rustc::session; +pub use rustc::util; + use middle::def; use middle::resolve; use middle::infer; @@ -76,6 +103,9 @@ use syntax::codemap::Span; use syntax::print::pprust::*; use syntax::{ast, ast_map, abi}; +#[cfg(stage0)] +mod diagnostics; + mod check; mod rscope; mod astconv; diff --git a/src/librustc/middle/typeck/rscope.rs b/src/librustc_typeck/rscope.rs similarity index 100% rename from src/librustc/middle/typeck/rscope.rs rename to src/librustc_typeck/rscope.rs diff --git a/src/librustc/middle/typeck/variance.rs b/src/librustc_typeck/variance.rs similarity index 100% rename from src/librustc/middle/typeck/variance.rs rename to src/librustc_typeck/variance.rs From 61edb0ccb791934fa86000a9e59a23a9fc1f983c Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Thu, 27 Nov 2014 09:57:47 -0500 Subject: [PATCH 67/83] Separate the driver into its own crate that uses trans, typeck. --- mk/crates.mk | 17 +++--- src/driver/driver.rs | 2 +- .../driver => librustc_driver}/driver.rs | 31 +++++----- .../driver/mod.rs => librustc_driver/lib.rs} | 58 +++++++++++++++---- src/librustc_driver/mod.rs | 10 ++++ .../driver => librustc_driver}/pretty.rs | 25 ++++---- .../test.rs | 0 src/librustc_trans/lib.rs | 11 ---- src/librustdoc/core.rs | 2 +- src/librustdoc/lib.rs | 3 +- src/librustdoc/test.rs | 2 +- 11 files changed, 100 insertions(+), 61 deletions(-) rename src/{librustc_trans/driver => librustc_driver}/driver.rs (98%) rename src/{librustc_trans/driver/mod.rs => librustc_driver/lib.rs} (92%) create mode 100644 src/librustc_driver/mod.rs rename src/{librustc_trans/driver => librustc_driver}/pretty.rs (98%) rename src/{librustc_trans => librustc_driver}/test.rs (100%) diff --git a/mk/crates.mk b/mk/crates.mk index 839631f02ab63..3a2def389cc24 100644 --- a/mk/crates.mk +++ b/mk/crates.mk @@ -53,8 +53,8 @@ TARGET_CRATES := libc std flate arena term \ serialize getopts collections test time rand \ log regex graphviz core rbml alloc rustrt \ unicode -HOST_CRATES := syntax rustc rustc_typeck rustc_trans rustdoc regex_macros fmt_macros \ - rustc_llvm rustc_back +RUSTC_CRATES := rustc rustc_typeck rustc_driver rustc_trans rustc_back rustc_llvm +HOST_CRATES := syntax $(RUSTC_CRATES) rustdoc regex_macros fmt_macros CRATES := $(TARGET_CRATES) $(HOST_CRATES) TOOLS := compiletest rustdoc rustc @@ -67,14 +67,16 @@ DEPS_std := core libc rand alloc collections rustrt unicode \ native:rust_builtin native:backtrace DEPS_graphviz := std DEPS_syntax := std term serialize log fmt_macros arena libc +DEPS_rustc_driver := arena flate getopts graphviz libc rustc rustc_back \ + rustc_typeck log syntax serialize rustc_llvm rustc_trans DEPS_rustc_trans := arena flate getopts graphviz libc rustc rustc_back \ - rustc_typeck log syntax serialize rustc_llvm + log syntax serialize rustc_llvm DEPS_rustc_typeck := rustc syntax DEPS_rustc := syntax flate arena serialize getopts rbml \ time log graphviz rustc_llvm rustc_back DEPS_rustc_llvm := native:rustllvm libc std DEPS_rustc_back := std syntax rustc_llvm flate log libc -DEPS_rustdoc := rustc rustc_trans native:hoedown serialize getopts \ +DEPS_rustdoc := rustc rustc_driver native:hoedown serialize getopts \ test time DEPS_flate := std native:miniz DEPS_arena := std @@ -96,7 +98,7 @@ DEPS_fmt_macros = std TOOL_DEPS_compiletest := test getopts TOOL_DEPS_rustdoc := rustdoc -TOOL_DEPS_rustc := rustc_trans +TOOL_DEPS_rustc := rustc_driver TOOL_SOURCE_compiletest := $(S)src/compiletest/compiletest.rs TOOL_SOURCE_rustdoc := $(S)src/driver/driver.rs TOOL_SOURCE_rustc := $(S)src/driver/driver.rs @@ -115,8 +117,9 @@ ONLY_RLIB_unicode := 1 DOC_CRATES := $(filter-out rustc, \ $(filter-out rustc_trans, \ $(filter-out rustc_typeck, \ - $(filter-out syntax, $(CRATES))))) -COMPILER_DOC_CRATES := rustc rustc_trans rustc_typeck syntax + $(filter-out rustc_driver, \ + $(filter-out syntax, $(CRATES)))))) +COMPILER_DOC_CRATES := rustc rustc_trans rustc_typeck rustc_driver syntax # This macro creates some simple definitions for each crate being built, just # some munging of all of the parameters above. diff --git a/src/driver/driver.rs b/src/driver/driver.rs index 632d21d7b9c05..5c29cb4ec7276 100644 --- a/src/driver/driver.rs +++ b/src/driver/driver.rs @@ -12,6 +12,6 @@ extern crate "rustdoc" as this; #[cfg(rustc)] -extern crate "rustc_trans" as this; +extern crate "rustc_driver" as this; fn main() { this::main() } diff --git a/src/librustc_trans/driver/driver.rs b/src/librustc_driver/driver.rs similarity index 98% rename from src/librustc_trans/driver/driver.rs rename to src/librustc_driver/driver.rs index f7cdc7de56088..437b0257a9759 100644 --- a/src/librustc_trans/driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -8,22 +8,22 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use back::link; -use back::write; -use session::Session; -use session::config::{mod, Input, OutputFilenames}; -use lint; -use metadata::creader; -use middle::{stability, ty, reachable}; -use middle::dependency_format; -use middle; -use plugin::load::Plugins; -use plugin::registry::Registry; -use plugin; +use rustc::session::Session; +use rustc::session::config::{mod, Input, OutputFilenames}; +use rustc::lint; +use rustc::metadata::creader; +use rustc::middle::{stability, ty, reachable}; +use rustc::middle::dependency_format; +use rustc::middle; +use rustc::plugin::load::Plugins; +use rustc::plugin::registry::Registry; +use rustc::plugin; +use rustc::util::common::time; +use rustc_trans::back::link; +use rustc_trans::back::write; +use rustc_trans::save; +use rustc_trans::trans; use rustc_typeck as typeck; -use trans; - -use util::common::time; use serialize::{json, Encodable}; @@ -31,7 +31,6 @@ use std::io; use std::io::fs; use std::os; use arena::TypedArena; -use save; use syntax::ast; use syntax::ast_map; use syntax::attr; diff --git a/src/librustc_trans/driver/mod.rs b/src/librustc_driver/lib.rs similarity index 92% rename from src/librustc_trans/driver/mod.rs rename to src/librustc_driver/lib.rs index dc450ac5f3814..efad07005b9b9 100644 --- a/src/librustc_trans/driver/mod.rs +++ b/src/librustc_driver/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // @@ -8,15 +8,46 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -pub use syntax::diagnostic; +//! The Rust compiler. +//! +//! # Note +//! +//! This API is completely unstable and subject to change. + +#![crate_name = "rustc_driver"] +#![experimental] +#![crate_type = "dylib"] +#![crate_type = "rlib"] +#![doc(html_logo_url = "http://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png", + html_favicon_url = "http://www.rust-lang.org/favicon.ico", + html_root_url = "http://doc.rust-lang.org/nightly/")] + +#![feature(default_type_params, globs, if_let, import_shadowing, macro_rules, phase, quote)] +#![feature(slicing_syntax, unsafe_destructor)] +#![feature(rustc_diagnostic_macros)] + +extern crate arena; +extern crate flate; +extern crate getopts; +extern crate graphviz; +extern crate libc; +extern crate rustc; +extern crate rustc_typeck; +extern crate rustc_back; +extern crate rustc_trans; +#[phase(plugin, link)] extern crate log; +#[phase(plugin, link)] extern crate syntax; +extern crate serialize; +extern crate "rustc_llvm" as llvm; -use back::link; -use session::{config, Session, build_session}; -use session::config::Input; -use lint::Lint; -use lint; -use metadata; +pub use syntax::diagnostic; +use rustc_trans::back::link; +use rustc::session::{config, Session, build_session}; +use rustc::session::config::Input; +use rustc::lint::Lint; +use rustc::lint; +use rustc::metadata; use rustc::DIAGNOSTICS; use std::any::AnyRefExt; @@ -24,14 +55,15 @@ use std::io; use std::os; use std::task::TaskBuilder; -use session::early_error; +use rustc::session::early_error; use syntax::ast; use syntax::parse; use syntax::diagnostic::Emitter; use syntax::diagnostics; -use getopts; +#[cfg(test)] +pub mod test; pub mod driver; pub mod pretty; @@ -507,3 +539,9 @@ pub fn monitor(f: proc():Send) { } } +pub fn main() { + let args = std::os::args(); + let result = run(args); + std::os::set_exit_status(result); +} + diff --git a/src/librustc_driver/mod.rs b/src/librustc_driver/mod.rs new file mode 100644 index 0000000000000..1fbbc9c05213b --- /dev/null +++ b/src/librustc_driver/mod.rs @@ -0,0 +1,10 @@ +// Copyright 2012 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. + diff --git a/src/librustc_trans/driver/pretty.rs b/src/librustc_driver/pretty.rs similarity index 98% rename from src/librustc_trans/driver/pretty.rs rename to src/librustc_driver/pretty.rs index ad110cfeafcbc..b6441ab4944f7 100644 --- a/src/librustc_trans/driver/pretty.rs +++ b/src/librustc_driver/pretty.rs @@ -15,19 +15,18 @@ pub use self::PpSourceMode::*; pub use self::PpMode::*; use self::NodesMatchingUII::*; -use back::link; - -use session::Session; -use session::config::{mod, Input}; -use driver::driver::{mod}; - -use middle::ty; -use middle::borrowck::{mod, FnPartsWithCFG}; -use middle::borrowck::graphviz as borrowck_dot; -use middle::cfg; -use middle::cfg::graphviz::LabelledCFG; - -use util::ppaux; +use rustc_trans::back::link; + +use driver; + +use rustc::middle::ty; +use rustc::middle::borrowck::{mod, FnPartsWithCFG}; +use rustc::middle::borrowck::graphviz as borrowck_dot; +use rustc::middle::cfg; +use rustc::middle::cfg::graphviz::LabelledCFG; +use rustc::session::Session; +use rustc::session::config::{mod, Input}; +use rustc::util::ppaux; use syntax::ast; use syntax::ast_map::{mod, blocks, NodePrinter}; diff --git a/src/librustc_trans/test.rs b/src/librustc_driver/test.rs similarity index 100% rename from src/librustc_trans/test.rs rename to src/librustc_driver/test.rs diff --git a/src/librustc_trans/lib.rs b/src/librustc_trans/lib.rs index 8070b319f1850..4e25921e0b29b 100644 --- a/src/librustc_trans/lib.rs +++ b/src/librustc_trans/lib.rs @@ -32,7 +32,6 @@ extern crate getopts; extern crate graphviz; extern crate libc; extern crate rustc; -extern crate rustc_typeck; extern crate rustc_back; #[phase(plugin, link)] extern crate log; #[phase(plugin, link)] extern crate syntax; @@ -66,17 +65,7 @@ pub mod back { pub mod trans; pub mod save; -pub mod driver; pub mod lib { pub use llvm; } - -pub fn main() { - let args = std::os::args(); - let result = driver::run(args); - std::os::set_exit_status(result); -} - -#[cfg(test)] -pub mod test; diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index 1eb6ba65860a1..4cd88bca51e93 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -9,7 +9,7 @@ // except according to those terms. pub use self::MaybeTyped::*; -use rustc_trans::driver::driver; +use rustc_driver::driver; use rustc::session::{mod, config}; use rustc::middle::{privacy, ty}; use rustc::lint; diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index cc946a6ca4a96..1e7de50cf879d 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -21,6 +21,7 @@ extern crate getopts; extern crate libc; extern crate rustc; extern crate rustc_trans; +extern crate rustc_driver; extern crate serialize; extern crate syntax; extern crate "test" as testing; @@ -163,7 +164,7 @@ pub fn main_args(args: &[String]) -> int { usage(args[0].as_slice()); return 0; } else if matches.opt_present("version") { - match rustc_trans::driver::version("rustdoc", &matches) { + match rustc_driver::version("rustdoc", &matches) { Some(err) => { println!("{}", err); return 1 diff --git a/src/librustdoc/test.rs b/src/librustdoc/test.rs index 8a18c75beddc3..7ca7ae4b21149 100644 --- a/src/librustdoc/test.rs +++ b/src/librustdoc/test.rs @@ -19,7 +19,7 @@ use std::string::String; use std::collections::{HashSet, HashMap}; use testing; use rustc::session::{mod, config}; -use rustc_trans::driver::driver; +use rustc_driver::driver; use syntax::ast; use syntax::codemap::{CodeMap, dummy_spanned}; use syntax::diagnostic; From 5d19432679b1047dc00d1fd110d630c4de1dbcfc Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 3 Dec 2014 08:33:45 -0500 Subject: [PATCH 68/83] FIXME(#19497) -- Stop messing around and just give rustc 32MB of stack unconditionally. This is prompted by some sort of bug in trans that causes a stack overflow when the modules in trans are made private. (In particular, the overflow can also be avoided by making `controlflow` and `callee` public, but that seems strictly worse than just using more stack.) --- src/librustc_driver/lib.rs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/librustc_driver/lib.rs b/src/librustc_driver/lib.rs index efad07005b9b9..33c009cf3291b 100644 --- a/src/librustc_driver/lib.rs +++ b/src/librustc_driver/lib.rs @@ -470,13 +470,7 @@ pub fn list_metadata(sess: &Session, path: &Path, /// The diagnostic emitter yielded to the procedure should be used for reporting /// errors of the compiler. pub fn monitor(f: proc():Send) { - // FIXME: This is a hack for newsched since it doesn't support split stacks. - // rustc needs a lot of stack! When optimizations are disabled, it needs - // even *more* stack than usual as well. - #[cfg(rtopt)] - static STACK_SIZE: uint = 6000000; // 6MB - #[cfg(not(rtopt))] - static STACK_SIZE: uint = 20000000; // 20MB + static STACK_SIZE: uint = 32000000; // 32MB let (tx, rx) = channel(); let w = io::ChanWriter::new(tx); From 298b525951ea4ce7a78364e835f45a549b7f865e Mon Sep 17 00:00:00 2001 From: Erick Tryzelaar Date: Thu, 4 Dec 2014 07:57:13 -0800 Subject: [PATCH 69/83] core: fix a doctest --- src/libcore/result.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libcore/result.rs b/src/libcore/result.rs index 2368739a66f00..7626b4eed7634 100644 --- a/src/libcore/result.rs +++ b/src/libcore/result.rs @@ -444,9 +444,9 @@ impl Result { /// ignoring I/O and parse errors: /// /// ``` - /// use std::io::{BufReader, IoResult}; + /// use std::io::IoResult; /// - /// let mut buffer = "1\n2\n3\n4\n"; + /// let mut buffer = &mut b"1\n2\n3\n4\n"; /// /// let mut sum = 0; /// From f0f7a9006853902882f7475b400fc9075c798c29 Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Thu, 4 Dec 2014 11:36:08 -0500 Subject: [PATCH 70/83] Some small copy edits to the guide. Fixes #19335. --- src/doc/guide.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/doc/guide.md b/src/doc/guide.md index c2d43a20ec46c..f019a11485571 100644 --- a/src/doc/guide.md +++ b/src/doc/guide.md @@ -140,7 +140,7 @@ $ editor main.rs ``` Rust files always end in a `.rs` extension. If you're using more than one word -in your file name, use an underscore. `hello_world.rs` rather than +in your filename, use an underscore. `hello_world.rs` rather than `helloworld.rs`. Now that you've got your file open, type this in: @@ -200,7 +200,7 @@ about this difference. Just know that sometimes, you'll see a `!`, and that means that you're calling a macro instead of a normal function. Rust implements `println!` as a macro rather than a function for good reasons, but that's a very advanced topic. You'll learn more when we talk about macros later. One -last thing to mention: Rust's macros are significantly different than C macros, +last thing to mention: Rust's macros are significantly different from C macros, if you've used those. Don't be scared of using macros. We'll get to the details eventually, you'll just have to trust us for now. @@ -595,8 +595,8 @@ let y = if x == 5i { 10i } else { 15i }; ``` This reveals two interesting things about Rust: it is an expression-based -language, and semicolons are different than in other 'curly brace and -semicolon'-based languages. These two things are related. +language, and semicolons are different from semicolons in other 'curly brace +and semicolon'-based languages. These two things are related. ## Expressions vs. Statements @@ -1454,7 +1454,7 @@ Both `continue` and `break` are valid in both kinds of loops. # Strings Strings are an important concept for any programmer to master. Rust's string -handling system is a bit different than in other languages, due to its systems +handling system is a bit different from other languages, due to its systems focus. Any time you have a data structure of variable size, things can get tricky, and strings are a re-sizable data structure. That said, Rust's strings also work differently than in some other systems languages, such as C. From 010cbd011a644512afdca2378c5788181703cac3 Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Thu, 4 Dec 2014 11:43:22 -0500 Subject: [PATCH 71/83] Tasks aren't actually lightweight :frown: Fixes #19402. --- src/doc/guide.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/doc/guide.md b/src/doc/guide.md index c2d43a20ec46c..0280fb9e97cbb 100644 --- a/src/doc/guide.md +++ b/src/doc/guide.md @@ -5174,12 +5174,12 @@ processor. Rust's semantics lend themselves very nicely to solving a number of issues that programmers have with concurrency. Many concurrency errors that are runtime errors in other languages are compile-time errors in Rust. -Rust's concurrency primitive is called a **task**. Tasks are lightweight, and -do not share memory in an unsafe manner, preferring message passing to -communicate. It's worth noting that tasks are implemented as a library, and -not part of the language. This means that in the future, other concurrency -libraries can be written for Rust to help in specific scenarios. Here's an -example of creating a task: +Rust's concurrency primitive is called a **task**. Tasks are similar to +threads, and do not share memory in an unsafe manner, preferring message +passing to communicate. It's worth noting that tasks are implemented as a +library, and not part of the language. This means that in the future, other +concurrency libraries can be written for Rust to help in specific scenarios. +Here's an example of creating a task: ```{rust} spawn(proc() { From 0d3c41561751bbcbc8f2fccf7369280e35dbb68f Mon Sep 17 00:00:00 2001 From: Aaron Liblong Date: Thu, 4 Dec 2014 12:14:06 -0500 Subject: [PATCH 72/83] Add capacity() to VecMap Changed capacity() tag to unstable and fixed doc assert --- src/libcollections/vec_map.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/libcollections/vec_map.rs b/src/libcollections/vec_map.rs index 36e66ed27f3c9..986e7ef5bc24e 100644 --- a/src/libcollections/vec_map.rs +++ b/src/libcollections/vec_map.rs @@ -115,6 +115,22 @@ impl VecMap { VecMap { v: Vec::with_capacity(capacity) } } + /// Returns the number of elements the `VecMap` can hold without + /// reallocating. + /// + /// # Example + /// + /// ``` + /// use std::collections::VecMap; + /// let map: VecMap = VecMap::with_capacity(10); + /// assert!(map.capacity() >= 10); + /// ``` + #[inline] + #[unstable = "matches collection reform specification, waiting for dust to settle"] + pub fn capacity(&self) -> uint { + self.v.capacity() + } + /// Returns an iterator visiting all keys in ascending order by the keys. /// The iterator's element type is `uint`. #[unstable = "matches collection reform specification, waiting for dust to settle"] From fdb0d9026e0189141dfdbe08926252efeb21e4d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adolfo=20Ochagav=C3=ADa?= Date: Thu, 4 Dec 2014 20:56:44 +0100 Subject: [PATCH 73/83] Remove reduntant compile-fail test Fixes https://github.com/rust-lang/rust/issues/19510 --- src/test/compile-fail/issue-17718-extern-const.rs | 15 --------------- 1 file changed, 15 deletions(-) delete mode 100644 src/test/compile-fail/issue-17718-extern-const.rs diff --git a/src/test/compile-fail/issue-17718-extern-const.rs b/src/test/compile-fail/issue-17718-extern-const.rs deleted file mode 100644 index 235d1222d81c0..0000000000000 --- a/src/test/compile-fail/issue-17718-extern-const.rs +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright 2014 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. - -extern { - const FOO: uint; //~ ERROR: unexpected token: `const` -} - -fn main() {} From 87235687a13f3fca2e300674997880f0f9ba12a7 Mon Sep 17 00:00:00 2001 From: Alexander Light Date: Thu, 4 Dec 2014 15:02:59 -0500 Subject: [PATCH 74/83] Add ability to use custom alloc::heap::imp Adds the ability to use a custom allocator heap by passing either --cfg external_crate and --extern external= or --cfg external_funcs and defining the allocator functions prefixed by 'rust_' somewhere. This is useful for many reasons including OS/embedded development, and allocator development and testing. --- src/liballoc/heap.rs | 58 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 55 insertions(+), 3 deletions(-) diff --git a/src/liballoc/heap.rs b/src/liballoc/heap.rs index 579f47ee87466..067c235c9ae49 100644 --- a/src/liballoc/heap.rs +++ b/src/liballoc/heap.rs @@ -123,7 +123,59 @@ const MIN_ALIGN: uint = 8; target_arch = "x86_64"))] const MIN_ALIGN: uint = 16; -#[cfg(jemalloc)] +#[cfg(external_funcs)] +mod imp { + extern { + fn rust_allocate(size: uint, align: uint) -> *mut u8; + fn rust_deallocate(ptr: *mut u8, old_size: uint, align: uint); + fn rust_reallocate(ptr: *mut u8, old_size: uint, size: uint, align: uint) -> *mut u8; + fn rust_reallocate_inplace(ptr: *mut u8, old_size: uint, size: uint, + align: uint) -> uint; + fn rust_usable_size(size: uint, align: uint) -> uint; + fn rust_stats_print(); + } + + #[inline] + pub unsafe fn allocate(size: uint, align: uint) -> *mut u8 { + rust_allocate(size, align) + } + + #[inline] + pub unsafe fn reallocate_inplace(ptr: *mut u8, old_size: uint, size: uint, + align: uint) -> uint { + rust_reallocate_inplace(ptr, old_size, size, align) + } + + #[inline] + pub unsafe fn deallocate(ptr: *mut u8, old_size: uint, align: uint) { + rust_deallocate(ptr, old_size, align) + } + + #[inline] + pub unsafe fn reallocate_inplace(ptr: *mut u8, old_size: uint, size: uint, + align: uint) -> uint { + rust_reallocate_inplace(ptr, old_size, size, align) + } + + #[inline] + pub fn usable_size(size: uint, align: uint) -> uint { + unsafe { rust_usable_size(size, align) } + } + + #[inline] + pub fn stats_print() { + unsafe { rust_stats_print() } + } +} + +#[cfg(external_crate)] +mod imp { + extern crate external; + pub use self::external::{allocate, deallocate, reallocate_inplace, reallocate}; + pub use self::external::{usable_size, stats_print}; +} + +#[cfg(all(not(external_funcs), not(external_crate), jemalloc))] mod imp { use core::option::{None, Option}; use core::ptr::{null_mut, null}; @@ -199,7 +251,7 @@ mod imp { } } -#[cfg(all(not(jemalloc), unix))] +#[cfg(all(not(external_funcs), not(external_crate), not(jemalloc), unix))] mod imp { use core::cmp; use core::ptr; @@ -260,7 +312,7 @@ mod imp { pub fn stats_print() {} } -#[cfg(all(not(jemalloc), windows))] +#[cfg(all(not(external_funcs), not(external_crate), not(jemalloc), windows))] mod imp { use libc::{c_void, size_t}; use libc; From 14f9127d8ad8c6076c30bcacb7dc19e05ec265b1 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Thu, 4 Dec 2014 16:34:13 -0500 Subject: [PATCH 75/83] Delete diagnostics tests because that model doesn't scale to multiple crates --- src/test/compile-fail/rustc-diagnostics-1.rs | 28 -------------------- src/test/compile-fail/rustc-diagnostics-2.rs | 20 -------------- src/test/compile-fail/rustc-diagnostics-3.rs | 20 -------------- 3 files changed, 68 deletions(-) delete mode 100644 src/test/compile-fail/rustc-diagnostics-1.rs delete mode 100644 src/test/compile-fail/rustc-diagnostics-2.rs delete mode 100644 src/test/compile-fail/rustc-diagnostics-3.rs diff --git a/src/test/compile-fail/rustc-diagnostics-1.rs b/src/test/compile-fail/rustc-diagnostics-1.rs deleted file mode 100644 index 55d836092fa71..0000000000000 --- a/src/test/compile-fail/rustc-diagnostics-1.rs +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2014 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. - -#![feature(rustc_diagnostic_macros)] - -__register_diagnostic!(E0001) -__register_diagnostic!(E0003) - -fn main() { - __diagnostic_used!(E0002); - //~^ ERROR unknown diagnostic code E0002 - - __diagnostic_used!(E0001); - //~^ NOTE previous invocation - - __diagnostic_used!(E0001); - //~^ WARNING diagnostic code E0001 already used -} - -__build_diagnostic_array!(DIAGNOSTICS) -//~^ WARN diagnostic code E0003 never used diff --git a/src/test/compile-fail/rustc-diagnostics-2.rs b/src/test/compile-fail/rustc-diagnostics-2.rs deleted file mode 100644 index c4e011bcea042..0000000000000 --- a/src/test/compile-fail/rustc-diagnostics-2.rs +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright 2014 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. - -#![feature(rustc_diagnostic_macros)] - -__register_diagnostic!(E0001) -__register_diagnostic!(E0001) -//~^ ERROR diagnostic code E0001 already registered - -fn main() { -} - -__build_diagnostic_array!(DIAGNOSTICS) diff --git a/src/test/compile-fail/rustc-diagnostics-3.rs b/src/test/compile-fail/rustc-diagnostics-3.rs deleted file mode 100644 index d160664a48c78..0000000000000 --- a/src/test/compile-fail/rustc-diagnostics-3.rs +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright 2014 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. - -__register_diagnostic!(E0001) -//~^ ERROR macro undefined: '__register_diagnostic!' - -fn main() { - __diagnostic_used!(E0001); - //~^ ERROR macro undefined: '__diagnostic_used!' -} - -__build_diagnostic_array!(DIAGNOSTICS) -//~^ ERROR macro undefined: '__build_diagnostic_array!' From d424af480fac92849fe9ac99bd606024865a8fc5 Mon Sep 17 00:00:00 2001 From: Chase Southwood Date: Thu, 4 Dec 2014 00:35:38 -0600 Subject: [PATCH 76/83] Implement BitOps for TrieSet --- src/libcollections/trie/set.rs | 66 +++++++++++++++++++++++++++++++++- 1 file changed, 65 insertions(+), 1 deletion(-) diff --git a/src/libcollections/trie/set.rs b/src/libcollections/trie/set.rs index dd884b6ee41d8..9ddb8dd879870 100644 --- a/src/libcollections/trie/set.rs +++ b/src/libcollections/trie/set.rs @@ -9,7 +9,6 @@ // except according to those terms. // FIXME(conventions): implement bounded iterators -// FIXME(conventions): implement BitOr, BitAnd, BitXor, and Sub // FIXME(conventions): replace each_reverse by making iter DoubleEnded // FIXME(conventions): implement iter_mut and into_iter @@ -463,6 +462,30 @@ impl Extend for TrieSet { } } +impl BitOr for TrieSet { + fn bitor(&self, rhs: &TrieSet) -> TrieSet { + self.union(rhs).collect() + } +} + +impl BitAnd for TrieSet { + fn bitand(&self, rhs: &TrieSet) -> TrieSet { + self.intersection(rhs).collect() + } +} + +impl BitXor for TrieSet { + fn bitxor(&self, rhs: &TrieSet) -> TrieSet { + self.symmetric_difference(rhs).collect() + } +} + +impl Sub for TrieSet { + fn sub(&self, rhs: &TrieSet) -> TrieSet { + self.difference(rhs).collect() + } +} + /// A forward iterator over a set. pub struct SetItems<'a> { iter: Entries<'a, ()> @@ -569,6 +592,7 @@ impl<'a> Iterator for UnionItems<'a> { mod test { use std::prelude::*; use std::uint; + use vec::Vec; use super::TrieSet; @@ -738,4 +762,44 @@ mod test { &[1, 5, 9, 13, 19], &[1, 3, 5, 9, 11, 13, 16, 19, 24]); } + + #[test] + fn test_bit_or() { + let a: TrieSet = vec![1, 2, 3].into_iter().collect(); + let b: TrieSet = vec![3, 4, 5].into_iter().collect(); + + let set: TrieSet = a | b; + let v: Vec = set.iter().collect(); + assert_eq!(v, vec![1u, 2, 3, 4, 5]); + } + + #[test] + fn test_bit_and() { + let a: TrieSet = vec![1, 2, 3].into_iter().collect(); + let b: TrieSet = vec![2, 3, 4].into_iter().collect(); + + let set: TrieSet = a & b; + let v: Vec = set.iter().collect(); + assert_eq!(v, vec![2u, 3]); + } + + #[test] + fn test_bit_xor() { + let a: TrieSet = vec![1, 2, 3].into_iter().collect(); + let b: TrieSet = vec![3, 4, 5].into_iter().collect(); + + let set: TrieSet = a ^ b; + let v: Vec = set.iter().collect(); + assert_eq!(v, vec![1u, 2, 4, 5]); + } + + #[test] + fn test_sub() { + let a: TrieSet = vec![1, 2, 3].into_iter().collect(); + let b: TrieSet = vec![3, 4, 5].into_iter().collect(); + + let set: TrieSet = a - b; + let v: Vec = set.iter().collect(); + assert_eq!(v, vec![1u, 2]); + } } From 74fb798a200dc82cf5b4a18065e3ea565229adc3 Mon Sep 17 00:00:00 2001 From: "NODA, Kai" Date: Fri, 5 Dec 2014 02:05:57 +0800 Subject: [PATCH 77/83] libstd/sys/unix/process.rs: reap a zombie who didn't get through to exec(2). After the library successfully called fork(2), the child does several setup works such as setting UID, GID and current directory before it calls exec(2). When those setup works failed, the child exits but the parent didn't call waitpid(2) and left it as a zombie. This patch also add several sanity checks. They shouldn't make any noticeable impact to runtime performance. The new test case run-pass/wait-forked-but-failed-child.rs calls the ps command to check if the new code can really reap a zombie. When I intentionally create many zombies with my test program ./spawn-failure, The output of "ps -A -o pid,sid,command" should look like this: PID SID COMMAND 1 1 /sbin/init 2 0 [kthreadd] 3 0 [ksoftirqd/0] ... 12562 9237 ./spawn-failure 12563 9237 [spawn-failure] 12564 9237 [spawn-failure] ... 12592 9237 [spawn-failure] 12593 9237 ps -A -o pid,sid,command 12884 12884 /bin/zsh 12922 12922 /bin/zsh ... Filtering the output with the "SID" (session ID) column is a quick way to tell if a process (zombie) was spawned by my own test program. Then the number of "defunct" lines is the number of zombie children. Signed-off-by: NODA, Kai --- src/libstd/sys/unix/process.rs | 43 +++++++--- .../run-pass/wait-forked-but-failed-child.rs | 79 +++++++++++++++++++ 2 files changed, 112 insertions(+), 10 deletions(-) create mode 100644 src/test/run-pass/wait-forked-but-failed-child.rs diff --git a/src/libstd/sys/unix/process.rs b/src/libstd/sys/unix/process.rs index 76c316076f93e..7dde19a64762a 100644 --- a/src/libstd/sys/unix/process.rs +++ b/src/libstd/sys/unix/process.rs @@ -11,7 +11,7 @@ use self::Req::*; use libc::{mod, pid_t, c_void, c_int}; use c_str::CString; -use io::{mod, IoResult, IoError}; +use io::{mod, IoResult, IoError, EndOfFile}; use mem; use os; use ptr; @@ -39,6 +39,8 @@ enum Req { NewChild(libc::pid_t, Sender, u64), } +const CLOEXEC_MSG_FOOTER: &'static [u8] = b"NOEX"; + impl Process { pub fn id(&self) -> pid_t { self.pid @@ -106,18 +108,36 @@ impl Process { if pid < 0 { return Err(super::last_error()) } else if pid > 0 { + #[inline] + fn combine(arr: &[u8]) -> i32 { + let a = arr[0] as u32; + let b = arr[1] as u32; + let c = arr[2] as u32; + let d = arr[3] as u32; + + ((a << 24) | (b << 16) | (c << 8) | (d << 0)) as i32 + } + + let p = Process{ pid: pid }; drop(output); - let mut bytes = [0, ..4]; + let mut bytes = [0, ..8]; return match input.read(&mut bytes) { - Ok(4) => { - let errno = (bytes[0] as i32 << 24) | - (bytes[1] as i32 << 16) | - (bytes[2] as i32 << 8) | - (bytes[3] as i32 << 0); + Ok(8) => { + assert!(combine(CLOEXEC_MSG_FOOTER) == combine(bytes.slice(4, 8)), + "Validation on the CLOEXEC pipe failed: {}", bytes); + let errno = combine(bytes.slice(0, 4)); + assert!(p.wait(0).is_ok(), "wait(0) should either return Ok or panic"); Err(super::decode_error(errno)) } - Err(..) => Ok(Process { pid: pid }), - Ok(..) => panic!("short read on the cloexec pipe"), + Err(ref e) if e.kind == EndOfFile => Ok(p), + Err(e) => { + assert!(p.wait(0).is_ok(), "wait(0) should either return Ok or panic"); + panic!("the CLOEXEC pipe failed: {}", e) + }, + Ok(..) => { // pipe I/O up to PIPE_BUF bytes should be atomic + assert!(p.wait(0).is_ok(), "wait(0) should either return Ok or panic"); + panic!("short read on the CLOEXEC pipe") + } }; } @@ -154,13 +174,16 @@ impl Process { let _ = libc::close(input.fd()); fn fail(output: &mut FileDesc) -> ! { - let errno = sys::os::errno(); + let errno = sys::os::errno() as u32; let bytes = [ (errno >> 24) as u8, (errno >> 16) as u8, (errno >> 8) as u8, (errno >> 0) as u8, + CLOEXEC_MSG_FOOTER[0], CLOEXEC_MSG_FOOTER[1], + CLOEXEC_MSG_FOOTER[2], CLOEXEC_MSG_FOOTER[3] ]; + // pipe I/O up to PIPE_BUF bytes should be atomic assert!(output.write(&bytes).is_ok()); unsafe { libc::_exit(1) } } diff --git a/src/test/run-pass/wait-forked-but-failed-child.rs b/src/test/run-pass/wait-forked-but-failed-child.rs new file mode 100644 index 0000000000000..17dfb9e331945 --- /dev/null +++ b/src/test/run-pass/wait-forked-but-failed-child.rs @@ -0,0 +1,79 @@ +// Copyright 2014 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. + + +extern crate libc; + +use std::io::process::Command; +use std::iter::IteratorExt; + +use libc::funcs::posix88::unistd; + + +// "ps -A -o pid,sid,command" with GNU ps should output something like this: +// PID SID COMMAND +// 1 1 /sbin/init +// 2 0 [kthreadd] +// 3 0 [ksoftirqd/0] +// ... +// 12562 9237 ./spawn-failure +// 12563 9237 [spawn-failure] +// 12564 9237 [spawn-failure] +// ... +// 12592 9237 [spawn-failure] +// 12593 9237 ps -A -o pid,sid,command +// 12884 12884 /bin/zsh +// 12922 12922 /bin/zsh +// ... + +#[cfg(unix)] +fn find_zombies() { + // http://man.freebsd.org/ps(1) + // http://man7.org/linux/man-pages/man1/ps.1.html + #[cfg(not(target_os = "macos"))] + const FIELDS: &'static str = "pid,sid,command"; + + // https://developer.apple.com/library/mac/documentation/Darwin/ + // Reference/ManPages/man1/ps.1.html + #[cfg(target_os = "macos")] + const FIELDS: &'static str = "pid,sess,command"; + + let my_sid = unsafe { unistd::getsid(0) }; + + let ps_cmd_output = Command::new("ps").args(&["-A", "-o", FIELDS]).output().unwrap(); + let ps_output = String::from_utf8_lossy(ps_cmd_output.output.as_slice()); + + let found = ps_output.split('\n').enumerate().any(|(line_no, line)| + 0 < line_no && 0 < line.len() && + my_sid == from_str(line.split(' ').filter(|w| 0 < w.len()).nth(1) + .expect("1st column should be Session ID") + ).expect("Session ID string into integer") && + line.contains("defunct") && { + println!("Zombie child {}", line); + true + } + ); + + assert!( ! found, "Found at least one zombie child"); +} + +#[cfg(windows)] +fn find_zombies() { } + +fn main() { + let too_long = format!("/NoSuchCommand{:0300}", 0u8); + + for _ in range(0u32, 100) { + let invalid = Command::new(too_long.as_slice()).spawn(); + assert!(invalid.is_err()); + } + + find_zombies(); +} From 714ce7919719e6a70719c873dec506765c00686f Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Thu, 4 Dec 2014 20:20:09 -0800 Subject: [PATCH 78/83] Make missing_doc lint check typedefs Closes #19543 --- src/libcore/fmt/mod.rs | 1 + src/librustc/lint/builtin.rs | 1 + src/test/compile-fail/lint-missing-doc.rs | 3 +++ 3 files changed, 5 insertions(+) diff --git a/src/libcore/fmt/mod.rs b/src/libcore/fmt/mod.rs index 1d6906c13a8fa..7b9dd70c58f02 100644 --- a/src/libcore/fmt/mod.rs +++ b/src/libcore/fmt/mod.rs @@ -34,6 +34,7 @@ mod float; pub mod rt; #[experimental = "core and I/O reconciliation may alter this definition"] +/// The type returned by formatter methods. pub type Result = result::Result<(), Error>; /// The error type which is returned from formatting a message into a stream. diff --git a/src/librustc/lint/builtin.rs b/src/librustc/lint/builtin.rs index 9a214d531d157..884615c7aae8c 100644 --- a/src/librustc/lint/builtin.rs +++ b/src/librustc/lint/builtin.rs @@ -1430,6 +1430,7 @@ impl LintPass for MissingDoc { ast::ItemEnum(..) => "an enum", ast::ItemStruct(..) => "a struct", ast::ItemTrait(..) => "a trait", + ast::ItemTy(..) => "a type alias", _ => return }; self.check_missing_docs_attrs(cx, Some(it.id), it.attrs.as_slice(), diff --git a/src/test/compile-fail/lint-missing-doc.rs b/src/test/compile-fail/lint-missing-doc.rs index 365081aee1ab5..8d4ecde692d72 100644 --- a/src/test/compile-fail/lint-missing-doc.rs +++ b/src/test/compile-fail/lint-missing-doc.rs @@ -17,6 +17,9 @@ //! Some garbage docs for the crate here #![doc="More garbage"] +type Typedef = String; +pub type PubTypedef = String; //~ ERROR: missing documentation + struct Foo { a: int, b: int, From 70c1463519e2f505fcc072c374c7189654203c5a Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 5 Dec 2014 00:00:06 -0500 Subject: [PATCH 79/83] Fix various references in late-running tests and things --- src/librustc_driver/test.rs | 36 ++++++++++++++-------------- src/test/run-make/issue-19371/foo.rs | 8 +++---- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/src/librustc_driver/test.rs b/src/librustc_driver/test.rs index 9244e6909e8a4..9404802cb681b 100644 --- a/src/librustc_driver/test.rs +++ b/src/librustc_driver/test.rs @@ -10,28 +10,28 @@ //! # Standalone Tests for the Inference Module -use driver::diagnostic; -use driver::diagnostic::Emitter; -use driver::driver; -use middle::lang_items; -use middle::region::{mod, CodeExtent}; -use middle::resolve; -use middle::resolve_lifetime; -use middle::stability; -use middle::subst; -use middle::subst::Subst; -use middle::ty::{mod, Ty}; -use middle::infer::combine::Combine; -use middle::infer; -use middle::infer::lub::Lub; -use middle::infer::glb::Glb; -use session::{mod,config}; +use diagnostic; +use diagnostic::Emitter; +use driver; +use rustc_typeck::middle::lang_items; +use rustc_typeck::middle::region::{mod, CodeExtent}; +use rustc_typeck::middle::resolve; +use rustc_typeck::middle::resolve_lifetime; +use rustc_typeck::middle::stability; +use rustc_typeck::middle::subst; +use rustc_typeck::middle::subst::Subst; +use rustc_typeck::middle::ty::{mod, Ty}; +use rustc_typeck::middle::infer::combine::Combine; +use rustc_typeck::middle::infer; +use rustc_typeck::middle::infer::lub::Lub; +use rustc_typeck::middle::infer::glb::Glb; +use rustc_typeck::util::ppaux::{ty_to_string, Repr, UserString}; +use rustc::session::{mod,config}; use syntax::{abi, ast, ast_map, ast_util}; use syntax::codemap; use syntax::codemap::{Span, CodeMap, DUMMY_SP}; use syntax::diagnostic::{Level, RenderSpan, Bug, Fatal, Error, Warning, Note, Help}; use syntax::parse::token; -use util::ppaux::{ty_to_string, Repr, UserString}; use arena::TypedArena; @@ -108,7 +108,7 @@ fn test_env(source_string: &str, let sess = session::build_session_(options, None, span_diagnostic_handler); let krate_config = Vec::new(); - let input = driver::StrInput(source_string.to_string()); + let input = config::Input::Str(source_string.to_string()); let krate = driver::phase_1_parse_input(&sess, krate_config, &input); let krate = driver::phase_2_configure_and_expand(&sess, krate, "test", None) .expect("phase 2 aborted"); diff --git a/src/test/run-make/issue-19371/foo.rs b/src/test/run-make/issue-19371/foo.rs index 715fae314b673..8a0c14d2d7e36 100644 --- a/src/test/run-make/issue-19371/foo.rs +++ b/src/test/run-make/issue-19371/foo.rs @@ -9,12 +9,12 @@ // except according to those terms. extern crate rustc; -extern crate rustc_trans; +extern crate rustc_driver; extern crate syntax; use rustc::session::{build_session, Session}; -use rustc::session::config::{basic_options, build_configuration, OutputTypeExe}; -use rustc_trans::driver::driver::{Input, StrInput, compile_input}; +use rustc::session::config::{basic_options, build_configuration, Input, OutputTypeExe}; +use rustc_driver::driver::{compile_input}; use syntax::diagnostics::registry::Registry; fn main() { @@ -55,7 +55,7 @@ fn compile(code: String, output: Path, sysroot: Path) { compile_input(sess, cfg, - &StrInput(code), + &Input::Str(code), &None, &Some(output), None); From 602fc781ff0990b7f94851e210c6a4a516c80935 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 5 Dec 2014 02:01:57 -0500 Subject: [PATCH 80/83] Remove crates from test list so that we don't waste time building them. --- mk/tests.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mk/tests.mk b/mk/tests.mk index e5cde66293c92..b4b8249a8cb42 100644 --- a/mk/tests.mk +++ b/mk/tests.mk @@ -21,7 +21,7 @@ $(eval $(call RUST_CRATE,coretest)) TEST_TARGET_CRATES = $(filter-out core unicode,$(TARGET_CRATES)) coretest TEST_DOC_CRATES = $(DOC_CRATES) -TEST_HOST_CRATES = $(HOST_CRATES) +TEST_HOST_CRATES = $(filter-out rustc_typeck rustc_trans,$(HOST_CRATES)) TEST_CRATES = $(TEST_TARGET_CRATES) $(TEST_HOST_CRATES) ###################################################################### From d6d4088bbf3489bf1a30463406622ac43efea348 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 25 Nov 2014 21:53:08 -0800 Subject: [PATCH 81/83] std: Close TcpListener with closesocket() This may have inadvertently switched during the runtime overhaul, so this switches TcpListener back to using sockets instead of file descriptors. This also renames a bunch of variables called `fd` to `socket` to clearly show that it's not a file descriptor. Closes #19333 --- src/libstd/sys/windows/ext.rs | 4 ++-- src/libstd/sys/windows/tcp.rs | 40 +++++++++++++++++++---------------- 2 files changed, 24 insertions(+), 20 deletions(-) diff --git a/src/libstd/sys/windows/ext.rs b/src/libstd/sys/windows/ext.rs index 2c58ee69e8b7c..049aca3f59064 100644 --- a/src/libstd/sys/windows/ext.rs +++ b/src/libstd/sys/windows/ext.rs @@ -76,13 +76,13 @@ impl AsRawSocket for io::net::tcp::TcpStream { impl AsRawSocket for io::net::tcp::TcpListener { fn as_raw_socket(&self) -> Socket { - self.as_inner().fd() + self.as_inner().socket() } } impl AsRawSocket for io::net::tcp::TcpAcceptor { fn as_raw_socket(&self) -> Socket { - self.as_inner().fd() + self.as_inner().socket() } } diff --git a/src/libstd/sys/windows/tcp.rs b/src/libstd/sys/windows/tcp.rs index 3baf2be08d238..b577372d2fc59 100644 --- a/src/libstd/sys/windows/tcp.rs +++ b/src/libstd/sys/windows/tcp.rs @@ -48,37 +48,35 @@ impl Drop for Event { // TCP listeners //////////////////////////////////////////////////////////////////////////////// -pub struct TcpListener { - inner: FileDesc, -} +pub struct TcpListener { sock: sock_t } impl TcpListener { pub fn bind(addr: ip::SocketAddr) -> IoResult { sys::init_net(); - let fd = try!(socket(addr, libc::SOCK_STREAM)); - let ret = TcpListener { inner: FileDesc::new(fd as libc::c_int, true) }; + let sock = try!(socket(addr, libc::SOCK_STREAM)); + let ret = TcpListener { sock: sock }; let mut storage = unsafe { mem::zeroed() }; let len = addr_to_sockaddr(addr, &mut storage); let addrp = &storage as *const _ as *const libc::sockaddr; - match unsafe { libc::bind(fd, addrp, len) } { + match unsafe { libc::bind(sock, addrp, len) } { -1 => Err(last_net_error()), _ => Ok(ret), } } - pub fn fd(&self) -> sock_t { self.inner.fd as sock_t } + pub fn socket(&self) -> sock_t { self.sock } pub fn listen(self, backlog: int) -> IoResult { - match unsafe { libc::listen(self.fd(), backlog as libc::c_int) } { + match unsafe { libc::listen(self.socket(), backlog as libc::c_int) } { -1 => Err(last_net_error()), _ => { let accept = try!(Event::new()); let ret = unsafe { - c::WSAEventSelect(self.fd(), accept.handle(), c::FD_ACCEPT) + c::WSAEventSelect(self.socket(), accept.handle(), c::FD_ACCEPT) }; if ret != 0 { return Err(last_net_error()) @@ -97,7 +95,13 @@ impl TcpListener { } pub fn socket_name(&mut self) -> IoResult { - sockname(self.fd(), libc::getsockname) + sockname(self.socket(), libc::getsockname) + } +} + +impl Drop for TcpListener { + fn drop(&mut self) { + unsafe { super::close_sock(self.sock); } } } @@ -114,7 +118,7 @@ struct AcceptorInner { } impl TcpAcceptor { - pub fn fd(&self) -> sock_t { self.inner.listener.fd() } + pub fn socket(&self) -> sock_t { self.inner.listener.socket() } pub fn accept(&mut self) -> IoResult { // Unlink unix, windows cannot invoke `select` on arbitrary file @@ -161,13 +165,13 @@ impl TcpAcceptor { let mut wsaevents: c::WSANETWORKEVENTS = unsafe { mem::zeroed() }; let ret = unsafe { - c::WSAEnumNetworkEvents(self.fd(), events[1], &mut wsaevents) + c::WSAEnumNetworkEvents(self.socket(), events[1], &mut wsaevents) }; if ret != 0 { return Err(last_net_error()) } if wsaevents.lNetworkEvents & c::FD_ACCEPT == 0 { continue } match unsafe { - libc::accept(self.fd(), ptr::null_mut(), ptr::null_mut()) + libc::accept(self.socket(), ptr::null_mut(), ptr::null_mut()) } { -1 if wouldblock() => {} -1 => return Err(last_net_error()), @@ -175,13 +179,13 @@ impl TcpAcceptor { // Accepted sockets inherit the same properties as the caller, // so we need to deregister our event and switch the socket back // to blocking mode - fd => { - let stream = TcpStream::new(fd); + socket => { + let stream = TcpStream::new(socket); let ret = unsafe { - c::WSAEventSelect(fd, events[1], 0) + c::WSAEventSelect(socket, events[1], 0) }; if ret != 0 { return Err(last_net_error()) } - try!(set_nonblocking(fd, false)); + try!(set_nonblocking(socket, false)); return Ok(stream) } } @@ -191,7 +195,7 @@ impl TcpAcceptor { } pub fn socket_name(&mut self) -> IoResult { - sockname(self.fd(), libc::getsockname) + sockname(self.socket(), libc::getsockname) } pub fn set_timeout(&mut self, timeout: Option) { From 71d4e77db8ad4b6d821da7e5d5300134ac95974e Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 24 Nov 2014 11:16:40 -0800 Subject: [PATCH 82/83] std: Rewrite the `sync` module This commit is a reimplementation of `std::sync` to be based on the system-provided primitives wherever possible. The previous implementation was fundamentally built on top of channels, and as part of the runtime reform it has become clear that this is not the level of abstraction that the standard level should be providing. This rewrite aims to provide as thin of a shim as possible on top of the system primitives in order to make them safe. The overall interface of the `std::sync` module has in general not changed, but there are a few important distinctions, highlighted below: * The condition variable type, `Condvar`, has been separated out of a `Mutex`. A condition variable is now an entirely separate type. This separation benefits users who only use one mutex, and provides a clearer distinction of who's responsible for managing condition variables (the application). * All of `Condvar`, `Mutex`, and `RWLock` are now directly built on top of system primitives rather than using a custom implementation. The `Once`, `Barrier`, and `Semaphore` types are still built upon these abstractions of the system primitives. * The `Condvar`, `Mutex`, and `RWLock` types all have a new static type and constant initializer corresponding to them. These are provided primarily for C FFI interoperation, but are often useful to otherwise simply have a global lock. The types, however, will leak memory unless `destroy()` is called on them, which is clearly documented. * The `Condvar` implementation for an `RWLock` write lock has been removed. This may be added back in the future with a userspace implementation, but this commit is focused on exposing the system primitives first. * The fundamental architecture of this design is to provide two separate layers. The first layer is that exposed by `sys_common` which is a cross-platform bare-metal abstraction of the system synchronization primitives. No attempt is made at making this layer safe, and it is quite unsafe to use! It is currently not exported as part of the API of the standard library, but the stabilization of the `sys` module will ensure that these will be exposed in time. The purpose of this layer is to provide the core cross-platform abstractions if necessary to implementors. The second layer is the layer provided by `std::sync` which is intended to be the thinnest possible layer on top of `sys_common` which is entirely safe to use. There are a few concerns which need to be addressed when making these system primitives safe: * Once used, the OS primitives can never be **moved**. This means that they essentially need to have a stable address. The static primitives use `&'static self` to enforce this, and the non-static primitives all use a `Box` to provide this guarantee. * Poisoning is leveraged to ensure that invalid data is not accessible from other tasks after one has panicked. In addition to these overall blanket safety limitations, each primitive has a few restrictions of its own: * Mutexes and rwlocks can only be unlocked from the same thread that they were locked by. This is achieved through RAII lock guards which cannot be sent across threads. * Mutexes and rwlocks can only be unlocked if they were previously locked. This is achieved by not exposing an unlocking method. * A condition variable can only be waited on with a locked mutex. This is achieved by requiring a `MutexGuard` in the `wait()` method. * A condition variable cannot be used concurrently with more than one mutex. This is guaranteed by dynamically binding a condition variable to precisely one mutex for its entire lifecycle. This restriction may be able to be relaxed in the future (a mutex is unbound when no threads are waiting on the condvar), but for now it is sufficient to guarantee safety. * Condvars now support timeouts for their blocking operations. The implementation for these operations is provided by the system. Due to the modification of the `Condvar` API, removal of the `std::sync::mutex` API, and reimplementation, this is a breaking change. Most code should be fairly easy to port using the examples in the documentation of these primitives. [breaking-change] Closes #17094 Closes #18003 --- src/libstd/{sync => comm}/mpsc_queue.rs | 9 - src/libstd/{sync => comm}/spsc_queue.rs | 160 ++-- src/libstd/sync/barrier.rs | 116 +++ src/libstd/sync/condvar.rs | 358 +++++++ src/libstd/sync/deque.rs | 663 ------------- src/libstd/sync/future.rs | 2 +- src/libstd/sync/lock.rs | 805 ---------------- src/libstd/sync/mod.rs | 44 +- src/libstd/sync/mpmc_bounded_queue.rs | 219 ----- src/libstd/sync/mutex.rs | 364 ++++++-- src/libstd/sync/{one.rs => once.rs} | 17 +- src/libstd/sync/poison.rs | 48 + src/libstd/sync/raw.rs | 1132 ----------------------- src/libstd/sync/rwlock.rs | 514 ++++++++++ src/libstd/sync/semaphore.rs | 195 ++++ src/libstd/sys/common/condvar.rs | 67 ++ src/libstd/sys/common/mod.rs | 5 +- src/libstd/sys/common/mutex.rs | 64 ++ src/libstd/sys/common/rwlock.rs | 86 ++ src/libstd/sys/unix/condvar.rs | 83 ++ src/libstd/sys/unix/mod.rs | 6 +- src/libstd/sys/unix/mutex.rs | 52 ++ src/libstd/sys/unix/rwlock.rs | 57 ++ src/libstd/sys/unix/sync.rs | 208 +++++ src/libstd/sys/windows/condvar.rs | 63 ++ src/libstd/sys/windows/mod.rs | 4 + src/libstd/sys/windows/mutex.rs | 76 ++ src/libstd/sys/windows/rwlock.rs | 53 ++ src/libstd/sys/windows/sync.rs | 58 ++ 29 files changed, 2480 insertions(+), 3048 deletions(-) rename src/libstd/{sync => comm}/mpsc_queue.rs (95%) rename src/libstd/{sync => comm}/spsc_queue.rs (74%) create mode 100644 src/libstd/sync/barrier.rs create mode 100644 src/libstd/sync/condvar.rs delete mode 100644 src/libstd/sync/deque.rs delete mode 100644 src/libstd/sync/lock.rs delete mode 100644 src/libstd/sync/mpmc_bounded_queue.rs rename src/libstd/sync/{one.rs => once.rs} (96%) create mode 100644 src/libstd/sync/poison.rs delete mode 100644 src/libstd/sync/raw.rs create mode 100644 src/libstd/sync/rwlock.rs create mode 100644 src/libstd/sync/semaphore.rs create mode 100644 src/libstd/sys/common/condvar.rs create mode 100644 src/libstd/sys/common/mutex.rs create mode 100644 src/libstd/sys/common/rwlock.rs create mode 100644 src/libstd/sys/unix/condvar.rs create mode 100644 src/libstd/sys/unix/mutex.rs create mode 100644 src/libstd/sys/unix/rwlock.rs create mode 100644 src/libstd/sys/unix/sync.rs create mode 100644 src/libstd/sys/windows/condvar.rs create mode 100644 src/libstd/sys/windows/mutex.rs create mode 100644 src/libstd/sys/windows/rwlock.rs create mode 100644 src/libstd/sys/windows/sync.rs diff --git a/src/libstd/sync/mpsc_queue.rs b/src/libstd/comm/mpsc_queue.rs similarity index 95% rename from src/libstd/sync/mpsc_queue.rs rename to src/libstd/comm/mpsc_queue.rs index 09212e4dfb65c..d4249abc3dda1 100644 --- a/src/libstd/sync/mpsc_queue.rs +++ b/src/libstd/comm/mpsc_queue.rs @@ -132,15 +132,6 @@ impl Queue { if self.head.load(Acquire) == tail {Empty} else {Inconsistent} } } - - /// Attempts to pop data from this queue, but doesn't attempt too hard. This - /// will canonicalize inconsistent states to a `None` value. - pub fn casual_pop(&self) -> Option { - match self.pop() { - Data(t) => Some(t), - Empty | Inconsistent => None, - } - } } #[unsafe_destructor] diff --git a/src/libstd/sync/spsc_queue.rs b/src/libstd/comm/spsc_queue.rs similarity index 74% rename from src/libstd/sync/spsc_queue.rs rename to src/libstd/comm/spsc_queue.rs index f0eabe6173718..a6b4ab71bacc1 100644 --- a/src/libstd/sync/spsc_queue.rs +++ b/src/libstd/comm/spsc_queue.rs @@ -40,7 +40,6 @@ use core::prelude::*; use alloc::boxed::Box; use core::mem; use core::cell::UnsafeCell; -use alloc::arc::Arc; use sync::atomic::{AtomicPtr, Relaxed, AtomicUint, Acquire, Release}; @@ -74,39 +73,6 @@ pub struct Queue { cache_subtractions: AtomicUint, } -/// A safe abstraction for the consumer in a single-producer single-consumer -/// queue. -pub struct Consumer { - inner: Arc> -} - -impl Consumer { - /// Attempts to pop the value from the head of the queue, returning `None` - /// if the queue is empty. - pub fn pop(&mut self) -> Option { - self.inner.pop() - } - - /// Attempts to peek at the head of the queue, returning `None` if the queue - /// is empty. - pub fn peek<'a>(&'a mut self) -> Option<&'a mut T> { - self.inner.peek() - } -} - -/// A safe abstraction for the producer in a single-producer single-consumer -/// queue. -pub struct Producer { - inner: Arc> -} - -impl Producer { - /// Pushes a new value onto the queue. - pub fn push(&mut self, t: T) { - self.inner.push(t) - } -} - impl Node { fn new() -> *mut Node { unsafe { @@ -118,30 +84,6 @@ impl Node { } } -/// Creates a new queue with a consumer-producer pair. -/// -/// The producer returned is connected to the consumer to push all data to -/// the consumer. -/// -/// # Arguments -/// -/// * `bound` - This queue implementation is implemented with a linked -/// list, and this means that a push is always a malloc. In -/// order to amortize this cost, an internal cache of nodes is -/// maintained to prevent a malloc from always being -/// necessary. This bound is the limit on the size of the -/// cache (if desired). If the value is 0, then the cache has -/// no bound. Otherwise, the cache will never grow larger than -/// `bound` (although the queue itself could be much larger. -pub fn queue(bound: uint) -> (Consumer, Producer) { - let q = unsafe { Queue::new(bound) }; - let arc = Arc::new(q); - let consumer = Consumer { inner: arc.clone() }; - let producer = Producer { inner: arc }; - - (consumer, producer) -} - impl Queue { /// Creates a new queue. /// @@ -296,78 +238,88 @@ impl Drop for Queue { mod test { use prelude::*; - use super::{queue}; + use sync::Arc; + use super::Queue; #[test] fn smoke() { - let (mut consumer, mut producer) = queue(0); - producer.push(1i); - producer.push(2); - assert_eq!(consumer.pop(), Some(1i)); - assert_eq!(consumer.pop(), Some(2)); - assert_eq!(consumer.pop(), None); - producer.push(3); - producer.push(4); - assert_eq!(consumer.pop(), Some(3)); - assert_eq!(consumer.pop(), Some(4)); - assert_eq!(consumer.pop(), None); + unsafe { + let queue = Queue::new(0); + queue.push(1i); + queue.push(2); + assert_eq!(queue.pop(), Some(1i)); + assert_eq!(queue.pop(), Some(2)); + assert_eq!(queue.pop(), None); + queue.push(3); + queue.push(4); + assert_eq!(queue.pop(), Some(3)); + assert_eq!(queue.pop(), Some(4)); + assert_eq!(queue.pop(), None); + } } #[test] fn peek() { - let (mut consumer, mut producer) = queue(0); - producer.push(vec![1i]); + unsafe { + let queue = Queue::new(0); + queue.push(vec![1i]); + + // Ensure the borrowchecker works + match queue.peek() { + Some(vec) => match vec.as_slice() { + // Note that `pop` is not allowed here due to borrow + [1] => {} + _ => return + }, + None => unreachable!() + } - // Ensure the borrowchecker works - match consumer.peek() { - Some(vec) => match vec.as_slice() { - // Note that `pop` is not allowed here due to borrow - [1] => {} - _ => return - }, - None => unreachable!() + queue.pop(); } - - consumer.pop(); } #[test] fn drop_full() { - let (_, mut producer) = queue(0); - producer.push(box 1i); - producer.push(box 2i); + unsafe { + let q = Queue::new(0); + q.push(box 1i); + q.push(box 2i); + } } #[test] fn smoke_bound() { - let (mut consumer, mut producer) = queue(1); - producer.push(1i); - producer.push(2); - assert_eq!(consumer.pop(), Some(1)); - assert_eq!(consumer.pop(), Some(2)); - assert_eq!(consumer.pop(), None); - producer.push(3); - producer.push(4); - assert_eq!(consumer.pop(), Some(3)); - assert_eq!(consumer.pop(), Some(4)); - assert_eq!(consumer.pop(), None); + unsafe { + let q = Queue::new(0); + q.push(1i); + q.push(2); + assert_eq!(q.pop(), Some(1)); + assert_eq!(q.pop(), Some(2)); + assert_eq!(q.pop(), None); + q.push(3); + q.push(4); + assert_eq!(q.pop(), Some(3)); + assert_eq!(q.pop(), Some(4)); + assert_eq!(q.pop(), None); + } } #[test] fn stress() { - stress_bound(0); - stress_bound(1); + unsafe { + stress_bound(0); + stress_bound(1); + } - fn stress_bound(bound: uint) { - let (consumer, mut producer) = queue(bound); + unsafe fn stress_bound(bound: uint) { + let q = Arc::new(Queue::new(bound)); let (tx, rx) = channel(); + let q2 = q.clone(); spawn(proc() { - // Move the consumer to a local mutable slot - let mut consumer = consumer; for _ in range(0u, 100000) { loop { - match consumer.pop() { + match q2.pop() { Some(1i) => break, Some(_) => panic!(), None => {} @@ -377,7 +329,7 @@ mod test { tx.send(()); }); for _ in range(0i, 100000) { - producer.push(1); + q.push(1); } rx.recv(); } diff --git a/src/libstd/sync/barrier.rs b/src/libstd/sync/barrier.rs new file mode 100644 index 0000000000000..5e6dc6ec65083 --- /dev/null +++ b/src/libstd/sync/barrier.rs @@ -0,0 +1,116 @@ +// Copyright 2014 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 sync::{Mutex, Condvar}; + +/// A barrier enables multiple tasks to synchronize the beginning +/// of some computation. +/// +/// ```rust +/// use std::sync::{Arc, Barrier}; +/// +/// let barrier = Arc::new(Barrier::new(10)); +/// for _ in range(0u, 10) { +/// let c = barrier.clone(); +/// // The same messages will be printed together. +/// // You will NOT see any interleaving. +/// spawn(proc() { +/// println!("before wait"); +/// c.wait(); +/// println!("after wait"); +/// }); +/// } +/// ``` +pub struct Barrier { + lock: Mutex, + cvar: Condvar, + num_threads: uint, +} + +// The inner state of a double barrier +struct BarrierState { + count: uint, + generation_id: uint, +} + +impl Barrier { + /// Create a new barrier that can block a given number of threads. + /// + /// A barrier will block `n`-1 threads which call `wait` and then wake up + /// all threads at once when the `n`th thread calls `wait`. + pub fn new(n: uint) -> Barrier { + Barrier { + lock: Mutex::new(BarrierState { + count: 0, + generation_id: 0, + }), + cvar: Condvar::new(), + num_threads: n, + } + } + + /// Block the current thread until all threads has rendezvoused here. + /// + /// Barriers are re-usable after all threads have rendezvoused once, and can + /// be used continuously. + pub fn wait(&self) { + let mut lock = self.lock.lock(); + let local_gen = lock.generation_id; + lock.count += 1; + if lock.count < self.num_threads { + // We need a while loop to guard against spurious wakeups. + // http://en.wikipedia.org/wiki/Spurious_wakeup + while local_gen == lock.generation_id && + lock.count < self.num_threads { + self.cvar.wait(&lock); + } + } else { + lock.count = 0; + lock.generation_id += 1; + self.cvar.notify_all(); + } + } +} + +#[cfg(test)] +mod tests { + use prelude::*; + + use sync::{Arc, Barrier}; + use comm::Empty; + + #[test] + fn test_barrier() { + let barrier = Arc::new(Barrier::new(10)); + let (tx, rx) = channel(); + + for _ in range(0u, 9) { + let c = barrier.clone(); + let tx = tx.clone(); + spawn(proc() { + c.wait(); + tx.send(true); + }); + } + + // At this point, all spawned tasks should be blocked, + // so we shouldn't get anything from the port + assert!(match rx.try_recv() { + Err(Empty) => true, + _ => false, + }); + + barrier.wait(); + // Now, the barrier is cleared and we should get data. + for _ in range(0u, 9) { + rx.recv(); + } + } +} diff --git a/src/libstd/sync/condvar.rs b/src/libstd/sync/condvar.rs new file mode 100644 index 0000000000000..581b6b4e41202 --- /dev/null +++ b/src/libstd/sync/condvar.rs @@ -0,0 +1,358 @@ +// Copyright 2014 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 prelude::*; + +use sync::atomic::{mod, AtomicUint}; +use sync::{mutex, StaticMutexGuard}; +use sys_common::condvar as sys; +use sys_common::mutex as sys_mutex; +use time::Duration; + +/// A Condition Variable +/// +/// Condition variables represent the ability to block a thread such that it +/// consumes no CPU time while waiting for an event to occur. Condition +/// variables are typically associated with a boolean predicate (a condition) +/// and a mutex. The predicate is always verified inside of the mutex before +/// determining that thread must block. +/// +/// Functions in this module will block the current **thread** of execution and +/// are bindings to system-provided condition variables where possible. Note +/// that this module places one additional restriction over the system condition +/// variables: each condvar can be used with precisely one mutex at runtime. Any +/// attempt to use multiple mutexes on the same condition variable will result +/// in a runtime panic. If this is not desired, then the unsafe primitives in +/// `sys` do not have this restriction but may result in undefined behavior. +/// +/// # Example +/// +/// ``` +/// use std::sync::{Arc, Mutex, Condvar}; +/// +/// let pair = Arc::new((Mutex::new(false), Condvar::new())); +/// let pair2 = pair.clone(); +/// +/// // Inside of our lock, spawn a new thread, and then wait for it to start +/// spawn(proc() { +/// let &(ref lock, ref cvar) = &*pair2; +/// let mut started = lock.lock(); +/// *started = true; +/// cvar.notify_one(); +/// }); +/// +/// // wait for the thread to start up +/// let &(ref lock, ref cvar) = &*pair; +/// let started = lock.lock(); +/// while !*started { +/// cvar.wait(&started); +/// } +/// ``` +pub struct Condvar { inner: Box } + +/// Statically allocated condition variables. +/// +/// This structure is identical to `Condvar` except that it is suitable for use +/// in static initializers for other structures. +/// +/// # Example +/// +/// ``` +/// use std::sync::{StaticCondvar, CONDVAR_INIT}; +/// +/// static CVAR: StaticCondvar = CONDVAR_INIT; +/// ``` +pub struct StaticCondvar { + inner: sys::Condvar, + mutex: AtomicUint, +} + +/// Constant initializer for a statically allocated condition variable. +pub const CONDVAR_INIT: StaticCondvar = StaticCondvar { + inner: sys::CONDVAR_INIT, + mutex: atomic::INIT_ATOMIC_UINT, +}; + +/// A trait for vaules which can be passed to the waiting methods of condition +/// variables. This is implemented by the mutex guards in this module. +/// +/// Note that this trait should likely not be implemented manually unless you +/// really know what you're doing. +pub trait AsMutexGuard { + #[allow(missing_docs)] + unsafe fn as_mutex_guard(&self) -> &StaticMutexGuard; +} + +impl Condvar { + /// Creates a new condition variable which is ready to be waited on and + /// notified. + pub fn new() -> Condvar { + Condvar { + inner: box StaticCondvar { + inner: unsafe { sys::Condvar::new() }, + mutex: AtomicUint::new(0), + } + } + } + + /// Block the current thread until this condition variable receives a + /// notification. + /// + /// This function will atomically unlock the mutex specified (represented by + /// `guard`) and block the current thread. This means that any calls to + /// `notify_*()` which happen logically after the mutex is unlocked are + /// candidates to wake this thread up. When this function call returns, the + /// lock specified will have been re-acquired. + /// + /// Note that this function is susceptible to spurious wakeups. Condition + /// variables normally have a boolean predicate associated with them, and + /// the predicate must always be checked each time this function returns to + /// protect against spurious wakeups. + /// + /// # Panics + /// + /// This function will `panic!()` if it is used with more than one mutex + /// over time. Each condition variable is dynamically bound to exactly one + /// mutex to ensure defined behavior across platforms. If this functionality + /// is not desired, then unsafe primitives in `sys` are provided. + pub fn wait(&self, mutex_guard: &T) { + unsafe { + let me: &'static Condvar = &*(self as *const _); + me.inner.wait(mutex_guard) + } + } + + /// Wait on this condition variable for a notification, timing out after a + /// specified duration. + /// + /// The semantics of this function are equivalent to `wait()` except that + /// the thread will be blocked for roughly no longer than `dur`. This method + /// should not be used for precise timing due to anomalies such as + /// preemption or platform differences that may not cause the maximum amount + /// of time waited to be precisely `dur`. + /// + /// If the wait timed out, then `false` will be returned. Otherwise if a + /// notification was received then `true` will be returned. + /// + /// Like `wait`, the lock specified will be re-acquired when this function + /// returns, regardless of whether the timeout elapsed or not. + pub fn wait_timeout(&self, mutex_guard: &T, + dur: Duration) -> bool { + unsafe { + let me: &'static Condvar = &*(self as *const _); + me.inner.wait_timeout(mutex_guard, dur) + } + } + + /// Wake up one blocked thread on this condvar. + /// + /// If there is a blocked thread on this condition variable, then it will + /// be woken up from its call to `wait` or `wait_timeout`. Calls to + /// `notify_one` are not buffered in any way. + /// + /// To wake up all threads, see `notify_one()`. + pub fn notify_one(&self) { unsafe { self.inner.inner.notify_one() } } + + /// Wake up all blocked threads on this condvar. + /// + /// This method will ensure that any current waiters on the condition + /// variable are awoken. Calls to `notify_all()` are not buffered in any + /// way. + /// + /// To wake up only one thread, see `notify_one()`. + pub fn notify_all(&self) { unsafe { self.inner.inner.notify_all() } } +} + +impl Drop for Condvar { + fn drop(&mut self) { + unsafe { self.inner.inner.destroy() } + } +} + +impl StaticCondvar { + /// Block the current thread until this condition variable receives a + /// notification. + /// + /// See `Condvar::wait`. + pub fn wait(&'static self, mutex_guard: &T) { + unsafe { + let lock = mutex_guard.as_mutex_guard(); + let sys = mutex::guard_lock(lock); + self.verify(sys); + self.inner.wait(sys); + (*mutex::guard_poison(lock)).check("mutex"); + } + } + + /// Wait on this condition variable for a notification, timing out after a + /// specified duration. + /// + /// See `Condvar::wait_timeout`. + pub fn wait_timeout(&'static self, mutex_guard: &T, + dur: Duration) -> bool { + unsafe { + let lock = mutex_guard.as_mutex_guard(); + let sys = mutex::guard_lock(lock); + self.verify(sys); + let ret = self.inner.wait_timeout(sys, dur); + (*mutex::guard_poison(lock)).check("mutex"); + return ret; + } + } + + /// Wake up one blocked thread on this condvar. + /// + /// See `Condvar::notify_one`. + pub fn notify_one(&'static self) { unsafe { self.inner.notify_one() } } + + /// Wake up all blocked threads on this condvar. + /// + /// See `Condvar::notify_all`. + pub fn notify_all(&'static self) { unsafe { self.inner.notify_all() } } + + /// Deallocate all resources associated with this static condvar. + /// + /// This method is unsafe to call as there is no guarantee that there are no + /// active users of the condvar, and this also doesn't prevent any future + /// users of the condvar. This method is required to be called to not leak + /// memory on all platforms. + pub unsafe fn destroy(&'static self) { + self.inner.destroy() + } + + fn verify(&self, mutex: &sys_mutex::Mutex) { + let addr = mutex as *const _ as uint; + match self.mutex.compare_and_swap(0, addr, atomic::SeqCst) { + // If we got out 0, then we have successfully bound the mutex to + // this cvar. + 0 => {} + + // If we get out a value that's the same as `addr`, then someone + // already beat us to the punch. + n if n == addr => {} + + // Anything else and we're using more than one mutex on this cvar, + // which is currently disallowed. + _ => panic!("attempted to use a condition variable with two \ + mutexes"), + } + } +} + +#[cfg(test)] +mod tests { + use prelude::*; + + use time::Duration; + use super::{StaticCondvar, CONDVAR_INIT}; + use sync::{StaticMutex, MUTEX_INIT, Condvar, Mutex, Arc}; + + #[test] + fn smoke() { + let c = Condvar::new(); + c.notify_one(); + c.notify_all(); + } + + #[test] + fn static_smoke() { + static C: StaticCondvar = CONDVAR_INIT; + C.notify_one(); + C.notify_all(); + unsafe { C.destroy(); } + } + + #[test] + fn notify_one() { + static C: StaticCondvar = CONDVAR_INIT; + static M: StaticMutex = MUTEX_INIT; + + let g = M.lock(); + spawn(proc() { + let _g = M.lock(); + C.notify_one(); + }); + C.wait(&g); + drop(g); + unsafe { C.destroy(); M.destroy(); } + } + + #[test] + fn notify_all() { + const N: uint = 10; + + let data = Arc::new((Mutex::new(0), Condvar::new())); + let (tx, rx) = channel(); + for _ in range(0, N) { + let data = data.clone(); + let tx = tx.clone(); + spawn(proc() { + let &(ref lock, ref cond) = &*data; + let mut cnt = lock.lock(); + *cnt += 1; + if *cnt == N { + tx.send(()); + } + while *cnt != 0 { + cond.wait(&cnt); + } + tx.send(()); + }); + } + drop(tx); + + let &(ref lock, ref cond) = &*data; + rx.recv(); + let mut cnt = lock.lock(); + *cnt = 0; + cond.notify_all(); + drop(cnt); + + for _ in range(0, N) { + rx.recv(); + } + } + + #[test] + fn wait_timeout() { + static C: StaticCondvar = CONDVAR_INIT; + static M: StaticMutex = MUTEX_INIT; + + let g = M.lock(); + assert!(!C.wait_timeout(&g, Duration::nanoseconds(1000))); + spawn(proc() { + let _g = M.lock(); + C.notify_one(); + }); + assert!(C.wait_timeout(&g, Duration::days(1))); + drop(g); + unsafe { C.destroy(); M.destroy(); } + } + + #[test] + #[should_fail] + fn two_mutexes() { + static M1: StaticMutex = MUTEX_INIT; + static M2: StaticMutex = MUTEX_INIT; + static C: StaticCondvar = CONDVAR_INIT; + + let g = M1.lock(); + spawn(proc() { + let _g = M1.lock(); + C.notify_one(); + }); + C.wait(&g); + drop(g); + + C.wait(&M2.lock()); + + } +} + diff --git a/src/libstd/sync/deque.rs b/src/libstd/sync/deque.rs deleted file mode 100644 index 33f6f77eb62a3..0000000000000 --- a/src/libstd/sync/deque.rs +++ /dev/null @@ -1,663 +0,0 @@ -// Copyright 2013 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. - -//! A (mostly) lock-free concurrent work-stealing deque -//! -//! This module contains an implementation of the Chase-Lev work stealing deque -//! described in "Dynamic Circular Work-Stealing Deque". The implementation is -//! heavily based on the pseudocode found in the paper. -//! -//! This implementation does not want to have the restriction of a garbage -//! collector for reclamation of buffers, and instead it uses a shared pool of -//! buffers. This shared pool is required for correctness in this -//! implementation. -//! -//! The only lock-synchronized portions of this deque are the buffer allocation -//! and deallocation portions. Otherwise all operations are lock-free. -//! -//! # Example -//! -//! use std::sync::deque::BufferPool; -//! -//! let mut pool = BufferPool::new(); -//! let (mut worker, mut stealer) = pool.deque(); -//! -//! // Only the worker may push/pop -//! worker.push(1i); -//! worker.pop(); -//! -//! // Stealers take data from the other end of the deque -//! worker.push(1i); -//! stealer.steal(); -//! -//! // Stealers can be cloned to have many stealers stealing in parallel -//! worker.push(1i); -//! let mut stealer2 = stealer.clone(); -//! stealer2.steal(); - -#![experimental] - -// NB: the "buffer pool" strategy is not done for speed, but rather for -// correctness. For more info, see the comment on `swap_buffer` - -// FIXME: all atomic operations in this module use a SeqCst ordering. That is -// probably overkill - -pub use self::Stolen::*; - -use core::prelude::*; - -use alloc::arc::Arc; -use alloc::heap::{allocate, deallocate}; -use alloc::boxed::Box; -use vec::Vec; -use core::kinds::marker; -use core::mem::{forget, min_align_of, size_of, transmute}; -use core::ptr; -use rustrt::exclusive::Exclusive; - -use sync::atomic::{AtomicInt, AtomicPtr, SeqCst}; - -// Once the queue is less than 1/K full, then it will be downsized. Note that -// the deque requires that this number be less than 2. -static K: int = 4; - -// Minimum number of bits that a buffer size should be. No buffer will resize to -// under this value, and all deques will initially contain a buffer of this -// size. -// -// The size in question is 1 << MIN_BITS -static MIN_BITS: uint = 7; - -struct Deque { - bottom: AtomicInt, - top: AtomicInt, - array: AtomicPtr>, - pool: BufferPool, -} - -/// Worker half of the work-stealing deque. This worker has exclusive access to -/// one side of the deque, and uses `push` and `pop` method to manipulate it. -/// -/// There may only be one worker per deque. -pub struct Worker { - deque: Arc>, - _noshare: marker::NoSync, -} - -/// The stealing half of the work-stealing deque. Stealers have access to the -/// opposite end of the deque from the worker, and they only have access to the -/// `steal` method. -pub struct Stealer { - deque: Arc>, - _noshare: marker::NoSync, -} - -/// When stealing some data, this is an enumeration of the possible outcomes. -#[deriving(PartialEq, Show)] -pub enum Stolen { - /// The deque was empty at the time of stealing - Empty, - /// The stealer lost the race for stealing data, and a retry may return more - /// data. - Abort, - /// The stealer has successfully stolen some data. - Data(T), -} - -/// The allocation pool for buffers used by work-stealing deques. Right now this -/// structure is used for reclamation of memory after it is no longer in use by -/// deques. -/// -/// This data structure is protected by a mutex, but it is rarely used. Deques -/// will only use this structure when allocating a new buffer or deallocating a -/// previous one. -pub struct BufferPool { - pool: Arc>>>>, -} - -/// An internal buffer used by the chase-lev deque. This structure is actually -/// implemented as a circular buffer, and is used as the intermediate storage of -/// the data in the deque. -/// -/// This type is implemented with *T instead of Vec for two reasons: -/// -/// 1. There is nothing safe about using this buffer. This easily allows the -/// same value to be read twice in to rust, and there is nothing to -/// prevent this. The usage by the deque must ensure that one of the -/// values is forgotten. Furthermore, we only ever want to manually run -/// destructors for values in this buffer (on drop) because the bounds -/// are defined by the deque it's owned by. -/// -/// 2. We can certainly avoid bounds checks using *T instead of Vec, although -/// LLVM is probably pretty good at doing this already. -struct Buffer { - storage: *const T, - log_size: uint, -} - -impl BufferPool { - /// Allocates a new buffer pool which in turn can be used to allocate new - /// deques. - pub fn new() -> BufferPool { - BufferPool { pool: Arc::new(Exclusive::new(Vec::new())) } - } - - /// Allocates a new work-stealing deque which will send/receiving memory to - /// and from this buffer pool. - pub fn deque(&self) -> (Worker, Stealer) { - let a = Arc::new(Deque::new(self.clone())); - let b = a.clone(); - (Worker { deque: a, _noshare: marker::NoSync }, - Stealer { deque: b, _noshare: marker::NoSync }) - } - - fn alloc(&mut self, bits: uint) -> Box> { - unsafe { - let mut pool = self.pool.lock(); - match pool.iter().position(|x| x.size() >= (1 << bits)) { - Some(i) => pool.remove(i).unwrap(), - None => box Buffer::new(bits) - } - } - } - - fn free(&self, buf: Box>) { - unsafe { - let mut pool = self.pool.lock(); - match pool.iter().position(|v| v.size() > buf.size()) { - Some(i) => pool.insert(i, buf), - None => pool.push(buf), - } - } - } -} - -impl Clone for BufferPool { - fn clone(&self) -> BufferPool { BufferPool { pool: self.pool.clone() } } -} - -impl Worker { - /// Pushes data onto the front of this work queue. - pub fn push(&self, t: T) { - unsafe { self.deque.push(t) } - } - /// Pops data off the front of the work queue, returning `None` on an empty - /// queue. - pub fn pop(&self) -> Option { - unsafe { self.deque.pop() } - } - - /// Gets access to the buffer pool that this worker is attached to. This can - /// be used to create more deques which share the same buffer pool as this - /// deque. - pub fn pool<'a>(&'a self) -> &'a BufferPool { - &self.deque.pool - } -} - -impl Stealer { - /// Steals work off the end of the queue (opposite of the worker's end) - pub fn steal(&self) -> Stolen { - unsafe { self.deque.steal() } - } - - /// Gets access to the buffer pool that this stealer is attached to. This - /// can be used to create more deques which share the same buffer pool as - /// this deque. - pub fn pool<'a>(&'a self) -> &'a BufferPool { - &self.deque.pool - } -} - -impl Clone for Stealer { - fn clone(&self) -> Stealer { - Stealer { deque: self.deque.clone(), _noshare: marker::NoSync } - } -} - -// Almost all of this code can be found directly in the paper so I'm not -// personally going to heavily comment what's going on here. - -impl Deque { - fn new(mut pool: BufferPool) -> Deque { - let buf = pool.alloc(MIN_BITS); - Deque { - bottom: AtomicInt::new(0), - top: AtomicInt::new(0), - array: AtomicPtr::new(unsafe { transmute(buf) }), - pool: pool, - } - } - - unsafe fn push(&self, data: T) { - let mut b = self.bottom.load(SeqCst); - let t = self.top.load(SeqCst); - let mut a = self.array.load(SeqCst); - let size = b - t; - if size >= (*a).size() - 1 { - // You won't find this code in the chase-lev deque paper. This is - // alluded to in a small footnote, however. We always free a buffer - // when growing in order to prevent leaks. - a = self.swap_buffer(b, a, (*a).resize(b, t, 1)); - b = self.bottom.load(SeqCst); - } - (*a).put(b, data); - self.bottom.store(b + 1, SeqCst); - } - - unsafe fn pop(&self) -> Option { - let b = self.bottom.load(SeqCst); - let a = self.array.load(SeqCst); - let b = b - 1; - self.bottom.store(b, SeqCst); - let t = self.top.load(SeqCst); - let size = b - t; - if size < 0 { - self.bottom.store(t, SeqCst); - return None; - } - let data = (*a).get(b); - if size > 0 { - self.maybe_shrink(b, t); - return Some(data); - } - if self.top.compare_and_swap(t, t + 1, SeqCst) == t { - self.bottom.store(t + 1, SeqCst); - return Some(data); - } else { - self.bottom.store(t + 1, SeqCst); - forget(data); // someone else stole this value - return None; - } - } - - unsafe fn steal(&self) -> Stolen { - let t = self.top.load(SeqCst); - let old = self.array.load(SeqCst); - let b = self.bottom.load(SeqCst); - let a = self.array.load(SeqCst); - let size = b - t; - if size <= 0 { return Empty } - if size % (*a).size() == 0 { - if a == old && t == self.top.load(SeqCst) { - return Empty - } - return Abort - } - let data = (*a).get(t); - if self.top.compare_and_swap(t, t + 1, SeqCst) == t { - Data(data) - } else { - forget(data); // someone else stole this value - Abort - } - } - - unsafe fn maybe_shrink(&self, b: int, t: int) { - let a = self.array.load(SeqCst); - if b - t < (*a).size() / K && b - t > (1 << MIN_BITS) { - self.swap_buffer(b, a, (*a).resize(b, t, -1)); - } - } - - // Helper routine not mentioned in the paper which is used in growing and - // shrinking buffers to swap in a new buffer into place. As a bit of a - // recap, the whole point that we need a buffer pool rather than just - // calling malloc/free directly is that stealers can continue using buffers - // after this method has called 'free' on it. The continued usage is simply - // a read followed by a forget, but we must make sure that the memory can - // continue to be read after we flag this buffer for reclamation. - unsafe fn swap_buffer(&self, b: int, old: *mut Buffer, - buf: Buffer) -> *mut Buffer { - let newbuf: *mut Buffer = transmute(box buf); - self.array.store(newbuf, SeqCst); - let ss = (*newbuf).size(); - self.bottom.store(b + ss, SeqCst); - let t = self.top.load(SeqCst); - if self.top.compare_and_swap(t, t + ss, SeqCst) != t { - self.bottom.store(b, SeqCst); - } - self.pool.free(transmute(old)); - return newbuf; - } -} - - -#[unsafe_destructor] -impl Drop for Deque { - fn drop(&mut self) { - let t = self.top.load(SeqCst); - let b = self.bottom.load(SeqCst); - let a = self.array.load(SeqCst); - // Free whatever is leftover in the dequeue, and then move the buffer - // back into the pool. - for i in range(t, b) { - let _: T = unsafe { (*a).get(i) }; - } - self.pool.free(unsafe { transmute(a) }); - } -} - -#[inline] -fn buffer_alloc_size(log_size: uint) -> uint { - (1 << log_size) * size_of::() -} - -impl Buffer { - unsafe fn new(log_size: uint) -> Buffer { - let size = buffer_alloc_size::(log_size); - let buffer = allocate(size, min_align_of::()); - if buffer.is_null() { ::alloc::oom() } - Buffer { - storage: buffer as *const T, - log_size: log_size, - } - } - - fn size(&self) -> int { 1 << self.log_size } - - // Apparently LLVM cannot optimize (foo % (1 << bar)) into this implicitly - fn mask(&self) -> int { (1 << self.log_size) - 1 } - - unsafe fn elem(&self, i: int) -> *const T { - self.storage.offset(i & self.mask()) - } - - // This does not protect against loading duplicate values of the same cell, - // nor does this clear out the contents contained within. Hence, this is a - // very unsafe method which the caller needs to treat specially in case a - // race is lost. - unsafe fn get(&self, i: int) -> T { - ptr::read(self.elem(i)) - } - - // Unsafe because this unsafely overwrites possibly uninitialized or - // initialized data. - unsafe fn put(&self, i: int, t: T) { - ptr::write(self.elem(i) as *mut T, t); - } - - // Again, unsafe because this has incredibly dubious ownership violations. - // It is assumed that this buffer is immediately dropped. - unsafe fn resize(&self, b: int, t: int, delta: int) -> Buffer { - // NB: not entirely obvious, but thanks to 2's complement, - // casting delta to uint and then adding gives the desired - // effect. - let buf = Buffer::new(self.log_size + delta as uint); - for i in range(t, b) { - buf.put(i, self.get(i)); - } - return buf; - } -} - -#[unsafe_destructor] -impl Drop for Buffer { - fn drop(&mut self) { - // It is assumed that all buffers are empty on drop. - let size = buffer_alloc_size::(self.log_size); - unsafe { deallocate(self.storage as *mut u8, size, min_align_of::()) } - } -} - -#[cfg(test)] -mod tests { - use prelude::*; - use super::{Data, BufferPool, Abort, Empty, Worker, Stealer}; - - use mem; - use rustrt::thread::Thread; - use rand; - use rand::Rng; - use sync::atomic::{AtomicBool, INIT_ATOMIC_BOOL, SeqCst, - AtomicUint, INIT_ATOMIC_UINT}; - use vec; - - #[test] - fn smoke() { - let pool = BufferPool::new(); - let (w, s) = pool.deque(); - assert_eq!(w.pop(), None); - assert_eq!(s.steal(), Empty); - w.push(1i); - assert_eq!(w.pop(), Some(1)); - w.push(1); - assert_eq!(s.steal(), Data(1)); - w.push(1); - assert_eq!(s.clone().steal(), Data(1)); - } - - #[test] - fn stealpush() { - static AMT: int = 100000; - let pool = BufferPool::::new(); - let (w, s) = pool.deque(); - let t = Thread::start(proc() { - let mut left = AMT; - while left > 0 { - match s.steal() { - Data(i) => { - assert_eq!(i, 1); - left -= 1; - } - Abort | Empty => {} - } - } - }); - - for _ in range(0, AMT) { - w.push(1); - } - - t.join(); - } - - #[test] - fn stealpush_large() { - static AMT: int = 100000; - let pool = BufferPool::<(int, int)>::new(); - let (w, s) = pool.deque(); - let t = Thread::start(proc() { - let mut left = AMT; - while left > 0 { - match s.steal() { - Data((1, 10)) => { left -= 1; } - Data(..) => panic!(), - Abort | Empty => {} - } - } - }); - - for _ in range(0, AMT) { - w.push((1, 10)); - } - - t.join(); - } - - fn stampede(w: Worker>, s: Stealer>, - nthreads: int, amt: uint) { - for _ in range(0, amt) { - w.push(box 20); - } - let mut remaining = AtomicUint::new(amt); - let unsafe_remaining: *mut AtomicUint = &mut remaining; - - let threads = range(0, nthreads).map(|_| { - let s = s.clone(); - Thread::start(proc() { - unsafe { - while (*unsafe_remaining).load(SeqCst) > 0 { - match s.steal() { - Data(box 20) => { - (*unsafe_remaining).fetch_sub(1, SeqCst); - } - Data(..) => panic!(), - Abort | Empty => {} - } - } - } - }) - }).collect::>>(); - - while remaining.load(SeqCst) > 0 { - match w.pop() { - Some(box 20) => { remaining.fetch_sub(1, SeqCst); } - Some(..) => panic!(), - None => {} - } - } - - for thread in threads.into_iter() { - thread.join(); - } - } - - #[test] - fn run_stampede() { - let pool = BufferPool::>::new(); - let (w, s) = pool.deque(); - stampede(w, s, 8, 10000); - } - - #[test] - fn many_stampede() { - static AMT: uint = 4; - let pool = BufferPool::>::new(); - let threads = range(0, AMT).map(|_| { - let (w, s) = pool.deque(); - Thread::start(proc() { - stampede(w, s, 4, 10000); - }) - }).collect::>>(); - - for thread in threads.into_iter() { - thread.join(); - } - } - - #[test] - fn stress() { - static AMT: int = 100000; - static NTHREADS: int = 8; - static DONE: AtomicBool = INIT_ATOMIC_BOOL; - static HITS: AtomicUint = INIT_ATOMIC_UINT; - let pool = BufferPool::::new(); - let (w, s) = pool.deque(); - - let threads = range(0, NTHREADS).map(|_| { - let s = s.clone(); - Thread::start(proc() { - loop { - match s.steal() { - Data(2) => { HITS.fetch_add(1, SeqCst); } - Data(..) => panic!(), - _ if DONE.load(SeqCst) => break, - _ => {} - } - } - }) - }).collect::>>(); - - let mut rng = rand::task_rng(); - let mut expected = 0; - while expected < AMT { - if rng.gen_range(0i, 3) == 2 { - match w.pop() { - None => {} - Some(2) => { HITS.fetch_add(1, SeqCst); }, - Some(_) => panic!(), - } - } else { - expected += 1; - w.push(2); - } - } - - while HITS.load(SeqCst) < AMT as uint { - match w.pop() { - None => {} - Some(2) => { HITS.fetch_add(1, SeqCst); }, - Some(_) => panic!(), - } - } - DONE.store(true, SeqCst); - - for thread in threads.into_iter() { - thread.join(); - } - - assert_eq!(HITS.load(SeqCst), expected as uint); - } - - #[test] - #[cfg_attr(windows, ignore)] // apparently windows scheduling is weird? - fn no_starvation() { - static AMT: int = 10000; - static NTHREADS: int = 4; - static DONE: AtomicBool = INIT_ATOMIC_BOOL; - let pool = BufferPool::<(int, uint)>::new(); - let (w, s) = pool.deque(); - - let (threads, hits) = vec::unzip(range(0, NTHREADS).map(|_| { - let s = s.clone(); - let unique_box = box AtomicUint::new(0); - let thread_box = unsafe { - *mem::transmute::<&Box, - *const *mut AtomicUint>(&unique_box) - }; - (Thread::start(proc() { - unsafe { - loop { - match s.steal() { - Data((1, 2)) => { - (*thread_box).fetch_add(1, SeqCst); - } - Data(..) => panic!(), - _ if DONE.load(SeqCst) => break, - _ => {} - } - } - } - }), unique_box) - })); - - let mut rng = rand::task_rng(); - let mut myhit = false; - 'outer: loop { - for _ in range(0, rng.gen_range(0, AMT)) { - if !myhit && rng.gen_range(0i, 3) == 2 { - match w.pop() { - None => {} - Some((1, 2)) => myhit = true, - Some(_) => panic!(), - } - } else { - w.push((1, 2)); - } - } - - for slot in hits.iter() { - let amt = slot.load(SeqCst); - if amt == 0 { continue 'outer; } - } - if myhit { - break - } - } - - DONE.store(true, SeqCst); - - for thread in threads.into_iter() { - thread.join(); - } - } -} diff --git a/src/libstd/sync/future.rs b/src/libstd/sync/future.rs index f2f9351fd0d58..79e0d487cadb9 100644 --- a/src/libstd/sync/future.rs +++ b/src/libstd/sync/future.rs @@ -148,7 +148,7 @@ mod test { use prelude::*; use sync::Future; use task; - use comm::{channel, Sender}; + use comm::channel; #[test] fn test_from_value() { diff --git a/src/libstd/sync/lock.rs b/src/libstd/sync/lock.rs deleted file mode 100644 index 77f5b01351908..0000000000000 --- a/src/libstd/sync/lock.rs +++ /dev/null @@ -1,805 +0,0 @@ -// Copyright 2012-2014 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. - -//! Wrappers for safe, shared, mutable memory between tasks -//! -//! The wrappers in this module build on the primitives from `sync::raw` to -//! provide safe interfaces around using the primitive locks. These primitives -//! implement a technique called "poisoning" where when a task panicked with a -//! held lock, all future attempts to use the lock will panic. -//! -//! For example, if two tasks are contending on a mutex and one of them panics -//! after grabbing the lock, the second task will immediately panic because the -//! lock is now poisoned. - -use core::prelude::*; - -use self::Inner::*; - -use core::cell::UnsafeCell; -use rustrt::local::Local; -use rustrt::task::Task; - -use super::raw; - -// Poisoning helpers - -struct PoisonOnFail<'a> { - flag: &'a mut bool, - failed: bool, -} - -fn failing() -> bool { - Local::borrow(None::).unwinder.unwinding() -} - -impl<'a> PoisonOnFail<'a> { - fn check(flag: bool, name: &str) { - if flag { - panic!("Poisoned {} - another task failed inside!", name); - } - } - - fn new<'a>(flag: &'a mut bool, name: &str) -> PoisonOnFail<'a> { - PoisonOnFail::check(*flag, name); - PoisonOnFail { - flag: flag, - failed: failing() - } - } -} - -#[unsafe_destructor] -impl<'a> Drop for PoisonOnFail<'a> { - fn drop(&mut self) { - if !self.failed && failing() { - *self.flag = true; - } - } -} - -// Condvar - -enum Inner<'a> { - InnerMutex(raw::MutexGuard<'a>), - InnerRWLock(raw::RWLockWriteGuard<'a>), -} - -impl<'b> Inner<'b> { - fn cond<'a>(&'a self) -> &'a raw::Condvar<'b> { - match *self { - InnerMutex(ref m) => &m.cond, - InnerRWLock(ref m) => &m.cond, - } - } -} - -/// A condition variable, a mechanism for unlock-and-descheduling and -/// signaling, for use with the lock types. -pub struct Condvar<'a> { - name: &'static str, - // n.b. Inner must be after PoisonOnFail because we must set the poison flag - // *inside* the mutex, and struct fields are destroyed top-to-bottom - // (destroy the lock guard last). - poison: PoisonOnFail<'a>, - inner: Inner<'a>, -} - -impl<'a> Condvar<'a> { - /// Atomically exit the associated lock and block until a signal is sent. - /// - /// wait() is equivalent to wait_on(0). - /// - /// # Panics - /// - /// A task which is killed while waiting on a condition variable will wake - /// up, panic, and unlock the associated lock as it unwinds. - #[inline] - pub fn wait(&self) { self.wait_on(0) } - - /// Atomically exit the associated lock and block on a specified condvar - /// until a signal is sent on that same condvar. - /// - /// The associated lock must have been initialised with an appropriate - /// number of condvars. The condvar_id must be between 0 and num_condvars-1 - /// or else this call will fail. - #[inline] - pub fn wait_on(&self, condvar_id: uint) { - assert!(!*self.poison.flag); - self.inner.cond().wait_on(condvar_id); - // This is why we need to wrap sync::condvar. - PoisonOnFail::check(*self.poison.flag, self.name); - } - - /// Wake up a blocked task. Returns false if there was no blocked task. - #[inline] - pub fn signal(&self) -> bool { self.signal_on(0) } - - /// Wake up a blocked task on a specified condvar (as - /// sync::cond.signal_on). Returns false if there was no blocked task. - #[inline] - pub fn signal_on(&self, condvar_id: uint) -> bool { - assert!(!*self.poison.flag); - self.inner.cond().signal_on(condvar_id) - } - - /// Wake up all blocked tasks. Returns the number of tasks woken. - #[inline] - pub fn broadcast(&self) -> uint { self.broadcast_on(0) } - - /// Wake up all blocked tasks on a specified condvar (as - /// sync::cond.broadcast_on). Returns the number of tasks woken. - #[inline] - pub fn broadcast_on(&self, condvar_id: uint) -> uint { - assert!(!*self.poison.flag); - self.inner.cond().broadcast_on(condvar_id) - } -} - -/// A wrapper type which provides synchronized access to the underlying data, of -/// type `T`. A mutex always provides exclusive access, and concurrent requests -/// will block while the mutex is already locked. -/// -/// # Example -/// -/// ``` -/// use std::sync::{Mutex, Arc}; -/// -/// let mutex = Arc::new(Mutex::new(1i)); -/// let mutex2 = mutex.clone(); -/// -/// spawn(proc() { -/// let mut val = mutex2.lock(); -/// *val += 1; -/// val.cond.signal(); -/// }); -/// -/// let value = mutex.lock(); -/// while *value != 2 { -/// value.cond.wait(); -/// } -/// ``` -pub struct Mutex { - lock: raw::Mutex, - failed: UnsafeCell, - data: UnsafeCell, -} - -/// An guard which is created by locking a mutex. Through this guard the -/// underlying data can be accessed. -pub struct MutexGuard<'a, T:'a> { - // FIXME #12808: strange name to try to avoid interfering with - // field accesses of the contained type via Deref - _data: &'a mut T, - /// Inner condition variable connected to the locked mutex that this guard - /// was created from. This can be used for atomic-unlock-and-deschedule. - pub cond: Condvar<'a>, -} - -impl Mutex { - /// Creates a new mutex to protect the user-supplied data. - pub fn new(user_data: T) -> Mutex { - Mutex::new_with_condvars(user_data, 1) - } - - /// Create a new mutex, with a specified number of associated condvars. - /// - /// This will allow calling wait_on/signal_on/broadcast_on with condvar IDs - /// between 0 and num_condvars-1. (If num_condvars is 0, lock_cond will be - /// allowed but any operations on the condvar will fail.) - pub fn new_with_condvars(user_data: T, num_condvars: uint) -> Mutex { - Mutex { - lock: raw::Mutex::new_with_condvars(num_condvars), - failed: UnsafeCell::new(false), - data: UnsafeCell::new(user_data), - } - } - - /// Access the underlying mutable data with mutual exclusion from other - /// tasks. The returned value is an RAII guard which will unlock the mutex - /// when dropped. All concurrent tasks attempting to lock the mutex will - /// block while the returned value is still alive. - /// - /// # Panics - /// - /// Panicking while inside the Mutex will unlock the Mutex while unwinding, so - /// that other tasks won't block forever. It will also poison the Mutex: - /// any tasks that subsequently try to access it (including those already - /// blocked on the mutex) will also panic immediately. - #[inline] - pub fn lock<'a>(&'a self) -> MutexGuard<'a, T> { - let guard = self.lock.lock(); - - // These two accesses are safe because we're guaranteed at this point - // that we have exclusive access to this mutex. We are indeed able to - // promote ourselves from &Mutex to `&mut T` - let poison = unsafe { &mut *self.failed.get() }; - let data = unsafe { &mut *self.data.get() }; - - MutexGuard { - _data: data, - cond: Condvar { - name: "Mutex", - poison: PoisonOnFail::new(poison, "Mutex"), - inner: InnerMutex(guard), - }, - } - } -} - -impl<'a, T: Send> Deref for MutexGuard<'a, T> { - fn deref<'a>(&'a self) -> &'a T { &*self._data } -} -impl<'a, T: Send> DerefMut for MutexGuard<'a, T> { - fn deref_mut<'a>(&'a mut self) -> &'a mut T { &mut *self._data } -} - -/// A dual-mode reader-writer lock. The data can be accessed mutably or -/// immutably, and immutably-accessing tasks may run concurrently. -/// -/// # Example -/// -/// ``` -/// use std::sync::{RWLock, Arc}; -/// -/// let lock1 = Arc::new(RWLock::new(1i)); -/// let lock2 = lock1.clone(); -/// -/// spawn(proc() { -/// let mut val = lock2.write(); -/// *val = 3; -/// let val = val.downgrade(); -/// println!("{}", *val); -/// }); -/// -/// let val = lock1.read(); -/// println!("{}", *val); -/// ``` -pub struct RWLock { - lock: raw::RWLock, - failed: UnsafeCell, - data: UnsafeCell, -} - -/// A guard which is created by locking an rwlock in write mode. Through this -/// guard the underlying data can be accessed. -pub struct RWLockWriteGuard<'a, T:'a> { - // FIXME #12808: strange name to try to avoid interfering with - // field accesses of the contained type via Deref - _data: &'a mut T, - /// Inner condition variable that can be used to sleep on the write mode of - /// this rwlock. - pub cond: Condvar<'a>, -} - -/// A guard which is created by locking an rwlock in read mode. Through this -/// guard the underlying data can be accessed. -pub struct RWLockReadGuard<'a, T:'a> { - // FIXME #12808: strange names to try to avoid interfering with - // field accesses of the contained type via Deref - _data: &'a T, - _guard: raw::RWLockReadGuard<'a>, -} - -impl RWLock { - /// Create a reader/writer lock with the supplied data. - pub fn new(user_data: T) -> RWLock { - RWLock::new_with_condvars(user_data, 1) - } - - /// Create a reader/writer lock with the supplied data and a specified number - /// of condvars (as sync::RWLock::new_with_condvars). - pub fn new_with_condvars(user_data: T, num_condvars: uint) -> RWLock { - RWLock { - lock: raw::RWLock::new_with_condvars(num_condvars), - failed: UnsafeCell::new(false), - data: UnsafeCell::new(user_data), - } - } - - /// Access the underlying data mutably. Locks the rwlock in write mode; - /// other readers and writers will block. - /// - /// # Panics - /// - /// Panicking while inside the lock will unlock the lock while unwinding, so - /// that other tasks won't block forever. As Mutex.lock, it will also poison - /// the lock, so subsequent readers and writers will both also panic. - #[inline] - pub fn write<'a>(&'a self) -> RWLockWriteGuard<'a, T> { - let guard = self.lock.write(); - - // These two accesses are safe because we're guaranteed at this point - // that we have exclusive access to this rwlock. We are indeed able to - // promote ourselves from &RWLock to `&mut T` - let poison = unsafe { &mut *self.failed.get() }; - let data = unsafe { &mut *self.data.get() }; - - RWLockWriteGuard { - _data: data, - cond: Condvar { - name: "RWLock", - poison: PoisonOnFail::new(poison, "RWLock"), - inner: InnerRWLock(guard), - }, - } - } - - /// Access the underlying data immutably. May run concurrently with other - /// reading tasks. - /// - /// # Panics - /// - /// Panicking will unlock the lock while unwinding. However, unlike all other - /// access modes, this will not poison the lock. - pub fn read<'a>(&'a self) -> RWLockReadGuard<'a, T> { - let guard = self.lock.read(); - PoisonOnFail::check(unsafe { *self.failed.get() }, "RWLock"); - RWLockReadGuard { - _guard: guard, - _data: unsafe { &*self.data.get() }, - } - } -} - -impl<'a, T: Send + Sync> RWLockWriteGuard<'a, T> { - /// Consumes this write lock token, returning a new read lock token. - /// - /// This will allow pending readers to come into the lock. - pub fn downgrade(self) -> RWLockReadGuard<'a, T> { - let RWLockWriteGuard { _data, cond } = self; - // convert the data to read-only explicitly - let data = &*_data; - let guard = match cond.inner { - InnerMutex(..) => unreachable!(), - InnerRWLock(guard) => guard.downgrade() - }; - RWLockReadGuard { _guard: guard, _data: data } - } -} - -impl<'a, T: Send + Sync> Deref for RWLockReadGuard<'a, T> { - fn deref<'a>(&'a self) -> &'a T { self._data } -} -impl<'a, T: Send + Sync> Deref for RWLockWriteGuard<'a, T> { - fn deref<'a>(&'a self) -> &'a T { &*self._data } -} -impl<'a, T: Send + Sync> DerefMut for RWLockWriteGuard<'a, T> { - fn deref_mut<'a>(&'a mut self) -> &'a mut T { &mut *self._data } -} - -/// A barrier enables multiple tasks to synchronize the beginning -/// of some computation. -/// -/// ```rust -/// use std::sync::{Arc, Barrier}; -/// -/// let barrier = Arc::new(Barrier::new(10)); -/// for _ in range(0u, 10) { -/// let c = barrier.clone(); -/// // The same messages will be printed together. -/// // You will NOT see any interleaving. -/// spawn(proc() { -/// println!("before wait"); -/// c.wait(); -/// println!("after wait"); -/// }); -/// } -/// ``` -pub struct Barrier { - lock: Mutex, - num_tasks: uint, -} - -// The inner state of a double barrier -struct BarrierState { - count: uint, - generation_id: uint, -} - -impl Barrier { - /// Create a new barrier that can block a given number of tasks. - pub fn new(num_tasks: uint) -> Barrier { - Barrier { - lock: Mutex::new(BarrierState { - count: 0, - generation_id: 0, - }), - num_tasks: num_tasks, - } - } - - /// Block the current task until a certain number of tasks is waiting. - pub fn wait(&self) { - let mut lock = self.lock.lock(); - let local_gen = lock.generation_id; - lock.count += 1; - if lock.count < self.num_tasks { - // We need a while loop to guard against spurious wakeups. - // http://en.wikipedia.org/wiki/Spurious_wakeup - while local_gen == lock.generation_id && - lock.count < self.num_tasks { - lock.cond.wait(); - } - } else { - lock.count = 0; - lock.generation_id += 1; - lock.cond.broadcast(); - } - } -} - -#[cfg(test)] -mod tests { - use prelude::*; - use comm::Empty; - use task; - use task::try_future; - use sync::Arc; - - use super::{Mutex, Barrier, RWLock}; - - #[test] - fn test_mutex_arc_condvar() { - let arc = Arc::new(Mutex::new(false)); - let arc2 = arc.clone(); - let (tx, rx) = channel(); - task::spawn(proc() { - // wait until parent gets in - rx.recv(); - let mut lock = arc2.lock(); - *lock = true; - lock.cond.signal(); - }); - - let lock = arc.lock(); - tx.send(()); - assert!(!*lock); - while !*lock { - lock.cond.wait(); - } - } - - #[test] #[should_fail] - fn test_arc_condvar_poison() { - let arc = Arc::new(Mutex::new(1i)); - let arc2 = arc.clone(); - let (tx, rx) = channel(); - - spawn(proc() { - rx.recv(); - let lock = arc2.lock(); - lock.cond.signal(); - // Parent should fail when it wakes up. - panic!(); - }); - - let lock = arc.lock(); - tx.send(()); - while *lock == 1 { - lock.cond.wait(); - } - } - - #[test] #[should_fail] - fn test_mutex_arc_poison() { - let arc = Arc::new(Mutex::new(1i)); - let arc2 = arc.clone(); - let _ = task::try(proc() { - let lock = arc2.lock(); - assert_eq!(*lock, 2); - }); - let lock = arc.lock(); - assert_eq!(*lock, 1); - } - - #[test] - fn test_mutex_arc_nested() { - // Tests nested mutexes and access - // to underlying data. - let arc = Arc::new(Mutex::new(1i)); - let arc2 = Arc::new(Mutex::new(arc)); - task::spawn(proc() { - let lock = arc2.lock(); - let lock2 = lock.deref().lock(); - assert_eq!(*lock2, 1); - }); - } - - #[test] - fn test_mutex_arc_access_in_unwind() { - let arc = Arc::new(Mutex::new(1i)); - let arc2 = arc.clone(); - let _ = task::try::<()>(proc() { - struct Unwinder { - i: Arc>, - } - impl Drop for Unwinder { - fn drop(&mut self) { - let mut lock = self.i.lock(); - *lock += 1; - } - } - let _u = Unwinder { i: arc2 }; - panic!(); - }); - let lock = arc.lock(); - assert_eq!(*lock, 2); - } - - #[test] #[should_fail] - fn test_rw_arc_poison_wr() { - let arc = Arc::new(RWLock::new(1i)); - let arc2 = arc.clone(); - let _ = task::try(proc() { - let lock = arc2.write(); - assert_eq!(*lock, 2); - }); - let lock = arc.read(); - assert_eq!(*lock, 1); - } - #[test] #[should_fail] - fn test_rw_arc_poison_ww() { - let arc = Arc::new(RWLock::new(1i)); - let arc2 = arc.clone(); - let _ = task::try(proc() { - let lock = arc2.write(); - assert_eq!(*lock, 2); - }); - let lock = arc.write(); - assert_eq!(*lock, 1); - } - #[test] - fn test_rw_arc_no_poison_rr() { - let arc = Arc::new(RWLock::new(1i)); - let arc2 = arc.clone(); - let _ = task::try(proc() { - let lock = arc2.read(); - assert_eq!(*lock, 2); - }); - let lock = arc.read(); - assert_eq!(*lock, 1); - } - #[test] - fn test_rw_arc_no_poison_rw() { - let arc = Arc::new(RWLock::new(1i)); - let arc2 = arc.clone(); - let _ = task::try(proc() { - let lock = arc2.read(); - assert_eq!(*lock, 2); - }); - let lock = arc.write(); - assert_eq!(*lock, 1); - } - #[test] - fn test_rw_arc_no_poison_dr() { - let arc = Arc::new(RWLock::new(1i)); - let arc2 = arc.clone(); - let _ = task::try(proc() { - let lock = arc2.write().downgrade(); - assert_eq!(*lock, 2); - }); - let lock = arc.write(); - assert_eq!(*lock, 1); - } - - #[test] - fn test_rw_arc() { - let arc = Arc::new(RWLock::new(0i)); - let arc2 = arc.clone(); - let (tx, rx) = channel(); - - task::spawn(proc() { - let mut lock = arc2.write(); - for _ in range(0u, 10) { - let tmp = *lock; - *lock = -1; - task::deschedule(); - *lock = tmp + 1; - } - tx.send(()); - }); - - // Readers try to catch the writer in the act - let mut children = Vec::new(); - for _ in range(0u, 5) { - let arc3 = arc.clone(); - children.push(try_future(proc() { - let lock = arc3.read(); - assert!(*lock >= 0); - })); - } - - // Wait for children to pass their asserts - for r in children.iter_mut() { - assert!(r.get_ref().is_ok()); - } - - // Wait for writer to finish - rx.recv(); - let lock = arc.read(); - assert_eq!(*lock, 10); - } - - #[test] - fn test_rw_arc_access_in_unwind() { - let arc = Arc::new(RWLock::new(1i)); - let arc2 = arc.clone(); - let _ = task::try::<()>(proc() { - struct Unwinder { - i: Arc>, - } - impl Drop for Unwinder { - fn drop(&mut self) { - let mut lock = self.i.write(); - *lock += 1; - } - } - let _u = Unwinder { i: arc2 }; - panic!(); - }); - let lock = arc.read(); - assert_eq!(*lock, 2); - } - - #[test] - fn test_rw_downgrade() { - // (1) A downgrader gets in write mode and does cond.wait. - // (2) A writer gets in write mode, sets state to 42, and does signal. - // (3) Downgrader wakes, sets state to 31337. - // (4) tells writer and all other readers to contend as it downgrades. - // (5) Writer attempts to set state back to 42, while downgraded task - // and all reader tasks assert that it's 31337. - let arc = Arc::new(RWLock::new(0i)); - - // Reader tasks - let mut reader_convos = Vec::new(); - for _ in range(0u, 10) { - let ((tx1, rx1), (tx2, rx2)) = (channel(), channel()); - reader_convos.push((tx1, rx2)); - let arcn = arc.clone(); - task::spawn(proc() { - rx1.recv(); // wait for downgrader to give go-ahead - let lock = arcn.read(); - assert_eq!(*lock, 31337); - tx2.send(()); - }); - } - - // Writer task - let arc2 = arc.clone(); - let ((tx1, rx1), (tx2, rx2)) = (channel(), channel()); - task::spawn(proc() { - rx1.recv(); - { - let mut lock = arc2.write(); - assert_eq!(*lock, 0); - *lock = 42; - lock.cond.signal(); - } - rx1.recv(); - { - let mut lock = arc2.write(); - // This shouldn't happen until after the downgrade read - // section, and all other readers, finish. - assert_eq!(*lock, 31337); - *lock = 42; - } - tx2.send(()); - }); - - // Downgrader (us) - let mut lock = arc.write(); - tx1.send(()); // send to another writer who will wake us up - while *lock == 0 { - lock.cond.wait(); - } - assert_eq!(*lock, 42); - *lock = 31337; - // send to other readers - for &(ref mut rc, _) in reader_convos.iter_mut() { - rc.send(()) - } - let lock = lock.downgrade(); - // complete handshake with other readers - for &(_, ref mut rp) in reader_convos.iter_mut() { - rp.recv() - } - tx1.send(()); // tell writer to try again - assert_eq!(*lock, 31337); - drop(lock); - - rx2.recv(); // complete handshake with writer - } - - #[cfg(test)] - fn test_rw_write_cond_downgrade_read_race_helper() { - // Tests that when a downgrader hands off the "reader cloud" lock - // because of a contending reader, a writer can't race to get it - // instead, which would result in readers_and_writers. This tests - // the raw module rather than this one, but it's here because an - // rwarc gives us extra shared state to help check for the race. - let x = Arc::new(RWLock::new(true)); - let (tx, rx) = channel(); - - // writer task - let xw = x.clone(); - task::spawn(proc() { - let mut lock = xw.write(); - tx.send(()); // tell downgrader it's ok to go - lock.cond.wait(); - // The core of the test is here: the condvar reacquire path - // must involve order_lock, so that it cannot race with a reader - // trying to receive the "reader cloud lock hand-off". - *lock = false; - }); - - rx.recv(); // wait for writer to get in - - let lock = x.write(); - assert!(*lock); - // make writer contend in the cond-reacquire path - lock.cond.signal(); - // make a reader task to trigger the "reader cloud lock" handoff - let xr = x.clone(); - let (tx, rx) = channel(); - task::spawn(proc() { - tx.send(()); - drop(xr.read()); - }); - rx.recv(); // wait for reader task to exist - - let lock = lock.downgrade(); - // if writer mistakenly got in, make sure it mutates state - // before we assert on it - for _ in range(0u, 5) { task::deschedule(); } - // make sure writer didn't get in. - assert!(*lock); - } - #[test] - fn test_rw_write_cond_downgrade_read_race() { - // Ideally the above test case would have deschedule statements in it - // that helped to expose the race nearly 100% of the time... but adding - // deschedules in the intuitively-right locations made it even less - // likely, and I wasn't sure why :( . This is a mediocre "next best" - // option. - for _ in range(0u, 8) { - test_rw_write_cond_downgrade_read_race_helper(); - } - } - - #[test] - fn test_barrier() { - let barrier = Arc::new(Barrier::new(10)); - let (tx, rx) = channel(); - - for _ in range(0u, 9) { - let c = barrier.clone(); - let tx = tx.clone(); - spawn(proc() { - c.wait(); - tx.send(true); - }); - } - - // At this point, all spawned tasks should be blocked, - // so we shouldn't get anything from the port - assert!(match rx.try_recv() { - Err(Empty) => true, - _ => false, - }); - - barrier.wait(); - // Now, the barrier is cleared and we should get data. - for _ in range(0u, 9) { - rx.recv(); - } - } -} diff --git a/src/libstd/sync/mod.rs b/src/libstd/sync/mod.rs index 944b852db35f9..7605a6a96a005 100644 --- a/src/libstd/sync/mod.rs +++ b/src/libstd/sync/mod.rs @@ -17,41 +17,27 @@ #![experimental] -pub use self::one::{Once, ONCE_INIT}; - pub use alloc::arc::{Arc, Weak}; -pub use self::lock::{Mutex, MutexGuard, Condvar, Barrier, - RWLock, RWLockReadGuard, RWLockWriteGuard}; -// The mutex/rwlock in this module are not meant for reexport -pub use self::raw::{Semaphore, SemaphoreGuard}; +pub use self::mutex::{Mutex, MutexGuard, StaticMutex, StaticMutexGuard, MUTEX_INIT}; +pub use self::rwlock::{RWLock, StaticRWLock, RWLOCK_INIT}; +pub use self::rwlock::{RWLockReadGuard, RWLockWriteGuard}; +pub use self::rwlock::{StaticRWLockReadGuard, StaticRWLockWriteGuard}; +pub use self::condvar::{Condvar, StaticCondvar, CONDVAR_INIT, AsMutexGuard}; +pub use self::once::{Once, ONCE_INIT}; +pub use self::semaphore::{Semaphore, SemaphoreGuard}; +pub use self::barrier::Barrier; pub use self::future::Future; pub use self::task_pool::TaskPool; -// Core building blocks for all primitives in this crate - -#[stable] pub mod atomic; - -// Concurrent data structures - -pub mod spsc_queue; -pub mod mpsc_queue; -pub mod mpmc_bounded_queue; -pub mod deque; - -// Low-level concurrency primitives - -mod raw; -mod mutex; -mod one; - -// Higher level primitives based on those above - -mod lock; - -// Task management - +mod barrier; +mod condvar; mod future; +mod mutex; +mod once; +mod poison; +mod rwlock; +mod semaphore; mod task_pool; diff --git a/src/libstd/sync/mpmc_bounded_queue.rs b/src/libstd/sync/mpmc_bounded_queue.rs deleted file mode 100644 index dca2d4098c6a6..0000000000000 --- a/src/libstd/sync/mpmc_bounded_queue.rs +++ /dev/null @@ -1,219 +0,0 @@ -/* Copyright (c) 2010-2011 Dmitry Vyukov. All rights reserved. - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY DMITRY VYUKOV "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT - * SHALL DMITRY VYUKOV OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE - * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * The views and conclusions contained in the software and documentation are - * those of the authors and should not be interpreted as representing official - * policies, either expressed or implied, of Dmitry Vyukov. - */ - -#![experimental] -#![allow(missing_docs, dead_code)] - -// http://www.1024cores.net/home/lock-free-algorithms/queues/bounded-mpmc-queue - -use core::prelude::*; - -use alloc::arc::Arc; -use vec::Vec; -use core::num::UnsignedInt; -use core::cell::UnsafeCell; - -use sync::atomic::{AtomicUint,Relaxed,Release,Acquire}; - -struct Node { - sequence: AtomicUint, - value: Option, -} - -struct State { - pad0: [u8, ..64], - buffer: Vec>>, - mask: uint, - pad1: [u8, ..64], - enqueue_pos: AtomicUint, - pad2: [u8, ..64], - dequeue_pos: AtomicUint, - pad3: [u8, ..64], -} - -pub struct Queue { - state: Arc>, -} - -impl State { - fn with_capacity(capacity: uint) -> State { - let capacity = if capacity < 2 || (capacity & (capacity - 1)) != 0 { - if capacity < 2 { - 2u - } else { - // use next power of 2 as capacity - capacity.next_power_of_two() - } - } else { - capacity - }; - let buffer = Vec::from_fn(capacity, |i| { - UnsafeCell::new(Node { sequence:AtomicUint::new(i), value: None }) - }); - State{ - pad0: [0, ..64], - buffer: buffer, - mask: capacity-1, - pad1: [0, ..64], - enqueue_pos: AtomicUint::new(0), - pad2: [0, ..64], - dequeue_pos: AtomicUint::new(0), - pad3: [0, ..64], - } - } - - fn push(&self, value: T) -> bool { - let mask = self.mask; - let mut pos = self.enqueue_pos.load(Relaxed); - loop { - let node = &self.buffer[pos & mask]; - let seq = unsafe { (*node.get()).sequence.load(Acquire) }; - let diff: int = seq as int - pos as int; - - if diff == 0 { - let enqueue_pos = self.enqueue_pos.compare_and_swap(pos, pos+1, Relaxed); - if enqueue_pos == pos { - unsafe { - (*node.get()).value = Some(value); - (*node.get()).sequence.store(pos+1, Release); - } - break - } else { - pos = enqueue_pos; - } - } else if diff < 0 { - return false - } else { - pos = self.enqueue_pos.load(Relaxed); - } - } - true - } - - fn pop(&self) -> Option { - let mask = self.mask; - let mut pos = self.dequeue_pos.load(Relaxed); - loop { - let node = &self.buffer[pos & mask]; - let seq = unsafe { (*node.get()).sequence.load(Acquire) }; - let diff: int = seq as int - (pos + 1) as int; - if diff == 0 { - let dequeue_pos = self.dequeue_pos.compare_and_swap(pos, pos+1, Relaxed); - if dequeue_pos == pos { - unsafe { - let value = (*node.get()).value.take(); - (*node.get()).sequence.store(pos + mask + 1, Release); - return value - } - } else { - pos = dequeue_pos; - } - } else if diff < 0 { - return None - } else { - pos = self.dequeue_pos.load(Relaxed); - } - } - } -} - -impl Queue { - pub fn with_capacity(capacity: uint) -> Queue { - Queue{ - state: Arc::new(State::with_capacity(capacity)) - } - } - - pub fn push(&self, value: T) -> bool { - self.state.push(value) - } - - pub fn pop(&self) -> Option { - self.state.pop() - } -} - -impl Clone for Queue { - fn clone(&self) -> Queue { - Queue { state: self.state.clone() } - } -} - -#[cfg(test)] -mod tests { - use prelude::*; - use super::Queue; - - #[test] - fn test() { - let nthreads = 8u; - let nmsgs = 1000u; - let q = Queue::with_capacity(nthreads*nmsgs); - assert_eq!(None, q.pop()); - let (tx, rx) = channel(); - - for _ in range(0, nthreads) { - let q = q.clone(); - let tx = tx.clone(); - spawn(proc() { - let q = q; - for i in range(0, nmsgs) { - assert!(q.push(i)); - } - tx.send(()); - }); - } - - let mut completion_rxs = vec![]; - for _ in range(0, nthreads) { - let (tx, rx) = channel(); - completion_rxs.push(rx); - let q = q.clone(); - spawn(proc() { - let q = q; - let mut i = 0u; - loop { - match q.pop() { - None => {}, - Some(_) => { - i += 1; - if i == nmsgs { break } - } - } - } - tx.send(i); - }); - } - - for rx in completion_rxs.iter_mut() { - assert_eq!(nmsgs, rx.recv()); - } - for _ in range(0, nthreads) { - rx.recv(); - } - } -} diff --git a/src/libstd/sync/mutex.rs b/src/libstd/sync/mutex.rs index c9e90210c30f3..3d17f2bc64b6e 100644 --- a/src/libstd/sync/mutex.rs +++ b/src/libstd/sync/mutex.rs @@ -8,43 +8,68 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -//! A simple native mutex implementation. Warning: this API is likely -//! to change soon. +use prelude::*; -#![allow(dead_code)] - -use core::prelude::*; -use alloc::boxed::Box; -use rustrt::mutex; - -pub const LOCKED: uint = 1 << 0; -pub const BLOCKED: uint = 1 << 1; +use cell::UnsafeCell; +use kinds::marker; +use sync::{poison, AsMutexGuard}; +use sys_common::mutex as sys; /// A mutual exclusion primitive useful for protecting shared data /// -/// This mutex will properly block tasks waiting for the lock to become -/// available. The mutex can also be statically initialized or created via a -/// `new` constructor. +/// This mutex will block threads waiting for the lock to become available. The +/// mutex can also be statically initialized or created via a `new` +/// constructor. Each mutex has a type parameter which represents the data that +/// it is protecting. The data can only be accessed through the RAII guards +/// returned from `lock` and `try_lock`, which guarantees that the data is only +/// ever accessed when the mutex is locked. +/// +/// # Poisoning +/// +/// In order to prevent access to otherwise invalid data, each mutex will +/// propagate any panics which occur while the lock is held. Once a thread has +/// panicked while holding the lock, then all other threads will immediately +/// panic as well once they hold the lock. /// /// # Example /// -/// ```rust,ignore -/// use std::sync::mutex::Mutex; +/// ```rust +/// use std::sync::{Arc, Mutex}; +/// const N: uint = 10; /// -/// let m = Mutex::new(); -/// let guard = m.lock(); -/// // do some work -/// drop(guard); // unlock the lock +/// // Spawn a few threads to increment a shared variable (non-atomically), and +/// // let the main thread know once all increments are done. +/// // +/// // Here we're using an Arc to share memory among tasks, and the data inside +/// // the Arc is protected with a mutex. +/// let data = Arc::new(Mutex::new(0)); +/// +/// let (tx, rx) = channel(); +/// for _ in range(0, 10) { +/// let (data, tx) = (data.clone(), tx.clone()); +/// spawn(proc() { +/// // The shared static can only be accessed once the lock is held. +/// // Our non-atomic increment is safe because we're the only thread +/// // which can access the shared state when the lock is held. +/// let mut data = data.lock(); +/// *data += 1; +/// if *data == N { +/// tx.send(()); +/// } +/// // the lock is unlocked here when `data` goes out of scope. +/// }); +/// } +/// +/// rx.recv(); /// ``` -pub struct Mutex { +pub struct Mutex { // Note that this static mutex is in a *box*, not inlined into the struct - // itself. This is done for memory safety reasons with the usage of a - // StaticNativeMutex inside the static mutex above. Once a native mutex has - // been used once, its address can never change (it can't be moved). This - // mutex type can be safely moved at any time, so to ensure that the native - // mutex is used correctly we box the inner lock to give it a constant - // address. - lock: Box, + // itself. Once a native mutex has been used once, its address can never + // change (it can't be moved). This mutex type can be safely moved at any + // time, so to ensure that the native mutex is used correctly we box the + // inner lock to give it a constant address. + inner: Box, + data: UnsafeCell, } /// The static mutex type is provided to allow for static allocation of mutexes. @@ -57,8 +82,8 @@ pub struct Mutex { /// /// # Example /// -/// ```rust,ignore -/// use std::sync::mutex::{StaticMutex, MUTEX_INIT}; +/// ```rust +/// use std::sync::{StaticMutex, MUTEX_INIT}; /// /// static LOCK: StaticMutex = MUTEX_INIT; /// @@ -69,35 +94,113 @@ pub struct Mutex { /// // lock is unlocked here. /// ``` pub struct StaticMutex { - lock: mutex::StaticNativeMutex, + lock: sys::Mutex, + poison: UnsafeCell, } /// An RAII implementation of a "scoped lock" of a mutex. When this structure is /// dropped (falls out of scope), the lock will be unlocked. +/// +/// The data protected by the mutex can be access through this guard via its +/// Deref and DerefMut implementations #[must_use] -pub struct Guard<'a> { - guard: mutex::LockGuard<'a>, +pub struct MutexGuard<'a, T: 'a> { + // funny underscores due to how Deref/DerefMut currently work (they + // disregard field privacy). + __lock: &'a Mutex, + __guard: StaticMutexGuard, } -fn lift_guard(guard: mutex::LockGuard) -> Guard { - Guard { guard: guard } +/// An RAII implementation of a "scoped lock" of a static mutex. When this +/// structure is dropped (falls out of scope), the lock will be unlocked. +#[must_use] +pub struct StaticMutexGuard { + lock: &'static sys::Mutex, + marker: marker::NoSend, + poison: poison::Guard<'static>, } /// Static initialization of a mutex. This constant can be used to initialize /// other mutex constants. pub const MUTEX_INIT: StaticMutex = StaticMutex { - lock: mutex::NATIVE_MUTEX_INIT + lock: sys::MUTEX_INIT, + poison: UnsafeCell { value: poison::Flag { failed: false } }, }; -impl StaticMutex { - /// Attempts to grab this lock, see `Mutex::try_lock` - pub fn try_lock<'a>(&'a self) -> Option> { - unsafe { self.lock.trylock().map(lift_guard) } +impl Mutex { + /// Creates a new mutex in an unlocked state ready for use. + pub fn new(t: T) -> Mutex { + Mutex { + inner: box MUTEX_INIT, + data: UnsafeCell::new(t), + } + } + + /// Acquires a mutex, blocking the current task until it is able to do so. + /// + /// This function will block the local task until it is available to acquire + /// the mutex. Upon returning, the task is the only task with the mutex + /// held. An RAII guard is returned to allow scoped unlock of the lock. When + /// the guard goes out of scope, the mutex will be unlocked. + /// + /// # Panics + /// + /// If another user of this mutex panicked while holding the mutex, then + /// this call will immediately panic once the mutex is acquired. + pub fn lock(&self) -> MutexGuard { + unsafe { + let lock: &'static StaticMutex = &*(&*self.inner as *const _); + MutexGuard::new(self, lock.lock()) + } + } + + /// Attempts to acquire this lock. + /// + /// If the lock could not be acquired at this time, then `None` is returned. + /// Otherwise, an RAII guard is returned. The lock will be unlocked when the + /// guard is dropped. + /// + /// This function does not block. + /// + /// # Panics + /// + /// If another user of this mutex panicked while holding the mutex, then + /// this call will immediately panic if the mutex would otherwise be + /// acquired. + pub fn try_lock(&self) -> Option> { + unsafe { + let lock: &'static StaticMutex = &*(&*self.inner as *const _); + lock.try_lock().map(|guard| { + MutexGuard::new(self, guard) + }) + } } +} +#[unsafe_destructor] +impl Drop for Mutex { + fn drop(&mut self) { + // This is actually safe b/c we know that there is no further usage of + // this mutex (it's up to the user to arrange for a mutex to get + // dropped, that's not our job) + unsafe { self.inner.lock.destroy() } + } +} + +impl StaticMutex { /// Acquires this lock, see `Mutex::lock` - pub fn lock<'a>(&'a self) -> Guard<'a> { - lift_guard(unsafe { self.lock.lock() }) + pub fn lock(&'static self) -> StaticMutexGuard { + unsafe { self.lock.lock() } + StaticMutexGuard::new(self) + } + + /// Attempts to grab this lock, see `Mutex::try_lock` + pub fn try_lock(&'static self) -> Option { + if unsafe { self.lock.try_lock() } { + Some(StaticMutexGuard::new(self)) + } else { + None + } } /// Deallocates resources associated with this static mutex. @@ -110,58 +213,73 @@ impl StaticMutex { /// *all* platforms. It may be the case that some platforms do not leak /// memory if this method is not called, but this is not guaranteed to be /// true on all platforms. - pub unsafe fn destroy(&self) { + pub unsafe fn destroy(&'static self) { self.lock.destroy() } } -impl Mutex { - /// Creates a new mutex in an unlocked state ready for use. - pub fn new() -> Mutex { - Mutex { - lock: box StaticMutex { - lock: unsafe { mutex::StaticNativeMutex::new() }, - } - } +impl<'mutex, T> MutexGuard<'mutex, T> { + fn new(lock: &Mutex, guard: StaticMutexGuard) -> MutexGuard { + MutexGuard { __lock: lock, __guard: guard } } +} - /// Attempts to acquire this lock. - /// - /// If the lock could not be acquired at this time, then `None` is returned. - /// Otherwise, an RAII guard is returned. The lock will be unlocked when the - /// guard is dropped. - /// - /// This function does not block. - pub fn try_lock<'a>(&'a self) -> Option> { - self.lock.try_lock() +impl<'mutex, T> AsMutexGuard for MutexGuard<'mutex, T> { + unsafe fn as_mutex_guard(&self) -> &StaticMutexGuard { &self.__guard } +} + +impl<'mutex, T> Deref for MutexGuard<'mutex, T> { + fn deref<'a>(&'a self) -> &'a T { unsafe { &*self.__lock.data.get() } } +} +impl<'mutex, T> DerefMut for MutexGuard<'mutex, T> { + fn deref_mut<'a>(&'a mut self) -> &'a mut T { + unsafe { &mut *self.__lock.data.get() } } +} - /// Acquires a mutex, blocking the current task until it is able to do so. - /// - /// This function will block the local task until it is available to acquire - /// the mutex. Upon returning, the task is the only task with the mutex - /// held. An RAII guard is returned to allow scoped unlock of the lock. When - /// the guard goes out of scope, the mutex will be unlocked. - pub fn lock<'a>(&'a self) -> Guard<'a> { self.lock.lock() } +impl StaticMutexGuard { + fn new(lock: &'static StaticMutex) -> StaticMutexGuard { + unsafe { + let guard = StaticMutexGuard { + lock: &lock.lock, + marker: marker::NoSend, + poison: (*lock.poison.get()).borrow(), + }; + guard.poison.check("mutex"); + return guard; + } + } +} + +pub fn guard_lock(guard: &StaticMutexGuard) -> &sys::Mutex { guard.lock } +pub fn guard_poison(guard: &StaticMutexGuard) -> &poison::Guard { + &guard.poison +} + +impl AsMutexGuard for StaticMutexGuard { + unsafe fn as_mutex_guard(&self) -> &StaticMutexGuard { self } } -impl Drop for Mutex { +#[unsafe_destructor] +impl Drop for StaticMutexGuard { fn drop(&mut self) { - // This is actually safe b/c we know that there is no further usage of - // this mutex (it's up to the user to arrange for a mutex to get - // dropped, that's not our job) - unsafe { self.lock.destroy() } + unsafe { + self.poison.done(); + self.lock.unlock(); + } } } #[cfg(test)] mod test { use prelude::*; - use super::{Mutex, StaticMutex, MUTEX_INIT}; + + use task; + use sync::{Arc, Mutex, StaticMutex, MUTEX_INIT, Condvar}; #[test] fn smoke() { - let m = Mutex::new(); + let m = Mutex::new(()); drop(m.lock()); drop(m.lock()); } @@ -211,8 +329,104 @@ mod test { } #[test] - fn trylock() { - let m = Mutex::new(); + fn try_lock() { + let m = Mutex::new(()); assert!(m.try_lock().is_some()); } + + #[test] + fn test_mutex_arc_condvar() { + let arc = Arc::new((Mutex::new(false), Condvar::new())); + let arc2 = arc.clone(); + let (tx, rx) = channel(); + spawn(proc() { + // wait until parent gets in + rx.recv(); + let &(ref lock, ref cvar) = &*arc2; + let mut lock = lock.lock(); + *lock = true; + cvar.notify_one(); + }); + + let &(ref lock, ref cvar) = &*arc; + let lock = lock.lock(); + tx.send(()); + assert!(!*lock); + while !*lock { + cvar.wait(&lock); + } + } + + #[test] + #[should_fail] + fn test_arc_condvar_poison() { + let arc = Arc::new((Mutex::new(1i), Condvar::new())); + let arc2 = arc.clone(); + let (tx, rx) = channel(); + + spawn(proc() { + rx.recv(); + let &(ref lock, ref cvar) = &*arc2; + let _g = lock.lock(); + cvar.notify_one(); + // Parent should fail when it wakes up. + panic!(); + }); + + let &(ref lock, ref cvar) = &*arc; + let lock = lock.lock(); + tx.send(()); + while *lock == 1 { + cvar.wait(&lock); + } + } + + #[test] + #[should_fail] + fn test_mutex_arc_poison() { + let arc = Arc::new(Mutex::new(1i)); + let arc2 = arc.clone(); + let _ = task::try(proc() { + let lock = arc2.lock(); + assert_eq!(*lock, 2); + }); + let lock = arc.lock(); + assert_eq!(*lock, 1); + } + + #[test] + fn test_mutex_arc_nested() { + // Tests nested mutexes and access + // to underlying data. + let arc = Arc::new(Mutex::new(1i)); + let arc2 = Arc::new(Mutex::new(arc)); + let (tx, rx) = channel(); + spawn(proc() { + let lock = arc2.lock(); + let lock2 = lock.deref().lock(); + assert_eq!(*lock2, 1); + tx.send(()); + }); + rx.recv(); + } + + #[test] + fn test_mutex_arc_access_in_unwind() { + let arc = Arc::new(Mutex::new(1i)); + let arc2 = arc.clone(); + let _ = task::try::<()>(proc() { + struct Unwinder { + i: Arc>, + } + impl Drop for Unwinder { + fn drop(&mut self) { + *self.i.lock() += 1; + } + } + let _u = Unwinder { i: arc2 }; + panic!(); + }); + let lock = arc.lock(); + assert_eq!(*lock, 2); + } } diff --git a/src/libstd/sync/one.rs b/src/libstd/sync/once.rs similarity index 96% rename from src/libstd/sync/one.rs rename to src/libstd/sync/once.rs index f710a6da59bd7..a75088120f869 100644 --- a/src/libstd/sync/one.rs +++ b/src/libstd/sync/once.rs @@ -13,12 +13,10 @@ //! This primitive is meant to be used to run one-time initialization. An //! example use case would be for initializing an FFI library. -use core::prelude::*; - -use core::int; -use core::atomic; - -use super::mutex::{StaticMutex, MUTEX_INIT}; +use int; +use mem::drop; +use sync::atomic; +use sync::{StaticMutex, MUTEX_INIT}; /// A synchronization primitive which can be used to run a one-time global /// initialization. Useful for one-time initialization for FFI or related @@ -27,8 +25,8 @@ use super::mutex::{StaticMutex, MUTEX_INIT}; /// /// # Example /// -/// ```rust,ignore -/// use std::sync::one::{Once, ONCE_INIT}; +/// ```rust +/// use std::sync::{Once, ONCE_INIT}; /// /// static START: Once = ONCE_INIT; /// @@ -59,7 +57,7 @@ impl Once { /// /// When this function returns, it is guaranteed that some initialization /// has run and completed (it may not be the closure specified). - pub fn doit(&self, f: ||) { + pub fn doit(&'static self, f: ||) { // Optimize common path: load is much cheaper than fetch_add. if self.cnt.load(atomic::SeqCst) < 0 { return @@ -121,6 +119,7 @@ impl Once { #[cfg(test)] mod test { use prelude::*; + use task; use super::{ONCE_INIT, Once}; diff --git a/src/libstd/sync/poison.rs b/src/libstd/sync/poison.rs new file mode 100644 index 0000000000000..eb46fd771477e --- /dev/null +++ b/src/libstd/sync/poison.rs @@ -0,0 +1,48 @@ +// Copyright 2014 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 option::None; +use rustrt::task::Task; +use rustrt::local::Local; + +pub struct Flag { pub failed: bool } + +impl Flag { + pub fn borrow(&mut self) -> Guard { + Guard { flag: &mut self.failed, failing: failing() } + } +} + +pub struct Guard<'a> { + flag: &'a mut bool, + failing: bool, +} + +impl<'a> Guard<'a> { + pub fn check(&self, name: &str) { + if *self.flag { + panic!("poisoned {} - another task failed inside", name); + } + } + + pub fn done(&mut self) { + if !self.failing && failing() { + *self.flag = true; + } + } +} + +fn failing() -> bool { + if Local::exists(None::) { + Local::borrow(None::).unwinder.unwinding() + } else { + false + } +} diff --git a/src/libstd/sync/raw.rs b/src/libstd/sync/raw.rs deleted file mode 100644 index 47580a115131b..0000000000000 --- a/src/libstd/sync/raw.rs +++ /dev/null @@ -1,1132 +0,0 @@ -// Copyright 2012-2014 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. - -//! Raw concurrency primitives you know and love. -//! -//! These primitives are not recommended for general use, but are provided for -//! flavorful use-cases. It is recommended to use the types at the top of the -//! `sync` crate which wrap values directly and provide safer abstractions for -//! containing data. - -// A side-effect of merging libsync into libstd; will go away once -// libsync rewrite lands -#![allow(dead_code)] - -use core::prelude::*; -use self::ReacquireOrderLock::*; - -use core::atomic; -use core::finally::Finally; -use core::kinds::marker; -use core::mem; -use core::cell::UnsafeCell; -use vec::Vec; - -use super::mutex; -use comm::{Receiver, Sender, channel}; - -// Each waiting task receives on one of these. -type WaitEnd = Receiver<()>; -type SignalEnd = Sender<()>; -// A doubly-ended queue of waiting tasks. -struct WaitQueue { - head: Receiver, - tail: Sender, -} - -impl WaitQueue { - fn new() -> WaitQueue { - let (block_tail, block_head) = channel(); - WaitQueue { head: block_head, tail: block_tail } - } - - // Signals one live task from the queue. - fn signal(&self) -> bool { - match self.head.try_recv() { - Ok(ch) => { - // Send a wakeup signal. If the waiter was killed, its port will - // have closed. Keep trying until we get a live task. - if ch.send_opt(()).is_ok() { - true - } else { - self.signal() - } - } - _ => false - } - } - - fn broadcast(&self) -> uint { - let mut count = 0; - loop { - match self.head.try_recv() { - Ok(ch) => { - if ch.send_opt(()).is_ok() { - count += 1; - } - } - _ => break - } - } - count - } - - fn wait_end(&self) -> WaitEnd { - let (signal_end, wait_end) = channel(); - self.tail.send(signal_end); - wait_end - } -} - -// The building-block used to make semaphores, mutexes, and rwlocks. -struct Sem { - lock: mutex::Mutex, - // n.b, we need Sem to be `Sync`, but the WaitQueue type is not send/share - // (for good reason). We have an internal invariant on this semaphore, - // however, that the queue is never accessed outside of a locked - // context. - inner: UnsafeCell> -} - -struct SemInner { - count: int, - waiters: WaitQueue, - // Can be either unit or another waitqueue. Some sems shouldn't come with - // a condition variable attached, others should. - blocked: Q, -} - -#[must_use] -struct SemGuard<'a, Q:'a> { - sem: &'a Sem, -} - -impl Sem { - fn new(count: int, q: Q) -> Sem { - assert!(count >= 0, - "semaphores cannot be initialized with negative values"); - Sem { - lock: mutex::Mutex::new(), - inner: UnsafeCell::new(SemInner { - waiters: WaitQueue::new(), - count: count, - blocked: q, - }) - } - } - - unsafe fn with(&self, f: |&mut SemInner|) { - let _g = self.lock.lock(); - // This &mut is safe because, due to the lock, we are the only one who can touch the data - f(&mut *self.inner.get()) - } - - pub fn acquire(&self) { - unsafe { - let mut waiter_nobe = None; - self.with(|state| { - state.count -= 1; - if state.count < 0 { - // Create waiter nobe, enqueue ourself, and tell - // outer scope we need to block. - waiter_nobe = Some(state.waiters.wait_end()); - } - }); - // Uncomment if you wish to test for sem races. Not - // valgrind-friendly. - /* for _ in range(0u, 1000) { task::deschedule(); } */ - // Need to wait outside the exclusive. - if waiter_nobe.is_some() { - let _ = waiter_nobe.unwrap().recv(); - } - } - } - - pub fn release(&self) { - unsafe { - self.with(|state| { - state.count += 1; - if state.count <= 0 { - state.waiters.signal(); - } - }) - } - } - - pub fn access<'a>(&'a self) -> SemGuard<'a, Q> { - self.acquire(); - SemGuard { sem: self } - } -} - -#[unsafe_destructor] -impl<'a, Q: Send> Drop for SemGuard<'a, Q> { - fn drop(&mut self) { - self.sem.release(); - } -} - -impl Sem> { - fn new_and_signal(count: int, num_condvars: uint) -> Sem> { - let mut queues = Vec::new(); - for _ in range(0, num_condvars) { queues.push(WaitQueue::new()); } - Sem::new(count, queues) - } - - // The only other places that condvars get built are rwlock.write_cond() - // and rwlock_write_mode. - pub fn access_cond<'a>(&'a self) -> SemCondGuard<'a> { - SemCondGuard { - guard: self.access(), - cvar: Condvar { sem: self, order: Nothing, nocopy: marker::NoCopy }, - } - } -} - -// FIXME(#3598): Want to use an Option down below, but we need a custom enum -// that's not polymorphic to get around the fact that lifetimes are invariant -// inside of type parameters. -enum ReacquireOrderLock<'a> { - Nothing, // c.c - Just(&'a Semaphore), -} - -/// A mechanism for atomic-unlock-and-deschedule blocking and signalling. -pub struct Condvar<'a> { - // The 'Sem' object associated with this condvar. This is the one that's - // atomically-unlocked-and-descheduled upon and reacquired during wakeup. - sem: &'a Sem >, - // This is (can be) an extra semaphore which is held around the reacquire - // operation on the first one. This is only used in cvars associated with - // rwlocks, and is needed to ensure that, when a downgrader is trying to - // hand off the access lock (which would be the first field, here), a 2nd - // writer waking up from a cvar wait can't race with a reader to steal it, - // See the comment in write_cond for more detail. - order: ReacquireOrderLock<'a>, - // Make sure condvars are non-copyable. - nocopy: marker::NoCopy, -} - -impl<'a> Condvar<'a> { - /// Atomically drop the associated lock, and block until a signal is sent. - /// - /// # Panics - /// - /// A task which is killed while waiting on a condition variable will wake - /// up, panic, and unlock the associated lock as it unwinds. - pub fn wait(&self) { self.wait_on(0) } - - /// As wait(), but can specify which of multiple condition variables to - /// wait on. Only a signal_on() or broadcast_on() with the same condvar_id - /// will wake this thread. - /// - /// The associated lock must have been initialised with an appropriate - /// number of condvars. The condvar_id must be between 0 and num_condvars-1 - /// or else this call will panic. - /// - /// wait() is equivalent to wait_on(0). - pub fn wait_on(&self, condvar_id: uint) { - let mut wait_end = None; - let mut out_of_bounds = None; - // Release lock, 'atomically' enqueuing ourselves in so doing. - unsafe { - self.sem.with(|state| { - if condvar_id < state.blocked.len() { - // Drop the lock. - state.count += 1; - if state.count <= 0 { - state.waiters.signal(); - } - // Create waiter nobe, and enqueue ourself to - // be woken up by a signaller. - wait_end = Some(state.blocked[condvar_id].wait_end()); - } else { - out_of_bounds = Some(state.blocked.len()); - } - }) - } - - // If deschedule checks start getting inserted anywhere, we can be - // killed before or after enqueueing. - check_cvar_bounds(out_of_bounds, condvar_id, "cond.wait_on()", || { - // Unconditionally "block". (Might not actually block if a - // signaller already sent -- I mean 'unconditionally' in contrast - // with acquire().) - (|| { - let _ = wait_end.take().unwrap().recv(); - }).finally(|| { - // Reacquire the condvar. - match self.order { - Just(lock) => { - let _g = lock.access(); - self.sem.acquire(); - } - Nothing => self.sem.acquire(), - } - }) - }) - } - - /// Wake up a blocked task. Returns false if there was no blocked task. - pub fn signal(&self) -> bool { self.signal_on(0) } - - /// As signal, but with a specified condvar_id. See wait_on. - pub fn signal_on(&self, condvar_id: uint) -> bool { - unsafe { - let mut out_of_bounds = None; - let mut result = false; - self.sem.with(|state| { - if condvar_id < state.blocked.len() { - result = state.blocked[condvar_id].signal(); - } else { - out_of_bounds = Some(state.blocked.len()); - } - }); - check_cvar_bounds(out_of_bounds, - condvar_id, - "cond.signal_on()", - || result) - } - } - - /// Wake up all blocked tasks. Returns the number of tasks woken. - pub fn broadcast(&self) -> uint { self.broadcast_on(0) } - - /// As broadcast, but with a specified condvar_id. See wait_on. - pub fn broadcast_on(&self, condvar_id: uint) -> uint { - let mut out_of_bounds = None; - let mut queue = None; - unsafe { - self.sem.with(|state| { - if condvar_id < state.blocked.len() { - // To avoid :broadcast_heavy, we make a new waitqueue, - // swap it out with the old one, and broadcast on the - // old one outside of the little-lock. - queue = Some(mem::replace(&mut state.blocked[condvar_id], - WaitQueue::new())); - } else { - out_of_bounds = Some(state.blocked.len()); - } - }); - check_cvar_bounds(out_of_bounds, - condvar_id, - "cond.signal_on()", - || { - queue.take().unwrap().broadcast() - }) - } - } -} - -// Checks whether a condvar ID was out of bounds, and panics if so, or does -// something else next on success. -#[inline] -fn check_cvar_bounds( - out_of_bounds: Option, - id: uint, - act: &str, - blk: || -> U) - -> U { - match out_of_bounds { - Some(0) => - panic!("{} with illegal ID {} - this lock has no condvars!", act, id), - Some(length) => - panic!("{} with illegal ID {} - ID must be less than {}", act, id, length), - None => blk() - } -} - -#[must_use] -struct SemCondGuard<'a> { - guard: SemGuard<'a, Vec>, - cvar: Condvar<'a>, -} - -/// A counting, blocking, bounded-waiting semaphore. -pub struct Semaphore { - sem: Sem<()>, -} - -/// An RAII guard used to represent an acquired resource to a semaphore. When -/// dropped, this value will release the resource back to the semaphore. -#[must_use] -pub struct SemaphoreGuard<'a> { - _guard: SemGuard<'a, ()>, -} - -impl Semaphore { - /// Create a new semaphore with the specified count. - /// - /// # Panics - /// - /// This function will panic if `count` is negative. - pub fn new(count: int) -> Semaphore { - Semaphore { sem: Sem::new(count, ()) } - } - - /// Acquire a resource represented by the semaphore. Blocks if necessary - /// until resource(s) become available. - pub fn acquire(&self) { self.sem.acquire() } - - /// Release a held resource represented by the semaphore. Wakes a blocked - /// contending task, if any exist. Won't block the caller. - pub fn release(&self) { self.sem.release() } - - /// Acquire a resource of this semaphore, returning an RAII guard which will - /// release the resource when dropped. - pub fn access<'a>(&'a self) -> SemaphoreGuard<'a> { - SemaphoreGuard { _guard: self.sem.access() } - } -} - -/// A blocking, bounded-waiting, mutual exclusion lock with an associated -/// FIFO condition variable. -/// -/// # Panics -/// -/// A task which panicks while holding a mutex will unlock the mutex as it -/// unwinds. -pub struct Mutex { - sem: Sem>, -} - -/// An RAII structure which is used to gain access to a mutex's condition -/// variable. Additionally, when a value of this type is dropped, the -/// corresponding mutex is also unlocked. -#[must_use] -pub struct MutexGuard<'a> { - _guard: SemGuard<'a, Vec>, - /// Inner condition variable which is connected to the outer mutex, and can - /// be used for atomic-unlock-and-deschedule. - pub cond: Condvar<'a>, -} - -impl Mutex { - /// Create a new mutex, with one associated condvar. - pub fn new() -> Mutex { Mutex::new_with_condvars(1) } - - /// Create a new mutex, with a specified number of associated condvars. This - /// will allow calling wait_on/signal_on/broadcast_on with condvar IDs - /// between 0 and num_condvars-1. (If num_condvars is 0, lock_cond will be - /// allowed but any operations on the condvar will panic.) - pub fn new_with_condvars(num_condvars: uint) -> Mutex { - Mutex { sem: Sem::new_and_signal(1, num_condvars) } - } - - /// Acquires ownership of this mutex, returning an RAII guard which will - /// unlock the mutex when dropped. The associated condition variable can - /// also be accessed through the returned guard. - pub fn lock<'a>(&'a self) -> MutexGuard<'a> { - let SemCondGuard { guard, cvar } = self.sem.access_cond(); - MutexGuard { _guard: guard, cond: cvar } - } -} - -// NB: Wikipedia - Readers-writers_problem#The_third_readers-writers_problem - -/// A blocking, no-starvation, reader-writer lock with an associated condvar. -/// -/// # Panics -/// -/// A task which panics while holding an rwlock will unlock the rwlock as it -/// unwinds. -pub struct RWLock { - order_lock: Semaphore, - access_lock: Sem>, - - // The only way the count flag is ever accessed is with xadd. Since it is - // a read-modify-write operation, multiple xadds on different cores will - // always be consistent with respect to each other, so a monotonic/relaxed - // consistency ordering suffices (i.e., no extra barriers are needed). - // - // FIXME(#6598): The atomics module has no relaxed ordering flag, so I use - // acquire/release orderings superfluously. Change these someday. - read_count: atomic::AtomicUint, -} - -/// An RAII helper which is created by acquiring a read lock on an RWLock. When -/// dropped, this will unlock the RWLock. -#[must_use] -pub struct RWLockReadGuard<'a> { - lock: &'a RWLock, -} - -/// An RAII helper which is created by acquiring a write lock on an RWLock. When -/// dropped, this will unlock the RWLock. -/// -/// A value of this type can also be consumed to downgrade to a read-only lock. -#[must_use] -pub struct RWLockWriteGuard<'a> { - lock: &'a RWLock, - /// Inner condition variable that is connected to the write-mode of the - /// outer rwlock. - pub cond: Condvar<'a>, -} - -impl RWLock { - /// Create a new rwlock, with one associated condvar. - pub fn new() -> RWLock { RWLock::new_with_condvars(1) } - - /// Create a new rwlock, with a specified number of associated condvars. - /// Similar to mutex_with_condvars. - pub fn new_with_condvars(num_condvars: uint) -> RWLock { - RWLock { - order_lock: Semaphore::new(1), - access_lock: Sem::new_and_signal(1, num_condvars), - read_count: atomic::AtomicUint::new(0), - } - } - - /// Acquires a read-lock, returning an RAII guard that will unlock the lock - /// when dropped. Calls to 'read' from other tasks may run concurrently with - /// this one. - pub fn read<'a>(&'a self) -> RWLockReadGuard<'a> { - let _guard = self.order_lock.access(); - let old_count = self.read_count.fetch_add(1, atomic::Acquire); - if old_count == 0 { - self.access_lock.acquire(); - } - RWLockReadGuard { lock: self } - } - - /// Acquire a write-lock, returning an RAII guard that will unlock the lock - /// when dropped. No calls to 'read' or 'write' from other tasks will run - /// concurrently with this one. - /// - /// You can also downgrade a write to a read by calling the `downgrade` - /// method on the returned guard. Additionally, the guard will contain a - /// `Condvar` attached to this lock. - /// - /// # Example - /// - /// ```{rust,ignore} - /// use std::sync::raw::RWLock; - /// - /// let lock = RWLock::new(); - /// let write = lock.write(); - /// // ... exclusive access ... - /// let read = write.downgrade(); - /// // ... shared access ... - /// drop(read); - /// ``` - pub fn write<'a>(&'a self) -> RWLockWriteGuard<'a> { - let _g = self.order_lock.access(); - self.access_lock.acquire(); - - // It's important to thread our order lock into the condvar, so that - // when a cond.wait() wakes up, it uses it while reacquiring the - // access lock. If we permitted a waking-up writer to "cut in line", - // there could arise a subtle race when a downgrader attempts to hand - // off the reader cloud lock to a waiting reader. This race is tested - // in arc.rs (test_rw_write_cond_downgrade_read_race) and looks like: - // T1 (writer) T2 (downgrader) T3 (reader) - // [in cond.wait()] - // [locks for writing] - // [holds access_lock] - // [is signalled, perhaps by - // downgrader or a 4th thread] - // tries to lock access(!) - // lock order_lock - // xadd read_count[0->1] - // tries to lock access - // [downgrade] - // xadd read_count[1->2] - // unlock access - // Since T1 contended on the access lock before T3 did, it will steal - // the lock handoff. Adding order_lock in the condvar reacquire path - // solves this because T1 will hold order_lock while waiting on access, - // which will cause T3 to have to wait until T1 finishes its write, - // which can't happen until T2 finishes the downgrade-read entirely. - // The astute reader will also note that making waking writers use the - // order_lock is better for not starving readers. - RWLockWriteGuard { - lock: self, - cond: Condvar { - sem: &self.access_lock, - order: Just(&self.order_lock), - nocopy: marker::NoCopy, - } - } - } -} - -impl<'a> RWLockWriteGuard<'a> { - /// Consumes this write lock and converts it into a read lock. - pub fn downgrade(self) -> RWLockReadGuard<'a> { - let lock = self.lock; - // Don't run the destructor of the write guard, we're in charge of - // things from now on - unsafe { mem::forget(self) } - - let old_count = lock.read_count.fetch_add(1, atomic::Release); - // If another reader was already blocking, we need to hand-off - // the "reader cloud" access lock to them. - if old_count != 0 { - // Guaranteed not to let another writer in, because - // another reader was holding the order_lock. Hence they - // must be the one to get the access_lock (because all - // access_locks are acquired with order_lock held). See - // the comment in write_cond for more justification. - lock.access_lock.release(); - } - RWLockReadGuard { lock: lock } - } -} - -#[unsafe_destructor] -impl<'a> Drop for RWLockWriteGuard<'a> { - fn drop(&mut self) { - self.lock.access_lock.release(); - } -} - -#[unsafe_destructor] -impl<'a> Drop for RWLockReadGuard<'a> { - fn drop(&mut self) { - let old_count = self.lock.read_count.fetch_sub(1, atomic::Release); - assert!(old_count > 0); - if old_count == 1 { - // Note: this release used to be outside of a locked access - // to exclusive-protected state. If this code is ever - // converted back to such (instead of using atomic ops), - // this access MUST NOT go inside the exclusive access. - self.lock.access_lock.release(); - } - } -} - -#[cfg(test)] -mod tests { - pub use self::RWLockMode::*; - - use sync::Arc; - use prelude::*; - use super::{Semaphore, Mutex, RWLock, Condvar}; - - use mem; - use result; - use task; - - #[test] - fn test_sem_acquire_release() { - let s = Semaphore::new(1); - s.acquire(); - s.release(); - s.acquire(); - } - - #[test] - fn test_sem_basic() { - let s = Semaphore::new(1); - let _g = s.access(); - } - - #[test] - #[should_fail] - fn test_sem_basic2() { - Semaphore::new(-1); - } - - #[test] - fn test_sem_as_mutex() { - let s = Arc::new(Semaphore::new(1)); - let s2 = s.clone(); - task::spawn(proc() { - let _g = s2.access(); - for _ in range(0u, 5) { task::deschedule(); } - }); - let _g = s.access(); - for _ in range(0u, 5) { task::deschedule(); } - } - - #[test] - fn test_sem_as_cvar() { - /* Child waits and parent signals */ - let (tx, rx) = channel(); - let s = Arc::new(Semaphore::new(0)); - let s2 = s.clone(); - task::spawn(proc() { - s2.acquire(); - tx.send(()); - }); - for _ in range(0u, 5) { task::deschedule(); } - s.release(); - let _ = rx.recv(); - - /* Parent waits and child signals */ - let (tx, rx) = channel(); - let s = Arc::new(Semaphore::new(0)); - let s2 = s.clone(); - task::spawn(proc() { - for _ in range(0u, 5) { task::deschedule(); } - s2.release(); - let _ = rx.recv(); - }); - s.acquire(); - tx.send(()); - } - - #[test] - fn test_sem_multi_resource() { - // Parent and child both get in the critical section at the same - // time, and shake hands. - let s = Arc::new(Semaphore::new(2)); - let s2 = s.clone(); - let (tx1, rx1) = channel(); - let (tx2, rx2) = channel(); - task::spawn(proc() { - let _g = s2.access(); - let _ = rx2.recv(); - tx1.send(()); - }); - let _g = s.access(); - tx2.send(()); - let _ = rx1.recv(); - } - - #[test] - fn test_sem_runtime_friendly_blocking() { - // Force the runtime to schedule two threads on the same sched_loop. - // When one blocks, it should schedule the other one. - let s = Arc::new(Semaphore::new(1)); - let s2 = s.clone(); - let (tx, rx) = channel(); - { - let _g = s.access(); - task::spawn(proc() { - tx.send(()); - drop(s2.access()); - tx.send(()); - }); - rx.recv(); // wait for child to come alive - for _ in range(0u, 5) { task::deschedule(); } // let the child contend - } - rx.recv(); // wait for child to be done - } - - #[test] - fn test_mutex_lock() { - // Unsafely achieve shared state, and do the textbook - // "load tmp = move ptr; inc tmp; store ptr <- tmp" dance. - let (tx, rx) = channel(); - let m = Arc::new(Mutex::new()); - let m2 = m.clone(); - let mut sharedstate = box 0; - { - let ptr: *mut int = &mut *sharedstate; - task::spawn(proc() { - access_shared(ptr, &m2, 10); - tx.send(()); - }); - } - { - access_shared(&mut *sharedstate, &m, 10); - let _ = rx.recv(); - - assert_eq!(*sharedstate, 20); - } - - fn access_shared(sharedstate: *mut int, m: &Arc, n: uint) { - for _ in range(0u, n) { - let _g = m.lock(); - let oldval = unsafe { *sharedstate }; - task::deschedule(); - unsafe { *sharedstate = oldval + 1; } - } - } - } - - #[test] - fn test_mutex_cond_wait() { - let m = Arc::new(Mutex::new()); - - // Child wakes up parent - { - let lock = m.lock(); - let m2 = m.clone(); - task::spawn(proc() { - let lock = m2.lock(); - let woken = lock.cond.signal(); - assert!(woken); - }); - lock.cond.wait(); - } - // Parent wakes up child - let (tx, rx) = channel(); - let m3 = m.clone(); - task::spawn(proc() { - let lock = m3.lock(); - tx.send(()); - lock.cond.wait(); - tx.send(()); - }); - rx.recv(); // Wait until child gets in the mutex - { - let lock = m.lock(); - let woken = lock.cond.signal(); - assert!(woken); - } - rx.recv(); // Wait until child wakes up - } - - fn test_mutex_cond_broadcast_helper(num_waiters: uint) { - let m = Arc::new(Mutex::new()); - let mut rxs = Vec::new(); - - for _ in range(0u, num_waiters) { - let mi = m.clone(); - let (tx, rx) = channel(); - rxs.push(rx); - task::spawn(proc() { - let lock = mi.lock(); - tx.send(()); - lock.cond.wait(); - tx.send(()); - }); - } - - // wait until all children get in the mutex - for rx in rxs.iter_mut() { rx.recv(); } - { - let lock = m.lock(); - let num_woken = lock.cond.broadcast(); - assert_eq!(num_woken, num_waiters); - } - // wait until all children wake up - for rx in rxs.iter_mut() { rx.recv(); } - } - - #[test] - fn test_mutex_cond_broadcast() { - test_mutex_cond_broadcast_helper(12); - } - - #[test] - fn test_mutex_cond_broadcast_none() { - test_mutex_cond_broadcast_helper(0); - } - - #[test] - fn test_mutex_cond_no_waiter() { - let m = Arc::new(Mutex::new()); - let m2 = m.clone(); - let _ = task::try(proc() { - drop(m.lock()); - }); - let lock = m2.lock(); - assert!(!lock.cond.signal()); - } - - #[test] - fn test_mutex_killed_simple() { - use any::Any; - - // Mutex must get automatically unlocked if panicked/killed within. - let m = Arc::new(Mutex::new()); - let m2 = m.clone(); - - let result: result::Result<(), Box> = task::try(proc() { - let _lock = m2.lock(); - panic!(); - }); - assert!(result.is_err()); - // child task must have finished by the time try returns - drop(m.lock()); - } - - #[test] - fn test_mutex_cond_signal_on_0() { - // Tests that signal_on(0) is equivalent to signal(). - let m = Arc::new(Mutex::new()); - let lock = m.lock(); - let m2 = m.clone(); - task::spawn(proc() { - let lock = m2.lock(); - lock.cond.signal_on(0); - }); - lock.cond.wait(); - } - - #[test] - fn test_mutex_no_condvars() { - let result = task::try(proc() { - let m = Mutex::new_with_condvars(0); - m.lock().cond.wait(); - }); - assert!(result.is_err()); - let result = task::try(proc() { - let m = Mutex::new_with_condvars(0); - m.lock().cond.signal(); - }); - assert!(result.is_err()); - let result = task::try(proc() { - let m = Mutex::new_with_condvars(0); - m.lock().cond.broadcast(); - }); - assert!(result.is_err()); - } - - #[cfg(test)] - pub enum RWLockMode { Read, Write, Downgrade, DowngradeRead } - - #[cfg(test)] - fn lock_rwlock_in_mode(x: &Arc, mode: RWLockMode, blk: ||) { - match mode { - Read => { let _g = x.read(); blk() } - Write => { let _g = x.write(); blk() } - Downgrade => { let _g = x.write(); blk() } - DowngradeRead => { let _g = x.write().downgrade(); blk() } - } - } - - #[cfg(test)] - fn test_rwlock_exclusion(x: Arc, - mode1: RWLockMode, - mode2: RWLockMode) { - // Test mutual exclusion between readers and writers. Just like the - // mutex mutual exclusion test, a ways above. - let (tx, rx) = channel(); - let x2 = x.clone(); - let mut sharedstate = box 0; - { - let ptr: *const int = &*sharedstate; - task::spawn(proc() { - let sharedstate: &mut int = - unsafe { mem::transmute(ptr) }; - access_shared(sharedstate, &x2, mode1, 10); - tx.send(()); - }); - } - { - access_shared(&mut *sharedstate, &x, mode2, 10); - let _ = rx.recv(); - - assert_eq!(*sharedstate, 20); - } - - fn access_shared(sharedstate: &mut int, x: &Arc, - mode: RWLockMode, n: uint) { - for _ in range(0u, n) { - lock_rwlock_in_mode(x, mode, || { - let oldval = *sharedstate; - task::deschedule(); - *sharedstate = oldval + 1; - }) - } - } - } - - #[test] - fn test_rwlock_readers_wont_modify_the_data() { - test_rwlock_exclusion(Arc::new(RWLock::new()), Read, Write); - test_rwlock_exclusion(Arc::new(RWLock::new()), Write, Read); - test_rwlock_exclusion(Arc::new(RWLock::new()), Read, Downgrade); - test_rwlock_exclusion(Arc::new(RWLock::new()), Downgrade, Read); - test_rwlock_exclusion(Arc::new(RWLock::new()), Write, DowngradeRead); - test_rwlock_exclusion(Arc::new(RWLock::new()), DowngradeRead, Write); - } - - #[test] - fn test_rwlock_writers_and_writers() { - test_rwlock_exclusion(Arc::new(RWLock::new()), Write, Write); - test_rwlock_exclusion(Arc::new(RWLock::new()), Write, Downgrade); - test_rwlock_exclusion(Arc::new(RWLock::new()), Downgrade, Write); - test_rwlock_exclusion(Arc::new(RWLock::new()), Downgrade, Downgrade); - } - - #[cfg(test)] - fn test_rwlock_handshake(x: Arc, - mode1: RWLockMode, - mode2: RWLockMode, - make_mode2_go_first: bool) { - // Much like sem_multi_resource. - let x2 = x.clone(); - let (tx1, rx1) = channel(); - let (tx2, rx2) = channel(); - task::spawn(proc() { - if !make_mode2_go_first { - rx2.recv(); // parent sends to us once it locks, or ... - } - lock_rwlock_in_mode(&x2, mode2, || { - if make_mode2_go_first { - tx1.send(()); // ... we send to it once we lock - } - rx2.recv(); - tx1.send(()); - }) - }); - if make_mode2_go_first { - rx1.recv(); // child sends to us once it locks, or ... - } - lock_rwlock_in_mode(&x, mode1, || { - if !make_mode2_go_first { - tx2.send(()); // ... we send to it once we lock - } - tx2.send(()); - rx1.recv(); - }) - } - - #[test] - fn test_rwlock_readers_and_readers() { - test_rwlock_handshake(Arc::new(RWLock::new()), Read, Read, false); - // The downgrader needs to get in before the reader gets in, otherwise - // they cannot end up reading at the same time. - test_rwlock_handshake(Arc::new(RWLock::new()), DowngradeRead, Read, false); - test_rwlock_handshake(Arc::new(RWLock::new()), Read, DowngradeRead, true); - // Two downgrade_reads can never both end up reading at the same time. - } - - #[test] - fn test_rwlock_downgrade_unlock() { - // Tests that downgrade can unlock the lock in both modes - let x = Arc::new(RWLock::new()); - lock_rwlock_in_mode(&x, Downgrade, || { }); - test_rwlock_handshake(x, Read, Read, false); - let y = Arc::new(RWLock::new()); - lock_rwlock_in_mode(&y, DowngradeRead, || { }); - test_rwlock_exclusion(y, Write, Write); - } - - #[test] - fn test_rwlock_read_recursive() { - let x = RWLock::new(); - let _g1 = x.read(); - let _g2 = x.read(); - } - - #[test] - fn test_rwlock_cond_wait() { - // As test_mutex_cond_wait above. - let x = Arc::new(RWLock::new()); - - // Child wakes up parent - { - let lock = x.write(); - let x2 = x.clone(); - task::spawn(proc() { - let lock = x2.write(); - assert!(lock.cond.signal()); - }); - lock.cond.wait(); - } - // Parent wakes up child - let (tx, rx) = channel(); - let x3 = x.clone(); - task::spawn(proc() { - let lock = x3.write(); - tx.send(()); - lock.cond.wait(); - tx.send(()); - }); - rx.recv(); // Wait until child gets in the rwlock - drop(x.read()); // Must be able to get in as a reader - { - let x = x.write(); - assert!(x.cond.signal()); - } - rx.recv(); // Wait until child wakes up - drop(x.read()); // Just for good measure - } - - #[cfg(test)] - fn test_rwlock_cond_broadcast_helper(num_waiters: uint) { - // Much like the mutex broadcast test. Downgrade-enabled. - fn lock_cond(x: &Arc, blk: |c: &Condvar|) { - let lock = x.write(); - blk(&lock.cond); - } - - let x = Arc::new(RWLock::new()); - let mut rxs = Vec::new(); - - for _ in range(0u, num_waiters) { - let xi = x.clone(); - let (tx, rx) = channel(); - rxs.push(rx); - task::spawn(proc() { - lock_cond(&xi, |cond| { - tx.send(()); - cond.wait(); - tx.send(()); - }) - }); - } - - // wait until all children get in the mutex - for rx in rxs.iter_mut() { let _ = rx.recv(); } - lock_cond(&x, |cond| { - let num_woken = cond.broadcast(); - assert_eq!(num_woken, num_waiters); - }); - // wait until all children wake up - for rx in rxs.iter_mut() { let _ = rx.recv(); } - } - - #[test] - fn test_rwlock_cond_broadcast() { - test_rwlock_cond_broadcast_helper(0); - test_rwlock_cond_broadcast_helper(12); - } - - #[cfg(test)] - fn rwlock_kill_helper(mode1: RWLockMode, mode2: RWLockMode) { - use any::Any; - - // Mutex must get automatically unlocked if panicked/killed within. - let x = Arc::new(RWLock::new()); - let x2 = x.clone(); - - let result: result::Result<(), Box> = task::try(proc() { - lock_rwlock_in_mode(&x2, mode1, || { - panic!(); - }) - }); - assert!(result.is_err()); - // child task must have finished by the time try returns - lock_rwlock_in_mode(&x, mode2, || { }) - } - - #[test] - fn test_rwlock_reader_killed_writer() { - rwlock_kill_helper(Read, Write); - } - - #[test] - fn test_rwlock_writer_killed_reader() { - rwlock_kill_helper(Write, Read); - } - - #[test] - fn test_rwlock_reader_killed_reader() { - rwlock_kill_helper(Read, Read); - } - - #[test] - fn test_rwlock_writer_killed_writer() { - rwlock_kill_helper(Write, Write); - } - - #[test] - fn test_rwlock_kill_downgrader() { - rwlock_kill_helper(Downgrade, Read); - rwlock_kill_helper(Read, Downgrade); - rwlock_kill_helper(Downgrade, Write); - rwlock_kill_helper(Write, Downgrade); - rwlock_kill_helper(DowngradeRead, Read); - rwlock_kill_helper(Read, DowngradeRead); - rwlock_kill_helper(DowngradeRead, Write); - rwlock_kill_helper(Write, DowngradeRead); - rwlock_kill_helper(DowngradeRead, Downgrade); - rwlock_kill_helper(DowngradeRead, Downgrade); - rwlock_kill_helper(Downgrade, DowngradeRead); - rwlock_kill_helper(Downgrade, DowngradeRead); - } -} diff --git a/src/libstd/sync/rwlock.rs b/src/libstd/sync/rwlock.rs new file mode 100644 index 0000000000000..a4f8b1df6af52 --- /dev/null +++ b/src/libstd/sync/rwlock.rs @@ -0,0 +1,514 @@ +// Copyright 2014 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 prelude::*; + +use kinds::marker; +use cell::UnsafeCell; +use sys_common::rwlock as sys; +use sync::poison; + +/// A reader-writer lock +/// +/// This type of lock allows a number of readers or at most one writer at any +/// point in time. The write portion of this lock typically allows modification +/// of the underlying data (exclusive access) and the read portion of this lock +/// typically allows for read-only access (shared access). +/// +/// The type parameter `T` represents the data that this lock protects. It is +/// required that `T` satisfies `Send` to be shared across tasks and `Sync` to +/// allow concurrent access through readers. The RAII guards returned from the +/// locking methods implement `Deref` (and `DerefMut` for the `write` methods) +/// to allow access to the contained of the lock. +/// +/// RWLocks, like Mutexes, will become poisoned on panics. Note, however, that +/// an RWLock may only be poisoned if a panic occurs while it is locked +/// exclusively (write mode). If a panic occurs in any reader, then the lock +/// will not be poisoned. +/// +/// # Example +/// +/// ``` +/// use std::sync::RWLock; +/// +/// let lock = RWLock::new(5i); +/// +/// // many reader locks can be held at once +/// { +/// let r1 = lock.read(); +/// let r2 = lock.read(); +/// assert_eq!(*r1, 5); +/// assert_eq!(*r2, 5); +/// } // read locks are dropped at this point +/// +/// // only one write lock may be held, however +/// { +/// let mut w = lock.write(); +/// *w += 1; +/// assert_eq!(*w, 6); +/// } // write lock is dropped here +/// ``` +pub struct RWLock { + inner: Box, + data: UnsafeCell, +} + +/// Structure representing a staticaly allocated RWLock. +/// +/// This structure is intended to be used inside of a `static` and will provide +/// automatic global access as well as lazy initialization. The internal +/// resources of this RWLock, however, must be manually deallocated. +/// +/// # Example +/// +/// ``` +/// use std::sync::{StaticRWLock, RWLOCK_INIT}; +/// +/// static LOCK: StaticRWLock = RWLOCK_INIT; +/// +/// { +/// let _g = LOCK.read(); +/// // ... shared read access +/// } +/// { +/// let _g = LOCK.write(); +/// // ... exclusive write access +/// } +/// unsafe { LOCK.destroy() } // free all resources +/// ``` +pub struct StaticRWLock { + inner: sys::RWLock, + poison: UnsafeCell, +} + +/// Constant initialization for a statically-initialized rwlock. +pub const RWLOCK_INIT: StaticRWLock = StaticRWLock { + inner: sys::RWLOCK_INIT, + poison: UnsafeCell { value: poison::Flag { failed: false } }, +}; + +/// RAII structure used to release the shared read access of a lock when +/// dropped. +#[must_use] +pub struct RWLockReadGuard<'a, T: 'a> { + __lock: &'a RWLock, + __guard: StaticRWLockReadGuard, +} + +/// RAII structure used to release the exclusive write access of a lock when +/// dropped. +#[must_use] +pub struct RWLockWriteGuard<'a, T: 'a> { + __lock: &'a RWLock, + __guard: StaticRWLockWriteGuard, +} + +/// RAII structure used to release the shared read access of a lock when +/// dropped. +#[must_use] +pub struct StaticRWLockReadGuard { + lock: &'static sys::RWLock, + marker: marker::NoSend, +} + +/// RAII structure used to release the exclusive write access of a lock when +/// dropped. +#[must_use] +pub struct StaticRWLockWriteGuard { + lock: &'static sys::RWLock, + marker: marker::NoSend, + poison: poison::Guard<'static>, +} + +impl RWLock { + /// Creates a new instance of an RWLock which is unlocked and read to go. + pub fn new(t: T) -> RWLock { + RWLock { inner: box RWLOCK_INIT, data: UnsafeCell::new(t) } + } + + /// Locks this rwlock with shared read access, blocking the current thread + /// until it can be acquired. + /// + /// The calling thread will be blocked until there are no more writers which + /// hold the lock. There may be other readers currently inside the lock when + /// this method returns. This method does not provide any guarantees with + /// respect to the ordering of whether contentious readers or writers will + /// acquire the lock first. + /// + /// Returns an RAII guard which will release this thread's shared access + /// once it is dropped. + /// + /// # Panics + /// + /// This function will panic if the RWLock is poisoned. An RWLock is + /// poisoned whenever a writer panics while holding an exclusive lock. The + /// panic will occur immediately after the lock has been acquired. + #[inline] + pub fn read(&self) -> RWLockReadGuard { + unsafe { + let lock: &'static StaticRWLock = &*(&*self.inner as *const _); + RWLockReadGuard::new(self, lock.read()) + } + } + + /// Attempt to acquire this lock with shared read access. + /// + /// This function will never block and will return immediately if `read` + /// would otherwise succeed. Returns `Some` of an RAII guard which will + /// release the shared access of this thread when dropped, or `None` if the + /// access could not be granted. This method does not provide any + /// guarantees with respect to the ordering of whether contentious readers + /// or writers will acquire the lock first. + /// + /// # Panics + /// + /// This function will panic if the RWLock is poisoned. An RWLock is + /// poisoned whenever a writer panics while holding an exclusive lock. A + /// panic will only occur if the lock is acquired. + #[inline] + pub fn try_read(&self) -> Option> { + unsafe { + let lock: &'static StaticRWLock = &*(&*self.inner as *const _); + lock.try_read().map(|guard| { + RWLockReadGuard::new(self, guard) + }) + } + } + + /// Lock this rwlock with exclusive write access, blocking the current + /// thread until it can be acquired. + /// + /// This function will not return while other writers or other readers + /// currently have access to the lock. + /// + /// Returns an RAII guard which will drop the write access of this rwlock + /// when dropped. + /// + /// # Panics + /// + /// This function will panic if the RWLock is poisoned. An RWLock is + /// poisoned whenever a writer panics while holding an exclusive lock. The + /// panic will occur when the lock is acquired. + #[inline] + pub fn write(&self) -> RWLockWriteGuard { + unsafe { + let lock: &'static StaticRWLock = &*(&*self.inner as *const _); + RWLockWriteGuard::new(self, lock.write()) + } + } + + /// Attempt to lock this rwlock with exclusive write access. + /// + /// This function does not ever block, and it will return `None` if a call + /// to `write` would otherwise block. If successful, an RAII guard is + /// returned. + /// + /// # Panics + /// + /// This function will panic if the RWLock is poisoned. An RWLock is + /// poisoned whenever a writer panics while holding an exclusive lock. A + /// panic will only occur if the lock is acquired. + #[inline] + pub fn try_write(&self) -> Option> { + unsafe { + let lock: &'static StaticRWLock = &*(&*self.inner as *const _); + lock.try_write().map(|guard| { + RWLockWriteGuard::new(self, guard) + }) + } + } +} + +#[unsafe_destructor] +impl Drop for RWLock { + fn drop(&mut self) { + unsafe { self.inner.inner.destroy() } + } +} + +impl StaticRWLock { + /// Locks this rwlock with shared read access, blocking the current thread + /// until it can be acquired. + /// + /// See `RWLock::read`. + #[inline] + pub fn read(&'static self) -> StaticRWLockReadGuard { + unsafe { self.inner.read() } + StaticRWLockReadGuard::new(self) + } + + /// Attempt to acquire this lock with shared read access. + /// + /// See `RWLock::try_read`. + #[inline] + pub fn try_read(&'static self) -> Option { + if unsafe { self.inner.try_read() } { + Some(StaticRWLockReadGuard::new(self)) + } else { + None + } + } + + /// Lock this rwlock with exclusive write access, blocking the current + /// thread until it can be acquired. + /// + /// See `RWLock::write`. + #[inline] + pub fn write(&'static self) -> StaticRWLockWriteGuard { + unsafe { self.inner.write() } + StaticRWLockWriteGuard::new(self) + } + + /// Attempt to lock this rwlock with exclusive write access. + /// + /// See `RWLock::try_write`. + #[inline] + pub fn try_write(&'static self) -> Option { + if unsafe { self.inner.try_write() } { + Some(StaticRWLockWriteGuard::new(self)) + } else { + None + } + } + + /// Deallocate all resources associated with this static lock. + /// + /// This method is unsafe to call as there is no guarantee that there are no + /// active users of the lock, and this also doesn't prevent any future users + /// of this lock. This method is required to be called to not leak memory on + /// all platforms. + pub unsafe fn destroy(&'static self) { + self.inner.destroy() + } +} + +impl<'rwlock, T> RWLockReadGuard<'rwlock, T> { + fn new(lock: &RWLock, guard: StaticRWLockReadGuard) + -> RWLockReadGuard { + RWLockReadGuard { __lock: lock, __guard: guard } + } +} +impl<'rwlock, T> RWLockWriteGuard<'rwlock, T> { + fn new(lock: &RWLock, guard: StaticRWLockWriteGuard) + -> RWLockWriteGuard { + RWLockWriteGuard { __lock: lock, __guard: guard } + } +} + +impl<'rwlock, T> Deref for RWLockReadGuard<'rwlock, T> { + fn deref(&self) -> &T { unsafe { &*self.__lock.data.get() } } +} +impl<'rwlock, T> Deref for RWLockWriteGuard<'rwlock, T> { + fn deref(&self) -> &T { unsafe { &*self.__lock.data.get() } } +} +impl<'rwlock, T> DerefMut for RWLockWriteGuard<'rwlock, T> { + fn deref_mut(&mut self) -> &mut T { unsafe { &mut *self.__lock.data.get() } } +} + +impl StaticRWLockReadGuard { + fn new(lock: &'static StaticRWLock) -> StaticRWLockReadGuard { + let guard = StaticRWLockReadGuard { + lock: &lock.inner, + marker: marker::NoSend, + }; + unsafe { (*lock.poison.get()).borrow().check("rwlock"); } + return guard; + } +} +impl StaticRWLockWriteGuard { + fn new(lock: &'static StaticRWLock) -> StaticRWLockWriteGuard { + unsafe { + let guard = StaticRWLockWriteGuard { + lock: &lock.inner, + marker: marker::NoSend, + poison: (*lock.poison.get()).borrow(), + }; + guard.poison.check("rwlock"); + return guard; + } + } +} + +#[unsafe_destructor] +impl Drop for StaticRWLockReadGuard { + fn drop(&mut self) { + unsafe { self.lock.read_unlock(); } + } +} + +#[unsafe_destructor] +impl Drop for StaticRWLockWriteGuard { + fn drop(&mut self) { + self.poison.done(); + unsafe { self.lock.write_unlock(); } + } +} + +#[cfg(test)] +mod tests { + use prelude::*; + + use rand::{mod, Rng}; + use task; + use sync::{Arc, RWLock, StaticRWLock, RWLOCK_INIT}; + + #[test] + fn smoke() { + let l = RWLock::new(()); + drop(l.read()); + drop(l.write()); + drop((l.read(), l.read())); + drop(l.write()); + } + + #[test] + fn static_smoke() { + static R: StaticRWLock = RWLOCK_INIT; + drop(R.read()); + drop(R.write()); + drop((R.read(), R.read())); + drop(R.write()); + unsafe { R.destroy(); } + } + + #[test] + fn frob() { + static R: StaticRWLock = RWLOCK_INIT; + static N: uint = 10; + static M: uint = 1000; + + let (tx, rx) = channel::<()>(); + for _ in range(0, N) { + let tx = tx.clone(); + spawn(proc() { + let mut rng = rand::task_rng(); + for _ in range(0, M) { + if rng.gen_weighted_bool(N) { + drop(R.write()); + } else { + drop(R.read()); + } + } + drop(tx); + }); + } + drop(tx); + let _ = rx.recv_opt(); + unsafe { R.destroy(); } + } + + #[test] + #[should_fail] + fn test_rw_arc_poison_wr() { + let arc = Arc::new(RWLock::new(1i)); + let arc2 = arc.clone(); + let _ = task::try(proc() { + let lock = arc2.write(); + assert_eq!(*lock, 2); + }); + let lock = arc.read(); + assert_eq!(*lock, 1); + } + + #[test] + #[should_fail] + fn test_rw_arc_poison_ww() { + let arc = Arc::new(RWLock::new(1i)); + let arc2 = arc.clone(); + let _ = task::try(proc() { + let lock = arc2.write(); + assert_eq!(*lock, 2); + }); + let lock = arc.write(); + assert_eq!(*lock, 1); + } + + #[test] + fn test_rw_arc_no_poison_rr() { + let arc = Arc::new(RWLock::new(1i)); + let arc2 = arc.clone(); + let _ = task::try(proc() { + let lock = arc2.read(); + assert_eq!(*lock, 2); + }); + let lock = arc.read(); + assert_eq!(*lock, 1); + } + #[test] + fn test_rw_arc_no_poison_rw() { + let arc = Arc::new(RWLock::new(1i)); + let arc2 = arc.clone(); + let _ = task::try(proc() { + let lock = arc2.read(); + assert_eq!(*lock, 2); + }); + let lock = arc.write(); + assert_eq!(*lock, 1); + } + + #[test] + fn test_rw_arc() { + let arc = Arc::new(RWLock::new(0i)); + let arc2 = arc.clone(); + let (tx, rx) = channel(); + + task::spawn(proc() { + let mut lock = arc2.write(); + for _ in range(0u, 10) { + let tmp = *lock; + *lock = -1; + task::deschedule(); + *lock = tmp + 1; + } + tx.send(()); + }); + + // Readers try to catch the writer in the act + let mut children = Vec::new(); + for _ in range(0u, 5) { + let arc3 = arc.clone(); + children.push(task::try_future(proc() { + let lock = arc3.read(); + assert!(*lock >= 0); + })); + } + + // Wait for children to pass their asserts + for r in children.iter_mut() { + assert!(r.get_ref().is_ok()); + } + + // Wait for writer to finish + rx.recv(); + let lock = arc.read(); + assert_eq!(*lock, 10); + } + + #[test] + fn test_rw_arc_access_in_unwind() { + let arc = Arc::new(RWLock::new(1i)); + let arc2 = arc.clone(); + let _ = task::try::<()>(proc() { + struct Unwinder { + i: Arc>, + } + impl Drop for Unwinder { + fn drop(&mut self) { + let mut lock = self.i.write(); + *lock += 1; + } + } + let _u = Unwinder { i: arc2 }; + panic!(); + }); + let lock = arc.read(); + assert_eq!(*lock, 2); + } +} diff --git a/src/libstd/sync/semaphore.rs b/src/libstd/sync/semaphore.rs new file mode 100644 index 0000000000000..03fb84c38d470 --- /dev/null +++ b/src/libstd/sync/semaphore.rs @@ -0,0 +1,195 @@ +// Copyright 2014 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 ops::Drop; +use sync::{Mutex, Condvar}; + +/// A counting, blocking, semaphore. +/// +/// Semaphores are a form of atomic counter where access is only granted if the +/// counter is a positive value. Each acquisition will block the calling thread +/// until the counter is positive, and each release will increment the counter +/// and unblock any threads if necessary. +/// +/// # Example +/// +/// ``` +/// use std::sync::Semaphore; +/// +/// // Create a semaphore that represents 5 resources +/// let sem = Semaphore::new(5); +/// +/// // Acquire one of the resources +/// sem.acquire(); +/// +/// // Acquire one of the resources for a limited period of time +/// { +/// let _guard = sem.access(); +/// // ... +/// } // resources is released here +/// +/// // Release our initially acquired resource +/// sem.release(); +/// ``` +pub struct Semaphore { + lock: Mutex, + cvar: Condvar, +} + +/// An RAII guard which will release a resource acquired from a semaphore when +/// dropped. +pub struct SemaphoreGuard<'a> { + sem: &'a Semaphore, +} + +impl Semaphore { + /// Creates a new semaphore with the initial count specified. + /// + /// The count specified can be thought of as a number of resources, and a + /// call to `acquire` or `access` will block until at least one resource is + /// available. It is valid to initialize a semaphore with a negative count. + pub fn new(count: int) -> Semaphore { + Semaphore { + lock: Mutex::new(count), + cvar: Condvar::new(), + } + } + + /// Acquires a resource of this semaphore, blocking the current thread until + /// it can do so. + /// + /// This method will block until the internal count of the semaphore is at + /// least 1. + pub fn acquire(&self) { + let mut count = self.lock.lock(); + while *count <= 0 { + self.cvar.wait(&count); + } + *count -= 1; + } + + /// Release a resource from this semaphore. + /// + /// This will increment the number of resources in this semaphore by 1 and + /// will notify any pending waiters in `acquire` or `access` if necessary. + pub fn release(&self) { + *self.lock.lock() += 1; + self.cvar.notify_one(); + } + + /// Acquires a resource of this semaphore, returning an RAII guard to + /// release the semaphore when dropped. + /// + /// This function is semantically equivalent to an `acquire` followed by a + /// `release` when the guard returned is dropped. + pub fn access(&self) -> SemaphoreGuard { + self.acquire(); + SemaphoreGuard { sem: self } + } +} + +#[unsafe_destructor] +impl<'a> Drop for SemaphoreGuard<'a> { + fn drop(&mut self) { + self.sem.release(); + } +} + +#[cfg(test)] +mod tests { + use prelude::*; + + use sync::Arc; + use super::Semaphore; + + #[test] + fn test_sem_acquire_release() { + let s = Semaphore::new(1); + s.acquire(); + s.release(); + s.acquire(); + } + + #[test] + fn test_sem_basic() { + let s = Semaphore::new(1); + let _g = s.access(); + } + + #[test] + fn test_sem_as_mutex() { + let s = Arc::new(Semaphore::new(1)); + let s2 = s.clone(); + spawn(proc() { + let _g = s2.access(); + }); + let _g = s.access(); + } + + #[test] + fn test_sem_as_cvar() { + /* Child waits and parent signals */ + let (tx, rx) = channel(); + let s = Arc::new(Semaphore::new(0)); + let s2 = s.clone(); + spawn(proc() { + s2.acquire(); + tx.send(()); + }); + s.release(); + let _ = rx.recv(); + + /* Parent waits and child signals */ + let (tx, rx) = channel(); + let s = Arc::new(Semaphore::new(0)); + let s2 = s.clone(); + spawn(proc() { + s2.release(); + let _ = rx.recv(); + }); + s.acquire(); + tx.send(()); + } + + #[test] + fn test_sem_multi_resource() { + // Parent and child both get in the critical section at the same + // time, and shake hands. + let s = Arc::new(Semaphore::new(2)); + let s2 = s.clone(); + let (tx1, rx1) = channel(); + let (tx2, rx2) = channel(); + spawn(proc() { + let _g = s2.access(); + let _ = rx2.recv(); + tx1.send(()); + }); + let _g = s.access(); + tx2.send(()); + let _ = rx1.recv(); + } + + #[test] + fn test_sem_runtime_friendly_blocking() { + let s = Arc::new(Semaphore::new(1)); + let s2 = s.clone(); + let (tx, rx) = channel(); + { + let _g = s.access(); + spawn(proc() { + tx.send(()); + drop(s2.access()); + tx.send(()); + }); + rx.recv(); // wait for child to come alive + } + rx.recv(); // wait for child to be done + } +} diff --git a/src/libstd/sys/common/condvar.rs b/src/libstd/sys/common/condvar.rs new file mode 100644 index 0000000000000..e09d970402966 --- /dev/null +++ b/src/libstd/sys/common/condvar.rs @@ -0,0 +1,67 @@ +// Copyright 2014 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 time::Duration; +use sys_common::mutex::{mod, Mutex}; +use sys::condvar as imp; + +/// An OS-based condition variable. +/// +/// This structure is the lowest layer possible on top of the OS-provided +/// condition variables. It is consequently entirely unsafe to use. It is +/// recommended to use the safer types at the top level of this crate instead of +/// this type. +pub struct Condvar(imp::Condvar); + +/// Static initializer for condition variables. +pub const CONDVAR_INIT: Condvar = Condvar(imp::CONDVAR_INIT); + +impl Condvar { + /// Creates a new condition variable for use. + /// + /// Behavior is undefined if the condition variable is moved after it is + /// first used with any of the functions below. + #[inline] + pub unsafe fn new() -> Condvar { Condvar(imp::Condvar::new()) } + + /// Signal one waiter on this condition variable to wake up. + #[inline] + pub unsafe fn notify_one(&self) { self.0.notify_one() } + + /// Awaken all current waiters on this condition variable. + #[inline] + pub unsafe fn notify_all(&self) { self.0.notify_all() } + + /// Wait for a signal on the specified mutex. + /// + /// Behavior is undefined if the mutex is not locked by the current thread. + /// Behavior is also undefined if more than one mutex is used concurrently + /// on this condition variable. + #[inline] + pub unsafe fn wait(&self, mutex: &Mutex) { self.0.wait(mutex::raw(mutex)) } + + /// Wait for a signal on the specified mutex with a timeout duration + /// specified by `dur` (a relative time into the future). + /// + /// Behavior is undefined if the mutex is not locked by the current thread. + /// Behavior is also undefined if more than one mutex is used concurrently + /// on this condition variable. + #[inline] + pub unsafe fn wait_timeout(&self, mutex: &Mutex, dur: Duration) -> bool { + self.0.wait_timeout(mutex::raw(mutex), dur) + } + + /// Deallocate all resources associated with this condition variable. + /// + /// Behavior is undefined if there are current or will be future users of + /// this condition variable. + #[inline] + pub unsafe fn destroy(&self) { self.0.destroy() } +} diff --git a/src/libstd/sys/common/mod.rs b/src/libstd/sys/common/mod.rs index e382ec261a0b4..f8861c20464dd 100644 --- a/src/libstd/sys/common/mod.rs +++ b/src/libstd/sys/common/mod.rs @@ -19,8 +19,11 @@ use num::Int; use path::BytesContainer; use collections; -pub mod net; +pub mod condvar; pub mod helper_thread; +pub mod mutex; +pub mod net; +pub mod rwlock; pub mod thread_local; // common error constructors diff --git a/src/libstd/sys/common/mutex.rs b/src/libstd/sys/common/mutex.rs new file mode 100644 index 0000000000000..117d33db32896 --- /dev/null +++ b/src/libstd/sys/common/mutex.rs @@ -0,0 +1,64 @@ +// Copyright 2014 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. + +pub use sys::mutex::raw; + +use sys::mutex as imp; + +/// An OS-based mutual exclusion lock. +/// +/// This is the thinnest cross-platform wrapper around OS mutexes. All usage of +/// this mutex is unsafe and it is recommended to instead use the safe wrapper +/// at the top level of the crate instead of this type. +pub struct Mutex(imp::Mutex); + +/// Constant initializer for statically allocated mutexes. +pub const MUTEX_INIT: Mutex = Mutex(imp::MUTEX_INIT); + +impl Mutex { + /// Creates a newly initialized mutex. + /// + /// Behavior is undefined if the mutex is moved after the first method is + /// called on the mutex. + #[inline] + pub unsafe fn new() -> Mutex { Mutex(imp::Mutex::new()) } + + /// Lock the mutex blocking the current thread until it is available. + /// + /// Behavior is undefined if the mutex has been moved between this and any + /// previous function call. + #[inline] + pub unsafe fn lock(&self) { self.0.lock() } + + /// Attempt to lock the mutex without blocking, returning whether it was + /// successfully acquired or not. + /// + /// Behavior is undefined if the mutex has been moved between this and any + /// previous function call. + #[inline] + pub unsafe fn try_lock(&self) -> bool { self.0.try_lock() } + + /// Unlock the mutex. + /// + /// Behavior is undefined if the current thread does not actually hold the + /// mutex. + #[inline] + pub unsafe fn unlock(&self) { self.0.unlock() } + + /// Deallocate all resources associated with this mutex. + /// + /// Behavior is undefined if there are current or will be future users of + /// this mutex. + #[inline] + pub unsafe fn destroy(&self) { self.0.destroy() } +} + +// not meant to be exported to the outside world, just the containing module +pub fn raw(mutex: &Mutex) -> &imp::Mutex { &mutex.0 } diff --git a/src/libstd/sys/common/rwlock.rs b/src/libstd/sys/common/rwlock.rs new file mode 100644 index 0000000000000..df016b9e293b7 --- /dev/null +++ b/src/libstd/sys/common/rwlock.rs @@ -0,0 +1,86 @@ +// Copyright 2014 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 sys::rwlock as imp; + +/// An OS-based reader-writer lock. +/// +/// This structure is entirely unsafe and serves as the lowest layer of a +/// cross-platform binding of system rwlocks. It is recommended to use the +/// safer types at the top level of this crate instead of this type. +pub struct RWLock(imp::RWLock); + +/// Constant initializer for static RWLocks. +pub const RWLOCK_INIT: RWLock = RWLock(imp::RWLOCK_INIT); + +impl RWLock { + /// Creates a new instance of an RWLock. + /// + /// Usage of an RWLock is undefined if it is moved after its first use (any + /// function calls below). + #[inline] + pub unsafe fn new() -> RWLock { RWLock(imp::RWLock::new()) } + + /// Acquire shared access to the underlying lock, blocking the current + /// thread to do so. + /// + /// Behavior is undefined if the rwlock has been moved between this and any + /// previous methodo call. + #[inline] + pub unsafe fn read(&self) { self.0.read() } + + /// Attempt to acquire shared access to this lock, returning whether it + /// succeeded or not. + /// + /// This function does not block the current thread. + /// + /// Behavior is undefined if the rwlock has been moved between this and any + /// previous methodo call. + #[inline] + pub unsafe fn try_read(&self) -> bool { self.0.try_read() } + + /// Acquire write access to the underlying lock, blocking the current thread + /// to do so. + /// + /// Behavior is undefined if the rwlock has been moved between this and any + /// previous methodo call. + #[inline] + pub unsafe fn write(&self) { self.0.write() } + + /// Attempt to acquire exclusive access to this lock, returning whether it + /// succeeded or not. + /// + /// This function does not block the current thread. + /// + /// Behavior is undefined if the rwlock has been moved between this and any + /// previous methodo call. + #[inline] + pub unsafe fn try_write(&self) -> bool { self.0.try_write() } + + /// Unlock previously acquired shared access to this lock. + /// + /// Behavior is undefined if the current thread does not have shared access. + #[inline] + pub unsafe fn read_unlock(&self) { self.0.read_unlock() } + + /// Unlock previously acquired exclusive access to this lock. + /// + /// Behavior is undefined if the current thread does not currently have + /// exclusive access. + #[inline] + pub unsafe fn write_unlock(&self) { self.0.write_unlock() } + + /// Destroy OS-related resources with this RWLock. + /// + /// Behavior is undefined if there are any currently active users of this + /// lock. + #[inline] + pub unsafe fn destroy(&self) { self.0.destroy() } +} diff --git a/src/libstd/sys/unix/condvar.rs b/src/libstd/sys/unix/condvar.rs new file mode 100644 index 0000000000000..f64718539ef0c --- /dev/null +++ b/src/libstd/sys/unix/condvar.rs @@ -0,0 +1,83 @@ +// Copyright 2014 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 cell::UnsafeCell; +use libc; +use sys::mutex::{mod, Mutex}; +use sys::sync as ffi; +use time::Duration; + +pub struct Condvar { inner: UnsafeCell } + +pub const CONDVAR_INIT: Condvar = Condvar { + inner: UnsafeCell { value: ffi::PTHREAD_COND_INITIALIZER }, +}; + +impl Condvar { + #[inline] + pub unsafe fn new() -> Condvar { + // Might be moved and address is changing it is better to avoid + // initialization of potentially opaque OS data before it landed + Condvar { inner: UnsafeCell::new(ffi::PTHREAD_COND_INITIALIZER) } + } + + #[inline] + pub unsafe fn notify_one(&self) { + let r = ffi::pthread_cond_signal(self.inner.get()); + debug_assert_eq!(r, 0); + } + + #[inline] + pub unsafe fn notify_all(&self) { + let r = ffi::pthread_cond_broadcast(self.inner.get()); + debug_assert_eq!(r, 0); + } + + #[inline] + pub unsafe fn wait(&self, mutex: &Mutex) { + let r = ffi::pthread_cond_wait(self.inner.get(), mutex::raw(mutex)); + debug_assert_eq!(r, 0); + } + + pub unsafe fn wait_timeout(&self, mutex: &Mutex, dur: Duration) -> bool { + assert!(dur >= Duration::nanoseconds(0)); + + // First, figure out what time it currently is + let mut tv = libc::timeval { tv_sec: 0, tv_usec: 0 }; + let r = ffi::gettimeofday(&mut tv, 0 as *mut _); + debug_assert_eq!(r, 0); + + // Offset that time with the specified duration + let abs = Duration::seconds(tv.tv_sec as i64) + + Duration::microseconds(tv.tv_usec as i64) + + dur; + let ns = abs.num_nanoseconds().unwrap() as u64; + let timeout = libc::timespec { + tv_sec: (ns / 1000000000) as libc::time_t, + tv_nsec: (ns % 1000000000) as libc::c_long, + }; + + // And wait! + let r = ffi::pthread_cond_timedwait(self.inner.get(), mutex::raw(mutex), + &timeout); + if r != 0 { + debug_assert_eq!(r as int, libc::ETIMEDOUT as int); + false + } else { + true + } + } + + #[inline] + pub unsafe fn destroy(&self) { + let r = ffi::pthread_cond_destroy(self.inner.get()); + debug_assert_eq!(r, 0); + } +} diff --git a/src/libstd/sys/unix/mod.rs b/src/libstd/sys/unix/mod.rs index af2389051190d..7b37fb3fb0f7c 100644 --- a/src/libstd/sys/unix/mod.rs +++ b/src/libstd/sys/unix/mod.rs @@ -34,14 +34,18 @@ macro_rules! helper_init( (static $name:ident: Helper<$m:ty>) => ( pub mod c; pub mod ext; +pub mod condvar; pub mod fs; pub mod helper_signal; +pub mod mutex; pub mod os; pub mod pipe; pub mod process; +pub mod rwlock; +pub mod sync; pub mod tcp; -pub mod timer; pub mod thread_local; +pub mod timer; pub mod tty; pub mod udp; diff --git a/src/libstd/sys/unix/mutex.rs b/src/libstd/sys/unix/mutex.rs new file mode 100644 index 0000000000000..2f01c53cb2cf5 --- /dev/null +++ b/src/libstd/sys/unix/mutex.rs @@ -0,0 +1,52 @@ +// Copyright 2014 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 cell::UnsafeCell; +use sys::sync as ffi; +use sys_common::mutex; + +pub struct Mutex { inner: UnsafeCell } + +#[inline] +pub unsafe fn raw(m: &Mutex) -> *mut ffi::pthread_mutex_t { + m.inner.get() +} + +pub const MUTEX_INIT: Mutex = Mutex { + inner: UnsafeCell { value: ffi::PTHREAD_MUTEX_INITIALIZER }, +}; + +impl Mutex { + #[inline] + pub unsafe fn new() -> Mutex { + // Might be moved and address is changing it is better to avoid + // initialization of potentially opaque OS data before it landed + MUTEX_INIT + } + #[inline] + pub unsafe fn lock(&self) { + let r = ffi::pthread_mutex_lock(self.inner.get()); + debug_assert_eq!(r, 0); + } + #[inline] + pub unsafe fn unlock(&self) { + let r = ffi::pthread_mutex_unlock(self.inner.get()); + debug_assert_eq!(r, 0); + } + #[inline] + pub unsafe fn try_lock(&self) -> bool { + ffi::pthread_mutex_trylock(self.inner.get()) == 0 + } + #[inline] + pub unsafe fn destroy(&self) { + let r = ffi::pthread_mutex_destroy(self.inner.get()); + debug_assert_eq!(r, 0); + } +} diff --git a/src/libstd/sys/unix/rwlock.rs b/src/libstd/sys/unix/rwlock.rs new file mode 100644 index 0000000000000..0d63ff14ff26b --- /dev/null +++ b/src/libstd/sys/unix/rwlock.rs @@ -0,0 +1,57 @@ +// Copyright 2014 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 cell::UnsafeCell; +use sys::sync as ffi; + +pub struct RWLock { inner: UnsafeCell } + +pub const RWLOCK_INIT: RWLock = RWLock { + inner: UnsafeCell { value: ffi::PTHREAD_RWLOCK_INITIALIZER }, +}; + +impl RWLock { + #[inline] + pub unsafe fn new() -> RWLock { + // Might be moved and address is changing it is better to avoid + // initialization of potentially opaque OS data before it landed + RWLOCK_INIT + } + #[inline] + pub unsafe fn read(&self) { + let r = ffi::pthread_rwlock_rdlock(self.inner.get()); + debug_assert_eq!(r, 0); + } + #[inline] + pub unsafe fn try_read(&self) -> bool { + ffi::pthread_rwlock_tryrdlock(self.inner.get()) == 0 + } + #[inline] + pub unsafe fn write(&self) { + let r = ffi::pthread_rwlock_wrlock(self.inner.get()); + debug_assert_eq!(r, 0); + } + #[inline] + pub unsafe fn try_write(&self) -> bool { + ffi::pthread_rwlock_trywrlock(self.inner.get()) == 0 + } + #[inline] + pub unsafe fn read_unlock(&self) { + let r = ffi::pthread_rwlock_unlock(self.inner.get()); + debug_assert_eq!(r, 0); + } + #[inline] + pub unsafe fn write_unlock(&self) { self.read_unlock() } + #[inline] + pub unsafe fn destroy(&self) { + let r = ffi::pthread_rwlock_destroy(self.inner.get()); + debug_assert_eq!(r, 0); + } +} diff --git a/src/libstd/sys/unix/sync.rs b/src/libstd/sys/unix/sync.rs new file mode 100644 index 0000000000000..007826b4b9d58 --- /dev/null +++ b/src/libstd/sys/unix/sync.rs @@ -0,0 +1,208 @@ +// Copyright 2014 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. + +#![allow(bad_style)] + +use libc; + +pub use self::os::{PTHREAD_MUTEX_INITIALIZER, pthread_mutex_t}; +pub use self::os::{PTHREAD_COND_INITIALIZER, pthread_cond_t}; +pub use self::os::{PTHREAD_RWLOCK_INITIALIZER, pthread_rwlock_t}; + +extern { + // mutexes + pub fn pthread_mutex_destroy(lock: *mut pthread_mutex_t) -> libc::c_int; + pub fn pthread_mutex_lock(lock: *mut pthread_mutex_t) -> libc::c_int; + pub fn pthread_mutex_trylock(lock: *mut pthread_mutex_t) -> libc::c_int; + pub fn pthread_mutex_unlock(lock: *mut pthread_mutex_t) -> libc::c_int; + + // cvars + pub fn pthread_cond_wait(cond: *mut pthread_cond_t, + lock: *mut pthread_mutex_t) -> libc::c_int; + pub fn pthread_cond_timedwait(cond: *mut pthread_cond_t, + lock: *mut pthread_mutex_t, + abstime: *const libc::timespec) -> libc::c_int; + pub fn pthread_cond_signal(cond: *mut pthread_cond_t) -> libc::c_int; + pub fn pthread_cond_broadcast(cond: *mut pthread_cond_t) -> libc::c_int; + pub fn pthread_cond_destroy(cond: *mut pthread_cond_t) -> libc::c_int; + pub fn gettimeofday(tp: *mut libc::timeval, + tz: *mut libc::c_void) -> libc::c_int; + + // rwlocks + pub fn pthread_rwlock_destroy(lock: *mut pthread_rwlock_t) -> libc::c_int; + pub fn pthread_rwlock_rdlock(lock: *mut pthread_rwlock_t) -> libc::c_int; + pub fn pthread_rwlock_tryrdlock(lock: *mut pthread_rwlock_t) -> libc::c_int; + pub fn pthread_rwlock_wrlock(lock: *mut pthread_rwlock_t) -> libc::c_int; + pub fn pthread_rwlock_trywrlock(lock: *mut pthread_rwlock_t) -> libc::c_int; + pub fn pthread_rwlock_unlock(lock: *mut pthread_rwlock_t) -> libc::c_int; +} + +#[cfg(any(target_os = "freebsd", target_os = "dragonfly"))] +mod os { + use libc; + + pub type pthread_mutex_t = *mut libc::c_void; + pub type pthread_cond_t = *mut libc::c_void; + pub type pthread_rwlock_t = *mut libc::c_void; + + pub const PTHREAD_MUTEX_INITIALIZER: pthread_mutex_t = 0 as *mut _; + pub const PTHREAD_COND_INITIALIZER: pthread_cond_t = 0 as *mut _; + pub const PTHREAD_RWLOCK_INITIALIZER: pthread_rwlock_t = 0 as *mut _; +} + +#[cfg(any(target_os = "macos", target_os = "ios"))] +mod os { + use libc; + + #[cfg(target_arch = "x86_64")] + const __PTHREAD_MUTEX_SIZE__: uint = 56; + #[cfg(any(target_arch = "x86", + target_arch = "arm"))] + const __PTHREAD_MUTEX_SIZE__: uint = 40; + + #[cfg(target_arch = "x86_64")] + const __PTHREAD_COND_SIZE__: uint = 40; + #[cfg(any(target_arch = "x86", + target_arch = "arm"))] + const __PTHREAD_COND_SIZE__: uint = 24; + + #[cfg(target_arch = "x86_64")] + const __PTHREAD_RWLOCK_SIZE__: uint = 192; + #[cfg(any(target_arch = "x86", + target_arch = "arm"))] + const __PTHREAD_RWLOCK_SIZE__: uint = 124; + + const _PTHREAD_MUTEX_SIG_INIT: libc::c_long = 0x32AAABA7; + const _PTHREAD_COND_SIG_INIT: libc::c_long = 0x3CB0B1BB; + const _PTHREAD_RWLOCK_SIG_INIT: libc::c_long = 0x2DA8B3B4; + + #[repr(C)] + pub struct pthread_mutex_t { + __sig: libc::c_long, + __opaque: [u8, ..__PTHREAD_MUTEX_SIZE__], + } + #[repr(C)] + pub struct pthread_cond_t { + __sig: libc::c_long, + __opaque: [u8, ..__PTHREAD_COND_SIZE__], + } + #[repr(C)] + pub struct pthread_rwlock_t { + __sig: libc::c_long, + __opaque: [u8, ..__PTHREAD_RWLOCK_SIZE__], + } + + pub const PTHREAD_MUTEX_INITIALIZER: pthread_mutex_t = pthread_mutex_t { + __sig: _PTHREAD_MUTEX_SIG_INIT, + __opaque: [0, ..__PTHREAD_MUTEX_SIZE__], + }; + pub const PTHREAD_COND_INITIALIZER: pthread_cond_t = pthread_cond_t { + __sig: _PTHREAD_COND_SIG_INIT, + __opaque: [0, ..__PTHREAD_COND_SIZE__], + }; + pub const PTHREAD_RWLOCK_INITIALIZER: pthread_rwlock_t = pthread_rwlock_t { + __sig: _PTHREAD_RWLOCK_SIG_INIT, + __opaque: [0, ..__PTHREAD_RWLOCK_SIZE__], + }; +} + +#[cfg(target_os = "linux")] +mod os { + use libc; + + // minus 8 because we have an 'align' field + #[cfg(target_arch = "x86_64")] + const __SIZEOF_PTHREAD_MUTEX_T: uint = 40 - 8; + #[cfg(any(target_arch = "x86", + target_arch = "arm", + target_arch = "mips", + target_arch = "mipsel"))] + const __SIZEOF_PTHREAD_MUTEX_T: uint = 24 - 8; + + #[cfg(any(target_arch = "x86_64", + target_arch = "x86", + target_arch = "arm", + target_arch = "mips", + target_arch = "mipsel"))] + const __SIZEOF_PTHREAD_COND_T: uint = 48 - 8; + + #[cfg(target_arch = "x86_64")] + const __SIZEOF_PTHREAD_RWLOCK_T: uint = 56 - 8; + + #[cfg(any(target_arch = "x86", + target_arch = "arm", + target_arch = "mips", + target_arch = "mipsel"))] + const __SIZEOF_PTHREAD_RWLOCK_T: uint = 32 - 8; + + #[repr(C)] + pub struct pthread_mutex_t { + __align: libc::c_longlong, + size: [u8, ..__SIZEOF_PTHREAD_MUTEX_T], + } + #[repr(C)] + pub struct pthread_cond_t { + __align: libc::c_longlong, + size: [u8, ..__SIZEOF_PTHREAD_COND_T], + } + #[repr(C)] + pub struct pthread_rwlock_t { + __align: libc::c_longlong, + size: [u8, ..__SIZEOF_PTHREAD_RWLOCK_T], + } + + pub const PTHREAD_MUTEX_INITIALIZER: pthread_mutex_t = pthread_mutex_t { + __align: 0, + size: [0, ..__SIZEOF_PTHREAD_MUTEX_T], + }; + pub const PTHREAD_COND_INITIALIZER: pthread_cond_t = pthread_cond_t { + __align: 0, + size: [0, ..__SIZEOF_PTHREAD_COND_T], + }; + pub const PTHREAD_RWLOCK_INITIALIZER: pthread_rwlock_t = pthread_rwlock_t { + __align: 0, + size: [0, ..__SIZEOF_PTHREAD_RWLOCK_T], + }; +} +#[cfg(target_os = "android")] +mod os { + use libc; + + #[repr(C)] + pub struct pthread_mutex_t { value: libc::c_int } + #[repr(C)] + pub struct pthread_cond_t { value: libc::c_int } + #[repr(C)] + pub struct pthread_rwlock_t { + lock: pthread_mutex_t, + cond: pthread_cond_t, + numLocks: libc::c_int, + writerThreadId: libc::c_int, + pendingReaders: libc::c_int, + pendingWriters: libc::c_int, + reserved: [*mut libc::c_void, ..4], + } + + pub const PTHREAD_MUTEX_INITIALIZER: pthread_mutex_t = pthread_mutex_t { + value: 0, + }; + pub const PTHREAD_COND_INITIALIZER: pthread_cond_t = pthread_cond_t { + value: 0, + }; + pub const PTHREAD_RWLOCK_INITIALIZER: pthread_rwlock_t = pthread_rwlock_t { + lock: PTHREAD_MUTEX_INITIALIZER, + cond: PTHREAD_COND_INITIALIZER, + numLocks: 0, + writerThreadId: 0, + pendingReaders: 0, + pendingWriters: 0, + reserved: [0 as *mut _, ..4], + }; +} diff --git a/src/libstd/sys/windows/condvar.rs b/src/libstd/sys/windows/condvar.rs new file mode 100644 index 0000000000000..3cabf3a63194c --- /dev/null +++ b/src/libstd/sys/windows/condvar.rs @@ -0,0 +1,63 @@ +// Copyright 2014 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 cell::UnsafeCell; +use libc::{mod, DWORD}; +use libc; +use os; +use sys::mutex::{mod, Mutex}; +use sys::sync as ffi; +use time::Duration; + +pub struct Condvar { inner: UnsafeCell } + +pub const CONDVAR_INIT: Condvar = Condvar { + inner: UnsafeCell { value: ffi::CONDITION_VARIABLE_INIT } +}; + +impl Condvar { + #[inline] + pub unsafe fn new() -> Condvar { CONDVAR_INIT } + + #[inline] + pub unsafe fn wait(&self, mutex: &Mutex) { + let r = ffi::SleepConditionVariableCS(self.inner.get(), + mutex::raw(mutex), + libc::INFINITE); + debug_assert!(r != 0); + } + + pub unsafe fn wait_timeout(&self, mutex: &Mutex, dur: Duration) -> bool { + let r = ffi::SleepConditionVariableCS(self.inner.get(), + mutex::raw(mutex), + dur.num_milliseconds() as DWORD); + if r == 0 { + const ERROR_TIMEOUT: DWORD = 0x5B4; + debug_assert_eq!(os::errno() as uint, ERROR_TIMEOUT as uint); + false + } else { + true + } + } + + #[inline] + pub unsafe fn notify_one(&self) { + ffi::WakeConditionVariable(self.inner.get()) + } + + #[inline] + pub unsafe fn notify_all(&self) { + ffi::WakeAllConditionVariable(self.inner.get()) + } + + pub unsafe fn destroy(&self) { + // ... + } +} diff --git a/src/libstd/sys/windows/mod.rs b/src/libstd/sys/windows/mod.rs index 6b9555c52cec7..e9243c5040c80 100644 --- a/src/libstd/sys/windows/mod.rs +++ b/src/libstd/sys/windows/mod.rs @@ -35,11 +35,15 @@ macro_rules! helper_init( (static $name:ident: Helper<$m:ty>) => ( pub mod c; pub mod ext; +pub mod condvar; pub mod fs; pub mod helper_signal; +pub mod mutex; pub mod os; pub mod pipe; pub mod process; +pub mod rwlock; +pub mod sync; pub mod tcp; pub mod thread_local; pub mod timer; diff --git a/src/libstd/sys/windows/mutex.rs b/src/libstd/sys/windows/mutex.rs new file mode 100644 index 0000000000000..10ebcf4bd0976 --- /dev/null +++ b/src/libstd/sys/windows/mutex.rs @@ -0,0 +1,76 @@ +// Copyright 2014 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 sync::atomic; +use alloc::{mod, heap}; + +use libc::DWORD; +use sys::sync as ffi; + +const SPIN_COUNT: DWORD = 4000; + +pub struct Mutex { inner: atomic::AtomicUint } + +pub const MUTEX_INIT: Mutex = Mutex { inner: atomic::INIT_ATOMIC_UINT }; + +#[inline] +pub unsafe fn raw(m: &super::Mutex) -> ffi::LPCRITICAL_SECTION { + m.0.get() +} + +impl Mutex { + #[inline] + pub unsafe fn new() -> Mutex { + Mutex { inner: atomic::AtomicUint::new(init_lock() as uint) } + } + #[inline] + pub unsafe fn lock(&self) { + ffi::EnterCriticalSection(self.get()) + } + #[inline] + pub unsafe fn try_lock(&self) -> bool { + ffi::TryEnterCriticalSection(self.get()) != 0 + } + #[inline] + pub unsafe fn unlock(&self) { + ffi::LeaveCriticalSection(self.get()) + } + pub unsafe fn destroy(&self) { + let lock = self.inner.swap(0, atomic::SeqCst); + if lock != 0 { free_lock(lock as ffi::LPCRITICAL_SECTION) } + } + + unsafe fn get(&self) -> ffi::LPCRITICAL_SECTION { + match self.inner.load(atomic::SeqCst) { + 0 => {} + n => return n as ffi::LPCRITICAL_SECTION + } + let lock = init_lock(); + match self.inner.compare_and_swap(0, lock as uint, atomic::SeqCst) { + 0 => return lock as ffi::LPCRITICAL_SECTION, + _ => {} + } + free_lock(lock); + return self.inner.load(atomic::SeqCst) as ffi::LPCRITICAL_SECTION; + } +} + +unsafe fn init_lock() -> ffi::LPCRITICAL_SECTION { + let block = heap::allocate(ffi::CRITICAL_SECTION_SIZE, 8) + as ffi::LPCRITICAL_SECTION; + if block.is_null() { alloc::oom() } + ffi::InitializeCriticalSectionAndSpinCount(block, SPIN_COUNT); + return block; +} + +unsafe fn free_lock(h: ffi::LPCRITICAL_SECTION) { + ffi::DeleteCriticalSection(h); + heap::deallocate(h as *mut _, ffi::CRITICAL_SECTION_SIZE, 8); +} diff --git a/src/libstd/sys/windows/rwlock.rs b/src/libstd/sys/windows/rwlock.rs new file mode 100644 index 0000000000000..88ce85c39f625 --- /dev/null +++ b/src/libstd/sys/windows/rwlock.rs @@ -0,0 +1,53 @@ +// Copyright 2014 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 cell::UnsafeCell; +use sys::sync as ffi; + +pub struct RWLock { inner: UnsafeCell } + +pub const RWLOCK_INIT: RWLock = RWLock { + inner: UnsafeCell { value: ffi::SRWLOCK_INIT } +}; + +impl RWLock { + #[inline] + pub unsafe fn new() -> RWLock { RWLOCK_INIT } + + #[inline] + pub unsafe fn read(&self) { + ffi::AcquireSRWLockShared(self.inner.get()) + } + #[inline] + pub unsafe fn try_read(&self) -> bool { + ffi::TryAcquireSRWLockShared(self.inner.get()) != 0 + } + #[inline] + pub unsafe fn write(&self) { + ffi::AcquireSRWLockExclusive(self.inner.get()) + } + #[inline] + pub unsafe fn try_write(&self) -> bool { + ffi::TryAcquireSRWLockExclusive(self.inner.get()) != 0 + } + #[inline] + pub unsafe fn read_unlock(&self) { + ffi::ReleaseSRWLockShared(self.inner.get()) + } + #[inline] + pub unsafe fn write_unlock(&self) { + ffi::ReleaseSRWLockExclusive(self.inner.get()) + } + + #[inline] + pub unsafe fn destroy(&self) { + // ... + } +} diff --git a/src/libstd/sys/windows/sync.rs b/src/libstd/sys/windows/sync.rs new file mode 100644 index 0000000000000..cbca47912b511 --- /dev/null +++ b/src/libstd/sys/windows/sync.rs @@ -0,0 +1,58 @@ +// Copyright 2014 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 libc::{BOOL, DWORD, c_void, LPVOID}; +use libc::types::os::arch::extra::BOOLEAN; + +pub type LPCRITICAL_SECTION = *mut c_void; +pub type LPCONDITION_VARIABLE = *mut CONDITION_VARIABLE; +pub type LPSRWLOCK = *mut SRWLOCK; + +#[cfg(target_arch = "x86")] +pub const CRITICAL_SECTION_SIZE: uint = 24; +#[cfg(target_arch = "x86_64")] +pub const CRITICAL_SECTION_SIZE: uint = 40; + +#[repr(C)] +pub struct CONDITION_VARIABLE { pub ptr: LPVOID } +#[repr(C)] +pub struct SRWLOCK { pub ptr: LPVOID } + +pub const CONDITION_VARIABLE_INIT: CONDITION_VARIABLE = CONDITION_VARIABLE { + ptr: 0 as *mut _, +}; +pub const SRWLOCK_INIT: SRWLOCK = SRWLOCK { ptr: 0 as *mut _ }; + +extern "system" { + // critical sections + pub fn InitializeCriticalSectionAndSpinCount( + lpCriticalSection: LPCRITICAL_SECTION, + dwSpinCount: DWORD) -> BOOL; + pub fn DeleteCriticalSection(lpCriticalSection: LPCRITICAL_SECTION); + pub fn EnterCriticalSection(lpCriticalSection: LPCRITICAL_SECTION); + pub fn LeaveCriticalSection(lpCriticalSection: LPCRITICAL_SECTION); + pub fn TryEnterCriticalSection(lpCriticalSection: LPCRITICAL_SECTION) -> BOOL; + + // condition variables + pub fn SleepConditionVariableCS(ConditionVariable: LPCONDITION_VARIABLE, + CriticalSection: LPCRITICAL_SECTION, + dwMilliseconds: DWORD) -> BOOL; + pub fn WakeConditionVariable(ConditionVariable: LPCONDITION_VARIABLE); + pub fn WakeAllConditionVariable(ConditionVariable: LPCONDITION_VARIABLE); + + // slim rwlocks + pub fn AcquireSRWLockExclusive(SRWLock: LPSRWLOCK); + pub fn AcquireSRWLockShared(SRWLock: LPSRWLOCK); + pub fn ReleaseSRWLockExclusive(SRWLock: LPSRWLOCK); + pub fn ReleaseSRWLockShared(SRWLock: LPSRWLOCK); + pub fn TryAcquireSRWLockExclusive(SRWLock: LPSRWLOCK) -> BOOLEAN; + pub fn TryAcquireSRWLockShared(SRWLock: LPSRWLOCK) -> BOOLEAN; +} + From c3adbd34c4e637d20a184eb03f09b30c69de8b6e Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 24 Nov 2014 11:16:40 -0800 Subject: [PATCH 83/83] Fall out of the std::sync rewrite --- src/etc/licenseck.py | 5 +- src/libstd/comm/mod.rs | 20 ++-- src/libstd/comm/shared.rs | 21 ++-- src/libstd/comm/stream.rs | 2 +- src/libstd/dynamic_lib.rs | 4 +- src/libstd/lib.rs | 2 +- src/libstd/os.rs | 10 +- src/libstd/rt/backtrace.rs | 16 +-- src/libstd/sync/condvar.rs | 15 ++- src/libstd/sync/mutex.rs | 2 +- src/libstd/sys/common/helper_thread.rs | 21 ++-- src/libstd/sys/common/net.rs | 12 +-- src/libstd/sys/unix/mod.rs | 4 +- src/libstd/sys/unix/pipe.rs | 7 +- src/libstd/sys/windows/mod.rs | 4 +- src/libstd/sys/windows/mutex.rs | 6 +- src/libstd/sys/windows/pipe.rs | 7 +- src/test/bench/msgsend-ring-mutex-arcs.rs | 16 +-- src/test/bench/msgsend-ring-rw-arcs.rs | 113 ---------------------- 19 files changed, 99 insertions(+), 188 deletions(-) delete mode 100644 src/test/bench/msgsend-ring-rw-arcs.rs diff --git a/src/etc/licenseck.py b/src/etc/licenseck.py index 9162edcb53001..7669df36b041d 100644 --- a/src/etc/licenseck.py +++ b/src/etc/licenseck.py @@ -38,9 +38,8 @@ "rt/isaac/randport.cpp", # public domain "rt/isaac/rand.h", # public domain "rt/isaac/standard.h", # public domain - "libstd/sync/mpsc_queue.rs", # BSD - "libstd/sync/spsc_queue.rs", # BSD - "libstd/sync/mpmc_bounded_queue.rs", # BSD + "libstd/comm/mpsc_queue.rs", # BSD + "libstd/comm/spsc_queue.rs", # BSD "test/bench/shootout-binarytrees.rs", # BSD "test/bench/shootout-chameneos-redux.rs", # BSD "test/bench/shootout-fannkuch-redux.rs", # BSD diff --git a/src/libstd/comm/mod.rs b/src/libstd/comm/mod.rs index 2b66e91c00db0..d291ed7256743 100644 --- a/src/libstd/comm/mod.rs +++ b/src/libstd/comm/mod.rs @@ -354,6 +354,8 @@ mod select; mod shared; mod stream; mod sync; +mod mpsc_queue; +mod spsc_queue; /// The receiving-half of Rust's channel type. This half can only be owned by /// one task @@ -628,24 +630,26 @@ impl Sender { #[unstable] impl Clone for Sender { fn clone(&self) -> Sender { - let (packet, sleeper) = match *unsafe { self.inner() } { + let (packet, sleeper, guard) = match *unsafe { self.inner() } { Oneshot(ref p) => { let a = Arc::new(UnsafeCell::new(shared::Packet::new())); unsafe { - (*a.get()).postinit_lock(); + let guard = (*a.get()).postinit_lock(); match (*p.get()).upgrade(Receiver::new(Shared(a.clone()))) { - oneshot::UpSuccess | oneshot::UpDisconnected => (a, None), - oneshot::UpWoke(task) => (a, Some(task)) + oneshot::UpSuccess | + oneshot::UpDisconnected => (a, None, guard), + oneshot::UpWoke(task) => (a, Some(task), guard) } } } Stream(ref p) => { let a = Arc::new(UnsafeCell::new(shared::Packet::new())); unsafe { - (*a.get()).postinit_lock(); + let guard = (*a.get()).postinit_lock(); match (*p.get()).upgrade(Receiver::new(Shared(a.clone()))) { - stream::UpSuccess | stream::UpDisconnected => (a, None), - stream::UpWoke(task) => (a, Some(task)), + stream::UpSuccess | + stream::UpDisconnected => (a, None, guard), + stream::UpWoke(task) => (a, Some(task), guard), } } } @@ -657,7 +661,7 @@ impl Clone for Sender { }; unsafe { - (*packet.get()).inherit_blocker(sleeper); + (*packet.get()).inherit_blocker(sleeper, guard); let tmp = Sender::new(Shared(packet.clone())); mem::swap(self.inner_mut(), tmp.inner_mut()); diff --git a/src/libstd/comm/shared.rs b/src/libstd/comm/shared.rs index 6396edbdbd148..13b5e10fcd3dc 100644 --- a/src/libstd/comm/shared.rs +++ b/src/libstd/comm/shared.rs @@ -26,12 +26,11 @@ use alloc::boxed::Box; use core::cmp; use core::int; use rustrt::local::Local; -use rustrt::mutex::NativeMutex; use rustrt::task::{Task, BlockedTask}; use rustrt::thread::Thread; -use sync::atomic; -use sync::mpsc_queue as mpsc; +use sync::{atomic, Mutex, MutexGuard}; +use comm::mpsc_queue as mpsc; const DISCONNECTED: int = int::MIN; const FUDGE: int = 1024; @@ -56,7 +55,7 @@ pub struct Packet { // this lock protects various portions of this implementation during // select() - select_lock: NativeMutex, + select_lock: Mutex<()>, } pub enum Failure { @@ -76,7 +75,7 @@ impl Packet { channels: atomic::AtomicInt::new(2), port_dropped: atomic::AtomicBool::new(false), sender_drain: atomic::AtomicInt::new(0), - select_lock: unsafe { NativeMutex::new() }, + select_lock: Mutex::new(()), }; return p; } @@ -86,8 +85,8 @@ impl Packet { // In other case mutex data will be duplicated while cloning // and that could cause problems on platforms where it is // represented by opaque data structure - pub fn postinit_lock(&mut self) { - unsafe { self.select_lock.lock_noguard() } + pub fn postinit_lock(&self) -> MutexGuard<()> { + self.select_lock.lock() } // This function is used at the creation of a shared packet to inherit a @@ -95,7 +94,9 @@ impl Packet { // tasks in select(). // // This can only be called at channel-creation time - pub fn inherit_blocker(&mut self, task: Option) { + pub fn inherit_blocker(&mut self, + task: Option, + guard: MutexGuard<()>) { match task { Some(task) => { assert_eq!(self.cnt.load(atomic::SeqCst), 0); @@ -135,7 +136,7 @@ impl Packet { // interfere with this method. After we unlock this lock, we're // signifying that we're done modifying self.cnt and self.to_wake and // the port is ready for the world to continue using it. - unsafe { self.select_lock.unlock_noguard() } + drop(guard); } pub fn send(&mut self, t: T) -> Result<(), T> { @@ -441,7 +442,7 @@ impl Packet { // done with. Without this bounce, we can race with inherit_blocker // about looking at and dealing with to_wake. Once we have acquired the // lock, we are guaranteed that inherit_blocker is done. - unsafe { + { let _guard = self.select_lock.lock(); } diff --git a/src/libstd/comm/stream.rs b/src/libstd/comm/stream.rs index 23d042960b16b..06ab4f4427aa6 100644 --- a/src/libstd/comm/stream.rs +++ b/src/libstd/comm/stream.rs @@ -32,7 +32,7 @@ use rustrt::task::{Task, BlockedTask}; use rustrt::thread::Thread; use sync::atomic; -use sync::spsc_queue as spsc; +use comm::spsc_queue as spsc; use comm::Receiver; const DISCONNECTED: int = int::MIN; diff --git a/src/libstd/dynamic_lib.rs b/src/libstd/dynamic_lib.rs index 3cd0c0eeaf290..160365dac3612 100644 --- a/src/libstd/dynamic_lib.rs +++ b/src/libstd/dynamic_lib.rs @@ -225,8 +225,8 @@ pub mod dl { } pub fn check_for_errors_in(f: || -> T) -> Result { - use rustrt::mutex::{StaticNativeMutex, NATIVE_MUTEX_INIT}; - static LOCK: StaticNativeMutex = NATIVE_MUTEX_INIT; + use sync::{StaticMutex, MUTEX_INIT}; + static LOCK: StaticMutex = MUTEX_INIT; unsafe { // dlerror isn't thread safe, so we need to lock around this entire // sequence diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index f6b73f037f25b..d4274d7e4017e 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -106,7 +106,7 @@ #![allow(unknown_features)] #![feature(macro_rules, globs, linkage)] #![feature(default_type_params, phase, lang_items, unsafe_destructor)] -#![feature(import_shadowing, slicing_syntax)] +#![feature(import_shadowing, slicing_syntax, tuple_indexing)] // Don't link to std. We are std. #![no_std] diff --git a/src/libstd/os.rs b/src/libstd/os.rs index 0abd030a16347..a8adfec34ed68 100644 --- a/src/libstd/os.rs +++ b/src/libstd/os.rs @@ -209,14 +209,12 @@ Accessing environment variables is not generally threadsafe. Serialize access through a global lock. */ fn with_env_lock(f: || -> T) -> T { - use rustrt::mutex::{StaticNativeMutex, NATIVE_MUTEX_INIT}; + use sync::{StaticMutex, MUTEX_INIT}; - static LOCK: StaticNativeMutex = NATIVE_MUTEX_INIT; + static LOCK: StaticMutex = MUTEX_INIT; - unsafe { - let _guard = LOCK.lock(); - f() - } + let _guard = LOCK.lock(); + f() } /// Returns a vector of (variable, value) pairs, for all the environment diff --git a/src/libstd/rt/backtrace.rs b/src/libstd/rt/backtrace.rs index 0103fe670e76f..159fc3080e836 100644 --- a/src/libstd/rt/backtrace.rs +++ b/src/libstd/rt/backtrace.rs @@ -238,7 +238,7 @@ mod imp { use mem; use option::{Some, None, Option}; use result::{Ok, Err}; - use rustrt::mutex::{StaticNativeMutex, NATIVE_MUTEX_INIT}; + use sync::{StaticMutex, MUTEX_INIT}; /// As always - iOS on arm uses SjLj exceptions and /// _Unwind_Backtrace is even not available there. Still, @@ -264,8 +264,8 @@ mod imp { // while it doesn't requires lock for work as everything is // local, it still displays much nicer backtraces when a // couple of tasks panic simultaneously - static LOCK: StaticNativeMutex = NATIVE_MUTEX_INIT; - let _g = unsafe { LOCK.lock() }; + static LOCK: StaticMutex = MUTEX_INIT; + let _g = LOCK.lock(); try!(writeln!(w, "stack backtrace:")); // 100 lines should be enough @@ -297,8 +297,8 @@ mod imp { // is semi-reasonable in terms of printing anyway, and we know that all // I/O done here is blocking I/O, not green I/O, so we don't have to // worry about this being a native vs green mutex. - static LOCK: StaticNativeMutex = NATIVE_MUTEX_INIT; - let _g = unsafe { LOCK.lock() }; + static LOCK: StaticMutex = MUTEX_INIT; + let _g = LOCK.lock(); try!(writeln!(w, "stack backtrace:")); @@ -667,7 +667,7 @@ mod imp { use option::{Some, None}; use path::Path; use result::{Ok, Err}; - use rustrt::mutex::{StaticNativeMutex, NATIVE_MUTEX_INIT}; + use sync::{StaticMutex, MUTEX_INIT}; use slice::SlicePrelude; use str::StrPrelude; use dynamic_lib::DynamicLibrary; @@ -928,8 +928,8 @@ mod imp { pub fn write(w: &mut Writer) -> IoResult<()> { // According to windows documentation, all dbghelp functions are // single-threaded. - static LOCK: StaticNativeMutex = NATIVE_MUTEX_INIT; - let _g = unsafe { LOCK.lock() }; + static LOCK: StaticMutex = MUTEX_INIT; + let _g = LOCK.lock(); // Open up dbghelp.dll, we don't link to it explicitly because it can't // always be found. Additionally, it's nice having fewer dependencies. diff --git a/src/libstd/sync/condvar.rs b/src/libstd/sync/condvar.rs index 581b6b4e41202..0fdd57b27922c 100644 --- a/src/libstd/sync/condvar.rs +++ b/src/libstd/sync/condvar.rs @@ -143,8 +143,14 @@ impl Condvar { /// /// Like `wait`, the lock specified will be re-acquired when this function /// returns, regardless of whether the timeout elapsed or not. - pub fn wait_timeout(&self, mutex_guard: &T, - dur: Duration) -> bool { + // Note that this method is *not* public, and this is quite intentional + // because we're not quite sure about the semantics of relative vs absolute + // durations or how the timing guarantees play into what the system APIs + // provide. There are also additional concerns about the unix-specific + // implementation which may need to be addressed. + #[allow(dead_code)] + fn wait_timeout(&self, mutex_guard: &T, + dur: Duration) -> bool { unsafe { let me: &'static Condvar = &*(self as *const _); me.inner.wait_timeout(mutex_guard, dur) @@ -195,8 +201,9 @@ impl StaticCondvar { /// specified duration. /// /// See `Condvar::wait_timeout`. - pub fn wait_timeout(&'static self, mutex_guard: &T, - dur: Duration) -> bool { + #[allow(dead_code)] // may want to stabilize this later, see wait_timeout above + fn wait_timeout(&'static self, mutex_guard: &T, + dur: Duration) -> bool { unsafe { let lock = mutex_guard.as_mutex_guard(); let sys = mutex::guard_lock(lock); diff --git a/src/libstd/sync/mutex.rs b/src/libstd/sync/mutex.rs index 3d17f2bc64b6e..4e07d54c57e7d 100644 --- a/src/libstd/sync/mutex.rs +++ b/src/libstd/sync/mutex.rs @@ -45,7 +45,7 @@ use sys_common::mutex as sys; /// let data = Arc::new(Mutex::new(0)); /// /// let (tx, rx) = channel(); -/// for _ in range(0, 10) { +/// for _ in range(0u, 10) { /// let (data, tx) = (data.clone(), tx.clone()); /// spawn(proc() { /// // The shared static can only be accessed once the lock is held. diff --git a/src/libstd/sys/common/helper_thread.rs b/src/libstd/sys/common/helper_thread.rs index 9508d8d92325b..c0018c5d97042 100644 --- a/src/libstd/sys/common/helper_thread.rs +++ b/src/libstd/sys/common/helper_thread.rs @@ -20,13 +20,14 @@ //! can be created in the future and there must be no active timers at that //! time. +use prelude::*; + +use cell::UnsafeCell; use mem; use rustrt::bookkeeping; -use rustrt::mutex::StaticNativeMutex; use rustrt; -use cell::UnsafeCell; +use sync::{StaticMutex, StaticCondvar}; use sys::helper_signal; -use prelude::*; use task; @@ -39,7 +40,8 @@ use task; /// is for static initialization. pub struct Helper { /// Internal lock which protects the remaining fields - pub lock: StaticNativeMutex, + pub lock: StaticMutex, + pub cond: StaticCondvar, // You'll notice that the remaining fields are UnsafeCell, and this is // because all helper thread operations are done through &self, but we need @@ -53,6 +55,9 @@ pub struct Helper { /// Flag if this helper thread has booted and been initialized yet. pub initialized: UnsafeCell, + + /// Flag if this helper thread has shut down + pub shutdown: UnsafeCell, } impl Helper { @@ -80,7 +85,9 @@ impl Helper { task::spawn(proc() { bookkeeping::decrement(); helper(receive, rx, t); - self.lock.lock().signal() + let _g = self.lock.lock(); + *self.shutdown.get() = true; + self.cond.notify_one() }); rustrt::at_exit(proc() { self.shutdown() }); @@ -119,7 +126,9 @@ impl Helper { helper_signal::signal(*self.signal.get() as helper_signal::signal); // Wait for the child to exit - guard.wait(); + while !*self.shutdown.get() { + self.cond.wait(&guard); + } drop(guard); // Clean up after ourselves diff --git a/src/libstd/sys/common/net.rs b/src/libstd/sys/common/net.rs index 029fc85274261..ddc6dd021c30f 100644 --- a/src/libstd/sys/common/net.rs +++ b/src/libstd/sys/common/net.rs @@ -16,13 +16,13 @@ use libc::{mod, c_char, c_int}; use mem; use num::Int; use ptr::{mod, null, null_mut}; -use rustrt::mutex; use io::net::ip::{SocketAddr, IpAddr, Ipv4Addr, Ipv6Addr}; use io::net::addrinfo; use io::{IoResult, IoError}; use sys::{mod, retry, c, sock_t, last_error, last_net_error, last_gai_error, close_sock, wrlen, msglen_t, os, wouldblock, set_nonblocking, timer, ms_to_timeval, decode_error_detailed}; +use sync::{Mutex, MutexGuard}; use sys_common::{mod, keep_going, short_write, timeout}; use prelude::*; use cmp; @@ -557,12 +557,12 @@ struct Inner { // Unused on Linux, where this lock is not necessary. #[allow(dead_code)] - lock: mutex::NativeMutex + lock: Mutex<()>, } impl Inner { fn new(fd: sock_t) -> Inner { - Inner { fd: fd, lock: unsafe { mutex::NativeMutex::new() } } + Inner { fd: fd, lock: Mutex::new(()) } } } @@ -572,7 +572,7 @@ impl Drop for Inner { pub struct Guard<'a> { pub fd: sock_t, - pub guard: mutex::LockGuard<'a>, + pub guard: MutexGuard<'a, ()>, } #[unsafe_destructor] @@ -666,7 +666,7 @@ impl TcpStream { fn lock_nonblocking<'a>(&'a self) -> Guard<'a> { let ret = Guard { fd: self.fd(), - guard: unsafe { self.inner.lock.lock() }, + guard: self.inner.lock.lock(), }; assert!(set_nonblocking(self.fd(), true).is_ok()); ret @@ -805,7 +805,7 @@ impl UdpSocket { fn lock_nonblocking<'a>(&'a self) -> Guard<'a> { let ret = Guard { fd: self.fd(), - guard: unsafe { self.inner.lock.lock() }, + guard: self.inner.lock.lock(), }; assert!(set_nonblocking(self.fd(), true).is_ok()); ret diff --git a/src/libstd/sys/unix/mod.rs b/src/libstd/sys/unix/mod.rs index 7b37fb3fb0f7c..4effedbe3abd8 100644 --- a/src/libstd/sys/unix/mod.rs +++ b/src/libstd/sys/unix/mod.rs @@ -25,10 +25,12 @@ use sys_common::mkerr_libc; macro_rules! helper_init( (static $name:ident: Helper<$m:ty>) => ( static $name: Helper<$m> = Helper { - lock: ::rustrt::mutex::NATIVE_MUTEX_INIT, + lock: ::sync::MUTEX_INIT, + cond: ::sync::CONDVAR_INIT, chan: ::cell::UnsafeCell { value: 0 as *mut Sender<$m> }, signal: ::cell::UnsafeCell { value: 0 }, initialized: ::cell::UnsafeCell { value: false }, + shutdown: ::cell::UnsafeCell { value: false }, }; ) ) diff --git a/src/libstd/sys/unix/pipe.rs b/src/libstd/sys/unix/pipe.rs index 3f70fb5c1a56c..08e6f7059d8c6 100644 --- a/src/libstd/sys/unix/pipe.rs +++ b/src/libstd/sys/unix/pipe.rs @@ -12,8 +12,7 @@ use alloc::arc::Arc; use libc; use c_str::CString; use mem; -use rustrt::mutex; -use sync::atomic; +use sync::{atomic, Mutex}; use io::{mod, IoResult, IoError}; use prelude::*; @@ -60,12 +59,12 @@ struct Inner { // Unused on Linux, where this lock is not necessary. #[allow(dead_code)] - lock: mutex::NativeMutex + lock: Mutex<()>, } impl Inner { fn new(fd: fd_t) -> Inner { - Inner { fd: fd, lock: unsafe { mutex::NativeMutex::new() } } + Inner { fd: fd, lock: Mutex::new(()) } } } diff --git a/src/libstd/sys/windows/mod.rs b/src/libstd/sys/windows/mod.rs index e9243c5040c80..9fce308cb9468 100644 --- a/src/libstd/sys/windows/mod.rs +++ b/src/libstd/sys/windows/mod.rs @@ -26,10 +26,12 @@ use sync::{Once, ONCE_INIT}; macro_rules! helper_init( (static $name:ident: Helper<$m:ty>) => ( static $name: Helper<$m> = Helper { - lock: ::rustrt::mutex::NATIVE_MUTEX_INIT, + lock: ::sync::MUTEX_INIT, + cond: ::sync::CONDVAR_INIT, chan: ::cell::UnsafeCell { value: 0 as *mut Sender<$m> }, signal: ::cell::UnsafeCell { value: 0 }, initialized: ::cell::UnsafeCell { value: false }, + shutdown: ::cell::UnsafeCell { value: false }, }; ) ) diff --git a/src/libstd/sys/windows/mutex.rs b/src/libstd/sys/windows/mutex.rs index 10ebcf4bd0976..ddd89070ed53d 100644 --- a/src/libstd/sys/windows/mutex.rs +++ b/src/libstd/sys/windows/mutex.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use prelude::*; + use sync::atomic; use alloc::{mod, heap}; @@ -21,8 +23,8 @@ pub struct Mutex { inner: atomic::AtomicUint } pub const MUTEX_INIT: Mutex = Mutex { inner: atomic::INIT_ATOMIC_UINT }; #[inline] -pub unsafe fn raw(m: &super::Mutex) -> ffi::LPCRITICAL_SECTION { - m.0.get() +pub unsafe fn raw(m: &Mutex) -> ffi::LPCRITICAL_SECTION { + m.get() } impl Mutex { diff --git a/src/libstd/sys/windows/pipe.rs b/src/libstd/sys/windows/pipe.rs index ca7985aa35bf8..bf658d0efd029 100644 --- a/src/libstd/sys/windows/pipe.rs +++ b/src/libstd/sys/windows/pipe.rs @@ -89,8 +89,7 @@ use libc; use c_str::CString; use mem; use ptr; -use sync::atomic; -use rustrt::mutex; +use sync::{atomic, Mutex}; use io::{mod, IoError, IoResult}; use prelude::*; @@ -126,7 +125,7 @@ impl Drop for Event { struct Inner { handle: libc::HANDLE, - lock: mutex::NativeMutex, + lock: Mutex<()>, read_closed: atomic::AtomicBool, write_closed: atomic::AtomicBool, } @@ -135,7 +134,7 @@ impl Inner { fn new(handle: libc::HANDLE) -> Inner { Inner { handle: handle, - lock: unsafe { mutex::NativeMutex::new() }, + lock: Mutex::new(()), read_closed: atomic::AtomicBool::new(false), write_closed: atomic::AtomicBool::new(false), } diff --git a/src/test/bench/msgsend-ring-mutex-arcs.rs b/src/test/bench/msgsend-ring-mutex-arcs.rs index d06e6c8cd191f..863c3c879a7c1 100644 --- a/src/test/bench/msgsend-ring-mutex-arcs.rs +++ b/src/test/bench/msgsend-ring-mutex-arcs.rs @@ -19,28 +19,30 @@ // ignore-lexer-test FIXME #15679 use std::os; -use std::sync::{Arc, Future, Mutex}; +use std::sync::{Arc, Future, Mutex, Condvar}; use std::time::Duration; use std::uint; // A poor man's pipe. -type pipe = Arc>>; +type pipe = Arc<(Mutex>, Condvar)>; fn send(p: &pipe, msg: uint) { - let mut arr = p.lock(); + let &(ref lock, ref cond) = &**p; + let mut arr = lock.lock(); arr.push(msg); - arr.cond.signal(); + cond.notify_one(); } fn recv(p: &pipe) -> uint { - let mut arr = p.lock(); + let &(ref lock, ref cond) = &**p; + let mut arr = lock.lock(); while arr.is_empty() { - arr.cond.wait(); + cond.wait(&arr); } arr.pop().unwrap() } fn init() -> (pipe,pipe) { - let m = Arc::new(Mutex::new(Vec::new())); + let m = Arc::new((Mutex::new(Vec::new()), Condvar::new())); ((&m).clone(), m) } diff --git a/src/test/bench/msgsend-ring-rw-arcs.rs b/src/test/bench/msgsend-ring-rw-arcs.rs deleted file mode 100644 index 03066d40512f3..0000000000000 --- a/src/test/bench/msgsend-ring-rw-arcs.rs +++ /dev/null @@ -1,113 +0,0 @@ -// Copyright 2012 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. - -// This test creates a bunch of tasks that simultaneously send to each -// other in a ring. The messages should all be basically -// independent. -// This is like msgsend-ring-pipes but adapted to use Arcs. - -// This also serves as a pipes test, because Arcs are implemented with pipes. - -// no-pretty-expanded FIXME #15189 -// ignore-lexer-test FIXME #15679 - -use std::os; -use std::sync::{RWLock, Arc, Future}; -use std::time::Duration; -use std::uint; - -// A poor man's pipe. -type pipe = Arc>>; - -fn send(p: &pipe, msg: uint) { - let mut arr = p.write(); - arr.push(msg); - arr.cond.signal(); -} -fn recv(p: &pipe) -> uint { - let mut arr = p.write(); - while arr.is_empty() { - arr.cond.wait(); - } - arr.pop().unwrap() -} - -fn init() -> (pipe,pipe) { - let x = Arc::new(RWLock::new(Vec::new())); - ((&x).clone(), x) -} - - -fn thread_ring(i: uint, count: uint, num_chan: pipe, num_port: pipe) { - let mut num_chan = Some(num_chan); - let mut num_port = Some(num_port); - // Send/Receive lots of messages. - for j in range(0u, count) { - //println!("task %?, iter %?", i, j); - let num_chan2 = num_chan.take().unwrap(); - let num_port2 = num_port.take().unwrap(); - send(&num_chan2, i * j); - num_chan = Some(num_chan2); - let _n = recv(&num_port2); - //log(error, _n); - num_port = Some(num_port2); - }; -} - -fn main() { - let args = os::args(); - let args = if os::getenv("RUST_BENCH").is_some() { - vec!("".to_string(), "100".to_string(), "10000".to_string()) - } else if args.len() <= 1u { - vec!("".to_string(), "10".to_string(), "100".to_string()) - } else { - args.clone().into_iter().collect() - }; - - let num_tasks = from_str::(args[1].as_slice()).unwrap(); - let msg_per_task = from_str::(args[2].as_slice()).unwrap(); - - let (mut num_chan, num_port) = init(); - - let mut p = Some((num_chan, num_port)); - let dur = Duration::span(|| { - let (mut num_chan, num_port) = p.take().unwrap(); - - // create the ring - let mut futures = Vec::new(); - - for i in range(1u, num_tasks) { - //println!("spawning %?", i); - let (new_chan, num_port) = init(); - let num_chan_2 = num_chan.clone(); - let new_future = Future::spawn(proc() { - thread_ring(i, msg_per_task, num_chan_2, num_port) - }); - futures.push(new_future); - num_chan = new_chan; - }; - - // do our iteration - thread_ring(0, msg_per_task, num_chan, num_port); - - // synchronize - for f in futures.iter_mut() { - let _ = f.get(); - } - }); - - // all done, report stats. - let num_msgs = num_tasks * msg_per_task; - let rate = (num_msgs as f64) / (dur.num_milliseconds() as f64); - - println!("Sent {} messages in {} ms", num_msgs, dur.num_milliseconds()); - println!(" {} messages / second", rate / 1000.0); - println!(" {} μs / message", 1000000. / rate / 1000.0); -}