diff --git a/doc/lib/codemirror-rust.js b/doc/lib/codemirror-rust.js index 900180c5eb5e3..6c05ee252b11d 100644 --- a/doc/lib/codemirror-rust.js +++ b/doc/lib/codemirror-rust.js @@ -5,11 +5,11 @@ CodeMirror.defineMode("rust", function() { "do": "else-style", "ret": "else-style", "fail": "else-style", "break": "atom", "cont": "atom", "const": "let", "resource": "fn", "let": "let", "fn": "fn", "for": "for", "alt": "alt", "iface": "iface", - "impl": "impl", "type": "type", "enum": "enum", "mod": "mod", + "impl": "impl", "type": "type", "enum": "enum", "class": "atom", "mod": "mod", "as": "op", "true": "atom", "false": "atom", "assert": "op", "check": "op", "claim": "op", "extern": "ignore", "unsafe": "ignore", "import": "else-style", "export": "else-style", "copy": "op", "log": "op", "log_err": "op", - "use": "op", "bind": "op", "self": "atom" + "use": "op", "bind": "op", "self": "atom", "new": "atom" }; var typeKeywords = function() { var keywords = {"fn": "fn"}; diff --git a/doc/rust.md b/doc/rust.md index 074913c8bf1e6..f002393b71ace 100644 --- a/doc/rust.md +++ b/doc/rust.md @@ -209,9 +209,9 @@ import export use mod The keywords in [source files](#source-files) are the following strings: ~~~~~~~~ {.keyword} -alt assert +alt again assert break -check claim class const cont copy +check claim class const copy drop else enum export extern fail false fn for @@ -2034,19 +2034,19 @@ break_expr : "break" ; Executing a `break` expression immediately terminates the innermost loop enclosing it. It is only permitted in the body of a loop. -### Continue expressions +### Again expressions ~~~~~~~~{.ebnf .gram} -break_expr : "cont" ; +again_expr : "again" ; ~~~~~~~~ -Evaluating a `cont` expression immediately terminates the current iteration of +Evaluating an `again` expression immediately terminates the current iteration of the innermost loop enclosing it, returning control to the loop *head*. In the case of a `while` loop, the head is the conditional expression controlling the -loop. In the case of a `for` loop, the head is the vector-element increment -controlling the loop. +loop. In the case of a `for` loop, the head is the call-expression controlling +the loop. -A `cont` expression is only permitted in the body of a loop. +An `again` expression is only permitted in the body of a loop. ### For expressions diff --git a/doc/tutorial.md b/doc/tutorial.md index a6d2f153f0c40..cce8fed220e01 100644 --- a/doc/tutorial.md +++ b/doc/tutorial.md @@ -11,7 +11,34 @@ comparisons to other languages in the C family. The tutorial covers the whole language, though not with the depth and precision of the [language reference](rust.html). -## First Impressions +## Language overview + +Rust is a systems programming language with a focus on type safety, +memory safety, concurrency and performance. It is intended for writing +large, high performance applications while preventing several classes +of errors commonly found in languages like C++. Rust has a +sophisticated memory model that enables many of the efficient data +structures used in C++ while disallowing invalid memory access that +would otherwise cause segmentation faults. Like other systems +languages it is statically typed and compiled ahead of time. + +As a multi-paradigm language it has strong support for writing code in +procedural, functional and object-oriented styles. Some of it's nice +high-level features include: + +* Pattern matching and algebraic data types (enums) - common in functional + languages, pattern matching on ADTs provides a compact and expressive + way to encode program logic +* Task-based concurrency - Rust uses lightweight tasks that do not share + memory +* Higher-order functions - Closures in Rust are very powerful and used + pervasively +* Polymorphism - Rust's type system features a unique combination of + Java-style interfaces and Haskell-style typeclasses +* Generics - Functions and types can be parameterized over generic + types with optional type constraints + +## First impressions As a curly-brace language in the tradition of C, C++, and JavaScript, Rust looks a lot like other languages you may be familiar with. @@ -321,9 +348,6 @@ Rust identifiers must start with an alphabetic character or an underscore, and after that may contain any alphanumeric character, and more underscores. -***Note:*** The parser doesn't currently recognize non-ascii alphabetic -characters. This is a bug that will eventually be fixed. - The double-colon (`::`) is used as a module separator, so `io::println` means 'the thing named `println` in the module named `io`'. @@ -756,7 +780,7 @@ a specific value, are not allowed. `while` produces a loop that runs as long as its given condition (which must have type `bool`) evaluates to true. Inside a loop, the -keyword `break` can be used to abort the loop, and `cont` can be used +keyword `break` can be used to abort the loop, and `again` can be used to abort the current iteration and continue with the next. ~~~~ @@ -795,6 +819,21 @@ handle the failure, allowing the program to continue running. to access a vector out of bounds, or running a pattern match with no matching clauses, both result in the equivalent of a `fail`. +## Assertions + +The keyword `assert`, followed by an expression with boolean type, +will check that the given expression results in `true`, and cause a +failure otherwise. It is typically used to double-check things that +*should* hold at a certain point in a program. `assert` statements are +always active; there is no way to build Rust code with assertions +disabled. + +~~~~ +let mut x = 100; +while (x > 10) { x -= 10; } +assert x == 10; +~~~~ + ## Logging Rust has a built-in logging mechanism, using the `log` statement. @@ -837,38 +876,61 @@ and will log the formatted string: Because the macros `#debug`, `#warn`, and `#error` expand to calls to `log`, their arguments are also lazily evaluated. -## Assertions +# Functions -The keyword `assert`, followed by an expression with boolean type, -will check that the given expression results in `true`, and cause a -failure otherwise. It is typically used to double-check things that -*should* hold at a certain point in a program. `assert` statements are -always active; there is no way to build Rust code with assertions -disabled. +Like all other static declarations, such as `type`, functions can be +declared both at the top level and inside other functions (or modules, +which we'll come back to [later](#modules-and-crates)). + +We've already seen several function definitions. They are introduced +with the `fn` keyword. The type of arguments are specified following +colons and the return type follows the arrow. ~~~~ -let mut x = 100; -while (x > 10) { x -= 10; } -assert x == 10; +fn int_to_str(i: int) -> str { + ret "tube sock"; +} ~~~~ -# Functions +The `ret` keyword immediately returns from the body of a function. It +is optionally followed by an expression to return. A function can +also return a value by having its top level block produce an +expression. -Like all other static declarations, such as `type`, functions can be -declared both at the top level and inside other functions (or modules, -which we'll come back to in moment). +~~~~ +# const copernicus: int = 0; +fn int_to_str(i: int) -> str { + if i == copernicus { + ret "tube sock"; + } else { + ret "violin"; + } +} +~~~~ -The `ret` keyword immediately returns from a function. It is -optionally followed by an expression to return. In functions that -return `()`, the returned expression can be left off. A function can -also return a value by having its top level block produce an -expression (by omitting the final semicolon). +~~~~ +# const copernicus: int = 0; +fn int_to_str(i: int) -> str { + if i == copernicus { "tube sock" } + else { "violin" } +} +~~~~ + +Functions that do not return a value are said to return nil, `()`, +and both the return type and the return value may be omitted from +the definition. The following two functions are equivalent. + +~~~~ +fn do_nothing_the_hard_way() -> () { ret (); } + +fn do_nothing_the_easy_way() { } +~~~~ Some functions (such as the C function `exit`) never return normally. In Rust, these are annotated with the pseudo-return type '`!`': ~~~~ -fn dead_end() -> ! { fail; } +fn dead_end() -> ! { fail } ~~~~ This helps the compiler avoid spurious error messages. For example, @@ -885,7 +947,84 @@ let dir = if can_go_left() { left } else { dead_end(); }; ~~~~ -## Closures + + +# The Rust Memory Model + +At this junction let's take a detour to explain the concepts involved +in Rust's memory model. Rust has a very particular approach to +memory management that plays a significant role in shaping the "feel" +of the language. Understanding the memory landscape will illuminate +several of Rust's unique features as we encounter them. + +Rust has three competing goals that inform its view of memory: + +* Memory safety - memory that is managed by and is accessible to + the Rust language must be guaranteed to be valid. Under normal + circumstances it is impossible for Rust to trigger a segmentation + fault or leak memory +* Performance - high-performance low-level code tends to employ + a number of allocation strategies. low-performance high-level + code often uses a single, GC-based, heap allocation strategy +* Concurrency - Rust maintain memory safety guarantees even + for code running in parallel + +## How performance considerations influence the memory model + +Many languages that ofter the kinds of memory safety guarentees that +Rust does have a single allocation strategy: objects live on the heap, +live for as long as they are needed, and are periodically garbage +collected. This is very straightforword both conceptually and in +implementation, but has very significant costs. Such languages tend to +aggressively pursue ways to ameliorate allocation costs (think the +Java virtual machine). Rust supports this strategy with _shared +boxes_, memory allocated on the heap that may be referred to (shared) +by multiple variables. + +In comparison, languages like C++ offer a very precise control over +where objects are allocated. In particular, it is common to put +them directly on the stack, avoiding expensive heap allocation. In +Rust this is possible as well, and the compiler will use a clever +lifetime analysis to ensure that no variable can refer to stack +objects after they are destroyed. + +## How concurrency considerations influence the memory model + +Memory safety in a concurrent environment tends to mean avoiding race +conditions between two threads of execution accessing the same +memory. Even high-level languages frequently avoid solving this +problem, requiring programmers to correctly employ locking to unsure +their program is free of races. + +Rust starts from the position that memory simply cannot be shared +between tasks. Experience in other languages has proven that isolating +each tasks' heap from each other is a reliable strategy and one that +is easy for programmers to reason about. Having isolated heaps +additionally means that garbage collection must only be done +per-heap. Rust never 'stops the world' to garbage collect memory. + +If Rust tasks have completely isolated heaps then that seems to imply +that any data transferred between them must be copied. While this +is a fine and useful way to implement communication between tasks, +it is also very inefficient for large data structures. + +Because of this Rust also introduces a global "exchange heap". Objects +allocated here have _ownership semantics_, meaning that there is only +a single variable that refers to them. For this reason they are +refered to as _unique boxes_. All tasks may allocate objects on this +heap, then _move_ those allocations to other tasks, avoiding expensive +copies. + +## What to be aware of + +Rust has three "realms" in which objects can be allocated: the stack, +the local heap, and the exchange heap. These realms have corresponding +pointer types: the borrowed pointer (`&T`), the shared pointer (`@T`), +and the unique pointer (`~T`). These three sigils will appear +repeatedly as we explore the language. Learning the appropriate role +of each is key to using Rust effectively. + +# Closures Named functions, like those in the previous section, may not refer to local variables decalared outside the function - they do not @@ -940,7 +1079,7 @@ position and cannot be stored in structures nor returned from functions. Despite the limitations stack closures are used pervasively in Rust code. -### Boxed closures +## Boxed closures When you need to store a closure in a data structure, a stack closure will not do, since the compiler will refuse to let you store it. For @@ -982,7 +1121,7 @@ fn mk_appender(suffix: str) -> fn@(str) -> str { } ~~~~ -### Unique closures +## Unique closures Unique closures, written `fn~` in analogy to the `~` pointer type (see next section), hold on to things that can safely be sent between @@ -991,7 +1130,7 @@ closures, but they also 'own' them—meaning no other code can access them. Unique closures are used in concurrent code, particularly for spawning [tasks](#tasks). -### Closure compatibility +## Closure compatibility A nice property of Rust closures is that you can pass any kind of closure (as long as the arguments and return types match) to functions @@ -1010,7 +1149,7 @@ fn bare_function() { "I am a plain function"; } call_twice(bare_function); ~~~~ -### Do syntax +## Do syntax Closures in Rust are frequently used in combination with higher-order functions to simulate control structures like `if` and @@ -1081,11 +1220,11 @@ do spawn { Empty argument lists can be omitted from `do` expressions. -### For loops +## For loops Most iteration in Rust is done with `for` loops. Like `do`, `for` is a nice syntax for doing control flow with closures. -Additionally, within a `for` loop, `break, `cont`, and `ret` +Additionally, within a `for` loop, `break, `again`, and `ret` work just as they do with `while` and `loop`. Consider again our `each` function, this time improved to @@ -1119,8 +1258,8 @@ each(~[2, 4, 8, 5, 16], |n| { With `for`, functions like `each` can be treated more like builtin looping structures. When calling `each` in a `for` loop, instead of returning `false` to break -out of the loop, you just write `break`. To continue -to the next iteration, write `cont`. +out of the loop, you just write `break`. To skip ahead +to the next iteration, write `again`. ~~~~ # import each = vec::each; @@ -1153,17 +1292,32 @@ fn contains(v: ~[int], elt: int) -> bool { # Datatypes -Rust datatypes are, by default, immutable. The core datatypes of Rust -are structural records and 'enums' (tagged unions, algebraic data -types). +The core datatypes of Rust are structural records, enums (tagged +unions, algebraic data types), and classes. They are immutable +by default. ~~~~ type point = {x: float, y: float}; + enum shape { circle(point, float), rectangle(point, point) } -let my_shape = circle({x: 0.0, y: 0.0}, 10.0); + +class drawing { + let mut shapes: [shape]; + + new() { + self.shapes = []; + } + + fn add_shape(new_shape: shape) { + self.shapes += [new_shape]; + } +} + +let my_drawing = drawing(); +my_drawing.add_shape(circle({x: 0.0, y: 0.0}, 10.0)); ~~~~ ## Records @@ -1172,12 +1326,10 @@ Rust record types are written `{field1: T1, field2: T2 [, ...]}`, where `T1`, `T2`, ... denote types. Record literals are written in the same way, but with expressions instead of types. They are quite similar to C structs, and even laid out the same way in memory (so you -can read from a Rust struct in C, and vice-versa). - -The dot operator is used to access record fields (`mypoint.x`). +can read from a Rust struct in C, and vice-versa). The dot operator is +used to access record fields (`mypoint.x`). -Fields that you want to mutate must be explicitly marked as such. For -example... +Fields that you want to mutate must be explicitly marked `mut`. ~~~~ type stack = {content: ~[int], mut head: uint}; @@ -1187,8 +1339,8 @@ With such a type, you can do `mystack.head += 1u`. If `mut` were omitted from the type, such an assignment would result in a type error. -To 'update' an immutable record, you use functional record update -syntax, by ending a record literal with the keyword `with`: +To create a new record based on the value of an existing record +you construct it using the `with` keyword: ~~~~ let oldpoint = {x: 10f, y: 20f}; @@ -1196,7 +1348,7 @@ let newpoint = {x: 0f with oldpoint}; assert newpoint == {x: 0f, y: 20f}; ~~~~ -This will create a new struct, copying all the fields from `oldpoint` +This will create a new record, copying all the fields from `oldpoint` into it, except for the ones that are explicitly set in the literal. Rust record types are *structural*. This means that `{x: float, y: @@ -1230,8 +1382,8 @@ the fields of a record, a record pattern may end with `, _` (as in ## Enums -Enums are datatypes that have several different representations. For -example, the type shown earlier: +Enums are datatypes that have several alternate representations. For +example, consider the type shown earlier: ~~~~ # type point = {x: float, y: float}; @@ -1253,7 +1405,7 @@ which can be used to construct values of the type (taking arguments of the specified types). So `circle({x: 0f, y: 0f}, 10f)` is the way to create a new circle. -Enum variants do not have to have parameters. This, for example, is +Enum variants need not have type parameters. This, for example, is equivalent to a C enum: ~~~~ @@ -1349,8 +1501,8 @@ fn point_from_direction(dir: direction) -> point { Tuples in Rust behave exactly like records, except that their fields do not have names (and can thus not be accessed with dot notation). -Tuples can have any arity except for 0 or 1 (though you may see nil, -`()`, as the empty tuple if you like). +Tuples can have any arity except for 0 or 1 (though you may consider +nil, `()`, as the empty tuple if you like). ~~~~ let mytup: (int, int, float) = (10, 20, 30.0); @@ -1373,11 +1525,15 @@ allocating memory and going through a pointer. But for big records, or records with mutable fields, it can be useful to have a single copy on the heap, and refer to that through a pointer. -Rust supports several types of pointers. The simplest is the unsafe -pointer, written `*T`, which is a completely unchecked pointer type -only used in unsafe code (and thus, in typical Rust code, very -rarely). The safe pointer types are `@T` for shared, reference-counted -boxes, and `~T`, for uniquely-owned pointers. +Rust supports several types of pointers. The safe pointer types are +`@T` for shared boxes allocated on the local heap, `~T`, for +uniquely-owned boxes allocated on the exchange heap, and `&T`, for +borrowed pointers, which may point to any memory, and whose lifetimes +are governed by the call stack. + +Rust also has an unsafe pointer, written `*T`, which is a completely +unchecked pointer type only used in unsafe code (and thus, in typical +Rust code, very rarely). All pointer types can be dereferenced with the `*` unary operator. @@ -1397,20 +1553,32 @@ let y = x; // Copy the pointer, increase refcount // When x and y go out of scope, refcount goes to 0, box is freed ~~~~ -***Note:*** We may in the future switch to garbage collection, rather +***Note:*** We will in the future switch to garbage collection, rather than reference counting, for shared boxes. Shared boxes never cross task boundaries. ### Unique boxes -In contrast to shared boxes, unique boxes are not reference counted. -Instead, it is statically guaranteed that only a single owner of the -box exists at any time. +In contrast to shared boxes, unique boxes have a single owner and thus +two unique boxes may not refer to the same memory. All unique boxes +across all tasks are allocated on a single _exchange heap_, where +their uniquely owned nature allows them to be passed between tasks. + +Because unique boxes are uniquely owned, copying them involves allocating +a new unique box and duplicating the contents. Copying unique boxes +is expensive so the compiler will complain if you do. ~~~~ let x = ~10; -let y <- x; +let y = x; // error: copying a non-implicitly copyable type +~~~~ + +If you really want to copy a unique box you must say so explicitly. + +~~~~ +let x = ~10; +let y = copy x; ~~~~ This is where the 'move' (`<-`) operator comes in. It is similar to @@ -1419,11 +1587,43 @@ from `x` to `y`, without violating the constraint that it only has a single owner (if you used assignment instead of the move operator, the box would, in principle, be copied). +~~~~ +let x = ~10; +let y <- x; +~~~~ + Unique boxes, when they do not contain any shared boxes, can be sent to other tasks. The sending task will give up ownership of the box, and won't be able to access it afterwards. The receiving task will become the sole owner of the box. +### Borrowed pointers + +Rust borrowed pointers are a general purpose reference/pointer type, +similar to the C++ reference type, but guaranteed to point to valid +memory. In contrast to unique pointers, where the holder of a unique +pointer is the owner of the pointed-to memory, borrowed pointers never +imply ownership. Pointers may be borrowed from any type, in which case +the pointer is guaranteed not to outlive the value it points to. + +~~~~ +# fn work_with_foo_by_pointer(f: &str) { } +let foo = "foo"; +work_with_foo_by_pointer(&foo); +~~~~ + +The following shows an example of what is _not_ possible with borrowed +pointers. If you were able to write this then the pointer to `foo` +would outlive `foo` itself. + +~~~~ {.ignore} +let foo_ptr; +{ + let foo = "foo"; + foo_ptr = &foo; +} +~~~~ + ### Mutability All pointer types have a mutable variant, written `@mut T` or `~mut diff --git a/src/cargo/cargo.rs b/src/cargo/cargo.rs index 5116d75f6f59c..efed7db81cf56 100644 --- a/src/cargo/cargo.rs +++ b/src/cargo/cargo.rs @@ -751,7 +751,7 @@ fn install_one_crate(c: cargo, path: str, cf: str) { #debug(" lib: %s", ct); install_to_dir(ct, c.libdir); } - } + } } @@ -799,6 +799,7 @@ fn install_source(c: cargo, path: str) { }; install_query(c, wd, query); + } os::change_dir(path); @@ -809,7 +810,6 @@ fn install_source(c: cargo, path: str) { } } } - } } fn install_git(c: cargo, wd: str, url: str, ref: option) { diff --git a/src/etc/emacs/rust-mode.el b/src/etc/emacs/rust-mode.el index 86e5f867cbaa3..a9691a836cb67 100644 --- a/src/etc/emacs/rust-mode.el +++ b/src/etc/emacs/rust-mode.el @@ -56,9 +56,9 @@ "trait" "fn" "enum" "iface" "impl")) (puthash word 'def table)) - (dolist (word '("assert" + (dolist (word '("again" "assert" "break" - "check" "claim" "cont" "copy" + "check" "claim" "copy" "do" "drop" "else" "export" "extern" "fail" "for" diff --git a/src/etc/indenter b/src/etc/indenter index f2e41da9b4e44..017cb926981fb 100755 --- a/src/etc/indenter +++ b/src/etc/indenter @@ -4,13 +4,13 @@ use warnings; my $indent = 0; while (<>) { - if (/^rust: ">>/) { + if (/^rust: ~">>/) { $indent += 1; } printf "%03d %s%s", $indent, (" " x $indent), $_; - if (/^rust: "< -" Last Change: 2010 Oct 13 +" Maintainer: Ben Blum +" Last Change: 2012 Jul 06 if version < 600 syntax clear @@ -12,8 +13,8 @@ endif syn keyword rustAssert assert syn match rustAssert "assert\(\w\)*" syn keyword rustKeyword alt as break -syn keyword rustKeyword check claim cont const copy else export extern fail -syn keyword rustKeyword do drop for if impl import in let log +syn keyword rustKeyword check claim cont const copy do drop else export extern fail +syn keyword rustKeyword for if impl import in let log syn keyword rustKeyword loop mod mut new of pure syn keyword rustKeyword ret self to unchecked syn match rustKeyword "unsafe" " Allows also matching unsafe::foo() @@ -30,6 +31,16 @@ syn keyword rustKeyword m32 m64 m128 f80 f16 f128 syn keyword rustType any int uint float char bool u8 u16 u32 u64 f32 syn keyword rustType f64 i8 i16 i32 i64 str +syn keyword rustType option either + +" Types from libc +syn keyword rustType c_float c_double c_void FILE fpos_t +syn keyword rustType DIR dirent +syn keyword rustType c_char c_schar c_uchar +syn keyword rustType c_short c_ushort c_int c_uint c_long c_ulong +syn keyword rustType size_t ptrdiff_t clock_t time_t +syn keyword rustType c_longlong c_ulonglong intptr_t uintptr_t +syn keyword rustType off_t dev_t ino_t pid_t mode_t ssize_t syn keyword rustBoolean true false @@ -37,9 +48,19 @@ syn keyword rustConstant some none " option syn keyword rustConstant left right " either syn keyword rustConstant ok err " result syn keyword rustConstant success failure " task -" syn keyword rustConstant cons nil " list +syn keyword rustConstant cons nil " list " syn keyword rustConstant empty node " tree +" Constants from libc +syn keyword rustConstant EXIT_FAILURE EXIT_SUCCESS RAND_MAX +syn keyword rustConstant EOF SEEK_SET SEEK_CUR SEEK_END _IOFBF _IONBF +syn keyword rustConstant _IOLBF BUFSIZ FOPEN_MAX FILENAME_MAX L_tmpnam +syn keyword rustConstant TMP_MAX O_RDONLY O_WRONLY O_RDWR O_APPEND O_CREAT +syn keyword rustConstant O_EXCL O_TRUNC S_IFIFO S_IFCHR S_IFBLK S_IFDIR +syn keyword rustConstant S_IFREG S_IFMT S_IEXEC S_IWRITE S_IREAD S_IRWXU +syn keyword rustConstant S_IXUSR S_IWUSR S_IRUSR F_OK R_OK W_OK X_OK +syn keyword rustConstant STDIN_FILENO STDOUT_FILENO STDERR_FILENO + " If foo::bar changes to foo.bar, change this ("::" to "\."). " If foo::bar changes to Foo::bar, change this (first "\w" to "\u"). syn match rustModPath "\w\(\w\)*::[^<]"he=e-3,me=e-3 diff --git a/src/fuzzer/fuzzer.rs b/src/fuzzer/fuzzer.rs index e6a7a9fcfe6f8..4faf7a2f48d0c 100644 --- a/src/fuzzer/fuzzer.rs +++ b/src/fuzzer/fuzzer.rs @@ -42,7 +42,7 @@ fn common_exprs() -> ~[ast::expr] { } ~[dse(ast::expr_break), - dse(ast::expr_cont), + dse(ast::expr_again), dse(ast::expr_fail(option::none)), dse(ast::expr_fail(option::some( @dse(ast::expr_lit(@dsl(ast::lit_str(@"boo"))))))), diff --git a/src/libcore/char.rs b/src/libcore/char.rs index 28645df1b46c0..d68800a96fc78 100644 --- a/src/libcore/char.rs +++ b/src/libcore/char.rs @@ -139,9 +139,9 @@ fn escape_unicode(c: char) -> str { else { ('U', 8u) }); assert str::len(s) <= pad; let mut out = "\\"; - out += str::from_char(c); - for uint::range(str::len(s), pad) |_i| { out += "0"; } - out += s; + str::push_str(out, str::from_char(c)); + for uint::range(str::len(s), pad) |_i| { str::push_str(out, "0"); } + str::push_str(out, s); ret out; } diff --git a/src/libcore/core.rc b/src/libcore/core.rc index d4e9180066096..d17cef756295b 100644 --- a/src/libcore/core.rc +++ b/src/libcore/core.rc @@ -39,7 +39,7 @@ export float, f32, f64; export box, char, str, ptr, vec, bool; export either, option, result, iter; export libc, os, io, run, rand, sys, unsafe, logging; -export arc, newcomm, comm, task, future; +export arc, comm, task, future, pipes; export extfmt; export tuple; export to_str, to_bytes; @@ -183,11 +183,10 @@ mod dlist_iter { // Concurrency mod arc; -mod newcomm; mod comm; mod task; mod future; - +mod pipes; // Runtime and language-primitive support diff --git a/src/libcore/dvec.rs b/src/libcore/dvec.rs index 9c1d6cae36b14..ec396f474a7cf 100644 --- a/src/libcore/dvec.rs +++ b/src/libcore/dvec.rs @@ -10,7 +10,7 @@ import unsafe::reinterpret_cast; import ptr::{null, extensions}; export dvec; -export from_elt; +export from_elem; export from_vec; export extensions; export unwrap; @@ -56,7 +56,7 @@ fn dvec() -> dvec { } /// Creates a new dvec with a single element -fn from_elt(+e: A) -> dvec { +fn from_elem(+e: A) -> dvec { {mut data: ~[mut e]} } diff --git a/src/libcore/future.rs b/src/libcore/future.rs index 322b75da7dae7..bf3c30a573b86 100644 --- a/src/libcore/future.rs +++ b/src/libcore/future.rs @@ -22,7 +22,10 @@ export get; export with; export spawn; -/// The future type +// for task.rs +export future_pipe; + +#[doc = "The future type"] enum future = { mut v: either<@A, fn@() -> A> }; @@ -56,16 +59,34 @@ fn from_value(+val: A) -> future { }) } -fn from_port(-port: comm::port) -> future { - /*! - * Create a future from a port - * - * The first time that the value is requested the task will block - * waiting for the result to be received on the port. - */ +fn macros() { + #macro[ + [#recv[chan], + chan.recv()(chan)] + ]; + #macro[ + [#move[x], + unsafe { let y <- *ptr::addr_of(x); y }] + ]; +} - do from_fn { - comm::recv(port) +fn from_port(-port: future_pipe::client::waiting) -> future { + #[doc = " + Create a future from a port + + The first time that the value is requested the task will block + waiting for the result to be received on the port. + "]; + import future_pipe::client::recv; + + let port = ~mut some(port); + do from_fn |move port| { + let mut port_ = none; + port_ <-> *port; + let port = option::unwrap(port_); + alt (#recv(port)) { + future_pipe::completed(data, _next) { #move(data) } + } } } @@ -91,12 +112,9 @@ fn spawn(+blk: fn~() -> A) -> future { * value of the future. */ - let mut po = comm::port(); - let ch = comm::chan(po); - do task::spawn { - comm::send(ch, blk()) - }; - from_port(po) + from_port(pipes::spawn_service_recv(future_pipe::init, |ch| { + future_pipe::server::completed(ch, blk()); + })) } fn get(future: future) -> A { @@ -119,6 +137,57 @@ fn with(future: future, blk: fn(A) -> B) -> B { blk(*v) } +// The pipe protocol, generated by pipec +/* +proto! future_pipe { + waiting:recv { + completed(T) -> terminated + } + + terminated { } +} +*/ +mod future_pipe { + fn init() -> (client::waiting, server::waiting) { + { let (s, c) = pipes::entangle(); (c, s) } + } + enum waiting { completed(T, client::terminated), } + enum terminated { } + mod client { + impl recv for waiting { + fn recv() -> extern fn(+waiting) -> future_pipe::waiting { + fn recv(+pipe: waiting) -> + future_pipe::waiting { + option::unwrap(pipes::recv(pipe)) + } + recv + } + } + type waiting = pipes::recv_packet>; + type terminated = pipes::send_packet; + } + mod server { + fn completed(+pipe: waiting, +x_0: T) -> terminated { + { + let (s, c) = pipes::entangle(); + let message = future_pipe::completed(x_0, s); + pipes::send(pipe, message); + c + } + } + type waiting = pipes::send_packet>; + impl recv for terminated { + fn recv() -> extern fn(+terminated) -> future_pipe::terminated { + fn recv(+pipe: terminated) -> future_pipe::terminated { + option::unwrap(pipes::recv(pipe)) + } + recv + } + } + type terminated = pipes::recv_packet; + } +} + #[test] fn test_from_value() { let f = from_value("snail"); @@ -127,9 +196,8 @@ fn test_from_value() { #[test] fn test_from_port() { - let po = comm::port(); - let ch = comm::chan(po); - comm::send(ch, "whale"); + let (po, ch) = future_pipe::init(); + future_pipe::server::completed(ch, "whale"); let f = from_port(po); assert get(f) == "whale"; } diff --git a/src/libcore/iter-trait.rs b/src/libcore/iter-trait.rs index 1fae52b4afe05..02c32a1bf0db8 100644 --- a/src/libcore/iter-trait.rs +++ b/src/libcore/iter-trait.rs @@ -35,4 +35,11 @@ impl extensions for IMPL_T { fn min() -> A { iter::min(self) } fn max() -> A { iter::max(self) } + + fn find(p: fn(A) -> bool) -> option { + for self.each |i| { + if p(i) { ret some(i) } + } + ret none; + } } diff --git a/src/libcore/newcomm.rs b/src/libcore/newcomm.rs deleted file mode 100644 index e78b8551c6dd4..0000000000000 --- a/src/libcore/newcomm.rs +++ /dev/null @@ -1,85 +0,0 @@ -/** - * A new implementation of communication. - * - * This should be implementing almost entirely in Rust, and hopefully - * avoid needing a single global lock. - */ - -import arc::methods; -import dvec::dvec; -import dvec::{extensions}; -import sys::methods; - -export port; -export chan; -export send, recv; -export methods; - -type raw_port = arc::exclusive>; - -enum port { - port_(raw_port) -} -enum chan { - chan_(raw_port) -} - -fn port() -> port { - port_(arc::exclusive(dvec())) -} - -fn chan(p: port) -> chan { - chan_((*p).clone()) -} - -fn send(c: chan, -x: T) { - let mut x <- some(x); - do (*c).with |cond, data| { - let mut xx = none; - xx <-> x; - (*data).push(option::unwrap(xx)); - cond.signal(); - } -} - -fn recv(p: port) -> T { - do (*p).with |cond, data| { - if (*data).len() == 0u { - cond.wait(); - } - assert (*data).len() > 0u; - (*data).shift() - } -} - -impl methods for chan { - fn send(-x: T) { - send(self, x) - } - - fn clone() -> chan { - chan_((*self).clone()) - } -} - -impl methods for port { - fn recv() -> T { - recv(self) - } - - fn chan() -> chan { - chan(self) - } -} - -#[cfg(test)] -mod test { - #[test] - fn newport_simple() { - let p = port(); - let c = chan(p); - - c.send(42); - assert p.recv() == 42; - } -} diff --git a/src/libcore/pipes.rs b/src/libcore/pipes.rs new file mode 100644 index 0000000000000..f1617c59ac7d3 --- /dev/null +++ b/src/libcore/pipes.rs @@ -0,0 +1,358 @@ +// Runtime support for pipes. + +import unsafe::{forget, reinterpret_cast}; + +enum state { + empty, + full, + blocked, + terminated +} + +type packet_header = { + mut state: state, + mut blocked_task: option<*rust_task>, +}; + +type packet = { + header: packet_header, + mut payload: option +}; + +fn packet() -> *packet unsafe { + let p: *packet = unsafe::transmute(~{ + header: { + mut state: empty, + mut blocked_task: none::, + }, + mut payload: none:: + }); + p +} + +#[abi = "rust-intrinsic"] +extern mod rusti { + fn atomic_xchng(&dst: int, src: int) -> int; + fn atomic_xchng_acq(&dst: int, src: int) -> int; + fn atomic_xchng_rel(&dst: int, src: int) -> int; +} + +type rust_task = libc::c_void; + +extern mod rustrt { + #[rust_stack] + fn rust_get_task() -> *rust_task; + + #[rust_stack] + fn task_clear_event_reject(task: *rust_task); + + fn task_wait_event(this: *rust_task) -> *libc::c_void; + fn task_signal_event(target: *rust_task, event: *libc::c_void); +} + +// We should consider moving this to core::unsafe, although I +// suspect graydon would want us to use void pointers instead. +unsafe fn uniquify(x: *T) -> ~T { + unsafe { unsafe::reinterpret_cast(x) } +} + +fn swap_state_acq(&dst: state, src: state) -> state { + unsafe { + reinterpret_cast(rusti::atomic_xchng_acq( + *(ptr::mut_addr_of(dst) as *mut int), + src as int)) + } +} + +fn swap_state_rel(&dst: state, src: state) -> state { + unsafe { + reinterpret_cast(rusti::atomic_xchng_rel( + *(ptr::mut_addr_of(dst) as *mut int), + src as int)) + } +} + +fn send(-p: send_packet, -payload: T) { + let p_ = p.unwrap(); + let p = unsafe { uniquify(p_) }; + assert (*p).payload == none; + (*p).payload <- some(payload); + let old_state = swap_state_rel(p.header.state, full); + alt old_state { + empty { + // Yay, fastpath. + + // The receiver will eventually clean this up. + unsafe { forget(p); } + } + full { fail "duplicate send" } + blocked { + #debug("waking up task for %?", p_); + alt p.header.blocked_task { + some(task) { + rustrt::task_signal_event( + task, ptr::addr_of(p.header) as *libc::c_void); + } + none { fail "blocked packet has no task" } + } + + // The receiver will eventually clean this up. + unsafe { forget(p); } + } + terminated { + // The receiver will never receive this. Rely on drop_glue + // to clean everything up. + } + } +} + +fn recv(-p: recv_packet) -> option { + let p_ = p.unwrap(); + let p = unsafe { uniquify(p_) }; + let this = rustrt::rust_get_task(); + rustrt::task_clear_event_reject(this); + p.header.blocked_task = some(this); + loop { + let old_state = swap_state_acq(p.header.state, + blocked); + #debug("%?", old_state); + alt old_state { + empty { + #debug("no data available on %?, going to sleep.", p_); + rustrt::task_wait_event(this); + #debug("woke up, p.state = %?", p.header.state); + if p.header.state == full { + let mut payload = none; + payload <-> (*p).payload; + p.header.state = terminated; + ret some(option::unwrap(payload)) + } + } + blocked { fail "blocking on already blocked packet" } + full { + let mut payload = none; + payload <-> (*p).payload; + p.header.state = terminated; + ret some(option::unwrap(payload)) + } + terminated { + assert old_state == terminated; + ret none; + } + } + } +} + +fn sender_terminate(p: *packet) { + let p = unsafe { uniquify(p) }; + alt swap_state_rel(p.header.state, terminated) { + empty | blocked { + // The receiver will eventually clean up. + unsafe { forget(p) } + } + full { + // This is impossible + fail "you dun goofed" + } + terminated { + // I have to clean up, use drop_glue + } + } +} + +fn receiver_terminate(p: *packet) { + let p = unsafe { uniquify(p) }; + alt swap_state_rel(p.header.state, terminated) { + empty { + // the sender will clean up + unsafe { forget(p) } + } + blocked { + // this shouldn't happen. + fail "terminating a blocked packet" + } + terminated | full { + // I have to clean up, use drop_glue + } + } +} + +impl private_methods for packet_header { + // Returns the old state. + fn mark_blocked(this: *rust_task) -> state { + self.blocked_task = some(this); + swap_state_acq(self.state, blocked) + } + + fn unblock() { + alt swap_state_acq(self.state, empty) { + empty | blocked { } + terminated { self.state = terminated; } + full { self.state = full; } + } + } +} + +#[doc = "Returns when one of the packet headers reports data is +available."] +fn wait_many(pkts: ~[&a.packet_header]) -> uint { + let this = rustrt::rust_get_task(); + + rustrt::task_clear_event_reject(this); + let mut data_avail = false; + let mut ready_packet = pkts.len(); + for pkts.eachi |i, p| { + let old = p.mark_blocked(this); + alt old { + full | terminated { + data_avail = true; + ready_packet = i; + p.state = old; + break; + } + blocked { fail "blocking on blocked packet" } + empty { } + } + } + + while !data_avail { + #debug("sleeping on %? packets", pkts.len()); + let event = rustrt::task_wait_event(this) as *packet_header; + let pos = vec::position(pkts, |p| ptr::addr_of(*p) == event); + + alt pos { + some(i) { + ready_packet = i; + data_avail = true; + } + none { + #debug("ignoring spurious event, %?", event); + } + } + } + + #debug("%?", pkts[ready_packet]); + + for pkts.each |p| { p.unblock() } + + #debug("%?, %?", ready_packet, pkts[ready_packet]); + + assert pkts[ready_packet].state == full + || pkts[ready_packet].state == terminated; + + ready_packet +} + +#[doc = "Waits on a set of endpoints. Returns a message, its index, + and a list of the remaining endpoints."] +fn select(+endpoints: ~[recv_packet]) + -> (uint, option, ~[recv_packet]) +{ + let endpoints = vec::map_consume( + endpoints, + |p| unsafe { uniquify(p.unwrap()) }); + let endpoints_r = vec::view(endpoints, 0, endpoints.len()); + let ready = wait_many(endpoints_r.map_r(|p| &p.header)); + let mut remaining = ~[]; + let mut result = none; + do vec::consume(endpoints) |i, p| { + let p = recv_packet(unsafe { unsafe::transmute(p) }); + if i == ready { + result = recv(p); + } + else { + vec::push(remaining, p); + } + } + + (ready, result, remaining) +} + +class send_packet { + let mut p: option<*packet>; + new(p: *packet) { + //#debug("take send %?", p); + self.p = some(p); + } + drop { + //if self.p != none { + // #debug("drop send %?", option::get(self.p)); + //} + if self.p != none { + let mut p = none; + p <-> self.p; + sender_terminate(option::unwrap(p)) + } + } + fn unwrap() -> *packet { + let mut p = none; + p <-> self.p; + option::unwrap(p) + } +} + +class recv_packet { + let mut p: option<*packet>; + new(p: *packet) { + //#debug("take recv %?", p); + self.p = some(p); + } + drop { + //if self.p != none { + // #debug("drop recv %?", option::get(self.p)); + //} + if self.p != none { + let mut p = none; + p <-> self.p; + receiver_terminate(option::unwrap(p)) + } + } + fn unwrap() -> *packet { + let mut p = none; + p <-> self.p; + option::unwrap(p) + } +} + +fn entangle() -> (send_packet, recv_packet) { + let p = packet(); + (send_packet(p), recv_packet(p)) +} + +fn spawn_service( + init: extern fn() -> (send_packet, recv_packet), + +service: fn~(+recv_packet)) + -> send_packet +{ + let (client, server) = init(); + + // This is some nasty gymnastics required to safely move the pipe + // into a new task. + let server = ~mut some(server); + do task::spawn |move service| { + let mut server_ = none; + server_ <-> *server; + service(option::unwrap(server_)) + } + + client +} + +fn spawn_service_recv( + init: extern fn() -> (recv_packet, send_packet), + +service: fn~(+send_packet)) + -> recv_packet +{ + let (client, server) = init(); + + // This is some nasty gymnastics required to safely move the pipe + // into a new task. + let server = ~mut some(server); + do task::spawn |move service| { + let mut server_ = none; + server_ <-> *server; + service(option::unwrap(server_)) + } + + client +} diff --git a/src/libcore/rand.rs b/src/libcore/rand.rs index 4db2cdb086d5e..ce4a29f376ae5 100644 --- a/src/libcore/rand.rs +++ b/src/libcore/rand.rs @@ -144,9 +144,9 @@ impl extensions for rng { * Return a random string of the specified length composed of A-Z,a-z,0-9 */ fn gen_str(len: uint) -> str { - let charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + - "abcdefghijklmnopqrstuvwxyz" + - "0123456789"; + let charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ\ + abcdefghijklmnopqrstuvwxyz\ + 0123456789"; let mut s = ""; let mut i = 0u; while (i < len) { diff --git a/src/libcore/str.rs b/src/libcore/str.rs index 43a9e8f6981bf..2d9d97979097e 100644 --- a/src/libcore/str.rs +++ b/src/libcore/str.rs @@ -15,6 +15,7 @@ export from_byte, from_char, from_chars, + append, concat, connect, @@ -25,6 +26,7 @@ export unpack_slice, // Adding things to and removing things from a string + push_str, push_char, pop_char, shift_char, @@ -233,10 +235,38 @@ pure fn from_chars(chs: &[const char]) -> str { ret buf; } +/// Appends a string slice to the back of a string +#[inline(always)] +fn push_str(&lhs: str, rhs: str/&) { + unsafe { + let llen = lhs.len(); + let rlen = rhs.len(); + reserve(lhs, llen + rlen); + do as_buf(lhs) |lbuf| { + do unpack_slice(rhs) |rbuf, _rlen| { + let dst = ptr::offset(lbuf, llen); + ptr::memcpy(dst, rbuf, rlen); + } + } + unsafe::set_len(lhs, llen + rlen); + } +} + +/// Concatenate two strings together +#[inline(always)] +pure fn append(+lhs: str, rhs: str/&) -> str { + let mut v <- lhs; + unchecked { + push_str(v, rhs); + } + ret v; +} + + /// Concatenate a vector of strings pure fn concat(v: &[const str]) -> str { let mut s: str = ""; - for vec::each(v) |ss| { s += ss; } + for vec::each(v) |ss| { unchecked { push_str(s, ss) }; } ret s; } @@ -244,8 +274,8 @@ pure fn concat(v: &[const str]) -> str { pure fn connect(v: &[const str], sep: str) -> str { let mut s = "", first = true; for vec::each(v) |ss| { - if first { first = false; } else { s += sep; } - s += ss; + if first { first = false; } else { unchecked { push_str(s, sep); } } + unchecked { push_str(s, ss) }; } ret s; } @@ -576,8 +606,8 @@ pure fn to_upper(s: str/&) -> str { pure fn replace(s: str, from: str, to: str) -> str { let mut result = "", first = true; do iter_between_matches(s, from) |start, end| { - if first { first = false; } else { result += to; } - unsafe { result += unsafe::slice_bytes(s, start, end); } + if first { first = false; } else { unchecked {push_str(result, to); }} + unsafe { push_str(result, unsafe::slice_bytes(s, start, end)); } } result } @@ -1694,7 +1724,7 @@ pure fn escape_default(s: str/&) -> str { let mut out: str = ""; unchecked { reserve_at_least(out, str::len(s)); - chars_iter(s, |c| out += char::escape_default(c)); + chars_iter(s, |c| push_str(out, char::escape_default(c))); } ret out; } @@ -1704,7 +1734,7 @@ pure fn escape_unicode(s: str/&) -> str { let mut out: str = ""; unchecked { reserve_at_least(out, str::len(s)); - chars_iter(s, |c| out += char::escape_unicode(c)); + chars_iter(s, |c| push_str(out, char::escape_unicode(c))); } ret out; } @@ -1863,6 +1893,12 @@ impl extensions for str { /// Returns a string with trailing whitespace removed #[inline] fn trim_right() -> str { trim_right(self) } + + /// Concatenate two strings: operator version + #[inline(always)] + pure fn +(rhs: str/&) -> str { + append(self, rhs) + } } /// Extension methods for strings @@ -2311,13 +2347,13 @@ mod tests { fn a_million_letter_a() -> str { let mut i = 0; let mut rs = ""; - while i < 100000 { rs += "aaaaaaaaaa"; i += 1; } + while i < 100000 { push_str(rs, "aaaaaaaaaa"); i += 1; } ret rs; } fn half_a_million_letter_a() -> str { let mut i = 0; let mut rs = ""; - while i < 100000 { rs += "aaaaa"; i += 1; } + while i < 100000 { push_str(rs, "aaaaa"); i += 1; } ret rs; } assert eq(half_a_million_letter_a(), @@ -2422,13 +2458,13 @@ mod tests { fn a_million_letter_X() -> str { let mut i = 0; let mut rs = ""; - while i < 100000 { rs += "华华华华华华华华华华"; i += 1; } + while i < 100000 { push_str(rs, "华华华华华华华华华华"); i += 1; } ret rs; } fn half_a_million_letter_X() -> str { let mut i = 0; let mut rs = ""; - while i < 100000 { rs += "华华华华华"; i += 1; } + while i < 100000 { push_str(rs, "华华华华华"); i += 1; } ret rs; } assert eq(half_a_million_letter_X(), diff --git a/src/libcore/task.rs b/src/libcore/task.rs index fd69525d63e2d..f41e24c623f70 100644 --- a/src/libcore/task.rs +++ b/src/libcore/task.rs @@ -47,6 +47,7 @@ export unsupervise; export run_listener; export spawn; +export spawn_with; export spawn_listener; export spawn_sched; export try; @@ -308,11 +309,21 @@ fn future_result(builder: builder) -> future::future { fn future_task(builder: builder) -> future::future { //! Get a future representing the handle to the new task - let mut po = comm::port(); - let ch = comm::chan(po); - do add_wrapper(builder) |body| { - fn~() { - comm::send(ch, get_task()); + import future::future_pipe; + + let (po, ch) = future_pipe::init(); + + let ch = ~mut some(ch); + + do add_wrapper(builder) |body, move ch| { + let ch = { let mut t = none; + t <-> *ch; + ~mut t}; + fn~(move ch) { + let mut po = none; + po <-> *ch; + future_pipe::server::completed(option::unwrap(po), + get_task()); body(); } } @@ -328,6 +339,28 @@ fn unsupervise(builder: builder) { }); } +fn run_with(-builder: builder, + +arg: A, + +f: fn~(+A)) { + + /*! + * + * Runs a task, while transfering ownership of one argument to the + * child. + * + * This is useful for transfering ownership of noncopyables to + * another task. + * + */ + + let arg = ~mut some(arg); + do run(builder) { + let mut my_arg = none; + my_arg <-> *arg; + f(option::unwrap(my_arg)) + } +} + fn run_listener(-builder: builder, +f: fn~(comm::port)) -> comm::chan { /*! @@ -371,6 +404,22 @@ fn spawn(+f: fn~()) { run(builder(), f); } +fn spawn_with(+arg: A, +f: fn~(+A)) { + /*! + * Runs a new task while providing a channel from the parent to the child + * + * Sets up a communication channel from the current task to the new + * child task, passes the port to child's body, and returns a channel + * linked to the port to the parent. + * + * This encapsulates some boilerplate handshaking logic that would + * otherwise be required to establish communication from the parent + * to the child. + */ + + run_with(builder(), arg, f) +} + fn spawn_listener(+f: fn~(comm::port)) -> comm::chan { /*! * Runs a new task while providing a channel from the parent to the child @@ -782,6 +831,7 @@ extern mod rustrt { fn rust_new_sched(num_threads: libc::uintptr_t) -> sched_id; fn get_task_id() -> task_id; + #[rust_stack] fn rust_get_task() -> *rust_task; fn new_task() -> *rust_task; diff --git a/src/libcore/to_str.rs b/src/libcore/to_str.rs index 359f5ea4a9cd8..c54a8887f8465 100644 --- a/src/libcore/to_str.rs +++ b/src/libcore/to_str.rs @@ -61,10 +61,10 @@ impl of to_str for ~[A] { let mut acc = "[", first = true; for vec::each(self) |elt| { if first { first = false; } - else { acc += ", "; } - acc += elt.to_str(); + else { str::push_str(acc, ", "); } + str::push_str(acc, elt.to_str()); } - acc += "]"; + str::push_char(acc, ']'); acc } } diff --git a/src/libcore/vec.rs b/src/libcore/vec.rs index 895377170bc0b..eb3d52edc24e7 100644 --- a/src/libcore/vec.rs +++ b/src/libcore/vec.rs @@ -6,6 +6,7 @@ import libc::size_t; export append; export append_one; +export consume; export init_op; export is_empty; export is_not_empty; @@ -40,6 +41,7 @@ export grow_set; export map; export mapi; export map2; +export map_consume; export flat_map; export filter_map; export filter; @@ -261,8 +263,8 @@ pure fn slice(v: &[const T], start: uint, end: uint) -> ~[T] { ret result; } -/// Return a slice that points into another slice. -pure fn view(v: &[const T], start: uint, end: uint) -> &a.[T] { +#[doc = "Return a slice that points into another slice."] +pure fn view(v: &[const T], start: uint, end: uint) -> &a.[T] { assert (start <= end); assert (end <= len(v)); do unpack_slice(v) |p, _len| { @@ -373,7 +375,7 @@ fn rsplitn(v: &[T], n: uint, f: fn(T) -> bool) -> ~[~[T]] { /// Removes the first element from a vector and return it fn shift(&v: ~[T]) -> T { let ln = len::(v); - assert (ln > 0u); + assert (ln > 0); let mut vv = ~[]; v <-> vv; @@ -384,12 +386,12 @@ fn shift(&v: ~[T]) -> T { let vv = unsafe::to_ptr(vv); rr <- *vv; - for uint::range(1u, ln) |i| { + for uint::range(1, ln) |i| { let r <- *ptr::offset(vv, i); push(v, r); } } - unsafe::set_len(vv, 0u); + unsafe::set_len(vv, 0); rr } @@ -404,6 +406,17 @@ fn unshift(&v: ~[T], +x: T) { } } +fn consume(+v: ~[T], f: fn(uint, +T)) unsafe { + do unpack_slice(v) |p, ln| { + for uint::range(0, ln) |i| { + let x <- *ptr::offset(p, i); + f(i, x); + } + } + + unsafe::set_len(v, 0); +} + /// Remove the last element from a vector and return it fn pop(&v: ~[const T]) -> T { let ln = len(v); @@ -575,6 +588,14 @@ pure fn map(v: &[T], f: fn(T) -> U) -> ~[U] { ret result; } +fn map_consume(+v: ~[T], f: fn(+T) -> U) -> ~[U] { + let mut result = ~[]; + do consume(v) |_i, x| { + vec::push(result, f(x)); + } + result +} + /// Apply a function to each element of a vector and return the results pure fn mapi(v: &[T], f: fn(uint, T) -> U) -> ~[U] { let mut result = ~[]; @@ -1162,14 +1183,14 @@ pure fn unpack_mut_slice(s: &[mut T], impl extensions for ~[T] { #[inline(always)] - pure fn +(rhs: &[T]) -> ~[T] { + pure fn +(rhs: &[const T]) -> ~[T] { append(self, rhs) } } impl extensions for ~[mut T] { #[inline(always)] - pure fn +(rhs: &[mut T]) -> ~[mut T] { + pure fn +(rhs: &[const T]) -> ~[mut T] { append_mut(self, rhs) } } @@ -1277,6 +1298,18 @@ impl extensions/& for &[T] { pure fn mapi(f: fn(uint, T) -> U) -> ~[U] { mapi(self, f) } + + #[inline] + fn map_r(f: fn(x: &self.T) -> U) -> ~[U] { + let mut r = ~[]; + let mut i = 0; + while i < self.len() { + push(r, f(&self[i])); + i += 1; + } + r + } + /** * Returns true if the function returns true for all elements. * diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index a817faad0691e..1941d809d7b87 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -336,7 +336,7 @@ enum expr_ { expr_addr_of(mutability, @expr), expr_fail(option<@expr>), expr_break, - expr_cont, + expr_again, expr_ret(option<@expr>), expr_log(int, @expr, @expr), @@ -704,6 +704,7 @@ enum item_ { item_trait(~[ty_param], region_param, ~[ty_method]), item_impl(~[ty_param], region_param, option<@trait_ref> /* trait */, @ty /* self */, ~[@method]), + item_mac(mac), } #[auto_serialize] diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs index 28e5c2c5f171c..0e6cce27b2b85 100644 --- a/src/libsyntax/ext/base.rs +++ b/src/libsyntax/ext/base.rs @@ -17,12 +17,18 @@ type item_decorator = type syntax_expander_tt = {expander: syntax_expander_tt_, span: option}; type syntax_expander_tt_ = fn@(ext_ctxt, span, ast::token_tree) -> @ast::expr; +type syntax_expander_tt_item + = {expander: syntax_expander_tt_item_, span: option}; +type syntax_expander_tt_item_ + = fn@(ext_ctxt, span, ast::ident, ast::token_tree) -> @ast::item; + enum syntax_extension { normal(syntax_expander), macro_defining(macro_definer), item_decorator(item_decorator), - normal_tt(syntax_expander_tt) + normal_tt(syntax_expander_tt), + item_tt(syntax_expander_tt_item), } // A temporary hard-coded map of methods for expanding syntax extension @@ -30,6 +36,9 @@ enum syntax_extension { fn syntax_expander_table() -> hashmap { fn builtin(f: syntax_expander_) -> syntax_extension {normal({expander: f, span: none})} + fn builtin_item_tt(f: syntax_expander_tt_item_) -> syntax_extension { + item_tt({expander: f, span: none}) + } let syntax_expanders = str_hash::(); syntax_expanders.insert("fmt", builtin(ext::fmt::expand_syntax_ext)); syntax_expanders.insert("auto_serialize", @@ -61,6 +70,8 @@ fn syntax_expander_table() -> hashmap { builtin(ext::source_util::expand_include_bin)); syntax_expanders.insert("mod", builtin(ext::source_util::expand_mod)); + syntax_expanders.insert("proto", + builtin_item_tt(ext::pipes::expand_proto)); ret syntax_expanders; } diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index a037d87166ae9..4ac7dc19c0007 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -1,7 +1,7 @@ import std::map::hashmap; import ast::{crate, expr_, expr_mac, mac_invoc, mac_invoc_tt, - tt_delim, tt_flat}; + tt_delim, tt_flat, item_mac}; import fold::*; import ext::base::*; import ext::qquote::{qq_helper}; @@ -52,6 +52,10 @@ fn expand_expr(exts: hashmap, cx: ext_ctxt, #fmt["this tt-style macro should be \ invoked '%s!{...}'", *extname]) } + some(item_tt(*)) { + cx.span_fatal(pth.span, + "cannot use item macros in this context"); + } } } mac_invoc_tt(pth, tt) { @@ -109,7 +113,7 @@ fn expand_mod_items(exts: hashmap, cx: ext_ctxt, }; alt exts.find(*mname) { none | some(normal(_)) | some(macro_defining(_)) - | some(normal_tt(_)) { + | some(normal_tt(_)) | some(item_tt(*)) { items } @@ -124,7 +128,8 @@ fn expand_mod_items(exts: hashmap, cx: ext_ctxt, } /* record module we enter for `#mod` */ -fn expand_item(cx: ext_ctxt, &&it: @ast::item, fld: ast_fold, +fn expand_item(exts: hashmap, + cx: ext_ctxt, &&it: @ast::item, fld: ast_fold, orig: fn@(&&@ast::item, ast_fold) -> @ast::item) -> @ast::item { @@ -132,12 +137,48 @@ fn expand_item(cx: ext_ctxt, &&it: @ast::item, fld: ast_fold, ast::item_mod(_) | ast::item_foreign_mod(_) {true} _ {false} }; + let it = alt it.node { + ast::item_mac(*) { + expand_item_mac(exts, cx, it, fld) + } + _ { it } + }; if is_mod { cx.mod_push(it.ident); } let ret_val = orig(it, fld); if is_mod { cx.mod_pop(); } ret ret_val; } +fn expand_item_mac(exts: hashmap, + cx: ext_ctxt, &&it: @ast::item, + fld: ast_fold) -> @ast::item { + alt it.node { + item_mac({node: mac_invoc_tt(pth, tt), span}) { + let extname = pth.idents[0]; + alt exts.find(*extname) { + none { + cx.span_fatal(pth.span, + #fmt("macro undefined: '%s'", *extname)) + } + some(item_tt(expand)) { + cx.bt_push(expanded_from({call_site: it.span, + callie: {name: *extname, + span: expand.span}})); + let it = fld.fold_item( + expand.expander(cx, it.span, it.ident, tt)); + cx.bt_pop(); + ret it + } + _ { cx.span_fatal(it.span, + #fmt("%s is not a legal here", *extname)) } + } + } + _ { + cx.span_bug(it.span, "invalid item macro invocation"); + } + } +} + fn new_span(cx: ext_ctxt, sp: span) -> span { /* this discards information in the case of macro-defining macros */ ret {lo: sp.lo, hi: sp.hi, expn_info: cx.backtrace()}; @@ -166,7 +207,7 @@ fn expand_crate(parse_sess: parse::parse_sess, let f_pre = @{fold_expr: |a,b,c| expand_expr(exts, cx, a, b, c, afp.fold_expr), fold_mod: |a,b| expand_mod_items(exts, cx, a, b, afp.fold_mod), - fold_item: |a,b| expand_item(cx, a, b, afp.fold_item), + fold_item: |a,b| expand_item(exts, cx, a, b, afp.fold_item), new_span: |a|new_span(cx, a) with *afp}; let f = make_fold(f_pre); diff --git a/src/libsyntax/ext/pipes.rs b/src/libsyntax/ext/pipes.rs new file mode 100644 index 0000000000000..4ca47254a943d --- /dev/null +++ b/src/libsyntax/ext/pipes.rs @@ -0,0 +1,29 @@ + +import codemap::span; +import ext::base::ext_ctxt; +import ast::tt_delim; +import parse::lexer::{new_tt_reader, reader, tt_reader_as_reader}; +import parse::parser::{parser, SOURCE_FILE}; +import parse::common::parser_common; + +import pipes::parse_proto::proto_parser; + +import pipes::pipec::*; + +fn expand_proto(cx: ext_ctxt, _sp: span, id: ast::ident, tt: ast::token_tree) + -> @ast::item +{ + let sess = cx.parse_sess(); + let cfg = cx.cfg(); + let body_core = alt tt { tt_delim(tts) { tts } _ {fail}}; + let tt_rdr = new_tt_reader(cx.parse_sess().span_diagnostic, + cx.parse_sess().interner, + none, + body_core); + let rdr = tt_rdr as reader; + let rust_parser = parser(sess, cfg, rdr.dup(), SOURCE_FILE); + + let proto = rust_parser.parse_proto(id); + + proto.compile(cx) +} \ No newline at end of file diff --git a/src/libsyntax/ext/pipes/ast_builder.rs b/src/libsyntax/ext/pipes/ast_builder.rs new file mode 100644 index 0000000000000..b23e707f4eb2c --- /dev/null +++ b/src/libsyntax/ext/pipes/ast_builder.rs @@ -0,0 +1,182 @@ +// Functions for building ASTs, without having to fuss with spans. +// +// To start with, it will be use dummy spans, but it might someday do +// something smarter. + +import ast::{ident, node_id}; +import codemap::span; +import ext::base::mk_ctxt; + +fn ident(s: str) -> ast::ident { + @(copy s) +} + +fn empty_span() -> span { + {lo: 0, hi: 0, expn_info: none} +} + +fn span(+x: T) -> ast::spanned { + {node: x, + span: empty_span()} +} + +fn path(id: ident) -> @ast::path { + @{span: empty_span(), + global: false, + idents: ~[id], + rp: none, + types: ~[]} +} + +impl methods for ident { + fn +(id: ident) -> @ast::path { + path(self) + id + } +} + +impl methods for @ast::path { + fn +(id: ident) -> @ast::path { + @{idents: vec::append_one(self.idents, id) + with *self} + } + + fn add_ty(ty: @ast::ty) -> @ast::path { + @{types: vec::append_one(self.types, ty) + with *self} + } + + fn add_tys(+tys: ~[@ast::ty]) -> @ast::path { + @{types: vec::append(self.types, tys) + with *self} + } +} + +impl ast_builder for ext_ctxt { + fn ty_param(id: ast::ident, +bounds: ~[ast::ty_param_bound]) + -> ast::ty_param + { + {ident: id, id: self.next_id(), bounds: @bounds} + } + + fn arg(name: ident, ty: @ast::ty) -> ast::arg { + {mode: ast::infer(self.next_id()), + ty: ty, + ident: name, + // TODO: should this be the same as the infer id? + id: self.next_id()} + } + + fn arg_mode(name: ident, ty: @ast::ty, mode: ast::rmode) -> ast::arg { + {mode: ast::expl(mode), + ty: ty, + ident: name, + id: self.next_id()} + } + + fn expr_block(e: @ast::expr) -> ast::blk { + let blk = {view_items: ~[], + stmts: ~[], + expr: some(e), + id: self.next_id(), + rules: ast::default_blk}; + + {node: blk, + span: empty_span()} + } + + fn fn_decl(+inputs: ~[ast::arg], + output: @ast::ty) -> ast::fn_decl { + {inputs: inputs, + output: output, + purity: ast::impure_fn, + cf: ast::return_val, + // TODO: we'll probably want a variant that does constrained + // types. + constraints: ~[]} + } + + fn item(name: ident, + +node: ast::item_) -> @ast::item { + @{ident: name, + attrs: ~[], + id: self.next_id(), + node: node, + vis: ast::public, + span: empty_span()} + } + + fn item_fn_poly(name: ident, + +inputs: ~[ast::arg], + output: @ast::ty, + +ty_params: ~[ast::ty_param], + +body: ast::blk) -> @ast::item { + self.item(name, + ast::item_fn(self.fn_decl(inputs, output), + ty_params, + body)) + } + + fn item_fn(name: ident, + +inputs: ~[ast::arg], + output: @ast::ty, + +body: ast::blk) -> @ast::item { + self.item_fn_poly(name, inputs, output, ~[], body) + } + + fn item_enum_poly(name: ident, + +variants: ~[ast::variant], + +ty_params: ~[ast::ty_param]) -> @ast::item { + self.item(name, + ast::item_enum(variants, + ty_params, + ast::rp_none)) + } + + fn item_enum(name: ident, + +variants: ~[ast::variant]) -> @ast::item { + self.item_enum_poly(name, variants, ~[]) + } + + fn variant(name: ident, + +tys: ~[@ast::ty]) -> ast::variant { + let args = tys.map(|ty| {ty: ty, id: self.next_id()}); + + span({name: name, + attrs: ~[], + args: args, + id: self.next_id(), + disr_expr: none, + vis: ast::public}) + } + + fn item_mod(name: ident, + +items: ~[@ast::item]) -> @ast::item { + self.item(name, + ast::item_mod({ + view_items: ~[], + items: items})) + } + + fn ty_path(path: @ast::path) -> @ast::ty { + // TODO: make sure the node ids are legal. + @{id: self.next_id(), + node: ast::ty_path(path, self.next_id()), + span: empty_span()} + } + + fn item_ty_poly(name: ident, + ty: @ast::ty, + +params: ~[ast::ty_param]) -> @ast::item { + self.item(name, + ast::item_ty(ty, params, ast::rp_none)) + } + + fn item_ty(name: ident, + ty: @ast::ty) -> @ast::item { + self.item_ty_poly(name, ty, ~[]) + } + + fn ty_vars(+ty_params: ~[ast::ty_param]) -> ~[@ast::ty] { + ty_params.map(|p| self.ty_path(path(p.ident))) + } +} diff --git a/src/libsyntax/ext/pipes/parse_proto.rs b/src/libsyntax/ext/pipes/parse_proto.rs new file mode 100644 index 0000000000000..abf19825fdd27 --- /dev/null +++ b/src/libsyntax/ext/pipes/parse_proto.rs @@ -0,0 +1,77 @@ +// Parsing pipes protocols from token trees. + +import parse::parser; +import ast::ident; +import parse::token; + +import pipec::*; + +impl proto_parser for parser { + fn parse_proto(id: ident) -> protocol { + let proto = protocol(id); + + self.parse_unspanned_seq(token::LBRACE, + token::RBRACE, + {sep: none, trailing_sep_allowed: false}, + |self| self.parse_state(proto)); + + ret proto; + } + + fn parse_state(proto: protocol) { + let id = self.parse_ident(); + self.expect(token::COLON); + let dir = alt copy self.token { + token::IDENT(n, _) { + self.get_str(n) + } + _ { fail } + }; + self.bump(); + let dir = alt dir { + @"send" { send } + @"recv" { recv } + _ { fail } + }; + + let typarms = if self.token == token::LT { + self.parse_ty_params() + } + else { ~[] }; + + let state = proto.add_state_poly(id, dir, typarms); + + // parse the messages + self.parse_unspanned_seq( + token::LBRACE, token::RBRACE, + {sep: some(token::COMMA), trailing_sep_allowed: true}, + |self| { + let mname = self.parse_ident(); + + let args = if self.token == token::LPAREN { + self.parse_unspanned_seq(token::LPAREN, + token::RPAREN, + {sep: some(token::COMMA), + trailing_sep_allowed: true}, + |p| p.parse_ty(false)) + } + else { ~[] }; + + self.expect(token::RARROW); + + let next = self.parse_ident(); + + let ntys = if self.token == token::LT { + self.parse_unspanned_seq(token::LT, + token::GT, + {sep: some(token::COMMA), + trailing_sep_allowed: true}, + |p| p.parse_ty(false)) + } + else { ~[] }; + + state.add_message(mname, args, next, ntys); + + }); + } +} diff --git a/src/libsyntax/ext/pipes/pipec.rs b/src/libsyntax/ext/pipes/pipec.rs new file mode 100644 index 0000000000000..e9dfffcd543fa --- /dev/null +++ b/src/libsyntax/ext/pipes/pipec.rs @@ -0,0 +1,395 @@ +// A protocol compiler for Rust. + +import to_str::to_str; + +import dvec::dvec; +import dvec::extensions; + +import ast::ident; +import util::interner; +import interner::{intern, get}; +import print::pprust; +import pprust::{item_to_str, ty_to_str}; +import ext::base::{mk_ctxt, ext_ctxt}; +import parse; +import parse::*; + +import ast_builder::ast_builder; +import ast_builder::methods; +import ast_builder::path; + +enum direction { + send, recv +} + +impl of to_str for direction { + fn to_str() -> str { + alt self { + send { "send" } + recv { "recv" } + } + } +} + +impl methods for direction { + fn reverse() -> direction { + alt self { + send { recv } + recv { send } + } + } +} + +enum message { + // name, data, current state, next state, next tys + message(ident, ~[@ast::ty], state, ident, ~[@ast::ty]) +} + +impl methods for message { + fn name() -> ident { + alt self { + message(id, _, _, _, _) { + id + } + } + } + + // Return the type parameters actually used by this message + fn get_params() -> ~[ast::ty_param] { + let mut used = ~[]; + alt self { + message(_, tys, this, _, next_tys) { + let parms = this.ty_params; + for vec::append(tys, next_tys).each |ty| { + alt ty.node { + ast::ty_path(path, _) { + if path.idents.len() == 1 { + let id = path.idents[0]; + + let found = parms.find(|p| id == p.ident); + + alt found { + some(p) { + if !used.contains(p) { + vec::push(used, p); + } + } + none { } + } + } + } + _ { } + } + } + } + } + used + } + + fn gen_send(cx: ext_ctxt) -> @ast::item { + alt self { + message(id, tys, this, next, next_tys) { + let next = this.proto.get_state(next); + assert next_tys.len() == next.ty_params.len(); + let arg_names = tys.mapi(|i, _ty| @("x_" + i.to_str())); + + let args_ast = (arg_names, tys).map( + |n, t| cx.arg_mode(n, t, ast::by_copy) + ); + + let args_ast = vec::append( + ~[cx.arg_mode(@"pipe", + cx.ty_path(path(this.data_name()) + .add_tys(cx.ty_vars(this.ty_params))), + ast::by_copy)], + args_ast); + + let pat = alt (this.dir, next.dir) { + (send, send) { "(c, s)" } + (send, recv) { "(s, c)" } + (recv, send) { "(s, c)" } + (recv, recv) { "(c, s)" } + }; + + let mut body = #fmt("{ let %s = pipes::entangle();\n", pat); + body += #fmt("let message = %s::%s(%s);\n", + *this.proto.name, + *self.name(), + str::connect(vec::append_one(arg_names, @"s") + .map(|x| *x), + ", ")); + body += #fmt("pipes::send(pipe, message);\n"); + body += "c }"; + + let body = cx.parse_expr(body); + + cx.item_fn_poly(self.name(), + args_ast, + cx.ty_path(path(next.data_name()) + .add_tys(next_tys)), + self.get_params(), + cx.expr_block(body)) + } + } + } +} + +enum state { + state_(@{ + name: ident, + dir: direction, + ty_params: ~[ast::ty_param], + messages: dvec, + proto: protocol, + }), +} + +impl methods for state { + fn add_message(name: ident, +data: ~[@ast::ty], next: ident, + +next_tys: ~[@ast::ty]) { + self.messages.push(message(name, data, self, next, next_tys)); + } + + fn filename() -> str { + (*self).proto.filename() + } + + fn data_name() -> ident { + self.name + } + + fn to_ty(cx: ext_ctxt) -> @ast::ty { + cx.ty_path(path(self.name).add_tys(cx.ty_vars(self.ty_params))) + } + + fn to_type_decls(cx: ext_ctxt) -> [@ast::item]/~ { + // This compiles into two different type declarations. Say the + // state is called ping. This will generate both `ping` and + // `ping_message`. The first contains data that the user cares + // about. The second is the same thing, but extended with a + // next packet pointer, which is used under the covers. + + let name = self.data_name(); + + let mut items_msg = []/~; + + for self.messages.each |m| { + let message(_, tys, this, next, next_tys) = m; + + let name = m.name(); + let next = this.proto.get_state(next); + let next_name = next.data_name(); + + let dir = alt this.dir { + send { @"server" } + recv { @"client" } + }; + + let v = cx.variant(name, + vec::append_one( + tys, + cx.ty_path((dir + next_name) + .add_tys(next_tys)))); + + vec::push(items_msg, v); + } + + ~[cx.item_enum_poly(name, items_msg, self.ty_params)] + } + + fn to_endpoint_decls(cx: ext_ctxt, dir: direction) -> [@ast::item]/~ { + let dir = alt dir { + send { (*self).dir } + recv { (*self).dir.reverse() } + }; + let mut items = ~[]; + for self.messages.each |m| { + if dir == send { + vec::push(items, m.gen_send(cx)) + } + } + + vec::push(items, + cx.item_ty_poly( + self.data_name(), + cx.ty_path( + (@"pipes" + @(dir.to_str() + "_packet")) + .add_ty(cx.ty_path( + (self.proto.name + self.data_name()) + .add_tys(cx.ty_vars(self.ty_params))))), + self.ty_params)); + items + } +} + +enum protocol { + protocol_(@{ + name: ident, + states: dvec, + }), +} + +fn protocol(name: ident) -> protocol { + protocol_(@{name: name, states: dvec()}) +} + +impl methods for protocol { + fn add_state(name: ident, dir: direction) -> state { + self.add_state_poly(name, dir, ~[]) + } + + /// Get or create a state. + fn get_state(name: ident) -> state { + self.states.find(|i| i.name == name).get() + } + + fn add_state_poly(name: ident, dir: direction, + +ty_params: ~[ast::ty_param]) -> state { + let messages = dvec(); + + let state = state_(@{ + name: name, + dir: dir, + ty_params: ty_params, + messages: messages, + proto: self + }); + + self.states.push(state); + state + } + + fn filename() -> str { + "proto://" + *self.name + } + + fn gen_init(cx: ext_ctxt) -> @ast::item { + let start_state = self.states[0]; + + let body = alt start_state.dir { + send { cx.parse_expr("pipes::entangle()") } + recv { + cx.parse_expr("{ \ + let (s, c) = pipes::entangle(); \ + (c, s) \ + }") + } + }; + + parse_item_from_source_str( + self.filename(), + @#fmt("fn init%s() -> (client::%s, server::%s)\ + { %s }", + start_state.ty_params.to_source(), + start_state.to_ty(cx).to_source(), + start_state.to_ty(cx).to_source(), + body.to_source()), + cx.cfg(), + []/~, + ast::public, + cx.parse_sess()).get() + } + + fn compile(cx: ext_ctxt) -> @ast::item { + let mut items = ~[self.gen_init(cx)]; + let mut client_states = ~[]; + let mut server_states = ~[]; + + // :( + for (copy self.states).each |s| { + items += s.to_type_decls(cx); + + client_states += s.to_endpoint_decls(cx, send); + server_states += s.to_endpoint_decls(cx, recv); + } + + vec::push(items, + cx.item_mod(@"client", + client_states)); + vec::push(items, + cx.item_mod(@"server", + server_states)); + + cx.item_mod(self.name, items) + } +} + +iface to_source { + // Takes a thing and generates a string containing rust code for it. + fn to_source() -> str; +} + +impl of to_source for @ast::item { + fn to_source() -> str { + item_to_str(self) + } +} + +impl of to_source for [@ast::item]/~ { + fn to_source() -> str { + str::connect(self.map(|i| i.to_source()), "\n\n") + } +} + +impl of to_source for @ast::ty { + fn to_source() -> str { + ty_to_str(self) + } +} + +impl of to_source for [@ast::ty]/~ { + fn to_source() -> str { + str::connect(self.map(|i| i.to_source()), ", ") + } +} + +impl of to_source for ~[ast::ty_param] { + fn to_source() -> str { + pprust::typarams_to_str(self) + } +} + +impl of to_source for @ast::expr { + fn to_source() -> str { + pprust::expr_to_str(self) + } +} + +impl parse_utils for ext_ctxt { + fn parse_item(s: str) -> @ast::item { + let res = parse::parse_item_from_source_str( + "***protocol expansion***", + @(copy s), + self.cfg(), + []/~, + ast::public, + self.parse_sess()); + alt res { + some(ast) { ast } + none { + #error("Parse error with ```\n%s\n```", s); + fail + } + } + } + + fn parse_expr(s: str) -> @ast::expr { + parse::parse_expr_from_source_str( + "***protocol expansion***", + @(copy s), + self.cfg(), + self.parse_sess()) + } +} + +impl methods for ([A]/~, [B]/~) { + fn zip() -> [(A, B)]/~ { + let (a, b) = self; + vec::zip(a, b) + } + + fn map(f: fn(A, B) -> C) -> [C]/~ { + let (a, b) = self; + vec::map2(a, b, f) + } +} diff --git a/src/libsyntax/ext/tt/earley_parser.rs b/src/libsyntax/ext/tt/earley_parser.rs index 26db2842ef772..191da586ce5a2 100644 --- a/src/libsyntax/ext/tt/earley_parser.rs +++ b/src/libsyntax/ext/tt/earley_parser.rs @@ -272,6 +272,7 @@ fn parse_nt(p: parser, name: str) -> whole_nt { + token::to_str(*p.reader.interner(), copy p.token)) } } } "path" { token::w_path(p.parse_path_with_tps(false)) } + "tt" { token::w_tt(@p.parse_token_tree()) } _ { p.fatal("Unsupported builtin nonterminal parser: " + name)} } } diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs index ad55c85496b3a..7f83b16b8ab71 100644 --- a/src/libsyntax/fold.rs +++ b/src/libsyntax/fold.rs @@ -283,6 +283,10 @@ fn noop_fold_item_underscore(i: item_, fld: ast_fold) -> item_ { rp, /* FIXME (#2543) */ copy methods) } + item_mac(m) { + // TODO: we might actually want to do something here. + item_mac(m) + } }; } @@ -460,7 +464,7 @@ fn noop_fold_expr(e: expr_, fld: ast_fold) -> expr_ { } expr_path(pth) { expr_path(fld.fold_path(pth)) } expr_fail(e) { expr_fail(option::map(e, fld.fold_expr)) } - expr_break | expr_cont { copy e } + expr_break | expr_again { copy e } expr_ret(e) { expr_ret(option::map(e, fld.fold_expr)) } expr_log(i, lv, e) { expr_log(i, fld.fold_expr(lv), fld.fold_expr(e)) } diff --git a/src/libsyntax/parse.rs b/src/libsyntax/parse.rs index 9c143257e9eae..b2d06311e673a 100644 --- a/src/libsyntax/parse.rs +++ b/src/libsyntax/parse.rs @@ -12,6 +12,9 @@ export parse_crate_from_source_str; export parse_expr_from_source_str, parse_item_from_source_str; export parse_from_source_str; +// this used to be `import common::parser_common`, but it was causing +// unresolved import errors. Maybe resolve3 will fix it. +import common::*; import parser::parser; //import attr::parser_attr; import attr::*; //resolve bug? @@ -20,8 +23,7 @@ import common::*; //resolve bug? import ast::node_id; import util::interner; // FIXME (#1935): resolve badness -import lexer::{string_reader_as_reader, tt_reader_as_reader, reader, - string_reader, tt_reader}; +import lexer::*; import diagnostic::{span_handler, mk_span_handler, mk_handler, emitter}; type parse_sess = @{ diff --git a/src/libsyntax/parse/attr.rs b/src/libsyntax/parse/attr.rs index d7ae4995520ec..e62de46f5dbf1 100644 --- a/src/libsyntax/parse/attr.rs +++ b/src/libsyntax/parse/attr.rs @@ -1,7 +1,6 @@ import either::{either, left, right}; import ast_util::spanned; import common::*; //resolve bug? -//import common::{parser_common, seq_sep_trailing_disallowed}; export attr_or_ext; export parser_attr; diff --git a/src/libsyntax/parse/common.rs b/src/libsyntax/parse/common.rs index d4331ee766f8b..16059b473bbb6 100644 --- a/src/libsyntax/parse/common.rs +++ b/src/libsyntax/parse/common.rs @@ -92,6 +92,15 @@ impl parser_common for parser { self.token_is_keyword(word, self.token) } + fn is_any_keyword(tok: token::token) -> bool { + alt tok { + token::IDENT(sid, false) { + self.keywords.contains_key(*self.get_str(sid)) + } + _ { false } + } + } + fn eat_keyword(word: str) -> bool { self.require_keyword(word); diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 8bac3e0d751e7..c94e2acbb2b44 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -9,7 +9,7 @@ import lexer::reader; import prec::{as_prec, token_to_binop}; import attr::parser_attr; import common::{seq_sep_trailing_disallowed, seq_sep_trailing_allowed, - seq_sep_none, token_to_str, parser_common}; + seq_sep_none, token_to_str}; import dvec::{dvec, extensions}; import vec::{push}; import ast::*; @@ -463,7 +463,8 @@ class parser { } mt { ty_uniq(mt) } } - } else if self.token == token::BINOP(token::STAR) { + } else if self.token == token::BINOP(token::STAR) || + self.token == token::BINOP(token::CARET) { self.bump(); ty_ptr(self.parse_mt()) } else if self.token == token::LBRACE { @@ -966,8 +967,9 @@ class parser { } else if self.eat_keyword("break") { ex = expr_break; hi = self.span.hi; - } else if self.eat_keyword("cont") { - ex = expr_cont; + } else if self.eat_keyword("cont") || + self.eat_keyword("again") { + ex = expr_again; hi = self.span.hi; } else if self.eat_keyword("copy") { let e = self.parse_expr(); @@ -2595,6 +2597,21 @@ class parser { self.parse_item_impl() } else if self.eat_keyword("class") { self.parse_item_class() + } else if !self.is_any_keyword(copy self.token) + && self.look_ahead(1) == token::NOT + { + // item macro. + let pth = self.parse_path_without_tps(); + #error("parsing invocation of %s", *pth.idents[0]); + self.expect(token::NOT); + let id = self.parse_ident(); + let tt = self.parse_token_tree(); + let m = ast::mac_invoc_tt(pth, tt); + let m: ast::mac = {node: m, + span: {lo: self.span.lo, + hi: self.span.hi, + expn_info: none}}; + (id, item_mac(m), none) } else { ret none; }; some(self.mk_item(lo, self.last_span.hi, ident, item_, vis, alt extra_attrs { diff --git a/src/libsyntax/parse/token.rs b/src/libsyntax/parse/token.rs index 1fc8b10f38573..b3fad7e9ffd94 100644 --- a/src/libsyntax/parse/token.rs +++ b/src/libsyntax/parse/token.rs @@ -96,6 +96,10 @@ enum whole_nt { w_ty( @ast::ty), w_ident(str_num, bool), w_path(@ast::path), + // TODO: this seems to cause infinite recursion in + // type_structually_contains if it's not an @-box. We should at least get + // failure instead. + w_tt(@ast::token_tree), } fn binop_to_str(o: binop) -> str { @@ -189,6 +193,7 @@ fn to_str(in: interner<@str>, t: token) -> str { w_stmt(*) { "statement" } w_pat(*) { "pattern" } w_expr(*) { "expression" } w_ty(*) { "type" } w_ident(*) { "identifier" } w_path(*) { "path" } + w_tt(*) { "tt" } } } } @@ -299,8 +304,7 @@ fn contextual_keyword_table() -> hashmap { fn restricted_keyword_table() -> hashmap { let words = str_hash(); let keys = ~[ - "alt", - "assert", + "alt", "again", "assert", "break", "check", "claim", "class", "const", "cont", "copy", "do", "drop", diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index 8e85de17613c4..4e6b47db1b2ed 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -341,7 +341,7 @@ fn print_type_ex(s: ps, &&ty: @ast::ty, print_colons: bool) { print_type(s, mt.ty); word(s.s, "]"); } - ast::ty_ptr(mt) { word(s.s, "*"); print_mt(s, mt); } + ast::ty_ptr(mt) { word(s.s, "^"); print_mt(s, mt); } ast::ty_rptr(region, mt) { alt region.node { ast::re_anon { word(s.s, "&"); } @@ -589,6 +589,9 @@ fn print_item(s: ps, &&item: @ast::item) { for methods.each |meth| { print_ty_method(s, meth); } bclose(s, item.span); } + ast::item_mac(_m) { + fail "item macros unimplemented" + } } s.ann.post(ann_node); } @@ -1060,7 +1063,7 @@ fn print_expr(s: ps, &&expr: @ast::expr) { } } ast::expr_break { word(s.s, "break"); } - ast::expr_cont { word(s.s, "cont"); } + ast::expr_again { word(s.s, "again"); } ast::expr_ret(result) { word(s.s, "ret"); alt result { diff --git a/src/libsyntax/syntax.rc b/src/libsyntax/syntax.rc index 9cf5528e1a34e..7c453cc96e453 100644 --- a/src/libsyntax/syntax.rc +++ b/src/libsyntax/syntax.rc @@ -78,4 +78,10 @@ mod ext { mod log_syntax; mod auto_serialize; mod source_util; + + mod pipes { + mod ast_builder; + mod parse_proto; + mod pipec; + } } diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs index 25c61535fcf8b..0a7e757bbba89 100644 --- a/src/libsyntax/visit.rs +++ b/src/libsyntax/visit.rs @@ -163,6 +163,7 @@ fn visit_item(i: @item, e: E, v: vt) { v.visit_ty(m.decl.output, e, v); } } + item_mac(_m) { fail "item macros unimplemented" } } } @@ -420,7 +421,7 @@ fn visit_expr(ex: @expr, e: E, v: vt) { expr_path(p) { visit_path(p, e, v); } expr_fail(eo) { visit_expr_opt(eo, e, v); } expr_break { } - expr_cont { } + expr_again { } expr_ret(eo) { visit_expr_opt(eo, e, v); } expr_log(_, lv, x) { v.visit_expr(lv, e, v); diff --git a/src/rt/rust_builtin.cpp b/src/rt/rust_builtin.cpp index a33d3cb90fedb..732dbaa329311 100644 --- a/src/rt/rust_builtin.cpp +++ b/src/rt/rust_builtin.cpp @@ -922,6 +922,26 @@ rust_task_local_data_atexit(rust_task *task, void (*cleanup_fn)(void *data)) { task->task_local_data_cleanup = cleanup_fn; } +extern "C" void +task_clear_event_reject(rust_task *task) { + task->clear_event_reject(); +} + +// Waits on an event, returning the pointer to the event that unblocked this +// task. +extern "C" void * +task_wait_event(rust_task *task) { + // TODO: we should assert that the passed in task is the currently running + // task. We wouldn't want to wait some other task. + + return task->wait_event(); +} + +extern "C" void +task_signal_event(rust_task *target, void *event) { + target->signal_event(event); +} + // // Local Variables: // mode: C++ diff --git a/src/rt/rust_task.cpp b/src/rt/rust_task.cpp index d7d43bcd27d2b..6a9f5cf50012f 100644 --- a/src/rt/rust_task.cpp +++ b/src/rt/rust_task.cpp @@ -36,6 +36,8 @@ rust_task::rust_task(rust_sched_loop *sched_loop, rust_task_state state, state(state), cond(NULL), cond_name("none"), + event_reject(false), + event(NULL), killed(false), reentered_rust_stack(false), disallow_kill(0), @@ -407,13 +409,20 @@ rust_task::free(void *p) void rust_task::transition(rust_task_state src, rust_task_state dst, rust_cond *cond, const char* cond_name) { + scoped_lock with(state_lock); + transition_locked(src, dst, cond, cond_name); +} + +void rust_task::transition_locked(rust_task_state src, rust_task_state dst, + rust_cond *cond, const char* cond_name) { + state_lock.must_have_lock(); sched_loop->transition(this, src, dst, cond, cond_name); } void rust_task::set_state(rust_task_state state, rust_cond *cond, const char* cond_name) { - scoped_lock with(state_lock); + state_lock.must_have_lock(); this->state = state; this->cond = cond; this->cond_name = cond_name; @@ -422,7 +431,11 @@ rust_task::set_state(rust_task_state state, bool rust_task::block(rust_cond *on, const char* name) { scoped_lock with(kill_lock); + return block_locked(on, name); +} +bool +rust_task::block_locked(rust_cond *on, const char* name) { if (must_fail_from_being_killed_unlocked()) { // We're already going to die. Don't block. Tell the task to fail return false; @@ -433,19 +446,25 @@ rust_task::block(rust_cond *on, const char* name) { assert(cond == NULL && "Cannot block an already blocked task."); assert(on != NULL && "Cannot block on a NULL object."); - transition(task_state_running, task_state_blocked, on, name); + transition_locked(task_state_running, task_state_blocked, on, name); return true; } void rust_task::wakeup(rust_cond *from) { + scoped_lock with(state_lock); + wakeup_locked(from); +} + +void +rust_task::wakeup_locked(rust_cond *from) { assert(cond != NULL && "Cannot wake up unblocked task."); LOG(this, task, "Blocked on 0x%" PRIxPTR " woken up on 0x%" PRIxPTR, (uintptr_t) cond, (uintptr_t) from); assert(cond == from && "Cannot wake up blocked task on wrong condition."); - transition(task_state_blocked, task_state_running, NULL, "none"); + transition_locked(task_state_blocked, task_state_running, NULL, "none"); } void @@ -693,6 +712,34 @@ rust_task::allow_kill() { disallow_kill--; } +void * +rust_task::wait_event() { + scoped_lock with(state_lock); + + if(!event_reject) { + block_locked(&event_cond, "waiting on event"); + bool killed = false; + state_lock.unlock(); + yield(&killed); + state_lock.lock(); + // TODO: what is the right thing to do if we are killed? + } + + event_reject = false; + return event; +} + +void +rust_task::signal_event(void *event) { + scoped_lock with(state_lock); + + this->event = event; + event_reject = true; + if(task_state_blocked == state) { + wakeup_locked(&event_cond); + } +} + // // Local Variables: // mode: C++ diff --git a/src/rt/rust_task.h b/src/rt/rust_task.h index 96b0bddd4e06f..c5bdc50e43084 100644 --- a/src/rt/rust_task.h +++ b/src/rt/rust_task.h @@ -175,6 +175,10 @@ rust_task : public kernel_owned, rust_cond rust_cond *cond; const char *cond_name; + bool event_reject; + rust_cond event_cond; + void *event; + // Protects the killed flag, disallow_kill flag, reentered_rust_stack lock_and_signal kill_lock; // Indicates that the task was killed and needs to unwind @@ -205,6 +209,8 @@ rust_task : public kernel_owned, rust_cond void transition(rust_task_state src, rust_task_state dst, rust_cond *cond, const char* cond_name); + void transition_locked(rust_task_state src, rust_task_state dst, + rust_cond *cond, const char* cond_name); bool must_fail_from_being_killed_unlocked(); // Called by rust_task_fail to unwind on failure @@ -221,6 +227,9 @@ rust_task : public kernel_owned, rust_cond char const *file, size_t line); + bool block_locked(rust_cond *on, const char* name); + void wakeup_locked(rust_cond *from); + public: // Only a pointer to 'name' is kept, so it must live as long as this task. @@ -303,6 +312,13 @@ rust_task : public kernel_owned, rust_cond rust_cond *get_cond() { return cond; } const char *get_cond_name() { return cond_name; } + void clear_event_reject() { + this->event_reject = false; + } + + void *wait_event(); + void signal_event(void *event); + void cleanup_after_turn(); void inhibit_kill(); diff --git a/src/rt/rustrt.def.in b/src/rt/rustrt.def.in index a218782dcbe63..e674d6fa19774 100644 --- a/src/rt/rustrt.def.in +++ b/src/rt/rustrt.def.in @@ -64,6 +64,9 @@ start_task vec_reserve_shared str_reserve_shared vec_from_buf_shared +task_clear_event_reject +task_wait_event +task_signal_event unsupervise upcall_cmp_type upcall_fail diff --git a/src/rustc/metadata/encoder.rs b/src/rustc/metadata/encoder.rs index 8da5ac9420fe1..46a3cd84be3ba 100644 --- a/src/rustc/metadata/encoder.rs +++ b/src/rustc/metadata/encoder.rs @@ -217,6 +217,7 @@ fn encode_module_item_paths(ebml_w: ebml::writer, ecx: @encode_ctxt, } } item_impl(*) {} + item_mac(*) { fail "item macros unimplemented" } } } } @@ -749,6 +750,7 @@ fn encode_info_for_item(ecx: @encode_ctxt, ebml_w: ebml::writer, item: @item, encode_path(ebml_w, path, ast_map::path_name(item.ident)); ebml_w.end_tag(); } + item_mac(*) { fail "item macros unimplemented" } } } diff --git a/src/rustc/middle/borrowck.rs b/src/rustc/middle/borrowck.rs index 38559aec28dd2..ec2002f8f6b18 100644 --- a/src/rustc/middle/borrowck.rs +++ b/src/rustc/middle/borrowck.rs @@ -467,7 +467,9 @@ impl to_str_methods for borrowck_ctxt { cat_special(sk_method) { "method" } cat_special(sk_static_item) { "static item" } cat_special(sk_self) { "self reference" } - cat_special(sk_heap_upvar) { "variable declared in an outer block" } + cat_special(sk_heap_upvar) { + "captured outer variable from within a heap closure" + } cat_rvalue { "non-lvalue" } cat_local(_) { mut_str + " local variable" } cat_binding(_) { "pattern binding" } @@ -475,7 +477,7 @@ impl to_str_methods for borrowck_ctxt { cat_deref(_, _, pk) { #fmt["dereference of %s %s pointer", mut_str, self.pk_to_sigil(pk)] } cat_stack_upvar(_) { - mut_str + " variable declared in an outer block" + "captured " + mut_str + " variable from within a stack closure" } cat_comp(_, comp_field(*)) { mut_str + " field" } cat_comp(_, comp_tuple) { "tuple content" } diff --git a/src/rustc/middle/borrowck/categorization.rs b/src/rustc/middle/borrowck/categorization.rs index deccf0af2b46d..1a2b4d7834eca 100644 --- a/src/rustc/middle/borrowck/categorization.rs +++ b/src/rustc/middle/borrowck/categorization.rs @@ -176,7 +176,7 @@ impl public_methods for borrowck_ctxt { ast::expr_new(*) | ast::expr_binary(*) | ast::expr_while(*) | ast::expr_block(*) | ast::expr_loop(*) | ast::expr_alt(*) | ast::expr_lit(*) | ast::expr_break | ast::expr_mac(*) | - ast::expr_cont | ast::expr_rec(*) { + ast::expr_again | ast::expr_rec(*) { ret self.cat_rvalue(expr, expr_ty); } } diff --git a/src/rustc/middle/check_loop.rs b/src/rustc/middle/check_loop.rs index 44fbdaef7ce40..e8e2c57a8b227 100644 --- a/src/rustc/middle/check_loop.rs +++ b/src/rustc/middle/check_loop.rs @@ -33,7 +33,7 @@ fn check_crate(tcx: ty::ctxt, crate: @crate) { tcx.sess.span_err(e.span, "`break` outside of loop"); } } - expr_cont { + expr_again { if !cx.in_loop { tcx.sess.span_err(e.span, "`cont` outside of loop"); } diff --git a/src/rustc/middle/liveness.rs b/src/rustc/middle/liveness.rs index 2f87f6d55de79..50ab6b4b627bf 100644 --- a/src/rustc/middle/liveness.rs +++ b/src/rustc/middle/liveness.rs @@ -470,7 +470,7 @@ fn visit_expr(expr: @expr, &&self: @ir_maps, vt: vt<@ir_maps>) { expr_assert(*) | expr_check(*) | expr_addr_of(*) | expr_copy(*) | expr_loop_body(*) | expr_do_body(*) | expr_cast(*) | expr_unary(*) | expr_fail(*) | - expr_break | expr_cont | expr_lit(_) | expr_ret(*) | + expr_break | expr_again | expr_lit(_) | expr_ret(*) | expr_block(*) | expr_move(*) | expr_assign(*) | expr_swap(*) | expr_assign_op(*) | expr_mac(*) { visit::visit_expr(expr, self, vt); @@ -1009,7 +1009,7 @@ class liveness { self.break_ln } - expr_cont { + expr_again { if !self.cont_ln.is_valid() { self.tcx.sess.span_bug( expr.span, "cont with invalid cont_ln"); @@ -1457,7 +1457,7 @@ fn check_expr(expr: @expr, &&self: @liveness, vt: vt<@liveness>) { expr_assert(*) | expr_check(*) | expr_copy(*) | expr_loop_body(*) | expr_do_body(*) | expr_cast(*) | expr_unary(*) | expr_fail(*) | - expr_ret(*) | expr_break | expr_cont | expr_lit(_) | + expr_ret(*) | expr_break | expr_again | expr_lit(_) | expr_block(*) | expr_swap(*) | expr_mac(*) | expr_addr_of(*) { visit::visit_expr(expr, self, vt); } diff --git a/src/rustc/middle/resolve.rs b/src/rustc/middle/resolve.rs index 51c48f6a64c04..d8b1f4db64010 100644 --- a/src/rustc/middle/resolve.rs +++ b/src/rustc/middle/resolve.rs @@ -1352,6 +1352,7 @@ fn found_def_item(i: @ast::item, ns: namespace) -> option { } } ast::item_impl(*) { /* ??? */ } + ast::item_mac(*) { fail "item macros unimplemented" } } ret none; } @@ -1658,6 +1659,9 @@ fn index_mod(md: ast::_mod) -> mod_index { // add the class name itself add_to_index(index, it.ident, mie_item(it)); } + ast::item_mac(*) { + fail "item macros unimplemented" + } } } ret index; diff --git a/src/rustc/middle/resolve3.rs b/src/rustc/middle/resolve3.rs index b7dc314c801c1..6ca613d528103 100644 --- a/src/rustc/middle/resolve3.rs +++ b/src/rustc/middle/resolve3.rs @@ -13,7 +13,7 @@ import syntax::ast::{expr_binary, expr_cast, expr_field, expr_fn}; import syntax::ast::{expr_fn_block, expr_index, expr_new, expr_path}; import syntax::ast::{expr_unary, fn_decl, foreign_item, foreign_item_fn}; import syntax::ast::{ident, trait_ref, impure_fn, instance_var, item}; -import syntax::ast::{item_class, item_const, item_enum, item_fn}; +import syntax::ast::{item_class, item_const, item_enum, item_fn, item_mac}; import syntax::ast::{item_foreign_mod, item_trait, item_impl, item_mod}; import syntax::ast::{item_ty, local, local_crate, method, node_id, pat}; import syntax::ast::{pat_enum, pat_ident, path, prim_ty, stmt_decl, ty}; @@ -871,6 +871,10 @@ class Resolver { (*name_bindings).define_type(def_ty(local_def(item.id))); visit_item(item, new_parent, visitor); } + + item_mac(*) { + fail "item macros unimplemented" + } } } @@ -2854,6 +2858,10 @@ class Resolver { item_const(*) { visit_item(item, (), visitor); } + + item_mac(*) { + fail "item macros unimplemented" + } } self.xray_context = orig_xray_flag; diff --git a/src/rustc/middle/trans/base.rs b/src/rustc/middle/trans/base.rs index cff52a8c1ddd1..459b14038450e 100644 --- a/src/rustc/middle/trans/base.rs +++ b/src/rustc/middle/trans/base.rs @@ -1660,9 +1660,6 @@ fn trans_eager_binop(cx: block, span: span, op: ast::binop, lhs: ValueRef, let rhs = cast_shift_expr_rhs(cx, op, lhs, rhs); - if op == ast::add && ty::type_is_sequence(intype) { - ret tvec::trans_add(cx, intype, lhs, rhs, dest); - } let mut cx = cx; let val = alt op { ast::add { @@ -1758,28 +1755,7 @@ fn trans_assign_op(bcx: block, ex: @ast::expr, op: ast::binop, _ {} } - // Special case for `+= ~[x]` - alt ty::get(t).struct { - ty::ty_vec(_) { - alt src.node { - ast::expr_vec(args, _) { - ret tvec::trans_append_literal(lhs_res.bcx, - lhs_res.val, t, args); - } - _ { } - } - } - _ { } - } let {bcx, val: rhs_val} = trans_temp_expr(lhs_res.bcx, src); - if ty::type_is_sequence(t) { - alt op { - ast::add { - ret tvec::trans_append(bcx, t, lhs_res.val, rhs_val); - } - _ { } - } - } ret trans_eager_binop(bcx, ex.span, op, Load(bcx, lhs_res.val), t, rhs_val, t, save_in(lhs_res.val)); @@ -3668,7 +3644,7 @@ fn trans_expr(bcx: block, e: @ast::expr, dest: dest) -> block { assert dest == ignore; ret trans_break(bcx); } - ast::expr_cont { + ast::expr_again { assert dest == ignore; ret trans_cont(bcx); } diff --git a/src/rustc/middle/trans/reachable.rs b/src/rustc/middle/trans/reachable.rs index edf76f4230396..f9b031f1a51df 100644 --- a/src/rustc/middle/trans/reachable.rs +++ b/src/rustc/middle/trans/reachable.rs @@ -148,6 +148,7 @@ fn traverse_public_item(cx: ctx, item: @item) { } item_const(*) | item_enum(*) | item_trait(*) {} + item_mac(*) { fail "item macros unimplemented" } } } diff --git a/src/rustc/middle/trans/tvec.rs b/src/rustc/middle/trans/tvec.rs index a15bb2938139b..f7725cdb0771d 100644 --- a/src/rustc/middle/trans/tvec.rs +++ b/src/rustc/middle/trans/tvec.rs @@ -306,117 +306,6 @@ fn trans_estr(bcx: block, s: @str, vstore: ast::vstore, base::store_in_dest(bcx, c, dest) } -fn trans_append(bcx: block, vec_ty: ty::t, lhsptr: ValueRef, - rhs: ValueRef) -> block { - let _icx = bcx.insn_ctxt("tvec::trans_append"); - // Cast to opaque interior vector types if necessary. - let ccx = bcx.ccx(); - let unit_ty = ty::sequence_element_type(ccx.tcx, vec_ty); - let strings = ty::type_is_str(vec_ty); - - let lhs = Load(bcx, lhsptr); - let self_append = ICmp(bcx, lib::llvm::IntEQ, lhs, rhs); - let lfill = get_fill(bcx, get_bodyptr(bcx, lhs)); - let rfill = get_fill(bcx, get_bodyptr(bcx, rhs)); - let mut new_fill = Add(bcx, lfill, rfill); - if strings { new_fill = Sub(bcx, new_fill, C_int(ccx, 1)); } - let opaque_lhs = PointerCast(bcx, lhsptr, - T_ptr(T_ptr(T_i8()))); - Call(bcx, ccx.upcalls.vec_grow, - ~[opaque_lhs, new_fill]); - // Was overwritten if we resized - let lhs = Load(bcx, lhsptr); - let rhs = Select(bcx, self_append, lhs, rhs); - - let lbody = get_bodyptr(bcx, lhs); - - let lhs_data = get_dataptr(bcx, lbody); - let mut lhs_off = lfill; - if strings { lhs_off = Sub(bcx, lhs_off, C_int(ccx, 1)); } - let write_ptr = pointer_add(bcx, lhs_data, lhs_off); - let write_ptr_ptr = do_spill_noroot(bcx, write_ptr); - iter_vec_uniq(bcx, rhs, vec_ty, rfill, |bcx, addr, _ty| { - let write_ptr = Load(bcx, write_ptr_ptr); - let bcx = copy_val(bcx, INIT, write_ptr, - load_if_immediate(bcx, addr, unit_ty), unit_ty); - Store(bcx, InBoundsGEP(bcx, write_ptr, ~[C_int(ccx, 1)]), - write_ptr_ptr); - bcx - }) -} - -fn trans_append_literal(bcx: block, vptrptr: ValueRef, vec_ty: ty::t, - vals: ~[@ast::expr]) -> block { - let _icx = bcx.insn_ctxt("tvec::trans_append_literal"); - let mut bcx = bcx, ccx = bcx.ccx(); - let elt_ty = ty::sequence_element_type(bcx.tcx(), vec_ty); - let elt_llty = type_of::type_of(ccx, elt_ty); - let elt_sz = shape::llsize_of(ccx, elt_llty); - let scratch = base::alloca(bcx, elt_llty); - for vec::each(vals) |val| { - bcx = base::trans_expr_save_in(bcx, val, scratch); - let vptr = get_bodyptr(bcx, Load(bcx, vptrptr)); - let old_fill = get_fill(bcx, vptr); - let new_fill = Add(bcx, old_fill, elt_sz); - let do_grow = ICmp(bcx, lib::llvm::IntUGT, new_fill, - get_alloc(bcx, vptr)); - bcx = do base::with_cond(bcx, do_grow) |bcx| { - let pt = PointerCast(bcx, vptrptr, - T_ptr(T_ptr(T_i8()))); - Call(bcx, ccx.upcalls.vec_grow, ~[pt, new_fill]); - bcx - }; - let vptr = get_bodyptr(bcx, Load(bcx, vptrptr)); - set_fill(bcx, vptr, new_fill); - let targetptr = pointer_add(bcx, get_dataptr(bcx, vptr), - old_fill); - call_memmove(bcx, targetptr, scratch, elt_sz); - } - bcx -} - -fn trans_add(bcx: block, vec_ty: ty::t, lhs: ValueRef, - rhs: ValueRef, dest: dest) -> block { - let _icx = bcx.insn_ctxt("tvec::trans_add"); - let ccx = bcx.ccx(); - - let unit_ty = ty::sequence_element_type(bcx.tcx(), vec_ty); - let llunitty = type_of::type_of(ccx, unit_ty); - - if ty::get(vec_ty).struct == ty::ty_str { - let lhs = PointerCast(bcx, lhs, T_ptr(T_i8())); - let rhs = PointerCast(bcx, rhs, T_ptr(T_i8())); - let n = Call(bcx, ccx.upcalls.str_concat, ~[lhs, rhs]); - let n = PointerCast( - bcx, n, T_unique_ptr(T_unique(ccx, T_vec(ccx, llunitty)))); - ret base::store_in_dest(bcx, n, dest); - } - - let lhs_fill = get_fill(bcx, get_bodyptr(bcx, lhs)); - let rhs_fill = get_fill(bcx, get_bodyptr(bcx, rhs)); - let new_fill = Add(bcx, lhs_fill, rhs_fill); - let mut {bcx: bcx, val: new_vec_ptr} = - alloc_uniq_raw(bcx, unit_ty, new_fill, new_fill); - - let new_vec_body_ptr = get_bodyptr(bcx, new_vec_ptr); - let write_ptr_ptr = do_spill_noroot - (bcx, get_dataptr(bcx, new_vec_body_ptr)); - let copy_fn = fn@(bcx: block, addr: ValueRef, - _ty: ty::t) -> block { - let ccx = bcx.ccx(); - let write_ptr = Load(bcx, write_ptr_ptr); - let bcx = copy_val(bcx, INIT, write_ptr, - load_if_immediate(bcx, addr, unit_ty), unit_ty); - Store(bcx, InBoundsGEP(bcx, write_ptr, ~[C_int(ccx, 1)]), - write_ptr_ptr); - ret bcx; - }; - - let bcx = iter_vec_uniq(bcx, lhs, vec_ty, lhs_fill, copy_fn); - let bcx = iter_vec_uniq(bcx, rhs, vec_ty, rhs_fill, copy_fn); - ret base::store_in_dest(bcx, new_vec_ptr, dest); -} - type val_and_ty_fn = fn@(block, ValueRef, ty::t) -> result; type iter_vec_block = fn(block, ValueRef, ty::t) -> block; diff --git a/src/rustc/middle/trans/type_use.rs b/src/rustc/middle/trans/type_use.rs index c8ff5ef4c1649..386bf06ef1434 100644 --- a/src/rustc/middle/trans/type_use.rs +++ b/src/rustc/middle/trans/type_use.rs @@ -242,7 +242,7 @@ fn mark_for_expr(cx: ctx, e: @expr) { }) } expr_alt(_, _, _) | expr_block(_) | expr_if(_, _, _) | - expr_while(_, _) | expr_fail(_) | expr_break | expr_cont | + expr_while(_, _) | expr_fail(_) | expr_break | expr_again | expr_unary(_, _) | expr_lit(_) | expr_assert(_) | expr_check(_, _) | expr_if_check(_, _, _) | expr_mac(_) | expr_addr_of(_, _) | expr_ret(_) | expr_loop(_) | diff --git a/src/rustc/middle/tstate/pre_post_conditions.rs b/src/rustc/middle/tstate/pre_post_conditions.rs index 0561a04544a6e..c2c8810ec1bb1 100644 --- a/src/rustc/middle/tstate/pre_post_conditions.rs +++ b/src/rustc/middle/tstate/pre_post_conditions.rs @@ -55,6 +55,7 @@ fn find_pre_post_item(ccx: crate_ctxt, i: item) { item_impl(_, _, _, _, ms) { for ms.each |m| { find_pre_post_method(ccx, m); } } + item_mac(*) { fail "item macros unimplemented" } } } @@ -445,7 +446,7 @@ fn find_pre_post_expr(fcx: fn_ctxt, e: @expr) { join_then_else(fcx, p, conseq, maybe_alt, e.id, if_check); } expr_break { clear_pp(expr_pp(fcx.ccx, e)); } - expr_cont { clear_pp(expr_pp(fcx.ccx, e)); } + expr_again { clear_pp(expr_pp(fcx.ccx, e)); } expr_mac(_) { fcx.ccx.tcx.sess.bug("unexpanded macro"); } } } diff --git a/src/rustc/middle/tstate/states.rs b/src/rustc/middle/tstate/states.rs index adf1efc85626f..f6cf240eeca34 100644 --- a/src/rustc/middle/tstate/states.rs +++ b/src/rustc/middle/tstate/states.rs @@ -498,7 +498,7 @@ fn find_pre_post_state_expr(fcx: fn_ctxt, pres: prestate, e: @expr) -> bool { ret join_then_else(fcx, p, conseq, maybe_alt, e.id, if_check, pres); } expr_break { ret pure_exp(fcx.ccx, e.id, pres); } - expr_cont { ret pure_exp(fcx.ccx, e.id, pres); } + expr_again { ret pure_exp(fcx.ccx, e.id, pres); } } } diff --git a/src/rustc/middle/ty.rs b/src/rustc/middle/ty.rs index 6580feab3301c..fba8d54cfba01 100644 --- a/src/rustc/middle/ty.rs +++ b/src/rustc/middle/ty.rs @@ -2963,8 +2963,8 @@ fn is_binopable(_cx: ctxt, ty: t, op: ast::binop) -> bool { /*bool*/ ~[f, f, f, f, t, t, t, t], /*int*/ ~[t, t, t, t, t, t, t, f], /*float*/ ~[t, t, t, f, t, t, f, f], - /*str*/ ~[t, f, f, f, t, t, f, f], - /*vec*/ ~[t, f, f, f, t, t, f, f], + /*str*/ ~[f, f, f, f, t, t, f, f], + /*vec*/ ~[f, f, f, f, t, t, f, f], /*bot*/ ~[f, f, f, f, t, t, f, f], /*struct*/ ~[t, t, t, t, t, t, t, t]]; diff --git a/src/rustc/middle/typeck/check.rs b/src/rustc/middle/typeck/check.rs index 647498a97e1d3..a983240489ac8 100644 --- a/src/rustc/middle/typeck/check.rs +++ b/src/rustc/middle/typeck/check.rs @@ -1220,7 +1220,7 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, fcx.write_bot(id); } ast::expr_break { fcx.write_bot(id); bot = true; } - ast::expr_cont { fcx.write_bot(id); bot = true; } + ast::expr_again { fcx.write_bot(id); bot = true; } ast::expr_ret(expr_opt) { bot = true; let ret_ty = alt fcx.indirect_ret_ty { diff --git a/src/rustc/middle/typeck/check/method.rs b/src/rustc/middle/typeck/check/method.rs index 709db8733d584..b91f9778e1c84 100644 --- a/src/rustc/middle/typeck/check/method.rs +++ b/src/rustc/middle/typeck/check/method.rs @@ -77,16 +77,20 @@ class lookup { // loop for impls in scope. Note: I don't love these // semantics, but that's what we had so I am preserving // it. - if self.candidates.len() > 0u { - break; - } + if self.candidates.len() > 0u { break; } + + // now look for impls in scope, but don't look for impls that + // would require doing an implicit borrow of the lhs. + self.add_candidates_from_scope(false); - self.add_candidates_from_scope(); + // if we found anything, stop before trying borrows + if self.candidates.len() > 0u { break; } + + // now look for impls in scope that might require a borrow + self.add_candidates_from_scope(true); // if we found anything, stop before attempting auto-deref. - if self.candidates.len() > 0u { - break; - } + if self.candidates.len() > 0u { break; } // check whether we can autoderef and if so loop around again. alt ty::deref(self.tcx(), self.self_ty, false) { @@ -290,7 +294,7 @@ class lookup { */ } - fn add_candidates_from_scope() { + fn add_candidates_from_scope(use_assignability: bool) { let impls_vecs = self.fcx.ccx.impl_map.get(self.expr.id); let mut added_any = false; @@ -306,13 +310,18 @@ class lookup { let {substs: impl_substs, ty: impl_ty} = impl_self_ty(self.fcx, im.did); - // if we can assign the caller to the callee, that's a - // potential match. Collect those in the vector. - let can_assign = self.fcx.can_mk_assignty( - self.self_expr, self.borrow_scope, - self.self_ty, impl_ty); - #debug["can_assign = %?", can_assign]; - alt can_assign { + // Depending on our argument, we find potential + // matches either by checking subtypability or + // type assignability. Collect the matches. + let matches = if use_assignability { + self.fcx.can_mk_assignty( + self.self_expr, self.borrow_scope, + self.self_ty, impl_ty) + } else { + self.fcx.can_mk_subty(self.self_ty, impl_ty) + }; + #debug["matches = %?", matches]; + alt matches { result::err(_) { /* keep looking */ } result::ok(_) { if !self.candidate_impls.contains_key(im.did) { diff --git a/src/rustc/middle/typeck/collect.rs b/src/rustc/middle/typeck/collect.rs index 84ed98be6f4b2..4bae90d22144b 100644 --- a/src/rustc/middle/typeck/collect.rs +++ b/src/rustc/middle/typeck/collect.rs @@ -537,6 +537,7 @@ fn ty_of_item(ccx: @crate_ctxt, it: @ast::item) } ast::item_impl(*) | ast::item_mod(_) | ast::item_foreign_mod(_) { fail; } + ast::item_mac(*) { fail "item macros unimplemented" } } } diff --git a/src/rustc/util/common.rs b/src/rustc/util/common.rs index 6a594879d1d41..26b3077210511 100644 --- a/src/rustc/util/common.rs +++ b/src/rustc/util/common.rs @@ -57,7 +57,7 @@ fn loop_query(b: ast::blk, p: fn@(ast::expr_) -> bool) -> bool { fn has_nonlocal_exits(b: ast::blk) -> bool { do loop_query(b) |e| { alt e { - ast::expr_break | ast::expr_cont { true } + ast::expr_break | ast::expr_again { true } _ { false }}} } diff --git a/src/rustc/util/ppaux.rs b/src/rustc/util/ppaux.rs index c8238d6b05704..86a2eb65b90bb 100644 --- a/src/rustc/util/ppaux.rs +++ b/src/rustc/util/ppaux.rs @@ -52,6 +52,10 @@ fn re_scope_id_to_str(cx: ctxt, node_id: ast::node_id) -> str { ast_map::node_id_to_str(cx.items, node_id)]) } } } + none { + // FIXME(#2586) + #fmt["", node_id] + } _ { cx.sess.bug( #fmt["re_scope refers to %s", ast_map::node_id_to_str(cx.items, node_id)]) } diff --git a/src/test/bench/msgsend-ring-new.rs b/src/test/bench/msgsend-ring-new.rs deleted file mode 100644 index 93f78df7d7e27..0000000000000 --- a/src/test/bench/msgsend-ring-new.rs +++ /dev/null @@ -1,77 +0,0 @@ -// This test creates a bunch of tasks that simultaneously send to each -// other in a ring. The messages should all be basically -// independent. It's designed to hammer the global kernel lock, so -// that things will look really good once we get that lock out of the -// message path. - -import newcomm::*; -import future::future; -import future::extensions; - -use std; -import std::time; - -fn thread_ring(i: uint, - count: uint, - num_chan: chan, - num_port: port) { - // Send/Receive lots of messages. - for uint::range(0u, count) |j| { - num_chan.send(i * j); - num_port.recv(); - }; -} - -fn main(args: ~[str]) { - let args = if os::getenv("RUST_BENCH").is_some() { - ~["", "100", "10000"] - } else if args.len() <= 1u { - ~["", "100", "1000"] - } else { - args - }; - - let num_tasks = option::get(uint::from_str(args[1])); - let msg_per_task = option::get(uint::from_str(args[2])); - - let num_port = port(); - let mut num_chan = chan(num_port); - - let start = time::precise_time_s(); - - // create the ring - let mut futures = ~[]; - - for uint::range(1u, num_tasks) |i| { - let get_chan = port(); - let get_chan_chan = chan(get_chan); - { - let num_chan = num_chan.clone(); - futures += ~[do future::spawn |move num_chan, move get_chan_chan| { - let p = port(); - get_chan_chan.send(chan(p)); - thread_ring(i, msg_per_task, num_chan, p) - }]; - } - - num_chan = get_chan.recv(); - }; - - // do our iteration - thread_ring(0u, msg_per_task, num_chan, num_port); - - // synchronize - for futures.each |f| { f.get() }; - - let stop = time::precise_time_s(); - - // all done, report stats. - let num_msgs = num_tasks * msg_per_task; - let elapsed = (stop - start); - let rate = (num_msgs as float) / elapsed; - - io::println(#fmt("Sent %? messages in %? seconds", - num_msgs, elapsed)); - io::println(#fmt(" %? messages / second", rate)); - io::println(#fmt(" %? μs / message", 1000000. / rate)); -} diff --git a/src/test/bench/msgsend-ring-pipes.rs b/src/test/bench/msgsend-ring-pipes.rs new file mode 100644 index 0000000000000..2eb630d642746 --- /dev/null +++ b/src/test/bench/msgsend-ring-pipes.rs @@ -0,0 +1,110 @@ +// This test creates a bunch of tasks that simultaneously send to each +// other in a ring. The messages should all be basically +// independent. It's designed to hammer the global kernel lock, so +// that things will look really good once we get that lock out of the +// message path. + +// This version uses automatically compiled channel contracts. + +// xfail-pretty + +import future::future; + +use std; +import std::time; + +import pipes::recv; + +proto! ring { + num:send { + num(uint) -> num + } +} + +fn macros() { + #macro[ + [#move[x], + unsafe { let y <- *ptr::addr_of(x); y }] + ]; +} + +fn thread_ring(i: uint, + count: uint, + +num_chan: ring::client::num, + +num_port: ring::server::num) { + let mut num_chan <- some(num_chan); + let mut num_port <- some(num_port); + // Send/Receive lots of messages. + for uint::range(0u, count) |j| { + //#error("task %?, iter %?", i, j); + let mut num_chan2 = none; + let mut num_port2 = none; + num_chan2 <-> num_chan; + num_port2 <-> num_port; + num_chan = some(ring::client::num(option::unwrap(num_chan2), i * j)); + let port = option::unwrap(num_port2); + alt (option::unwrap(recv(port))) { + ring::num(_n, p) { + //log(error, _n); + num_port = some(#move(p)); + } + } + }; +} + +fn main(args: [str]/~) { + let args = if os::getenv("RUST_BENCH").is_some() { + ["", "100", "10000"]/~ + } else if args.len() <= 1u { + ["", "100", "1000"]/~ + } else { + copy args + }; + + let num_tasks = option::get(uint::from_str(args[1])); + let msg_per_task = option::get(uint::from_str(args[2])); + + let (num_chan, num_port) = ring::init(); + let mut num_chan = some(num_chan); + + let start = time::precise_time_s(); + + // create the ring + let mut futures = []/~; + + for uint::range(1u, num_tasks) |i| { + //#error("spawning %?", i); + let (new_chan, num_port) = ring::init(); + let num_chan2 = ~mut none; + *num_chan2 <-> num_chan; + let num_port = ~mut some(num_port); + futures += [future::spawn(|move num_chan2, move num_port| { + let mut num_chan = none; + num_chan <-> *num_chan2; + let mut num_port1 = none; + num_port1 <-> *num_port; + thread_ring(i, msg_per_task, + option::unwrap(num_chan), + option::unwrap(num_port1)) + })]/~; + num_chan = some(new_chan); + }; + + // do our iteration + thread_ring(0u, msg_per_task, option::unwrap(num_chan), num_port); + + // synchronize + for futures.each |f| { future::get(f) }; + + let stop = time::precise_time_s(); + + // all done, report stats. + let num_msgs = num_tasks * msg_per_task; + let elapsed = (stop - start); + let rate = (num_msgs as float) / elapsed; + + io::println(#fmt("Sent %? messages in %? seconds", + num_msgs, elapsed)); + io::println(#fmt(" %? messages / second", rate)); + io::println(#fmt(" %? μs / message", 1000000. / rate)); +} diff --git a/src/test/bench/shootout-chameneos-redux.rs b/src/test/bench/shootout-chameneos-redux.rs new file mode 100644 index 0000000000000..9b44f42ee3729 --- /dev/null +++ b/src/test/bench/shootout-chameneos-redux.rs @@ -0,0 +1,201 @@ +// chameneos + +import io::reader_util; + +use std; +import std::map; +import std::map::hashmap; +import std::sort; + +fn print_complements() { + let all = ~[Blue, Red, Yellow]; + for vec::each(all) |aa| { + for vec::each(all) |bb| { + io::println(show_color(aa) + " + " + show_color(bb) + + " -> " + show_color(transform(aa,bb))); + } + } +} + +enum color { Red, Yellow, Blue } + +type creature_info = { name: uint, color: color }; + +fn show_color(cc: color) -> str { + alt (cc) { + Red {"red"} + Yellow {"yellow"} + Blue {"blue"} + } +} + +fn show_color_list(set: ~[color]) -> str { + let mut out = ""; + for vec::eachi(set) |_ii, col| { + out += " "; + out += show_color(col); + } + ret out; +} + +fn show_digit(nn: uint) -> str { + alt (nn) { + 0 {"zero"} + 1 {"one"} + 2 {"two"} + 3 {"three"} + 4 {"four"} + 5 {"five"} + 6 {"six"} + 7 {"seven"} + 8 {"eight"} + 9 {"nine"} + _ {fail "expected digits from 0 to 9..."} + } +} + +fn show_number(nn: uint) -> str { + let mut out = ""; + let mut num = nn; + let mut dig; + + if num == 0 { out = show_digit(0) }; + + while num != 0 { + dig = num % 10; + num = num / 10; + out = show_digit(dig) + " " + out; + } + + ret out; +} + +fn transform(aa: color, bb: color) -> color { + alt (aa, bb) { + (Red, Red ) { Red } + (Red, Yellow) { Blue } + (Red, Blue ) { Yellow } + (Yellow, Red ) { Blue } + (Yellow, Yellow) { Yellow } + (Yellow, Blue ) { Red } + (Blue, Red ) { Yellow } + (Blue, Yellow) { Red } + (Blue, Blue ) { Blue } + } +} + +fn creature( + name: uint, + color: color, + from_rendezvous: comm::port>, + to_rendezvous: comm::chan, + to_rendezvous_log: comm::chan +) { + let mut color = color; + let mut creatures_met = 0; + let mut evil_clones_met = 0; + + loop { + // ask for a pairing + comm::send(to_rendezvous, {name: name, color: color}); + let resp = comm::recv(from_rendezvous); + + // log and change, or print and quit + alt resp { + option::some(other_creature) { + color = transform(color, other_creature.color); + + // track some statistics + creatures_met += 1; + if other_creature.name == name { + evil_clones_met += 1; + } + } + option::none { + // log creatures met and evil clones of self + let report = #fmt("%u", creatures_met) + " " + + show_number(evil_clones_met); + comm::send(to_rendezvous_log, report); + break; + } + } + } +} + +fn rendezvous(nn: uint, set: ~[color]) { + // these ports will allow us to hear from the creatures + let from_creatures: comm::port = comm::port(); + let from_creatures_log: comm::port = comm::port(); + + // these channels will be passed to the creatures so they can talk to us + let to_rendezvous = comm::chan(from_creatures); + let to_rendezvous_log = comm::chan(from_creatures_log); + + // these channels will allow us to talk to each creature by 'name'/index + let to_creature: ~[comm::chan>] = + vec::mapi(set, + fn@(ii: uint, col: color) -> comm::chan> { + // create each creature as a listener with a port, and + // give us a channel to talk to each + ret do task::spawn_listener |from_rendezvous| { + creature(ii, col, from_rendezvous, to_rendezvous, + to_rendezvous_log); + }; + } + ); + + let mut creatures_met = 0; + + // set up meetings... + for nn.times { + let fst_creature: creature_info = comm::recv(from_creatures); + let snd_creature: creature_info = comm::recv(from_creatures); + + creatures_met += 2; + + comm::send(to_creature[fst_creature.name], some(snd_creature)); + comm::send(to_creature[snd_creature.name], some(fst_creature)); + } + + // tell each creature to stop + for vec::eachi(to_creature) |_ii, to_one| { + comm::send(to_one, none); + } + + // save each creature's meeting stats + let mut report = ~[]; + for vec::each(to_creature) |_to_one| { + vec::push(report, comm::recv(from_creatures_log)); + } + + // print each color in the set + io::println(show_color_list(set)); + + // print each creature's stats + for vec::each(report) |rep| { + io::println(rep); + } + + // print the total number of creatures met + io::println(show_number(creatures_met)); +} + +fn main(args: ~[str]) { + let args = if os::getenv("RUST_BENCH").is_some() || args.len() <= 1u { + ~["", "600"] + } else { + args + }; + + let nn = uint::from_str(args[1]).get(); + + print_complements(); + io::println(""); + + rendezvous(nn, ~[Blue, Red, Yellow]); + io::println(""); + + rendezvous(nn, + ~[Blue, Red, Yellow, Red, Yellow, Blue, Red, Yellow, Red, Blue]); +} + diff --git a/src/test/bench/shootout-k-nucleotide-pipes.rs b/src/test/bench/shootout-k-nucleotide-pipes.rs new file mode 100644 index 0000000000000..7212bbc765b1a --- /dev/null +++ b/src/test/bench/shootout-k-nucleotide-pipes.rs @@ -0,0 +1,250 @@ +// xfail-pretty + +// multi tasking k-nucleotide + +import io::reader_util; + +use std; +import std::map; +import std::map::hashmap; +import std::sort; + +import stream::{stream, chan, port}; + +// After a snapshot, this should move into core, or std. +mod stream { + import option::unwrap; + + proto! streamp { + open:send { + data(T) -> open + } + } + + type chan = { mut endp: option> }; + type port = { mut endp: option> }; + + fn stream() -> (chan, port) { + let (c, s) = streamp::init(); + ({ mut endp: some(c) }, { mut endp: some(s) }) + } + + impl chan for chan { + fn send(+x: T) { + let mut endp = none; + endp <-> self.endp; + self.endp = some( + streamp::client::data(unwrap(endp), x)) + } + } + + impl port for port { + fn recv() -> T { + let mut endp = none; + endp <-> self.endp; + let streamp::data(x, endp) = unwrap( + pipes::recv(unwrap(endp))); + self.endp = some(endp); + x + } + } +} + +// given a map, print a sorted version of it +fn sort_and_fmt(mm: hashmap<~[u8], uint>, total: uint) -> str { + fn pct(xx: uint, yy: uint) -> float { + ret (xx as float) * 100f / (yy as float); + } + + fn le_by_val(kv0: (TT,UU), kv1: (TT,UU)) -> bool { + let (_, v0) = kv0; + let (_, v1) = kv1; + ret v0 >= v1; + } + + fn le_by_key(kv0: (TT,UU), kv1: (TT,UU)) -> bool { + let (k0, _) = kv0; + let (k1, _) = kv1; + ret k0 <= k1; + } + + // sort by key, then by value + fn sortKV(orig: ~[(TT,UU)]) -> ~[(TT,UU)] { + ret sort::merge_sort(le_by_val, sort::merge_sort(le_by_key, orig)); + } + + let mut pairs = ~[]; + + // map -> [(k,%)] + mm.each(fn&(key: ~[u8], val: uint) -> bool { + vec::push(pairs, (key, pct(val, total))); + ret true; + }); + + let pairs_sorted = sortKV(pairs); + + let mut buffer = ""; + + pairs_sorted.each(fn&(kv: (~[u8], float)) -> bool unsafe { + let (k,v) = kv; + buffer += (#fmt["%s %0.3f\n", str::to_upper(str::unsafe::from_bytes(k)), v]); + ret true; + }); + + ret buffer; +} + +// given a map, search for the frequency of a pattern +fn find(mm: hashmap<~[u8], uint>, key: str) -> uint { + alt mm.find(str::bytes(str::to_lower(key))) { + option::none { ret 0u; } + option::some(num) { ret num; } + } +} + +// given a map, increment the counter for a key +fn update_freq(mm: hashmap<~[u8], uint>, key: &[u8]) { + let key = vec::slice(key, 0, key.len()); + alt mm.find(key) { + option::none { mm.insert(key, 1u ); } + option::some(val) { mm.insert(key, 1u + val); } + } +} + +// given a ~[u8], for each window call a function +// i.e., for "hello" and windows of size four, +// run it("hell") and it("ello"), then return "llo" +fn windows_with_carry(bb: ~[const u8], nn: uint, + it: fn(window: &[u8])) -> ~[u8] { + let mut ii = 0u; + + let len = vec::len(bb); + while ii < len - (nn - 1u) { + it(vec::view(bb, ii, ii+nn)); + ii += 1u; + } + + ret vec::slice(bb, len - (nn - 1u), len); +} + +fn make_sequence_processor(sz: uint, from_parent: stream::port<~[u8]>, + to_parent: stream::chan) { + + let freqs: hashmap<~[u8], uint> = map::bytes_hash(); + let mut carry: ~[u8] = ~[]; + let mut total: uint = 0u; + + let mut line: ~[u8]; + + loop { + + line = from_parent.recv(); + if line == ~[] { break; } + + carry = windows_with_carry(carry + line, sz, |window| { + update_freq(freqs, window); + total += 1u; + }); + } + + let buffer = alt sz { + 1u { sort_and_fmt(freqs, total) } + 2u { sort_and_fmt(freqs, total) } + 3u { #fmt["%u\t%s", find(freqs, "GGT"), "GGT"] } + 4u { #fmt["%u\t%s", find(freqs, "GGTA"), "GGTA"] } + 6u { #fmt["%u\t%s", find(freqs, "GGTATT"), "GGTATT"] } + 12u { #fmt["%u\t%s", find(freqs, "GGTATTTTAATT"), "GGTATTTTAATT"] } + 18u { #fmt["%u\t%s", find(freqs, "GGTATTTTAATTTATAGT"), "GGTATTTTAATTTATAGT"] } + _ { "" } + }; + + //comm::send(to_parent, #fmt["yay{%u}", sz]); + to_parent.send(buffer); +} + +// given a FASTA file on stdin, process sequence THREE +fn main(args: ~[str]) { + let rdr = if os::getenv("RUST_BENCH").is_some() { + // FIXME: Using this compile-time env variable is a crummy way to + // get to this massive data set, but #include_bin chokes on it (#2598) + let path = path::connect( + #env("CFG_SRC_DIR"), + "src/test/bench/shootout-k-nucleotide.data" + ); + result::get(io::file_reader(path)) + } else { + io::stdin() + }; + + + + // initialize each sequence sorter + let sizes = ~[1u,2u,3u,4u,6u,12u,18u]; + let streams = vec::map(sizes, |_sz| some(stream())); + let streams = vec::to_mut(streams); + let mut from_child = ~[]; + let to_child = vec::mapi(sizes, |ii, sz| { + let mut stream = none; + stream <-> streams[ii]; + let (to_parent_, from_child_) = option::unwrap(stream); + + vec::push(from_child, from_child_); + + let (to_child, from_parent) = stream::stream(); + + do task::spawn_with(from_parent) |from_parent| { + make_sequence_processor(sz, from_parent, to_parent_); + }; + + to_child + }); + + + // latch stores true after we've started + // reading the sequence of interest + let mut proc_mode = false; + + while !rdr.eof() { + let line: str = rdr.read_line(); + + if str::len(line) == 0u { cont; } + + alt (line[0], proc_mode) { + + // start processing if this is the one + ('>' as u8, false) { + alt str::find_str_from(line, "THREE", 1u) { + option::some(_) { proc_mode = true; } + option::none { } + } + } + + // break our processing + ('>' as u8, true) { break; } + + // process the sequence for k-mers + (_, true) { + let line_bytes = str::bytes(line); + + for sizes.eachi |ii, _sz| { + let mut lb = line_bytes; + to_child[ii].send(lb); + } + } + + // whatever + _ { } + } + } + + // finish... + for sizes.eachi |ii, _sz| { + to_child[ii].send(~[]); + } + + // now fetch and print result messages + for sizes.eachi |ii, _sz| { + io::println(from_child[ii].recv()); + } +} + diff --git a/src/test/run-pass/assignability-iface.rs b/src/test/run-pass/assignability-iface.rs new file mode 100644 index 0000000000000..47cf7535a6e08 --- /dev/null +++ b/src/test/run-pass/assignability-iface.rs @@ -0,0 +1,43 @@ +// Tests that type assignability is used to search for instances when +// making method calls, but only if there aren't any matches without +// it. + +iface iterable { + fn iterate(blk: fn(A) -> bool); +} + +impl vec/& of iterable for &[const A] { + fn iterate(f: fn(A) -> bool) { + vec::each(self, f); + } +} + +impl vec of iterable for ~[const A] { + fn iterate(f: fn(A) -> bool) { + vec::each(self, f); + } +} + +fn length>(x: T) -> uint { + let mut len = 0; + for x.iterate() |_y| { len += 1 } + ret len; +} + +fn main() { + let x = ~[0,1,2,3]; + // Call a method + for x.iterate() |y| { assert x[y] == y; } + // Call a parameterized function + assert length(x) == vec::len(x); + // Call a parameterized function, with type arguments that require + // a borrow + assert length::(x) == vec::len(x); + + // Now try it with a type that *needs* to be borrowed + let z = [0,1,2,3]/_; + // Call a method + for z.iterate() |y| { assert z[y] == y; } + // Call a parameterized function + assert length::(z) == vec::len(z); +} diff --git a/src/test/run-pass/module-polymorphism.rc b/src/test/run-pass/module-polymorphism.rc index ed8e8be283ea6..781e3a279b194 100644 --- a/src/test/run-pass/module-polymorphism.rc +++ b/src/test/run-pass/module-polymorphism.rc @@ -42,4 +42,4 @@ mod f32 { #[path = "template.rs"] mod template; -} \ No newline at end of file +} diff --git a/src/test/run-pass/module-polymorphism4.rc b/src/test/run-pass/module-polymorphism4.rc index 04348ad6dc220..bbca74cd397cc 100644 --- a/src/test/run-pass/module-polymorphism4.rc +++ b/src/test/run-pass/module-polymorphism4.rc @@ -1,5 +1,3 @@ -#[no_core]; - #[path = "module-polymorphism4-files"] mod cat { diff --git a/src/test/run-pass/pipe-bank-proto.rs b/src/test/run-pass/pipe-bank-proto.rs new file mode 100644 index 0000000000000..e6a4a011b30d2 --- /dev/null +++ b/src/test/run-pass/pipe-bank-proto.rs @@ -0,0 +1,70 @@ +// xfail-pretty + +// An example of the bank protocol from eholk's blog post. +// +// http://theincredibleholk.wordpress.com/2012/07/06/rusty-pipes/ + +import pipes::recv; + +type username = str; +type password = str; +type money = float; +type amount = float; + +proto! bank { + login:send { + login(username, password) -> login_response + } + + login_response:recv { + ok -> connected, + invalid -> login + } + + connected:send { + deposit(money) -> connected, + withdrawal(amount) -> withdrawal_response + } + + withdrawal_response:recv { + money(money) -> connected, + insufficient_funds -> connected + } +} + +fn macros() { + #macro[ + [#move[x], + unsafe { let y <- *ptr::addr_of(x); y }] + ]; +} + +fn bank_client(+bank: bank::client::login) { + import bank::*; + + let bank = client::login(bank, "theincredibleholk", "1234"); + let bank = alt recv(bank) { + some(ok(connected)) { + #move(connected) + } + some(invalid(_)) { fail "login unsuccessful" } + none { fail "bank closed the connection" } + }; + + let bank = client::deposit(bank, 100.00); + let bank = client::withdrawal(bank, 50.00); + alt recv(bank) { + some(money(m, _)) { + io::println("Yay! I got money!"); + } + some(insufficient_funds(_)) { + fail "someone stole my money" + } + none { + fail "bank closed the connection" + } + } +} + +fn main() { +} \ No newline at end of file diff --git a/src/test/run-pass/pipe-pingpong-proto.rs b/src/test/run-pass/pipe-pingpong-proto.rs new file mode 100644 index 0000000000000..479b2059bfe3c --- /dev/null +++ b/src/test/run-pass/pipe-pingpong-proto.rs @@ -0,0 +1,53 @@ +// An example to make sure the protocol parsing syntax extension works. + +// xfail-pretty + +proto! pingpong { + ping:send { + ping -> pong + } + + pong:recv { + pong -> ping + } +} + +mod test { + import pipes::recv; + import pingpong::{ping, pong}; + + fn client(-chan: pingpong::client::ping) { + import pingpong::client; + + let chan = client::ping(chan); + log(error, "Sent ping"); + let pong(_chan) = option::unwrap(recv(chan)); + log(error, "Received pong"); + } + + fn server(-chan: pingpong::server::ping) { + import pingpong::server; + + let ping(chan) = option::unwrap(recv(chan)); + log(error, "Received ping"); + let _chan = server::pong(chan); + log(error, "Sent pong"); + } +} + +fn main() { + let (client_, server_) = pingpong::init(); + let client_ = ~mut some(client_); + let server_ = ~mut some(server_); + + do task::spawn |move client_| { + let mut client__ = none; + *client_ <-> client__; + test::client(option::unwrap(client__)); + }; + do task::spawn |move server_| { + let mut server_ˊ = none; + *server_ <-> server_ˊ; + test::server(option::unwrap(server_ˊ)); + }; +} diff --git a/src/test/run-pass/pipe-select.rs b/src/test/run-pass/pipe-select.rs new file mode 100644 index 0000000000000..af01e568ffd40 --- /dev/null +++ b/src/test/run-pass/pipe-select.rs @@ -0,0 +1,75 @@ +// xfail-pretty + +use std; +import std::timer::sleep; +import std::uv; + +import pipes::{recv, select}; + +proto! oneshot { + waiting:send { + signal -> signaled + } + + signaled:send { } +} + +proto! stream { + stream:send { + send(T) -> stream + } +} + +fn main() { + import oneshot::client::*; + import stream::client::*; + + let iotask = uv::global_loop::get(); + + #macro[ + [#recv[chan], + chan.recv()(chan)] + ]; + + let c = pipes::spawn_service(stream::init, |p| { + #error("waiting for pipes"); + let stream::send(x, p) = option::unwrap(recv(p)); + #error("got pipes"); + let (left, right) : (oneshot::server::waiting, + oneshot::server::waiting) + = x; + #error("selecting"); + let (i, _, _) = select(~[left, right]); + #error("selected"); + assert i == 0; + + #error("waiting for pipes"); + let stream::send(x, _) = option::unwrap(recv(p)); + #error("got pipes"); + let (left, right) : (oneshot::server::waiting, + oneshot::server::waiting) + = x; + #error("selecting"); + let (i, _, _) = select(~[left, right]); + #error("selected"); + assert i == 1; + }); + + let (c1, p1) = oneshot::init(); + let (c2, p2) = oneshot::init(); + + let c = send(c, (p1, p2)); + + sleep(iotask, 1000); + + signal(c1); + + let (c1, p1) = oneshot::init(); + let (c2, p2) = oneshot::init(); + + send(c, (p1, p2)); + + sleep(iotask, 1000); + + signal(c2); +} \ No newline at end of file diff --git a/src/test/run-pass/pipe-sleep.rs b/src/test/run-pass/pipe-sleep.rs new file mode 100644 index 0000000000000..c4b44ff0be729 --- /dev/null +++ b/src/test/run-pass/pipe-sleep.rs @@ -0,0 +1,25 @@ +// xfail-pretty + +use std; +import std::timer::sleep; +import std::uv; +import pipes::recv; + +proto! oneshot { + waiting:send { + signal -> signaled + } + + signaled:send { } +} + +fn main() { + import oneshot::client::*; + + let c = pipes::spawn_service(oneshot::init, |p| { recv(p); }); + + let iotask = uv::global_loop::get(); + sleep(iotask, 5000); + + signal(c); +} \ No newline at end of file