diff --git a/configure b/configure index 35dadfc6613ae..70d6ba38a121c 100755 --- a/configure +++ b/configure @@ -1044,7 +1044,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/crates.mk b/mk/crates.mk index 77cea02f43b73..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_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,12 +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_trans := rustc rustc_back rustc_llvm 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 \ + 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 @@ -94,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 @@ -110,8 +114,12 @@ 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 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/mk/docs.mk b/mk/docs.mk index 898e4eb8c75cd..6d1a3bfa7a326 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/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) ###################################################################### diff --git a/src/doc/guide-lifetimes.md b/src/doc/guide-lifetimes.md deleted file mode 100644 index 7a5c535827c25..0000000000000 --- a/src/doc/guide-lifetimes.md +++ /dev/null @@ -1,565 +0,0 @@ -% 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() -} -~~~ - -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 - -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; -} -~~~ - -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 -} -~~~ - -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 -} -~~~ - -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 -} -~~~ - -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 - } -} -~~~ - -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 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} -# } - // -+ 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 -# } -# 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} -} -~~~ - -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} -} -~~~ - -This is equivalent to the previous definition. - -## Labeled Control Structures - -Named lifetime notation can also be used to control the flow of execution: - -~~~ -'h: for i in range(0u, 10) { - 'g: loop { - if i % 2 == 0 { continue 'h; } - if i == 9 { break 'h; } - break 'g; - } -} -~~~ - -> *Note:* Labelled breaks are not currently supported within `while` loops. - -Named labels are hygienic and can be used safely within macros. -See the macros guide section on hygiene for more details. - -# Conclusion - -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. diff --git a/src/doc/guide-ownership.md b/src/doc/guide-ownership.md new file mode 100644 index 0000000000000..c1180f7e6a93a --- /dev/null +++ b/src/doc/guide-ownership.md @@ -0,0 +1,454 @@ +% 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); +} +``` + +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: + +```rust +{ + let x = box 5i; +} +``` + +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); +} + +fn add_one(mut num: Box) { + *num += 1; +} +``` + +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); +} + +fn add_one(mut num: Box) { + *num += 1; +} +``` + +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); +} + +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, +# } +``` + +declares a lifetime, and + +```rust +# struct Foo<'a> { +x: &'a int, +# } +``` + +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, +} + +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, +} + +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, +} + +fn main() { + let car = Car { name: "DeLorian".to_string() }; + + let car_owner = Rc::new(car); + + for _ in range(0u, 4) { + Wheel { size: 360, owner: car_owner.clone() }; + } +} +``` + +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`. + +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`. + +# Related Resources + +Coming Soon. diff --git a/src/doc/guide-pointers.md b/src/doc/guide-pointers.md index 8b6d00168e942..f6216760d6477 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 @@ -547,7 +547,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: @@ -780,5 +780,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/guide.md b/src/doc/guide.md index c2d43a20ec46c..3f3b533bbd59b 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. @@ -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; @@ -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. @@ -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() { 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 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 : "::" | "->" | '#' | '[' | ']' | '(' | ')' | '{' | '}' | ',' | ';' ; ``` 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/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/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/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/etc/rustup.sh b/src/etc/rustup.sh old mode 100644 new mode 100755 index 4829e15fb0ffa..b610bc65b09c6 --- 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 @@ -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" @@ -388,89 +389,109 @@ 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" +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_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_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="${TMP_DIR}/${CARGO_TARBALL_NAME}" -CARGO_LOCAL_INSTALL_DIR="${TMP_DIR}/${CARGO_PACKAGE_NAME_AND_TRIPLE}" +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}" -need_ok "failed to remove temporary installation directory" +# Fetch the package. +download_package() { + remote_tarball="$1" + local_tarball="$2" -mkdir -p "${TMP_DIR}" -need_ok "failed to create create temporary installation directory" + msg "Downloading ${remote_tarball} to ${local_tarball}" -msg "downloading rust installer" -"${CFG_CURL}" "${REMOTE_TARBALL}" > "${LOCAL_TARBALL}" -if [ $? -ne 0 ] -then - rm -Rf "${TMP_DIR}" - err "failed to download installer" -fi + mkdir -p "${CFG_TMP_DIR}" + need_ok "failed to create create download directory" -if [ -z "${CFG_DISABLE_CARGO}" ]; then - msg "downloading cargo installer" - "${CFG_CURL}" "${CARGO_REMOTE_TARBALL}" > "${CARGO_LOCAL_TARBALL}" + "${CFG_CURL}" -f -o "${local_tarball}" "${remote_tarball}" if [ $? -ne 0 ] then - rm -Rf "${TMP_DIR}" - err "failed to download cargo installer" + rm -Rf "${CFG_TMP_DIR}" + err "failed to download installer" fi -fi +} +# Wrap all the commands needed to install a package. +install_package() { + tarball_name="$1" + install_script="$2" -(cd "${TMP_DIR}" && tar xzf "${TARBALL_NAME}") -if [ $? -ne 0 ] -then - rm -Rf "${TMP_DIR}" + 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 - -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 "${LOCAL_INSTALL_SCRIPT}" "${MAYBE_UNINSTALL}" "${MAYBE_PREFIX}" -if [ $? -ne 0 ] -then - rm -Rf "${TMP_DIR}" - err "failed to install Rust" -fi + fi -if [ -z "${CFG_DISABLE_CARGO}" ]; then - (cd "${TMP_DIR}" && tar xzf "${CARGO_TARBALL_NAME}") + sh "${install_script}" "${CFG_INSTALL_FLAGS}" if [ $? -ne 0 ] then - rm -Rf "${TMP_DIR}" - err "failed to unpack cargo installer" + rm -Rf "${CFG_TMP_DIR}" + err "failed to install Rust" fi +} - sh "${CARGO_LOCAL_INSTALL_SCRIPT}" "${MAYBE_UNINSTALL}" "${MAYBE_PREFIX}" - if [ $? -ne 0 ] - then - rm -Rf "${TMP_DIR}" - err "failed to install Cargo" +# 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" + + 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_LOCAL_TARBALL}" + + if [ -z "${CFG_DISABLE_CARGO}" ]; then + download_package \ + "${CARGO_URL}/${CARGO_TARBALL_NAME}" \ + "${CARGO_LOCAL_TARBALL}" fi -fi -rm -Rf "${TMP_DIR}" -need_ok "couldn't rm temporary installation directory" + install_package \ + "${RUST_TARBALL_NAME}" \ + "${RUST_LOCAL_INSTALL_SCRIPT}" + + if [ -z "${CFG_DISABLE_CARGO}" ]; then + install_package \ + "${CARGO_TARBALL_NAME}" \ + "${CARGO_LOCAL_INSTALL_SCRIPT}" + fi + + rm -Rf "${CFG_TMP_DIR}" + need_ok "couldn't rm temporary installation directory" +} + +install_packages 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; 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/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 38b67fbd74451..fbb0bb5c4ce86 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] @@ -779,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 { @@ -967,10 +1011,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 5edd3d0b780be..2396cf8cec67c 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] @@ -554,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() } @@ -1813,13 +1869,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] @@ -1827,13 +1883,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/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"] 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..b44b87bd93807 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. /// @@ -196,9 +208,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/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 } } diff --git a/src/libcore/cmp.rs b/src/libcore/cmp.rs index 51122d0a17023..df19256471ede 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, @@ -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; @@ -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/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/libcore/intrinsics.rs b/src/libcore/intrinsics.rs index 78c74075d4867..ece2ac6975ed1 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 //! @@ -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/iter.rs b/src/libcore/iter.rs index 2d488a4b15563..f137f43058fa6 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> { @@ -2473,7 +2504,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 +2519,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/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; 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 /// diff --git a/src/libcore/ops.rs b/src/libcore/ops.rs index d85481098e4ff..fdc8b9e7400f5 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; } @@ -833,48 +833,52 @@ impl FnOnce for F } } +#[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<(),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/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/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/result.rs b/src/libcore/result.rs index 202ac46449754..7c88106c9eced 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"] @@ -444,15 +444,14 @@ impl Result { /// ignoring I/O and parse errors: /// /// ``` - /// use std::io::{BufReader, IoResult}; + /// use std::io::IoResult; /// - /// let buffer = "1\n2\n3\n4\n"; - /// let mut reader = BufReader::new(buffer.as_bytes()); + /// let mut buffer = &mut b"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/libcore/slice.rs b/src/libcore/slice.rs index 950f04a5d97e3..ce9f551670bb5 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"] @@ -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(), @@ -1802,12 +1803,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()) } @@ -1816,13 +1817,15 @@ impl PartialEq for [T] { #[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/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/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/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/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/librustc/lib.rs b/src/librustc/lib.rs index a83f8afd39617..a964609e4e634 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; @@ -79,6 +80,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; @@ -93,7 +95,6 @@ pub mod middle { pub mod traits; pub mod ty; pub mod ty_fold; - pub mod typeck; pub mod weak_lang_items; } diff --git a/src/librustc/lint/builtin.rs b/src/librustc/lint/builtin.rs index aa99fe996f02a..b0ac98c94e748 100644 --- a/src/librustc/lint/builtin.rs +++ b/src/librustc/lint/builtin.rs @@ -29,8 +29,6 @@ use self::MethodContext::*; 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::{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"); } @@ -1377,7 +1375,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 } }); @@ -1430,6 +1428,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(), @@ -1589,22 +1588,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/lint/context.rs b/src/librustc/lint/context.rs index c7bed838eb919..442d3aab92dba 100644 --- a/src/librustc/lint/context.rs +++ b/src/librustc/lint/context.rs @@ -26,17 +26,13 @@ use self::TargetLint::*; 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}; 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| { 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/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/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/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/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/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/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/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 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/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 99% rename from src/librustc/middle/typeck/infer/region_inference/doc.rs rename to src/librustc/middle/infer/region_inference/doc.rs index b4eac4c002677..686174b73060b 100644 --- a/src/librustc/middle/typeck/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/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/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/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 { 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 34278c25cfa0b..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; @@ -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..0e6a0c19f7061 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}; @@ -30,13 +30,13 @@ 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; 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,44 @@ 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. 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(()); + } + + 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 +1592,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 +1693,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 + } + _ => { + 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 +2056,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 +2080,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..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; @@ -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.rs b/src/librustc/middle/ty.rs index 35aed356303d8..994f0c2090a57 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}; @@ -90,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, @@ -412,7 +425,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 +640,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 +715,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 +3825,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 +3866,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 +3997,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 +5914,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 +5923,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 +6177,55 @@ 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") + } + } + } +} + +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/ty_fold.rs b/src/librustc/middle/ty_fold.rs index 08dcbffc9287b..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 } } } @@ -466,6 +465,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/session/config.rs b/src/librustc/session/config.rs index 3c2dbae665fa9..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 @@ -944,7 +997,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/util/ppaux.rs b/src/librustc/util/ppaux.rs index b739a97f734be..1283e89c29d0c 100644 --- a/src/librustc/util/ppaux.rs +++ b/src/librustc/util/ppaux.rs @@ -23,8 +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; use syntax::abi; @@ -1018,7 +1016,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 +1025,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 +1052,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,25 +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; 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/driver/driver.rs b/src/librustc_driver/driver.rs similarity index 88% rename from src/librustc_trans/driver/driver.rs rename to src/librustc_driver/driver.rs index aeef16276e5b5..437b0257a9759 100644 --- a/src/librustc_trans/driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -8,26 +8,22 @@ // 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 lint; -use llvm::{ContextRef, ModuleRef}; -use metadata::common::LinkMeta; -use metadata::creader; -use middle::{stability, ty, typeck, reachable}; -use middle::dependency_format; -use middle; -use plugin::load::Plugins; -use plugin::registry::Registry; -use plugin; -use trans; - -use util::common::time; -use util::nodemap::{NodeSet}; +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 serialize::{json, Encodable}; @@ -35,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; @@ -113,36 +108,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() + Input::File(ref ifile) => ifile.as_str().unwrap().to_string(), + Input::Str(_) => 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(), - } - } -} - - 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(), @@ -342,23 +320,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(); @@ -473,7 +441,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, @@ -485,7 +453,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; @@ -494,25 +462,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", (), |_| @@ -520,13 +473,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; @@ -554,7 +507,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(); @@ -639,8 +592,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 @@ -679,19 +632,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, @@ -751,43 +704,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_driver/lib.rs similarity index 90% rename from src/librustc_trans/driver/mod.rs rename to src/librustc_driver/lib.rs index 658be9169afdd..33c009cf3291b 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 driver::driver::{Input, FileInput, StrInput}; -use session::{config, Session, build_session}; -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; @@ -89,9 +121,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 +148,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 +443,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(), @@ -438,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); @@ -507,3 +533,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 97% rename from src/librustc_trans/driver/pretty.rs rename to src/librustc_driver/pretty.rs index 7bb83d7c2a819..b6441ab4944f7 100644 --- a/src/librustc_trans/driver/pretty.rs +++ b/src/librustc_driver/pretty.rs @@ -15,18 +15,18 @@ pub use self::PpSourceMode::*; pub use self::PpMode::*; use self::NodesMatchingUII::*; -use back::link; +use rustc_trans::back::link; -use session::{config, Session}; -use driver::driver::{mod, CrateAnalysis}; +use driver; -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::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}; @@ -242,7 +242,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 +409,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 +536,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/test.rs b/src/librustc_driver/test.rs similarity index 97% rename from src/librustc_trans/test.rs rename to src/librustc_driver/test.rs index 41fbe85576933..9404802cb681b 100644 --- a/src/librustc_trans/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::typeck::infer::combine::Combine; -use middle::typeck::infer; -use middle::typeck::infer::lub::Lub; -use middle::typeck::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/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/lib.rs b/src/librustc_trans/lib.rs index 4dc8c4d173633..4e25921e0b29b 100644 --- a/src/librustc_trans/lib.rs +++ b/src/librustc_trans/lib.rs @@ -65,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/librustc_trans/save/mod.rs b/src/librustc_trans/save/mod.rs index 7a41be1dbe46f..1482422b8d03e 100644 --- a/src/librustc_trans/save/mod.rs +++ b/src/librustc_trans/save/mod.rs @@ -27,10 +27,9 @@ //! 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, typeck}; +use middle::def; use middle::ty::{mod, Ty}; use std::cell::Cell; @@ -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, @@ -912,10 +911,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 +935,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, @@ -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 23072dee3b6b1..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; @@ -995,9 +997,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 +1017,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 { @@ -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/callee.rs b/src/librustc_trans/trans/callee.rs index 50d2f885afa17..746109ef11346 100644 --- a/src/librustc_trans/trans/callee.rs +++ b/src/librustc_trans/trans/callee.rs @@ -49,8 +49,7 @@ use trans::monomorphize; use trans::type_::Type; use trans::type_of; use middle::ty::{mod, Ty}; -use middle::typeck::coherence::make_substs_for_receiver_types; -use middle::typeck::MethodCall; +use middle::ty::MethodCall; use util::ppaux::Repr; use util::ppaux::ty_to_string; @@ -242,6 +241,120 @@ 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(); + + 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)); + + // 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); + + ccx.fn_pointer_shims().borrow_mut().insert(bare_fn_ty, llfn); + + 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>, @@ -459,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 diff --git a/src/librustc_trans/trans/common.rs b/src/librustc_trans/trans/common.rs index febb33f6c54af..a8256176c2658 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,8 +37,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}; @@ -273,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 @@ -468,7 +462,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 +475,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 +864,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/context.rs b/src/librustc_trans/trans/context.rs index a0b7eb02f02e4..fd9d6b8f2c3b2 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 @@ -346,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 } @@ -394,6 +391,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), @@ -573,6 +571,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 } 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/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/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/librustc_trans/trans/expr.rs b/src/librustc_trans/trans/expr.rs index 646ee601a4ca4..d130dc0a55b1c 100644 --- a/src/librustc_trans/trans/expr.rs +++ b/src/librustc_trans/trans/expr.rs @@ -41,15 +41,23 @@ 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}; 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 +2099,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 a49b7b21627f8..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 => { @@ -368,9 +367,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 +614,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/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/librustc/middle/typeck/astconv.rs b/src/librustc_typeck/astconv.rs similarity index 95% rename from src/librustc/middle/typeck/astconv.rs rename to src/librustc_typeck/astconv.rs index 89c004fc64596..d95ad9a11c87f 100644 --- a/src/librustc/middle/typeck/astconv.rs +++ b/src/librustc_typeck/astconv.rs @@ -46,17 +46,17 @@ //! Note that the self region for the `foo` defaulted to `&` in the first //! case but `&a` in the second. Basically, defaults that appear inside //! an rptr (`&r.T`) use the region `r` that appears in the rptr. + +use middle::astconv_util::{ast_ty_to_prim_ty, check_path_args, NO_TPS, NO_REGIONS}; use middle::const_eval; use middle::def; 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 rscope::{mod, UnelidableRscope, RegionScope, SpecificRscope, + ShiftedRscope, BindingRscope}; +use TypeAndSubsts; use util::common::ErrorReported; use util::nodemap::DefIdMap; use util::ppaux::{mod, Repr, UserString}; @@ -428,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)); @@ -553,74 +553,6 @@ pub fn ast_path_to_ty_relaxed<'tcx,AC,RS>( } } -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>( @@ -1544,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 98% rename from src/librustc/middle/typeck/check/_match.rs rename to src/librustc_typeck/check/_match.rs index cdfd607d06710..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 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::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 0a93b3a5ec7dc..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 middle::typeck::astconv; -use middle::typeck::infer; -use middle::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 86% rename from src/librustc/middle/typeck/check/demand.rs rename to src/librustc_typeck/check/demand.rs index 1e45d059b849b..2b8a52f050d6f 100644 --- a/src/librustc/middle/typeck/check/demand.rs +++ b/src/librustc_typeck/check/demand.rs @@ -9,11 +9,11 @@ // except according to those terms. +use check::FnCtxt; 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; @@ -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_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 e866627be3d29..1fe73f0478d56 100644 --- a/src/librustc/middle/typeck/check/method/confirm.rs +++ b/src/librustc_typeck/check/method/confirm.rs @@ -10,13 +10,14 @@ use super::probe; +use check::{mod, FnCtxt, NoPreference, PreferMutLvalue}; use middle::subst::{mod, Subst}; use middle::traits; use middle::ty::{mod, Ty}; -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::{MethodCall, MethodCallee, MethodObject, MethodOrigin, + MethodParam, MethodStatic, MethodTraitObject, MethodTypeParam}; +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/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 34c3292f8cd69..f87a4c9294bab 100644 --- a/src/librustc/middle/typeck/check/method/mod.rs +++ b/src/librustc_typeck/check/method/mod.rs @@ -10,19 +10,17 @@ //! 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 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 middle::typeck::infer; -use middle::typeck::{MethodCallee}; -use middle::typeck::{MethodParam, MethodTypeParam}; +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_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 484d72130e61d..6ff276edbce7e 100644 --- a/src/librustc/middle/typeck/check/method/probe.rs +++ b/src/librustc_typeck/check/method/probe.rs @@ -12,17 +12,17 @@ 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; 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 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_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 641cbd11d64e0..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,8 +82,10 @@ 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; use middle::mem_categorization::{mod, McResult}; use middle::pat_util::{mod, pat_id_map}; @@ -93,12 +95,12 @@ 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 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}; @@ -279,7 +281,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 +289,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) @@ -359,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) { @@ -1860,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, @@ -1919,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>, @@ -3260,7 +3256,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, @@ -4670,25 +4666,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); @@ -4696,10 +4685,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 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 08f7f9cf5e37f..2aec4393de942 100644 --- a/src/librustc/middle/typeck/check/regionck.rs +++ b/src/librustc_typeck/check/regionck.rs @@ -114,20 +114,19 @@ //! 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}; -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::typeck::MethodCall; +use middle::ty::{mod, Ty, MethodCall}; +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/regionmanip.rs b/src/librustc_typeck/check/regionmanip.rs similarity index 96% rename from src/librustc/middle/typeck/check/regionmanip.rs rename to src/librustc_typeck/check/regionmanip.rs index 55214618aa90b..92dfd8b5f56d2 100644 --- a/src/librustc/middle/typeck/check/regionmanip.rs +++ b/src/librustc_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/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 84cb74b4de248..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,9 +16,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, - 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/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 8535ec4fa6e80..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 middle::typeck::astconv::AstConv; -use middle::typeck::check::{FnCtxt, Inherited, blank_fn_ctxt, vtable, regionck}; -use middle::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 97% rename from src/librustc/middle/typeck/check/writeback.rs rename to src/librustc_typeck/check/writeback.rs index 23af30b44d935..777f354bec126 100644 --- a/src/librustc/middle/typeck/check/writeback.rs +++ b/src/librustc_typeck/check/writeback.rs @@ -13,18 +13,17 @@ // substitutions. use self::ResolveReason::*; +use astconv::AstConv; +use check::FnCtxt; 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 middle::infer::{force_all, resolve_all, resolve_region}; +use middle::infer::resolve_type; +use middle::infer; +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 94% rename from src/librustc/middle/typeck/coherence/mod.rs rename to src/librustc_typeck/coherence/mod.rs index 758608b79c2cb..b8642ddde4082 100644 --- a/src/librustc/middle/typeck/coherence/mod.rs +++ b/src/librustc_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,10 +30,10 @@ use middle::ty::{ty_closure}; 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 CrateCtxt; +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; @@ -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/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 98% rename from src/librustc/middle/typeck/coherence/overlap.rs rename to src/librustc_typeck/coherence/overlap.rs index 9f10a58f45852..0e74d4578d95b 100644 --- a/src/librustc/middle/typeck/coherence/overlap.rs +++ b/src/librustc_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_typeck/collect.rs similarity index 99% rename from src/librustc/middle/typeck/collect.rs rename to src/librustc_typeck/collect.rs index 6e989dd73dbef..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 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::typeck::rscope::*; -use middle::typeck::{CrateCtxt, lookup_def_tcx, no_params, write_ty_to_tcx}; -use middle::typeck; +use middle::infer; +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 new file mode 100644 index 0000000000000..36e81f18103b8 --- /dev/null +++ b/src/librustc_typeck/diagnostics.rs @@ -0,0 +1,151 @@ +// 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, + E0171, + E0172 +) diff --git a/src/librustc/middle/typeck/mod.rs b/src/librustc_typeck/lib.rs similarity index 60% rename from src/librustc/middle/typeck/mod.rs rename to src/librustc_typeck/lib.rs index 501dfcb2e2d9e..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,16 +57,40 @@ 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)] -pub use self::ExprAdjustment::*; -pub use self::vtable_origin::*; -pub use self::MethodOrigin::*; +#[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; use middle::subst; use middle::subst::VecPerParamSpace; use middle::ty::{mod, Ty}; @@ -74,222 +98,40 @@ 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}; -pub mod check; -pub mod rscope; -pub mod astconv; -pub mod infer; -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), +#[cfg(stage0)] +mod diagnostics; - // 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 -} +mod check; +mod rscope; +mod astconv; +mod collect; +mod coherence; +mod variance; -pub struct TypeAndSubsts<'tcx> { +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> { +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() { @@ -302,7 +144,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(), _ => { @@ -311,12 +153,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()}, @@ -324,7 +166,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/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 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..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; @@ -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/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..0ad12b957ba8f 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; @@ -35,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, @@ -44,27 +42,62 @@ 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::Function, // no ForeignFunction + clean::ForeignStaticItem(..) => ItemType::Static, // no 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::Macro => "macro", + ItemType::Primitive => "primitive", + ItemType::AssociatedType => "associatedtype", + ItemType::Constant => "constant", } } } @@ -75,27 +108,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/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, diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs index 2be703e2458bb..9eee8e04f0c2b 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() => { @@ -1251,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, @@ -1435,7 +1432,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(()) } @@ -1490,45 +1488,48 @@ 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::Trait => 8, + ItemType::Function => 9, + ItemType::Typedef => 10, + _ => 11 + 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 } } @@ -1545,26 +1546,24 @@ 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::Macro => ("macros", "Macros"), + ItemType::Primitive => ("primitives", "Primitive Types"), + ItemType::AssociatedType => ("associated-types", "Associated Types"), }; try!(write!(w, "

\ diff --git a/src/librustdoc/html/static/main.js b/src/librustdoc/html/static/main.js index 7c6f7ed3fe230..978af31cdc689 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; } @@ -566,8 +567,8 @@ "method", "structfield", "variant", - "ffi", - "ffs", + "ffi", // retained for backward compatibility + "ffs", // retained for backward compatibility "macro", "primitive", "associatedtype", @@ -707,8 +708,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); diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index c592474b057ff..66108bea9885c 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)] @@ -21,6 +25,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 +168,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 @@ -172,7 +177,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/librustdoc/test.rs b/src/librustdoc/test.rs index 2a5972bb3d90b..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; @@ -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()), diff --git a/src/libstd/collections/hash/map.rs b/src/libstd/collections/hash/map.rs index 17e6becdfaffa..cf09c93f91785 100644 --- a/src/libstd/collections/hash/map.rs +++ b/src/libstd/collections/hash/map.rs @@ -432,12 +432,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/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/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/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/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/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/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/fs.rs b/src/libstd/io/fs.rs index bd334f52628fe..e52c00f8247a8 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)) }) @@ -237,7 +251,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())) @@ -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"); } @@ -1528,7 +1542,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/io/mem.rs b/src/libstd/io/mem.rs index f27951f263da2..8a329a5d27c08 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 @@ -280,10 +315,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], @@ -362,6 +397,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 +430,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); @@ -457,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/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/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/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 90203709627ff..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 @@ -2034,7 +2032,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 +2052,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/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")); 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/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..0fdd57b27922c --- /dev/null +++ b/src/libstd/sync/condvar.rs @@ -0,0 +1,365 @@ +// 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. + // 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) + } + } + + /// 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`. + #[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); + 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..4e07d54c57e7d 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(0u, 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/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/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/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/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..4effedbe3abd8 100644 --- a/src/libstd/sys/unix/mod.rs +++ b/src/libstd/sys/unix/mod.rs @@ -25,23 +25,29 @@ 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 }, }; ) ) 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/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/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/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/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/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)), diff --git a/src/libstd/sys/windows/mod.rs b/src/libstd/sys/windows/mod.rs index 6b9555c52cec7..9fce308cb9468 100644 --- a/src/libstd/sys/windows/mod.rs +++ b/src/libstd/sys/windows/mod.rs @@ -26,20 +26,26 @@ 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 }, }; ) ) 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..ddd89070ed53d --- /dev/null +++ b/src/libstd/sys/windows/mutex.rs @@ -0,0 +1,78 @@ +// 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; +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: &Mutex) -> ffi::LPCRITICAL_SECTION { + m.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/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/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; +} + 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) { 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/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()) 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/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) } diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index 7453da6374e00..11c65d531f6fd 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), @@ -65,15 +63,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 you change this list without updating src/doc/reference.md, cmr will be sad + ("if_let", Accepted), + ("while_let", Accepted), // A temporary feature gate used to enable parser extensions needed // to bootstrap fix for #5723. @@ -172,12 +168,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") @@ -309,24 +305,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); @@ -429,7 +412,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/attr.rs b/src/libsyntax/parse/attr.rs index db7bc6c323f89..41fee1556abff 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/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/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 } diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 920bcc3a951ae..bb3d28ce2bb54 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; } }; } @@ -3111,14 +3157,19 @@ 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 { - 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 +3186,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 +3211,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 +3238,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 +3318,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 +3369,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 +3672,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 +3985,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 +4385,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 +4410,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 +4667,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 +4852,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 +5095,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 +5236,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 +5400,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 +5681,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 +5705,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 +5729,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 +5898,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/libsyntax/parse/token.rs b/src/libsyntax/parse/token.rs index 37df2bf14c2e1..52b54bc7f2d6d 100644 --- a/src/libsyntax/parse/token.rs +++ b/src/libsyntax/parse/token.rs @@ -623,12 +623,35 @@ 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() } } +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( 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/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 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, 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); -} 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/rustc-diagnostics-2.rs b/src/test/compile-fail/better-expected.rs similarity index 71% rename from src/test/compile-fail/rustc-diagnostics-2.rs rename to src/test/compile-fail/better-expected.rs index c4e011bcea042..489f892726a3b 100644 --- a/src/test/compile-fail/rustc-diagnostics-2.rs +++ b/src/test/compile-fail/better-expected.rs @@ -8,13 +8,6 @@ // 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() { + let x: [int ..3]; //~ ERROR expected one of `(`, `+`, `,`, `::`, or `]`, found `..` } - -__build_diagnostic_array!(DIAGNOSTICS) 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/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/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/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-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-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-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/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-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-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/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/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() {} 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, 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/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/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/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/rustc-diagnostics-3.rs b/src/test/compile-fail/new-unicode-escapes-1.rs similarity index 62% rename from src/test/compile-fail/rustc-diagnostics-3.rs rename to src/test/compile-fail/new-unicode-escapes-1.rs index d160664a48c78..f2422830a21cc 100644 --- a/src/test/compile-fail/rustc-diagnostics-3.rs +++ b/src/test/compile-fail/new-unicode-escapes-1.rs @@ -8,13 +8,6 @@ // 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!' +pub fn main() { + let s = "\u{2603"; //~ ERROR unterminated unicode escape (needed a `}`) } - -__build_diagnostic_array!(DIAGNOSTICS) -//~^ ERROR macro undefined: '__build_diagnostic_array!' 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/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"); } diff --git a/src/test/compile-fail/issue-17718-extern-const.rs b/src/test/compile-fail/trailing-comma-array-repeat.rs similarity index 85% rename from src/test/compile-fail/issue-17718-extern-const.rs rename to src/test/compile-fail/trailing-comma-array-repeat.rs index 235d1222d81c0..dadd657158384 100644 --- a/src/test/compile-fail/issue-17718-extern-const.rs +++ b/src/test/compile-fail/trailing-comma-array-repeat.rs @@ -8,8 +8,6 @@ // 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() { + let [_, ..,] = [(), ()]; //~ ERROR unexpected token: `]` } - -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/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/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{ 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 { 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; 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); 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); +} 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-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-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(); }); } 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"); } diff --git a/src/test/compile-fail/rustc-diagnostics-1.rs b/src/test/run-pass/new-unicode-escapes.rs similarity index 52% rename from src/test/compile-fail/rustc-diagnostics-1.rs rename to src/test/run-pass/new-unicode-escapes.rs index 55d836092fa71..2888389bcceab 100644 --- a/src/test/compile-fail/rustc-diagnostics-1.rs +++ b/src/test/run-pass/new-unicode-escapes.rs @@ -8,21 +8,15 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(rustc_diagnostic_macros)] +pub fn main() { + let s = "\u{2603}"; + assert_eq!(s, "☃"); -__register_diagnostic!(E0001) -__register_diagnostic!(E0003) + let s = "\u{2a10}\u{2A01}\u{2Aa0}"; + assert_eq!(s, "⨐⨁⪠"); -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 + let s = "\\{20}"; + let mut correct_s = String::from_str("\\"); + correct_s.push_str("{20}"); + assert_eq!(s, correct_s.as_slice()); } - -__build_diagnostic_array!(DIAGNOSTICS) -//~^ WARN diagnostic code E0003 never used 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::; 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)] 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(); +}