diff options
683 files changed, 16827 insertions, 8126 deletions
diff --git a/README.md b/README.md index 1da02fea018..e919f3329fe 100644 --- a/README.md +++ b/README.md @@ -71,7 +71,7 @@ To easily build on windows we can use [MSYS2](http://sourceforge.net/projects/ms 3. With that now start `mingw32_shell.bat` from where you installed MSYS2 (i.e. `C:\msys`). 4. From there just navigate to where you have Rust's source code, configure and build it: - $ ./configure --build=i686-pc-mingw32 + $ ./configure $ make && make install [repo]: https://github.com/rust-lang/rust diff --git a/configure b/configure index 636d50276ff..5a2f9b5f20c 100755 --- a/configure +++ b/configure @@ -299,13 +299,19 @@ case $CFG_OSTYPE in CFG_OSTYPE=apple-darwin ;; - MINGW32*) + MINGW*) + # msys' `uname` does not print gcc configuration, but prints msys + # configuration. so we cannot believe `uname -m`: + # msys1 is always i686 and msys2 is always x86_64. + # instead, msys defines $MSYSTEM which is MINGW32 on i686 and + # MINGW64 on x86_64. + CFG_CPUTYPE=i686 CFG_OSTYPE=pc-mingw32 - ;; - - MINGW64*) - # msys2, MSYSTEM=MINGW64 - CFG_OSTYPE=w64-mingw32 + if [ "$MSYSTEM" = MINGW64 ] + then + CFG_CPUTYPE=x86_64 + CFG_OSTYPE=w64-mingw32 + fi ;; # Thad's Cygwin identifers below @@ -635,7 +641,7 @@ then LLVM_VERSION=$($LLVM_CONFIG --version) case $LLVM_VERSION in - (3.[2-5]*) + (3.[2-6]*) msg "found ok version of LLVM: $LLVM_VERSION" ;; (*) diff --git a/mk/crates.mk b/mk/crates.mk index 5bc4505fb05..f830e1a9d95 100644 --- a/mk/crates.mk +++ b/mk/crates.mk @@ -96,7 +96,7 @@ DEPS_test := std getopts serialize rbml term time regex native:rust_test_helpers DEPS_time := std serialize DEPS_rand := core DEPS_url := std -DEPS_log := std +DEPS_log := std regex DEPS_regex := std DEPS_regex_macros = rustc syntax std regex DEPS_fmt_macros = std diff --git a/src/compiletest/runtest.rs b/src/compiletest/runtest.rs index 94b6ea0d7b4..b36bc96cd35 100644 --- a/src/compiletest/runtest.rs +++ b/src/compiletest/runtest.rs @@ -17,7 +17,6 @@ use header; use procsrv; use util::logv; #[cfg(target_os = "windows")] -#[cfg(stage0, target_os = "win32")] // NOTE: Remove after snapshot use util; use std::io::File; @@ -819,7 +818,6 @@ fn check_expected_errors(expected_errors: Vec<errors::ExpectedError> , }).collect::<Vec<String> >(); #[cfg(target_os = "windows")] - #[cfg(stage0, target_os = "win32")] // NOTE: Remove after snapshot fn to_lower( s : &str ) -> String { let i = s.chars(); let c : Vec<char> = i.map( |c| { @@ -833,7 +831,6 @@ fn check_expected_errors(expected_errors: Vec<errors::ExpectedError> , } #[cfg(target_os = "windows")] - #[cfg(stage0, target_os = "win32")] // NOTE: Remove after snapshot fn prefix_matches( line : &str, prefix : &str ) -> bool { to_lower(line).as_slice().starts_with(to_lower(prefix).as_slice()) } @@ -1251,7 +1248,6 @@ fn make_cmdline(_libpath: &str, prog: &str, args: &[String]) -> String { } #[cfg(target_os = "windows")] -#[cfg(stage0, target_os = "win32")] // NOTE: Remove after snapshot fn make_cmdline(libpath: &str, prog: &str, args: &[String]) -> String { format!("{} {} {}", lib_path_cmd_prefix(libpath), prog, args.connect(" ")) } @@ -1259,7 +1255,6 @@ fn make_cmdline(libpath: &str, prog: &str, args: &[String]) -> String { // Build the LD_LIBRARY_PATH variable as it would be seen on the command line // for diagnostic purposes #[cfg(target_os = "windows")] -#[cfg(stage0, target_os = "win32")] // NOTE: Remove after snapshot fn lib_path_cmd_prefix(path: &str) -> String { format!("{}=\"{}\"", util::lib_path_env_var(), util::make_new_path(path)) } diff --git a/src/compiletest/util.rs b/src/compiletest/util.rs index 8947856e332..22b5600d5bf 100644 --- a/src/compiletest/util.rs +++ b/src/compiletest/util.rs @@ -11,7 +11,6 @@ use common::Config; #[cfg(target_os = "windows")] -#[cfg(stage0, target_os = "win32")] // NOTE: Remove after snapshot use std::os::getenv; /// Conversion table from triple OS name to Rust SYSNAME @@ -36,7 +35,6 @@ pub fn get_os(triple: &str) -> &'static str { } #[cfg(target_os = "windows")] -#[cfg(stage0, target_os = "win32")] // NOTE: Remove after snapshot pub fn make_new_path(path: &str) -> String { // Windows just uses PATH as the library search path, so we have to @@ -50,11 +48,9 @@ pub fn make_new_path(path: &str) -> String { } #[cfg(target_os = "windows")] -#[cfg(stage0, target_os = "win32")] // NOTE: Remove after snapshot pub fn lib_path_env_var() -> &'static str { "PATH" } #[cfg(target_os = "windows")] -#[cfg(stage0, target_os = "win32")] // NOTE: Remove after snapshot pub fn path_div() -> &'static str { ";" } pub fn logv(config: &Config, s: String) { diff --git a/src/doc/complement-design-faq.md b/src/doc/complement-design-faq.md index eb060d06cc4..a90a8737308 100644 --- a/src/doc/complement-design-faq.md +++ b/src/doc/complement-design-faq.md @@ -1,6 +1,6 @@ % The Rust Design FAQ -This document describes decisions were arrived at after lengthy discussion and +This document describes decisions that were arrived at after lengthy discussion and experimenting with alternatives. Please do not propose reversing them unless you have a new, extremely compelling argument. Note that this document specifically talks about the *language* and not any library or implementation. diff --git a/src/doc/guide-ffi.md b/src/doc/guide-ffi.md index 600a9019e6b..0ded9db3ad0 100644 --- a/src/doc/guide-ffi.md +++ b/src/doc/guide-ffi.md @@ -263,12 +263,13 @@ Rust code: ~~~~no_run +#[repr(C)] struct RustObject { a: i32, // other members } -extern fn callback(target: *mut RustObject, a:i32) { +extern "C" fn callback(target: *mut RustObject, a:i32) { println!("I'm called from C with value {0}", a); unsafe { // Update the value in RustObject with the value received from the callback @@ -506,16 +507,16 @@ to define a block for all windows systems, not just x86 ones. # Interoperability with foreign code -Rust guarantees that the layout of a `struct` is compatible with the platform's representation in C. -A `#[packed]` attribute is available, which will lay out the struct members without padding. -However, there are currently no guarantees about the layout of an `enum`. +Rust guarantees that the layout of a `struct` is compatible with the platform's representation in C +only if the `#[repr(C)]` attribute is applied to it. `#[repr(C, packed)]` can be used to lay out +struct members without padding. `#[repr(C)]` can also be applied to an enum. -Rust's owned and managed boxes use non-nullable pointers as handles which point to the contained +Rust's owned boxes (`Box<T>`) use non-nullable pointers as handles which point to the contained object. However, they should not be manually created because they are managed by internal -allocators. References can safely be assumed to be non-nullable pointers directly to the -type. However, breaking the borrow checking or mutability rules is not guaranteed to be safe, so -prefer using raw pointers (`*`) if that's needed because the compiler can't make as many assumptions -about them. +allocators. References can safely be assumed to be non-nullable pointers directly to the type. +However, breaking the borrow checking or mutability rules is not guaranteed to be safe, so prefer +using raw pointers (`*`) if that's needed because the compiler can't make as many assumptions about +them. Vectors and strings share the same basic memory layout, and utilities are available in the `vec` and `str` modules for working with C APIs. However, strings are not terminated with `\0`. If you need a diff --git a/src/doc/guide-pointers.md b/src/doc/guide-pointers.md index a4d081c5fdc..b196997b399 100644 --- a/src/doc/guide-pointers.md +++ b/src/doc/guide-pointers.md @@ -332,7 +332,7 @@ let z = &x; Mutable ones, however, are not: ```{rust,ignore} -let x = 5i; +let mut x = 5i; let y = &mut x; let z = &mut x; // error: cannot borrow `x` as mutable more than once at a time ``` diff --git a/src/doc/guide-tasks.md b/src/doc/guide-tasks.md index 6ef273e7a1a..687f2a3a833 100644 --- a/src/doc/guide-tasks.md +++ b/src/doc/guide-tasks.md @@ -2,114 +2,51 @@ # Introduction -Rust provides safe concurrency through a combination -of lightweight, memory-isolated tasks and message passing. -This guide will describe the concurrency model in Rust, how it -relates to the Rust type system, and introduce -the fundamental library abstractions for constructing concurrent programs. - -Rust tasks are not the same as traditional threads: rather, -they are considered _green threads_, lightweight units of execution that the Rust -runtime schedules cooperatively onto a small number of operating system threads. -On a multi-core system Rust tasks will be scheduled in parallel by default. -Because tasks are significantly -cheaper to create than traditional threads, Rust can create hundreds of -thousands of concurrent tasks on a typical 32-bit system. -In general, all Rust code executes inside a task, including the `main` function. - -In order to make efficient use of memory Rust tasks have dynamically sized stacks. -A task begins its life with a small -amount of stack space (currently in the low thousands of bytes, depending on -platform), and acquires more stack as needed. -Unlike in languages such as C, a Rust task cannot accidentally write to -memory beyond the end of the stack, causing crashes or worse. +Rust provides safe concurrent abstractions through a number of core library +primitives. This guide will describe the concurrency model in Rust, how it +relates to the Rust type system, and introduce the fundamental library +abstractions for constructing concurrent programs. Tasks provide failure isolation and recovery. When a fatal error occurs in Rust code as a result of an explicit call to `fail!()`, an assertion failure, or -another invalid operation, the runtime system destroys the entire -task. Unlike in languages such as Java and C++, there is no way to `catch` an -exception. Instead, tasks may monitor each other for failure. - -Tasks use Rust's type system to provide strong memory safety guarantees. In -particular, the type system guarantees that tasks cannot share mutable state -with each other. Tasks communicate with each other by transferring _owned_ -data through the global _exchange heap_. - -## A note about the libraries - -While Rust's type system provides the building blocks needed for safe -and efficient tasks, all of the task functionality itself is implemented -in the standard and sync libraries, which are still under development -and do not always present a consistent or complete interface. - -For your reference, these are the standard modules involved in Rust -concurrency at this writing: - -* [`std::task`] - All code relating to tasks and task scheduling, -* [`std::comm`] - The message passing interface, -* [`sync::DuplexStream`] - An extension of `pipes::stream` that allows both sending and receiving, -* [`sync::Arc`] - The Arc (atomically reference counted) type, for safely sharing immutable data, -* [`sync::Semaphore`] - A counting, blocking, bounded-waiting semaphore, -* [`sync::Mutex`] - A blocking, bounded-waiting, mutual exclusion lock with an associated - FIFO condition variable, -* [`sync::RWLock`] - A blocking, no-starvation, reader-writer lock with an associated condvar, -* [`sync::Barrier`] - A barrier enables multiple tasks to synchronize the beginning - of some computation, -* [`sync::TaskPool`] - A task pool abstraction, -* [`sync::Future`] - A type encapsulating the result of a computation which may not be complete, -* [`sync::one`] - A "once initialization" primitive -* [`sync::mutex`] - A proper mutex implementation regardless of the "flavor of task" which is - acquiring the lock. - -[`std::task`]: std/task/index.html -[`std::comm`]: std/comm/index.html -[`sync::DuplexStream`]: sync/struct.DuplexStream.html -[`sync::Arc`]: sync/struct.Arc.html -[`sync::Semaphore`]: sync/raw/struct.Semaphore.html -[`sync::Mutex`]: sync/struct.Mutex.html -[`sync::RWLock`]: sync/struct.RWLock.html -[`sync::Barrier`]: sync/struct.Barrier.html -[`sync::TaskPool`]: sync/struct.TaskPool.html -[`sync::Future`]: sync/struct.Future.html -[`sync::one`]: sync/one/index.html -[`sync::mutex`]: sync/mutex/index.html +another invalid operation, the runtime system destroys the entire task. Unlike +in languages such as Java and C++, there is no way to `catch` an exception. +Instead, tasks may monitor each other for failure. + +Tasks use Rust's type system to provide strong memory safety guarantees. In +particular, the type system guarantees that tasks cannot induce a data race +from shared mutable state. # Basics -The programming interface for creating and managing tasks lives -in the `task` module of the `std` library, and is thus available to all -Rust code by default. At its simplest, creating a task is a matter of -calling the `spawn` function with a closure argument. `spawn` executes the -closure in the new task. +At its simplest, creating a task is a matter of calling the `spawn` function +with a closure argument. `spawn` executes the closure in the new task. -~~~~ +```{rust} # use std::task::spawn; // Print something profound in a different task using a named function fn print_message() { println!("I am running in a different task!"); } spawn(print_message); -// Print something profound in a different task using a `proc` expression -// The `proc` expression evaluates to an (unnamed) owned closure. -// That closure will call `println!(...)` when the spawned task runs. - +// Alternatively, use a `proc` expression instead of a named function. +// The `proc` expression evaluates to an (unnamed) proc. +// That proc will call `println!(...)` when the spawned task runs. spawn(proc() println!("I am also running in a different task!") ); -~~~~ - -In Rust, there is nothing special about creating tasks: a task is not a -concept that appears in the language semantics. Instead, Rust's type system -provides all the tools necessary to implement safe concurrency: particularly, -_owned types_. The language leaves the implementation details to the standard -library. - -The `spawn` function has a very simple type signature: `fn spawn(f: -proc())`. Because it accepts only owned closures, and owned closures -contain only owned data, `spawn` can safely move the entire closure -and all its associated state into an entirely different task for -execution. Like any closure, the function passed to `spawn` may capture -an environment that it carries across tasks. - -~~~ +``` + +In Rust, a task is not a concept that appears in the language semantics. +Instead, Rust's type system provides all the tools necessary to implement safe +concurrency: particularly, ownership. The language leaves the implementation +details to the standard library. + +The `spawn` function has a very simple type signature: `fn spawn(f: proc(): +Send)`. Because it accepts only procs, and procs contain only owned data, +`spawn` can safely move the entire proc and all its associated state into an +entirely different task for execution. Like any closure, the function passed to +`spawn` may capture an environment that it carries across tasks. + +```{rust} # use std::task::spawn; # fn generate_task_number() -> int { 0 } // Generate some state locally @@ -119,27 +56,20 @@ spawn(proc() { // Capture it in the remote task println!("I am child number {}", child_task_number); }); -~~~ +``` ## Communication -Now that we have spawned a new task, it would be nice if we could -communicate with it. Recall that Rust does not have shared mutable -state, so one task may not manipulate variables owned by another task. -Instead we use *pipes*. +Now that we have spawned a new task, it would be nice if we could communicate +with it. For this, we use *channels*. A channel is simply a pair of endpoints: +one for sending messages and another for receiving messages. -A pipe is simply a pair of endpoints: one for sending messages and another for -receiving messages. Pipes are low-level communication building-blocks and so -come in a variety of forms, each one appropriate for a different use case. In -what follows, we cover the most commonly used varieties. +The simplest way to create a channel is to use the `channel` function to create a +`(Sender, Receiver)` pair. In Rust parlance, a **sender** is a sending endpoint +of a channel, and a **receiver** is the receiving endpoint. Consider the following +example of calculating two results concurrently: -The simplest way to create a pipe is to use the `channel` -function to create a `(Sender, Receiver)` pair. In Rust parlance, a *sender* -is a sending endpoint of a pipe, and a *receiver* is the receiving -endpoint. Consider the following example of calculating two results -concurrently: - -~~~~ +```{rust} # use std::task::spawn; let (tx, rx): (Sender<int>, Receiver<int>) = channel(); @@ -153,22 +83,22 @@ some_other_expensive_computation(); let result = rx.recv(); # fn some_expensive_computation() -> int { 42 } # fn some_other_expensive_computation() {} -~~~~ +``` Let's examine this example in detail. First, the `let` statement creates a stream for sending and receiving integers (the left-hand side of the `let`, -`(tx, rx)`, is an example of a *destructuring let*: the pattern separates -a tuple into its component parts). +`(tx, rx)`, is an example of a destructuring let: the pattern separates a tuple +into its component parts). -~~~~ +```{rust} let (tx, rx): (Sender<int>, Receiver<int>) = channel(); -~~~~ +``` -The child task will use the sender to send data to the parent task, -which will wait to receive the data on the receiver. The next statement -spawns the child task. +The child task will use the sender to send data to the parent task, which will +wait to receive the data on the receiver. The next statement spawns the child +task. -~~~~ +```{rust} # use std::task::spawn; # fn some_expensive_computation() -> int { 42 } # let (tx, rx) = channel(); @@ -176,25 +106,24 @@ spawn(proc() { let result = some_expensive_computation(); tx.send(result); }); -~~~~ +``` -Notice that the creation of the task closure transfers `tx` to the child -task implicitly: the closure captures `tx` in its environment. Both `Sender` -and `Receiver` are sendable types and may be captured into tasks or otherwise +Notice that the creation of the task closure transfers `tx` to the child task +implicitly: the closure captures `tx` in its environment. Both `Sender` and +`Receiver` are sendable types and may be captured into tasks or otherwise transferred between them. In the example, the child task runs an expensive computation, then sends the result over the captured channel. -Finally, the parent continues with some other expensive -computation, then waits for the child's result to arrive on the -receiver: +Finally, the parent continues with some other expensive computation, then waits +for the child's result to arrive on the receiver: -~~~~ +```{rust} # fn some_other_expensive_computation() {} # let (tx, rx) = channel::<int>(); # tx.send(0); some_other_expensive_computation(); let result = rx.recv(); -~~~~ +``` The `Sender` and `Receiver` pair created by `channel` enables efficient communication between a single sender and a single receiver, but multiple @@ -202,7 +131,7 @@ senders cannot use a single `Sender` value, and multiple receivers cannot use a single `Receiver` value. What if our example needed to compute multiple results across a number of tasks? The following program is ill-typed: -~~~ {.ignore} +```{rust,ignore} # fn some_expensive_computation() -> int { 42 } let (tx, rx) = channel(); @@ -215,11 +144,11 @@ spawn(proc() { spawn(proc() { tx.send(some_expensive_computation()); }); -~~~ +``` Instead we can clone the `tx`, which allows for multiple senders. -~~~ +```{rust} let (tx, rx) = channel(); for init_val in range(0u, 3) { @@ -232,7 +161,7 @@ for init_val in range(0u, 3) { let result = rx.recv() + rx.recv() + rx.recv(); # fn some_expensive_computation(_i: uint) -> int { 42 } -~~~ +``` Cloning a `Sender` produces a new handle to the same channel, allowing multiple tasks to send data to a single receiver. It upgrades the channel internally in @@ -240,12 +169,11 @@ order to allow this functionality, which means that channels that are not cloned can avoid the overhead required to handle multiple senders. But this fact has no bearing on the channel's usage: the upgrade is transparent. -Note that the above cloning example is somewhat contrived since -you could also simply use three `Sender` pairs, but it serves to -illustrate the point. For reference, written with multiple streams, it -might look like the example below. +Note that the above cloning example is somewhat contrived since you could also +simply use three `Sender` pairs, but it serves to illustrate the point. For +reference, written with multiple streams, it might look like the example below. -~~~ +```{rust} # use std::task::spawn; // Create a vector of ports, one for each child task @@ -260,15 +188,16 @@ let rxs = Vec::from_fn(3, |init_val| { // Wait on each port, accumulating the results let result = rxs.iter().fold(0, |accum, rx| accum + rx.recv() ); # fn some_expensive_computation(_i: uint) -> int { 42 } -~~~ +``` ## Backgrounding computations: Futures -With `sync::Future`, rust has a mechanism for requesting a computation and getting the result -later. + +With `sync::Future`, rust has a mechanism for requesting a computation and +getting the result later. The basic example below illustrates this. -~~~ +```{rust} use std::sync::Future; # fn main() { @@ -282,18 +211,20 @@ let mut delayed_fib = Future::spawn(proc() fib(50)); make_a_sandwich(); println!("fib(50) = {}", delayed_fib.get()) # } -~~~ +``` -The call to `future::spawn` returns immediately a `future` object regardless of how long it -takes to run `fib(50)`. You can then make yourself a sandwich while the computation of `fib` is -running. The result of the execution of the method is obtained by calling `get` on the future. -This call will block until the value is available (*i.e.* the computation is complete). Note that -the future needs to be mutable so that it can save the result for next time `get` is called. +The call to `future::spawn` returns immediately a `future` object regardless of +how long it takes to run `fib(50)`. You can then make yourself a sandwich while +the computation of `fib` is running. The result of the execution of the method +is obtained by calling `get` on the future. This call will block until the +value is available (*i.e.* the computation is complete). Note that the future +needs to be mutable so that it can save the result for next time `get` is +called. -Here is another example showing how futures allow you to background computations. The workload will -be distributed on the available cores. +Here is another example showing how futures allow you to background +computations. The workload will be distributed on the available cores. -~~~ +```{rust} # use std::sync::Future; fn partial_sum(start: uint) -> f64 { let mut local_sum = 0f64; @@ -312,23 +243,25 @@ fn main() { } println!("π^2/6 is not far from : {}", final_res); } -~~~ +``` -## Sharing immutable data without copy: Arc +## Sharing without copying: Arc -To share immutable data between tasks, a first approach would be to only use pipes as we have seen -previously. A copy of the data to share would then be made for each task. In some cases, this would -add up to a significant amount of wasted memory and would require copying the same data more than -necessary. +To share data between tasks, a first approach would be to only use channel as +we have seen previously. A copy of the data to share would then be made for +each task. In some cases, this would add up to a significant amount of wasted +memory and would require copying the same data more than necessary. -To tackle this issue, one can use an Atomically Reference Counted wrapper (`Arc`) as implemented in -the `sync` library of Rust. With an Arc, the data will no longer be copied for each task. The Arc -acts as a reference to the shared data and only this reference is shared and cloned. +To tackle this issue, one can use an Atomically Reference Counted wrapper +(`Arc`) as implemented in the `sync` library of Rust. With an Arc, the data +will no longer be copied for each task. The Arc acts as a reference to the +shared data and only this reference is shared and cloned. -Here is a small example showing how to use Arcs. We wish to run concurrently several computations on -a single large vector of floats. Each task needs the full vector to perform its duty. +Here is a small example showing how to use Arcs. We wish to run concurrently +several computations on a single large vector of floats. Each task needs the +full vector to perform its duty. -~~~ +```{rust} use std::rand; use std::sync::Arc; @@ -348,26 +281,27 @@ fn main() { }); } } -~~~ +``` -The function `pnorm` performs a simple computation on the vector (it computes the sum of its items -at the power given as argument and takes the inverse power of this value). The Arc on the vector is -created by the line +The function `pnorm` performs a simple computation on the vector (it computes +the sum of its items at the power given as argument and takes the inverse power +of this value). The Arc on the vector is created by the line: -~~~ +```{rust} # use std::rand; # use std::sync::Arc; # fn main() { # let numbers = Vec::from_fn(1000000, |_| rand::random::<f64>()); -let numbers_arc=Arc::new(numbers); +let numbers_arc = Arc::new(numbers); # } -~~~ +``` -and a unique clone is captured for each task via a procedure. This only copies the wrapper and not -it's contents. Within the task's procedure, the captured Arc reference can be used as an immutable -reference to the underlying vector as if it were local. +and a clone is captured for each task via a procedure. This only copies +the wrapper and not it's contents. Within the task's procedure, the captured +Arc reference can be used as a shared reference to the underlying vector as +if it were local. -~~~ +```{rust} # use std::rand; # use std::sync::Arc; # fn pnorm(nums: &[f64], p: uint) -> f64 { 4.0 } @@ -381,36 +315,34 @@ spawn(proc() { println!("{}-norm = {}", num, pnorm(task_numbers.as_slice(), num)); }); # } -~~~ - -The `arc` module also implements Arcs around mutable data that are not covered here. +``` # Handling task failure Rust has a built-in mechanism for raising exceptions. The `fail!()` macro (which can also be written with an error string as an argument: `fail!( -~reason)`) and the `assert!` construct (which effectively calls `fail!()` -if a boolean expression is false) are both ways to raise exceptions. When a -task raises an exception the task unwinds its stack---running destructors and +~reason)`) and the `assert!` construct (which effectively calls `fail!()` if a +boolean expression is false) are both ways to raise exceptions. When a task +raises an exception the task unwinds its stack---running destructors and freeing memory along the way---and then exits. Unlike exceptions in C++, exceptions in Rust are unrecoverable within a single task: once a task fails, there is no way to "catch" the exception. While it isn't possible for a task to recover from failure, tasks may notify each other of failure. The simplest way of handling task failure is with the -`try` function, which is similar to `spawn`, but immediately blocks waiting -for the child task to finish. `try` returns a value of type `Result<T, -()>`. `Result` is an `enum` type with two variants: `Ok` and `Err`. In this +`try` function, which is similar to `spawn`, but immediately blocks waiting for +the child task to finish. `try` returns a value of type `Result<T, Box<Any + +Send>>`. `Result` is an `enum` type with two variants: `Ok` and `Err`. In this case, because the type arguments to `Result` are `int` and `()`, callers can pattern-match on a result to check whether it's an `Ok` result with an `int` field (representing a successful result) or an `Err` result (representing termination with an error). -~~~{.ignore .linked-failure} +```{rust} # use std::task; # fn some_condition() -> bool { false } # fn calculate_result() -> int { 0 } -let result: Result<int, ()> = task::try(proc() { +let result: Result<int, Box<std::any::Any + Send>> = task::try(proc() { if some_condition() { calculate_result() } else { @@ -418,13 +350,12 @@ let result: Result<int, ()> = task::try(proc() { } }); assert!(result.is_err()); -~~~ +``` -Unlike `spawn`, the function spawned using `try` may return a value, -which `try` will dutifully propagate back to the caller in a [`Result`] -enum. If the child task terminates successfully, `try` will -return an `Ok` result; if the child task fails, `try` will return -an `Error` result. +Unlike `spawn`, the function spawned using `try` may return a value, which +`try` will dutifully propagate back to the caller in a [`Result`] enum. If the +child task terminates successfully, `try` will return an `Ok` result; if the +child task fails, `try` will return an `Error` result. [`Result`]: std/result/index.html @@ -433,90 +364,9 @@ an `Error` result. > future, it may be possible for tasks to intercept the value passed to > `fail!()`. -TODO: Need discussion of `future_result` in order to make failure -modes useful. - -But not all failures are created equal. In some cases you might need to -abort the entire program (perhaps you're writing an assert which, if -it trips, indicates an unrecoverable logic error); in other cases you -might want to contain the failure at a certain boundary (perhaps a -small piece of input from the outside world, which you happen to be -processing in parallel, is malformed and its processing task can't -proceed). - -## Creating a task with a bi-directional communication path - -A very common thing to do is to spawn a child task where the parent -and child both need to exchange messages with each other. The -function `sync::comm::duplex` supports this pattern. We'll -look briefly at how to use it. - -To see how `duplex` works, we will create a child task -that repeatedly receives a `uint` message, converts it to a string, and sends -the string in response. The child terminates when it receives `0`. -Here is the function that implements the child task: - -~~~ -#![allow(deprecated)] - -use std::comm::DuplexStream; -# fn main() { -fn stringifier(channel: &DuplexStream<String, uint>) { - let mut value: uint; - loop { - value = channel.recv(); - channel.send(value.to_string()); - if value == 0 { break; } - } -} -# } -~~~ - -The implementation of `DuplexStream` supports both sending and -receiving. The `stringifier` function takes a `DuplexStream` that can -send strings (the first type parameter) and receive `uint` messages -(the second type parameter). The body itself simply loops, reading -from the channel and then sending its response back. The actual -response itself is simply the stringified version of the received value, -`uint::to_string(value)`. - -Here is the code for the parent task: - -~~~ -#![allow(deprecated)] - -use std::comm::duplex; -# use std::task::spawn; -# use std::comm::DuplexStream; -# fn stringifier(channel: &DuplexStream<String, uint>) { -# let mut value: uint; -# loop { -# value = channel.recv(); -# channel.send(value.to_string()); -# if value == 0u { break; } -# } -# } -# fn main() { - -let (from_child, to_child) = duplex(); - -spawn(proc() { - stringifier(&to_child); -}); - -from_child.send(22); -assert!(from_child.recv().as_slice() == "22"); - -from_child.send(23); -from_child.send(0); - -assert!(from_child.recv().as_slice() == "23"); -assert!(from_child.recv().as_slice() == "0"); - -# } -~~~ - -The parent task first calls `DuplexStream` to create a pair of bidirectional -endpoints. It then uses `task::spawn` to create the child task, which captures -one end of the communication channel. As a result, both parent and child can -send and receive data to and from the other. +But not all failures are created equal. In some cases you might need to abort +the entire program (perhaps you're writing an assert which, if it trips, +indicates an unrecoverable logic error); in other cases you might want to +contain the failure at a certain boundary (perhaps a small piece of input from +the outside world, which you happen to be processing in parallel, is malformed +and its processing task can't proceed). diff --git a/src/doc/guide-unsafe.md b/src/doc/guide-unsafe.md index b46b09781ca..a95401682cf 100644 --- a/src/doc/guide-unsafe.md +++ b/src/doc/guide-unsafe.md @@ -446,7 +446,7 @@ possible in two ways: the `#[start]` attribute, or overriding the default shim for the C `main` function with your own. The function marked `#[start]` is passed the command line parameters -in the same format as a C: +in the same format as C: ``` #![no_std] @@ -593,7 +593,7 @@ standard library itself. # Interacting with the compiler internals > **Note**: this section is specific to the `rustc` compiler; these -> parts of the language may never be full specified and so details may +> parts of the language may never be fully specified and so details may > differ wildly between implementations (and even versions of `rustc` > itself). > diff --git a/src/doc/guide.md b/src/doc/guide.md index ad44e55e57d..8a634a083e8 100644 --- a/src/doc/guide.md +++ b/src/doc/guide.md @@ -342,7 +342,7 @@ Once you have this file in place, we should be ready to build! Try this: ```{bash} $ cargo build - Compiling hello_world v0.1.0 (file:/home/yourname/projects/hello_world) + Compiling hello_world v0.0.1 (file:///home/yourname/projects/hello_world) $ ./target/hello_world Hello, world! ``` @@ -486,7 +486,7 @@ You can use `cargo build` on the command line to build it. You'll get a warning, but it will still print "Hello, world!": ```{ignore,notrust} - Compiling hello_world v0.1.0 (file:/home/you/projects/hello_world) + Compiling hello_world v0.0.1 (file:///home/you/projects/hello_world) src/hello_world.rs:2:9: 2:10 warning: unused variable: `x`, #[warn(unused_variable)] on by default src/hello_world.rs:2 let x: int; ^ @@ -508,7 +508,7 @@ And try to build it. You'll get an error: ```{bash} $ cargo build - Compiling hello_world v0.1.0 (file:/home/you/projects/hello_world) + Compiling hello_world v0.0.1 (file:///home/you/projects/hello_world) src/hello_world.rs:4:39: 4:40 error: use of possibly uninitialized variable: `x` src/hello_world.rs:4 println!("The value of x is: {}", x); ^ @@ -535,7 +535,7 @@ arguments we pass to functions and macros, if you're passing more than one. When you just use the curly braces, Rust will attempt to display the value in a meaningful way by checking out its type. If you want to specify the format in a more detailed manner, there are a [wide number of options -available](/std/fmt/index.html). For now, we'll just stick to the default: +available](std/fmt/index.html). For now, we'll just stick to the default: integers aren't very complicated to print. So, we've cleared up all of the confusion around bindings, with one exception: @@ -1073,10 +1073,10 @@ destructuring `let`. ## Enums Finally, Rust has a "sum type", an **enum**. Enums are an incredibly useful -feature of Rust, and are used throughout the standard library. Enums look -like this: +feature of Rust, and are used throughout the standard library. This is an enum +that is provided by the Rust standard library: -``` +```{rust} enum Ordering { Less, Equal, @@ -1084,11 +1084,10 @@ enum Ordering { } ``` -This is an enum that is provided by the Rust standard library. An `Ordering` -can only be _one_ of `Less`, `Equal`, or `Greater` at any given time. Here's -an example: +An `Ordering` can only be _one_ of `Less`, `Equal`, or `Greater` at any given +time. Here's an example: -```rust +```{rust} fn cmp(a: int, b: int) -> Ordering { if a < b { Less } else if a > b { Greater } @@ -1155,7 +1154,7 @@ gotten there yet! You can have any number of values in an enum: -``` +```{rust} enum OptionalColor { Color(int, int, int), Missing @@ -1571,6 +1570,19 @@ for i in vec.iter() { This code will print each number in order, on its own line. +You can access a particular element of a vector, array, or slice by using +**subscript notation**: + +```{rust} +let names = ["Graydon", "Brian", "Niko"]; + +println!("The second name is: {}", names[1]); +``` + +These subscripts start at zero, like in most programming languages, so the +first name is `names[0]` and the second name is `names[1]`. The above example +prints `The second name is Brian`. + There's a whole lot more to vectors, but that's enough to get started. We have now learned all of the most basic Rust concepts. We're ready to start building our guessing game, but we need to know how to do one last thing first: get @@ -1652,7 +1664,7 @@ a full line of input. Nice and easy. Do you remember this code? -``` +```{rust} enum OptionalInt { Value(int), Missing, @@ -1769,7 +1781,7 @@ Check out the generated `Cargo.toml`: [package] name = "guessing_game" -version = "0.1.0" +version = "0.0.1" authors = ["Your Name <you@example.com>"] ``` @@ -1780,7 +1792,7 @@ Finally, Cargo generated a hello, world for us. Check out `src/main.rs`: ```{rust} fn main() { - println!("Hello world!"); + println!("Hello, world!"); } ``` @@ -1788,8 +1800,7 @@ Let's try compiling what Cargo gave us: ```{bash} $ cargo build - Compiling guessing_game v0.1.0 (file:/home/you/projects/guessing_game) -$ + Compiling guessing_game v0.0.1 (file:///home/you/projects/guessing_game) ``` Excellent! Open up your `src/main.rs` again. We'll be writing all of @@ -1802,10 +1813,9 @@ Try it out: ```{notrust,ignore} $ cargo run - Compiling guessing_game v0.1.0 (file:/home/you/projects/guessing_game) + Compiling guessing_game v0.1.0 (file:///home/you/projects/guessing_game) Running `target/guessing_game` Hello, world! -$ ``` Great! The `run` command comes in handy when you need to rapidly iterate on a project. @@ -1888,12 +1898,20 @@ fn main() { The first thing we changed was to `use std::rand`, as the docs explained. We then added in a `let` expression to create a variable binding -named `secret_number`, and we printed out its result. Let's try to compile -this using `cargo build`: +named `secret_number`, and we printed out its result. + +Also, you may wonder why we are using `%` on the result of `rand::random()`. +This operator is called 'modulo', and it returns the remainder of a division. +By taking the modulo of the result of `rand::random()`, we're limiting the +values to be between 0 and 99. Then, we add one to the result, making it from 1 +to 100. Using modulo can give you a very, very small bias in the result, but +for this example, it is not important. + +Let's try to compile this using `cargo build`: ```{notrust,no_run} $ cargo build - Compiling guessing_game v0.1.0 (file:/home/you/projects/guessing_game) + Compiling guessing_game v0.0.1 (file:///home/you/projects/guessing_game) src/main.rs:7:26: 7:34 error: the type of this value must be known in this context src/main.rs:7 let secret_number = (rand::random() % 100i) + 1i; ^~~~~~~~ @@ -1937,18 +1955,12 @@ fn main() { } ``` -... and then recompile: - -```{notrust,ignore} -$ cargo build - Compiling guessing_game v0.1.0 (file:/home/you/projects/guessing_game) -$ -``` - -Excellent! Try running our new program a few times: +Try running our new program a few times: ```{notrust,ignore} -$ ./target/guessing_game +$ cargo run + Compiling guessing_game v0.1.0 (file:///home/you/projects/guessing_game) + Running `target/guessing_game` Guess the number! The secret number is: 7 Please input your guess. @@ -1999,9 +2011,9 @@ fn main() { And trying it out: ```{notrust,ignore} -$ cargo build - Compiling guessing_game v0.1.0 (file:/home/you/projects/guessing_game) -$ ./target/guessing_game +$ cargo run + Compiling guessing_game v0.1.0 (file:///home/you/projects/guessing_game) + Running `target/guessing_game` Guess the number! The secret number is: 57 Please input your guess. @@ -2055,7 +2067,7 @@ If we try to compile, we'll get some errors: ```{notrust,ignore} $ cargo build - Compiling guessing_game v0.1.0 (file:/home/you/projects/guessing_game) + Compiling guessing_game v0.0.1 (file:///home/you/projects/guessing_game) src/main.rs:20:15: 20:20 error: mismatched types: expected `int` but found `collections::string::String` (expected int but found struct collections::string::String) src/main.rs:20 match cmp(input, secret_number) { ^~~~~ @@ -2109,7 +2121,7 @@ And try compiling again: ```{notrust,ignore} $ cargo build - Compiling guessing_game v0.1.0 (file:/home/you/projects/guessing_game) + Compiling guessing_game v0.0.1 (file:///home/you/projects/guessing_game) src/main.rs:20:15: 20:20 error: mismatched types: expected `uint` but found `collections::string::String` (expected uint but found struct collections::string::String) src/main.rs:20 match cmp(input, secret_number) { ^~~~~ @@ -2140,7 +2152,7 @@ a function for that: let input = io::stdin().read_line() .ok() .expect("Failed to read line"); -let guess: Option<uint> = from_str(input.as_slice()); +let input_num: Option<uint> = from_str(input.as_slice()); ``` The `from_str` function takes in a `&str` value and converts it into something. @@ -2162,8 +2174,8 @@ In this case, we say `x` is a `uint` explicitly, so Rust is able to properly tell `random()` what to generate. In a similar fashion, both of these work: ```{rust,ignore} -let guess = from_str::<Option<uint>>("5"); -let guess: Option<uint> = from_str("5"); +let input_num = from_str::<Option<uint>>("5"); +let input_num: Option<uint> = from_str("5"); ``` In this case, I happen to prefer the latter, and in the `random()` case, I prefer @@ -2212,7 +2224,7 @@ Let's try it out! ```{notrust,ignore} $ cargo build - Compiling guessing_game v0.1.0 (file:/home/you/projects/guessing_game) + Compiling guessing_game v0.0.1 (file:///home/you/projects/guessing_game) src/main.rs:22:15: 22:24 error: mismatched types: expected `uint` but found `core::option::Option<uint>` (expected uint but found enum core::option::Option) src/main.rs:22 match cmp(input_num, secret_number) { ^~~~~~~~~ @@ -2270,15 +2282,14 @@ We use a `match` to either give us the `uint` inside of the `Option`, or we print an error message and return. Let's give this a shot: ```{notrust,ignore} -$ cargo build - Compiling guessing_game v0.1.0 (file:/home/you/projects/guessing_game) -$ ./target/guessing_game +$ cargo run + Compiling guessing_game v0.1.0 (file:///home/you/projects/guessing_game) + Running `target/guessing_game` Guess the number! The secret number is: 17 Please input your guess. 5 Please input a number! -$ ``` Uh, what? But we did! @@ -2336,16 +2347,15 @@ fn cmp(a: uint, b: uint) -> Ordering { Let's try it! ```{notrust,ignore} -$ cargo build - Compiling guessing_game v0.1.0 (file:/home/you/projects/guessing_game) -$ ./target/guessing_game +$ cargo run + Compiling guessing_game v0.1.0 (file:///home/you/projects/guessing_game) + Running `target/guessing_game` Guess the number! The secret number is: 58 Please input your guess. - 76 + 76 You guessed: 76 Too big! -$ ``` Nice! You can see I even added spaces before my guess, and it still figured @@ -2414,9 +2424,9 @@ And try it out. But wait, didn't we just add an infinite loop? Yup. Remember that `return`? If we give a non-number answer, we'll `return` and quit. Observe: ```{notrust,ignore} -$ cargo build - Compiling guessing_game v0.1.0 (file:/home/you/projects/guessing_game) -$ ./target/guessing_game +$ cargo run + Compiling guessing_game v0.1.0 (file:///home/you/projects/guessing_game) + Running `target/guessing_game` Guess the number! The secret number is: 59 Please input your guess. @@ -2434,7 +2444,6 @@ You win! Please input your guess. quit Please input a number! -$ ``` Ha! `quit` actually quits. As does any other non-number input. Well, this is @@ -2546,10 +2555,10 @@ fn cmp(a: uint, b: uint) -> Ordering { Now we should be good! Let's try: -```{rust,ignore} -$ cargo build - Compiling guessing_game v0.1.0 (file:/home/you/projects/guessing_game) -$ ./target/guessing_game +```{notrust,ignore} +$ cargo run + Compiling guessing_game v0.1.0 (file:///home/you/projects/guessing_game) + Running `target/guessing_game` Guess the number! The secret number is: 61 Please input your guess. @@ -2663,7 +2672,7 @@ Let's double check our work by compiling: ```{bash,ignore} $ cargo build - Compiling modules v0.1.0 (file:/home/you/projects/modules) + Compiling modules v0.0.1 (file:///home/you/projects/modules) $ ./target/modules Hello, world! ``` @@ -2724,7 +2733,7 @@ mod hello { It gives an error: ```{notrust,ignore} - Compiling modules v0.1.0 (file:/home/you/projects/modules) + Compiling modules v0.0.1 (file:///home/you/projects/modules) src/main.rs:2:5: 2:23 error: function `print_hello` is private src/main.rs:2 hello::print_hello(); ^~~~~~~~~~~~~~~~~~ @@ -2748,10 +2757,9 @@ This will work: ```{notrust,ignore} $ cargo run - Compiling modules v0.1.0 (file:/home/steve/tmp/modules) + Compiling modules v0.0.1 (file:///home/you/projects/modules) Running `target/modules` Hello, world! -$ ``` Nice! @@ -2791,14 +2799,13 @@ be named, by convention. Next, we added an `extern crate modules` to the top of our `src/main.rs`. This, as you can guess, lets Rust know that our crate relies on another, external crate. We also had to modify our call to `print_hello`: now that it's in -another crate, we need to first specify the crate, then the module inside of it, -then the function name. +another crate, we need to specify that crate first. This doesn't _quite_ work yet. Try it: ```{notrust,ignore} $ cargo build - Compiling modules v0.1.0 (file:/home/you/projects/modules) + Compiling modules v0.0.1 (file:///home/you/projects/modules) /home/you/projects/modules/src/lib.rs:2:5: 4:6 warning: code is never used: `print_hello`, #[warn(dead_code)] on by default /home/you/projects/modules/src/lib.rs:2 pub fn print_hello() { /home/you/projects/modules/src/lib.rs:3 println!("Hello, world!"); @@ -2834,7 +2841,7 @@ And everything should work: ```{notrust,ignore} $ cargo run - Compiling modules v0.1.0 (file:/home/you/projects/modules) + Compiling modules v0.0.1 (file:///home/you/projects/modules) Running `target/modules` Hello, world! ``` @@ -2876,9 +2883,11 @@ pub fn print_hello() { } ``` -When we include a module like this, we don't need to make the `mod` declaration, -it's just understood. This helps prevent 'rightward drift': when you end up -indenting so many times that your code is hard to read. +When we include a module like this, we don't need to make the `mod` declaration +in `hello.rs`, because it's already been declared in `lib.rs`. `hello.rs` just +contains the body of the module which is defined (by the `pub mod hello`) in +`lib.rs`. This helps prevent 'rightward drift': when you end up indenting so +many times that your code is hard to read. Finally, make a new directory, `src/goodbye`, and make a new file in it, `src/goodbye/mod.rs`: @@ -2900,8 +2909,7 @@ This should all compile as usual: ```{notrust,ignore} $ cargo build - Compiling modules v0.1.0 (file:/home/you/projects/modules) -$ + Compiling modules v0.0.1 (file:///home/you/projects/modules) ``` We've seen how the `::` operator can be used to call into modules, but when @@ -3072,10 +3080,9 @@ And try it out: ```{notrust,ignore} $ cargo run - Compiling testing v0.1.0 (file:/home/you/projects/testing) + Compiling testing v0.0.1 (file:///home/you/projects/testing) Running `target/testing` Hello, world! -$ ``` Great. Rust's infrastructure supports tests in two sorts of places, and they're @@ -3105,7 +3112,7 @@ it `false`, so this test should fail. Let's try it! ```{notrust,ignore} $ cargo test - Compiling testing v0.1.0 (file:/home/you/projects/testing) + Compiling testing v0.0.1 (file:///home/you/projects/testing) /home/you/projects/testing/src/main.rs:1:1: 3:2 warning: code is never used: `main`, #[warn(dead_code)] on by default /home/you/projects/testing/src/main.rs:1 fn main() { /home/you/projects/testing/src/main.rs:2 println!("Hello, world"); @@ -3138,7 +3145,7 @@ Lots of output! Let's break this down: ```{notrust,ignore} $ cargo test - Compiling testing v0.1.0 (file:/home/you/projects/testing) + Compiling testing v0.0.1 (file:///home/you/projects/testing) ``` You can run all of your tests with `cargo test`. This runs both your tests in @@ -3213,7 +3220,7 @@ And then try to run our tests again: ```{notrust,ignore} $ cargo test - Compiling testing v0.1.0 (file:/home/you/projects/testing) + Compiling testing v0.0.1 (file:///home/you/projects/testing) /home/you/projects/testing/src/main.rs:1:1: 3:2 warning: code is never used: `main`, #[warn(dead_code)] on by default /home/you/projects/testing/src/main.rs:1 fn main() { /home/you/projects/testing/src/main.rs:2 println!("Hello, world"); @@ -3228,7 +3235,6 @@ running 1 test test foo ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured -$ ``` Nice! Our test passes, as we expected. Let's get rid of that warning for our `main` @@ -3252,7 +3258,7 @@ With this attribute, we won't get the warning: ```{notrust,ignore} $ cargo test - Compiling testing v0.1.0 (file:/home/you/projects/testing) + Compiling testing v0.0.1 (file:///home/you/projects/testing) running 0 tests @@ -3281,7 +3287,7 @@ And try to run the test: ```{notrust,ignore} $ cargo test - Compiling testing v0.1.0 (file:/home/youg/projects/testing) + Compiling testing v0.0.1 (file:///home/youg/projects/testing) /home/youg/projects/testing/tests/lib.rs:3:18: 3:38 error: unresolved name `add_three_times_four`. /home/youg/projects/testing/tests/lib.rs:3 let result = add_three_times_four(5i); ^~~~~~~~~~~~~~~~~~~~ @@ -3303,7 +3309,8 @@ To do that, we'll need to make a new module. Make a new file, `src/lib.rs`, and put this in it: ```{rust} -fn add_three_times_four(x: int) -> int { +# fn main() {} +pub fn add_three_times_four(x: int) -> int { (x + 3) * 4 } ``` @@ -3340,7 +3347,7 @@ Let's give it a run: ```{ignore,notrust} $ cargo test - Compiling testing v0.1.0 (file:/home/you/projects/testing) + Compiling testing v0.0.1 (file:///home/you/projects/testing) running 0 tests @@ -3380,7 +3387,7 @@ If you run `cargo test`, you should get the same output: ```{ignore,notrust} $ cargo test - Compiling testing v0.1.0 (file:/home/you/projects/testing) + Compiling testing v0.0.1 (file:///home/you/projects/testing) running 0 tests @@ -3424,7 +3431,7 @@ fn test_add_three() { We'd get this error: ```{notrust,ignore} - Compiling testing v0.1.0 (file:/home/you/projects/testing) + Compiling testing v0.0.1 (file:///home/you/projects/testing) /home/you/projects/testing/tests/lib.rs:3:5: 3:24 error: function `add_three` is private /home/you/projects/testing/tests/lib.rs:3 use testing::add_three; ^~~~~~~~~~~~~~~~~~~ @@ -3467,7 +3474,7 @@ Let's give it a shot: ```{ignore,notrust} $ cargo test - Compiling testing v0.1.0 (file:/home/you/projects/testing) + Compiling testing v0.0.1 (file:///home/you/projects/testing) running 1 test test test::test_times_four ... ok @@ -3512,15 +3519,15 @@ out. In systems programming, pointers are an incredibly important topic. Rust has a very rich set of pointers, and they operate differently than in many other languages. They are important enough that we have a specific [Pointer -Guide](/guide-pointers.html) that goes into pointers in much detail. In fact, +Guide](guide-pointers.html) that goes into pointers in much detail. In fact, while you're currently reading this guide, which covers the language in broad overview, there are a number of other guides that put a specific topic under a microscope. You can find the list of guides on the [documentation index -page](/index.html#guides). +page](index.html#guides). In this section, we'll assume that you're familiar with pointers as a general concept. If you aren't, please read the [introduction to -pointers](/guide-pointers.html#an-introduction) section of the Pointer Guide, +pointers](guide-pointers.html#an-introduction) section of the Pointer Guide, and then come back here. We'll wait. Got the gist? Great. Let's talk about pointers in Rust. @@ -3647,6 +3654,173 @@ In order to truly understand this error, we have to learn a few new concepts: ## Ownership, borrowing, and lifetimes +Whenever a resource of some kind is created, something must be responsible +for destroying that resource as well. Given that we're discussing pointers +right now, let's discuss this in the context of memory allocation, though +it applies to other resources as well. + +When you allocate heap memory, you need a mechanism to free that memory. Many +languages let the programmer control the allocation, and then use a garbage +collector to handle the deallocation. This is a valid, time-tested strategy, +but it's not without its drawbacks. Because the programmer does not have to +think as much about deallocation, allocation becomes something commonplace, +because it's easy. And if you need precise control over when something is +deallocated, leaving it up to your runtime can make this difficult. + +Rust chooses a different path, and that path is called **ownership**. Any +binding that creates a resource is the **owner** of that resource. Being an +owner gives you three privileges, with two restrictions: + +1. You control when that resource is deallocated. +2. You may lend that resource, immutably, to as many borrowers as you'd like. +3. You may lend that resource, mutably, to a single borrower. **BUT** +4. Once you've done so, you may not also lend it out otherwise, mutably or + immutably. +5. You may not lend it out mutably if you're currently lending it to someone. + +What's up with all this 'lending' and 'borrowing'? When you allocate memory, +you get a pointer to that memory. This pointer allows you to manipulate said +memory. If you are the owner of a pointer, then you may allow another +binding to temporarily borrow that pointer, and then they can manipulate the +memory. The length of time that the borrower is borrowing the pointer +from you is called a **lifetime**. + +If two distinct bindings share a pointer, and the memory that pointer points to +is immutable, then there are no problems. But if it's mutable, both pointers +can attempt to write to the memory at the same time, causing a **race +condition**. Therefore, if someone wants to mutate something that they've +borrowed from you, you must not have lent out that pointer to anyone else. + +Rust has a sophisticated system called the **borrow checker** to make sure that +everyone plays by these rules. At compile time, it verifies that none of these +rules are broken. If there's no problem, our program compiles successfully, and +there is no runtime overhead for any of this. The borrow checker works only at +compile time. If the borrow checker did find a problem, it will report a +**lifetime error**, and your program will refuse to compile. + +That's a lot to take in. It's also one of the _most_ important concepts in +all of Rust. Let's see this syntax in action: + +```{rust} +{ + let x = 5i; // x is the owner of this integer, which is memory on the stack. + + // other code here... + +} // privilege 1: when x goes out of scope, this memory is deallocated + +/// this function borrows an integer. It's given back automatically when the +/// function returns. +fn foo(x: &int) -> &int { x } + +{ + let x = 5i; // x is the owner of this integer, which is memory on the stack. + + // privilege 2: you may lend that resource, to as many borrowers as you'd like + let y = &x; + let z = &x; + + foo(&x); // functions can borrow too! + + let a = &x; // we can do this alllllll day! +} + +{ + let mut x = 5i; // x is the owner of this integer, which is memory on the stack. + + let y = &mut x; // privilege 3: you may lend that resource to a single borrower, + // mutably +} +``` + +If you are a borrower, you get a few privileges as well, but must also obey a +restriction: + +1. If the borrow is immutable, you may read the data the pointer points to. +2. If the borrow is mutable, you may read and write the data the pointer points to. +3. You may lend the pointer to someone else in an immutable fashion, **BUT** +4. When you do so, they must return it to you before you must give your own + borrow back. + +This last requirement can seem odd, but it also makes sense. If you have to +return something, and you've lent it to someone, they need to give it back to +you for you to give it back! If we didn't, then the owner could deallocate +the memory, and the person we've loaned it out to would have a pointer to +invalid memory. This is called a 'dangling pointer.' + +Let's re-examine the error that led us to talk about all of this, which was a +violation of the restrictions placed on owners who lend something out mutably. +The code: + +```{rust,ignore} +let mut x = 5i; +let y = &mut x; +let z = &mut x; +``` + +The error: + +```{notrust,ignore} +error: cannot borrow `x` as mutable more than once at a time + let z = &mut x; + ^ +note: previous borrow of `x` occurs here; the mutable borrow prevents subsequent moves, borrows, or modification of `x` until the borrow ends + let y = &mut x; + ^ +note: previous borrow ends here + fn main() { + let mut x = 5i; + let y = &mut x; + let z = &mut x; + } + ^ +``` + +This error comes in three parts. Let's go over each in turn. + +```{notrust,ignore} +error: cannot borrow `x` as mutable more than once at a time + let z = &mut x; + ^ +``` + +This error states the restriction: you cannot lend out something mutable more +than once at the same time. The borrow checker knows the rules! + +```{notrust,ignore} +note: previous borrow of `x` occurs here; the mutable borrow prevents subsequent moves, borrows, or modification of `x` until the borrow ends + let y = &mut x; + ^ +``` + +Some compiler errors come with notes to help you fix the error. This error comes +with two notes, and this is the first. This note informs us of exactly where +the first mutable borrow occurred. The error showed us the second. So now we +see both parts of the problem. It also alludes to rule #3, by reminding us that +we can't change `x` until the borrow is over. + +```{notrust,ignore} +note: previous borrow ends here + fn main() { + let mut x = 5i; + let y = &mut x; + let z = &mut x; + } + ^ +``` + +Here's the second note, which lets us know where the first borrow would be over. +This is useful, because if we wait to try to borrow `x` after this borrow is +over, then everything will work. + +These rules are very simple, but that doesn't mean that they're easy. For more +advanced patterns, please consult the [Lifetime Guide](guide-lifetimes.html). +You'll also learn what this type signature with the `'a` syntax is: + +```{rust,ignore} +pub fn as_maybe_owned(&self) -> MaybeOwned<'a> { ... } +``` + ## Boxes All of our references so far have been to variables we've created on the stack. @@ -3763,6 +3937,173 @@ guide](http://doc.rust-lang.org/guide-pointers.html#rc-and-arc). # Patterns +We've made use of patterns a few times in the guide: first with `let` bindings, +then with `match` statements. Let's go on a whirlwind tour of all of the things +patterns can do! + +A quick refresher: you can match against literals directly, and `_` acts as an +'any' case: + +```{rust} +let x = 1i; + +match x { + 1 => println!("one"), + 2 => println!("two"), + 3 => println!("three"), + _ => println!("anything"), +} +``` + +You can match multiple patterns with `|`: + +```{rust} +let x = 1i; + +match x { + 1 | 2 => println!("one or two"), + 3 => println!("three"), + _ => println!("anything"), +} +``` + +You can match a range of values with `..`: + +```{rust} +let x = 1i; + +match x { + 1 .. 5 => println!("one through five"), + _ => println!("anything"), +} +``` + +Ranges are mostly used with integers and single characters. + +If you're matching multiple things, via a `|` or a `..`, you can bind +the value to a name with `@`: + +```{rust} +let x = 1i; + +match x { + x @ 1 .. 5 => println!("got {}", x), + _ => println!("anything"), +} +``` + +If you're matching on an enum which has variants, you can use `..` to +ignore the value in the variant: + +```{rust} +enum OptionalInt { + Value(int), + Missing, +} + +let x = Value(5i); + +match x { + Value(..) => println!("Got an int!"), + Missing => println!("No such luck."), +} +``` + +You can introduce **match guards** with `if`: + +```{rust} +enum OptionalInt { + Value(int), + Missing, +} + +let x = Value(5i); + +match x { + Value(x) if x > 5 => println!("Got an int bigger than five!"), + Value(..) => println!("Got an int!"), + Missing => println!("No such luck."), +} +``` + +If you're matching on a pointer, you can use the same syntax as you declared it +with. First, `&`: + +```{rust} +let x = &5i; + +match x { + &x => println!("Got a value: {}", x), +} +``` + +Here, the `x` inside the `match` has type `int`. In other words, the left hand +side of the pattern destructures the value. If we have `&5i`, then in `&x`, `x` +would be `5i`. + +If you want to get a reference, use the `ref` keyword: + +```{rust} +let x = 5i; + +match x { + ref x => println!("Got a reference to {}", x), +} +``` + +Here, the `x` inside the `match` has the type `&int`. In other words, the `ref` +keyword _creates_ a reference, for use in the pattern. If you need a mutable +reference, `ref mut` will work in the same way: + +```{rust} +let mut x = 5i; + +match x { + ref mut x => println!("Got a mutable reference to {}", x), +} +``` + +If you have a struct, you can desugar it inside of a pattern: + +```{rust} +struct Point { + x: int, + y: int, +} + +let origin = Point { x: 0i, y: 0i }; + +match origin { + Point { x: x, y: y } => println!("({},{})", x, y), +} +``` + +If we only care about some of the values, we don't have to give them all names: + +```{rust} +struct Point { + x: int, + y: int, +} + +let origin = Point { x: 0i, y: 0i }; + +match origin { + Point { x: x, .. } => println!("x is {}", x), +} +``` + +Whew! That's a lot of different ways to match things, and they can all be +mixed and matched, depending on what you're doing: + +```{rust,ignore} +match x { + Foo { x: Some(ref name), y: None } => ... +} +``` + +Patterns are very powerful. Make good use of them. + # Method Syntax Functions are great, but if you want to call a bunch of them on some data, it @@ -3850,7 +4191,6 @@ This **static method** builds a new `Circle` for us. Note that static methods are called with the `Struct::method()` syntax, rather than the `ref.method()` syntax. - # Closures So far, we've made lots of functions in Rust. But we've given them all names. @@ -4053,9 +4393,343 @@ Doing this is not particularly common, but every once in a while, it's useful. That's all you need to get the hang of closures! Closures are a little bit strange at first, but once you're used to using them, you'll miss them in any language that doesn't have them. Passing functions to other functions is -incredibly powerful. Next, let's look at one of those things: iterators. +incredibly powerful. Next, let's look at one of those things: iterators. + +# Iterators + +Let's talk about loops. + +Remember Rust's `for` loop? Here's an example: + +```{rust} +for x in range(0i, 10i) { + println!("{:d}", x); +} +``` + +Now that you know more Rust, we can talk in detail about how this works. The +`range` function returns an **iterator**. An iterator is something that we can +call the `.next()` method on repeatedly, and it gives us a sequence of things. + +Like this: + +```{rust} +let mut range = range(0i, 10i); + +loop { + match range.next() { + Some(x) => { + println!("{}", x); + } + None => { break } + } +} +``` + +We make a mutable binding to the return value of `range`, which is our iterator. +We then `loop`, with an inner `match`. This `match` is used on the result of +`range.next()`, which gives us a reference to the next value of the iterator. +`next` returns an `Option<int>`, in this case, which will be `Some(int)` when +we have a value and `None` once we run out. If we get `Some(int)`, we print it +out, and if we get `None`, we `break` out of the loop. + +This code sample is basically the same as our `for` loop version. The `for` +loop is just a handy way to write this `loop`/`match`/`break` construct. + +`for` loops aren't the only thing that uses iterators, however. Writing your +own iterator involves implementing the `Iterator` trait. While doing that is +outside of the scope of this guide, Rust provides a number of useful iterators +to accomplish various tasks. Before we talk about those, we should talk about a +Rust anti-pattern. And that's `range`. + +Yes, we just talked about how `range` is cool. But `range` is also very +primitive. For example, if you needed to iterate over the contents of +a vector, you may be tempted to write this: + +```{rust} +let nums = vec![1i, 2i, 3i]; + +for i in range(0u, nums.len()) { + println!("{}", nums[i]); +} +``` + +This is strictly worse than using an actual iterator. The `.iter()` method on +vectors returns an iterator which iterates through a reference to each element +of the vector in turn. So write this: + +```{rust} +let nums = vec![1i, 2i, 3i]; + +for num in nums.iter() { + println!("{}", num); +} +``` + +There are two reasons for this. First, this is more semantic. We iterate +through the entire vector, rather than iterating through indexes, and then +indexing the vector. Second, this version is more efficient: the first version +will have extra bounds checking because it used indexing, `nums[i]`. But since +we yield a reference to each element of the vector in turn with the iterator, +there's no bounds checking in the second example. This is very common with +iterators: we can ignore unnecessary bounds checks, but still know that we're +safe. + +There's another detail here that's not 100% clear because of how `println!` +works. `num` is actually of type `&int`, that is, it's a reference to an `int`, +not an `int` itself. `println!` handles the dereferencing for us, so we don't +see it. This code works fine too: + +```{rust} +let nums = vec![1i, 2i, 3i]; + +for num in nums.iter() { + println!("{}", *num); +} +``` + +Now we're explicitly dereferencing `num`. Why does `iter()` give us references? +Well, if it gave us the data itself, we would have to be its owner, which would +involve making a copy of the data and giving us the copy. With references, +we're just borrowing a reference to the data, and so it's just passing +a reference, without needing to do the copy. + +So, now that we've established that `range` is often not what you want, let's +talk about what you do want instead. + +There are three broad classes of things that are relevant here: iterators, +**iterator adapters**, and **consumers**. Here's some definitions: + +* 'iterators' give you a sequence of values. +* 'iterator adapters' operate on an iterator, producing a new iterator with a + different output sequence. +* 'consumers' operate on an iterator, producing some final set of values. + +Let's talk about consumers first, since you've already seen an iterator, +`range`. + +## Consumers + +A 'consumer' operates on an iterator, returning some kind of value or values. +The most common consumer is `collect()`. This code doesn't quite compile, +but it shows the intention: + +```{rust,ignore} +let one_to_one_hundred = range(0i, 100i).collect(); +``` + +As you can see, we call `collect()` on our iterator. `collect()` takes +as many values as the iterator will give it, and returns a collection +of the results. So why won't this compile? Rust can't determine what +type of things you want to collect, and so you need to let it know. +Here's the version that does compile: + +```{rust} +let one_to_one_hundred = range(0i, 100i).collect::<Vec<int>>(); +``` + +If you remember, the `::<>` syntax allows us to give a type hint, +and so we tell it that we want a vector of integers. + +`collect()` is the most common consumer, but there are others too. `find()` +is one: + +```{rust} +let one_to_one_hundred = range(0i, 100i); + +let greater_than_forty_two = range(0i, 100i) + .find(|x| *x >= 42); + +match greater_than_forty_two { + Some(_) => println!("We got some numbers!"), + None => println!("No numbers found :("), +} +``` + +`find` takes a closure, and works on a reference to each element of an +iterator. This closure returns `true` if the element is the element we're +looking for, and `false` otherwise. Because we might not find a matching +element, `find` returns an `Option` rather than the element itself. + +Another important consumer is `fold`. Here's what it looks like: + +```{rust} +let sum = range(1i, 100i) + .fold(0i, |sum, x| sum + x); +``` + +`fold()` is a consumer that looks like this: +`fold(base, |accumulator, element| ...)`. It takes two arguments: the first +is an element called the "base". The second is a closure that itself takes two +arguments: the first is called the "accumulator," and the second is an +"element." Upon each iteration, the closure is called, and the result is the +value of the accumulator on the next iteration. On the first iteration, the +base is the value of the accumulator. + +Okay, that's a bit confusing. Let's examine the values of all of these things +in this iterator: -# iterators +| base | accumulator | element | closure result | +|------|-------------|---------|----------------| +| 0i | 0i | 1i | 1i | +| 0i | 1i | 2i | 3i | +| 0i | 3i | 3i | 6i | + +We called `fold()` with these arguments: + +```{rust} +# range(1i, 5i) +.fold(0i, |sum, x| sum + x); +``` + +So, `0i` is our base, `sum` is our accumulator, and `x` is our element. On the +first iteration, we set `sum` to `0i`, and `x` is the first element of `nums`, +`1i`. We then add `sum` and `x`, which gives us `0i + 1i = 1i`. On the second +iteration, that value becomes our accumulator, `sum`, and the element is +the second element of the array, `2i`. `1i + 2i = 3i`, and so that becomes +the value of the accumulator for the last iteration. On that iteration, +`x` is the last element, `3i`, and `3i + 3i = 6i`, which is our final +result for our sum. `1 + 2 + 3 = 6`, and that's the result we got. + +Whew. `fold` can be a bit strange the first few times you see it, but once it +clicks, you can use it all over the place. Any time you have a list of things, +and you want a single result, `fold` is appropriate. + +Consumers are important due to one additional property of iterators we haven't +talked about yet: laziness. Let's talk some more about iterators, and you'll +see why consumers matter. + +## Iterators + +As we've said before, an iterator is something that we can call the `.next()` +method on repeatedly, and it gives us a sequence of things. Because you need +to call the method, this means that iterators are **lazy**. This code, for +example, does not actually generate the numbers `1-100`, and just creates a +value that represents the sequence: + +```{rust} +let nums = range(1i, 100i); +``` + +Since we didn't do anything with the range, it didn't generate the sequence. +Once we add the consumer: + +```{rust} +let nums = range(1i, 100i).collect::<Vec<int>>(); +``` + +Now, `collect()` will require that `range()` give it some numbers, and so +it will do the work of generating the sequence. + +`range` is one of two basic iterators that you'll see. The other is `iter()`, +which you've used before. `iter()` can turn a vector into a simple iterator +that gives you each element in turn: + +```{rust} +let nums = [1i, 2i, 3i]; + +for num in nums.iter() { + println!("{}", num); +} +``` + +These two basic iterators should serve you well. There are some more +advanced iterators, including ones that are infinite. Like `count`: + +```{rust} +std::iter::count(1i, 5i); +``` + +This iterator counts up from one, adding five each time. It will give +you a new integer every time, forever. Well, technically, until the +maximum number that an `int` can represent. But since iterators are lazy, +that's okay! You probably don't want to use `collect()` on it, though... + +That's enough about iterators. Iterator adapters are the last concept +we need to talk about with regards to iterators. Let's get to it! + +## Iterator adapters + +"Iterator adapters" take an iterator and modify it somehow, producing +a new iterator. The simplest one is called `map`: + +```{rust,ignore} +range(1i, 100i).map(|x| x + 1i); +``` + +`map` is called upon another iterator, and produces a new iterator where each +element reference has the closure it's been given as an argument called on it. +So this would give us the numbers from `2-101`. Well, almost! If you +compile the example, you'll get a warning: + +```{notrust,ignore} +2:37 warning: unused result which must be used: iterator adaptors are lazy and + do nothing unless consumed, #[warn(unused_must_use)] on by default + range(1i, 100i).map(|x| x + 1i); + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +``` + +Laziness strikes again! That closure will never execute. This example +doesn't print any numbers: + +```{rust,ignore} +range(1i, 100i).map(|x| println!("{}", x)); +``` + +If you are trying to execute a closure on an iterator for its side effects, +just use `for` instead. + +There are tons of interesting iterator adapters. `take(n)` will get the +first `n` items out of an iterator, and return them as a list. Let's +try it out with our infinite iterator from before, `count()`: + +```{rust} +for i in std::iter::count(1i, 5i).take(5) { + println!("{}", i); +} +``` + +This will print + +```{notrust,ignore} +1 +6 +11 +16 +21 +``` + +`filter()` is an adapter that takes a closure as an argument. This closure +returns `true` or `false`. The new iterator `filter()` produces returns +only the elements that that closure returned `true` for: + +```{rust} +for i in range(1i, 100i).filter(|x| x % 2 == 0) { + println!("{}", i); +} +``` + +This will print all of the even numbers between one and a hundred. + +You can chain all three things together: start with an iterator, adapt it +a few times, and then consume the result. Check it out: + +```{rust} +range(1i, 1000i) + .filter(|x| x % 2 == 0) + .filter(|x| x % 3 == 0) + .take(5) + .collect::<Vec<int>>(); +``` + +This will give you a vector containing `6`, `12`, `18`, `24`, and `30`. + +This is just a small taste of what iterators, iterator adapters, and consumers +can help you with. There are a number of really useful iterators, and you can +write your own as well. Iterators provide a safe, efficient way to manipulate +all kinds of lists. They're a little unusual at first, but if you play with +them, you'll get hooked. For a full list of the different iterators and +consumers, check out the [iterator module documentation](std/iter/index.html). # Generics @@ -4703,4 +5377,177 @@ fail. # Macros +One of Rust's most advanced features is its system of **macro**s. While +functions allow you to provide abstractions over values and operations, macros +allow you to provide abstractions over syntax. Do you wish Rust had the ability +to do something that it can't currently do? You may be able to write a macro +to extend Rust's capabilities. + +You've already used one macro extensively: `println!`. When we invoke +a Rust macro, we need to use the exclamation mark (`!`). There's two reasons +that this is true: the first is that it makes it clear when you're using a +macro. The second is that macros allow for flexible syntax, and so Rust must +be able to tell where a macro starts and ends. The `!(...)` helps with this. + +Let's talk some more about `println!`. We could have implemented `println!` as +a function, but it would be worse. Why? Well, what macros allow you to do +is write code that generates more code. So when we call `println!` like this: + +```{rust} +let x = 5i; +println!("x is: {}", x); +``` + +The `println!` macro does a few things: + +1. It parses the string to find any `{}`s +2. It checks that the number of `{}`s matches the number of other arguments. +3. It generates a bunch of Rust code, taking this in mind. + +What this means is that you get type checking at compile time, because +Rust will generate code that takes all of the types into account. If +`println!` was a function, it could still do this type checking, but it +would happen at run time rather than compile time. + +We can check this out using a special flag to `rustc`. This code, in a file +`print.rs`: + +```{rust} +fn main() { + let x = "Hello"; + println!("x is: {:s}", x); +} +``` + +Can have its macros expanded like this: `rustc print.rs --pretty=expanded`, will +give us this huge result: + +```{rust,ignore} +#![feature(phase)] +#![no_std] +#![feature(globs)] +#[phase(plugin, link)] +extern crate std = "std"; +extern crate rt = "native"; +use std::prelude::*; +fn main() { + let x = "Hello"; + match (&x,) { + (__arg0,) => { + #[inline] + #[allow(dead_code)] + static __STATIC_FMTSTR: [::std::fmt::rt::Piece<'static>, ..2u] = + [::std::fmt::rt::String("x is: "), + ::std::fmt::rt::Argument(::std::fmt::rt::Argument{position: + ::std::fmt::rt::ArgumentNext, + format: + ::std::fmt::rt::FormatSpec{fill: + ' ', + align: + ::std::fmt::rt::AlignUnknown, + flags: + 0u, + precision: + ::std::fmt::rt::CountImplied, + width: + ::std::fmt::rt::CountImplied,},})]; + let __args_vec = + &[::std::fmt::argument(::std::fmt::secret_string, __arg0)]; + let __args = + unsafe { + ::std::fmt::Arguments::new(__STATIC_FMTSTR, __args_vec) + }; + ::std::io::stdio::println_args(&__args) + } + }; +} +``` + +Intense. Here's a trimmed down version that's a bit easier to read: + +```{rust,ignore} +fn main() { + let x = 5i; + match (&x,) { + (__arg0,) => { + static __STATIC_FMTSTR: = + [String("x is: "), + Argument(Argument { + position: ArgumentNext, + format: FormatSpec { + fill: ' ', + align: AlignUnknown, + flags: 0u, + precision: CountImplied, + width: CountImplied, + }, + }, + ]; + let __args_vec = &[argument(secret_string, __arg0)]; + let __args = unsafe { Arguments::new(__STATIC_FMTSTR, __args_vec) }; + + println_args(&__args) + } + }; +} +``` + +Whew! This isn't too terrible. You can see that we still `let x = 5i`, +but then things get a little bit hairy. Three more bindings get set: a +static format string, an argument vector, and the aruments. We then +invoke the `println_args` function with the generated arguments. + +This is the code (well, the full version) that Rust actually compiles. You can +see all of the extra information that's here. We get all of the type safety and +options that it provides, but at compile time, and without needing to type all +of this out. This is how macros are powerful. Without them, you would need to +type all of this by hand to get a type checked `println`. + +For more on macros, please consult [the Macros Guide](guide-macros.html). +Macros are a very advanced and still slightly experimental feature, but don't +require a deep understanding to call, since they look just like functions. The +Guide can help you if you want to write your own. + # Unsafe + +Finally, there's one more concept that you should be aware in Rust: `unsafe`. +There are two circumstances where Rust's safety provisions don't work well. +The first is when interfacing with C code, and the second is when building +certain kinds of abstractions. + +Rust has support for FFI (which you can read about in the [FFI +Guide](guide-ffi.html)), but can't guarantee that the C code will be safe. +Therefore, Rust marks such functions with the `unsafe` +keyword, which indicates that the function may not behave properly. + +Second, if you'd like to create some sort of shared-memory data structure, Rust +won't allow it, because memory must be owned by a single owner. However, if +you're planning on making access to that shared memory safe, such as with a +mutex, _you_ know that it's safe, but Rust can't know. Writing an `unsafe` +block allows you to ask the compiler to trust you. In this case, the _internal_ +implementation of the mutex is considered unsafe, but the _external_ interface +we present is safe. This allows it to be effectively used in normal Rust, while +being able to implement functionality that the compiler can't double check for +us. + +Doesn't an escape hatch undermine the safety of the entire system? Well, if +Rust code segfaults, it _must_ be because of unsafe code somewhere. By +annotating exactly where that is, you have a significantly smaller area to +search. + +We haven't even talked about any examples here, and that's because I want to +emphasize that you should not be writing unsafe code unless you know exactly +what you're doing. The vast majority of Rust developers will only interact with +it when doing FFI, and advanced library authors may use it to build certain +kinds of abstraction. + +# Conclusion + +We covered a lot of ground here. When you've mastered everything in this Guide, +you will have a firm grasp of basic Rust development. There's a whole lot more +out there, we've just covered the surface. There's tons of topics that you can +dig deeper into, and we've built specialized guides for many of them. To learn +more, dig into the [full documentation +index](http://doc.rust-lang.org/index.html). + +Happy hacking! diff --git a/src/doc/rust.md b/src/doc/rust.md index 33216c64a33..88c22024a60 100644 --- a/src/doc/rust.md +++ b/src/doc/rust.md @@ -169,6 +169,10 @@ sequence (`/**`), are interpreted as a special syntax for `doc` `#[doc="..."]` around the body of the comment (this includes the comment characters themselves, ie `/// Foo` turns into `#[doc="/// Foo"]`). +`//!` comments apply to the parent of the comment, rather than the item that +follows. `//!` comments are usually used to display information on the crate +index page. + Non-doc comments are interpreted as a form of whitespace. ## Whitespace @@ -887,9 +891,8 @@ There are several kinds of view item: ##### Extern crate declarations ~~~~ {.ebnf .gram} -extern_crate_decl : "extern" "crate" ident [ '(' link_attrs ')' ] ? [ '=' string_lit ] ? ; -link_attrs : link_attr [ ',' link_attrs ] + ; -link_attr : ident '=' literal ; +extern_crate_decl : "extern" "crate" crate_name +crate_name: ident | ( string_lit as ident ) ~~~~ An _`extern crate` declaration_ specifies a dependency on an external crate. @@ -909,11 +912,9 @@ Four examples of `extern crate` declarations: ~~~~ {.ignore} extern crate pcre; -extern crate std; // equivalent to: extern crate std = "std"; - -extern crate ruststd = "std"; // linking to 'std' under another name +extern crate std; // equivalent to: extern crate std as std; -extern crate foo = "some/where/rust-foo#foo:1.0"; // a full crate ID for external tools +extern crate "std" as ruststd; // linking to 'std' under another name ~~~~ ##### Use declarations @@ -1304,6 +1305,9 @@ struct Cookie; let c = [Cookie, Cookie, Cookie, Cookie]; ~~~~ +The precise memory layout of a structure is not specified. One can specify a +particular layout using the [`repr` attribute](#ffi-attributes). + By using the `struct_inherit` feature gate, structures may use single inheritance. A Structure may only inherit from a single other structure, called the _super-struct_. The inheriting structure (sub-struct) acts as if all fields in the super-struct were present in the sub-struct. Fields declared in a sub-struct @@ -1937,6 +1941,23 @@ interpreted: - `linkage` - on a static, this specifies the [linkage type](http://llvm.org/docs/LangRef.html#linkage-types). +On `enum`s: + +- `repr` - on C-like enums, this sets the underlying type used for + representation. Takes one argument, which is the primitive + type this enum should be represented for, or `C`, which specifies that it + should be the default `enum` size of the C ABI for that platform. Note that + enum representation in C is undefined, and this may be incorrect when the C + code is compiled with certain flags. + +On `struct`s: + +- `repr` - specifies the representation to use for this struct. Takes a list + of options. The currently accepted ones are `C` and `packed`, which may be + combined. `C` will use a C ABI comptible struct layout, and `packed` will + remove any padding between fields (note that this is very fragile and may + break platforms which require aligned access). + ### Miscellaneous attributes - `export_name` - on statics and functions, this determines the name of the @@ -1954,12 +1975,6 @@ interpreted: crate at compile-time and use any syntax extensions or lints that the crate defines. They can both be specified, `#[phase(link, plugin)]` to use a crate both at runtime and compiletime. -- `repr` - on C-like enums, this sets the underlying type used for - representation. Useful for FFI. Takes one argument, which is the primitive - type this enum should be represented for, or `C`, which specifies that it - should be the default `enum` size of the C ABI for that platform. Note that - enum representation in C is undefined, and this may be incorrect when the C - code is compiled with certain flags. - `simd` - on certain tuple structs, derive the arithmetic operators, which lower to the target's SIMD instructions, if any; the `simd` feature gate is necessary to use this attribute. diff --git a/src/etc/maketest.py b/src/etc/maketest.py index 0e2c1e77ab4..b46a3b03600 100644 --- a/src/etc/maketest.py +++ b/src/etc/maketest.py @@ -15,13 +15,14 @@ import sys # msys1/msys2 automatically converts `/abs/path1:/abs/path2` into # `c:\real\abs\path1;c:\real\abs\path2` (semicolons) if shell thinks # the value is list of paths. +# (if there is only one path, it becomes `c:/real/abs/path`.) # this causes great confusion and error: shell and Makefile doesn't like # windows paths so it is really error-prone. revert it for peace. def normalize_path(v): - # c:\path -> /c/path - if ':\\' in v: - v = '/' + v.replace(':\\', '/') v = v.replace('\\', '/') + # c:/path -> /c/path + if ':/' in v: + v = '/' + v.replace(':/', '/') return v diff --git a/src/etc/mklldeps.py b/src/etc/mklldeps.py index 5d0f35fbd60..f184e07891b 100644 --- a/src/etc/mklldeps.py +++ b/src/etc/mklldeps.py @@ -68,8 +68,6 @@ for llconfig in sys.argv[4:]: ] f.write("#[cfg(" + ', '.join(cfg) + ")]\n") - if os == "windows": # NOTE: Remove after snapshot - f.write("#[cfg(stage0, target_arch = \"%s\", target_os = \"win32\")]\n" % (arch,)) version = run([llconfig, '--version']).strip() diff --git a/src/liballoc/arc.rs b/src/liballoc/arc.rs index 1d6714430a8..0d9e4f0a1c2 100644 --- a/src/liballoc/arc.rs +++ b/src/liballoc/arc.rs @@ -77,7 +77,7 @@ struct ArcInner<T> { } impl<T: Sync + Send> Arc<T> { - /// Create an atomically reference counted wrapper. + /// Creates an atomically reference counted wrapper. #[inline] #[stable] pub fn new(data: T) -> Arc<T> { @@ -101,7 +101,7 @@ impl<T: Sync + Send> Arc<T> { unsafe { &*self._ptr } } - /// Downgrades a strong pointer to a weak pointer + /// Downgrades a strong pointer to a weak pointer. /// /// Weak pointers will not keep the data alive. Once all strong references /// to the underlying data have been dropped, the data itself will be @@ -224,7 +224,7 @@ impl<T: Sync + Send> Weak<T> { /// /// This method will fail to upgrade this reference if the strong reference /// count has already reached 0, but if there are still other active strong - /// references this function will return a new strong reference to the data + /// references this function will return a new strong reference to the data. pub fn upgrade(&self) -> Option<Arc<T>> { // We use a CAS loop to increment the strong count instead of a // fetch_add because once the count hits 0 is must never be above 0. diff --git a/src/liballoc/boxed.rs b/src/liballoc/boxed.rs index 58278d5664e..13d4a0a1f0a 100644 --- a/src/liballoc/boxed.rs +++ b/src/liballoc/boxed.rs @@ -1,4 +1,4 @@ -// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -//! A unique pointer type +//! A unique pointer type. use core::any::{Any, AnyRefExt}; use core::clone::Clone; @@ -26,12 +26,14 @@ use core::result::{Ok, Err, Result}; /// /// The following two examples are equivalent: /// -/// use std::boxed::HEAP; +/// ```rust +/// use std::boxed::HEAP; /// -/// # struct Bar; -/// # impl Bar { fn new(_a: int) { } } -/// let foo = box(HEAP) Bar::new(2); -/// let foo = box Bar::new(2); +/// # struct Bar; +/// # impl Bar { fn new(_a: int) { } } +/// let foo = box(HEAP) Bar::new(2); +/// let foo = box Bar::new(2); +/// ``` #[lang = "exchange_heap"] #[experimental = "may be renamed; uncertain about custom allocator design"] pub static HEAP: () = (); @@ -47,11 +49,11 @@ impl<T: Default> Default for Box<T> { #[unstable] impl<T: Clone> Clone for Box<T> { - /// Return a copy of the owned box. + /// Returns a copy of the owned box. #[inline] fn clone(&self) -> Box<T> { box {(**self).clone()} } - /// Perform copy-assignment from `source` by reusing the existing allocation. + /// Performs copy-assignment from `source` by reusing the existing allocation. #[inline] fn clone_from(&mut self, source: &Box<T>) { (**self).clone_from(&(**source)); @@ -86,7 +88,7 @@ impl<T: Ord> Ord for Box<T> { } impl<T: Eq> Eq for Box<T> {} -/// Extension methods for an owning `Any` trait object +/// Extension methods for an owning `Any` trait object. #[unstable = "post-DST and coherence changes, this will not be a trait but \ rather a direct `impl` on `Box<Any>`"] pub trait BoxAny { @@ -103,9 +105,9 @@ pub trait BoxAny { } #[stable] -impl BoxAny for Box<Any> { +impl BoxAny for Box<Any+'static> { #[inline] - fn downcast<T: 'static>(self) -> Result<Box<T>, Box<Any>> { + fn downcast<T: 'static>(self) -> Result<Box<T>, Box<Any+'static>> { if self.is::<T>() { unsafe { // Get the raw representation of the trait object @@ -130,7 +132,7 @@ impl<T: fmt::Show> fmt::Show for Box<T> { } } -impl fmt::Show for Box<Any> { +impl fmt::Show for Box<Any+'static> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.pad("Box<Any>") } diff --git a/src/liballoc/heap.rs b/src/liballoc/heap.rs index 3175c516d8e..38216fa5b59 100644 --- a/src/liballoc/heap.rs +++ b/src/liballoc/heap.rs @@ -12,10 +12,11 @@ // FIXME: #13996: mark the `allocate` and `reallocate` return value as `noalias` // and `nonnull` +use core::ptr::RawPtr; #[cfg(not(test))] use core::raw; #[cfg(not(test))] use util; -/// Return a pointer to `size` bytes of memory. +/// Returns a pointer to `size` bytes of memory. /// /// Behavior is undefined if the requested size is 0 or the alignment is not a /// power of 2. The alignment must be no larger than the largest supported page @@ -25,7 +26,7 @@ pub unsafe fn allocate(size: uint, align: uint) -> *mut u8 { imp::allocate(size, align) } -/// Extend or shrink the allocation referenced by `ptr` to `size` bytes of +/// Extends or shrinks the allocation referenced by `ptr` to `size` bytes of /// memory. /// /// Behavior is undefined if the requested size is 0 or the alignment is not a @@ -41,10 +42,10 @@ pub unsafe fn reallocate(ptr: *mut u8, size: uint, align: uint, imp::reallocate(ptr, size, align, old_size) } -/// Extend or shrink the allocation referenced by `ptr` to `size` bytes of +/// Extends or shrinks the allocation referenced by `ptr` to `size` bytes of /// memory in-place. /// -/// Return true if successful, otherwise false if the allocation was not +/// Returns true if successful, otherwise false if the allocation was not /// altered. /// /// Behavior is undefined if the requested size is 0 or the alignment is not a @@ -60,7 +61,7 @@ pub unsafe fn reallocate_inplace(ptr: *mut u8, size: uint, align: uint, imp::reallocate_inplace(ptr, size, align, old_size) } -/// Deallocate the memory referenced by `ptr`. +/// Deallocates the memory referenced by `ptr`. /// /// The `ptr` parameter must not be null. /// @@ -69,17 +70,22 @@ pub unsafe fn reallocate_inplace(ptr: *mut u8, size: uint, align: uint, /// the value returned by `usable_size` for the requested size. #[inline] pub unsafe fn deallocate(ptr: *mut u8, size: uint, align: uint) { + // FIXME(14395) This is only required for DST ~[T], it should be removed once + // we fix that representation to not use null pointers. + if ptr.is_null() { + return; + } imp::deallocate(ptr, size, align) } -/// Return the usable size of an allocation created with the specified the +/// Returns the usable size of an allocation created with the specified the /// `size` and `align`. #[inline] pub fn usable_size(size: uint, align: uint) -> uint { imp::usable_size(size, align) } -/// Print implementation-defined allocator statistics. +/// Prints implementation-defined allocator statistics. /// /// These statistics may be inconsistent if other threads use the allocator /// during the call. @@ -88,7 +94,7 @@ pub fn stats_print() { imp::stats_print(); } -// The compiler never calls `exchange_free` on ~ZeroSizeType, so zero-size +// The compiler never calls `exchange_free` on Box<ZeroSizeType>, so zero-size // allocations can point to this `static`. It would be incorrect to use a null // pointer, due to enums assuming types like unique pointers are never null. pub static mut EMPTY: uint = 12345; @@ -208,6 +214,7 @@ mod imp { #[cfg(not(jemalloc), unix)] mod imp { + use core::cmp; use core::mem; use core::ptr; use libc; @@ -248,7 +255,7 @@ mod imp { pub unsafe fn reallocate(ptr: *mut u8, size: uint, align: uint, old_size: uint) -> *mut u8 { let new_ptr = allocate(size, align); - ptr::copy_memory(new_ptr, ptr as *const u8, old_size); + ptr::copy_memory(new_ptr, ptr as *const u8, cmp::min(size, old_size)); deallocate(ptr, old_size, align); return new_ptr; } diff --git a/src/liballoc/lib.rs b/src/liballoc/lib.rs index 7809c17d938..cacb9e28989 100644 --- a/src/liballoc/lib.rs +++ b/src/liballoc/lib.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -//! Rust's core allocation library +//! # The Rust core allocation library //! //! This is the lowest level library through which allocation in Rust can be //! performed where the allocation is assumed to succeed. This library will @@ -23,13 +23,13 @@ //! //! ## Boxed values //! -//! The [`Box`](boxed/index.html) type is the core owned pointer type in rust. +//! The [`Box`](boxed/index.html) type is the core owned pointer type in Rust. //! There can only be one owner of a `Box`, and the owner can decide to mutate //! the contents, which live on the heap. //! //! This type can be sent among tasks efficiently as the size of a `Box` value -//! is just a pointer. Tree-like data structures are often built on owned -//! pointers because each node often has only one owner, the parent. +//! is the same as that of a pointer. Tree-like data structures are often built +//! with boxes because each node often has only one owner, the parent. //! //! ## Reference counted pointers //! @@ -37,8 +37,8 @@ //! type intended for sharing memory within a task. An `Rc` pointer wraps a //! type, `T`, and only allows access to `&T`, a shared reference. //! -//! This type is useful when inherited mutability is too constraining for an -//! application (such as using `Box`), and is often paired with the `Cell` or +//! This type is useful when inherited mutability (such as using `Box`) is too +//! constraining for an application, and is often paired with the `Cell` or //! `RefCell` types in order to allow mutation. //! //! ## Atomically reference counted pointers diff --git a/src/liballoc/libc_heap.rs b/src/liballoc/libc_heap.rs index 25938ba0d54..e3fa639929f 100644 --- a/src/liballoc/libc_heap.rs +++ b/src/liballoc/libc_heap.rs @@ -1,4 +1,4 @@ -// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // @@ -14,7 +14,7 @@ use libc::{c_void, size_t, free, malloc, realloc}; use core::ptr::{RawPtr, mut_null}; -/// A wrapper around libc::malloc, aborting on out-of-memory +/// A wrapper around libc::malloc, aborting on out-of-memory. #[inline] pub unsafe fn malloc_raw(size: uint) -> *mut u8 { // `malloc(0)` may allocate, but it may also return a null pointer @@ -30,7 +30,7 @@ pub unsafe fn malloc_raw(size: uint) -> *mut u8 { } } -/// A wrapper around libc::realloc, aborting on out-of-memory +/// A wrapper around libc::realloc, aborting on out-of-memory. #[inline] pub unsafe fn realloc_raw(ptr: *mut u8, size: uint) -> *mut u8 { // `realloc(ptr, 0)` may allocate, but it may also return a null pointer diff --git a/src/liballoc/rc.rs b/src/liballoc/rc.rs index 060f9875bfc..ec19844a24a 100644 --- a/src/liballoc/rc.rs +++ b/src/liballoc/rc.rs @@ -8,145 +8,142 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -/*! Task-local reference-counted boxes (`Rc` type) - -The `Rc` type provides shared ownership of an immutable value. Destruction is -deterministic, and will occur as soon as the last owner is gone. It is marked -as non-sendable because it avoids the overhead of atomic reference counting. - -The `downgrade` method can be used to create a non-owning `Weak` pointer to the -box. A `Weak` pointer can be upgraded to an `Rc` pointer, but will return -`None` if the value has already been freed. - -For example, a tree with parent pointers can be represented by putting the -nodes behind strong `Rc` pointers, and then storing the parent pointers as -`Weak` pointers. - - -## Examples - -Consider a scenario where a set of Gadgets are owned by a given Owner. We want -to have our Gadgets point to their Owner. We can't do this with unique -ownership, because more than one gadget may belong to the same Owner. Rc -allows us to share an Owner between multiple Gadgets, and have the Owner kept -alive as long as any Gadget points at it. - -```rust -use std::rc::Rc; - -struct Owner { - name: String - // ...other fields -} - -struct Gadget { - id: int, - owner: Rc<Owner> - // ...other fields -} - -fn main() { - // Create a reference counted Owner. - let gadget_owner : Rc<Owner> = Rc::new( - Owner { name: String::from_str("Gadget Man") } - ); - - // Create Gadgets belonging to gadget_owner. To increment the reference - // count we clone the Rc object. - let gadget1 = Gadget { id: 1, owner: gadget_owner.clone() }; - let gadget2 = Gadget { id: 2, owner: gadget_owner.clone() }; - - drop(gadget_owner); - - // Despite dropping gadget_owner, we're still able to print out the name of - // the Owner of the Gadgets. This is because we've only dropped the - // reference count object, not the Owner it wraps. As long as there are - // other Rc objects pointing at the same Owner, it will stay alive. Notice - // that the Rc wrapper around Gadget.owner gets automatically dereferenced - // for us. - println!("Gadget {} owned by {}", gadget1.id, gadget1.owner.name); - println!("Gadget {} owned by {}", gadget2.id, gadget2.owner.name); - - // At the end of the method, gadget1 and gadget2 get destroyed, and with - // them the last counted references to our Owner. Gadget Man now gets - // destroyed as well. -} -``` - -If our requirements change, and we also need to be able to traverse from -Owner->Gadget, we will run into problems: an Rc pointer from Owner->Gadget -introduces a cycle between the objects. This means that their reference counts -can never reach 0, and the objects will stay alive: a memory leak. In order to -get around this, we can use `Weak` pointers. These are reference counted -pointers that don't keep an object alive if there are no normal `Rc` (or -*strong*) pointers left. - -Rust actually makes it somewhat difficult to produce this loop in the first -place: in order to end up with two objects that point at each other, one of -them needs to be mutable. This is problematic because Rc enforces memory -safety by only giving out shared references to the object it wraps, and these -don't allow direct mutation. We need to wrap the part of the object we wish to -mutate in a `RefCell`, which provides *interior mutability*: a method to -achieve mutability through a shared reference. `RefCell` enforces Rust's -borrowing rules at runtime. Read the `Cell` documentation for more details on -interior mutability. - -```rust -use std::rc::Rc; -use std::rc::Weak; -use std::cell::RefCell; - -struct Owner { - name: String, - gadgets: RefCell<Vec<Weak<Gadget>>> - // ...other fields -} - -struct Gadget { - id: int, - owner: Rc<Owner> - // ...other fields -} - -fn main() { - // Create a reference counted Owner. Note the fact that we've put the - // Owner's vector of Gadgets inside a RefCell so that we can mutate it - // through a shared reference. - let gadget_owner : Rc<Owner> = Rc::new( - Owner { - name: "Gadget Man".to_string(), - gadgets: RefCell::new(Vec::new()) - } - ); - - // Create Gadgets belonging to gadget_owner as before. - let gadget1 = Rc::new(Gadget{id: 1, owner: gadget_owner.clone()}); - let gadget2 = Rc::new(Gadget{id: 2, owner: gadget_owner.clone()}); - - // Add the Gadgets to their Owner. To do this we mutably borrow from - // the RefCell holding the Owner's Gadgets. - gadget_owner.gadgets.borrow_mut().push(gadget1.clone().downgrade()); - gadget_owner.gadgets.borrow_mut().push(gadget2.clone().downgrade()); - - // Iterate over our Gadgets, printing their details out - for gadget_opt in gadget_owner.gadgets.borrow().iter() { - - // gadget_opt is a Weak<Gadget>. Since weak pointers can't guarantee - // that their object is still alive, we need to call upgrade() on them - // to turn them into a strong reference. This returns an Option, which - // contains a reference to our object if it still exists. - let gadget = gadget_opt.upgrade().unwrap(); - println!("Gadget {} owned by {}", gadget.id, gadget.owner.name); - } - - // At the end of the method, gadget_owner, gadget1 and gadget2 get - // destroyed. There are now no strong (Rc) references to the gadgets. - // Once they get destroyed, the Gadgets get destroyed. This zeroes the - // reference count on Gadget Man, so he gets destroyed as well. -} -``` - -*/ +//! Task-local reference-counted boxes (the `Rc` type). +//! +//! The `Rc` type provides shared ownership of an immutable value. Destruction is +//! deterministic, and will occur as soon as the last owner is gone. It is marked +//! as non-sendable because it avoids the overhead of atomic reference counting. +//! +//! The `downgrade` method can be used to create a non-owning `Weak` pointer to the +//! box. A `Weak` pointer can be upgraded to an `Rc` pointer, but will return +//! `None` if the value has already been freed. +//! +//! For example, a tree with parent pointers can be represented by putting the +//! nodes behind strong `Rc` pointers, and then storing the parent pointers as +//! `Weak` pointers. +//! +//! # Examples +//! +//! Consider a scenario where a set of `Gadget`s are owned by a given `Owner`. +//! We want to have our `Gadget`s point to their `Owner`. We can't do this with +//! unique ownership, because more than one gadget may belong to the same +//! `Owner`. `Rc` allows us to share an `Owner` between multiple `Gadget`s, and +//! have the `Owner` kept alive as long as any `Gadget` points at it. +//! +//! ```rust +//! use std::rc::Rc; +//! +//! struct Owner { +//! name: String +//! // ...other fields +//! } +//! +//! struct Gadget { +//! id: int, +//! owner: Rc<Owner> +//! // ...other fields +//! } +//! +//! fn main() { +//! // Create a reference counted Owner. +//! let gadget_owner : Rc<Owner> = Rc::new( +//! Owner { name: String::from_str("Gadget Man") } +//! ); +//! +//! // Create Gadgets belonging to gadget_owner. To increment the reference +//! // count we clone the Rc object. +//! let gadget1 = Gadget { id: 1, owner: gadget_owner.clone() }; +//! let gadget2 = Gadget { id: 2, owner: gadget_owner.clone() }; +//! +//! drop(gadget_owner); +//! +//! // Despite dropping gadget_owner, we're still able to print out the name of +//! // the Owner of the Gadgets. This is because we've only dropped the +//! // reference count object, not the Owner it wraps. As long as there are +//! // other Rc objects pointing at the same Owner, it will stay alive. Notice +//! // that the Rc wrapper around Gadget.owner gets automatically dereferenced +//! // for us. +//! println!("Gadget {} owned by {}", gadget1.id, gadget1.owner.name); +//! println!("Gadget {} owned by {}", gadget2.id, gadget2.owner.name); +//! +//! // At the end of the method, gadget1 and gadget2 get destroyed, and with +//! // them the last counted references to our Owner. Gadget Man now gets +//! // destroyed as well. +//! } +//! ``` +//! +//! If our requirements change, and we also need to be able to traverse from +//! Owner → Gadget, we will run into problems: an `Rc` pointer from Owner → Gadget +//! introduces a cycle between the objects. This means that their reference counts +//! can never reach 0, and the objects will stay alive: a memory leak. In order to +//! get around this, we can use `Weak` pointers. These are reference counted +//! pointers that don't keep an object alive if there are no normal `Rc` (or +//! *strong*) pointers left. +//! +//! Rust actually makes it somewhat difficult to produce this loop in the first +//! place: in order to end up with two objects that point at each other, one of +//! them needs to be mutable. This is problematic because `Rc` enforces memory +//! safety by only giving out shared references to the object it wraps, and these +//! don't allow direct mutation. We need to wrap the part of the object we wish to +//! mutate in a `RefCell`, which provides *interior mutability*: a method to +//! achieve mutability through a shared reference. `RefCell` enforces Rust's +//! borrowing rules at runtime. Read the `Cell` documentation for more details on +//! interior mutability. +//! +//! ```rust +//! use std::rc::Rc; +//! use std::rc::Weak; +//! use std::cell::RefCell; +//! +//! struct Owner { +//! name: String, +//! gadgets: RefCell<Vec<Weak<Gadget>>> +//! // ...other fields +//! } +//! +//! struct Gadget { +//! id: int, +//! owner: Rc<Owner> +//! // ...other fields +//! } +//! +//! fn main() { +//! // Create a reference counted Owner. Note the fact that we've put the +//! // Owner's vector of Gadgets inside a RefCell so that we can mutate it +//! // through a shared reference. +//! let gadget_owner : Rc<Owner> = Rc::new( +//! Owner { +//! name: "Gadget Man".to_string(), +//! gadgets: RefCell::new(Vec::new()) +//! } +//! ); +//! +//! // Create Gadgets belonging to gadget_owner as before. +//! let gadget1 = Rc::new(Gadget{id: 1, owner: gadget_owner.clone()}); +//! let gadget2 = Rc::new(Gadget{id: 2, owner: gadget_owner.clone()}); +//! +//! // Add the Gadgets to their Owner. To do this we mutably borrow from +//! // the RefCell holding the Owner's Gadgets. +//! gadget_owner.gadgets.borrow_mut().push(gadget1.clone().downgrade()); +//! gadget_owner.gadgets.borrow_mut().push(gadget2.clone().downgrade()); +//! +//! // Iterate over our Gadgets, printing their details out +//! for gadget_opt in gadget_owner.gadgets.borrow().iter() { +//! +//! // gadget_opt is a Weak<Gadget>. Since weak pointers can't guarantee +//! // that their object is still alive, we need to call upgrade() on them +//! // to turn them into a strong reference. This returns an Option, which +//! // contains a reference to our object if it still exists. +//! let gadget = gadget_opt.upgrade().unwrap(); +//! println!("Gadget {} owned by {}", gadget.id, gadget.owner.name); +//! } +//! +//! // At the end of the method, gadget_owner, gadget1 and gadget2 get +//! // destroyed. There are now no strong (Rc) references to the gadgets. +//! // Once they get destroyed, the Gadgets get destroyed. This zeroes the +//! // reference count on Gadget Man, so he gets destroyed as well. +//! } +//! ``` #![stable] @@ -171,7 +168,7 @@ struct RcBox<T> { weak: Cell<uint> } -/// Immutable reference counted pointer type +/// An immutable reference-counted pointer type. #[unsafe_no_drop_flag] #[stable] pub struct Rc<T> { @@ -184,7 +181,7 @@ pub struct Rc<T> { #[stable] impl<T> Rc<T> { - /// Construct a new reference-counted box + /// Constructs a new reference-counted pointer. pub fn new(value: T) -> Rc<T> { unsafe { Rc { @@ -206,8 +203,8 @@ impl<T> Rc<T> { } impl<T> Rc<T> { - /// Downgrade the reference-counted pointer to a weak reference - #[experimental = "Weak pointers may not belong in this module."] + /// Downgrades the reference-counted pointer to a weak reference. + #[experimental = "Weak pointers may not belong in this module"] pub fn downgrade(&self) -> Weak<T> { self.inc_weak(); Weak { @@ -234,7 +231,7 @@ pub fn is_unique<T>(rc: &Rc<T>) -> bool { /// If the `Rc` does not have unique ownership, `Err` is returned with the /// same `Rc`. /// -/// # Example: +/// # Example /// /// ``` /// use std::rc::{mod, Rc}; @@ -267,7 +264,7 @@ pub fn try_unwrap<T>(rc: Rc<T>) -> Result<T, Rc<T>> { /// /// Returns `None` if the `Rc` does not have unique ownership. /// -/// # Example: +/// # Example /// /// ``` /// use std::rc::{mod, Rc}; @@ -312,7 +309,7 @@ impl<T: Clone> Rc<T> { #[experimental = "Deref is experimental."] impl<T> Deref<T> for Rc<T> { - /// Borrow the value contained in the reference-counted box + /// Borrows the value contained in the reference-counted pointer. #[inline(always)] fn deref(&self) -> &T { &self.inner().value @@ -404,7 +401,7 @@ impl<T: fmt::Show> fmt::Show for Rc<T> { } } -/// Weak reference to a reference-counted box +/// A weak reference to a reference-counted pointer. #[unsafe_no_drop_flag] #[experimental = "Weak pointers may not belong in this module."] pub struct Weak<T> { @@ -417,7 +414,10 @@ pub struct Weak<T> { #[experimental = "Weak pointers may not belong in this module."] impl<T> Weak<T> { - /// Upgrade a weak reference to a strong reference + /// Upgrades a weak reference to a strong reference. + /// + /// Returns `None` if there were no strong references and the data was + /// destroyed. pub fn upgrade(&self) -> Option<Rc<T>> { if self.strong() == 0 { None diff --git a/src/libarena/lib.rs b/src/libarena/lib.rs index 5d316cdb51e..f212cdd4638 100644 --- a/src/libarena/lib.rs +++ b/src/libarena/lib.rs @@ -7,7 +7,7 @@ // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your // option. This file may not be copied, modified, or distributed // except according to those terms. -// + //! The arena, a fast but limited type of allocator. //! //! Arenas are a type of allocator that destroy the objects within, all at @@ -15,9 +15,9 @@ //! of individual objects while the arena itself is still alive. The benefit //! of an arena is very fast allocation; just a pointer bump. //! -//! This crate has two arenas implemented: TypedArena, which is a simpler -//! arena but can only hold objects of a single type, and Arena, which is a -//! more complex, slower Arena which can hold objects of any type. +//! This crate has two arenas implemented: `TypedArena`, which is a simpler +//! arena but can only hold objects of a single type, and `Arena`, which is a +//! more complex, slower arena which can hold objects of any type. #![crate_name = "arena"] #![experimental] @@ -62,24 +62,24 @@ impl Chunk { /// A slower reflection-based arena that can allocate objects of any type. /// -/// This arena uses Vec<u8> as a backing store to allocate objects from. For +/// This arena uses `Vec<u8>` as a backing store to allocate objects from. For /// each allocated object, the arena stores a pointer to the type descriptor -/// followed by the object. (Potentially with alignment padding after each -/// element.) When the arena is destroyed, it iterates through all of its +/// followed by the object (potentially with alignment padding after each +/// element). When the arena is destroyed, it iterates through all of its /// chunks, and uses the tydesc information to trace through the objects, -/// calling the destructors on them. One subtle point that needs to be +/// calling the destructors on them. One subtle point that needs to be /// addressed is how to handle failures while running the user provided /// initializer function. It is important to not run the destructor on /// uninitialized objects, but how to detect them is somewhat subtle. Since -/// alloc() can be invoked recursively, it is not sufficient to simply exclude +/// `alloc()` can be invoked recursively, it is not sufficient to simply exclude /// the most recent object. To solve this without requiring extra space, we /// use the low order bit of the tydesc pointer to encode whether the object /// it describes has been fully initialized. /// -/// As an optimization, objects with destructors are stored in -/// different chunks than objects without destructors. This reduces -/// overhead when initializing plain-old-data and means we don't need -/// to waste time running the destructors of POD. +/// As an optimization, objects with destructors are stored in different chunks +/// than objects without destructors. This reduces overhead when initializing +/// plain-old-data (`Copy` types) and means we don't need to waste time running +/// their destructors. pub struct Arena { // The head is separated out from the list as a unbenchmarked // microoptimization, to avoid needing to case on the list to access the @@ -90,12 +90,12 @@ pub struct Arena { } impl Arena { - /// Allocate a new Arena with 32 bytes preallocated. + /// Allocates a new Arena with 32 bytes preallocated. pub fn new() -> Arena { Arena::new_with_size(32u) } - /// Allocate a new Arena with `initial_size` bytes preallocated. + /// Allocates a new Arena with `initial_size` bytes preallocated. pub fn new_with_size(initial_size: uint) -> Arena { Arena { head: RefCell::new(chunk(initial_size, false)), @@ -282,8 +282,8 @@ impl Arena { } } - /// Allocate a new item in the arena, using `op` to initialize the value - /// and returning a reference to it. + /// Allocates a new item in the arena, using `op` to initialize the value, + /// and returns a reference to it. #[inline] pub fn alloc<T>(&self, op: || -> T) -> &T { unsafe { @@ -438,13 +438,13 @@ impl<T> TypedArenaChunk<T> { } impl<T> TypedArena<T> { - /// Creates a new TypedArena with preallocated space for 8 objects. + /// Creates a new `TypedArena` with preallocated space for eight objects. #[inline] pub fn new() -> TypedArena<T> { TypedArena::with_capacity(8) } - /// Creates a new TypedArena with preallocated space for the given number of + /// Creates a new `TypedArena` with preallocated space for the given number of /// objects. #[inline] pub fn with_capacity(capacity: uint) -> TypedArena<T> { @@ -456,7 +456,7 @@ impl<T> TypedArena<T> { } } - /// Allocates an object in the TypedArena, returning a reference to it. + /// Allocates an object in the `TypedArena`, returning a reference to it. #[inline] pub fn alloc(&self, object: T) -> &T { if self.ptr == self.end { @@ -563,7 +563,7 @@ mod tests { struct Noncopy { string: String, - array: Vec<int> , + array: Vec<int>, } #[test] diff --git a/src/libcollections/bitv.rs b/src/libcollections/bitv.rs index 1b3c6e148cd..d13767077c7 100644 --- a/src/libcollections/bitv.rs +++ b/src/libcollections/bitv.rs @@ -66,7 +66,7 @@ use core::prelude::*; use core::cmp; use core::default::Default; use core::fmt; -use core::iter::Take; +use core::iter::{Chain, Enumerate, Repeat, Skip, Take}; use core::iter; use core::slice; use core::uint; @@ -75,25 +75,22 @@ use std::hash; use {Mutable, Set, MutableSet, MutableSeq}; use vec::Vec; +type MatchWords<'a> = Chain<MaskWords<'a>, Skip<Take<Enumerate<Repeat<uint>>>>>; // Take two BitV's, and return iterators of their words, where the shorter one // has been padded with 0's -macro_rules! match_words( - ($a_expr:expr, $b_expr:expr) => ({ - let a = $a_expr; - let b = $b_expr; - let a_len = a.storage.len(); - let b_len = b.storage.len(); - - // have to uselessly pretend to pad the longer one for type matching - if a_len < b_len { - (a.mask_words(0).chain(iter::Repeat::new(0u).enumerate().take(b_len).skip(a_len)), - b.mask_words(0).chain(iter::Repeat::new(0u).enumerate().take(0).skip(0))) - } else { - (a.mask_words(0).chain(iter::Repeat::new(0u).enumerate().take(0).skip(0)), - b.mask_words(0).chain(iter::Repeat::new(0u).enumerate().take(a_len).skip(b_len))) - } - }) -) +fn match_words <'a,'b>(a: &'a Bitv, b: &'b Bitv) -> (MatchWords<'a>, MatchWords<'b>) { + let a_len = a.storage.len(); + let b_len = b.storage.len(); + + // have to uselessly pretend to pad the longer one for type matching + if a_len < b_len { + (a.mask_words(0).chain(Repeat::new(0u).enumerate().take(b_len).skip(a_len)), + b.mask_words(0).chain(Repeat::new(0u).enumerate().take(0).skip(0))) + } else { + (a.mask_words(0).chain(Repeat::new(0u).enumerate().take(0).skip(0)), + b.mask_words(0).chain(Repeat::new(0u).enumerate().take(a_len).skip(b_len))) + } +} static TRUE: bool = true; static FALSE: bool = false; @@ -112,7 +109,7 @@ struct BigBitv { #[deriving(Clone)] enum BitvVariant { Big(BigBitv), Small(SmallBitv) } -/// The bitvector type +/// The bitvector type. /// /// # Example /// @@ -225,7 +222,7 @@ impl Bitv { } } - /// Create an empty Bitv. + /// Creates an empty `Bitv`. /// /// # Example /// @@ -237,7 +234,7 @@ impl Bitv { Bitv { storage: Vec::new(), nbits: 0 } } - /// Create a Bitv that holds `nbits` elements, setting each element + /// Creates a `Bitv` that holds `nbits` elements, setting each element /// to `init`. /// /// # Example @@ -259,11 +256,11 @@ impl Bitv { } } - /// Retrieve the value at index `i`. + /// Retrieves the value at index `i`. /// /// # Failure /// - /// Assert if `i` out of bounds. + /// Fails if `i` is out of bounds. /// /// # Example /// @@ -286,11 +283,11 @@ impl Bitv { x != 0 } - /// Set the value of a bit at a index `i`. + /// Sets the value of a bit at a index `i`. /// /// # Failure /// - /// Assert if `i` out of bounds. + /// Fails if `i` is out of bounds. /// /// # Example /// @@ -311,7 +308,7 @@ impl Bitv { else { self.storage[w] & !flag }; } - /// Set all bits to 1. + /// Sets all bits to 1. /// /// # Example /// @@ -330,7 +327,7 @@ impl Bitv { for w in self.storage.mut_iter() { *w = !0u; } } - /// Flip all bits. + /// Flips all bits. /// /// # Example /// @@ -349,14 +346,15 @@ impl Bitv { for w in self.storage.mut_iter() { *w = !*w; } } - /// Calculate the union of two bitvectors, acts like bitwise or. + /// Calculates the union of two bitvectors. This acts like the bitwise `or` + /// function. /// - /// Set `self` to the union of `self` and `other`. Both bitvectors must be - /// the same length. Return `true` if `self` changed. + /// Sets `self` to the union of `self` and `other`. Both bitvectors must be + /// the same length. Returns `true` if `self` changed. /// /// # Failure /// - /// Assert if the bitvectors are of different length. + /// Fails if the bitvectors are of different lengths. /// /// # Example /// @@ -378,14 +376,15 @@ impl Bitv { self.process(other, |w1, w2| w1 | w2) } - /// Calculate the intersection of two bitvectors, acts like bitwise and. + /// Calculates the intersection of two bitvectors. This acts like the + /// bitwise `and` function. /// - /// Set `self` to the intersection of `self` and `other`. Both bitvectors - /// must be the same length. Return `true` if `self` changed. + /// Sets `self` to the intersection of `self` and `other`. Both bitvectors + /// must be the same length. Returns `true` if `self` changed. /// /// # Failure /// - /// Assert if the bitvectors are of different length. + /// Fails if the bitvectors are of different lengths. /// /// # Example /// @@ -407,15 +406,15 @@ impl Bitv { self.process(other, |w1, w2| w1 & w2) } - /// Calculate the difference between two bitvectors. + /// Calculates the difference between two bitvectors. /// - /// Set each element of `self` to the value of that element minus the + /// Sets each element of `self` to the value of that element minus the /// element of `other` at the same index. Both bitvectors must be the same - /// length. Return `true` if `self` changed. + /// length. Returns `true` if `self` changed. /// /// # Failure /// - /// Assert if the bitvectors are of different length. + /// Fails if the bitvectors are of different length. /// /// # Example /// @@ -467,7 +466,7 @@ impl Bitv { (last_word == ((1 << self.nbits % uint::BITS) - 1) || last_word == !0u) } - /// Return an iterator over the elements of the vector in order. + /// Returns an iterator over the elements of the vector in order. /// /// # Example /// @@ -482,7 +481,7 @@ impl Bitv { Bits {bitv: self, next_idx: 0, end_idx: self.nbits} } - /// Return `true` if all bits are 0. + /// Returns `true` if all bits are 0. /// /// # Example /// @@ -499,7 +498,7 @@ impl Bitv { self.mask_words(0).all(|(_, w)| w == 0) } - /// Return `true` if any bit is 1. + /// Returns `true` if any bit is 1. /// /// # Example /// @@ -517,9 +516,9 @@ impl Bitv { !self.none() } - /// Organise the bits into bytes, such that the first bit in the + /// Organises the bits into bytes, such that the first bit in the /// `Bitv` becomes the high-order bit of the first byte. If the - /// size of the `Bitv` is not a multiple of 8 then trailing bits + /// size of the `Bitv` is not a multiple of eight then trailing bits /// will be filled-in with `false`. /// /// # Example @@ -562,7 +561,7 @@ impl Bitv { ) } - /// Transform `self` into a `Vec<bool>` by turning each bit into a `bool`. + /// Transforms `self` into a `Vec<bool>` by turning each bit into a `bool`. /// /// # Example /// @@ -577,11 +576,12 @@ impl Bitv { Vec::from_fn(self.nbits, |i| self.get(i)) } - /// Compare a bitvector to a vector of `bool`. - /// Both the bitvector and vector must have the same length. + /// Compares a `Bitv` to a slice of `bool`s. + /// Both the `Bitv` and slice must have the same length. + /// /// # Failure /// - /// Assert if the bitvectors are of different length. + /// Fails if the the `Bitv` and slice are of different length. /// /// # Example /// @@ -603,7 +603,7 @@ impl Bitv { true } - /// Shorten a Bitv, dropping excess elements. + /// Shortens a `Bitv`, dropping excess elements. /// /// If `len` is greater than the vector's current length, this has no /// effect. @@ -629,7 +629,7 @@ impl Bitv { } } - /// Grow the vector to be able to store `size` bits without resizing. + /// Grows the vector to be able to store `size` bits without resizing. /// /// # Example /// @@ -649,7 +649,7 @@ impl Bitv { } } - /// Return the capacity in bits for this bit vector. Inserting any + /// Returns the capacity in bits for this bit vector. Inserting any /// element less than this amount will not trigger a resizing. /// /// # Example @@ -666,7 +666,7 @@ impl Bitv { self.storage.len() * uint::BITS } - /// Grow the `Bitv` in-place. Add `n` copies of `value` to the `Bitv`. + /// Grows the `Bitv` in-place, adding `n` copies of `value` to the `Bitv`. /// /// # Example /// @@ -707,7 +707,7 @@ impl Bitv { self.nbits = new_nbits; } - /// Shorten by one and return the removed element. + /// Shortens by one element and returns the removed element. /// /// # Failure /// @@ -734,7 +734,7 @@ impl Bitv { ret } - /// Push a `bool` onto the end. + /// Pushes a `bool` onto the end. /// /// # Example /// @@ -756,7 +756,7 @@ impl Bitv { } } -/// Transform a byte-vector into a `Bitv`. Each byte becomes 8 bits, +/// Transforms a byte-vector into a `Bitv`. Each byte becomes eight bits, /// with the most significant bits of each byte coming first. Each /// bit becomes `true` if equal to 1 or `false` if equal to 0. /// @@ -779,7 +779,7 @@ pub fn from_bytes(bytes: &[u8]) -> Bitv { }) } -/// Create a `Bitv` of the specified length where the value at each +/// Creates a `Bitv` of the specified length where the value at each /// index is `f(index)`. /// /// # Example @@ -1014,7 +1014,7 @@ impl Extendable<bool> for BitvSet { impl PartialOrd for BitvSet { #[inline] fn partial_cmp(&self, other: &BitvSet) -> Option<Ordering> { - let (a_iter, b_iter) = match_words!(self.get_ref(), other.get_ref()); + let (a_iter, b_iter) = match_words(self.get_ref(), other.get_ref()); iter::order::partial_cmp(a_iter, b_iter) } } @@ -1022,7 +1022,7 @@ impl PartialOrd for BitvSet { impl Ord for BitvSet { #[inline] fn cmp(&self, other: &BitvSet) -> Ordering { - let (a_iter, b_iter) = match_words!(self.get_ref(), other.get_ref()); + let (a_iter, b_iter) = match_words(self.get_ref(), other.get_ref()); iter::order::cmp(a_iter, b_iter) } } @@ -1030,7 +1030,7 @@ impl Ord for BitvSet { impl cmp::PartialEq for BitvSet { #[inline] fn eq(&self, other: &BitvSet) -> bool { - let (a_iter, b_iter) = match_words!(self.get_ref(), other.get_ref()); + let (a_iter, b_iter) = match_words(self.get_ref(), other.get_ref()); iter::order::eq(a_iter, b_iter) } } @@ -1038,7 +1038,7 @@ impl cmp::PartialEq for BitvSet { impl cmp::Eq for BitvSet {} impl BitvSet { - /// Create a new bit vector set with initially no contents. + /// Creates a new bit vector set with initially no contents. /// /// # Example /// @@ -1051,7 +1051,7 @@ impl BitvSet { BitvSet(Bitv::new()) } - /// Create a new bit vector set with initially no contents, able to + /// Creates a new bit vector set with initially no contents, able to /// hold `nbits` elements without resizing. /// /// # Example @@ -1066,7 +1066,7 @@ impl BitvSet { BitvSet(Bitv::with_capacity(nbits, false)) } - /// Create a new bit vector set from the given bit vector. + /// Creates a new bit vector set from the given bit vector. /// /// # Example /// @@ -1119,7 +1119,7 @@ impl BitvSet { bitv.reserve(size) } - /// Consume this set to return the underlying bit vector. + /// Consumes this set to return the underlying bit vector. /// /// # Example /// @@ -1139,7 +1139,7 @@ impl BitvSet { bitv } - /// Return a reference to the underlying bit vector. + /// Returns a reference to the underlying bit vector. /// /// # Example /// @@ -1158,7 +1158,7 @@ impl BitvSet { bitv } - /// Return a mutable reference to the underlying bit vector. + /// Returns a mutable reference to the underlying bit vector. /// /// # Example /// @@ -1191,10 +1191,10 @@ impl BitvSet { self_bitv.reserve(other_bitv.capacity()); // virtually pad other with 0's for equal lengths - let self_len = self_bitv.storage.len(); - let other_len = other_bitv.storage.len(); - let mut other_words = other_bitv.mask_words(0) - .chain(iter::Repeat::new(0u).enumerate().take(self_len).skip(other_len)); + let mut other_words = { + let (_, result) = match_words(self_bitv, other_bitv); + result + }; // Apply values found in other for (i, w) in other_words { @@ -1204,7 +1204,7 @@ impl BitvSet { } } - /// Truncate the underlying vector to the least length required. + /// Truncates the underlying vector to the least length required. /// /// # Example /// @@ -1235,7 +1235,7 @@ impl BitvSet { bitv.nbits = trunc_len * uint::BITS; } - /// Iterator over each uint stored in the BitvSet. + /// Iterator over each uint stored in the `BitvSet`. /// /// # Example /// @@ -1376,7 +1376,7 @@ impl BitvSet { } } - /// Union in-place with the specified other bit vector. + /// Unions in-place with the specified other bit vector. /// /// # Example /// @@ -1399,7 +1399,7 @@ impl BitvSet { self.other_op(other, |w1, w2| w1 | w2); } - /// Intersect in-place with the specified other bit vector. + /// Intersects in-place with the specified other bit vector. /// /// # Example /// @@ -1422,7 +1422,8 @@ impl BitvSet { self.other_op(other, |w1, w2| w1 & w2); } - /// Difference in-place with the specified other bit vector. + /// Makes this bit vector the difference with the specified other bit vector + /// in-place. /// /// # Example /// @@ -1452,7 +1453,8 @@ impl BitvSet { self.other_op(other, |w1, w2| w1 & !w2); } - /// Symmetric difference in-place with the specified other bit vector. + /// Makes this bit vector the symmetric difference with the specified other + /// bit vector in-place. /// /// # Example /// @@ -1524,7 +1526,7 @@ impl Set<uint> for BitvSet { #[inline] fn is_disjoint(&self, other: &BitvSet) -> bool { - self.intersection(other).count() > 0 + self.intersection(other).next().is_none() } #[inline] @@ -2267,6 +2269,24 @@ mod tests { } #[test] + fn test_bitv_set_is_disjoint() { + let a = BitvSet::from_bitv(from_bytes([0b10100010])); + let b = BitvSet::from_bitv(from_bytes([0b01000000])); + let c = BitvSet::new(); + let d = BitvSet::from_bitv(from_bytes([0b00110000])); + + assert!(!a.is_disjoint(&d)); + assert!(!d.is_disjoint(&a)); + + assert!(a.is_disjoint(&b)) + assert!(a.is_disjoint(&c)) + assert!(b.is_disjoint(&a)) + assert!(b.is_disjoint(&c)) + assert!(c.is_disjoint(&a)) + assert!(c.is_disjoint(&b)) + } + + #[test] fn test_bitv_set_intersect_with() { // Explicitly 0'ed bits let mut a = BitvSet::from_bitv(from_bytes([0b10100010])); @@ -2537,7 +2557,7 @@ mod tests { } fn rng() -> rand::IsaacRng { - let seed = &[1, 2, 3, 4, 5, 6, 7, 8, 9, 0]; + let seed: &[_] = &[1, 2, 3, 4, 5, 6, 7, 8, 9, 0]; rand::SeedableRng::from_seed(seed) } @@ -2546,7 +2566,9 @@ mod tests { let mut r = rng(); let mut bitv = 0 as uint; b.iter(|| { - bitv |= 1 << ((r.next_u32() as uint) % uint::BITS); + for _ in range(0u, 100) { + bitv |= 1 << ((r.next_u32() as uint) % uint::BITS); + } &bitv }) } @@ -2556,7 +2578,9 @@ mod tests { let mut r = rng(); let mut bitv = Bitv::with_capacity(BENCH_BITS, false); b.iter(|| { - bitv.set((r.next_u32() as uint) % BENCH_BITS, true); + for _ in range(0u, 100) { + bitv.set((r.next_u32() as uint) % BENCH_BITS, true); + } &bitv }) } @@ -2566,7 +2590,9 @@ mod tests { let mut r = rng(); let mut bitv = Bitv::with_capacity(uint::BITS, false); b.iter(|| { - bitv.set((r.next_u32() as uint) % uint::BITS, true); + for _ in range(0u, 100) { + bitv.set((r.next_u32() as uint) % uint::BITS, true); + } &bitv }) } @@ -2576,7 +2602,9 @@ mod tests { let mut r = rng(); let mut bitv = BitvSet::new(); b.iter(|| { - bitv.insert((r.next_u32() as uint) % uint::BITS); + for _ in range(0u, 100) { + bitv.insert((r.next_u32() as uint) % uint::BITS); + } &bitv }) } @@ -2586,7 +2614,9 @@ mod tests { let mut r = rng(); let mut bitv = BitvSet::new(); b.iter(|| { - bitv.insert((r.next_u32() as uint) % BENCH_BITS); + for _ in range(0u, 100) { + bitv.insert((r.next_u32() as uint) % BENCH_BITS); + } &bitv }) } @@ -2596,18 +2626,21 @@ mod tests { let mut b1 = Bitv::with_capacity(BENCH_BITS, false); let b2 = Bitv::with_capacity(BENCH_BITS, false); b.iter(|| { - b1.union(&b2); + b1.union(&b2) }) } #[bench] - fn bench_btv_small_iter(b: &mut Bencher) { + fn bench_bitv_small_iter(b: &mut Bencher) { let bitv = Bitv::with_capacity(uint::BITS, false); b.iter(|| { - let mut _sum = 0; - for pres in bitv.iter() { - _sum += pres as uint; + let mut sum = 0; + for _ in range(0u, 10) { + for pres in bitv.iter() { + sum += pres as uint; + } } + sum }) } @@ -2615,10 +2648,11 @@ mod tests { fn bench_bitv_big_iter(b: &mut Bencher) { let bitv = Bitv::with_capacity(BENCH_BITS, false); b.iter(|| { - let mut _sum = 0; + let mut sum = 0; for pres in bitv.iter() { - _sum += pres as uint; + sum += pres as uint; } + sum }) } @@ -2627,10 +2661,11 @@ mod tests { let bitv = BitvSet::from_bitv(from_fn(BENCH_BITS, |idx| {idx % 3 == 0})); b.iter(|| { - let mut _sum = 0; + let mut sum = 0; for idx in bitv.iter() { - _sum += idx; + sum += idx; } + sum }) } } diff --git a/src/libcollections/btree.rs b/src/libcollections/btree.rs index ca4b4ee6e83..f6011976b65 100644 --- a/src/libcollections/btree.rs +++ b/src/libcollections/btree.rs @@ -1,4 +1,4 @@ -// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // @@ -8,8 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. // -// btree.rs -// // NB. this is not deprecated for removal, just deprecating the // current implementation. If the major pain-points are addressed @@ -18,12 +16,12 @@ prefer a HashMap, TreeMap or TrieMap"] #![allow(deprecated)] -//! Starting implementation of a btree for rust. -//! Structure inspired by github user davidhalperin's gist. +//! Starting implementation of a B-tree for Rust. +//! Structure inspired by Github user davidhalperin's gist. -///A B-tree contains a root node (which contains a vector of elements), -///a length (the height of the tree), and lower and upper bounds on the -///number of elements that a given node can contain. +// A B-tree contains a root node (which contains a vector of elements), +// a length (the height of the tree), and lower and upper bounds on the +// number of elements that a given node can contain. use core::prelude::*; @@ -43,9 +41,8 @@ pub struct BTree<K, V> { } impl<K: Ord, V> BTree<K, V> { - - ///Returns new BTree with root node (leaf) and user-supplied lower bound - ///The lower bound applies to every node except the root node. + /// Returns new `BTree` with root node (leaf) and user-supplied lower bound + /// The lower bound applies to every node except the root node. pub fn new(k: K, v: V, lb: uint) -> BTree<K, V> { BTree { root: Node::new_leaf(vec!(LeafElt::new(k, v))), @@ -55,8 +52,8 @@ impl<K: Ord, V> BTree<K, V> { } } - ///Helper function for clone: returns new BTree with supplied root node, - ///length, and lower bound. For use when the length is known already. + /// Helper function for `clone`: returns new BTree with supplied root node, + /// length, and lower bound. For use when the length is known already. fn new_with_node_len(n: Node<K, V>, length: uint, lb: uint) -> BTree<K, V> { @@ -69,17 +66,17 @@ impl<K: Ord, V> BTree<K, V> { } } -//We would probably want to remove the dependence on the Clone trait in the future. -//It is here as a crutch to ensure values can be passed around through the tree's nodes -//especially during insertions and deletions. +// We would probably want to remove the dependence on the Clone trait in the future. +// It is here as a crutch to ensure values can be passed around through the tree's nodes +// especially during insertions and deletions. impl<K: Clone + Ord, V: Clone> BTree<K, V> { - ///Returns the value of a given key, which may not exist in the tree. - ///Calls the root node's get method. + /// Returns the value of a given key, which may not exist in the tree. + /// Calls the root node's get method. pub fn get(self, k: K) -> Option<V> { return self.root.get(k); } - ///An insert method that uses the clone() feature for support. + /// An insert method that uses the `clone` method for support. pub fn insert(mut self, k: K, v: V) -> BTree<K, V> { let (a, b) = self.root.clone().insert(k, v, self.upper_bound.clone()); if b { @@ -98,8 +95,6 @@ impl<K: Clone + Ord, V: Clone> BTree<K, V> { } impl<K: Clone + Ord, V: Clone> Clone for BTree<K, V> { - ///Implements the Clone trait for the BTree. - ///Uses a helper function/constructor to produce a new BTree. fn clone(&self) -> BTree<K, V> { BTree::new_with_node_len(self.root.clone(), self.len, self.lower_bound) } @@ -120,46 +115,46 @@ impl<K: Ord, V: Eq> PartialOrd for BTree<K, V> { } impl<K: Ord, V: Eq> Ord for BTree<K, V> { - ///Returns an ordering based on the root nodes of each BTree. + /// Returns an ordering based on the root nodes of each `BTree`. fn cmp(&self, other: &BTree<K, V>) -> Ordering { self.root.cmp(&other.root) } } impl<K: fmt::Show + Ord, V: fmt::Show> fmt::Show for BTree<K, V> { - ///Returns a string representation of the BTree + /// Returns a string representation of the `BTree`. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.root.fmt(f) } } -//Node types -//A node is either a LeafNode or a BranchNode, which contain either a Leaf or a Branch. -//Branches contain BranchElts, which contain a left child (another node) and a key-value -//pair. Branches also contain the rightmost child of the elements in the array. -//Leaves contain LeafElts, which do not have children. +// Node types +// +// A node is either a LeafNode or a BranchNode, which contain either a Leaf or a Branch. +// Branches contain BranchElts, which contain a left child (another node) and a key-value +// pair. Branches also contain the rightmost child of the elements in the array. +// Leaves contain LeafElts, which do not have children. enum Node<K, V> { LeafNode(Leaf<K, V>), BranchNode(Branch<K, V>) } -//Node functions/methods impl<K: Ord, V> Node<K, V> { - ///Creates a new leaf node given a vector of elements. + /// Creates a new leaf node given a vector of elements. fn new_leaf(vec: Vec<LeafElt<K, V>>) -> Node<K,V> { LeafNode(Leaf::new(vec)) } - ///Creates a new branch node given a vector of an elements and a pointer to a rightmost child. + /// Creates a new branch node given a vector of an elements and a pointer to a rightmost child. fn new_branch(vec: Vec<BranchElt<K, V>>, right: Box<Node<K, V>>) -> Node<K, V> { BranchNode(Branch::new(vec, right)) } - ///Determines whether the given Node contains a Branch or a Leaf. - ///Used in testing. + /// Determines whether the given Node contains a Branch or a Leaf. + /// Used in testing. fn is_leaf(&self) -> bool { match self { &LeafNode(..) => true, @@ -167,8 +162,8 @@ impl<K: Ord, V> Node<K, V> { } } - ///A binary search function for Nodes. - ///Calls either the Branch's or the Leaf's bsearch function. + /// A binary search function for Nodes. + /// Calls either the Branch's or the Leaf's bsearch function. fn bsearch_node(&self, k: K) -> Option<uint> { match self { &LeafNode(ref leaf) => leaf.bsearch_leaf(k), @@ -178,8 +173,8 @@ impl<K: Ord, V> Node<K, V> { } impl<K: Clone + Ord, V: Clone> Node<K, V> { - ///Returns the corresponding value to the provided key. - ///get() is called in different ways on a branch or a leaf. + /// Returns the corresponding value to the provided key. + /// `get()` is called in different ways on a branch or a leaf. fn get(&self, k: K) -> Option<V> { match *self { LeafNode(ref leaf) => return leaf.get(k), @@ -187,7 +182,7 @@ impl<K: Clone + Ord, V: Clone> Node<K, V> { } } - ///Matches on the Node, then performs and returns the appropriate insert method. + /// Matches on the `Node`, then performs and returns the appropriate insert method. fn insert(self, k: K, v: V, ub: uint) -> (Node<K, V>, bool) { match self { LeafNode(leaf) => leaf.insert(k, v, ub), @@ -197,7 +192,7 @@ impl<K: Clone + Ord, V: Clone> Node<K, V> { } impl<K: Clone + Ord, V: Clone> Clone for Node<K, V> { - ///Returns a new node based on whether or not it is a branch or a leaf. + /// Returns a new `Node` based on whether or not it is a branch or a leaf. fn clone(&self) -> Node<K, V> { match *self { LeafNode(ref leaf) => { @@ -242,7 +237,7 @@ impl<K: Ord, V: Eq> PartialOrd for Node<K, V> { } impl<K: Ord, V: Eq> Ord for Node<K, V> { - ///Implementation of Ord for Nodes. + /// Implementation of `Ord` for `Node`s. fn cmp(&self, other: &Node<K, V>) -> Ordering { match *self { LeafNode(ref leaf) => { @@ -262,10 +257,10 @@ impl<K: Ord, V: Eq> Ord for Node<K, V> { } impl<K: fmt::Show + Ord, V: fmt::Show> fmt::Show for Node<K, V> { - ///Returns a string representation of a Node. - ///Will iterate over the Node and show "Key: x, value: y, child: () // " - ///for all elements in the Node. "Child" only exists if the Node contains - ///a branch. + /// Returns a string representation of a `Node`. + /// Will iterate over the Node and show `Key: x, value: y, child: ()` + /// for all elements in the `Node`. `child` only exists if the `Node` contains + /// a branch. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { LeafNode(ref leaf) => leaf.fmt(f), @@ -275,13 +270,13 @@ impl<K: fmt::Show + Ord, V: fmt::Show> fmt::Show for Node<K, V> { } -//A leaf is a vector with elements that contain no children. A leaf also -//does not contain a rightmost child. +// A leaf is a vector with elements that contain no children. A leaf also +// does not contain a rightmost child. struct Leaf<K, V> { elts: Vec<LeafElt<K, V>> } -//Vector of values with children, plus a rightmost child (greater than all) +// Vector of values with children, plus a rightmost child (greater than all) struct Branch<K, V> { elts: Vec<BranchElt<K,V>>, rightmost_child: Box<Node<K, V>>, @@ -289,15 +284,15 @@ struct Branch<K, V> { impl<K: Ord, V> Leaf<K, V> { - ///Creates a new Leaf from a vector of LeafElts. + /// Creates a new `Leaf` from a vector of `LeafElts`. fn new(vec: Vec<LeafElt<K, V>>) -> Leaf<K, V> { Leaf { elts: vec } } - ///Searches a leaf for a spot for a new element using a binary search. - ///Returns None if the element is already in the vector. + /// Searches a leaf for a spot for a new element using a binary search. + /// Returns `None` if the element is already in the vector. fn bsearch_leaf(&self, k: K) -> Option<uint> { let mut high: uint = self.elts.len(); let mut low: uint = 0; @@ -349,7 +344,7 @@ impl<K: Ord, V> Leaf<K, V> { impl<K: Clone + Ord, V: Clone> Leaf<K, V> { - ///Returns the corresponding value to the supplied key. + /// Returns the corresponding value to the supplied key. fn get(&self, k: K) -> Option<V> { for s in self.elts.iter() { let order = s.key.cmp(&k); @@ -361,7 +356,7 @@ impl<K: Clone + Ord, V: Clone> Leaf<K, V> { return None; } - ///Uses clone() to facilitate inserting new elements into a tree. + /// Uses `clone()` to facilitate inserting new elements into a tree. fn insert(mut self, k: K, v: V, ub: uint) -> (Node<K, V>, bool) { let to_insert = LeafElt::new(k, v); let index: Option<uint> = self.bsearch_leaf(to_insert.clone().key); @@ -400,7 +395,7 @@ impl<K: Clone + Ord, V: Clone> Leaf<K, V> { } impl<K: Clone + Ord, V: Clone> Clone for Leaf<K, V> { - ///Returns a new Leaf with the same elts. + /// Returns a new `Leaf` with the same elts. fn clone(&self) -> Leaf<K, V> { Leaf::new(self.elts.clone()) } @@ -421,7 +416,7 @@ impl<K: Ord, V: Eq> PartialOrd for Leaf<K, V> { } impl<K: Ord, V: Eq> Ord for Leaf<K, V> { - ///Returns an ordering based on the first element of each Leaf. + /// Returns an ordering based on the first element of each `Leaf`. fn cmp(&self, other: &Leaf<K, V>) -> Ordering { if self.elts.len() > other.elts.len() { return Greater; @@ -435,7 +430,7 @@ impl<K: Ord, V: Eq> Ord for Leaf<K, V> { impl<K: fmt::Show + Ord, V: fmt::Show> fmt::Show for Leaf<K, V> { - ///Returns a string representation of a Leaf. + /// Returns a string representation of a `Leaf`. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { for (i, s) in self.elts.iter().enumerate() { if i != 0 { try!(write!(f, " // ")) } @@ -447,7 +442,7 @@ impl<K: fmt::Show + Ord, V: fmt::Show> fmt::Show for Leaf<K, V> { impl<K: Ord, V> Branch<K, V> { - ///Creates a new Branch from a vector of BranchElts and a rightmost child (a node). + /// Creates a new `Branch` from a vector of `BranchElts` and a rightmost child (a node). fn new(vec: Vec<BranchElt<K, V>>, right: Box<Node<K, V>>) -> Branch<K, V> { Branch { @@ -506,8 +501,8 @@ impl<K: Ord, V> Branch<K, V> { } impl<K: Clone + Ord, V: Clone> Branch<K, V> { - ///Returns the corresponding value to the supplied key. - ///If the key is not there, find the child that might hold it. + /// Returns the corresponding value to the supplied key. + /// If the key is not there, find the child that might hold it. fn get(&self, k: K) -> Option<V> { for s in self.elts.iter() { let order = s.key.cmp(&k); @@ -520,7 +515,7 @@ impl<K: Clone + Ord, V: Clone> Branch<K, V> { self.rightmost_child.get(k) } - ///An insert method that uses .clone() for support. + /// An insert method that uses `.clone()` for support. fn insert(mut self, k: K, v: V, ub: uint) -> (Node<K, V>, bool) { let mut new_branch = Node::new_branch(self.clone().elts, self.clone().rightmost_child); let mut outcome = false; @@ -630,7 +625,7 @@ impl<K: Clone + Ord, V: Clone> Branch<K, V> { } impl<K: Clone + Ord, V: Clone> Clone for Branch<K, V> { - ///Returns a new branch using the clone methods of the Branch's internal variables. + /// Returns a new branch using the clone methods of the `Branch`'s internal variables. fn clone(&self) -> Branch<K, V> { Branch::new(self.elts.clone(), self.rightmost_child.clone()) } @@ -651,7 +646,8 @@ impl<K: Ord, V: Eq> PartialOrd for Branch<K, V> { } impl<K: Ord, V: Eq> Ord for Branch<K, V> { - ///Compares the first elements of two branches to determine an ordering + /// Compares the first elements of two `Branch`es to determine an + /// `Ordering`. fn cmp(&self, other: &Branch<K, V>) -> Ordering { if self.elts.len() > other.elts.len() { return Greater; @@ -664,7 +660,7 @@ impl<K: Ord, V: Eq> Ord for Branch<K, V> { } impl<K: fmt::Show + Ord, V: fmt::Show> fmt::Show for Branch<K, V> { - ///Returns a string representation of a Branch. + /// Returns a string representation of a `Branch`. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { for (i, s) in self.elts.iter().enumerate() { if i != 0 { try!(write!(f, " // ")) } @@ -688,7 +684,7 @@ struct BranchElt<K, V> { } impl<K: Ord, V> LeafElt<K, V> { - ///Creates a new LeafElt from a supplied key-value pair. + /// Creates a new `LeafElt` from a supplied key-value pair. fn new(k: K, v: V) -> LeafElt<K, V> { LeafElt { key: k, @@ -698,7 +694,7 @@ impl<K: Ord, V> LeafElt<K, V> { } impl<K: Clone + Ord, V: Clone> Clone for LeafElt<K, V> { - ///Returns a new LeafElt by cloning the key and value. + /// Returns a new `LeafElt` by cloning the key and value. fn clone(&self) -> LeafElt<K, V> { LeafElt::new(self.key.clone(), self.value.clone()) } @@ -719,21 +715,21 @@ impl<K: Ord, V: Eq> PartialOrd for LeafElt<K, V> { } impl<K: Ord, V: Eq> Ord for LeafElt<K, V> { - ///Returns an ordering based on the keys of the LeafElts. + /// Returns an ordering based on the keys of the `LeafElt`s. fn cmp(&self, other: &LeafElt<K, V>) -> Ordering { self.key.cmp(&other.key) } } impl<K: fmt::Show + Ord, V: fmt::Show> fmt::Show for LeafElt<K, V> { - ///Returns a string representation of a LeafElt. + /// Returns a string representation of a `LeafElt`. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "Key: {}, value: {};", self.key, self.value) } } impl<K: Ord, V> BranchElt<K, V> { - ///Creates a new BranchElt from a supplied key, value, and left child. + /// Creates a new `BranchElt` from a supplied key, value, and left child. fn new(k: K, v: V, n: Box<Node<K, V>>) -> BranchElt<K, V> { BranchElt { left: n, @@ -745,7 +741,7 @@ impl<K: Ord, V> BranchElt<K, V> { impl<K: Clone + Ord, V: Clone> Clone for BranchElt<K, V> { - ///Returns a new BranchElt by cloning the key, value, and left child. + /// Returns a new `BranchElt` by cloning the key, value, and left child. fn clone(&self) -> BranchElt<K, V> { BranchElt::new(self.key.clone(), self.value.clone(), @@ -768,15 +764,15 @@ impl<K: Ord, V: Eq> PartialOrd for BranchElt<K, V> { } impl<K: Ord, V: Eq> Ord for BranchElt<K, V> { - ///Fulfills Ord for BranchElts + /// Fulfills `Ord` for `BranchElts`. fn cmp(&self, other: &BranchElt<K, V>) -> Ordering { self.key.cmp(&other.key) } } impl<K: fmt::Show + Ord, V: fmt::Show> fmt::Show for BranchElt<K, V> { - /// Returns string containing key, value, and child (which should recur to a - /// leaf) Consider changing in future to be more readable. + /// Formats as a string containing the key, value, and child (which should recur to a + /// leaf). Consider changing in future to be more readable. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "Key: {}, value: {}, (child: {})", self.key, self.value, *self.left) diff --git a/src/libcollections/deque.rs b/src/libcollections/deque.rs index c56b265b43a..d7970ed8d60 100644 --- a/src/libcollections/deque.rs +++ b/src/libcollections/deque.rs @@ -1,4 +1,4 @@ -// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -//! Container traits for collections +//! Container traits for collections. #[cfg(test)] pub mod bench { @@ -18,9 +18,9 @@ pub mod bench { use test::Bencher; use MutableMap; - pub fn insert_rand_n<M:MutableMap<uint,uint>>(n: uint, - map: &mut M, - b: &mut Bencher) { + pub fn insert_rand_n<M: MutableMap<uint, uint>>(n: uint, + map: &mut M, + b: &mut Bencher) { // setup let mut rng = rand::weak_rng(); @@ -37,9 +37,9 @@ pub mod bench { }) } - pub fn insert_seq_n<M:MutableMap<uint,uint>>(n: uint, - map: &mut M, - b: &mut Bencher) { + pub fn insert_seq_n<M: MutableMap<uint, uint>>(n: uint, + map: &mut M, + b: &mut Bencher) { // setup map.clear(); for i in range(0u, n) { diff --git a/src/libcollections/dlist.rs b/src/libcollections/dlist.rs index 6f47780a0e6..47c56375ada 100644 --- a/src/libcollections/dlist.rs +++ b/src/libcollections/dlist.rs @@ -10,10 +10,10 @@ //! A doubly-linked list with owned nodes. //! -//! The DList allows pushing and popping elements at either end. +//! The `DList` allows pushing and popping elements at either end. //! -//! DList implements the trait Deque. It should be imported with `use -//! collections::Deque`. +//! `DList` implements the trait `Deque`. It should be imported with +//! `use collections::Deque`. // DList is constructed like a singly-linked list over the field `next`. // including the last link being None; each Node owns its `next` field. @@ -49,19 +49,29 @@ struct Node<T> { value: T, } -/// Double-ended DList iterator +/// Note: stage0-specific version that lacks bound on A. +#[cfg(stage0)] pub struct Items<'a, T> { head: &'a Link<T>, tail: Rawlink<Node<T>>, nelem: uint, } +/// An iterator over references to the items of a `DList`. +#[cfg(not(stage0))] +pub struct Items<'a, T:'a> { + head: &'a Link<T>, + tail: Rawlink<Node<T>>, + nelem: uint, +} + // FIXME #11820: the &'a Option<> of the Link stops clone working. impl<'a, T> Clone for Items<'a, T> { fn clone(&self) -> Items<'a, T> { *self } } -/// Double-ended mutable DList iterator +/// Note: stage0-specific version that lacks bound on A. +#[cfg(stage0)] pub struct MutItems<'a, T> { list: &'a mut DList<T>, head: Rawlink<Node<T>>, @@ -69,7 +79,16 @@ pub struct MutItems<'a, T> { nelem: uint, } -/// DList consuming iterator +/// An iterator over mutable references to the items of a `DList`. +#[cfg(not(stage0))] +pub struct MutItems<'a, T:'a> { + list: &'a mut DList<T>, + head: Rawlink<Node<T>>, + tail: Rawlink<Node<T>>, + nelem: uint, +} + +/// An iterator over mutable references to the items of a `DList`. #[deriving(Clone)] pub struct MoveItems<T> { list: DList<T> @@ -130,12 +149,17 @@ fn link_with_prev<T>(mut next: Box<Node<T>>, prev: Rawlink<Node<T>>) } impl<T> Collection for DList<T> { - /// O(1) + /// Returns `true` if the `DList` is empty. + /// + /// This operation should compute in O(1) time. #[inline] fn is_empty(&self) -> bool { self.list_head.is_none() } - /// O(1) + + /// Returns the length of the `DList`. + /// + /// This operation should compute in O(1) time. #[inline] fn len(&self) -> uint { self.length @@ -143,9 +167,9 @@ impl<T> Collection for DList<T> { } impl<T> Mutable for DList<T> { - /// Remove all elements from the DList + /// Removes all elements from the `DList`. /// - /// O(N) + /// This operation should compute in O(n) time. #[inline] fn clear(&mut self) { *self = DList::new() @@ -213,40 +237,45 @@ impl<T> DList<T> { } impl<T> Deque<T> for DList<T> { - /// Provide a reference to the front element, or None if the list is empty + /// Provides a reference to the front element, or `None` if the list is + /// empty. #[inline] fn front<'a>(&'a self) -> Option<&'a T> { self.list_head.as_ref().map(|head| &head.value) } - /// Provide a mutable reference to the front element, or None if the list is empty + /// Provides a mutable reference to the front element, or `None` if the list + /// is empty. #[inline] fn front_mut<'a>(&'a mut self) -> Option<&'a mut T> { self.list_head.as_mut().map(|head| &mut head.value) } - /// Provide a reference to the back element, or None if the list is empty + /// Provides a reference to the back element, or `None` if the list is + /// empty. #[inline] fn back<'a>(&'a self) -> Option<&'a T> { self.list_tail.resolve_immut().as_ref().map(|tail| &tail.value) } - /// Provide a mutable reference to the back element, or None if the list is empty + /// Provides a mutable reference to the back element, or `None` if the list + /// is empty. #[inline] fn back_mut<'a>(&'a mut self) -> Option<&'a mut T> { self.list_tail.resolve().map(|tail| &mut tail.value) } - /// Add an element first in the list + /// Adds an element first in the list. /// - /// O(1) + /// This operation should compute in O(1) time. fn push_front(&mut self, elt: T) { self.push_front_node(box Node::new(elt)) } - /// Remove the first element and return it, or None if the list is empty + /// Removes the first element and returns it, or `None` if the list is + /// empty. /// - /// O(1) + /// This operation should compute in O(1) time. fn pop_front(&mut self) -> Option<T> { self.pop_front_node().map(|box Node{value, ..}| value) } @@ -267,15 +296,15 @@ impl<T> Default for DList<T> { } impl<T> DList<T> { - /// Create an empty DList + /// Creates an empty `DList`. #[inline] pub fn new() -> DList<T> { DList{list_head: None, list_tail: Rawlink::none(), length: 0} } - /// Move the last element to the front of the list. + /// Moves the last element to the front of the list. /// - /// If the list is empty, do nothing. + /// If the list is empty, does nothing. /// /// # Example /// @@ -300,9 +329,9 @@ impl<T> DList<T> { }); } - /// Move the first element to the back of the list. + /// Moves the first element to the back of the list. /// - /// If the list is empty, do nothing. + /// If the list is empty, does nothing. /// /// # Example /// @@ -327,9 +356,9 @@ impl<T> DList<T> { }); } - /// Add all elements from `other` to the end of the list + /// Adds all elements from `other` to the end of the list. /// - /// O(1) + /// This operation should compute in O(1) time. /// /// # Example /// @@ -368,9 +397,9 @@ impl<T> DList<T> { } } - /// Add all elements from `other` to the beginning of the list + /// Adds all elements from `other` to the beginning of the list. /// - /// O(1) + /// This operation should compute in O(1) time. /// /// # Example /// @@ -396,10 +425,10 @@ impl<T> DList<T> { self.append(other); } - /// Insert `elt` before the first `x` in the list where `f(x, elt)` is true, - /// or at the end. + /// Inserts `elt` before the first `x` in the list where `f(x, elt)` is + /// true, or at the end. /// - /// O(N) + /// This operation should compute in O(N) time. /// /// # Example /// @@ -433,11 +462,12 @@ impl<T> DList<T> { } } - /// Merge DList `other` into this DList, using the function `f`. - /// Iterate the both DList with `a` from self and `b` from `other`, and - /// put `a` in the result if `f(a, b)` is true, else `b`. + /// Merges `other` into this `DList`, using the function `f`. + /// + /// Iterates both `DList`s with `a` from self and `b` from `other`, and + /// put `a` in the result if `f(a, b)` is true, and otherwise `b`. /// - /// O(max(N, M)) + /// This operation should compute in O(max(N, M)) time. pub fn merge(&mut self, mut other: DList<T>, f: |&T, &T| -> bool) { { let mut it = self.mut_iter(); @@ -458,13 +488,13 @@ impl<T> DList<T> { } - /// Provide a forward iterator + /// Provides a forward iterator. #[inline] pub fn iter<'a>(&'a self) -> Items<'a, T> { Items{nelem: self.len(), head: &self.list_head, tail: self.list_tail} } - /// Provide a forward iterator with mutable references + /// Provides a forward iterator with mutable references. #[inline] pub fn mut_iter<'a>(&'a mut self) -> MutItems<'a, T> { let head_raw = match self.list_head { @@ -480,7 +510,7 @@ impl<T> DList<T> { } - /// Consume the list into an iterator yielding elements by value + /// Consumes the list into an iterator yielding elements by value. #[inline] pub fn move_iter(self) -> MoveItems<T> { MoveItems{list: self} @@ -488,9 +518,9 @@ impl<T> DList<T> { } impl<T: Ord> DList<T> { - /// Insert `elt` sorted in ascending order + /// Inserts `elt` sorted in ascending order. /// - /// O(N) + /// This operation should compute in O(N) time. #[inline] pub fn insert_ordered(&mut self, elt: T) { self.insert_when(elt, |a, b| a >= b) @@ -593,14 +623,15 @@ impl<'a, A> DoubleEndedIterator<&'a mut A> for MutItems<'a, A> { impl<'a, A> ExactSize<&'a mut A> for MutItems<'a, A> {} -/// Allow mutating the DList while iterating +/// Allows mutating a `DList` while iterating. pub trait ListInsertion<A> { - /// Insert `elt` just after to the element most recently returned by `.next()` + /// Inserts `elt` just after to the element most recently returned by + /// `.next()` /// /// The inserted element does not appear in the iteration. fn insert_next(&mut self, elt: A); - /// Provide a reference to the next element, without changing the iterator + /// Provides a reference to the next element, without changing the iterator fn peek_next<'a>(&'a mut self) -> Option<&'a mut A>; } @@ -1076,7 +1107,8 @@ mod tests { let n = list_from([1i,2,3]); spawn(proc() { check_links(&n); - assert_eq!(&[&1,&2,&3], n.iter().collect::<Vec<&int>>().as_slice()); + let a: &[_] = &[&1,&2,&3]; + assert_eq!(a, n.iter().collect::<Vec<&int>>().as_slice()); }); } diff --git a/src/libcollections/enum_set.rs b/src/libcollections/enum_set.rs index ca3f6a746f3..12456e9f79d 100644 --- a/src/libcollections/enum_set.rs +++ b/src/libcollections/enum_set.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -//! A structure for holding a set of enum variants +//! A structure for holding a set of enum variants. //! //! This module defines a container which uses an efficient bit mask //! representation to hold C-like enum variants. @@ -16,7 +16,7 @@ use core::prelude::*; #[deriving(Clone, PartialEq, Eq, Hash, Show)] -/// A specialized Set implementation to use enum types. +/// A specialized `Set` implementation to use enum types. pub struct EnumSet<E> { // We must maintain the invariant that no bits are set // for which no variant exists @@ -25,9 +25,9 @@ pub struct EnumSet<E> { /// An interface for casting C-like enum to uint and back. pub trait CLike { - /// Converts C-like enum to uint. + /// Converts a C-like enum to a `uint`. fn to_uint(&self) -> uint; - /// Converts uint to C-like enum. + /// Converts a `uint` to a C-like enum. fn from_uint(uint) -> Self; } @@ -36,47 +36,47 @@ fn bit<E:CLike>(e: E) -> uint { } impl<E:CLike> EnumSet<E> { - /// Returns an empty EnumSet. + /// Returns an empty `EnumSet`. pub fn empty() -> EnumSet<E> { EnumSet {bits: 0} } - /// Returns true if an EnumSet is empty. + /// Returns true if the `EnumSet` is empty. pub fn is_empty(&self) -> bool { self.bits == 0 } - /// Returns true if an EnumSet contains any enum of a given EnumSet + /// Returns `true` if the `EnumSet` contains any enum of the given `EnumSet`. pub fn intersects(&self, e: EnumSet<E>) -> bool { (self.bits & e.bits) != 0 } - /// Returns an intersection of both EnumSets. + /// Returns the intersection of both `EnumSets`. pub fn intersection(&self, e: EnumSet<E>) -> EnumSet<E> { EnumSet {bits: self.bits & e.bits} } - /// Returns true if a given EnumSet is included in an EnumSet. + /// Returns `true` if a given `EnumSet` is included in an `EnumSet`. pub fn contains(&self, e: EnumSet<E>) -> bool { (self.bits & e.bits) == e.bits } - /// Returns a union of both EnumSets. + /// Returns the union of both `EnumSets`. pub fn union(&self, e: EnumSet<E>) -> EnumSet<E> { EnumSet {bits: self.bits | e.bits} } - /// Add an enum to an EnumSet + /// Adds an enum to an `EnumSet`. pub fn add(&mut self, e: E) { self.bits |= bit(e); } - /// Returns true if an EnumSet contains a given enum + /// Returns `true` if an `EnumSet` contains a given enum. pub fn contains_elem(&self, e: E) -> bool { (self.bits & bit(e)) != 0 } - /// Returns an iterator over an EnumSet + /// Returns an iterator over an `EnumSet`. pub fn iter(&self) -> Items<E> { Items::new(self.bits) } diff --git a/src/libcollections/hash/mod.rs b/src/libcollections/hash/mod.rs index 4ce39a683ae..ef26452a529 100644 --- a/src/libcollections/hash/mod.rs +++ b/src/libcollections/hash/mod.rs @@ -77,18 +77,18 @@ pub use self::sip::hash as hash; pub mod sip; -/// A trait that represents a hashable type. The `S` type parameter is an -/// abstract hash state that is used by the `Hash` to compute the hash. -/// It defaults to `std::hash::sip::SipState`. +/// A hashable type. The `S` type parameter is an abstract hash state that is +/// used by the `Hash` to compute the hash. It defaults to +/// `std::hash::sip::SipState`. pub trait Hash<S = sip::SipState> { - /// Compute a hash of the value. + /// Computes the hash of a value. fn hash(&self, state: &mut S); } /// A trait that computes a hash for a value. The main users of this trait are /// containers like `HashMap`, which need a generic way hash multiple types. pub trait Hasher<S> { - /// Compute a hash of the value. + /// Compute the hash of a value. fn hash<T: Hash<S>>(&self, value: &T) -> u64; } @@ -346,7 +346,8 @@ mod tests { assert_eq!(hasher.hash(&'a'), 97); assert_eq!(hasher.hash(&("a")), 97 + 0xFF); - assert_eq!(hasher.hash(& &[1u8, 2u8, 3u8]), 9); + let cs: &[u8] = &[1u8, 2u8, 3u8]; + assert_eq!(hasher.hash(& cs), 9); unsafe { let ptr: *const int = mem::transmute(5i); diff --git a/src/libcollections/hash/sip.rs b/src/libcollections/hash/sip.rs index b31d811c2c9..f3798e5f9e0 100644 --- a/src/libcollections/hash/sip.rs +++ b/src/libcollections/hash/sip.rs @@ -10,21 +10,19 @@ // // ignore-lexer-test FIXME #15883 -/*! - * Implementation of SipHash 2-4 - * - * See: http://131002.net/siphash/ - * - * Consider this as a main "general-purpose" hash for all hashtables: it - * runs at good speed (competitive with spooky and city) and permits - * strong _keyed_ hashing. Key your hashtables from a strong RNG, - * such as `rand::Rng`. - * - * Although the SipHash algorithm is considered to be cryptographically - * strong, this implementation has not been reviewed for such purposes. - * As such, all cryptographic uses of this implementation are strongly - * discouraged. - */ +//! An implementation of SipHash 2-4. +//! +//! See: http://131002.net/siphash/ +//! +//! Consider this as a main "general-purpose" hash for all hashtables: it +//! runs at good speed (competitive with spooky and city) and permits +//! strong _keyed_ hashing. Key your hashtables from a strong RNG, +//! such as `rand::Rng`. +//! +//! Although the SipHash algorithm is considered to be cryptographically +//! strong, this implementation has not been reviewed for such purposes. +//! As such, all cryptographic uses of this implementation are strongly +//! discouraged. use core::prelude::*; @@ -89,13 +87,13 @@ macro_rules! compress ( ) impl SipState { - /// Create a `SipState` that is keyed off the provided keys. + /// Creates a `SipState` that is keyed off the provided keys. #[inline] pub fn new() -> SipState { SipState::new_with_keys(0, 0) } - /// Create a `SipState` that is keyed off the provided keys. + /// Creates a `SipState` that is keyed off the provided keys. #[inline] pub fn new_with_keys(key0: u64, key1: u64) -> SipState { let mut state = SipState { @@ -113,7 +111,7 @@ impl SipState { state } - /// Reset the state back to it's initial state. + /// Resets the state to its initial state. #[inline] pub fn reset(&mut self) { self.length = 0; @@ -124,7 +122,7 @@ impl SipState { self.ntail = 0; } - /// Return the computed hash. + /// Returns the computed hash. #[inline] pub fn result(&self) -> u64 { let mut v0 = self.v0; @@ -219,13 +217,13 @@ pub struct SipHasher { } impl SipHasher { - /// Create a `Sip`. + /// Creates a `Sip`. #[inline] pub fn new() -> SipHasher { SipHasher::new_with_keys(0, 0) } - /// Create a `Sip` that is keyed off the provided keys. + /// Creates a `Sip` that is keyed off the provided keys. #[inline] pub fn new_with_keys(key0: u64, key1: u64) -> SipHasher { SipHasher { @@ -251,7 +249,7 @@ impl Default for SipHasher { } } -/// Hash a value using the SipHash algorithm. +/// Hashes a value using the SipHash algorithm. #[inline] pub fn hash<T: Hash<SipState>>(value: &T) -> u64 { let mut state = SipState::new(); @@ -259,7 +257,7 @@ pub fn hash<T: Hash<SipState>>(value: &T) -> u64 { state.result() } -/// Hash a value with the SipHash algorithm with the provided keys. +/// Hashes a value with the SipHash algorithm with the provided keys. #[inline] pub fn hash_with_keys<T: Hash<SipState>>(k0: u64, k1: u64, value: &T) -> u64 { let mut state = SipState::new_with_keys(k0, k1); @@ -497,8 +495,8 @@ mod tests { assert!(s != t && t != u); assert!(hash(&s) != hash(&t) && hash(&s) != hash(&u)); - let v = (&[1u8], &[0u8, 0], &[0u8]); - let w = (&[1u8, 0, 0, 0], &[], &[]); + let v: (&[u8], &[u8], &[u8]) = (&[1u8], &[0u8, 0], &[0u8]); + let w: (&[u8], &[u8], &[u8]) = (&[1u8, 0, 0, 0], &[], &[]); assert!(v != w); assert!(hash(&v) != hash(&w)); diff --git a/src/libcollections/lib.rs b/src/libcollections/lib.rs index 5fdcee14f89..a98d9ddb9db 100644 --- a/src/libcollections/lib.rs +++ b/src/libcollections/lib.rs @@ -8,9 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -/*! - * Collection types. - */ +//! Collection types. #![crate_name = "collections"] #![experimental] @@ -73,9 +71,9 @@ pub mod hash; mod deque; -/// A trait to represent mutable containers +/// A mutable container type. pub trait Mutable: Collection { - /// Clear the container, removing all values. + /// Clears the container, removing all values. /// /// # Example /// @@ -87,10 +85,10 @@ pub trait Mutable: Collection { fn clear(&mut self); } -/// A map is a key-value store where values may be looked up by their keys. This -/// trait provides basic operations to operate on these stores. +/// A key-value store where values may be looked up by their keys. This trait +/// provides basic operations to operate on these stores. pub trait Map<K, V>: Collection { - /// Return a reference to the value corresponding to the key. + /// Returns a reference to the value corresponding to the key. /// /// # Example /// @@ -104,7 +102,7 @@ pub trait Map<K, V>: Collection { /// ``` fn find<'a>(&'a self, key: &K) -> Option<&'a V>; - /// Return true if the map contains a value for the specified key. + /// Returns true if the map contains a value for the specified key. /// /// # Example /// @@ -122,10 +120,10 @@ pub trait Map<K, V>: Collection { } } -/// This trait provides basic operations to modify the contents of a map. +/// A key-value store (map) where the values can be modified. pub trait MutableMap<K, V>: Map<K, V> + Mutable { - /// Insert a key-value pair into the map. An existing value for a - /// key is replaced by the new value. Return true if the key did + /// Inserts a key-value pair into the map. An existing value for a + /// key is replaced by the new value. Returns `true` if the key did /// not already exist in the map. /// /// # Example @@ -143,8 +141,8 @@ pub trait MutableMap<K, V>: Map<K, V> + Mutable { self.swap(key, value).is_none() } - /// Remove a key-value pair from the map. Return true if the key - /// was present in the map, otherwise false. + /// Removes a key-value pair from the map. Returns `true` if the key + /// was present in the map. /// /// # Example /// @@ -161,8 +159,9 @@ pub trait MutableMap<K, V>: Map<K, V> + Mutable { self.pop(key).is_some() } - /// Insert a key-value pair from the map. If the key already had a value - /// present in the map, that value is returned. Otherwise None is returned. + /// Inserts a key-value pair into the map. If the key already had a value + /// present in the map, that value is returned. Otherwise, `None` is + /// returned. /// /// # Example /// @@ -194,7 +193,7 @@ pub trait MutableMap<K, V>: Map<K, V> + Mutable { /// ``` fn pop(&mut self, k: &K) -> Option<V>; - /// Return a mutable reference to the value corresponding to the key. + /// Returns a mutable reference to the value corresponding to the key. /// /// # Example /// @@ -212,11 +211,11 @@ pub trait MutableMap<K, V>: Map<K, V> + Mutable { fn find_mut<'a>(&'a mut self, key: &K) -> Option<&'a mut V>; } -/// A set is a group of objects which are each distinct from one another. This +/// A group of objects which are each distinct from one another. This /// trait represents actions which can be performed on sets to iterate over /// them. pub trait Set<T>: Collection { - /// Return true if the set contains a value. + /// Returns `true` if the set contains a value. /// /// # Example /// @@ -229,7 +228,7 @@ pub trait Set<T>: Collection { /// ``` fn contains(&self, value: &T) -> bool; - /// Return true if the set has no elements in common with `other`. + /// Returns `true` if the set has no elements in common with `other`. /// This is equivalent to checking for an empty intersection. /// /// # Example @@ -248,7 +247,7 @@ pub trait Set<T>: Collection { /// ``` fn is_disjoint(&self, other: &Self) -> bool; - /// Return true if the set is a subset of another. + /// Returns `true` if the set is a subset of another. /// /// # Example /// @@ -266,7 +265,7 @@ pub trait Set<T>: Collection { /// ``` fn is_subset(&self, other: &Self) -> bool; - /// Return true if the set is a superset of another. + /// Returns `true` if the set is a superset of another. /// /// # Example /// @@ -292,10 +291,10 @@ pub trait Set<T>: Collection { // FIXME #8154: Add difference, sym. difference, intersection and union iterators } -/// This trait represents actions which can be performed on sets to mutate -/// them. +/// A mutable collection of values which are distinct from one another that +/// can be mutaed. pub trait MutableSet<T>: Set<T> + Mutable { - /// Add a value to the set. Return true if the value was not already + /// Adds a value to the set. Returns `true` if the value was not already /// present in the set. /// /// # Example @@ -311,7 +310,7 @@ pub trait MutableSet<T>: Set<T> + Mutable { /// ``` fn insert(&mut self, value: T) -> bool; - /// Remove a value from the set. Return true if the value was + /// Removes a value from the set. Returns `true` if the value was /// present in the set. /// /// # Example @@ -329,7 +328,7 @@ pub trait MutableSet<T>: Set<T> + Mutable { } pub trait MutableSeq<T>: Mutable { - /// Append an element to the back of a collection. + /// Appends an element to the back of a collection. /// /// # Example /// @@ -339,8 +338,9 @@ pub trait MutableSeq<T>: Mutable { /// assert_eq!(vec, vec!(1, 2, 3)); /// ``` fn push(&mut self, t: T); - /// Remove the last element from a collection and return it, or `None` if it is - /// empty. + + /// Removes the last element from a collection and returns it, or `None` if + /// it is empty. /// /// # Example /// @@ -412,7 +412,7 @@ pub trait MutableSeq<T>: Mutable { /// } /// ``` pub trait Deque<T> : MutableSeq<T> { - /// Provide a reference to the front element, or `None` if the sequence is + /// Provides a reference to the front element, or `None` if the sequence is /// empty. /// /// # Example @@ -429,7 +429,7 @@ pub trait Deque<T> : MutableSeq<T> { /// ``` fn front<'a>(&'a self) -> Option<&'a T>; - /// Provide a mutable reference to the front element, or `None` if the + /// Provides a mutable reference to the front element, or `None` if the /// sequence is empty. /// /// # Example @@ -450,7 +450,7 @@ pub trait Deque<T> : MutableSeq<T> { /// ``` fn front_mut<'a>(&'a mut self) -> Option<&'a mut T>; - /// Provide a reference to the back element, or `None` if the sequence is + /// Provides a reference to the back element, or `None` if the sequence is /// empty. /// /// # Example @@ -467,8 +467,8 @@ pub trait Deque<T> : MutableSeq<T> { /// ``` fn back<'a>(&'a self) -> Option<&'a T>; - /// Provide a mutable reference to the back element, or `None` if the sequence - /// is empty. + /// Provides a mutable reference to the back element, or `None` if the + /// sequence is empty. /// /// # Example /// @@ -488,7 +488,7 @@ pub trait Deque<T> : MutableSeq<T> { /// ``` fn back_mut<'a>(&'a mut self) -> Option<&'a mut T>; - /// Insert an element first in the sequence. + /// Inserts an element first in the sequence. /// /// # Example /// @@ -502,7 +502,7 @@ pub trait Deque<T> : MutableSeq<T> { /// ``` fn push_front(&mut self, elt: T); - /// Insert an element last in the sequence. + /// Inserts an element last in the sequence. /// /// # Example /// @@ -517,7 +517,8 @@ pub trait Deque<T> : MutableSeq<T> { #[deprecated = "use the `push` method"] fn push_back(&mut self, elt: T) { self.push(elt) } - /// Remove the last element and return it, or `None` if the sequence is empty. + /// Removes the last element and returns it, or `None` if the sequence is + /// empty. /// /// # Example /// @@ -535,7 +536,8 @@ pub trait Deque<T> : MutableSeq<T> { #[deprecated = "use the `pop` method"] fn pop_back(&mut self) -> Option<T> { self.pop() } - /// Remove the first element and return it, or `None` if the sequence is empty. + /// Removes the first element and returns it, or `None` if the sequence is + /// empty. /// /// # Example /// diff --git a/src/libcollections/macros.rs b/src/libcollections/macros.rs index db062a70bbb..ba8b3b8c7d3 100644 --- a/src/libcollections/macros.rs +++ b/src/libcollections/macros.rs @@ -10,7 +10,7 @@ #![macro_escape] -/// Create a `std::vec::Vec` containing the arguments. +/// Creates a `std::vec::Vec` containing the arguments. macro_rules! vec( ($($e:expr),*) => ({ // leading _ to allow empty construction without a warning. diff --git a/src/libcollections/priority_queue.rs b/src/libcollections/priority_queue.rs index 34cc0225815..905078ccc3c 100644 --- a/src/libcollections/priority_queue.rs +++ b/src/libcollections/priority_queue.rs @@ -167,12 +167,12 @@ pub struct PriorityQueue<T> { } impl<T: Ord> Collection for PriorityQueue<T> { - /// Returns the length of the queue + /// Returns the length of the queue. fn len(&self) -> uint { self.data.len() } } impl<T: Ord> Mutable for PriorityQueue<T> { - /// Drop all items from the queue + /// Drops all items from the queue. fn clear(&mut self) { self.data.truncate(0) } } @@ -182,7 +182,7 @@ impl<T: Ord> Default for PriorityQueue<T> { } impl<T: Ord> PriorityQueue<T> { - /// Create an empty PriorityQueue as a max-heap. + /// Creates an empty `PriorityQueue` as a max-heap. /// /// # Example /// @@ -192,9 +192,9 @@ impl<T: Ord> PriorityQueue<T> { /// ``` pub fn new() -> PriorityQueue<T> { PriorityQueue{data: vec!(),} } - /// Create an empty PriorityQueue with a specific capacity. + /// Creates an empty `PriorityQueue` with a specific capacity. /// This preallocates enough memory for `capacity` elements, - /// so that the PriorityQueue does not have to be reallocated + /// so that the `PriorityQueue` does not have to be reallocated /// until it contains at least that many values. /// /// # Example @@ -207,7 +207,7 @@ impl<T: Ord> PriorityQueue<T> { PriorityQueue { data: Vec::with_capacity(capacity) } } - /// Create a PriorityQueue from a vector. This is sometimes called + /// Creates a `PriorityQueue` from a vector. This is sometimes called /// `heapifying` the vector. /// /// # Example @@ -244,7 +244,7 @@ impl<T: Ord> PriorityQueue<T> { Items { iter: self.data.iter() } } - /// Returns the greatest item in a queue or `None` if it is empty. + /// Returns the greatest item in a queue, or `None` if it is empty. /// /// # Example /// @@ -279,7 +279,7 @@ impl<T: Ord> PriorityQueue<T> { /// ``` pub fn capacity(&self) -> uint { self.data.capacity() } - /// Reserve capacity for exactly `n` elements in the PriorityQueue. + /// Reserves capacity for exactly `n` elements in the `PriorityQueue`. /// Do nothing if the capacity is already sufficient. /// /// # Example @@ -293,7 +293,7 @@ impl<T: Ord> PriorityQueue<T> { /// ``` pub fn reserve_exact(&mut self, n: uint) { self.data.reserve_exact(n) } - /// Reserve capacity for at least `n` elements in the PriorityQueue. + /// Reserves capacity for at least `n` elements in the `PriorityQueue`. /// Do nothing if the capacity is already sufficient. /// /// # Example @@ -309,8 +309,8 @@ impl<T: Ord> PriorityQueue<T> { self.data.reserve(n) } - /// Remove the greatest item from a queue and return it, or `None` if it is - /// empty. + /// Removes the greatest item from a queue and returns it, or `None` if it + /// is empty. /// /// # Example /// @@ -339,7 +339,7 @@ impl<T: Ord> PriorityQueue<T> { #[deprecated="renamed to `pop`"] pub fn maybe_pop(&mut self) -> Option<T> { self.pop() } - /// Push an item onto the queue. + /// Pushes an item onto the queue. /// /// # Example /// @@ -360,7 +360,8 @@ impl<T: Ord> PriorityQueue<T> { self.siftup(0, new_len); } - /// Optimized version of a push followed by a pop. + /// Pushes an item onto a queue then pops the greatest item off the queue in + /// an optimized fashion. /// /// # Example /// @@ -384,8 +385,9 @@ impl<T: Ord> PriorityQueue<T> { item } - /// Optimized version of a pop followed by a push. The push is done - /// regardless of whether the queue is empty. + /// Pops the greatest item off a queue then pushes an item onto the queue in + /// an optimized fashion. The push is done regardless of whether the queue + /// was empty. /// /// # Example /// @@ -418,7 +420,7 @@ impl<T: Ord> PriorityQueue<T> { #[deprecated="renamed to `into_sorted_vec`"] fn to_sorted_vec(self) -> Vec<T> { self.into_sorted_vec() } - /// Consume the PriorityQueue and return the underlying vector + /// Consumes the `PriorityQueue` and returns the underlying vector /// in arbitrary order. /// /// # Example @@ -436,7 +438,7 @@ impl<T: Ord> PriorityQueue<T> { /// ``` pub fn into_vec(self) -> Vec<T> { let PriorityQueue{data: v} = self; v } - /// Consume the PriorityQueue and return a vector in sorted + /// Consumes the `PriorityQueue` and returns a vector in sorted /// (ascending) order. /// /// # Example @@ -513,11 +515,18 @@ impl<T: Ord> PriorityQueue<T> { } } -/// PriorityQueue iterator. +/// Note: stage0-specific version that lacks bound on A. +#[cfg(stage0)] pub struct Items <'a, T> { iter: slice::Items<'a, T>, } +/// `PriorityQueue` iterator. +#[cfg(not(stage0))] +pub struct Items <'a, T:'a> { + iter: slice::Items<'a, T>, +} + impl<'a, T> Iterator<&'a T> for Items<'a, T> { #[inline] fn next(&mut self) -> Option<(&'a T)> { self.iter.next() } @@ -527,10 +536,9 @@ impl<'a, T> Iterator<&'a T> for Items<'a, T> { } impl<T: Ord> FromIterator<T> for PriorityQueue<T> { - fn from_iter<Iter: Iterator<T>>(iter: Iter) -> PriorityQueue<T> { - let mut q = PriorityQueue::new(); - q.extend(iter); - q + fn from_iter<Iter: Iterator<T>>(mut iter: Iter) -> PriorityQueue<T> { + let vec: Vec<T> = iter.collect(); + PriorityQueue::from_vec(vec) } } diff --git a/src/libcollections/ringbuf.rs b/src/libcollections/ringbuf.rs index 9d074813343..6b293c9f4d8 100644 --- a/src/libcollections/ringbuf.rs +++ b/src/libcollections/ringbuf.rs @@ -8,10 +8,10 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -//! A double-ended queue implemented as a circular buffer +//! A double-ended queue implemented as a circular buffer. //! -//! RingBuf implements the trait Deque. It should be imported with `use -//! collections::Deque`. +//! `RingBuf` implements the trait `Deque`. It should be imported with +//! `use collections::Deque`. use core::prelude::*; @@ -27,7 +27,7 @@ use vec::Vec; static INITIAL_CAPACITY: uint = 8u; // 2^3 static MINIMUM_CAPACITY: uint = 2u; -/// RingBuf is a circular buffer that implements Deque. +/// `RingBuf` is a circular buffer that implements `Deque`. #[deriving(Clone)] pub struct RingBuf<T> { nelts: uint, @@ -36,12 +36,12 @@ pub struct RingBuf<T> { } impl<T> Collection for RingBuf<T> { - /// Return the number of elements in the RingBuf + /// Returns the number of elements in the `RingBuf`. fn len(&self) -> uint { self.nelts } } impl<T> Mutable for RingBuf<T> { - /// Clear the RingBuf, removing all values. + /// Clears the `RingBuf`, removing all values. fn clear(&mut self) { for x in self.elts.mut_iter() { *x = None } self.nelts = 0; @@ -50,28 +50,29 @@ impl<T> Mutable for RingBuf<T> { } impl<T> Deque<T> for RingBuf<T> { - /// Return a reference to the first element in the RingBuf + /// Returns a reference to the first element in the `RingBuf`. fn front<'a>(&'a self) -> Option<&'a T> { if self.nelts > 0 { Some(&self[0]) } else { None } } - /// Return a mutable reference to the first element in the RingBuf + /// Returns a mutable reference to the first element in the `RingBuf`. fn front_mut<'a>(&'a mut self) -> Option<&'a mut T> { if self.nelts > 0 { Some(self.get_mut(0)) } else { None } } - /// Return a reference to the last element in the RingBuf + /// Returns a reference to the last element in the `RingBuf`. fn back<'a>(&'a self) -> Option<&'a T> { if self.nelts > 0 { Some(&self[self.nelts - 1]) } else { None } } - /// Return a mutable reference to the last element in the RingBuf + /// Returns a mutable reference to the last element in the `RingBuf`. fn back_mut<'a>(&'a mut self) -> Option<&'a mut T> { let nelts = self.nelts; if nelts > 0 { Some(self.get_mut(nelts - 1)) } else { None } } - /// Remove and return the first element in the RingBuf, or None if it is empty + /// Removes and returns the first element in the `RingBuf`, or `None` if it + /// is empty. fn pop_front(&mut self) -> Option<T> { let result = self.elts.get_mut(self.lo).take(); if result.is_some() { @@ -81,7 +82,7 @@ impl<T> Deque<T> for RingBuf<T> { result } - /// Prepend an element to the RingBuf + /// Prepends an element to the `RingBuf`. fn push_front(&mut self, t: T) { if self.nelts == self.elts.len() { grow(self.nelts, &mut self.lo, &mut self.elts); @@ -120,20 +121,20 @@ impl<T> Default for RingBuf<T> { } impl<T> RingBuf<T> { - /// Create an empty RingBuf + /// Creates an empty `RingBuf`. pub fn new() -> RingBuf<T> { RingBuf::with_capacity(INITIAL_CAPACITY) } - /// Create an empty RingBuf with space for at least `n` elements. + /// Creates an empty `RingBuf` with space for at least `n` elements. pub fn with_capacity(n: uint) -> RingBuf<T> { RingBuf{nelts: 0, lo: 0, elts: Vec::from_fn(cmp::max(MINIMUM_CAPACITY, n), |_| None)} } - /// Retrieve an element in the RingBuf by index + /// Retrieva an element in the `RingBuf` by index. /// - /// Fails if there is no element with the given index + /// Fails if there is no element with the given index. /// /// # Example /// @@ -157,9 +158,9 @@ impl<T> RingBuf<T> { } } - /// Retrieve an element in the RingBuf by index + /// Retrieves an element in the `RingBuf` by index. /// - /// Fails if there is no element with the given index + /// Fails if there is no element with the given index. /// /// # Example /// @@ -181,11 +182,11 @@ impl<T> RingBuf<T> { } } - /// Swap elements at indices `i` and `j` + /// Swaps elements at indices `i` and `j`. /// /// `i` and `j` may be equal. /// - /// Fails if there is no element with the given index + /// Fails if there is no element with either index. /// /// # Example /// @@ -208,37 +209,30 @@ impl<T> RingBuf<T> { self.elts.as_mut_slice().swap(ri, rj); } - /// Return index in underlying vec for a given logical element index + /// Returns the index in the underlying `Vec` for a given logical element + /// index. fn raw_index(&self, idx: uint) -> uint { raw_index(self.lo, self.elts.len(), idx) } - /// Reserve capacity for exactly `n` elements in the given RingBuf, + /// Reserves capacity for exactly `n` elements in the given `RingBuf`, /// doing nothing if `self`'s capacity is already equal to or greater - /// than the requested capacity - /// - /// # Arguments - /// - /// * n - The number of elements to reserve space for + /// than the requested capacity. pub fn reserve_exact(&mut self, n: uint) { self.elts.reserve_exact(n); } - /// Reserve capacity for at least `n` elements in the given RingBuf, + /// Reserves capacity for at least `n` elements in the given `RingBuf`, /// over-allocating in case the caller needs to reserve additional /// space. /// /// Do nothing if `self`'s capacity is already equal to or greater /// than the requested capacity. - /// - /// # Arguments - /// - /// * n - The number of elements to reserve space for pub fn reserve(&mut self, n: uint) { self.elts.reserve(n); } - /// Front-to-back iterator. + /// Returns a front-to-back iterator. /// /// # Example /// @@ -249,13 +243,14 @@ impl<T> RingBuf<T> { /// buf.push(5i); /// buf.push(3); /// buf.push(4); - /// assert_eq!(buf.iter().collect::<Vec<&int>>().as_slice(), &[&5, &3, &4]); + /// let b: &[_] = &[&5, &3, &4]; + /// assert_eq!(buf.iter().collect::<Vec<&int>>().as_slice(), b); /// ``` pub fn iter<'a>(&'a self) -> Items<'a, T> { Items{index: 0, rindex: self.nelts, lo: self.lo, elts: self.elts.as_slice()} } - /// Front-to-back iterator which returns mutable values. + /// Returns a front-to-back iterator which returns mutable references. /// /// # Example /// @@ -269,7 +264,8 @@ impl<T> RingBuf<T> { /// for num in buf.mut_iter() { /// *num = *num - 2; /// } - /// assert_eq!(buf.mut_iter().collect::<Vec<&mut int>>().as_slice(), &[&mut 3, &mut 1, &mut 2]); + /// let b: &[_] = &[&mut 3, &mut 1, &mut 2]; + /// assert_eq!(buf.mut_iter().collect::<Vec<&mut int>>().as_slice(), b); /// ``` pub fn mut_iter<'a>(&'a mut self) -> MutItems<'a, T> { let start_index = raw_index(self.lo, self.elts.len(), 0); @@ -297,7 +293,8 @@ impl<T> RingBuf<T> { } } -/// RingBuf iterator +/// Note: stage0-specific version that lacks bound on A. +#[cfg(stage0)] pub struct Items<'a, T> { lo: uint, index: uint, @@ -305,6 +302,15 @@ pub struct Items<'a, T> { elts: &'a [Option<T>], } +/// `RingBuf` iterator. +#[cfg(not(stage0))] +pub struct Items<'a, T:'a> { + lo: uint, + index: uint, + rindex: uint, + elts: &'a [Option<T>], +} + impl<'a, T> Iterator<&'a T> for Items<'a, T> { #[inline] fn next(&mut self) -> Option<&'a T> { @@ -352,13 +358,22 @@ impl<'a, T> RandomAccessIterator<&'a T> for Items<'a, T> { } } -/// RingBuf mutable iterator +/// Note: stage0-specific version that lacks bound on A. +#[cfg(stage0)] pub struct MutItems<'a, T> { remaining1: &'a mut [Option<T>], remaining2: &'a mut [Option<T>], nelts: uint, } +/// `RingBuf` mutable iterator. +#[cfg(not(stage0))] +pub struct MutItems<'a, T:'a> { + remaining1: &'a mut [Option<T>], + remaining2: &'a mut [Option<T>], + nelts: uint, +} + impl<'a, T> Iterator<&'a mut T> for MutItems<'a, T> { #[inline] #[allow(deprecated)] // mut_shift_ref @@ -437,7 +452,7 @@ fn grow<T>(nelts: uint, loptr: &mut uint, elts: &mut Vec<Option<T>>) { } } -/// Return index in underlying vec for a given logical element index +/// Returns the index in the underlying `Vec` for a given logical element index. fn raw_index(lo: uint, len: uint, index: uint) -> uint { if lo >= len - index { lo + index - len @@ -871,12 +886,18 @@ mod tests { for i in range(0i, 5) { d.push_back(i); } - assert_eq!(d.iter().collect::<Vec<&int>>().as_slice(), &[&0,&1,&2,&3,&4]); + { + let b: &[_] = &[&0,&1,&2,&3,&4]; + assert_eq!(d.iter().collect::<Vec<&int>>().as_slice(), b); + } for i in range(6i, 9) { d.push_front(i); } - assert_eq!(d.iter().collect::<Vec<&int>>().as_slice(), &[&8,&7,&6,&0,&1,&2,&3,&4]); + { + let b: &[_] = &[&8,&7,&6,&0,&1,&2,&3,&4]; + assert_eq!(d.iter().collect::<Vec<&int>>().as_slice(), b); + } let mut it = d.iter(); let mut len = d.len(); @@ -896,12 +917,16 @@ mod tests { for i in range(0i, 5) { d.push_back(i); } - assert_eq!(d.iter().rev().collect::<Vec<&int>>().as_slice(), &[&4,&3,&2,&1,&0]); + { + let b: &[_] = &[&4,&3,&2,&1,&0]; + assert_eq!(d.iter().rev().collect::<Vec<&int>>().as_slice(), b); + } for i in range(6i, 9) { d.push_front(i); } - assert_eq!(d.iter().rev().collect::<Vec<&int>>().as_slice(), &[&4,&3,&2,&1,&0,&6,&7,&8]); + let b: &[_] = &[&4,&3,&2,&1,&0,&6,&7,&8]; + assert_eq!(d.iter().rev().collect::<Vec<&int>>().as_slice(), b); } #[test] diff --git a/src/libcollections/slice.rs b/src/libcollections/slice.rs index 7190bbfbc01..71b9673d279 100644 --- a/src/libcollections/slice.rs +++ b/src/libcollections/slice.rs @@ -8,81 +8,77 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -/*! - -Utilities for slice manipulation - -The `slice` module contains useful code to help work with slice values. -Slices are a view into a block of memory represented as a pointer and a length. - -```rust -// slicing a Vec -let vec = vec!(1i, 2, 3); -let int_slice = vec.as_slice(); -// coercing an array to a slice -let str_slice: &[&str] = ["one", "two", "three"]; -``` - -Slices are either mutable or shared. The shared slice type is `&[T]`, -while the mutable slice type is `&mut[T]`. For example, you can mutate the -block of memory that a mutable slice points to: - -```rust -let x: &mut[int] = [1i, 2, 3]; -x[1] = 7; -assert_eq!(x[0], 1); -assert_eq!(x[1], 7); -assert_eq!(x[2], 3); -``` - -Here are some of the things this module contains: - -## Structs - -There are several structs that are useful for slices, such as `Items`, which -represents iteration over a slice. - -## Traits - -A number of traits add methods that allow you to accomplish tasks with slices. -These traits include `ImmutableSlice`, which is defined for `&[T]` types, -and `MutableSlice`, defined for `&mut [T]` types. - -An example is the method `.slice(a, b)` that returns an immutable "view" into -a `Vec` or another slice from the index interval `[a, b)`: - -```rust -let numbers = [0i, 1i, 2i]; -let last_numbers = numbers.slice(1, 3); -// last_numbers is now &[1i, 2i] -``` - -## Implementations of other traits - -There are several implementations of common traits for slices. Some examples -include: - -* `Clone` -* `Eq`, `Ord` - for immutable slices whose element type are `Eq` or `Ord`. -* `Hash` - for slices whose element type is `Hash` - -## Iteration - -The method `iter()` returns an iteration value for a slice. The iterator -yields references to the slice's elements, so if the element -type of the slice is `int`, the element type of the iterator is `&int`. - -```rust -let numbers = [0i, 1i, 2i]; -for &x in numbers.iter() { - println!("{} is a number!", x); -} -``` - -* `.mut_iter()` returns an iterator that allows modifying each value. -* Further iterators exist that split, chunk or permute the slice. - -*/ +//! Utilities for slice manipulation +//! +//! The `slice` module contains useful code to help work with slice values. +//! Slices are a view into a block of memory represented as a pointer and a length. +//! +//! ```rust +//! // slicing a Vec +//! let vec = vec!(1i, 2, 3); +//! let int_slice = vec.as_slice(); +//! // coercing an array to a slice +//! let str_slice: &[&str] = ["one", "two", "three"]; +//! ``` +//! +//! Slices are either mutable or shared. The shared slice type is `&[T]`, +//! while the mutable slice type is `&mut[T]`. For example, you can mutate the +//! block of memory that a mutable slice points to: +//! +//! ```rust +//! let x: &mut[int] = [1i, 2, 3]; +//! x[1] = 7; +//! assert_eq!(x[0], 1); +//! assert_eq!(x[1], 7); +//! assert_eq!(x[2], 3); +//! ``` +//! +//! Here are some of the things this module contains: +//! +//! ## Structs +//! +//! There are several structs that are useful for slices, such as `Items`, which +//! represents iteration over a slice. +//! +//! ## Traits +//! +//! A number of traits add methods that allow you to accomplish tasks with slices. +//! These traits include `ImmutableSlice`, which is defined for `&[T]` types, +//! and `MutableSlice`, defined for `&mut [T]` types. +//! +//! An example is the method `.slice(a, b)` that returns an immutable "view" into +//! a `Vec` or another slice from the index interval `[a, b)`: +//! +//! ```rust +//! let numbers = [0i, 1i, 2i]; +//! let last_numbers = numbers.slice(1, 3); +//! // last_numbers is now &[1i, 2i] +//! ``` +//! +//! ## Implementations of other traits +//! +//! There are several implementations of common traits for slices. Some examples +//! include: +//! +//! * `Clone` +//! * `Eq`, `Ord` - for immutable slices whose element type are `Eq` or `Ord`. +//! * `Hash` - for slices whose element type is `Hash` +//! +//! ## Iteration +//! +//! The method `iter()` returns an iteration value for a slice. The iterator +//! yields references to the slice's elements, so if the element +//! type of the slice is `int`, the element type of the iterator is `&int`. +//! +//! ```rust +//! let numbers = [0i, 1i, 2i]; +//! for &x in numbers.iter() { +//! println!("{} is a number!", x); +//! } +//! ``` +//! +//! * `.mut_iter()` returns an iterator that allows modifying each value. +//! * Further iterators exist that split, chunk or permute the slice. #![doc(primitive = "slice")] @@ -109,7 +105,7 @@ pub use core::slice::{Found, NotFound}; pub trait VectorVector<T> { // FIXME #5898: calling these .concat and .connect conflicts with // StrVector::con{cat,nect}, since they have generic contents. - /// Flattens a vector of vectors of T into a single vector of T. + /// Flattens a vector of vectors of `T` into a single `Vec<T>`. fn concat_vec(&self) -> Vec<T>; /// Concatenate a vector of vectors, placing a given separator between each. @@ -138,7 +134,7 @@ impl<'a, T: Clone, V: Slice<T>> VectorVector<T> for &'a [V] { } } -/// An Iterator that yields the element swaps needed to produce +/// An iterator that yields the element swaps needed to produce /// a sequence of all possible permutations for an indexed sequence of /// elements. Each permutation is only a single swap apart. /// @@ -150,13 +146,14 @@ impl<'a, T: Clone, V: Slice<T>> VectorVector<T> for &'a [V] { /// sequence to its initial order. pub struct ElementSwaps { sdir: Vec<SizeDirection>, - /// If true, emit the last swap that returns the sequence to initial state + /// If `true`, emit the last swap that returns the sequence to initial + /// state. emit_reset: bool, swaps_made : uint, } impl ElementSwaps { - /// Create an `ElementSwaps` iterator for a sequence of `length` elements + /// Creates an `ElementSwaps` iterator for a sequence of `length` elements. pub fn new(length: uint) -> ElementSwaps { // Initialize `sdir` with a direction that position should move in // (all negative at the beginning) and the `size` of the @@ -171,7 +168,7 @@ impl ElementSwaps { enum Direction { Pos, Neg } -/// An Index and Direction together +/// An `Index` and `Direction` together. struct SizeDirection { size: uint, dir: Direction, @@ -229,7 +226,7 @@ impl Iterator<(uint, uint)> for ElementSwaps { } } -/// An Iterator that uses `ElementSwaps` to iterate through +/// An iterator that uses `ElementSwaps` to iterate through /// all possible permutations of a vector. /// /// The first iteration yields a clone of the vector as it is, @@ -264,16 +261,16 @@ impl<T: Clone> Iterator<Vec<T>> for Permutations<T> { /// Extension methods for vector slices with cloneable elements pub trait CloneableVector<T> { - /// Copy `self` into a new vector + /// Copies `self` into a new `Vec`. fn to_vec(&self) -> Vec<T>; - /// Deprecated. Use `to_vec` + /// Deprecated. Use `to_vec`. #[deprecated = "Replaced by `to_vec`"] fn to_owned(&self) -> Vec<T> { self.to_vec() } - /// Convert `self` into an owned vector, not making a copy if possible. + /// Converts `self` into an owned vector, not making a copy if possible. fn into_vec(self) -> Vec<T>; /// Deprecated. Use `into_vec` @@ -283,7 +280,6 @@ pub trait CloneableVector<T> { } } -/// Extension methods for vector slices impl<'a, T: Clone> CloneableVector<T> for &'a [T] { /// Returns a copy of `v`. #[inline] @@ -295,11 +291,11 @@ impl<'a, T: Clone> CloneableVector<T> for &'a [T] { /// Extension methods for vectors containing `Clone` elements. pub trait ImmutableCloneableVector<T> { - /// Partitions the vector into two vectors `(A,B)`, where all - /// elements of `A` satisfy `f` and all elements of `B` do not. + /// Partitions the vector into two vectors `(a, b)`, where all + /// elements of `a` satisfy `f` and all elements of `b` do not. fn partitioned(&self, f: |&T| -> bool) -> (Vec<T>, Vec<T>); - /// Create an iterator that yields every possible permutation of the + /// Creates an iterator that yields every possible permutation of the /// vector in succession. /// /// # Example @@ -559,7 +555,7 @@ fn merge_sort<T>(v: &mut [T], compare: |&T, &T| -> Ordering) { /// Extension methods for vectors such that their elements are /// mutable. pub trait MutableSliceAllocating<'a, T> { - /// Sort the vector, in place, using `compare` to compare + /// Sorts the slice, in place, using `compare` to compare /// elements. /// /// This sort is `O(n log n)` worst-case and stable, but allocates @@ -578,29 +574,27 @@ pub trait MutableSliceAllocating<'a, T> { /// ``` fn sort_by(self, compare: |&T, &T| -> Ordering); - /** - * Consumes `src` and moves as many elements as it can into `self` - * from the range [start,end). - * - * Returns the number of elements copied (the shorter of self.len() - * and end - start). - * - * # Arguments - * - * * src - A mutable vector of `T` - * * start - The index into `src` to start copying from - * * end - The index into `src` to stop copying from - * - * # Example - * - * ```rust - * let mut a = [1i, 2, 3, 4, 5]; - * let b = vec![6i, 7, 8]; - * let num_moved = a.move_from(b, 0, 3); - * assert_eq!(num_moved, 3); - * assert!(a == [6i, 7, 8, 4, 5]); - * ``` - */ + /// Consumes `src` and moves as many elements as it can into `self` + /// from the range [start,end). + /// + /// Returns the number of elements copied (the shorter of `self.len()` + /// and `end - start`). + /// + /// # Arguments + /// + /// * src - A mutable vector of `T` + /// * start - The index into `src` to start copying from + /// * end - The index into `src` to stop copying from + /// + /// # Example + /// + /// ```rust + /// let mut a = [1i, 2, 3, 4, 5]; + /// let b = vec![6i, 7, 8]; + /// let num_moved = a.move_from(b, 0, 3); + /// assert_eq!(num_moved, 3); + /// assert!(a == [6i, 7, 8, 4, 5]); + /// ``` fn move_from(self, src: Vec<T>, start: uint, end: uint) -> uint; } @@ -622,7 +616,7 @@ impl<'a,T> MutableSliceAllocating<'a, T> for &'a mut [T] { /// Methods for mutable vectors with orderable elements, such as /// in-place sorting. pub trait MutableOrdSlice<T> { - /// Sort the vector, in place. + /// Sorts the slice, in place. /// /// This is equivalent to `self.sort_by(|a, b| a.cmp(b))`. /// @@ -638,31 +632,37 @@ pub trait MutableOrdSlice<T> { /// Mutates the slice to the next lexicographic permutation. /// - /// Returns `true` if successful, `false` if the slice is at the last-ordered permutation. + /// Returns `true` if successful and `false` if the slice is at the + /// last-ordered permutation. /// /// # Example /// /// ```rust - /// let v = &mut [0i, 1, 2]; + /// let v: &mut [_] = &mut [0i, 1, 2]; /// v.next_permutation(); - /// assert_eq!(v, &mut [0i, 2, 1]); + /// let b: &mut [_] = &mut [0i, 2, 1]; + /// assert!(v == b); /// v.next_permutation(); - /// assert_eq!(v, &mut [1i, 0, 2]); + /// let b: &mut [_] = &mut [1i, 0, 2]; + /// assert!(v == b); /// ``` fn next_permutation(self) -> bool; /// Mutates the slice to the previous lexicographic permutation. /// - /// Returns `true` if successful, `false` if the slice is at the first-ordered permutation. + /// Returns `true` if successful and `false` if the slice is at the + /// first-ordered permutation. /// /// # Example /// /// ```rust - /// let v = &mut [1i, 0, 2]; + /// let v: &mut [_] = &mut [1i, 0, 2]; /// v.prev_permutation(); - /// assert_eq!(v, &mut [0i, 2, 1]); + /// let b: &mut [_] = &mut [0i, 2, 1]; + /// assert!(v == b); /// v.prev_permutation(); - /// assert_eq!(v, &mut [0i, 1, 2]); + /// let b: &mut [_] = &mut [0i, 1, 2]; + /// assert!(v == b); /// ``` fn prev_permutation(self) -> bool; } @@ -849,9 +849,11 @@ mod tests { #[test] fn test_tail() { let mut a = vec![11i]; - assert_eq!(a.tail(), &[]); + let b: &[int] = &[]; + assert_eq!(a.tail(), b); a = vec![11i, 12]; - assert_eq!(a.tail(), &[12]); + let b: &[int] = &[12]; + assert_eq!(a.tail(), b); } #[test] @@ -864,9 +866,11 @@ mod tests { #[test] fn test_tailn() { let mut a = vec![11i, 12, 13]; - assert_eq!(a.tailn(0), &[11, 12, 13]); + let b: &[int] = &[11, 12, 13]; + assert_eq!(a.tailn(0), b); a = vec![11i, 12, 13]; - assert_eq!(a.tailn(2), &[13]); + let b: &[int] = &[13]; + assert_eq!(a.tailn(2), b); } #[test] @@ -879,9 +883,11 @@ mod tests { #[test] fn test_init() { let mut a = vec![11i]; - assert_eq!(a.init(), &[]); + let b: &[int] = &[]; + assert_eq!(a.init(), b); a = vec![11i, 12]; - assert_eq!(a.init(), &[11]); + let b: &[int] = &[11]; + assert_eq!(a.init(), b); } #[test] @@ -894,9 +900,11 @@ mod tests { #[test] fn test_initn() { let mut a = vec![11i, 12, 13]; - assert_eq!(a.as_slice().initn(0), &[11, 12, 13]); + let b: &[int] = &[11, 12, 13]; + assert_eq!(a.as_slice().initn(0), b); a = vec![11i, 12, 13]; - assert_eq!(a.as_slice().initn(2), &[11]); + let b: &[int] = &[11]; + assert_eq!(a.as_slice().initn(2), b); } #[test] @@ -949,18 +957,22 @@ mod tests { #[test] fn test_slice_from() { - let vec = &[1i, 2, 3, 4]; + let vec: &[int] = &[1, 2, 3, 4]; assert_eq!(vec.slice_from(0), vec); - assert_eq!(vec.slice_from(2), &[3, 4]); - assert_eq!(vec.slice_from(4), &[]); + let b: &[int] = &[3, 4]; + assert_eq!(vec.slice_from(2), b); + let b: &[int] = &[]; + assert_eq!(vec.slice_from(4), b); } #[test] fn test_slice_to() { - let vec = &[1i, 2, 3, 4]; + let vec: &[int] = &[1, 2, 3, 4]; assert_eq!(vec.slice_to(4), vec); - assert_eq!(vec.slice_to(2), &[1, 2]); - assert_eq!(vec.slice_to(0), &[]); + let b: &[int] = &[1, 2]; + assert_eq!(vec.slice_to(2), b); + let b: &[int] = &[]; + assert_eq!(vec.slice_to(0), b); } @@ -1214,23 +1226,30 @@ mod tests { let v : &mut[int] = &mut[1i, 2, 3, 4, 5]; assert!(v.prev_permutation() == false); assert!(v.next_permutation()); - assert_eq!(v, &mut[1, 2, 3, 5, 4]); + let b: &mut[int] = &mut[1, 2, 3, 5, 4]; + assert!(v == b); assert!(v.prev_permutation()); - assert_eq!(v, &mut[1, 2, 3, 4, 5]); + let b: &mut[int] = &mut[1, 2, 3, 4, 5]; + assert!(v == b); assert!(v.next_permutation()); assert!(v.next_permutation()); - assert_eq!(v, &mut[1, 2, 4, 3, 5]); + let b: &mut[int] = &mut[1, 2, 4, 3, 5]; + assert!(v == b); assert!(v.next_permutation()); - assert_eq!(v, &mut[1, 2, 4, 5, 3]); + let b: &mut[int] = &mut[1, 2, 4, 5, 3]; + assert!(v == b); let v : &mut[int] = &mut[1i, 0, 0, 0]; assert!(v.next_permutation() == false); assert!(v.prev_permutation()); - assert_eq!(v, &mut[0, 1, 0, 0]); + let b: &mut[int] = &mut[0, 1, 0, 0]; + assert!(v == b); assert!(v.prev_permutation()); - assert_eq!(v, &mut[0, 0, 1, 0]); + let b: &mut[int] = &mut[0, 0, 1, 0]; + assert!(v == b); assert!(v.prev_permutation()); - assert_eq!(v, &mut[0, 0, 0, 1]); + let b: &mut[int] = &mut[0, 0, 0, 1]; + assert!(v == b); assert!(v.prev_permutation() == false); } @@ -1238,27 +1257,31 @@ mod tests { fn test_lexicographic_permutations_empty_and_short() { let empty : &mut[int] = &mut[]; assert!(empty.next_permutation() == false); - assert_eq!(empty, &mut[]); + let b: &mut[int] = &mut[]; + assert!(empty == b); assert!(empty.prev_permutation() == false); - assert_eq!(empty, &mut[]); + assert!(empty == b); let one_elem : &mut[int] = &mut[4i]; assert!(one_elem.prev_permutation() == false); - assert_eq!(one_elem, &mut[4]); + let b: &mut[int] = &mut[4]; + assert!(one_elem == b); assert!(one_elem.next_permutation() == false); - assert_eq!(one_elem, &mut[4]); + assert!(one_elem == b); let two_elem : &mut[int] = &mut[1i, 2]; assert!(two_elem.prev_permutation() == false); - assert_eq!(two_elem, &mut[1, 2]); + let b : &mut[int] = &mut[1, 2]; + let c : &mut[int] = &mut[2, 1]; + assert!(two_elem == b); assert!(two_elem.next_permutation()); - assert_eq!(two_elem, &mut[2, 1]); + assert!(two_elem == c); assert!(two_elem.next_permutation() == false); - assert_eq!(two_elem, &mut[2, 1]); + assert!(two_elem == c); assert!(two_elem.prev_permutation()); - assert_eq!(two_elem, &mut[1, 2]); + assert!(two_elem == b); assert!(two_elem.prev_permutation() == false); - assert_eq!(two_elem, &mut[1, 2]); + assert!(two_elem == b); } #[test] @@ -1412,7 +1435,10 @@ mod tests { assert_eq!(v.concat_vec(), vec![]); assert_eq!([vec![1i], vec![2i,3i]].concat_vec(), vec![1, 2, 3]); - assert_eq!([&[1i], &[2i,3i]].concat_vec(), vec![1, 2, 3]); + let v: [&[int], ..2] = [&[1], &[2, 3]]; + assert_eq!(v.connect_vec(&0), vec![1, 0, 2, 3]); + let v: [&[int], ..3] = [&[1], &[2], &[3]]; + assert_eq!(v.connect_vec(&0), vec![1, 0, 2, 0, 3]); } #[test] @@ -1422,8 +1448,10 @@ mod tests { assert_eq!([vec![1i], vec![2i, 3]].connect_vec(&0), vec![1, 0, 2, 3]); assert_eq!([vec![1i], vec![2i], vec![3i]].connect_vec(&0), vec![1, 0, 2, 0, 3]); - assert_eq!([&[1i], &[2i, 3]].connect_vec(&0), vec![1, 0, 2, 3]); - assert_eq!([&[1i], &[2i], &[3]].connect_vec(&0), vec![1, 0, 2, 0, 3]); + let v: [&[int], ..2] = [&[1], &[2, 3]]; + assert_eq!(v.connect_vec(&0), vec![1, 0, 2, 3]); + let v: [&[int], ..3] = [&[1], &[2], &[3]]; + assert_eq!(v.connect_vec(&0), vec![1, 0, 2, 0, 3]); } #[test] @@ -1581,11 +1609,16 @@ mod tests { #[test] fn test_total_ord() { - [1i, 2, 3, 4].cmp(& &[1, 2, 3]) == Greater; - [1i, 2, 3].cmp(& &[1, 2, 3, 4]) == Less; - [1i, 2, 3, 4].cmp(& &[1, 2, 3, 4]) == Equal; - [1i, 2, 3, 4, 5, 5, 5, 5].cmp(& &[1, 2, 3, 4, 5, 6]) == Less; - [2i, 2].cmp(& &[1, 2, 3, 4]) == Greater; + let c: &[int] = &[1, 2, 3]; + [1, 2, 3, 4].cmp(& c) == Greater; + let c: &[int] = &[1, 2, 3, 4]; + [1, 2, 3].cmp(& c) == Less; + let c: &[int] = &[1, 2, 3, 6]; + [1, 2, 3, 4].cmp(& c) == Equal; + let c: &[int] = &[1, 2, 3, 4, 5, 6]; + [1, 2, 3, 4, 5, 5, 5, 5].cmp(& c) == Less; + let c: &[int] = &[1, 2, 3, 4]; + [2, 2].cmp(& c) == Greater; } #[test] @@ -1709,74 +1742,95 @@ mod tests { fn test_splitator() { let xs = &[1i,2,3,4,5]; + let splits: &[&[int]] = &[&[1], &[3], &[5]]; assert_eq!(xs.split(|x| *x % 2 == 0).collect::<Vec<&[int]>>().as_slice(), - &[&[1], &[3], &[5]]); + splits); + let splits: &[&[int]] = &[&[], &[2,3,4,5]]; assert_eq!(xs.split(|x| *x == 1).collect::<Vec<&[int]>>().as_slice(), - &[&[], &[2,3,4,5]]); + splits); + let splits: &[&[int]] = &[&[1,2,3,4], &[]]; assert_eq!(xs.split(|x| *x == 5).collect::<Vec<&[int]>>().as_slice(), - &[&[1,2,3,4], &[]]); + splits); + let splits: &[&[int]] = &[&[1,2,3,4,5]]; assert_eq!(xs.split(|x| *x == 10).collect::<Vec<&[int]>>().as_slice(), - &[&[1,2,3,4,5]]); + splits); + let splits: &[&[int]] = &[&[], &[], &[], &[], &[], &[]]; assert_eq!(xs.split(|_| true).collect::<Vec<&[int]>>().as_slice(), - &[&[], &[], &[], &[], &[], &[]]); + splits); let xs: &[int] = &[]; - assert_eq!(xs.split(|x| *x == 5).collect::<Vec<&[int]>>().as_slice(), &[&[]]); + let splits: &[&[int]] = &[&[]]; + assert_eq!(xs.split(|x| *x == 5).collect::<Vec<&[int]>>().as_slice(), splits); } #[test] fn test_splitnator() { let xs = &[1i,2,3,4,5]; + let splits: &[&[int]] = &[&[1,2,3,4,5]]; assert_eq!(xs.splitn(0, |x| *x % 2 == 0).collect::<Vec<&[int]>>().as_slice(), - &[&[1,2,3,4,5]]); + splits); + let splits: &[&[int]] = &[&[1], &[3,4,5]]; assert_eq!(xs.splitn(1, |x| *x % 2 == 0).collect::<Vec<&[int]>>().as_slice(), - &[&[1], &[3,4,5]]); + splits); + let splits: &[&[int]] = &[&[], &[], &[], &[4,5]]; assert_eq!(xs.splitn(3, |_| true).collect::<Vec<&[int]>>().as_slice(), - &[&[], &[], &[], &[4,5]]); + splits); let xs: &[int] = &[]; - assert_eq!(xs.splitn(1, |x| *x == 5).collect::<Vec<&[int]>>().as_slice(), &[&[]]); + let splits: &[&[int]] = &[&[]]; + assert_eq!(xs.splitn(1, |x| *x == 5).collect::<Vec<&[int]>>().as_slice(), splits); } #[test] fn test_rsplitator() { let xs = &[1i,2,3,4,5]; + let splits: &[&[int]] = &[&[5], &[3], &[1]]; assert_eq!(xs.split(|x| *x % 2 == 0).rev().collect::<Vec<&[int]>>().as_slice(), - &[&[5], &[3], &[1]]); + splits); + let splits: &[&[int]] = &[&[2,3,4,5], &[]]; assert_eq!(xs.split(|x| *x == 1).rev().collect::<Vec<&[int]>>().as_slice(), - &[&[2,3,4,5], &[]]); + splits); + let splits: &[&[int]] = &[&[], &[1,2,3,4]]; assert_eq!(xs.split(|x| *x == 5).rev().collect::<Vec<&[int]>>().as_slice(), - &[&[], &[1,2,3,4]]); + splits); + let splits: &[&[int]] = &[&[1,2,3,4,5]]; assert_eq!(xs.split(|x| *x == 10).rev().collect::<Vec<&[int]>>().as_slice(), - &[&[1,2,3,4,5]]); + splits); let xs: &[int] = &[]; - assert_eq!(xs.split(|x| *x == 5).rev().collect::<Vec<&[int]>>().as_slice(), &[&[]]); + let splits: &[&[int]] = &[&[]]; + assert_eq!(xs.split(|x| *x == 5).rev().collect::<Vec<&[int]>>().as_slice(), splits); } #[test] fn test_rsplitnator() { let xs = &[1,2,3,4,5]; + let splits: &[&[int]] = &[&[1,2,3,4,5]]; assert_eq!(xs.rsplitn(0, |x| *x % 2 == 0).collect::<Vec<&[int]>>().as_slice(), - &[&[1,2,3,4,5]]); + splits); + let splits: &[&[int]] = &[&[5], &[1,2,3]]; assert_eq!(xs.rsplitn(1, |x| *x % 2 == 0).collect::<Vec<&[int]>>().as_slice(), - &[&[5], &[1,2,3]]); + splits); + let splits: &[&[int]] = &[&[], &[], &[], &[1,2]]; assert_eq!(xs.rsplitn(3, |_| true).collect::<Vec<&[int]>>().as_slice(), - &[&[], &[], &[], &[1,2]]); + splits); let xs: &[int] = &[]; - assert_eq!(xs.rsplitn(1, |x| *x == 5).collect::<Vec<&[int]>>().as_slice(), &[&[]]); + let splits: &[&[int]] = &[&[]]; + assert_eq!(xs.rsplitn(1, |x| *x == 5).collect::<Vec<&[int]>>().as_slice(), splits); } #[test] fn test_windowsator() { let v = &[1i,2,3,4]; - assert_eq!(v.windows(2).collect::<Vec<&[int]>>().as_slice(), &[&[1,2], &[2,3], &[3,4]]); - assert_eq!(v.windows(3).collect::<Vec<&[int]>>().as_slice(), &[&[1i,2,3], &[2,3,4]]); + let wins: &[&[int]] = &[&[1,2], &[2,3], &[3,4]]; + assert_eq!(v.windows(2).collect::<Vec<&[int]>>().as_slice(), wins); + let wins: &[&[int]] = &[&[1i,2,3], &[2,3,4]]; + assert_eq!(v.windows(3).collect::<Vec<&[int]>>().as_slice(), wins); assert!(v.windows(6).next().is_none()); } @@ -1791,16 +1845,23 @@ mod tests { fn test_chunksator() { let v = &[1i,2,3,4,5]; - assert_eq!(v.chunks(2).collect::<Vec<&[int]>>().as_slice(), &[&[1i,2], &[3,4], &[5]]); - assert_eq!(v.chunks(3).collect::<Vec<&[int]>>().as_slice(), &[&[1i,2,3], &[4,5]]); - assert_eq!(v.chunks(6).collect::<Vec<&[int]>>().as_slice(), &[&[1i,2,3,4,5]]); + let chunks: &[&[int]] = &[&[1i,2], &[3,4], &[5]]; + assert_eq!(v.chunks(2).collect::<Vec<&[int]>>().as_slice(), chunks); + let chunks: &[&[int]] = &[&[1i,2,3], &[4,5]]; + assert_eq!(v.chunks(3).collect::<Vec<&[int]>>().as_slice(), chunks); + let chunks: &[&[int]] = &[&[1i,2,3,4,5]]; + assert_eq!(v.chunks(6).collect::<Vec<&[int]>>().as_slice(), chunks); - assert_eq!(v.chunks(2).rev().collect::<Vec<&[int]>>().as_slice(), &[&[5i], &[3,4], &[1,2]]); + let chunks: &[&[int]] = &[&[5i], &[3,4], &[1,2]]; + assert_eq!(v.chunks(2).rev().collect::<Vec<&[int]>>().as_slice(), chunks); let mut it = v.chunks(2); assert_eq!(it.indexable(), 3); - assert_eq!(it.idx(0).unwrap(), &[1,2]); - assert_eq!(it.idx(1).unwrap(), &[3,4]); - assert_eq!(it.idx(2).unwrap(), &[5]); + let chunk: &[int] = &[1,2]; + assert_eq!(it.idx(0).unwrap(), chunk); + let chunk: &[int] = &[3,4]; + assert_eq!(it.idx(1).unwrap(), chunk); + let chunk: &[int] = &[5]; + assert_eq!(it.idx(2).unwrap(), chunk); assert_eq!(it.idx(3), None); } @@ -1868,10 +1929,12 @@ mod tests { let empty_mut: &mut [int] = &mut[]; test_show_vec!(empty_mut, "[]".to_string()); - test_show_vec!(&mut[1i], "[1]".to_string()); - test_show_vec!(&mut[1i, 2, 3], "[1, 2, 3]".to_string()); - test_show_vec!(&mut[&mut[], &mut[1u], &mut[1u, 1u]], - "[[], [1], [1, 1]]".to_string()); + let v: &mut[int] = &mut[1]; + test_show_vec!(v, "[1]".to_string()); + let v: &mut[int] = &mut[1, 2, 3]; + test_show_vec!(v, "[1, 2, 3]".to_string()); + let v: &mut [&mut[uint]] = &mut[&mut[], &mut[1u], &mut[1u, 1u]]; + test_show_vec!(v, "[[], [1], [1, 1]]".to_string()); } #[test] diff --git a/src/libcollections/smallintmap.rs b/src/libcollections/smallintmap.rs index 44db9147226..5ef1dd2ab22 100644 --- a/src/libcollections/smallintmap.rs +++ b/src/libcollections/smallintmap.rs @@ -66,24 +66,24 @@ pub struct SmallIntMap<T> { } impl<V> Collection for SmallIntMap<V> { - /// Return the number of elements in the map. + /// Returns the number of elements in the map. fn len(&self) -> uint { self.v.iter().filter(|elt| elt.is_some()).count() } - /// Return `true` if there are no elements in the map. + /// Returns`true` if there are no elements in the map. fn is_empty(&self) -> bool { self.v.iter().all(|elt| elt.is_none()) } } impl<V> Mutable for SmallIntMap<V> { - /// Clear the map, removing all key-value pairs. + /// Clears the map, removing all key-value pairs. fn clear(&mut self) { self.v.clear() } } impl<V> Map<uint, V> for SmallIntMap<V> { - /// Return a reference to the value corresponding to the key. + /// Returns a reference to the value corresponding to the key. fn find<'a>(&'a self, key: &uint) -> Option<&'a V> { if *key < self.v.len() { match self.v[*key] { @@ -97,7 +97,7 @@ impl<V> Map<uint, V> for SmallIntMap<V> { } impl<V> MutableMap<uint, V> for SmallIntMap<V> { - /// Return a mutable reference to the value corresponding to the key. + /// Returns a mutable reference to the value corresponding to the key. fn find_mut<'a>(&'a mut self, key: &uint) -> Option<&'a mut V> { if *key < self.v.len() { match *self.v.get_mut(*key) { @@ -109,8 +109,8 @@ impl<V> MutableMap<uint, V> for SmallIntMap<V> { } } - /// Insert a key-value pair into the map. An existing value for a - /// key is replaced by the new value. Return `true` if the key did + /// Inserts a key-value pair into the map. An existing value for a + /// key is replaced by the new value. Returns `true` if the key did /// not already exist in the map. fn insert(&mut self, key: uint, value: V) -> bool { let exists = self.contains_key(&key); @@ -122,13 +122,13 @@ impl<V> MutableMap<uint, V> for SmallIntMap<V> { !exists } - /// Remove a key-value pair from the map. Return `true` if the key - /// was present in the map, otherwise `false`. + /// Removes a key-value pair from the map. Returns `true` if the key + /// was present in the map. fn remove(&mut self, key: &uint) -> bool { self.pop(key).is_some() } - /// Insert a key-value pair from the map. If the key already had a value + /// Inserts a key-value pair into the map. If the key already had a value /// present in the map, that value is returned. Otherwise `None` is returned. fn swap(&mut self, key: uint, value: V) -> Option<V> { match self.find_mut(&key) { @@ -176,7 +176,7 @@ impl <S: hash::Writer, T: Hash<S>> Hash<S> for SmallIntMap<T> { } impl<V> SmallIntMap<V> { - /// Create an empty SmallIntMap. + /// Creates an empty `SmallIntMap`. /// /// # Example /// @@ -186,8 +186,8 @@ impl<V> SmallIntMap<V> { /// ``` pub fn new() -> SmallIntMap<V> { SmallIntMap{v: vec!()} } - /// Create an empty SmallIntMap with space for at least `capacity` elements - /// before resizing. + /// Creates an empty `SmallIntMap` with space for at least `capacity` + /// elements before resizing. /// /// # Example /// @@ -222,20 +222,20 @@ impl<V> SmallIntMap<V> { self.find(key).expect("key not present") } - /// An iterator visiting all keys in ascending order by the keys. - /// Iterator element type is `uint`. + /// Returns an iterator visiting all keys in ascending order by the keys. + /// The iterator's element type is `uint`. pub fn keys<'r>(&'r self) -> Keys<'r, V> { self.iter().map(|(k, _v)| k) } - /// An iterator visiting all values in ascending order by the keys. - /// Iterator element type is `&'r V`. + /// Returns an iterator visiting all values in ascending order by the keys. + /// The iterator's element type is `&'r V`. pub fn values<'r>(&'r self) -> Values<'r, V> { self.iter().map(|(_k, v)| v) } - /// An iterator visiting all key-value pairs in ascending order by the keys. - /// Iterator element type is `(uint, &'r V)`. + /// Returns an iterator visiting all key-value pairs in ascending order by the keys. + /// The iterator's element type is `(uint, &'r V)`. /// /// # Example /// @@ -260,9 +260,9 @@ impl<V> SmallIntMap<V> { } } - /// An iterator visiting all key-value pairs in ascending order by the keys, - /// with mutable references to the values - /// Iterator element type is `(uint, &'r mut V)`. + /// Returns an iterator visiting all key-value pairs in ascending order by the keys, + /// with mutable references to the values. + /// The iterator's element type is `(uint, &'r mut V)`. /// /// # Example /// @@ -290,7 +290,9 @@ impl<V> SmallIntMap<V> { } } - /// Empties the map, moving all values into the specified closure. + /// Returns an iterator visiting all key-value pairs in ascending order by + /// the keys, emptying (but not consuming) the original `SmallIntMap`. + /// The iterator's element type is `(uint, &'r V)`. /// /// # Example /// @@ -319,10 +321,10 @@ impl<V> SmallIntMap<V> { } impl<V:Clone> SmallIntMap<V> { - /// Update a value in the map. If the key already exists in the map, - /// modify the value with `ff` taking `oldval, newval`. - /// Otherwise set the value to `newval`. - /// Return `true` if the key did not already exist in the map. + /// Updates a value in the map. If the key already exists in the map, + /// modifies the value with `ff` taking `oldval, newval`. + /// Otherwise, sets the value to `newval`. + /// Returasn `true` if the key did not already exist in the map. /// /// # Example /// @@ -343,10 +345,10 @@ impl<V:Clone> SmallIntMap<V> { self.update_with_key(key, newval, |_k, v, v1| ff(v,v1)) } - /// Update a value in the map. If the key already exists in the map, - /// modify the value with `ff` taking `key, oldval, newval`. - /// Otherwise set the value to `newval`. - /// Return `true` if the key did not already exist in the map. + /// Updates a value in the map. If the key already exists in the map, + /// modifies the value with `ff` taking `key, oldval, newval`. + /// Otherwise, sets the value to `newval`. + /// Returns `true` if the key did not already exist in the map. /// /// # Example /// @@ -487,19 +489,37 @@ macro_rules! double_ended_iterator { } } -/// Forward iterator over a map. +/// Note: stage0-specific version that lacks bound on A. +#[cfg(stage0)] pub struct Entries<'a, T> { front: uint, back: uint, iter: slice::Items<'a, Option<T>> } +/// Forward iterator over a map. +#[cfg(not(stage0))] +pub struct Entries<'a, T:'a> { + front: uint, + back: uint, + iter: slice::Items<'a, Option<T>> +} + iterator!(impl Entries -> (uint, &'a T), get_ref) double_ended_iterator!(impl Entries -> (uint, &'a T), get_ref) +/// Note: stage0-specific version that lacks bound on A. +#[cfg(stage0)] +pub struct MutEntries<'a, T> { + front: uint, + back: uint, + iter: slice::MutItems<'a, Option<T>> +} + /// Forward iterator over the key-value pairs of a map, with the /// values being mutable. -pub struct MutEntries<'a, T> { +#[cfg(not(stage0))] +pub struct MutEntries<'a, T:'a> { front: uint, back: uint, iter: slice::MutItems<'a, Option<T>> diff --git a/src/libcollections/str.rs b/src/libcollections/str.rs index 9ca1011f166..3f9a179872e 100644 --- a/src/libcollections/str.rs +++ b/src/libcollections/str.rs @@ -10,51 +10,47 @@ // // ignore-lexer-test FIXME #15679 -/*! - -Unicode string manipulation (`str` type) - -# Basic Usage - -Rust's string type is one of the core primitive types of the language. While -represented by the name `str`, the name `str` is not actually a valid type in -Rust. Each string must also be decorated with a pointer. `String` is used -for an owned string, so there is only one commonly-used `str` type in Rust: -`&str`. - -`&str` is the borrowed string type. This type of string can only be created -from other strings, unless it is a static string (see below). As the word -"borrowed" implies, this type of string is owned elsewhere, and this string -cannot be moved out of. - -As an example, here's some code that uses a string. - -```rust -fn main() { - let borrowed_string = "This string is borrowed with the 'static lifetime"; -} -``` - -From the example above, you can see that Rust's string literals have the -`'static` lifetime. This is akin to C's concept of a static string. - -String literals are allocated statically in the rodata of the -executable/library. The string then has the type `&'static str` meaning that -the string is valid for the `'static` lifetime, otherwise known as the -lifetime of the entire program. As can be inferred from the type, these static -strings are not mutable. - -# Representation - -Rust's string type, `str`, is a sequence of unicode scalar values encoded as a -stream of UTF-8 bytes. All strings are guaranteed to be validly encoded UTF-8 -sequences. Additionally, strings are not null-terminated and can contain null -bytes. - -The actual representation of strings have direct mappings to vectors: `&str` -is the same as `&[u8]`. - -*/ +//! Unicode string manipulation (`str` type) +//! +//! # Basic Usage +//! +//! Rust's string type is one of the core primitive types of the language. While +//! represented by the name `str`, the name `str` is not actually a valid type in +//! Rust. Each string must also be decorated with a pointer. `String` is used +//! for an owned string, so there is only one commonly-used `str` type in Rust: +//! `&str`. +//! +//! `&str` is the borrowed string type. This type of string can only be created +//! from other strings, unless it is a static string (see below). As the word +//! "borrowed" implies, this type of string is owned elsewhere, and this string +//! cannot be moved out of. +//! +//! As an example, here's some code that uses a string. +//! +//! ```rust +//! fn main() { +//! let borrowed_string = "This string is borrowed with the 'static lifetime"; +//! } +//! ``` +//! +//! From the example above, you can see that Rust's string literals have the +//! `'static` lifetime. This is akin to C's concept of a static string. +//! +//! String literals are allocated statically in the rodata of the +//! executable/library. The string then has the type `&'static str` meaning that +//! the string is valid for the `'static` lifetime, otherwise known as the +//! lifetime of the entire program. As can be inferred from the type, these static +//! strings are not mutable. +//! +//! # Representation +//! +//! Rust's string type, `str`, is a sequence of unicode scalar values encoded as a +//! stream of UTF-8 bytes. All strings are guaranteed to be validly encoded UTF-8 +//! sequences. Additionally, strings are not null-terminated and can contain null +//! bytes. +//! +//! The actual representation of strings have direct mappings to slices: `&str` +//! is the same as `&[u8]`. #![doc(primitive = "str")] @@ -88,34 +84,34 @@ pub use unicode::str::{UnicodeStrSlice, Words, Graphemes, GraphemeIndices}; Section: Creating a string */ -/// Deprecated. Replaced by `String::from_utf8` +/// Deprecated. Replaced by `String::from_utf8`. #[deprecated = "Replaced by `String::from_utf8`"] pub fn from_utf8_owned(vv: Vec<u8>) -> Result<String, Vec<u8>> { String::from_utf8(vv) } -/// Deprecated. Replaced by `String::from_byte` +/// Deprecated. Replaced by `String::from_byte`. #[deprecated = "Replaced by String::from_byte"] pub fn from_byte(b: u8) -> String { assert!(b < 128u8); String::from_char(1, b as char) } -/// Deprecated. Use `String::from_char` or `char::to_string()` instead +/// Deprecated. Use `String::from_char` or `char::to_string()` instead. #[deprecated = "use String::from_char or char.to_string()"] pub fn from_char(ch: char) -> String { String::from_char(1, ch) } -/// Deprecated. Replaced by `String::from_chars` +/// Deprecated. Replaced by `String::from_chars`. #[deprecated = "use String::from_chars instead"] pub fn from_chars(chs: &[char]) -> String { chs.iter().map(|c| *c).collect() } -/// Methods for vectors of strings +/// Methods for vectors of strings. pub trait StrVector { - /// Concatenate a vector of strings. + /// Concatenates a vector of strings. /// /// # Example /// @@ -127,7 +123,7 @@ pub trait StrVector { /// ``` fn concat(&self) -> String; - /// Concatenate a vector of strings, placing a given separator between each. + /// Concatenates a vector of strings, placing a given separator between each. /// /// # Example /// @@ -394,7 +390,7 @@ impl<'a> Iterator<char> for Recompositions<'a> { } } -/// Replace all occurrences of one string with another +/// Replaces all occurrences of one string with another. /// /// # Arguments /// @@ -404,7 +400,7 @@ impl<'a> Iterator<char> for Recompositions<'a> { /// /// # Return value /// -/// The original string with all occurrences of `from` replaced with `to` +/// The original string with all occurrences of `from` replaced with `to`. /// /// # Example /// @@ -464,21 +460,21 @@ pub fn from_utf8_lossy<'a>(v: &'a [u8]) -> MaybeOwned<'a> { Section: MaybeOwned */ -/// A `MaybeOwned` is a string that can hold either a `String` or a `&str`. +/// A string type that can hold either a `String` or a `&str`. /// This can be useful as an optimization when an allocation is sometimes /// needed but not always. pub enum MaybeOwned<'a> { - /// A borrowed string + /// A borrowed string. Slice(&'a str), - /// An owned string + /// An owned string. Owned(String) } -/// `SendStr` is a specialization of `MaybeOwned` to be sendable +/// A specialization of `MaybeOwned` to be sendable. pub type SendStr = MaybeOwned<'static>; impl<'a> MaybeOwned<'a> { - /// Returns `true` if this `MaybeOwned` wraps an owned string + /// Returns `true` if this `MaybeOwned` wraps an owned string. /// /// # Example /// @@ -495,7 +491,7 @@ impl<'a> MaybeOwned<'a> { } } - /// Returns `true` if this `MaybeOwned` wraps a borrowed string + /// Returns `true` if this `MaybeOwned` wraps a borrowed string. /// /// # Example /// @@ -513,47 +509,47 @@ impl<'a> MaybeOwned<'a> { } } -/// Trait for moving into a `MaybeOwned` +/// Trait for moving into a `MaybeOwned`. pub trait IntoMaybeOwned<'a> { - /// Moves self into a `MaybeOwned` + /// Moves `self` into a `MaybeOwned`. fn into_maybe_owned(self) -> MaybeOwned<'a>; } -/// # Example -/// -/// ```rust -/// let owned_string = String::from_str("orange"); -/// let maybe_owned_string = owned_string.into_maybe_owned(); -/// assert_eq!(true, maybe_owned_string.is_owned()); -/// ``` impl<'a> IntoMaybeOwned<'a> for String { + /// # Example + /// + /// ```rust + /// let owned_string = String::from_str("orange"); + /// let maybe_owned_string = owned_string.into_maybe_owned(); + /// assert_eq!(true, maybe_owned_string.is_owned()); + /// ``` #[inline] fn into_maybe_owned(self) -> MaybeOwned<'a> { Owned(self) } } -/// # Example -/// -/// ```rust -/// let string = "orange"; -/// let maybe_owned_str = string.as_slice().into_maybe_owned(); -/// assert_eq!(false, maybe_owned_str.is_owned()); -/// ``` impl<'a> IntoMaybeOwned<'a> for &'a str { + /// # Example + /// + /// ```rust + /// let string = "orange"; + /// let maybe_owned_str = string.as_slice().into_maybe_owned(); + /// assert_eq!(false, maybe_owned_str.is_owned()); + /// ``` #[inline] fn into_maybe_owned(self) -> MaybeOwned<'a> { Slice(self) } } -/// # Example -/// -/// ```rust -/// let str = "orange"; -/// let maybe_owned_str = str.as_slice().into_maybe_owned(); -/// let maybe_maybe_owned_str = maybe_owned_str.into_maybe_owned(); -/// assert_eq!(false, maybe_maybe_owned_str.is_owned()); -/// ``` impl<'a> IntoMaybeOwned<'a> for MaybeOwned<'a> { + /// # Example + /// + /// ```rust + /// let str = "orange"; + /// let maybe_owned_str = str.as_slice().into_maybe_owned(); + /// let maybe_maybe_owned_str = maybe_owned_str.into_maybe_owned(); + /// assert_eq!(false, maybe_maybe_owned_str.is_owned()); + /// ``` #[inline] fn into_maybe_owned(self) -> MaybeOwned<'a> { self } } @@ -645,7 +641,7 @@ impl<'a> fmt::Show for MaybeOwned<'a> { } } -/// Unsafe operations +/// Unsafe string operations. pub mod raw { use string; use string::String; @@ -685,9 +681,9 @@ pub mod raw { Section: Trait implementations */ -/// Any string that can be represented as a slice +/// Any string that can be represented as a slice. pub trait StrAllocating: Str { - /// Convert `self` into a `String`, not making a copy if possible. + /// Converts `self` into a `String`, not making a copy if possible. fn into_string(self) -> String; #[allow(missing_doc)] @@ -696,7 +692,7 @@ pub trait StrAllocating: Str { self.into_string() } - /// Escape each char in `s` with `char::escape_default`. + /// Escapes each char in `s` with `char::escape_default`. fn escape_default(&self) -> String { let me = self.as_slice(); let mut out = String::with_capacity(me.len()); @@ -706,7 +702,7 @@ pub trait StrAllocating: Str { out } - /// Escape each char in `s` with `char::escape_unicode`. + /// Escapes each char in `s` with `char::escape_unicode`. fn escape_unicode(&self) -> String { let me = self.as_slice(); let mut out = String::with_capacity(me.len()); @@ -716,7 +712,7 @@ pub trait StrAllocating: Str { out } - /// Replace all occurrences of one string with another. + /// Replaces all occurrences of one string with another. /// /// # Arguments /// @@ -768,7 +764,7 @@ pub trait StrAllocating: Str { self.as_slice().utf16_units().collect::<Vec<u16>>() } - /// Given a string, make a new string with repeated copies of it. + /// Given a string, makes a new string with repeated copies of it. fn repeat(&self, nn: uint) -> String { let me = self.as_slice(); let mut ret = String::with_capacity(nn * me.len()); @@ -778,7 +774,7 @@ pub trait StrAllocating: Str { ret } - /// Levenshtein Distance between two strings. + /// Returns the Levenshtein Distance between two strings. fn lev_distance(&self, t: &str) -> uint { let me = self.as_slice(); let slen = me.len(); @@ -813,7 +809,7 @@ pub trait StrAllocating: Str { return dcol[tlen]; } - /// An Iterator over the string in Unicode Normalization Form D + /// Returns an iterator over the string in Unicode Normalization Form D /// (canonical decomposition). #[inline] fn nfd_chars<'a>(&'a self) -> Decompositions<'a> { @@ -825,7 +821,7 @@ pub trait StrAllocating: Str { } } - /// An Iterator over the string in Unicode Normalization Form KD + /// Returns an iterator over the string in Unicode Normalization Form KD /// (compatibility decomposition). #[inline] fn nfkd_chars<'a>(&'a self) -> Decompositions<'a> { @@ -1269,12 +1265,14 @@ mod tests { fn test_trim_left_chars() { let v: &[char] = &[]; assert_eq!(" *** foo *** ".trim_left_chars(v), " *** foo *** "); - assert_eq!(" *** foo *** ".trim_left_chars(&['*', ' ']), "foo *** "); - assert_eq!(" *** *** ".trim_left_chars(&['*', ' ']), ""); - assert_eq!("foo *** ".trim_left_chars(&['*', ' ']), "foo *** "); + let chars: &[char] = &['*', ' ']; + assert_eq!(" *** foo *** ".trim_left_chars(chars), "foo *** "); + assert_eq!(" *** *** ".trim_left_chars(chars), ""); + assert_eq!("foo *** ".trim_left_chars(chars), "foo *** "); assert_eq!("11foo1bar11".trim_left_chars('1'), "foo1bar11"); - assert_eq!("12foo1bar12".trim_left_chars(&['1', '2']), "foo1bar12"); + let chars: &[char] = &['1', '2']; + assert_eq!("12foo1bar12".trim_left_chars(chars), "foo1bar12"); assert_eq!("123foo1bar123".trim_left_chars(|c: char| c.is_digit()), "foo1bar123"); } @@ -1282,12 +1280,14 @@ mod tests { fn test_trim_right_chars() { let v: &[char] = &[]; assert_eq!(" *** foo *** ".trim_right_chars(v), " *** foo *** "); - assert_eq!(" *** foo *** ".trim_right_chars(&['*', ' ']), " *** foo"); - assert_eq!(" *** *** ".trim_right_chars(&['*', ' ']), ""); - assert_eq!(" *** foo".trim_right_chars(&['*', ' ']), " *** foo"); + let chars: &[char] = &['*', ' ']; + assert_eq!(" *** foo *** ".trim_right_chars(chars), " *** foo"); + assert_eq!(" *** *** ".trim_right_chars(chars), ""); + assert_eq!(" *** foo".trim_right_chars(chars), " *** foo"); assert_eq!("11foo1bar11".trim_right_chars('1'), "11foo1bar"); - assert_eq!("12foo1bar12".trim_right_chars(&['1', '2']), "12foo1bar"); + let chars: &[char] = &['1', '2']; + assert_eq!("12foo1bar12".trim_right_chars(chars), "12foo1bar"); assert_eq!("123foo1bar123".trim_right_chars(|c: char| c.is_digit()), "123foo1bar"); } @@ -1295,12 +1295,14 @@ mod tests { fn test_trim_chars() { let v: &[char] = &[]; assert_eq!(" *** foo *** ".trim_chars(v), " *** foo *** "); - assert_eq!(" *** foo *** ".trim_chars(&['*', ' ']), "foo"); - assert_eq!(" *** *** ".trim_chars(&['*', ' ']), ""); - assert_eq!("foo".trim_chars(&['*', ' ']), "foo"); + let chars: &[char] = &['*', ' ']; + assert_eq!(" *** foo *** ".trim_chars(chars), "foo"); + assert_eq!(" *** *** ".trim_chars(chars), ""); + assert_eq!("foo".trim_chars(chars), "foo"); assert_eq!("11foo1bar11".trim_chars('1'), "foo1bar"); - assert_eq!("12foo1bar12".trim_chars(&['1', '2']), "foo1bar"); + let chars: &[char] = &['1', '2']; + assert_eq!("12foo1bar12".trim_chars(chars), "foo1bar"); assert_eq!("123foo1bar123".trim_chars(|c: char| c.is_digit()), "foo1bar"); } @@ -1447,7 +1449,8 @@ mod tests { 184, 173, 229, 141, 142, 86, 105, 225, 187, 135, 116, 32, 78, 97, 109 ]; - assert_eq!("".as_bytes(), &[]); + let b: &[u8] = &[]; + assert_eq!("".as_bytes(), b); assert_eq!("abc".as_bytes(), b"abc"); assert_eq!("ศไทย中华Việt Nam".as_bytes(), v.as_slice()); } @@ -1546,19 +1549,23 @@ mod tests { #[test] fn test_truncate_utf16_at_nul() { let v = []; - assert_eq!(truncate_utf16_at_nul(v), &[]); + let b: &[u16] = &[]; + assert_eq!(truncate_utf16_at_nul(v), b); let v = [0, 2, 3]; - assert_eq!(truncate_utf16_at_nul(v), &[]); + assert_eq!(truncate_utf16_at_nul(v), b); let v = [1, 0, 3]; - assert_eq!(truncate_utf16_at_nul(v), &[1]); + let b: &[u16] = &[1]; + assert_eq!(truncate_utf16_at_nul(v), b); let v = [1, 2, 0]; - assert_eq!(truncate_utf16_at_nul(v), &[1, 2]); + let b: &[u16] = &[1, 2]; + assert_eq!(truncate_utf16_at_nul(v), b); let v = [1, 2, 3]; - assert_eq!(truncate_utf16_at_nul(v), &[1, 2, 3]); + let b: &[u16] = &[1, 2, 3]; + assert_eq!(truncate_utf16_at_nul(v), b); } #[test] @@ -1964,7 +1971,7 @@ mod tests { use std::iter::order; // official Unicode test data // from http://www.unicode.org/Public/UCD/latest/ucd/auxiliary/GraphemeBreakTest.txt - let test_same = [ + let test_same: [(_, &[_]), .. 325] = [ ("\u0020\u0020", &["\u0020", "\u0020"]), ("\u0020\u0308\u0020", &["\u0020\u0308", "\u0020"]), ("\u0020\u000D", &["\u0020", "\u000D"]), ("\u0020\u0308\u000D", &["\u0020\u0308", "\u000D"]), ("\u0020\u000A", &["\u0020", "\u000A"]), @@ -2173,7 +2180,7 @@ mod tests { ("\u0646\u200D\u0020", &["\u0646\u200D", "\u0020"]), ]; - let test_diff = [ + let test_diff: [(_, &[_], &[_]), .. 23] = [ ("\u0020\u0903", &["\u0020\u0903"], &["\u0020", "\u0903"]), ("\u0020\u0308\u0903", &["\u0020\u0308\u0903"], &["\u0020\u0308", "\u0903"]), ("\u000D\u0308\u0903", &["\u000D", "\u0308\u0903"], &["\u000D", "\u0308", "\u0903"]), ("\u000A\u0308\u0903", @@ -2222,9 +2229,11 @@ mod tests { // test the indices iterators let s = "a̐éö̲\r\n"; let gr_inds = s.grapheme_indices(true).collect::<Vec<(uint, &str)>>(); - assert_eq!(gr_inds.as_slice(), &[(0u, "a̐"), (3, "é"), (6, "ö̲"), (11, "\r\n")]); + let b: &[_] = &[(0u, "a̐"), (3, "é"), (6, "ö̲"), (11, "\r\n")]; + assert_eq!(gr_inds.as_slice(), b); let gr_inds = s.grapheme_indices(true).rev().collect::<Vec<(uint, &str)>>(); - assert_eq!(gr_inds.as_slice(), &[(11, "\r\n"), (6, "ö̲"), (3, "é"), (0u, "a̐")]); + let b: &[_] = &[(11, "\r\n"), (6, "ö̲"), (3, "é"), (0u, "a̐")]; + assert_eq!(gr_inds.as_slice(), b); let mut gr_inds = s.grapheme_indices(true); let e1 = gr_inds.size_hint(); assert_eq!(e1, (1, Some(13))); @@ -2236,7 +2245,8 @@ mod tests { // make sure the reverse iterator does the right thing with "\n" at beginning of string let s = "\n\r\n\r"; let gr = s.graphemes(true).rev().collect::<Vec<&str>>(); - assert_eq!(gr.as_slice(), &["\r", "\r\n", "\n"]); + let b: &[_] = &["\r", "\r\n", "\n"]; + assert_eq!(gr.as_slice(), b); } #[test] @@ -2498,7 +2508,8 @@ mod bench { let s = "Mary had a little lamb, Little lamb, little-lamb."; let len = s.split(' ').count(); - b.iter(|| assert_eq!(s.split(&[' ']).count(), len)); + let c: &[char] = &[' ']; + b.iter(|| assert_eq!(s.split(c).count(), len)); } #[bench] diff --git a/src/libcollections/string.rs b/src/libcollections/string.rs index 66973fd4100..05d91a75041 100644 --- a/src/libcollections/string.rs +++ b/src/libcollections/string.rs @@ -120,8 +120,8 @@ impl String { } } - /// Converts a vector of bytes to a new utf-8 string. - /// Any invalid utf-8 sequences are replaced with U+FFFD REPLACEMENT CHARACTER. + /// Converts a vector of bytes to a new UTF-8 string. + /// Any invalid UTF-8 sequences are replaced with U+FFFD REPLACEMENT CHARACTER. /// /// # Example /// @@ -289,7 +289,7 @@ impl String { str::utf16_items(v).map(|c| c.to_char_lossy()).collect() } - /// Convert a vector of chars to a string. + /// Convert a vector of `char`s to a `String`. /// /// # Example /// @@ -317,8 +317,8 @@ impl String { self.vec } - /// Pushes the given string onto this buffer; then, returns `self` so that it can be used - /// again. + /// Pushes the given `String` onto this buffer then returns `self` so that it can be + /// used again. /// /// # Example /// @@ -359,11 +359,11 @@ impl String { buf } - /// Convert a byte to a UTF-8 string. + /// Converts a byte to a UTF-8 string. /// /// # Failure /// - /// Fails if invalid UTF-8 + /// Fails with invalid UTF-8 (i.e., the byte is greater than 127). /// /// # Example /// @@ -390,7 +390,7 @@ impl String { self.vec.push_all(string.as_bytes()) } - /// Push `ch` onto the given string `count` times. + /// Pushes `ch` onto the given string `count` times. /// /// # Example /// @@ -531,7 +531,8 @@ impl String { /// /// ``` /// let s = String::from_str("hello"); - /// assert_eq!(s.as_bytes(), &[104, 101, 108, 108, 111]); + /// let b: &[_] = &[104, 101, 108, 108, 111]; + /// assert_eq!(s.as_bytes(), b); /// ``` #[inline] pub fn as_bytes<'a>(&'a self) -> &'a [u8] { @@ -552,7 +553,8 @@ impl String { /// bytes[1] = 51; /// bytes[4] = 48; /// } - /// assert_eq!(s.as_bytes(), &[104, 51, 108, 108, 48]); + /// let b: &[_] = &[104, 51, 108, 108, 48]; + /// assert_eq!(s.as_bytes(), b); /// assert_eq!(s.as_slice(), "h3ll0") /// ``` #[inline] @@ -560,7 +562,7 @@ impl String { self.vec.as_mut_slice() } - /// Shorten a string to the specified length. + /// Shortens a string to the specified length. /// /// # Failure /// @@ -815,11 +817,11 @@ pub mod raw { use super::String; use vec::Vec; - /// Creates a new `String` from length, capacity, and a pointer. + /// Creates a new `String` from a length, capacity, and pointer. /// /// This is unsafe because: - /// * We call `Vec::from_raw_parts` to get a `Vec<u8>` - /// * We assume that the `Vec` contains valid UTF-8 + /// * We call `Vec::from_raw_parts` to get a `Vec<u8>`; + /// * We assume that the `Vec` contains valid UTF-8. #[inline] pub unsafe fn from_parts(buf: *mut u8, length: uint, capacity: uint) -> String { String { @@ -827,11 +829,11 @@ pub mod raw { } } - /// Create `String` from a *u8 buffer of the given length + /// Creates a `String` from a `*const u8` buffer of the given length. /// /// This function is unsafe because of two reasons: - /// * A raw pointer is dereferenced and transmuted to `&[u8]` - /// * The slice is not checked to see whether it contains valid UTF-8 + /// * A raw pointer is dereferenced and transmuted to `&[u8]`; + /// * The slice is not checked to see whether it contains valid UTF-8. pub unsafe fn from_buf_len(buf: *const u8, len: uint) -> String { use slice::CloneableVector; let slice: &[u8] = mem::transmute(Slice { @@ -841,7 +843,7 @@ pub mod raw { self::from_utf8(slice.to_vec()) } - /// Create a `String` from a null-terminated *u8 buffer + /// Creates a `String` from a null-terminated `*const u8` buffer. /// /// This function is unsafe because we dereference memory until we find the NUL character, /// which is not guaranteed to be present. Additionally, the slice is not checked to see @@ -856,7 +858,7 @@ pub mod raw { /// Converts a vector of bytes to a new `String` without checking if /// it contains valid UTF-8. This is unsafe because it assumes that - /// the utf-8-ness of the vector has already been validated. + /// the UTF-8-ness of the vector has already been validated. #[inline] pub unsafe fn from_utf8(bytes: Vec<u8>) -> String { String { vec: bytes } diff --git a/src/libcollections/treemap.rs b/src/libcollections/treemap.rs index 7787893925d..6bb1e4a5ad0 100644 --- a/src/libcollections/treemap.rs +++ b/src/libcollections/treemap.rs @@ -261,7 +261,7 @@ impl<K: Ord, V> Index<K, V> for TreeMap<K, V> { }*/ impl<K: Ord, V> TreeMap<K, V> { - /// Create an empty `TreeMap`. + /// Creates an empty `TreeMap`. /// /// # Example /// @@ -271,7 +271,7 @@ impl<K: Ord, V> TreeMap<K, V> { /// ``` pub fn new() -> TreeMap<K, V> { TreeMap{root: None, length: 0} } - /// Get a lazy iterator over the keys in the map, in ascending order. + /// Gets a lazy iterator over the keys in the map, in ascending order. /// /// # Example /// @@ -291,7 +291,7 @@ impl<K: Ord, V> TreeMap<K, V> { self.iter().map(|(k, _v)| k) } - /// Get a lazy iterator over the values in the map, in ascending order + /// Gets a lazy iterator over the values in the map, in ascending order /// with respect to the corresponding keys. /// /// # Example @@ -312,7 +312,7 @@ impl<K: Ord, V> TreeMap<K, V> { self.iter().map(|(_k, v)| v) } - /// Get a lazy iterator over the key-value pairs in the map, in ascending order. + /// Gets a lazy iterator over the key-value pairs in the map, in ascending order. /// /// # Example /// @@ -337,7 +337,7 @@ impl<K: Ord, V> TreeMap<K, V> { } } - /// Get a lazy reverse iterator over the key-value pairs in the map, in descending order. + /// Gets a lazy reverse iterator over the key-value pairs in the map, in descending order. /// /// # Example /// @@ -357,7 +357,7 @@ impl<K: Ord, V> TreeMap<K, V> { RevEntries{iter: self.iter()} } - /// Get a lazy forward iterator over the key-value pairs in the + /// Gets a lazy forward iterator over the key-value pairs in the /// map, with the values being mutable. /// /// # Example @@ -387,7 +387,8 @@ impl<K: Ord, V> TreeMap<K, V> { remaining_max: self.length } } - /// Get a lazy reverse iterator over the key-value pairs in the + + /// Gets a lazy reverse iterator over the key-value pairs in the /// map, with the values being mutable. /// /// # Example @@ -414,8 +415,7 @@ impl<K: Ord, V> TreeMap<K, V> { } - /// Get a lazy iterator that consumes the treemap, it is not usable - /// after calling this. + /// Gets a lazy iterator that consumes the treemap. /// /// # Example /// @@ -444,7 +444,7 @@ impl<K: Ord, V> TreeMap<K, V> { } impl<K, V> TreeMap<K, V> { - /// Return the value for which `f(key)` returns `Equal`. `f` is invoked + /// Returns the value for which `f(key)` returns `Equal`. `f` is invoked /// with current key and guides tree navigation. That means `f` should /// be aware of natural ordering of the tree. /// @@ -473,7 +473,7 @@ impl<K, V> TreeMap<K, V> { tree_find_with(&self.root, f) } - /// Return the value for which `f(key)` returns `Equal`. `f` is invoked + /// Returns the value for which `f(key)` returns `Equal`. `f` is invoked /// with current key and guides tree navigation. That means `f` should /// be aware of natural ordering of the tree. /// @@ -533,7 +533,7 @@ macro_rules! bound_setup { impl<K: Ord, V> TreeMap<K, V> { - /// Get a lazy iterator that should be initialized using + /// Gets a lazy iterator that should be initialized using /// `traverse_left`/`traverse_right`/`traverse_complete`. fn iter_for_traversal<'a>(&'a self) -> Entries<'a, K, V> { Entries { @@ -544,7 +544,7 @@ impl<K: Ord, V> TreeMap<K, V> { } } - /// Return a lazy iterator to the first key-value pair whose key is not less than `k` + /// Returns a lazy iterator to the first key-value pair whose key is not less than `k` /// If all keys in map are less than `k` an empty iterator is returned. /// /// # Example @@ -566,7 +566,7 @@ impl<K: Ord, V> TreeMap<K, V> { bound_setup!(self.iter_for_traversal(), k, true) } - /// Return a lazy iterator to the first key-value pair whose key is greater than `k` + /// Returns a lazy iterator to the first key-value pair whose key is greater than `k` /// If all keys in map are less than or equal to `k` an empty iterator is returned. /// /// # Example @@ -588,7 +588,7 @@ impl<K: Ord, V> TreeMap<K, V> { bound_setup!(self.iter_for_traversal(), k, false) } - /// Get a lazy iterator that should be initialized using + /// Gets a lazy iterator that should be initialized using /// `traverse_left`/`traverse_right`/`traverse_complete`. fn mut_iter_for_traversal<'a>(&'a mut self) -> MutEntries<'a, K, V> { MutEntries { @@ -599,7 +599,7 @@ impl<K: Ord, V> TreeMap<K, V> { } } - /// Return a lazy value iterator to the first key-value pair (with + /// Returns a lazy value iterator to the first key-value pair (with /// the value being mutable) whose key is not less than `k`. /// /// If all keys in map are less than `k` an empty iterator is @@ -633,7 +633,7 @@ impl<K: Ord, V> TreeMap<K, V> { bound_setup!(self.mut_iter_for_traversal(), k, true) } - /// Return a lazy iterator to the first key-value pair (with the + /// Returns a lazy iterator to the first key-value pair (with the /// value being mutable) whose key is greater than `k`. /// /// If all keys in map are less than or equal to `k` an empty iterator @@ -668,7 +668,8 @@ impl<K: Ord, V> TreeMap<K, V> { } } -/// Lazy forward iterator over a map +/// Note: stage0-specific version that lacks bound on A. +#[cfg(stage0)] pub struct Entries<'a, K, V> { stack: Vec<&'a TreeNode<K, V>>, // See the comment on MutEntries; this is just to allow @@ -679,14 +680,62 @@ pub struct Entries<'a, K, V> { remaining_max: uint } -/// Lazy backward iterator over a map +/// Lazy forward iterator over a map +#[cfg(not(stage0))] +pub struct Entries<'a, K:'a, V:'a> { + stack: Vec<&'a TreeNode<K, V>>, + // See the comment on MutEntries; this is just to allow + // code-sharing (for this immutable-values iterator it *could* very + // well be Option<&'a TreeNode<K,V>>). + node: *const TreeNode<K, V>, + remaining_min: uint, + remaining_max: uint +} + +/// Note: stage0-specific version that lacks bound on A. +#[cfg(stage0)] pub struct RevEntries<'a, K, V> { iter: Entries<'a, K, V>, } +/// Lazy backward iterator over a map +#[cfg(not(stage0))] +pub struct RevEntries<'a, K:'a, V:'a> { + iter: Entries<'a, K, V>, +} + +/// Note: stage0-specific version that lacks bound on A. +#[cfg(stage0)] +pub struct MutEntries<'a, K, V> { + stack: Vec<&'a mut TreeNode<K, V>>, + // Unfortunately, we require some unsafe-ness to get around the + // fact that we would be storing a reference *into* one of the + // nodes in the stack. + // + // As far as the compiler knows, this would let us invalidate the + // reference by assigning a new value to this node's position in + // its parent, which would cause this current one to be + // deallocated so this reference would be invalid. (i.e. the + // compilers complaints are 100% correct.) + // + // However, as far as you humans reading this code know (or are + // about to know, if you haven't read far enough down yet), we are + // only reading from the TreeNode.{left,right} fields. the only + // thing that is ever mutated is the .value field (although any + // actual mutation that happens is done externally, by the + // iterator consumer). So, don't be so concerned, rustc, we've got + // it under control. + // + // (This field can legitimately be null.) + node: *mut TreeNode<K, V>, + remaining_min: uint, + remaining_max: uint +} + /// Lazy forward iterator over a map that allows for the mutation of /// the values. -pub struct MutEntries<'a, K, V> { +#[cfg(not(stage0))] +pub struct MutEntries<'a, K:'a, V:'a> { stack: Vec<&'a mut TreeNode<K, V>>, // Unfortunately, we require some unsafe-ness to get around the // fact that we would be storing a reference *into* one of the @@ -712,17 +761,23 @@ pub struct MutEntries<'a, K, V> { remaining_max: uint } -/// Lazy backward iterator over a map +/// Note: stage0-specific version that lacks bound on A. +#[cfg(stage0)] pub struct RevMutEntries<'a, K, V> { iter: MutEntries<'a, K, V>, } +/// Lazy backward iterator over a map +#[cfg(not(stage0))] +pub struct RevMutEntries<'a, K:'a, V:'a> { + iter: MutEntries<'a, K, V>, +} -/// TreeMap keys iterator +/// TreeMap keys iterator. pub type Keys<'a, K, V> = iter::Map<'static, (&'a K, &'a V), &'a K, Entries<'a, K, V>>; -/// TreeMap values iterator +/// TreeMap values iterator. pub type Values<'a, K, V> = iter::Map<'static, (&'a K, &'a V), &'a V, Entries<'a, K, V>>; @@ -821,7 +876,7 @@ macro_rules! define_iterator { // the forward Iterator impl. item!(impl<'a, K, V> Iterator<(&'a K, &'a $($addr_mut)* V)> for $name<'a, K, V> { - /// Advance the iterator to the next node (in order) and return a + /// Advances the iterator to the next node (in order) and return a /// tuple with a reference to the key and value. If there are no /// more nodes, return `None`. fn next(&mut self) -> Option<(&'a K, &'a $($addr_mut)* V)> { @@ -885,8 +940,6 @@ fn mut_deref<K, V>(x: &mut Option<Box<TreeNode<K, V>>>) } } - - /// Lazy forward iterator over a map that consumes the map while iterating pub struct MoveEntries<K, V> { stack: Vec<TreeNode<K, V>>, @@ -951,7 +1004,7 @@ impl<'a, T> Iterator<&'a T> for RevSetItems<'a, T> { } } -/// A implementation of the `Set` trait on top of the `TreeMap` container. The +/// An implementation of the `Set` trait on top of the `TreeMap` container. The /// only requirement is that the type of the elements contained ascribes to the /// `Ord` trait. /// @@ -1121,7 +1174,7 @@ impl<T: Ord> Default for TreeSet<T> { } impl<T: Ord> TreeSet<T> { - /// Create an empty `TreeSet`. + /// Creates an empty `TreeSet`. /// /// # Example /// @@ -1132,7 +1185,7 @@ impl<T: Ord> TreeSet<T> { #[inline] pub fn new() -> TreeSet<T> { TreeSet{map: TreeMap::new()} } - /// Get a lazy iterator over the values in the set, in ascending order. + /// Gets a lazy iterator over the values in the set, in ascending order. /// /// # Example /// @@ -1150,7 +1203,7 @@ impl<T: Ord> TreeSet<T> { SetItems{iter: self.map.iter()} } - /// Get a lazy iterator over the values in the set, in descending order. + /// Gets a lazy iterator over the values in the set, in descending order. /// /// # Example /// @@ -1186,7 +1239,7 @@ impl<T: Ord> TreeSet<T> { self.map.move_iter().map(|(value, _)| value) } - /// Get a lazy iterator pointing to the first value not less than `v` (greater or equal). + /// Gets a lazy iterator pointing to the first value not less than `v` (greater or equal). /// If all elements in the set are less than `v` empty iterator is returned. /// /// # Example @@ -1204,7 +1257,7 @@ impl<T: Ord> TreeSet<T> { SetItems{iter: self.map.lower_bound(v)} } - /// Get a lazy iterator pointing to the first value greater than `v`. + /// Gets a lazy iterator pointing to the first value greater than `v`. /// If all elements in the set are less than or equal to `v` an /// empty iterator is returned. /// @@ -1223,7 +1276,7 @@ impl<T: Ord> TreeSet<T> { SetItems{iter: self.map.upper_bound(v)} } - /// Visit the values representing the difference, in ascending order. + /// Visits the values representing the difference, in ascending order. /// /// # Example /// @@ -1250,7 +1303,7 @@ impl<T: Ord> TreeSet<T> { DifferenceItems{a: self.iter().peekable(), b: other.iter().peekable()} } - /// Visit the values representing the symmetric difference, in ascending order. + /// Visits the values representing the symmetric difference, in ascending order. /// /// # Example /// @@ -1276,7 +1329,7 @@ impl<T: Ord> TreeSet<T> { SymDifferenceItems{a: self.iter().peekable(), b: other.iter().peekable()} } - /// Visit the values representing the intersection, in ascending order. + /// Visits the values representing the intersection, in ascending order. /// /// # Example /// @@ -1299,7 +1352,7 @@ impl<T: Ord> TreeSet<T> { IntersectionItems{a: self.iter().peekable(), b: other.iter().peekable()} } - /// Visit the values representing the union, in ascending order. + /// Visits the values representing the union, in ascending order. /// /// # Example /// @@ -1322,43 +1375,89 @@ impl<T: Ord> TreeSet<T> { } } -/// Lazy forward iterator over a set +/// Note: stage0-specific version that lacks bound on A. +#[cfg(stage0)] pub struct SetItems<'a, T> { iter: Entries<'a, T, ()> } -/// Lazy backward iterator over a set +/// A lazy forward iterator over a set. +#[cfg(not(stage0))] +pub struct SetItems<'a, T:'a> { + iter: Entries<'a, T, ()> +} + +/// Note: stage0-specific version that lacks bound on A. +#[cfg(stage0)] pub struct RevSetItems<'a, T> { iter: RevEntries<'a, T, ()> } -/// Lazy forward iterator over a set that consumes the set while iterating +/// A lazy backward iterator over a set. +#[cfg(not(stage0))] +pub struct RevSetItems<'a, T:'a> { + iter: RevEntries<'a, T, ()> +} + +/// A lazy forward iterator over a set that consumes the set while iterating. pub type MoveSetItems<T> = iter::Map<'static, (T, ()), T, MoveEntries<T, ()>>; -/// Lazy iterator producing elements in the set difference (in-order) +/// Note: stage0-specific version that lacks bound on A. +#[cfg(stage0)] pub struct DifferenceItems<'a, T> { a: Peekable<&'a T, SetItems<'a, T>>, b: Peekable<&'a T, SetItems<'a, T>>, } -/// Lazy iterator producing elements in the set symmetric difference (in-order) +/// A lazy iterator producing elements in the set difference (in-order). +#[cfg(not(stage0))] +pub struct DifferenceItems<'a, T:'a> { + a: Peekable<&'a T, SetItems<'a, T>>, + b: Peekable<&'a T, SetItems<'a, T>>, +} + +/// Note: stage0-specific version that lacks bound on A. +#[cfg(stage0)] pub struct SymDifferenceItems<'a, T> { a: Peekable<&'a T, SetItems<'a, T>>, b: Peekable<&'a T, SetItems<'a, T>>, } -/// Lazy iterator producing elements in the set intersection (in-order) +/// A lazy iterator producing elements in the set symmetric difference (in-order). +#[cfg(not(stage0))] +pub struct SymDifferenceItems<'a, T:'a> { + a: Peekable<&'a T, SetItems<'a, T>>, + b: Peekable<&'a T, SetItems<'a, T>>, +} + +/// Note: stage0-specific version that lacks bound on A. +#[cfg(stage0)] pub struct IntersectionItems<'a, T> { a: Peekable<&'a T, SetItems<'a, T>>, b: Peekable<&'a T, SetItems<'a, T>>, } -/// Lazy iterator producing elements in the set union (in-order) +/// A lazy iterator producing elements in the set intersection (in-order). +#[cfg(not(stage0))] +pub struct IntersectionItems<'a, T:'a> { + a: Peekable<&'a T, SetItems<'a, T>>, + b: Peekable<&'a T, SetItems<'a, T>>, +} + +/// Note: stage0-specific version that lacks bound on A. +#[cfg(stage0)] pub struct UnionItems<'a, T> { a: Peekable<&'a T, SetItems<'a, T>>, b: Peekable<&'a T, SetItems<'a, T>>, } +/// A lazy iterator producing elements in the set union (in-order). +#[cfg(not(stage0))] +pub struct UnionItems<'a, T:'a> { + a: Peekable<&'a T, SetItems<'a, T>>, + b: Peekable<&'a T, SetItems<'a, T>>, +} + /// Compare `x` and `y`, but return `short` if x is None and `long` if y is None fn cmp_opt<T: Ord>(x: Option<&T>, y: Option<&T>, short: Ordering, long: Ordering) -> Ordering { @@ -1852,7 +1951,8 @@ mod test_treemap { check_equal(ctrl.as_slice(), &map); assert!(map.find(&5).is_none()); - let mut rng: rand::IsaacRng = rand::SeedableRng::from_seed(&[42]); + let seed: &[_] = &[42]; + let mut rng: rand::IsaacRng = rand::SeedableRng::from_seed(seed); for _ in range(0u, 3) { for _ in range(0u, 90) { diff --git a/src/libcollections/trie.rs b/src/libcollections/trie.rs index 911262e90cb..e79ec67cba0 100644 --- a/src/libcollections/trie.rs +++ b/src/libcollections/trie.rs @@ -121,13 +121,13 @@ impl<T: Show> Show for TrieMap<T> { } impl<T> Collection for TrieMap<T> { - /// Return the number of elements in the map. + /// Returns the number of elements in the map. #[inline] fn len(&self) -> uint { self.length } } impl<T> Mutable for TrieMap<T> { - /// Clear the map, removing all values. + /// Clears the map, removing all values. #[inline] fn clear(&mut self) { self.root = TrieNode::new(); @@ -136,7 +136,7 @@ impl<T> Mutable for TrieMap<T> { } impl<T> Map<uint, T> for TrieMap<T> { - /// Return a reference to the value corresponding to the key. + /// Returns a reference to the value corresponding to the key. #[inline] fn find<'a>(&'a self, key: &uint) -> Option<&'a T> { let mut node: &'a TrieNode<T> = &self.root; @@ -159,14 +159,14 @@ impl<T> Map<uint, T> for TrieMap<T> { } impl<T> MutableMap<uint, T> for TrieMap<T> { - /// Return a mutable reference to the value corresponding to the key. + /// Returns a mutable reference to the value corresponding to the key. #[inline] fn find_mut<'a>(&'a mut self, key: &uint) -> Option<&'a mut T> { find_mut(&mut self.root.children[chunk(*key, 0)], *key, 1) } - /// Insert a key-value pair from the map. If the key already had a value - /// present in the map, that value is returned. Otherwise None is returned. + /// Inserts a key-value pair from the map. If the key already had a value + /// present in the map, that value is returned. Otherwise, `None` is returned. fn swap(&mut self, key: uint, value: T) -> Option<T> { let ret = insert(&mut self.root.count, &mut self.root.children[chunk(key, 0)], @@ -192,7 +192,7 @@ impl<T> Default for TrieMap<T> { } impl<T> TrieMap<T> { - /// Create an empty TrieMap. + /// Creates an empty `TrieMap`. /// /// # Example /// @@ -205,8 +205,8 @@ impl<T> TrieMap<T> { TrieMap{root: TrieNode::new(), length: 0} } - /// Visit all key-value pairs in reverse order. Abort traversal when f returns false. - /// Return true if f returns true for all elements. + /// Visits all key-value pairs in reverse order. Aborts traversal when `f` returns `false`. + /// Returns `true` if `f` returns `true` for all elements. /// /// # Example /// @@ -228,19 +228,19 @@ impl<T> TrieMap<T> { self.root.each_reverse(f) } - /// Get an iterator visiting all keys in ascending order by the keys. - /// Iterator element type is `uint`. + /// Gets an iterator visiting all keys in ascending order by the keys. + /// The iterator's element type is `uint`. pub fn keys<'r>(&'r self) -> Keys<'r, T> { self.iter().map(|(k, _v)| k) } - /// Get an iterator visiting all values in ascending order by the keys. - /// Iterator element type is `&'r T`. + /// Gets an iterator visiting all values in ascending order by the keys. + /// The iterator's element type is `&'r T`. pub fn values<'r>(&'r self) -> Values<'r, T> { self.iter().map(|(_k, v)| v) } - /// Get an iterator over the key-value pairs in the map, ordered by keys. + /// Gets an iterator over the key-value pairs in the map, ordered by keys. /// /// # Example /// @@ -262,7 +262,7 @@ impl<T> TrieMap<T> { iter } - /// Get an iterator over the key-value pairs in the map, with the + /// Gets an iterator over the key-value pairs in the map, with the /// ability to mutate the values. /// /// # Example @@ -385,7 +385,7 @@ impl<T> TrieMap<T> { mutability = ) } - /// Get an iterator pointing to the first key-value pair whose key is not less than `key`. + /// Gets an iterator pointing to the first key-value pair whose key is not less than `key`. /// If all keys in the map are less than `key` an empty iterator is returned. /// /// # Example @@ -402,7 +402,7 @@ impl<T> TrieMap<T> { self.bound(key, false) } - /// Get an iterator pointing to the first key-value pair whose key is greater than `key`. + /// Gets an iterator pointing to the first key-value pair whose key is greater than `key`. /// If all keys in the map are not greater than `key` an empty iterator is returned. /// /// # Example @@ -427,7 +427,7 @@ impl<T> TrieMap<T> { mutability = mut) } - /// Get an iterator pointing to the first key-value pair whose key is not less than `key`. + /// Gets an iterator pointing to the first key-value pair whose key is not less than `key`. /// If all keys in the map are less than `key` an empty iterator is returned. /// /// # Example @@ -452,7 +452,7 @@ impl<T> TrieMap<T> { self.mut_bound(key, false) } - /// Get an iterator pointing to the first key-value pair whose key is greater than `key`. + /// Gets an iterator pointing to the first key-value pair whose key is greater than `key`. /// If all keys in the map are not greater than `key` an empty iterator is returned. /// /// # Example @@ -565,13 +565,13 @@ impl Show for TrieSet { } impl Collection for TrieSet { - /// Return the number of elements in the set. + /// Returns the number of elements in the set. #[inline] fn len(&self) -> uint { self.map.len() } } impl Mutable for TrieSet { - /// Clear the set, removing all values. + /// Clears the set, removing all values. #[inline] fn clear(&mut self) { self.map.clear() } } @@ -616,7 +616,7 @@ impl Default for TrieSet { } impl TrieSet { - /// Create an empty TrieSet. + /// Creates an empty TrieSet. /// /// # Example /// @@ -629,8 +629,8 @@ impl TrieSet { TrieSet{map: TrieMap::new()} } - /// Visit all values in reverse order. Abort traversal when `f` returns false. - /// Return `true` if `f` returns `true` for all elements. + /// Visits all values in reverse order. Aborts traversal when `f` returns `false`. + /// Returns `true` if `f` returns `true` for all elements. /// /// # Example /// @@ -653,7 +653,7 @@ impl TrieSet { self.map.each_reverse(|k, _| f(k)) } - /// Get an iterator over the values in the set, in sorted order. + /// Gets an iterator over the values in the set, in sorted order. /// /// # Example /// @@ -676,7 +676,7 @@ impl TrieSet { SetItems{iter: self.map.iter()} } - /// Get an iterator pointing to the first value that is not less than `val`. + /// Gets an iterator pointing to the first value that is not less than `val`. /// If all values in the set are less than `val` an empty iterator is returned. /// /// # Example @@ -693,7 +693,7 @@ impl TrieSet { SetItems{iter: self.map.lower_bound(val)} } - /// Get an iterator pointing to the first value that key is greater than `val`. + /// Gets an iterator pointing to the first value that key is greater than `val`. /// If all values in the set are less than or equal to `val` an empty iterator is returned. /// /// # Example @@ -857,7 +857,8 @@ fn remove<T>(count: &mut uint, child: &mut Child<T>, key: uint, return ret; } -/// Forward iterator over a map. +/// Note: stage0-specific version that lacks bound on A. +#[cfg(stage0)] pub struct Entries<'a, T> { stack: [slice::Items<'a, Child<T>>, .. NUM_CHUNKS], length: uint, @@ -865,8 +866,17 @@ pub struct Entries<'a, T> { remaining_max: uint } -/// Forward iterator over the key-value pairs of a map, with the -/// values being mutable. +/// A forward iterator over a map. +#[cfg(not(stage0))] +pub struct Entries<'a, T:'a> { + stack: [slice::Items<'a, Child<T>>, .. NUM_CHUNKS], + length: uint, + remaining_min: uint, + remaining_max: uint +} + +/// Note: stage0-specific version that lacks bound on A. +#[cfg(stage0)] pub struct MutEntries<'a, T> { stack: [slice::MutItems<'a, Child<T>>, .. NUM_CHUNKS], length: uint, @@ -874,11 +884,21 @@ pub struct MutEntries<'a, T> { remaining_max: uint } -/// Forward iterator over the keys of a map +/// A forward iterator over the key-value pairs of a map, with the +/// values being mutable. +#[cfg(not(stage0))] +pub struct MutEntries<'a, T:'a> { + stack: [slice::MutItems<'a, Child<T>>, .. NUM_CHUNKS], + length: uint, + remaining_min: uint, + remaining_max: uint +} + +/// A forward iterator over the keys of a map. pub type Keys<'a, T> = iter::Map<'static, (uint, &'a T), uint, Entries<'a, T>>; -/// Forward iterator over the values of a map +/// A forward iterator over the values of a map. pub type Values<'a, T> = iter::Map<'static, (uint, &'a T), &'a T, Entries<'a, T>>; @@ -999,7 +1019,7 @@ macro_rules! iterator_impl { iterator_impl! { Entries, iter = iter, mutability = } iterator_impl! { MutEntries, iter = mut_iter, mutability = mut } -/// Forward iterator over a set. +/// A forward iterator over a set. pub struct SetItems<'a> { iter: Entries<'a, ()> } diff --git a/src/libcollections/vec.rs b/src/libcollections/vec.rs index 6e4f2bc5481..f383677ed14 100644 --- a/src/libcollections/vec.rs +++ b/src/libcollections/vec.rs @@ -190,7 +190,7 @@ impl<T> Vec<T> { } } - /// Create a `Vec<T>` directly from the raw constituents. + /// Creates a `Vec<T>` directly from the raw constituents. /// /// This is highly unsafe: /// @@ -399,7 +399,7 @@ impl<T: Clone> Vec<T> { /// Partitions a vector based on a predicate. /// /// Clones the elements of the vector, partitioning them into two `Vec`s - /// `(A,B)`, where all elements of `A` satisfy `f` and all elements of `B` + /// `(a, b)`, where all elements of `a` satisfy `f` and all elements of `b` /// do not. The order of elements is preserved. /// /// # Example @@ -635,7 +635,7 @@ impl<T> Vec<T> { } } - /// Shrink the capacity of the vector as much as possible + /// Shrinks the capacity of the vector as much as possible. /// /// # Example /// @@ -706,7 +706,7 @@ impl<T> Vec<T> { } } - /// Work with `self` as a mutable slice. + /// Returns a mutable slice of the elements of `self`. /// /// # Example /// @@ -841,7 +841,7 @@ impl<T> Vec<T> { self.as_mut_slice().mut_iter() } - /// Sort the vector, in place, using `compare` to compare elements. + /// Sorts the vector, in place, using `compare` to compare elements. /// /// This sort is `O(n log n)` worst-case and stable, but allocates /// approximately `2 * n`, where `n` is the length of `self`. @@ -944,7 +944,7 @@ impl<T> Vec<T> { self.as_mut_slice().mut_last() } - /// Remove an element from anywhere in the vector and return it, replacing + /// Removes an element from anywhere in the vector and return it, replacing /// it with the last element. This does not preserve ordering, but is O(1). /// /// Returns `None` if `index` is out of bounds. @@ -973,7 +973,7 @@ impl<T> Vec<T> { self.pop() } - /// Prepend an element to the vector. + /// Prepends an element to the vector. /// /// # Warning /// @@ -1014,8 +1014,8 @@ impl<T> Vec<T> { self.remove(0) } - /// Insert an element at position `index` within the vector, shifting all - /// elements after position i one position to the right. + /// Inserts an element at position `index` within the vector, shifting all + /// elements after position `i` one position to the right. /// /// # Failure /// @@ -1052,7 +1052,7 @@ impl<T> Vec<T> { } } - /// Remove and return the element at position `index` within the vector, + /// Removes and returns the element at position `index` within the vector, /// shifting all elements after position `index` one position to the left. /// Returns `None` if `i` is out of bounds. /// @@ -1126,7 +1126,7 @@ impl<T> Vec<T> { self.as_mut_slice().mut_slice(start, end) } - /// Returns a mutable slice of self from `start` to the end of the vec. + /// Returns a mutable slice of `self` from `start` to the end of the `Vec`. /// /// # Failure /// @@ -1143,7 +1143,7 @@ impl<T> Vec<T> { self.as_mut_slice().mut_slice_from(start) } - /// Returns a mutable slice of self from the start of the vec to `end`. + /// Returns a mutable slice of `self` from the start of the `Vec` to `end`. /// /// # Failure /// @@ -1160,7 +1160,7 @@ impl<T> Vec<T> { self.as_mut_slice().mut_slice_to(end) } - /// Returns a pair of mutable slices that divides the vec at an index. + /// Returns a pair of mutable slices that divides the `Vec` at an index. /// /// The first will contain all indices from `[0, mid)` (excluding /// the index `mid` itself) and the second will contain all @@ -1199,7 +1199,7 @@ impl<T> Vec<T> { self.as_mut_slice().mut_split_at(mid) } - /// Reverse the order of elements in a vector, in place. + /// Reverses the order of elements in a vector, in place. /// /// # Example /// @@ -1392,8 +1392,8 @@ impl<T> Mutable for Vec<T> { } } -impl<T:PartialEq> Vec<T> { - /// Return true if a vector contains an element with the given value +impl<T: PartialEq> Vec<T> { + /// Returns true if a vector contains an element equal to the given value. /// /// # Example /// @@ -1406,7 +1406,7 @@ impl<T:PartialEq> Vec<T> { self.as_slice().contains(x) } - /// Remove consecutive repeated elements in the vector. + /// Removes consecutive repeated elements in the vector. /// /// If the vector is sorted, this removes all duplicates. /// @@ -1503,7 +1503,7 @@ impl<T:PartialEq> Vec<T> { } impl<T> Slice<T> for Vec<T> { - /// Work with `self` as a slice. + /// Returns a slice into `self`. /// /// # Example /// @@ -1558,7 +1558,7 @@ impl<T:fmt::Show> fmt::Show for Vec<T> { } impl<T> MutableSeq<T> for Vec<T> { - /// Append an element to the back of a collection. + /// Appends an element to the back of a collection. /// /// # Failure /// @@ -1620,9 +1620,13 @@ pub struct MoveItems<T> { impl<T> Iterator<T> for MoveItems<T> { #[inline] - fn next(&mut self) -> Option<T> { + fn next<'a>(&'a mut self) -> Option<T> { unsafe { - self.iter.next().map(|x| ptr::read(x)) + // Unsafely transmute from Items<'static, T> to Items<'a, + // T> because otherwise the type checker requires that T + // be bounded by 'static. + let iter: &mut Items<'a, T> = mem::transmute(&mut self.iter); + iter.next().map(|x| ptr::read(x)) } } @@ -1634,9 +1638,13 @@ impl<T> Iterator<T> for MoveItems<T> { impl<T> DoubleEndedIterator<T> for MoveItems<T> { #[inline] - fn next_back(&mut self) -> Option<T> { + fn next_back<'a>(&'a mut self) -> Option<T> { unsafe { - self.iter.next_back().map(|x| ptr::read(x)) + // Unsafely transmute from Items<'static, T> to Items<'a, + // T> because otherwise the type checker requires that T + // be bounded by 'static. + let iter: &mut Items<'a, T> = mem::transmute(&mut self.iter); + iter.next_back().map(|x| ptr::read(x)) } } } @@ -1654,14 +1662,12 @@ impl<T> Drop for MoveItems<T> { } } -/** - * Convert an iterator of pairs into a pair of vectors. - * - * Returns a tuple containing two vectors where the i-th element of the first - * vector contains the first element of the i-th tuple of the input iterator, - * and the i-th element of the second vector contains the second element - * of the i-th tuple of the input iterator. - */ +/// Converts an iterator of pairs into a pair of vectors. +/// +/// Returns a tuple containing two vectors where the i-th element of the first +/// vector contains the first element of the i-th tuple of the input iterator, +/// and the i-th element of the second vector contains the second element +/// of the i-th tuple of the input iterator. pub fn unzip<T, U, V: Iterator<(T, U)>>(mut iter: V) -> (Vec<T>, Vec<U>) { let (lo, _) = iter.size_hint(); let mut ts = Vec::with_capacity(lo); @@ -1673,7 +1679,7 @@ pub fn unzip<T, U, V: Iterator<(T, U)>>(mut iter: V) -> (Vec<T>, Vec<U>) { (ts, us) } -/// Unsafe operations +/// Unsafe vector operations. pub mod raw { use super::Vec; use core::ptr; diff --git a/src/libcore/any.rs b/src/libcore/any.rs index 1809988847b..625b89b3bae 100644 --- a/src/libcore/any.rs +++ b/src/libcore/any.rs @@ -132,7 +132,7 @@ pub trait AnyRefExt<'a> { } #[stable] -impl<'a> AnyRefExt<'a> for &'a Any { +impl<'a> AnyRefExt<'a> for &'a Any+'a { #[inline] #[stable] fn is<T: 'static>(self) -> bool { @@ -181,7 +181,7 @@ pub trait AnyMutRefExt<'a> { } #[stable] -impl<'a> AnyMutRefExt<'a> for &'a mut Any { +impl<'a> AnyMutRefExt<'a> for &'a mut Any+'a { #[inline] #[unstable = "naming conventions around acquiring references may change"] fn downcast_mut<T: 'static>(self) -> Option<&'a mut T> { diff --git a/src/libcore/cell.rs b/src/libcore/cell.rs index 2a7b1630edf..4cbe7d6d963 100644 --- a/src/libcore/cell.rs +++ b/src/libcore/cell.rs @@ -324,6 +324,16 @@ impl<T: PartialEq> PartialEq for RefCell<T> { /// Wraps a borrowed reference to a value in a `RefCell` box. #[unstable] +#[cfg(not(stage0))] +pub struct Ref<'b, T:'b> { + // FIXME #12808: strange name to try to avoid interfering with + // field accesses of the contained type via Deref + _parent: &'b RefCell<T> +} + +/// Dox. +#[unstable] +#[cfg(stage0)] pub struct Ref<'b, T> { // FIXME #12808: strange name to try to avoid interfering with // field accesses of the contained type via Deref @@ -369,6 +379,16 @@ pub fn clone_ref<'b, T>(orig: &Ref<'b, T>) -> Ref<'b, T> { /// Wraps a mutable borrowed reference to a value in a `RefCell` box. #[unstable] +#[cfg(not(stage0))] +pub struct RefMut<'b, T:'b> { + // FIXME #12808: strange name to try to avoid interfering with + // field accesses of the contained type via Deref + _parent: &'b RefCell<T> +} + +/// Dox. +#[unstable] +#[cfg(stage0)] pub struct RefMut<'b, T> { // FIXME #12808: strange name to try to avoid interfering with // field accesses of the contained type via Deref diff --git a/src/libcore/cmp.rs b/src/libcore/cmp.rs index bfd10c835e2..b90c6daf9eb 100644 --- a/src/libcore/cmp.rs +++ b/src/libcore/cmp.rs @@ -112,12 +112,13 @@ impl Ordering { /// assert_eq!(Greater.reverse(), Less); /// /// - /// let mut data = &mut [2u, 10, 5, 8]; + /// let mut data: &mut [_] = &mut [2u, 10, 5, 8]; /// /// // sort the array from largest to smallest. /// data.sort_by(|a, b| a.cmp(b).reverse()); /// - /// assert_eq!(data, &mut [10u, 8, 5, 2]); + /// let b: &mut [_] = &mut [10u, 8, 5, 2]; + /// assert!(data == b); /// ``` #[inline] #[experimental] diff --git a/src/libcore/finally.rs b/src/libcore/finally.rs index 514b3f90df7..c36150eb964 100644 --- a/src/libcore/finally.rs +++ b/src/libcore/finally.rs @@ -102,6 +102,13 @@ pub fn try_finally<T,U,R>(mutate: &mut T, try_fn(&mut *f.mutate, drop) } +#[cfg(not(stage0))] +struct Finallyalizer<'a,A:'a> { + mutate: &'a mut A, + dtor: |&mut A|: 'a +} + +#[cfg(stage0)] struct Finallyalizer<'a,A> { mutate: &'a mut A, dtor: |&mut A|: 'a diff --git a/src/libcore/fmt/mod.rs b/src/libcore/fmt/mod.rs index 85a289f1a30..f7ff92f5ce3 100644 --- a/src/libcore/fmt/mod.rs +++ b/src/libcore/fmt/mod.rs @@ -92,7 +92,7 @@ pub struct Formatter<'a> { /// Optionally specified precision for numeric types pub precision: Option<uint>, - buf: &'a mut FormatWriter, + buf: &'a mut FormatWriter+'a, curarg: slice::Items<'a, Argument<'a>>, args: &'a [Argument<'a>], } @@ -524,7 +524,7 @@ impl<'a, T: Show> Show for &'a T { impl<'a, T: Show> Show for &'a mut T { fn fmt(&self, f: &mut Formatter) -> Result { secret_show(&**self, f) } } -impl<'a> Show for &'a Show { +impl<'a> Show for &'a Show+'a { fn fmt(&self, f: &mut Formatter) -> Result { (*self).fmt(f) } } @@ -692,7 +692,7 @@ macro_rules! tuple ( tuple! { T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, } -impl<'a> Show for &'a any::Any { +impl<'a> Show for &'a any::Any+'a { fn fmt(&self, f: &mut Formatter) -> Result { f.pad("&Any") } } diff --git a/src/libcore/intrinsics.rs b/src/libcore/intrinsics.rs index 84827c63572..4ecc1b8f45f 100644 --- a/src/libcore/intrinsics.rs +++ b/src/libcore/intrinsics.rs @@ -93,6 +93,8 @@ pub trait TyVisitor { fn visit_char(&mut self) -> bool; fn visit_estr_slice(&mut self) -> bool; + // NOTE: remove after snapshot + #[cfg(stage0)] fn visit_estr_fixed(&mut self, n: uint, sz: uint, align: uint) -> bool; fn visit_box(&mut self, mtbl: uint, inner: *const TyDesc) -> bool; @@ -101,8 +103,13 @@ pub trait TyVisitor { fn visit_rptr(&mut self, mtbl: uint, inner: *const TyDesc) -> bool; fn visit_evec_slice(&mut self, mtbl: uint, inner: *const TyDesc) -> bool; + // NOTE: remove after snapshot + #[cfg(stage0)] fn visit_evec_fixed(&mut self, n: uint, sz: uint, align: uint, mtbl: uint, inner: *const TyDesc) -> bool; + #[cfg(not(stage0))] + fn visit_evec_fixed(&mut self, n: uint, sz: uint, align: uint, + inner: *const TyDesc) -> bool; fn visit_enter_rec(&mut self, n_fields: uint, sz: uint, align: uint) -> bool; diff --git a/src/libcore/iter.rs b/src/libcore/iter.rs index b8ed8bdf923..7df8a7864d9 100644 --- a/src/libcore/iter.rs +++ b/src/libcore/iter.rs @@ -673,7 +673,7 @@ pub trait MutableDoubleEndedIterator { fn reverse_(&mut self); } -impl<'a, A, T: DoubleEndedIterator<&'a mut A>> MutableDoubleEndedIterator for T { +impl<'a, A:'a, T: DoubleEndedIterator<&'a mut A>> MutableDoubleEndedIterator for T { // FIXME: #5898: should be called `reverse` /// Use an iterator to reverse a container in-place fn reverse_(&mut self) { @@ -728,6 +728,10 @@ pub trait ExactSize<A> : DoubleEndedIterator<A> { /// Return the exact length of the iterator. fn len(&self) -> uint { let (lower, upper) = self.size_hint(); + // Note: This assertion is overly defensive, but it checks the invariant + // guaranteed by the trait. If this trait were rust-internal, + // we could use debug_assert!; assert_eq! will check all Rust user + // implementations too. assert_eq!(upper, Some(lower)); lower } @@ -773,18 +777,26 @@ impl<A, T: DoubleEndedIterator<A> + RandomAccessIterator<A>> RandomAccessIterato /// A mutable reference to an iterator #[must_use = "iterator adaptors are lazy and do nothing unless consumed"] +#[cfg(not(stage0))] +pub struct ByRef<'a, T:'a> { + iter: &'a mut T +} + +/// Dox +#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] +#[cfg(stage0)] pub struct ByRef<'a, T> { iter: &'a mut T } -impl<'a, A, T: Iterator<A>> Iterator<A> for ByRef<'a, T> { +impl<'a, A, T: Iterator<A>+'a> Iterator<A> for ByRef<'a, T> { #[inline] fn next(&mut self) -> Option<A> { self.iter.next() } #[inline] fn size_hint(&self) -> (uint, Option<uint>) { self.iter.size_hint() } } -impl<'a, A, T: DoubleEndedIterator<A>> DoubleEndedIterator<A> for ByRef<'a, T> { +impl<'a, A, T: DoubleEndedIterator<A>+'a> DoubleEndedIterator<A> for ByRef<'a, T> { #[inline] fn next_back(&mut self) -> Option<A> { self.iter.next_back() } } @@ -1195,21 +1207,20 @@ impl<A, B, T: ExactSize<A>, U: ExactSize<B>> DoubleEndedIterator<(A, B)> for Zip<T, U> { #[inline] fn next_back(&mut self) -> Option<(A, B)> { - let (a_sz, a_upper) = self.a.size_hint(); - let (b_sz, b_upper) = self.b.size_hint(); - assert!(a_upper == Some(a_sz)); - assert!(b_upper == Some(b_sz)); - if a_sz < b_sz { - for _ in range(0, b_sz - a_sz) { self.b.next_back(); } - } else if a_sz > b_sz { - for _ in range(0, a_sz - b_sz) { self.a.next_back(); } - } - let (a_sz, _) = self.a.size_hint(); - let (b_sz, _) = self.b.size_hint(); - assert!(a_sz == b_sz); + let a_sz = self.a.len(); + let b_sz = self.b.len(); + if a_sz != b_sz { + // Adjust a, b to equal length + if a_sz > b_sz { + for _ in range(0, a_sz - b_sz) { self.a.next_back(); } + } else { + for _ in range(0, b_sz - a_sz) { self.b.next_back(); } + } + } match (self.a.next_back(), self.b.next_back()) { (Some(x), Some(y)) => Some((x, y)), - _ => None + (None, None) => None, + _ => unreachable!(), } } } @@ -1395,9 +1406,8 @@ impl<A, T: ExactSize<A>> DoubleEndedIterator<(uint, A)> for Enumerate<T> { fn next_back(&mut self) -> Option<(uint, A)> { match self.iter.next_back() { Some(a) => { - let (lower, upper) = self.iter.size_hint(); - assert!(upper == Some(lower)); - Some((self.count + lower, a)) + let len = self.iter.len(); + Some((self.count + len, a)) } _ => None } diff --git a/src/libcore/lib.rs b/src/libcore/lib.rs index 7e2ea492d4c..050e2348111 100644 --- a/src/libcore/lib.rs +++ b/src/libcore/lib.rs @@ -58,7 +58,7 @@ #![no_std] #![feature(globs, intrinsics, lang_items, macro_rules, managed_boxes, phase)] -#![feature(simd, unsafe_destructor)] +#![feature(simd, unsafe_destructor, issue_5723_bootstrap)] #![deny(missing_doc)] mod macros; diff --git a/src/libcore/mem.rs b/src/libcore/mem.rs index 06e28816c1c..947fa2ec92e 100644 --- a/src/libcore/mem.rs +++ b/src/libcore/mem.rs @@ -19,6 +19,16 @@ use ptr; pub use intrinsics::transmute; +/// Moves a thing into the void. +/// +/// The forget function will take ownership of the provided value but neglect +/// to run any required cleanup or memory management operations on it. +/// +/// This function is the unsafe version of the `drop` function because it does +/// not run any destructors. +#[stable] +pub use intrinsics::forget; + /// Returns the size of a type in bytes. #[inline] #[stable] @@ -164,7 +174,7 @@ pub unsafe fn overwrite<T>(dst: *mut T, src: T) { /// Deprecated, use `overwrite` instead #[inline] -#[deprecated = "use ptr::write"] +#[deprecated = "this function has been renamed to overwrite()"] pub unsafe fn move_val_init<T>(dst: &mut T, src: T) { ptr::write(dst, src) } @@ -337,17 +347,6 @@ pub fn replace<T>(dest: &mut T, mut src: T) -> T { #[stable] pub fn drop<T>(_x: T) { } -/// Moves a thing into the void. -/// -/// The forget function will take ownership of the provided value but neglect -/// to run any required cleanup or memory management operations on it. -/// -/// This function is the unsafe version of the `drop` function because it does -/// not run any destructors. -#[inline] -#[stable] -pub unsafe fn forget<T>(thing: T) { intrinsics::forget(thing) } - /// Interprets `src` as `&U`, and then reads `src` without moving the contained /// value. /// @@ -370,7 +369,7 @@ pub unsafe fn transmute_copy<T, U>(src: &T) -> U { #[inline] #[unstable = "this function may be removed in the future due to its \ questionable utility"] -pub unsafe fn copy_lifetime<'a, S, T>(_ptr: &'a S, ptr: &T) -> &'a T { +pub unsafe fn copy_lifetime<'a, S, T:'a>(_ptr: &'a S, ptr: &T) -> &'a T { transmute(ptr) } @@ -378,7 +377,7 @@ pub unsafe fn copy_lifetime<'a, S, T>(_ptr: &'a S, ptr: &T) -> &'a T { #[inline] #[unstable = "this function may be removed in the future due to its \ questionable utility"] -pub unsafe fn copy_mut_lifetime<'a, S, T>(_ptr: &'a mut S, +pub unsafe fn copy_mut_lifetime<'a, S, T:'a>(_ptr: &'a mut S, ptr: &mut T) -> &'a mut T { transmute(ptr) } diff --git a/src/libcore/option.rs b/src/libcore/option.rs index bf8a92a4f95..7773e03416e 100644 --- a/src/libcore/option.rs +++ b/src/libcore/option.rs @@ -221,8 +221,14 @@ impl<T> Option<T> { #[inline] pub fn as_mut_slice<'r>(&'r mut self) -> &'r mut [T] { match *self { - Some(ref mut x) => slice::mut_ref_slice(x), - None => &mut [] + Some(ref mut x) => { + let result: &mut [T] = slice::mut_ref_slice(x); + result + } + None => { + let result: &mut [T] = &mut []; + result + } } } @@ -524,7 +530,10 @@ impl<T> Slice<T> for Option<T> { fn as_slice<'a>(&'a self) -> &'a [T] { match *self { Some(ref x) => slice::ref_slice(x), - None => &[] + None => { + let result: &[_] = &[]; + result + } } } } diff --git a/src/libcore/raw.rs b/src/libcore/raw.rs index da9fab0fc6f..5daa693c774 100644 --- a/src/libcore/raw.rs +++ b/src/libcore/raw.rs @@ -51,10 +51,16 @@ pub struct Procedure { /// /// This struct does not have a `Repr` implementation /// because there is no way to refer to all trait objects generically. +#[cfg(stage0)] pub struct TraitObject { pub vtable: *mut (), pub data: *mut (), } +#[cfg(not(stage0))] +pub struct TraitObject { + pub data: *mut (), + pub vtable: *mut (), +} /// This trait is meant to map equivalences between raw structs and their /// corresponding rust values. diff --git a/src/libcore/simd.rs b/src/libcore/simd.rs index 54e7d077bb1..42418ccbc1a 100644 --- a/src/libcore/simd.rs +++ b/src/libcore/simd.rs @@ -40,6 +40,7 @@ #[experimental] #[simd] #[deriving(Show)] +#[repr(C)] pub struct i8x16(pub i8, pub i8, pub i8, pub i8, pub i8, pub i8, pub i8, pub i8, pub i8, pub i8, pub i8, pub i8, @@ -48,22 +49,26 @@ pub struct i8x16(pub i8, pub i8, pub i8, pub i8, #[experimental] #[simd] #[deriving(Show)] +#[repr(C)] pub struct i16x8(pub i16, pub i16, pub i16, pub i16, pub i16, pub i16, pub i16, pub i16); #[experimental] #[simd] #[deriving(Show)] +#[repr(C)] pub struct i32x4(pub i32, pub i32, pub i32, pub i32); #[experimental] #[simd] #[deriving(Show)] +#[repr(C)] pub struct i64x2(pub i64, pub i64); #[experimental] #[simd] #[deriving(Show)] +#[repr(C)] pub struct u8x16(pub u8, pub u8, pub u8, pub u8, pub u8, pub u8, pub u8, pub u8, pub u8, pub u8, pub u8, pub u8, @@ -72,25 +77,30 @@ pub struct u8x16(pub u8, pub u8, pub u8, pub u8, #[experimental] #[simd] #[deriving(Show)] +#[repr(C)] pub struct u16x8(pub u16, pub u16, pub u16, pub u16, pub u16, pub u16, pub u16, pub u16); #[experimental] #[simd] #[deriving(Show)] +#[repr(C)] pub struct u32x4(pub u32, pub u32, pub u32, pub u32); #[experimental] #[simd] #[deriving(Show)] +#[repr(C)] pub struct u64x2(pub u64, pub u64); #[experimental] #[simd] #[deriving(Show)] +#[repr(C)] pub struct f32x4(pub f32, pub f32, pub f32, pub f32); #[experimental] #[simd] #[deriving(Show)] +#[repr(C)] pub struct f64x2(pub f64, pub f64); diff --git a/src/libcore/slice.rs b/src/libcore/slice.rs index 826f25101fb..5a70cd8c847 100644 --- a/src/libcore/slice.rs +++ b/src/libcore/slice.rs @@ -996,9 +996,6 @@ impl<'a, T> Default for &'a [T] { fn default() -> &'a [T] { &[] } } - - - // // Iterators // @@ -1128,7 +1125,16 @@ impl<'a, T> ExactSize<&'a mut T> for MutItems<'a, T> {} /// An iterator over the slices of a vector separated by elements that /// match a predicate function. +#[cfg(not(stage0))] #[experimental = "needs review"] +pub struct Splits<'a, T:'a> { + v: &'a [T], + pred: |t: &T|: 'a -> bool, + finished: bool +} + +/// Dox. +#[cfg(stage0)] pub struct Splits<'a, T> { v: &'a [T], pred: |t: &T|: 'a -> bool, @@ -1186,7 +1192,16 @@ impl<'a, T> DoubleEndedIterator<&'a [T]> for Splits<'a, T> { /// An iterator over the subslices of the vector which are separated /// by elements that match `pred`. +#[cfg(not(stage0))] #[experimental = "needs review"] +pub struct MutSplits<'a, T:'a> { + v: &'a mut [T], + pred: |t: &T|: 'a -> bool, + finished: bool +} + +/// Dox +#[cfg(stage0)] pub struct MutSplits<'a, T> { v: &'a mut [T], pred: |t: &T|: 'a -> bool, @@ -1255,7 +1270,16 @@ impl<'a, T> DoubleEndedIterator<&'a mut [T]> for MutSplits<'a, T> { /// An iterator over the slices of a vector separated by elements that /// match a predicate function, splitting at most a fixed number of times. +#[cfg(not(stage0))] #[experimental = "needs review"] +pub struct SplitsN<'a, T:'a> { + iter: Splits<'a, T>, + count: uint, + invert: bool +} + +/// Dox. +#[cfg(stage0)] pub struct SplitsN<'a, T> { iter: Splits<'a, T>, count: uint, @@ -1291,6 +1315,7 @@ impl<'a, T> Iterator<&'a [T]> for SplitsN<'a, T> { /// An iterator over the (overlapping) slices of length `size` within /// a vector. +#[cfg(stage0)] #[deriving(Clone)] #[experimental = "needs review"] pub struct Windows<'a, T> { @@ -1298,7 +1323,16 @@ pub struct Windows<'a, T> { size: uint } +/// An iterator over the (overlapping) slices of length `size` within +/// a vector. +#[cfg(not(stage0))] +#[deriving(Clone)] #[experimental = "needs review"] +pub struct Windows<'a, T:'a> { + v: &'a [T], + size: uint +} + impl<'a, T> Iterator<&'a [T]> for Windows<'a, T> { #[inline] fn next(&mut self) -> Option<&'a [T]> { @@ -1327,6 +1361,7 @@ impl<'a, T> Iterator<&'a [T]> for Windows<'a, T> { /// /// When the vector len is not evenly divided by the chunk size, /// the last slice of the iteration will be the remainder. +#[cfg(stage0)] #[deriving(Clone)] #[experimental = "needs review"] pub struct Chunks<'a, T> { @@ -1334,6 +1369,18 @@ pub struct Chunks<'a, T> { size: uint } +/// An iterator over a vector in (non-overlapping) chunks (`size` +/// elements at a time). +/// +/// When the vector len is not evenly divided by the chunk size, +/// the last slice of the iteration will be the remainder. +#[cfg(not(stage0))] +#[deriving(Clone)] +pub struct Chunks<'a, T:'a> { + v: &'a [T], + size: uint +} + #[experimental = "needs review"] impl<'a, T> Iterator<&'a [T]> for Chunks<'a, T> { #[inline] @@ -1400,7 +1447,15 @@ impl<'a, T> RandomAccessIterator<&'a [T]> for Chunks<'a, T> { /// An iterator over a vector in (non-overlapping) mutable chunks (`size` elements at a time). When /// the vector len is not evenly divided by the chunk size, the last slice of the iteration will be /// the remainder. +#[cfg(not(stage0))] #[experimental = "needs review"] +pub struct MutChunks<'a, T:'a> { + v: &'a mut [T], + chunk_size: uint +} + +/// Dox. +#[cfg(stage0)] pub struct MutChunks<'a, T> { v: &'a mut [T], chunk_size: uint @@ -1623,7 +1678,6 @@ pub mod bytes { - // // Boilerplate traits // @@ -1650,6 +1704,27 @@ impl<'a,T:PartialEq, V: Slice<T>> Equiv<V> for &'a [T] { } #[unstable = "waiting for DST"] +impl<'a,T:PartialEq> PartialEq for &'a mut [T] { + fn eq(&self, other: & &'a mut [T]) -> bool { + self.len() == other.len() && + order::eq(self.iter(), other.iter()) + } + fn ne(&self, other: & &'a mut [T]) -> bool { + self.len() != other.len() || + order::ne(self.iter(), other.iter()) + } +} + +#[unstable = "waiting for DST"] +impl<'a,T:Eq> Eq for &'a mut [T] {} + +#[unstable = "waiting for DST"] +impl<'a,T:PartialEq, V: Slice<T>> Equiv<V> for &'a mut [T] { + #[inline] + fn equiv(&self, other: &V) -> bool { self.as_slice() == other.as_slice() } +} + +#[unstable = "waiting for DST"] impl<'a,T:Ord> Ord for &'a [T] { fn cmp(&self, other: & &'a [T]) -> Ordering { order::cmp(self.iter(), other.iter()) diff --git a/src/libcore/str.rs b/src/libcore/str.rs index 095605326c7..5cbeda94d0f 100644 --- a/src/libcore/str.rs +++ b/src/libcore/str.rs @@ -419,6 +419,8 @@ struct TwoWaySearcher { memory: uint } +// This is the Two-Way search algorithm, which was introduced in the paper: +// Crochemore, M., Perrin, D., 1991, Two-way string-matching, Journal of the ACM 38(3):651-675. impl TwoWaySearcher { fn new(needle: &[u8]) -> TwoWaySearcher { let (critPos1, period1) = TwoWaySearcher::maximal_suffix(needle, false); @@ -437,7 +439,14 @@ impl TwoWaySearcher { let byteset = needle.iter() .fold(0, |a, &b| (1 << ((b & 0x3f) as uint)) | a); - if needle.slice_to(critPos) == needle.slice_from(needle.len() - critPos) { + + // The logic here (calculating critPos and period, the final if statement to see which + // period to use for the TwoWaySearcher) is essentially an implementation of the + // "small-period" function from the paper (p. 670) + // + // In the paper they check whether `needle.slice_to(critPos)` is a suffix of + // `needle.slice(critPos, critPos + period)`, which is precisely what this does + if needle.slice_to(critPos) == needle.slice(period, period + critPos) { TwoWaySearcher { critPos: critPos, period: period, @@ -508,6 +517,9 @@ impl TwoWaySearcher { } } + // returns (i, p) where i is the "critical position", the starting index of + // of maximal suffix, and p is the period of the suffix + // see p. 668 of the paper #[inline] fn maximal_suffix(arr: &[u8], reversed: bool) -> (uint, uint) { let mut left = -1; // Corresponds to i in the paper @@ -562,7 +574,7 @@ enum Searcher { impl Searcher { fn new(haystack: &[u8], needle: &[u8]) -> Searcher { // FIXME: Tune this. - if needle.len() > haystack.len() - 20 { + if needle.len() + 20 > haystack.len() { Naive(NaiveSearcher::new()) } else { let searcher = TwoWaySearcher::new(needle); @@ -906,8 +918,8 @@ pub fn utf16_items<'a>(v: &'a [u16]) -> Utf16Items<'a> { /// /// // "ab\0d" /// v[2] = 0; -/// assert_eq!(str::truncate_utf16_at_nul(v), -/// &['a' as u16, 'b' as u16]); +/// let b: &[_] = &['a' as u16, 'b' as u16]; +/// assert_eq!(str::truncate_utf16_at_nul(v), b); /// ``` pub fn truncate_utf16_at_nul<'a>(v: &'a [u16]) -> &'a [u16] { match v.iter().position(|c| *c == 0) { @@ -1094,6 +1106,12 @@ pub trait StrSlice<'a> { /// # Arguments /// /// - needle - The string to look for + /// + /// # Example + /// + /// ```rust + /// assert!("bananas".contains("nana")); + /// ``` fn contains<'a>(&self, needle: &'a str) -> bool; /// Returns true if a string contains a char. @@ -1101,6 +1119,12 @@ pub trait StrSlice<'a> { /// # Arguments /// /// - needle - The char to look for + /// + /// # Example + /// + /// ```rust + /// assert!("hello".contains_char('e')); + /// ``` fn contains_char(&self, needle: char) -> bool; /// An iterator over the characters of `self`. Note, this iterates @@ -1115,6 +1139,13 @@ pub trait StrSlice<'a> { fn chars(&self) -> Chars<'a>; /// An iterator over the bytes of `self` + /// + /// # Example + /// + /// ```rust + /// let v: Vec<u8> = "bors".bytes().collect(); + /// assert_eq!(v, b"bors".to_vec()); + /// ``` fn bytes(&self) -> Bytes<'a>; /// An iterator over the characters of `self` and their byte offsets. @@ -1381,9 +1412,21 @@ pub trait StrSlice<'a> { fn slice_chars(&self, begin: uint, end: uint) -> &'a str; /// Returns true if `needle` is a prefix of the string. + /// + /// # Example + /// + /// ```rust + /// assert!("banana".starts_with("ba")); + /// ``` fn starts_with(&self, needle: &str) -> bool; /// Returns true if `needle` is a suffix of the string. + /// + /// # Example + /// + /// ```rust + /// assert!("banana".ends_with("nana")); + /// ``` fn ends_with(&self, needle: &str) -> bool; /// Returns a string with characters that match `to_trim` removed. @@ -1396,7 +1439,8 @@ pub trait StrSlice<'a> { /// /// ```rust /// assert_eq!("11foo1bar11".trim_chars('1'), "foo1bar") - /// assert_eq!("12foo1bar12".trim_chars(&['1', '2']), "foo1bar") + /// let x: &[_] = &['1', '2']; + /// assert_eq!("12foo1bar12".trim_chars(x), "foo1bar") /// assert_eq!("123foo1bar123".trim_chars(|c: char| c.is_digit()), "foo1bar") /// ``` fn trim_chars<C: CharEq>(&self, to_trim: C) -> &'a str; @@ -1411,7 +1455,8 @@ pub trait StrSlice<'a> { /// /// ```rust /// assert_eq!("11foo1bar11".trim_left_chars('1'), "foo1bar11") - /// assert_eq!("12foo1bar12".trim_left_chars(&['1', '2']), "foo1bar12") + /// let x: &[_] = &['1', '2']; + /// assert_eq!("12foo1bar12".trim_left_chars(x), "foo1bar12") /// assert_eq!("123foo1bar123".trim_left_chars(|c: char| c.is_digit()), "foo1bar123") /// ``` fn trim_left_chars<C: CharEq>(&self, to_trim: C) -> &'a str; @@ -1426,7 +1471,8 @@ pub trait StrSlice<'a> { /// /// ```rust /// assert_eq!("11foo1bar11".trim_right_chars('1'), "11foo1bar") - /// assert_eq!("12foo1bar12".trim_right_chars(&['1', '2']), "12foo1bar") + /// let x: &[_] = &['1', '2']; + /// assert_eq!("12foo1bar12".trim_right_chars(x), "12foo1bar") /// assert_eq!("123foo1bar123".trim_right_chars(|c: char| c.is_digit()), "123foo1bar") /// ``` fn trim_right_chars<C: CharEq>(&self, to_trim: C) -> &'a str; @@ -1464,8 +1510,8 @@ pub trait StrSlice<'a> { /// /// # Example /// - /// This example manually iterate through the characters of a - /// string; this should normally by done by `.chars()` or + /// This example manually iterates through the characters of a + /// string; this should normally be done by `.chars()` or /// `.char_indices`. /// /// ```rust @@ -1525,6 +1571,15 @@ pub trait StrSlice<'a> { /// Plucks the character starting at the `i`th byte of a string. /// + /// # Example + /// + /// ```rust + /// let s = "abπc"; + /// assert_eq!(s.char_at(1), 'b'); + /// assert_eq!(s.char_at(2), 'π'); + /// assert_eq!(s.char_at(4), 'c'); + /// ``` + /// /// # Failure /// /// If `i` is greater than or equal to the length of the string. @@ -1540,6 +1595,12 @@ pub trait StrSlice<'a> { fn char_at_reverse(&self, i: uint) -> char; /// Work with the byte buffer of a string as a byte slice. + /// + /// # Example + /// + /// ```rust + /// assert_eq!("bors".as_bytes(), b"bors"); + /// ``` fn as_bytes(&self) -> &'a [u8]; /// Returns the byte index of the first character of `self` that @@ -1562,7 +1623,8 @@ pub trait StrSlice<'a> { /// assert_eq!(s.find(|c: char| c.is_whitespace()), Some(5)); /// /// // neither are found - /// assert_eq!(s.find(&['1', '2']), None); + /// let x: &[_] = &['1', '2']; + /// assert_eq!(s.find(x), None); /// ``` fn find<C: CharEq>(&self, search: C) -> Option<uint>; @@ -1586,7 +1648,8 @@ pub trait StrSlice<'a> { /// assert_eq!(s.rfind(|c: char| c.is_whitespace()), Some(12)); /// /// // searches for an occurrence of either `1` or `2`, but neither are found - /// assert_eq!(s.rfind(&['1', '2']), None); + /// let x: &[_] = &['1', '2']; + /// assert_eq!(s.rfind(x), None); /// ``` fn rfind<C: CharEq>(&self, search: C) -> Option<uint>; @@ -1659,6 +1722,13 @@ pub trait StrSlice<'a> { fn utf16_units(&self) -> Utf16CodeUnits<'a>; } +#[inline(never)] +fn slice_error_fail(s: &str, begin: uint, end: uint) -> ! { + assert!(begin <= end); + fail!("index {} and/or {} in `{}` do not lie on character boundary", + begin, end, s); +} + impl<'a> StrSlice<'a> for &'a str { #[inline] fn contains<'a>(&self, needle: &'a str) -> bool { @@ -1762,22 +1832,34 @@ impl<'a> StrSlice<'a> for &'a str { #[inline] fn slice(&self, begin: uint, end: uint) -> &'a str { - assert!(self.is_char_boundary(begin) && self.is_char_boundary(end), - "index {} and/or {} in `{}` do not lie on character boundary", begin, - end, *self); - unsafe { raw::slice_bytes(*self, begin, end) } + // is_char_boundary checks that the index is in [0, .len()] + if begin <= end && + self.is_char_boundary(begin) && + self.is_char_boundary(end) { + unsafe { raw::slice_unchecked(*self, begin, end) } + } else { + slice_error_fail(*self, begin, end) + } } #[inline] fn slice_from(&self, begin: uint) -> &'a str { - self.slice(begin, self.len()) + // is_char_boundary checks that the index is in [0, .len()] + if self.is_char_boundary(begin) { + unsafe { raw::slice_unchecked(*self, begin, self.len()) } + } else { + slice_error_fail(*self, begin, self.len()) + } } #[inline] fn slice_to(&self, end: uint) -> &'a str { - assert!(self.is_char_boundary(end), "index {} in `{}` does not lie on \ - a character boundary", end, *self); - unsafe { raw::slice_bytes(*self, 0, end) } + // is_char_boundary checks that the index is in [0, .len()] + if self.is_char_boundary(end) { + unsafe { raw::slice_unchecked(*self, 0, end) } + } else { + slice_error_fail(*self, 0, end) + } } fn slice_chars(&self, begin: uint, end: uint) -> &'a str { @@ -1852,9 +1934,10 @@ impl<'a> StrSlice<'a> for &'a str { #[inline] fn is_char_boundary(&self, index: uint) -> bool { if index == self.len() { return true; } - if index > self.len() { return false; } - let b = self.as_bytes()[index]; - return b < 128u8 || b >= 192u8; + match self.as_bytes().get(index) { + None => false, + Some(&b) => b < 128u8 || b >= 192u8, + } } #[inline] diff --git a/src/libcoretest/iter.rs b/src/libcoretest/iter.rs index d25ffb5b84c..99ac7cfed02 100644 --- a/src/libcoretest/iter.rs +++ b/src/libcoretest/iter.rs @@ -368,7 +368,7 @@ fn test_collect() { #[test] fn test_all() { - let v: Box<&[int]> = box &[1i, 2, 3, 4, 5]; + let v: Box<[int]> = box [1i, 2, 3, 4, 5]; assert!(v.iter().all(|&x| x < 10)); assert!(!v.iter().all(|&x| x % 2 == 0)); assert!(!v.iter().all(|&x| x > 100)); @@ -377,7 +377,7 @@ fn test_all() { #[test] fn test_any() { - let v: Box<&[int]> = box &[1i, 2, 3, 4, 5]; + let v: Box<[int]> = box [1i, 2, 3, 4, 5]; assert!(v.iter().any(|&x| x < 10)); assert!(v.iter().any(|&x| x % 2 == 0)); assert!(!v.iter().any(|&x| x > 100)); diff --git a/src/libcoretest/lib.rs b/src/libcoretest/lib.rs index 3864321586c..7866d2f4a11 100644 --- a/src/libcoretest/lib.rs +++ b/src/libcoretest/lib.rs @@ -29,4 +29,5 @@ mod ptr; mod raw; mod result; mod slice; +mod str; mod tuple; diff --git a/src/libcoretest/str.rs b/src/libcoretest/str.rs new file mode 100644 index 00000000000..be2275dcd4a --- /dev/null +++ b/src/libcoretest/str.rs @@ -0,0 +1,34 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +fn check_contains_all_substrings(s: &str) { + assert!(s.contains("")); + for i in range(0, s.len()) { + for j in range(i+1, s.len() + 1) { + assert!(s.contains(s.slice(i, j))); + } + } +} + +#[test] +fn strslice_issue_16589() { + assert!("bananas".contains("nana")); + + // prior to the fix for #16589, x.contains("abcdabcd") returned false + // test all substrings for good measure + check_contains_all_substrings("012345678901234567890123456789bcdabcdabcd"); +} + + +#[test] +fn test_strslice_contains() { + let x = "There are moments, Jeeves, when one asks oneself, 'Do trousers matter?'"; + check_contains_all_substrings(x); +} diff --git a/src/libdebug/lib.rs b/src/libdebug/lib.rs index 6341a380563..cc97eeffe7a 100644 --- a/src/libdebug/lib.rs +++ b/src/libdebug/lib.rs @@ -25,7 +25,7 @@ html_favicon_url = "http://www.rust-lang.org/favicon.ico", html_root_url = "http://doc.rust-lang.org/master/")] #![experimental] -#![feature(managed_boxes, macro_rules)] +#![feature(managed_boxes, macro_rules, issue_5723_bootstrap)] #![allow(experimental)] pub mod fmt; diff --git a/src/libdebug/reflect.rs b/src/libdebug/reflect.rs index 0cbae6ee2d3..b89ea4d373d 100644 --- a/src/libdebug/reflect.rs +++ b/src/libdebug/reflect.rs @@ -193,6 +193,8 @@ impl<V:TyVisitor + MovePtr> TyVisitor for MovePtrAdaptor<V> { true } + // NOTE: remove after snapshot + #[cfg(stage0)] fn visit_estr_fixed(&mut self, n: uint, sz: uint, align: uint) -> bool { @@ -237,6 +239,7 @@ impl<V:TyVisitor + MovePtr> TyVisitor for MovePtrAdaptor<V> { true } + #[cfg(stage0)] fn visit_evec_fixed(&mut self, n: uint, sz: uint, align: uint, mtbl: uint, inner: *const TyDesc) -> bool { self.align(align); @@ -246,6 +249,16 @@ impl<V:TyVisitor + MovePtr> TyVisitor for MovePtrAdaptor<V> { self.bump(sz); true } + #[cfg(not(stage0))] + fn visit_evec_fixed(&mut self, n: uint, sz: uint, align: uint, + inner: *const TyDesc) -> bool { + self.align(align); + if ! self.inner.visit_evec_fixed(n, sz, align, inner) { + return false; + } + self.bump(sz); + true + } fn visit_enter_rec(&mut self, n_fields: uint, sz: uint, align: uint) -> bool { self.align(align); diff --git a/src/libdebug/repr.rs b/src/libdebug/repr.rs index b85097e6623..dbd2c09497b 100644 --- a/src/libdebug/repr.rs +++ b/src/libdebug/repr.rs @@ -95,7 +95,7 @@ pub struct ReprVisitor<'a> { ptr: *const u8, ptr_stk: Vec<*const u8>, var_stk: Vec<VariantState>, - writer: &'a mut io::Writer, + writer: &'a mut io::Writer+'a, last_err: Option<io::IoError>, } @@ -275,6 +275,8 @@ impl<'a> TyVisitor for ReprVisitor<'a> { } // Type no longer exists, vestigial function. + // NOTE: remove after snapshot + #[cfg(stage0)] fn visit_estr_fixed(&mut self, _n: uint, _sz: uint, _align: uint) -> bool { fail!(); } @@ -328,6 +330,8 @@ impl<'a> TyVisitor for ReprVisitor<'a> { }) } + // NOTE: remove after snapshot + #[cfg(stage0)] fn visit_evec_fixed(&mut self, n: uint, sz: uint, _align: uint, _: uint, inner: *const TyDesc) -> bool { let assumed_size = if sz == 0 { n } else { sz }; @@ -336,6 +340,16 @@ impl<'a> TyVisitor for ReprVisitor<'a> { }) } + #[cfg(not(stage0))] + fn visit_evec_fixed(&mut self, n: uint, sz: uint, _align: uint, + inner: *const TyDesc) -> bool { + let assumed_size = if sz == 0 { n } else { sz }; + self.get::<()>(|this, b| { + this.write_vec_range(b, assumed_size, inner) + }) + } + + fn visit_enter_rec(&mut self, _n_fields: uint, _sz: uint, _align: uint) -> bool { try!(self, self.writer.write([b'{'])); diff --git a/src/libfmt_macros/lib.rs b/src/libfmt_macros/lib.rs index d41035d3a16..cef258fac20 100644 --- a/src/libfmt_macros/lib.rs +++ b/src/libfmt_macros/lib.rs @@ -200,7 +200,7 @@ impl<'a> Parser<'a> { self.cur.next(); } Some((_, other)) => { - self.err(format!("expected `{}` but found `{}`", + self.err(format!("expected `{}`, found `{}`", c, other).as_slice()); } diff --git a/src/libfourcc/lib.rs b/src/libfourcc/lib.rs index e4d61c47cc2..9e46da56a8e 100644 --- a/src/libfourcc/lib.rs +++ b/src/libfourcc/lib.rs @@ -72,8 +72,8 @@ pub fn plugin_registrar(reg: &mut Registry) { reg.register_macro("fourcc", expand_syntax_ext); } -pub fn expand_syntax_ext(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree]) - -> Box<base::MacResult> { +pub fn expand_syntax_ext<'cx>(cx: &'cx mut ExtCtxt, sp: Span, tts: &[ast::TokenTree]) + -> Box<base::MacResult+'cx> { let (expr, endian) = parse_tts(cx, tts); let little = match endian { diff --git a/src/libgetopts/lib.rs b/src/libgetopts/lib.rs index 3286c329755..475dd2c6743 100644 --- a/src/libgetopts/lib.rs +++ b/src/libgetopts/lib.rs @@ -571,7 +571,6 @@ pub fn getopts(args: &[String], optgrps: &[OptGroup]) -> Result { } } else { let mut j = 1; - let mut last_valid_opt_id = None; names = Vec::new(); while j < curlen { let range = cur.as_slice().char_range_at(j); @@ -584,27 +583,24 @@ pub fn getopts(args: &[String], optgrps: &[OptGroup]) -> Result { interpreted correctly */ - match find_opt(opts.as_slice(), opt.clone()) { - Some(id) => last_valid_opt_id = Some(id), - None => { - let arg_follows = - last_valid_opt_id.is_some() && - match opts[last_valid_opt_id.unwrap()] - .hasarg { - - Yes | Maybe => true, - No => false - }; - if arg_follows && j < curlen { - i_arg = Some(cur.as_slice() - .slice(j, curlen).to_string()); - break; - } else { - last_valid_opt_id = None; - } - } - } + let opt_id = match find_opt(opts.as_slice(), opt.clone()) { + Some(id) => id, + None => return Err(UnrecognizedOption(opt.to_string())) + }; + names.push(opt); + + let arg_follows = match opts[opt_id].hasarg { + Yes | Maybe => true, + No => false + }; + + if arg_follows && range.next < curlen { + i_arg = Some(cur.as_slice() + .slice(range.next, curlen).to_string()); + break; + } + j = range.next; } } @@ -617,7 +613,7 @@ pub fn getopts(args: &[String], optgrps: &[OptGroup]) -> Result { }; match opts[optid].hasarg { No => { - if !i_arg.is_none() { + if name_pos == names.len() && !i_arg.is_none() { return Err(UnexpectedArgument(nm.to_string())); } vals.get_mut(optid).push(Given); @@ -1442,6 +1438,21 @@ mod tests { } #[test] + fn test_nospace_conflict() { + let args = vec!("-vvLverbose".to_string(), "-v".to_string() ); + let opts = vec!(optmulti("L", "", "library directory", "LIB"), + optflagmulti("v", "verbose", "Verbose")); + let matches = &match getopts(args.as_slice(), opts.as_slice()) { + result::Ok(m) => m, + result::Err(e) => fail!( "{}", e ) + }; + assert!(matches.opts_present(["L".to_string()])); + assert_eq!(matches.opts_str(["L".to_string()]).unwrap(), "verbose".to_string()); + assert!(matches.opts_present(["v".to_string()])); + assert_eq!(3, matches.opt_count("v")); + } + + #[test] fn test_long_to_short() { let mut short = Opt { name: Long("banana".to_string()), diff --git a/src/libglob/lib.rs b/src/libglob/lib.rs index d539283f0a7..c9af44c9479 100644 --- a/src/libglob/lib.rs +++ b/src/libglob/lib.rs @@ -24,7 +24,8 @@ */ #![crate_name = "glob"] -#![experimental] +#![deprecated = "This is now a cargo package located at: \ + https://github.com/rust-lang/glob"] #![crate_type = "rlib"] #![crate_type = "dylib"] #![license = "MIT/ASL2"] @@ -32,6 +33,7 @@ html_favicon_url = "http://www.rust-lang.org/favicon.ico", html_root_url = "http://doc.rust-lang.org/master/", html_playground_url = "http://play.rust-lang.org/")] +#![allow(deprecated)] use std::cell::Cell; use std::{cmp, os, path}; @@ -64,6 +66,7 @@ pub struct Paths { /// `puppies.jpg` and `hamsters.gif`: /// /// ```rust +/// # #![allow(deprecated)] /// use glob::glob; /// /// for path in glob("/media/pictures/*.jpg") { diff --git a/src/libgraphviz/lib.rs b/src/libgraphviz/lib.rs index 7cac0d25abf..10cc7e8afe9 100644 --- a/src/libgraphviz/lib.rs +++ b/src/libgraphviz/lib.rs @@ -271,6 +271,7 @@ pub fn main() { #![crate_type = "rlib"] #![crate_type = "dylib"] #![license = "MIT/ASL2"] +#![feature(issue_5723_bootstrap)] #![doc(html_logo_url = "http://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png", html_favicon_url = "http://www.rust-lang.org/favicon.ico", html_root_url = "http://doc.rust-lang.org/master/")] @@ -499,7 +500,7 @@ pub trait GraphWalk<'a, N, E> { /// Renders directed graph `g` into the writer `w` in DOT syntax. /// (Main entry point for the library.) -pub fn render<'a, N, E, G:Labeller<'a,N,E>+GraphWalk<'a,N,E>, W:Writer>( +pub fn render<'a, N:'a, E:'a, G:Labeller<'a,N,E>+GraphWalk<'a,N,E>, W:Writer>( g: &'a G, w: &mut W) -> io::IoResult<()> { diff --git a/src/libgraphviz/maybe_owned_vec.rs b/src/libgraphviz/maybe_owned_vec.rs index 987f214b153..17c75b76bc1 100644 --- a/src/libgraphviz/maybe_owned_vec.rs +++ b/src/libgraphviz/maybe_owned_vec.rs @@ -34,6 +34,14 @@ use std::slice; /// Some clients will have a pre-allocated vector ready to hand off in /// a slice; others will want to create the set on the fly and hand /// off ownership, via `Growable`. +#[cfg(not(stage0))] +pub enum MaybeOwnedVector<'a,T:'a> { + Growable(Vec<T>), + Borrowed(&'a [T]), +} + +/// Stage0 only. +#[cfg(stage0)] pub enum MaybeOwnedVector<'a,T> { Growable(Vec<T>), Borrowed(&'a [T]), @@ -45,7 +53,7 @@ pub trait IntoMaybeOwnedVector<'a,T> { fn into_maybe_owned(self) -> MaybeOwnedVector<'a,T>; } -impl<'a,T> IntoMaybeOwnedVector<'a,T> for Vec<T> { +impl<'a,T:'a> IntoMaybeOwnedVector<'a,T> for Vec<T> { #[inline] fn into_maybe_owned(self) -> MaybeOwnedVector<'a,T> { Growable(self) } } diff --git a/src/libgreen/context.rs b/src/libgreen/context.rs index b63758cdcc5..296615e15ff 100644 --- a/src/libgreen/context.rs +++ b/src/libgreen/context.rs @@ -15,6 +15,7 @@ use std::rt::stack; use std::raw; #[cfg(target_arch = "x86_64")] use std::simd; +use libc; // FIXME #7761: Registers is boxed so that it is 16-byte aligned, for storing // SSE regs. It would be marginally better not to do this. In C++ we @@ -69,7 +70,7 @@ impl Context { // overflow). Additionally, their coroutine stacks are listed as being // zero-length, so that's how we detect what's what here. let stack_base: *const uint = stack.start(); - let bounds = if sp as uint == stack_base as uint { + let bounds = if sp as libc::uintptr_t == stack_base as libc::uintptr_t { None } else { Some((stack_base as uint, sp as uint)) @@ -111,7 +112,7 @@ impl Context { // the stack limit to 0 to make morestack never fail None => stack::record_rust_managed_stack_bounds(0, uint::MAX), } - rust_swap_registers(out_regs, in_regs) + rust_swap_registers(out_regs, in_regs); } } } @@ -166,7 +167,7 @@ fn new_regs() -> Box<Registers> { #[cfg(target_arch = "x86")] fn initialize_call_frame(regs: &mut Registers, fptr: InitFn, arg: uint, procedure: raw::Procedure, sp: *mut uint) { - + let sp = sp as *mut uint; // x86 has interesting stack alignment requirements, so do some alignment // plus some offsetting to figure out what the actual stack should be. let sp = align_down(sp); @@ -188,13 +189,15 @@ fn initialize_call_frame(regs: &mut Registers, fptr: InitFn, arg: uint, // windows requires saving more registers (both general and XMM), so the windows // register context must be larger. #[cfg(windows, target_arch = "x86_64")] +#[repr(C)] struct Registers { - gpr:[uint, ..14], + gpr:[libc::uintptr_t, ..14], _xmm:[simd::u32x4, ..10] } #[cfg(not(windows), target_arch = "x86_64")] +#[repr(C)] struct Registers { - gpr:[uint, ..10], + gpr:[libc::uintptr_t, ..10], _xmm:[simd::u32x4, ..6] } @@ -234,30 +237,30 @@ fn initialize_call_frame(regs: &mut Registers, fptr: InitFn, arg: uint, unsafe { *sp = 0; } rtdebug!("creating call frame"); - rtdebug!("fptr {:#x}", fptr as uint); + rtdebug!("fptr {:#x}", fptr as libc::uintptr_t); rtdebug!("arg {:#x}", arg); rtdebug!("sp {}", sp); // These registers are frobbed by rust_bootstrap_green_task into the right // location so we can invoke the "real init function", `fptr`. - regs.gpr[RUSTRT_R12] = arg as uint; - regs.gpr[RUSTRT_R13] = procedure.code as uint; - regs.gpr[RUSTRT_R14] = procedure.env as uint; - regs.gpr[RUSTRT_R15] = fptr as uint; + regs.gpr[RUSTRT_R12] = arg as libc::uintptr_t; + regs.gpr[RUSTRT_R13] = procedure.code as libc::uintptr_t; + regs.gpr[RUSTRT_R14] = procedure.env as libc::uintptr_t; + regs.gpr[RUSTRT_R15] = fptr as libc::uintptr_t; // These registers are picked up by the regular context switch paths. These // will put us in "mostly the right context" except for frobbing all the // arguments to the right place. We have the small trampoline code inside of // rust_bootstrap_green_task to do that. - regs.gpr[RUSTRT_RSP] = sp as uint; - regs.gpr[RUSTRT_IP] = rust_bootstrap_green_task as uint; + regs.gpr[RUSTRT_RSP] = sp as libc::uintptr_t; + regs.gpr[RUSTRT_IP] = rust_bootstrap_green_task as libc::uintptr_t; // Last base pointer on the stack should be 0 regs.gpr[RUSTRT_RBP] = 0; } #[cfg(target_arch = "arm")] -type Registers = [uint, ..32]; +type Registers = [libc::uintptr_t, ..32]; #[cfg(target_arch = "arm")] fn new_regs() -> Box<Registers> { box {[0, .. 32]} } @@ -277,17 +280,17 @@ fn initialize_call_frame(regs: &mut Registers, fptr: InitFn, arg: uint, // ARM uses the same technique as x86_64 to have a landing pad for the start // of all new green tasks. Neither r1/r2 are saved on a context switch, so // the shim will copy r3/r4 into r1/r2 and then execute the function in r5 - regs[0] = arg as uint; // r0 - regs[3] = procedure.code as uint; // r3 - regs[4] = procedure.env as uint; // r4 - regs[5] = fptr as uint; // r5 - regs[13] = sp as uint; // #52 sp, r13 - regs[14] = rust_bootstrap_green_task as uint; // #56 pc, r14 --> lr + regs[0] = arg as libc::uintptr_t; // r0 + regs[3] = procedure.code as libc::uintptr_t; // r3 + regs[4] = procedure.env as libc::uintptr_t; // r4 + regs[5] = fptr as libc::uintptr_t; // r5 + regs[13] = sp as libc::uintptr_t; // #52 sp, r13 + regs[14] = rust_bootstrap_green_task as libc::uintptr_t; // #56 pc, r14 --> lr } #[cfg(target_arch = "mips")] #[cfg(target_arch = "mipsel")] -type Registers = [uint, ..32]; +type Registers = [libc::uintptr_t, ..32]; #[cfg(target_arch = "mips")] #[cfg(target_arch = "mipsel")] @@ -304,12 +307,12 @@ fn initialize_call_frame(regs: &mut Registers, fptr: InitFn, arg: uint, // The final return address. 0 indicates the bottom of the stack unsafe { *sp = 0; } - regs[4] = arg as uint; - regs[5] = procedure.code as uint; - regs[6] = procedure.env as uint; - regs[29] = sp as uint; - regs[25] = fptr as uint; - regs[31] = fptr as uint; + regs[4] = arg as libc::uintptr_t; + regs[5] = procedure.code as libc::uintptr_t; + regs[6] = procedure.env as libc::uintptr_t; + regs[29] = sp as libc::uintptr_t; + regs[25] = fptr as libc::uintptr_t; + regs[31] = fptr as libc::uintptr_t; } fn align_down(sp: *mut uint) -> *mut uint { diff --git a/src/libgreen/simple.rs b/src/libgreen/simple.rs index 6254e8c55f0..058a00bcd4b 100644 --- a/src/libgreen/simple.rs +++ b/src/libgreen/simple.rs @@ -82,7 +82,7 @@ impl Runtime for SimpleTask { fn local_io<'a>(&'a mut self) -> Option<rtio::LocalIo<'a>> { None } fn stack_bounds(&self) -> (uint, uint) { fail!() } fn can_block(&self) -> bool { true } - fn wrap(self: Box<SimpleTask>) -> Box<Any> { fail!() } + fn wrap(self: Box<SimpleTask>) -> Box<Any+'static> { fail!() } } pub fn task() -> Box<Task> { diff --git a/src/libgreen/stack.rs b/src/libgreen/stack.rs index 601758f8a25..4673e7b3ba2 100644 --- a/src/libgreen/stack.rs +++ b/src/libgreen/stack.rs @@ -68,7 +68,8 @@ impl Stack { // FIXME: Using the FFI to call a C macro. Slow stk.valgrind_id = unsafe { - rust_valgrind_stack_register(stk.start(), stk.end()) + rust_valgrind_stack_register(stk.start() as *const libc::uintptr_t, + stk.end() as *const libc::uintptr_t) }; return stk; } diff --git a/src/libgreen/task.rs b/src/libgreen/task.rs index 73fe8f6a93f..ffd94e0b86f 100644 --- a/src/libgreen/task.rs +++ b/src/libgreen/task.rs @@ -488,7 +488,9 @@ impl Runtime for GreenTask { fn can_block(&self) -> bool { false } - fn wrap(self: Box<GreenTask>) -> Box<Any> { self as Box<Any> } + fn wrap(self: Box<GreenTask>) -> Box<Any+'static> { + self as Box<Any+'static> + } } #[cfg(test)] diff --git a/src/libhexfloat/lib.rs b/src/libhexfloat/lib.rs index 936e7cb4403..03bd96fc260 100644 --- a/src/libhexfloat/lib.rs +++ b/src/libhexfloat/lib.rs @@ -105,7 +105,7 @@ fn hex_float_lit_err(s: &str) -> Option<(uint, String)> { } pub fn expand_syntax_ext(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree]) - -> Box<base::MacResult> { + -> Box<base::MacResult+'static> { let (expr, ty_lit) = parse_tts(cx, tts); let ty = match ty_lit { diff --git a/src/liblibc/lib.rs b/src/liblibc/lib.rs index 370dc4a0822..949dd08eaa3 100644 --- a/src/liblibc/lib.rs +++ b/src/liblibc/lib.rs @@ -64,7 +64,7 @@ * sanity while editing, filling-in-details and eliminating duplication) into * definitions common-to-all (held in modules named c95, c99, posix88, posix01 * and posix08) and definitions that appear only on *some* platforms (named -* 'extra'). This would be things like significant OSX foundation kit, or win32 +* 'extra'). This would be things like significant OSX foundation kit, or Windows * library kernel32.dll, or various fancy glibc, linux or BSD extensions. * * In addition to the per-platform 'extra' modules, we define a module of @@ -304,7 +304,7 @@ extern {} // If/when libprim happens, this can be removed in favor of that pub enum Nullable<T> { Null, - Some(T) + NotNull(T) } pub mod types { @@ -334,6 +334,7 @@ pub mod types { __variant1, __variant2, } + pub enum FILE {} pub enum fpos_t {} } @@ -369,6 +370,7 @@ pub mod types { pub type pthread_t = c_ulong; + #[repr(C)] pub struct glob_t { pub gl_pathc: size_t, pub gl_pathv: *mut *mut c_char, @@ -381,11 +383,13 @@ pub mod types { pub __unused5: *mut c_void, } + #[repr(C)] pub struct timeval { pub tv_sec: time_t, pub tv_usec: suseconds_t, } + #[repr(C)] pub struct timespec { pub tv_sec: time_t, pub tv_nsec: c_long, @@ -402,24 +406,29 @@ pub mod types { pub type sa_family_t = u16; pub type in_port_t = u16; pub type in_addr_t = u32; + #[repr(C)] pub struct sockaddr { pub sa_family: sa_family_t, pub sa_data: [u8, ..14], } + #[repr(C)] pub struct sockaddr_storage { pub ss_family: sa_family_t, pub __ss_align: i64, pub __ss_pad2: [u8, ..112], } + #[repr(C)] pub struct sockaddr_in { pub sin_family: sa_family_t, pub sin_port: in_port_t, pub sin_addr: in_addr, pub sin_zero: [u8, ..8], } + #[repr(C)] pub struct in_addr { pub s_addr: in_addr_t, } + #[repr(C)] pub struct sockaddr_in6 { pub sin6_family: sa_family_t, pub sin6_port: in_port_t, @@ -427,17 +436,21 @@ pub mod types { pub sin6_addr: in6_addr, pub sin6_scope_id: u32, } + #[repr(C)] pub struct in6_addr { pub s6_addr: [u16, ..8] } + #[repr(C)] pub struct ip_mreq { pub imr_multiaddr: in_addr, pub imr_interface: in_addr, } + #[repr(C)] pub struct ip6_mreq { pub ipv6mr_multiaddr: in6_addr, pub ipv6mr_interface: c_uint, } + #[repr(C)] pub struct addrinfo { pub ai_flags: c_int, pub ai_family: c_int, @@ -459,6 +472,7 @@ pub mod types { pub ai_next: *mut addrinfo, } + #[repr(C)] pub struct sockaddr_un { pub sun_family: sa_family_t, pub sun_path: [c_char, ..108] @@ -493,8 +507,8 @@ pub mod types { pub mod c99 { pub type c_longlong = i64; pub type c_ulonglong = u64; - pub type intptr_t = int; - pub type uintptr_t = uint; + pub type intptr_t = i32; + pub type uintptr_t = u32; } #[cfg(target_arch = "x86")] #[cfg(target_arch = "mips")] @@ -533,6 +547,7 @@ pub mod types { pub type blksize_t = i32; pub type blkcnt_t = i32; + #[repr(C)] pub struct stat { pub st_dev: dev_t, pub __pad1: c_short, @@ -556,11 +571,13 @@ pub mod types { pub __unused5: c_long, } + #[repr(C)] pub struct utimbuf { pub actime: time_t, pub modtime: time_t, } + #[repr(C)] pub struct pthread_attr_t { pub __size: [u32, ..9] } @@ -575,6 +592,7 @@ pub mod types { pub type blksize_t = u32; pub type blkcnt_t = u32; + #[repr(C)] pub struct stat { pub st_dev: c_ulonglong, pub __pad0: [c_uchar, ..4], @@ -597,11 +615,13 @@ pub mod types { pub st_ino: c_ulonglong, } + #[repr(C)] pub struct utimbuf { pub actime: time_t, pub modtime: time_t, } + #[repr(C)] pub struct pthread_attr_t { pub __size: [u32, ..9] } @@ -618,6 +638,7 @@ pub mod types { pub type blksize_t = i32; pub type blkcnt_t = i32; + #[repr(C)] pub struct stat { pub st_dev: c_ulong, pub st_pad1: [c_long, ..3], @@ -641,11 +662,13 @@ pub mod types { pub st_pad5: [c_long, ..14], } + #[repr(C)] pub struct utimbuf { pub actime: time_t, pub modtime: time_t, } + #[repr(C)] pub struct pthread_attr_t { pub __size: [u32, ..9] } @@ -679,8 +702,8 @@ pub mod types { pub mod c99 { pub type c_longlong = i64; pub type c_ulonglong = u64; - pub type intptr_t = int; - pub type uintptr_t = uint; + pub type intptr_t = i64; + pub type uintptr_t = u64; } pub mod posix88 { pub type off_t = i64; @@ -702,6 +725,7 @@ pub mod types { pub type nlink_t = u64; pub type blksize_t = i64; pub type blkcnt_t = i64; + #[repr(C)] pub struct stat { pub st_dev: dev_t, pub st_ino: ino_t, @@ -723,11 +747,13 @@ pub mod types { pub __unused: [c_long, ..3], } + #[repr(C)] pub struct utimbuf { pub actime: time_t, pub modtime: time_t, } + #[repr(C)] pub struct pthread_attr_t { pub __size: [u64, ..7] } @@ -752,6 +778,7 @@ pub mod types { pub type pthread_t = uintptr_t; + #[repr(C)] pub struct glob_t { pub gl_pathc: size_t, pub __unused1: size_t, @@ -768,11 +795,13 @@ pub mod types { pub __unused8: *mut c_void, } + #[repr(C)] pub struct timeval { pub tv_sec: time_t, pub tv_usec: suseconds_t, } + #[repr(C)] pub struct timespec { pub tv_sec: time_t, pub tv_nsec: c_long, @@ -789,11 +818,13 @@ pub mod types { pub type sa_family_t = u8; pub type in_port_t = u16; pub type in_addr_t = u32; + #[repr(C)] pub struct sockaddr { pub sa_len: u8, pub sa_family: sa_family_t, pub sa_data: [u8, ..14], } + #[repr(C)] pub struct sockaddr_storage { pub ss_len: u8, pub ss_family: sa_family_t, @@ -801,6 +832,7 @@ pub mod types { pub __ss_align: i64, pub __ss_pad2: [u8, ..112], } + #[repr(C)] pub struct sockaddr_in { pub sin_len: u8, pub sin_family: sa_family_t, @@ -808,9 +840,11 @@ pub mod types { pub sin_addr: in_addr, pub sin_zero: [u8, ..8], } + #[repr(C)] pub struct in_addr { pub s_addr: in_addr_t, } + #[repr(C)] pub struct sockaddr_in6 { pub sin6_len: u8, pub sin6_family: sa_family_t, @@ -819,17 +853,21 @@ pub mod types { pub sin6_addr: in6_addr, pub sin6_scope_id: u32, } + #[repr(C)] pub struct in6_addr { pub s6_addr: [u16, ..8] } + #[repr(C)] pub struct ip_mreq { pub imr_multiaddr: in_addr, pub imr_interface: in_addr, } + #[repr(C)] pub struct ip6_mreq { pub ipv6mr_multiaddr: in6_addr, pub ipv6mr_interface: c_uint, } + #[repr(C)] pub struct addrinfo { pub ai_flags: c_int, pub ai_family: c_int, @@ -840,6 +878,7 @@ pub mod types { pub ai_addr: *mut sockaddr, pub ai_next: *mut addrinfo, } + #[repr(C)] pub struct sockaddr_un { pub sun_len: u8, pub sun_family: sa_family_t, @@ -872,8 +911,8 @@ pub mod types { pub mod c99 { pub type c_longlong = i64; pub type c_ulonglong = u64; - pub type intptr_t = int; - pub type uintptr_t = uint; + pub type intptr_t = i64; + pub type uintptr_t = u64; } pub mod posix88 { pub type off_t = i64; @@ -898,6 +937,7 @@ pub mod types { pub type blksize_t = i64; pub type blkcnt_t = i64; pub type fflags_t = u32; + #[repr(C)] pub struct stat { pub st_dev: dev_t, pub st_ino: ino_t, @@ -923,6 +963,7 @@ pub mod types { pub __unused: [uint8_t, ..2], } + #[repr(C)] pub struct utimbuf { pub actime: time_t, pub modtime: time_t, @@ -950,6 +991,7 @@ pub mod types { pub type pthread_t = uintptr_t; + #[repr(C)] pub struct glob_t { pub gl_pathc: size_t, pub __unused1: size_t, @@ -966,11 +1008,13 @@ pub mod types { pub __unused8: *mut c_void, } + #[repr(C)] pub struct timeval { pub tv_sec: time_t, pub tv_usec: suseconds_t, } + #[repr(C)] pub struct timespec { pub tv_sec: time_t, pub tv_nsec: c_long, @@ -987,11 +1031,13 @@ pub mod types { pub type sa_family_t = u8; pub type in_port_t = u16; pub type in_addr_t = u32; + #[repr(C)] pub struct sockaddr { pub sa_len: u8, pub sa_family: sa_family_t, pub sa_data: [u8, ..14], } + #[repr(C)] pub struct sockaddr_storage { pub ss_len: u8, pub ss_family: sa_family_t, @@ -999,6 +1045,7 @@ pub mod types { pub __ss_align: i64, pub __ss_pad2: [u8, ..112], } + #[repr(C)] pub struct sockaddr_in { pub sin_len: u8, pub sin_family: sa_family_t, @@ -1006,9 +1053,11 @@ pub mod types { pub sin_addr: in_addr, pub sin_zero: [u8, ..8], } + #[repr(C)] pub struct in_addr { pub s_addr: in_addr_t, } + #[repr(C)] pub struct sockaddr_in6 { pub sin6_len: u8, pub sin6_family: sa_family_t, @@ -1017,17 +1066,21 @@ pub mod types { pub sin6_addr: in6_addr, pub sin6_scope_id: u32, } + #[repr(C)] pub struct in6_addr { pub s6_addr: [u16, ..8] } + #[repr(C)] pub struct ip_mreq { pub imr_multiaddr: in_addr, pub imr_interface: in_addr, } + #[repr(C)] pub struct ip6_mreq { pub ipv6mr_multiaddr: in6_addr, pub ipv6mr_interface: c_uint, } + #[repr(C)] pub struct addrinfo { pub ai_flags: c_int, pub ai_family: c_int, @@ -1038,6 +1091,7 @@ pub mod types { pub ai_addr: *mut sockaddr, pub ai_next: *mut addrinfo, } + #[repr(C)] pub struct sockaddr_un { pub sun_len: u8, pub sun_family: sa_family_t, @@ -1070,8 +1124,8 @@ pub mod types { pub mod c99 { pub type c_longlong = i64; pub type c_ulonglong = u64; - pub type intptr_t = int; - pub type uintptr_t = uint; + pub type intptr_t = i64; + pub type uintptr_t = u64; } pub mod posix88 { pub type off_t = i64; @@ -1098,6 +1152,7 @@ pub mod types { pub type blkcnt_t = i64; pub type fflags_t = u32; + #[repr(C)] pub struct stat { pub st_ino: ino_t, pub st_nlink: nlink_t, @@ -1122,6 +1177,7 @@ pub mod types { pub st_qspare1: int64_t, pub st_qspare2: int64_t, } + #[repr(C)] pub struct utimbuf { pub actime: time_t, pub modtime: time_t, @@ -1139,7 +1195,6 @@ pub mod types { } #[cfg(target_os = "windows")] - #[cfg(stage0, target_os = "win32")] // NOTE: Remove after snapshot pub mod os { pub mod common { pub mod posix01 { @@ -1147,8 +1202,9 @@ pub mod types { use types::os::arch::extra::{int64, time64_t}; use types::os::arch::posix88::{dev_t, ino_t}; - // pub Note: this is the struct called stat64 in win32. Not stat, + // pub Note: this is the struct called stat64 in Windows. Not stat, // nor stati64. + #[repr(C)] pub struct stat { pub st_dev: dev_t, pub st_ino: ino_t, @@ -1163,17 +1219,20 @@ pub mod types { pub st_ctime: time64_t, } - // note that this is called utimbuf64 in win32 + // note that this is called utimbuf64 in Windows + #[repr(C)] pub struct utimbuf { pub actime: time64_t, pub modtime: time64_t, } + #[repr(C)] pub struct timeval { pub tv_sec: c_long, pub tv_usec: c_long, } + #[repr(C)] pub struct timespec { pub tv_sec: time_t, pub tv_nsec: c_long, @@ -1184,31 +1243,37 @@ pub mod types { pub mod bsd44 { use types::os::arch::c95::{c_char, c_int, c_uint, size_t}; + use types::os::arch::c99::uintptr_t; - pub type SOCKET = uint; + pub type SOCKET = uintptr_t; pub type socklen_t = c_int; pub type sa_family_t = u16; pub type in_port_t = u16; pub type in_addr_t = u32; + #[repr(C)] pub struct sockaddr { pub sa_family: sa_family_t, pub sa_data: [u8, ..14], } + #[repr(C)] pub struct sockaddr_storage { pub ss_family: sa_family_t, pub __ss_pad1: [u8, ..6], pub __ss_align: i64, pub __ss_pad2: [u8, ..112], } + #[repr(C)] pub struct sockaddr_in { pub sin_family: sa_family_t, pub sin_port: in_port_t, pub sin_addr: in_addr, pub sin_zero: [u8, ..8], } + #[repr(C)] pub struct in_addr { pub s_addr: in_addr_t, } + #[repr(C)] pub struct sockaddr_in6 { pub sin6_family: sa_family_t, pub sin6_port: in_port_t, @@ -1216,17 +1281,21 @@ pub mod types { pub sin6_addr: in6_addr, pub sin6_scope_id: u32, } + #[repr(C)] pub struct in6_addr { pub s6_addr: [u16, ..8] } + #[repr(C)] pub struct ip_mreq { pub imr_multiaddr: in_addr, pub imr_interface: in_addr, } + #[repr(C)] pub struct ip6_mreq { pub ipv6mr_multiaddr: in6_addr, pub ipv6mr_interface: c_uint, } + #[repr(C)] pub struct addrinfo { pub ai_flags: c_int, pub ai_family: c_int, @@ -1237,6 +1306,7 @@ pub mod types { pub ai_addr: *mut sockaddr, pub ai_next: *mut addrinfo, } + #[repr(C)] pub struct sockaddr_un { pub sun_family: sa_family_t, pub sun_path: [c_char, ..108] @@ -1286,8 +1356,16 @@ pub mod types { pub mod c99 { pub type c_longlong = i64; pub type c_ulonglong = u64; - pub type intptr_t = int; - pub type uintptr_t = uint; + + #[cfg(target_arch = "x86")] + pub type intptr_t = i32; + #[cfg(target_arch = "x86_64")] + pub type intptr_t = i64; + + #[cfg(target_arch = "x86")] + pub type uintptr_t = u32; + #[cfg(target_arch = "x86_64")] + pub type uintptr_t = u64; } pub mod posix88 { @@ -1319,7 +1397,7 @@ pub mod types { use types::os::arch::c95::{c_char, c_int, c_uint, size_t}; use types::os::arch::c95::{c_long, c_ulong}; use types::os::arch::c95::{wchar_t}; - use types::os::arch::c99::{c_ulonglong, c_longlong}; + use types::os::arch::c99::{c_ulonglong, c_longlong, uintptr_t}; pub type BOOL = c_int; pub type BYTE = u8; @@ -1353,6 +1431,7 @@ pub mod types { pub type LPWCH = *mut WCHAR; pub type LPCH = *mut CHAR; + #[repr(C)] pub struct SECURITY_ATTRIBUTES { pub nLength: DWORD, pub lpSecurityDescriptor: LPVOID, @@ -1376,6 +1455,7 @@ pub mod types { pub type time64_t = i64; pub type int64 = i64; + #[repr(C)] pub struct STARTUPINFO { pub cb: DWORD, pub lpReserved: LPWSTR, @@ -1398,6 +1478,7 @@ pub mod types { } pub type LPSTARTUPINFO = *mut STARTUPINFO; + #[repr(C)] pub struct PROCESS_INFORMATION { pub hProcess: HANDLE, pub hThread: HANDLE, @@ -1406,13 +1487,14 @@ pub mod types { } pub type LPPROCESS_INFORMATION = *mut PROCESS_INFORMATION; + #[repr(C)] pub struct SYSTEM_INFO { pub wProcessorArchitecture: WORD, pub wReserved: WORD, pub dwPageSize: DWORD, pub lpMinimumApplicationAddress: LPVOID, pub lpMaximumApplicationAddress: LPVOID, - pub dwActiveProcessorMask: uint, + pub dwActiveProcessorMask: uintptr_t, pub dwNumberOfProcessors: DWORD, pub dwProcessorType: DWORD, pub dwAllocationGranularity: DWORD, @@ -1421,6 +1503,7 @@ pub mod types { } pub type LPSYSTEM_INFO = *mut SYSTEM_INFO; + #[repr(C)] pub struct MEMORY_BASIC_INFORMATION { pub BaseAddress: LPVOID, pub AllocationBase: LPVOID, @@ -1432,6 +1515,7 @@ pub mod types { } pub type LPMEMORY_BASIC_INFORMATION = *mut MEMORY_BASIC_INFORMATION; + #[repr(C)] pub struct OVERLAPPED { pub Internal: *mut c_ulong, pub InternalHigh: *mut c_ulong, @@ -1442,6 +1526,7 @@ pub mod types { pub type LPOVERLAPPED = *mut OVERLAPPED; + #[repr(C)] pub struct FILETIME { pub dwLowDateTime: DWORD, pub dwHighDateTime: DWORD, @@ -1449,6 +1534,7 @@ pub mod types { pub type LPFILETIME = *mut FILETIME; + #[repr(C)] pub struct GUID { pub Data1: DWORD, pub Data2: WORD, @@ -1456,6 +1542,7 @@ pub mod types { pub Data4: [BYTE, ..8], } + #[repr(C)] pub struct WSAPROTOCOLCHAIN { pub ChainLen: c_int, pub ChainEntries: [DWORD, ..MAX_PROTOCOL_CHAIN as uint], @@ -1463,6 +1550,7 @@ pub mod types { pub type LPWSAPROTOCOLCHAIN = *mut WSAPROTOCOLCHAIN; + #[repr(C)] pub struct WSAPROTOCOL_INFO { pub dwServiceFlags1: DWORD, pub dwServiceFlags2: DWORD, @@ -1505,6 +1593,7 @@ pub mod types { pub type pthread_t = uintptr_t; + #[repr(C)] pub struct glob_t { pub gl_pathc: size_t, pub __unused1: c_int, @@ -1521,11 +1610,13 @@ pub mod types { pub __unused8: *mut c_void, } + #[repr(C)] pub struct timeval { pub tv_sec: time_t, pub tv_usec: suseconds_t, } + #[repr(C)] pub struct timespec { pub tv_sec: time_t, pub tv_nsec: c_long, @@ -1543,11 +1634,13 @@ pub mod types { pub type sa_family_t = u8; pub type in_port_t = u16; pub type in_addr_t = u32; + #[repr(C)] pub struct sockaddr { pub sa_len: u8, pub sa_family: sa_family_t, pub sa_data: [u8, ..14], } + #[repr(C)] pub struct sockaddr_storage { pub ss_len: u8, pub ss_family: sa_family_t, @@ -1555,6 +1648,7 @@ pub mod types { pub __ss_align: i64, pub __ss_pad2: [u8, ..112], } + #[repr(C)] pub struct sockaddr_in { pub sin_len: u8, pub sin_family: sa_family_t, @@ -1562,9 +1656,11 @@ pub mod types { pub sin_addr: in_addr, pub sin_zero: [u8, ..8], } + #[repr(C)] pub struct in_addr { pub s_addr: in_addr_t, } + #[repr(C)] pub struct sockaddr_in6 { pub sin6_len: u8, pub sin6_family: sa_family_t, @@ -1573,17 +1669,21 @@ pub mod types { pub sin6_addr: in6_addr, pub sin6_scope_id: u32, } + #[repr(C)] pub struct in6_addr { pub s6_addr: [u16, ..8] } + #[repr(C)] pub struct ip_mreq { pub imr_multiaddr: in_addr, pub imr_interface: in_addr, } + #[repr(C)] pub struct ip6_mreq { pub ipv6mr_multiaddr: in6_addr, pub ipv6mr_interface: c_uint, } + #[repr(C)] pub struct addrinfo { pub ai_flags: c_int, pub ai_family: c_int, @@ -1594,6 +1694,7 @@ pub mod types { pub ai_addr: *mut sockaddr, pub ai_next: *mut addrinfo, } + #[repr(C)] pub struct sockaddr_un { pub sun_len: u8, pub sun_family: sa_family_t, @@ -1627,8 +1728,8 @@ pub mod types { pub mod c99 { pub type c_longlong = i64; pub type c_ulonglong = u64; - pub type intptr_t = int; - pub type uintptr_t = uint; + pub type intptr_t = i32; + pub type uintptr_t = u32; } pub mod posix88 { pub type off_t = i64; @@ -1651,6 +1752,7 @@ pub mod types { pub type blksize_t = i64; pub type blkcnt_t = i32; + #[repr(C)] pub struct stat { pub st_dev: dev_t, pub st_mode: mode_t, @@ -1676,11 +1778,13 @@ pub mod types { pub st_qspare: [int64_t, ..2], } + #[repr(C)] pub struct utimbuf { pub actime: time_t, pub modtime: time_t, } + #[repr(C)] pub struct pthread_attr_t { pub __sig: c_long, pub __opaque: [c_char, ..36] @@ -1691,6 +1795,7 @@ pub mod types { pub mod bsd44 { } pub mod extra { + #[repr(C)] pub struct mach_timebase_info { pub numer: u32, pub denom: u32, @@ -1724,8 +1829,8 @@ pub mod types { pub mod c99 { pub type c_longlong = i64; pub type c_ulonglong = u64; - pub type intptr_t = int; - pub type uintptr_t = uint; + pub type intptr_t = i64; + pub type uintptr_t = u64; } pub mod posix88 { pub type off_t = i64; @@ -1749,6 +1854,7 @@ pub mod types { pub type blksize_t = i64; pub type blkcnt_t = i32; + #[repr(C)] pub struct stat { pub st_dev: dev_t, pub st_mode: mode_t, @@ -1774,11 +1880,13 @@ pub mod types { pub st_qspare: [int64_t, ..2], } + #[repr(C)] pub struct utimbuf { pub actime: time_t, pub modtime: time_t, } + #[repr(C)] pub struct pthread_attr_t { pub __sig: c_long, pub __opaque: [c_char, ..56] @@ -1789,6 +1897,7 @@ pub mod types { pub mod bsd44 { } pub mod extra { + #[repr(C)] pub struct mach_timebase_info { pub numer: u32, pub denom: u32, @@ -1805,7 +1914,6 @@ pub mod consts { // into this module. #[cfg(target_os = "windows")] - #[cfg(stage0, target_os = "win32")] // NOTE: Remove after snapshot pub mod os { pub mod c95 { use types::os::arch::c95::{c_int, c_uint}; @@ -3890,7 +3998,6 @@ pub mod funcs { // with the same POSIX functions and types as other platforms. #[cfg(target_os = "windows")] - #[cfg(stage0, target_os = "win32")] // NOTE: Remove after snapshot pub mod posix88 { pub mod stat_ { use types::os::common::posix01::{stat, utimbuf}; @@ -4300,7 +4407,7 @@ pub mod funcs { pub fn glob(pattern: *const c_char, flags: c_int, errfunc: ::Nullable<extern "C" fn(epath: *const c_char, - errno: int) -> int>, + errno: c_int) -> int>, pglob: *mut glob_t); pub fn globfree(pglob: *mut glob_t); } @@ -4320,7 +4427,6 @@ pub mod funcs { } #[cfg(target_os = "windows")] - #[cfg(stage0, target_os = "win32")] // NOTE: Remove after snapshot pub mod posix01 { pub mod stat_ { } @@ -4337,7 +4443,6 @@ pub mod funcs { #[cfg(target_os = "windows")] - #[cfg(stage0, target_os = "win32")] // NOTE: Remove after snapshot #[cfg(target_os = "linux")] #[cfg(target_os = "android")] #[cfg(target_os = "macos")] @@ -4476,7 +4581,6 @@ pub mod funcs { #[cfg(target_os = "windows")] - #[cfg(stage0, target_os = "win32")] // NOTE: Remove after snapshot pub mod bsd44 { } @@ -4503,7 +4607,6 @@ pub mod funcs { #[cfg(target_os = "windows")] - #[cfg(stage0, target_os = "win32")] // NOTE: Remove after snapshot pub mod extra { pub mod kernel32 { diff --git a/src/liblog/directive.rs b/src/liblog/directive.rs index d692c99e8c2..a93cf4c1568 100644 --- a/src/liblog/directive.rs +++ b/src/liblog/directive.rs @@ -8,6 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use regex::Regex; use std::ascii::AsciiExt; use std::cmp; @@ -28,14 +29,23 @@ fn parse_log_level(level: &str) -> Option<u32> { }).map(|p| cmp::min(p, ::MAX_LOG_LEVEL)) } -/// Parse a logging specification string (e.g: "crate1,crate2::mod3,crate3::x=1") +/// Parse a logging specification string (e.g: "crate1,crate2::mod3,crate3::x=1/foo") /// and return a vector with log directives. /// /// Valid log levels are 0-255, with the most likely ones being 1-4 (defined in /// std::). Also supports string log levels of error, warn, info, and debug -pub fn parse_logging_spec(spec: &str) -> Vec<LogDirective> { +pub fn parse_logging_spec(spec: &str) -> (Vec<LogDirective>, Option<Regex>) { let mut dirs = Vec::new(); - for s in spec.split(',') { + + let mut parts = spec.split('/'); + let mods = parts.next(); + let filter = parts.next(); + if parts.next().is_some() { + println!("warning: invalid logging spec '{}', \ + ignoring it (too many '/'s)", spec); + return (dirs, None); + } + mods.map(|m| { for s in m.split(',') { if s.len() == 0 { continue } let mut parts = s.split('='); let (log_level, name) = match (parts.next(), parts.next().map(|s| s.trim()), parts.next()) { @@ -68,8 +78,19 @@ pub fn parse_logging_spec(spec: &str) -> Vec<LogDirective> { name: name.map(|s| s.to_string()), level: log_level, }); - } - return dirs; + }}); + + let filter = filter.map_or(None, |filter| { + match Regex::new(filter) { + Ok(re) => Some(re), + Err(e) => { + println!("warning: invalid regex filter - {}", e); + None + } + } + }); + + return (dirs, filter); } #[cfg(test)] @@ -78,7 +99,7 @@ mod tests { #[test] fn parse_logging_spec_valid() { - let dirs = parse_logging_spec("crate1::mod1=1,crate1::mod2,crate2=4"); + let (dirs, filter) = parse_logging_spec("crate1::mod1=1,crate1::mod2,crate2=4"); let dirs = dirs.as_slice(); assert_eq!(dirs.len(), 3); assert_eq!(dirs[0].name, Some("crate1::mod1".to_string())); @@ -89,57 +110,99 @@ mod tests { assert_eq!(dirs[2].name, Some("crate2".to_string())); assert_eq!(dirs[2].level, 4); + assert!(filter.is_none()); } #[test] fn parse_logging_spec_invalid_crate() { // test parse_logging_spec with multiple = in specification - let dirs = parse_logging_spec("crate1::mod1=1=2,crate2=4"); + let (dirs, filter) = parse_logging_spec("crate1::mod1=1=2,crate2=4"); let dirs = dirs.as_slice(); assert_eq!(dirs.len(), 1); assert_eq!(dirs[0].name, Some("crate2".to_string())); assert_eq!(dirs[0].level, 4); + assert!(filter.is_none()); } #[test] fn parse_logging_spec_invalid_log_level() { // test parse_logging_spec with 'noNumber' as log level - let dirs = parse_logging_spec("crate1::mod1=noNumber,crate2=4"); + let (dirs, filter) = parse_logging_spec("crate1::mod1=noNumber,crate2=4"); let dirs = dirs.as_slice(); assert_eq!(dirs.len(), 1); assert_eq!(dirs[0].name, Some("crate2".to_string())); assert_eq!(dirs[0].level, 4); + assert!(filter.is_none()); } #[test] fn parse_logging_spec_string_log_level() { // test parse_logging_spec with 'warn' as log level - let dirs = parse_logging_spec("crate1::mod1=wrong,crate2=warn"); + let (dirs, filter) = parse_logging_spec("crate1::mod1=wrong,crate2=warn"); let dirs = dirs.as_slice(); assert_eq!(dirs.len(), 1); assert_eq!(dirs[0].name, Some("crate2".to_string())); assert_eq!(dirs[0].level, ::WARN); + assert!(filter.is_none()); } #[test] fn parse_logging_spec_empty_log_level() { // test parse_logging_spec with '' as log level - let dirs = parse_logging_spec("crate1::mod1=wrong,crate2="); + let (dirs, filter) = parse_logging_spec("crate1::mod1=wrong,crate2="); let dirs = dirs.as_slice(); assert_eq!(dirs.len(), 1); assert_eq!(dirs[0].name, Some("crate2".to_string())); assert_eq!(dirs[0].level, ::MAX_LOG_LEVEL); + assert!(filter.is_none()); } #[test] fn parse_logging_spec_global() { // test parse_logging_spec with no crate - let dirs = parse_logging_spec("warn,crate2=4"); + let (dirs, filter) = parse_logging_spec("warn,crate2=4"); let dirs = dirs.as_slice(); assert_eq!(dirs.len(), 2); assert_eq!(dirs[0].name, None); assert_eq!(dirs[0].level, 2); assert_eq!(dirs[1].name, Some("crate2".to_string())); assert_eq!(dirs[1].level, 4); + assert!(filter.is_none()); + } + + #[test] + fn parse_logging_spec_valid_filter() { + let (dirs, filter) = parse_logging_spec("crate1::mod1=1,crate1::mod2,crate2=4/abc"); + let dirs = dirs.as_slice(); + assert_eq!(dirs.len(), 3); + assert_eq!(dirs[0].name, Some("crate1::mod1".to_string())); + assert_eq!(dirs[0].level, 1); + + assert_eq!(dirs[1].name, Some("crate1::mod2".to_string())); + assert_eq!(dirs[1].level, ::MAX_LOG_LEVEL); + + assert_eq!(dirs[2].name, Some("crate2".to_string())); + assert_eq!(dirs[2].level, 4); + assert!(filter.is_some() && filter.unwrap().to_string().as_slice() == "abc"); + } + + #[test] + fn parse_logging_spec_invalid_crate_filter() { + let (dirs, filter) = parse_logging_spec("crate1::mod1=1=2,crate2=4/a.c"); + let dirs = dirs.as_slice(); + assert_eq!(dirs.len(), 1); + assert_eq!(dirs[0].name, Some("crate2".to_string())); + assert_eq!(dirs[0].level, 4); + assert!(filter.is_some() && filter.unwrap().to_string().as_slice() == "a.c"); + } + + #[test] + fn parse_logging_spec_empty_with_filter() { + let (dirs, filter) = parse_logging_spec("crate1/a*c"); + let dirs = dirs.as_slice(); + assert_eq!(dirs.len(), 1); + assert_eq!(dirs[0].name, Some("crate1".to_string())); + assert_eq!(dirs[0].level, ::MAX_LOG_LEVEL); + assert!(filter.is_some() && filter.unwrap().to_string().as_slice() == "a*c"); } } diff --git a/src/liblog/lib.rs b/src/liblog/lib.rs index 554f27b881b..0bad742933b 100644 --- a/src/liblog/lib.rs +++ b/src/liblog/lib.rs @@ -80,14 +80,32 @@ all modules is set to this value. Some examples of valid values of `RUST_LOG` are: -```text -hello // turns on all logging for the 'hello' module -info // turns on all info logging -hello=debug // turns on debug logging for 'hello' -hello=3 // turns on info logging for 'hello' -hello,std::option // turns on hello, and std's option logging -error,hello=warn // turn on global error logging and also warn for hello -``` +* `hello` turns on all logging for the 'hello' module +* `info` turns on all info logging +* `hello=debug` turns on debug logging for 'hello' +* `hello=3` turns on info logging for 'hello' +* `hello,std::option` turns on hello, and std's option logging +* `error,hello=warn` turn on global error logging and also warn for hello + +## Filtering results + +A RUST_LOG directive may include a regex filter. The syntax is to append `/` +followed by a regex. Each message is checked against the regex, and is only +logged if it matches. Note that the matching is done after formatting the log +string but before adding any logging meta-data. There is a single filter for all +modules. + +Some examples: + +* `hello/foo` turns on all logging for the 'hello' module where the log message +includes 'foo'. +* `info/f.o` turns on all info logging where the log message includes 'foo', +'f1o', 'fao', etc. +* `hello=debug/foo*foo` turns on debug logging for 'hello' where the the log +message includes 'foofoo' or 'fofoo' or 'fooooooofoo', etc. +* `error,hello=warn/[0-9] scopes` turn on global error logging and also warn for + hello. In both cases the log message must include a single digit number + followed by 'scopes' ## Performance and Side Effects @@ -117,6 +135,9 @@ if logging is disabled, none of the components of the log will be executed. #![feature(macro_rules)] #![deny(missing_doc)] +extern crate regex; + +use regex::Regex; use std::fmt; use std::io::LineBufferedWriter; use std::io; @@ -146,6 +167,9 @@ static mut LOG_LEVEL: u32 = MAX_LOG_LEVEL; static mut DIRECTIVES: *const Vec<directive::LogDirective> = 0 as *const Vec<directive::LogDirective>; +/// Optional regex filter. +static mut FILTER: *const Regex = 0 as *const _; + /// Debug log level pub static DEBUG: u32 = 4; /// Info log level @@ -222,6 +246,13 @@ impl Drop for DefaultLogger { /// invoked through the logging family of macros. #[doc(hidden)] pub fn log(level: u32, loc: &'static LogLocation, args: &fmt::Arguments) { + // Test the literal string from args against the current filter, if there + // is one. + match unsafe { FILTER.to_option() } { + Some(filter) if filter.is_match(args.to_string().as_slice()) => return, + _ => {} + } + // Completely remove the local logger from TLS in case anyone attempts to // frob the slot while we're doing the logging. This will destroy any logger // set during logging. @@ -321,9 +352,9 @@ fn enabled(level: u32, /// This is not threadsafe at all, so initialization os performed through a /// `Once` primitive (and this function is called from that primitive). fn init() { - let mut directives = match os::getenv("RUST_LOG") { + let (mut directives, filter) = match os::getenv("RUST_LOG") { Some(spec) => directive::parse_logging_spec(spec.as_slice()), - None => Vec::new(), + None => (Vec::new(), None), }; // Sort the provided directives by length of their name, this allows a @@ -342,15 +373,26 @@ fn init() { unsafe { LOG_LEVEL = max_level; + assert!(FILTER.is_null()); + match filter { + Some(f) => FILTER = mem::transmute(box f), + None => {} + } + assert!(DIRECTIVES.is_null()); DIRECTIVES = mem::transmute(box directives); - // Schedule the cleanup for this global for when the runtime exits. + // Schedule the cleanup for the globals for when the runtime exits. rt::at_exit(proc() { assert!(!DIRECTIVES.is_null()); let _directives: Box<Vec<directive::LogDirective>> = mem::transmute(DIRECTIVES); DIRECTIVES = 0 as *const Vec<directive::LogDirective>; + + if !FILTER.is_null() { + let _filter: Box<Regex> = mem::transmute(FILTER); + FILTER = 0 as *const _; + } }); } } diff --git a/src/libnative/io/c_unix.rs b/src/libnative/io/c_unix.rs index af4d309dfe2..fa7da1de914 100644 --- a/src/libnative/io/c_unix.rs +++ b/src/libnative/io/c_unix.rs @@ -91,6 +91,7 @@ extern { mod select { pub static FD_SETSIZE: uint = 1024; + #[repr(C)] pub struct fd_set { fds_bits: [i32, ..(FD_SETSIZE / 32)] } @@ -106,12 +107,14 @@ mod select { #[cfg(target_os = "linux")] mod select { use std::uint; + use libc; pub static FD_SETSIZE: uint = 1024; + #[repr(C)] pub struct fd_set { // FIXME: shouldn't this be a c_ulong? - fds_bits: [uint, ..(FD_SETSIZE / uint::BITS)] + fds_bits: [libc::uintptr_t, ..(FD_SETSIZE / uint::BITS)] } pub fn fd_set(set: &mut fd_set, fd: i32) { @@ -139,6 +142,7 @@ mod signal { // This definition is not as accurate as it could be, {pid, uid, status} is // actually a giant union. Currently we're only interested in these fields, // however. + #[repr(C)] pub struct siginfo { si_signo: libc::c_int, si_errno: libc::c_int, @@ -148,6 +152,7 @@ mod signal { pub status: libc::c_int, } + #[repr(C)] pub struct sigaction { pub sa_handler: extern fn(libc::c_int), pub sa_mask: sigset_t, @@ -155,10 +160,13 @@ mod signal { sa_restorer: *mut libc::c_void, } + #[repr(C)] #[cfg(target_word_size = "32")] pub struct sigset_t { __val: [libc::c_ulong, ..32], } + + #[repr(C)] #[cfg(target_word_size = "64")] pub struct sigset_t { __val: [libc::c_ulong, ..16], @@ -182,6 +190,7 @@ mod signal { // This definition is not as accurate as it could be, {pid, uid, status} is // actually a giant union. Currently we're only interested in these fields, // however. + #[repr(C)] pub struct siginfo { si_signo: libc::c_int, si_code: libc::c_int, @@ -191,6 +200,7 @@ mod signal { pub status: libc::c_int, } + #[repr(C)] pub struct sigaction { pub sa_flags: libc::c_uint, pub sa_handler: extern fn(libc::c_int), @@ -199,6 +209,7 @@ mod signal { sa_resv: [libc::c_int, ..1], } + #[repr(C)] pub struct sigset_t { __val: [libc::c_ulong, ..32], } @@ -225,12 +236,14 @@ mod signal { pub type sigset_t = u32; #[cfg(target_os = "freebsd")] #[cfg(target_os = "dragonfly")] + #[repr(C)] pub struct sigset_t { bits: [u32, ..4], } // This structure has more fields, but we're not all that interested in // them. + #[repr(C)] pub struct siginfo { pub si_signo: libc::c_int, pub si_errno: libc::c_int, @@ -242,6 +255,7 @@ mod signal { #[cfg(target_os = "macos")] #[cfg(target_os = "ios")] + #[repr(C)] pub struct sigaction { pub sa_handler: extern fn(libc::c_int), sa_tramp: *mut libc::c_void, @@ -251,6 +265,7 @@ mod signal { #[cfg(target_os = "freebsd")] #[cfg(target_os = "dragonfly")] + #[repr(C)] pub struct sigaction { pub sa_handler: extern fn(libc::c_int), pub sa_flags: libc::c_int, diff --git a/src/libnative/io/c_win32.rs b/src/libnative/io/c_windows.rs index 80c9e91b48f..909b37895b7 100644 --- a/src/libnative/io/c_win32.rs +++ b/src/libnative/io/c_windows.rs @@ -26,6 +26,14 @@ pub static ENABLE_INSERT_MODE: libc::DWORD = 0x20; pub static ENABLE_LINE_INPUT: libc::DWORD = 0x2; pub static ENABLE_PROCESSED_INPUT: libc::DWORD = 0x1; pub static ENABLE_QUICK_EDIT_MODE: libc::DWORD = 0x40; +pub static WSA_INVALID_EVENT: WSAEVENT = 0 as WSAEVENT; + +pub static FD_ACCEPT: libc::c_long = 0x08; +pub static FD_MAX_EVENTS: uint = 10; +pub static WSA_INFINITE: libc::DWORD = libc::INFINITE; +pub static WSA_WAIT_TIMEOUT: libc::DWORD = libc::consts::os::extra::WAIT_TIMEOUT; +pub static WSA_WAIT_EVENT_0: libc::DWORD = libc::consts::os::extra::WAIT_OBJECT_0; +pub static WSA_WAIT_FAILED: libc::DWORD = libc::consts::os::extra::WAIT_FAILED; #[repr(C)] #[cfg(target_arch = "x86")] @@ -53,6 +61,16 @@ pub struct WSADATA { pub type LPWSADATA = *mut WSADATA; #[repr(C)] +pub struct WSANETWORKEVENTS { + pub lNetworkEvents: libc::c_long, + pub iErrorCode: [libc::c_int, ..FD_MAX_EVENTS], +} + +pub type LPWSANETWORKEVENTS = *mut WSANETWORKEVENTS; + +pub type WSAEVENT = libc::HANDLE; + +#[repr(C)] pub struct fd_set { fd_count: libc::c_uint, fd_array: [libc::SOCKET, ..FD_SETSIZE], @@ -68,6 +86,21 @@ extern "system" { pub fn WSAStartup(wVersionRequested: libc::WORD, lpWSAData: LPWSADATA) -> libc::c_int; pub fn WSAGetLastError() -> libc::c_int; + pub fn WSACloseEvent(hEvent: WSAEVENT) -> libc::BOOL; + pub fn WSACreateEvent() -> WSAEVENT; + pub fn WSAEventSelect(s: libc::SOCKET, + hEventObject: WSAEVENT, + lNetworkEvents: libc::c_long) -> libc::c_int; + pub fn WSASetEvent(hEvent: WSAEVENT) -> libc::BOOL; + pub fn WSAWaitForMultipleEvents(cEvents: libc::DWORD, + lphEvents: *const WSAEVENT, + fWaitAll: libc::BOOL, + dwTimeout: libc::DWORD, + fAltertable: libc::BOOL) -> libc::DWORD; + pub fn WSAEnumNetworkEvents(s: libc::SOCKET, + hEventObject: WSAEVENT, + lpNetworkEvents: LPWSANETWORKEVENTS) + -> libc::c_int; pub fn ioctlsocket(s: libc::SOCKET, cmd: libc::c_long, argp: *mut libc::c_ulong) -> libc::c_int; @@ -82,6 +115,12 @@ extern "system" { optval: *mut libc::c_char, optlen: *mut libc::c_int) -> libc::c_int; + pub fn SetEvent(hEvent: libc::HANDLE) -> libc::BOOL; + pub fn WaitForMultipleObjects(nCount: libc::DWORD, + lpHandles: *const libc::HANDLE, + bWaitAll: libc::BOOL, + dwMilliseconds: libc::DWORD) -> libc::DWORD; + pub fn CancelIo(hFile: libc::HANDLE) -> libc::BOOL; pub fn CancelIoEx(hFile: libc::HANDLE, lpOverlapped: libc::LPOVERLAPPED) -> libc::BOOL; diff --git a/src/libnative/io/file_win32.rs b/src/libnative/io/file_windows.rs index fe29c024529..cb1d79b8397 100644 --- a/src/libnative/io/file_win32.rs +++ b/src/libnative/io/file_windows.rs @@ -8,14 +8,14 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -//! Blocking win32-based file I/O +//! Blocking Windows-based file I/O use alloc::arc::Arc; use libc::{c_int, c_void}; use libc; use std::c_str::CString; use std::mem; -use std::os::win32::fill_utf16_buf_and_decode; +use std::os::windows::fill_utf16_buf_and_decode; use std::ptr; use std::rt::rtio; use std::rt::rtio::{IoResult, IoError}; diff --git a/src/libnative/io/mod.rs b/src/libnative/io/mod.rs index 5d661007329..2dc6539b178 100644 --- a/src/libnative/io/mod.rs +++ b/src/libnative/io/mod.rs @@ -46,7 +46,7 @@ mod util; #[path = "file_unix.rs"] pub mod file; #[cfg(windows)] -#[path = "file_win32.rs"] +#[path = "file_windows.rs"] pub mod file; #[cfg(target_os = "macos")] @@ -59,8 +59,7 @@ pub mod file; pub mod timer; #[cfg(target_os = "windows")] -#[cfg(stage0, target_os = "win32")] // NOTE: Remove after snapshot -#[path = "timer_win32.rs"] +#[path = "timer_windows.rs"] pub mod timer; #[cfg(unix)] @@ -68,15 +67,15 @@ pub mod timer; pub mod pipe; #[cfg(windows)] -#[path = "pipe_win32.rs"] +#[path = "pipe_windows.rs"] pub mod pipe; #[cfg(windows)] -#[path = "tty_win32.rs"] +#[path = "tty_windows.rs"] mod tty; #[cfg(unix)] #[path = "c_unix.rs"] mod c; -#[cfg(windows)] #[path = "c_win32.rs"] mod c; +#[cfg(windows)] #[path = "c_windows.rs"] mod c; fn unimpl() -> IoError { #[cfg(unix)] use libc::ENOSYS as ERROR; diff --git a/src/libnative/io/net.rs b/src/libnative/io/net.rs index 2255578ba80..368b5914444 100644 --- a/src/libnative/io/net.rs +++ b/src/libnative/io/net.rs @@ -11,21 +11,25 @@ use alloc::arc::Arc; use libc; use std::mem; +use std::ptr; use std::rt::mutex; use std::rt::rtio; use std::rt::rtio::{IoResult, IoError}; +use std::sync::atomic; use super::{retry, keep_going}; use super::c; use super::util; +#[cfg(unix)] use super::process; +#[cfg(unix)] use super::file::FileDesc; + +pub use self::os::{init, sock_t, last_error}; + //////////////////////////////////////////////////////////////////////////////// // sockaddr and misc bindings //////////////////////////////////////////////////////////////////////////////// -#[cfg(windows)] pub type sock_t = libc::SOCKET; -#[cfg(unix)] pub type sock_t = super::file::fd_t; - pub fn htons(u: u16) -> u16 { u.to_be() } @@ -97,7 +101,7 @@ fn socket(addr: rtio::SocketAddr, ty: libc::c_int) -> IoResult<sock_t> { rtio::Ipv6Addr(..) => libc::AF_INET6, }; match libc::socket(fam, ty, 0) { - -1 => Err(super::last_error()), + -1 => Err(os::last_error()), fd => Ok(fd), } } @@ -111,7 +115,7 @@ fn setsockopt<T>(fd: sock_t, opt: libc::c_int, val: libc::c_int, payload, mem::size_of::<T>() as libc::socklen_t); if ret != 0 { - Err(last_error()) + Err(os::last_error()) } else { Ok(()) } @@ -127,7 +131,7 @@ pub fn getsockopt<T: Copy>(fd: sock_t, opt: libc::c_int, &mut slot as *mut _ as *mut _, &mut len); if ret != 0 { - Err(last_error()) + Err(os::last_error()) } else { assert!(len as uint == mem::size_of::<T>()); Ok(slot) @@ -135,25 +139,6 @@ pub fn getsockopt<T: Copy>(fd: sock_t, opt: libc::c_int, } } -#[cfg(windows)] -pub fn last_error() -> IoError { - use std::os; - let code = unsafe { c::WSAGetLastError() as uint }; - IoError { - code: code, - extra: 0, - detail: Some(os::error_string(code)), - } -} - -#[cfg(not(windows))] -fn last_error() -> IoError { - super::last_error() -} - -#[cfg(windows)] unsafe fn close(sock: sock_t) { let _ = libc::closesocket(sock); } -#[cfg(unix)] unsafe fn close(sock: sock_t) { let _ = libc::close(sock); } - fn sockname(fd: sock_t, f: unsafe extern "system" fn(sock_t, *mut libc::sockaddr, *mut libc::socklen_t) -> libc::c_int) @@ -167,7 +152,7 @@ fn sockname(fd: sock_t, storage as *mut libc::sockaddr, &mut len as *mut libc::socklen_t); if ret != 0 { - return Err(last_error()) + return Err(os::last_error()) } } return sockaddr_to_addr(&storage, len as uint); @@ -221,28 +206,6 @@ pub fn sockaddr_to_addr(storage: &libc::sockaddr_storage, } } -#[cfg(unix)] -pub fn init() {} - -#[cfg(windows)] -pub fn init() { - - unsafe { - use std::rt::mutex::{StaticNativeMutex, NATIVE_MUTEX_INIT}; - static mut INITIALIZED: bool = false; - static mut LOCK: StaticNativeMutex = NATIVE_MUTEX_INIT; - - let _guard = LOCK.lock(); - if !INITIALIZED { - let mut data: c::WSADATA = mem::zeroed(); - let ret = c::WSAStartup(0x202, // version 2.2 - &mut data); - assert_eq!(ret, 0); - INITIALIZED = true; - } - } -} - //////////////////////////////////////////////////////////////////////////////// // TCP streams //////////////////////////////////////////////////////////////////////////////// @@ -289,7 +252,7 @@ impl TcpStream { }, None => { match retry(|| unsafe { libc::connect(fd, addrp, len) }) { - -1 => Err(last_error()), + -1 => Err(os::last_error()), _ => Ok(ret), } } @@ -435,7 +398,7 @@ impl rtio::RtioSocket for TcpStream { } impl Drop for Inner { - fn drop(&mut self) { unsafe { close(self.fd); } } + fn drop(&mut self) { unsafe { os::close(self.fd); } } } #[unsafe_destructor] @@ -471,7 +434,7 @@ impl TcpListener { } match unsafe { libc::bind(fd, addrp, len) } { - -1 => Err(last_error()), + -1 => Err(os::last_error()), _ => Ok(ret), } } @@ -480,8 +443,44 @@ impl TcpListener { pub fn native_listen(self, backlog: int) -> IoResult<TcpAcceptor> { match unsafe { libc::listen(self.fd(), backlog as libc::c_int) } { - -1 => Err(last_error()), - _ => Ok(TcpAcceptor { listener: self, deadline: 0 }) + -1 => Err(os::last_error()), + + #[cfg(unix)] + _ => { + let (reader, writer) = try!(process::pipe()); + try!(util::set_nonblocking(reader.fd(), true)); + try!(util::set_nonblocking(writer.fd(), true)); + try!(util::set_nonblocking(self.fd(), true)); + Ok(TcpAcceptor { + inner: Arc::new(AcceptorInner { + listener: self, + reader: reader, + writer: writer, + closed: atomic::AtomicBool::new(false), + }), + deadline: 0, + }) + } + + #[cfg(windows)] + _ => { + let accept = try!(os::Event::new()); + let ret = unsafe { + c::WSAEventSelect(self.fd(), accept.handle(), c::FD_ACCEPT) + }; + if ret != 0 { + return Err(os::last_error()) + } + Ok(TcpAcceptor { + inner: Arc::new(AcceptorInner { + listener: self, + abort: try!(os::Event::new()), + accept: accept, + closed: atomic::AtomicBool::new(false), + }), + deadline: 0, + }) + } } } } @@ -502,31 +501,135 @@ impl rtio::RtioSocket for TcpListener { } pub struct TcpAcceptor { - listener: TcpListener, + inner: Arc<AcceptorInner>, deadline: u64, } +#[cfg(unix)] +struct AcceptorInner { + listener: TcpListener, + reader: FileDesc, + writer: FileDesc, + closed: atomic::AtomicBool, +} + +#[cfg(windows)] +struct AcceptorInner { + listener: TcpListener, + abort: os::Event, + accept: os::Event, + closed: atomic::AtomicBool, +} + impl TcpAcceptor { - pub fn fd(&self) -> sock_t { self.listener.fd() } + pub fn fd(&self) -> sock_t { self.inner.listener.fd() } + #[cfg(unix)] pub fn native_accept(&mut self) -> IoResult<TcpStream> { - if self.deadline != 0 { - try!(util::await(self.fd(), Some(self.deadline), util::Readable)); + // In implementing accept, the two main concerns are dealing with + // close_accept() and timeouts. The unix implementation is based on a + // nonblocking accept plus a call to select(). Windows ends up having + // an entirely separate implementation than unix, which is explained + // below. + // + // To implement timeouts, all blocking is done via select() instead of + // accept() by putting the socket in non-blocking mode. Because + // select() takes a timeout argument, we just pass through the timeout + // to select(). + // + // To implement close_accept(), we have a self-pipe to ourselves which + // is passed to select() along with the socket being accepted on. The + // self-pipe is never written to unless close_accept() is called. + let deadline = if self.deadline == 0 {None} else {Some(self.deadline)}; + + while !self.inner.closed.load(atomic::SeqCst) { + match retry(|| unsafe { + libc::accept(self.fd(), ptr::mut_null(), ptr::mut_null()) + }) { + -1 if util::wouldblock() => {} + -1 => return Err(os::last_error()), + fd => return Ok(TcpStream::new(Inner::new(fd as sock_t))), + } + try!(util::await([self.fd(), self.inner.reader.fd()], + deadline, util::Readable)); } - unsafe { - let mut storage: libc::sockaddr_storage = mem::zeroed(); - let storagep = &mut storage as *mut libc::sockaddr_storage; - let size = mem::size_of::<libc::sockaddr_storage>(); - let mut size = size as libc::socklen_t; - match retry(|| { - libc::accept(self.fd(), - storagep as *mut libc::sockaddr, - &mut size as *mut libc::socklen_t) as libc::c_int - }) as sock_t { - -1 => Err(last_error()), - fd => Ok(TcpStream::new(Inner::new(fd))), + + Err(util::eof()) + } + + #[cfg(windows)] + pub fn native_accept(&mut self) -> IoResult<TcpStream> { + // Unlink unix, windows cannot invoke `select` on arbitrary file + // descriptors like pipes, only sockets. Consequently, windows cannot + // use the same implementation as unix for accept() when close_accept() + // is considered. + // + // In order to implement close_accept() and timeouts, windows uses + // event handles. An acceptor-specific abort event is created which + // will only get set in close_accept(), and it will never be un-set. + // Additionally, another acceptor-specific event is associated with the + // FD_ACCEPT network event. + // + // These two events are then passed to WaitForMultipleEvents to see + // which one triggers first, and the timeout passed to this function is + // the local timeout for the acceptor. + // + // If the wait times out, then the accept timed out. If the wait + // succeeds with the abort event, then we were closed, and if the wait + // succeeds otherwise, then we do a nonblocking poll via `accept` to + // see if we can accept a connection. The connection is candidate to be + // stolen, so we do all of this in a loop as well. + let events = [self.inner.abort.handle(), self.inner.accept.handle()]; + + while !self.inner.closed.load(atomic::SeqCst) { + let ms = if self.deadline == 0 { + c::WSA_INFINITE as u64 + } else { + let now = ::io::timer::now(); + if self.deadline < now {0} else {self.deadline - now} + }; + let ret = unsafe { + c::WSAWaitForMultipleEvents(2, events.as_ptr(), libc::FALSE, + ms as libc::DWORD, libc::FALSE) + }; + match ret { + c::WSA_WAIT_TIMEOUT => { + return Err(util::timeout("accept timed out")) + } + c::WSA_WAIT_FAILED => return Err(os::last_error()), + c::WSA_WAIT_EVENT_0 => break, + n => assert_eq!(n, c::WSA_WAIT_EVENT_0 + 1), + } + + let mut wsaevents: c::WSANETWORKEVENTS = unsafe { mem::zeroed() }; + let ret = unsafe { + c::WSAEnumNetworkEvents(self.fd(), events[1], &mut wsaevents) + }; + if ret != 0 { return Err(os::last_error()) } + + if wsaevents.lNetworkEvents & c::FD_ACCEPT == 0 { continue } + match unsafe { + libc::accept(self.fd(), ptr::mut_null(), ptr::mut_null()) + } { + -1 if util::wouldblock() => {} + -1 => return Err(os::last_error()), + + // Accepted sockets inherit the same properties as the caller, + // so we need to deregister our event and switch the socket back + // to blocking mode + fd => { + let stream = TcpStream::new(Inner::new(fd)); + let ret = unsafe { + c::WSAEventSelect(fd, events[1], 0) + }; + if ret != 0 { return Err(os::last_error()) } + try!(util::set_nonblocking(fd, false)); + return Ok(stream) + } } } + + Err(util::eof()) } } @@ -546,6 +649,35 @@ impl rtio::RtioTcpAcceptor for TcpAcceptor { fn set_timeout(&mut self, timeout: Option<u64>) { self.deadline = timeout.map(|a| ::io::timer::now() + a).unwrap_or(0); } + + fn clone(&self) -> Box<rtio::RtioTcpAcceptor + Send> { + box TcpAcceptor { + inner: self.inner.clone(), + deadline: 0, + } as Box<rtio::RtioTcpAcceptor + Send> + } + + #[cfg(unix)] + fn close_accept(&mut self) -> IoResult<()> { + self.inner.closed.store(true, atomic::SeqCst); + let mut fd = FileDesc::new(self.inner.writer.fd(), false); + match fd.inner_write([0]) { + Ok(..) => Ok(()), + Err(..) if util::wouldblock() => Ok(()), + Err(e) => Err(e), + } + } + + #[cfg(windows)] + fn close_accept(&mut self) -> IoResult<()> { + self.inner.closed.store(true, atomic::SeqCst); + let ret = unsafe { c::WSASetEvent(self.inner.abort.handle()) }; + if ret == libc::TRUE { + Ok(()) + } else { + Err(os::last_error()) + } + } } //////////////////////////////////////////////////////////////////////////////// @@ -572,7 +704,7 @@ impl UdpSocket { let addrp = &storage as *const _ as *const libc::sockaddr; match unsafe { libc::bind(fd, addrp, len) } { - -1 => Err(last_error()), + -1 => Err(os::last_error()), _ => Ok(ret), } } @@ -817,7 +949,7 @@ pub fn read<T>(fd: sock_t, // With a timeout, first we wait for the socket to become // readable using select(), specifying the relevant timeout for // our previously set deadline. - try!(util::await(fd, deadline, util::Readable)); + try!(util::await([fd], deadline, util::Readable)); // At this point, we're still within the timeout, and we've // determined that the socket is readable (as returned by @@ -828,7 +960,7 @@ pub fn read<T>(fd: sock_t, let _guard = lock(); match retry(|| read(deadline.is_some())) { -1 if util::wouldblock() => { assert!(deadline.is_some()); } - -1 => return Err(last_error()), + -1 => return Err(os::last_error()), n => { ret = n; break } } } @@ -836,7 +968,7 @@ pub fn read<T>(fd: sock_t, match ret { 0 => Err(util::eof()), - n if n < 0 => Err(last_error()), + n if n < 0 => Err(os::last_error()), n => Ok(n as uint) } } @@ -871,7 +1003,7 @@ pub fn write<T>(fd: sock_t, while written < buf.len() && (write_everything || written == 0) { // As with read(), first wait for the socket to be ready for // the I/O operation. - match util::await(fd, deadline, util::Writable) { + match util::await([fd], deadline, util::Writable) { Err(ref e) if e.code == libc::EOF as uint && written > 0 => { assert!(deadline.is_some()); return Err(util::short_write(written, "short write")) @@ -887,15 +1019,88 @@ pub fn write<T>(fd: sock_t, let len = buf.len() - written; match retry(|| write(deadline.is_some(), ptr, len) as libc::c_int) { -1 if util::wouldblock() => {} - -1 => return Err(last_error()), + -1 => return Err(os::last_error()), n => { written += n as uint; } } } ret = 0; } if ret < 0 { - Err(last_error()) + Err(os::last_error()) } else { Ok(written) } } + +#[cfg(windows)] +mod os { + use libc; + use std::mem; + use std::rt::rtio::{IoError, IoResult}; + + use io::c; + + pub type sock_t = libc::SOCKET; + pub struct Event(c::WSAEVENT); + + impl Event { + pub fn new() -> IoResult<Event> { + let event = unsafe { c::WSACreateEvent() }; + if event == c::WSA_INVALID_EVENT { + Err(last_error()) + } else { + Ok(Event(event)) + } + } + + pub fn handle(&self) -> c::WSAEVENT { let Event(handle) = *self; handle } + } + + impl Drop for Event { + fn drop(&mut self) { + unsafe { let _ = c::WSACloseEvent(self.handle()); } + } + } + + pub fn init() { + unsafe { + use std::rt::mutex::{StaticNativeMutex, NATIVE_MUTEX_INIT}; + static mut INITIALIZED: bool = false; + static mut LOCK: StaticNativeMutex = NATIVE_MUTEX_INIT; + + let _guard = LOCK.lock(); + if !INITIALIZED { + let mut data: c::WSADATA = mem::zeroed(); + let ret = c::WSAStartup(0x202, // version 2.2 + &mut data); + assert_eq!(ret, 0); + INITIALIZED = true; + } + } + } + + pub fn last_error() -> IoError { + use std::os; + let code = unsafe { c::WSAGetLastError() as uint }; + IoError { + code: code, + extra: 0, + detail: Some(os::error_string(code)), + } + } + + pub unsafe fn close(sock: sock_t) { let _ = libc::closesocket(sock); } +} + +#[cfg(unix)] +mod os { + use libc; + use std::rt::rtio::IoError; + use io; + + pub type sock_t = io::file::fd_t; + + pub fn init() {} + pub fn last_error() -> IoError { io::last_error() } + pub unsafe fn close(sock: sock_t) { let _ = libc::close(sock); } +} diff --git a/src/libnative/io/pipe_unix.rs b/src/libnative/io/pipe_unix.rs index 895b8b5929c..a3564dfe2cc 100644 --- a/src/libnative/io/pipe_unix.rs +++ b/src/libnative/io/pipe_unix.rs @@ -15,12 +15,14 @@ use std::mem; use std::rt::mutex; use std::rt::rtio; use std::rt::rtio::{IoResult, IoError}; +use std::sync::atomic; use super::retry; use super::net; use super::util; use super::c; -use super::file::fd_t; +use super::process; +use super::file::{fd_t, FileDesc}; fn unix_socket(ty: libc::c_int) -> IoResult<fd_t> { match unsafe { libc::socket(libc::AF_UNIX, ty, 0) } { @@ -225,7 +227,23 @@ impl UnixListener { pub fn native_listen(self, backlog: int) -> IoResult<UnixAcceptor> { match unsafe { libc::listen(self.fd(), backlog as libc::c_int) } { -1 => Err(super::last_error()), - _ => Ok(UnixAcceptor { listener: self, deadline: 0 }) + + #[cfg(unix)] + _ => { + let (reader, writer) = try!(process::pipe()); + try!(util::set_nonblocking(reader.fd(), true)); + try!(util::set_nonblocking(writer.fd(), true)); + try!(util::set_nonblocking(self.fd(), true)); + Ok(UnixAcceptor { + inner: Arc::new(AcceptorInner { + listener: self, + reader: reader, + writer: writer, + closed: atomic::AtomicBool::new(false), + }), + deadline: 0, + }) + } } } } @@ -240,29 +258,45 @@ impl rtio::RtioUnixListener for UnixListener { } pub struct UnixAcceptor { - listener: UnixListener, + inner: Arc<AcceptorInner>, deadline: u64, } +#[cfg(unix)] +struct AcceptorInner { + listener: UnixListener, + reader: FileDesc, + writer: FileDesc, + closed: atomic::AtomicBool, +} + impl UnixAcceptor { - fn fd(&self) -> fd_t { self.listener.fd() } + fn fd(&self) -> fd_t { self.inner.listener.fd() } pub fn native_accept(&mut self) -> IoResult<UnixStream> { - if self.deadline != 0 { - try!(util::await(self.fd(), Some(self.deadline), util::Readable)); - } - let mut storage: libc::sockaddr_storage = unsafe { mem::zeroed() }; - let storagep = &mut storage as *mut libc::sockaddr_storage; - let size = mem::size_of::<libc::sockaddr_storage>(); - let mut size = size as libc::socklen_t; - match retry(|| unsafe { - libc::accept(self.fd(), - storagep as *mut libc::sockaddr, - &mut size as *mut libc::socklen_t) as libc::c_int - }) { - -1 => Err(super::last_error()), - fd => Ok(UnixStream::new(Arc::new(Inner::new(fd)))) + let deadline = if self.deadline == 0 {None} else {Some(self.deadline)}; + + while !self.inner.closed.load(atomic::SeqCst) { + unsafe { + let mut storage: libc::sockaddr_storage = mem::zeroed(); + let storagep = &mut storage as *mut libc::sockaddr_storage; + let size = mem::size_of::<libc::sockaddr_storage>(); + let mut size = size as libc::socklen_t; + match retry(|| { + libc::accept(self.fd(), + storagep as *mut libc::sockaddr, + &mut size as *mut libc::socklen_t) as libc::c_int + }) { + -1 if util::wouldblock() => {} + -1 => return Err(super::last_error()), + fd => return Ok(UnixStream::new(Arc::new(Inner::new(fd)))), + } + } + try!(util::await([self.fd(), self.inner.reader.fd()], + deadline, util::Readable)); } + + Err(util::eof()) } } @@ -273,6 +307,24 @@ impl rtio::RtioUnixAcceptor for UnixAcceptor { fn set_timeout(&mut self, timeout: Option<u64>) { self.deadline = timeout.map(|a| ::io::timer::now() + a).unwrap_or(0); } + + fn clone(&self) -> Box<rtio::RtioUnixAcceptor + Send> { + box UnixAcceptor { + inner: self.inner.clone(), + deadline: 0, + } as Box<rtio::RtioUnixAcceptor + Send> + } + + #[cfg(unix)] + fn close_accept(&mut self) -> IoResult<()> { + self.inner.closed.store(true, atomic::SeqCst); + let mut fd = FileDesc::new(self.inner.writer.fd(), false); + match fd.inner_write([0]) { + Ok(..) => Ok(()), + Err(..) if util::wouldblock() => Ok(()), + Err(e) => Err(e), + } + } } impl Drop for UnixListener { diff --git a/src/libnative/io/pipe_win32.rs b/src/libnative/io/pipe_windows.rs index 717915e5d23..95afa11f4a9 100644 --- a/src/libnative/io/pipe_win32.rs +++ b/src/libnative/io/pipe_windows.rs @@ -169,23 +169,30 @@ unsafe fn pipe(name: *const u16, init: bool) -> libc::HANDLE { } pub fn await(handle: libc::HANDLE, deadline: u64, - overlapped: &mut libc::OVERLAPPED) -> bool { - if deadline == 0 { return true } + events: &[libc::HANDLE]) -> IoResult<uint> { + use libc::consts::os::extra::{WAIT_FAILED, WAIT_TIMEOUT, WAIT_OBJECT_0}; // If we've got a timeout, use WaitForSingleObject in tandem with CancelIo // to figure out if we should indeed get the result. - let now = ::io::timer::now(); - let timeout = deadline < now || unsafe { - let ms = (deadline - now) as libc::DWORD; - let r = libc::WaitForSingleObject(overlapped.hEvent, - ms); - r != libc::WAIT_OBJECT_0 - }; - if timeout { - unsafe { let _ = c::CancelIo(handle); } - false + let ms = if deadline == 0 { + libc::INFINITE as u64 } else { - true + let now = ::io::timer::now(); + if deadline < now {0} else {deadline - now} + }; + let ret = unsafe { + c::WaitForMultipleObjects(events.len() as libc::DWORD, + events.as_ptr(), + libc::FALSE, + ms as libc::DWORD) + }; + match ret { + WAIT_FAILED => Err(super::last_error()), + WAIT_TIMEOUT => unsafe { + let _ = c::CancelIo(handle); + Err(util::timeout("operation timed out")) + }, + n => Ok((n - WAIT_OBJECT_0) as uint) } } @@ -390,8 +397,8 @@ impl rtio::RtioPipe for UnixStream { drop(guard); loop { // Process a timeout if one is pending - let succeeded = await(self.handle(), self.read_deadline, - &mut overlapped); + let wait_succeeded = await(self.handle(), self.read_deadline, + [overlapped.hEvent]); let ret = unsafe { libc::GetOverlappedResult(self.handle(), @@ -408,7 +415,7 @@ impl rtio::RtioPipe for UnixStream { // If the reading half is now closed, then we're done. If we woke up // because the writing half was closed, keep trying. - if !succeeded { + if wait_succeeded.is_err() { return Err(util::timeout("read timed out")) } if self.read_closed() { @@ -458,8 +465,8 @@ impl rtio::RtioPipe for UnixStream { }) } // Process a timeout if one is pending - let succeeded = await(self.handle(), self.write_deadline, - &mut overlapped); + let wait_succeeded = await(self.handle(), self.write_deadline, + [overlapped.hEvent]); let ret = unsafe { libc::GetOverlappedResult(self.handle(), &mut overlapped, @@ -473,7 +480,7 @@ impl rtio::RtioPipe for UnixStream { if os::errno() != libc::ERROR_OPERATION_ABORTED as uint { return Err(super::last_error()) } - if !succeeded { + if !wait_succeeded.is_ok() { let amt = offset + bytes_written as uint; return if amt > 0 { Err(IoError { @@ -577,6 +584,10 @@ impl UnixListener { listener: self, event: try!(Event::new(true, false)), deadline: 0, + inner: Arc::new(AcceptorState { + abort: try!(Event::new(true, false)), + closed: atomic::AtomicBool::new(false), + }), }) } } @@ -597,11 +608,17 @@ impl rtio::RtioUnixListener for UnixListener { } pub struct UnixAcceptor { + inner: Arc<AcceptorState>, listener: UnixListener, event: Event, deadline: u64, } +struct AcceptorState { + abort: Event, + closed: atomic::AtomicBool, +} + impl UnixAcceptor { pub fn native_accept(&mut self) -> IoResult<UnixStream> { // This function has some funky implementation details when working with @@ -638,6 +655,10 @@ impl UnixAcceptor { // using the original server pipe. let handle = self.listener.handle; + // If we've had an artifical call to close_accept, be sure to never + // proceed in accepting new clients in the future + if self.inner.closed.load(atomic::SeqCst) { return Err(util::eof()) } + let name = try!(to_utf16(&self.listener.name)); // Once we've got a "server handle", we need to wait for a client to @@ -652,7 +673,9 @@ impl UnixAcceptor { if err == libc::ERROR_IO_PENDING as libc::DWORD { // Process a timeout if one is pending - let _ = await(handle, self.deadline, &mut overlapped); + let wait_succeeded = await(handle, self.deadline, + [self.inner.abort.handle(), + overlapped.hEvent]); // This will block until the overlapped I/O is completed. The // timeout was previously handled, so this will either block in @@ -665,7 +688,11 @@ impl UnixAcceptor { libc::TRUE) }; if ret == 0 { - err = unsafe { libc::GetLastError() }; + if wait_succeeded.is_ok() { + err = unsafe { libc::GetLastError() }; + } else { + return Err(util::timeout("accept timed out")) + } } else { // we succeeded, bypass the check below err = libc::ERROR_PIPE_CONNECTED as libc::DWORD; @@ -709,5 +736,34 @@ impl rtio::RtioUnixAcceptor for UnixAcceptor { fn set_timeout(&mut self, timeout: Option<u64>) { self.deadline = timeout.map(|i| i + ::io::timer::now()).unwrap_or(0); } + + fn clone(&self) -> Box<rtio::RtioUnixAcceptor + Send> { + let name = to_utf16(&self.listener.name).ok().unwrap(); + box UnixAcceptor { + inner: self.inner.clone(), + event: Event::new(true, false).ok().unwrap(), + deadline: 0, + listener: UnixListener { + name: self.listener.name.clone(), + handle: unsafe { + let p = pipe(name.as_ptr(), false) ; + assert!(p != libc::INVALID_HANDLE_VALUE as libc::HANDLE); + p + }, + }, + } as Box<rtio::RtioUnixAcceptor + Send> + } + + fn close_accept(&mut self) -> IoResult<()> { + self.inner.closed.store(true, atomic::SeqCst); + let ret = unsafe { + c::SetEvent(self.inner.abort.handle()) + }; + if ret == 0 { + Err(super::last_error()) + } else { + Ok(()) + } + } } diff --git a/src/libnative/io/process.rs b/src/libnative/io/process.rs index 443d7645388..b8ec0cd5496 100644 --- a/src/libnative/io/process.rs +++ b/src/libnative/io/process.rs @@ -191,7 +191,7 @@ impl Drop for Process { } } -fn pipe() -> IoResult<(file::FileDesc, file::FileDesc)> { +pub fn pipe() -> IoResult<(file::FileDesc, file::FileDesc)> { #[cfg(unix)] use libc::EMFILE as ERROR; #[cfg(windows)] use libc::WSAEMFILE as ERROR; struct Closer { fd: libc::c_int } @@ -786,7 +786,7 @@ fn with_envp<T>(env: Option<&[(&CString, &CString)]>, #[cfg(windows)] fn with_envp<T>(env: Option<&[(&CString, &CString)]>, cb: |*mut c_void| -> T) -> T { - // On win32 we pass an "environment block" which is not a char**, but + // On Windows we pass an "environment block" which is not a char**, but // rather a concatenation of null-terminated k=v\0 sequences, with a final // \0 to terminate. match env { diff --git a/src/libnative/io/timer_win32.rs b/src/libnative/io/timer_windows.rs index 1e5594126b1..8d781f50d35 100644 --- a/src/libnative/io/timer_win32.rs +++ b/src/libnative/io/timer_windows.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -//! Timers based on win32 WaitableTimers +//! Timers based on Windows WaitableTimers //! //! This implementation is meant to be used solely on windows. As with other //! implementations, there is a worker thread which is doing all the waiting on diff --git a/src/libnative/io/tty_win32.rs b/src/libnative/io/tty_windows.rs index e98fe1e20b1..e98fe1e20b1 100644 --- a/src/libnative/io/tty_win32.rs +++ b/src/libnative/io/tty_windows.rs diff --git a/src/libnative/io/util.rs b/src/libnative/io/util.rs index 356805d91de..078989b0581 100644 --- a/src/libnative/io/util.rs +++ b/src/libnative/io/util.rs @@ -9,6 +9,7 @@ // except according to those terms. use libc; +use std::cmp; use std::mem; use std::os; use std::ptr; @@ -166,10 +167,18 @@ pub fn connect_timeout(fd: net::sock_t, } } -pub fn await(fd: net::sock_t, deadline: Option<u64>, +pub fn await(fds: &[net::sock_t], deadline: Option<u64>, status: SocketStatus) -> IoResult<()> { let mut set: c::fd_set = unsafe { mem::zeroed() }; - c::fd_set(&mut set, fd); + let mut max = 0; + for &fd in fds.iter() { + c::fd_set(&mut set, fd); + max = cmp::max(max, fd + 1); + } + if cfg!(windows) { + max = fds.len() as net::sock_t; + } + let (read, write) = match status { Readable => (&mut set as *mut _, ptr::mut_null()), Writable => (ptr::mut_null(), &mut set as *mut _), @@ -188,8 +197,9 @@ pub fn await(fd: net::sock_t, deadline: Option<u64>, &mut tv as *mut _ } }; - let n = if cfg!(windows) {1} else {fd as libc::c_int + 1}; - let r = unsafe { c::select(n, read, write, ptr::mut_null(), tvp) }; + let r = unsafe { + c::select(max as libc::c_int, read, write, ptr::mut_null(), tvp) + }; r }) { -1 => Err(last_error()), diff --git a/src/libnative/task.rs b/src/libnative/task.rs index 55806caaf13..5c3beeec8ab 100644 --- a/src/libnative/task.rs +++ b/src/libnative/task.rs @@ -145,8 +145,8 @@ impl rt::Runtime for Ops { Local::put(cur_task); } - fn wrap(self: Box<Ops>) -> Box<Any> { - self as Box<Any> + fn wrap(self: Box<Ops>) -> Box<Any+'static> { + self as Box<Any+'static> } fn stack_bounds(&self) -> (uint, uint) { self.stack_bounds } diff --git a/src/libnum/bigint.rs b/src/libnum/bigint.rs index 48fc9fb4a38..ba45d2b2e73 100644 --- a/src/libnum/bigint.rs +++ b/src/libnum/bigint.rs @@ -1449,8 +1449,8 @@ mod biguint_tests { #[test] fn test_cmp() { - let data: Vec<BigUint> = [ &[], &[1], &[2], &[-1], &[0, 1], &[2, 1], &[1, 1, 1] ] - .iter().map(|v| BigUint::from_slice(*v)).collect(); + let data: [&[_], ..7] = [ &[], &[1], &[2], &[-1], &[0, 1], &[2, 1], &[1, 1, 1] ]; + let data: Vec<BigUint> = data.iter().map(|v| BigUint::from_slice(*v)).collect(); for (i, ni) in data.iter().enumerate() { for (j0, nj) in data.slice(i, data.len()).iter().enumerate() { let j = j0 + i; @@ -2311,7 +2311,7 @@ mod bigint_tests { #[test] fn test_cmp() { - let vs = [ &[2 as BigDigit], &[1, 1], &[2, 1], &[1, 1, 1] ]; + let vs: [&[BigDigit], ..4] = [ &[2 as BigDigit], &[1, 1], &[2, 1], &[1, 1, 1] ]; let mut nums = Vec::new(); for s in vs.iter().rev() { nums.push(BigInt::from_slice(Minus, *s)); diff --git a/src/librand/distributions/mod.rs b/src/librand/distributions/mod.rs index 9d401d1307c..447e3eea061 100644 --- a/src/librand/distributions/mod.rs +++ b/src/librand/distributions/mod.rs @@ -79,6 +79,13 @@ pub struct Weighted<T> { pub item: T, } +/// Note: stage0-specific version that lacks bound on A. +#[cfg(stage0)] +pub struct WeightedChoice<'a, T> { + items: &'a mut [Weighted<T>], + weight_range: Range<uint> +} + /// A distribution that selects from a finite collection of weighted items. /// /// Each item has an associated weight that influences how likely it @@ -105,7 +112,8 @@ pub struct Weighted<T> { /// println!("{}", wc.ind_sample(&mut rng)); /// } /// ``` -pub struct WeightedChoice<'a, T> { +#[cfg(not(stage0))] +pub struct WeightedChoice<'a, T:'a> { items: &'a mut [Weighted<T>], weight_range: Range<uint> } diff --git a/src/librand/isaac.rs b/src/librand/isaac.rs index 2fbfa6d6e85..0f7cda42a8a 100644 --- a/src/librand/isaac.rs +++ b/src/librand/isaac.rs @@ -13,7 +13,6 @@ use core::prelude::*; use core::iter::{range_step, Repeat}; use core::slice::raw; -use core::mem; use {Rng, SeedableRng, Rand}; @@ -46,6 +45,7 @@ static EMPTY: IsaacRng = IsaacRng { }; impl IsaacRng { + /// Create an ISAAC random number generator using the default /// fixed seed. pub fn new_unseeded() -> IsaacRng { @@ -225,7 +225,7 @@ impl Rand for IsaacRng { let ptr = ret.rsl.as_mut_ptr(); raw::mut_buf_as_slice(ptr as *mut u8, - mem::size_of_val(&ret.rsl), |slice| { + (RAND_SIZE*4) as uint, |slice| { other.fill_bytes(slice); }) } @@ -456,7 +456,7 @@ impl Rand for Isaac64Rng { let ptr = ret.rsl.as_mut_ptr(); raw::mut_buf_as_slice(ptr as *mut u8, - mem::size_of_val(&ret.rsl), |slice| { + (RAND_SIZE_64*8) as uint, |slice| { other.fill_bytes(slice); }) } @@ -497,7 +497,7 @@ mod test { #[test] fn test_rng_32_seeded() { - let seed = &[1, 23, 456, 7890, 12345]; + let seed: &[_] = &[1, 23, 456, 7890, 12345]; let mut ra: IsaacRng = SeedableRng::from_seed(seed); let mut rb: IsaacRng = SeedableRng::from_seed(seed); assert!(order::equals(ra.gen_ascii_chars().take(100), @@ -505,7 +505,7 @@ mod test { } #[test] fn test_rng_64_seeded() { - let seed = &[1, 23, 456, 7890, 12345]; + let seed: &[_] = &[1, 23, 456, 7890, 12345]; let mut ra: Isaac64Rng = SeedableRng::from_seed(seed); let mut rb: Isaac64Rng = SeedableRng::from_seed(seed); assert!(order::equals(ra.gen_ascii_chars().take(100), @@ -537,7 +537,7 @@ mod test { #[test] fn test_rng_32_true_values() { - let seed = &[1, 23, 456, 7890, 12345]; + let seed: &[_] = &[1, 23, 456, 7890, 12345]; let mut ra: IsaacRng = SeedableRng::from_seed(seed); // Regression test that isaac is actually using the above vector let v = Vec::from_fn(10, |_| ra.next_u32()); @@ -545,7 +545,7 @@ mod test { vec!(2558573138, 873787463, 263499565, 2103644246, 3595684709, 4203127393, 264982119, 2765226902, 2737944514, 3900253796)); - let seed = &[12345, 67890, 54321, 9876]; + let seed: &[_] = &[12345, 67890, 54321, 9876]; let mut rb: IsaacRng = SeedableRng::from_seed(seed); // skip forward to the 10000th number for _ in range(0u, 10000) { rb.next_u32(); } @@ -557,7 +557,7 @@ mod test { } #[test] fn test_rng_64_true_values() { - let seed = &[1, 23, 456, 7890, 12345]; + let seed: &[_] = &[1, 23, 456, 7890, 12345]; let mut ra: Isaac64Rng = SeedableRng::from_seed(seed); // Regression test that isaac is actually using the above vector let v = Vec::from_fn(10, |_| ra.next_u64()); @@ -567,7 +567,7 @@ mod test { 4469761996653280935, 15552757044682284409, 6860251611068737823, 13722198873481261842)); - let seed = &[12345, 67890, 54321, 9876]; + let seed: &[_] = &[12345, 67890, 54321, 9876]; let mut rb: Isaac64Rng = SeedableRng::from_seed(seed); // skip forward to the 10000th number for _ in range(0u, 10000) { rb.next_u64(); } diff --git a/src/librand/lib.rs b/src/librand/lib.rs index 9c33b713e4a..f1ed9ae8997 100644 --- a/src/librand/lib.rs +++ b/src/librand/lib.rs @@ -269,10 +269,17 @@ pub trait Rng { } } +/// Note: stage0-specific version that lacks bound on A. +#[cfg(stage0)] +pub struct Generator<'a, T, R> { + rng: &'a mut R, +} + /// Iterator which will generate a stream of random items. /// /// This iterator is created via the `gen_iter` method on `Rng`. -pub struct Generator<'a, T, R> { +#[cfg(not(stage0))] +pub struct Generator<'a, T, R:'a> { rng: &'a mut R, } @@ -282,10 +289,17 @@ impl<'a, T: Rand, R: Rng> Iterator<T> for Generator<'a, T, R> { } } +/// Note: stage0-specific version. +#[cfg(stage0)] +pub struct AsciiGenerator<'a, R> { + rng: &'a mut R, +} + /// Iterator which will continuously generate random ascii characters. /// /// This iterator is created via the `gen_ascii_chars` method on `Rng`. -pub struct AsciiGenerator<'a, R> { +#[cfg(not(stage0))] +pub struct AsciiGenerator<'a, R:'a> { rng: &'a mut R, } @@ -309,7 +323,8 @@ pub trait SeedableRng<Seed>: Rng { /// ```rust /// use std::rand::{Rng, SeedableRng, StdRng}; /// - /// let mut rng: StdRng = SeedableRng::from_seed(&[1, 2, 3, 4]); + /// let seed: &[_] = &[1, 2, 3, 4]; + /// let mut rng: StdRng = SeedableRng::from_seed(seed); /// println!("{}", rng.gen::<f64>()); /// rng.reseed([5, 6, 7, 8]); /// println!("{}", rng.gen::<f64>()); @@ -323,7 +338,8 @@ pub trait SeedableRng<Seed>: Rng { /// ```rust /// use std::rand::{Rng, SeedableRng, StdRng}; /// - /// let mut rng: StdRng = SeedableRng::from_seed(&[1, 2, 3, 4]); + /// let seed: &[_] = &[1, 2, 3, 4]; + /// let mut rng: StdRng = SeedableRng::from_seed(seed); /// println!("{}", rng.gen::<f64>()); /// ``` fn from_seed(seed: Seed) -> Self; diff --git a/src/librbml/io.rs b/src/librbml/io.rs index 9ab163c5f47..443bf4d34ff 100644 --- a/src/librbml/io.rs +++ b/src/librbml/io.rs @@ -96,7 +96,8 @@ impl Writer for SeekableMemWriter { let (left, right) = if cap <= buf.len() { (buf.slice_to(cap), buf.slice_from(cap)) } else { - (buf, &[]) + let result: (_, &[_]) = (buf, &[]); + result }; // Do the necessary writes @@ -142,24 +143,29 @@ mod tests { writer.write([1, 2, 3]).unwrap(); writer.write([4, 5, 6, 7]).unwrap(); assert_eq!(writer.tell(), Ok(8)); - assert_eq!(writer.get_ref(), &[0, 1, 2, 3, 4, 5, 6, 7]); + let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7]; + assert_eq!(writer.get_ref(), b); writer.seek(0, io::SeekSet).unwrap(); assert_eq!(writer.tell(), Ok(0)); writer.write([3, 4]).unwrap(); - assert_eq!(writer.get_ref(), &[3, 4, 2, 3, 4, 5, 6, 7]); + let b: &[_] = &[3, 4, 2, 3, 4, 5, 6, 7]; + assert_eq!(writer.get_ref(), b); writer.seek(1, io::SeekCur).unwrap(); writer.write([0, 1]).unwrap(); - assert_eq!(writer.get_ref(), &[3, 4, 2, 0, 1, 5, 6, 7]); + let b: &[_] = &[3, 4, 2, 0, 1, 5, 6, 7]; + assert_eq!(writer.get_ref(), b); writer.seek(-1, io::SeekEnd).unwrap(); writer.write([1, 2]).unwrap(); - assert_eq!(writer.get_ref(), &[3, 4, 2, 0, 1, 5, 6, 1, 2]); + let b: &[_] = &[3, 4, 2, 0, 1, 5, 6, 1, 2]; + assert_eq!(writer.get_ref(), b); writer.seek(1, io::SeekEnd).unwrap(); writer.write([1]).unwrap(); - assert_eq!(writer.get_ref(), &[3, 4, 2, 0, 1, 5, 6, 1, 2, 0, 1]); + let b: &[_] = &[3, 4, 2, 0, 1, 5, 6, 1, 2, 0, 1]; + assert_eq!(writer.get_ref(), b); } #[test] diff --git a/src/librbml/lib.rs b/src/librbml/lib.rs index 4927a8293a4..a05c877a6a6 100644 --- a/src/librbml/lib.rs +++ b/src/librbml/lib.rs @@ -24,7 +24,7 @@ html_favicon_url = "http://www.rust-lang.org/favicon.ico", html_root_url = "http://doc.rust-lang.org/master/", html_playground_url = "http://play.rust-lang.org/")] -#![feature(macro_rules, phase)] +#![feature(macro_rules, phase, issue_5723_bootstrap)] #![allow(missing_doc)] extern crate serialize; @@ -662,11 +662,19 @@ pub mod writer { pub type EncodeResult = io::IoResult<()>; // rbml writing + #[cfg(stage0)] pub struct Encoder<'a, W> { pub writer: &'a mut W, size_positions: Vec<uint>, } + // rbml writing + #[cfg(not(stage0))] + pub struct Encoder<'a, W:'a> { + pub writer: &'a mut W, + size_positions: Vec<uint>, + } + fn write_sized_vuint<W: Writer>(w: &mut W, n: uint, size: uint) -> EncodeResult { match size { 1u => w.write(&[0x80u8 | (n as u8)]), diff --git a/src/libregex_macros/lib.rs b/src/libregex_macros/lib.rs index 8610621d30a..8aa9a2fc8fb 100644 --- a/src/libregex_macros/lib.rs +++ b/src/libregex_macros/lib.rs @@ -77,7 +77,7 @@ pub fn plugin_registrar(reg: &mut Registry) { /// strategy is identical and vm.rs has comments and will be easier to follow. #[allow(experimental)] fn native(cx: &mut ExtCtxt, sp: codemap::Span, tts: &[ast::TokenTree]) - -> Box<MacResult> { + -> Box<MacResult+'static> { let regex = match parse(cx, tts) { Some(r) => r, // error is logged in 'parse' with cx.span_err diff --git a/src/librustc/back/link.rs b/src/librustc/back/link.rs index b475edf7780..2877442e42b 100644 --- a/src/librustc/back/link.rs +++ b/src/librustc/back/link.rs @@ -536,6 +536,13 @@ pub mod write { llvm::LLVMPassManagerBuilderPopulateFunctionPassManager(builder, fpm); llvm::LLVMPassManagerBuilderPopulateModulePassManager(builder, mpm); llvm::LLVMPassManagerBuilderDispose(builder); + + match opt { + llvm::CodeGenLevelDefault | llvm::CodeGenLevelAggressive => { + "mergefunc".with_c_str(|s| llvm::LLVMRustAddPass(mpm, s)); + } + _ => {} + }; } } @@ -856,7 +863,7 @@ pub fn get_cc_prog(sess: &Session) -> String { // In the future, FreeBSD will use clang as default compiler. // It would be flexible to use cc (system's default C compiler) // instead of hard-coded gcc. - // For win32, there is no cc command, so we add a condition to make it use gcc. + // For Windows, there is no cc command, so we add a condition to make it use gcc. match sess.targ_cfg.os { abi::OsWindows => "gcc", _ => "cc", @@ -1305,6 +1312,8 @@ fn link_natively(sess: &Session, trans: &CrateTranslation, dylib: bool, sess.note(str::from_utf8(output.as_slice()).unwrap()); sess.abort_if_errors(); } + debug!("linker stderr:\n{}", str::from_utf8_owned(prog.error).unwrap()); + debug!("linker stdout:\n{}", str::from_utf8_owned(prog.output).unwrap()); }, Err(e) => { sess.err(format!("could not exec the linker `{}`: {}", diff --git a/src/librustc/diagnostics.rs b/src/librustc/diagnostics.rs index a789049d4de..77e73c46c40 100644 --- a/src/librustc/diagnostics.rs +++ b/src/librustc/diagnostics.rs @@ -34,7 +34,6 @@ register_diagnostics!( E0015, E0016, E0017, - E0018, E0019, E0020, E0021, @@ -53,7 +52,6 @@ register_diagnostics!( E0034, E0035, E0036, - E0037, E0038, E0039, E0040, @@ -80,8 +78,6 @@ register_diagnostics!( E0061, E0062, E0063, - E0064, - E0065, E0066, E0067, E0068, @@ -127,8 +123,6 @@ register_diagnostics!( E0108, E0109, E0110, - E0111, - E0112, E0113, E0114, E0115, diff --git a/src/librustc/driver/driver.rs b/src/librustc/driver/driver.rs index 261d4be86b4..14642a3708a 100644 --- a/src/librustc/driver/driver.rs +++ b/src/librustc/driver/driver.rs @@ -958,11 +958,11 @@ pub fn pretty_print_input(sess: Session, let mut rdr = MemReader::new(src); let out = match ofile { - None => box io::stdout() as Box<Writer>, + None => box io::stdout() as Box<Writer+'static>, Some(p) => { let r = io::File::create(&p); match r { - Ok(w) => box w as Box<Writer>, + Ok(w) => box w as Box<Writer+'static>, Err(e) => fail!("print-print failed to open {} due to {}", p.display(), e), } diff --git a/src/librustc/front/feature_gate.rs b/src/librustc/front/feature_gate.rs index 7c19b25e01c..18f4b79de6e 100644 --- a/src/librustc/front/feature_gate.rs +++ b/src/librustc/front/feature_gate.rs @@ -74,7 +74,7 @@ static KNOWN_FEATURES: &'static [(&'static str, Status)] = &[ // A temporary feature gate used to enable parser extensions needed // to bootstrap fix for #5723. - ("issue_5723_bootstrap", Active), + ("issue_5723_bootstrap", Accepted), // These are used to test this portion of the compiler, they don't actually // mean anything @@ -97,7 +97,6 @@ enum Status { /// A set of features to be used by later passes. pub struct Features { pub default_type_params: Cell<bool>, - pub issue_5723_bootstrap: Cell<bool>, pub overloaded_calls: Cell<bool>, pub rustc_diagnostic_macros: Cell<bool>, pub import_shadowing: Cell<bool>, @@ -107,7 +106,6 @@ impl Features { pub fn new() -> Features { Features { default_type_params: Cell::new(false), - issue_5723_bootstrap: Cell::new(false), overloaded_calls: Cell::new(false), rustc_diagnostic_macros: Cell::new(false), import_shadowing: Cell::new(false), @@ -310,7 +308,7 @@ impl<'a> Visitor<()> for Context<'a> { fn visit_ty(&mut self, t: &ast::Ty, _: ()) { match t.node { - ast::TyClosure(closure, _) if closure.onceness == ast::Once => { + ast::TyClosure(closure) if closure.onceness == ast::Once => { self.gate_feature("once_fns", t.span, "once functions are \ experimental and likely to be removed"); @@ -439,7 +437,6 @@ pub fn check_crate(sess: &Session, krate: &ast::Crate) { sess.abort_if_errors(); sess.features.default_type_params.set(cx.has_feature("default_type_params")); - sess.features.issue_5723_bootstrap.set(cx.has_feature("issue_5723_bootstrap")); sess.features.overloaded_calls.set(cx.has_feature("overloaded_calls")); sess.features.rustc_diagnostic_macros.set(cx.has_feature("rustc_diagnostic_macros")); sess.features.import_shadowing.set(cx.has_feature("import_shadowing")); diff --git a/src/librustc/front/test.rs b/src/librustc/front/test.rs index ceb7dcc5456..5f26a966f97 100644 --- a/src/librustc/front/test.rs +++ b/src/librustc/front/test.rs @@ -477,13 +477,14 @@ fn mk_test_descs(cx: &TestCtxt) -> Gc<ast::Expr> { box(GC) ast::Expr { id: ast::DUMMY_NODE_ID, - node: ast::ExprVstore(box(GC) ast::Expr { - id: ast::DUMMY_NODE_ID, - node: ast::ExprVec(cx.testfns.iter().map(|test| { - mk_test_desc_and_fn_rec(cx, test) + node: ast::ExprAddrOf(ast::MutImmutable, + box(GC) ast::Expr { + id: ast::DUMMY_NODE_ID, + node: ast::ExprVec(cx.testfns.iter().map(|test| { + mk_test_desc_and_fn_rec(cx, test) }).collect()), span: DUMMY_SP, - }, ast::ExprVstoreSlice), + }), span: DUMMY_SP, } } diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs index 239e858eeeb..9b3cc2b6a0a 100644 --- a/src/librustc/lib.rs +++ b/src/librustc/lib.rs @@ -31,6 +31,7 @@ This API is completely unstable and subject to change. #![allow(deprecated)] #![feature(macro_rules, globs, struct_variant, managed_boxes, quote)] #![feature(default_type_params, phase, unsafe_destructor)] +#![feature(issue_5723_bootstrap)] #![allow(unknown_features)] // NOTE: Remove after next snapshot #![feature(rustc_diagnostic_macros)] diff --git a/src/librustc/lint/builtin.rs b/src/librustc/lint/builtin.rs index 7e0ba613e3d..01fea98dfa0 100644 --- a/src/librustc/lint/builtin.rs +++ b/src/librustc/lint/builtin.rs @@ -27,7 +27,6 @@ use metadata::csearch; use middle::def::*; -use middle::trans::adt; // for `adt::is_ffi_safe` use middle::typeck::astconv::ast_ty_to_ty; use middle::typeck::infer; use middle::{typeck, ty, def, pat_util, stability}; @@ -362,12 +361,17 @@ impl LintPass for CTypes { "found rust type `uint` in foreign module, while \ libc::c_uint or libc::c_ulong should be used"); } - def::DefTy(def_id) => { - if !adt::is_ffi_safe(cx.tcx, def_id) { + def::DefTy(..) => { + let tty = match cx.tcx.ast_ty_to_ty_cache.borrow().find(&ty.id) { + Some(&ty::atttce_resolved(t)) => t, + _ => fail!("ast_ty_to_ty_cache was incomplete after typeck!") + }; + + if !ty::is_ffi_safe(cx.tcx, tty) { cx.span_lint(CTYPES, ty.span, - "found enum type without foreign-function-safe + "found type without foreign-function-safe representation annotation in foreign module, consider \ - adding a #[repr(...)] attribute to the enumeration"); + adding a #[repr(...)] attribute to the type"); } } _ => () @@ -750,7 +754,7 @@ impl LintPass for NonCamelCaseTypes { // start with a non-lowercase letter rather than non-uppercase // ones (some scripts don't have a concept of upper/lowercase) - !ident.char_at(0).is_lowercase() && !ident.contains_char('_') + ident.len() > 0 && !ident.char_at(0).is_lowercase() && !ident.contains_char('_') } fn to_camel_case(s: &str) -> String { @@ -764,15 +768,20 @@ impl LintPass for NonCamelCaseTypes { let s = token::get_ident(ident); if !is_camel_case(ident) { - cx.span_lint(NON_CAMEL_CASE_TYPES, span, - format!("{} `{}` should have a camel case name such as `{}`", - sort, s, to_camel_case(s.get())).as_slice()); + let c = to_camel_case(s.get()); + let m = if c.is_empty() { + format!("{} `{}` should have a camel case name such as `CamelCase`", sort, s) + } else { + format!("{} `{}` should have a camel case name such as `{}`", sort, s, c) + }; + cx.span_lint(NON_CAMEL_CASE_TYPES, span, m.as_slice()); } } - let has_extern_repr = it.attrs.iter().fold(attr::ReprAny, |acc, attr| { - attr::find_repr_attr(cx.tcx.sess.diagnostic(), attr, acc) - }) == attr::ReprExtern; + let has_extern_repr = it.attrs.iter().map(|attr| { + attr::find_repr_attrs(cx.tcx.sess.diagnostic(), attr).iter() + .any(|r| r == &attr::ReprExtern) + }).any(|x| x); if has_extern_repr { return } match it.node { @@ -783,6 +792,7 @@ impl LintPass for NonCamelCaseTypes { check_case(cx, "trait", it.ident, it.span) } ast::ItemEnum(ref enum_definition, _) => { + if has_extern_repr { return } check_case(cx, "type", it.ident, it.span); for variant in enum_definition.variants.iter() { check_case(cx, "variant", variant.node.name, variant.span); @@ -1234,18 +1244,8 @@ impl LintPass for UnnecessaryAllocation { } fn check_expr(&mut self, cx: &Context, e: &ast::Expr) { - // Warn if string and vector literals with sigils, or boxing expressions, - // are immediately borrowed. + // Warn if boxing expressions are immediately borrowed. let allocation = match e.node { - ast::ExprVstore(e2, ast::ExprVstoreUniq) => { - match e2.node { - ast::ExprLit(lit) if ast_util::lit_is_str(lit) => { - VectorAllocation - } - ast::ExprVec(..) => VectorAllocation, - _ => return - } - } ast::ExprUnary(ast::UnUniq, _) | ast::ExprUnary(ast::UnBox, _) => BoxAllocation, @@ -1255,19 +1255,19 @@ impl LintPass for UnnecessaryAllocation { match cx.tcx.adjustments.borrow().find(&e.id) { Some(adjustment) => { match *adjustment { - ty::AutoDerefRef(ty::AutoDerefRef { autoref, .. }) => { + ty::AutoDerefRef(ty::AutoDerefRef { ref autoref, .. }) => { match (allocation, autoref) { - (VectorAllocation, Some(ty::AutoBorrowVec(..))) => { + (VectorAllocation, &Some(ty::AutoPtr(_, _, None))) => { cx.span_lint(UNNECESSARY_ALLOCATION, e.span, "unnecessary allocation, the sigil can be removed"); } (BoxAllocation, - Some(ty::AutoPtr(_, ast::MutImmutable))) => { + &Some(ty::AutoPtr(_, ast::MutImmutable, None))) => { cx.span_lint(UNNECESSARY_ALLOCATION, e.span, "unnecessary allocation, use & instead"); } (BoxAllocation, - Some(ty::AutoPtr(_, ast::MutMutable))) => { + &Some(ty::AutoPtr(_, ast::MutMutable, None))) => { cx.span_lint(UNNECESSARY_ALLOCATION, e.span, "unnecessary allocation, use &mut instead"); } @@ -1560,6 +1560,9 @@ declare_lint!(pub UNKNOWN_CRATE_TYPE, Deny, declare_lint!(pub VARIANT_SIZE_DIFFERENCE, Allow, "detects enums with widely varying variant sizes") +declare_lint!(pub TRANSMUTE_FAT_PTR, Allow, + "detects transmutes of fat pointers") + /// Does nothing as a lint pass, but registers some `Lint`s /// which are used by other parts of the compiler. pub struct HardwiredLints; diff --git a/src/librustc/metadata/common.rs b/src/librustc/metadata/common.rs index 6a1c7c6c951..ca2f47328db 100644 --- a/src/librustc/metadata/common.rs +++ b/src/librustc/metadata/common.rs @@ -27,8 +27,6 @@ pub static tag_items_data_item: uint = 0x04; pub static tag_items_data_item_family: uint = 0x05; -pub static tag_items_data_item_ty_param_bounds: uint = 0x06; - pub static tag_items_data_item_type: uint = 0x07; pub static tag_items_data_item_symbol: uint = 0x08; @@ -179,7 +177,6 @@ pub static tag_lang_items_missing: uint = 0x74; pub static tag_item_unnamed_field: uint = 0x75; pub static tag_items_data_item_visibility: uint = 0x76; -pub static tag_items_data_item_sized: uint = 0x77; pub static tag_item_method_tps: uint = 0x79; pub static tag_item_method_fty: uint = 0x7a; @@ -222,12 +219,6 @@ pub struct LinkMeta { pub crate_hash: Svh, } -pub static tag_region_param_def: uint = 0x90; -pub static tag_region_param_def_ident: uint = 0x91; -pub static tag_region_param_def_def_id: uint = 0x92; -pub static tag_region_param_def_space: uint = 0x93; -pub static tag_region_param_def_index: uint = 0x94; - pub static tag_unboxed_closures: uint = 0x95; pub static tag_unboxed_closure: uint = 0x96; pub static tag_unboxed_closure_type: uint = 0x97; @@ -239,3 +230,18 @@ pub static tag_struct_field_id: uint = 0x9b; pub static tag_attribute_is_sugared_doc: uint = 0x9c; +pub static tag_trait_def_bounds: uint = 0x9d; + +pub static tag_items_data_region: uint = 0x9e; + +pub static tag_region_param_def: uint = 0xa0; +pub static tag_region_param_def_ident: uint = 0xa1; +pub static tag_region_param_def_def_id: uint = 0xa2; +pub static tag_region_param_def_space: uint = 0xa3; +pub static tag_region_param_def_index: uint = 0xa4; + +pub static tag_type_param_def: uint = 0xa5; + +pub static tag_item_generics: uint = 0xa6; +pub static tag_method_ty_generics: uint = 0xa7; + diff --git a/src/librustc/metadata/decoder.rs b/src/librustc/metadata/decoder.rs index 74810261d4a..904ca2416e0 100644 --- a/src/librustc/metadata/decoder.rs +++ b/src/librustc/metadata/decoder.rs @@ -18,9 +18,9 @@ use metadata::common::*; use metadata::csearch::StaticMethodInfo; use metadata::csearch; use metadata::cstore; -use metadata::tydecode::{parse_ty_data, parse_def_id}; -use metadata::tydecode::{parse_type_param_def_data, parse_bare_fn_ty_data}; -use metadata::tydecode::{parse_trait_ref_data}; +use metadata::tydecode::{parse_ty_data, parse_region_data, parse_def_id, + parse_type_param_def_data, parse_bounds_data, + parse_bare_fn_ty_data, parse_trait_ref_data}; use middle::def; use middle::lang_items; use middle::resolve::TraitItemKind; @@ -242,48 +242,14 @@ fn item_trait_ref(doc: rbml::Doc, tcx: &ty::ctxt, cdata: Cmd) -> ty::TraitRef { doc_trait_ref(tp, tcx, cdata) } -fn item_ty_param_defs(item: rbml::Doc, - tcx: &ty::ctxt, - cdata: Cmd, - tag: uint) - -> subst::VecPerParamSpace<ty::TypeParameterDef> { - let mut bounds = subst::VecPerParamSpace::empty(); - reader::tagged_docs(item, tag, |p| { - let bd = parse_type_param_def_data( - p.data, p.start, cdata.cnum, tcx, - |_, did| translate_def_id(cdata, did)); - bounds.push(bd.space, bd); - true - }); - bounds +fn doc_bounds(doc: rbml::Doc, tcx: &ty::ctxt, cdata: Cmd) -> ty::ParamBounds { + parse_bounds_data(doc.data, cdata.cnum, doc.start, tcx, + |_, did| translate_def_id(cdata, did)) } -fn item_region_param_defs(item_doc: rbml::Doc, cdata: Cmd) - -> subst::VecPerParamSpace<ty::RegionParameterDef> -{ - let mut v = subst::VecPerParamSpace::empty(); - reader::tagged_docs(item_doc, tag_region_param_def, |rp_doc| { - let ident_str_doc = reader::get_doc(rp_doc, - tag_region_param_def_ident); - let ident = item_name(&*token::get_ident_interner(), ident_str_doc); - let def_id_doc = reader::get_doc(rp_doc, - tag_region_param_def_def_id); - let def_id = reader::with_doc_data(def_id_doc, parse_def_id); - let def_id = translate_def_id(cdata, def_id); - - let doc = reader::get_doc(rp_doc, tag_region_param_def_space); - let space = subst::ParamSpace::from_uint(reader::doc_as_u64(doc) as uint); - - let doc = reader::get_doc(rp_doc, tag_region_param_def_index); - let index = reader::doc_as_u64(doc) as uint; - - v.push(space, ty::RegionParameterDef { name: ident.name, - def_id: def_id, - space: space, - index: index }); - true - }); - v +fn trait_def_bounds(doc: rbml::Doc, tcx: &ty::ctxt, cdata: Cmd) -> ty::ParamBounds { + let d = reader::get_doc(doc, tag_trait_def_bounds); + doc_bounds(d, tcx, cdata) } fn enum_variant_ids(item: rbml::Doc, cdata: Cmd) -> Vec<ast::DefId> { @@ -382,24 +348,11 @@ pub fn get_trait_def(cdata: Cmd, tcx: &ty::ctxt) -> ty::TraitDef { let item_doc = lookup_item(item_id, cdata.data()); - let tp_defs = item_ty_param_defs(item_doc, tcx, cdata, - tag_items_data_item_ty_param_bounds); - let rp_defs = item_region_param_defs(item_doc, cdata); - let mut bounds = ty::empty_builtin_bounds(); - // Collect the builtin bounds from the encoded supertraits. - // FIXME(#8559): They should be encoded directly. - reader::tagged_docs(item_doc, tag_item_super_trait_ref, |trait_doc| { - // NB. Bypasses real supertraits. See get_supertraits() if you wanted them. - let trait_ref = doc_trait_ref(trait_doc, tcx, cdata); - tcx.lang_items.to_builtin_kind(trait_ref.def_id).map(|bound| { - bounds.add(bound); - }); - true - }); + let generics = doc_generics(item_doc, tcx, cdata, tag_item_generics); + let bounds = trait_def_bounds(item_doc, tcx, cdata); ty::TraitDef { - generics: ty::Generics {types: tp_defs, - regions: rp_defs}, + generics: generics, bounds: bounds, trait_ref: Rc::new(item_trait_ref(item_doc, tcx, cdata)) } @@ -413,12 +366,10 @@ pub fn get_type(cdata: Cmd, id: ast::NodeId, tcx: &ty::ctxt) let t = item_type(ast::DefId { krate: cdata.cnum, node: id }, item, tcx, cdata); - let tp_defs = item_ty_param_defs(item, tcx, cdata, tag_items_data_item_ty_param_bounds); - let rp_defs = item_region_param_defs(item, cdata); + let generics = doc_generics(item, tcx, cdata, tag_item_generics); ty::Polytype { - generics: ty::Generics {types: tp_defs, - regions: rp_defs}, + generics: generics, ty: t } } @@ -794,6 +745,7 @@ pub fn get_impl_or_trait_item(intr: Rc<IdentInterner>, tcx: &ty::ctxt) -> ty::ImplOrTraitItem { let method_doc = lookup_item(id, cdata.data()); + let def_id = item_def_id(method_doc, cdata); let container_id = item_reqd_and_translated_parent_item(cdata.cnum, @@ -808,18 +760,13 @@ pub fn get_impl_or_trait_item(intr: Rc<IdentInterner>, match item_sort(method_doc) { 'r' | 'p' => { - let type_param_defs = item_ty_param_defs(method_doc, tcx, cdata, - tag_item_method_tps); - let rp_defs = item_region_param_defs(method_doc, cdata); + let generics = doc_generics(method_doc, tcx, cdata, + tag_method_ty_generics); let fty = doc_method_fty(method_doc, tcx, cdata); let vis = item_visibility(method_doc); let explicit_self = get_explicit_self(method_doc); let provided_source = get_provided_source(method_doc, cdata); - let generics = ty::Generics { - types: type_param_defs, - regions: rp_defs, - }; ty::MethodTraitItem(Rc::new(ty::Method::new(name, generics, fty, @@ -1392,3 +1339,57 @@ pub fn is_typedef(cdata: Cmd, id: ast::NodeId) -> bool { _ => false, } } + +fn doc_generics(base_doc: rbml::Doc, + tcx: &ty::ctxt, + cdata: Cmd, + tag: uint) + -> ty::Generics +{ + let doc = reader::get_doc(base_doc, tag); + + let mut types = subst::VecPerParamSpace::empty(); + reader::tagged_docs(doc, tag_type_param_def, |p| { + let bd = parse_type_param_def_data( + p.data, p.start, cdata.cnum, tcx, + |_, did| translate_def_id(cdata, did)); + types.push(bd.space, bd); + true + }); + + let mut regions = subst::VecPerParamSpace::empty(); + reader::tagged_docs(doc, tag_region_param_def, |rp_doc| { + let ident_str_doc = reader::get_doc(rp_doc, + tag_region_param_def_ident); + let ident = item_name(&*token::get_ident_interner(), ident_str_doc); + let def_id_doc = reader::get_doc(rp_doc, + tag_region_param_def_def_id); + let def_id = reader::with_doc_data(def_id_doc, parse_def_id); + let def_id = translate_def_id(cdata, def_id); + + let doc = reader::get_doc(rp_doc, tag_region_param_def_space); + let space = subst::ParamSpace::from_uint(reader::doc_as_u64(doc) as uint); + + let doc = reader::get_doc(rp_doc, tag_region_param_def_index); + let index = reader::doc_as_u64(doc) as uint; + + let mut bounds = Vec::new(); + reader::tagged_docs(rp_doc, tag_items_data_region, |p| { + bounds.push( + parse_region_data( + p.data, cdata.cnum, p.start, tcx, + |_, did| translate_def_id(cdata, did))); + true + }); + + regions.push(space, ty::RegionParameterDef { name: ident.name, + def_id: def_id, + space: space, + index: index, + bounds: bounds }); + + true + }); + + ty::Generics { types: types, regions: regions } +} diff --git a/src/librustc/metadata/encoder.rs b/src/librustc/metadata/encoder.rs index 29b5db51cc4..1386e23b77d 100644 --- a/src/librustc/metadata/encoder.rs +++ b/src/librustc/metadata/encoder.rs @@ -19,8 +19,7 @@ use metadata::common::*; use metadata::cstore; use metadata::decoder; use metadata::tyencode; -use middle::subst::VecPerParamSpace; -use middle::ty::{node_id_to_type, lookup_item_type}; +use middle::ty::{lookup_item_type}; use middle::astencode; use middle::ty; use middle::typeck; @@ -150,45 +149,6 @@ pub fn def_to_string(did: DefId) -> String { format!("{}:{}", did.krate, did.node) } -fn encode_ty_type_param_defs(rbml_w: &mut Encoder, - ecx: &EncodeContext, - params: &VecPerParamSpace<ty::TypeParameterDef>, - tag: uint) { - let ty_str_ctxt = &tyencode::ctxt { - diag: ecx.diag, - ds: def_to_string, - tcx: ecx.tcx, - abbrevs: &ecx.type_abbrevs - }; - for param in params.iter() { - rbml_w.start_tag(tag); - tyencode::enc_type_param_def(rbml_w.writer, ty_str_ctxt, param); - rbml_w.end_tag(); - } -} - -fn encode_region_param_defs(rbml_w: &mut Encoder, - params: &VecPerParamSpace<ty::RegionParameterDef>) { - for param in params.iter() { - rbml_w.start_tag(tag_region_param_def); - - rbml_w.start_tag(tag_region_param_def_ident); - encode_name(rbml_w, param.name); - rbml_w.end_tag(); - - rbml_w.wr_tagged_str(tag_region_param_def_def_id, - def_to_string(param.def_id).as_slice()); - - rbml_w.wr_tagged_u64(tag_region_param_def_space, - param.space.to_uint() as u64); - - rbml_w.wr_tagged_u64(tag_region_param_def_index, - param.index as u64); - - rbml_w.end_tag(); - } -} - fn encode_item_variances(rbml_w: &mut Encoder, ecx: &EncodeContext, id: ast::NodeId) { @@ -201,9 +161,7 @@ fn encode_item_variances(rbml_w: &mut Encoder, fn encode_bounds_and_type(rbml_w: &mut Encoder, ecx: &EncodeContext, pty: &ty::Polytype) { - encode_ty_type_param_defs(rbml_w, ecx, &pty.generics.types, - tag_items_data_item_ty_param_bounds); - encode_region_param_defs(rbml_w, &pty.generics.regions); + encode_generics(rbml_w, ecx, &pty.generics, tag_item_generics); encode_type(ecx, rbml_w, pty.ty); } @@ -238,6 +196,33 @@ pub fn write_type(ecx: &EncodeContext, tyencode::enc_ty(rbml_w.writer, ty_str_ctxt, typ); } +pub fn write_region(ecx: &EncodeContext, + rbml_w: &mut Encoder, + r: ty::Region) { + let ty_str_ctxt = &tyencode::ctxt { + diag: ecx.diag, + ds: def_to_string, + tcx: ecx.tcx, + abbrevs: &ecx.type_abbrevs + }; + tyencode::enc_region(rbml_w.writer, ty_str_ctxt, r); +} + +fn encode_bounds(rbml_w: &mut Encoder, + ecx: &EncodeContext, + bounds: &ty::ParamBounds, + tag: uint) { + rbml_w.start_tag(tag); + + let ty_str_ctxt = &tyencode::ctxt { diag: ecx.diag, + ds: def_to_string, + tcx: ecx.tcx, + abbrevs: &ecx.type_abbrevs }; + tyencode::enc_bounds(rbml_w.writer, ty_str_ctxt, bounds); + + rbml_w.end_tag(); +} + fn encode_type(ecx: &EncodeContext, rbml_w: &mut Encoder, typ: ty::t) { @@ -246,6 +231,14 @@ fn encode_type(ecx: &EncodeContext, rbml_w.end_tag(); } +fn encode_region(ecx: &EncodeContext, + rbml_w: &mut Encoder, + r: ty::Region) { + rbml_w.start_tag(tag_items_data_region); + write_region(ecx, rbml_w, r); + rbml_w.end_tag(); +} + fn encode_method_fty(ecx: &EncodeContext, rbml_w: &mut Encoder, typ: &ty::BareFnTy) { @@ -728,7 +721,6 @@ fn encode_info_for_struct(ecx: &EncodeContext, /* Each class has its own index, since different classes may have fields with the same name */ let mut index = Vec::new(); - let tcx = ecx.tcx; /* We encode both private and public fields -- need to include private fields to get the offsets right */ for field in fields.iter() { @@ -745,7 +737,8 @@ fn encode_info_for_struct(ecx: &EncodeContext, token::get_name(nm), id); encode_struct_field_family(rbml_w, field.vis); encode_name(rbml_w, nm); - encode_type(ecx, rbml_w, node_id_to_type(tcx, id)); + encode_bounds_and_type(rbml_w, ecx, + &lookup_item_type(ecx.tcx, local_def(id))); encode_def_id(rbml_w, local_def(id)); let stab = stability::lookup(ecx.tcx, field.id); @@ -773,7 +766,6 @@ fn encode_info_for_struct_ctor(ecx: &EncodeContext, encode_bounds_and_type(rbml_w, ecx, &lookup_item_type(ecx.tcx, local_def(ctor_id))); encode_name(rbml_w, name.name); - encode_type(ecx, rbml_w, node_id_to_type(ecx.tcx, ctor_id)); ecx.tcx.map.with_path(ctor_id, |path| encode_path(rbml_w, path)); encode_parent_item(rbml_w, local_def(struct_id)); @@ -793,13 +785,60 @@ fn encode_info_for_struct_ctor(ecx: &EncodeContext, rbml_w.end_tag(); } +fn encode_generics(rbml_w: &mut Encoder, + ecx: &EncodeContext, + generics: &ty::Generics, + tag: uint) +{ + rbml_w.start_tag(tag); + + // Type parameters + let ty_str_ctxt = &tyencode::ctxt { + diag: ecx.diag, + ds: def_to_string, + tcx: ecx.tcx, + abbrevs: &ecx.type_abbrevs + }; + for param in generics.types.iter() { + rbml_w.start_tag(tag_type_param_def); + tyencode::enc_type_param_def(rbml_w.writer, ty_str_ctxt, param); + rbml_w.end_tag(); + } + + // Region parameters + for param in generics.regions.iter() { + rbml_w.start_tag(tag_region_param_def); + + rbml_w.start_tag(tag_region_param_def_ident); + encode_name(rbml_w, param.name); + rbml_w.end_tag(); + + rbml_w.wr_tagged_str(tag_region_param_def_def_id, + def_to_string(param.def_id).as_slice()); + + rbml_w.wr_tagged_u64(tag_region_param_def_space, + param.space.to_uint() as u64); + + rbml_w.wr_tagged_u64(tag_region_param_def_index, + param.index as u64); + + for &bound_region in param.bounds.iter() { + encode_region(ecx, rbml_w, bound_region); + } + + rbml_w.end_tag(); + } + + rbml_w.end_tag(); +} + fn encode_method_ty_fields(ecx: &EncodeContext, rbml_w: &mut Encoder, method_ty: &ty::Method) { encode_def_id(rbml_w, method_ty.def_id); encode_name(rbml_w, method_ty.ident.name); - encode_ty_type_param_defs(rbml_w, ecx, &method_ty.generics.types, - tag_item_method_tps); + encode_generics(rbml_w, ecx, &method_ty.generics, + tag_method_ty_generics); encode_method_fty(ecx, rbml_w, &method_ty.fty); encode_visibility(rbml_w, method_ty.vis); encode_explicit_self(rbml_w, &method_ty.explicit_self); @@ -982,7 +1021,7 @@ fn encode_info_for_item(ecx: &EncodeContext, } else { encode_family(rbml_w, 'c'); } - encode_type(ecx, rbml_w, node_id_to_type(tcx, item.id)); + encode_bounds_and_type(rbml_w, ecx, &lookup_item_type(tcx, def_id)); encode_symbol(ecx, rbml_w, item.id); encode_name(rbml_w, item.ident.name); encode_path(rbml_w, path); @@ -1222,17 +1261,14 @@ fn encode_info_for_item(ecx: &EncodeContext, } } } - ItemTrait(_, _, ref super_traits, ref ms) => { + ItemTrait(_, _, _, ref ms) => { add_to_index(item, rbml_w, index); rbml_w.start_tag(tag_items_data_item); encode_def_id(rbml_w, def_id); encode_family(rbml_w, 'I'); encode_item_variances(rbml_w, ecx, item.id); let trait_def = ty::lookup_trait_def(tcx, def_id); - encode_ty_type_param_defs(rbml_w, ecx, - &trait_def.generics.types, - tag_items_data_item_ty_param_bounds); - encode_region_param_defs(rbml_w, &trait_def.generics.regions); + encode_generics(rbml_w, ecx, &trait_def.generics, tag_item_generics); encode_trait_ref(rbml_w, ecx, &*trait_def.trait_ref, tag_item_trait_ref); encode_name(rbml_w, item.ident.name); encode_attributes(rbml_w, item.attrs.as_slice()); @@ -1253,13 +1289,8 @@ fn encode_info_for_item(ecx: &EncodeContext, rbml_w.end_tag(); } encode_path(rbml_w, path.clone()); - // FIXME(#8559): This should use the tcx's supertrait cache instead of - // reading the AST's list, because the former has already filtered out - // the builtin-kinds-as-supertraits. See corresponding fixme in decoder. - for ast_trait_ref in super_traits.iter() { - let trait_ref = ty::node_id_to_trait_ref(ecx.tcx, ast_trait_ref.ref_id); - encode_trait_ref(rbml_w, ecx, &*trait_ref, tag_item_super_trait_ref); - } + + encode_bounds(rbml_w, ecx, &trait_def.bounds, tag_trait_def_bounds); // Encode the implementations of this trait. encode_extension_implementations(ecx, rbml_w, def_id); @@ -1371,6 +1402,7 @@ fn encode_info_for_foreign_item(ecx: &EncodeContext, rbml_w.start_tag(tag_items_data_item); encode_def_id(rbml_w, local_def(nitem.id)); + encode_visibility(rbml_w, nitem.vis); match nitem.node { ForeignItemFn(..) => { encode_family(rbml_w, style_fn_family(NormalFn)); @@ -1389,7 +1421,8 @@ fn encode_info_for_foreign_item(ecx: &EncodeContext, } else { encode_family(rbml_w, 'c'); } - encode_type(ecx, rbml_w, node_id_to_type(ecx.tcx, nitem.id)); + encode_bounds_and_type(rbml_w, ecx, + &lookup_item_type(ecx.tcx,local_def(nitem.id))); encode_symbol(ecx, rbml_w, nitem.id); encode_name(rbml_w, nitem.ident.name); } @@ -1433,7 +1466,7 @@ fn my_visit_foreign_item(ni: &ForeignItem, }); } -struct EncodeVisitor<'a,'b> { +struct EncodeVisitor<'a,'b:'a> { rbml_w_for_visit_item: &'a mut Encoder<'b>, ecx_ptr:*const int, index: &'a mut Vec<entry<i64>>, @@ -1737,7 +1770,7 @@ fn encode_unboxed_closures<'a>( } fn encode_struct_field_attrs(rbml_w: &mut Encoder, krate: &Crate) { - struct StructFieldVisitor<'a, 'b> { + struct StructFieldVisitor<'a, 'b:'a> { rbml_w: &'a mut Encoder<'b>, } @@ -1759,7 +1792,7 @@ fn encode_struct_field_attrs(rbml_w: &mut Encoder, krate: &Crate) { -struct ImplVisitor<'a,'b,'c> { +struct ImplVisitor<'a,'b:'a,'c:'a> { ecx: &'a EncodeContext<'b>, rbml_w: &'a mut Encoder<'c>, } diff --git a/src/librustc/metadata/tydecode.rs b/src/librustc/metadata/tydecode.rs index e6e7d8bf8d1..c18d2a7ebf4 100644 --- a/src/librustc/metadata/tydecode.rs +++ b/src/librustc/metadata/tydecode.rs @@ -147,6 +147,13 @@ pub fn parse_ty_data(data: &[u8], crate_num: ast::CrateNum, pos: uint, tcx: &ty: parse_ty(&mut st, conv) } +pub fn parse_region_data(data: &[u8], crate_num: ast::CrateNum, pos: uint, tcx: &ty::ctxt, + conv: conv_did) -> ty::Region { + debug!("parse_region_data {}", data_log_string(data, pos)); + let mut st = parse_state_from_data(data, crate_num, pos, tcx); + parse_region(&mut st, conv) +} + pub fn parse_bare_fn_ty_data(data: &[u8], crate_num: ast::CrateNum, pos: uint, tcx: &ty::ctxt, conv: conv_did) -> ty::BareFnTy { debug!("parse_bare_fn_ty_data {}", data_log_string(data, pos)); @@ -168,6 +175,27 @@ pub fn parse_substs_data(data: &[u8], crate_num: ast::CrateNum, pos: uint, tcx: parse_substs(&mut st, conv) } +pub fn parse_bounds_data(data: &[u8], crate_num: ast::CrateNum, + pos: uint, tcx: &ty::ctxt, conv: conv_did) + -> ty::ParamBounds { + let mut st = parse_state_from_data(data, crate_num, pos, tcx); + parse_bounds(&mut st, conv) +} + +pub fn parse_existential_bounds_data(data: &[u8], crate_num: ast::CrateNum, + pos: uint, tcx: &ty::ctxt, conv: conv_did) + -> ty::ExistentialBounds { + let mut st = parse_state_from_data(data, crate_num, pos, tcx); + parse_existential_bounds(&mut st, conv) +} + +pub fn parse_builtin_bounds_data(data: &[u8], crate_num: ast::CrateNum, + pos: uint, tcx: &ty::ctxt, conv: conv_did) + -> ty::BuiltinBounds { + let mut st = parse_state_from_data(data, crate_num, pos, tcx); + parse_builtin_bounds(&mut st, conv) +} + fn parse_size(st: &mut PState) -> Option<uint> { assert_eq!(next(st), '/'); @@ -355,9 +383,9 @@ fn parse_ty(st: &mut PState, conv: conv_did) -> ty::t { assert_eq!(next(st), '['); let def = parse_def(st, NominalType, |x,y| conv(x,y)); let substs = parse_substs(st, |x,y| conv(x,y)); - let bounds = parse_bounds(st, |x,y| conv(x,y)); + let bounds = parse_existential_bounds(st, |x,y| conv(x,y)); assert_eq!(next(st), ']'); - return ty::mk_trait(st.tcx, def, substs, bounds.builtin_bounds); + return ty::mk_trait(st.tcx, def, substs, bounds); } 'p' => { let did = parse_def(st, TypeParameter, |x,y| conv(x,y)); @@ -377,9 +405,9 @@ fn parse_ty(st: &mut PState, conv: conv_did) -> ty::t { return ty::mk_rptr(st.tcx, r, mt); } 'V' => { - let mt = parse_mt(st, |x,y| conv(x,y)); + let t = parse_ty(st, |x,y| conv(x,y)); let sz = parse_size(st); - return ty::mk_vec(st.tcx, mt, sz); + return ty::mk_vec(st.tcx, t, sz); } 'v' => { return ty::mk_str(st.tcx); @@ -515,14 +543,14 @@ fn parse_closure_ty(st: &mut PState, conv: conv_did) -> ty::ClosureTy { let fn_style = parse_fn_style(next(st)); let onceness = parse_onceness(next(st)); let store = parse_trait_store(st, |x,y| conv(x,y)); - let bounds = parse_bounds(st, |x,y| conv(x,y)); + let bounds = parse_existential_bounds(st, |x,y| conv(x,y)); let sig = parse_sig(st, |x,y| conv(x,y)); let abi = parse_abi_set(st); ty::ClosureTy { fn_style: fn_style, onceness: onceness, store: store, - bounds: bounds.builtin_bounds, + bounds: bounds, sig: sig, abi: abi, } @@ -575,12 +603,12 @@ pub fn parse_def_id(buf: &[u8]) -> ast::DefId { let crate_num = match uint::parse_bytes(crate_part, 10u) { Some(cn) => cn as ast::CrateNum, - None => fail!("internal error: parse_def_id: crate number expected, but found {:?}", + None => fail!("internal error: parse_def_id: crate number expected, found {:?}", crate_part) }; let def_num = match uint::parse_bytes(def_part, 10u) { Some(dn) => dn as ast::NodeId, - None => fail!("internal error: parse_def_id: id expected, but found {:?}", + None => fail!("internal error: parse_def_id: id expected, found {:?}", def_part) }; ast::DefId { krate: crate_num, node: def_num } @@ -601,7 +629,7 @@ fn parse_type_param_def(st: &mut PState, conv: conv_did) -> ty::TypeParameterDef assert_eq!(next(st), '|'); let index = parse_uint(st); assert_eq!(next(st), '|'); - let bounds = Rc::new(parse_bounds(st, |x,y| conv(x,y))); + let bounds = parse_bounds(st, |x,y| conv(x,y)); let default = parse_opt(st, |st| parse_ty(st, |x,y| conv(x,y))); ty::TypeParameterDef { @@ -614,27 +642,51 @@ fn parse_type_param_def(st: &mut PState, conv: conv_did) -> ty::TypeParameterDef } } -fn parse_bounds(st: &mut PState, conv: conv_did) -> ty::ParamBounds { - let mut param_bounds = ty::ParamBounds { - builtin_bounds: ty::empty_builtin_bounds(), - trait_bounds: Vec::new() - }; +fn parse_existential_bounds(st: &mut PState, conv: conv_did) -> ty::ExistentialBounds { + let r = parse_region(st, |x,y| conv(x,y)); + let bb = parse_builtin_bounds(st, conv); + return ty::ExistentialBounds { region_bound: r, builtin_bounds: bb }; +} + +fn parse_builtin_bounds(st: &mut PState, _conv: conv_did) -> ty::BuiltinBounds { + let mut builtin_bounds = ty::empty_builtin_bounds(); + loop { match next(st) { 'S' => { - param_bounds.builtin_bounds.add(ty::BoundSend); - } - 'O' => { - param_bounds.builtin_bounds.add(ty::BoundStatic); + builtin_bounds.add(ty::BoundSend); } 'Z' => { - param_bounds.builtin_bounds.add(ty::BoundSized); + builtin_bounds.add(ty::BoundSized); } 'P' => { - param_bounds.builtin_bounds.add(ty::BoundCopy); + builtin_bounds.add(ty::BoundCopy); } 'T' => { - param_bounds.builtin_bounds.add(ty::BoundSync); + builtin_bounds.add(ty::BoundSync); + } + '.' => { + return builtin_bounds; + } + c => { + fail!("parse_bounds: bad builtin bounds ('{}')", c) + } + } + } +} + +fn parse_bounds(st: &mut PState, conv: conv_did) -> ty::ParamBounds { + let builtin_bounds = parse_builtin_bounds(st, |x,y| conv(x,y)); + + let mut param_bounds = ty::ParamBounds { + opt_region_bound: None, + builtin_bounds: builtin_bounds, + trait_bounds: Vec::new() + }; + loop { + match next(st) { + 'R' => { + param_bounds.opt_region_bound = Some(parse_region(st, |x, y| conv (x, y))); } 'I' => { param_bounds.trait_bounds.push(Rc::new(parse_trait_ref(st, |x,y| conv(x,y)))); diff --git a/src/librustc/metadata/tyencode.rs b/src/librustc/metadata/tyencode.rs index fc5e267aa90..09be5094dc5 100644 --- a/src/librustc/metadata/tyencode.rs +++ b/src/librustc/metadata/tyencode.rs @@ -127,7 +127,7 @@ fn enc_region_substs(w: &mut SeekableMemWriter, cx: &ctxt, substs: &subst::Regio } } -fn enc_region(w: &mut SeekableMemWriter, cx: &ctxt, r: ty::Region) { +pub fn enc_region(w: &mut SeekableMemWriter, cx: &ctxt, r: ty::Region) { match r { ty::ReLateBound(id, br) => { mywrite!(w, "b[{}|", id); @@ -232,13 +232,11 @@ fn enc_sty(w: &mut SeekableMemWriter, cx: &ctxt, st: &ty::sty) { ty::ty_trait(box ty::TyTrait { def_id, ref substs, - bounds + ref bounds }) => { mywrite!(w, "x[{}|", (cx.ds)(def_id)); enc_substs(w, cx, substs); - let bounds = ty::ParamBounds {builtin_bounds: bounds, - trait_bounds: Vec::new()}; - enc_bounds(w, cx, &bounds); + enc_existential_bounds(w, cx, bounds); mywrite!(w, "]"); } ty::ty_tup(ref ts) => { @@ -254,9 +252,9 @@ fn enc_sty(w: &mut SeekableMemWriter, cx: &ctxt, st: &ty::sty) { enc_region(w, cx, r); enc_mt(w, cx, mt); } - ty::ty_vec(mt, sz) => { + ty::ty_vec(t, sz) => { mywrite!(w, "V"); - enc_mt(w, cx, mt); + enc_ty(w, cx, t); mywrite!(w, "/"); match sz { Some(n) => mywrite!(w, "{}|", n), @@ -292,6 +290,9 @@ fn enc_sty(w: &mut SeekableMemWriter, cx: &ctxt, st: &ty::sty) { ty::ty_err => { mywrite!(w, "e"); } + ty::ty_open(_) => { + cx.diag.handler().bug("unexpected type in enc_sty (ty_open)"); + } } } @@ -325,9 +326,7 @@ pub fn enc_closure_ty(w: &mut SeekableMemWriter, cx: &ctxt, ft: &ty::ClosureTy) enc_fn_style(w, ft.fn_style); enc_onceness(w, ft.onceness); enc_trait_store(w, cx, ft.store); - let bounds = ty::ParamBounds {builtin_bounds: ft.bounds, - trait_bounds: Vec::new()}; - enc_bounds(w, cx, &bounds); + enc_existential_bounds(w, cx, &ft.bounds); enc_fn_sig(w, cx, &ft.sig); enc_abi(w, ft.abi); } @@ -346,17 +345,32 @@ fn enc_fn_sig(w: &mut SeekableMemWriter, cx: &ctxt, fsig: &ty::FnSig) { enc_ty(w, cx, fsig.output); } -fn enc_bounds(w: &mut SeekableMemWriter, cx: &ctxt, bs: &ty::ParamBounds) { - for bound in bs.builtin_bounds.iter() { +pub fn enc_builtin_bounds(w: &mut SeekableMemWriter, _cx: &ctxt, bs: &ty::BuiltinBounds) { + for bound in bs.iter() { match bound { ty::BoundSend => mywrite!(w, "S"), - ty::BoundStatic => mywrite!(w, "O"), ty::BoundSized => mywrite!(w, "Z"), ty::BoundCopy => mywrite!(w, "P"), ty::BoundSync => mywrite!(w, "T"), } } + mywrite!(w, "."); +} + +pub fn enc_existential_bounds(w: &mut SeekableMemWriter, cx: &ctxt, bs: &ty::ExistentialBounds) { + enc_region(w, cx, bs.region_bound); + enc_builtin_bounds(w, cx, &bs.builtin_bounds); +} + +pub fn enc_bounds(w: &mut SeekableMemWriter, cx: &ctxt, bs: &ty::ParamBounds) { + enc_builtin_bounds(w, cx, &bs.builtin_bounds); + + for &r in bs.opt_region_bound.iter() { + mywrite!(w, "R"); + enc_region(w, cx, r); + } + for tp in bs.trait_bounds.iter() { mywrite!(w, "I"); enc_trait_ref(w, cx, &**tp); @@ -369,6 +383,6 @@ pub fn enc_type_param_def(w: &mut SeekableMemWriter, cx: &ctxt, v: &ty::TypePara mywrite!(w, "{}:{}|{}|{}|", token::get_ident(v.ident), (cx.ds)(v.def_id), v.space.to_uint(), v.index); - enc_bounds(w, cx, &*v.bounds); + enc_bounds(w, cx, &v.bounds); enc_opt(w, v.default, |w, t| enc_ty(w, cx, t)); } diff --git a/src/librustc/middle/astencode.rs b/src/librustc/middle/astencode.rs index 3359d7ed030..6acd79f2976 100644 --- a/src/librustc/middle/astencode.rs +++ b/src/librustc/middle/astencode.rs @@ -495,25 +495,7 @@ impl tr for def::Def { } // ______________________________________________________________________ -// Encoding and decoding of adjustment information - -impl tr for ty::AutoDerefRef { - fn tr(&self, xcx: &ExtendedDecodeContext) -> ty::AutoDerefRef { - ty::AutoDerefRef { - autoderefs: self.autoderefs, - autoref: match self.autoref { - Some(ref autoref) => Some(autoref.tr(xcx)), - None => None - } - } - } -} - -impl tr for ty::AutoRef { - fn tr(&self, xcx: &ExtendedDecodeContext) -> ty::AutoRef { - self.map_region(|r| r.tr(xcx)) - } -} +// Encoding and decoding of ancillary information impl tr for ty::Region { fn tr(&self, xcx: &ExtendedDecodeContext) -> ty::Region { @@ -960,7 +942,12 @@ trait rbml_writer_helpers { ecx: &e::EncodeContext, pty: ty::Polytype); fn emit_substs(&mut self, ecx: &e::EncodeContext, substs: &subst::Substs); + fn emit_existential_bounds(&mut self, ecx: &e::EncodeContext, bounds: &ty::ExistentialBounds); + fn emit_builtin_bounds(&mut self, ecx: &e::EncodeContext, bounds: &ty::BuiltinBounds); fn emit_auto_adjustment(&mut self, ecx: &e::EncodeContext, adj: &ty::AutoAdjustment); + fn emit_autoref(&mut self, ecx: &e::EncodeContext, autoref: &ty::AutoRef); + fn emit_auto_deref_ref(&mut self, ecx: &e::EncodeContext, auto_deref_ref: &ty::AutoDerefRef); + fn emit_unsize_kind(&mut self, ecx: &e::EncodeContext, uk: &ty::UnsizeKind); } impl<'a> rbml_writer_helpers for Encoder<'a> { @@ -1016,6 +1003,18 @@ impl<'a> rbml_writer_helpers for Encoder<'a> { }); } + fn emit_existential_bounds(&mut self, ecx: &e::EncodeContext, bounds: &ty::ExistentialBounds) { + self.emit_opaque(|this| Ok(tyencode::enc_existential_bounds(this.writer, + &ecx.ty_str_ctxt(), + bounds))); + } + + fn emit_builtin_bounds(&mut self, ecx: &e::EncodeContext, bounds: &ty::BuiltinBounds) { + self.emit_opaque(|this| Ok(tyencode::enc_builtin_bounds(this.writer, + &ecx.ty_str_ctxt(), + bounds))); + } + fn emit_substs(&mut self, ecx: &e::EncodeContext, substs: &subst::Substs) { self.emit_opaque(|this| Ok(tyencode::enc_substs(this.writer, &ecx.ty_str_ctxt(), @@ -1035,16 +1034,92 @@ impl<'a> rbml_writer_helpers for Encoder<'a> { ty::AutoDerefRef(ref auto_deref_ref) => { this.emit_enum_variant("AutoDerefRef", 1, 1, |this| { - this.emit_enum_variant_arg(0, |this| auto_deref_ref.encode(this)) + this.emit_enum_variant_arg(0, + |this| Ok(this.emit_auto_deref_ref(ecx, auto_deref_ref))) }) } + } + }); + } + + fn emit_autoref(&mut self, ecx: &e::EncodeContext, autoref: &ty::AutoRef) { + use serialize::Encoder; - ty::AutoObject(store, b, def_id, ref substs) => { - this.emit_enum_variant("AutoObject", 2, 4, |this| { - this.emit_enum_variant_arg(0, |this| store.encode(this)); - this.emit_enum_variant_arg(1, |this| b.encode(this)); - this.emit_enum_variant_arg(2, |this| def_id.encode(this)); - this.emit_enum_variant_arg(3, |this| Ok(this.emit_substs(ecx, substs))) + self.emit_enum("AutoRef", |this| { + match autoref { + &ty::AutoPtr(r, m, None) => { + this.emit_enum_variant("AutoPtr", 0, 3, |this| { + this.emit_enum_variant_arg(0, |this| r.encode(this)); + this.emit_enum_variant_arg(1, |this| m.encode(this)); + this.emit_enum_variant_arg(2, + |this| this.emit_option(|this| this.emit_option_none())) + }) + } + &ty::AutoPtr(r, m, Some(box ref a)) => { + this.emit_enum_variant("AutoPtr", 0, 3, |this| { + this.emit_enum_variant_arg(0, |this| r.encode(this)); + this.emit_enum_variant_arg(1, |this| m.encode(this)); + this.emit_enum_variant_arg(2, |this| this.emit_option( + |this| this.emit_option_some(|this| Ok(this.emit_autoref(ecx, a))))) + }) + } + &ty::AutoUnsize(ref uk) => { + this.emit_enum_variant("AutoUnsize", 1, 1, |this| { + this.emit_enum_variant_arg(0, |this| Ok(this.emit_unsize_kind(ecx, uk))) + }) + } + &ty::AutoUnsizeUniq(ref uk) => { + this.emit_enum_variant("AutoUnsizeUniq", 2, 1, |this| { + this.emit_enum_variant_arg(0, |this| Ok(this.emit_unsize_kind(ecx, uk))) + }) + } + &ty::AutoUnsafe(m) => { + this.emit_enum_variant("AutoUnsafe", 3, 1, |this| { + this.emit_enum_variant_arg(0, |this| m.encode(this)) + }) + } + } + }); + } + + fn emit_auto_deref_ref(&mut self, ecx: &e::EncodeContext, auto_deref_ref: &ty::AutoDerefRef) { + use serialize::Encoder; + + self.emit_struct("AutoDerefRef", 2, |this| { + this.emit_struct_field("autoderefs", 0, |this| auto_deref_ref.autoderefs.encode(this)); + this.emit_struct_field("autoref", 1, |this| { + this.emit_option(|this| { + match auto_deref_ref.autoref { + None => this.emit_option_none(), + Some(ref a) => this.emit_option_some(|this| Ok(this.emit_autoref(ecx, a))), + } + }) + }) + }); + } + + fn emit_unsize_kind(&mut self, ecx: &e::EncodeContext, uk: &ty::UnsizeKind) { + use serialize::Encoder; + + self.emit_enum("UnsizeKind", |this| { + match *uk { + ty::UnsizeLength(len) => { + this.emit_enum_variant("UnsizeLength", 0, 1, |this| { + this.emit_enum_variant_arg(0, |this| len.encode(this)) + }) + } + ty::UnsizeStruct(box ref uk, idx) => { + this.emit_enum_variant("UnsizeStruct", 1, 2, |this| { + this.emit_enum_variant_arg(0, |this| Ok(this.emit_unsize_kind(ecx, uk))); + this.emit_enum_variant_arg(1, |this| idx.encode(this)) + }) + } + ty::UnsizeVtable(ref b, def_id, ref substs) => { + this.emit_enum_variant("UnsizeVtable", 2, 3, |this| { + this.emit_enum_variant_arg( + 0, |this| Ok(this.emit_existential_bounds(ecx, b))); + this.emit_enum_variant_arg(1, |this| def_id.encode(this)); + this.emit_enum_variant_arg(2, |this| Ok(this.emit_substs(ecx, substs))) }) } } @@ -1071,7 +1146,7 @@ impl<'a> write_tag_and_id for Encoder<'a> { } } -struct SideTableEncodingIdVisitor<'a,'b> { +struct SideTableEncodingIdVisitor<'a,'b:'a> { ecx_ptr: *const libc::c_void, new_rbml_w: &'a mut Encoder<'b>, } @@ -1227,9 +1302,30 @@ fn encode_side_tables_for_id(ecx: &e::EncodeContext, }) } - for &adj in tcx.adjustments.borrow().find(&id).iter() { - match *adj { - ty::AutoDerefRef(adj) => { + for &adjustment in tcx.adjustments.borrow().find(&id).iter() { + match *adjustment { + _ if ty::adjust_is_object(adjustment) => { + let method_call = MethodCall::autoobject(id); + for &method in tcx.method_map.borrow().find(&method_call).iter() { + rbml_w.tag(c::tag_table_method_map, |rbml_w| { + rbml_w.id(id); + rbml_w.tag(c::tag_table_val, |rbml_w| { + encode_method_callee(ecx, rbml_w, method_call.adjustment, method) + }) + }) + } + + for &dr in tcx.vtable_map.borrow().find(&method_call).iter() { + rbml_w.tag(c::tag_table_vtable_map, |rbml_w| { + rbml_w.id(id); + rbml_w.tag(c::tag_table_val, |rbml_w| { + encode_vtable_res_with_key(ecx, rbml_w, method_call.adjustment, dr); + }) + }) + } + } + ty::AutoDerefRef(ref adj) => { + assert!(!ty::adjust_is_object(adjustment)); for autoderef in range(0, adj.autoderefs) { let method_call = MethodCall::autoderef(id, autoderef); for &method in tcx.method_map.borrow().find(&method_call).iter() { @@ -1253,33 +1349,15 @@ fn encode_side_tables_for_id(ecx: &e::EncodeContext, } } } - ty::AutoObject(..) => { - let method_call = MethodCall::autoobject(id); - for &method in tcx.method_map.borrow().find(&method_call).iter() { - rbml_w.tag(c::tag_table_method_map, |rbml_w| { - rbml_w.id(id); - rbml_w.tag(c::tag_table_val, |rbml_w| { - encode_method_callee(ecx, rbml_w, method_call.adjustment, method) - }) - }) - } - - for &dr in tcx.vtable_map.borrow().find(&method_call).iter() { - rbml_w.tag(c::tag_table_vtable_map, |rbml_w| { - rbml_w.id(id); - rbml_w.tag(c::tag_table_val, |rbml_w| { - encode_vtable_res_with_key(ecx, rbml_w, method_call.adjustment, dr); - }) - }) - } + _ => { + assert!(!ty::adjust_is_object(adjustment)); } - _ => {} } rbml_w.tag(c::tag_table_adjustments, |rbml_w| { rbml_w.id(id); rbml_w.tag(c::tag_table_val, |rbml_w| { - rbml_w.emit_auto_adjustment(ecx, adj); + rbml_w.emit_auto_adjustment(ecx, adjustment); }) }) } @@ -1317,10 +1395,14 @@ trait rbml_decoder_decoder_helpers { -> ty::TypeParameterDef; fn read_polytype(&mut self, xcx: &ExtendedDecodeContext) -> ty::Polytype; + fn read_existential_bounds(&mut self, xcx: &ExtendedDecodeContext) -> ty::ExistentialBounds; fn read_substs(&mut self, xcx: &ExtendedDecodeContext) -> subst::Substs; fn read_auto_adjustment(&mut self, xcx: &ExtendedDecodeContext) -> ty::AutoAdjustment; fn read_unboxed_closure(&mut self, xcx: &ExtendedDecodeContext) -> ty::UnboxedClosure; + fn read_auto_deref_ref(&mut self, xcx: &ExtendedDecodeContext) -> ty::AutoDerefRef; + fn read_autoref(&mut self, xcx: &ExtendedDecodeContext) -> ty::AutoRef; + fn read_unsize_kind(&mut self, xcx: &ExtendedDecodeContext) -> ty::UnsizeKind; fn convert_def_id(&mut self, xcx: &ExtendedDecodeContext, source: DefIdSource, @@ -1448,6 +1530,17 @@ impl<'a> rbml_decoder_decoder_helpers for reader::Decoder<'a> { }).unwrap() } + fn read_existential_bounds(&mut self, xcx: &ExtendedDecodeContext) -> ty::ExistentialBounds + { + self.read_opaque(|this, doc| { + Ok(tydecode::parse_existential_bounds_data(doc.data, + xcx.dcx.cdata.cnum, + doc.start, + xcx.dcx.tcx, + |s, a| this.convert_def_id(xcx, s, a))) + }).unwrap() + } + fn read_substs(&mut self, xcx: &ExtendedDecodeContext) -> subst::Substs { self.read_opaque(|this, doc| { Ok(tydecode::parse_substs_data(doc.data, @@ -1460,34 +1553,129 @@ impl<'a> rbml_decoder_decoder_helpers for reader::Decoder<'a> { fn read_auto_adjustment(&mut self, xcx: &ExtendedDecodeContext) -> ty::AutoAdjustment { self.read_enum("AutoAdjustment", |this| { - let variants = ["AutoAddEnv", "AutoDerefRef", "AutoObject"]; + let variants = ["AutoAddEnv", "AutoDerefRef"]; this.read_enum_variant(variants, |this, i| { Ok(match i { 0 => { let store: ty::TraitStore = this.read_enum_variant_arg(0, |this| Decodable::decode(this)).unwrap(); - ty:: AutoAddEnv(store.tr(xcx)) + ty::AutoAddEnv(store.tr(xcx)) } 1 => { let auto_deref_ref: ty::AutoDerefRef = + this.read_enum_variant_arg(0, + |this| Ok(this.read_auto_deref_ref(xcx))).unwrap(); + + ty::AutoDerefRef(auto_deref_ref) + } + _ => fail!("bad enum variant for ty::AutoAdjustment") + }) + }) + }).unwrap() + } + + fn read_auto_deref_ref(&mut self, xcx: &ExtendedDecodeContext) -> ty::AutoDerefRef { + self.read_struct("AutoDerefRef", 2, |this| { + Ok(ty::AutoDerefRef { + autoderefs: this.read_struct_field("autoderefs", 0, |this| { + Decodable::decode(this) + }).unwrap(), + autoref: this.read_struct_field("autoref", 1, |this| { + this.read_option(|this, b| { + if b { + Ok(Some(this.read_autoref(xcx))) + } else { + Ok(None) + } + }) + }).unwrap(), + }) + }).unwrap() + } + + fn read_autoref(&mut self, xcx: &ExtendedDecodeContext) -> ty::AutoRef { + self.read_enum("AutoRef", |this| { + let variants = ["AutoPtr", + "AutoUnsize", + "AutoUnsizeUniq", + "AutoUnsafe"]; + this.read_enum_variant(variants, |this, i| { + Ok(match i { + 0 => { + let r: ty::Region = this.read_enum_variant_arg(0, |this| Decodable::decode(this)).unwrap(); + let m: ast::Mutability = + this.read_enum_variant_arg(1, |this| Decodable::decode(this)).unwrap(); + let a: Option<Box<ty::AutoRef>> = + this.read_enum_variant_arg(2, |this| this.read_option(|this, b| { + if b { + Ok(Some(box this.read_autoref(xcx))) + } else { + Ok(None) + } + })).unwrap(); + + ty::AutoPtr(r.tr(xcx), m, a) + } + 1 => { + let uk: ty::UnsizeKind = + this.read_enum_variant_arg(0, + |this| Ok(this.read_unsize_kind(xcx))).unwrap(); - ty::AutoDerefRef(auto_deref_ref.tr(xcx)) + ty::AutoUnsize(uk) } 2 => { - let store: ty::TraitStore = + let uk: ty::UnsizeKind = + this.read_enum_variant_arg(0, + |this| Ok(this.read_unsize_kind(xcx))).unwrap(); + + ty::AutoUnsizeUniq(uk) + } + 3 => { + let m: ast::Mutability = + this.read_enum_variant_arg(0, |this| Decodable::decode(this)).unwrap(); + + ty::AutoUnsafe(m) + } + _ => fail!("bad enum variant for ty::AutoRef") + }) + }) + }).unwrap() + } + + fn read_unsize_kind(&mut self, xcx: &ExtendedDecodeContext) -> ty::UnsizeKind { + self.read_enum("UnsizeKind", |this| { + let variants = ["UnsizeLength", "UnsizeStruct", "UnsizeVtable"]; + this.read_enum_variant(variants, |this, i| { + Ok(match i { + 0 => { + let len: uint = this.read_enum_variant_arg(0, |this| Decodable::decode(this)).unwrap(); - let b: ty::BuiltinBounds = + + ty::UnsizeLength(len) + } + 1 => { + let uk: ty::UnsizeKind = + this.read_enum_variant_arg(0, + |this| Ok(this.read_unsize_kind(xcx))).unwrap(); + let idx: uint = this.read_enum_variant_arg(1, |this| Decodable::decode(this)).unwrap(); + + ty::UnsizeStruct(box uk, idx) + } + 2 => { + let b = + this.read_enum_variant_arg( + 0, |this| Ok(this.read_existential_bounds(xcx))).unwrap(); let def_id: ast::DefId = - this.read_enum_variant_arg(2, |this| Decodable::decode(this)).unwrap(); - let substs = this.read_enum_variant_arg(3, |this| Ok(this.read_substs(xcx))) - .unwrap(); + this.read_enum_variant_arg(1, |this| Decodable::decode(this)).unwrap(); + let substs = this.read_enum_variant_arg(2, + |this| Ok(this.read_substs(xcx))).unwrap(); - ty::AutoObject(store.tr(xcx), b, def_id.tr(xcx), substs) + ty::UnsizeVtable(b, def_id.tr(xcx), substs) } - _ => fail!("bad enum variant for ty::AutoAdjustment") + _ => fail!("bad enum variant for ty::UnsizeKind") }) }) }).unwrap() diff --git a/src/librustc/middle/borrowck/gather_loans/restrictions.rs b/src/librustc/middle/borrowck/gather_loans/restrictions.rs index d1e9a2c0a74..a686084a4a2 100644 --- a/src/librustc/middle/borrowck/gather_loans/restrictions.rs +++ b/src/librustc/middle/borrowck/gather_loans/restrictions.rs @@ -70,18 +70,19 @@ impl<'a> RestrictionsContext<'a> { mc::cat_arg(local_id) => { // R-Variable, locally declared let lp = Rc::new(LpVar(local_id)); - SafeIf(lp.clone(), vec!(lp)) + SafeIf(lp.clone(), vec![lp]) } mc::cat_upvar(upvar_id, _) => { // R-Variable, captured into closure let lp = Rc::new(LpUpvar(upvar_id)); - SafeIf(lp.clone(), vec!(lp)) + SafeIf(lp.clone(), vec![lp]) } - mc::cat_copied_upvar(..) => { - // FIXME(#2152) allow mutation of upvars - Safe + mc::cat_copied_upvar(mc::CopiedUpvar { upvar_id, .. }) => { + // R-Variable, copied/moved into closure + let lp = Rc::new(LpVar(upvar_id)); + SafeIf(lp.clone(), vec![lp]) } mc::cat_downcast(cmt_base) => { diff --git a/src/librustc/middle/borrowck/mod.rs b/src/librustc/middle/borrowck/mod.rs index 9de55ccc468..230786924d7 100644 --- a/src/librustc/middle/borrowck/mod.rs +++ b/src/librustc/middle/borrowck/mod.rs @@ -428,16 +428,15 @@ impl<'a> BorrowckCtxt<'a> { adj: &ty::AutoAdjustment) -> mc::cmt { let r = match *adj { - ty::AutoAddEnv(..) | ty::AutoObject(..) => { - // no autoderefs - self.mc().cat_expr_unadjusted(expr) - } - ty::AutoDerefRef( ty::AutoDerefRef { autoderefs: autoderefs, ..}) => { self.mc().cat_expr_autoderefd(expr, autoderefs) } + ty::AutoAddEnv(..) => { + // no autoderefs + self.mc().cat_expr_unadjusted(expr) + } }; match r { diff --git a/src/librustc/middle/cfg/construct.rs b/src/librustc/middle/cfg/construct.rs index dba873a72b6..2acc92cd227 100644 --- a/src/librustc/middle/cfg/construct.rs +++ b/src/librustc/middle/cfg/construct.rs @@ -79,12 +79,14 @@ impl<'a> CFGBuilder<'a> { fn stmt(&mut self, stmt: Gc<ast::Stmt>, pred: CFGIndex) -> CFGIndex { match stmt.node { - ast::StmtDecl(ref decl, _) => { - self.decl(&**decl, pred) + ast::StmtDecl(ref decl, id) => { + let exit = self.decl(&**decl, pred); + self.add_node(id, [exit]) } - ast::StmtExpr(ref expr, _) | ast::StmtSemi(ref expr, _) => { - self.expr(expr.clone(), pred) + ast::StmtExpr(ref expr, id) | ast::StmtSemi(ref expr, id) => { + let exit = self.expr(expr.clone(), pred); + self.add_node(id, [exit]) } ast::StmtMac(..) => { @@ -465,7 +467,6 @@ impl<'a> CFGBuilder<'a> { ast::ExprCast(e, _) | ast::ExprUnary(_, e) | ast::ExprParen(e) | - ast::ExprVstore(e, _) | ast::ExprField(e, _, _) => { self.straightline(expr, pred, [e]) } @@ -473,14 +474,15 @@ impl<'a> CFGBuilder<'a> { ast::ExprInlineAsm(ref inline_asm) => { let inputs = inline_asm.inputs.iter(); let outputs = inline_asm.outputs.iter(); - fn extract_expr<A>(&(_, expr): &(A, Gc<ast::Expr>)) -> Gc<ast::Expr> { expr } let post_inputs = self.exprs(inputs.map(|a| { debug!("cfg::construct InlineAsm id:{} input:{:?}", expr.id, a); - extract_expr(a) + let &(_, expr) = a; + expr }), pred); let post_outputs = self.exprs(outputs.map(|a| { debug!("cfg::construct InlineAsm id:{} output:{:?}", expr.id, a); - extract_expr(a) + let &(_, expr, _) = a; + expr }), post_inputs); self.add_node(expr.id, [post_outputs]) } diff --git a/src/librustc/middle/check_const.rs b/src/librustc/middle/check_const.rs index 283d667f3a8..629aba8092c 100644 --- a/src/librustc/middle/check_const.rs +++ b/src/librustc/middle/check_const.rs @@ -62,7 +62,7 @@ fn check_item(v: &mut CheckCrateVisitor, it: &Item, _is_const: bool) { fn check_pat(v: &mut CheckCrateVisitor, p: &Pat, _is_const: bool) { fn is_str(e: &Expr) -> bool { match e.node { - ExprVstore(expr, ExprVstoreUniq) => { + ExprBox(_, expr) => { match expr.node { ExprLit(lit) => ast_util::lit_is_str(lit), _ => false, @@ -169,8 +169,6 @@ fn check_expr(v: &mut CheckCrateVisitor, e: &Expr, is_const: bool) { None => {} } } - ExprVstore(_, ExprVstoreMutSlice) | - ExprVstore(_, ExprVstoreSlice) | ExprVec(_) | ExprAddrOf(MutImmutable, _) | ExprParen(..) | @@ -179,13 +177,14 @@ fn check_expr(v: &mut CheckCrateVisitor, e: &Expr, is_const: bool) { ExprTup(..) | ExprRepeat(..) | ExprStruct(..) => { } - ExprAddrOf(..) => { - span_err!(v.tcx.sess, e.span, E0017, - "references in constants may only refer to immutable values"); - }, - ExprVstore(_, ExprVstoreUniq) => { - span_err!(v.tcx.sess, e.span, E0018, - "cannot allocate vectors in constant expressions"); + ExprAddrOf(_, inner) => { + match inner.node { + // Mutable slices are allowed. + ExprVec(_) => {} + _ => span_err!(v.tcx.sess, e.span, E0017, + "references in constants may only refer to immutable values") + + } }, _ => { diff --git a/src/librustc/middle/check_match.rs b/src/librustc/middle/check_match.rs index 230668e7066..5cb7651e99a 100644 --- a/src/librustc/middle/check_match.rs +++ b/src/librustc/middle/check_match.rs @@ -1018,7 +1018,7 @@ fn check_legality_of_bindings_in_at_patterns(cx: &MatchCheckCtxt, pat: &Pat) { visitor.visit_pat(pat, true); } -struct AtBindingPatternVisitor<'a,'b> { +struct AtBindingPatternVisitor<'a,'b:'a> { cx: &'a MatchCheckCtxt<'b>, } diff --git a/src/librustc/middle/check_static.rs b/src/librustc/middle/check_static.rs index 8985e633ad4..042a5b8f60a 100644 --- a/src/librustc/middle/check_static.rs +++ b/src/librustc/middle/check_static.rs @@ -107,21 +107,19 @@ impl<'a> Visitor<bool> for CheckStaticVisitor<'a> { match e.node { ast::ExprField(..) | ast::ExprVec(..) | - ast::ExprBlock(..) | ast::ExprTup(..) | - ast::ExprVstore(_, ast::ExprVstoreSlice) => { + ast::ExprBlock(..) | ast::ExprTup(..) => { visit::walk_expr(self, e, is_const); } - ast::ExprVstore(_, ast::ExprVstoreMutSlice) => { + ast::ExprAddrOf(ast::MutMutable, _) => { span_err!(self.tcx.sess, e.span, E0020, "static items are not allowed to have mutable slices"); - }, + }, ast::ExprUnary(ast::UnBox, _) => { span_err!(self.tcx.sess, e.span, E0021, "static items are not allowed to have managed pointers"); } ast::ExprBox(..) | - ast::ExprUnary(ast::UnUniq, _) | - ast::ExprVstore(_, ast::ExprVstoreUniq) => { + ast::ExprUnary(ast::UnUniq, _) => { span_err!(self.tcx.sess, e.span, E0022, "static items are not allowed to have custom pointers"); } diff --git a/src/librustc/middle/const_eval.rs b/src/librustc/middle/const_eval.rs index 3c9fb1f7624..03a7021b70d 100644 --- a/src/librustc/middle/const_eval.rs +++ b/src/librustc/middle/const_eval.rs @@ -206,14 +206,6 @@ impl<'a> ConstEvalVisitor<'a> { ast::ExprVec(ref es) => join_all(es.iter().map(|e| self.classify(&**e))), - ast::ExprVstore(ref e, vstore) => { - match vstore { - ast::ExprVstoreSlice => self.classify(&**e), - ast::ExprVstoreUniq | - ast::ExprVstoreMutSlice => non_const - } - } - ast::ExprStruct(_, ref fs, None) => { let cs = fs.iter().map(|f| self.classify(&*f.expr)); join_all(cs) @@ -517,26 +509,29 @@ pub fn eval_const_expr_partial<T: ty::ExprTyProvider>(tcx: &T, e: &Expr) match ty::get(ety).sty { ty::ty_float(_) => { match val { + const_bool(b) => Ok(const_float(b as f64)), const_uint(u) => Ok(const_float(u as f64)), const_int(i) => Ok(const_float(i as f64)), const_float(f) => Ok(const_float(f)), - _ => Err("can't cast float to str".to_string()), + _ => Err("can't cast this type to float".to_string()), } } ty::ty_uint(_) => { match val { + const_bool(b) => Ok(const_uint(b as u64)), const_uint(u) => Ok(const_uint(u)), const_int(i) => Ok(const_uint(i as u64)), const_float(f) => Ok(const_uint(f as u64)), - _ => Err("can't cast str to uint".to_string()), + _ => Err("can't cast this type to uint".to_string()), } } - ty::ty_int(_) | ty::ty_bool => { + ty::ty_int(_) => { match val { + const_bool(b) => Ok(const_int(b as i64)), const_uint(u) => Ok(const_int(u as i64)), const_int(i) => Ok(const_int(i)), const_float(f) => Ok(const_int(f as i64)), - _ => Err("can't cast str to int".to_string()), + _ => Err("can't cast this type to int".to_string()), } } _ => Err("can't cast this type".to_string()) @@ -551,8 +546,6 @@ pub fn eval_const_expr_partial<T: ty::ExprTyProvider>(tcx: &T, e: &Expr) } } ExprLit(ref lit) => Ok(lit_to_const(&**lit)), - // If we have a vstore, just keep going; it has to be a string - ExprVstore(ref e, _) => eval_const_expr_partial(tcx, &**e), ExprParen(ref e) => eval_const_expr_partial(tcx, &**e), ExprBlock(ref block) => { match block.expr { diff --git a/src/librustc/middle/dataflow.rs b/src/librustc/middle/dataflow.rs index 7c5b001354d..91c227cd5bc 100644 --- a/src/librustc/middle/dataflow.rs +++ b/src/librustc/middle/dataflow.rs @@ -80,11 +80,18 @@ pub trait DataFlowOperator : BitwiseOperator { fn initial_value(&self) -> bool; } +#[cfg(stage0)] struct PropagationContext<'a, 'b, O> { dfcx: &'a mut DataFlowContext<'b, O>, changed: bool } +#[cfg(not(stage0))] +struct PropagationContext<'a, 'b:'a, O:'a> { + dfcx: &'a mut DataFlowContext<'b, O>, + changed: bool +} + fn to_cfgidx_or_die(id: ast::NodeId, index: &NodeMap<CFGIndex>) -> CFGIndex { let opt_cfgindex = index.find(&id).map(|&i|i); opt_cfgindex.unwrap_or_else(|| { @@ -458,7 +465,7 @@ impl<'a, O:DataFlowOperator+Clone+'static> DataFlowContext<'a, O> { }); } - fn pretty_print_to(&self, wr: Box<io::Writer>, + fn pretty_print_to(&self, wr: Box<io::Writer+'static>, blk: &ast::Block) -> io::IoResult<()> { let mut ps = pprust::rust_printer_annotated(wr, self); try!(ps.cbox(pprust::indent_unit)); diff --git a/src/librustc/middle/dead.rs b/src/librustc/middle/dead.rs index 9a7bfb0e6dc..0dfdf455743 100644 --- a/src/librustc/middle/dead.rs +++ b/src/librustc/middle/dead.rs @@ -195,9 +195,10 @@ impl<'a> MarkSymbolVisitor<'a> { ast_map::NodeItem(item) => { match item.node { ast::ItemStruct(..) => { - let has_extern_repr = item.attrs.iter().fold(attr::ReprAny, |acc, attr| { - attr::find_repr_attr(self.tcx.sess.diagnostic(), attr, acc) - }) == attr::ReprExtern; + let has_extern_repr = item.attrs.iter().fold(false, |acc, attr| { + acc || attr::find_repr_attrs(self.tcx.sess.diagnostic(), attr) + .iter().any(|&x| x == attr::ReprExtern) + }); visit::walk_item(self, &*item, MarkSymbolVisitorContext { struct_has_extern_repr: has_extern_repr, diff --git a/src/librustc/middle/effect.rs b/src/librustc/middle/effect.rs index e6160038b1d..dc4ecb35830 100644 --- a/src/librustc/middle/effect.rs +++ b/src/librustc/middle/effect.rs @@ -122,7 +122,7 @@ impl<'a> Visitor<()> for EffectCheckVisitor<'a> { // we need to create a new context, when we're // - outside `unsafe` and found a `unsafe` block // (normal case) - // - inside `unsafe` but found an `unsafe` block + // - inside `unsafe`, found an `unsafe` block // created internally to the compiler // // The second case is necessary to ensure that the diff --git a/src/librustc/middle/expr_use_visitor.rs b/src/librustc/middle/expr_use_visitor.rs index 6caf54790d1..ae9a3fa6a67 100644 --- a/src/librustc/middle/expr_use_visitor.rs +++ b/src/librustc/middle/expr_use_visitor.rs @@ -130,16 +130,11 @@ impl OverloadedCallType { fn from_method_id(tcx: &ty::ctxt, method_id: ast::DefId) -> OverloadedCallType { - let method_descriptor = - match tcx.impl_or_trait_items.borrow_mut().find(&method_id) { - Some(&ty::MethodTraitItem(ref method_descriptor)) => { - (*method_descriptor).clone() - } - None => { - tcx.sess.bug("overloaded call method wasn't in method \ - map") - } - }; + let method_descriptor = match ty::impl_or_trait_item(tcx, method_id) { + ty::MethodTraitItem(ref method_descriptor) => { + (*method_descriptor).clone() + } + }; let impl_id = match method_descriptor.container { ty::TraitContainer(_) => { tcx.sess.bug("statically resolved overloaded call method \ @@ -157,6 +152,19 @@ impl OverloadedCallType { OverloadedCallType::from_trait_id(tcx, trait_ref.def_id) } + fn from_unboxed_closure(tcx: &ty::ctxt, closure_did: ast::DefId) + -> OverloadedCallType { + let trait_did = + tcx.unboxed_closures + .borrow() + .find(&closure_did) + .expect("OverloadedCallType::from_unboxed_closure: didn't \ + find closure id") + .kind + .trait_did(tcx); + OverloadedCallType::from_trait_id(tcx, trait_did) + } + fn from_method_origin(tcx: &ty::ctxt, origin: &MethodOrigin) -> OverloadedCallType { match *origin { @@ -164,7 +172,7 @@ impl OverloadedCallType { OverloadedCallType::from_method_id(tcx, def_id) } MethodStaticUnboxedClosure(def_id) => { - OverloadedCallType::from_method_id(tcx, def_id) + OverloadedCallType::from_unboxed_closure(tcx, def_id) } MethodParam(ref method_param) => { OverloadedCallType::from_trait_id(tcx, method_param.trait_id) @@ -184,10 +192,18 @@ impl OverloadedCallType { // supplies types from the tree. After type checking is complete, you // can just use the tcx as the typer. +#[cfg(stage0)] pub struct ExprUseVisitor<'d,'t,TYPER> { typer: &'t TYPER, mc: mc::MemCategorizationContext<'t,TYPER>, - delegate: &'d mut Delegate, + delegate: &'d mut Delegate+'d, +} + +#[cfg(not(stage0))] +pub struct ExprUseVisitor<'d,'t,TYPER:'t> { + typer: &'t TYPER, + mc: mc::MemCategorizationContext<'t,TYPER>, + delegate: &'d mut Delegate+'d, } // If the TYPER results in an error, it's because the type check @@ -226,7 +242,7 @@ impl<'d,'t,TYPER:mc::Typer> ExprUseVisitor<'d,'t,TYPER> { decl: &ast::FnDecl, body: &ast::Block) { for arg in decl.inputs.iter() { - let arg_ty = ty::node_id_to_type(self.tcx(), arg.pat.id); + let arg_ty = return_if_err!(self.typer.node_ty(arg.pat.id)); let arg_cmt = self.mc.cat_rvalue( arg.id, @@ -380,8 +396,9 @@ impl<'d,'t,TYPER:mc::Typer> ExprUseVisitor<'d,'t,TYPER> { self.consume_expr(&**input); } - for &(_, ref output) in ia.outputs.iter() { - self.mutate_expr(expr, &**output, JustWrite); + for &(_, ref output, is_rw) in ia.outputs.iter() { + self.mutate_expr(expr, &**output, + if is_rw { WriteAndRead } else { JustWrite }); } } @@ -405,7 +422,7 @@ impl<'d,'t,TYPER:mc::Typer> ExprUseVisitor<'d,'t,TYPER> { // Fetch the type of the value that the iteration yields to // produce the pattern's categorized mutable type. - let pattern_type = ty::node_id_to_type(self.tcx(), pat.id); + let pattern_type = return_if_err!(self.typer.node_ty(pat.id)); let pat_cmt = self.mc.cat_rvalue(pat.id, pat.span, ty::ReScope(blk.id), @@ -465,10 +482,6 @@ impl<'d,'t,TYPER:mc::Typer> ExprUseVisitor<'d,'t,TYPER> { self.walk_captures(expr) } - ast::ExprVstore(ref base, _) => { - self.consume_expr(&**base); - } - ast::ExprBox(ref place, ref base) => { self.consume_expr(&**place); self.consume_expr(&**base); @@ -663,11 +676,10 @@ impl<'d,'t,TYPER:mc::Typer> ExprUseVisitor<'d,'t,TYPER> { None => { } Some(adjustment) => { match *adjustment { - ty::AutoAddEnv(..) | - ty::AutoObject(..) => { - // Creating an object or closure consumes the - // input and stores it into the resulting rvalue. - debug!("walk_adjustment(AutoAddEnv|AutoObject)"); + ty::AutoAddEnv(..) => { + // Creating a closure consumes the input and stores it + // into the resulting rvalue. + debug!("walk_adjustment(AutoAddEnv)"); let cmt_unadjusted = return_if_err!(self.mc.cat_expr_unadjusted(expr)); self.delegate_consume(expr.id, expr.span, cmt_unadjusted); @@ -726,42 +738,39 @@ impl<'d,'t,TYPER:mc::Typer> ExprUseVisitor<'d,'t,TYPER> { fn walk_autoref(&mut self, expr: &ast::Expr, autoref: &ty::AutoRef, - autoderefs: uint) { - debug!("walk_autoref expr={} autoderefs={}", expr.repr(self.tcx()), autoderefs); + n: uint) { + debug!("walk_autoref expr={}", expr.repr(self.tcx())); - let cmt_derefd = return_if_err!( - self.mc.cat_expr_autoderefd(expr, autoderefs)); + // Match for unique trait coercions first, since we don't need the + // call to cat_expr_autoderefd. + match *autoref { + ty::AutoUnsizeUniq(ty::UnsizeVtable(..)) | + ty::AutoUnsize(ty::UnsizeVtable(..)) => { + assert!(n == 1, format!("Expected exactly 1 deref with Uniq \ + AutoRefs, found: {}", n)); + let cmt_unadjusted = + return_if_err!(self.mc.cat_expr_unadjusted(expr)); + self.delegate_consume(expr.id, expr.span, cmt_unadjusted); + return; + } + _ => {} + } - debug!("walk_autoref: cmt_derefd={}", cmt_derefd.repr(self.tcx())); + let cmt_derefd = return_if_err!( + self.mc.cat_expr_autoderefd(expr, n)); + debug!("walk_adjustment: cmt_derefd={}", + cmt_derefd.repr(self.tcx())); match *autoref { - ty::AutoPtr(r, m) => { + ty::AutoPtr(r, m, _) => { self.delegate.borrow(expr.id, expr.span, cmt_derefd, r, ty::BorrowKind::from_mutbl(m), - AutoRef) - } - ty::AutoBorrowVec(r, m) | ty::AutoBorrowVecRef(r, m) => { - let cmt_index = self.mc.cat_index(expr, cmt_derefd, autoderefs+1); - self.delegate.borrow(expr.id, - expr.span, - cmt_index, - r, - ty::BorrowKind::from_mutbl(m), - AutoRef) - } - ty::AutoBorrowObj(r, m) => { - let cmt_deref = self.mc.cat_deref_obj(expr, cmt_derefd); - self.delegate.borrow(expr.id, - expr.span, - cmt_deref, - r, - ty::BorrowKind::from_mutbl(m), - AutoRef) + AutoRef); } - ty::AutoUnsafe(_) => {} + ty::AutoUnsizeUniq(_) | ty::AutoUnsize(_) | ty::AutoUnsafe(_) => {} } } @@ -819,7 +828,7 @@ impl<'d,'t,TYPER:mc::Typer> ExprUseVisitor<'d,'t,TYPER> { pat.repr(tcx)); // pat_ty: the type of the binding being produced. - let pat_ty = ty::node_id_to_type(tcx, pat.id); + let pat_ty = return_if_err!(typer.node_ty(pat.id)); // Each match binding is effectively an assignment to the // binding being produced. @@ -962,7 +971,7 @@ impl<'d,'t,TYPER:mc::Typer> ExprUseVisitor<'d,'t,TYPER> { // Create the cmt for the variable being borrowed, from the // caller's perspective let var_id = upvar_def.def_id().node; - let var_ty = ty::node_id_to_type(self.tcx(), var_id); + let var_ty = try!(self.typer.node_ty(var_id)); self.mc.cat_def(closure_id, closure_span, var_ty, upvar_def) } } diff --git a/src/librustc/middle/graph.rs b/src/librustc/middle/graph.rs index 78eeb26997d..2c79c655a99 100644 --- a/src/librustc/middle/graph.rs +++ b/src/librustc/middle/graph.rs @@ -64,6 +64,7 @@ pub struct EdgeIndex(pub uint); pub static InvalidEdgeIndex: EdgeIndex = EdgeIndex(uint::MAX); // Use a private field here to guarantee no more instances are created: +#[deriving(Show)] pub struct Direction { repr: uint } pub static Outgoing: Direction = Direction { repr: 0 }; pub static Incoming: Direction = Direction { repr: 1 }; diff --git a/src/librustc/middle/intrinsicck.rs b/src/librustc/middle/intrinsicck.rs index 6669147b0dc..be587c25574 100644 --- a/src/librustc/middle/intrinsicck.rs +++ b/src/librustc/middle/intrinsicck.rs @@ -73,9 +73,13 @@ struct IntrinsicCheckingVisitor<'a> { impl<'a> IntrinsicCheckingVisitor<'a> { fn def_id_is_transmute(&self, def_id: DefId) -> bool { + let intrinsic = match ty::get(ty::lookup_item_type(self.tcx, def_id).ty).sty { + ty::ty_bare_fn(ref bfty) => bfty.abi == RustIntrinsic, + _ => return false + }; if def_id.krate == ast::LOCAL_CRATE { match self.tcx.map.get(def_id.node) { - NodeForeignItem(ref item) => { + NodeForeignItem(ref item) if intrinsic => { token::get_ident(item.ident) == token::intern_and_get_ident("transmute") } @@ -83,16 +87,16 @@ impl<'a> IntrinsicCheckingVisitor<'a> { } } else { match csearch::get_item_path(self.tcx, def_id).last() { - None => false, - Some(ref last) => { + Some(ref last) if intrinsic => { token::get_name(last.name()) == token::intern_and_get_ident("transmute") } + _ => false, } } } - fn check_transmute(&self, span: Span, from: ty::t, to: ty::t) { + fn check_transmute(&self, span: Span, from: ty::t, to: ty::t, id: ast::NodeId) { if type_size_is_affected_by_type_parameters(self.tcx, from) { span_err!(self.tcx.sess, span, E0139, "cannot transmute from a type that contains type parameters"); @@ -106,6 +110,7 @@ impl<'a> IntrinsicCheckingVisitor<'a> { span: span, from: from, to: to, + id: id, }; self.tcx.transmute_restrictions.borrow_mut().push(restriction); } @@ -123,7 +128,7 @@ impl<'a> Visitor<()> for IntrinsicCheckingVisitor<'a> { if bare_fn_ty.abi == RustIntrinsic => { let from = *bare_fn_ty.sig.inputs.get(0); let to = bare_fn_ty.sig.output; - self.check_transmute(expr.span, from, to); + self.check_transmute(expr.span, from, to, expr.id); } _ => { self.tcx diff --git a/src/librustc/middle/kind.rs b/src/librustc/middle/kind.rs index dbd7d6a5d6a..08dde0c0607 100644 --- a/src/librustc/middle/kind.rs +++ b/src/librustc/middle/kind.rs @@ -144,14 +144,17 @@ fn check_impl_of_trait(cx: &mut Context, it: &Item, trait_ref: &TraitRef, self_t // If this trait has builtin-kind supertraits, meet them. let self_ty: ty::t = ty::node_id_to_type(cx.tcx, it.id); debug!("checking impl with self type {}", ty::get(self_ty).sty); - check_builtin_bounds(cx, self_ty, trait_def.bounds, |missing| { - span_err!(cx.tcx.sess, self_type.span, E0142, - "the type `{}', which does not fulfill `{}`, cannot implement this trait", - ty_to_string(cx.tcx, self_ty), missing.user_string(cx.tcx)); - span_note!(cx.tcx.sess, self_type.span, - "types implementing this trait must fulfill `{}`", - trait_def.bounds.user_string(cx.tcx)); - }); + check_builtin_bounds( + cx, self_ty, trait_def.bounds.builtin_bounds, + |missing| { + span_err!(cx.tcx.sess, self_type.span, E0142, + "the type `{}', which does not fulfill `{}`, \ + cannot implement this trait", + ty_to_string(cx.tcx, self_ty), missing.user_string(cx.tcx)); + span_note!(cx.tcx.sess, self_type.span, + "types implementing this trait must fulfill `{}`", + trait_def.bounds.user_string(cx.tcx)); + }); // If this is a destructor, check kinds. if cx.tcx.lang_items.drop_trait() == Some(trait_def_id) { @@ -297,17 +300,15 @@ fn with_appropriate_checker(cx: &Context, match ty::get(fty).sty { ty::ty_closure(box ty::ClosureTy { store: ty::UniqTraitStore, - bounds: mut bounds, .. + bounds: bounds, + .. }) => { - // Procs can't close over non-static references! - bounds.add(ty::BoundStatic); - - b(|cx, fv| check_for_uniq(cx, fv, bounds)) + b(|cx, fv| check_for_uniq(cx, fv, bounds.builtin_bounds)) } ty::ty_closure(box ty::ClosureTy { store: ty::RegionTraitStore(region, _), bounds, .. - }) => b(|cx, fv| check_for_block(cx, fv, bounds, region)), + }) => b(|cx, fv| check_for_block(cx, fv, bounds.builtin_bounds, region)), ty::ty_bare_fn(_) => { b(check_for_bare) @@ -377,13 +378,6 @@ pub fn check_expr(cx: &mut Context, e: &Expr) { expression_type); match e.node { - ExprBox(ref loc, ref interior) => { - let def = ty::resolve_expr(cx.tcx, &**loc); - if Some(def.def_id()) == cx.tcx.lang_items.managed_heap() { - let interior_type = ty::expr_ty(cx.tcx, &**interior); - let _ = check_static(cx.tcx, interior_type, interior.span); - } - } ExprCast(ref source, _) => { let source_ty = ty::expr_ty(cx.tcx, &**source); let target_ty = ty::expr_ty(cx.tcx, e); @@ -405,14 +399,27 @@ pub fn check_expr(cx: &mut Context, e: &Expr) { "repeated element will be copied"); } } + ExprAssign(ref lhs, _) | + ExprAssignOp(_, ref lhs, _) => { + let lhs_ty = ty::expr_ty(cx.tcx, &**lhs); + if !ty::type_is_sized(cx.tcx, lhs_ty) { + cx.tcx.sess.span_err(lhs.span, "dynamically sized type on lhs of assignment"); + } + } + ExprStruct(..) => { + let e_ty = ty::expr_ty(cx.tcx, e); + if !ty::type_is_sized(cx.tcx, e_ty) { + cx.tcx.sess.span_err(e.span, "trying to initialise a dynamically sized struct"); + } + } _ => {} } // Search for auto-adjustments to find trait coercions. match cx.tcx.adjustments.borrow().find(&e.id) { Some(adjustment) => { - match *adjustment { - ty::AutoObject(..) => { + match adjustment { + adj if ty::adjust_is_object(adj) => { let source_ty = ty::expr_ty(cx.tcx, e); let target_ty = ty::expr_ty_adjusted(cx.tcx, e); let method_call = MethodCall { @@ -425,8 +432,7 @@ pub fn check_expr(cx: &mut Context, e: &Expr) { e.span, method_call); } - ty::AutoAddEnv(..) | - ty::AutoDerefRef(..) => {} + _ => {} } } None => {} @@ -550,7 +556,6 @@ fn check_trait_cast(cx: &mut Context, target_ty: ty::t, span: Span, method_call: MethodCall) { - check_cast_for_escaping_regions(cx, source_ty, target_ty, span); match ty::get(target_ty).sty { ty::ty_uniq(ty) | ty::ty_rptr(_, ty::mt{ ty, .. }) => { match ty::get(ty).sty { @@ -568,7 +573,8 @@ fn check_trait_cast(cx: &mut Context, vtable_res) } }; - check_trait_cast_bounds(cx, span, source_ty, bounds); + check_trait_cast_bounds(cx, span, source_ty, + bounds.builtin_bounds); } _ => {} } @@ -608,7 +614,7 @@ pub fn check_builtin_bounds(cx: &Context, let kind = ty::type_contents(cx.tcx, ty); let mut missing = ty::empty_builtin_bounds(); for bound in bounds.iter() { - if !kind.meets_bound(cx.tcx, bound) { + if !kind.meets_builtin_bound(cx.tcx, bound) { missing.add(bound); } } @@ -752,132 +758,6 @@ fn check_copy(cx: &Context, ty: ty::t, sp: Span, reason: &str) { } } -pub fn check_static(tcx: &ty::ctxt, ty: ty::t, sp: Span) -> bool { - if !ty::type_is_static(tcx, ty) { - match ty::get(ty).sty { - ty::ty_param(..) => { - span_err!(tcx.sess, sp, E0149, - "value may contain references; \ - add `'static` bound to `{}`", - ty_to_string(tcx, ty)); - } - _ => { - span_err!(tcx.sess, sp, E0150, - "value may contain references"); - } - } - false - } else { - true - } -} - -/// This is rather subtle. When we are casting a value to an instantiated -/// trait like `a as trait<'r>`, regionck already ensures that any references -/// that appear in the type of `a` are bounded by `'r` (ed.: rem -/// FIXME(#5723)). However, it is possible that there are *type parameters* -/// in the type of `a`, and those *type parameters* may have references -/// within them. We have to guarantee that the regions which appear in those -/// type parameters are not obscured. -/// -/// Therefore, we ensure that one of three conditions holds: -/// -/// (1) The trait instance cannot escape the current fn. This is -/// guaranteed if the region bound `&r` is some scope within the fn -/// itself. This case is safe because whatever references are -/// found within the type parameter, they must enclose the fn body -/// itself. -/// -/// (2) The type parameter appears in the type of the trait. For -/// example, if the type parameter is `T` and the trait type is -/// `deque<T>`, then whatever references may appear in `T` also -/// appear in `deque<T>`. -/// -/// (3) The type parameter is sendable (and therefore does not contain -/// references). -/// -/// FIXME(#5723)---This code should probably move into regionck. -pub fn check_cast_for_escaping_regions( - cx: &Context, - source_ty: ty::t, - target_ty: ty::t, - source_span: Span) -{ - // Determine what type we are casting to; if it is not a trait, then no - // worries. - if !ty::type_is_trait(target_ty) { - return; - } - - // Collect up the regions that appear in the target type. We want to - // ensure that these lifetimes are shorter than all lifetimes that are in - // the source type. See test `src/test/compile-fail/regions-trait-2.rs` - let mut target_regions = Vec::new(); - ty::walk_regions_and_ty( - cx.tcx, - target_ty, - |r| { - if !r.is_bound() { - target_regions.push(r); - } - }, - |_| ()); - - // Check, based on the region associated with the trait, whether it can - // possibly escape the enclosing fn item (note that all type parameters - // must have been declared on the enclosing fn item). - if target_regions.iter().any(|r| is_ReScope(*r)) { - return; /* case (1) */ - } - - // Assuming the trait instance can escape, then ensure that each parameter - // either appears in the trait type or is sendable. - let target_params = ty::param_tys_in_type(target_ty); - ty::walk_regions_and_ty( - cx.tcx, - source_ty, - - |_r| { - // FIXME(#5723) --- turn this check on once &Objects are usable - // - // if !target_regions.iter().any(|t_r| is_subregion_of(cx, *t_r, r)) { - // cx.tcx.sess.span_err( - // source_span, - // format!("source contains reference with lifetime \ - // not found in the target type `{}`", - // ty_to_string(cx.tcx, target_ty))); - // note_and_explain_region( - // cx.tcx, "source data is only valid for ", r, ""); - // } - }, - - |ty| { - match ty::get(ty).sty { - ty::ty_param(source_param) => { - if source_param.space == subst::SelfSpace { - // FIXME (#5723) -- there is no reason that - // Self should be exempt from this check, - // except for historical accident. Bottom - // line, we need proper region bounding. - } else if target_params.iter().any(|x| x == &source_param) { - /* case (2) */ - } else { - check_static(cx.tcx, ty, source_span); /* case (3) */ - } - } - _ => {} - } - }); - - #[allow(non_snake_case_functions)] - fn is_ReScope(r: ty::Region) -> bool { - match r { - ty::ReScope(..) => true, - _ => false - } - } -} - // Ensure that `ty` has a statically known size (i.e., it has the `Sized` bound). fn check_sized(tcx: &ty::ctxt, ty: ty::t, name: String, sp: Span) { if !ty::type_is_sized(tcx, ty) { diff --git a/src/librustc/middle/liveness.rs b/src/librustc/middle/liveness.rs index baac68904d5..38288bf3011 100644 --- a/src/librustc/middle/liveness.rs +++ b/src/librustc/middle/liveness.rs @@ -511,7 +511,7 @@ fn visit_expr(ir: &mut IrMaps, expr: &Expr) { } // otherwise, live nodes are not required: - ExprIndex(..) | ExprField(..) | ExprVstore(..) | ExprVec(..) | + ExprIndex(..) | ExprField(..) | ExprVec(..) | ExprCall(..) | ExprMethodCall(..) | ExprTup(..) | ExprBinary(..) | ExprAddrOf(..) | ExprCast(..) | ExprUnary(..) | ExprBreak(_) | @@ -1119,10 +1119,6 @@ impl<'a> Liveness<'a> { // Uninteresting cases: just propagate in rev exec order - ExprVstore(ref expr, _) => { - self.propagate_through_expr(&**expr, succ) - } - ExprVec(ref exprs) => { self.propagate_through_exprs(exprs.as_slice(), succ) } @@ -1192,9 +1188,10 @@ impl<'a> Liveness<'a> { } ExprInlineAsm(ref ia) => { - let succ = ia.outputs.iter().rev().fold(succ, |succ, &(_, ref expr)| { - // see comment on lvalues in - // propagate_through_lvalue_components() + + let succ = ia.outputs.iter().rev().fold(succ, |succ, &(_, ref expr, _)| { + // see comment on lvalues + // in propagate_through_lvalue_components() let succ = self.write_lvalue(&**expr, succ, ACC_WRITE); self.propagate_through_lvalue_components(&**expr, succ) }); @@ -1437,7 +1434,7 @@ fn check_expr(this: &mut Liveness, expr: &Expr) { } // Output operands must be lvalues - for &(_, ref out) in ia.outputs.iter() { + for &(_, ref out, _) in ia.outputs.iter() { this.check_lvalue(&**out); this.visit_expr(&**out, ()); } @@ -1448,8 +1445,7 @@ fn check_expr(this: &mut Liveness, expr: &Expr) { // no correctness conditions related to liveness ExprCall(..) | ExprMethodCall(..) | ExprIf(..) | ExprMatch(..) | ExprWhile(..) | ExprLoop(..) | ExprIndex(..) | ExprField(..) | - ExprVstore(..) | ExprVec(..) | ExprTup(..) | - ExprBinary(..) | + ExprVec(..) | ExprTup(..) | ExprBinary(..) | ExprCast(..) | ExprUnary(..) | ExprRet(..) | ExprBreak(..) | ExprAgain(..) | ExprLit(_) | ExprBlock(..) | ExprMac(..) | ExprAddrOf(..) | ExprStruct(..) | ExprRepeat(..) | diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs index ef1e0515156..662b55ba361 100644 --- a/src/librustc/middle/mem_categorization.rs +++ b/src/librustc/middle/mem_categorization.rs @@ -205,7 +205,7 @@ pub fn opt_deref_kind(t: ty::t) -> Option<deref_kind> { Some(deref_interior(InteriorField(PositionalField(0)))) } - ty::ty_vec(_, Some(_)) => { + ty::ty_vec(_, _) | ty::ty_str => { Some(deref_interior(InteriorElement(element_kind(t)))) } @@ -214,11 +214,12 @@ pub fn opt_deref_kind(t: ty::t) -> Option<deref_kind> { } pub fn deref_kind(tcx: &ty::ctxt, t: ty::t) -> deref_kind { + debug!("deref_kind {}", ty_to_string(tcx, t)); match opt_deref_kind(t) { Some(k) => k, None => { tcx.sess.bug( - format!("deref_cat() invoked on non-derefable type {}", + format!("deref_kind() invoked on non-derefable type {}", ty_to_string(tcx, t)).as_slice()); } } @@ -239,10 +240,16 @@ impl ast_node for ast::Pat { fn span(&self) -> Span { self.span } } +#[cfg(stage0)] pub struct MemCategorizationContext<'t,TYPER> { typer: &'t TYPER } +#[cfg(not(stage0))] +pub struct MemCategorizationContext<'t,TYPER:'t> { + typer: &'t TYPER +} + pub type McResult<T> = Result<T, ()>; /** @@ -411,13 +418,6 @@ impl<'t,TYPER:Typer> MemCategorizationContext<'t,TYPER> { Some(adjustment) => { match *adjustment { - ty::AutoObject(..) => { - // Implicitly cast a concrete object to trait object. - // Result is an rvalue. - let expr_ty = if_ok!(self.expr_ty_adjusted(expr)); - Ok(self.cat_rvalue_node(expr.id(), expr.span(), expr_ty)) - } - ty::AutoAddEnv(..) => { // Convert a bare fn to a closure by adding NULL env. // Result is an rvalue. @@ -485,7 +485,7 @@ impl<'t,TYPER:Typer> MemCategorizationContext<'t,TYPER> { } None => { let base_cmt = if_ok!(self.cat_expr(&**base)); - Ok(self.cat_index(expr, base_cmt, 0)) + Ok(self.cat_index(expr, base_cmt)) } } } @@ -504,7 +504,7 @@ impl<'t,TYPER:Typer> MemCategorizationContext<'t,TYPER> { ast::ExprFnBlock(..) | ast::ExprProc(..) | ast::ExprUnboxedFn(..) | ast::ExprRet(..) | ast::ExprUnary(..) | - ast::ExprMethodCall(..) | ast::ExprCast(..) | ast::ExprVstore(..) | + ast::ExprMethodCall(..) | ast::ExprCast(..) | ast::ExprVec(..) | ast::ExprTup(..) | ast::ExprIf(..) | ast::ExprBinary(..) | ast::ExprWhile(..) | ast::ExprBlock(..) | ast::ExprLoop(..) | ast::ExprMatch(..) | @@ -703,7 +703,10 @@ impl<'t,TYPER:Typer> MemCategorizationContext<'t,TYPER> { -> cmt { match self.typer.temporary_scope(id) { Some(scope) => { - self.cat_rvalue(id, span, ty::ReScope(scope), expr_ty) + match ty::get(expr_ty).sty { + ty::ty_vec(_, Some(0)) => self.cat_rvalue(id, span, ty::ReStatic, expr_ty), + _ => self.cat_rvalue(id, span, ty::ReScope(scope), expr_ty) + } } None => { self.cat_rvalue(id, span, ty::ReStatic, expr_ty) @@ -751,10 +754,11 @@ impl<'t,TYPER:Typer> MemCategorizationContext<'t,TYPER> { implicit: bool) -> cmt { let adjustment = match self.typer.adjustments().borrow().find(&node.id()) { - Some(&ty::AutoObject(..)) => typeck::AutoObject, + Some(adj) if ty::adjust_is_object(adj) => typeck::AutoObject, _ if deref_cnt != 0 => typeck::AutoDeref(deref_cnt), _ => typeck::NoAdjustment }; + let method_call = typeck::MethodCall { expr_id: node.id(), adjustment: adjustment @@ -820,13 +824,9 @@ impl<'t,TYPER:Typer> MemCategorizationContext<'t,TYPER> { pub fn cat_index<N:ast_node>(&self, elt: &N, - mut base_cmt: cmt, - derefs: uint) + mut base_cmt: cmt) -> cmt { - //! Creates a cmt for an indexing operation (`[]`); this - //! indexing operation may occurs as part of an - //! AutoBorrowVec, which when converting a `~[]` to an `&[]` - //! effectively takes the address of the 0th element. + //! Creates a cmt for an indexing operation (`[]`). //! //! One subtle aspect of indexing that may not be //! immediately obvious: for anything other than a fixed-length @@ -839,20 +839,9 @@ impl<'t,TYPER:Typer> MemCategorizationContext<'t,TYPER> { //! cmt containing both this deref and the indexing, //! presuming that `base_cmt` is not of fixed-length type. //! - //! In the event that a deref is needed, the "deref count" - //! is taken from the parameter `derefs`. See the comment - //! on the def'n of `root_map_key` in borrowck/mod.rs - //! for more details about deref counts; the summary is - //! that `derefs` should be 0 for an explicit indexing - //! operation and N+1 for an indexing that is part of - //! an auto-adjustment, where N is the number of autoderefs - //! in that adjustment. - //! //! # Parameters //! - `elt`: the AST node being indexed //! - `base_cmt`: the cmt of `elt` - //! - `derefs`: the deref number to be used for - //! the implicit index deref, if any (see above) let method_call = typeck::MethodCall::expr(elt.id()); let method_ty = self.typer.node_method_ty(method_call); @@ -865,7 +854,7 @@ impl<'t,TYPER:Typer> MemCategorizationContext<'t,TYPER> { } None => { match ty::array_element_ty(base_cmt.ty) { - Some(ref mt) => mt.ty, + Some(ty) => ty, None => { self.tcx().sess.span_bug( elt.span(), @@ -876,30 +865,8 @@ impl<'t,TYPER:Typer> MemCategorizationContext<'t,TYPER> { } }; - return match deref_kind(self.tcx(), base_cmt.ty) { - deref_ptr(ptr) => { - // for unique ptrs, we inherit mutability from the - // owning reference. - let m = MutabilityCategory::from_pointer_kind(base_cmt.mutbl, ptr); - - // the deref is explicit in the resulting cmt - let deref_cmt = Rc::new(cmt_ { - id:elt.id(), - span:elt.span(), - cat:cat_deref(base_cmt.clone(), derefs, ptr), - mutbl:m, - ty:element_ty - }); - - interior(elt, deref_cmt, base_cmt.ty, m.inherit(), element_ty) - } - - deref_interior(_) => { - // fixed-length vectors have no deref - let m = base_cmt.mutbl.inherit(); - interior(elt, base_cmt.clone(), base_cmt.ty, m, element_ty) - } - }; + let m = base_cmt.mutbl.inherit(); + return interior(elt, base_cmt.clone(), base_cmt.ty, m, element_ty); fn interior<N: ast_node>(elt: &N, of_cmt: cmt, @@ -917,6 +884,37 @@ impl<'t,TYPER:Typer> MemCategorizationContext<'t,TYPER> { } } + // Takes either a vec or a reference to a vec and returns the cmt for the + // underlying vec. + fn deref_vec<N:ast_node>(&self, + elt: &N, + base_cmt: cmt) + -> cmt { + match deref_kind(self.tcx(), base_cmt.ty) { + deref_ptr(ptr) => { + // for unique ptrs, we inherit mutability from the + // owning reference. + let m = MutabilityCategory::from_pointer_kind(base_cmt.mutbl, ptr); + + // the deref is explicit in the resulting cmt + Rc::new(cmt_ { + id:elt.id(), + span:elt.span(), + cat:cat_deref(base_cmt.clone(), 0, ptr), + mutbl:m, + ty: match ty::deref(base_cmt.ty, false) { + Some(mt) => mt.ty, + None => self.tcx().sess.bug("Found non-derefable type") + } + }) + } + + deref_interior(_) => { + base_cmt + } + } + } + pub fn cat_slice_pattern(&self, vec_cmt: cmt, slice_pat: &ast::Pat) @@ -935,7 +933,7 @@ impl<'t,TYPER:Typer> MemCategorizationContext<'t,TYPER> { let (slice_mutbl, slice_r) = vec_slice_info(self.tcx(), slice_pat, slice_ty); - let cmt_slice = self.cat_index(slice_pat, vec_cmt, 0); + let cmt_slice = self.cat_index(slice_pat, self.deref_vec(slice_pat, vec_cmt)); return Ok((cmt_slice, slice_mutbl, slice_r)); fn vec_slice_info(tcx: &ty::ctxt, @@ -951,7 +949,7 @@ impl<'t,TYPER:Typer> MemCategorizationContext<'t,TYPER> { match ty::get(slice_ty).sty { ty::ty_rptr(r, ref mt) => match ty::get(mt.ty).sty { - ty::ty_vec(slice_mt, None) => (slice_mt.mutbl, r), + ty::ty_vec(_, None) => (mt.mutbl, r), _ => vec_slice_info(tcx, pat, mt.ty), }, @@ -1143,7 +1141,7 @@ impl<'t,TYPER:Typer> MemCategorizationContext<'t,TYPER> { } ast::PatVec(ref before, slice, ref after) => { - let elt_cmt = self.cat_index(pat, cmt, 0); + let elt_cmt = self.cat_index(pat, self.deref_vec(pat, cmt)); for before_pat in before.iter() { if_ok!(self.cat_pattern(elt_cmt.clone(), &**before_pat, |x,y,z| op(x,y,z))); diff --git a/src/librustc/middle/privacy.rs b/src/librustc/middle/privacy.rs index 61e6debb086..42d6cee9654 100644 --- a/src/librustc/middle/privacy.rs +++ b/src/librustc/middle/privacy.rs @@ -1221,8 +1221,8 @@ struct VisiblePrivateTypesVisitor<'a> { public_items: &'a PublicItems, } -struct CheckTypeForPrivatenessVisitor<'a, 'b> { - inner: &'b VisiblePrivateTypesVisitor<'a>, +struct CheckTypeForPrivatenessVisitor<'a, 'b:'a> { + inner: &'a VisiblePrivateTypesVisitor<'b>, /// whether the type refers to private types. contains_private: bool, /// whether we've recurred at all (i.e. if we're pointing at the diff --git a/src/librustc/middle/region.rs b/src/librustc/middle/region.rs index 5f5a324857a..11dd3eee88e 100644 --- a/src/librustc/middle/region.rs +++ b/src/librustc/middle/region.rs @@ -25,6 +25,7 @@ use driver::session::Session; use middle::ty::{FreeRegion}; use middle::ty; use util::nodemap::NodeMap; +use util::common::can_reach; use std::cell::RefCell; use std::collections::{HashMap, HashSet}; @@ -78,7 +79,7 @@ The region maps encode information about region relationships. pub struct RegionMaps { scope_map: RefCell<NodeMap<ast::NodeId>>, var_map: RefCell<NodeMap<ast::NodeId>>, - free_region_map: RefCell<HashMap<FreeRegion, Vec<FreeRegion> >>, + free_region_map: RefCell<HashMap<FreeRegion, Vec<FreeRegion>>>, rvalue_scopes: RefCell<NodeMap<ast::NodeId>>, terminating_scopes: RefCell<HashSet<ast::NodeId>>, } @@ -255,34 +256,7 @@ impl RegionMaps { * (that is, the user can give two different names to the same lifetime). */ - if sub == sup { - return true; - } - - // Do a little breadth-first-search here. The `queue` list - // doubles as a way to detect if we've seen a particular FR - // before. Note that we expect this graph to be an *extremely - // shallow* tree. - let mut queue = vec!(sub); - let mut i = 0; - while i < queue.len() { - match self.free_region_map.borrow().find(queue.get(i)) { - Some(parents) => { - for parent in parents.iter() { - if *parent == sup { - return true; - } - - if !queue.iter().any(|x| x == parent) { - queue.push(*parent); - } - } - } - None => {} - } - i += 1; - } - return false; + can_reach(&*self.free_region_map.borrow(), sub, sup) } pub fn is_subregion_of(&self, @@ -300,6 +274,7 @@ impl RegionMaps { sub_region == super_region || { match (sub_region, super_region) { + (ty::ReEmpty, _) | (_, ty::ReStatic) => { true } @@ -740,10 +715,6 @@ fn resolve_local(visitor: &mut RegionResolutionVisitor, visitor, &*field.expr, blk_id); } } - ast::ExprVstore(ref subexpr, _) => { - visitor.region_maps.record_rvalue_scope(subexpr.id, blk_id); - record_rvalue_scope_if_borrow_expr(visitor, &**subexpr, blk_id); - } ast::ExprVec(ref subexprs) | ast::ExprTup(ref subexprs) => { for subexpr in subexprs.iter() { diff --git a/src/librustc/middle/resolve.rs b/src/librustc/middle/resolve.rs index bd779b865d6..58be2c73bd9 100644 --- a/src/librustc/middle/resolve.rs +++ b/src/librustc/middle/resolve.rs @@ -30,10 +30,10 @@ use syntax::ast::{Ident, ImplItem, Item, ItemEnum, ItemFn, ItemForeignMod}; use syntax::ast::{ItemImpl, ItemMac, ItemMod, ItemStatic, ItemStruct}; use syntax::ast::{ItemTrait, ItemTy, LOCAL_CRATE, Local, Method}; use syntax::ast::{MethodImplItem, Mod, Name, NamedField, NodeId}; -use syntax::ast::{OtherRegionTyParamBound, P, Pat, PatEnum, PatIdent, PatLit}; +use syntax::ast::{P, Pat, PatEnum, PatIdent, PatLit}; use syntax::ast::{PatRange, PatStruct, Path, PathListIdent, PathListMod}; use syntax::ast::{PrimTy, Public, SelfExplicit, SelfStatic}; -use syntax::ast::{StaticRegionTyParamBound, StmtDecl, StructField}; +use syntax::ast::{RegionTyParamBound, StmtDecl, StructField}; use syntax::ast::{StructVariantKind, TraitRef, TraitTyParamBound}; use syntax::ast::{TupleVariantKind, Ty, TyBool, TyChar, TyClosure, TyF32}; use syntax::ast::{TyF64, TyFloat, TyI, TyI8, TyI16, TyI32, TyI64, TyInt}; @@ -901,7 +901,7 @@ struct Resolver<'a> { used_imports: HashSet<(NodeId, Namespace)>, } -struct BuildReducedGraphVisitor<'a, 'b> { +struct BuildReducedGraphVisitor<'a, 'b:'a> { resolver: &'a mut Resolver<'b>, } @@ -933,7 +933,9 @@ impl<'a, 'b> Visitor<ReducedGraphParent> for BuildReducedGraphVisitor<'a, 'b> { } -struct UnusedImportCheckVisitor<'a, 'b> { resolver: &'a mut Resolver<'b> } +struct UnusedImportCheckVisitor<'a, 'b:'a> { + resolver: &'a mut Resolver<'b> +} impl<'a, 'b> Visitor<()> for UnusedImportCheckVisitor<'a, 'b> { fn visit_view_item(&mut self, vi: &ViewItem, _: ()) { @@ -3946,7 +3948,7 @@ impl<'a> Resolver<'a> { impl_items.as_slice()); } - ItemTrait(ref generics, ref unbound, ref traits, ref methods) => { + ItemTrait(ref generics, ref unbound, ref bounds, ref methods) => { // Create a new rib for the self type. let self_type_rib = Rib::new(ItemRibKind); @@ -3965,10 +3967,9 @@ impl<'a> Resolver<'a> { this.resolve_type_parameters(&generics.ty_params); this.resolve_where_clause(&generics.where_clause); - // Resolve derived traits. - for trt in traits.iter() { - this.resolve_trait_reference(item.id, trt, TraitDerivation); - } + this.resolve_type_parameter_bounds(item.id, bounds, + TraitDerivation); + match unbound { &Some(ast::TraitTyParamBound(ref tpb)) => { this.resolve_trait_reference(item.id, tpb, TraitDerivation); @@ -4199,10 +4200,13 @@ impl<'a> Resolver<'a> { type_parameters: &OwnedSlice<TyParam>) { for type_parameter in type_parameters.iter() { for bound in type_parameter.bounds.iter() { - self.resolve_type_parameter_bound(type_parameter.id, bound); + self.resolve_type_parameter_bound(type_parameter.id, bound, + TraitBoundingTypeParameter); } match &type_parameter.unbound { - &Some(ref unbound) => self.resolve_type_parameter_bound(type_parameter.id, unbound), + &Some(ref unbound) => + self.resolve_type_parameter_bound( + type_parameter.id, unbound, TraitBoundingTypeParameter), &None => {} } match type_parameter.default { @@ -4212,12 +4216,23 @@ impl<'a> Resolver<'a> { } } + fn resolve_type_parameter_bounds(&mut self, + id: NodeId, + type_parameter_bounds: &OwnedSlice<TyParamBound>, + reference_type: TraitReferenceType) { + for type_parameter_bound in type_parameter_bounds.iter() { + self.resolve_type_parameter_bound(id, type_parameter_bound, + reference_type); + } + } + fn resolve_type_parameter_bound(&mut self, id: NodeId, - type_parameter_bound: &TyParamBound) { + type_parameter_bound: &TyParamBound, + reference_type: TraitReferenceType) { match *type_parameter_bound { TraitTyParamBound(ref tref) => { - self.resolve_trait_reference(id, tref, TraitBoundingTypeParameter) + self.resolve_trait_reference(id, tref, reference_type) } UnboxedFnTyParamBound(ref unboxed_function) => { for argument in unboxed_function.decl.inputs.iter() { @@ -4226,7 +4241,7 @@ impl<'a> Resolver<'a> { self.resolve_type(&*unboxed_function.decl.output); } - StaticRegionTyParamBound | OtherRegionTyParamBound(_) => {} + RegionTyParamBound(..) => {} } } @@ -4240,7 +4255,7 @@ impl<'a> Resolver<'a> { let usage_str = match reference_type { TraitBoundingTypeParameter => "bound type parameter with", TraitImplementation => "implement", - TraitDerivation => "derive" + TraitDerivation => "derive", }; let msg = format!("attempt to {} a nonexistent trait `{}`", usage_str, path_str); @@ -4295,7 +4310,8 @@ impl<'a> Resolver<'a> { } for bound in predicate.bounds.iter() { - self.resolve_type_parameter_bound(predicate.id, bound); + self.resolve_type_parameter_bound(predicate.id, bound, + TraitBoundingTypeParameter); } } } @@ -4679,18 +4695,14 @@ impl<'a> Resolver<'a> { } bounds.as_ref().map(|bound_vec| { - for bound in bound_vec.iter() { - self.resolve_type_parameter_bound(ty.id, bound); - } + self.resolve_type_parameter_bounds(ty.id, bound_vec, + TraitBoundingTypeParameter); }); } - TyClosure(c, _) | TyProc(c) => { - c.bounds.as_ref().map(|bounds| { - for bound in bounds.iter() { - self.resolve_type_parameter_bound(ty.id, bound); - } - }); + TyClosure(c) | TyProc(c) => { + self.resolve_type_parameter_bounds(ty.id, &c.bounds, + TraitBoundingTypeParameter); visit::walk_ty(self, ty, ()); } diff --git a/src/librustc/middle/resolve_lifetime.rs b/src/librustc/middle/resolve_lifetime.rs index 5001e7a88bd..1bc37e2f1e4 100644 --- a/src/librustc/middle/resolve_lifetime.rs +++ b/src/librustc/middle/resolve_lifetime.rs @@ -21,7 +21,6 @@ use driver::session::Session; use middle::subst; use syntax::ast; use syntax::codemap::Span; -use syntax::owned_slice::OwnedSlice; use syntax::parse::token::special_idents; use syntax::parse::token; use syntax::print::pprust::{lifetime_to_string}; @@ -99,8 +98,10 @@ impl<'a, 'b> Visitor<Scope<'a>> for LifetimeContext<'b> { ast::ItemStruct(_, ref generics) | ast::ItemImpl(ref generics, _, _, _) | ast::ItemTrait(ref generics, _, _, _) => { - self.check_lifetime_names(&generics.lifetimes); - EarlyScope(subst::TypeSpace, &generics.lifetimes, &root) + let scope: ScopeChain = + EarlyScope(subst::TypeSpace, &generics.lifetimes, &root); + self.check_lifetime_defs(&generics.lifetimes, &scope); + scope } }; debug!("entering scope {:?}", scope); @@ -126,7 +127,7 @@ impl<'a, 'b> Visitor<Scope<'a>> for LifetimeContext<'b> { fn visit_ty(&mut self, ty: &ast::Ty, scope: Scope<'a>) { match ty.node { - ast::TyClosure(c, _) | ast::TyProc(c) => { + ast::TyClosure(c) | ast::TyProc(c) => { push_fn_scope(self, ty, scope, &c.lifetimes); } ast::TyBareFn(c) => push_fn_scope(self, ty, scope, &c.lifetimes), @@ -137,8 +138,8 @@ impl<'a, 'b> Visitor<Scope<'a>> for LifetimeContext<'b> { ty: &ast::Ty, scope: Scope, lifetimes: &Vec<ast::LifetimeDef>) { - let scope1 = LateScope(ty.id, lifetimes, scope); - this.check_lifetime_names(lifetimes); + let scope1: ScopeChain = LateScope(ty.id, lifetimes, scope); + this.check_lifetime_defs(lifetimes, &scope1); debug!("pushing fn scope id={} due to type", ty.id); visit::walk_ty(this, ty, &scope1); debug!("popping fn scope id={} due to type", ty.id); @@ -204,23 +205,22 @@ impl<'a> LifetimeContext<'a> { * the ordering is not important there. */ - self.check_lifetime_names(&generics.lifetimes); - - let referenced_idents = free_lifetimes(&generics.ty_params); + let referenced_idents = early_bound_lifetime_names(generics); debug!("pushing fn scope id={} due to fn item/method\ referenced_idents={:?}", n, referenced_idents.iter().map(lifetime_show).collect::<Vec<token::InternedString>>()); if referenced_idents.is_empty() { - let scope1 = LateScope(n, &generics.lifetimes, scope); - walk(self, &scope1) + let scope1: ScopeChain = LateScope(n, &generics.lifetimes, scope); + self.check_lifetime_defs(&generics.lifetimes, &scope1); + walk(self, &scope1); } else { let (early, late) = generics.lifetimes.clone().partition( |l| referenced_idents.iter().any(|&i| i == l.lifetime.name)); let scope1 = EarlyScope(subst::FnSpace, &early, scope); - let scope2 = LateScope(n, &late, &scope1); - + let scope2: ScopeChain = LateScope(n, &late, &scope1); + self.check_lifetime_defs(&generics.lifetimes, &scope2); walk(self, &scope2); } debug!("popping fn scope id={} due to fn item/method", n); @@ -334,7 +334,9 @@ impl<'a> LifetimeContext<'a> { token::get_name(lifetime_ref.name)).as_slice()); } - fn check_lifetime_names(&self, lifetimes: &Vec<ast::LifetimeDef>) { + fn check_lifetime_defs<'b>(&mut self, + lifetimes: &Vec<ast::LifetimeDef>, + scope: Scope<'b>) { for i in range(0, lifetimes.len()) { let lifetime_i = lifetimes.get(i); @@ -363,11 +365,7 @@ impl<'a> LifetimeContext<'a> { } for bound in lifetime_i.bounds.iter() { - if !self.sess.features.issue_5723_bootstrap.get() { - self.sess.span_err( - bound.span, - "region bounds require `issue_5723_bootstrap`"); - } + self.resolve_lifetime_ref(bound, scope); } } } @@ -403,7 +401,7 @@ fn search_lifetimes(lifetimes: &Vec<ast::LifetimeDef>, /////////////////////////////////////////////////////////////////////////// pub fn early_bound_lifetimes<'a>(generics: &'a ast::Generics) -> Vec<ast::LifetimeDef> { - let referenced_idents = free_lifetimes(&generics.ty_params); + let referenced_idents = early_bound_lifetime_names(generics); if referenced_idents.is_empty() { return Vec::new(); } @@ -414,29 +412,72 @@ pub fn early_bound_lifetimes<'a>(generics: &'a ast::Generics) -> Vec<ast::Lifeti .collect() } -pub fn free_lifetimes(ty_params: &OwnedSlice<ast::TyParam>) -> Vec<ast::Name> { +fn early_bound_lifetime_names(generics: &ast::Generics) -> Vec<ast::Name> { /*! - * Gathers up and returns the names of any lifetimes that appear - * free in `ty_params`. Of course, right now, all lifetimes appear - * free, since we don't currently have any binders in type parameter - * declarations; just being forwards compatible with future extensions. + * Given a set of generic declarations, returns a list of names + * containing all early bound lifetime names for those + * generics. (In fact, this list may also contain other names.) */ - let mut collector = FreeLifetimeCollector { names: vec!() }; - for ty_param in ty_params.iter() { - visit::walk_ty_param_bounds(&mut collector, &ty_param.bounds, ()); + // Create two lists, dividing the lifetimes into early/late bound. + // Initially, all of them are considered late, but we will move + // things from late into early as we go if we find references to + // them. + let mut early_bound = Vec::new(); + let mut late_bound = generics.lifetimes.iter() + .map(|l| l.lifetime.name) + .collect(); + + // Any lifetime that appears in a type bound is early. + { + let mut collector = + FreeLifetimeCollector { early_bound: &mut early_bound, + late_bound: &mut late_bound }; + for ty_param in generics.ty_params.iter() { + visit::walk_ty_param_bounds(&mut collector, &ty_param.bounds, ()); + } + for predicate in generics.where_clause.predicates.iter() { + visit::walk_ty_param_bounds(&mut collector, &predicate.bounds, ()); + } + } + + // Any lifetime that either has a bound or is referenced by a + // bound is early. + for lifetime_def in generics.lifetimes.iter() { + if !lifetime_def.bounds.is_empty() { + shuffle(&mut early_bound, &mut late_bound, + lifetime_def.lifetime.name); + for bound in lifetime_def.bounds.iter() { + shuffle(&mut early_bound, &mut late_bound, + bound.name); + } + } } - return collector.names; + return early_bound; - struct FreeLifetimeCollector { - names: Vec<ast::Name>, + struct FreeLifetimeCollector<'a> { + early_bound: &'a mut Vec<ast::Name>, + late_bound: &'a mut Vec<ast::Name>, } - impl Visitor<()> for FreeLifetimeCollector { + impl<'a> Visitor<()> for FreeLifetimeCollector<'a> { fn visit_lifetime_ref(&mut self, lifetime_ref: &ast::Lifetime, _: ()) { - self.names.push(lifetime_ref.name); + shuffle(self.early_bound, self.late_bound, + lifetime_ref.name); + } + } + + fn shuffle(early_bound: &mut Vec<ast::Name>, + late_bound: &mut Vec<ast::Name>, + name: ast::Name) { + match late_bound.iter().position(|n| *n == name) { + Some(index) => { + late_bound.swap_remove(index); + early_bound.push(name); + } + None => { } } } } diff --git a/src/librustc/middle/save/mod.rs b/src/librustc/middle/save/mod.rs index 11b16f18533..cd3f47e8b2f 100644 --- a/src/librustc/middle/save/mod.rs +++ b/src/librustc/middle/save/mod.rs @@ -50,6 +50,7 @@ use syntax::attr; use syntax::codemap::*; use syntax::parse::token; use syntax::parse::token::{get_ident,keywords}; +use syntax::owned_slice::OwnedSlice; use syntax::visit; use syntax::visit::Visitor; use syntax::print::pprust::{path_to_string,ty_to_string}; @@ -653,7 +654,7 @@ impl <'l> DxrVisitor<'l> { item: &ast::Item, e: DxrVisitorEnv, generics: &ast::Generics, - trait_refs: &Vec<ast::TraitRef>, + trait_refs: &OwnedSlice<ast::TyParamBound>, methods: &Vec<ast::TraitItem>) { let qualname = self.analysis.ty_cx.map.path_to_string(item.id); @@ -665,7 +666,16 @@ impl <'l> DxrVisitor<'l> { e.cur_scope); // super-traits - for trait_ref in trait_refs.iter() { + for super_bound in trait_refs.iter() { + let trait_ref = match *super_bound { + ast::TraitTyParamBound(ref trait_ref) => { + trait_ref + } + ast::UnboxedFnTyParamBound(..) | ast::RegionTyParamBound(..) => { + continue; + } + }; + match self.lookup_type_ref(trait_ref.ref_id) { Some(id) => { let sub_span = self.span.sub_span_for_type_name(trait_ref.path.span); @@ -1499,7 +1509,7 @@ pub fn process_crate(sess: &Session, collected_paths: vec!(), collecting: false, fmt: FmtStrs::new(box Recorder { - out: output_file as Box<Writer>, + out: output_file as Box<Writer+'static>, dump_spans: false, }, SpanUtils { diff --git a/src/librustc/middle/save/recorder.rs b/src/librustc/middle/save/recorder.rs index 1af6fde02af..0695b6b360c 100644 --- a/src/librustc/middle/save/recorder.rs +++ b/src/librustc/middle/save/recorder.rs @@ -19,7 +19,7 @@ use syntax::codemap::*; pub struct Recorder { // output file - pub out: Box<Writer>, + pub out: Box<Writer+'static>, pub dump_spans: bool, } diff --git a/src/librustc/middle/subst.rs b/src/librustc/middle/subst.rs index 673872103af..d992e840b46 100644 --- a/src/librustc/middle/subst.rs +++ b/src/librustc/middle/subst.rs @@ -403,7 +403,7 @@ impl<T> VecPerParamSpace<T> { self.content.slice(start, limit) } - fn get_mut_slice<'a>(&'a mut self, space: ParamSpace) -> &'a mut [T] { + pub fn get_mut_slice<'a>(&'a mut self, space: ParamSpace) -> &'a mut [T] { let (start, limit) = self.limits(space); self.content.mut_slice(start, limit) } @@ -611,7 +611,7 @@ impl<'a> TypeFolder for SubstFolder<'a> { let t1 = match ty::get(t).sty { ty::ty_param(p) => { - check(self, t, self.substs.types.opt_get(p.space, p.idx)) + check(self, p, t, self.substs.types.opt_get(p.space, p.idx)) } _ => { ty_fold::super_fold_ty(self, t) @@ -627,6 +627,7 @@ impl<'a> TypeFolder for SubstFolder<'a> { return t1; fn check(this: &SubstFolder, + p: ty::ParamTy, source_ty: ty::t, opt_ty: Option<&ty::t>) -> ty::t { @@ -636,8 +637,9 @@ impl<'a> TypeFolder for SubstFolder<'a> { let span = this.span.unwrap_or(DUMMY_SP); this.tcx().sess.span_bug( span, - format!("Type parameter {} out of range \ + format!("Type parameter `{}` ({}) out of range \ when substituting (root type={})", + p.repr(this.tcx()), source_ty.repr(this.tcx()), this.root_ty.repr(this.tcx())).as_slice()); } diff --git a/src/librustc/middle/trans/_match.rs b/src/librustc/middle/trans/_match.rs index d8bdeb0abf6..bbd6c252849 100644 --- a/src/librustc/middle/trans/_match.rs +++ b/src/librustc/middle/trans/_match.rs @@ -280,7 +280,7 @@ fn trans_opt<'a>(mut bcx: &'a Block<'a>, o: &Opt) -> opt_result<'a> { match *o { lit(lit_expr) => { let lit_ty = ty::node_id_to_type(bcx.tcx(), lit_expr.id); - let (llval, _) = consts::const_expr(ccx, &*lit_expr, true); + let (llval, _, _) = consts::const_expr(ccx, &*lit_expr, true); let lit_datum = immediate_rvalue(llval, lit_ty); let lit_datum = unpack_datum!(bcx, lit_datum.to_appropriate_datum(bcx)); return single_result(Result::new(bcx, lit_datum.val)); @@ -289,8 +289,8 @@ fn trans_opt<'a>(mut bcx: &'a Block<'a>, o: &Opt) -> opt_result<'a> { return adt::trans_case(bcx, &**repr, disr_val); } range(ref l1, ref l2) => { - let (l1, _) = consts::const_expr(ccx, &**l1, true); - let (l2, _) = consts::const_expr(ccx, &**l2, true); + let (l1, _, _) = consts::const_expr(ccx, &**l1, true); + let (l2, _, _) = consts::const_expr(ccx, &**l2, true); return range_result(Result::new(bcx, l1), Result::new(bcx, l2)); } vec_len(n, vec_len_eq, _) => { @@ -340,6 +340,15 @@ struct ArmData<'a, 'b> { * As we proceed `bound_ptrs` are filled with pointers to values to be bound, * these pointers are stored in llmatch variables just before executing `data` arm. */ +#[cfg(not(stage0))] +struct Match<'a, 'b:'a> { + pats: Vec<Gc<ast::Pat>>, + data: &'a ArmData<'a, 'b>, + bound_ptrs: Vec<(Ident, ValueRef)> +} + +///Dox +#[cfg(stage0)] struct Match<'a, 'b> { pats: Vec<Gc<ast::Pat>>, data: &'a ArmData<'a, 'b>, @@ -692,7 +701,7 @@ fn extract_vec_elems<'a>( let vec_datum = match_datum(bcx, val, pat_id); let (base, len) = vec_datum.get_vec_base_and_len(bcx); let vec_ty = node_id_type(bcx, pat_id); - let vt = tvec::vec_types(bcx, ty::sequence_element_type(bcx.tcx(), vec_ty)); + let vt = tvec::vec_types(bcx, ty::sequence_element_type(bcx.tcx(), ty::type_content(vec_ty))); let mut elems = Vec::from_fn(elem_count, |i| { match slice { @@ -863,7 +872,7 @@ fn compare_values<'a>( match ty::get(rhs_t).sty { ty::ty_rptr(_, mt) => match ty::get(mt.ty).sty { ty::ty_str => compare_str(cx, lhs, rhs, rhs_t), - ty::ty_vec(mt, _) => match ty::get(mt.ty).sty { + ty::ty_vec(ty, _) => match ty::get(ty).sty { ty::ty_uint(ast::TyU8) => { // NOTE: cast &[u8] to &str and abuse the str_eq lang item, // which calls memcmp(). diff --git a/src/librustc/middle/trans/adt.rs b/src/librustc/middle/trans/adt.rs index 9c68631baa4..f3f4a88fdee 100644 --- a/src/librustc/middle/trans/adt.rs +++ b/src/librustc/middle/trans/adt.rs @@ -46,6 +46,8 @@ #![allow(unsigned_negate)] use libc::c_ulonglong; +use std::collections::Map; +use std::num::Int; use std::rc::Rc; use llvm::{ValueRef, True, IntEQ, IntNE}; @@ -125,8 +127,11 @@ pub enum Repr { /// For structs, and struct-like parts of anything fancier. pub struct Struct { + // If the struct is DST, then the size and alignment do not take into + // account the unsized fields of the struct. pub size: u64, pub align: u64, + pub sized: bool, pub packed: bool, pub fields: Vec<ty::t> } @@ -178,7 +183,8 @@ fn represent_type_uncached(cx: &CrateContext, t: ty::t) -> Repr { } ty::ty_enum(def_id, ref substs) => { let cases = get_cases(cx.tcx(), def_id, substs); - let hint = ty::lookup_repr_hint(cx.tcx(), def_id); + let hint = *ty::lookup_repr_hints(cx.tcx(), def_id).as_slice().get(0) + .unwrap_or(&attr::ReprAny); let dtor = ty::ty_dtor(cx.tcx(), def_id).has_drop_flag(); @@ -262,37 +268,8 @@ fn represent_type_uncached(cx: &CrateContext, t: ty::t) -> Repr { mk_struct(cx, ftys.as_slice(), false) }).collect(), dtor); } - _ => cx.sess().bug("adt::represent_type called on non-ADT type") - } -} - -/// Determine, without doing translation, whether an ADT must be FFI-safe. -/// For use in lint or similar, where being sound but slightly incomplete is acceptable. -pub fn is_ffi_safe(tcx: &ty::ctxt, def_id: ast::DefId) -> bool { - match ty::get(ty::lookup_item_type(tcx, def_id).ty).sty { - ty::ty_enum(def_id, _) => { - let variants = ty::enum_variants(tcx, def_id); - // Univariant => like struct/tuple. - if variants.len() <= 1 { - return true; - } - let hint = ty::lookup_repr_hint(tcx, def_id); - // Appropriate representation explicitly selected? - if hint.is_ffi_safe() { - return true; - } - // Option<Box<T>> and similar are used in FFI. Rather than try to - // resolve type parameters and recognize this case exactly, this - // overapproximates -- assuming that if a non-C-like enum is being - // used in FFI then the user knows what they're doing. - if variants.iter().any(|vi| !vi.args.is_empty()) { - return true; - } - false - } - // struct, tuple, etc. - // (is this right in the present of typedefs?) - _ => true + _ => cx.sess().bug(format!("adt::represent_type called on non-ADT type: {}", + ty_to_string(cx.tcx(), t)).as_slice()) } } @@ -313,6 +290,7 @@ impl Case { fn is_zerolen(&self, cx: &CrateContext) -> bool { mk_struct(cx, self.tys.as_slice(), false).size == 0 } + fn find_ptr(&self) -> Option<PointerField> { use back::abi::{fn_field_code, slice_elt_base, trt_field_box}; @@ -332,8 +310,7 @@ impl Case { // Box<T> could either be a thin or fat pointer depending on T ty::ty_uniq(t) => match ty::get(t).sty { - // Box<[T]>/Box<str> might be FatPointer in a post DST world - ty::ty_vec(_, None) | ty::ty_str => continue, + ty::ty_vec(_, None) => return Some(FatPointer(i, slice_elt_base)), // Box<Trait> is a pair of pointers: the actual object and a vtable ty::ty_trait(..) => return Some(FatPointer(i, trt_field_box)), @@ -353,7 +330,6 @@ impl Case { // Anything else is not a pointer _ => continue - } } @@ -371,13 +347,28 @@ fn get_cases(tcx: &ty::ctxt, def_id: ast::DefId, substs: &subst::Substs) -> Vec< } fn mk_struct(cx: &CrateContext, tys: &[ty::t], packed: bool) -> Struct { - let lltys = tys.iter().map(|&ty| type_of::sizing_type_of(cx, ty)).collect::<Vec<_>>(); - let llty_rec = Type::struct_(cx, lltys.as_slice(), packed); - Struct { - size: machine::llsize_of_alloc(cx, llty_rec) /*bad*/as u64, - align: machine::llalign_of_min(cx, llty_rec) /*bad*/as u64, - packed: packed, - fields: Vec::from_slice(tys), + if tys.iter().all(|&ty| ty::type_is_sized(cx.tcx(), ty)) { + let lltys = tys.iter().map(|&ty| type_of::sizing_type_of(cx, ty)).collect::<Vec<_>>(); + let llty_rec = Type::struct_(cx, lltys.as_slice(), packed); + Struct { + size: machine::llsize_of_alloc(cx, llty_rec), + align: machine::llalign_of_min(cx, llty_rec), + sized: true, + packed: packed, + fields: Vec::from_slice(tys), + } + } else { + // Ignore any dynamically sized fields. + let lltys = tys.iter().filter(|&ty| ty::type_is_sized(cx.tcx(), *ty)) + .map(|&ty| type_of::sizing_type_of(cx, ty)).collect::<Vec<_>>(); + let llty_rec = Type::struct_(cx, lltys.as_slice(), packed); + Struct { + size: machine::llsize_of_alloc(cx, llty_rec), + align: machine::llalign_of_min(cx, llty_rec), + sized: false, + packed: packed, + fields: Vec::from_slice(tys), + } } } @@ -427,6 +418,9 @@ fn range_to_inttype(cx: &CrateContext, hint: Hint, bounds: &IntBounds) -> IntTyp } attr::ReprAny => { attempts = choose_shortest; + }, + attr::ReprPacked => { + cx.tcx.sess.bug("range_to_inttype: found ReprPacked on an enum"); } } for &ity in attempts.iter() { @@ -479,31 +473,38 @@ pub fn ty_of_inttype(ity: IntType) -> ty::t { * unbounded recursion; see also the comments in `trans::type_of`. */ pub fn type_of(cx: &CrateContext, r: &Repr) -> Type { - generic_type_of(cx, r, None, false) + generic_type_of(cx, r, None, false, false) } -pub fn sizing_type_of(cx: &CrateContext, r: &Repr) -> Type { - generic_type_of(cx, r, None, true) +// Pass dst=true if the type you are passing is a DST. Yes, we could figure +// this out, but if you call this on an unsized type without realising it, you +// are going to get the wrong type (it will not include the unsized parts of it). +pub fn sizing_type_of(cx: &CrateContext, r: &Repr, dst: bool) -> Type { + generic_type_of(cx, r, None, true, dst) } pub fn incomplete_type_of(cx: &CrateContext, r: &Repr, name: &str) -> Type { - generic_type_of(cx, r, Some(name), false) + generic_type_of(cx, r, Some(name), false, false) } pub fn finish_type_of(cx: &CrateContext, r: &Repr, llty: &mut Type) { match *r { CEnum(..) | General(..) | RawNullablePointer { .. } => { } Univariant(ref st, _) | StructWrappedNullablePointer { nonnull: ref st, .. } => - llty.set_struct_body(struct_llfields(cx, st, false).as_slice(), + llty.set_struct_body(struct_llfields(cx, st, false, false).as_slice(), st.packed) } } -fn generic_type_of(cx: &CrateContext, r: &Repr, name: Option<&str>, sizing: bool) -> Type { +fn generic_type_of(cx: &CrateContext, + r: &Repr, + name: Option<&str>, + sizing: bool, + dst: bool) -> Type { match *r { CEnum(ity, _, _) => ll_inttype(cx, ity), RawNullablePointer { nnty, .. } => type_of::sizing_type_of(cx, nnty), Univariant(ref st, _) | StructWrappedNullablePointer { nonnull: ref st, .. } => { match name { None => { - Type::struct_(cx, struct_llfields(cx, st, sizing).as_slice(), + Type::struct_(cx, struct_llfields(cx, st, sizing, dst).as_slice(), st.packed) } Some(name) => { assert_eq!(sizing, false); Type::named_struct(cx, name) } @@ -555,9 +556,10 @@ fn generic_type_of(cx: &CrateContext, r: &Repr, name: Option<&str>, sizing: bool } } -fn struct_llfields(cx: &CrateContext, st: &Struct, sizing: bool) -> Vec<Type> { +fn struct_llfields(cx: &CrateContext, st: &Struct, sizing: bool, dst: bool) -> Vec<Type> { if sizing { - st.fields.iter().map(|&ty| type_of::sizing_type_of(cx, ty)).collect() + st.fields.iter().filter(|&ty| !dst || ty::type_is_sized(cx.tcx(), *ty)) + .map(|&ty| type_of::sizing_type_of(cx, ty)).collect() } else { st.fields.iter().map(|&ty| type_of::type_of(cx, ty)).collect() } @@ -900,7 +902,7 @@ pub fn trans_drop_flag_ptr<'b>(mut bcx: &'b Block<'b>, r: &Repr, * depending on which case of an enum it is. * * To understand the alignment situation, consider `enum E { V64(u64), - * V32(u32, u32) }` on win32. The type has 8-byte alignment to + * V32(u32, u32) }` on Windows. The type has 8-byte alignment to * accommodate the u64, but `V32(x, y)` would have LLVM type `{i32, * i32, i32}`, which is 4-byte aligned. * @@ -970,7 +972,7 @@ fn compute_struct_field_offsets(ccx: &CrateContext, st: &Struct) -> Vec<u64> { for &ty in st.fields.iter() { let llty = type_of::sizing_type_of(ccx, ty); if !st.packed { - let type_align = machine::llalign_of_min(ccx, llty) as u64; + let type_align = type_of::align_of(ccx, ty) as u64; offset = roundup(offset, type_align); } offsets.push(offset); @@ -1014,7 +1016,7 @@ fn build_const_struct(ccx: &CrateContext, st: &Struct, vals: &[ValueRef]) offset += machine::llsize_of_alloc(ccx, val_ty(val)) as u64; } - assert!(offset <= st.size); + assert!(st.sized && offset <= st.size); if offset != st.size { cfields.push(padding(ccx, st.size - offset)); } diff --git a/src/librustc/middle/trans/asm.rs b/src/librustc/middle/trans/asm.rs index 9760ef07a40..be5f586bb97 100644 --- a/src/librustc/middle/trans/asm.rs +++ b/src/librustc/middle/trans/asm.rs @@ -36,13 +36,27 @@ pub fn trans_inline_asm<'a>(bcx: &'a Block<'a>, ia: &ast::InlineAsm) let temp_scope = fcx.push_custom_cleanup_scope(); + let mut ext_inputs = Vec::new(); + let mut ext_constraints = Vec::new(); + // Prepare the output operands - let outputs = ia.outputs.iter().map(|&(ref c, ref out)| { + let outputs = ia.outputs.iter().enumerate().map(|(i, &(ref c, ref out, is_rw))| { constraints.push((*c).clone()); let out_datum = unpack_datum!(bcx, expr::trans(bcx, &**out)); output_types.push(type_of::type_of(bcx.ccx(), out_datum.ty)); - out_datum.val + let val = out_datum.val; + if is_rw { + ext_inputs.push(unpack_result!(bcx, { + callee::trans_arg_datum(bcx, + expr_ty(bcx, &**out), + out_datum, + cleanup::CustomScope(temp_scope), + callee::DontAutorefArg) + })); + ext_constraints.push(i.to_string()); + } + val }).collect::<Vec<_>>(); @@ -58,7 +72,7 @@ pub fn trans_inline_asm<'a>(bcx: &'a Block<'a>, ia: &ast::InlineAsm) cleanup::CustomScope(temp_scope), callee::DontAutorefArg) }) - }).collect::<Vec<_>>(); + }).collect::<Vec<_>>().append(ext_inputs.as_slice()); // no failure occurred preparing operands, no need to cleanup fcx.pop_custom_cleanup_scope(temp_scope); @@ -66,6 +80,7 @@ pub fn trans_inline_asm<'a>(bcx: &'a Block<'a>, ia: &ast::InlineAsm) let mut constraints = String::from_str(constraints.iter() .map(|s| s.get().to_string()) + .chain(ext_constraints.move_iter()) .collect::<Vec<String>>() .connect(",") .as_slice()); diff --git a/src/librustc/middle/trans/base.rs b/src/librustc/middle/trans/base.rs index 2098f923dbe..8968c8cc259 100644 --- a/src/librustc/middle/trans/base.rs +++ b/src/librustc/middle/trans/base.rs @@ -65,7 +65,7 @@ use middle::trans::glue; use middle::trans::inline; use middle::trans::intrinsic; use middle::trans::machine; -use middle::trans::machine::{llalign_of_min, llsize_of, llsize_of_real}; +use middle::trans::machine::{llsize_of, llsize_of_real}; use middle::trans::meth; use middle::trans::monomorphize; use middle::trans::tvec; @@ -87,7 +87,7 @@ use std::cell::{Cell, RefCell}; use std::rc::Rc; use std::{i8, i16, i32, i64}; use syntax::abi::{X86, X86_64, Arm, Mips, Mipsel, Rust, RustCall}; -use syntax::abi::{RustIntrinsic, Abi}; +use syntax::abi::{RustIntrinsic, Abi, OsWindows}; use syntax::ast_util::{local_def, is_local}; use syntax::attr::AttrMetaMethods; use syntax::attr; @@ -364,20 +364,19 @@ fn require_alloc_fn(bcx: &Block, info_ty: ty::t, it: LangItem) -> ast::DefId { // a given type, but with a potentially dynamic size. pub fn malloc_raw_dyn<'a>(bcx: &'a Block<'a>, - ptr_ty: ty::t, + llty_ptr: Type, + info_ty: ty::t, size: ValueRef, align: ValueRef) -> Result<'a> { let _icx = push_ctxt("malloc_raw_exchange"); - let ccx = bcx.ccx(); // Allocate space: let r = callee::trans_lang_call(bcx, - require_alloc_fn(bcx, ptr_ty, ExchangeMallocFnLangItem), + require_alloc_fn(bcx, info_ty, ExchangeMallocFnLangItem), [size, align], None); - let llty_ptr = type_of::type_of(ccx, ptr_ty); Result::new(r.bcx, PointerCast(r.bcx, r.val, llty_ptr)) } @@ -395,7 +394,7 @@ pub fn malloc_raw_dyn_managed<'a>( // Grab the TypeRef type of box_ptr_ty. let box_ptr_ty = ty::mk_box(bcx.tcx(), t); let llty = type_of(ccx, box_ptr_ty); - let llalign = C_uint(ccx, llalign_of_min(ccx, llty) as uint); + let llalign = C_uint(ccx, type_of::align_of(ccx, box_ptr_ty) as uint); // Allocate space: let drop_glue = glue::get_drop_glue(ccx, t); @@ -711,14 +710,33 @@ pub fn iter_structural_ty<'r, return cx; } + let (data_ptr, info) = if ty::type_is_sized(cx.tcx(), t) { + (av, None) + } else { + let data = GEPi(cx, av, [0, abi::slice_elt_base]); + let info = GEPi(cx, av, [0, abi::slice_elt_len]); + (Load(cx, data), Some(Load(cx, info))) + }; + let mut cx = cx; match ty::get(t).sty { ty::ty_struct(..) => { let repr = adt::represent_type(cx.ccx(), t); expr::with_field_tys(cx.tcx(), t, None, |discr, field_tys| { for (i, field_ty) in field_tys.iter().enumerate() { - let llfld_a = adt::trans_field_ptr(cx, &*repr, av, discr, i); - cx = f(cx, llfld_a, field_ty.mt.ty); + let field_ty = field_ty.mt.ty; + let llfld_a = adt::trans_field_ptr(cx, &*repr, data_ptr, discr, i); + + let val = if ty::type_is_sized(cx.tcx(), field_ty) { + llfld_a + } else { + let boxed_ty = ty::mk_open(cx.tcx(), field_ty); + let scratch = datum::rvalue_scratch_datum(cx, boxed_ty, "__fat_ptr_iter"); + Store(cx, llfld_a, GEPi(cx, scratch.val, [0, abi::slice_elt_base])); + Store(cx, info.unwrap(), GEPi(cx, scratch.val, [0, abi::slice_elt_len])); + scratch.val + }; + cx = f(cx, val, field_ty); } }) } @@ -726,19 +744,19 @@ pub fn iter_structural_ty<'r, let repr = adt::represent_type(cx.ccx(), t); let upvars = ty::unboxed_closure_upvars(cx.tcx(), def_id); for (i, upvar) in upvars.iter().enumerate() { - let llupvar = adt::trans_field_ptr(cx, &*repr, av, 0, i); + let llupvar = adt::trans_field_ptr(cx, &*repr, data_ptr, 0, i); cx = f(cx, llupvar, upvar.ty); } } ty::ty_vec(_, Some(n)) => { + let (base, len) = tvec::get_fixed_base_and_len(cx, data_ptr, n); let unit_ty = ty::sequence_element_type(cx.tcx(), t); - let (base, len) = tvec::get_fixed_base_and_byte_len(cx, av, unit_ty, n); cx = tvec::iter_vec_raw(cx, base, unit_ty, len, f); } ty::ty_tup(ref args) => { let repr = adt::represent_type(cx.ccx(), t); for (i, arg) in args.iter().enumerate() { - let llfld_a = adt::trans_field_ptr(cx, &*repr, av, 0, i); + let llfld_a = adt::trans_field_ptr(cx, &*repr, data_ptr, 0, i); cx = f(cx, llfld_a, *arg); } } @@ -782,7 +800,7 @@ pub fn iter_structural_ty<'r, let variant_cx = iter_variant(variant_cx, &*repr, - av, + data_ptr, &**variant, substs, |x,y,z| f(x,y,z)); @@ -956,14 +974,23 @@ pub fn invoke<'a>( llfn: ValueRef, llargs: Vec<ValueRef> , fn_ty: ty::t, - call_info: Option<NodeInfo>) + call_info: Option<NodeInfo>, + // FIXME(15064) is_lang_item is a horrible hack, please remove it + // at the soonest opportunity. + is_lang_item: bool) -> (ValueRef, &'a Block<'a>) { let _icx = push_ctxt("invoke_"); if bcx.unreachable.get() { return (C_null(Type::i8(bcx.ccx())), bcx); } - let attributes = get_fn_llvm_attributes(bcx.ccx(), fn_ty); + // FIXME(15064) Lang item methods may (in the reflect case) not have proper + // types, so doing an attribute lookup will fail. + let attributes = if is_lang_item { + llvm::AttrBuilder::new() + } else { + get_fn_llvm_attributes(bcx.ccx(), fn_ty) + }; match bcx.opt_node_id { None => { @@ -1150,7 +1177,7 @@ pub fn memcpy_ty(bcx: &Block, dst: ValueRef, src: ValueRef, t: ty::t) { if ty::type_is_structural(t) { let llty = type_of::type_of(ccx, t); let llsz = llsize_of(ccx, llty); - let llalign = llalign_of_min(ccx, llty); + let llalign = type_of::align_of(ccx, t); call_memcpy(bcx, dst, src, llsz, llalign as u32); } else { store_ty(bcx, Load(bcx, src), dst, t); @@ -1161,9 +1188,7 @@ pub fn zero_mem(cx: &Block, llptr: ValueRef, t: ty::t) { if cx.unreachable.get() { return; } let _icx = push_ctxt("zero_mem"); let bcx = cx; - let ccx = cx.ccx(); - let llty = type_of::type_of(ccx, t); - memzero(&B(bcx), llptr, llty); + memzero(&B(bcx), llptr, t); } // Always use this function instead of storing a zero constant to the memory @@ -1171,10 +1196,12 @@ pub fn zero_mem(cx: &Block, llptr: ValueRef, t: ty::t) { // allocation for large data structures, and the generated code will be // awful. (A telltale sign of this is large quantities of // `mov [byte ptr foo],0` in the generated code.) -fn memzero(b: &Builder, llptr: ValueRef, ty: Type) { +fn memzero(b: &Builder, llptr: ValueRef, ty: ty::t) { let _icx = push_ctxt("memzero"); let ccx = b.ccx; + let llty = type_of::type_of(ccx, ty); + let intrinsic_key = match ccx.sess().targ_cfg.arch { X86 | Arm | Mips | Mipsel => "llvm.memset.p0i8.i32", X86_64 => "llvm.memset.p0i8.i64" @@ -1183,8 +1210,8 @@ fn memzero(b: &Builder, llptr: ValueRef, ty: Type) { let llintrinsicfn = ccx.get_intrinsic(&intrinsic_key); let llptr = b.pointercast(llptr, Type::i8(ccx).ptr_to()); let llzeroval = C_u8(ccx, 0); - let size = machine::llsize_of(ccx, ty); - let align = C_i32(ccx, llalign_of_min(ccx, ty) as i32); + let size = machine::llsize_of(ccx, llty); + let align = C_i32(ccx, type_of::align_of(ccx, ty) as i32); let volatile = C_bool(ccx, false); b.call(llintrinsicfn, [llptr, llzeroval, size, align, volatile], None); } @@ -1215,13 +1242,14 @@ pub fn alloca_no_lifetime(cx: &Block, ty: Type, name: &str) -> ValueRef { Alloca(cx, ty, name) } -pub fn alloca_zeroed(cx: &Block, ty: Type, name: &str) -> ValueRef { +pub fn alloca_zeroed(cx: &Block, ty: ty::t, name: &str) -> ValueRef { + let llty = type_of::type_of(cx.ccx(), ty); if cx.unreachable.get() { unsafe { - return llvm::LLVMGetUndef(ty.ptr_to().to_ref()); + return llvm::LLVMGetUndef(llty.ptr_to().to_ref()); } } - let p = alloca_no_lifetime(cx, ty, name); + let p = alloca_no_lifetime(cx, llty, name); let b = cx.fcx.ccx.builder(); b.position_before(cx.fcx.alloca_insert_pt.get().unwrap()); memzero(&b, p, ty); @@ -1640,7 +1668,8 @@ fn copy_unboxed_closure_args_to_allocas<'a>( for j in range(0, args.len()) { let tuple_element_type = untupled_arg_types[j]; let tuple_element_datum = - tuple_datum.get_element(tuple_element_type, + tuple_datum.get_element(bcx, + tuple_element_type, |llval| GEPi(bcx, llval, [0, j])); let tuple_element_datum = tuple_element_datum.to_expr_datum(); let tuple_element_datum = @@ -2240,26 +2269,44 @@ pub fn get_fn_llvm_attributes(ccx: &CrateContext, fn_ty: ty::t) ty::ty_bare_fn(ref f) => (f.sig.clone(), f.abi, false), ty::ty_unboxed_closure(closure_did, _) => { let unboxed_closures = ccx.tcx.unboxed_closures.borrow(); - let function_type = unboxed_closures.get(&closure_did) - .closure_type - .clone(); + let ref function_type = unboxed_closures.get(&closure_did) + .closure_type; + (function_type.sig.clone(), RustCall, true) } - _ => fail!("expected closure or function.") + _ => ccx.sess().bug("expected closure or function.") }; + // Since index 0 is the return value of the llvm func, we start // at either 1 or 2 depending on whether there's an env slot or not let mut first_arg_offset = if has_env { 2 } else { 1 }; let mut attrs = llvm::AttrBuilder::new(); let ret_ty = fn_sig.output; - // These have an odd calling convention, so we skip them for now. - // - // FIXME(pcwalton): We don't have to skip them; just untuple the result. - if abi == RustCall { - return attrs; - } + // These have an odd calling convention, so we need to manually + // unpack the input ty's + let input_tys = match ty::get(fn_ty).sty { + ty::ty_unboxed_closure(_, _) => { + assert!(abi == RustCall); + + match ty::get(fn_sig.inputs[0]).sty { + ty::ty_nil => Vec::new(), + ty::ty_tup(ref inputs) => inputs.clone(), + _ => ccx.sess().bug("expected tuple'd inputs") + } + }, + ty::ty_bare_fn(_) if abi == RustCall => { + let inputs = vec![fn_sig.inputs[0]]; + + match ty::get(fn_sig.inputs[1]).sty { + ty::ty_nil => inputs, + ty::ty_tup(ref t_in) => inputs.append(t_in.as_slice()), + _ => ccx.sess().bug("expected tuple'd inputs") + } + } + _ => fn_sig.inputs.clone() + }; // A function pointer is called without the declaration // available, so we have to apply any attributes with ABI @@ -2284,9 +2331,7 @@ pub fn get_fn_llvm_attributes(ccx: &CrateContext, fn_ty: ty::t) match ty::get(ret_ty).sty { // `~` pointer return values never alias because ownership // is transferred - ty::ty_uniq(it) if match ty::get(it).sty { - ty::ty_str | ty::ty_vec(..) | ty::ty_trait(..) => true, _ => false - } => {} + ty::ty_uniq(it) if !ty::type_is_sized(ccx.tcx(), it) => {} ty::ty_uniq(_) => { attrs.ret(llvm::NoAliasAttribute); } @@ -2297,9 +2342,7 @@ pub fn get_fn_llvm_attributes(ccx: &CrateContext, fn_ty: ty::t) match ty::get(ret_ty).sty { // These are not really pointers but pairs, (pointer, len) ty::ty_uniq(it) | - ty::ty_rptr(_, ty::mt { ty: it, .. }) if match ty::get(it).sty { - ty::ty_str | ty::ty_vec(..) | ty::ty_trait(..) => true, _ => false - } => {} + ty::ty_rptr(_, ty::mt { ty: it, .. }) if !ty::type_is_sized(ccx.tcx(), it) => {} ty::ty_uniq(inner) | ty::ty_rptr(_, ty::mt { ty: inner, .. }) => { let llret_sz = llsize_of_real(ccx, type_of::type_of(ccx, inner)); attrs.ret(llvm::DereferenceableAttribute(llret_sz)); @@ -2315,7 +2358,7 @@ pub fn get_fn_llvm_attributes(ccx: &CrateContext, fn_ty: ty::t) } } - for (idx, &t) in fn_sig.inputs.iter().enumerate().map(|(i, v)| (i + first_arg_offset, v)) { + for (idx, &t) in input_tys.iter().enumerate().map(|(i, v)| (i + first_arg_offset, v)) { match ty::get(t).sty { // this needs to be first to prevent fat pointers from falling through _ if !type_is_immediate(ccx, t) => { @@ -2446,6 +2489,13 @@ pub fn create_entry_wrapper(ccx: &CrateContext, &ccx.int_type); let llfn = decl_cdecl_fn(ccx, "main", llfty, ty::mk_nil()); + + // FIXME: #16581: Marking a symbol in the executable with `dllexport` + // linkage forces MinGW's linker to output a `.reloc` section for ASLR + if ccx.sess().targ_cfg.os == OsWindows { + unsafe { llvm::LLVMRustSetDLLExportStorageClass(llfn) } + } + let llbb = "top".with_c_str(|buf| { unsafe { llvm::LLVMAppendBasicBlockInContext(ccx.llcx, llfn, buf) @@ -2559,7 +2609,7 @@ pub fn get_item_val(ccx: &CrateContext, id: ast::NodeId) -> ValueRef { // We need the translated value here, because for enums the // LLVM type is not fully determined by the Rust type. - let (v, inlineable) = consts::const_expr(ccx, &**expr, is_local); + let (v, inlineable, _) = consts::const_expr(ccx, &**expr, is_local); ccx.const_values.borrow_mut().insert(id, v); let mut inlineable = inlineable; diff --git a/src/librustc/middle/trans/cabi_x86_64.rs b/src/librustc/middle/trans/cabi_x86_64.rs index 32e481dd2d4..2ab4814fa0c 100644 --- a/src/librustc/middle/trans/cabi_x86_64.rs +++ b/src/librustc/middle/trans/cabi_x86_64.rs @@ -176,11 +176,15 @@ fn classify_ty(ty: Type) -> Vec<RegClass> { } fn classify_struct(tys: &[Type], - cls: &mut [RegClass], i: uint, - off: uint) { + cls: &mut [RegClass], + i: uint, + off: uint, + packed: bool) { let mut field_off = off; for ty in tys.iter() { - field_off = align(field_off, *ty); + if !packed { + field_off = align(field_off, *ty); + } classify(*ty, cls, i, field_off); field_off += ty_size(*ty); } @@ -219,7 +223,7 @@ fn classify_ty(ty: Type) -> Vec<RegClass> { unify(cls, ix + off / 8u, SSEDs); } Struct => { - classify_struct(ty.field_types().as_slice(), cls, ix, off); + classify_struct(ty.field_types().as_slice(), cls, ix, off, ty.is_packed()); } Array => { let len = ty.array_length(); diff --git a/src/librustc/middle/trans/callee.rs b/src/librustc/middle/trans/callee.rs index e397ee94c8a..7566cfbac56 100644 --- a/src/librustc/middle/trans/callee.rs +++ b/src/librustc/middle/trans/callee.rs @@ -51,6 +51,7 @@ use middle::typeck; use middle::typeck::coherence::make_substs_for_receiver_types; use middle::typeck::MethodCall; use util::ppaux::Repr; +use util::ppaux::ty_to_string; use std::gc::Gc; use syntax::abi as synabi; @@ -288,7 +289,7 @@ fn resolve_default_method_vtables(bcx: &Block, /// `Trait` so that a by-value self method can be called. pub fn trans_unboxing_shim(bcx: &Block, llshimmedfn: ValueRef, - method: &ty::Method, + fty: &ty::BareFnTy, method_id: ast::DefId, substs: subst::Substs) -> ValueRef { @@ -297,29 +298,29 @@ pub fn trans_unboxing_shim(bcx: &Block, let tcx = bcx.tcx(); // Transform the self type to `Box<self_type>`. - let self_type = *method.fty.sig.inputs.get(0); + let self_type = *fty.sig.inputs.get(0); let boxed_self_type = ty::mk_uniq(tcx, self_type); let boxed_function_type = ty::FnSig { - binder_id: method.fty.sig.binder_id, - inputs: method.fty.sig.inputs.iter().enumerate().map(|(i, typ)| { + binder_id: fty.sig.binder_id, + inputs: fty.sig.inputs.iter().enumerate().map(|(i, typ)| { if i == 0 { boxed_self_type } else { *typ } }).collect(), - output: method.fty.sig.output, + output: fty.sig.output, variadic: false, }; let boxed_function_type = ty::BareFnTy { - fn_style: method.fty.fn_style, - abi: method.fty.abi, + fn_style: fty.fn_style, + abi: fty.abi, sig: boxed_function_type, }; let boxed_function_type = ty::mk_bare_fn(tcx, boxed_function_type).subst(tcx, &substs); let function_type = - ty::mk_bare_fn(tcx, method.fty.clone()).subst(tcx, &substs); + ty::mk_bare_fn(tcx, (*fty).clone()).subst(tcx, &substs); let function_name = ty::with_path(tcx, method_id, |path| { link::mangle_internal_name_by_path_and_seq(path, "unboxing_shim") @@ -853,7 +854,8 @@ pub fn trans_call_inner<'a>( llfn, llargs, callee_ty, - call_info); + call_info, + dest.is_none()); bcx = b; llresult = llret; @@ -968,6 +970,7 @@ fn trans_args_under_call_abi<'a>( let repr_ptr = &*repr; for i in range(0, field_types.len()) { let arg_datum = tuple_lvalue_datum.get_element( + bcx, *field_types.get(i), |srcval| { adt::trans_field_ptr(bcx, repr_ptr, srcval, 0, i) @@ -1194,6 +1197,8 @@ pub fn trans_arg_datum<'a>( let llformal_arg_ty = type_of::type_of_explicit_arg(ccx, formal_arg_ty); debug!("casting actual type ({}) to match formal ({})", bcx.val_to_string(val), bcx.llty_str(llformal_arg_ty)); + debug!("Rust types: {}; {}", ty_to_string(bcx.tcx(), arg_datum_ty), + ty_to_string(bcx.tcx(), formal_arg_ty)); val = PointerCast(bcx, val, llformal_arg_ty); } } diff --git a/src/librustc/middle/trans/cleanup.rs b/src/librustc/middle/trans/cleanup.rs index cf2410f6571..b36887c80e9 100644 --- a/src/librustc/middle/trans/cleanup.rs +++ b/src/librustc/middle/trans/cleanup.rs @@ -25,7 +25,6 @@ use middle::ty; use syntax::ast; use util::ppaux::Repr; - pub struct CleanupScope<'a> { // The id of this cleanup scope. If the id is None, // this is a *temporary scope* that is pushed during trans to @@ -35,7 +34,7 @@ pub struct CleanupScope<'a> { kind: CleanupScopeKind<'a>, // Cleanups to run upon scope exit. - cleanups: Vec<Box<Cleanup>>, + cleanups: Vec<CleanupObj>, cached_early_exits: Vec<CachedEarlyExit>, cached_landing_pad: Option<BasicBlockRef>, @@ -73,6 +72,8 @@ pub trait Cleanup { fn trans<'a>(&self, bcx: &'a Block<'a>) -> &'a Block<'a>; } +pub type CleanupObj = Box<Cleanup+'static>; + pub enum ScopeId { AstScope(ast::NodeId), CustomScope(CustomScopeIndex) @@ -238,7 +239,7 @@ impl<'a> CleanupMethods<'a> for FunctionContext<'a> { cleanup_scope, self.ccx.tn.val_to_string(val)); - self.schedule_clean(cleanup_scope, drop as Box<Cleanup>); + self.schedule_clean(cleanup_scope, drop as CleanupObj); } fn schedule_drop_mem(&self, @@ -264,7 +265,7 @@ impl<'a> CleanupMethods<'a> for FunctionContext<'a> { self.ccx.tn.val_to_string(val), ty.repr(self.ccx.tcx())); - self.schedule_clean(cleanup_scope, drop as Box<Cleanup>); + self.schedule_clean(cleanup_scope, drop as CleanupObj); } fn schedule_drop_and_zero_mem(&self, @@ -291,7 +292,7 @@ impl<'a> CleanupMethods<'a> for FunctionContext<'a> { ty.repr(self.ccx.tcx()), true); - self.schedule_clean(cleanup_scope, drop as Box<Cleanup>); + self.schedule_clean(cleanup_scope, drop as CleanupObj); } fn schedule_drop_immediate(&self, @@ -316,7 +317,7 @@ impl<'a> CleanupMethods<'a> for FunctionContext<'a> { self.ccx.tn.val_to_string(val), ty.repr(self.ccx.tcx())); - self.schedule_clean(cleanup_scope, drop as Box<Cleanup>); + self.schedule_clean(cleanup_scope, drop as CleanupObj); } fn schedule_free_value(&self, @@ -336,12 +337,12 @@ impl<'a> CleanupMethods<'a> for FunctionContext<'a> { self.ccx.tn.val_to_string(val), heap); - self.schedule_clean(cleanup_scope, drop as Box<Cleanup>); + self.schedule_clean(cleanup_scope, drop as CleanupObj); } fn schedule_clean(&self, cleanup_scope: ScopeId, - cleanup: Box<Cleanup>) { + cleanup: CleanupObj) { match cleanup_scope { AstScope(id) => self.schedule_clean_in_ast_scope(id, cleanup), CustomScope(id) => self.schedule_clean_in_custom_scope(id, cleanup), @@ -350,7 +351,7 @@ impl<'a> CleanupMethods<'a> for FunctionContext<'a> { fn schedule_clean_in_ast_scope(&self, cleanup_scope: ast::NodeId, - cleanup: Box<Cleanup>) { + cleanup: CleanupObj) { /*! * Schedules a cleanup to occur upon exit from `cleanup_scope`. * If `cleanup_scope` is not provided, then the cleanup is scheduled @@ -378,7 +379,7 @@ impl<'a> CleanupMethods<'a> for FunctionContext<'a> { fn schedule_clean_in_custom_scope(&self, custom_scope: CustomScopeIndex, - cleanup: Box<Cleanup>) { + cleanup: CleanupObj) { /*! * Schedules a cleanup to occur in the top-most scope, * which must be a temporary scope. @@ -1021,13 +1022,13 @@ pub trait CleanupMethods<'a> { content_ty: ty::t); fn schedule_clean(&self, cleanup_scope: ScopeId, - cleanup: Box<Cleanup>); + cleanup: CleanupObj); fn schedule_clean_in_ast_scope(&self, cleanup_scope: ast::NodeId, - cleanup: Box<Cleanup>); + cleanup: CleanupObj); fn schedule_clean_in_custom_scope(&self, custom_scope: CustomScopeIndex, - cleanup: Box<Cleanup>); + cleanup: CleanupObj); fn needs_invoke(&self) -> bool; fn get_landing_pad(&'a self) -> BasicBlockRef; } diff --git a/src/librustc/middle/trans/common.rs b/src/librustc/middle/trans/common.rs index 0244d0ddc6e..05528d2b3d8 100644 --- a/src/librustc/middle/trans/common.rs +++ b/src/librustc/middle/trans/common.rs @@ -65,14 +65,18 @@ fn type_is_newtype_immediate(ccx: &CrateContext, ty: ty::t) -> bool { pub fn type_is_immediate(ccx: &CrateContext, ty: ty::t) -> bool { use middle::trans::machine::llsize_of_alloc; use middle::trans::type_of::sizing_type_of; + let tcx = ccx.tcx(); let simple = ty::type_is_scalar(ty) || ty::type_is_boxed(ty) || ty::type_is_unique(ty) || ty::type_is_region_ptr(ty) || type_is_newtype_immediate(ccx, ty) || ty::type_is_bot(ty) || ty::type_is_simd(tcx, ty); - if simple && !ty::type_is_trait(ty) { + if simple && !ty::type_is_fat_ptr(tcx, ty) { return true; } + if !ty::type_is_sized(tcx, ty) { + return false; + } match ty::get(ty).sty { ty::ty_bot => true, ty::ty_struct(..) | ty::ty_enum(..) | ty::ty_tup(..) | @@ -201,7 +205,9 @@ impl param_substs { } fn param_substs_to_string(this: ¶m_substs, tcx: &ty::ctxt) -> String { - format!("param_substs({})", this.substs.repr(tcx)) + format!("param_substs(substs={},vtables={})", + this.substs.repr(tcx), + this.vtables.repr(tcx)) } impl Repr for param_substs { @@ -859,8 +865,7 @@ pub fn find_vtable(tcx: &ty::ctxt, debug!("find_vtable(n_param={:?}, n_bound={}, ps={})", n_param, n_bound, ps.repr(tcx)); - let param_bounds = ps.vtables.get(n_param.space, - n_param.index); + let param_bounds = ps.vtables.get(n_param.space, n_param.index); param_bounds.get(n_bound).clone() } diff --git a/src/librustc/middle/trans/consts.rs b/src/librustc/middle/trans/consts.rs index 07f0dbaf73f..b3798c9f84d 100644 --- a/src/librustc/middle/trans/consts.rs +++ b/src/librustc/middle/trans/consts.rs @@ -11,11 +11,9 @@ use back::abi; use llvm; -use llvm::{ConstFCmp, ConstICmp, SetLinkage, PrivateLinkage, ValueRef, Bool, True, - False}; +use llvm::{ConstFCmp, ConstICmp, SetLinkage, PrivateLinkage, ValueRef, Bool, True, False}; use llvm::{IntEQ, IntNE, IntUGT, IntUGE, IntULT, IntULE, IntSGT, IntSGE, IntSLT, IntSLE, RealOEQ, RealOGT, RealOGE, RealOLT, RealOLE, RealONE}; - use metadata::csearch; use middle::const_eval; use middle::def; @@ -98,12 +96,17 @@ pub fn const_ptrcast(cx: &CrateContext, a: ValueRef, t: Type) -> ValueRef { } } +// Helper function because we don't have tuple-swizzling. +fn first_two<R, S, T>((a, b, _): (R, S, T)) -> (R, S) { + (a, b) +} + fn const_vec(cx: &CrateContext, e: &ast::Expr, es: &[Gc<ast::Expr>], is_local: bool) -> (ValueRef, Type, bool) { let vec_ty = ty::expr_ty(cx.tcx(), e); let unit_ty = ty::sequence_element_type(cx.tcx(), vec_ty); let llunitty = type_of::type_of(cx, unit_ty); - let (vs, inlineable) = vec::unzip(es.iter().map(|e| const_expr(cx, &**e, is_local))); + let (vs, inlineable) = vec::unzip(es.iter().map(|e| first_two(const_expr(cx, &**e, is_local)))); // If the vector contains enums, an LLVM array won't work. let v = if vs.iter().any(|vi| val_ty(*vi) != llunitty) { C_struct(cx, vs.as_slice(), false) @@ -113,13 +116,14 @@ fn const_vec(cx: &CrateContext, e: &ast::Expr, (v, llunitty, inlineable.iter().fold(true, |a, &b| a && b)) } -pub fn const_addr_of(cx: &CrateContext, cv: ValueRef) -> ValueRef { +pub fn const_addr_of(cx: &CrateContext, cv: ValueRef, mutbl: ast::Mutability) -> ValueRef { unsafe { let gv = "const".with_c_str(|name| { llvm::LLVMAddGlobal(cx.llmod, val_ty(cv).to_ref(), name) }); llvm::LLVMSetInitializer(gv, cv); - llvm::LLVMSetGlobalConstant(gv, True); + llvm::LLVMSetGlobalConstant(gv, + if mutbl == ast::MutImmutable {True} else {False}); SetLinkage(gv, PrivateLinkage); gv } @@ -131,7 +135,6 @@ fn const_deref_ptr(cx: &CrateContext, v: ValueRef) -> ValueRef { None => v }; unsafe { - assert_eq!(llvm::LLVMIsGlobalConstant(v), True); llvm::LLVMGetInitializer(v) } } @@ -146,25 +149,25 @@ fn const_deref(cx: &CrateContext, v: ValueRef, t: ty::t, explicit: bool) -> (ValueRef, ty::t) { match ty::deref(t, explicit) { Some(ref mt) => { - assert!(mt.mutbl != ast::MutMutable); - let dv = match ty::get(t).sty { + match ty::get(t).sty { ty::ty_ptr(mt) | ty::ty_rptr(_, mt) => { - match ty::get(mt.ty).sty { - ty::ty_vec(_, None) | ty::ty_str | ty::ty_trait(..) => { - cx.sess().bug("unexpected unsized type") - } - _ => const_deref_ptr(cx, v), + if ty::type_is_sized(cx.tcx(), mt.ty) { + (const_deref_ptr(cx, v), mt.ty) + } else { + // Derefing a fat pointer does not change the representation, + // just the type to ty_open. + (v, ty::mk_open(cx.tcx(), mt.ty)) } } ty::ty_enum(..) | ty::ty_struct(..) => { - const_deref_newtype(cx, v, t) + assert!(mt.mutbl != ast::MutMutable); + (const_deref_newtype(cx, v, t), mt.ty) } _ => { cx.sess().bug(format!("unexpected dereferenceable type {}", ty_to_string(cx.tcx(), t)).as_slice()) } - }; - (dv, mt.ty) + } } None => { cx.sess().bug(format!("can't dereference const of type {}", @@ -193,12 +196,12 @@ pub fn get_const_val(cx: &CrateContext, !cx.non_inlineable_statics.borrow().contains(&def_id.node)) } -pub fn const_expr(cx: &CrateContext, e: &ast::Expr, is_local: bool) -> (ValueRef, bool) { +pub fn const_expr(cx: &CrateContext, e: &ast::Expr, is_local: bool) -> (ValueRef, bool, ty::t) { let (llconst, inlineable) = const_expr_unadjusted(cx, e, is_local); let mut llconst = llconst; let mut inlineable = inlineable; let ety = ty::expr_ty(cx.tcx(), e); - let ety_adjusted = ty::expr_ty_adjusted(cx.tcx(), e); + let mut ety_adjusted = ty::expr_ty_adjusted(cx.tcx(), e); let opt_adj = cx.tcx.adjustments.borrow().find_copy(&e.id); match opt_adj { None => { } @@ -219,51 +222,64 @@ pub fn const_expr(cx: &CrateContext, e: &ast::Expr, is_local: bool) -> (ValueRef format!("unexpected static function: {:?}", store).as_slice()) } - ty::AutoObject(..) => { - cx.sess() - .span_unimpl(e.span, - "unimplemented const coercion to trait \ - object"); - } ty::AutoDerefRef(ref adj) => { let mut ty = ety; - let mut maybe_ptr = None; - for _ in range(0, adj.autoderefs) { + // Save the last autoderef in case we can avoid it. + for _ in range(0, adj.autoderefs-1) { let (dv, dt) = const_deref(cx, llconst, ty, false); - maybe_ptr = Some(llconst); llconst = dv; ty = dt; } match adj.autoref { - None => { } + None => { + let (dv, dt) = const_deref(cx, llconst, ty, false); + llconst = dv; + + // If we derefed a fat pointer then we will have an + // open type here. So we need to update the type with + // the one returned from const_deref. + ety_adjusted = dt; + } Some(ref autoref) => { - // Don't copy data to do a deref+ref. - let llptr = match maybe_ptr { - Some(ptr) => ptr, - None => { - inlineable = false; - const_addr_of(cx, llconst) - } - }; match *autoref { - ty::AutoUnsafe(m) | - ty::AutoPtr(ty::ReStatic, m) => { - assert!(m != ast::MutMutable); - llconst = llptr; + ty::AutoUnsafe(_) | + ty::AutoPtr(ty::ReStatic, _, None) => { + // Don't copy data to do a deref+ref + // (i.e., skip the last auto-deref). + if adj.autoderefs == 0 { + inlineable = false; + llconst = const_addr_of(cx, llconst, ast::MutImmutable); + } } - ty::AutoBorrowVec(ty::ReStatic, m) => { - assert!(m != ast::MutMutable); - assert_eq!(abi::slice_elt_base, 0); - assert_eq!(abi::slice_elt_len, 1); + ty::AutoPtr(ty::ReStatic, _, Some(box ty::AutoUnsize(..))) => { + if adj.autoderefs > 0 { + // Seeing as we are deref'ing here and take a reference + // again to make the pointer part of the far pointer below, + // we just skip the whole thing. We still need the type + // though. This works even if we don't need to deref + // because of byref semantics. Note that this is not just + // an optimisation, it is necessary for mutable vectors to + // work properly. + let (_, dt) = const_deref(cx, llconst, ty, false); + ty = dt; + } + match ty::get(ty).sty { - ty::ty_vec(_, Some(len)) => { + ty::ty_vec(unit_ty, Some(len)) => { + inlineable = false; + let llunitty = type_of::type_of(cx, unit_ty); + let llptr = const_ptrcast(cx, llconst, llunitty); + assert_eq!(abi::slice_elt_base, 0); + assert_eq!(abi::slice_elt_len, 1); llconst = C_struct(cx, [ llptr, C_uint(cx, len) ], false); } - _ => {} + _ => cx.sess().span_bug(e.span, + format!("unimplemented type in const unsize: {}", + ty_to_string(cx.tcx(), ty)).as_slice()) } } _ => { @@ -294,7 +310,7 @@ pub fn const_expr(cx: &CrateContext, e: &ast::Expr, is_local: bool) -> (ValueRef e.repr(cx.tcx()), ty_to_string(cx.tcx(), ety), csize, tsize).as_slice()); } - (llconst, inlineable) + (llconst, inlineable, ety_adjusted) } // the bool returned is whether this expression can be inlined into other crates @@ -302,7 +318,7 @@ pub fn const_expr(cx: &CrateContext, e: &ast::Expr, is_local: bool) -> (ValueRef fn const_expr_unadjusted(cx: &CrateContext, e: &ast::Expr, is_local: bool) -> (ValueRef, bool) { let map_list = |exprs: &[Gc<ast::Expr>]| { - exprs.iter().map(|e| const_expr(cx, &**e, is_local)) + exprs.iter().map(|e| first_two(const_expr(cx, &**e, is_local))) .fold((Vec::new(), true), |(l, all_inlineable), (val, inlineable)| { (l.append_one(val), all_inlineable && inlineable) @@ -315,8 +331,8 @@ fn const_expr_unadjusted(cx: &CrateContext, e: &ast::Expr, (consts::const_lit(cx, e, (**lit).clone()), true) } ast::ExprBinary(b, ref e1, ref e2) => { - let (te1, _) = const_expr(cx, &**e1, is_local); - let (te2, _) = const_expr(cx, &**e2, is_local); + let (te1, _, _) = const_expr(cx, &**e1, is_local); + let (te2, _, _) = const_expr(cx, &**e2, is_local); let te2 = base::cast_shift_const_rhs(b, te1, te2); @@ -397,7 +413,7 @@ fn const_expr_unadjusted(cx: &CrateContext, e: &ast::Expr, }, true) }, ast::ExprUnary(u, ref e) => { - let (te, _) = const_expr(cx, &**e, is_local); + let (te, _, _) = const_expr(cx, &**e, is_local); let ty = ty::expr_ty(cx.tcx(), &**e); let is_float = ty::type_is_fp(ty); return (match u { @@ -413,9 +429,8 @@ fn const_expr_unadjusted(cx: &CrateContext, e: &ast::Expr, }, true) } ast::ExprField(ref base, field, _) => { - let bt = ty::expr_ty_adjusted(cx.tcx(), &**base); + let (bv, inlineable, bt) = const_expr(cx, &**base, is_local); let brepr = adt::represent_type(cx, bt); - let (bv, inlineable) = const_expr(cx, &**base, is_local); expr::with_field_tys(cx.tcx(), bt, None, |discr, field_tys| { let ix = ty::field_idx_strict(cx.tcx(), field.node.name, field_tys); (adt::const_get_field(cx, &*brepr, bv, discr, ix), inlineable) @@ -423,8 +438,7 @@ fn const_expr_unadjusted(cx: &CrateContext, e: &ast::Expr, } ast::ExprIndex(ref base, ref index) => { - let bt = ty::expr_ty_adjusted(cx.tcx(), &**base); - let (bv, inlineable) = const_expr(cx, &**base, is_local); + let (bv, inlineable, bt) = const_expr(cx, &**base, is_local); let iv = match const_eval::eval_const_expr(cx.tcx(), &**index) { const_eval::const_int(i) => i as u64, const_eval::const_uint(u) => u, @@ -433,16 +447,29 @@ fn const_expr_unadjusted(cx: &CrateContext, e: &ast::Expr, }; let (arr, len) = match ty::get(bt).sty { ty::ty_vec(_, Some(u)) => (bv, C_uint(cx, u)), - ty::ty_rptr(_, mt) => match ty::get(mt.ty).sty { + ty::ty_open(ty) => match ty::get(ty).sty { ty::ty_vec(_, None) | ty::ty_str => { let e1 = const_get_elt(cx, bv, [0]); (const_deref_ptr(cx, e1), const_get_elt(cx, bv, [1])) }, _ => cx.sess().span_bug(base.span, - "index-expr base must be a vector or string type") + format!("index-expr base must be a vector \ + or string type, found {}", + ty_to_string(cx.tcx(), bt)).as_slice()) + }, + ty::ty_rptr(_, mt) => match ty::get(mt.ty).sty { + ty::ty_vec(_, Some(u)) => { + (const_deref_ptr(cx, bv), C_uint(cx, u)) + }, + _ => cx.sess().span_bug(base.span, + format!("index-expr base must be a vector \ + or string type, found {}", + ty_to_string(cx.tcx(), bt)).as_slice()) }, _ => cx.sess().span_bug(base.span, - "index-expr base must be a vector or string type") + format!("index-expr base must be a vector \ + or string type, found {}", + ty_to_string(cx.tcx(), bt)).as_slice()) }; let len = llvm::LLVMConstIntGetZExtValue(len) as u64; @@ -467,10 +494,9 @@ fn const_expr_unadjusted(cx: &CrateContext, e: &ast::Expr, ast::ExprCast(ref base, _) => { let ety = ty::expr_ty(cx.tcx(), e); let llty = type_of::type_of(cx, ety); - let basety = ty::expr_ty(cx.tcx(), &**base); - let (v, inlineable) = const_expr(cx, &**base, is_local); - return (match (expr::cast_type_kind(basety), - expr::cast_type_kind(ety)) { + let (v, inlineable, basety) = const_expr(cx, &**base, is_local); + return (match (expr::cast_type_kind(cx.tcx(), basety), + expr::cast_type_kind(cx.tcx(), ety)) { (expr::cast_integral, expr::cast_integral) => { let s = ty::type_is_signed(basety) as Bool; @@ -494,7 +520,7 @@ fn const_expr_unadjusted(cx: &CrateContext, e: &ast::Expr, let repr = adt::represent_type(cx, basety); let discr = adt::const_get_discrim(cx, &*repr, v); let iv = C_integral(cx.int_type, discr, false); - let ety_cast = expr::cast_type_kind(ety); + let ety_cast = expr::cast_type_kind(cx.tcx(), ety); match ety_cast { expr::cast_integral => { let s = ty::type_is_signed(ety) as Bool; @@ -516,9 +542,9 @@ fn const_expr_unadjusted(cx: &CrateContext, e: &ast::Expr, } }, inlineable) } - ast::ExprAddrOf(ast::MutImmutable, ref sub) => { - let (e, _) = const_expr(cx, &**sub, is_local); - (const_addr_of(cx, e), false) + ast::ExprAddrOf(mutbl, ref sub) => { + let (e, _, _) = const_expr(cx, &**sub, is_local); + (const_addr_of(cx, e, mutbl), false) } ast::ExprTup(ref es) => { let ety = ty::expr_ty(cx.tcx(), e); @@ -540,10 +566,10 @@ fn const_expr_unadjusted(cx: &CrateContext, e: &ast::Expr, let (cs, inlineable) = vec::unzip(field_tys.iter().enumerate() .map(|(ix, &field_ty)| { match fs.iter().find(|f| field_ty.ident.name == f.ident.node.name) { - Some(ref f) => const_expr(cx, &*f.expr, is_local), + Some(ref f) => first_two(const_expr(cx, &*f.expr, is_local)), None => { match base_val { - Some((bv, inlineable)) => { + Some((bv, inlineable, _)) => { (adt::const_get_field(cx, &*repr, bv, discr, ix), inlineable) } @@ -563,34 +589,6 @@ fn const_expr_unadjusted(cx: &CrateContext, e: &ast::Expr, is_local); (v, inlineable) } - ast::ExprVstore(ref sub, store @ ast::ExprVstoreSlice) | - ast::ExprVstore(ref sub, store @ ast::ExprVstoreMutSlice) => { - match sub.node { - ast::ExprLit(ref lit) => { - match lit.node { - ast::LitStr(..) => { const_expr(cx, &**sub, is_local) } - _ => { cx.sess().span_bug(e.span, "bad const-slice lit") } - } - } - ast::ExprVec(ref es) => { - let (cv, llunitty, _) = const_vec(cx, - e, - es.as_slice(), - is_local); - let llty = val_ty(cv); - let gv = "const".with_c_str(|name| { - llvm::LLVMAddGlobal(cx.llmod, llty.to_ref(), name) - }); - llvm::LLVMSetInitializer(gv, cv); - llvm::LLVMSetGlobalConstant(gv, - if store == ast::ExprVstoreMutSlice { False } else { True }); - SetLinkage(gv, PrivateLinkage); - let p = const_ptrcast(cx, gv, llunitty); - (C_struct(cx, [p, C_uint(cx, es.len())], false), false) - } - _ => cx.sess().span_bug(e.span, "bad const-slice expr") - } - } ast::ExprRepeat(ref elem, ref count) => { let vec_ty = ty::expr_ty(cx.tcx(), e); let unit_ty = ty::sequence_element_type(cx.tcx(), vec_ty); @@ -669,10 +667,10 @@ fn const_expr_unadjusted(cx: &CrateContext, e: &ast::Expr, _ => cx.sess().span_bug(e.span, "expected a struct or variant def") } } - ast::ExprParen(ref e) => { const_expr(cx, &**e, is_local) } + ast::ExprParen(ref e) => first_two(const_expr(cx, &**e, is_local)), ast::ExprBlock(ref block) => { match block.expr { - Some(ref expr) => const_expr(cx, &**expr, is_local), + Some(ref expr) => first_two(const_expr(cx, &**expr, is_local)), None => (C_nil(cx), true) } } diff --git a/src/librustc/middle/trans/controlflow.rs b/src/librustc/middle/trans/controlflow.rs index 7a1864448e2..9ec52341348 100644 --- a/src/librustc/middle/trans/controlflow.rs +++ b/src/librustc/middle/trans/controlflow.rs @@ -497,7 +497,7 @@ pub fn trans_fail<'a>( let filename = C_str_slice(ccx, filename); let line = C_int(ccx, loc.line as int); let expr_file_line_const = C_struct(ccx, &[v_str, filename, line], false); - let expr_file_line = consts::const_addr_of(ccx, expr_file_line_const); + let expr_file_line = consts::const_addr_of(ccx, expr_file_line_const, ast::MutImmutable); let args = vec!(expr_file_line); let did = langcall(bcx, Some(sp), "", FailFnLangItem); let bcx = callee::trans_lang_call(bcx, @@ -525,7 +525,7 @@ pub fn trans_fail_bounds_check<'a>( let filename = C_str_slice(ccx, filename); let line = C_int(ccx, loc.line as int); let file_line_const = C_struct(ccx, &[filename, line], false); - let file_line = consts::const_addr_of(ccx, file_line_const); + let file_line = consts::const_addr_of(ccx, file_line_const, ast::MutImmutable); let args = vec!(file_line, index, len); let did = langcall(bcx, Some(sp), "", FailBoundsCheckFnLangItem); let bcx = callee::trans_lang_call(bcx, diff --git a/src/librustc/middle/trans/datum.rs b/src/librustc/middle/trans/datum.rs index f69a8af9c08..2882eaf10be 100644 --- a/src/librustc/middle/trans/datum.rs +++ b/src/librustc/middle/trans/datum.rs @@ -15,6 +15,7 @@ use llvm::ValueRef; use middle::trans::base::*; +use middle::trans::build::Load; use middle::trans::common::*; use middle::trans::cleanup; use middle::trans::cleanup::CleanupMethods; @@ -119,10 +120,10 @@ pub fn lvalue_scratch_datum<'a, A>(bcx: &'a Block<'a>, * does not dominate the end of `scope`. */ - let llty = type_of::type_of(bcx.ccx(), ty); let scratch = if zero { - alloca_zeroed(bcx, llty, name) + alloca_zeroed(bcx, ty, name) } else { + let llty = type_of::type_of(bcx.ccx(), ty); alloca(bcx, llty, name) }; @@ -450,6 +451,8 @@ impl Datum<Expr> { name: &str, expr_id: ast::NodeId) -> DatumBlock<'a, Lvalue> { + assert!(ty::lltype_is_sized(bcx.tcx(), self.ty), + "Trying to convert unsized value to lval"); self.match_kind( |l| DatumBlock::new(bcx, l), |r| { @@ -504,12 +507,28 @@ impl Datum<Lvalue> { self.val } - pub fn get_element(&self, - ty: ty::t, - gep: |ValueRef| -> ValueRef) - -> Datum<Lvalue> { + // Extracts a component of a compound data structure (e.g., a field from a + // struct). Note that if self is an opened, unsized type then the returned + // datum may also be unsized _without the size information_. It is the + // callers responsibility to package the result in some way to make a valid + // datum in that case (e.g., by making a fat pointer or opened pair). + pub fn get_element<'a>(&self, + bcx: &'a Block<'a>, + ty: ty::t, + gep: |ValueRef| -> ValueRef) + -> Datum<Lvalue> { + let val = match ty::get(self.ty).sty { + _ if ty::type_is_sized(bcx.tcx(), self.ty) => gep(self.val), + ty::ty_open(_) => { + let base = Load(bcx, expr::get_dataptr(bcx, self.val)); + gep(base) + } + _ => bcx.tcx().sess.bug( + format!("Unexpected unsized type in get_element: {}", + bcx.ty_to_string(self.ty)).as_slice()) + }; Datum { - val: gep(self.val), + val: val, kind: Lvalue, ty: ty, } diff --git a/src/librustc/middle/trans/debuginfo.rs b/src/librustc/middle/trans/debuginfo.rs index 28319a377fa..26973910400 100644 --- a/src/librustc/middle/trans/debuginfo.rs +++ b/src/librustc/middle/trans/debuginfo.rs @@ -56,10 +56,11 @@ This file consists of three conceptual sections: ## Recursive Types Some kinds of types, such as structs and enums can be recursive. That means that -the type definition of some type X refers to some other type which in turn (transitively) -refers to X. This introduces cycles into the type referral graph. A naive algorithm doing -an on-demand, depth-first traversal of this graph when describing types, can get trapped -in an endless loop when it reaches such a cycle. +the type definition of some type X refers to some other type which in turn +(transitively) refers to X. This introduces cycles into the type referral graph. +A naive algorithm doing an on-demand, depth-first traversal of this graph when +describing types, can get trapped in an endless loop when it reaches such a +cycle. For example, the following simple type for a singly-linked list... @@ -402,7 +403,7 @@ impl TypeMap { let inner_type_id = self.get_unique_type_id_as_string(inner_type_id); unique_type_id.push_str(inner_type_id.as_slice()); }, - ty::ty_vec(ty::mt { ty: inner_type, .. }, optional_length) => { + ty::ty_vec(inner_type, optional_length) => { match optional_length { Some(len) => { unique_type_id.push_str(format!("[{}]", len).as_slice()); @@ -497,9 +498,8 @@ impl TypeMap { unique_type_id.push_char(':'); - for bound in bounds.iter() { + for bound in bounds.builtin_bounds.iter() { match bound { - ty::BoundStatic => unique_type_id.push_str("'static"), ty::BoundSend => unique_type_id.push_str("Send"), ty::BoundSized => unique_type_id.push_str("Sized"), ty::BoundCopy => unique_type_id.push_str("Copy"), @@ -595,18 +595,6 @@ impl TypeMap { UniqueTypeId(interner_key) } - fn get_unique_type_id_of_heap_vec_box(&mut self, - cx: &CrateContext, - element_type: ty::t) - -> UniqueTypeId { - let element_type_id = self.get_unique_type_id_of_type(cx, element_type); - let heap_vec_box_type_id = format!("{{HEAP_VEC_BOX<{}>}}", - self.get_unique_type_id_as_string(element_type_id) - .as_slice()); - let interner_key = self.unique_id_interner.intern(Rc::new(heap_vec_box_type_id)); - UniqueTypeId(interner_key) - } - fn get_unique_type_id_of_gc_box(&mut self, cx: &CrateContext, element_type: ty::t) @@ -2718,81 +2706,6 @@ fn fixed_vec_metadata(cx: &CrateContext, return MetadataCreationResult::new(metadata, false); } -fn heap_vec_metadata(cx: &CrateContext, - vec_pointer_type: ty::t, - element_type: ty::t, - unique_type_id: UniqueTypeId, - span: Span) - -> MetadataCreationResult { - let element_type_metadata = type_metadata(cx, element_type, span); - let element_llvm_type = type_of::type_of(cx, element_type); - let (element_size, element_align) = size_and_align_of(cx, element_llvm_type); - - return_if_metadata_created_in_meantime!(cx, unique_type_id); - - let vecbox_llvm_type = Type::vec(cx, &element_llvm_type); - let vec_pointer_type_name = compute_debuginfo_type_name(cx, - vec_pointer_type, - true); - let vec_pointer_type_name = vec_pointer_type_name.as_slice(); - - let member_llvm_types = vecbox_llvm_type.field_types(); - - let int_type_metadata = type_metadata(cx, ty::mk_int(), span); - let array_type_metadata = unsafe { - llvm::LLVMDIBuilderCreateArrayType( - DIB(cx), - bytes_to_bits(element_size), - bytes_to_bits(element_align), - element_type_metadata, - create_DIArray(DIB(cx), [llvm::LLVMDIBuilderGetOrCreateSubrange(DIB(cx), 0, 0)])) - }; - - let member_descriptions = [ - MemberDescription { - name: "fill".to_string(), - llvm_type: *member_llvm_types.get(0), - type_metadata: int_type_metadata, - offset: ComputedMemberOffset, - }, - MemberDescription { - name: "alloc".to_string(), - llvm_type: *member_llvm_types.get(1), - type_metadata: int_type_metadata, - offset: ComputedMemberOffset, - }, - MemberDescription { - name: "elements".to_string(), - llvm_type: *member_llvm_types.get(2), - type_metadata: array_type_metadata, - offset: ComputedMemberOffset, - } - ]; - - assert!(member_descriptions.len() == member_llvm_types.len()); - - let loc = span_start(cx, span); - let file_metadata = file_metadata(cx, loc.file.name.as_slice()); - - let vec_box_unique_id = debug_context(cx).type_map - .borrow_mut() - .get_unique_type_id_of_heap_vec_box(cx, - element_type); - - let vecbox_metadata = composite_type_metadata(cx, - vecbox_llvm_type, - vec_pointer_type_name, - vec_box_unique_id, - member_descriptions, - UNKNOWN_SCOPE_METADATA, - file_metadata, - span); - - MetadataCreationResult::new(pointer_type_metadata(cx, - vec_pointer_type, - vecbox_metadata), false) -} - fn vec_slice_metadata(cx: &CrateContext, vec_type: ty::t, element_type: ty::t, @@ -2885,47 +2798,42 @@ fn subroutine_type_metadata(cx: &CrateContext, false); } +// FIXME(1563) This is all a bit of a hack because 'trait pointer' is an ill- +// defined concept. For the case of an actual trait pointer (i.e., Box<Trait>, +// &Trait), trait_object_type should be the whole thing (e.g, Box<Trait>) and +// trait_type should be the actual trait (e.g., Trait). Where the trait is part +// of a DST struct, there is no trait_object_type and the results of this +// function will be a little bit weird. fn trait_pointer_metadata(cx: &CrateContext, - // trait_pointer_type must be the type of the fat - // pointer to the concrete trait object - trait_pointer_type: ty::t, + trait_type: ty::t, + trait_object_type: Option<ty::t>, unique_type_id: UniqueTypeId) -> DIType { // The implementation provided here is a stub. It makes sure that the trait // type is assigned the correct name, size, namespace, and source location. // But it does not describe the trait's methods. - let trait_object_type = match ty::get(trait_pointer_type).sty { - ty::ty_uniq(pointee_type) => pointee_type, - ty::ty_rptr(_, ty::mt { ty, .. }) => ty, - _ => { - let pp_type_name = ppaux::ty_to_string(cx.tcx(), trait_pointer_type); - cx.sess().bug(format!("debuginfo: Unexpected trait-pointer type in \ - trait_pointer_metadata(): {}", - pp_type_name.as_slice()).as_slice()); - } - }; - - let def_id = match ty::get(trait_object_type).sty { + let def_id = match ty::get(trait_type).sty { ty::ty_trait(box ty::TyTrait { def_id, .. }) => def_id, _ => { - let pp_type_name = ppaux::ty_to_string(cx.tcx(), trait_object_type); + let pp_type_name = ppaux::ty_to_string(cx.tcx(), trait_type); cx.sess().bug(format!("debuginfo: Unexpected trait-object type in \ trait_pointer_metadata(): {}", pp_type_name.as_slice()).as_slice()); } }; - let trait_pointer_type_name = - compute_debuginfo_type_name(cx, trait_pointer_type, false); + let trait_object_type = trait_object_type.unwrap_or(trait_type); + let trait_type_name = + compute_debuginfo_type_name(cx, trait_object_type, false); let (containing_scope, _) = get_namespace_and_span_for_item(cx, def_id); - let trait_pointer_llvm_type = type_of::type_of(cx, trait_pointer_type); + let trait_llvm_type = type_of::type_of(cx, trait_object_type); composite_type_metadata(cx, - trait_pointer_llvm_type, - trait_pointer_type_name.as_slice(), + trait_llvm_type, + trait_type_name.as_slice(), unique_type_id, [], containing_scope, @@ -2989,54 +2897,32 @@ fn type_metadata(cx: &CrateContext, ty::ty_box(pointee_type) => { at_box_metadata(cx, t, pointee_type, unique_type_id) } - ty::ty_vec(ref mt, Some(len)) => { - fixed_vec_metadata(cx, unique_type_id, mt.ty, len, usage_site_span) + ty::ty_vec(typ, Some(len)) => { + fixed_vec_metadata(cx, unique_type_id, typ, len, usage_site_span) } - ty::ty_uniq(pointee_type) => { - match ty::get(pointee_type).sty { - ty::ty_vec(ref mt, None) => { - heap_vec_metadata(cx, pointee_type, mt.ty, unique_type_id, usage_site_span) - } - ty::ty_str => { - let i8_t = ty::mk_i8(); - heap_vec_metadata(cx, pointee_type, i8_t, unique_type_id, usage_site_span) - } - ty::ty_trait(..) => { - MetadataCreationResult::new( - trait_pointer_metadata(cx, t, unique_type_id), - false) - } - _ => { - let pointee_metadata = type_metadata(cx, - pointee_type, - usage_site_span); - match debug_context(cx).type_map - .borrow() - .find_metadata_for_unique_id(unique_type_id) { - Some(metadata) => return metadata, - None => { /* proceed normally */ } - }; - - MetadataCreationResult::new(pointer_type_metadata(cx, t, pointee_metadata), - false) - } - } + // FIXME Can we do better than this for unsized vec/str fields? + ty::ty_vec(typ, None) => fixed_vec_metadata(cx, unique_type_id, typ, 0, usage_site_span), + ty::ty_str => fixed_vec_metadata(cx, unique_type_id, ty::mk_i8(), 0, usage_site_span), + ty::ty_trait(..) => { + MetadataCreationResult::new( + trait_pointer_metadata(cx, t, None, unique_type_id), + false) } - ty::ty_ptr(ref mt) | ty::ty_rptr(_, ref mt) => { - match ty::get(mt.ty).sty { - ty::ty_vec(ref mt, None) => { - vec_slice_metadata(cx, t, mt.ty, unique_type_id, usage_site_span) + ty::ty_uniq(ty) | ty::ty_ptr(ty::mt{ty, ..}) | ty::ty_rptr(_, ty::mt{ty, ..}) => { + match ty::get(ty).sty { + ty::ty_vec(typ, None) => { + vec_slice_metadata(cx, t, typ, unique_type_id, usage_site_span) } ty::ty_str => { vec_slice_metadata(cx, t, ty::mk_u8(), unique_type_id, usage_site_span) } ty::ty_trait(..) => { MetadataCreationResult::new( - trait_pointer_metadata(cx, t, unique_type_id), + trait_pointer_metadata(cx, ty, Some(t), unique_type_id), false) } _ => { - let pointee = type_metadata(cx, mt.ty, usage_site_span); + let pointee_metadata = type_metadata(cx, ty, usage_site_span); match debug_context(cx).type_map .borrow() @@ -3045,7 +2931,8 @@ fn type_metadata(cx: &CrateContext, None => { /* proceed normally */ } }; - MetadataCreationResult::new(pointer_type_metadata(cx, t, pointee), false) + MetadataCreationResult::new(pointer_type_metadata(cx, t, pointee_metadata), + false) } } } @@ -3544,7 +3431,6 @@ fn populate_scope_map(cx: &CrateContext, ast::ExprAgain(_) | ast::ExprPath(_) => {} - ast::ExprVstore(ref sub_exp, _) | ast::ExprCast(ref sub_exp, _) | ast::ExprAddrOf(_, ref sub_exp) | ast::ExprField(ref sub_exp, _, _) | @@ -3729,7 +3615,7 @@ fn populate_scope_map(cx: &CrateContext, walk_expr(cx, &**exp, scope_stack, scope_map); } - for &(_, ref exp) in outputs.iter() { + for &(_, ref exp, _) in outputs.iter() { walk_expr(cx, &**exp, scope_stack, scope_map); } } @@ -3820,7 +3706,7 @@ fn push_debuginfo_type_name(cx: &CrateContext, push_debuginfo_type_name(cx, inner_type, true, output); }, - ty::ty_vec(ty::mt { ty: inner_type, .. }, optional_length) => { + ty::ty_vec(inner_type, optional_length) => { output.push_char('['); push_debuginfo_type_name(cx, inner_type, true, output); @@ -3933,6 +3819,7 @@ fn push_debuginfo_type_name(cx: &CrateContext, } ty::ty_err | ty::ty_infer(_) | + ty::ty_open(_) | ty::ty_param(_) => { cx.sess().bug(format!("debuginfo: Trying to create type name for \ unexpected type: {}", ppaux::ty_to_string(cx.tcx(), t)).as_slice()); diff --git a/src/librustc/middle/trans/expr.rs b/src/librustc/middle/trans/expr.rs index 748274b1201..c49bb7f4e6b 100644 --- a/src/librustc/middle/trans/expr.rs +++ b/src/librustc/middle/trans/expr.rs @@ -40,6 +40,7 @@ use metadata::csearch; use middle::def; use middle::lang_items::MallocFnLangItem; use middle::mem_categorization::Typer; +use middle::subst; use middle::trans::_match; use middle::trans::adt; use middle::trans::asm; @@ -62,15 +63,15 @@ use middle::trans::inline; use middle::trans::tvec; use middle::trans::type_of; use middle::ty::struct_fields; -use middle::ty::{AutoBorrowObj, AutoDerefRef, AutoAddEnv, AutoObject, AutoUnsafe}; -use middle::ty::{AutoPtr, AutoBorrowVec, AutoBorrowVecRef}; +use middle::ty::{AutoDerefRef, AutoAddEnv, AutoUnsafe}; +use middle::ty::{AutoPtr}; use middle::ty; use middle::typeck; use middle::typeck::MethodCall; use util::common::indenter; use util::ppaux::Repr; use util::nodemap::NodeMap; -use middle::trans::machine::{llalign_of_min, llsize_of, llsize_of_alloc}; +use middle::trans::machine::{llsize_of, llsize_of_alloc}; use middle::trans::type_::Type; use syntax::ast; @@ -160,6 +161,14 @@ pub fn trans<'a>(bcx: &'a Block<'a>, return DatumBlock::new(bcx, datum); } +pub fn get_len(bcx: &Block, fat_ptr: ValueRef) -> ValueRef { + GEPi(bcx, fat_ptr, [0u, abi::slice_elt_len]) +} + +pub fn get_dataptr(bcx: &Block, fat_ptr: ValueRef) -> ValueRef { + GEPi(bcx, fat_ptr, [0u, abi::slice_elt_base]) +} + fn apply_adjustments<'a>(bcx: &'a Block<'a>, expr: &ast::Expr, datum: Datum<Expr>) @@ -184,71 +193,289 @@ fn apply_adjustments<'a>(bcx: &'a Block<'a>, datum = unpack_datum!(bcx, add_env(bcx, expr, datum)); } AutoDerefRef(ref adj) => { - if adj.autoderefs > 0 { + let (autoderefs, use_autoref) = match adj.autoref { + // Extracting a value from a box counts as a deref, but if we are + // just converting Box<[T, ..n]> to Box<[T]> we aren't really doing + // a deref (and wouldn't if we could treat Box like a normal struct). + Some(ty::AutoUnsizeUniq(..)) => (adj.autoderefs - 1, true), + // We are a bit paranoid about adjustments and thus might have a re- + // borrow here which merely derefs and then refs again (it might have + // a different region or mutability, but we don't care here. It might + // also be just in case we need to unsize. But if there are no nested + // adjustments then it should be a no-op). + Some(ty::AutoPtr(_, _, None)) if adj.autoderefs == 1 => { + match ty::get(datum.ty).sty { + // Don't skip a conversion from Box<T> to &T, etc. + ty::ty_rptr(..) => { + let method_call = MethodCall::autoderef(expr.id, adj.autoderefs-1); + let method = bcx.tcx().method_map.borrow().find(&method_call).is_some(); + if method { + // Don't skip an overloaded deref. + (adj.autoderefs, true) + } else { + (adj.autoderefs - 1, false) + } + } + _ => (adj.autoderefs, true), + } + } + _ => (adj.autoderefs, true) + }; + + if autoderefs > 0 { + // Schedule cleanup. + let lval = unpack_datum!(bcx, datum.to_lvalue_datum(bcx, "auto_deref", expr.id)); datum = unpack_datum!( - bcx, deref_multiple(bcx, expr, datum, adj.autoderefs)); + bcx, deref_multiple(bcx, expr, lval.to_expr_datum(), autoderefs)); } - datum = match adj.autoref { - None => { - datum - } - Some(AutoUnsafe(..)) | // region + unsafe ptrs have same repr - Some(AutoPtr(..)) => { - unpack_datum!(bcx, auto_ref(bcx, datum, expr)) - } - Some(AutoBorrowVec(..)) => { - unpack_datum!(bcx, auto_slice(bcx, expr, datum)) - } - Some(AutoBorrowVecRef(..)) => { - unpack_datum!(bcx, auto_slice_and_ref(bcx, expr, datum)) + // (You might think there is a more elegant way to do this than a + // use_autoref bool, but then you remember that the borrow checker exists). + match (use_autoref, &adj.autoref) { + (true, &Some(ref a)) => { + datum = unpack_datum!(bcx, apply_autoref(a, + bcx, + expr, + datum)); } - Some(AutoBorrowObj(..)) => { - unpack_datum!(bcx, auto_borrow_obj(bcx, expr, datum)) + _ => {} + } + } + } + debug!("after adjustments, datum={}", datum.to_string(bcx.ccx())); + return DatumBlock::new(bcx, datum); + + fn apply_autoref<'a>(autoref: &ty::AutoRef, + bcx: &'a Block<'a>, + expr: &ast::Expr, + datum: Datum<Expr>) + -> DatumBlock<'a, Expr> { + let mut bcx = bcx; + let mut datum = datum; + + let datum = match autoref { + &AutoUnsafe(..) => { + debug!(" AutoUnsafe"); + unpack_datum!(bcx, ref_ptr(bcx, expr, datum)) + } + &AutoPtr(_, _, ref a) => { + debug!(" AutoPtr"); + match a { + &Some(box ref a) => datum = unpack_datum!(bcx, + apply_autoref(a, bcx, expr, datum)), + _ => {} } - }; + unpack_datum!(bcx, ref_ptr(bcx, expr, datum)) + } + &ty::AutoUnsize(ref k) => { + debug!(" AutoUnsize"); + unpack_datum!(bcx, unsize_expr(bcx, expr, datum, k)) + } + + &ty::AutoUnsizeUniq(ty::UnsizeLength(len)) => { + debug!(" AutoUnsizeUniq(UnsizeLength)"); + unpack_datum!(bcx, unsize_unique_vec(bcx, expr, datum, len)) + } + &ty::AutoUnsizeUniq(ref k) => { + debug!(" AutoUnsizeUniq"); + unpack_datum!(bcx, unsize_unique_expr(bcx, expr, datum, k)) + } + }; + + DatumBlock::new(bcx, datum) + } + + fn ref_ptr<'a>(bcx: &'a Block<'a>, + expr: &ast::Expr, + datum: Datum<Expr>) + -> DatumBlock<'a, Expr> { + if !ty::type_is_sized(bcx.tcx(), datum.ty) { + debug!("Taking address of unsized type {}", + bcx.ty_to_string(datum.ty)); + ref_fat_ptr(bcx, expr, datum) + } else { + debug!("Taking address of sized type {}", + bcx.ty_to_string(datum.ty)); + auto_ref(bcx, datum, expr) } - AutoObject(..) => { - let adjusted_ty = ty::expr_ty_adjusted(bcx.tcx(), expr); - let scratch = rvalue_scratch_datum(bcx, adjusted_ty, "__adjust"); - bcx = meth::trans_trait_cast( - bcx, datum, expr.id, SaveIn(scratch.val)); - datum = scratch.to_expr_datum(); + } + + // Retrieve the information we are losing (making dynamic) in an unsizing + // adjustment. + // When making a dtor, we need to do different things depending on the + // ownership of the object.. mk_ty is a function for turning unsized_type + // into a type to be destructed. If we want to end up with a Box pointer, + // then mk_ty should make a Box pointer (T -> Box<T>), if we want a + // borrowed reference then it should be T -> &T. + fn unsized_info<'a>(bcx: &'a Block<'a>, + kind: &ty::UnsizeKind, + id: ast::NodeId, + unsized_ty: ty::t, + mk_ty: |ty::t| -> ty::t) -> ValueRef { + match kind { + &ty::UnsizeLength(len) => C_uint(bcx.ccx(), len), + &ty::UnsizeStruct(box ref k, tp_index) => match ty::get(unsized_ty).sty { + ty::ty_struct(_, ref substs) => { + let ty_substs = substs.types.get_slice(subst::TypeSpace); + // The dtor for a field treats it like a value, so mk_ty + // should just be the identity function. + unsized_info(bcx, k, id, ty_substs[tp_index], |t| t) + } + _ => bcx.sess().bug(format!("UnsizeStruct with bad sty: {}", + bcx.ty_to_string(unsized_ty)).as_slice()) + }, + &ty::UnsizeVtable(..) => + PointerCast(bcx, + meth::vtable_ptr(bcx, id, mk_ty(unsized_ty)), + Type::vtable_ptr(bcx.ccx())) } } - debug!("after adjustments, datum={}", datum.to_string(bcx.ccx())); - return DatumBlock {bcx: bcx, datum: datum}; - fn auto_slice<'a>( - bcx: &'a Block<'a>, - expr: &ast::Expr, - datum: Datum<Expr>) - -> DatumBlock<'a, Expr> { - // This is not the most efficient thing possible; since slices - // are two words it'd be better if this were compiled in - // 'dest' mode, but I can't find a nice way to structure the - // code and keep it DRY that accommodates that use case at the - // moment. + fn unsize_expr<'a>(bcx: &'a Block<'a>, + expr: &ast::Expr, + datum: Datum<Expr>, + k: &ty::UnsizeKind) + -> DatumBlock<'a, Expr> { + let tcx = bcx.tcx(); + let datum_ty = datum.ty; + let unsized_ty = ty::unsize_ty(tcx, datum_ty, k, expr.span); + let dest_ty = ty::mk_open(tcx, unsized_ty); + // Closures for extracting and manipulating the data and payload parts of + // the fat pointer. + let base = match k { + &ty::UnsizeStruct(..) => + |bcx, val| PointerCast(bcx, + val, + type_of::type_of(bcx.ccx(), unsized_ty).ptr_to()), + &ty::UnsizeLength(..) => + |bcx, val| GEPi(bcx, val, [0u, 0u]), + &ty::UnsizeVtable(..) => + |_bcx, val| PointerCast(bcx, val, Type::i8p(bcx.ccx())) + }; + let info = |bcx, _val| unsized_info(bcx, + k, + expr.id, + ty::deref_or_dont(datum_ty), + |t| ty::mk_rptr(tcx, + ty::ReStatic, + ty::mt{ + ty: t, + mutbl: ast::MutImmutable + })); + into_fat_ptr(bcx, expr, datum, dest_ty, base, info) + } + fn ref_fat_ptr<'a>(bcx: &'a Block<'a>, + expr: &ast::Expr, + datum: Datum<Expr>) + -> DatumBlock<'a, Expr> { + let tcx = bcx.tcx(); + let dest_ty = ty::close_type(tcx, datum.ty); + let base = |bcx, val| Load(bcx, get_dataptr(bcx, val)); + let len = |bcx, val| Load(bcx, get_len(bcx, val)); + into_fat_ptr(bcx, expr, datum, dest_ty, base, len) + } + + fn into_fat_ptr<'a>(bcx: &'a Block<'a>, + expr: &ast::Expr, + datum: Datum<Expr>, + dest_ty: ty::t, + base: |&'a Block<'a>, ValueRef| -> ValueRef, + info: |&'a Block<'a>, ValueRef| -> ValueRef) + -> DatumBlock<'a, Expr> { + let mut bcx = bcx; + + // Arrange cleanup + let lval = unpack_datum!(bcx, + datum.to_lvalue_datum(bcx, "into_fat_ptr", expr.id)); + let base = base(bcx, lval.val); + let info = info(bcx, lval.val); + + let scratch = rvalue_scratch_datum(bcx, dest_ty, "__fat_ptr"); + Store(bcx, base, get_dataptr(bcx, scratch.val)); + Store(bcx, info, get_len(bcx, scratch.val)); + + DatumBlock::new(bcx, scratch.to_expr_datum()) + } + + fn unsize_unique_vec<'a>(bcx: &'a Block<'a>, + expr: &ast::Expr, + datum: Datum<Expr>, + len: uint) + -> DatumBlock<'a, Expr> { + let mut bcx = bcx; + let tcx = bcx.tcx(); + + let datum_ty = datum.ty; + // Arrange cleanup + let lval = unpack_datum!(bcx, + datum.to_lvalue_datum(bcx, "unsize_unique_vec", expr.id)); + + let ll_len = C_uint(bcx.ccx(), len); + let unit_ty = ty::sequence_element_type(tcx, ty::type_content(datum_ty)); + let vec_ty = ty::mk_uniq(tcx, ty::mk_vec(tcx, unit_ty, None)); + let scratch = rvalue_scratch_datum(bcx, vec_ty, "__unsize_unique"); + + if len == 0 { + Store(bcx, + C_null(type_of::type_of(bcx.ccx(), unit_ty).ptr_to()), + get_dataptr(bcx, scratch.val)); + } else { + // Box<[(), ..n]> will not allocate, but ~[()] expects an + // allocation of n bytes, so we must allocate here (yuck). + let llty = type_of::type_of(bcx.ccx(), unit_ty); + if llsize_of_alloc(bcx.ccx(), llty) == 0 { + let ptr_unit_ty = type_of::type_of(bcx.ccx(), unit_ty).ptr_to(); + let align = C_uint(bcx.ccx(), 8); + let alloc_result = malloc_raw_dyn(bcx, ptr_unit_ty, vec_ty, ll_len, align); + bcx = alloc_result.bcx; + let base = get_dataptr(bcx, scratch.val); + Store(bcx, alloc_result.val, base); + } else { + let base = get_dataptr(bcx, scratch.val); + let base = PointerCast(bcx, + base, + type_of::type_of(bcx.ccx(), datum_ty).ptr_to()); + bcx = lval.store_to(bcx, base); + } + } + + Store(bcx, ll_len, get_len(bcx, scratch.val)); + DatumBlock::new(bcx, scratch.to_expr_datum()) + } + + fn unsize_unique_expr<'a>(bcx: &'a Block<'a>, + expr: &ast::Expr, + datum: Datum<Expr>, + k: &ty::UnsizeKind) + -> DatumBlock<'a, Expr> { let mut bcx = bcx; let tcx = bcx.tcx(); - let unit_ty = ty::sequence_element_type(tcx, datum.ty); - // Arrange cleanup, if not already done. This is needed in - // case we are auto-slicing an owned vector or some such. - let datum = unpack_datum!( - bcx, datum.to_lvalue_datum(bcx, "auto_slice", expr.id)); + let datum_ty = datum.ty; + let unboxed_ty = match ty::get(datum_ty).sty { + ty::ty_uniq(t) => t, + _ => bcx.sess().bug(format!("Expected ty_uniq, found {}", + bcx.ty_to_string(datum_ty)).as_slice()) + }; + let result_ty = ty::mk_uniq(tcx, ty::unsize_ty(tcx, unboxed_ty, k, expr.span)); + + let lval = unpack_datum!(bcx, + datum.to_lvalue_datum(bcx, "unsize_unique_expr", expr.id)); + + let scratch = rvalue_scratch_datum(bcx, result_ty, "__uniq_fat_ptr"); + let llbox_ty = type_of::type_of(bcx.ccx(), datum_ty); + let base = PointerCast(bcx, get_dataptr(bcx, scratch.val), llbox_ty.ptr_to()); + bcx = lval.store_to(bcx, base); - let (base, len) = datum.get_vec_base_and_len(bcx); + let info = unsized_info(bcx, k, expr.id, unboxed_ty, |t| ty::mk_uniq(tcx, t)); + Store(bcx, info, get_len(bcx, scratch.val)); - // this type may have a different region/mutability than the - // real one, but it will have the same runtime representation - let slice_ty = ty::mk_slice(tcx, ty::ReStatic, - ty::mt { ty: unit_ty, mutbl: ast::MutImmutable }); + let scratch = unpack_datum!(bcx, + scratch.to_expr_datum().to_lvalue_datum(bcx, + "fresh_uniq_fat_ptr", + expr.id)); - let scratch = rvalue_scratch_datum(bcx, slice_ty, "__adjust"); - Store(bcx, base, GEPi(bcx, scratch.val, [0u, abi::slice_elt_base])); - Store(bcx, len, GEPi(bcx, scratch.val, [0u, abi::slice_elt_len])); DatumBlock::new(bcx, scratch.to_expr_datum()) } @@ -267,32 +494,6 @@ fn apply_adjustments<'a>(bcx: &'a Block<'a>, let def = ty::resolve_expr(bcx.tcx(), expr); closure::make_closure_from_bare_fn(bcx, closure_ty, def, fn_ptr) } - - fn auto_slice_and_ref<'a>( - bcx: &'a Block<'a>, - expr: &ast::Expr, - datum: Datum<Expr>) - -> DatumBlock<'a, Expr> { - let DatumBlock { bcx, datum } = auto_slice(bcx, expr, datum); - auto_ref(bcx, datum, expr) - } - - fn auto_borrow_obj<'a>(mut bcx: &'a Block<'a>, - expr: &ast::Expr, - source_datum: Datum<Expr>) - -> DatumBlock<'a, Expr> { - let tcx = bcx.tcx(); - let target_obj_ty = expr_ty_adjusted(bcx, expr); - debug!("auto_borrow_obj(target={})", target_obj_ty.repr(tcx)); - - // Arrange cleanup, if not already done. This is needed in - // case we are auto-borrowing a Box<Trait> to &Trait - let datum = unpack_datum!( - bcx, source_datum.to_lvalue_datum(bcx, "autoborrowobj", expr.id)); - let mut datum = datum.to_expr_datum(); - datum.ty = target_obj_ty; - DatumBlock::new(bcx, datum) - } } pub fn trans_to_lvalue<'a>(bcx: &'a Block<'a>, @@ -398,20 +599,31 @@ fn trans_datum_unadjusted<'a>(bcx: &'a Block<'a>, ast::ExprIndex(ref base, ref idx) => { trans_index(bcx, expr, &**base, &**idx, MethodCall::expr(expr.id)) } - ast::ExprVstore(ref contents, ast::ExprVstoreUniq) => { - fcx.push_ast_cleanup_scope(contents.id); - let datum = unpack_datum!( - bcx, tvec::trans_uniq_vstore(bcx, expr, &**contents)); - bcx = fcx.pop_and_trans_ast_cleanup_scope(bcx, contents.id); - DatumBlock::new(bcx, datum) - } ast::ExprBox(_, ref contents) => { // Special case for `Box<T>` and `Gc<T>` let box_ty = expr_ty(bcx, expr); let contents_ty = expr_ty(bcx, &**contents); match ty::get(box_ty).sty { ty::ty_uniq(..) => { - trans_uniq_expr(bcx, box_ty, &**contents, contents_ty) + let is_vec = match contents.node { + ast::ExprRepeat(..) | ast::ExprVec(..) => true, + ast::ExprLit(lit) => match lit.node { + ast::LitStr(..) => true, + _ => false + }, + _ => false + }; + + if is_vec { + // Special case for owned vectors. + fcx.push_ast_cleanup_scope(contents.id); + let datum = unpack_datum!( + bcx, tvec::trans_uniq_vec(bcx, expr, &**contents)); + bcx = fcx.pop_and_trans_ast_cleanup_scope(bcx, contents.id); + DatumBlock::new(bcx, datum) + } else { + trans_uniq_expr(bcx, box_ty, &**contents, contents_ty) + } } ty::ty_box(..) => { trans_managed_expr(bcx, box_ty, &**contents, contents_ty) @@ -419,6 +631,7 @@ fn trans_datum_unadjusted<'a>(bcx: &'a Block<'a>, _ => bcx.sess().span_bug(expr.span, "expected unique or managed box") } + } ast::ExprLit(ref lit) => trans_immediate_lit(bcx, expr, (**lit).clone()), ast::ExprBinary(op, ref lhs, ref rhs) => { @@ -428,7 +641,19 @@ fn trans_datum_unadjusted<'a>(bcx: &'a Block<'a>, trans_unary(bcx, expr, op, &**x) } ast::ExprAddrOf(_, ref x) => { - trans_addr_of(bcx, expr, &**x) + match x.node { + ast::ExprRepeat(..) | ast::ExprVec(..) => { + // Special case for slices. + fcx.push_ast_cleanup_scope(x.id); + let datum = unpack_datum!( + bcx, tvec::trans_slice_vec(bcx, expr, &**x)); + bcx = fcx.pop_and_trans_ast_cleanup_scope(bcx, x.id); + DatumBlock::new(bcx, datum) + } + _ => { + trans_addr_of(bcx, expr, &**x) + } + } } ast::ExprCast(ref val, _) => { // Datum output mode means this is a scalar cast: @@ -454,14 +679,27 @@ fn trans_rec_field<'a>(bcx: &'a Block<'a>, let _icx = push_ctxt("trans_rec_field"); let base_datum = unpack_datum!(bcx, trans_to_lvalue(bcx, base, "field")); - let repr = adt::represent_type(bcx.ccx(), base_datum.ty); - with_field_tys(bcx.tcx(), base_datum.ty, None, |discr, field_tys| { - let ix = ty::field_idx_strict(bcx.tcx(), field.name, field_tys); - let d = base_datum.get_element( - field_tys[ix].mt.ty, - |srcval| adt::trans_field_ptr(bcx, &*repr, srcval, discr, ix)); + let bare_ty = ty::unopen_type(base_datum.ty); + let repr = adt::represent_type(bcx.ccx(), bare_ty); + with_field_tys(bcx.tcx(), bare_ty, None, |discr, field_tys| { + let ix = ty::field_idx_strict(bcx.tcx(), field.name, field_tys); + let d = base_datum.get_element( + bcx, + field_tys[ix].mt.ty, + |srcval| adt::trans_field_ptr(bcx, &*repr, srcval, discr, ix)); + + if ty::type_is_sized(bcx.tcx(), d.ty) { DatumBlock { datum: d.to_expr_datum(), bcx: bcx } - }) + } else { + let scratch = rvalue_scratch_datum(bcx, ty::mk_open(bcx.tcx(), d.ty), ""); + Store(bcx, d.val, get_dataptr(bcx, scratch.val)); + let info = Load(bcx, get_len(bcx, base_datum.val)); + Store(bcx, info, get_len(bcx, scratch.val)); + + DatumBlock::new(bcx, scratch.to_expr_datum()) + + } + }) } fn trans_index<'a>(bcx: &'a Block<'a>, @@ -727,7 +965,6 @@ fn trans_rvalue_dps_unadjusted<'a>(bcx: &'a Block<'a>, let _icx = push_ctxt("trans_rvalue_dps_unadjusted"); let mut bcx = bcx; let tcx = bcx.tcx(); - let fcx = bcx.fcx; match expr.node { ast::ExprParen(ref e) => { @@ -772,14 +1009,8 @@ fn trans_rvalue_dps_unadjusted<'a>(bcx: &'a Block<'a>, } } } - ast::ExprVstore(ref contents, ast::ExprVstoreSlice) | - ast::ExprVstore(ref contents, ast::ExprVstoreMutSlice) => { - fcx.push_ast_cleanup_scope(contents.id); - bcx = tvec::trans_slice_vstore(bcx, expr, &**contents, dest); - fcx.pop_and_trans_ast_cleanup_scope(bcx, contents.id) - } ast::ExprVec(..) | ast::ExprRepeat(..) => { - tvec::trans_fixed_vstore(bcx, expr, expr, dest) + tvec::trans_fixed_vstore(bcx, expr, dest) } ast::ExprFnBlock(_, ref decl, ref body) | ast::ExprProc(ref decl, ref body) => { @@ -1146,7 +1377,8 @@ pub fn trans_adt<'a>(mut bcx: &'a Block<'a>, let base_datum = unpack_datum!(bcx, trans_to_lvalue(bcx, &*base.expr, "base")); for &(i, t) in base.fields.iter() { let datum = base_datum.get_element( - t, |srcval| adt::trans_field_ptr(bcx, &*repr, srcval, discr, i)); + bcx, t, |srcval| adt::trans_field_ptr(bcx, &*repr, srcval, discr, i)); + assert!(ty::type_is_sized(bcx.tcx(), datum.ty)); let dest = adt::trans_field_ptr(bcx, &*repr, addr, discr, i); bcx = datum.store_to(bcx, dest); } @@ -1253,13 +1485,12 @@ fn trans_uniq_expr<'a>(bcx: &'a Block<'a>, -> DatumBlock<'a, Expr> { let _icx = push_ctxt("trans_uniq_expr"); let fcx = bcx.fcx; + assert!(ty::type_is_sized(bcx.tcx(), contents_ty)); let llty = type_of::type_of(bcx.ccx(), contents_ty); let size = llsize_of(bcx.ccx(), llty); - let align = C_uint(bcx.ccx(), llalign_of_min(bcx.ccx(), llty) as uint); - // We need to a make a pointer type because box_ty is ty_bot - // if content_ty is, e.g. box fail!(). - let real_box_ty = ty::mk_uniq(bcx.tcx(), contents_ty); - let Result { bcx, val } = malloc_raw_dyn(bcx, real_box_ty, size, align); + let align = C_uint(bcx.ccx(), type_of::align_of(bcx.ccx(), contents_ty) as uint); + let llty_ptr = llty.ptr_to(); + let Result { bcx, val } = malloc_raw_dyn(bcx, llty_ptr, box_ty, size, align); // Unique boxes do not allocate for zero-size types. The standard library // may assume that `free` is never called on the pointer returned for // `Box<ZeroSizeType>`. @@ -1303,8 +1534,28 @@ fn trans_addr_of<'a>(bcx: &'a Block<'a>, let _icx = push_ctxt("trans_addr_of"); let mut bcx = bcx; let sub_datum = unpack_datum!(bcx, trans_to_lvalue(bcx, subexpr, "addr_of")); - let ty = expr_ty(bcx, expr); - return immediate_rvalue_bcx(bcx, sub_datum.val, ty).to_expr_datumblock(); + match ty::get(sub_datum.ty).sty { + ty::ty_open(_) => { + // Opened DST value, close to a fat pointer + debug!("Closing fat pointer {}", bcx.ty_to_string(sub_datum.ty)); + + let scratch = rvalue_scratch_datum(bcx, + ty::close_type(bcx.tcx(), sub_datum.ty), + "fat_addr_of"); + let base = Load(bcx, get_dataptr(bcx, sub_datum.val)); + Store(bcx, base, get_dataptr(bcx, scratch.val)); + + let len = Load(bcx, get_len(bcx, sub_datum.val)); + Store(bcx, len, get_len(bcx, scratch.val)); + + DatumBlock::new(bcx, scratch.to_expr_datum()) + } + _ => { + // Sized value, ref to a thin pointer + let ty = expr_ty(bcx, expr); + immediate_rvalue_bcx(bcx, sub_datum.val, ty).to_expr_datumblock() + } + } } // Important to get types for both lhs and rhs, because one might be _|_ @@ -1592,15 +1843,18 @@ pub enum cast_kind { cast_other, } -pub fn cast_type_kind(t: ty::t) -> cast_kind { +pub fn cast_type_kind(tcx: &ty::ctxt, t: ty::t) -> cast_kind { match ty::get(t).sty { ty::ty_char => cast_integral, ty::ty_float(..) => cast_float, ty::ty_ptr(..) => cast_pointer, - ty::ty_rptr(_, mt) => match ty::get(mt.ty).sty{ - ty::ty_vec(_, None) | ty::ty_str | ty::ty_trait(..) => cast_other, - _ => cast_pointer, - }, + ty::ty_rptr(_, mt) => { + if ty::type_is_sized(tcx, mt.ty) { + cast_pointer + } else { + cast_other + } + } ty::ty_bare_fn(..) => cast_pointer, ty::ty_int(..) => cast_integral, ty::ty_uint(..) => cast_integral, @@ -1620,8 +1874,8 @@ fn trans_imm_cast<'a>(bcx: &'a Block<'a>, let t_in = expr_ty(bcx, expr); let t_out = node_id_type(bcx, id); - let k_in = cast_type_kind(t_in); - let k_out = cast_type_kind(t_out); + let k_in = cast_type_kind(bcx.tcx(), t_in); + let k_out = cast_type_kind(bcx.tcx(), t_out); let s_in = k_in == cast_integral && ty::type_is_signed(t_in); let ll_t_in = type_of::arg_type_of(ccx, t_in); let ll_t_out = type_of::arg_type_of(ccx, t_out); @@ -1809,10 +2063,14 @@ fn deref_once<'a>(bcx: &'a Block<'a>, let r = match ty::get(datum.ty).sty { ty::ty_uniq(content_ty) => { - match ty::get(content_ty).sty { - ty::ty_vec(_, None) | ty::ty_str | ty::ty_trait(..) - => bcx.tcx().sess.span_bug(expr.span, "unexpected unsized box"), - _ => deref_owned_pointer(bcx, expr, datum, content_ty), + if ty::type_is_sized(bcx.tcx(), content_ty) { + deref_owned_pointer(bcx, expr, datum, content_ty) + } else { + // A fat pointer and an opened DST value have the same represenation + // just different types. + DatumBlock::new(bcx, Datum::new(datum.val, + ty::mk_open(bcx.tcx(), content_ty), + datum.kind)) } } @@ -1827,21 +2085,21 @@ fn deref_once<'a>(bcx: &'a Block<'a>, ty::ty_ptr(ty::mt { ty: content_ty, .. }) | ty::ty_rptr(_, ty::mt { ty: content_ty, .. }) => { - match ty::get(content_ty).sty { - ty::ty_vec(_, None) | ty::ty_str | ty::ty_trait(..) - => bcx.tcx().sess.span_bug(expr.span, "unexpected unsized reference"), - _ => { - assert!(!ty::type_needs_drop(bcx.tcx(), datum.ty)); - - let ptr = datum.to_llscalarish(bcx); - - // Always generate an lvalue datum, even if datum.mode is - // an rvalue. This is because datum.mode is only an - // rvalue for non-owning pointers like &T or *T, in which - // case cleanup *is* scheduled elsewhere, by the true - // owner (or, in the case of *T, by the user). - DatumBlock::new(bcx, Datum::new(ptr, content_ty, LvalueExpr)) - } + if ty::type_is_sized(bcx.tcx(), content_ty) { + let ptr = datum.to_llscalarish(bcx); + + // Always generate an lvalue datum, even if datum.mode is + // an rvalue. This is because datum.mode is only an + // rvalue for non-owning pointers like &T or *T, in which + // case cleanup *is* scheduled elsewhere, by the true + // owner (or, in the case of *T, by the user). + DatumBlock::new(bcx, Datum::new(ptr, content_ty, LvalueExpr)) + } else { + // A fat pointer and an opened DST value have the same represenation + // just different types. + DatumBlock::new(bcx, Datum::new(datum.val, + ty::mk_open(bcx.tcx(), content_ty), + datum.kind)) } } diff --git a/src/librustc/middle/trans/glue.rs b/src/librustc/middle/trans/glue.rs index 570f4d37042..24c939dc3be 100644 --- a/src/librustc/middle/trans/glue.rs +++ b/src/librustc/middle/trans/glue.rs @@ -19,6 +19,7 @@ use llvm::{ValueRef, True, get_param}; use llvm; use middle::lang_items::{FreeFnLangItem, ExchangeFreeFnLangItem}; use middle::subst; +use middle::subst::Subst; use middle::trans::adt; use middle::trans::base::*; use middle::trans::build::*; @@ -26,14 +27,15 @@ use middle::trans::callee; use middle::trans::cleanup; use middle::trans::cleanup::CleanupMethods; use middle::trans::common::*; +use middle::trans::datum; use middle::trans::expr; use middle::trans::machine::*; use middle::trans::reflect; use middle::trans::tvec; use middle::trans::type_::Type; -use middle::trans::type_of::{type_of, sizing_type_of}; +use middle::trans::type_of::{type_of, sizing_type_of, align_of}; use middle::ty; -use util::ppaux::ty_to_short_str; +use util::ppaux::{ty_to_short_str, Repr}; use util::ppaux; use arena::TypedArena; @@ -51,24 +53,33 @@ pub fn trans_free<'a>(cx: &'a Block<'a>, v: ValueRef) -> &'a Block<'a> { Some(expr::Ignore)).bcx } -fn trans_exchange_free<'a>(cx: &'a Block<'a>, v: ValueRef, size: u64, - align: u64) -> &'a Block<'a> { +fn trans_exchange_free_internal<'a>(cx: &'a Block<'a>, v: ValueRef, size: ValueRef, + align: ValueRef) -> &'a Block<'a> { let _icx = push_ctxt("trans_exchange_free"); let ccx = cx.ccx(); callee::trans_lang_call(cx, langcall(cx, None, "", ExchangeFreeFnLangItem), - [PointerCast(cx, v, Type::i8p(ccx)), C_uint(ccx, size as uint), C_uint(ccx, align as uint)], + [PointerCast(cx, v, Type::i8p(ccx)), size, align], Some(expr::Ignore)).bcx } +pub fn trans_exchange_free<'a>(cx: &'a Block<'a>, v: ValueRef, size: u64, + align: u64) -> &'a Block<'a> { + trans_exchange_free_internal(cx, + v, + C_uint(cx.ccx(), size as uint), + C_uint(cx.ccx(), align as uint)) +} + pub fn trans_exchange_free_ty<'a>(bcx: &'a Block<'a>, ptr: ValueRef, content_ty: ty::t) -> &'a Block<'a> { + assert!(ty::type_is_sized(bcx.ccx().tcx(), content_ty)); let sizing_type = sizing_type_of(bcx.ccx(), content_ty); let content_size = llsize_of_alloc(bcx.ccx(), sizing_type); // `Box<ZeroSizeType>` does not allocate. if content_size != 0 { - let content_align = llalign_of_min(bcx.ccx(), sizing_type); + let content_align = align_of(bcx.ccx(), content_ty); trans_exchange_free(bcx, ptr, content_size, content_align) } else { bcx @@ -91,6 +102,11 @@ pub fn take_ty<'a>(bcx: &'a Block<'a>, v: ValueRef, t: ty::t) pub fn get_drop_glue_type(ccx: &CrateContext, t: ty::t) -> ty::t { let tcx = ccx.tcx(); + // Even if there is no dtor for t, there might be one deeper down and we + // might need to pass in the vtable ptr. + if !ty::type_is_sized(tcx, t) { + return t + } if !ty::type_needs_drop(tcx, t) { return ty::mk_i8(); } @@ -98,18 +114,14 @@ pub fn get_drop_glue_type(ccx: &CrateContext, t: ty::t) -> ty::t { ty::ty_box(typ) if !ty::type_needs_drop(tcx, typ) => ty::mk_box(tcx, ty::mk_i8()), - ty::ty_uniq(typ) if !ty::type_needs_drop(tcx, typ) => { - match ty::get(typ).sty { - ty::ty_vec(_, None) | ty::ty_str | ty::ty_trait(..) => t, - _ => { - let llty = sizing_type_of(ccx, typ); - // `Box<ZeroSizeType>` does not allocate. - if llsize_of_alloc(ccx, llty) == 0 { - ty::mk_i8() - } else { - ty::mk_uniq(tcx, ty::mk_i8()) - } - } + ty::ty_uniq(typ) if !ty::type_needs_drop(tcx, typ) + && ty::type_is_sized(tcx, typ) => { + let llty = sizing_type_of(ccx, typ); + // `Box<ZeroSizeType>` does not allocate. + if llsize_of_alloc(ccx, llty) == 0 { + ty::mk_i8() + } else { + ty::mk_uniq(tcx, ty::mk_i8()) } } _ => t @@ -119,9 +131,10 @@ pub fn get_drop_glue_type(ccx: &CrateContext, t: ty::t) -> ty::t { pub fn drop_ty<'a>(bcx: &'a Block<'a>, v: ValueRef, t: ty::t) -> &'a Block<'a> { // NB: v is an *alias* of type t here, not a direct value. + debug!("drop_ty(t={})", t.repr(bcx.tcx())); let _icx = push_ctxt("drop_ty"); - let ccx = bcx.ccx(); if ty::type_needs_drop(bcx.tcx(), t) { + let ccx = bcx.ccx(); let glue = get_drop_glue(ccx, t); let glue_type = get_drop_glue_type(ccx, t); let ptr = if glue_type != t { @@ -143,13 +156,21 @@ pub fn drop_ty_immediate<'a>(bcx: &'a Block<'a>, v: ValueRef, t: ty::t) } pub fn get_drop_glue(ccx: &CrateContext, t: ty::t) -> ValueRef { + debug!("make drop glue for {}", ppaux::ty_to_string(ccx.tcx(), t)); let t = get_drop_glue_type(ccx, t); + debug!("drop glue type {}", ppaux::ty_to_string(ccx.tcx(), t)); match ccx.drop_glues.borrow().find(&t) { Some(&glue) => return glue, _ => { } } - let llfnty = Type::glue_fn(ccx, type_of(ccx, t).ptr_to()); + let llty = if ty::type_is_sized(ccx.tcx(), t) { + type_of(ccx, t).ptr_to() + } else { + type_of(ccx, ty::mk_uniq(ccx.tcx(), t)).ptr_to() + }; + + let llfnty = Type::glue_fn(ccx, llty); let glue = declare_generic_glue(ccx, t, llfnty, "drop"); ccx.drop_glues.borrow_mut().insert(t, glue); @@ -193,6 +214,7 @@ fn make_visit_glue<'a>(bcx: &'a Block<'a>, v: ValueRef, t: ty::t) let _icx = push_ctxt("make_visit_glue"); let mut bcx = bcx; let (visitor_trait, object_ty) = match ty::visitor_object_ty(bcx.tcx(), + ty::ReStatic, ty::ReStatic) { Ok(pair) => pair, Err(s) => { @@ -212,7 +234,13 @@ fn trans_struct_drop_flag<'a>(mut bcx: &'a Block<'a>, substs: &subst::Substs) -> &'a Block<'a> { let repr = adt::represent_type(bcx.ccx(), t); - let drop_flag = unpack_datum!(bcx, adt::trans_drop_flag_ptr(bcx, &*repr, v0)); + let struct_data = if ty::type_is_sized(bcx.tcx(), t) { + v0 + } else { + let llval = GEPi(bcx, v0, [0, abi::slice_elt_base]); + Load(bcx, llval) + }; + let drop_flag = unpack_datum!(bcx, adt::trans_drop_flag_ptr(bcx, &*repr, struct_data)); with_cond(bcx, load_ty(bcx, drop_flag.val, ty::mk_bool()), |cx| { trans_struct_drop(cx, t, v0, dtor_did, class_did, substs) }) @@ -231,13 +259,31 @@ fn trans_struct_drop<'a>(bcx: &'a Block<'a>, let dtor_addr = get_res_dtor(bcx.ccx(), dtor_did, t, class_did, substs); - // The second argument is the "self" argument for drop + // The first argument is the "self" argument for drop let params = unsafe { let ty = Type::from_ref(llvm::LLVMTypeOf(dtor_addr)); ty.element_type().func_params() }; - adt::fold_variants(bcx, &*repr, v0, |variant_cx, st, value| { + let fty = ty::lookup_item_type(bcx.tcx(), dtor_did).ty.subst(bcx.tcx(), substs); + let self_ty = match ty::get(fty).sty { + ty::ty_bare_fn(ref f) => { + assert!(f.sig.inputs.len() == 1); + f.sig.inputs[0] + } + _ => bcx.sess().bug(format!("Expected function type, found {}", + bcx.ty_to_string(fty)).as_slice()) + }; + + let (struct_data, info) = if ty::type_is_sized(bcx.tcx(), t) { + (v0, None) + } else { + let data = GEPi(bcx, v0, [0, abi::slice_elt_base]); + let info = GEPi(bcx, v0, [0, abi::slice_elt_len]); + (Load(bcx, data), Some(Load(bcx, info))) + }; + + adt::fold_variants(bcx, &*repr, struct_data, |variant_cx, st, value| { // Be sure to put all of the fields into a scope so we can use an invoke // instruction to call the user destructor but still call the field // destructors if the user destructor fails. @@ -246,7 +292,22 @@ fn trans_struct_drop<'a>(bcx: &'a Block<'a>, // Class dtors have no explicit args, so the params should // just consist of the environment (self). assert_eq!(params.len(), 1); - let self_arg = PointerCast(variant_cx, value, *params.get(0)); + let self_arg = if ty::type_is_fat_ptr(bcx.tcx(), self_ty) { + // The dtor expects a fat pointer, so make one, even if we have to fake it. + let boxed_ty = ty::mk_open(bcx.tcx(), t); + let scratch = datum::rvalue_scratch_datum(bcx, boxed_ty, "__fat_ptr_drop_self"); + Store(bcx, value, GEPi(bcx, scratch.val, [0, abi::slice_elt_base])); + Store(bcx, + // If we just had a thin pointer, make a fat pointer by sticking + // null where we put the unsizing info. This works because t + // is a sized type, so we will only unpack the fat pointer, never + // use the fake info. + info.unwrap_or(C_null(Type::i8p(bcx.ccx()))), + GEPi(bcx, scratch.val, [0, abi::slice_elt_len])); + PointerCast(variant_cx, scratch.val, *params.get(0)) + } else { + PointerCast(variant_cx, value, *params.get(0)) + }; let args = vec!(self_arg); // Add all the fields as a value which needs to be cleaned at the end of @@ -254,19 +315,84 @@ fn trans_struct_drop<'a>(bcx: &'a Block<'a>, // the order in which fields get dropped. for (i, ty) in st.fields.iter().enumerate().rev() { let llfld_a = adt::struct_field_ptr(variant_cx, &*st, value, i, false); + + let val = if ty::type_is_sized(bcx.tcx(), *ty) { + llfld_a + } else { + let boxed_ty = ty::mk_open(bcx.tcx(), *ty); + let scratch = datum::rvalue_scratch_datum(bcx, boxed_ty, "__fat_ptr_drop_field"); + Store(bcx, llfld_a, GEPi(bcx, scratch.val, [0, abi::slice_elt_base])); + Store(bcx, info.unwrap(), GEPi(bcx, scratch.val, [0, abi::slice_elt_len])); + scratch.val + }; variant_cx.fcx.schedule_drop_mem(cleanup::CustomScope(field_scope), - llfld_a, *ty); + val, *ty); } let dtor_ty = ty::mk_ctor_fn(variant_cx.tcx(), ast::DUMMY_NODE_ID, [get_drop_glue_type(bcx.ccx(), t)], ty::mk_nil()); - let (_, variant_cx) = invoke(variant_cx, dtor_addr, args, dtor_ty, None); + let (_, variant_cx) = invoke(variant_cx, dtor_addr, args, dtor_ty, None, false); variant_cx.fcx.pop_and_trans_custom_cleanup_scope(variant_cx, field_scope); variant_cx }) } +fn size_and_align_of_dst<'a>(bcx: &'a Block<'a>, t :ty::t, info: ValueRef) -> (ValueRef, ValueRef) { + debug!("calculate size of DST: {}; with lost info: {}", + bcx.ty_to_string(t), bcx.val_to_string(info)); + if ty::type_is_sized(bcx.tcx(), t) { + let sizing_type = sizing_type_of(bcx.ccx(), t); + let size = C_uint(bcx.ccx(), llsize_of_alloc(bcx.ccx(), sizing_type) as uint); + let align = C_uint(bcx.ccx(), align_of(bcx.ccx(), t) as uint); + return (size, align); + } + match ty::get(t).sty { + ty::ty_struct(id, ref substs) => { + let ccx = bcx.ccx(); + // First get the size of all statically known fields. + // Don't use type_of::sizing_type_of because that expects t to be sized. + assert!(!ty::type_is_simd(bcx.tcx(), t)); + let repr = adt::represent_type(ccx, t); + let sizing_type = adt::sizing_type_of(ccx, &*repr, true); + let sized_size = C_uint(ccx, llsize_of_alloc(ccx, sizing_type) as uint); + let sized_align = C_uint(ccx, llalign_of_min(ccx, sizing_type) as uint); + + // Recurse to get the size of the dynamically sized field (must be + // the last field). + let fields = ty::struct_fields(bcx.tcx(), id, substs); + let last_field = fields[fields.len()-1]; + let field_ty = last_field.mt.ty; + let (unsized_size, unsized_align) = size_and_align_of_dst(bcx, field_ty, info); + + // Return the sum of sizes and max of aligns. + let size = Add(bcx, sized_size, unsized_size); + let align = Select(bcx, + ICmp(bcx, llvm::IntULT, sized_align, unsized_align), + sized_align, + unsized_align); + (size, align) + } + ty::ty_trait(..) => { + // info points to the vtable and the second entry in the vtable is the + // dynamic size of the object. + let info = PointerCast(bcx, info, Type::int(bcx.ccx()).ptr_to()); + let size_ptr = GEPi(bcx, info, [1u]); + let align_ptr = GEPi(bcx, info, [2u]); + (Load(bcx, size_ptr), Load(bcx, align_ptr)) + } + ty::ty_vec(unit_ty, None) => { + // The info in this case is the length of the vec, so the size is that + // times the unit size. + let llunit_ty = sizing_type_of(bcx.ccx(), unit_ty); + let unit_size = llsize_of_alloc(bcx.ccx(), llunit_ty); + (Mul(bcx, info, C_uint(bcx.ccx(), unit_size as uint)), C_uint(bcx.ccx(), 8)) + } + _ => bcx.sess().bug(format!("Unexpected unsized type, found {}", + bcx.ty_to_string(t)).as_slice()) + } +} + fn make_drop_glue<'a>(bcx: &'a Block<'a>, v0: ValueRef, t: ty::t) -> &'a Block<'a> { // NB: v0 is an *alias* of type t here, not a direct value. let _icx = push_ctxt("make_drop_glue"); @@ -276,29 +402,18 @@ fn make_drop_glue<'a>(bcx: &'a Block<'a>, v0: ValueRef, t: ty::t) -> &'a Block<' } ty::ty_uniq(content_ty) => { match ty::get(content_ty).sty { - ty::ty_vec(mt, None) => { - let llbox = Load(bcx, v0); - let not_null = IsNotNull(bcx, llbox); - with_cond(bcx, not_null, |bcx| { - let bcx = tvec::make_drop_glue_unboxed(bcx, llbox, mt.ty); - // FIXME: #13994: the old `Box<[T]>` will not support sized deallocation - trans_exchange_free(bcx, llbox, 0, 8) - }) + ty::ty_vec(ty, None) => { + tvec::make_drop_glue_unboxed(bcx, v0, ty) } ty::ty_str => { - let llbox = Load(bcx, v0); - let not_null = IsNotNull(bcx, llbox); - with_cond(bcx, not_null, |bcx| { - let unit_ty = ty::sequence_element_type(bcx.tcx(), t); - let bcx = tvec::make_drop_glue_unboxed(bcx, llbox, unit_ty); - // FIXME: #13994: the old `Box<str>` will not support sized deallocation - trans_exchange_free(bcx, llbox, 0, 8) - }) + let unit_ty = ty::sequence_element_type(bcx.tcx(), t); + tvec::make_drop_glue_unboxed(bcx, v0, unit_ty) } ty::ty_trait(..) => { let lluniquevalue = GEPi(bcx, v0, [0, abi::trt_field_box]); // Only drop the value when it is non-null - with_cond(bcx, IsNotNull(bcx, Load(bcx, lluniquevalue)), |bcx| { + let concrete_ptr = Load(bcx, lluniquevalue); + with_cond(bcx, IsNotNull(bcx, concrete_ptr), |bcx| { let dtor_ptr = Load(bcx, GEPi(bcx, v0, [0, abi::trt_field_vtable])); let dtor = Load(bcx, dtor_ptr); Call(bcx, @@ -308,8 +423,22 @@ fn make_drop_glue<'a>(bcx: &'a Block<'a>, v0: ValueRef, t: ty::t) -> &'a Block<' bcx }) } + ty::ty_struct(..) if !ty::type_is_sized(bcx.tcx(), content_ty) => { + let llval = GEPi(bcx, v0, [0, abi::slice_elt_base]); + let llbox = Load(bcx, llval); + let not_null = IsNotNull(bcx, llbox); + with_cond(bcx, not_null, |bcx| { + let bcx = drop_ty(bcx, v0, content_ty); + let info = GEPi(bcx, v0, [0, abi::slice_elt_len]); + let info = Load(bcx, info); + let (llsize, llalign) = size_and_align_of_dst(bcx, content_ty, info); + trans_exchange_free_internal(bcx, llbox, llsize, llalign) + }) + } _ => { - let llbox = Load(bcx, v0); + assert!(ty::type_is_sized(bcx.tcx(), content_ty)); + let llval = v0; + let llbox = Load(bcx, llval); let not_null = IsNotNull(bcx, llbox); with_cond(bcx, not_null, |bcx| { let bcx = drop_ty(bcx, llbox, content_ty); @@ -322,7 +451,21 @@ fn make_drop_glue<'a>(bcx: &'a Block<'a>, v0: ValueRef, t: ty::t) -> &'a Block<' let tcx = bcx.tcx(); match ty::ty_dtor(tcx, did) { ty::TraitDtor(dtor, true) => { - trans_struct_drop_flag(bcx, t, v0, dtor, did, substs) + // FIXME(16758) Since the struct is unsized, it is hard to + // find the drop flag (which is at the end of the struct). + // Lets just ignore the flag and pretend everything will be + // OK. + if ty::type_is_sized(bcx.tcx(), t) { + trans_struct_drop_flag(bcx, t, v0, dtor, did, substs) + } else { + // Give the user a heads up that we are doing something + // stupid and dangerous. + bcx.sess().warn(format!("Ignoring drop flag in destructor for {}\ + because the struct is unsized. See issue\ + #16758", + bcx.ty_to_string(t)).as_slice()); + trans_struct_drop(bcx, t, v0, dtor, did, substs) + } } ty::TraitDtor(dtor, false) => { trans_struct_drop(bcx, t, v0, dtor, did, substs) @@ -350,7 +493,23 @@ fn make_drop_glue<'a>(bcx: &'a Block<'a>, v0: ValueRef, t: ty::t) -> &'a Block<' trans_exchange_free(bcx, env, 0, 8) }) } + ty::ty_trait(..) => { + // No need to do a null check here (as opposed to the Box<trait case + // above), because this happens for a trait field in an unsized + // struct. If anything is null, it is the whole struct and we won't + // get here. + let lluniquevalue = GEPi(bcx, v0, [0, abi::trt_field_box]); + let dtor_ptr = Load(bcx, GEPi(bcx, v0, [0, abi::trt_field_vtable])); + let dtor = Load(bcx, dtor_ptr); + Call(bcx, + dtor, + [PointerCast(bcx, Load(bcx, lluniquevalue), Type::i8p(bcx.ccx()))], + None); + bcx + } + ty::ty_vec(ty, None) => tvec::make_drop_glue_unboxed(bcx, v0, ty), _ => { + assert!(ty::type_is_sized(bcx.tcx(), t)); if ty::type_needs_drop(bcx.tcx(), t) && ty::type_is_structural(t) { iter_structural_ty(bcx, v0, t, drop_ty) @@ -449,7 +608,6 @@ fn declare_generic_glue(ccx: &CrateContext, t: ty::t, llfnty: Type, ccx, t, format!("glue_{}", name).as_slice()); - debug!("{} is for type {}", fn_nm, ppaux::ty_to_string(ccx.tcx(), t)); let llfn = decl_cdecl_fn(ccx, fn_nm.as_slice(), llfnty, ty::mk_nil()); note_unique_llvm_symbol(ccx, fn_nm); return llfn; diff --git a/src/librustc/middle/trans/intrinsic.rs b/src/librustc/middle/trans/intrinsic.rs index 359c8d24f72..7d8e4679ae3 100644 --- a/src/librustc/middle/trans/intrinsic.rs +++ b/src/librustc/middle/trans/intrinsic.rs @@ -119,6 +119,17 @@ pub fn check_intrinsics(ccx: &CrateContext) { "s" }).as_slice()); } + if ty::type_is_fat_ptr(ccx.tcx(), transmute_restriction.to) || + ty::type_is_fat_ptr(ccx.tcx(), transmute_restriction.from) { + ccx.sess() + .add_lint(::lint::builtin::TRANSMUTE_FAT_PTR, + transmute_restriction.id, + transmute_restriction.span, + format!("Transmuting fat pointer types; {} to {}.\ + Beware of relying on the compiler's representation", + ty_to_string(ccx.tcx(), transmute_restriction.from), + ty_to_string(ccx.tcx(), transmute_restriction.to))); + } } ccx.sess().abort_if_errors(); } @@ -227,8 +238,7 @@ pub fn trans_intrinsic_call<'a>(mut bcx: &'a Block<'a>, node: ast::NodeId, } (_, "min_align_of") => { let tp_ty = *substs.types.get(FnSpace, 0); - let lltp_ty = type_of::type_of(ccx, tp_ty); - C_uint(ccx, machine::llalign_of_min(ccx, lltp_ty) as uint) + C_uint(ccx, type_of::align_of(ccx, tp_ty) as uint) } (_, "pref_align_of") => { let tp_ty = *substs.types.get(FnSpace, 0); @@ -542,7 +552,7 @@ fn copy_intrinsic(bcx: &Block, allow_overlap: bool, volatile: bool, tp_ty: ty::t, dst: ValueRef, src: ValueRef, count: ValueRef) -> ValueRef { let ccx = bcx.ccx(); let lltp_ty = type_of::type_of(ccx, tp_ty); - let align = C_i32(ccx, machine::llalign_of_min(ccx, lltp_ty) as i32); + let align = C_i32(ccx, type_of::align_of(ccx, tp_ty) as i32); let size = machine::llsize_of(ccx, lltp_ty); let int_size = machine::llbitsize_of_real(ccx, ccx.int_type); let name = if allow_overlap { @@ -571,7 +581,7 @@ fn memset_intrinsic(bcx: &Block, volatile: bool, tp_ty: ty::t, dst: ValueRef, val: ValueRef, count: ValueRef) -> ValueRef { let ccx = bcx.ccx(); let lltp_ty = type_of::type_of(ccx, tp_ty); - let align = C_i32(ccx, machine::llalign_of_min(ccx, lltp_ty) as i32); + let align = C_i32(ccx, type_of::align_of(ccx, tp_ty) as i32); let size = machine::llsize_of(ccx, lltp_ty); let name = if machine::llbitsize_of_real(ccx, ccx.int_type) == 32 { "llvm.memset.p0i8.i32" diff --git a/src/librustc/middle/trans/meth.rs b/src/librustc/middle/trans/meth.rs index 31f2a3df662..83bdcc9dead 100644 --- a/src/librustc/middle/trans/meth.rs +++ b/src/librustc/middle/trans/meth.rs @@ -25,6 +25,7 @@ use middle::trans::datum::*; use middle::trans::expr::{SaveIn, Ignore}; use middle::trans::expr; use middle::trans::glue; +use middle::trans::machine; use middle::trans::monomorphize; use middle::trans::type_::Type; use middle::trans::type_of::*; @@ -40,6 +41,9 @@ use syntax::parse::token; use syntax::{ast, ast_map, visit}; use syntax::ast_util::PostExpansionMethod; +// drop_glue pointer, size, align. +static VTABLE_OFFSET: uint = 3; + /** The main "translation" pass for methods. Generates code for non-monomorphized methods only. Other methods will @@ -450,7 +454,7 @@ pub fn trans_trait_callee_from_llval<'a>(bcx: &'a Block<'a>, GEPi(bcx, llpair, [0u, abi::trt_field_vtable]), Type::vtable(ccx).ptr_to().ptr_to())); - let mptr = Load(bcx, GEPi(bcx, llvtable, [0u, n_method + 1])); + let mptr = Load(bcx, GEPi(bcx, llvtable, [0u, n_method + VTABLE_OFFSET])); let mptr = PointerCast(bcx, mptr, llcallee_ty.ptr_to()); return Callee { @@ -515,22 +519,80 @@ fn get_vtable(bcx: &Block, bcx, closure_def_id); - let llfn = trans_fn_ref_with_vtables( + let mut llfn = trans_fn_ref_with_vtables( bcx, closure_def_id, ExprId(0), - callee_substs, + callee_substs.clone(), VecPerParamSpace::empty()); + { + let unboxed_closures = bcx.tcx() + .unboxed_closures + .borrow(); + let closure_info = + unboxed_closures.find(&closure_def_id) + .expect("get_vtable(): didn't find \ + unboxed closure"); + if closure_info.kind == ty::FnOnceUnboxedClosureKind { + // Untuple the arguments and create an unboxing shim. + let mut new_inputs = vec![ + ty::mk_unboxed_closure(bcx.tcx(), + closure_def_id, + ty::ReStatic) + ]; + match ty::get(closure_info.closure_type + .sig + .inputs[0]).sty { + ty::ty_tup(ref elements) => { + for element in elements.iter() { + new_inputs.push(*element) + } + } + ty::ty_nil => {} + _ => { + bcx.tcx().sess.bug("get_vtable(): closure \ + type wasn't a tuple") + } + } + + let closure_type = ty::BareFnTy { + fn_style: closure_info.closure_type.fn_style, + abi: Rust, + sig: ty::FnSig { + binder_id: closure_info.closure_type + .sig + .binder_id, + inputs: new_inputs, + output: closure_info.closure_type.sig.output, + variadic: false, + }, + }; + debug!("get_vtable(): closure type is {}", + closure_type.repr(bcx.tcx())); + llfn = trans_unboxing_shim(bcx, + llfn, + &closure_type, + closure_def_id, + callee_substs); + } + } + (vec!(llfn)).move_iter() } _ => ccx.sess().bug("get_vtable: expected a static origin"), } }); + let size_ty = sizing_type_of(ccx, self_ty); + let size = machine::llsize_of_alloc(ccx, size_ty); + let ll_size = C_uint(ccx, size as uint); + let align = align_of(ccx, self_ty); + let ll_align = C_uint(ccx, align as uint); + // Generate a destructor for the vtable. let drop_glue = glue::get_drop_glue(ccx, self_ty); - let vtable = make_vtable(ccx, drop_glue, methods); + let vtable = make_vtable(ccx, drop_glue, ll_size, ll_align, methods); ccx.vtables.borrow_mut().insert(hash_id, vtable); vtable @@ -539,11 +601,14 @@ fn get_vtable(bcx: &Block, /// Helper function to declare and initialize the vtable. pub fn make_vtable<I: Iterator<ValueRef>>(ccx: &CrateContext, drop_glue: ValueRef, + size: ValueRef, + align: ValueRef, ptrs: I) -> ValueRef { let _icx = push_ctxt("meth::make_vtable"); - let components: Vec<_> = Some(drop_glue).move_iter().chain(ptrs).collect(); + let head = vec![drop_glue, size, align]; + let components: Vec<_> = head.move_iter().chain(ptrs).collect(); unsafe { let tbl = C_struct(ccx, components.as_slice(), false); @@ -603,7 +668,7 @@ fn emit_vtable_methods(bcx: &Block, if m.explicit_self == ty::ByValueExplicitSelfCategory { fn_ref = trans_unboxing_shim(bcx, fn_ref, - &*m, + &m.fty, m_id, substs.clone()); } @@ -614,6 +679,26 @@ fn emit_vtable_methods(bcx: &Block, }).collect() } +pub fn vtable_ptr<'a>(bcx: &'a Block<'a>, + id: ast::NodeId, + self_ty: ty::t) -> ValueRef { + let ccx = bcx.ccx(); + let origins = { + let vtable_map = ccx.tcx.vtable_map.borrow(); + // This trait cast might be because of implicit coercion + let adjs = ccx.tcx.adjustments.borrow(); + let adjust = adjs.find(&id); + let method_call = if adjust.is_some() && ty::adjust_is_object(adjust.unwrap()) { + MethodCall::autoobject(id) + } else { + MethodCall::expr(id) + }; + let vres = vtable_map.get(&method_call).get_self().unwrap(); + resolve_param_vtables_under_param_substs(ccx.tcx(), bcx.fcx.param_substs, vres) + }; + get_vtable(bcx, self_ty, origins) +} + pub fn trans_trait_cast<'a>(bcx: &'a Block<'a>, datum: Datum<Expr>, id: ast::NodeId, @@ -636,27 +721,16 @@ pub fn trans_trait_cast<'a>(bcx: &'a Block<'a>, SaveIn(dest) => dest }; - let ccx = bcx.ccx(); let v_ty = datum.ty; - let llbox_ty = type_of(bcx.ccx(), datum.ty); + let llbox_ty = type_of(bcx.ccx(), v_ty); // Store the pointer into the first half of pair. - let mut llboxdest = GEPi(bcx, lldest, [0u, abi::trt_field_box]); - llboxdest = PointerCast(bcx, llboxdest, llbox_ty.ptr_to()); + let llboxdest = GEPi(bcx, lldest, [0u, abi::trt_field_box]); + let llboxdest = PointerCast(bcx, llboxdest, llbox_ty.ptr_to()); bcx = datum.store_to(bcx, llboxdest); // Store the vtable into the second half of pair. - let origins = { - let vtable_map = ccx.tcx.vtable_map.borrow(); - // This trait cast might be because of implicit coercion - let method_call = match ccx.tcx.adjustments.borrow().find(&id) { - Some(&ty::AutoObject(..)) => MethodCall::autoobject(id), - _ => MethodCall::expr(id) - }; - let vres = vtable_map.get(&method_call).get_self().unwrap(); - resolve_param_vtables_under_param_substs(ccx.tcx(), bcx.fcx.param_substs, vres) - }; - let vtable = get_vtable(bcx, v_ty, origins); + let vtable = vtable_ptr(bcx, id, v_ty); let llvtabledest = GEPi(bcx, lldest, [0u, abi::trt_field_vtable]); let llvtabledest = PointerCast(bcx, llvtabledest, val_ty(vtable).ptr_to()); Store(bcx, vtable, llvtabledest); diff --git a/src/librustc/middle/trans/reflect.rs b/src/librustc/middle/trans/reflect.rs index 2aff12c2b68..11c641f2d75 100644 --- a/src/librustc/middle/trans/reflect.rs +++ b/src/librustc/middle/trans/reflect.rs @@ -66,7 +66,7 @@ impl<'a, 'b> Reflector<'a, 'b> { pub fn c_size_and_align(&mut self, t: ty::t) -> Vec<ValueRef> { let tr = type_of(self.bcx.ccx(), t); let s = machine::llsize_of_real(self.bcx.ccx(), tr); - let a = machine::llalign_of_min(self.bcx.ccx(), tr); + let a = align_of(self.bcx.ccx(), t); return vec!(self.c_uint(s as uint), self.c_uint(a as uint)); } @@ -94,6 +94,7 @@ impl<'a, 'b> Reflector<'a, 'b> { ty::MethodTraitItem(ref method) => (*method).clone(), }; let mth_ty = ty::mk_bare_fn(tcx, method.fty.clone()); + debug!("Emit call visit method: visit_{}: {}", ty_name, ty_to_string(tcx, mth_ty)); let v = self.visitor_val; debug!("passing {} args:", args.len()); let mut bcx = self.bcx; @@ -149,13 +150,24 @@ impl<'a, 'b> Reflector<'a, 'b> { ty::ty_float(ast::TyF32) => self.leaf("f32"), ty::ty_float(ast::TyF64) => self.leaf("f64"), + ty::ty_open(_) | ty::ty_str | ty::ty_vec(_, None) | ty::ty_trait(..) => { + // Unfortunately we can't do anything here because at runtime we + // pass around the value by pointer (*u8). But unsized pointers are + // fat and so we can't just cast them to *u8 and back. So we have + // to work with the pointer directly (see ty_rptr/ty_uniq). + fail!("Can't reflect unsized type") + } + // FIXME(15049) Reflection for unsized structs. + ty::ty_struct(..) if !ty::type_is_sized(bcx.tcx(), t) => { + fail!("Can't reflect unsized type") + } + // Should rename to vec_*. - ty::ty_vec(ref mt, Some(sz)) => { - let extra = (vec!(self.c_uint(sz))).append(self.c_size_and_align(t).as_slice()); - let extra = extra.append(self.c_mt(mt).as_slice()); + ty::ty_vec(ty, Some(sz)) => { + let mut extra = (vec!(self.c_uint(sz))).append(self.c_size_and_align(t).as_slice()); + extra.push(self.c_tydesc(ty)); self.visit("evec_fixed", extra.as_slice()) } - ty::ty_vec(..) | ty::ty_str | ty::ty_trait(..) => fail!("unexpected unsized type"), // Should remove mt from box and uniq. ty::ty_box(typ) => { let extra = self.c_mt(&ty::mt { @@ -164,14 +176,12 @@ impl<'a, 'b> Reflector<'a, 'b> { }); self.visit("box", extra.as_slice()) } + ty::ty_ptr(ref mt) => { + let extra = self.c_mt(mt); + self.visit("ptr", extra.as_slice()) + } ty::ty_uniq(typ) => { match ty::get(typ).sty { - ty::ty_vec(ref mt, None) => { - let extra = Vec::new(); - let extra = extra.append(self.c_mt(mt).as_slice()); - self.visit("evec_uniq", extra.as_slice()) - } - ty::ty_str => self.visit("estr_uniq", &[]), ty::ty_trait(..) => { let extra = [ self.c_slice(token::intern_and_get_ident( @@ -179,6 +189,12 @@ impl<'a, 'b> Reflector<'a, 'b> { ]; self.visit("trait", extra); } + // FIXME(15049) allow reflection of Box<[T]>. You'll need to + // restore visit_evec_uniq. + ty::ty_vec(_, None) => { + fail!("Box<[T]> theoretically doesn't exist, so don't try to reflect it") + } + ty::ty_str => fail!("Can't reflect Box<str> which shouldn't be used anyway"), _ => { let extra = self.c_mt(&ty::mt { ty: typ, @@ -188,17 +204,11 @@ impl<'a, 'b> Reflector<'a, 'b> { } } } - ty::ty_ptr(ref mt) => { - let extra = self.c_mt(mt); - self.visit("ptr", extra.as_slice()) - } ty::ty_rptr(_, ref mt) => { match ty::get(mt.ty).sty { - ty::ty_vec(ref mt, None) => { - let (name, extra) = ("slice".to_string(), Vec::new()); - let extra = extra.append(self.c_mt(mt).as_slice()); - self.visit(format!("evec_{}", name).as_slice(), - extra.as_slice()) + ty::ty_vec(ty, None) => { + let extra = self.c_mt(&ty::mt{ty: ty, mutbl: mt.mutbl}); + self.visit("evec_slice", extra.as_slice()) } ty::ty_str => self.visit("estr_slice", &[]), ty::ty_trait(..) => { @@ -267,12 +277,18 @@ impl<'a, 'b> Reflector<'a, 'b> { special_idents::unnamed_field.name; } + // This and the type_is_sized check on individual field types are + // because we cannot reflect unsized types (see note above). We + // just pretend the unsized field does not exist and print nothing. + // This is sub-optimal. + let len = fields.len(); + let extra = (vec!( self.c_slice( token::intern_and_get_ident(ty_to_string(tcx, t).as_slice())), self.c_bool(named_fields), - self.c_uint(fields.len()) + self.c_uint(len) )).append(self.c_size_and_align(t).as_slice()); self.bracketed("class", extra.as_slice(), |this| { for (i, field) in fields.iter().enumerate() { diff --git a/src/librustc/middle/trans/tvec.rs b/src/librustc/middle/trans/tvec.rs index 1241a85e95c..94ca520c533 100644 --- a/src/librustc/middle/trans/tvec.rs +++ b/src/librustc/middle/trans/tvec.rs @@ -25,7 +25,7 @@ use middle::trans::datum::*; use middle::trans::expr::{Dest, Ignore, SaveIn}; use middle::trans::expr; use middle::trans::glue; -use middle::trans::machine::{llsize_of, nonzero_llsize_of, llsize_of_alloc}; +use middle::trans::machine::{nonzero_llsize_of, llsize_of_alloc}; use middle::trans::type_::Type; use middle::trans::type_of; use middle::ty; @@ -34,14 +34,14 @@ use util::ppaux::ty_to_string; use syntax::ast; use syntax::parse::token::InternedString; -pub fn get_fill(bcx: &Block, vptr: ValueRef) -> ValueRef { - let _icx = push_ctxt("tvec::get_fill"); - Load(bcx, GEPi(bcx, vptr, [0u, abi::vec_elt_fill])) +fn get_len(bcx: &Block, vptr: ValueRef) -> ValueRef { + let _icx = push_ctxt("tvec::get_lenl"); + Load(bcx, expr::get_len(bcx, vptr)) } -pub fn get_dataptr(bcx: &Block, vptr: ValueRef) -> ValueRef { +fn get_dataptr(bcx: &Block, vptr: ValueRef) -> ValueRef { let _icx = push_ctxt("tvec::get_dataptr"); - GEPi(bcx, vptr, [0u, abi::vec_elt_elems, 0u]) + Load(bcx, expr::get_dataptr(bcx, vptr)) } pub fn pointer_add_byte(bcx: &Block, ptr: ValueRef, bytes: ValueRef) -> ValueRef { @@ -56,13 +56,24 @@ pub fn make_drop_glue_unboxed<'a>( vptr: ValueRef, unit_ty: ty::t) -> &'a Block<'a> { - let _icx = push_ctxt("tvec::make_drop_glue_unboxed"); - let tcx = bcx.tcx(); - if ty::type_needs_drop(tcx, unit_ty) { - let fill = get_fill(bcx, vptr); + let not_null = IsNotNull(bcx, vptr); + with_cond(bcx, not_null, |bcx| { + let tcx = bcx.tcx(); + let _icx = push_ctxt("tvec::make_drop_glue_unboxed"); + + let len = get_len(bcx, vptr); let dataptr = get_dataptr(bcx, vptr); - iter_vec_raw(bcx, dataptr, unit_ty, fill, glue::drop_ty) - } else { bcx } + let bcx = if ty::type_needs_drop(tcx, unit_ty) { + iter_vec_raw(bcx, dataptr, unit_ty, len, glue::drop_ty) + } else { + bcx + }; + + let not_null = IsNotNull(bcx, dataptr); + with_cond(bcx, not_null, |bcx| { + glue::trans_exchange_free(bcx, dataptr, 0, 8) + }) + }) } pub struct VecTypes { @@ -85,8 +96,7 @@ impl VecTypes { pub fn trans_fixed_vstore<'a>( bcx: &'a Block<'a>, - vstore_expr: &ast::Expr, - content_expr: &ast::Expr, + expr: &ast::Expr, dest: expr::Dest) -> &'a Block<'a> { //! @@ -96,50 +106,53 @@ pub fn trans_fixed_vstore<'a>( // to store the array of the suitable size, so all we have to do is // generate the content. - debug!("trans_fixed_vstore(vstore_expr={}, dest={:?})", - bcx.expr_to_string(vstore_expr), dest.to_string(bcx.ccx())); + debug!("trans_fixed_vstore(expr={}, dest={:?})", + bcx.expr_to_string(expr), dest.to_string(bcx.ccx())); - let vt = vec_types_from_expr(bcx, vstore_expr); + let vt = vec_types_from_expr(bcx, expr); return match dest { - Ignore => write_content(bcx, &vt, vstore_expr, content_expr, dest), + Ignore => write_content(bcx, &vt, expr, expr, dest), SaveIn(lldest) => { // lldest will have type *[T x N], but we want the type *T, // so use GEP to convert: let lldest = GEPi(bcx, lldest, [0, 0]); - write_content(bcx, &vt, vstore_expr, content_expr, SaveIn(lldest)) + write_content(bcx, &vt, expr, expr, SaveIn(lldest)) } }; } -pub fn trans_slice_vstore<'a>( - bcx: &'a Block<'a>, - vstore_expr: &ast::Expr, - content_expr: &ast::Expr, - dest: expr::Dest) - -> &'a Block<'a> { +pub fn trans_slice_vec<'a>(bcx: &'a Block<'a>, + slice_expr: &ast::Expr, + content_expr: &ast::Expr) + -> DatumBlock<'a, Expr> { /*! * &[...] allocates memory on the stack and writes the values into it, - * returning a slice (pair of ptr, len). &"..." is similar except that - * the memory can be statically allocated. + * returning the vector (the caller must make the reference). "..." is + * similar except that the memory can be statically allocated and we return + * a reference (strings are always by-ref). */ let fcx = bcx.fcx; let ccx = fcx.ccx; let mut bcx = bcx; - debug!("trans_slice_vstore(vstore_expr={}, dest={})", - bcx.expr_to_string(vstore_expr), dest.to_string(ccx)); + debug!("trans_slice_vec(slice_expr={})", + bcx.expr_to_string(slice_expr)); - // Handle the &"..." case: + let vec_ty = node_id_type(bcx, slice_expr.id); + + // Handle the "..." case (returns a slice since strings are always unsized): match content_expr.node { ast::ExprLit(lit) => { match lit.node { ast::LitStr(ref s, _) => { - return trans_lit_str(bcx, - content_expr, - s.clone(), - dest) + let scratch = rvalue_scratch_datum(bcx, vec_ty, ""); + bcx = trans_lit_str(bcx, + content_expr, + s.clone(), + SaveIn(scratch.val)); + return DatumBlock::new(bcx, scratch.to_expr_datum()); } _ => {} } @@ -148,46 +161,39 @@ pub fn trans_slice_vstore<'a>( } // Handle the &[...] case: - let vt = vec_types_from_expr(bcx, vstore_expr); + let vt = vec_types_from_expr(bcx, content_expr); let count = elements_required(bcx, content_expr); - debug!("vt={}, count={:?}", vt.to_string(ccx), count); - + debug!(" vt={}, count={:?}", vt.to_string(ccx), count); let llcount = C_uint(ccx, count); - let llfixed; - if count == 0 { + + let fixed_ty = ty::mk_vec(bcx.tcx(), + vt.unit_ty, + Some(count)); + let llfixed_ty = type_of::type_of(bcx.ccx(), fixed_ty).ptr_to(); + + let llfixed = if count == 0 { // Just create a zero-sized alloca to preserve // the non-null invariant of the inner slice ptr - llfixed = base::arrayalloca(bcx, vt.llunit_ty, llcount); + let llfixed = base::arrayalloca(bcx, vt.llunit_ty, llcount); + BitCast(bcx, llfixed, llfixed_ty) } else { // Make a fixed-length backing array and allocate it on the stack. - llfixed = base::arrayalloca(bcx, vt.llunit_ty, llcount); + let llfixed = base::arrayalloca(bcx, vt.llunit_ty, llcount); // Arrange for the backing array to be cleaned up. - let fixed_ty = ty::mk_vec(bcx.tcx(), - ty::mt {ty: vt.unit_ty, - mutbl: ast::MutMutable}, - Some(count)); - let llfixed_ty = type_of::type_of(bcx.ccx(), fixed_ty).ptr_to(); let llfixed_casted = BitCast(bcx, llfixed, llfixed_ty); let cleanup_scope = cleanup::temporary_scope(bcx.tcx(), content_expr.id); fcx.schedule_lifetime_end(cleanup_scope, llfixed_casted); fcx.schedule_drop_mem(cleanup_scope, llfixed_casted, fixed_ty); // Generate the content into the backing array. - bcx = write_content(bcx, &vt, vstore_expr, + bcx = write_content(bcx, &vt, slice_expr, content_expr, SaveIn(llfixed)); - } - // Finally, create the slice pair itself. - match dest { - Ignore => {} - SaveIn(lldest) => { - Store(bcx, llfixed, GEPi(bcx, lldest, [0u, abi::slice_elt_base])); - Store(bcx, llcount, GEPi(bcx, lldest, [0u, abi::slice_elt_len])); - } - } + llfixed_casted + }; - return bcx; + immediate_rvalue_bcx(bcx, llfixed, vec_ty).to_expr_datumblock() } pub fn trans_lit_str<'a>( @@ -198,7 +204,7 @@ pub fn trans_lit_str<'a>( -> &'a Block<'a> { /*! * Literal strings translate to slices into static memory. This is - * different from trans_slice_vstore() above because it does need to copy + * different from trans_slice_vstore() above because it doesn't need to copy * the content anywhere. */ @@ -214,27 +220,24 @@ pub fn trans_lit_str<'a>( let llbytes = C_uint(bcx.ccx(), bytes); let llcstr = C_cstr(bcx.ccx(), str_lit, false); let llcstr = llvm::LLVMConstPointerCast(llcstr, Type::i8p(bcx.ccx()).to_ref()); - Store(bcx, llcstr, - GEPi(bcx, lldest, [0u, abi::slice_elt_base])); - Store(bcx, llbytes, - GEPi(bcx, lldest, [0u, abi::slice_elt_len])); + Store(bcx, llcstr, GEPi(bcx, lldest, [0u, abi::slice_elt_base])); + Store(bcx, llbytes, GEPi(bcx, lldest, [0u, abi::slice_elt_len])); bcx } } } } - -pub fn trans_uniq_vstore<'a>(bcx: &'a Block<'a>, - vstore_expr: &ast::Expr, - content_expr: &ast::Expr) - -> DatumBlock<'a, Expr> { +pub fn trans_uniq_vec<'a>(bcx: &'a Block<'a>, + uniq_expr: &ast::Expr, + content_expr: &ast::Expr) + -> DatumBlock<'a, Expr> { /*! - * ~[...] and "...".to_string() allocate boxes in the exchange heap and write + * Box<[...]> and "...".to_string() allocate boxes in the exchange heap and write * the array elements into them. */ - debug!("trans_uniq_vstore(vstore_expr={})", bcx.expr_to_string(vstore_expr)); + debug!("trans_uniq_vec(uniq_expr={})", bcx.expr_to_string(uniq_expr)); let fcx = bcx.fcx; let ccx = fcx.ccx; @@ -267,45 +270,50 @@ pub fn trans_uniq_vstore<'a>(bcx: &'a Block<'a>, _ => {} } - let vec_ty = node_id_type(bcx, vstore_expr.id); - let vt = vec_types(bcx, ty::sequence_element_type(bcx.tcx(), vec_ty)); + let vt = vec_types_from_expr(bcx, content_expr); let count = elements_required(bcx, content_expr); + debug!(" vt={}, count={:?}", vt.to_string(ccx), count); + let vec_ty = node_id_type(bcx, uniq_expr.id); - let llunitty = type_of::type_of(ccx, vt.unit_ty); - let unit_sz = nonzero_llsize_of(ccx, llunitty); - - let fill = Mul(bcx, C_uint(ccx, count), unit_sz); - let alloc = if count < 4u { Mul(bcx, C_int(ccx, 4), unit_sz) } - else { fill }; - - let vecsize = Add(bcx, alloc, llsize_of(ccx, ccx.opaque_vec_type)); - - // ~[T] is not going to be changed to support alignment, since it's obsolete. + let unit_sz = nonzero_llsize_of(ccx, type_of::type_of(ccx, vt.unit_ty)); + let llcount = if count < 4u { + C_int(ccx, 4) + } else { + C_uint(ccx, count) + }; + let alloc = Mul(bcx, llcount, unit_sz); + let llty_ptr = type_of::type_of(ccx, vt.unit_ty).ptr_to(); let align = C_uint(ccx, 8); - let Result { bcx: bcx, val: val } = malloc_raw_dyn(bcx, vec_ty, vecsize, align); - Store(bcx, fill, GEPi(bcx, val, [0u, abi::vec_elt_fill])); - Store(bcx, alloc, GEPi(bcx, val, [0u, abi::vec_elt_alloc])); + let Result { bcx: bcx, val: dataptr } = malloc_raw_dyn(bcx, + llty_ptr, + vec_ty, + alloc, + align); // Create a temporary scope lest execution should fail while // constructing the vector. let temp_scope = fcx.push_custom_cleanup_scope(); - - // FIXME: #13994: the old `Box<[T]> will not support sized deallocation, this is a placeholder - let content_ty = vt.unit_ty; + // FIXME: #13994: the old `Box<[T]> will not support sized deallocation, + // this is a placeholder fcx.schedule_free_value(cleanup::CustomScope(temp_scope), - val, cleanup::HeapExchange, content_ty); - - let dataptr = get_dataptr(bcx, val); + dataptr, cleanup::HeapExchange, vt.unit_ty); - debug!("alloc_uniq_vec() returned val={}, dataptr={}", - bcx.val_to_string(val), bcx.val_to_string(dataptr)); + debug!(" alloc_uniq_vec() returned dataptr={}, len={}", + bcx.val_to_string(dataptr), count); - let bcx = write_content(bcx, &vt, vstore_expr, - content_expr, SaveIn(dataptr)); + let bcx = write_content(bcx, &vt, uniq_expr, + content_expr, SaveIn(dataptr)); fcx.pop_custom_cleanup_scope(temp_scope); - immediate_rvalue_bcx(bcx, val, vec_ty).to_expr_datumblock() + if ty::type_is_sized(bcx.tcx(), vec_ty) { + immediate_rvalue_bcx(bcx, dataptr, vec_ty).to_expr_datumblock() + } else { + let scratch = rvalue_scratch_datum(bcx, vec_ty, ""); + Store(bcx, dataptr, GEPi(bcx, scratch.val, [0u, abi::slice_elt_base])); + Store(bcx, llcount, GEPi(bcx, scratch.val, [0u, abi::slice_elt_len])); + DatumBlock::new(bcx, scratch.to_expr_datum()) + } } pub fn write_content<'a>( @@ -451,21 +459,27 @@ pub fn elements_required(bcx: &Block, content_expr: &ast::Expr) -> uint { } } -pub fn get_fixed_base_and_byte_len(bcx: &Block, - llval: ValueRef, - unit_ty: ty::t, - vec_length: uint) - -> (ValueRef, ValueRef) { +pub fn get_fixed_base_and_len(bcx: &Block, + llval: ValueRef, + vec_length: uint) + -> (ValueRef, ValueRef) { /*! * Converts a fixed-length vector into the slice pair. * The vector should be stored in `llval` which should be by ref. */ let ccx = bcx.ccx(); - let vt = vec_types(bcx, unit_ty); - let base = GEPi(bcx, llval, [0u, 0u]); - let len = Mul(bcx, C_uint(ccx, vec_length), vt.llunit_size); + let base = expr::get_dataptr(bcx, llval); + let len = C_uint(ccx, vec_length); + (base, len) +} + +fn get_slice_base_and_len(bcx: &Block, + llval: ValueRef) + -> (ValueRef, ValueRef) { + let base = Load(bcx, GEPi(bcx, llval, [0u, abi::slice_elt_base])); + let len = Load(bcx, GEPi(bcx, llval, [0u, abi::slice_elt_len])); (base, len) } @@ -484,27 +498,20 @@ pub fn get_base_and_len(bcx: &Block, let ccx = bcx.ccx(); match ty::get(vec_ty).sty { - ty::ty_vec(_, Some(n)) => { - let base = GEPi(bcx, llval, [0u, 0u]); - (base, C_uint(ccx, n)) - } - ty::ty_rptr(_, mt) => match ty::get(mt.ty).sty { - ty::ty_vec(_, None) | ty::ty_str => { - assert!(!type_is_immediate(bcx.ccx(), vec_ty)); - let base = Load(bcx, GEPi(bcx, llval, [0u, abi::slice_elt_base])); - let count = Load(bcx, GEPi(bcx, llval, [0u, abi::slice_elt_len])); - (base, count) - } - _ => ccx.sess().bug("unexpected type (ty_rptr) in get_base_and_len"), + ty::ty_vec(_, Some(n)) => get_fixed_base_and_len(bcx, llval, n), + ty::ty_open(ty) => match ty::get(ty).sty { + ty::ty_vec(_, None) | ty::ty_str => get_slice_base_and_len(bcx, llval), + _ => ccx.sess().bug("unexpected type in get_base_and_len") }, - ty::ty_uniq(t) => match ty::get(t).sty { - ty::ty_vec(_, None) | ty::ty_str => { - assert!(type_is_immediate(bcx.ccx(), vec_ty)); - let vt = vec_types(bcx, ty::sequence_element_type(bcx.tcx(), vec_ty)); - let body = Load(bcx, llval); - (get_dataptr(bcx, body), UDiv(bcx, get_fill(bcx, body), vt.llunit_size)) + + // Only used for pattern matching. + ty::ty_uniq(ty) | ty::ty_rptr(_, ty::mt{ty, ..}) => match ty::get(ty).sty { + ty::ty_vec(_, None) | ty::ty_str => get_slice_base_and_len(bcx, llval), + ty::ty_vec(_, Some(n)) => { + let base = GEPi(bcx, Load(bcx, llval), [0u, 0u]); + (base, C_uint(ccx, n)) } - _ => ccx.sess().bug("unexpected type (ty_uniq) in get_base_and_len"), + _ => ccx.sess().bug("unexpected type in get_base_and_len"), }, _ => ccx.sess().bug("unexpected type in get_base_and_len"), } @@ -576,13 +583,15 @@ pub fn iter_vec_raw<'r, bcx: &'b Block<'b>, data_ptr: ValueRef, unit_ty: ty::t, - fill: ValueRef, + len: ValueRef, f: iter_vec_block<'r,'b>) -> &'b Block<'b> { let _icx = push_ctxt("tvec::iter_vec_raw"); let fcx = bcx.fcx; let vt = vec_types(bcx, unit_ty); + let fill = Mul(bcx, len, vt.llunit_size); + if vt.llunit_alloc_size == 0 { // Special-case vectors with elements of size 0 so they don't go out of bounds (#9890) iter_vec_loop(bcx, data_ptr, &vt, fill, f) diff --git a/src/librustc/middle/trans/type_.rs b/src/librustc/middle/trans/type_.rs index e9d92e45f62..017d61137e4 100644 --- a/src/librustc/middle/trans/type_.rs +++ b/src/librustc/middle/trans/type_.rs @@ -215,7 +215,7 @@ impl Type { pub fn vec(ccx: &CrateContext, ty: &Type) -> Type { Type::struct_(ccx, - [Type::int(ccx), Type::int(ccx), Type::array(ty, 0)], + [Type::array(ty, 0), Type::int(ccx)], false) } @@ -231,9 +231,16 @@ impl Type { ], false) } + pub fn vtable_ptr(ccx: &CrateContext) -> Type { + Type::glue_fn(ccx, Type::i8p(ccx)).ptr_to().ptr_to() + } + pub fn opaque_trait(ccx: &CrateContext) -> Type { - let vtable = Type::glue_fn(ccx, Type::i8p(ccx)).ptr_to().ptr_to(); - Type::struct_(ccx, [vtable, Type::i8p(ccx)], false) + Type::struct_(ccx, [Type::opaque_trait_data(ccx).ptr_to(), Type::vtable_ptr(ccx)], false) + } + + pub fn opaque_trait_data(ccx: &CrateContext) -> Type { + Type::i8(ccx) } pub fn kind(&self) -> TypeKind { diff --git a/src/librustc/middle/trans/type_of.rs b/src/librustc/middle/trans/type_of.rs index 8a445fc4839..83c792ecf87 100644 --- a/src/librustc/middle/trans/type_of.rs +++ b/src/librustc/middle/trans/type_of.rs @@ -14,7 +14,9 @@ use middle::subst; use middle::trans::adt; use middle::trans::common::*; use middle::trans::foreign; +use middle::trans::machine; use middle::ty; +use util::ppaux; use util::ppaux::Repr; use middle::trans::type_::Type; @@ -160,6 +162,11 @@ pub fn sizing_type_of(cx: &CrateContext, t: ty::t) -> Type { } let llsizingty = match ty::get(t).sty { + _ if !ty::lltype_is_sized(cx.tcx(), t) => { + cx.sess().bug(format!("trying to take the sizing type of {}, an unsized type", + ppaux::ty_to_string(cx.tcx(), t)).as_slice()) + } + ty::ty_nil | ty::ty_bot => Type::nil(cx), ty::ty_bool => Type::bool(cx), ty::ty_char => Type::char(cx), @@ -169,32 +176,24 @@ pub fn sizing_type_of(cx: &CrateContext, t: ty::t) -> Type { ty::ty_box(..) | ty::ty_ptr(..) => Type::i8p(cx), - ty::ty_uniq(ty) => { - match ty::get(ty).sty { - ty::ty_trait(..) => Type::opaque_trait(cx), - _ => Type::i8p(cx), - } - } - ty::ty_rptr(_, mt) => { - match ty::get(mt.ty).sty { - ty::ty_vec(_, None) | ty::ty_str => { - Type::struct_(cx, [Type::i8p(cx), Type::i8p(cx)], false) - } - ty::ty_trait(..) => Type::opaque_trait(cx), - _ => Type::i8p(cx), + ty::ty_uniq(ty) | ty::ty_rptr(_, ty::mt{ty, ..}) => { + if ty::type_is_sized(cx.tcx(), ty) { + Type::i8p(cx) + } else { + Type::struct_(cx, [Type::i8p(cx), Type::i8p(cx)], false) } } ty::ty_bare_fn(..) => Type::i8p(cx), ty::ty_closure(..) => Type::struct_(cx, [Type::i8p(cx), Type::i8p(cx)], false), - ty::ty_vec(mt, Some(size)) => { - Type::array(&sizing_type_of(cx, mt.ty), size as u64) + ty::ty_vec(ty, Some(size)) => { + Type::array(&sizing_type_of(cx, ty), size as u64) } ty::ty_tup(..) | ty::ty_enum(..) | ty::ty_unboxed_closure(..) => { let repr = adt::represent_type(cx, t); - adt::sizing_type_of(cx, &*repr) + adt::sizing_type_of(cx, &*repr, false) } ty::ty_struct(..) => { @@ -204,15 +203,19 @@ pub fn sizing_type_of(cx: &CrateContext, t: ty::t) -> Type { Type::vector(&type_of(cx, et), n as u64) } else { let repr = adt::represent_type(cx, t); - adt::sizing_type_of(cx, &*repr) + adt::sizing_type_of(cx, &*repr, false) } } - ty::ty_infer(..) | ty::ty_param(..) | - ty::ty_err(..) | ty::ty_vec(_, None) | ty::ty_str | ty::ty_trait(..) => { - cx.sess().bug(format!("fictitious type {:?} in sizing_type_of()", - ty::get(t).sty).as_slice()) + ty::ty_open(_) => { + Type::struct_(cx, [Type::i8p(cx), Type::i8p(cx)], false) + } + + ty::ty_infer(..) | ty::ty_param(..) | ty::ty_err(..) => { + cx.sess().bug(format!("fictitious type {} in sizing_type_of()", + ppaux::ty_to_string(cx.tcx(), t)).as_slice()) } + ty::ty_vec(_, None) | ty::ty_trait(..) | ty::ty_str => fail!("unreachable") }; cx.llsizingtypes.borrow_mut().insert(t, llsizingty); @@ -229,13 +232,30 @@ pub fn arg_type_of(cx: &CrateContext, t: ty::t) -> Type { // NB: If you update this, be sure to update `sizing_type_of()` as well. pub fn type_of(cx: &CrateContext, t: ty::t) -> Type { + fn type_of_unsize_info(cx: &CrateContext, t: ty::t) -> Type { + // It is possible to end up here with a sized type. This happens with a + // struct which might be unsized, but is monomorphised to a sized type. + // In this case we'll fake a fat pointer with no unsize info (we use 0). + // However, its still a fat pointer, so we need some type use. + if ty::type_is_sized(cx.tcx(), t) { + return Type::i8p(cx); + } + + match ty::get(ty::unsized_part_of_type(cx.tcx(), t)).sty { + ty::ty_str | ty::ty_vec(..) => Type::uint_from_ty(cx, ast::TyU), + ty::ty_trait(_) => Type::vtable_ptr(cx), + _ => fail!("Unexpected type returned from unsized_part_of_type : {}", + t.repr(cx.tcx())) + } + } + // Check the cache. match cx.lltypes.borrow().find(&t) { Some(&llty) => return llty, None => () } - debug!("type_of {} {:?}", t.repr(cx.tcx()), t); + debug!("type_of {} {:?}", t.repr(cx.tcx()), ty::get(t).sty); // Replace any typedef'd types with their equivalent non-typedef // type. This ensures that all LLVM nominal types that contain @@ -283,35 +303,37 @@ pub fn type_of(cx: &CrateContext, t: ty::t) -> Type { ty::ty_box(typ) => { Type::at_box(cx, type_of(cx, typ)).ptr_to() } - ty::ty_uniq(typ) => { - match ty::get(typ).sty { - ty::ty_vec(mt, None) => Type::vec(cx, &type_of(cx, mt.ty)).ptr_to(), - ty::ty_str => Type::vec(cx, &Type::i8(cx)).ptr_to(), - ty::ty_trait(..) => Type::opaque_trait(cx), - _ => type_of(cx, typ).ptr_to(), - } - } ty::ty_ptr(ref mt) => type_of(cx, mt.ty).ptr_to(), - ty::ty_rptr(_, ref mt) => { - match ty::get(mt.ty).sty { - ty::ty_vec(mt, None) => { - let p_ty = type_of(cx, mt.ty).ptr_to(); - let u_ty = Type::uint_from_ty(cx, ast::TyU); - Type::struct_(cx, [p_ty, u_ty], false) - } + + ty::ty_uniq(ty) | ty::ty_rptr(_, ty::mt{ty, ..}) => { + match ty::get(ty).sty { ty::ty_str => { - // This means we get a nicer name in the output + // This means we get a nicer name in the output (str is always + // unsized). cx.tn.find_type("str_slice").unwrap() } ty::ty_trait(..) => Type::opaque_trait(cx), - _ => type_of(cx, mt.ty).ptr_to(), + _ if !ty::type_is_sized(cx.tcx(), ty) => { + let p_ty = type_of(cx, ty).ptr_to(); + Type::struct_(cx, [p_ty, type_of_unsize_info(cx, ty)], false) + } + _ => type_of(cx, ty).ptr_to(), } } - ty::ty_vec(ref mt, Some(n)) => { - Type::array(&type_of(cx, mt.ty), n as u64) + ty::ty_vec(ty, Some(n)) => { + Type::array(&type_of(cx, ty), n as u64) + } + ty::ty_vec(ty, None) => { + type_of(cx, ty) + } + + ty::ty_trait(..) => { + Type::opaque_trait_data(cx) } + ty::ty_str => Type::i8(cx), + ty::ty_bare_fn(_) => { type_of_fn_from_ty(cx, t).ptr_to() } @@ -339,12 +361,27 @@ pub fn type_of(cx: &CrateContext, t: ty::t) -> Type { } } - ty::ty_vec(_, None) => cx.sess().bug("type_of with unsized ty_vec"), - ty::ty_str => cx.sess().bug("type_of with unsized (bare) ty_str"), - ty::ty_trait(..) => cx.sess().bug("type_of with unsized ty_trait"), + ty::ty_open(t) => match ty::get(t).sty { + ty::ty_struct(..) => { + let p_ty = type_of(cx, t).ptr_to(); + Type::struct_(cx, [p_ty, type_of_unsize_info(cx, t)], false) + } + ty::ty_vec(ty, None) => { + let p_ty = type_of(cx, ty).ptr_to(); + Type::struct_(cx, [p_ty, type_of_unsize_info(cx, t)], false) + } + ty::ty_str => { + let p_ty = Type::i8p(cx); + Type::struct_(cx, [p_ty, type_of_unsize_info(cx, t)], false) + } + ty::ty_trait(..) => Type::opaque_trait(cx), + _ => cx.sess().bug(format!("ty_open with sized type: {}", + ppaux::ty_to_string(cx.tcx(), t)).as_slice()) + }, + ty::ty_infer(..) => cx.sess().bug("type_of with ty_infer"), ty::ty_param(..) => cx.sess().bug("type_of with ty_param"), - ty::ty_err(..) => cx.sess().bug("type_of with ty_err") + ty::ty_err(..) => cx.sess().bug("type_of with ty_err"), }; debug!("--> mapped t={} {:?} to llty={}", @@ -367,6 +404,11 @@ pub fn type_of(cx: &CrateContext, t: ty::t) -> Type { return llty; } +pub fn align_of(cx: &CrateContext, t: ty::t) -> u64 { + let llty = sizing_type_of(cx, t); + machine::llalign_of_min(cx, llty) +} + // Want refinements! (Or case classes, I guess pub enum named_ty { a_struct, diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index ae96937757f..c2d0d27be6d 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -19,7 +19,8 @@ use middle::def; use middle::dependency_format; use middle::freevars::CaptureModeMap; use middle::freevars; -use middle::lang_items::{FnMutTraitLangItem, OpaqueStructLangItem}; +use middle::lang_items::{FnTraitLangItem, FnMutTraitLangItem}; +use middle::lang_items::{FnOnceTraitLangItem, OpaqueStructLangItem}; use middle::lang_items::{TyDescStructLangItem, TyVisitorTraitLangItem}; use middle::mem_categorization as mc; use middle::resolve; @@ -262,37 +263,138 @@ pub enum Variance { #[deriving(Clone)] pub enum AutoAdjustment { AutoAddEnv(ty::TraitStore), - AutoDerefRef(AutoDerefRef), - AutoObject(ty::TraitStore, - ty::BuiltinBounds, - ast::DefId, /* Trait ID */ - subst::Substs /* Trait substitutions */) + AutoDerefRef(AutoDerefRef) } -#[deriving(Clone, Decodable, Encodable)] +#[deriving(Clone, PartialEq)] +pub enum UnsizeKind { + // [T, ..n] -> [T], the uint field is n. + UnsizeLength(uint), + // An unsize coercion applied to the tail field of a struct. + // The uint is the index of the type parameter which is unsized. + UnsizeStruct(Box<UnsizeKind>, uint), + UnsizeVtable(ty::ExistentialBounds, + ast::DefId, /* Trait ID */ + subst::Substs /* Trait substitutions */) +} + +#[deriving(Clone)] pub struct AutoDerefRef { pub autoderefs: uint, pub autoref: Option<AutoRef> } -#[deriving(Clone, Decodable, Encodable, PartialEq, Show)] +#[deriving(Clone, PartialEq)] pub enum AutoRef { /// Convert from T to &T - AutoPtr(Region, ast::Mutability), + /// The third field allows us to wrap other AutoRef adjustments. + AutoPtr(Region, ast::Mutability, Option<Box<AutoRef>>), - /// Convert from ~[]/&[] to &[] or str - AutoBorrowVec(Region, ast::Mutability), + /// Convert [T, ..n] to [T] (or similar, depending on the kind) + AutoUnsize(UnsizeKind), - /// Convert from ~[]/&[] to &&[] or str - AutoBorrowVecRef(Region, ast::Mutability), + /// Convert Box<[T, ..n]> to Box<[T]> or something similar in a Box. + /// With DST and Box a library type, this should be replaced by UnsizeStruct. + AutoUnsizeUniq(UnsizeKind), /// Convert from T to *T + /// Value to thin pointer AutoUnsafe(ast::Mutability), +} + +// Ugly little helper function. The first bool in the returned tuple is true if +// there is an 'unsize to trait object' adjustment at the bottom of the +// adjustment. If that is surrounded by an AutoPtr, then we also return the +// region of the AutoPtr (in the third argument). The second bool is true if the +// adjustment is unique. +fn autoref_object_region(autoref: &AutoRef) -> (bool, bool, Option<Region>) { + fn unsize_kind_is_object(k: &UnsizeKind) -> bool { + match k { + &UnsizeVtable(..) => true, + &UnsizeStruct(box ref k, _) => unsize_kind_is_object(k), + _ => false + } + } + + match autoref { + &AutoUnsize(ref k) => (unsize_kind_is_object(k), false, None), + &AutoUnsizeUniq(ref k) => (unsize_kind_is_object(k), true, None), + &AutoPtr(adj_r, _, Some(box ref autoref)) => { + let (b, u, r) = autoref_object_region(autoref); + if r.is_some() || u { + (b, u, r) + } else { + (b, u, Some(adj_r)) + } + } + _ => (false, false, None) + } +} + +// If the adjustment introduces a borrowed reference to a trait object, then +// returns the region of the borrowed reference. +pub fn adjusted_object_region(adj: &AutoAdjustment) -> Option<Region> { + match adj { + &AutoDerefRef(AutoDerefRef{autoref: Some(ref autoref), ..}) => { + let (b, _, r) = autoref_object_region(autoref); + if b { + r + } else { + None + } + } + _ => None + } +} + +// Returns true if there is a trait cast at the bottom of the adjustment. +pub fn adjust_is_object(adj: &AutoAdjustment) -> bool { + match adj { + &AutoDerefRef(AutoDerefRef{autoref: Some(ref autoref), ..}) => { + let (b, _, _) = autoref_object_region(autoref); + b + } + _ => false + } +} + +// If possible, returns the type expected from the given adjustment. This is not +// possible if the adjustment depends on the type of the adjusted expression. +pub fn type_of_adjust(cx: &ctxt, adj: &AutoAdjustment) -> Option<t> { + fn type_of_autoref(cx: &ctxt, autoref: &AutoRef) -> Option<t> { + match autoref { + &AutoUnsize(ref k) => match k { + &UnsizeVtable(bounds, def_id, ref substs) => { + Some(mk_trait(cx, def_id, substs.clone(), bounds)) + } + _ => None + }, + &AutoUnsizeUniq(ref k) => match k { + &UnsizeVtable(bounds, def_id, ref substs) => { + Some(mk_uniq(cx, mk_trait(cx, def_id, substs.clone(), bounds))) + } + _ => None + }, + &AutoPtr(r, m, Some(box ref autoref)) => { + match type_of_autoref(cx, autoref) { + Some(t) => Some(mk_rptr(cx, r, mt {mutbl: m, ty: t})), + None => None + } + } + _ => None + } + } - /// Convert from Box<Trait>/&Trait to &Trait - AutoBorrowObj(Region, ast::Mutability), + match adj { + &AutoDerefRef(AutoDerefRef{autoref: Some(ref autoref), ..}) => { + type_of_autoref(cx, autoref) + } + _ => None + } } + + /// A restriction that certain types must be the same size. The use of /// `transmute` gives rise to these restrictions. pub struct TransmuteRestriction { @@ -302,6 +404,8 @@ pub struct TransmuteRestriction { pub from: t, /// The type being transmuted to. pub to: t, + /// NodeIf of the transmute intrinsic. + pub id: ast::NodeId, } /// The data structure to keep track of all the information that typechecker @@ -360,7 +464,6 @@ pub struct ctxt { pub lang_items: middle::lang_items::LanguageItems, /// A mapping of fake provided method def_ids to the default implementation pub provided_method_sources: RefCell<DefIdMap<ast::DefId>>, - pub supertraits: RefCell<DefIdMap<Rc<Vec<Rc<TraitRef>>>>>, pub superstructs: RefCell<DefIdMap<Option<ast::DefId>>>, pub struct_fields: RefCell<DefIdMap<Rc<Vec<field_ty>>>>, @@ -481,7 +584,7 @@ pub struct t { inner: *const t_opaque } impl fmt::Show for t { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - "*t_opaque".fmt(f) + write!(f, "{}", get(*self)) } } @@ -516,7 +619,7 @@ pub struct ClosureTy { pub fn_style: ast::FnStyle, pub onceness: ast::Onceness, pub store: TraitStore, - pub bounds: BuiltinBounds, + pub bounds: ExistentialBounds, pub sig: FnSig, pub abi: abi::Abi, } @@ -790,11 +893,18 @@ pub enum sty { ty_int(ast::IntTy), ty_uint(ast::UintTy), ty_float(ast::FloatTy), + /// Substs here, possibly against intuition, *may* contain `ty_param`s. + /// That is, even after substitution it is possible that there are type + /// variables. This happens when the `ty_enum` corresponds to an enum + /// definition and not a concerete use of it. To get the correct `ty_enum` + /// from the tcx, use the `NodeId` from the `ast::Ty` and look it up in + /// the `ast_ty_to_ty_cache`. This is probably true for `ty_struct` as + /// well.` ty_enum(DefId, Substs), ty_box(t), ty_uniq(t), ty_str, - ty_vec(mt, Option<uint>), // Second field is length. + ty_vec(t, Option<uint>), // Second field is length. ty_ptr(mt), ty_rptr(Region, mt), ty_bare_fn(BareFnTy), @@ -805,6 +915,12 @@ pub enum sty { ty_tup(Vec<t>), ty_param(ParamTy), // type parameter + ty_open(t), // A deref'ed fat pointer, i.e., a dynamically sized value + // and its size. Only ever used in trans. It is not necessary + // earlier since we don't need to distinguish a DST with its + // size (e.g., in a deref) vs a DST with the size elsewhere ( + // e.g., in a field). + ty_infer(InferTy), // something used only during inference/typeck ty_err, // Also only used during inference/typeck, to represent // the type of an erroneous expression (helps cut down @@ -815,7 +931,7 @@ pub enum sty { pub struct TyTrait { pub def_id: DefId, pub substs: Substs, - pub bounds: BuiltinBounds + pub bounds: ExistentialBounds } #[deriving(PartialEq, Eq, Hash, Show)] @@ -878,18 +994,30 @@ pub enum type_err { terr_variadic_mismatch(expected_found<bool>) } -#[deriving(PartialEq, Eq, Hash, Show)] +/// Bounds suitable for a named type parameter like `A` in `fn foo<A>` +/// as well as the existential type parameter in an object type. +#[deriving(PartialEq, Eq, Hash, Clone, Show)] pub struct ParamBounds { + pub opt_region_bound: Option<ty::Region>, pub builtin_bounds: BuiltinBounds, pub trait_bounds: Vec<Rc<TraitRef>> } +/// Bounds suitable for an existentially quantified type parameter +/// such as those that appear in object types or closure types. The +/// major difference between this case and `ParamBounds` is that +/// general purpose trait bounds are omitted. +#[deriving(PartialEq, Eq, Hash, Clone, Show)] +pub struct ExistentialBounds { + pub region_bound: ty::Region, + pub builtin_bounds: BuiltinBounds +} + pub type BuiltinBounds = EnumSet<BuiltinBound>; #[deriving(Clone, Encodable, PartialEq, Eq, Decodable, Hash, Show)] #[repr(uint)] pub enum BuiltinBound { - BoundStatic, BoundSend, BoundSized, BoundCopy, @@ -902,13 +1030,21 @@ pub fn empty_builtin_bounds() -> BuiltinBounds { pub fn all_builtin_bounds() -> BuiltinBounds { let mut set = EnumSet::empty(); - set.add(BoundStatic); set.add(BoundSend); set.add(BoundSized); set.add(BoundSync); set } +pub fn region_existential_bound(r: ty::Region) -> ExistentialBounds { + /*! + * An existential bound that does not implement any traits. + */ + + ty::ExistentialBounds { region_bound: r, + builtin_bounds: empty_builtin_bounds() } +} + impl CLike for BuiltinBound { fn to_uint(&self) -> uint { *self as uint @@ -1024,8 +1160,8 @@ pub struct TypeParameterDef { pub def_id: ast::DefId, pub space: subst::ParamSpace, pub index: uint, - pub bounds: Rc<ParamBounds>, - pub default: Option<ty::t> + pub bounds: ParamBounds, + pub default: Option<ty::t>, } #[deriving(Encodable, Decodable, Clone, Show)] @@ -1034,6 +1170,7 @@ pub struct RegionParameterDef { pub def_id: ast::DefId, pub space: subst::ParamSpace, pub index: uint, + pub bounds: Vec<ty::Region>, } /// Information about the type/lifetime parameters associated with an @@ -1081,6 +1218,12 @@ pub struct ParameterEnvironment { /// Bounds on the various type parameters pub bounds: VecPerParamSpace<ParamBounds>, + + /// Each type parameter has an implicit region bound that + /// indicates it must outlive at least the function body (the user + /// may specify stronger requirements). This field indicates the + /// region of the callee. + pub implicit_region_bound: ty::Region, } impl ParameterEnvironment { @@ -1175,7 +1318,7 @@ pub struct Polytype { /// As `Polytype` but for a trait ref. pub struct TraitDef { pub generics: Generics, - pub bounds: BuiltinBounds, + pub bounds: ParamBounds, pub trait_ref: Rc<ty::TraitRef>, } @@ -1205,6 +1348,24 @@ pub enum UnboxedClosureKind { FnOnceUnboxedClosureKind, } +impl UnboxedClosureKind { + pub fn trait_did(&self, cx: &ctxt) -> ast::DefId { + let result = match *self { + FnUnboxedClosureKind => cx.lang_items.require(FnTraitLangItem), + FnMutUnboxedClosureKind => { + cx.lang_items.require(FnMutTraitLangItem) + } + FnOnceUnboxedClosureKind => { + cx.lang_items.require(FnOnceTraitLangItem) + } + }; + match result { + Ok(trait_did) => trait_did, + Err(err) => cx.sess.fatal(err.as_slice()), + } + } +} + pub fn mk_ctxt(s: Session, dm: resolve::DefMap, named_region_map: resolve_lifetime::NamedRegionMap, @@ -1247,7 +1408,6 @@ pub fn mk_ctxt(s: Session, normalized_cache: RefCell::new(HashMap::new()), lang_items: lang_items, provided_method_sources: RefCell::new(DefIdMap::new()), - supertraits: RefCell::new(DefIdMap::new()), superstructs: RefCell::new(DefIdMap::new()), struct_fields: RefCell::new(DefIdMap::new()), destructor_for_type: RefCell::new(DefIdMap::new()), @@ -1324,6 +1484,9 @@ pub fn mk_t(cx: &ctxt, st: sty) -> t { } return f; } + fn flags_for_bounds(bounds: &ExistentialBounds) -> uint { + rflags(bounds.region_bound) + } match &st { &ty_nil | &ty_bool | &ty_char | &ty_int(_) | &ty_float(_) | &ty_uint(_) | &ty_str => {} @@ -1348,13 +1511,14 @@ pub fn mk_t(cx: &ctxt, st: sty) -> t { &ty_enum(_, ref substs) | &ty_struct(_, ref substs) => { flags |= sflags(substs); } - &ty_trait(box ty::TyTrait { ref substs, .. }) => { + &ty_trait(box ty::TyTrait { ref substs, ref bounds, .. }) => { flags |= sflags(substs); + flags |= flags_for_bounds(bounds); } - &ty_box(tt) | &ty_uniq(tt) => { + &ty_box(tt) | &ty_uniq(tt) | &ty_vec(tt, _) | &ty_open(tt) => { flags |= get(tt).flags } - &ty_ptr(ref m) | &ty_vec(ref m, _) => { + &ty_ptr(ref m) => { flags |= get(m.ty).flags; } &ty_rptr(r, ref m) => { @@ -1379,6 +1543,7 @@ pub fn mk_t(cx: &ctxt, st: sty) -> t { flags |= get(f.sig.output).flags; // T -> _|_ is *not* _|_ ! flags &= !(has_ty_bot as uint); + flags |= flags_for_bounds(&f.bounds); } } @@ -1532,14 +1697,14 @@ pub fn mk_nil_ptr(cx: &ctxt) -> t { mk_ptr(cx, mt {ty: mk_nil(), mutbl: ast::MutImmutable}) } -pub fn mk_vec(cx: &ctxt, tm: mt, sz: Option<uint>) -> t { - mk_t(cx, ty_vec(tm, sz)) +pub fn mk_vec(cx: &ctxt, t: t, sz: Option<uint>) -> t { + mk_t(cx, ty_vec(t, sz)) } pub fn mk_slice(cx: &ctxt, r: Region, tm: mt) -> t { mk_rptr(cx, r, mt { - ty: mk_vec(cx, tm, None), + ty: mk_vec(cx, tm.ty, None), mutbl: tm.mutbl }) } @@ -1576,8 +1741,8 @@ pub fn mk_ctor_fn(cx: &ctxt, pub fn mk_trait(cx: &ctxt, did: ast::DefId, substs: Substs, - bounds: BuiltinBounds) - -> t { + bounds: ExistentialBounds) + -> t { // take a copy of substs so that we own the vectors inside let inner = box TyTrait { def_id: did, @@ -1617,6 +1782,8 @@ pub fn mk_param_from_def(cx: &ctxt, def: &TypeParameterDef) -> t { mk_param(cx, def.space, def.index, def.def_id) } +pub fn mk_open(cx: &ctxt, t: t) -> t { mk_t(cx, ty_open(t)) } + pub fn walk_ty(ty: t, f: |t|) { maybe_walk_ty(ty, |t| { f(t); true }); } @@ -1627,10 +1794,9 @@ pub fn maybe_walk_ty(ty: t, f: |t| -> bool) { } match get(ty).sty { ty_nil | ty_bot | ty_bool | ty_char | ty_int(_) | ty_uint(_) | ty_float(_) | - ty_str | ty_infer(_) | ty_param(_) | ty_unboxed_closure(..) | - ty_err => {} - ty_box(ty) | ty_uniq(ty) => maybe_walk_ty(ty, f), - ty_ptr(ref tm) | ty_rptr(_, ref tm) | ty_vec(ref tm, _) => { + ty_str | ty_infer(_) | ty_param(_) | ty_unboxed_closure(_, _) | ty_err => {} + ty_box(ty) | ty_uniq(ty) | ty_vec(ty, _) | ty_open(ty) => maybe_walk_ty(ty, f), + ty_ptr(ref tm) | ty_rptr(_, ref tm) => { maybe_walk_ty(tm.ty, f); } ty_enum(_, ref substs) | ty_struct(_, ref substs) | @@ -1664,6 +1830,27 @@ pub fn walk_regions_and_ty(cx: &ctxt, ty: t, fldr: |r: Region|, fldt: |t: t|) |t| { fldt(t); t }).fold_ty(ty) } +impl ParamTy { + pub fn new(space: subst::ParamSpace, + index: uint, + def_id: ast::DefId) + -> ParamTy { + ParamTy { space: space, idx: index, def_id: def_id } + } + + pub fn for_self(trait_def_id: ast::DefId) -> ParamTy { + ParamTy::new(subst::SelfSpace, 0, trait_def_id) + } + + pub fn for_def(def: &TypeParameterDef) -> ParamTy { + ParamTy::new(def.space, def.index, def.def_id) + } + + pub fn to_ty(self, tcx: &ty::ctxt) -> ty::t { + ty::mk_param(tcx, self.space, self.idx, self.def_id) + } +} + impl ItemSubsts { pub fn empty() -> ItemSubsts { ItemSubsts { substs: Substs::empty() } @@ -1749,14 +1936,11 @@ pub fn type_is_simd(cx: &ctxt, ty: t) -> bool { pub fn sequence_element_type(cx: &ctxt, ty: t) -> t { match get(ty).sty { - ty_vec(mt, _) => mt.ty, - ty_ptr(mt{ty: t, ..}) | ty_rptr(_, mt{ty: t, ..}) | - ty_box(t) | ty_uniq(t) => match get(t).sty { - ty_vec(mt, None) => mt.ty, - ty_str => mk_mach_uint(ast::TyU8), - _ => cx.sess.bug("sequence_element_type called on non-sequence value"), - }, - _ => cx.sess.bug("sequence_element_type called on non-sequence value"), + ty_vec(ty, _) => ty, + ty_str => mk_mach_uint(ast::TyU8), + ty_open(ty) => sequence_element_type(cx, ty), + _ => cx.sess.bug(format!("sequence_element_type called on non-sequence value: {}", + ty_to_string(cx, ty)).as_slice()), } } @@ -1789,12 +1973,7 @@ pub fn type_is_boxed(ty: t) -> bool { pub fn type_is_region_ptr(ty: t) -> bool { match get(ty).sty { - ty_rptr(_, mt) => match get(mt.ty).sty { - // FIXME(nrc, DST) slices weren't regarded as rptrs, so we preserve this - // odd behaviour for now. (But ~[] were unique. I have no idea why). - ty_vec(_, None) | ty_str | ty_trait(..) => false, - _ => true - }, + ty_rptr(..) => true, _ => false } } @@ -1816,6 +1995,13 @@ pub fn type_is_unique(ty: t) -> bool { } } +pub fn type_is_fat_ptr(cx: &ctxt, ty: t) -> bool { + match get(ty).sty { + ty_rptr(_, mt{ty, ..}) | ty_uniq(ty) if !type_is_sized(cx, ty) => true, + _ => false, + } +} + /* A scalar type is one that denotes an atomic datum, with no sub-components. (A ty_ptr is scalar because it represents a non-managed pointer, so its @@ -1962,7 +2148,8 @@ def_type_content_sets!( // ReachesManaged /* see [1] below */ = 0b0000_0100__0000_0000__0000, ReachesMutable = 0b0000_1000__0000_0000__0000, ReachesNoSync = 0b0001_0000__0000_0000__0000, - ReachesAll = 0b0001_1111__0000_0000__0000, + ReachesFfiUnsafe = 0b0010_0000__0000_0000__0000, + ReachesAll = 0b0011_1111__0000_0000__0000, // Things that cause values to *move* rather than *copy* Moves = 0b0000_0000__0000_1011__0000, @@ -1979,9 +2166,6 @@ def_type_content_sets!( // that it neither reaches nor owns a managed pointer. Nonsendable = 0b0000_0111__0000_0100__0000, - // Things that prevent values from being considered 'static - Nonstatic = 0b0000_0010__0000_0000__0000, - // Things that prevent values from being considered sized Nonsized = 0b0000_0000__0000_0000__0001, @@ -2006,9 +2190,8 @@ def_type_content_sets!( ) impl TypeContents { - pub fn meets_bound(&self, cx: &ctxt, bb: BuiltinBound) -> bool { + pub fn meets_builtin_bound(&self, cx: &ctxt, bb: BuiltinBound) -> bool { match bb { - BoundStatic => self.is_static(cx), BoundSend => self.is_sendable(cx), BoundSized => self.is_sized(cx), BoundCopy => self.is_copy(cx), @@ -2024,10 +2207,6 @@ impl TypeContents { (self.bits & tc.bits) != 0 } - pub fn is_static(&self, _: &ctxt) -> bool { - !self.intersects(TC::Nonstatic) - } - pub fn is_sendable(&self, _: &ctxt) -> bool { !self.intersects(TC::Nonsendable) } @@ -2136,10 +2315,6 @@ impl fmt::Show for TypeContents { } } -pub fn type_is_static(cx: &ctxt, t: ty::t) -> bool { - type_contents(cx, t).is_static(cx) -} - pub fn type_is_sendable(cx: &ctxt, t: ty::t) -> bool { type_contents(cx, t).is_sendable(cx) } @@ -2199,29 +2374,34 @@ pub fn type_contents(cx: &ctxt, ty: t) -> TypeContents { cache.insert(ty_id, TC::None); let result = match get(ty).sty { + // uint and int are ffi-unsafe + ty_uint(ast::TyU) | ty_int(ast::TyI) => { + TC::ReachesFfiUnsafe + } + // Scalar and unique types are sendable, and durable ty_nil | ty_bot | ty_bool | ty_int(_) | ty_uint(_) | ty_float(_) | - ty_bare_fn(_) | ty::ty_char | ty_str => { + ty_bare_fn(_) | ty::ty_char => { TC::None } ty_closure(ref c) => { - closure_contents(cx, &**c) + closure_contents(cx, &**c) | TC::ReachesFfiUnsafe } ty_box(typ) => { - tc_ty(cx, typ, cache).managed_pointer() + tc_ty(cx, typ, cache).managed_pointer() | TC::ReachesFfiUnsafe } ty_uniq(typ) => { - match get(typ).sty { + TC::ReachesFfiUnsafe | match get(typ).sty { ty_str => TC::OwnsOwned, _ => tc_ty(cx, typ, cache).owned_pointer(), } } ty_trait(box ty::TyTrait { bounds, .. }) => { - object_contents(cx, bounds) + object_contents(cx, bounds) | TC::ReachesFfiUnsafe } ty_ptr(ref mt) => { @@ -2229,21 +2409,32 @@ pub fn type_contents(cx: &ctxt, ty: t) -> TypeContents { } ty_rptr(r, ref mt) => { - match get(mt.ty).sty { + TC::ReachesFfiUnsafe | match get(mt.ty).sty { ty_str => borrowed_contents(r, ast::MutImmutable), + ty_vec(..) => tc_ty(cx, mt.ty, cache).reference(borrowed_contents(r, mt.mutbl)), _ => tc_ty(cx, mt.ty, cache).reference(borrowed_contents(r, mt.mutbl)), } } - ty_vec(mt, _) => { - tc_mt(cx, mt, cache) + ty_vec(t, Some(_)) => { + tc_ty(cx, t, cache) } + ty_vec(t, None) => { + tc_ty(cx, t, cache) | TC::Nonsized + } + ty_str => TC::Nonsized, + ty_struct(did, ref substs) => { let flds = struct_fields(cx, did, substs); let mut res = TypeContents::union(flds.as_slice(), |f| tc_mt(cx, f.mt, cache)); + + if !lookup_repr_hints(cx, did).contains(&attr::ReprExtern) { + res = res | TC::ReachesFfiUnsafe; + } + if ty::has_dtor(cx, did) { res = res | TC::OwnsDtor; } @@ -2273,9 +2464,49 @@ pub fn type_contents(cx: &ctxt, ty: t) -> TypeContents { tc_ty(cx, *arg_ty, cache) }) }); + if ty::has_dtor(cx, did) { res = res | TC::OwnsDtor; } + + if variants.len() != 0 { + let repr_hints = lookup_repr_hints(cx, did); + if repr_hints.len() > 1 { + // this is an error later on, but this type isn't safe + res = res | TC::ReachesFfiUnsafe; + } + + match repr_hints.as_slice().get(0) { + Some(h) => if !h.is_ffi_safe() { + res = res | TC::ReachesFfiUnsafe; + }, + // ReprAny + None => { + res = res | TC::ReachesFfiUnsafe; + + // We allow ReprAny enums if they are eligible for + // the nullable pointer optimization and the + // contained type is an `extern fn` + + if variants.len() == 2 { + let mut data_idx = 0; + + if variants.get(0).args.len() == 0 { + data_idx = 1; + } + + if variants.get(data_idx).args.len() == 1 { + match get(*variants.get(data_idx).args.get(0)).sty { + ty_bare_fn(..) => { res = res - TC::ReachesFfiUnsafe; } + _ => { } + } + } + } + } + } + } + + apply_lang_items(cx, did, res) } @@ -2290,9 +2521,10 @@ pub fn type_contents(cx: &ctxt, ty: t) -> TypeContents { let ty_param_defs = cx.ty_param_defs.borrow(); let tp_def = ty_param_defs.get(&p.def_id.node); - kind_bounds_to_contents(cx, - tp_def.bounds.builtin_bounds, - tp_def.bounds.trait_bounds.as_slice()) + kind_bounds_to_contents( + cx, + tp_def.bounds.builtin_bounds, + tp_def.bounds.trait_bounds.as_slice()) } ty_infer(_) => { @@ -2301,6 +2533,12 @@ pub fn type_contents(cx: &ctxt, ty: t) -> TypeContents { TC::All } + ty_open(t) => { + let result = tc_ty(cx, t, cache); + assert!(!result.is_sized(cx)) + result.unsafe_pointer() | TC::Nonsized + } + ty_err => { cx.sess.bug("asked to compute contents of error type"); } @@ -2379,10 +2617,10 @@ pub fn type_contents(cx: &ctxt, ty: t) -> TypeContents { } fn object_contents(cx: &ctxt, - bounds: BuiltinBounds) + bounds: ExistentialBounds) -> TypeContents { // These are the type contents of the (opaque) interior - kind_bounds_to_contents(cx, bounds, []) + kind_bounds_to_contents(cx, bounds.builtin_bounds, []) } fn kind_bounds_to_contents(cx: &ctxt, @@ -2393,7 +2631,6 @@ pub fn type_contents(cx: &ctxt, ty: t) -> TypeContents { let mut tc = TC::All; each_inherited_builtin_bound(cx, bounds, traits, |bound| { tc = tc - match bound { - BoundStatic => TC::Nonstatic, BoundSend => TC::Nonsendable, BoundSized => TC::Nonsized, BoundCopy => TC::Noncopy, @@ -2414,7 +2651,7 @@ pub fn type_contents(cx: &ctxt, ty: t) -> TypeContents { each_bound_trait_and_supertraits(cx, traits, |trait_ref| { let trait_def = lookup_trait_def(cx, trait_ref.def_id); - for bound in trait_def.bounds.iter() { + for bound in trait_def.bounds.builtin_bounds.iter() { f(bound); } true @@ -2427,6 +2664,10 @@ pub fn type_moves_by_default(cx: &ctxt, ty: t) -> bool { type_contents(cx, ty).moves_by_default(cx) } +pub fn is_ffi_safe(cx: &ctxt, ty: t) -> bool { + !type_contents(cx, ty).intersects(TC::ReachesFfiUnsafe) +} + // True if instantiating an instance of `r_ty` requires an instance of `r_ty`. pub fn is_instantiable(cx: &ctxt, r_ty: t) -> bool { fn type_requires(cx: &ctxt, seen: &mut Vec<DefId>, @@ -2458,7 +2699,7 @@ pub fn is_instantiable(cx: &ctxt, r_ty: t) -> bool { // normal vectors, since they don't necessarily have the // possibility to have length zero. ty_vec(_, Some(0)) => false, // don't need no contents - ty_vec(mt, Some(_)) => type_requires(cx, seen, r_ty, mt.ty), + ty_vec(ty, Some(_)) => type_requires(cx, seen, r_ty, ty), ty_nil | ty_bot | @@ -2476,7 +2717,7 @@ pub fn is_instantiable(cx: &ctxt, r_ty: t) -> bool { ty_vec(_, None) => { false } - ty_box(typ) | ty_uniq(typ) => { + ty_box(typ) | ty_uniq(typ) | ty_open(typ) => { type_requires(cx, seen, r_ty, typ) } ty_rptr(_, ref mt) => { @@ -2599,8 +2840,8 @@ pub fn is_type_representable(cx: &ctxt, sp: Span, ty: t) -> Representability { } // Fixed-length vectors. // FIXME(#11924) Behavior undecided for zero-length vectors. - ty_vec(mt, Some(_)) => { - type_structurally_recursive(cx, sp, seen, mt.ty) + ty_vec(ty, Some(_)) => { + type_structurally_recursive(cx, sp, seen, ty) } // Push struct and enum def-ids onto `seen` before recursing. @@ -2719,11 +2960,40 @@ pub fn type_is_machine(ty: t) -> bool { } // Is the type's representation size known at compile time? -#[allow(dead_code)] // leaving in for DST -pub fn type_is_sized(cx: &ctxt, ty: ty::t) -> bool { +pub fn type_is_sized(cx: &ctxt, ty: t) -> bool { type_contents(cx, ty).is_sized(cx) } +pub fn lltype_is_sized(cx: &ctxt, ty: t) -> bool { + match get(ty).sty { + ty_open(_) => true, + _ => type_contents(cx, ty).is_sized(cx) + } +} + +// Return the smallest part of t which is unsized. Fails if t is sized. +// 'Smallest' here means component of the static representation of the type; not +// the size of an object at runtime. +pub fn unsized_part_of_type(cx: &ctxt, ty: t) -> t { + match get(ty).sty { + ty_str | ty_trait(..) | ty_vec(..) => ty, + ty_struct(_, ref substs) => { + // Exactly one of the type parameters must be unsized. + for tp in substs.types.get_slice(subst::TypeSpace).iter() { + if !type_is_sized(cx, *tp) { + return unsized_part_of_type(cx, *tp); + } + } + fail!("Unsized struct type with no unsized type params? {}", ty_to_string(cx, ty)); + } + _ => { + assert!(type_is_sized(cx, ty), + "unsized_part_of_type failed even though ty is unsized"); + fail!("called unsized_part_of_type with sized ty"); + } + } +} + // Whether a type is enum like, that is an enum type with only nullary // constructors pub fn type_is_c_like_enum(cx: &ctxt, ty: t) -> bool { @@ -2746,33 +3016,57 @@ pub fn type_is_c_like_enum(cx: &ctxt, ty: t) -> bool { // Some types---notably unsafe ptrs---can only be dereferenced explicitly. pub fn deref(t: t, explicit: bool) -> Option<mt> { match get(t).sty { - ty_box(typ) | ty_uniq(typ) => match get(typ).sty { - // Don't deref ~[] etc., might need to generalise this to all DST. - ty_vec(_, None) | ty_str | ty_trait(..) => None, - _ => Some(mt { - ty: typ, + ty_box(ty) | ty_uniq(ty) => { + Some(mt { + ty: ty, mutbl: ast::MutImmutable, - }), - }, - ty_rptr(_, mt) => match get(mt.ty).sty { - // Don't deref &[], might need to generalise this to all DST. - ty_vec(_, None) | ty_str | ty_trait(..) => None, - _ => Some(mt), + }) }, + ty_rptr(_, mt) => Some(mt), ty_ptr(mt) if explicit => Some(mt), _ => None } } -// Returns the type of t[i] -pub fn index(t: t) -> Option<mt> { +pub fn deref_or_dont(t: t) -> t { match get(t).sty { - ty_vec(mt, Some(_)) => Some(mt), - ty_ptr(mt{ty: t, ..}) | ty_rptr(_, mt{ty: t, ..}) | - ty_box(t) | ty_uniq(t) => match get(t).sty { - ty_vec(mt, None) => Some(mt), - _ => None, + ty_box(ty) | ty_uniq(ty) => { + ty }, + ty_rptr(_, mt) | ty_ptr(mt) => mt.ty, + _ => t + } +} + +pub fn close_type(cx: &ctxt, t: t) -> t { + match get(t).sty { + ty_open(t) => mk_rptr(cx, ReStatic, mt {ty: t, mutbl:ast::MutImmutable}), + _ => cx.sess.bug(format!("Trying to close a non-open type {}", + ty_to_string(cx, t)).as_slice()) + } +} + +pub fn type_content(t: t) -> t { + match get(t).sty { + ty_box(ty) | ty_uniq(ty) => ty, + ty_rptr(_, mt) |ty_ptr(mt) => mt.ty, + _ => t + } + +} + +// Extract the unsized type in an open type (or just return t if it is not open). +pub fn unopen_type(t: t) -> t { + match get(t).sty { + ty_open(t) => t, + _ => t + } +} + +// Returns the type of t[i] +pub fn index(ty: t) -> Option<t> { + match get(ty).sty { + ty_vec(t, _) => Some(t), _ => None } } @@ -2780,15 +3074,10 @@ pub fn index(t: t) -> Option<mt> { // Returns the type of elements contained within an 'array-like' type. // This is exactly the same as the above, except it supports strings, // which can't actually be indexed. -pub fn array_element_ty(t: t) -> Option<mt> { +pub fn array_element_ty(t: t) -> Option<t> { match get(t).sty { - ty_vec(mt, Some(_)) => Some(mt), - ty_ptr(mt{ty: t, ..}) | ty_rptr(_, mt{ty: t, ..}) | - ty_box(t) | ty_uniq(t) => match get(t).sty { - ty_vec(mt, None) => Some(mt), - ty_str => Some(mt {ty: mk_u8(), mutbl: ast::MutImmutable}), - _ => None, - }, + ty_vec(t, _) => Some(t), + ty_str => Some(mk_u8()), _ => None } } @@ -3022,16 +3311,19 @@ pub fn adjust_ty(cx: &ctxt, AutoAddEnv(store) => { match ty::get(unadjusted_ty).sty { ty::ty_bare_fn(ref b) => { + let bounds = ty::ExistentialBounds { + region_bound: ReStatic, + builtin_bounds: all_builtin_bounds(), + }; + ty::mk_closure( cx, - ty::ClosureTy { - fn_style: b.fn_style, - onceness: ast::Many, - store: store, - bounds: ty::all_builtin_bounds(), - sig: b.sig.clone(), - abi: b.abi, - }) + ty::ClosureTy {fn_style: b.fn_style, + onceness: ast::Many, + store: store, + bounds: bounds, + sig: b.sig.clone(), + abi: b.abi}) } ref b => { cx.sess.bug( @@ -3071,56 +3363,7 @@ pub fn adjust_ty(cx: &ctxt, match adj.autoref { None => adjusted_ty, - Some(ref autoref) => { - match *autoref { - AutoPtr(r, m) => { - mk_rptr(cx, r, mt { - ty: adjusted_ty, - mutbl: m - }) - } - - AutoBorrowVec(r, m) => { - borrow_vec(cx, span, r, m, adjusted_ty) - } - - AutoBorrowVecRef(r, m) => { - adjusted_ty = borrow_vec(cx, - span, - r, - m, - adjusted_ty); - mk_rptr(cx, r, mt { - ty: adjusted_ty, - mutbl: ast::MutImmutable - }) - } - - AutoUnsafe(m) => { - mk_ptr(cx, mt {ty: adjusted_ty, mutbl: m}) - } - - AutoBorrowObj(r, m) => { - borrow_obj(cx, span, r, m, adjusted_ty) - } - } - } - } - } - - AutoObject(store, bounds, def_id, ref substs) => { - - let tr = mk_trait(cx, def_id, substs.clone(), bounds); - match store { - UniqTraitStore => { - mk_uniq(cx, tr) - } - RegionTraitStore(r, m) => { - mk_rptr(cx, r, mt { - ty: tr, - mutbl: m - }) - } + Some(ref autoref) => adjust_for_autoref(cx, span, adjusted_ty, autoref) } } } @@ -3128,57 +3371,63 @@ pub fn adjust_ty(cx: &ctxt, None => unadjusted_ty }; - fn borrow_vec(cx: &ctxt, - span: Span, - r: Region, - m: ast::Mutability, - ty: ty::t) -> ty::t { - match get(ty).sty { - ty_uniq(t) | ty_ptr(mt{ty: t, ..}) | - ty_rptr(_, mt{ty: t, ..}) => match get(t).sty { - ty::ty_vec(mt, None) => ty::mk_slice(cx, r, ty::mt {ty: mt.ty, mutbl: m}), - ty::ty_str => ty::mk_str_slice(cx, r, m), - _ => { - cx.sess.span_bug( - span, - format!("borrow-vec associated with bad sty: {:?}", - get(ty).sty).as_slice()); - } - }, - ty_vec(mt, Some(_)) => ty::mk_slice(cx, r, ty::mt {ty: mt.ty, mutbl: m}), + fn adjust_for_autoref(cx: &ctxt, + span: Span, + ty: ty::t, + autoref: &AutoRef) -> ty::t{ + match *autoref { + AutoPtr(r, m, ref a) => { + let adjusted_ty = match a { + &Some(box ref a) => adjust_for_autoref(cx, span, ty, a), + &None => ty + }; + mk_rptr(cx, r, mt { + ty: adjusted_ty, + mutbl: m + }) + } - ref s => { - cx.sess.span_bug( - span, - format!("borrow-vec associated with bad sty: {:?}", - s).as_slice()); + AutoUnsafe(m) => { + mk_ptr(cx, mt {ty: ty, mutbl: m}) } + + AutoUnsize(ref k) => unsize_ty(cx, ty, k, span), + AutoUnsizeUniq(ref k) => ty::mk_uniq(cx, unsize_ty(cx, ty, k, span)), } } +} - fn borrow_obj(cx: &ctxt, span: Span, r: Region, - m: ast::Mutability, ty: ty::t) -> ty::t { - match get(ty).sty { - ty_uniq(t) | ty_rptr(_, mt{ty: t, ..}) => match get(t).sty { - ty_trait(box ty::TyTrait {def_id, ref substs, bounds, .. }) => { - mk_rptr(cx, r, mt { - ty: ty::mk_trait(cx, def_id, substs.clone(), bounds), - mutbl: m - }) - } - _ => { - cx.sess.span_bug( - span, - format!("borrow-trait-obj associated with bad sty: {:?}", - get(ty).sty).as_slice()); - } - }, - ref s => { - cx.sess.span_bug( - span, - format!("borrow-trait-obj associated with bad sty: {:?}", - s).as_slice()); +// Take a sized type and a sizing adjustment and produce an unsized version of +// the type. +pub fn unsize_ty(cx: &ctxt, + ty: ty::t, + kind: &UnsizeKind, + span: Span) + -> ty::t { + match kind { + &UnsizeLength(len) => match get(ty).sty { + ty_vec(t, Some(n)) => { + assert!(len == n); + mk_vec(cx, t, None) } + _ => cx.sess.span_bug(span, + format!("UnsizeLength with bad sty: {}", + ty_to_string(cx, ty)).as_slice()) + }, + &UnsizeStruct(box ref k, tp_index) => match get(ty).sty { + ty_struct(did, ref substs) => { + let ty_substs = substs.types.get_slice(subst::TypeSpace); + let new_ty = unsize_ty(cx, ty_substs[tp_index], k, span); + let mut unsized_substs = substs.clone(); + unsized_substs.types.get_mut_slice(subst::TypeSpace)[tp_index] = new_ty; + mk_struct(cx, did, unsized_substs) + } + _ => cx.sess.span_bug(span, + format!("UnsizeStruct with bad sty: {}", + ty_to_string(cx, ty)).as_slice()) + }, + &UnsizeVtable(bounds, def_id, ref substs) => { + mk_trait(cx, def_id, substs.clone(), bounds) } } } @@ -3186,28 +3435,32 @@ pub fn adjust_ty(cx: &ctxt, impl AutoRef { pub fn map_region(&self, f: |Region| -> Region) -> AutoRef { match *self { - ty::AutoPtr(r, m) => ty::AutoPtr(f(r), m), - ty::AutoBorrowVec(r, m) => ty::AutoBorrowVec(f(r), m), - ty::AutoBorrowVecRef(r, m) => ty::AutoBorrowVecRef(f(r), m), + ty::AutoPtr(r, m, None) => ty::AutoPtr(f(r), m, None), + ty::AutoPtr(r, m, Some(ref a)) => ty::AutoPtr(f(r), m, Some(box a.map_region(f))), + ty::AutoUnsize(ref k) => ty::AutoUnsize(k.clone()), + ty::AutoUnsizeUniq(ref k) => ty::AutoUnsizeUniq(k.clone()), ty::AutoUnsafe(m) => ty::AutoUnsafe(m), - ty::AutoBorrowObj(r, m) => ty::AutoBorrowObj(f(r), m), } } } -pub fn method_call_type_param_defs(tcx: &ctxt, origin: typeck::MethodOrigin) - -> VecPerParamSpace<TypeParameterDef> { +pub fn method_call_type_param_defs<T>(typer: &T, + origin: typeck::MethodOrigin) + -> VecPerParamSpace<TypeParameterDef> + where T: mc::Typer { match origin { typeck::MethodStatic(did) => { - ty::lookup_item_type(tcx, did).generics.types.clone() - } - typeck::MethodStaticUnboxedClosure(_) => { - match tcx.lang_items.require(FnMutTraitLangItem) { - Ok(def_id) => { - lookup_trait_def(tcx, def_id).generics.types.clone() - } - Err(s) => tcx.sess.fatal(s.as_slice()), - } + ty::lookup_item_type(typer.tcx(), did).generics.types.clone() + } + typeck::MethodStaticUnboxedClosure(did) => { + let def_id = typer.unboxed_closures() + .borrow() + .find(&did) + .expect("method_call_type_param_defs: didn't \ + find unboxed closure") + .kind + .trait_did(typer.tcx()); + lookup_trait_def(typer.tcx(), def_id).generics.types.clone() } typeck::MethodParam(typeck::MethodParam{ trait_id: trt_id, @@ -3219,7 +3472,7 @@ pub fn method_call_type_param_defs(tcx: &ctxt, origin: typeck::MethodOrigin) method_num: n_mth, .. }) => { - match ty::trait_item(tcx, trt_id, n_mth) { + match ty::trait_item(typer.tcx(), trt_id, n_mth) { ty::MethodTraitItem(method) => method.generics.types.clone(), } } @@ -3337,8 +3590,6 @@ pub fn expr_kind(tcx: &ctxt, expr: &ast::Expr) -> ExprKind { ast::ExprUnboxedFn(..) | ast::ExprBlock(..) | ast::ExprRepeat(..) | - ast::ExprVstore(_, ast::ExprVstoreSlice) | - ast::ExprVstore(_, ast::ExprVstoreMutSlice) | ast::ExprVec(..) => { RvalueDpsExpr } @@ -3388,8 +3639,7 @@ pub fn expr_kind(tcx: &ctxt, expr: &ast::Expr) -> ExprKind { ast::ExprLit(_) | // Note: LitStr is carved out above ast::ExprUnary(..) | ast::ExprAddrOf(..) | - ast::ExprBinary(..) | - ast::ExprVstore(_, ast::ExprVstoreUniq) => { + ast::ExprBinary(..) => { RvalueDatumExpr } @@ -3494,6 +3744,7 @@ pub fn ty_sort_string(cx: &ctxt, t: t) -> String { } } ty_err => "type error".to_string(), + ty_open(_) => "opened DST".to_string(), } } @@ -3517,17 +3768,17 @@ pub fn type_err_to_str(cx: &ctxt, err: &type_err) -> String { match *err { terr_mismatch => "types differ".to_string(), terr_fn_style_mismatch(values) => { - format!("expected {} fn but found {} fn", + format!("expected {} fn, found {} fn", values.expected.to_string(), values.found.to_string()) } terr_abi_mismatch(values) => { - format!("expected {} fn but found {} fn", + format!("expected {} fn, found {} fn", values.expected.to_string(), values.found.to_string()) } terr_onceness_mismatch(values) => { - format!("expected {} fn but found {} fn", + format!("expected {} fn, found {} fn", values.expected.to_string(), values.found.to_string()) } @@ -3544,20 +3795,20 @@ pub fn type_err_to_str(cx: &ctxt, err: &type_err) -> String { terr_ptr_mutability => "pointers differ in mutability".to_string(), terr_ref_mutability => "references differ in mutability".to_string(), terr_ty_param_size(values) => { - format!("expected a type with {} type params \ - but found one with {} type params", + format!("expected a type with {} type params, \ + found one with {} type params", values.expected, values.found) } terr_tuple_size(values) => { - format!("expected a tuple with {} elements \ - but found one with {} elements", + format!("expected a tuple with {} elements, \ + found one with {} elements", values.expected, values.found) } terr_record_size(values) => { - format!("expected a record with {} fields \ - but found one with {} fields", + format!("expected a record with {} fields, \ + found one with {} fields", values.expected, values.found) } @@ -3565,7 +3816,7 @@ pub fn type_err_to_str(cx: &ctxt, err: &type_err) -> String { "record elements differ in mutability".to_string() } terr_record_fields(values) => { - format!("expected a record with field `{}` but found one \ + format!("expected a record with field `{}`, found one \ with field `{}`", token::get_ident(values.expected), token::get_ident(values.found)) @@ -3584,57 +3835,57 @@ pub fn type_err_to_str(cx: &ctxt, err: &type_err) -> String { } terr_regions_insufficiently_polymorphic(br, _) => { format!("expected bound lifetime parameter {}, \ - but found concrete lifetime", + found concrete lifetime", bound_region_ptr_to_string(cx, br)) } terr_regions_overly_polymorphic(br, _) => { format!("expected concrete lifetime, \ - but found bound lifetime parameter {}", + found bound lifetime parameter {}", bound_region_ptr_to_string(cx, br)) } terr_trait_stores_differ(_, ref values) => { - format!("trait storage differs: expected `{}` but found `{}`", + format!("trait storage differs: expected `{}`, found `{}`", trait_store_to_string(cx, (*values).expected), trait_store_to_string(cx, (*values).found)) } terr_sorts(values) => { - format!("expected {} but found {}", + format!("expected {}, found {}", ty_sort_string(cx, values.expected), ty_sort_string(cx, values.found)) } terr_traits(values) => { - format!("expected trait `{}` but found trait `{}`", + format!("expected trait `{}`, found trait `{}`", item_path_str(cx, values.expected), item_path_str(cx, values.found)) } terr_builtin_bounds(values) => { if values.expected.is_empty() { - format!("expected no bounds but found `{}`", + format!("expected no bounds, found `{}`", values.found.user_string(cx)) } else if values.found.is_empty() { - format!("expected bounds `{}` but found no bounds", + format!("expected bounds `{}`, found no bounds", values.expected.user_string(cx)) } else { - format!("expected bounds `{}` but found bounds `{}`", + format!("expected bounds `{}`, found bounds `{}`", values.expected.user_string(cx), values.found.user_string(cx)) } } terr_integer_as_char => { - "expected an integral type but found `char`".to_string() + "expected an integral type, found `char`".to_string() } terr_int_mismatch(ref values) => { - format!("expected `{}` but found `{}`", + format!("expected `{}`, found `{}`", values.expected.to_string(), values.found.to_string()) } terr_float_mismatch(ref values) => { - format!("expected `{}` but found `{}`", + format!("expected `{}`, found `{}`", values.expected.to_string(), values.found.to_string()) } terr_variadic_mismatch(ref values) => { - format!("expected {} fn but found {} function", + format!("expected {} fn, found {} function", if values.expected { "variadic" } else { "non-variadic" }, if values.found { "variadic" } else { "non-variadic" }) } @@ -3711,30 +3962,6 @@ pub fn provided_trait_methods(cx: &ctxt, id: ast::DefId) -> Vec<Rc<Method>> { } } -pub fn trait_supertraits(cx: &ctxt, id: ast::DefId) -> Rc<Vec<Rc<TraitRef>>> { - // Check the cache. - match cx.supertraits.borrow().find(&id) { - Some(trait_refs) => { return trait_refs.clone(); } - None => {} // Continue. - } - - // Not in the cache. It had better be in the metadata, which means it - // shouldn't be local. - assert!(!is_local(id)); - - // Get the supertraits out of the metadata and create the - // TraitRef for each. - let result = Rc::new(csearch::get_supertraits(cx, id)); - cx.supertraits.borrow_mut().insert(id, result.clone()); - result -} - -pub fn trait_ref_supertraits(cx: &ctxt, trait_ref: &ty::TraitRef) -> Vec<Rc<TraitRef>> { - let supertrait_refs = trait_supertraits(cx, trait_ref.def_id); - supertrait_refs.iter().map( - |supertrait_ref| supertrait_ref.subst(cx, &trait_ref.substs)).collect() -} - fn lookup_locally_or_in_crate_store<V:Clone>( descr: &str, def_id: ast::DefId, @@ -3846,9 +4073,12 @@ pub fn trait_ref_to_def_id(tcx: &ctxt, tr: &ast::TraitRef) -> ast::DefId { def.def_id() } -pub fn try_add_builtin_trait(tcx: &ctxt, - trait_def_id: ast::DefId, - builtin_bounds: &mut BuiltinBounds) -> bool { +pub fn try_add_builtin_trait( + tcx: &ctxt, + trait_def_id: ast::DefId, + builtin_bounds: &mut EnumSet<BuiltinBound>) + -> bool +{ //! Checks whether `trait_ref` refers to one of the builtin //! traits, like `Send`, and adds the corresponding //! bound to the set `builtin_bounds` if so. Returns true if `trait_ref` @@ -3945,7 +4175,7 @@ pub fn substd_enum_variants(cx: &ctxt, -> Vec<Rc<VariantInfo>> { enum_variants(cx, id).iter().map(|variant_info| { let substd_args = variant_info.args.iter() - .map(|aty| aty.subst(cx, substs)).collect(); + .map(|aty| aty.subst(cx, substs)).collect::<Vec<_>>(); let substd_ctor_ty = variant_info.ctor_ty.subst(cx, substs); @@ -4134,6 +4364,18 @@ pub fn lookup_trait_def(cx: &ctxt, did: ast::DefId) -> Rc<ty::TraitDef> { } } +/// Given a reference to a trait, returns the bounds declared on the +/// trait, with appropriate substitutions applied. +pub fn bounds_for_trait_ref(tcx: &ctxt, + trait_ref: &TraitRef) + -> ty::ParamBounds +{ + let trait_def = lookup_trait_def(tcx, trait_ref.def_id); + debug!("bounds_for_trait_ref(trait_def={}, trait_ref={})", + trait_def.repr(tcx), trait_ref.repr(tcx)); + trait_def.bounds.subst(tcx, &trait_ref.substs) +} + /// Iterate over attributes of a definition. // (This should really be an iterator, but that would require csearch and // decoder to use iterators instead of higher-order functions.) @@ -4168,9 +4410,9 @@ pub fn has_attr(tcx: &ctxt, did: DefId, attr: &str) -> bool { found } -/// Determine whether an item is annotated with `#[packed]` +/// Determine whether an item is annotated with `#[repr(packed)]` pub fn lookup_packed(tcx: &ctxt, did: DefId) -> bool { - has_attr(tcx, did, "packed") + lookup_repr_hints(tcx, did).contains(&attr::ReprPacked) } /// Determine whether an item is annotated with `#[simd]` @@ -4178,14 +4420,16 @@ pub fn lookup_simd(tcx: &ctxt, did: DefId) -> bool { has_attr(tcx, did, "simd") } -// Obtain the representation annotation for a definition. -pub fn lookup_repr_hint(tcx: &ctxt, did: DefId) -> attr::ReprAttr { - let mut acc = attr::ReprAny; +/// Obtain the representation annotation for a struct definition. +pub fn lookup_repr_hints(tcx: &ctxt, did: DefId) -> Vec<attr::ReprAttr> { + let mut acc = Vec::new(); + ty::each_attr(tcx, did, |meta| { - acc = attr::find_repr_attr(tcx.sess.diagnostic(), meta, acc); + acc.extend(attr::find_repr_attrs(tcx.sess.diagnostic(), meta).move_iter()); true }); - return acc; + + acc } // Look up a field ID, whether or not it's local @@ -4199,14 +4443,10 @@ pub fn lookup_field_type(tcx: &ctxt, node_id_to_type(tcx, id.node) } else { let mut tcache = tcx.tcache.borrow_mut(); - match tcache.find(&id) { - Some(&Polytype {ty, ..}) => ty, - None => { - let tpt = csearch::get_field_type(tcx, struct_id, id); - tcache.insert(id, tpt.clone()); - tpt.ty - } - } + let pty = tcache.find_or_insert_with(id, |_| { + csearch::get_field_type(tcx, struct_id, id) + }); + pty.ty }; t.subst(tcx, substs) } @@ -4460,7 +4700,7 @@ pub fn eval_repeat_count<T: ExprTyProvider>(tcx: &T, count_expr: &ast::Expr) -> const_eval::const_int(count) => if count < 0 { tcx.ty_ctxt().sess.span_err(count_expr.span, "expected positive integer for \ - repeat count but found negative integer"); + repeat count, found negative integer"); return 0; } else { return count as uint @@ -4469,38 +4709,38 @@ pub fn eval_repeat_count<T: ExprTyProvider>(tcx: &T, count_expr: &ast::Expr) -> const_eval::const_float(count) => { tcx.ty_ctxt().sess.span_err(count_expr.span, "expected positive integer for \ - repeat count but found float"); + repeat count, found float"); return count as uint; } const_eval::const_str(_) => { tcx.ty_ctxt().sess.span_err(count_expr.span, "expected positive integer for \ - repeat count but found string"); + repeat count, found string"); return 0; } const_eval::const_bool(_) => { tcx.ty_ctxt().sess.span_err(count_expr.span, "expected positive integer for \ - repeat count but found boolean"); + repeat count, found boolean"); return 0; } const_eval::const_binary(_) => { tcx.ty_ctxt().sess.span_err(count_expr.span, "expected positive integer for \ - repeat count but found binary array"); + repeat count, found binary array"); return 0; } const_eval::const_nil => { tcx.ty_ctxt().sess.span_err(count_expr.span, "expected positive integer for \ - repeat count but found ()"); + repeat count, found ()"); return 0; } }, Err(..) => { tcx.ty_ctxt().sess.span_err(count_expr.span, - "expected constant integer for repeat count \ - but found variable"); + "expected constant integer for repeat count, \ + found variable"); return 0; } } @@ -4534,9 +4774,10 @@ pub fn each_bound_trait_and_supertraits(tcx: &ctxt, } // Add supertraits to supertrait_set - let supertrait_refs = trait_ref_supertraits(tcx, - &**trait_refs.get(i)); - for supertrait_ref in supertrait_refs.iter() { + let trait_ref = trait_refs.get(i).clone(); + let trait_def = lookup_trait_def(tcx, trait_ref.def_id); + for supertrait_ref in trait_def.bounds.trait_bounds.iter() { + let supertrait_ref = supertrait_ref.subst(tcx, &trait_ref.substs); debug!("each_bound_trait_and_supertraits(supertrait_ref={})", supertrait_ref.repr(tcx)); @@ -4554,6 +4795,61 @@ pub fn each_bound_trait_and_supertraits(tcx: &ctxt, return true; } +pub fn required_region_bounds(tcx: &ctxt, + region_bounds: &[ty::Region], + builtin_bounds: BuiltinBounds, + trait_bounds: &[Rc<TraitRef>]) + -> Vec<ty::Region> +{ + /*! + * Given a type which must meet the builtin bounds and trait + * bounds, returns a set of lifetimes which the type must outlive. + * + * Requires that trait definitions have been processed. + */ + + let mut all_bounds = Vec::new(); + + debug!("required_region_bounds(builtin_bounds={}, trait_bounds={})", + builtin_bounds.repr(tcx), + trait_bounds.repr(tcx)); + + all_bounds.push_all(region_bounds); + + push_region_bounds([], + builtin_bounds, + &mut all_bounds); + + debug!("from builtin bounds: all_bounds={}", all_bounds.repr(tcx)); + + each_bound_trait_and_supertraits( + tcx, + trait_bounds, + |trait_ref| { + let bounds = ty::bounds_for_trait_ref(tcx, &*trait_ref); + push_region_bounds(bounds.opt_region_bound.as_slice(), + bounds.builtin_bounds, + &mut all_bounds); + debug!("from {}: bounds={} all_bounds={}", + trait_ref.repr(tcx), + bounds.repr(tcx), + all_bounds.repr(tcx)); + true + }); + + return all_bounds; + + fn push_region_bounds(region_bounds: &[ty::Region], + builtin_bounds: ty::BuiltinBounds, + all_bounds: &mut Vec<ty::Region>) { + all_bounds.push_all(region_bounds.as_slice()); + + if builtin_bounds.contains_elem(ty::BoundSend) { + all_bounds.push(ty::ReStatic); + } + } +} + pub fn get_tydesc_ty(tcx: &ctxt) -> Result<t, String> { tcx.lang_items.require(TyDescStructLangItem).map(|tydesc_lang_item| { tcx.intrinsic_defs.borrow().find_copy(&tydesc_lang_item) @@ -4569,7 +4865,10 @@ pub fn get_opaque_ty(tcx: &ctxt) -> Result<t, String> { } pub fn visitor_object_ty(tcx: &ctxt, - region: ty::Region) -> Result<(Rc<TraitRef>, t), String> { + ptr_region: ty::Region, + trait_region: ty::Region) + -> Result<(Rc<TraitRef>, t), String> +{ let trait_lang_item = match tcx.lang_items.require(TyVisitorTraitLangItem) { Ok(id) => id, Err(s) => { return Err(s); } @@ -4577,11 +4876,12 @@ pub fn visitor_object_ty(tcx: &ctxt, let substs = Substs::empty(); let trait_ref = Rc::new(TraitRef { def_id: trait_lang_item, substs: substs }); Ok((trait_ref.clone(), - mk_rptr(tcx, region, mt {mutbl: ast::MutMutable, - ty: mk_trait(tcx, - trait_ref.def_id, - trait_ref.substs.clone(), - empty_builtin_bounds()) }))) + mk_rptr(tcx, ptr_region, + mt {mutbl: ast::MutMutable, + ty: mk_trait(tcx, + trait_ref.def_id, + trait_ref.substs.clone(), + ty::region_existential_bound(trait_region))}))) } pub fn item_variances(tcx: &ctxt, item_id: ast::DefId) -> Rc<ItemVariances> { @@ -4853,15 +5153,12 @@ pub fn hash_crate_independent(tcx: &ctxt, t: t, svh: &Svh) -> u64 { ty_uniq(_) => { byte!(10); } - ty_vec(m, Some(n)) => { + ty_vec(_, Some(n)) => { byte!(11); - mt(&mut state, m); n.hash(&mut state); - 1u8.hash(&mut state); } - ty_vec(m, None) => { + ty_vec(_, None) => { byte!(11); - mt(&mut state, m); 0u8.hash(&mut state); } ty_ptr(m) => { @@ -4910,6 +5207,7 @@ pub fn hash_crate_independent(tcx: &ctxt, t: t, svh: &Svh) -> u64 { hash!(p.idx); did(&mut state, p.def_id); } + ty_open(_) => byte!(22), ty_infer(_) => unreachable!(), ty_err => byte!(23), ty_unboxed_closure(d, r) => { @@ -4975,6 +5273,18 @@ pub fn construct_parameter_environment( generics.types.get_slice(space)); } + // + // Compute region bounds. For now, these relations are stored in a + // global table on the tcx, so just enter them there. I'm not + // crazy about this scheme, but it's convenient, at least. + // + + for &space in subst::ParamSpace::all().iter() { + record_region_bounds_from_defs(tcx, space, &free_substs, + generics.regions.get_slice(space)); + } + + debug!("construct_parameter_environment: free_id={} \ free_subst={} \ bounds={}", @@ -4984,7 +5294,8 @@ pub fn construct_parameter_environment( return ty::ParameterEnvironment { free_substs: free_substs, - bounds: bounds + bounds: bounds, + implicit_region_bound: ty::ReScope(free_id), }; fn push_region_params(regions: &mut VecPerParamSpace<ty::Region>, @@ -5013,10 +5324,41 @@ pub fn construct_parameter_environment( free_substs: &subst::Substs, defs: &[TypeParameterDef]) { for def in defs.iter() { - let b = (*def.bounds).subst(tcx, free_substs); + let b = def.bounds.subst(tcx, free_substs); bounds.push(space, b); } } + + fn record_region_bounds_from_defs(tcx: &ty::ctxt, + space: subst::ParamSpace, + free_substs: &subst::Substs, + defs: &[RegionParameterDef]) { + for (subst_region, def) in + free_substs.regions().get_slice(space).iter().zip( + defs.iter()) + { + // For each region parameter 'subst... + let bounds = def.bounds.subst(tcx, free_substs); + for bound_region in bounds.iter() { + // Which is declared with a bound like 'subst:'bound... + match (subst_region, bound_region) { + (&ty::ReFree(subst_fr), &ty::ReFree(bound_fr)) => { + // Record that 'subst outlives 'bound. Or, put + // another way, 'bound <= 'subst. + tcx.region_maps.relate_free_regions(bound_fr, subst_fr); + }, + _ => { + // All named regions are instantiated with free regions. + tcx.sess.bug( + format!("push_region_bounds_from_defs: \ + non free region: {} / {}", + subst_region.repr(tcx), + bound_region.repr(tcx)).as_slice()); + } + } + } + } + } } impl BorrowKind { @@ -5132,8 +5474,8 @@ pub fn accumulate_lifetimes_in_type(accumulator: &mut Vec<ty::Region>, ty_tup(_) | ty_param(_) | ty_infer(_) | + ty_open(_) | ty_err => {} } }) } - diff --git a/src/librustc/middle/ty_fold.rs b/src/librustc/middle/ty_fold.rs index 9f475bfd9d5..435c591f881 100644 --- a/src/librustc/middle/ty_fold.rs +++ b/src/librustc/middle/ty_fold.rs @@ -85,6 +85,11 @@ pub trait TypeFolder { super_fold_trait_store(self, s) } + fn fold_existential_bounds(&mut self, s: ty::ExistentialBounds) + -> ty::ExistentialBounds { + super_fold_existential_bounds(self, s) + } + fn fold_autoref(&mut self, ar: &ty::AutoRef) -> ty::AutoRef { super_fold_autoref(self, ar) } @@ -236,9 +241,16 @@ impl TypeFoldable for ty::BuiltinBounds { } } +impl TypeFoldable for ty::ExistentialBounds { + fn fold_with<F:TypeFolder>(&self, folder: &mut F) -> ty::ExistentialBounds { + folder.fold_existential_bounds(*self) + } +} + impl TypeFoldable for ty::ParamBounds { fn fold_with<F:TypeFolder>(&self, folder: &mut F) -> ty::ParamBounds { ty::ParamBounds { + opt_region_bound: self.opt_region_bound.fold_with(folder), builtin_bounds: self.builtin_bounds.fold_with(folder), trait_bounds: self.trait_bounds.fold_with(folder), } @@ -259,8 +271,14 @@ impl TypeFoldable for ty::TypeParameterDef { } impl TypeFoldable for ty::RegionParameterDef { - fn fold_with<F:TypeFolder>(&self, _folder: &mut F) -> ty::RegionParameterDef { - *self + fn fold_with<F:TypeFolder>(&self, folder: &mut F) -> ty::RegionParameterDef { + ty::RegionParameterDef { + name: self.name, + def_id: self.def_id, + space: self.space, + index: self.index, + bounds: self.bounds.fold_with(folder) + } } } @@ -273,6 +291,18 @@ impl TypeFoldable for ty::Generics { } } +impl TypeFoldable for ty::UnsizeKind { + fn fold_with<F:TypeFolder>(&self, folder: &mut F) -> ty::UnsizeKind { + match *self { + ty::UnsizeLength(len) => ty::UnsizeLength(len), + ty::UnsizeStruct(box ref k, n) => ty::UnsizeStruct(box k.fold_with(folder), n), + ty::UnsizeVtable(bounds, def_id, ref substs) => { + ty::UnsizeVtable(bounds.fold_with(folder), def_id, substs.fold_with(folder)) + } + } + } +} + /////////////////////////////////////////////////////////////////////////// // "super" routines: these are the default implementations for TypeFolder. // @@ -328,7 +358,7 @@ pub fn super_fold_closure_ty<T:TypeFolder>(this: &mut T, sig: fty.sig.fold_with(this), fn_style: fty.fn_style, onceness: fty.onceness, - bounds: fty.bounds, + bounds: fty.bounds.fold_with(this), abi: fty.abi, } } @@ -360,8 +390,11 @@ pub fn super_fold_sty<T:TypeFolder>(this: &mut T, ty::ty_ptr(ref tm) => { ty::ty_ptr(tm.fold_with(this)) } - ty::ty_vec(ref tm, sz) => { - ty::ty_vec(tm.fold_with(this), sz) + ty::ty_vec(typ, sz) => { + ty::ty_vec(typ.fold_with(this), sz) + } + ty::ty_open(typ) => { + ty::ty_open(typ.fold_with(this)) } ty::ty_enum(tid, ref substs) => { ty::ty_enum(tid, substs.fold_with(this)) @@ -374,7 +407,7 @@ pub fn super_fold_sty<T:TypeFolder>(this: &mut T, ty::ty_trait(box ty::TyTrait { def_id: def_id, substs: substs.fold_with(this), - bounds: bounds + bounds: this.fold_existential_bounds(bounds), }) } ty::ty_tup(ref ts) => { @@ -415,16 +448,27 @@ pub fn super_fold_trait_store<T:TypeFolder>(this: &mut T, } } +pub fn super_fold_existential_bounds<T:TypeFolder>(this: &mut T, + bounds: ty::ExistentialBounds) + -> ty::ExistentialBounds { + ty::ExistentialBounds { + region_bound: bounds.region_bound.fold_with(this), + builtin_bounds: bounds.builtin_bounds, + } +} + pub fn super_fold_autoref<T:TypeFolder>(this: &mut T, autoref: &ty::AutoRef) -> ty::AutoRef { match *autoref { - ty::AutoPtr(r, m) => ty::AutoPtr(r.fold_with(this), m), - ty::AutoBorrowVec(r, m) => ty::AutoBorrowVec(r.fold_with(this), m), - ty::AutoBorrowVecRef(r, m) => ty::AutoBorrowVecRef(r.fold_with(this), m), + ty::AutoPtr(r, m, None) => ty::AutoPtr(this.fold_region(r), m, None), + ty::AutoPtr(r, m, Some(ref a)) => { + ty::AutoPtr(this.fold_region(r), m, Some(box super_fold_autoref(this, &**a))) + } ty::AutoUnsafe(m) => ty::AutoUnsafe(m), - ty::AutoBorrowObj(r, m) => ty::AutoBorrowObj(r.fold_with(this), m), + ty::AutoUnsize(ref k) => ty::AutoUnsize(k.fold_with(this)), + ty::AutoUnsizeUniq(ref k) => ty::AutoUnsizeUniq(k.fold_with(this)), } } diff --git a/src/librustc/middle/typeck/astconv.rs b/src/librustc/middle/typeck/astconv.rs index add31ed87ce..6cd61a8c3f8 100644 --- a/src/librustc/middle/typeck/astconv.rs +++ b/src/librustc/middle/typeck/astconv.rs @@ -57,19 +57,19 @@ use middle::resolve_lifetime as rl; use middle::subst::{FnSpace, TypeSpace, SelfSpace, Subst, Substs}; use middle::subst::{VecPerParamSpace}; use middle::ty; -use middle::ty_fold::TypeFolder; -use middle::typeck::rscope::RegionScope; -use middle::typeck::rscope::{ExplicitRscope, ImpliedSingleRscope}; -use middle::typeck::{TypeAndSubsts, infer, lookup_def_tcx, rscope}; +use middle::typeck::lookup_def_tcx; +use middle::typeck::infer; +use middle::typeck::rscope::{ExplicitRscope, RegionScope, SpecificRscope}; +use middle::typeck::rscope; +use middle::typeck::TypeAndSubsts; use middle::typeck; -use util::ppaux::Repr; +use util::ppaux::{Repr, UserString}; +use std::collections::HashMap; use std::rc::Rc; use syntax::abi; use syntax::{ast, ast_util}; use syntax::codemap::Span; -use syntax::owned_slice::OwnedSlice; -use syntax::print::pprust::{lifetime_to_string, path_to_string}; pub trait AstConv { fn tcx<'a>(&'a self) -> &'a ty::ctxt; @@ -111,8 +111,9 @@ pub fn ast_region_to_region(tcx: &ty::ctxt, lifetime: &ast::Lifetime) }; debug!("ast_region_to_region(lifetime={} id={}) yields {}", - lifetime_to_string(lifetime), - lifetime.id, r.repr(tcx)); + lifetime.repr(tcx), + lifetime.id, + r.repr(tcx)); r } @@ -145,7 +146,7 @@ pub fn opt_ast_region_to_region<AC:AstConv,RS:RegionScope>( }; debug!("opt_ast_region_to_region(opt_lifetime={}) yields {}", - opt_lifetime.as_ref().map(|e| lifetime_to_string(e)), + opt_lifetime.repr(this.tcx()), r.repr(this.tcx())); r @@ -191,7 +192,7 @@ fn ast_path_substs<AC:AstConv,RS:RegionScope>( if supplied_num_region_params != 0 || anon_regions.is_err() { span_err!(tcx.sess, path.span, E0107, - "wrong number of lifetime parameters: expected {} but found {}", + "wrong number of lifetime parameters: expected {}, found {}", expected_num_region_params, supplied_num_region_params); } @@ -216,7 +217,7 @@ fn ast_path_substs<AC:AstConv,RS:RegionScope>( "expected" }; this.tcx().sess.span_fatal(path.span, - format!("wrong number of type arguments: {} {} but found {}", + format!("wrong number of type arguments: {} {}, found {}", expected, required_ty_param_count, supplied_ty_param_count).as_slice()); @@ -227,7 +228,7 @@ fn ast_path_substs<AC:AstConv,RS:RegionScope>( "expected" }; this.tcx().sess.span_fatal(path.span, - format!("wrong number of type arguments: {} {} but found {}", + format!("wrong number of type arguments: {} {}, found {}", expected, formal_ty_param_count, supplied_ty_param_count).as_slice()); @@ -284,11 +285,11 @@ pub fn ast_path_to_trait_ref<AC:AstConv,RS:RegionScope>( } pub fn ast_path_to_ty<AC:AstConv,RS:RegionScope>( - this: &AC, - rscope: &RS, - did: ast::DefId, - path: &ast::Path) - -> TypeAndSubsts + this: &AC, + rscope: &RS, + did: ast::DefId, + path: &ast::Path) + -> TypeAndSubsts { let tcx = this.tcx(); let ty::Polytype { @@ -370,7 +371,7 @@ pub fn ast_ty_to_prim_ty(tcx: &ty::ctxt, ast_ty: &ast::Ty) -> Option<ty::t> { None => { tcx.sess.span_bug(ast_ty.span, format!("unbound path {}", - path_to_string(path)).as_slice()) + path.repr(tcx)).as_slice()) } Some(&d) => d }; @@ -398,10 +399,7 @@ pub fn ast_ty_to_prim_ty(tcx: &ty::ctxt, ast_ty: &ast::Ty) -> Option<ty::t> { Some(ty::mk_mach_float(ft)) } ast::TyStr => { - span_err!(tcx.sess, ast_ty.span, E0037, - "bare `str` is not a type"); - // return /something/ so they can at least get more errors - Some(ty::mk_uniq(tcx, ty::mk_str(tcx))) + Some(ty::mk_str(tcx)) } } } @@ -433,7 +431,7 @@ pub fn ast_ty_to_builtin_ty<AC:AstConv, .sess .span_bug(ast_ty.span, format!("unbound path {}", - path_to_string(path)).as_slice()) + path.repr(this.tcx())).as_slice()) } Some(&d) => d }; @@ -462,21 +460,7 @@ pub fn ast_ty_to_builtin_ty<AC:AstConv, rscope, &mt, Uniq, - |typ| { - match ty::get(typ).sty { - ty::ty_str => { - span_err!(this.tcx().sess, path.span, E0111, - "`Box<str>` is not a type"); - ty::mk_err() - } - ty::ty_vec(_, None) => { - span_err!(this.tcx().sess, path.span, E0112, - "`Box<[T]>` is not a type"); - ty::mk_err() - } - _ => ty::mk_uniq(this.tcx(), typ), - } - })) + |typ| ty::mk_uniq(this.tcx(), typ))); } span_err!(this.tcx().sess, path.span, E0113, "not enough type parameters supplied to `Box<T>`"); @@ -537,10 +521,14 @@ enum PointerTy { Uniq } -fn ast_ty_to_mt<AC:AstConv, RS:RegionScope>(this: &AC, - rscope: &RS, - ty: &ast::Ty) -> ty::mt { - ty::mt {ty: ast_ty_to_ty(this, rscope, ty), mutbl: ast::MutImmutable} +impl PointerTy { + fn default_region(&self) -> ty::Region { + match *self { + Box => ty::ReStatic, + Uniq => ty::ReStatic, + RPtr(r) => r, + } + } } pub fn trait_ref_for_unboxed_function<AC:AstConv, @@ -563,7 +551,11 @@ pub fn trait_ref_for_unboxed_function<AC:AstConv, .map(|input| { ast_ty_to_ty(this, rscope, &*input.ty) }).collect::<Vec<_>>(); - let input_tuple = ty::mk_tup(this.tcx(), input_types); + let input_tuple = if input_types.len() == 0 { + ty::mk_nil() + } else { + ty::mk_tup(this.tcx(), input_types) + }; let output_type = ast_ty_to_ty(this, rscope, &*unboxed_function.decl.output); @@ -597,11 +589,8 @@ fn mk_pointer<AC:AstConv, match a_seq_ty.ty.node { ast::TyVec(ref ty) => { - let mut mt = ast_ty_to_mt(this, rscope, &**ty); - if a_seq_ty.mutbl == ast::MutMutable { - mt.mutbl = ast::MutMutable; - } - return constr(ty::mk_vec(tcx, mt, None)); + let ty = ast_ty_to_ty(this, rscope, &**ty); + return constr(ty::mk_vec(tcx, ty, None)); } ast::TyUnboxedFn(ref unboxed_function) => { let ty::TraitRef { @@ -611,10 +600,11 @@ fn mk_pointer<AC:AstConv, rscope, &**unboxed_function, None); + let r = ptr_ty.default_region(); let tr = ty::mk_trait(this.tcx(), def_id, substs, - ty::empty_builtin_bounds()); + ty::region_existential_bound(r)); match ptr_ty { Uniq => { return ty::mk_uniq(this.tcx(), tr); @@ -634,7 +624,7 @@ fn mk_pointer<AC:AstConv, } } - ast::TyPath(ref path, ref bounds, id) => { + ast::TyPath(ref path, ref opt_bounds, id) => { // Note that the "bounds must be empty if path is not a trait" // restriction is enforced in the below case for ty_path, which // will run after this as long as the path isn't a trait. @@ -658,37 +648,41 @@ fn mk_pointer<AC:AstConv, Some(&def::DefTrait(trait_def_id)) => { let result = ast_path_to_trait_ref( this, rscope, trait_def_id, None, path); - let trait_store = match ptr_ty { - Uniq => ty::UniqTraitStore, - RPtr(r) => { - ty::RegionTraitStore(r, a_seq_ty.mutbl) + let bounds = match *opt_bounds { + None => { + conv_existential_bounds(this, + rscope, + path.span, + [result.clone()].as_slice(), + [].as_slice()) } - _ => { - tcx.sess.span_err( - path.span, - "~trait or &trait are the only supported \ - forms of casting-to-trait"); - return ty::mk_err(); + Some(ref bounds) => { + conv_existential_bounds(this, + rscope, + path.span, + [result.clone()].as_slice(), + bounds.as_slice()) } }; - let bounds = conv_builtin_bounds(this.tcx(), - path.span, - bounds, - trait_store); let tr = ty::mk_trait(tcx, result.def_id, result.substs.clone(), bounds); - // We could just match on ptr_ty, but we need to pass a trait - // store to conv_builtin_bounds, so mathc twice for now. - return match trait_store { - ty::UniqTraitStore => { + return match ptr_ty { + Uniq => { return ty::mk_uniq(tcx, tr); } - ty::RegionTraitStore(r, m) => { - return ty::mk_rptr(tcx, r, ty::mt{mutbl: m, ty: tr}); + RPtr(r) => { + return ty::mk_rptr(tcx, r, ty::mt{mutbl: a_seq_ty.mutbl, ty: tr}); } - } + _ => { + tcx.sess.span_err( + path.span, + "~trait or &trait are the only supported \ + forms of casting-to-trait"); + return ty::mk_err(); + } + }; } _ => {} } @@ -734,10 +728,7 @@ pub fn ast_ty_to_ty<AC:AstConv, RS:RegionScope>( |ty| ty::mk_uniq(tcx, ty)) } ast::TyVec(ty) => { - tcx.sess.span_err(ast_ty.span, "bare `[]` is not a type"); - // return /something/ so they can at least get more errors - let vec_ty = ty::mk_vec(tcx, ast_ty_to_mt(this, rscope, &*ty), None); - ty::mk_uniq(tcx, vec_ty) + ty::mk_vec(tcx, ast_ty_to_ty(this, rscope, &*ty), None) } ast::TyPtr(ref mt) => { ty::mk_ptr(tcx, ty::mt { @@ -766,28 +757,22 @@ pub fn ast_ty_to_ty<AC:AstConv, RS:RegionScope>( ty::mk_bare_fn(tcx, ty_of_bare_fn(this, ast_ty.id, bf.fn_style, bf.abi, &*bf.decl)) } - ast::TyClosure(ref f, ref region) => { - - // resolve the function bound region in the original region - // scope `rscope`, not the scope of the function parameters - let bound_region = opt_ast_region_to_region(this, rscope, - ast_ty.span, region); - - let store = ty::RegionTraitStore(bound_region, ast::MutMutable); - + ast::TyClosure(ref f) => { // Use corresponding trait store to figure out default bounds // if none were specified. - let bounds = conv_builtin_bounds(this.tcx(), - ast_ty.span, - &f.bounds, - store); - + let bounds = conv_existential_bounds(this, + rscope, + ast_ty.span, + [].as_slice(), + f.bounds.as_slice()); let fn_decl = ty_of_closure(this, ast_ty.id, f.fn_style, f.onceness, bounds, - store, + ty::RegionTraitStore( + bounds.region_bound, + ast::MutMutable), &*f.decl, abi::Rust, None); @@ -796,10 +781,10 @@ pub fn ast_ty_to_ty<AC:AstConv, RS:RegionScope>( ast::TyProc(ref f) => { // Use corresponding trait store to figure out default bounds // if none were specified. - let bounds = conv_builtin_bounds(this.tcx(), - ast_ty.span, - &f.bounds, - ty::UniqTraitStore); + let bounds = conv_existential_bounds(this, rscope, + ast_ty.span, + [].as_slice(), + f.bounds.as_slice()); let fn_decl = ty_of_closure(this, ast_ty.id, @@ -810,6 +795,7 @@ pub fn ast_ty_to_ty<AC:AstConv, RS:RegionScope>( &*f.decl, abi::Rust, None); + ty::mk_closure(tcx, fn_decl) } ast::TyUnboxedFn(..) => { @@ -823,7 +809,7 @@ pub fn ast_ty_to_ty<AC:AstConv, RS:RegionScope>( tcx.sess .span_bug(ast_ty.span, format!("unbound path {}", - path_to_string(path)).as_slice()) + path.repr(tcx)).as_slice()) } Some(&d) => d }; @@ -837,15 +823,23 @@ pub fn ast_ty_to_ty<AC:AstConv, RS:RegionScope>( _ => { }, } match a_def { - def::DefTrait(_) => { - let path_str = path_to_string(path); - tcx.sess.span_err( - ast_ty.span, - format!("reference to trait `{name}` where a \ - type is expected; try `Box<{name}>` or \ - `&{name}`", - name=path_str).as_slice()); - ty::mk_err() + def::DefTrait(trait_def_id) => { + let result = ast_path_to_trait_ref( + this, rscope, trait_def_id, None, path); + let empty_bounds: &[ast::TyParamBound] = &[]; + let ast_bounds = match *bounds { + Some(ref b) => b.as_slice(), + None => empty_bounds + }; + let bounds = conv_existential_bounds(this, + rscope, + ast_ty.span, + &[result.clone()], + ast_bounds); + ty::mk_trait(tcx, + result.def_id, + result.substs.clone(), + bounds) } def::DefTy(did) | def::DefStruct(did) => { ast_path_to_ty(this, rscope, did, path).ty @@ -883,10 +877,10 @@ pub fn ast_ty_to_ty<AC:AstConv, RS:RegionScope>( Ok(ref r) => { match *r { const_eval::const_int(i) => - ty::mk_vec(tcx, ast_ty_to_mt(this, rscope, &*ty), + ty::mk_vec(tcx, ast_ty_to_ty(this, rscope, &*ty), Some(i as uint)), const_eval::const_uint(i) => - ty::mk_vec(tcx, ast_ty_to_mt(this, rscope, &*ty), + ty::mk_vec(tcx, ast_ty_to_ty(this, rscope, &*ty), Some(i as uint)), _ => { tcx.sess.span_fatal( @@ -1050,9 +1044,7 @@ fn ty_of_method_or_bare_fn<AC:AstConv>( _ => { match implied_output_region { Some(implied_output_region) => { - let rb = ImpliedSingleRscope { - region: implied_output_region, - }; + let rb = SpecificRscope::new(implied_output_region); ast_ty_to_ty(this, &rb, &*decl.output) } None => { @@ -1158,7 +1150,7 @@ pub fn ty_of_closure<AC:AstConv>( id: ast::NodeId, fn_style: ast::FnStyle, onceness: ast::Onceness, - bounds: ty::BuiltinBounds, + bounds: ty::ExistentialBounds, store: ty::TraitStore, decl: &ast::FnDecl, abi: abi::Abi, @@ -1204,69 +1196,250 @@ pub fn ty_of_closure<AC:AstConv>( } } -fn conv_builtin_bounds(tcx: &ty::ctxt, - span: Span, - ast_bounds: &Option<OwnedSlice<ast::TyParamBound>>, - store: ty::TraitStore) - -> ty::BuiltinBounds { - //! Converts a list of bounds from the AST into a `BuiltinBounds` - //! struct. Reports an error if any of the bounds that appear - //! in the AST refer to general traits and not the built-in traits - //! like `Send`. Used to translate the bounds that - //! appear in closure and trait types, where only builtin bounds are - //! legal. - //! If no bounds were specified, we choose a "default" bound based on - //! the allocation type of the fn/trait, as per issue #7264. The user can - //! override this with an empty bounds list, e.g. "Box<fn:()>" or - //! "Box<Trait:>". - - match (ast_bounds, store) { - (&Some(ref bound_vec), _) => { - let mut builtin_bounds = ty::empty_builtin_bounds(); - for ast_bound in bound_vec.iter() { - match *ast_bound { - ast::TraitTyParamBound(ref b) => { - match lookup_def_tcx(tcx, b.path.span, b.ref_id) { - def::DefTrait(trait_did) => { - if ty::try_add_builtin_trait(tcx, trait_did, - &mut builtin_bounds) { - continue; // success - } +pub fn conv_existential_bounds<AC:AstConv, RS:RegionScope>( + this: &AC, + rscope: &RS, + span: Span, + main_trait_refs: &[Rc<ty::TraitRef>], + ast_bounds: &[ast::TyParamBound]) + -> ty::ExistentialBounds +{ + /*! + * Given an existential type like `Foo+'a+Bar`, this routine + * converts the `'a` and `Bar` intos an `ExistentialBounds` + * struct. The `main_trait_refs` argument specifies the `Foo` -- + * it is absent for closures. Eventually this should all be + * normalized, I think, so that there is no "main trait ref" and + * instead we just have a flat list of bounds as the existential + * type. + */ + + let ast_bound_refs: Vec<&ast::TyParamBound> = + ast_bounds.iter().collect(); + + let PartitionedBounds { builtin_bounds, + trait_bounds, + region_bounds, + unboxed_fn_ty_bounds } = + partition_bounds(this.tcx(), span, ast_bound_refs.as_slice()); + + if !trait_bounds.is_empty() { + let b = trait_bounds.get(0); + this.tcx().sess.span_err( + b.path.span, + format!("only the builtin traits can be used \ + as closure or object bounds").as_slice()); + } + + if !unboxed_fn_ty_bounds.is_empty() { + this.tcx().sess.span_err( + span, + format!("only the builtin traits can be used \ + as closure or object bounds").as_slice()); + } + + // The "main trait refs", rather annoyingly, have no type + // specified for the `Self` parameter of the trait. The reason for + // this is that they are, after all, *existential* types, and + // hence that type is unknown. However, leaving this type missing + // causes the substitution code to go all awry when walking the + // bounds, so here we clone those trait refs and insert ty::err as + // the self type. Perhaps we should do this more generally, it'd + // be convenient (or perhaps something else, i.e., ty::erased). + let main_trait_refs: Vec<Rc<ty::TraitRef>> = + main_trait_refs.iter() + .map(|t| + Rc::new(ty::TraitRef { + def_id: t.def_id, + substs: t.substs.with_self_ty(ty::mk_err()) })) + .collect(); + + let region_bound = compute_region_bound(this, + rscope, + span, + builtin_bounds, + region_bounds.as_slice(), + main_trait_refs.as_slice()); + + ty::ExistentialBounds { + region_bound: region_bound, + builtin_bounds: builtin_bounds, + } +} + +pub fn compute_opt_region_bound(tcx: &ty::ctxt, + span: Span, + builtin_bounds: ty::BuiltinBounds, + region_bounds: &[&ast::Lifetime], + trait_bounds: &[Rc<ty::TraitRef>]) + -> Option<ty::Region> +{ + /*! + * Given the bounds on a type parameter / existential type, + * determines what single region bound (if any) we can use to + * summarize this type. The basic idea is that we will use the + * bound the user provided, if they provided one, and otherwise + * search the supertypes of trait bounds for region bounds. It may + * be that we can derive no bound at all, in which case we return + * `None`. + */ + + if region_bounds.len() > 1 { + tcx.sess.span_err( + region_bounds[1].span, + format!("only a single explicit lifetime bound is permitted").as_slice()); + } + + if region_bounds.len() != 0 { + // Explicitly specified region bound. Use that. + let r = region_bounds[0]; + return Some(ast_region_to_region(tcx, r)); + } + + // No explicit region bound specified. Therefore, examine trait + // bounds and see if we can derive region bounds from those. + let derived_region_bounds = + ty::required_region_bounds( + tcx, + [], + builtin_bounds, + trait_bounds); + + // If there are no derived region bounds, then report back that we + // can find no region bound. + if derived_region_bounds.len() == 0 { + return None; + } + + // If any of the derived region bounds are 'static, that is always + // the best choice. + if derived_region_bounds.iter().any(|r| ty::ReStatic == *r) { + return Some(ty::ReStatic); + } + + // Determine whether there is exactly one unique region in the set + // of derived region bounds. If so, use that. Otherwise, report an + // error. + let r = *derived_region_bounds.get(0); + if derived_region_bounds.slice_from(1).iter().any(|r1| r != *r1) { + tcx.sess.span_err( + span, + format!("ambiguous lifetime bound, \ + explicit lifetime bound required").as_slice()); + } + return Some(r); +} + +fn compute_region_bound<AC:AstConv, RS:RegionScope>( + this: &AC, + rscope: &RS, + span: Span, + builtin_bounds: ty::BuiltinBounds, + region_bounds: &[&ast::Lifetime], + trait_bounds: &[Rc<ty::TraitRef>]) + -> ty::Region +{ + /*! + * A version of `compute_opt_region_bound` for use where some + * region bound is required (existential types, + * basically). Reports an error if no region bound can be derived + * and we are in an `rscope` that does not provide a default. + */ + + match compute_opt_region_bound(this.tcx(), span, builtin_bounds, + region_bounds, trait_bounds) { + Some(r) => r, + None => { + match rscope.default_region_bound(span) { + Some(r) => { r } + None => { + this.tcx().sess.span_err( + span, + format!("explicit lifetime bound required").as_slice()); + ty::ReStatic + } + } + } + } +} + +pub struct PartitionedBounds<'a> { + pub builtin_bounds: ty::BuiltinBounds, + pub trait_bounds: Vec<&'a ast::TraitRef>, + pub unboxed_fn_ty_bounds: Vec<&'a ast::UnboxedFnTy>, + pub region_bounds: Vec<&'a ast::Lifetime>, +} + +pub fn partition_bounds<'a>(tcx: &ty::ctxt, + _span: Span, + ast_bounds: &'a [&ast::TyParamBound]) + -> PartitionedBounds<'a> +{ + /*! + * Divides a list of bounds from the AST into three groups: + * builtin bounds (Copy, Sized etc), general trait bounds, + * and region bounds. + */ + + let mut builtin_bounds = ty::empty_builtin_bounds(); + let mut region_bounds = Vec::new(); + let mut trait_bounds = Vec::new(); + let mut unboxed_fn_ty_bounds = Vec::new(); + let mut trait_def_ids = HashMap::new(); + for &ast_bound in ast_bounds.iter() { + match *ast_bound { + ast::TraitTyParamBound(ref b) => { + match lookup_def_tcx(tcx, b.path.span, b.ref_id) { + def::DefTrait(trait_did) => { + match trait_def_ids.find(&trait_did) { + // Already seen this trait. We forbid + // duplicates in the list (for some + // reason). + Some(span) => { + span_err!( + tcx.sess, b.path.span, E0127, + "trait `{}` already appears in the \ + list of bounds", + b.path.user_string(tcx)); + tcx.sess.span_note( + *span, + "previous appearance is here"); + + continue; } - _ => { } + + None => { } } - tcx.sess.span_fatal( - b.path.span, - "only the builtin traits can be used as closure \ - or object bounds"); - } - ast::StaticRegionTyParamBound => { - builtin_bounds.add(ty::BoundStatic); - } - ast::UnboxedFnTyParamBound(_) => { - tcx.sess.span_err(span, - "unboxed functions are not allowed \ - here"); - } - ast::OtherRegionTyParamBound(span) => { - if !tcx.sess.features.issue_5723_bootstrap.get() { - tcx.sess.span_err( - span, - "only the 'static lifetime is accepted \ - here."); + + trait_def_ids.insert(trait_did, b.path.span); + + if ty::try_add_builtin_trait(tcx, + trait_did, + &mut builtin_bounds) { + continue; // success } } + _ => { + // Not a trait? that's an error, but it'll get + // reported later. + } } + trait_bounds.push(b); + } + ast::RegionTyParamBound(ref l) => { + region_bounds.push(l); + } + ast::UnboxedFnTyParamBound(ref unboxed_function) => { + unboxed_fn_ty_bounds.push(unboxed_function); } - builtin_bounds - }, - // &'static Trait is sugar for &'static Trait:'static. - (&None, ty::RegionTraitStore(ty::ReStatic, _)) => { - let mut set = ty::empty_builtin_bounds(); set.add(ty::BoundStatic); set } - // No bounds are automatically applied for &'r Trait or ~Trait - (&None, ty::RegionTraitStore(..)) | - (&None, ty::UniqTraitStore) => ty::empty_builtin_bounds(), + } + + PartitionedBounds { + builtin_bounds: builtin_bounds, + trait_bounds: trait_bounds, + region_bounds: region_bounds, + unboxed_fn_ty_bounds: unboxed_fn_ty_bounds } } diff --git a/src/librustc/middle/typeck/check/_match.rs b/src/librustc/middle/typeck/check/_match.rs index ff49aa8a40b..77e5fbae6ee 100644 --- a/src/librustc/middle/typeck/check/_match.rs +++ b/src/librustc/middle/typeck/check/_match.rs @@ -169,7 +169,7 @@ pub fn check_pat_variant(pcx: &pat_ctxt, pat: &ast::Pat, path: &ast::Path, fcx.infcx().type_error_message_str_with_expected(pat.span, |expected, actual| { expected.map_or("".to_string(), |e| { - format!("mismatched types: expected `{}` but found {}", + format!("mismatched types: expected `{}`, found {}", e, actual) })}, Some(expected), @@ -223,7 +223,7 @@ pub fn check_pat_variant(pcx: &pat_ctxt, pat: &ast::Pat, path: &ast::Path, |expected, actual| { expected.map_or("".to_string(), |e| { - format!("mismatched types: expected `{}` but found {}", + format!("mismatched types: expected `{}`, found {}", e, actual) }) }, @@ -395,7 +395,7 @@ pub fn check_struct_like_enum_variant_pat(pcx: &pat_ctxt, Some(&def::DefTy(..)) => { let name = pprust::path_to_string(path); span_err!(tcx.sess, span, E0028, - "mismatched types: expected `{}` but found `{}`", + "mismatched types: expected `{}`, found `{}`", fcx.infcx().ty_to_string(expected), name); } _ => { @@ -548,7 +548,7 @@ pub fn check_pat(pcx: &pat_ctxt, pat: &ast::Pat, expected: ty::t) { expected.map_or("".to_string(), |e| { format!("mismatched types: expected \ - `{}` but found {}", e, actual) + `{}`, found {}", e, actual) })}, Some(expected), "a structure pattern".to_string(), @@ -607,8 +607,8 @@ pub fn check_pat(pcx: &pat_ctxt, pat: &ast::Pat, expected: ty::t) { |expected, actual| { expected.map_or("".to_string(), |e| { - format!("mismatched types: expected `{}` \ - but found {}", e, actual) + format!("mismatched types: expected `{}`, \ + found {}", e, actual) } )}, Some(expected), @@ -645,7 +645,7 @@ pub fn check_pat(pcx: &pat_ctxt, pat: &ast::Pat, expected: ty::t) { |expected, actual| { expected.map_or("".to_string(), |e| { - format!("mismatched types: expected `{}` but found {}", + format!("mismatched types: expected `{}`, found {}", e, actual) }) }, @@ -658,10 +658,10 @@ pub fn check_pat(pcx: &pat_ctxt, pat: &ast::Pat, expected: ty::t) { let (elt_type, region_var, mutbl, fixed) = match *structure_of(fcx, pat.span, expected) { - ty::ty_vec(mt, Some(fixed)) => - (mt.ty, default_region_var, ast::MutImmutable, Some(fixed)), + ty::ty_vec(ty, Some(fixed)) => + (ty, default_region_var, ast::MutImmutable, Some(fixed)), ty::ty_uniq(t) => match ty::get(t).sty { - ty::ty_vec(mt, None) => { + ty::ty_vec(ty, None) => { fcx.type_error_message(pat.span, |_| { "unique vector patterns are no \ @@ -669,7 +669,7 @@ pub fn check_pat(pcx: &pat_ctxt, pat: &ast::Pat, expected: ty::t) { }, expected, None); - (mt.ty, default_region_var, ast::MutImmutable, None) + (ty, default_region_var, ast::MutImmutable, None) } _ => { check_err("a vector pattern".to_string()); @@ -677,7 +677,7 @@ pub fn check_pat(pcx: &pat_ctxt, pat: &ast::Pat, expected: ty::t) { } }, ty::ty_rptr(r, mt) => match ty::get(mt.ty).sty { - ty::ty_vec(mt, None) => (mt.ty, r, mt.mutbl, None), + ty::ty_vec(ty, None) => (ty, r, mt.mutbl, None), _ => { check_err("a vector pattern".to_string()); return; @@ -763,7 +763,7 @@ fn check_pointer_pat(pcx: &pat_ctxt, span, |expected, actual| { expected.map_or("".to_string(), |e| { - format!("mismatched types: expected `{}` but found {}", + format!("mismatched types: expected `{}`, found {}", e, actual) }) }, diff --git a/src/librustc/middle/typeck/check/demand.rs b/src/librustc/middle/typeck/check/demand.rs index 2359f9d72d2..1b10b30b335 100644 --- a/src/librustc/middle/typeck/check/demand.rs +++ b/src/librustc/middle/typeck/check/demand.rs @@ -77,3 +77,17 @@ pub fn coerce(fcx: &FnCtxt, sp: Span, expected: ty::t, expr: &ast::Expr) { } } } + +pub fn coerce_with_fn(fcx: &FnCtxt, + sp: Span, + expected: ty::t, + expr: &ast::Expr, + handle_err: |Span, ty::t, ty::t, &ty::type_err|) { + let expr_ty = fcx.expr_ty(expr); + match fcx.mk_assignty(expr, expr_ty, expected) { + result::Ok(()) => { /* ok */ } + result::Err(ref err) => { + handle_err(sp, expected, expr_ty, err); + } + } +} diff --git a/src/librustc/middle/typeck/check/method.rs b/src/librustc/middle/typeck/check/method.rs index 1e3ea095704..3c15135807b 100644 --- a/src/librustc/middle/typeck/check/method.rs +++ b/src/librustc/middle/typeck/check/method.rs @@ -87,7 +87,6 @@ use middle::ty; use middle::typeck::astconv::AstConv; use middle::typeck::check::{FnCtxt, PreferMutLvalue, impl_self_ty}; use middle::typeck::check; -use middle::typeck::infer::MiscVariable; use middle::typeck::infer; use middle::typeck::MethodCallee; use middle::typeck::{MethodOrigin, MethodParam}; @@ -240,6 +239,7 @@ fn construct_transformed_self_ty_for_object( span: Span, trait_def_id: ast::DefId, rcvr_substs: &subst::Substs, + rcvr_bounds: ty::ExistentialBounds, method_ty: &ty::Method) -> ty::t { @@ -276,8 +276,7 @@ fn construct_transformed_self_ty_for_object( tcx.sess.span_bug(span, "static method for object type receiver"); } ByValueExplicitSelfCategory => { - let tr = ty::mk_trait(tcx, trait_def_id, obj_substs, - ty::empty_builtin_bounds()); + let tr = ty::mk_trait(tcx, trait_def_id, obj_substs, rcvr_bounds); ty::mk_uniq(tcx, tr) } ByReferenceExplicitSelfCategory(..) | ByBoxExplicitSelfCategory => { @@ -286,12 +285,12 @@ fn construct_transformed_self_ty_for_object( ty::ty_rptr(r, mt) => { // must be SelfRegion let r = r.subst(tcx, rcvr_substs); // handle Early-Bound lifetime let tr = ty::mk_trait(tcx, trait_def_id, obj_substs, - ty::empty_builtin_bounds()); + rcvr_bounds); ty::mk_rptr(tcx, r, ty::mt{ ty: tr, mutbl: mt.mutbl }) } ty::ty_uniq(_) => { // must be SelfUniq let tr = ty::mk_trait(tcx, trait_def_id, obj_substs, - ty::empty_builtin_bounds()); + rcvr_bounds); ty::mk_uniq(tcx, tr) } _ => { @@ -356,23 +355,14 @@ impl<'a> LookupContext<'a> { let span = self.self_expr.map_or(self.span, |e| e.span); let self_expr_id = self.self_expr.map(|e| e.id); - let (self_ty, autoderefs, result) = + let (_, _, result) = check::autoderef( self.fcx, span, self_ty, self_expr_id, PreferMutLvalue, |self_ty, autoderefs| self.search_step(self_ty, autoderefs)); match result { Some(Some(result)) => Some(result), - _ => { - if self.is_overloaded_deref() { - // If we are searching for an overloaded deref, no - // need to try coercing a `~[T]` to an `&[T]` and - // searching for an overloaded deref on *that*. - None - } else { - self.search_for_autosliced_method(self_ty, autoderefs) - } - } + _ => None } } @@ -408,6 +398,16 @@ impl<'a> LookupContext<'a> { } } + // If we are searching for an overloaded deref, no + // need to try coercing a `~[T]` to an `&[T]` and + // searching for an overloaded deref on *that*. + if !self.is_overloaded_deref() { + match self.search_for_autofatptrd_method(self_ty, autoderefs) { + Some(result) => return Some(Some(result)), + None => {} + } + } + // Don't autoderef if we aren't supposed to. if self.autoderef_receiver == DontAutoderefReceiver { Some(None) @@ -441,13 +441,11 @@ impl<'a> LookupContext<'a> { let span = self.self_expr.map_or(self.span, |e| e.span); check::autoderef(self.fcx, span, self_ty, None, PreferMutLvalue, |self_ty, _| { match get(self_ty).sty { - ty_uniq(ty) | ty_rptr(_, mt {ty, ..}) => match get(ty).sty{ - ty_trait(box TyTrait { def_id, ref substs, .. }) => { - self.push_inherent_candidates_from_object(def_id, substs); - self.push_inherent_impl_candidates_for_type(def_id); - } - _ => {} - }, + ty_trait(box TyTrait { def_id, ref substs, bounds, .. }) => { + self.push_inherent_candidates_from_object( + def_id, substs, bounds); + self.push_inherent_impl_candidates_for_type(def_id); + } ty_enum(did, _) | ty_struct(did, _) | ty_unboxed_closure(did, _) => { @@ -534,16 +532,19 @@ impl<'a> LookupContext<'a> { ty::MethodTraitItem(method) => method, }; + // Make sure it has the right name! + if method.ident.name != self.m_name { + return + } + let vcx = self.fcx.vtable_context(); - let region_params = - vec!(vcx.infcx.next_region_var(MiscVariable(self.span))); // Get the tupled type of the arguments. let arguments_type = *closure_function_type.sig.inputs.get(0); let return_type = closure_function_type.sig.output; let closure_region = - vcx.infcx.next_region_var(MiscVariable(self.span)); + vcx.infcx.next_region_var(infer::MiscVariable(self.span)); let unboxed_closure_type = ty::mk_unboxed_closure(self.tcx(), closure_did, closure_region); @@ -552,7 +553,7 @@ impl<'a> LookupContext<'a> { RcvrMatchesIfSubtype(unboxed_closure_type), rcvr_substs: subst::Substs::new_trait( vec![arguments_type, return_type], - region_params, + vec![], *vcx.infcx.next_ty_vars(1).get(0)), method_ty: method, origin: MethodStaticUnboxedClosure(closure_did), @@ -562,38 +563,28 @@ impl<'a> LookupContext<'a> { fn push_unboxed_closure_call_candidates_if_applicable( &mut self, closure_did: DefId) { - let trait_dids = [ - self.tcx().lang_items.fn_trait(), - self.tcx().lang_items.fn_mut_trait(), - self.tcx().lang_items.fn_once_trait() - ]; - for optional_trait_did in trait_dids.iter() { - let trait_did = match *optional_trait_did { - Some(trait_did) => trait_did, - None => continue, - }; - - match self.tcx().unboxed_closures.borrow().find(&closure_did) { - None => {} // Fall through to try inherited. - Some(closure) => { - self.push_unboxed_closure_call_candidate_if_applicable( - trait_did, - closure_did, - &closure.closure_type); - return - } + match self.tcx().unboxed_closures.borrow().find(&closure_did) { + None => {} // Fall through to try inherited. + Some(closure) => { + let tcx = self.tcx(); + self.push_unboxed_closure_call_candidate_if_applicable( + closure.kind.trait_did(tcx), + closure_did, + &closure.closure_type); + return } + } - match self.fcx.inh.unboxed_closures.borrow().find(&closure_did) { - Some(closure) => { - self.push_unboxed_closure_call_candidate_if_applicable( - trait_did, - closure_did, - &closure.closure_type); - return - } - None => {} + match self.fcx.inh.unboxed_closures.borrow().find(&closure_did) { + Some(closure) => { + let tcx = self.tcx(); + self.push_unboxed_closure_call_candidate_if_applicable( + closure.kind.trait_did(tcx), + closure_did, + &closure.closure_type); + return } + None => {} } self.tcx().sess.bug("didn't find unboxed closure type in tcx map or \ @@ -602,11 +593,11 @@ impl<'a> LookupContext<'a> { fn push_inherent_candidates_from_object(&mut self, did: DefId, - substs: &subst::Substs) { + substs: &subst::Substs, + bounds: ty::ExistentialBounds) { debug!("push_inherent_candidates_from_object(did={}, substs={})", self.did_to_string(did), substs.repr(self.tcx())); - let _indenter = indenter(); let tcx = self.tcx(); let span = self.span; @@ -624,28 +615,30 @@ impl<'a> LookupContext<'a> { substs: rcvr_substs.clone() }); - self.push_inherent_candidates_from_bounds_inner(&[trait_ref.clone()], - |new_trait_ref, m, method_num, _bound_num| { - let vtable_index = get_method_index(tcx, &*new_trait_ref, - trait_ref.clone(), method_num); - let mut m = (*m).clone(); - // We need to fix up the transformed self type. - *m.fty.sig.inputs.get_mut(0) = - construct_transformed_self_ty_for_object( - tcx, span, did, &rcvr_substs, &m); - - Some(Candidate { - rcvr_match_condition: RcvrMatchesIfObject(did), - rcvr_substs: new_trait_ref.substs.clone(), - method_ty: Rc::new(m), - origin: MethodObject(MethodObject { + self.push_inherent_candidates_from_bounds_inner( + &[trait_ref.clone()], + |_this, new_trait_ref, m, method_num, _bound_num| { + let vtable_index = + get_method_index(tcx, &*new_trait_ref, + trait_ref.clone(), method_num); + let mut m = (*m).clone(); + // We need to fix up the transformed self type. + *m.fty.sig.inputs.get_mut(0) = + construct_transformed_self_ty_for_object( + tcx, span, did, &rcvr_substs, bounds, &m); + + Some(Candidate { + rcvr_match_condition: RcvrMatchesIfObject(did), + rcvr_substs: new_trait_ref.substs.clone(), + method_ty: Rc::new(m), + origin: MethodObject(MethodObject { trait_id: new_trait_ref.def_id, object_trait_id: did, method_num: method_num, real_index: vtable_index }) - }) - }); + }) + }); } fn push_inherent_candidates_from_param(&mut self, @@ -673,7 +666,7 @@ impl<'a> LookupContext<'a> { self.fcx.inh.param_env.bounds.get(space, index).trait_bounds .as_slice(); self.push_inherent_candidates_from_bounds_inner(bounds, - |trait_ref, m, method_num, bound_num| { + |this, trait_ref, m, method_num, bound_num| { match restrict_to { Some(trait_did) => { if trait_did != trait_ref.def_id { @@ -682,6 +675,18 @@ impl<'a> LookupContext<'a> { } _ => {} } + debug!("found match: trait_ref={} substs={} m={}", + trait_ref.repr(this.tcx()), + trait_ref.substs.repr(this.tcx()), + m.repr(this.tcx())); + assert_eq!(m.generics.types.get_slice(subst::TypeSpace).len(), + trait_ref.substs.types.get_slice(subst::TypeSpace).len()); + assert_eq!(m.generics.regions.get_slice(subst::TypeSpace).len(), + trait_ref.substs.regions().get_slice(subst::TypeSpace).len()); + assert_eq!(m.generics.types.get_slice(subst::SelfSpace).len(), + trait_ref.substs.types.get_slice(subst::SelfSpace).len()); + assert_eq!(m.generics.regions.get_slice(subst::SelfSpace).len(), + trait_ref.substs.regions().get_slice(subst::SelfSpace).len()); Some(Candidate { rcvr_match_condition: RcvrMatchesIfSubtype(self_ty), rcvr_substs: trait_ref.substs.clone(), @@ -698,13 +703,15 @@ impl<'a> LookupContext<'a> { // Do a search through a list of bounds, using a callback to actually // create the candidates. - fn push_inherent_candidates_from_bounds_inner(&mut self, - bounds: &[Rc<TraitRef>], - mk_cand: |tr: Rc<TraitRef>, - m: Rc<ty::Method>, - method_num: uint, - bound_num: uint| - -> Option<Candidate>) { + fn push_inherent_candidates_from_bounds_inner( + &mut self, + bounds: &[Rc<TraitRef>], + mk_cand: |this: &mut LookupContext, + tr: Rc<TraitRef>, + m: Rc<ty::Method>, + method_num: uint, + bound_num: uint| + -> Option<Candidate>) { let tcx = self.tcx(); let mut next_bound_idx = 0; // count only trait bounds @@ -726,7 +733,8 @@ impl<'a> LookupContext<'a> { ty::MethodTraitItem(ref method) => (*method).clone(), }; - match mk_cand(bound_trait_ref, + match mk_cand(self, + bound_trait_ref, method, pos, this_bound_idx) { @@ -835,23 +843,22 @@ impl<'a> LookupContext<'a> { self_ty: ty::t, autoderefs: uint) -> Option<MethodCallee> { - let (self_ty, auto_deref_ref) = - self.consider_reborrow(self_ty, autoderefs); - // Hacky. For overloaded derefs, there may be an adjustment // added to the expression from the outside context, so we do not store // an explicit adjustment, but rather we hardwire the single deref // that occurs in trans and mem_categorization. - let adjustment = match self.self_expr { - Some(expr) => Some((expr.id, ty::AutoDerefRef(auto_deref_ref))), - None => return None - }; + if self.self_expr.is_none() { + return None; + } + + let (self_ty, auto_deref_ref) = self.consider_reborrow(self_ty, autoderefs); + let adjustment = Some((self.self_expr.unwrap().id, ty::AutoDerefRef(auto_deref_ref))); match self.search_for_method(self_ty) { None => None, Some(method) => { debug!("(searching for autoderef'd method) writing \ - adjustment {:?} for {}", adjustment, self.ty_to_string( self_ty)); + adjustment {:?} for {}", adjustment, self.ty_to_string(self_ty)); match adjustment { Some((self_expr_id, adj)) => { self.fcx.write_adjustment(self_expr_id, adj); @@ -895,16 +902,10 @@ impl<'a> LookupContext<'a> { ty::ty_rptr(_, self_mt) => { let region = self.infcx().next_region_var(infer::Autoref(self.span)); - let (extra_derefs, auto) = match ty::get(self_mt.ty).sty { - ty::ty_vec(_, None) => (0, ty::AutoBorrowVec(region, self_mt.mutbl)), - ty::ty_str => (0, ty::AutoBorrowVec(region, self_mt.mutbl)), - ty::ty_trait(..) => (0, ty::AutoBorrowObj(region, self_mt.mutbl)), - _ => (1, ty::AutoPtr(region, self_mt.mutbl)), - }; (ty::mk_rptr(tcx, region, self_mt), ty::AutoDerefRef { - autoderefs: autoderefs + extra_derefs, - autoref: Some(auto)}) + autoderefs: autoderefs + 1, + autoref: Some(ty::AutoPtr(region, self_mt.mutbl, None))}) } _ => { (self_ty, @@ -925,15 +926,18 @@ impl<'a> LookupContext<'a> { } } - fn auto_slice_vec(&self, mt: ty::mt, autoderefs: uint) -> Option<MethodCallee> { + // Takes an [T] - an unwrapped DST pointer (either ~ or &) + // [T] to &[T] or &&[T] (note that we started with a &[T] or ~[T] which has + // been implicitly derefed). + fn auto_slice_vec(&self, ty: ty::t, autoderefs: uint) -> Option<MethodCallee> { let tcx = self.tcx(); - debug!("auto_slice_vec {}", ppaux::ty_to_string(tcx, mt.ty)); + debug!("auto_slice_vec {}", ppaux::ty_to_string(tcx, ty)); // First try to borrow to a slice let entry = self.search_for_some_kind_of_autorefd_method( - AutoBorrowVec, autoderefs, [MutImmutable, MutMutable], + |r, m| AutoPtr(r, m, None), autoderefs, [MutImmutable, MutMutable], |m,r| ty::mk_slice(tcx, r, - ty::mt {ty:mt.ty, mutbl:m})); + ty::mt {ty:ty, mutbl:m})); if entry.is_some() { return entry; @@ -941,10 +945,11 @@ impl<'a> LookupContext<'a> { // Then try to borrow to a slice *and* borrow a pointer. self.search_for_some_kind_of_autorefd_method( - AutoBorrowVecRef, autoderefs, [MutImmutable, MutMutable], - |m,r| { + |r, m| AutoPtr(r, ast::MutImmutable, Some( box AutoPtr(r, m, None))), + autoderefs, [MutImmutable, MutMutable], + |m, r| { let slice_ty = ty::mk_slice(tcx, r, - ty::mt {ty:mt.ty, mutbl:m}); + ty::mt {ty:ty, mutbl:m}); // NB: we do not try to autoref to a mutable // pointer. That would be creating a pointer // to a temporary pointer (the borrowed @@ -954,22 +959,59 @@ impl<'a> LookupContext<'a> { }) } + // [T, ..len] -> [T] or &[T] or &&[T] + fn auto_unsize_vec(&self, ty: ty::t, autoderefs: uint, len: uint) -> Option<MethodCallee> { + let tcx = self.tcx(); + debug!("auto_unsize_vec {}", ppaux::ty_to_string(tcx, ty)); + + // First try to borrow to an unsized vec. + let entry = self.search_for_some_kind_of_autorefd_method( + |_r, _m| AutoUnsize(ty::UnsizeLength(len)), + autoderefs, [MutImmutable, MutMutable], + |_m, _r| ty::mk_vec(tcx, ty, None)); + + if entry.is_some() { + return entry; + } + + // Then try to borrow to a slice. + let entry = self.search_for_some_kind_of_autorefd_method( + |r, m| AutoPtr(r, m, Some(box AutoUnsize(ty::UnsizeLength(len)))), + autoderefs, [MutImmutable, MutMutable], + |m, r| ty::mk_slice(tcx, r, ty::mt {ty:ty, mutbl:m})); + + if entry.is_some() { + return entry; + } + + // Then try to borrow to a slice *and* borrow a pointer. + self.search_for_some_kind_of_autorefd_method( + |r, m| AutoPtr(r, m, + Some(box AutoPtr(r, m, + Some(box AutoUnsize(ty::UnsizeLength(len)))))), + autoderefs, [MutImmutable, MutMutable], + |m, r| { + let slice_ty = ty::mk_slice(tcx, r, ty::mt {ty:ty, mutbl:m}); + ty::mk_rptr(tcx, r, ty::mt {ty:slice_ty, mutbl:MutImmutable}) + }) + } fn auto_slice_str(&self, autoderefs: uint) -> Option<MethodCallee> { let tcx = self.tcx(); debug!("auto_slice_str"); let entry = self.search_for_some_kind_of_autorefd_method( - AutoBorrowVec, autoderefs, [MutImmutable], - |_m,r| ty::mk_str_slice(tcx, r, MutImmutable)); + |r, m| AutoPtr(r, m, None), autoderefs, [MutImmutable], + |_m, r| ty::mk_str_slice(tcx, r, MutImmutable)); if entry.is_some() { return entry; } self.search_for_some_kind_of_autorefd_method( - AutoBorrowVecRef, autoderefs, [MutImmutable], - |m,r| { + |r, m| AutoPtr(r, ast::MutImmutable, Some( box AutoPtr(r, m, None))), + autoderefs, [MutImmutable], + |m, r| { let slice_ty = ty::mk_str_slice(tcx, r, m); ty::mk_rptr(tcx, r, ty::mt {ty:slice_ty, mutbl:m}) }) @@ -977,6 +1019,7 @@ impl<'a> LookupContext<'a> { // Coerce Box/&Trait instances to &Trait. fn auto_slice_trait(&self, ty: ty::t, autoderefs: uint) -> Option<MethodCallee> { + debug!("auto_slice_trait"); match ty::get(ty).sty { ty_trait(box ty::TyTrait { def_id: trt_did, @@ -985,7 +1028,8 @@ impl<'a> LookupContext<'a> { .. }) => { let tcx = self.tcx(); self.search_for_some_kind_of_autorefd_method( - AutoBorrowObj, autoderefs, [MutImmutable, MutMutable], + |r, m| AutoPtr(r, m, None), + autoderefs, [MutImmutable, MutMutable], |m, r| { let tr = ty::mk_trait(tcx, trt_did, trt_substs.clone(), b); ty::mk_rptr(tcx, r, ty::mt{ ty: tr, mutbl: m }) @@ -995,31 +1039,24 @@ impl<'a> LookupContext<'a> { } } - fn search_for_autosliced_method(&self, - self_ty: ty::t, - autoderefs: uint) - -> Option<MethodCallee> { + fn search_for_autofatptrd_method(&self, + self_ty: ty::t, + autoderefs: uint) + -> Option<MethodCallee> { /*! * Searches for a candidate by converting things like * `~[]` to `&[]`. */ - debug!("search_for_autosliced_method {}", ppaux::ty_to_string(self.tcx(), self_ty)); + let tcx = self.tcx(); + debug!("search_for_autofatptrd_method {}", ppaux::ty_to_string(tcx, self_ty)); let sty = ty::get(self_ty).sty.clone(); match sty { - ty_rptr(_, mt) => match ty::get(mt.ty).sty { - ty_vec(mt, None) => self.auto_slice_vec(mt, autoderefs), - ty_trait(..) => self.auto_slice_trait(mt.ty, autoderefs), - _ => None - }, - ty_uniq(t) => match ty::get(t).sty { - ty_vec(mt, None) => self.auto_slice_vec(mt, autoderefs), - ty_str => self.auto_slice_str(autoderefs), - ty_trait(..) => self.auto_slice_trait(t, autoderefs), - _ => None - }, - ty_vec(mt, Some(_)) => self.auto_slice_vec(mt, autoderefs), + ty_vec(ty, Some(len)) => self.auto_unsize_vec(ty, autoderefs, len), + ty_vec(ty, None) => self.auto_slice_vec(ty, autoderefs), + ty_str => self.auto_slice_str(autoderefs), + ty_trait(..) => self.auto_slice_trait(self_ty, autoderefs), ty_closure(..) => { // This case should probably be handled similarly to @@ -1047,10 +1084,10 @@ impl<'a> LookupContext<'a> { ty_param(..) | ty_nil | ty_bot | ty_bool | ty_char | ty_int(..) | ty_uint(..) | ty_float(..) | ty_enum(..) | ty_ptr(..) | ty_struct(..) | - ty_unboxed_closure(..) | ty_tup(..) | + ty_unboxed_closure(..) | ty_tup(..) | ty_open(..) | ty_str | ty_vec(..) | ty_trait(..) | ty_closure(..) => { self.search_for_some_kind_of_autorefd_method( - AutoPtr, autoderefs, [MutImmutable, MutMutable], + |r, m| AutoPtr(r, m, None), autoderefs, [MutImmutable, MutMutable], |m,r| ty::mk_rptr(tcx, r, ty::mt {ty:self_ty, mutbl:m})) } @@ -1078,8 +1115,8 @@ impl<'a> LookupContext<'a> { Some(expr) => Some(expr.id), None => { assert_eq!(autoderefs, 0); - assert_eq!(kind(ty::ReEmpty, ast::MutImmutable), - ty::AutoPtr(ty::ReEmpty, ast::MutImmutable)); + assert!(kind(ty::ReEmpty, ast::MutImmutable) == + ty::AutoPtr(ty::ReEmpty, ast::MutImmutable, None)); None } }; @@ -1316,6 +1353,11 @@ impl<'a> LookupContext<'a> { } } + self.fcx.add_region_obligations_for_parameters( + self.span, + &all_substs, + &candidate.method_ty.generics); + MethodCallee { origin: candidate.origin, ty: fty, @@ -1447,17 +1489,15 @@ impl<'a> LookupContext<'a> { match ty::get(rcvr_ty).sty { ty::ty_rptr(_, mt) => { match ty::get(mt.ty).sty { - ty::ty_vec(_, None) | ty::ty_str => false, ty::ty_trait(box ty::TyTrait { def_id: self_did, .. }) => { mutability_matches(mt.mutbl, m) && rcvr_matches_object(self_did, candidate) } _ => mutability_matches(mt.mutbl, m) && - rcvr_matches_ty(self.fcx, mt.ty, candidate), + rcvr_matches_ty(self.fcx, mt.ty, candidate) } } - _ => false } } @@ -1467,7 +1507,6 @@ impl<'a> LookupContext<'a> { match ty::get(rcvr_ty).sty { ty::ty_uniq(typ) => { match ty::get(typ).sty { - ty::ty_vec(_, None) | ty::ty_str => false, ty::ty_trait(box ty::TyTrait { def_id: self_did, .. }) => { rcvr_matches_object(self_did, candidate) } diff --git a/src/librustc/middle/typeck/check/mod.rs b/src/librustc/middle/typeck/check/mod.rs index 7896316a472..b9e96c78533 100644 --- a/src/librustc/middle/typeck/check/mod.rs +++ b/src/librustc/middle/typeck/check/mod.rs @@ -79,7 +79,10 @@ type parameter). use middle::const_eval; use middle::def; +use middle::freevars; use middle::lang_items::IteratorItem; +use middle::mem_categorization::McResult; +use middle::mem_categorization; use middle::pat_util::pat_id_map; use middle::pat_util; use middle::subst; @@ -99,7 +102,6 @@ use middle::typeck::check::method::{CheckTraitsAndInherentMethods}; use middle::typeck::check::method::{DontAutoderefReceiver}; use middle::typeck::check::method::{IgnoreStaticMethods, ReportStaticMethods}; use middle::typeck::check::regionmanip::replace_late_bound_regions_in_fn_sig; -use middle::typeck::check::regionmanip::relate_free_regions; use middle::typeck::check::vtable::VtableContext; use middle::typeck::CrateCtxt; use middle::typeck::infer::{resolve_type, force_tvar}; @@ -110,6 +112,7 @@ use middle::typeck::no_params; use middle::typeck::{require_same_types, vtable_map}; use middle::typeck::{MethodCall, MethodMap}; use middle::typeck::{TypeAndSubsts}; +use middle::typeck; use middle::lang_items::TypeIdLangItem; use lint; use util::common::{block_query, indenter, loop_query}; @@ -169,6 +172,43 @@ pub struct Inherited<'a> { vtable_map: vtable_map, upvar_borrow_map: RefCell<ty::UpvarBorrowMap>, unboxed_closures: RefCell<DefIdMap<ty::UnboxedClosure>>, + + // A mapping from each fn's id to its signature, with all bound + // regions replaced with free ones. Unlike the other tables, this + // one is never copied into the tcx: it is only used by regionck. + fn_sig_map: RefCell<NodeMap<Vec<ty::t>>>, + + // A set of constraints that regionck must validate. Each + // constraint has the form `T:'a`, meaning "some type `T` must + // outlive the lifetime 'a". These constraints derive from + // instantiated type parameters. So if you had a struct defined + // like + // + // struct Foo<T:'static> { ... } + // + // then in some expression `let x = Foo { ... }` it will + // instantiate the type parameter `T` with a fresh type `$0`. At + // the same time, it will record a region obligation of + // `$0:'static`. This will get checked later by regionck. (We + // can't generally check these things right away because we have + // to wait until types are resolved.) + // + // These are stored in a map keyed to the id of the innermost + // enclosing fn body / static initializer expression. This is + // because the location where the obligation was incurred can be + // relevant with respect to which sublifetime assumptions are in + // place. The reason that we store under the fn-id, and not + // something more fine-grained, is so that it is easier for + // regionck to be sure that it has found *all* the region + // obligations (otherwise, it's easy to fail to walk to a + // particular node-id). + region_obligations: RefCell<NodeMap<Vec<RegionObligation>>>, +} + +struct RegionObligation { + sub_region: ty::Region, + sup_type: ty::t, + origin: infer::SubregionOrigin, } /// When type-checking an expression, we propagate downward @@ -228,6 +268,8 @@ enum IsBinopAssignment{ #[deriving(Clone)] pub struct FnCtxt<'a> { + body_id: ast::NodeId, + // This flag is set to true if, during the writeback phase, we encounter // a type error in this function. writeback_errors: Cell<bool>, @@ -239,28 +281,47 @@ pub struct FnCtxt<'a> { err_count_on_creation: uint, ret_ty: ty::t, - ps: RefCell<FnStyleState>, - // Sometimes we generate region pointers where the precise region - // to use is not known. For example, an expression like `&x.f` - // where `x` is of type `@T`: in this case, we will be rooting - // `x` onto the stack frame, and we could choose to root it until - // the end of (almost) any enclosing block or expression. We - // want to pick the narrowest block that encompasses all uses. - // - // What we do in such cases is to generate a region variable with - // `region_lb` as a lower bound. The regionck pass then adds - // other constraints based on how the variable is used and region - // inference selects the ultimate value. Finally, borrowck is - // charged with guaranteeing that the value whose address was taken - // can actually be made to live as long as it needs to live. - region_lb: Cell<ast::NodeId>, + ps: RefCell<FnStyleState>, inh: &'a Inherited<'a>, ccx: &'a CrateCtxt<'a>, } +impl<'a> mem_categorization::Typer for FnCtxt<'a> { + fn tcx<'a>(&'a self) -> &'a ty::ctxt { + self.ccx.tcx + } + fn node_ty(&self, id: ast::NodeId) -> McResult<ty::t> { + self.ccx.tcx.node_ty(id) + } + fn node_method_ty(&self, method_call: typeck::MethodCall) + -> Option<ty::t> { + self.ccx.tcx.node_method_ty(method_call) + } + fn adjustments<'a>(&'a self) -> &'a RefCell<NodeMap<ty::AutoAdjustment>> { + self.ccx.tcx.adjustments() + } + fn is_method_call(&self, id: ast::NodeId) -> bool { + self.ccx.tcx.is_method_call(id) + } + fn temporary_scope(&self, rvalue_id: ast::NodeId) -> Option<ast::NodeId> { + self.ccx.tcx.temporary_scope(rvalue_id) + } + fn upvar_borrow(&self, upvar_id: ty::UpvarId) -> ty::UpvarBorrow { + self.ccx.tcx.upvar_borrow(upvar_id) + } + fn capture_mode(&self, closure_expr_id: ast::NodeId) + -> freevars::CaptureMode { + self.ccx.tcx.capture_mode(closure_expr_id) + } + fn unboxed_closures<'a>(&'a self) + -> &'a RefCell<DefIdMap<ty::UnboxedClosure>> { + &self.inh.unboxed_closures + } +} + impl<'a> Inherited<'a> { fn new(tcx: &'a ty::ctxt, param_env: ty::ParameterEnvironment) @@ -276,6 +337,8 @@ impl<'a> Inherited<'a> { vtable_map: RefCell::new(FnvHashMap::new()), upvar_borrow_map: RefCell::new(HashMap::new()), unboxed_closures: RefCell::new(DefIdMap::new()), + fn_sig_map: RefCell::new(NodeMap::new()), + region_obligations: RefCell::new(NodeMap::new()), } } } @@ -285,25 +348,26 @@ pub fn blank_fn_ctxt<'a>( ccx: &'a CrateCtxt<'a>, inh: &'a Inherited<'a>, rty: ty::t, - region_bnd: ast::NodeId) + body_id: ast::NodeId) -> FnCtxt<'a> { FnCtxt { + body_id: body_id, writeback_errors: Cell::new(false), err_count_on_creation: ccx.tcx.sess.err_count(), ret_ty: rty, ps: RefCell::new(FnStyleState::function(ast::NormalFn, 0)), - region_lb: Cell::new(region_bnd), inh: inh, ccx: ccx } } -fn blank_inherited_fields<'a>(ccx: &'a CrateCtxt<'a>) -> Inherited<'a> { +fn static_inherited_fields<'a>(ccx: &'a CrateCtxt<'a>) -> Inherited<'a> { // It's kind of a kludge to manufacture a fake function context // and statement context, but we might as well do write the code only once let param_env = ty::ParameterEnvironment { free_substs: subst::Substs::empty(), - bounds: subst::VecPerParamSpace::empty() + bounds: subst::VecPerParamSpace::empty(), + implicit_region_bound: ty::ReStatic, }; Inherited::new(ccx.tcx, param_env) } @@ -318,6 +382,15 @@ impl<'a> ExprTyProvider for FnCtxt<'a> { } } +struct CheckTypeWellFormedVisitor<'a> { ccx: &'a CrateCtxt<'a> } + +impl<'a> Visitor<()> for CheckTypeWellFormedVisitor<'a> { + fn visit_item(&mut self, i: &ast::Item, _: ()) { + check_type_well_formed(self.ccx, i); + visit::walk_item(self, i, ()); + } +} + struct CheckItemTypesVisitor<'a> { ccx: &'a CrateCtxt<'a> } impl<'a> Visitor<()> for CheckItemTypesVisitor<'a> { @@ -337,6 +410,13 @@ impl<'a> Visitor<()> for CheckItemSizedTypesVisitor<'a> { } pub fn check_item_types(ccx: &CrateCtxt, krate: &ast::Crate) { + let mut visit = CheckTypeWellFormedVisitor { ccx: ccx }; + visit::walk_crate(&mut visit, krate, ()); + + // If types are not well-formed, it leads to all manner of errors + // downstream, so stop reporting errors at this point. + ccx.tcx.sess.abort_if_errors(); + let mut visit = CheckItemTypesVisitor { ccx: ccx }; visit::walk_crate(&mut visit, krate, ()); @@ -359,11 +439,11 @@ fn check_bare_fn(ccx: &CrateCtxt, match ty::get(fty).sty { ty::ty_bare_fn(ref fn_ty) => { let inh = Inherited::new(ccx.tcx, param_env); - let fcx = check_fn(ccx, fn_ty.fn_style, &fn_ty.sig, + let fcx = check_fn(ccx, fn_ty.fn_style, id, &fn_ty.sig, decl, id, body, &inh); vtable::resolve_in_block(&fcx, body); - regionck::regionck_fn(&fcx, body); + regionck::regionck_fn(&fcx, id, body); writeback::resolve_type_vars_in_fn(&fcx, decl, body); } _ => ccx.tcx.sess.impossible_case(body.span, @@ -428,7 +508,7 @@ impl<'a> Visitor<()> for GatherLocalsVisitor<'a> { // non-obvious: the `blk` variable maps to region lb, so // we have to keep this up-to-date. This // is... unfortunate. It'd be nice to not need this. - self.fcx.with_region_lb(b.id, || visit::walk_block(self, b, ())); + visit::walk_block(self, b, ()); } // Since an expr occurs as part of the type fixed size arrays we @@ -450,13 +530,16 @@ impl<'a> Visitor<()> for GatherLocalsVisitor<'a> { } -fn check_fn<'a>(ccx: &'a CrateCtxt<'a>, - fn_style: ast::FnStyle, - fn_sig: &ty::FnSig, - decl: &ast::FnDecl, - id: ast::NodeId, - body: &ast::Block, - inherited: &'a Inherited<'a>) -> FnCtxt<'a> +fn check_fn<'a>( + ccx: &'a CrateCtxt<'a>, + fn_style: ast::FnStyle, + fn_style_id: ast::NodeId, + fn_sig: &ty::FnSig, + decl: &ast::FnDecl, + fn_id: ast::NodeId, + body: &ast::Block, + inherited: &'a Inherited<'a>) + -> FnCtxt<'a> { /*! * Helper used by check_bare_fn and check_expr_fn. Does the @@ -477,30 +560,42 @@ fn check_fn<'a>(ccx: &'a CrateCtxt<'a>, ty::ReFree(ty::FreeRegion {scope_id: body.id, bound_region: br}) }); - relate_free_regions(tcx, &fn_sig); - let arg_tys = fn_sig.inputs.as_slice(); let ret_ty = fn_sig.output; - debug!("check_fn(arg_tys={}, ret_ty={})", + debug!("check_fn(arg_tys={}, ret_ty={}, fn_id={})", arg_tys.repr(tcx), - ret_ty.repr(tcx)); + ret_ty.repr(tcx), + fn_id); // Create the function context. This is either derived from scratch or, // in the case of function expressions, based on the outer context. let fcx = FnCtxt { + body_id: body.id, writeback_errors: Cell::new(false), err_count_on_creation: err_count_on_creation, ret_ty: ret_ty, - ps: RefCell::new(FnStyleState::function(fn_style, id)), - region_lb: Cell::new(body.id), + ps: RefCell::new(FnStyleState::function(fn_style, fn_style_id)), inh: inherited, ccx: ccx }; - { + // Remember return type so that regionck can access it later. + let fn_sig_tys: Vec<ty::t> = + arg_tys.iter() + .chain([ret_ty].iter()) + .map(|&ty| ty) + .collect(); + debug!("fn-sig-map: fn_id={} fn_sig_tys={}", + fn_id, + fn_sig_tys.repr(tcx)); + inherited.fn_sig_map + .borrow_mut() + .insert(fn_id, fn_sig_tys); + { let mut visit = GatherLocalsVisitor { fcx: &fcx, }; + // Add formal parameters. for (arg_ty, input) in arg_tys.iter().zip(decl.inputs.iter()) { // Create type variables for each argument. @@ -523,21 +618,6 @@ fn check_fn<'a>(ccx: &'a CrateCtxt<'a>, check_block_with_expected(&fcx, body, ExpectHasType(ret_ty)); - // We unify the tail expr's type with the - // function result type, if there is a tail expr. - match body.expr { - Some(ref tail_expr) => { - // Special case: we print a special error if there appears - // to be do-block/for-loop confusion - demand::suptype_with_fn(&fcx, tail_expr.span, false, - fcx.ret_ty, fcx.expr_ty(&**tail_expr), - |sp, e, a, s| { - fcx.report_mismatched_return_types(sp, e, a, s); - }); - } - None => {} - } - for (input, arg) in decl.inputs.iter().zip(arg_tys.iter()) { fcx.write_ty(input.id, *arg); } @@ -640,6 +720,71 @@ pub fn check_struct(ccx: &CrateCtxt, id: ast::NodeId, span: Span) { } } +fn check_type_well_formed(ccx: &CrateCtxt, item: &ast::Item) { + /*! + * Checks that the field types (in a struct def'n) or + * argument types (in an enum def'n) are well-formed, + * meaning that they do not require any constraints not + * declared in the struct definition itself. + * For example, this definition would be illegal: + * + * struct Ref<'a, T> { x: &'a T } + * + * because the type did not declare that `T:'a`. + * + * We do this check as a pre-pass before checking fn bodies + * because if these constraints are not included it frequently + * leads to confusing errors in fn bodies. So it's better to check + * the types first. + */ + + debug!("check_type_well_formed(it.id={}, it.ident={})", + item.id, + ty::item_path_str(ccx.tcx, local_def(item.id))); + + match item.node { + ast::ItemStruct(..) => { + check_type_defn(ccx, item, |fcx| { + ty::struct_fields(ccx.tcx, local_def(item.id), + &fcx.inh.param_env.free_substs) + .iter() + .map(|f| f.mt.ty) + .collect() + }); + } + ast::ItemEnum(..) => { + check_type_defn(ccx, item, |fcx| { + ty::substd_enum_variants(ccx.tcx, local_def(item.id), + &fcx.inh.param_env.free_substs) + .iter() + .flat_map(|variant| { + variant.args + .iter() + .map(|&arg_ty| arg_ty) + }) + .collect() + }); + } + _ => {} + } + + fn check_type_defn(ccx: &CrateCtxt, + item: &ast::Item, + lookup_fields: |&FnCtxt| -> Vec<ty::t>) + { + let item_def_id = local_def(item.id); + let polytype = ty::lookup_item_type(ccx.tcx, item_def_id); + let param_env = + ty::construct_parameter_environment(ccx.tcx, + &polytype.generics, + item.id); + let inh = Inherited::new(ccx.tcx, param_env); + let fcx = blank_fn_ctxt(ccx, &inh, polytype.ty, item.id); + let field_tys = lookup_fields(&fcx); + regionck::regionck_type_defn(&fcx, item.span, field_tys.as_slice()); + } +} + pub fn check_item_sized(ccx: &CrateCtxt, it: &ast::Item) { debug!("check_item(it.id={}, it.ident={})", it.id, @@ -955,9 +1100,6 @@ fn compare_impl_method(tcx: &ty::ctxt, return; } - let it = trait_m.generics.types.get_slice(subst::FnSpace).iter() - .zip(impl_m.generics.types.get_slice(subst::FnSpace).iter()); - // This code is best explained by example. Consider a trait: // // trait Trait<T> { @@ -1019,20 +1161,27 @@ fn compare_impl_method(tcx: &ty::ctxt, let impl_to_skol_substs = subst::Substs::new(skol_tps.clone(), skol_regions.clone()); - // Compute skolemized form of impl method ty. - let impl_fty = ty::mk_bare_fn(tcx, impl_m.fty.clone()); - let impl_fty = impl_fty.subst(tcx, &impl_to_skol_substs); - - // Compute skolemized form of trait method ty. + // Create mapping from trait to skolemized. let trait_to_skol_substs = trait_to_impl_substs .subst(tcx, &impl_to_skol_substs) .with_method(Vec::from_slice(skol_tps.get_slice(subst::FnSpace)), Vec::from_slice(skol_regions.get_slice(subst::FnSpace))); - let trait_fty = ty::mk_bare_fn(tcx, trait_m.fty.clone()); - let trait_fty = trait_fty.subst(tcx, &trait_to_skol_substs); + + // Check region bounds. + if !check_region_bounds_on_impl_method(tcx, + impl_m_span, + impl_m, + &trait_m.generics, + &impl_m.generics, + &trait_to_skol_substs, + &impl_to_skol_substs) { + return; + } // Check bounds. + let it = trait_m.generics.types.get_slice(subst::FnSpace).iter() + .zip(impl_m.generics.types.get_slice(subst::FnSpace).iter()); for (i, (trait_param_def, impl_param_def)) in it.enumerate() { // Check that the impl does not require any builtin-bounds // that the trait does not guarantee: @@ -1088,6 +1237,12 @@ fn compare_impl_method(tcx: &ty::ctxt, } } + // Compute skolemized form of impl and trait method tys. + let impl_fty = ty::mk_bare_fn(tcx, impl_m.fty.clone()); + let impl_fty = impl_fty.subst(tcx, &impl_to_skol_substs); + let trait_fty = ty::mk_bare_fn(tcx, trait_m.fty.clone()); + let trait_fty = trait_fty.subst(tcx, &trait_to_skol_substs); + // Check the impl method type IM is a subtype of the trait method // type TM. To see why this makes sense, think of a vtable. The // expected type of the function pointers in the vtable is the @@ -1112,6 +1267,152 @@ fn compare_impl_method(tcx: &ty::ctxt, // Finally, resolve all regions. This catches wily misuses of lifetime // parameters. infcx.resolve_regions_and_report_errors(); + + fn check_region_bounds_on_impl_method(tcx: &ty::ctxt, + span: Span, + impl_m: &ty::Method, + trait_generics: &ty::Generics, + impl_generics: &ty::Generics, + trait_to_skol_substs: &Substs, + impl_to_skol_substs: &Substs) + -> bool + { + /*! + + Check that region bounds on impl method are the same as those + on the trait. In principle, it could be ok for there to be + fewer region bounds on the impl method, but this leads to an + annoying corner case that is painful to handle (described + below), so for now we can just forbid it. + + Example (see + `src/test/compile-fail/regions-bound-missing-bound-in-impl.rs`): + + trait Foo<'a> { + fn method1<'b>(); + fn method2<'b:'a>(); + } + + impl<'a> Foo<'a> for ... { + fn method1<'b:'a>() { .. case 1, definitely bad .. } + fn method2<'b>() { .. case 2, could be ok .. } + } + + The "definitely bad" case is case #1. Here, the impl adds an + extra constraint not present in the trait. + + The "maybe bad" case is case #2. Here, the impl adds an extra + constraint not present in the trait. We could in principle + allow this, but it interacts in a complex way with early/late + bound resolution of lifetimes. Basically the presence or + absence of a lifetime bound affects whether the lifetime is + early/late bound, and right now the code breaks if the trait + has an early bound lifetime parameter and the method does not. + + */ + + let trait_params = trait_generics.regions.get_slice(subst::FnSpace); + let impl_params = impl_generics.regions.get_slice(subst::FnSpace); + + debug!("check_region_bounds_on_impl_method: \ + trait_generics={} \ + impl_generics={}", + trait_generics.repr(tcx), + impl_generics.repr(tcx)); + + // Must have same number of early-bound lifetime parameters. + // Unfortunately, if the user screws up the bounds, then this + // will change classification between early and late. E.g., + // if in trait we have `<'a,'b:'a>`, and in impl we just have + // `<'a,'b>`, then we have 2 early-bound lifetime parameters + // in trait but 0 in the impl. But if we report "expected 2 + // but found 0" it's confusing, because it looks like there + // are zero. Since I don't quite know how to phrase things at + // the moment, give a kind of vague error message. + if trait_params.len() != impl_params.len() { + tcx.sess.span_err( + span, + format!("lifetime parameters or bounds on method `{}` do \ + not match the trait declaration", + token::get_ident(impl_m.ident)).as_slice()); + return false; + } + + // Each parameter `'a:'b+'c+'d` in trait should have the same + // set of bounds in the impl, after subst. + for (trait_param, impl_param) in + trait_params.iter().zip( + impl_params.iter()) + { + let trait_bounds = + trait_param.bounds.subst(tcx, trait_to_skol_substs); + let impl_bounds = + impl_param.bounds.subst(tcx, impl_to_skol_substs); + + debug!("check_region_bounds_on_impl_method: \ + trait_param={} \ + impl_param={} \ + trait_bounds={} \ + impl_bounds={}", + trait_param.repr(tcx), + impl_param.repr(tcx), + trait_bounds.repr(tcx), + impl_bounds.repr(tcx)); + + // Collect the set of bounds present in trait but not in + // impl. + let missing: Vec<ty::Region> = + trait_bounds.iter() + .filter(|&b| !impl_bounds.contains(b)) + .map(|&b| b) + .collect(); + + // Collect set present in impl but not in trait. + let extra: Vec<ty::Region> = + impl_bounds.iter() + .filter(|&b| !trait_bounds.contains(b)) + .map(|&b| b) + .collect(); + + debug!("missing={} extra={}", + missing.repr(tcx), extra.repr(tcx)); + + let err = if missing.len() != 0 || extra.len() != 0 { + tcx.sess.span_err( + span, + format!( + "the lifetime parameter `{}` declared in the impl \ + has a distinct set of bounds \ + from its counterpart `{}` \ + declared in the trait", + impl_param.name.user_string(tcx), + trait_param.name.user_string(tcx)).as_slice()); + true + } else { + false + }; + + if missing.len() != 0 { + tcx.sess.span_note( + span, + format!("the impl is missing the following bounds: `{}`", + missing.user_string(tcx)).as_slice()); + } + + if extra.len() != 0 { + tcx.sess.span_note( + span, + format!("the impl has the following extra bounds: `{}`", + extra.user_string(tcx)).as_slice()); + } + + if err { + return false; + } + } + + return true; + } } fn check_cast(fcx: &FnCtxt, @@ -1127,7 +1428,7 @@ fn check_cast(fcx: &FnCtxt, if ty::type_is_scalar(t_1) { // Supply the type as a hint so as to influence integer // literals and other things that might care. - check_expr_with_hint(fcx, e, t_1) + check_expr_with_expectation(fcx, e, ExpectCastableToType(t_1)) } else { check_expr(fcx, e) } @@ -1141,6 +1442,7 @@ fn check_cast(fcx: &FnCtxt, fcx.write_error(id); return } + if ty::type_is_bot(t_e) { fcx.write_bot(id); return @@ -1279,6 +1581,8 @@ impl<'a> AstConv for FnCtxt<'a> { } impl<'a> FnCtxt<'a> { + fn tcx<'a>(&'a self) -> &'a ty::ctxt { self.ccx.tcx } + pub fn infcx<'b>(&'b self) -> &'b infer::InferCtxt<'a> { &self.inh.infcx } @@ -1297,6 +1601,10 @@ impl<'a> FnCtxt<'a> { } impl<'a> RegionScope for infer::InferCtxt<'a> { + fn default_region_bound(&self, span: Span) -> Option<ty::Region> { + Some(self.next_region_var(infer::MiscVariable(span))) + } + fn anon_regions(&self, span: Span, count: uint) -> Result<Vec<ty::Region> , ()> { Ok(Vec::from_fn(count, |_| { @@ -1472,19 +1780,10 @@ impl<'a> FnCtxt<'a> { } pub fn mk_subr(&self, - a_is_expected: bool, origin: infer::SubregionOrigin, sub: ty::Region, sup: ty::Region) { - infer::mk_subr(self.infcx(), a_is_expected, origin, sub, sup) - } - - pub fn with_region_lb<R>(&self, lb: ast::NodeId, f: || -> R) -> R { - let old_region_lb = self.region_lb.get(); - self.region_lb.set(lb); - let v = f(); - self.region_lb.set(old_region_lb); - v + infer::mk_subr(self.infcx(), origin, sub, sup) } pub fn type_error_message(&self, @@ -1514,6 +1813,112 @@ impl<'a> FnCtxt<'a> { err: &ty::type_err) { self.infcx().report_mismatched_types(sp, e, a, err) } + + pub fn register_region_obligation(&self, + origin: infer::SubregionOrigin, + ty: ty::t, + r: ty::Region) + { + /*! + * Registers an obligation for checking later, during + * regionck, that the type `ty` must outlive the region `r`. + */ + + let mut region_obligations = self.inh.region_obligations.borrow_mut(); + let v = region_obligations.find_or_insert_with(self.body_id, + |_| Vec::new()); + v.push(RegionObligation { sub_region: r, + sup_type: ty, + origin: origin }); + } + + pub fn add_region_obligations_for_parameters(&self, + span: Span, + substs: &Substs, + generics: &ty::Generics) + { + /*! + * Given a set of generic parameter definitions (`generics`) + * and the values provided for each of them (`substs`), + * creates and registers suitable region obligations. + * + * For example, if there is a function: + * + * fn foo<'a,T:'a>(...) + * + * and a reference: + * + * let f = foo; + * + * Then we will create a fresh region variable `'$0` and a + * fresh type variable `$1` for `'a` and `T`. This routine + * will add a region obligation `$1:'$0` and register it + * locally. + */ + + debug!("add_region_obligations_for_parameters(substs={}, generics={})", + substs.repr(self.tcx()), + generics.repr(self.tcx())); + + assert_eq!(generics.types.iter().len(), + substs.types.iter().len()); + for (type_def, &type_param) in + generics.types.iter().zip( + substs.types.iter()) + { + let param_ty = ty::ParamTy { space: type_def.space, + idx: type_def.index, + def_id: type_def.def_id }; + let bounds = type_def.bounds.subst(self.tcx(), substs); + add_region_obligations_for_type_parameter( + self, span, param_ty, &bounds, type_param); + } + + assert_eq!(generics.regions.iter().len(), + substs.regions().iter().len()); + for (region_def, ®ion_param) in + generics.regions.iter().zip( + substs.regions().iter()) + { + let bounds = region_def.bounds.subst(self.tcx(), substs); + add_region_obligations_for_region_parameter( + self, span, bounds.as_slice(), region_param); + } + + fn add_region_obligations_for_type_parameter( + fcx: &FnCtxt, + span: Span, + param_ty: ty::ParamTy, + param_bound: &ty::ParamBounds, + ty: ty::t) + { + // For each declared region bound `T:r`, `T` must outlive `r`. + let region_bounds = + ty::required_region_bounds( + fcx.tcx(), + param_bound.opt_region_bound.as_slice(), + param_bound.builtin_bounds, + param_bound.trait_bounds.as_slice()); + for &r in region_bounds.iter() { + let origin = infer::RelateParamBound(span, param_ty, ty); + fcx.register_region_obligation(origin, ty, r); + } + } + + fn add_region_obligations_for_region_parameter( + fcx: &FnCtxt, + span: Span, + region_bounds: &[ty::Region], + region_param: ty::Region) + { + for &b in region_bounds.iter() { + // For each bound `region:b`, `b <= region` must hold + // (i.e., `region` must outlive `b`). + let origin = infer::RelateRegionParamBound(span); + fcx.mk_subr(origin, b, region_param); + } + } + } } pub enum LvaluePreference { @@ -2011,7 +2416,6 @@ fn check_argument_types(fcx: &FnCtxt, } check_expr_coercable_to_type(fcx, &**arg, formal_ty); - } } } @@ -2248,7 +2652,8 @@ fn check_expr_with_unifier(fcx: &FnCtxt, lvalue_pref: LvaluePreference, unifier: ||) { - debug!(">> typechecking"); + debug!(">> typechecking: expr={} expected={}", + expr.repr(fcx.tcx()), expected.repr(fcx.tcx())); // A generic function for doing all of the checking for call expressions fn check_call(fcx: &FnCtxt, @@ -2276,7 +2681,7 @@ fn check_expr_with_unifier(fcx: &FnCtxt, ty::ty_closure(box ty::ClosureTy {sig: ref sig, ..}) => sig, _ => { fcx.type_error_message(call_expr.span, |actual| { - format!("expected function but found `{}`", actual) + format!("expected function, found `{}`", actual) }, fn_ty, None); &error_fn_sig } @@ -2399,8 +2804,20 @@ fn check_expr_with_unifier(fcx: &FnCtxt, // The tightest thing we can say is "must unify with // else branch". Note that in the case of a "has type" // constraint, this limitation does not hold. - let expected = expected.only_has_type(); + // If the expected type is just a type variable, then don't use + // an expected type. Otherwise, we might write parts of the type + // when checking the 'then' block which are incompatible with the + // 'else' branch. + let expected = match expected.only_has_type() { + ExpectHasType(ety) => { + match infer::resolve_type(fcx.infcx(), Some(sp), ety, force_tvar) { + Ok(rty) if !ty::type_is_ty_var(rty) => ExpectHasType(rty), + _ => NoExpectation + } + } + _ => NoExpectation + }; check_block_with_expected(fcx, then_blk, expected); let then_ty = fcx.node_ty(then_blk.id); check_expr_with_expectation(fcx, &**else_expr, expected); @@ -2641,17 +3058,19 @@ fn check_expr_with_unifier(fcx: &FnCtxt, kind: ast::UnboxedClosureKind, decl: &ast::FnDecl, body: ast::P<ast::Block>) { - // The `RegionTraitStore` is a lie, but we ignore it so it doesn't - // matter. - // - // FIXME(pcwalton): Refactor this API. let mut fn_ty = astconv::ty_of_closure( fcx, expr.id, ast::NormalFn, ast::Many, - ty::empty_builtin_bounds(), + + // The `RegionTraitStore` and region_existential_bounds + // are lies, but we ignore them so it doesn't matter. + // + // FIXME(pcwalton): Refactor this API. + ty::region_existential_bound(ty::ReStatic), ty::RegionTraitStore(ty::ReStatic, ast::MutImmutable), + decl, abi::RustCall, None); @@ -2670,6 +3089,7 @@ fn check_expr_with_unifier(fcx: &FnCtxt, check_fn(fcx.ccx, ast::NormalFn, + expr.id, &fn_ty.sig, decl, expr.id, @@ -2678,7 +3098,11 @@ fn check_expr_with_unifier(fcx: &FnCtxt, // Tuple up the arguments and insert the resulting function type into // the `unboxed_closures` table. - fn_ty.sig.inputs = vec![ty::mk_tup(fcx.tcx(), fn_ty.sig.inputs)]; + fn_ty.sig.inputs = if fn_ty.sig.inputs.len() == 0 { + vec![ty::mk_nil()] + } else { + vec![ty::mk_tup(fcx.tcx(), fn_ty.sig.inputs)] + }; let kind = match kind { ast::FnUnboxedClosureKind => ty::FnUnboxedClosureKind, @@ -2739,13 +3163,17 @@ fn check_expr_with_unifier(fcx: &FnCtxt, } _ => { // Not an error! Means we're inferring the closure type - let mut bounds = ty::empty_builtin_bounds(); - let onceness = match expr.node { + let (bounds, onceness) = match expr.node { ast::ExprProc(..) => { - bounds.add(ty::BoundSend); - ast::Once + let mut bounds = ty::region_existential_bound(ty::ReStatic); + bounds.builtin_bounds.add(ty::BoundSend); // FIXME + (bounds, ast::Once) + } + _ => { + let region = fcx.infcx().next_region_var( + infer::AddrOfRegion(expr.span)); + (ty::region_existential_bound(region), ast::Many) } - _ => ast::Many }; (None, onceness, bounds) } @@ -2771,7 +3199,7 @@ fn check_expr_with_unifier(fcx: &FnCtxt, // If the closure is a stack closure and hasn't had some non-standard // style inferred for it, then check it under its parent's style. // Otherwise, use its own - let (inherited_style, id) = match store { + let (inherited_style, inherited_style_id) = match store { ty::RegionTraitStore(..) => (fcx.ps.borrow().fn_style, fcx.ps.borrow().def), ty::UniqTraitStore => (ast::NormalFn, expr.id) @@ -2779,9 +3207,10 @@ fn check_expr_with_unifier(fcx: &FnCtxt, check_fn(fcx.ccx, inherited_style, + inherited_style_id, &fty_sig, - decl, - id, + &*decl, + expr.id, &*body, fcx.inh); } @@ -3029,96 +3458,9 @@ fn check_expr_with_unifier(fcx: &FnCtxt, type ExprCheckerWithTy = fn(&FnCtxt, &ast::Expr, ty::t); - fn check_fn_for_vec_elements_expected(fcx: &FnCtxt, - expected: Expectation) - -> (ExprCheckerWithTy, ty::t) { - let tcx = fcx.ccx.tcx; - let (coerce, t) = match expected { - // If we're given an expected type, we can try to coerce to it - ExpectHasType(t) if ty::type_is_vec(t) => (true, ty::sequence_element_type(tcx, t)), - // Otherwise we just leave the type to be resolved later - _ => (false, fcx.infcx().next_ty_var()) - }; - if coerce { - (check_expr_coercable_to_type, t) - } else { - (check_expr_has_type, t) - } - } - let tcx = fcx.ccx.tcx; let id = expr.id; match expr.node { - ast::ExprVstore(ev, vst) => { - let (check, t) = check_fn_for_vec_elements_expected(fcx, expected); - let typ = match ev.node { - ast::ExprVec(ref args) => { - let mutability = match vst { - ast::ExprVstoreMutSlice => ast::MutMutable, - _ => ast::MutImmutable, - }; - let mut any_error = false; - let mut any_bot = false; - for e in args.iter() { - check(fcx, &**e, t); - let arg_t = fcx.expr_ty(&**e); - if ty::type_is_error(arg_t) { - any_error = true; - } - else if ty::type_is_bot(arg_t) { - any_bot = true; - } - } - if any_error { - ty::mk_err() - } else if any_bot { - ty::mk_bot() - } else { - ast_expr_vstore_to_ty(fcx, &*ev, vst, || - ty::mt{ ty: ty::mk_vec(tcx, - ty::mt {ty: t, mutbl: mutability}, - None), - mutbl: mutability }) - } - } - ast::ExprRepeat(ref element, ref count_expr) => { - check_expr_with_hint(fcx, &**count_expr, ty::mk_uint()); - let _ = ty::eval_repeat_count(fcx, &**count_expr); - let mutability = match vst { - ast::ExprVstoreMutSlice => ast::MutMutable, - _ => ast::MutImmutable, - }; - check(fcx, &**element, t); - let arg_t = fcx.expr_ty(&**element); - if ty::type_is_error(arg_t) { - ty::mk_err() - } else if ty::type_is_bot(arg_t) { - ty::mk_bot() - } else { - ast_expr_vstore_to_ty(fcx, &*ev, vst, || - ty::mt{ ty: ty::mk_vec(tcx, - ty::mt {ty: t, mutbl: mutability}, - None), - mutbl: mutability}) - } - } - ast::ExprLit(_) => { - if vst == ast::ExprVstoreSlice { - span_err!(tcx.sess, expr.span, E0064, - "`&\"string\"` has been removed; use `\"string\"` instead"); - } else { - span_err!(tcx.sess, expr.span, E0065, - "`box \"string\"` has been removed; use \ - `\"string\".to_string()` instead"); - } - ty::mk_err() - } - _ => tcx.sess.span_bug(expr.span, "vstore modifier on non-sequence"), - }; - fcx.write_ty(ev.id, typ); - fcx.write_ty(id, typ); - } - ast::ExprBox(ref place, ref subexpr) => { check_expr(fcx, &**place); check_expr(fcx, &**subexpr); @@ -3130,13 +3472,15 @@ fn check_expr_with_unifier(fcx: &FnCtxt, // places: the exchange heap and the managed heap. let definition = lookup_def(fcx, path.span, place.id); let def_id = definition.def_id(); + let referent_ty = fcx.expr_ty(&**subexpr); if tcx.lang_items.exchange_heap() == Some(def_id) { - fcx.write_ty(id, ty::mk_uniq(tcx, - fcx.expr_ty(&**subexpr))); + fcx.write_ty(id, ty::mk_uniq(tcx, referent_ty)); checked = true } else if tcx.lang_items.managed_heap() == Some(def_id) { - fcx.write_ty(id, ty::mk_box(tcx, - fcx.expr_ty(&**subexpr))); + fcx.register_region_obligation(infer::Managed(expr.span), + referent_ty, + ty::ReStatic); + fcx.write_ty(id, ty::mk_box(tcx, referent_ty)); checked = true } } @@ -3189,7 +3533,6 @@ fn check_expr_with_unifier(fcx: &FnCtxt, } } ast::ExprUnary(unop, ref oprnd) => { - let expected = expected.only_has_type(); let expected_inner = expected.map(fcx, |sty| { match unop { ast::UnBox | ast::UnUniq => match *sty { @@ -3296,22 +3639,6 @@ fn check_expr_with_unifier(fcx: &FnCtxt, hint, lvalue_pref); - // Note: at this point, we cannot say what the best lifetime - // is to use for resulting pointer. We want to use the - // shortest lifetime possible so as to avoid spurious borrowck - // errors. Moreover, the longest lifetime will depend on the - // precise details of the value whose address is being taken - // (and how long it is valid), which we don't know yet until type - // inference is complete. - // - // Therefore, here we simply generate a region variable. The - // region inferencer will then select the ultimate value. - // Finally, borrowck is charged with guaranteeing that the - // value whose address was taken can actually be made to live - // as long as it needs to live. - let region = fcx.infcx().next_region_var( - infer::AddrOfRegion(expr.span)); - let tm = ty::mt { ty: fcx.expr_ty(&**oprnd), mutbl: mutbl }; let oprnd_t = if ty::type_is_error(tm.ty) { ty::mk_err() @@ -3319,20 +3646,54 @@ fn check_expr_with_unifier(fcx: &FnCtxt, ty::mk_bot() } else { - ty::mk_rptr(tcx, region, tm) + // Note: at this point, we cannot say what the best lifetime + // is to use for resulting pointer. We want to use the + // shortest lifetime possible so as to avoid spurious borrowck + // errors. Moreover, the longest lifetime will depend on the + // precise details of the value whose address is being taken + // (and how long it is valid), which we don't know yet until type + // inference is complete. + // + // Therefore, here we simply generate a region variable. The + // region inferencer will then select the ultimate value. + // Finally, borrowck is charged with guaranteeing that the + // value whose address was taken can actually be made to live + // as long as it needs to live. + match oprnd.node { + // String literals are already, implicitly converted to slices. + //ast::ExprLit(lit) if ast_util::lit_is_str(lit) => fcx.expr_ty(oprnd), + // Empty slices live in static memory. + ast::ExprVec(ref elements) if elements.len() == 0 => { + // Note: we do not assign a lifetime of + // static. This is because the resulting type + // `&'static [T]` would require that T outlives + // `'static`! + let region = fcx.infcx().next_region_var( + infer::AddrOfSlice(expr.span)); + ty::mk_rptr(tcx, region, tm) + } + _ => { + let region = fcx.infcx().next_region_var(infer::AddrOfRegion(expr.span)); + ty::mk_rptr(tcx, region, tm) + } + } }; fcx.write_ty(id, oprnd_t); } ast::ExprPath(ref pth) => { - let defn = lookup_def(fcx, pth.span, id); - let pty = polytype_for_def(fcx, expr.span, defn); - instantiate_path(fcx, pth, pty, defn, expr.span, expr.id); + let defn = lookup_def(fcx, pth.span, id); + let pty = polytype_for_def(fcx, expr.span, defn); + instantiate_path(fcx, pth, pty, defn, expr.span, expr.id); + + // We always require that the type provided as the value for + // a type parameter outlives the moment of instantiation. + constrain_path_type_parameters(fcx, expr); } ast::ExprInlineAsm(ref ia) => { for &(_, ref input) in ia.inputs.iter() { check_expr(fcx, &**input); } - for &(_, ref out) in ia.outputs.iter() { + for &(_, ref out, _) in ia.outputs.iter() { check_expr(fcx, &**out); } fcx.write_nil(id); @@ -3352,7 +3713,7 @@ fn check_expr_with_unifier(fcx: &FnCtxt, } }, Some(ref e) => { - check_expr_has_type(fcx, &**e, ret_ty); + check_expr_coercable_to_type(fcx, &**e, ret_ty); } } fcx.write_bot(id); @@ -3506,29 +3867,66 @@ fn check_expr_with_unifier(fcx: &FnCtxt, check_cast(fcx, &**e, &**t, id, expr.span); } ast::ExprVec(ref args) => { - let (check, t) = check_fn_for_vec_elements_expected(fcx, expected); - for e in args.iter() { - check(fcx, &**e, t); - } - let typ = ty::mk_vec(tcx, ty::mt {ty: t, mutbl: ast::MutImmutable}, - Some(args.len())); + let uty = match expected { + ExpectHasType(uty) => { + match ty::get(uty).sty { + ty::ty_vec(ty, _) => Some(ty), + _ => None + } + } + _ => None + }; + + let typ = match uty { + Some(uty) => { + for e in args.iter() { + check_expr_coercable_to_type(fcx, &**e, uty); + } + uty + } + None => { + let t: ty::t = fcx.infcx().next_ty_var(); + for e in args.iter() { + check_expr_has_type(fcx, &**e, t); + } + t + } + }; + let typ = ty::mk_vec(tcx, typ, Some(args.len())); fcx.write_ty(id, typ); } ast::ExprRepeat(ref element, ref count_expr) => { check_expr_has_type(fcx, &**count_expr, ty::mk_uint()); let count = ty::eval_repeat_count(fcx, &**count_expr); - let (check, t) = check_fn_for_vec_elements_expected(fcx, expected); - check(fcx, &**element, t); - let element_ty = fcx.expr_ty(&**element); + + let uty = match expected { + ExpectHasType(uty) => { + match ty::get(uty).sty { + ty::ty_vec(ty, _) => Some(ty), + _ => None + } + } + _ => None + }; + + let (element_ty, t) = match uty { + Some(uty) => { + check_expr_coercable_to_type(fcx, &**element, uty); + (uty, uty) + } + None => { + let t: ty::t = fcx.infcx().next_ty_var(); + check_expr_has_type(fcx, &**element, t); + (fcx.expr_ty(&**element), t) + } + }; + if ty::type_is_error(element_ty) { fcx.write_error(id); - } - else if ty::type_is_bot(element_ty) { + } else if ty::type_is_bot(element_ty) { fcx.write_bot(id); - } - else { - let t = ty::mk_vec(tcx, ty::mt {ty: t, mutbl: ast::MutImmutable}, - Some(count)); + } else { + let t = ty::mk_vec(tcx, t, Some(count)); fcx.write_ty(id, t); } } @@ -3544,12 +3942,17 @@ fn check_expr_with_unifier(fcx: &FnCtxt, let mut err_field = false; let elt_ts = elts.iter().enumerate().map(|(i, e)| { - let opt_hint = match flds { - Some(ref fs) if i < fs.len() => ExpectHasType(*fs.get(i)), - _ => NoExpectation + let t = match flds { + Some(ref fs) if i < fs.len() => { + let ety = *fs.get(i); + check_expr_coercable_to_type(fcx, &**e, ety); + ety + } + _ => { + check_expr_with_expectation(fcx, &**e, NoExpectation); + fcx.expr_ty(&**e) + } }; - check_expr_with_expectation(fcx, &**e, opt_hint); - let t = fcx.expr_ty(&**e); err_field = err_field || ty::type_is_error(t); bot_field = bot_field || ty::type_is_bot(t); t @@ -3661,9 +4064,9 @@ fn check_expr_with_unifier(fcx: &FnCtxt, autoderef(fcx, expr.span, raw_base_t, Some(base.id), lvalue_pref, |base_t, _| ty::index(base_t)); match field_ty { - Some(mt) => { + Some(ty) => { check_expr_has_type(fcx, &**idx, ty::mk_uint()); - fcx.write_ty(id, mt.ty); + fcx.write_ty(id, ty); fcx.write_autoderef_adjustment(base.id, autoderefs); } None => { @@ -3709,6 +4112,18 @@ fn check_expr_with_unifier(fcx: &FnCtxt, unifier(); } +fn constrain_path_type_parameters(fcx: &FnCtxt, + expr: &ast::Expr) +{ + fcx.opt_node_ty_substs(expr.id, |item_substs| { + for &ty in item_substs.substs.types.iter() { + let default_bound = ty::ReScope(expr.id); + let origin = infer::RelateDefaultParamBound(expr.span, ty); + fcx.register_region_obligation(origin, ty, default_bound); + } + }); +} + impl Expectation { fn only_has_type(self) -> Expectation { match self { @@ -3770,7 +4185,7 @@ impl Repr for Expectation { pub fn require_uint(fcx: &FnCtxt, sp: Span, t: ty::t) { if !type_is_uint(fcx, sp, t) { fcx.type_error_message(sp, |actual| { - format!("mismatched types: expected `uint` type but found `{}`", + format!("mismatched types: expected `uint` type, found `{}`", actual) }, t, None); } @@ -3779,7 +4194,7 @@ pub fn require_uint(fcx: &FnCtxt, sp: Span, t: ty::t) { pub fn require_integral(fcx: &FnCtxt, sp: Span, t: ty::t) { if !type_is_integral(fcx, sp, t) { fcx.type_error_message(sp, |actual| { - format!("mismatched types: expected integral type but found `{}`", + format!("mismatched types: expected integral type, found `{}`", actual) }, t, None); } @@ -3890,72 +4305,78 @@ fn check_block_with_expected(fcx: &FnCtxt, replace(&mut *fcx_ps, fn_style_state) }; - fcx.with_region_lb(blk.id, || { - let mut warned = false; - let mut last_was_bot = false; - let mut any_bot = false; - let mut any_err = false; - for s in blk.stmts.iter() { - check_stmt(fcx, &**s); - let s_id = ast_util::stmt_id(&**s); - let s_ty = fcx.node_ty(s_id); - if last_was_bot && !warned && match s.node { - ast::StmtDecl(decl, _) => { - match decl.node { - ast::DeclLocal(_) => true, - _ => false, - } - } - ast::StmtExpr(_, _) | ast::StmtSemi(_, _) => true, - _ => false - } { - fcx.ccx - .tcx - .sess - .add_lint(lint::builtin::UNREACHABLE_CODE, - s_id, - s.span, - "unreachable statement".to_string()); - warned = true; + let mut warned = false; + let mut last_was_bot = false; + let mut any_bot = false; + let mut any_err = false; + for s in blk.stmts.iter() { + check_stmt(fcx, &**s); + let s_id = ast_util::stmt_id(&**s); + let s_ty = fcx.node_ty(s_id); + if last_was_bot && !warned && match s.node { + ast::StmtDecl(decl, _) => { + match decl.node { + ast::DeclLocal(_) => true, + _ => false, + } } - if ty::type_is_bot(s_ty) { - last_was_bot = true; + ast::StmtExpr(_, _) | ast::StmtSemi(_, _) => true, + _ => false + } { + fcx.ccx + .tcx + .sess + .add_lint(lint::builtin::UNREACHABLE_CODE, + s_id, + s.span, + "unreachable statement".to_string()); + warned = true; + } + if ty::type_is_bot(s_ty) { + last_was_bot = true; + } + any_bot = any_bot || ty::type_is_bot(s_ty); + any_err = any_err || ty::type_is_error(s_ty); + } + match blk.expr { + None => if any_err { + fcx.write_error(blk.id); + } + else if any_bot { + fcx.write_bot(blk.id); + } + else { + fcx.write_nil(blk.id); + }, + Some(e) => { + if any_bot && !warned { + fcx.ccx + .tcx + .sess + .add_lint(lint::builtin::UNREACHABLE_CODE, + e.id, + e.span, + "unreachable expression".to_string()); } - any_bot = any_bot || ty::type_is_bot(s_ty); - any_err = any_err || ty::type_is_error(s_ty); - } - match blk.expr { - None => if any_err { + let ety = match expected { + ExpectHasType(ety) => { + check_expr_coercable_to_type(fcx, &*e, ety); + ety + } + _ => { + check_expr_with_expectation(fcx, &*e, expected); + fcx.expr_ty(&*e) + } + }; + + fcx.write_ty(blk.id, ety); + if any_err { fcx.write_error(blk.id); - } - else if any_bot { + } else if any_bot { fcx.write_bot(blk.id); } - else { - fcx.write_nil(blk.id); - }, - Some(e) => { - if any_bot && !warned { - fcx.ccx - .tcx - .sess - .add_lint(lint::builtin::UNREACHABLE_CODE, - e.id, - e.span, - "unreachable expression".to_string()); - } - check_expr_with_expectation(fcx, &*e, expected); - let ety = fcx.expr_ty(&*e); - fcx.write_ty(blk.id, ety); - if any_err { - fcx.write_error(blk.id); - } - else if any_bot { - fcx.write_bot(blk.id); - } - } - }; - }); + } + }; *fcx.ps.borrow_mut() = prev; } @@ -3973,7 +4394,7 @@ pub fn check_const_in_type(tcx: &ty::ctxt, trait_map: NodeMap::new(), tcx: tcx, }; - let inh = blank_inherited_fields(&ccx); + let inh = static_inherited_fields(&ccx); let fcx = blank_fn_ctxt(&ccx, &inh, expected_type, expr.id); check_const_with_ty(&fcx, expr.span, expr, expected_type); } @@ -3982,7 +4403,7 @@ pub fn check_const(ccx: &CrateCtxt, sp: Span, e: &ast::Expr, id: ast::NodeId) { - let inh = blank_inherited_fields(ccx); + let inh = static_inherited_fields(ccx); let rty = ty::node_id_to_type(ccx.tcx, id); let fcx = blank_fn_ctxt(ccx, &inh, rty, e.id); let declty = fcx.ccx.tcx.tcache.borrow().get(&local_def(id)).ty; @@ -3999,9 +4420,8 @@ pub fn check_const_with_ty(fcx: &FnCtxt, // emit a error. GatherLocalsVisitor { fcx: fcx }.visit_expr(e, ()); - check_expr(fcx, e); - let cty = fcx.expr_ty(e); - demand::suptype(fcx, e.span, declty, cty); + check_expr_with_hint(fcx, e, declty); + demand::coerce(fcx, e.span, declty, e); regionck::regionck_expr(fcx, e); writeback::resolve_type_vars_in_expr(fcx, e); } @@ -4091,6 +4511,7 @@ pub fn check_simd(tcx: &ty::ctxt, sp: Span, id: ast::NodeId) { } } + pub fn check_enum_variants_sized(ccx: &CrateCtxt, vs: &[ast::P<ast::Variant>]) { for &v in vs.iter() { @@ -4177,16 +4598,16 @@ pub fn check_enum_variants(ccx: &CrateCtxt, Some(e) => { debug!("disr expr, checking {}", pprust::expr_to_string(&*e)); - let inh = blank_inherited_fields(ccx); + let inh = static_inherited_fields(ccx); let fcx = blank_fn_ctxt(ccx, &inh, rty, e.id); let declty = match hint { - attr::ReprAny | attr::ReprExtern => ty::mk_int(), + attr::ReprAny | attr::ReprPacked | attr::ReprExtern => ty::mk_int(), attr::ReprInt(_, attr::SignedInt(ity)) => { ty::mk_mach_int(ity) } attr::ReprInt(_, attr::UnsignedInt(ity)) => { ty::mk_mach_uint(ity) - } + }, }; check_const_with_ty(&fcx, e.span, &*e, declty); // check_expr (from check_const pass) doesn't guarantee @@ -4225,6 +4646,9 @@ pub fn check_enum_variants(ccx: &CrateCtxt, "discriminant type specified here"); } } + attr::ReprPacked => { + ccx.tcx.sess.bug("range_to_inttype: found ReprPacked on an enum"); + } } disr_vals.push(current_disr_val); @@ -4238,7 +4662,9 @@ pub fn check_enum_variants(ccx: &CrateCtxt, return variants; } - let hint = ty::lookup_repr_hint(ccx.tcx, ast::DefId { krate: ast::LOCAL_CRATE, node: id }); + let hint = *ty::lookup_repr_hints(ccx.tcx, ast::DefId { krate: ast::LOCAL_CRATE, node: id }) + .as_slice().get(0).unwrap_or(&attr::ReprAny); + if hint != attr::ReprAny && vs.len() <= 1 { if vs.len() == 1 { span_err!(ccx.tcx.sess, sp, E0083, @@ -4290,28 +4716,28 @@ pub fn polytype_for_def(fcx: &FnCtxt, def::DefTy(_) | def::DefPrimTy(_) | def::DefTyParam(..)=> { - fcx.ccx.tcx.sess.span_bug(sp, "expected value but found type"); + fcx.ccx.tcx.sess.span_bug(sp, "expected value, found type"); } def::DefMod(..) | def::DefForeignMod(..) => { - fcx.ccx.tcx.sess.span_bug(sp, "expected value but found module"); + fcx.ccx.tcx.sess.span_bug(sp, "expected value, found module"); } def::DefUse(..) => { - fcx.ccx.tcx.sess.span_bug(sp, "expected value but found use"); + fcx.ccx.tcx.sess.span_bug(sp, "expected value, found use"); } def::DefRegion(..) => { - fcx.ccx.tcx.sess.span_bug(sp, "expected value but found region"); + fcx.ccx.tcx.sess.span_bug(sp, "expected value, found region"); } def::DefTyParamBinder(..) => { - fcx.ccx.tcx.sess.span_bug(sp, "expected value but found type parameter"); + fcx.ccx.tcx.sess.span_bug(sp, "expected value, found type parameter"); } def::DefLabel(..) => { - fcx.ccx.tcx.sess.span_bug(sp, "expected value but found label"); + fcx.ccx.tcx.sess.span_bug(sp, "expected value, found label"); } def::DefSelfTy(..) => { - fcx.ccx.tcx.sess.span_bug(sp, "expected value but found self ty"); + fcx.ccx.tcx.sess.span_bug(sp, "expected value, found self ty"); } def::DefMethod(..) => { - fcx.ccx.tcx.sess.span_bug(sp, "expected value but found method"); + fcx.ccx.tcx.sess.span_bug(sp, "expected value, found method"); } } } @@ -4483,6 +4909,9 @@ pub fn instantiate_path(fcx: &FnCtxt, assert_eq!(substs.regions().len(space), region_defs.len(space)); } + fcx.add_region_obligations_for_parameters( + span, &substs, &polytype.generics); + fcx.write_ty_substs(node_id, polytype.ty, ty::ItemSubsts { substs: substs, }); @@ -4539,8 +4968,8 @@ pub fn instantiate_path(fcx: &FnCtxt, } else if i == type_count { span_err!(fcx.tcx().sess, typ.span, E0087, "too many type parameters provided: \ - expected at most {} parameter(s) \ - but found {} parameter(s)", + expected at most {} parameter(s), \ + found {} parameter(s)", type_count, segment.types.len()); substs.types.truncate(space, 0); } @@ -4557,7 +4986,7 @@ pub fn instantiate_path(fcx: &FnCtxt, } else if i == region_count { span_err!(fcx.tcx().sess, lifetime.span, E0088, "too many lifetime parameters provided: \ - expected {} parameter(s) but found {} parameter(s)", + expected {} parameter(s), found {} parameter(s)", region_count, segment.lifetimes.len()); substs.mut_regions().truncate(space, 0); @@ -4606,7 +5035,7 @@ pub fn instantiate_path(fcx: &FnCtxt, if desired.len() != required_len { "at least " } else { "" }; span_err!(fcx.tcx().sess, span, E0089, "too few type parameters provided: expected {}{} parameter(s) \ - but found {} parameter(s)", + , found {} parameter(s)", qualifier, required_len, provided_len); substs.types.replace(space, Vec::from_elem(desired.len(), ty::mk_err())); @@ -4659,8 +5088,8 @@ pub fn instantiate_path(fcx: &FnCtxt, // Otherwise, too few were provided. Report an error and then // use inference variables. span_err!(fcx.tcx().sess, span, E0090, - "too few lifetime parameters provided: expected {} parameter(s) \ - but found {} parameter(s)", + "too few lifetime parameters provided: expected {} parameter(s), \ + found {} parameter(s)", desired.len(), provided_len); substs.mut_regions().replace( @@ -4701,39 +5130,39 @@ pub fn type_is_uint(fcx: &FnCtxt, sp: Span, typ: ty::t) -> bool { return ty::type_is_uint(typ_s); } -pub fn ast_expr_vstore_to_ty(fcx: &FnCtxt, - e: &ast::Expr, - v: ast::ExprVstore, - mk_inner: || -> ty::mt) - -> ty::t { - match v { - ast::ExprVstoreUniq => ty::mk_uniq(fcx.ccx.tcx, mk_inner().ty), - ast::ExprVstoreSlice | ast::ExprVstoreMutSlice => { - match e.node { - ast::ExprLit(..) => { - // string literals and *empty slices* live in static memory - ty::mk_rptr(fcx.ccx.tcx, ty::ReStatic, mk_inner()) - } - ast::ExprVec(ref elements) if elements.len() == 0 => { - // string literals and *empty slices* live in static memory - ty::mk_rptr(fcx.ccx.tcx, ty::ReStatic, mk_inner()) - } - ast::ExprRepeat(..) | - ast::ExprVec(..) => { - // vector literals are temporaries on the stack - match fcx.tcx().region_maps.temporary_scope(e.id) { - Some(scope) => ty::mk_rptr(fcx.ccx.tcx, ty::ReScope(scope), mk_inner()), - None => ty::mk_rptr(fcx.ccx.tcx, ty::ReStatic, mk_inner()), - } - } - _ => { - fcx.ccx.tcx.sess.span_bug(e.span, - "vstore with unexpected \ - contents") - } - } - } - } +pub fn type_is_scalar(fcx: &FnCtxt, sp: Span, typ: ty::t) -> bool { + let typ_s = structurally_resolved_type(fcx, sp, typ); + return ty::type_is_scalar(typ_s); +} + +pub fn type_is_char(fcx: &FnCtxt, sp: Span, typ: ty::t) -> bool { + let typ_s = structurally_resolved_type(fcx, sp, typ); + return ty::type_is_char(typ_s); +} + +pub fn type_is_bare_fn(fcx: &FnCtxt, sp: Span, typ: ty::t) -> bool { + let typ_s = structurally_resolved_type(fcx, sp, typ); + return ty::type_is_bare_fn(typ_s); +} + +pub fn type_is_floating_point(fcx: &FnCtxt, sp: Span, typ: ty::t) -> bool { + let typ_s = structurally_resolved_type(fcx, sp, typ); + return ty::type_is_floating_point(typ_s); +} + +pub fn type_is_unsafe_ptr(fcx: &FnCtxt, sp: Span, typ: ty::t) -> bool { + let typ_s = structurally_resolved_type(fcx, sp, typ); + return ty::type_is_unsafe_ptr(typ_s); +} + +pub fn type_is_region_ptr(fcx: &FnCtxt, sp: Span, typ: ty::t) -> bool { + let typ_s = structurally_resolved_type(fcx, sp, typ); + return ty::type_is_region_ptr(typ_s); +} + +pub fn type_is_c_like_enum(fcx: &FnCtxt, sp: Span, typ: ty::t) -> bool { + let typ_s = structurally_resolved_type(fcx, sp, typ); + return ty::type_is_c_like_enum(fcx.ccx.tcx, typ_s); } // Returns true if b contains a break that can exit from b @@ -4876,11 +5305,13 @@ pub fn check_intrinsic_type(ccx: &CrateCtxt, it: &ast::ForeignItem) { Ok(t) => t, Err(s) => { tcx.sess.span_fatal(it.span, s.as_slice()); } }; - let region = ty::ReLateBound(it.id, ty::BrAnon(0)); - let visitor_object_ty = match ty::visitor_object_ty(tcx, region) { - Ok((_, vot)) => vot, - Err(s) => { tcx.sess.span_fatal(it.span, s.as_slice()); } - }; + let region0 = ty::ReLateBound(it.id, ty::BrAnon(0)); + let region1 = ty::ReLateBound(it.id, ty::BrAnon(1)); + let visitor_object_ty = + match ty::visitor_object_ty(tcx, region0, region1) { + Ok((_, vot)) => vot, + Err(s) => { tcx.sess.span_fatal(it.span, s.as_slice()); } + }; let td_ptr = ty::mk_ptr(ccx.tcx, ty::mt { ty: tydesc_ty, @@ -5085,3 +5516,11 @@ pub fn check_intrinsic_type(ccx: &CrateCtxt, it: &ast::ForeignItem) { } } +impl Repr for RegionObligation { + fn repr(&self, tcx: &ty::ctxt) -> String { + format!("RegionObligation(sub_region={}, sup_type={}, origin={})", + self.sub_region.repr(tcx), + self.sup_type.repr(tcx), + self.origin.repr(tcx)) + } +} diff --git a/src/librustc/middle/typeck/check/regionck.rs b/src/librustc/middle/typeck/check/regionck.rs index 8e41c9463f6..72f33a2f984 100644 --- a/src/librustc/middle/typeck/check/regionck.rs +++ b/src/librustc/middle/typeck/check/regionck.rs @@ -126,14 +126,14 @@ use middle::ty::{ReScope}; use middle::ty; use middle::typeck::astconv::AstConv; use middle::typeck::check::FnCtxt; -use middle::typeck::check::regionmanip::relate_nested_regions; +use middle::typeck::check::regionmanip; use middle::typeck::infer::resolve_and_force_all_but_regions; use middle::typeck::infer::resolve_type; use middle::typeck::infer; use middle::typeck::MethodCall; use middle::pat_util; use util::nodemap::{DefIdMap, NodeMap}; -use util::ppaux::{ty_to_string, region_to_string, Repr}; +use util::ppaux::{ty_to_string, Repr}; use syntax::ast; use syntax::codemap::Span; @@ -143,6 +143,46 @@ use syntax::visit::Visitor; use std::cell::RefCell; use std::gc::Gc; +/////////////////////////////////////////////////////////////////////////// +// PUBLIC ENTRY POINTS + +pub fn regionck_expr(fcx: &FnCtxt, e: &ast::Expr) { + let mut rcx = Rcx::new(fcx, e.id); + if fcx.err_count_since_creation() == 0 { + // regionck assumes typeck succeeded + rcx.visit_expr(e, ()); + rcx.visit_region_obligations(e.id); + } + fcx.infcx().resolve_regions_and_report_errors(); +} + +pub fn regionck_type_defn(fcx: &FnCtxt, + span: Span, + component_tys: &[ty::t]) { + let mut rcx = Rcx::new(fcx, 0); + for &component_ty in component_tys.iter() { + // Check that each type outlives the empty region. Since the + // empty region is a subregion of all others, this can't fail + // unless the type does not meet the well-formedness + // requirements. + type_must_outlive(&mut rcx, infer::RelateRegionParamBound(span), + component_ty, ty::ReEmpty); + } + fcx.infcx().resolve_regions_and_report_errors(); +} + +pub fn regionck_fn(fcx: &FnCtxt, id: ast::NodeId, blk: &ast::Block) { + let mut rcx = Rcx::new(fcx, blk.id); + if fcx.err_count_since_creation() == 0 { + // regionck assumes typeck succeeded + rcx.visit_fn_body(id, blk); + } + fcx.infcx().resolve_regions_and_report_errors(); +} + +/////////////////////////////////////////////////////////////////////////// +// INTERNALS + // If mem categorization results in an error, it's because the type // check failed (or will fail, when the error is uncovered and // reported during writeback). In this case, we just ignore this part @@ -159,10 +199,32 @@ macro_rules! ignore_err( pub struct Rcx<'a> { fcx: &'a FnCtxt<'a>, + region_param_pairs: Vec<(ty::Region, ty::ParamTy)>, + // id of innermost fn or loop repeating_scope: ast::NodeId, } +/// When entering a function, we can derive relationships from the +/// signature between various regions and type parameters. Consider +/// a function like: +/// +/// fn foo<'a, A>(x: &'a A) { ... } +/// +/// Here, we can derive that `A` must outlive `'a`, because otherwise +/// the caller would be illegal. We record this by storing a series of +/// pairs (in this case, `('a, A)`). These pairs will be consulted +/// later during regionck. +/// +/// In the case of nested fns, additional relationships may be +/// derived. The result is a link list walking up the stack (hence +/// the `previous` field). +#[deriving(Clone)] +pub struct RegionSubParamConstraints<'a> { + pairs: Vec<(ty::Region, ty::ParamTy)>, + previous: Option<&'a RegionSubParamConstraints<'a>>, +} + fn region_of_def(fcx: &FnCtxt, def: def::Def) -> ty::Region { /*! * Returns the validity region of `def` -- that is, how long @@ -189,6 +251,13 @@ fn region_of_def(fcx: &FnCtxt, def: def::Def) -> ty::Region { } impl<'a> Rcx<'a> { + pub fn new(fcx: &'a FnCtxt<'a>, + initial_repeating_scope: ast::NodeId) -> Rcx<'a> { + Rcx { fcx: fcx, + repeating_scope: initial_repeating_scope, + region_param_pairs: Vec::new() } + } + pub fn tcx(&self) -> &'a ty::ctxt { self.fcx.ccx.tcx } @@ -259,11 +328,119 @@ impl<'a> Rcx<'a> { |method_call| self.resolve_method_type(method_call)) } } + + fn visit_fn_body(&mut self, + id: ast::NodeId, + body: &ast::Block) + { + // When we enter a function, we can derive + + let fn_sig_map = self.fcx.inh.fn_sig_map.borrow(); + let fn_sig = match fn_sig_map.find(&id) { + Some(f) => f, + None => { + self.tcx().sess.bug( + format!("No fn-sig entry for id={}", id).as_slice()); + } + }; + + let len = self.region_param_pairs.len(); + self.relate_free_regions(fn_sig.as_slice(), body.id); + self.visit_block(body, ()); + self.visit_region_obligations(body.id); + self.region_param_pairs.truncate(len); + } + + fn visit_region_obligations(&mut self, node_id: ast::NodeId) + { + debug!("visit_region_obligations: node_id={}", node_id); + let region_obligations = self.fcx.inh.region_obligations.borrow(); + match region_obligations.find(&node_id) { + None => { } + Some(vec) => { + for r_o in vec.iter() { + debug!("visit_region_obligations: r_o={}", + r_o.repr(self.tcx())); + let sup_type = self.resolve_type(r_o.sup_type); + type_must_outlive(self, r_o.origin.clone(), + sup_type, r_o.sub_region); + } + } + } + } + + fn relate_free_regions(&mut self, + fn_sig_tys: &[ty::t], + body_id: ast::NodeId) { + /*! + * This method populates the region map's `free_region_map`. + * It walks over the transformed argument and return types for + * each function just before we check the body of that + * function, looking for types where you have a borrowed + * pointer to other borrowed data (e.g., `&'a &'b [uint]`. We + * do not allow references to outlive the things they point + * at, so we can assume that `'a <= 'b`. This holds for both + * the argument and return types, basically because, on the caller + * side, the caller is responsible for checking that the type of + * every expression (including the actual values for the arguments, + * as well as the return type of the fn call) is well-formed. + * + * Tests: `src/test/compile-fail/regions-free-region-ordering-*.rs` + */ + + debug!("relate_free_regions >>"); + let tcx = self.tcx(); + + for &ty in fn_sig_tys.iter() { + let ty = self.resolve_type(ty); + debug!("relate_free_regions(t={})", ty.repr(tcx)); + let body_scope = ty::ReScope(body_id); + let constraints = + regionmanip::region_wf_constraints( + tcx, + ty, + body_scope); + for constraint in constraints.iter() { + debug!("constraint: {}", constraint.repr(tcx)); + match *constraint { + regionmanip::RegionSubRegionConstraint(_, + ty::ReFree(free_a), + ty::ReFree(free_b)) => { + tcx.region_maps.relate_free_regions(free_a, free_b); + } + regionmanip::RegionSubRegionConstraint(_, + ty::ReFree(free_a), + ty::ReInfer(ty::ReVar(vid_b))) => { + self.fcx.inh.infcx.add_given(free_a, vid_b); + } + regionmanip::RegionSubRegionConstraint(..) => { + // In principle, we could record (and take + // advantage of) every relationship here, but + // we are also free not to -- it simply means + // strictly less that we can successfully type + // check. (It may also be that we should + // revise our inference system to be more + // general and to make use of *every* + // relationship that arises here, but + // presently we do not.) + } + regionmanip::RegionSubParamConstraint(_, r_a, p_b) => { + debug!("RegionSubParamConstraint: {} <= {}", + r_a.repr(tcx), p_b.repr(tcx)); + + self.region_param_pairs.push((r_a, p_b)); + } + } + } + } + + debug!("<< relate_free_regions"); + } } impl<'fcx> mc::Typer for Rcx<'fcx> { fn tcx<'a>(&'a self) -> &'a ty::ctxt { - self.fcx.tcx() + self.fcx.ccx.tcx } fn node_ty(&self, id: ast::NodeId) -> mc::McResult<ty::t> { @@ -302,26 +479,6 @@ impl<'fcx> mc::Typer for Rcx<'fcx> { } } -pub fn regionck_expr(fcx: &FnCtxt, e: &ast::Expr) { - let mut rcx = Rcx { fcx: fcx, repeating_scope: e.id }; - let rcx = &mut rcx; - if fcx.err_count_since_creation() == 0 { - // regionck assumes typeck succeeded - rcx.visit_expr(e, ()); - } - fcx.infcx().resolve_regions_and_report_errors(); -} - -pub fn regionck_fn(fcx: &FnCtxt, blk: &ast::Block) { - let mut rcx = Rcx { fcx: fcx, repeating_scope: blk.id }; - let rcx = &mut rcx; - if fcx.err_count_since_creation() == 0 { - // regionck assumes typeck succeeded - rcx.visit_block(blk, ()); - } - fcx.infcx().resolve_regions_and_report_errors(); -} - impl<'a> Visitor<()> for Rcx<'a> { // (..) FIXME(#3238) should use visit_pat, not visit_arm/visit_local, // However, right now we run into an issue whereby some free @@ -331,6 +488,11 @@ impl<'a> Visitor<()> for Rcx<'a> { // hierarchy, and in particular the relationships between free // regions, until regionck, as described in #3238. + fn visit_fn(&mut self, _fk: &visit::FnKind, _fd: &ast::FnDecl, + b: &ast::Block, _s: Span, id: ast::NodeId, _e: ()) { + self.visit_fn_body(id, b) + } + fn visit_item(&mut self, i: &ast::Item, _: ()) { visit_item(self, i); } fn visit_expr(&mut self, ex: &ast::Expr, _: ()) { visit_expr(self, ex); } @@ -396,9 +558,9 @@ fn constrain_bindings_in_pat(pat: &ast::Pat, rcx: &mut Rcx) { // variable's type enclose at least the variable's scope. let var_region = tcx.region_maps.var_region(id); - constrain_regions_in_type_of_node( - rcx, id, var_region, - infer::BindingTypeIsNotValidAtDecl(span)); + type_of_node_must_outlive( + rcx, infer::BindingTypeIsNotValidAtDecl(span), + id, var_region); }) } @@ -406,6 +568,13 @@ fn visit_expr(rcx: &mut Rcx, expr: &ast::Expr) { debug!("regionck::visit_expr(e={}, repeating_scope={:?})", expr.repr(rcx.fcx.tcx()), rcx.repeating_scope); + // No matter what, the type of each expression must outlive the + // scope of that expression. This also guarantees basic WF. + let expr_ty = rcx.resolve_node_type(expr.id); + + type_must_outlive(rcx, infer::ExprTypeIsNotInScope(expr_ty, expr.span), + expr_ty, ty::ReScope(expr.id)); + let method_call = MethodCall::expr(expr.id); let has_method_map = rcx.fcx.inh.method_map.borrow().contains_key(&method_call); @@ -413,7 +582,7 @@ fn visit_expr(rcx: &mut Rcx, expr: &ast::Expr) { for &adjustment in rcx.fcx.inh.adjustments.borrow().find(&expr.id).iter() { debug!("adjustment={:?}", adjustment); match *adjustment { - ty::AutoDerefRef(ty::AutoDerefRef {autoderefs, autoref: opt_autoref}) => { + ty::AutoDerefRef(ty::AutoDerefRef {autoderefs, autoref: ref opt_autoref}) => { let expr_ty = rcx.resolve_node_type(expr.id); constrain_autoderefs(rcx, expr, autoderefs, expr_ty); for autoref in opt_autoref.iter() { @@ -423,30 +592,21 @@ fn visit_expr(rcx: &mut Rcx, expr: &ast::Expr) { // the current node. // // FIXME(#6268) remove to support nested method calls - constrain_regions_in_type_of_node( - rcx, expr.id, ty::ReScope(expr.id), - infer::AutoBorrow(expr.span)); + type_of_node_must_outlive( + rcx, infer::AutoBorrow(expr.span), + expr.id, ty::ReScope(expr.id)); } } - ty::AutoObject(ty::RegionTraitStore(trait_region, _), _, _, _) => { + /* + ty::AutoObject(_, ref bounds, _, _) => { // Determine if we are casting `expr` to a trait - // instance. If so, we have to be sure that the type of - // the source obeys the trait's region bound. - // - // Note: there is a subtle point here concerning type - // parameters. It is possible that the type of `source` - // contains type parameters, which in turn may contain - // regions that are not visible to us (only the caller - // knows about them). The kind checker is ultimately - // responsible for guaranteeing region safety in that - // particular case. There is an extensive comment on the - // function check_cast_for_escaping_regions() in kind.rs - // explaining how it goes about doing that. - + // instance. If so, we have to be sure that the type + // of the source obeys the new region bound. let source_ty = rcx.resolve_node_type(expr.id); - constrain_regions_in_type(rcx, trait_region, - infer::RelateObjectBound(expr.span), source_ty); + type_must_outlive(rcx, infer::RelateObjectBound(expr.span), + source_ty, bounds.region_bound); } + */ _ => {} } } @@ -454,12 +614,11 @@ fn visit_expr(rcx: &mut Rcx, expr: &ast::Expr) { match expr.node { ast::ExprCall(ref callee, ref args) => { if has_method_map { - constrain_call(rcx, None, expr, Some(*callee), + constrain_call(rcx, expr, Some(*callee), args.as_slice(), false); } else { constrain_callee(rcx, callee.id, expr, &**callee); constrain_call(rcx, - Some(callee.id), expr, None, args.as_slice(), @@ -470,7 +629,7 @@ fn visit_expr(rcx: &mut Rcx, expr: &ast::Expr) { } ast::ExprMethodCall(_, _, ref args) => { - constrain_call(rcx, None, expr, Some(*args.get(0)), + constrain_call(rcx, expr, Some(*args.get(0)), args.slice_from(1), false); visit::walk_expr(rcx, expr, ()); @@ -483,7 +642,7 @@ fn visit_expr(rcx: &mut Rcx, expr: &ast::Expr) { ast::ExprAssignOp(_, ref lhs, ref rhs) => { if has_method_map { - constrain_call(rcx, None, expr, Some(lhs.clone()), + constrain_call(rcx, expr, Some(lhs.clone()), [rhs.clone()], true); } @@ -498,7 +657,7 @@ fn visit_expr(rcx: &mut Rcx, expr: &ast::Expr) { // overloaded op. Note that we (sadly) currently use an // implicit "by ref" sort of passing style here. This // should be converted to an adjustment! - constrain_call(rcx, None, expr, Some(lhs.clone()), + constrain_call(rcx, expr, Some(lhs.clone()), [rhs.clone()], true); visit::walk_expr(rcx, expr, ()); @@ -506,8 +665,16 @@ fn visit_expr(rcx: &mut Rcx, expr: &ast::Expr) { ast::ExprUnary(_, ref lhs) if has_method_map => { // As above. - constrain_call(rcx, None, expr, Some(lhs.clone()), [], true); + constrain_call(rcx, expr, Some(lhs.clone()), [], true); + + visit::walk_expr(rcx, expr, ()); + } + ast::ExprUnary(ast::UnBox, ref base) => { + // Managed data must not have borrowed pointers within it: + let base_ty = rcx.resolve_node_type(base.id); + type_must_outlive(rcx, infer::Managed(expr.span), + base_ty, ty::ReStatic); visit::walk_expr(rcx, expr, ()); } @@ -516,7 +683,7 @@ fn visit_expr(rcx: &mut Rcx, expr: &ast::Expr) { let method_call = MethodCall::expr(expr.id); let base_ty = match rcx.fcx.inh.method_map.borrow().find(&method_call) { Some(method) => { - constrain_call(rcx, None, expr, Some(base.clone()), [], true); + constrain_call(rcx, expr, Some(base.clone()), [], true); ty::ty_fn_ret(method.ty) } None => rcx.resolve_node_type(base.id) @@ -544,34 +711,7 @@ fn visit_expr(rcx: &mut Rcx, expr: &ast::Expr) { // Determine if we are casting `source` to a trait // instance. If so, we have to be sure that the type of // the source obeys the trait's region bound. - // - // Note: there is a subtle point here concerning type - // parameters. It is possible that the type of `source` - // contains type parameters, which in turn may contain - // regions that are not visible to us (only the caller - // knows about them). The kind checker is ultimately - // responsible for guaranteeing region safety in that - // particular case. There is an extensive comment on the - // function check_cast_for_escaping_regions() in kind.rs - // explaining how it goes about doing that. - let target_ty = rcx.resolve_node_type(expr.id); - match ty::get(target_ty).sty { - ty::ty_rptr(trait_region, ty::mt{ty, ..}) => { - match ty::get(ty).sty { - ty::ty_trait(..) => { - let source_ty = rcx.resolve_expr_type_adjusted(&**source); - constrain_regions_in_type( - rcx, - trait_region, - infer::RelateObjectBound(expr.span), - source_ty); - } - _ => {} - } - } - _ => () - } - + constrain_cast(rcx, expr, &**source); visit::walk_expr(rcx, expr, ()); } @@ -586,8 +726,8 @@ fn visit_expr(rcx: &mut Rcx, expr: &ast::Expr) { // // FIXME(#6268) nested method calls requires that this rule change let ty0 = rcx.resolve_node_type(expr.id); - constrain_regions_in_type(rcx, ty::ReScope(expr.id), - infer::AddrOf(expr.span), ty0); + type_must_outlive(rcx, infer::AddrOf(expr.span), + ty0, ty::ReScope(expr.id)); visit::walk_expr(rcx, expr, ()); } @@ -641,42 +781,108 @@ fn visit_expr(rcx: &mut Rcx, expr: &ast::Expr) { } } +fn constrain_cast(rcx: &mut Rcx, + cast_expr: &ast::Expr, + source_expr: &ast::Expr) +{ + debug!("constrain_cast(cast_expr={}, source_expr={})", + cast_expr.repr(rcx.tcx()), + source_expr.repr(rcx.tcx())); + + let source_ty = rcx.resolve_node_type(source_expr.id); + let target_ty = rcx.resolve_node_type(cast_expr.id); + + walk_cast(rcx, cast_expr, source_ty, target_ty); + + fn walk_cast(rcx: &mut Rcx, + cast_expr: &ast::Expr, + from_ty: ty::t, + to_ty: ty::t) { + debug!("walk_cast(from_ty={}, to_ty={})", + from_ty.repr(rcx.tcx()), + to_ty.repr(rcx.tcx())); + match (&ty::get(from_ty).sty, &ty::get(to_ty).sty) { + /*From:*/ (&ty::ty_rptr(from_r, ref from_mt), + /*To: */ &ty::ty_rptr(to_r, ref to_mt)) => { + // Target cannot outlive source, naturally. + rcx.fcx.mk_subr(infer::Reborrow(cast_expr.span), to_r, from_r); + walk_cast(rcx, cast_expr, from_mt.ty, to_mt.ty); + } + + /*From:*/ (_, + /*To: */ &ty::ty_trait(box ty::TyTrait { bounds, .. })) => { + // When T is existentially quantified as a trait + // `Foo+'to`, it must outlive the region bound `'to`. + type_must_outlive(rcx, infer::RelateObjectBound(cast_expr.span), + from_ty, bounds.region_bound); + } + + /*From:*/ (&ty::ty_uniq(from_referent_ty), + /*To: */ &ty::ty_uniq(to_referent_ty)) => { + walk_cast(rcx, cast_expr, from_referent_ty, to_referent_ty); + } + + _ => { } + } + } +} + fn check_expr_fn_block(rcx: &mut Rcx, expr: &ast::Expr, body: &ast::Block) { let tcx = rcx.fcx.tcx(); let function_type = rcx.resolve_node_type(expr.id); + match ty::get(function_type).sty { - ty::ty_closure(box ty::ClosureTy { - store: ty::RegionTraitStore(region, _), ..}) => { + ty::ty_closure(box ty::ClosureTy{store: ty::RegionTraitStore(..), + bounds: ref bounds, + ..}) => { + // For closure, ensure that the variables outlive region + // bound, since they are captured by reference. freevars::with_freevars(tcx, expr.id, |freevars| { if freevars.is_empty() { // No free variables means that the environment // will be NULL at runtime and hence the closure // has static lifetime. } else { - // Closure must not outlive the variables it closes over. - constrain_free_variables(rcx, region, expr, freevars); + // Variables being referenced must outlive closure. + constrain_free_variables_in_stack_closure( + rcx, bounds.region_bound, expr, freevars); - // Closure cannot outlive the appropriate temporary scope. + // Closure is stack allocated and hence cannot + // outlive the appropriate temporary scope. let s = rcx.repeating_scope; - rcx.fcx.mk_subr(true, infer::InfStackClosure(expr.span), - region, ty::ReScope(s)); + rcx.fcx.mk_subr(infer::InfStackClosure(expr.span), + bounds.region_bound, ty::ReScope(s)); } }); } + ty::ty_closure(box ty::ClosureTy{store: ty::UniqTraitStore, + bounds: ref bounds, + ..}) => { + // For proc, ensure that the *types* of the variables + // outlive region bound, since they are captured by value. + freevars::with_freevars(tcx, expr.id, |freevars| { + ensure_free_variable_types_outlive_closure_bound( + rcx, bounds.region_bound, expr, freevars); + }); + } ty::ty_unboxed_closure(_, region) => { freevars::with_freevars(tcx, expr.id, |freevars| { // No free variables means that there is no environment and // hence the closure has static lifetime. Otherwise, the // closure must not outlive the variables it closes over // by-reference. + // + // NDM -- this seems wrong, discuss with pcwalton, should + // be straightforward enough. if !freevars.is_empty() { - constrain_free_variables(rcx, region, expr, freevars); + ensure_free_variable_types_outlive_closure_bound( + rcx, region, expr, freevars); } }) } - _ => () + _ => { } } let repeating_scope = rcx.set_repeating_scope(body.id); @@ -695,36 +901,74 @@ fn check_expr_fn_block(rcx: &mut Rcx, _ => () } - fn constrain_free_variables(rcx: &mut Rcx, - region: ty::Region, - expr: &ast::Expr, - freevars: &[freevars::freevar_entry]) { + fn ensure_free_variable_types_outlive_closure_bound( + rcx: &mut Rcx, + region_bound: ty::Region, + expr: &ast::Expr, + freevars: &[freevars::freevar_entry]) + { /*! - * Make sure that all free variables referenced inside the closure - * outlive the closure itself. Also, create an entry in the - * upvar_borrows map with a region. + * Make sure that the type of all free variables referenced + * inside a closure/proc outlive the closure/proc's lifetime + * bound. This is just a special case of the usual rules about + * closed over values outliving the object's lifetime bound. + */ + + let tcx = rcx.fcx.ccx.tcx; + + debug!("ensure_free_variable_types_outlive_closure_bound({}, {})", + region_bound.repr(tcx), expr.repr(tcx)); + + for freevar in freevars.iter() { + let var_node_id = { + let def_id = freevar.def.def_id(); + assert!(def_id.krate == ast::LOCAL_CRATE); + def_id.node + }; + + let var_ty = rcx.resolve_node_type(var_node_id); + + type_must_outlive( + rcx, infer::RelateProcBound(expr.span, var_node_id, var_ty), + var_ty, region_bound); + } + } + + fn constrain_free_variables_in_stack_closure( + rcx: &mut Rcx, + region_bound: ty::Region, + expr: &ast::Expr, + freevars: &[freevars::freevar_entry]) + { + /*! + * Make sure that all free variables referenced inside the + * closure outlive the closure's lifetime bound. Also, create + * an entry in the upvar_borrows map with a region. */ let tcx = rcx.fcx.ccx.tcx; let infcx = rcx.fcx.infcx(); debug!("constrain_free_variables({}, {})", - region.repr(tcx), expr.repr(tcx)); + region_bound.repr(tcx), expr.repr(tcx)); for freevar in freevars.iter() { debug!("freevar def is {:?}", freevar.def); // Identify the variable being closed over and its node-id. let def = freevar.def; - let def_id = def.def_id(); - assert!(def_id.krate == ast::LOCAL_CRATE); - let upvar_id = ty::UpvarId { var_id: def_id.node, + let var_node_id = { + let def_id = def.def_id(); + assert!(def_id.krate == ast::LOCAL_CRATE); + def_id.node + }; + let upvar_id = ty::UpvarId { var_id: var_node_id, closure_expr_id: expr.id }; // Create a region variable to represent this borrow. This borrow // must outlive the region on the closure. let origin = infer::UpvarRegion(upvar_id, expr.span); let freevar_region = infcx.next_region_var(origin); - rcx.fcx.mk_subr(true, infer::FreeVariable(freevar.span, def_id.node), - region, freevar_region); + rcx.fcx.mk_subr(infer::FreeVariable(freevar.span, var_node_id), + region_bound, freevar_region); // Create a UpvarBorrow entry. Note that we begin with a // const borrow_kind, but change it to either mut or @@ -735,10 +979,10 @@ fn check_expr_fn_block(rcx: &mut Rcx, upvar_borrow); // Guarantee that the closure does not outlive the variable itself. - let en_region = region_of_def(rcx.fcx, def); - debug!("en_region = {}", en_region.repr(tcx)); - rcx.fcx.mk_subr(true, infer::FreeVariable(freevar.span, def_id.node), - region, en_region); + let enclosing_region = region_of_def(rcx.fcx, def); + debug!("enclosing_region = {}", enclosing_region.repr(tcx)); + rcx.fcx.mk_subr(infer::FreeVariable(freevar.span, var_node_id), + region_bound, enclosing_region); } } @@ -814,7 +1058,7 @@ fn constrain_callee(rcx: &mut Rcx, } ty::UniqTraitStore => ty::ReStatic }; - rcx.fcx.mk_subr(true, infer::InvokeClosure(callee_expr.span), + rcx.fcx.mk_subr(infer::InvokeClosure(callee_expr.span), call_region, region); } _ => { @@ -829,9 +1073,6 @@ fn constrain_callee(rcx: &mut Rcx, } fn constrain_call(rcx: &mut Rcx, - // might be expr_call, expr_method_call, or an overloaded - // operator - fn_expr_id: Option<ast::NodeId>, call_expr: &ast::Expr, receiver: Option<Gc<ast::Expr>>, arg_exprs: &[Gc<ast::Expr>], @@ -850,16 +1091,6 @@ fn constrain_call(rcx: &mut Rcx, receiver.repr(tcx), arg_exprs.repr(tcx), implicitly_ref_args); - let callee_ty = match fn_expr_id { - Some(id) => rcx.resolve_node_type(id), - None => rcx.resolve_method_type(MethodCall::expr(call_expr.id)) - .expect("call should have been to a method") - }; - if ty::type_is_error(callee_ty) { - // Bail, as function type is unknown - return; - } - let fn_sig = ty::ty_fn_sig(callee_ty); // `callee_region` is the scope representing the time in which the // call occurs. @@ -868,14 +1099,16 @@ fn constrain_call(rcx: &mut Rcx, let callee_scope = call_expr.id; let callee_region = ty::ReScope(callee_scope); + debug!("callee_region={}", callee_region.repr(tcx)); + for arg_expr in arg_exprs.iter() { - debug!("Argument"); + debug!("Argument: {}", arg_expr.repr(tcx)); // ensure that any regions appearing in the argument type are // valid for at least the lifetime of the function: - constrain_regions_in_type_of_node( - rcx, arg_expr.id, callee_region, - infer::CallArg(arg_expr.span)); + type_of_node_must_outlive( + rcx, infer::CallArg(arg_expr.span), + arg_expr.id, callee_region); // unfortunately, there are two means of taking implicit // references, and we need to propagate constraints as a @@ -888,19 +1121,14 @@ fn constrain_call(rcx: &mut Rcx, // as loop above, but for receiver for r in receiver.iter() { - debug!("Receiver"); - constrain_regions_in_type_of_node( - rcx, r.id, callee_region, infer::CallRcvr(r.span)); + debug!("receiver: {}", r.repr(tcx)); + type_of_node_must_outlive( + rcx, infer::CallRcvr(r.span), + r.id, callee_region); if implicitly_ref_args { link_by_ref(rcx, &**r, callee_scope); } } - - // constrain regions that may appear in the return type to be - // valid for the function call: - constrain_regions_in_type( - rcx, callee_region, infer::CallReturn(call_expr.span), - fn_sig.output); } fn constrain_autoderefs(rcx: &mut Rcx, @@ -939,12 +1167,10 @@ fn constrain_autoderefs(rcx: &mut Rcx, } // Specialized version of constrain_call. - constrain_regions_in_type(rcx, r_deref_expr, - infer::CallRcvr(deref_expr.span), - self_ty); - constrain_regions_in_type(rcx, r_deref_expr, - infer::CallReturn(deref_expr.span), - fn_sig.output); + type_must_outlive(rcx, infer::CallRcvr(deref_expr.span), + self_ty, r_deref_expr); + type_must_outlive(rcx, infer::CallReturn(deref_expr.span), + fn_sig.output, r_deref_expr); fn_sig.output } None => derefd_ty @@ -971,7 +1197,7 @@ pub fn mk_subregion_due_to_dereference(rcx: &mut Rcx, deref_span: Span, minimum_lifetime: ty::Region, maximum_lifetime: ty::Region) { - rcx.fcx.mk_subr(true, infer::DerefPointer(deref_span), + rcx.fcx.mk_subr(infer::DerefPointer(deref_span), minimum_lifetime, maximum_lifetime) } @@ -993,7 +1219,7 @@ fn constrain_index(rcx: &mut Rcx, match ty::get(indexed_ty).sty { ty::ty_rptr(r_ptr, mt) => match ty::get(mt.ty).sty { ty::ty_vec(_, None) | ty::ty_str => { - rcx.fcx.mk_subr(true, infer::IndexSlice(index_expr.span), + rcx.fcx.mk_subr(infer::IndexSlice(index_expr.span), r_index_expr, r_ptr); } _ => {} @@ -1003,14 +1229,17 @@ fn constrain_index(rcx: &mut Rcx, } } -fn constrain_regions_in_type_of_node( +fn type_of_node_must_outlive( rcx: &mut Rcx, + origin: infer::SubregionOrigin, id: ast::NodeId, - minimum_lifetime: ty::Region, - origin: infer::SubregionOrigin) { - //! Guarantees that any lifetimes which appear in the type of - //! the node `id` (after applying adjustments) are valid for at - //! least `minimum_lifetime` + minimum_lifetime: ty::Region) +{ + /*! + * Guarantees that any lifetimes which appear in the type of + * the node `id` (after applying adjustments) are valid for at + * least `minimum_lifetime` + */ let tcx = rcx.fcx.tcx(); @@ -1025,54 +1254,7 @@ fn constrain_regions_in_type_of_node( ty={}, ty0={}, id={}, minimum_lifetime={:?})", ty_to_string(tcx, ty), ty_to_string(tcx, ty0), id, minimum_lifetime); - constrain_regions_in_type(rcx, minimum_lifetime, origin, ty); -} - -fn constrain_regions_in_type( - rcx: &mut Rcx, - minimum_lifetime: ty::Region, - origin: infer::SubregionOrigin, - ty: ty::t) { - /*! - * Requires that any regions which appear in `ty` must be - * superregions of `minimum_lifetime`. Also enforces the constraint - * that given a pointer type `&'r T`, T must not contain regions - * that outlive 'r, as well as analogous constraints for other - * lifetime'd types. - * - * This check prevents regions from being used outside of the block in - * which they are valid. Recall that regions represent blocks of - * code or expressions: this requirement basically says "any place - * that uses or may use a region R must be within the block of - * code that R corresponds to." - */ - - let tcx = rcx.fcx.ccx.tcx; - - debug!("constrain_regions_in_type(minimum_lifetime={}, ty={})", - region_to_string(tcx, "", false, minimum_lifetime), - ty_to_string(tcx, ty)); - - relate_nested_regions(tcx, Some(minimum_lifetime), ty, |r_sub, r_sup| { - debug!("relate_nested_regions(r_sub={}, r_sup={})", - r_sub.repr(tcx), - r_sup.repr(tcx)); - - if r_sup.is_bound() || r_sub.is_bound() { - // a bound region is one which appears inside an fn type. - // (e.g., the `&` in `fn(&T)`). Such regions need not be - // constrained by `minimum_lifetime` as they are placeholders - // for regions that are as-yet-unknown. - } else if r_sub == minimum_lifetime { - rcx.fcx.mk_subr( - true, origin.clone(), - r_sub, r_sup); - } else { - rcx.fcx.mk_subr( - true, infer::ReferenceOutlivesReferent(ty, origin.span()), - r_sub, r_sup); - } - }); + type_must_outlive(rcx, origin, ty, minimum_lifetime); } fn link_addr_of(rcx: &mut Rcx, expr: &ast::Expr, @@ -1176,24 +1358,12 @@ fn link_autoref(rcx: &Rcx, debug!("expr_cmt={}", expr_cmt.repr(rcx.tcx())); match *autoref { - ty::AutoPtr(r, m) => { - link_region(rcx, expr.span, r, - ty::BorrowKind::from_mutbl(m), expr_cmt); - } - - ty::AutoBorrowVec(r, m) | ty::AutoBorrowVecRef(r, m) => { - let cmt_index = mc.cat_index(expr, expr_cmt, autoderefs+1); + ty::AutoPtr(r, m, _) => { link_region(rcx, expr.span, r, - ty::BorrowKind::from_mutbl(m), cmt_index); + ty::BorrowKind::from_mutbl(m), expr_cmt); } - ty::AutoBorrowObj(r, m) => { - let cmt_deref = mc.cat_deref_obj(expr, expr_cmt); - link_region(rcx, expr.span, r, - ty::BorrowKind::from_mutbl(m), cmt_deref); - } - - ty::AutoUnsafe(_) => {} + ty::AutoUnsafe(_) | ty::AutoUnsizeUniq(_) | ty::AutoUnsize(_) => {} } } @@ -1299,7 +1469,7 @@ fn link_region(rcx: &Rcx, debug!("link_region: {} <= {}", region_min.repr(rcx.tcx()), r_borrowed.repr(rcx.tcx())); - rcx.fcx.mk_subr(true, cause, region_min, r_borrowed); + rcx.fcx.mk_subr(cause, region_min, r_borrowed); if kind != ty::ImmBorrow { // If this is a mutable borrow, then the thing @@ -1531,3 +1701,99 @@ fn adjust_upvar_borrow_kind(upvar_id: ty::UpvarId, } } } + +fn type_must_outlive(rcx: &mut Rcx, + origin: infer::SubregionOrigin, + ty: ty::t, + region: ty::Region) +{ + /*! + * Ensures that all borrowed data reachable via `ty` outlives `region`. + */ + + debug!("type_must_outlive(ty={}, region={})", + ty.repr(rcx.tcx()), + region.repr(rcx.tcx())); + + let constraints = + regionmanip::region_wf_constraints( + rcx.tcx(), + ty, + region); + for constraint in constraints.iter() { + debug!("constraint: {}", constraint.repr(rcx.tcx())); + match *constraint { + regionmanip::RegionSubRegionConstraint(None, r_a, r_b) => { + rcx.fcx.mk_subr(origin.clone(), r_a, r_b); + } + regionmanip::RegionSubRegionConstraint(Some(ty), r_a, r_b) => { + let o1 = infer::ReferenceOutlivesReferent(ty, origin.span()); + rcx.fcx.mk_subr(o1, r_a, r_b); + } + regionmanip::RegionSubParamConstraint(None, r_a, param_b) => { + param_must_outlive(rcx, origin.clone(), r_a, param_b); + } + regionmanip::RegionSubParamConstraint(Some(ty), r_a, param_b) => { + let o1 = infer::ReferenceOutlivesReferent(ty, origin.span()); + param_must_outlive(rcx, o1, r_a, param_b); + } + } + } +} + +fn param_must_outlive(rcx: &Rcx, + origin: infer::SubregionOrigin, + region: ty::Region, + param_ty: ty::ParamTy) { + let param_env = &rcx.fcx.inh.param_env; + + debug!("param_must_outlive(region={}, param_ty={})", + region.repr(rcx.tcx()), + param_ty.repr(rcx.tcx())); + + // Collect all regions that `param_ty` is known to outlive into + // this vector: + let mut param_bounds; + + // To start, collect bounds from user: + let param_bound = param_env.bounds.get(param_ty.space, param_ty.idx); + param_bounds = + ty::required_region_bounds(rcx.tcx(), + param_bound.opt_region_bound.as_slice(), + param_bound.builtin_bounds, + param_bound.trait_bounds.as_slice()); + + // Collect default bound of fn body that applies to all in scope + // type parameters: + param_bounds.push(param_env.implicit_region_bound); + + // Finally, collect regions we scraped from the well-formedness + // constraints in the fn signature. To do that, we walk the list + // of known relations from the fn ctxt. + // + // This is crucial because otherwise code like this fails: + // + // fn foo<'a, A>(x: &'a A) { x.bar() } + // + // The problem is that the type of `x` is `&'a A`. To be + // well-formed, then, A must be lower-bounded by `'a`, but we + // don't know that this holds from first principles. + for &(ref r, ref p) in rcx.region_param_pairs.iter() { + debug!("param_ty={}/{} p={}/{}", + param_ty.repr(rcx.tcx()), + param_ty.def_id, + p.repr(rcx.tcx()), + p.def_id); + if param_ty == *p { + param_bounds.push(*r); + } + } + + // Inform region inference that this parameter type must be + // properly bounded. + infer::verify_param_bound(rcx.fcx.infcx(), + origin, + param_ty, + region, + param_bounds); +} diff --git a/src/librustc/middle/typeck/check/regionmanip.rs b/src/librustc/middle/typeck/check/regionmanip.rs index 53e26f8696f..577da159162 100644 --- a/src/librustc/middle/typeck/check/regionmanip.rs +++ b/src/librustc/middle/typeck/check/regionmanip.rs @@ -10,13 +10,15 @@ // #![warn(deprecated_mode)] +use middle::subst::{ParamSpace, Subst, Substs}; use middle::ty; use middle::ty_fold; use middle::ty_fold::TypeFolder; +use syntax::ast; + use std::collections::HashMap; use util::ppaux::Repr; -use util::ppaux; // Helper functions related to manipulating region types. @@ -44,125 +46,363 @@ pub fn replace_late_bound_regions_in_fn_sig( (map, fn_sig) } -pub fn relate_nested_regions(tcx: &ty::ctxt, - opt_region: Option<ty::Region>, - ty: ty::t, - relate_op: |ty::Region, ty::Region|) { +pub enum WfConstraint { + RegionSubRegionConstraint(Option<ty::t>, ty::Region, ty::Region), + RegionSubParamConstraint(Option<ty::t>, ty::Region, ty::ParamTy), +} + +struct Wf<'a> { + tcx: &'a ty::ctxt, + stack: Vec<(ty::Region, Option<ty::t>)>, + out: Vec<WfConstraint>, +} + +pub fn region_wf_constraints( + tcx: &ty::ctxt, + ty: ty::t, + outer_region: ty::Region) + -> Vec<WfConstraint> +{ /*! - * This rather specialized function walks each region `r` that appear - * in `ty` and invokes `relate_op(r_encl, r)` for each one. `r_encl` - * here is the region of any enclosing `&'r T` pointer. If there is - * no enclosing pointer, and `opt_region` is Some, then `opt_region.get()` - * is used instead. Otherwise, no callback occurs at all). - * - * Here are some examples to give you an intution: - * - * - `relate_nested_regions(Some('r1), &'r2 uint)` invokes - * - `relate_op('r1, 'r2)` - * - `relate_nested_regions(Some('r1), &'r2 &'r3 uint)` invokes - * - `relate_op('r1, 'r2)` - * - `relate_op('r2, 'r3)` - * - `relate_nested_regions(None, &'r2 &'r3 uint)` invokes - * - `relate_op('r2, 'r3)` - * - `relate_nested_regions(None, &'r2 &'r3 &'r4 uint)` invokes - * - `relate_op('r2, 'r3)` - * - `relate_op('r2, 'r4)` - * - `relate_op('r3, 'r4)` - * - * This function is used in various pieces of code because we enforce the - * constraint that a region pointer cannot outlive the things it points at. - * Hence, in the second example above, `'r2` must be a subregion of `'r3`. + * This routine computes the well-formedness constraints that must + * hold for the type `ty` to appear in a context with lifetime + * `outer_region` */ - let mut rr = RegionRelator { tcx: tcx, - stack: Vec::new(), - relate_op: relate_op }; - match opt_region { - Some(o_r) => { rr.stack.push(o_r); } - None => {} - } - rr.fold_ty(ty); + let mut stack = Vec::new(); + stack.push((outer_region, None)); + let mut wf = Wf { tcx: tcx, + stack: stack, + out: Vec::new() }; + wf.accumulate_from_ty(ty); + wf.out +} - struct RegionRelator<'a> { - tcx: &'a ty::ctxt, - stack: Vec<ty::Region>, - relate_op: |ty::Region, ty::Region|: 'a, - } +impl<'a> Wf<'a> { + fn accumulate_from_ty(&mut self, ty: ty::t) { + debug!("Wf::accumulate_from_ty(ty={})", + ty.repr(self.tcx)); - // FIXME(#10151) -- Define more precisely when a region is - // considered "nested". Consider taking variance into account as - // well. + match ty::get(ty).sty { + ty::ty_nil | + ty::ty_bot | + ty::ty_bool | + ty::ty_char | + ty::ty_int(..) | + ty::ty_uint(..) | + ty::ty_float(..) | + ty::ty_bare_fn(..) | + ty::ty_err | + ty::ty_str => { + // No borrowed content reachable here. + } - impl<'a> TypeFolder for RegionRelator<'a> { - fn tcx<'a>(&'a self) -> &'a ty::ctxt { - self.tcx - } + ty::ty_closure(box ref c) => { + self.accumulate_from_closure_ty(ty, c); + } - fn fold_ty(&mut self, ty: ty::t) -> ty::t { - match ty::get(ty).sty { - ty::ty_rptr(r, ty::mt {ty, ..}) => { - self.relate(r); - self.stack.push(r); - ty_fold::super_fold_ty(self, ty); - self.stack.pop().unwrap(); - } + ty::ty_unboxed_closure(_, region) => { + // An "unboxed closure type" is basically + // modeled here as equivalent to a struct like + // + // struct TheClosure<'b> { + // ... + // } + // + // where the `'b` is the lifetime bound of the + // contents (i.e., all contents must outlive 'b). + self.push_region_constraint_from_top(region); + } - _ => { - ty_fold::super_fold_ty(self, ty); - } + ty::ty_trait(ref t) => { + self.accumulate_from_object_ty(ty, &t.bounds) } - ty - } + ty::ty_enum(def_id, ref substs) | + ty::ty_struct(def_id, ref substs) => { + self.accumulate_from_adt(ty, def_id, substs) + } - fn fold_region(&mut self, r: ty::Region) -> ty::Region { - self.relate(r); - r - } - } + ty::ty_vec(t, _) | + ty::ty_ptr(ty::mt { ty: t, .. }) | + ty::ty_box(t) | + ty::ty_uniq(t) => { + self.accumulate_from_ty(t) + } + + ty::ty_rptr(r_b, mt) => { + self.accumulate_from_rptr(ty, r_b, mt.ty); + } + + ty::ty_param(p) => { + self.push_param_constraint_from_top(p); + } - impl<'a> RegionRelator<'a> { - fn relate(&mut self, r_sub: ty::Region) { - for &r in self.stack.iter() { - if !r.is_bound() && !r_sub.is_bound() { - (self.relate_op)(r, r_sub); + ty::ty_tup(ref tuptys) => { + for &tupty in tuptys.iter() { + self.accumulate_from_ty(tupty); } } + + ty::ty_infer(_) => { + // This should not happen, BUT: + // + // Currently we uncover region relationships on + // entering the fn check. We should do this after + // the fn check, then we can call this case a bug(). + } + + ty::ty_open(_) => { + self.tcx.sess.bug( + format!("Unexpected type encountered while doing wf check: {}", + ty.repr(self.tcx)).as_slice()); + } } } -} -pub fn relate_free_regions(tcx: &ty::ctxt, fn_sig: &ty::FnSig) { - /*! - * This function populates the region map's `free_region_map`. - * It walks over the transformed self type and argument types - * for each function just before we check the body of that - * function, looking for types where you have a borrowed - * pointer to other borrowed data (e.g., `&'a &'b [uint]`. - * We do not allow references to outlive the things they - * point at, so we can assume that `'a <= 'b`. - * - * Tests: `src/test/compile-fail/regions-free-region-ordering-*.rs` - */ + fn accumulate_from_rptr(&mut self, + ty: ty::t, + r_b: ty::Region, + ty_b: ty::t) { + // We are walking down a type like this, and current + // position is indicated by caret: + // + // &'a &'b ty_b + // ^ + // + // At this point, top of stack will be `'a`. We must + // require that `'a <= 'b`. + + self.push_region_constraint_from_top(r_b); + + // Now we push `'b` onto the stack, because it must + // constrain any borrowed content we find within `T`. - debug!("relate_free_regions >>"); + self.stack.push((r_b, Some(ty))); + self.accumulate_from_ty(ty_b); + self.stack.pop().unwrap(); + } + + fn push_region_constraint_from_top(&mut self, + r_b: ty::Region) { + /*! + * Pushes a constraint that `r_b` must outlive the + * top region on the stack. + */ + + // Indicates that we have found borrowed content with a lifetime + // of at least `r_b`. This adds a constraint that `r_b` must + // outlive the region `r_a` on top of the stack. + // + // As an example, imagine walking a type like: + // + // &'a &'b T + // ^ + // + // when we hit the inner pointer (indicated by caret), `'a` will + // be on top of stack and `'b` will be the lifetime of the content + // we just found. So we add constraint that `'a <= 'b`. + + let &(r_a, opt_ty) = self.stack.last().unwrap(); + self.push_sub_region_constraint(opt_ty, r_a, r_b); + } - let mut all_tys = Vec::new(); - for arg in fn_sig.inputs.iter() { - all_tys.push(*arg); + fn push_sub_region_constraint(&mut self, + opt_ty: Option<ty::t>, + r_a: ty::Region, + r_b: ty::Region) { + /*! Pushes a constraint that `r_a <= r_b`, due to `opt_ty` */ + self.out.push(RegionSubRegionConstraint(opt_ty, r_a, r_b)); } - for &t in all_tys.iter() { - debug!("relate_free_regions(t={})", ppaux::ty_to_string(tcx, t)); - relate_nested_regions(tcx, None, t, |a, b| { - match (&a, &b) { - (&ty::ReFree(free_a), &ty::ReFree(free_b)) => { - tcx.region_maps.relate_free_regions(free_a, free_b); + fn push_param_constraint_from_top(&mut self, + param_ty: ty::ParamTy) { + /*! + * Pushes a constraint that `param_ty` must outlive the + * top region on the stack. + */ + + let &(region, opt_ty) = self.stack.last().unwrap(); + self.push_param_constraint(region, opt_ty, param_ty); + } + + fn push_param_constraint(&mut self, + region: ty::Region, + opt_ty: Option<ty::t>, + param_ty: ty::ParamTy) { + /*! Pushes a constraint that `region <= param_ty`, due to `opt_ty` */ + self.out.push(RegionSubParamConstraint(opt_ty, region, param_ty)); + } + + fn accumulate_from_adt(&mut self, + ty: ty::t, + def_id: ast::DefId, + substs: &Substs) + { + // The generic declarations from the type, appropriately + // substituted for the actual substitutions. + let generics = + ty::lookup_item_type(self.tcx, def_id) + .generics + .subst(self.tcx, substs); + + // Variance of each type/region parameter. + let variances = ty::item_variances(self.tcx, def_id); + + for &space in ParamSpace::all().iter() { + let region_params = substs.regions().get_slice(space); + let region_variances = variances.regions.get_slice(space); + let region_param_defs = generics.regions.get_slice(space); + assert_eq!(region_params.len(), region_variances.len()); + for (®ion_param, (®ion_variance, region_param_def)) in + region_params.iter().zip( + region_variances.iter().zip( + region_param_defs.iter())) + { + match region_variance { + ty::Covariant | ty::Bivariant => { + // Ignore covariant or bivariant region + // parameters. To understand why, consider a + // struct `Foo<'a>`. If `Foo` contains any + // references with lifetime `'a`, then `'a` must + // be at least contravariant (and possibly + // invariant). The only way to have a covariant + // result is if `Foo` contains only a field with a + // type like `fn() -> &'a T`; i.e., a bare + // function that can produce a reference of + // lifetime `'a`. In this case, there is no + // *actual data* with lifetime `'a` that is + // reachable. (Presumably this bare function is + // really returning static data.) + } + + ty::Contravariant | ty::Invariant => { + // If the parameter is contravariant or + // invariant, there may indeed be reachable + // data with this lifetime. See other case for + // more details. + self.push_region_constraint_from_top(region_param); + } + } + + for ®ion_bound in region_param_def.bounds.iter() { + // The type declared a constraint like + // + // 'b : 'a + // + // which means that `'a <= 'b` (after + // substitution). So take the region we + // substituted for `'a` (`region_bound`) and make + // it a subregion of the region we substituted + // `'b` (`region_param`). + self.push_sub_region_constraint( + Some(ty), region_bound, region_param); } - _ => {} } - }) + + let types = substs.types.get_slice(space); + let type_variances = variances.types.get_slice(space); + let type_param_defs = generics.types.get_slice(space); + assert_eq!(types.len(), type_variances.len()); + for (&type_param_ty, (&variance, type_param_def)) in + types.iter().zip( + type_variances.iter().zip( + type_param_defs.iter())) + { + debug!("type_param_ty={} variance={}", + type_param_ty.repr(self.tcx), + variance.repr(self.tcx)); + + match variance { + ty::Contravariant | ty::Bivariant => { + // As above, except that in this it is a + // *contravariant* reference that indices that no + // actual data of type T is reachable. + } + + ty::Covariant | ty::Invariant => { + self.accumulate_from_ty(type_param_ty); + } + } + + // Inspect bounds on this type parameter for any + // region bounds. + for &r in type_param_def.bounds.opt_region_bound.iter() { + self.stack.push((r, Some(ty))); + self.accumulate_from_ty(type_param_ty); + self.stack.pop().unwrap(); + } + } + } + } + + fn accumulate_from_closure_ty(&mut self, + ty: ty::t, + c: &ty::ClosureTy) + { + match c.store { + ty::RegionTraitStore(r_b, _) => { + self.push_region_constraint_from_top(r_b); + } + ty::UniqTraitStore => { } + } + + self.accumulate_from_object_ty(ty, &c.bounds) } - debug!("<< relate_free_regions"); + fn accumulate_from_object_ty(&mut self, + ty: ty::t, + bounds: &ty::ExistentialBounds) + { + // Imagine a type like this: + // + // trait Foo { } + // trait Bar<'c> : 'c { } + // + // &'b (Foo+'c+Bar<'d>) + // ^ + // + // In this case, the following relationships must hold: + // + // 'b <= 'c + // 'd <= 'c + // + // The first conditions is due to the normal region pointer + // rules, which say that a reference cannot outlive its + // referent. + // + // The final condition may be a bit surprising. In particular, + // you may expect that it would have been `'c <= 'd`, since + // usually lifetimes of outer things are conservative + // approximations for inner things. However, it works somewhat + // differently with trait objects: here the idea is that if the + // user specifies a region bound (`'c`, in this case) it is the + // "master bound" that *implies* that bounds from other traits are + // all met. (Remember that *all bounds* in a type like + // `Foo+Bar+Zed` must be met, not just one, hence if we write + // `Foo<'x>+Bar<'y>`, we know that the type outlives *both* 'x and + // 'y.) + // + // Note: in fact we only permit builtin traits, not `Bar<'d>`, I + // am looking forward to the future here. + + // The content of this object type must outlive + // `bounds.region_bound`: + let r_c = bounds.region_bound; + self.push_region_constraint_from_top(r_c); + + // And then, in turn, to be well-formed, the + // `region_bound` that user specified must imply the + // region bounds required from all of the trait types: + let required_region_bounds = + ty::required_region_bounds(self.tcx, + [], + bounds.builtin_bounds, + []); + for &r_d in required_region_bounds.iter() { + // Each of these is an instance of the `'c <= 'b` + // constraint above + self.out.push(RegionSubRegionConstraint(Some(ty), r_d, r_c)); + } + } } diff --git a/src/librustc/middle/typeck/check/vtable.rs b/src/librustc/middle/typeck/check/vtable.rs index 1aa469b15ba..2d4022a2eaa 100644 --- a/src/librustc/middle/typeck/check/vtable.rs +++ b/src/librustc/middle/typeck/check/vtable.rs @@ -10,7 +10,7 @@ use middle::ty; -use middle::ty::{AutoAddEnv, AutoDerefRef, AutoObject, ParamTy}; +use middle::ty::{AutoDerefRef, ParamTy}; use middle::ty_fold::TypeFolder; use middle::typeck::astconv::AstConv; use middle::typeck::check::{FnCtxt, impl_self_ty}; @@ -94,7 +94,7 @@ fn lookup_vtables(vcx: &VtableContext, let result = type_param_defs.map_rev(|def| { let ty = *substs.types.get(def.space, def.index); lookup_vtables_for_param(vcx, span, Some(substs), - &*def.bounds, ty, is_early) + &def.bounds, ty, is_early) }); debug!("lookup_vtables result(\ @@ -206,7 +206,7 @@ fn relate_trait_refs(vcx: &VtableContext, !ty::trait_ref_contains_error(&r_exp_trait_ref) { let tcx = vcx.tcx(); - span_err!(tcx.sess, span, E0095, "expected {}, but found {} ({})", + span_err!(tcx.sess, span, E0095, "expected {}, found {} ({})", ppaux::trait_ref_to_string(tcx, &r_exp_trait_ref), ppaux::trait_ref_to_string(tcx, &r_act_trait_ref), ty::type_err_to_str(tcx, err)); @@ -388,7 +388,6 @@ fn search_for_vtable(vcx: &VtableContext, trait_ref: Rc<ty::TraitRef>, is_early: bool) -> Option<vtable_origin> { - debug!("nrc - search_for_vtable"); let tcx = vcx.tcx(); // First, check to see whether this is a call to the `call` method of an @@ -565,7 +564,7 @@ fn fixup_substs(vcx: &VtableContext, // use a dummy type just to package up the substs that need fixing up let t = ty::mk_trait(tcx, id, substs, - ty::empty_builtin_bounds()); + ty::region_existential_bound(ty::ReStatic)); fixup_ty(vcx, span, t, is_early).map(|t_f| { match ty::get(t_f).sty { ty::ty_trait(ref inner) => inner.substs.clone(), @@ -630,14 +629,8 @@ pub fn early_resolve_expr(ex: &ast::Expr, fcx: &FnCtxt, is_early: bool) { let _indent = indenter(); let cx = fcx.ccx; - let resolve_object_cast = |src: &ast::Expr, target_ty: ty::t, key: MethodCall| { - // Look up vtables for the type we're casting to, - // passing in the source and target type. The source - // must be a pointer type suitable to the object sigil, - // e.g.: `&x as &Trait` or `box x as Box<Trait>` - // Bounds of type's contents are not checked here, but in kind.rs. - let src_ty = structurally_resolved_type(fcx, ex.span, - fcx.expr_ty(src)); + let check_object_cast = |src_ty: ty::t, target_ty: ty::t| { + // Check that a cast is of correct types. match (&ty::get(target_ty).sty, &ty::get(src_ty).sty) { (&ty::ty_rptr(_, ty::mt{ty, mutbl}), &ty::ty_rptr(_, mt)) if !mutability_allowed(mt.mutbl, mutbl) => { @@ -648,74 +641,13 @@ pub fn early_resolve_expr(ex: &ast::Expr, fcx: &FnCtxt, is_early: bool) { _ => {} } } - - (&ty::ty_uniq(ty), &ty::ty_uniq(..) ) | - (&ty::ty_rptr(_, ty::mt{ty, ..}), &ty::ty_rptr(..)) => { - match ty::get(ty).sty { - ty::ty_trait(box ty::TyTrait { - def_id: target_def_id, substs: ref target_substs, .. - }) => { - debug!("nrc correct path"); - let typ = match &ty::get(src_ty).sty { - &ty::ty_uniq(typ) => typ, - &ty::ty_rptr(_, mt) => mt.ty, - _ => fail!("shouldn't get here"), - }; - - let vcx = fcx.vtable_context(); - - // Take the type parameters from the object - // type, but set the Self type (which is - // unknown, for the object type) to be the type - // we are casting from. - let mut target_types = target_substs.types.clone(); - assert!(target_types.get_self().is_none()); - target_types.push(subst::SelfSpace, typ); - - let target_trait_ref = Rc::new(ty::TraitRef { - def_id: target_def_id, - substs: subst::Substs { - regions: target_substs.regions.clone(), - types: target_types - } - }); - - let param_bounds = ty::ParamBounds { - builtin_bounds: ty::empty_builtin_bounds(), - trait_bounds: vec!(target_trait_ref) - }; - let vtables = - lookup_vtables_for_param(&vcx, - ex.span, - None, - ¶m_bounds, - typ, - is_early); - - if !is_early { - let mut r = VecPerParamSpace::empty(); - r.push(subst::SelfSpace, vtables); - insert_vtables(fcx, key, r); - } - - // Now, if this is &trait, we need to link the - // regions. - match (&ty::get(src_ty).sty, &ty::get(target_ty).sty) { - (&ty::ty_rptr(ra, _), &ty::ty_rptr(rb, _)) => { - debug!("nrc - make subr"); - infer::mk_subr(fcx.infcx(), - false, - infer::RelateObjectBound(ex.span), - rb, - ra); - } - _ => {} - } - } - _ => {} - } + (&ty::ty_uniq(..), &ty::ty_uniq(..) ) => {} + (&ty::ty_rptr(r_t, _), &ty::ty_rptr(r_s, _)) => { + infer::mk_subr(fcx.infcx(), + infer::RelateObjectBound(ex.span), + r_t, + r_s); } - (&ty::ty_uniq(ty), _) => { match ty::get(ty).sty { ty::ty_trait(..) => { @@ -737,7 +669,57 @@ pub fn early_resolve_expr(ex: &ast::Expr, fcx: &FnCtxt, is_early: bool) { _ => {} } } + _ => {} + } + }; + let resolve_object_cast = |src_ty: ty::t, target_ty: ty::t, key: MethodCall| { + // Look up vtables for the type we're casting to, + // passing in the source and target type. The source + // must be a pointer type suitable to the object sigil, + // e.g.: `&x as &Trait` or `box x as Box<Trait>` + // Bounds of type's contents are not checked here, but in kind.rs. + match ty::get(target_ty).sty { + ty::ty_trait(box ty::TyTrait { + def_id: target_def_id, substs: ref target_substs, .. + }) => { + let vcx = fcx.vtable_context(); + + // Take the type parameters from the object + // type, but set the Self type (which is + // unknown, for the object type) to be the type + // we are casting from. + let mut target_types = target_substs.types.clone(); + assert!(target_types.get_self().is_none()); + target_types.push(subst::SelfSpace, src_ty); + + let target_trait_ref = Rc::new(ty::TraitRef { + def_id: target_def_id, + substs: subst::Substs { + regions: target_substs.regions.clone(), + types: target_types + } + }); + + let param_bounds = ty::ParamBounds { + opt_region_bound: None, + builtin_bounds: ty::empty_builtin_bounds(), + trait_bounds: vec!(target_trait_ref) + }; + + let vtables = + lookup_vtables_for_param(&vcx, + ex.span, + None, + ¶m_bounds, + src_ty, + is_early); + if !is_early { + let mut r = VecPerParamSpace::empty(); + r.push(subst::SelfSpace, vtables); + insert_vtables(fcx, key, r); + } + } _ => {} } }; @@ -769,12 +751,14 @@ pub fn early_resolve_expr(ex: &ast::Expr, fcx: &FnCtxt, is_early: bool) { ast::ExprAssignOp(_, _, _) | ast::ExprIndex(_, _) | ast::ExprMethodCall(_, _, _) | - ast::ExprForLoop(..) => { + ast::ExprForLoop(..) | + ast::ExprCall(..) => { match fcx.inh.method_map.borrow().find(&MethodCall::expr(ex.id)) { Some(method) => { debug!("vtable resolution on parameter bounds for method call {}", ex.repr(fcx.tcx())); - let type_param_defs = ty::method_call_type_param_defs(cx.tcx, method.origin); + let type_param_defs = + ty::method_call_type_param_defs(fcx, method.origin); let substs = fcx.method_ty_substs(ex.id); let vcx = fcx.vtable_context(); let vtbls = lookup_vtables(&vcx, ex.span, @@ -790,8 +774,16 @@ pub fn early_resolve_expr(ex: &ast::Expr, fcx: &FnCtxt, is_early: bool) { ast::ExprCast(ref src, _) => { debug!("vtable resolution on expr {}", ex.repr(fcx.tcx())); let target_ty = fcx.expr_ty(ex); - let key = MethodCall::expr(ex.id); - resolve_object_cast(&**src, target_ty, key); + let src_ty = structurally_resolved_type(fcx, ex.span, + fcx.expr_ty(&**src)); + check_object_cast(src_ty, target_ty); + match (ty::deref(src_ty, false), ty::deref(target_ty, false)) { + (Some(s), Some(t)) => { + let key = MethodCall::expr(ex.id); + resolve_object_cast(s.ty, t.ty, key) + } + _ => {} + } } _ => () } @@ -800,7 +792,25 @@ pub fn early_resolve_expr(ex: &ast::Expr, fcx: &FnCtxt, is_early: bool) { match fcx.inh.adjustments.borrow().find(&ex.id) { Some(adjustment) => { match *adjustment { - AutoDerefRef(adj) => { + _ if ty::adjust_is_object(adjustment) => { + let src_ty = structurally_resolved_type(fcx, ex.span, + fcx.expr_ty(ex)); + match ty::type_of_adjust(fcx.tcx(), adjustment) { + Some(target_ty) => { + check_object_cast(src_ty, target_ty) + } + None => {} + } + + match trait_cast_types(fcx, adjustment, src_ty, ex.span) { + Some((s, t)) => { + let key = MethodCall::autoobject(ex.id); + resolve_object_cast(s, t, key) + } + None => fail!("Couldn't extract types from adjustment") + } + } + AutoDerefRef(ref adj) => { for autoderef in range(0, adj.autoderefs) { let method_call = MethodCall::autoderef(ex.id, autoderef); match fcx.inh.method_map.borrow().find(&method_call) { @@ -821,34 +831,72 @@ pub fn early_resolve_expr(ex: &ast::Expr, fcx: &FnCtxt, is_early: bool) { } } } - AutoObject(store, - bounds, - def_id, - ref substs) => { - debug!("doing trait adjustment for expr {} {} \ - (early? {})", - ex.id, - ex.repr(fcx.tcx()), - is_early); - - let trait_ty = ty::mk_trait(cx.tcx, - def_id, - substs.clone(), - bounds); - let object_ty = match store { - ty::UniqTraitStore => ty::mk_uniq(cx.tcx, trait_ty), - ty::RegionTraitStore(r, m) => { - ty::mk_rptr(cx.tcx, r, ty::mt {ty: trait_ty, mutbl: m}) - } - }; + _ => {} + } + } + None => {} + } +} - let key = MethodCall::autoobject(ex.id); - resolve_object_cast(ex, object_ty, key); +// When we coerce (possibly implicitly) from a concrete type to a trait type, this +// function returns the concrete type and trait. This might happen arbitrarily +// deep in the adjustment. This function will fail if the adjustment does not +// match the source type. +// This function will always return types if ty::adjust_is_object is true for the +// adjustment +fn trait_cast_types(fcx: &FnCtxt, + adj: &ty::AutoAdjustment, + src_ty: ty::t, + sp: Span) + -> Option<(ty::t, ty::t)> { + fn trait_cast_types_autoref(fcx: &FnCtxt, + autoref: &ty::AutoRef, + src_ty: ty::t, + sp: Span) + -> Option<(ty::t, ty::t)> { + fn trait_cast_types_unsize(fcx: &FnCtxt, + k: &ty::UnsizeKind, + src_ty: ty::t, + sp: Span) + -> Option<(ty::t, ty::t)> { + match k { + &ty::UnsizeVtable(bounds, def_id, ref substs) => { + Some((src_ty, ty::mk_trait(fcx.tcx(), def_id, substs.clone(), bounds))) } - AutoAddEnv(..) => {} + &ty::UnsizeStruct(box ref k, tp_index) => match ty::get(src_ty).sty { + ty::ty_struct(_, ref substs) => { + let ty_substs = substs.types.get_slice(subst::TypeSpace); + let field_ty = structurally_resolved_type(fcx, sp, ty_substs[tp_index]); + trait_cast_types_unsize(fcx, k, field_ty, sp) + } + _ => fail!("Failed to find a ty_struct to correspond with \ + UnsizeStruct whilst walking adjustment. Found {}", + ppaux::ty_to_string(fcx.tcx(), src_ty)) + }, + _ => None } } - None => {} + + match autoref { + &ty::AutoUnsize(ref k) | + &ty::AutoUnsizeUniq(ref k) => trait_cast_types_unsize(fcx, k, src_ty, sp), + &ty::AutoPtr(_, _, Some(box ref autoref)) => { + trait_cast_types_autoref(fcx, autoref, src_ty, sp) + } + _ => None + } + } + + match adj { + &ty::AutoDerefRef(AutoDerefRef{autoref: Some(ref autoref), autoderefs}) => { + let mut derefed_type = src_ty; + for _ in range(0, autoderefs) { + derefed_type = ty::deref(derefed_type, false).unwrap().ty; + derefed_type = structurally_resolved_type(fcx, sp, derefed_type) + } + trait_cast_types_autoref(fcx, autoref, derefed_type, sp) + } + _ => None } } diff --git a/src/librustc/middle/typeck/check/writeback.rs b/src/librustc/middle/typeck/check/writeback.rs index 892a62249ac..7951c8dfc19 100644 --- a/src/librustc/middle/typeck/check/writeback.rs +++ b/src/librustc/middle/typeck/check/writeback.rs @@ -259,6 +259,7 @@ impl<'cx> WritebackCx<'cx> { } Some(adjustment) => { + let adj_object = ty::adjust_is_object(&adjustment); let resolved_adjustment = match adjustment { ty::AutoAddEnv(store) => { // FIXME(eddyb) #2190 Allow only statically resolved @@ -286,24 +287,17 @@ impl<'cx> WritebackCx<'cx> { self.visit_vtable_map_entry(reason, method_call); } + if adj_object { + let method_call = MethodCall::autoobject(id); + self.visit_method_map_entry(reason, method_call); + self.visit_vtable_map_entry(reason, method_call); + } + ty::AutoDerefRef(ty::AutoDerefRef { autoderefs: adj.autoderefs, autoref: self.resolve(&adj.autoref, reason), }) } - - ty::AutoObject(trait_store, bb, def_id, substs) => { - let method_call = MethodCall::autoobject(id); - self.visit_method_map_entry(reason, method_call); - self.visit_vtable_map_entry(reason, method_call); - - ty::AutoObject( - self.resolve(&trait_store, reason), - self.resolve(&bb, reason), - def_id, - self.resolve(&substs, reason) - ) - } }; debug!("Adjustments for node {}: {:?}", id, resolved_adjustment); self.tcx().adjustments.borrow_mut().insert( diff --git a/src/librustc/middle/typeck/coherence.rs b/src/librustc/middle/typeck/coherence.rs index 3dee787b6c9..a6fa9d84600 100644 --- a/src/librustc/middle/typeck/coherence.rs +++ b/src/librustc/middle/typeck/coherence.rs @@ -23,7 +23,7 @@ use middle::ty::get; use middle::ty::{ImplContainer, ImplOrTraitItemId, MethodTraitItemId}; use middle::ty::{lookup_item_type}; use middle::ty::{t, ty_bool, ty_char, ty_bot, ty_box, ty_enum, ty_err}; -use middle::ty::{ty_str, ty_vec, ty_float, ty_infer, ty_int, ty_nil}; +use middle::ty::{ty_str, ty_vec, ty_float, ty_infer, ty_int, ty_nil, ty_open}; use middle::ty::{ty_param, Polytype, ty_ptr}; use middle::ty::{ty_rptr, ty_struct, ty_trait, ty_tup}; use middle::ty::{ty_uint, ty_unboxed_closure, ty_uniq, ty_bare_fn}; @@ -86,7 +86,7 @@ fn get_base_type(inference_context: &InferCtxt, ty_nil | ty_bot | ty_bool | ty_char | ty_int(..) | ty_uint(..) | ty_float(..) | ty_str(..) | ty_vec(..) | ty_bare_fn(..) | ty_closure(..) | ty_tup(..) | - ty_infer(..) | ty_param(..) | ty_err | + ty_infer(..) | ty_param(..) | ty_err | ty_open(..) | ty_box(_) | ty_uniq(_) | ty_ptr(_) | ty_rptr(_, _) => { debug!("(getting base type) no base type; found {:?}", get(original_type).sty); @@ -166,6 +166,9 @@ fn get_base_type_def_id(inference_context: &InferCtxt, enum, struct, or trait"); } }, + ty_trait(box ty::TyTrait { def_id, .. }) => { + Some(def_id) + } _ => { fail!("get_base_type() returned a type that wasn't an \ enum, struct, or trait"); diff --git a/src/librustc/middle/typeck/collect.rs b/src/librustc/middle/typeck/collect.rs index 5c3317972cd..e7bc06d4972 100644 --- a/src/librustc/middle/typeck/collect.rs +++ b/src/librustc/middle/typeck/collect.rs @@ -42,47 +42,31 @@ use middle::ty::{Polytype}; use middle::ty; use middle::ty_fold::TypeFolder; use middle::typeck::astconv::{AstConv, ty_of_arg}; -use middle::typeck::astconv::{ast_ty_to_ty}; +use middle::typeck::astconv::{ast_ty_to_ty, ast_region_to_region}; use middle::typeck::astconv; use middle::typeck::infer; use middle::typeck::rscope::*; use middle::typeck::{CrateCtxt, lookup_def_tcx, no_params, write_ty_to_tcx}; use middle::typeck; use util::ppaux; -use util::ppaux::Repr; +use util::ppaux::{Repr,UserString}; use std::collections::{HashMap, HashSet}; use std::rc::Rc; use std::gc::Gc; use syntax::abi; -use syntax::ast::{StaticRegionTyParamBound, OtherRegionTyParamBound}; -use syntax::ast::{TraitTyParamBound, UnboxedFnTyParamBound}; use syntax::ast; use syntax::ast_map; use syntax::ast_util::{local_def, split_trait_methods, PostExpansionMethod}; use syntax::codemap::Span; -use syntax::codemap; -use syntax::owned_slice::OwnedSlice; -use syntax::parse::token::special_idents; +use syntax::parse::token::{special_idents}; use syntax::parse::token; use syntax::print::pprust::{path_to_string}; use syntax::visit; -struct CollectItemTypesVisitor<'a> { - ccx: &'a CrateCtxt<'a> -} - -impl<'a> visit::Visitor<()> for CollectItemTypesVisitor<'a> { - fn visit_item(&mut self, i: &ast::Item, _: ()) { - convert(self.ccx, i); - visit::walk_item(self, i, ()); - } - fn visit_foreign_item(&mut self, i: &ast::ForeignItem, _: ()) { - convert_foreign(self.ccx, i); - visit::walk_foreign_item(self, i, ()); - } -} +/////////////////////////////////////////////////////////////////////////// +// Main entry point pub fn collect_item_types(ccx: &CrateCtxt, krate: &ast::Crate) { fn collect_intrinsic_type(ccx: &CrateCtxt, @@ -99,10 +83,57 @@ pub fn collect_item_types(ccx: &CrateCtxt, krate: &ast::Crate) { Some(id) => { collect_intrinsic_type(ccx, id); } None => {} } + let mut visitor = CollectTraitDefVisitor{ ccx: ccx }; + visit::walk_crate(&mut visitor, krate, ()); + let mut visitor = CollectItemTypesVisitor{ ccx: ccx }; visit::walk_crate(&mut visitor, krate, ()); } +/////////////////////////////////////////////////////////////////////////// +// First phase: just collect *trait definitions* -- basically, the set +// of type parameters and supertraits. This is information we need to +// know later when parsing field defs. + +struct CollectTraitDefVisitor<'a> { + ccx: &'a CrateCtxt<'a> +} + +impl<'a> visit::Visitor<()> for CollectTraitDefVisitor<'a> { + fn visit_item(&mut self, i: &ast::Item, _: ()) { + match i.node { + ast::ItemTrait(..) => { + // computing the trait def also fills in the table + let _ = trait_def_of_item(self.ccx, i); + } + _ => { } + } + + visit::walk_item(self, i, ()); + } +} + +/////////////////////////////////////////////////////////////////////////// +// Second phase: collection proper. + +struct CollectItemTypesVisitor<'a> { + ccx: &'a CrateCtxt<'a> +} + +impl<'a> visit::Visitor<()> for CollectItemTypesVisitor<'a> { + fn visit_item(&mut self, i: &ast::Item, _: ()) { + convert(self.ccx, i); + visit::walk_item(self, i, ()); + } + fn visit_foreign_item(&mut self, i: &ast::ForeignItem, _: ()) { + convert_foreign(self.ccx, i); + visit::walk_foreign_item(self, i, ()); + } +} + +/////////////////////////////////////////////////////////////////////////// +// Utility types and common code for the above passes. + pub trait ToTy { fn to_ty<RS:RegionScope>(&self, rs: &RS, ast_ty: &ast::Ty) -> ty::t; } @@ -193,9 +224,9 @@ pub fn get_enum_variant_types(ccx: &CrateCtxt, } } -pub fn ensure_trait_methods(ccx: &CrateCtxt, - trait_id: ast::NodeId, - trait_def: &ty::TraitDef) { +fn collect_trait_methods(ccx: &CrateCtxt, + trait_id: ast::NodeId, + trait_def: &ty::TraitDef) { let tcx = ccx.tcx; match tcx.map.get(trait_id) { ast_map::NodeItem(item) => { @@ -360,7 +391,13 @@ fn convert_methods(ccx: &CrateCtxt, ms: &[Gc<ast::Method>], untransformed_rcvr_ty: ty::t, rcvr_ty_generics: &ty::Generics, - rcvr_visibility: ast::Visibility) { + rcvr_visibility: ast::Visibility) +{ + debug!("convert_methods(untransformed_rcvr_ty={}, \ + rcvr_ty_generics={})", + untransformed_rcvr_ty.repr(ccx.tcx), + rcvr_ty_generics.repr(ccx.tcx)); + let tcx = ccx.tcx; let mut seen_methods = HashSet::new(); for m in ms.iter() { @@ -388,6 +425,9 @@ fn convert_methods(ccx: &CrateCtxt, write_ty_to_tcx(tcx, m.id, fty); + debug!("writing method type: def_id={} mty={}", + mty.def_id, mty.repr(ccx.tcx)); + tcx.impl_or_trait_items .borrow_mut() .insert(mty.def_id, ty::MethodTraitItem(mty)); @@ -448,9 +488,20 @@ pub fn ensure_no_ty_param_bounds(ccx: &CrateCtxt, generics: &ast::Generics, thing: &'static str) { for ty_param in generics.ty_params.iter() { - if ty_param.bounds.len() > 0 { - span_err!(ccx.tcx.sess, span, E0122, - "trait bounds are not allowed in {} definitions", thing); + for bound in ty_param.bounds.iter() { + match *bound { + ast::TraitTyParamBound(..) | ast::UnboxedFnTyParamBound(..) => { + // According to accepted RFC #XXX, we should + // eventually accept these, but it will not be + // part of this PR. Still, convert to warning to + // make bootstrapping easier. + span_warn!(ccx.tcx.sess, span, E0122, + "trait bounds are not (yet) enforced \ + in {} definitions", + thing); + } + ast::RegionTyParamBound(..) => { } + } } } } @@ -520,6 +571,10 @@ pub fn convert(ccx: &CrateCtxt, it: &ast::Item) { ast::ItemTrait(_, _, _, ref trait_methods) => { let trait_def = trait_def_of_item(ccx, it); + debug!("trait_def: ident={} trait_def={}", + it.ident.repr(ccx.tcx), + trait_def.repr(ccx.tcx())); + for trait_method in trait_methods.iter() { let self_type = ty::mk_param(ccx.tcx, subst::SelfSpace, @@ -556,7 +611,7 @@ pub fn convert(ccx: &CrateCtxt, it: &ast::Item) { // We need to do this *after* converting methods, since // convert_methods produces a tcache entry that is wrong for // static trait methods. This is somewhat unfortunate. - ensure_trait_methods(ccx, it.id, &*trait_def); + collect_trait_methods(ccx, it.id, &*trait_def); }, ast::ItemStruct(struct_def, _) => { // Write the class type. @@ -739,6 +794,19 @@ pub fn instantiate_trait_ref(ccx: &CrateCtxt, } } +pub fn instantiate_unboxed_fn_ty(ccx: &CrateCtxt, + unboxed_function: &ast::UnboxedFnTy, + param_ty: ty::ParamTy) + -> Rc<ty::TraitRef> +{ + let rscope = ExplicitRscope; + let param_ty = param_ty.to_ty(ccx.tcx); + Rc::new(astconv::trait_ref_for_unboxed_function(ccx, + &rscope, + unboxed_function, + Some(param_ty))) +} + fn get_trait_def(ccx: &CrateCtxt, trait_id: ast::DefId) -> Rc<ty::TraitDef> { if trait_id.krate != ast::LOCAL_CRATE { return ty::lookup_trait_def(ccx.tcx, trait_id) @@ -761,9 +829,9 @@ pub fn trait_def_of_item(ccx: &CrateCtxt, it: &ast::Item) -> Rc<ty::TraitDef> { _ => {} } - let (generics, unbound, supertraits) = match it.node { - ast::ItemTrait(ref generics, ref unbound, ref supertraits, _) => { - (generics, unbound, supertraits) + let (generics, unbound, bounds) = match it.node { + ast::ItemTrait(ref generics, ref unbound, ref bounds, _) => { + (generics, unbound, bounds) } ref s => { tcx.sess.span_bug( @@ -779,13 +847,16 @@ pub fn trait_def_of_item(ccx: &CrateCtxt, it: &ast::Item) -> Rc<ty::TraitDef> { &substs, generics); - let builtin_bounds = - ensure_supertraits(ccx, it.id, it.span, supertraits, unbound); + let self_param_ty = ty::ParamTy::for_self(def_id); + + let bounds = compute_bounds(ccx, token::SELF_KEYWORD_NAME, self_param_ty, + bounds.as_slice(), unbound, it.span, + &generics.where_clause); let substs = mk_item_substs(ccx, &ty_generics); let trait_def = Rc::new(ty::TraitDef { generics: ty_generics, - bounds: builtin_bounds, + bounds: bounds, trait_ref: Rc::new(ty::TraitRef { def_id: def_id, substs: substs @@ -824,55 +895,6 @@ pub fn trait_def_of_item(ccx: &CrateCtxt, it: &ast::Item) -> Rc<ty::TraitDef> { subst::Substs::new_trait(types, regions, self_ty) } - - fn ensure_supertraits(ccx: &CrateCtxt, - id: ast::NodeId, - sp: codemap::Span, - ast_trait_refs: &Vec<ast::TraitRef>, - unbound: &Option<ast::TyParamBound>) - -> ty::BuiltinBounds - { - let tcx = ccx.tcx; - - // Called only the first time trait_def_of_item is called. - // Supertraits are ensured at the same time. - assert!(!tcx.supertraits.borrow().contains_key(&local_def(id))); - - let self_ty = ty::mk_self_type(ccx.tcx, local_def(id)); - let mut ty_trait_refs: Vec<Rc<ty::TraitRef>> = Vec::new(); - let mut bounds = ty::empty_builtin_bounds(); - for ast_trait_ref in ast_trait_refs.iter() { - let trait_def_id = ty::trait_ref_to_def_id(ccx.tcx, ast_trait_ref); - - // FIXME(#8559): Need to instantiate the trait_ref whether - // or not it's a builtin trait, so that the trait's node - // id appears in the tcx trait_ref map. This is only - // needed for metadata; see the similar fixme in - // encoder.rs. - - let trait_ref = instantiate_trait_ref(ccx, ast_trait_ref, self_ty); - if !ty::try_add_builtin_trait(ccx.tcx, trait_def_id, &mut bounds) { - - // FIXME(#5527) Could have same trait multiple times - if ty_trait_refs.iter().any( - |other_trait| other_trait.def_id == trait_ref.def_id) - { - // This means a trait inherited from the same - // supertrait more than once. - span_err!(tcx.sess, sp, E0127, - "duplicate supertrait in trait declaration"); - break; - } else { - ty_trait_refs.push(trait_ref); - } - } - } - - add_unsized_bound(ccx, unbound, &mut bounds, "trait", sp); - tcx.supertraits.borrow_mut().insert(local_def(id), - Rc::new(ty_trait_refs)); - bounds - } } pub fn ty_of_item(ccx: &CrateCtxt, it: &ast::Item) @@ -984,11 +1006,12 @@ pub fn ty_of_foreign_item(ccx: &CrateCtxt, fn ty_generics_for_type(ccx: &CrateCtxt, generics: &ast::Generics) - -> ty::Generics { + -> ty::Generics +{ ty_generics(ccx, subst::TypeSpace, - &generics.lifetimes, - &generics.ty_params, + generics.lifetimes.as_slice(), + generics.ty_params.as_slice(), ty::Generics::empty(), &generics.where_clause) } @@ -1000,8 +1023,8 @@ fn ty_generics_for_trait(ccx: &CrateCtxt, -> ty::Generics { let mut generics = ty_generics(ccx, subst::TypeSpace, - &generics.lifetimes, - &generics.ty_params, + generics.lifetimes.as_slice(), + generics.ty_params.as_slice(), ty::Generics::empty(), &generics.where_clause); @@ -1018,10 +1041,11 @@ fn ty_generics_for_trait(ccx: &CrateCtxt, index: 0, ident: special_idents::type_self, def_id: local_def(param_id), - bounds: Rc::new(ty::ParamBounds { + bounds: ty::ParamBounds { + opt_region_bound: None, builtin_bounds: ty::empty_builtin_bounds(), trait_bounds: vec!(self_trait_ref), - }), + }, default: None }; @@ -1039,8 +1063,8 @@ fn ty_generics_for_fn_or_method(ccx: &CrateCtxt, let early_lifetimes = resolve_lifetime::early_bound_lifetimes(generics); ty_generics(ccx, subst::FnSpace, - &early_lifetimes, - &generics.ty_params, + early_lifetimes.as_slice(), + generics.ty_params.as_slice(), base_generics, &generics.where_clause) } @@ -1053,7 +1077,7 @@ fn add_unsized_bound(ccx: &CrateCtxt, span: Span) { let kind_id = ccx.tcx.lang_items.require(SizedTraitLangItem); match unbound { - &Some(TraitTyParamBound(ref tpb)) => { + &Some(ast::TraitTyParamBound(ref tpb)) => { // #FIXME(8559) currently requires the unbound to be built-in. let trait_def_id = ty::trait_ref_to_def_id(ccx.tcx, tpb); match kind_id { @@ -1084,18 +1108,23 @@ fn add_unsized_bound(ccx: &CrateCtxt, fn ty_generics(ccx: &CrateCtxt, space: subst::ParamSpace, - lifetimes: &Vec<ast::LifetimeDef>, - types: &OwnedSlice<ast::TyParam>, + lifetime_defs: &[ast::LifetimeDef], + types: &[ast::TyParam], base_generics: ty::Generics, where_clause: &ast::WhereClause) - -> ty::Generics { + -> ty::Generics +{ let mut result = base_generics; - for (i, l) in lifetimes.iter().enumerate() { + for (i, l) in lifetime_defs.iter().enumerate() { + let bounds = l.bounds.iter() + .map(|l| ast_region_to_region(ccx.tcx, l)) + .collect(); let def = ty::RegionParameterDef { name: l.lifetime.name, space: space, index: i, - def_id: local_def(l.lifetime.id) }; + def_id: local_def(l.lifetime.id), + bounds: bounds }; debug!("ty_generics: def for region param: {}", def); result.regions.push(space, def); } @@ -1123,19 +1152,17 @@ fn ty_generics(ccx: &CrateCtxt, None => { } } - let param_ty = ty::ParamTy {space: space, - idx: index, - def_id: local_def(param.id)}; - let bounds = Rc::new(compute_bounds(ccx, - param_ty, - ¶m.bounds, - ¶m.unbound, - param.ident, - param.span, - where_clause)); + let param_ty = ty::ParamTy::new(space, index, local_def(param.id)); + let bounds = compute_bounds(ccx, + param.ident.name, + param_ty, + param.bounds.as_slice(), + ¶m.unbound, + param.span, + where_clause); let default = param.default.map(|path| { let ty = ast_ty_to_ty(ccx, &ExplicitRscope, &*path); - let cur_idx = param_ty.idx; + let cur_idx = index; ty::walk_ty(ty, |t| { match ty::get(t).sty { @@ -1164,130 +1191,139 @@ fn ty_generics(ccx: &CrateCtxt, def } +} - fn compute_bounds(ccx: &CrateCtxt, - param_ty: ty::ParamTy, - ast_bounds: &OwnedSlice<ast::TyParamBound>, - unbound: &Option<ast::TyParamBound>, - ident: ast::Ident, - span: Span, - where_clause: &ast::WhereClause) - -> ty::ParamBounds { - /*! - * Translate the AST's notion of ty param bounds (which are an - * enum consisting of a newtyped Ty or a region) to ty's - * notion of ty param bounds, which can either be user-defined - * traits, or the built-in trait (formerly known as kind): Send. - */ - - let mut param_bounds = ty::ParamBounds { - builtin_bounds: ty::empty_builtin_bounds(), - trait_bounds: Vec::new() - }; - for ast_bound in ast_bounds.iter() { - compute_bound(ccx, &mut param_bounds, param_ty, ast_bound); - } - for predicate in where_clause.predicates.iter() { - let predicate_param_id = ccx.tcx - .def_map - .borrow() - .find(&predicate.id) - .expect("compute_bounds(): resolve \ - didn't resolve the type \ - parameter identifier in a \ - `where` clause") - .def_id(); - if param_ty.def_id != predicate_param_id { - continue - } - for bound in predicate.bounds.iter() { - compute_bound(ccx, &mut param_bounds, param_ty, bound); - } - } - - add_unsized_bound(ccx, - unbound, - &mut param_bounds.builtin_bounds, - "type parameter", - span); - - check_bounds_compatible(ccx.tcx, ¶m_bounds, ident, span); +fn compute_bounds( + ccx: &CrateCtxt, + name_of_bounded_thing: ast::Name, + param_ty: ty::ParamTy, + ast_bounds: &[ast::TyParamBound], + unbound: &Option<ast::TyParamBound>, + span: Span, + where_clause: &ast::WhereClause) + -> ty::ParamBounds +{ + /*! + * Translate the AST's notion of ty param bounds (which are an + * enum consisting of a newtyped Ty or a region) to ty's + * notion of ty param bounds, which can either be user-defined + * traits, or the built-in trait (formerly known as kind): Send. + */ - param_bounds.trait_bounds.sort_by(|a,b| a.def_id.cmp(&b.def_id)); + let mut param_bounds = conv_param_bounds(ccx, + span, + param_ty, + ast_bounds, + where_clause); - param_bounds - } - /// Translates the AST's notion of a type parameter bound to - /// typechecking's notion of the same, and pushes the resulting bound onto - /// the appropriate section of `param_bounds`. - fn compute_bound(ccx: &CrateCtxt, - param_bounds: &mut ty::ParamBounds, - param_ty: ty::ParamTy, - ast_bound: &ast::TyParamBound) { - match *ast_bound { - TraitTyParamBound(ref b) => { - let ty = ty::mk_param(ccx.tcx, param_ty.space, - param_ty.idx, param_ty.def_id); - let trait_ref = instantiate_trait_ref(ccx, b, ty); - if !ty::try_add_builtin_trait( - ccx.tcx, trait_ref.def_id, - &mut param_bounds.builtin_bounds) { - // Must be a user-defined trait - param_bounds.trait_bounds.push(trait_ref); - } - } + add_unsized_bound(ccx, + unbound, + &mut param_bounds.builtin_bounds, + "type parameter", + span); - StaticRegionTyParamBound => { - param_bounds.builtin_bounds.add(ty::BoundStatic); - } + check_bounds_compatible(ccx.tcx, name_of_bounded_thing, + ¶m_bounds, span); - UnboxedFnTyParamBound(ref unboxed_function) => { - let rscope = ExplicitRscope; - let self_ty = ty::mk_param(ccx.tcx, - param_ty.space, - param_ty.idx, - param_ty.def_id); - let trait_ref = - astconv::trait_ref_for_unboxed_function(ccx, - &rscope, - unboxed_function, - Some(self_ty)); - param_bounds.trait_bounds.push(Rc::new(trait_ref)); - } + param_bounds.trait_bounds.sort_by(|a,b| a.def_id.cmp(&b.def_id)); - OtherRegionTyParamBound(span) => { - if !ccx.tcx.sess.features.issue_5723_bootstrap.get() { - ccx.tcx.sess.span_err( - span, - "only the 'static lifetime is accepted here."); - } - } - } - } + param_bounds +} - fn check_bounds_compatible(tcx: &ty::ctxt, - param_bounds: &ty::ParamBounds, - ident: ast::Ident, - span: Span) { - // Currently the only bound which is incompatible with other bounds is - // Sized/Unsized. - if !param_bounds.builtin_bounds.contains_elem(ty::BoundSized) { - ty::each_bound_trait_and_supertraits(tcx, - param_bounds.trait_bounds.as_slice(), - |trait_ref| { +fn check_bounds_compatible(tcx: &ty::ctxt, + name_of_bounded_thing: ast::Name, + param_bounds: &ty::ParamBounds, + span: Span) { + // Currently the only bound which is incompatible with other bounds is + // Sized/Unsized. + if !param_bounds.builtin_bounds.contains_elem(ty::BoundSized) { + ty::each_bound_trait_and_supertraits( + tcx, + param_bounds.trait_bounds.as_slice(), + |trait_ref| { let trait_def = ty::lookup_trait_def(tcx, trait_ref.def_id); - if trait_def.bounds.contains_elem(ty::BoundSized) { + if trait_def.bounds.builtin_bounds.contains_elem(ty::BoundSized) { span_err!(tcx.sess, span, E0129, - "incompatible bounds on type parameter {}, \ - bound {} does not allow unsized type", - token::get_ident(ident), + "incompatible bounds on type parameter `{}`, \ + bound `{}` does not allow unsized type", + name_of_bounded_thing.user_string(tcx), ppaux::trait_ref_to_string(tcx, &*trait_ref)); } true }); + } +} + +fn conv_param_bounds(ccx: &CrateCtxt, + span: Span, + param_ty: ty::ParamTy, + ast_bounds: &[ast::TyParamBound], + where_clause: &ast::WhereClause) + -> ty::ParamBounds +{ + let all_bounds = + merge_param_bounds(ccx, param_ty, ast_bounds, where_clause); + let astconv::PartitionedBounds { builtin_bounds, + trait_bounds, + region_bounds, + unboxed_fn_ty_bounds } = + astconv::partition_bounds(ccx.tcx, span, all_bounds.as_slice()); + let unboxed_fn_ty_bounds = + unboxed_fn_ty_bounds.move_iter() + .map(|b| instantiate_unboxed_fn_ty(ccx, b, param_ty)); + let trait_bounds: Vec<Rc<ty::TraitRef>> = + trait_bounds.move_iter() + .map(|b| instantiate_trait_ref(ccx, b, param_ty.to_ty(ccx.tcx))) + .chain(unboxed_fn_ty_bounds) + .collect(); + let opt_region_bound = + astconv::compute_opt_region_bound( + ccx.tcx, span, builtin_bounds, region_bounds.as_slice(), + trait_bounds.as_slice()); + ty::ParamBounds { + opt_region_bound: opt_region_bound, + builtin_bounds: builtin_bounds, + trait_bounds: trait_bounds, + } +} + +fn merge_param_bounds<'a>(ccx: &CrateCtxt, + param_ty: ty::ParamTy, + ast_bounds: &'a [ast::TyParamBound], + where_clause: &'a ast::WhereClause) + -> Vec<&'a ast::TyParamBound> +{ + /*! + * Merges the bounds declared on a type parameter with those + * found from where clauses into a single list. + */ + + let mut result = Vec::new(); + + for ast_bound in ast_bounds.iter() { + result.push(ast_bound); + } + + for predicate in where_clause.predicates.iter() { + let predicate_param_id = ccx.tcx + .def_map + .borrow() + .find(&predicate.id) + .expect("compute_bounds(): resolve \ + didn't resolve the type \ + parameter identifier in a \ + `where` clause") + .def_id(); + if param_ty.def_id != predicate_param_id { + continue + } + for bound in predicate.bounds.iter() { + result.push(bound); } } + + result } pub fn ty_of_foreign_fn_decl(ccx: &CrateCtxt, diff --git a/src/librustc/middle/typeck/infer/coercion.rs b/src/librustc/middle/typeck/infer/coercion.rs index 03890250f77..abf36638113 100644 --- a/src/librustc/middle/typeck/infer/coercion.rs +++ b/src/librustc/middle/typeck/infer/coercion.rs @@ -65,7 +65,7 @@ we may want to adjust precisely when coercions occur. */ use middle::subst; -use middle::ty::{AutoPtr, AutoBorrowVec, AutoBorrowObj, AutoDerefRef}; +use middle::ty::{AutoPtr, AutoDerefRef, AutoUnsize}; use middle::ty::{mt}; use middle::ty; use middle::typeck::infer::{CoerceResult, resolve_type, Coercion}; @@ -73,6 +73,7 @@ use middle::typeck::infer::combine::{CombineFields, Combine}; use middle::typeck::infer::sub::Sub; use middle::typeck::infer::resolve::try_resolve_tvar_shallow; use util::common::indenter; +use util::ppaux; use util::ppaux::Repr; use syntax::abi; @@ -94,38 +95,51 @@ impl<'f> Coerce<'f> { b.repr(self.get_ref().infcx.tcx)); let _indent = indenter(); + // Special case: if the subtype is a sized array literal (`[T, ..n]`), + // then it would get auto-borrowed to `&[T, ..n]` and then DST-ified + // to `&[T]`. Doing it all at once makes the target code a bit more + // efficient and spares us from having to handle multiple coercions. + match ty::get(b).sty { + ty::ty_rptr(_, mt_b) => { + match ty::get(mt_b.ty).sty { + ty::ty_vec(_, None) => { + let unsize_and_ref = self.unpack_actual_value(a, |sty_a| { + self.coerce_unsized_with_borrow(a, sty_a, b, mt_b.mutbl) + }); + if unsize_and_ref.is_ok() { + return unsize_and_ref; + } + } + _ => {} + } + } + _ => {} + } + + // Consider coercing the subtype to a DST + let unsize = self.unpack_actual_value(a, |sty_a| { + self.coerce_unsized(a, sty_a, b) + }); + if unsize.is_ok() { + return unsize; + } + // Examine the supertype and consider auto-borrowing. // // Note: does not attempt to resolve type variables we encounter. // See above for details. match ty::get(b).sty { - ty::ty_rptr(r_b, mt_b) => { + ty::ty_rptr(_, mt_b) => { match ty::get(mt_b.ty).sty { - ty::ty_vec(mt_b, None) => { - return self.unpack_actual_value(a, |sty_a| { - self.coerce_borrowed_vector(a, sty_a, b, mt_b.mutbl) - }); - } - ty::ty_vec(_, _) => {}, ty::ty_str => { return self.unpack_actual_value(a, |sty_a| { - self.coerce_borrowed_string(a, sty_a, b) + self.coerce_borrowed_pointer(a, sty_a, b, ast::MutImmutable) }); } - ty::ty_trait(box ty::TyTrait { def_id, ref substs, bounds }) => { + ty::ty_trait(..) => { let result = self.unpack_actual_value(a, |sty_a| { - match *sty_a { - ty::ty_rptr(_, mt_a) => match ty::get(mt_a.ty).sty { - ty::ty_trait(..) => { - self.coerce_borrowed_object(a, sty_a, b, mt_b.mutbl) - } - _ => self.coerce_object(a, sty_a, b, def_id, substs, - ty::RegionTraitStore(r_b, mt_b.mutbl), - bounds) - }, - _ => self.coerce_borrowed_object(a, sty_a, b, mt_b.mutbl) - } + self.coerce_borrowed_object(a, sty_a, b, mt_b.mutbl) }); match result { @@ -136,37 +150,12 @@ impl<'f> Coerce<'f> { _ => { return self.unpack_actual_value(a, |sty_a| { - self.coerce_borrowed_pointer(a, sty_a, b, mt_b) + self.coerce_borrowed_pointer(a, sty_a, b, mt_b.mutbl) }); } }; } - ty::ty_uniq(t_b) => { - match ty::get(t_b).sty { - ty::ty_trait(box ty::TyTrait { def_id, ref substs, bounds }) => { - let result = self.unpack_actual_value(a, |sty_a| { - match *sty_a { - ty::ty_uniq(t_a) => match ty::get(t_a).sty { - ty::ty_trait(..) => { - Err(ty::terr_mismatch) - } - _ => self.coerce_object(a, sty_a, b, def_id, substs, - ty::UniqTraitStore, bounds) - }, - _ => Err(ty::terr_mismatch) - } - }); - - match result { - Ok(t) => return Ok(t), - Err(..) => {} - } - } - _ => {} - } - } - ty::ty_closure(box ty::ClosureTy { store: ty::RegionTraitStore(..), .. @@ -210,8 +199,8 @@ impl<'f> Coerce<'f> { } } - pub fn unpack_actual_value(&self, a: ty::t, f: |&ty::sty| -> CoerceResult) - -> CoerceResult { + pub fn unpack_actual_value<T>(&self, a: ty::t, f: |&ty::sty| -> T) + -> T { match resolve_type(self.get_ref().infcx, None, a, try_resolve_tvar_shallow) { Ok(t) => { @@ -221,20 +210,21 @@ impl<'f> Coerce<'f> { self.get_ref().infcx.tcx.sess.span_bug( self.get_ref().trace.origin.span(), format!("failed to resolve even without \ - any force options: {:?}", e).as_slice()); + any force options: {:?}", e).as_slice()); } } } + // ~T -> &T or &mut T -> &T (including where T = [U] or str) pub fn coerce_borrowed_pointer(&self, a: ty::t, sty_a: &ty::sty, b: ty::t, - mt_b: ty::mt) + mutbl_b: ast::Mutability) -> CoerceResult { - debug!("coerce_borrowed_pointer(a={}, sty_a={:?}, b={}, mt_b={:?})", + debug!("coerce_borrowed_pointer(a={}, sty_a={:?}, b={})", a.repr(self.get_ref().infcx.tcx), sty_a, - b.repr(self.get_ref().infcx.tcx), mt_b); + b.repr(self.get_ref().infcx.tcx)); // If we have a parameter of type `&M T_a` and the value // provided is `expr`, we will be adding an implicit borrow, @@ -256,64 +246,182 @@ impl<'f> Coerce<'f> { let a_borrowed = ty::mk_rptr(self.get_ref().infcx.tcx, r_borrow, - mt {ty: inner_ty, mutbl: mt_b.mutbl}); + mt {ty: inner_ty, mutbl: mutbl_b}); if_ok!(sub.tys(a_borrowed, b)); + Ok(Some(AutoDerefRef(AutoDerefRef { autoderefs: 1, - autoref: Some(AutoPtr(r_borrow, mt_b.mutbl)) + autoref: Some(AutoPtr(r_borrow, mutbl_b, None)) }))) } - pub fn coerce_borrowed_string(&self, + // [T, ..n] -> &[T] or &mut [T] + fn coerce_unsized_with_borrow(&self, a: ty::t, sty_a: &ty::sty, - b: ty::t) + b: ty::t, + mutbl_b: ast::Mutability) -> CoerceResult { - debug!("coerce_borrowed_string(a={}, sty_a={:?}, b={})", + debug!("coerce_unsized_with_borrow(a={}, sty_a={:?}, b={})", a.repr(self.get_ref().infcx.tcx), sty_a, b.repr(self.get_ref().infcx.tcx)); match *sty_a { - ty::ty_uniq(_) => return Err(ty::terr_mismatch), - _ => return self.subtype(a, b), + ty::ty_vec(t_a, Some(len)) => { + let sub = Sub(self.get_ref().clone()); + let coercion = Coercion(self.get_ref().trace.clone()); + let r_borrow = self.get_ref().infcx.next_region_var(coercion); + let unsized_ty = ty::mk_slice(self.get_ref().infcx.tcx, r_borrow, + mt {ty: t_a, mutbl: mutbl_b}); + if_ok!(self.get_ref().infcx.try(|| sub.tys(unsized_ty, b))); + Ok(Some(AutoDerefRef(AutoDerefRef { + autoderefs: 0, + autoref: Some(ty::AutoPtr(r_borrow, + mutbl_b, + Some(box AutoUnsize(ty::UnsizeLength(len))))) + }))) + } + _ => Err(ty::terr_mismatch) } } - pub fn coerce_borrowed_vector(&self, - a: ty::t, - sty_a: &ty::sty, - b: ty::t, - mutbl_b: ast::Mutability) - -> CoerceResult { - debug!("coerce_borrowed_vector(a={}, sty_a={:?}, b={})", + // &[T, ..n] or &mut [T, ..n] -> &[T] + // or &mut [T, ..n] -> &mut [T] + // or &Concrete -> &Trait, etc. + fn coerce_unsized(&self, + a: ty::t, + sty_a: &ty::sty, + b: ty::t) + -> CoerceResult { + debug!("coerce_unsized(a={}, sty_a={:?}, b={})", a.repr(self.get_ref().infcx.tcx), sty_a, b.repr(self.get_ref().infcx.tcx)); + // Note, we want to avoid unnecessary unsizing. We don't want to coerce to + // a DST unless we have to. This currently comes out in the wash since + // we can't unify [T] with U. But to properly support DST, we need to allow + // that, at which point we will need extra checks on b here. + let sub = Sub(self.get_ref().clone()); - let coercion = Coercion(self.get_ref().trace.clone()); - let r_borrow = self.get_ref().infcx.next_region_var(coercion); - let ty_inner = match *sty_a { - ty::ty_uniq(_) => return Err(ty::terr_mismatch), - ty::ty_ptr(ty::mt{ty: t, ..}) | - ty::ty_rptr(_, ty::mt{ty: t, ..}) => match ty::get(t).sty { - ty::ty_vec(mt, None) => mt.ty, - _ => { - return self.subtype(a, b); - } - }, - ty::ty_vec(mt, _) => mt.ty, - _ => { - return self.subtype(a, b); + + let sty_b = &ty::get(b).sty; + match (sty_a, sty_b) { + (&ty::ty_uniq(_), &ty::ty_rptr(..)) => Err(ty::terr_mismatch), + (&ty::ty_rptr(_, ty::mt{ty: t_a, ..}), &ty::ty_rptr(_, mt_b)) => { + self.unpack_actual_value(t_a, |sty_a| { + match self.unsize_ty(sty_a, mt_b.ty) { + Some((ty, kind)) => { + let coercion = Coercion(self.get_ref().trace.clone()); + let r_borrow = self.get_ref().infcx.next_region_var(coercion); + let ty = ty::mk_rptr(self.get_ref().infcx.tcx, + r_borrow, + ty::mt{ty: ty, mutbl: mt_b.mutbl}); + if_ok!(self.get_ref().infcx.try(|| sub.tys(ty, b))); + debug!("Success, coerced with AutoDerefRef(1, \ + AutoPtr(AutoUnsize({:?})))", kind); + Ok(Some(AutoDerefRef(AutoDerefRef { + autoderefs: 1, + autoref: Some(ty::AutoPtr(r_borrow, mt_b.mutbl, + Some(box AutoUnsize(kind)))) + }))) + } + _ => Err(ty::terr_mismatch) + } + }) } - }; + (&ty::ty_uniq(t_a), &ty::ty_uniq(t_b)) => { + self.unpack_actual_value(t_a, |sty_a| { + match self.unsize_ty(sty_a, t_b) { + Some((ty, kind)) => { + let ty = ty::mk_uniq(self.get_ref().infcx.tcx, ty); + if_ok!(self.get_ref().infcx.try(|| sub.tys(ty, b))); + debug!("Success, coerced with AutoDerefRef(1, \ + AutoUnsizeUniq({:?}))", kind); + Ok(Some(AutoDerefRef(AutoDerefRef { + autoderefs: 1, + autoref: Some(ty::AutoUnsizeUniq(kind)) + }))) + } + _ => Err(ty::terr_mismatch) + } + }) + } + _ => Err(ty::terr_mismatch) + } + } - let a_borrowed = ty::mk_slice(self.get_ref().infcx.tcx, r_borrow, - mt {ty: ty_inner, mutbl: mutbl_b}); - if_ok!(sub.tys(a_borrowed, b)); - Ok(Some(AutoDerefRef(AutoDerefRef { - autoderefs: 0, - autoref: Some(AutoBorrowVec(r_borrow, mutbl_b)) - }))) + // Takes a type and returns an unsized version along with the adjustment + // performed to unsize it. + // E.g., `[T, ..n]` -> `([T], UnsizeLength(n))` + fn unsize_ty(&self, + sty_a: &ty::sty, + ty_b: ty::t) + -> Option<(ty::t, ty::UnsizeKind)> { + debug!("unsize_ty(sty_a={:?}", sty_a); + + let tcx = self.get_ref().infcx.tcx; + + self.unpack_actual_value(ty_b, |sty_b| + match (sty_a, sty_b) { + (&ty::ty_vec(t_a, Some(len)), _) => { + let ty = ty::mk_vec(tcx, t_a, None); + Some((ty, ty::UnsizeLength(len))) + } + (&ty::ty_trait(..), &ty::ty_trait(..)) => None, + (_, &ty::ty_trait(box ty::TyTrait { def_id, ref substs, bounds })) => { + let ty = ty::mk_trait(tcx, + def_id, + substs.clone(), + bounds); + Some((ty, ty::UnsizeVtable(bounds, + def_id, + substs.clone()))) + } + (&ty::ty_struct(did_a, ref substs_a), &ty::ty_struct(did_b, ref substs_b)) + if did_a == did_b => { + debug!("unsizing a struct"); + // Try unsizing each type param in turn to see if we end up with ty_b. + let ty_substs_a = substs_a.types.get_slice(subst::TypeSpace); + let ty_substs_b = substs_b.types.get_slice(subst::TypeSpace); + assert!(ty_substs_a.len() == ty_substs_b.len()); + + let sub = Sub(self.get_ref().clone()); + + let mut result = None; + let mut tps = ty_substs_a.iter().zip(ty_substs_b.iter()).enumerate(); + for (i, (tp_a, tp_b)) in tps { + if self.get_ref().infcx.try(|| sub.tys(*tp_a, *tp_b)).is_ok() { + continue; + } + match self.unpack_actual_value(*tp_a, |tp| self.unsize_ty(tp, *tp_b)) { + Some((new_tp, k)) => { + // Check that the whole types match. + let mut new_substs = substs_a.clone(); + new_substs.types.get_mut_slice(subst::TypeSpace)[i] = new_tp; + let ty = ty::mk_struct(tcx, did_a, new_substs); + if self.get_ref().infcx.try(|| sub.tys(ty, ty_b)).is_err() { + debug!("Unsized type parameter '{}', but still \ + could not match types {} and {}", + ppaux::ty_to_string(tcx, *tp_a), + ppaux::ty_to_string(tcx, ty), + ppaux::ty_to_string(tcx, ty_b)); + // We can only unsize a single type parameter, so + // if we unsize one and it doesn't give us the + // type we want, then we won't succeed later. + break; + } + + result = Some((ty, ty::UnsizeStruct(box k, i))); + break; + } + None => {} + } + } + result + } + _ => None + } + ) } fn coerce_borrowed_object(&self, @@ -352,8 +460,8 @@ impl<'f> Coerce<'f> { if_ok!(self.subtype(a_borrowed, b)); Ok(Some(AutoDerefRef(AutoDerefRef { - autoderefs: 0, - autoref: Some(AutoBorrowObj(r_a, b_mutbl)) + autoderefs: 1, + autoref: Some(AutoPtr(r_a, b_mutbl, None)) }))) } @@ -438,21 +546,4 @@ impl<'f> Coerce<'f> { autoref: Some(ty::AutoUnsafe(mt_b.mutbl)) }))) } - - pub fn coerce_object(&self, - a: ty::t, - sty_a: &ty::sty, - b: ty::t, - trait_def_id: ast::DefId, - trait_substs: &subst::Substs, - trait_store: ty::TraitStore, - bounds: ty::BuiltinBounds) -> CoerceResult { - - debug!("coerce_object(a={}, sty_a={:?}, b={})", - a.repr(self.get_ref().infcx.tcx), sty_a, - b.repr(self.get_ref().infcx.tcx)); - - Ok(Some(ty::AutoObject(trait_store, bounds, - trait_def_id, trait_substs.clone()))) - } } diff --git a/src/librustc/middle/typeck/infer/combine.rs b/src/librustc/middle/typeck/infer/combine.rs index d99d55d4d87..47085877ad7 100644 --- a/src/librustc/middle/typeck/infer/combine.rs +++ b/src/librustc/middle/typeck/infer/combine.rs @@ -221,7 +221,7 @@ pub trait Combine { }; let fn_style = if_ok!(self.fn_styles(a.fn_style, b.fn_style)); let onceness = if_ok!(self.oncenesses(a.onceness, b.onceness)); - let bounds = if_ok!(self.bounds(a.bounds, b.bounds)); + let bounds = if_ok!(self.existential_bounds(a.bounds, b.bounds)); let sig = if_ok!(self.fn_sigs(&a.sig, &b.sig)); let abi = if_ok!(self.abi(a.abi, b.abi)); Ok(ty::ClosureTy { @@ -251,9 +251,26 @@ pub trait Combine { } fn oncenesses(&self, a: Onceness, b: Onceness) -> cres<Onceness>; - fn bounds(&self, a: BuiltinBounds, b: BuiltinBounds) -> cres<BuiltinBounds>; + + fn existential_bounds(&self, + a: ty::ExistentialBounds, + b: ty::ExistentialBounds) + -> cres<ty::ExistentialBounds> + { + let r = try!(self.contraregions(a.region_bound, b.region_bound)); + let nb = try!(self.builtin_bounds(a.builtin_bounds, b.builtin_bounds)); + Ok(ty::ExistentialBounds { region_bound: r, + builtin_bounds: nb }) + } + + fn builtin_bounds(&self, + a: ty::BuiltinBounds, + b: ty::BuiltinBounds) + -> cres<ty::BuiltinBounds>; + fn contraregions(&self, a: ty::Region, b: ty::Region) -> cres<ty::Region>; + fn regions(&self, a: ty::Region, b: ty::Region) -> cres<ty::Region>; fn trait_stores(&self, @@ -479,7 +496,7 @@ pub fn super_tys<C:Combine>(this: &C, a: ty::t, b: ty::t) -> cres<ty::t> { if a_.def_id == b_.def_id => { debug!("Trying to match traits {:?} and {:?}", a, b); let substs = if_ok!(this.substs(a_.def_id, &a_.substs, &b_.substs)); - let bounds = if_ok!(this.bounds(a_.bounds, b_.bounds)); + let bounds = if_ok!(this.existential_bounds(a_.bounds, b_.bounds)); Ok(ty::mk_trait(tcx, a_.def_id, substs.clone(), @@ -529,10 +546,10 @@ pub fn super_tys<C:Combine>(this: &C, a: ty::t, b: ty::t) -> cres<ty::t> { check_ptr_to_unsized(this, a, b, a_mt.ty, b_mt.ty, ty::mk_rptr(tcx, r, mt)) } - (&ty::ty_vec(ref a_mt, sz_a), &ty::ty_vec(ref b_mt, sz_b)) => { - this.mts(a_mt, b_mt).and_then(|mt| { + (&ty::ty_vec(a_t, sz_a), &ty::ty_vec(b_t, sz_b)) => { + this.tys(a_t, b_t).and_then(|t| { if sz_a == sz_b { - Ok(ty::mk_vec(tcx, mt, sz_a)) + Ok(ty::mk_vec(tcx, t, sz_a)) } else { Err(ty::terr_sorts(expected_found(this, a, b))) } diff --git a/src/librustc/middle/typeck/infer/error_reporting.rs b/src/librustc/middle/typeck/infer/error_reporting.rs index beaf81409a3..2883a960df9 100644 --- a/src/librustc/middle/typeck/infer/error_reporting.rs +++ b/src/librustc/middle/typeck/infer/error_reporting.rs @@ -75,6 +75,7 @@ use middle::typeck::infer::region_inference::RegionResolutionError; use middle::typeck::infer::region_inference::ConcreteFailure; use middle::typeck::infer::region_inference::SubSupConflict; use middle::typeck::infer::region_inference::SupSupConflict; +use middle::typeck::infer::region_inference::ParamBoundFailure; use middle::typeck::infer::region_inference::ProcessedErrors; use middle::typeck::infer::region_inference::SameRegions; use std::cell::{Cell, RefCell}; @@ -89,10 +90,13 @@ use syntax::owned_slice::OwnedSlice; use syntax::codemap; use syntax::parse::token; use syntax::print::pprust; -use util::ppaux::UserString; use util::ppaux::bound_region_to_string; use util::ppaux::note_and_explain_region; +// Note: only import UserString, not Repr, since user-facing error +// messages shouldn't include debug serializations. +use util::ppaux::UserString; + pub trait ErrorReporting { fn report_region_errors(&self, errors: &Vec<RegionResolutionError>); @@ -118,6 +122,12 @@ pub trait ErrorReporting { sub: Region, sup: Region); + fn report_param_bound_failure(&self, + origin: SubregionOrigin, + param_ty: ty::ParamTy, + sub: Region, + sups: Vec<Region>); + fn report_sub_sup_conflict(&self, var_origin: RegionVariableOrigin, sub_origin: SubregionOrigin, @@ -145,7 +155,7 @@ trait ErrorReportingHelpers { var_origin: RegionVariableOrigin); fn note_region_origin(&self, - origin: SubregionOrigin); + origin: &SubregionOrigin); fn give_expl_lifetime_param(&self, decl: &ast::FnDecl, @@ -167,6 +177,10 @@ impl<'a> ErrorReporting for InferCtxt<'a> { self.report_concrete_failure(origin, sub, sup); } + ParamBoundFailure(origin, param_ty, sub, sups) => { + self.report_param_bound_failure(origin, param_ty, sub, sups); + } + SubSupConflict(var_origin, sub_origin, sub_r, sup_origin, sup_r) => { @@ -377,7 +391,7 @@ impl<'a> ErrorReporting for InferCtxt<'a> { fn values_str(&self, values: &ValuePairs) -> Option<String> { /*! - * Returns a string of the form "expected `{}` but found `{}`", + * Returns a string of the form "expected `{}`, found `{}`", * or None if this is a derived error. */ match *values { @@ -405,11 +419,67 @@ impl<'a> ErrorReporting for InferCtxt<'a> { return None; } - Some(format!("expected `{}` but found `{}`", + Some(format!("expected `{}`, found `{}`", expected.user_string(self.tcx), found.user_string(self.tcx))) } + fn report_param_bound_failure(&self, + origin: SubregionOrigin, + param_ty: ty::ParamTy, + sub: Region, + _sups: Vec<Region>) { + + // FIXME: it would be better to report the first error message + // with the span of the parameter itself, rather than the span + // where the error was detected. But that span is not readily + // accessible. + + match sub { + ty::ReFree(ty::FreeRegion {bound_region: ty::BrNamed(..), ..}) => { + // Does the required lifetime have a nice name we can print? + self.tcx.sess.span_err( + origin.span(), + format!( + "the parameter type `{}` may not live long enough; \ + consider adding an explicit lifetime bound `{}:{}`...", + param_ty.user_string(self.tcx), + param_ty.user_string(self.tcx), + sub.user_string(self.tcx)).as_slice()); + } + + ty::ReStatic => { + // Does the required lifetime have a nice name we can print? + self.tcx.sess.span_err( + origin.span(), + format!( + "the parameter type `{}` may not live long enough; \ + consider adding an explicit lifetime bound `{}:'static`...", + param_ty.user_string(self.tcx), + param_ty.user_string(self.tcx)).as_slice()); + } + + _ => { + // If not, be less specific. + self.tcx.sess.span_err( + origin.span(), + format!( + "the parameter type `{}` may not live long enough; \ + consider adding an explicit lifetime bound to `{}`", + param_ty.user_string(self.tcx), + param_ty.user_string(self.tcx)).as_slice()); + note_and_explain_region( + self.tcx, + format!("the parameter type `{}` must be valid for ", + param_ty.user_string(self.tcx)).as_slice(), + sub, + "..."); + } + } + + self.note_region_origin(&origin); + } + fn report_concrete_failure(&self, origin: SubregionOrigin, sub: Region, @@ -538,6 +608,67 @@ impl<'a> ErrorReporting for InferCtxt<'a> { sup, ""); } + infer::RelateProcBound(span, var_node_id, ty) => { + self.tcx.sess.span_err( + span, + format!( + "the type `{}` of captured variable `{}` \ + outlives the `proc()` it \ + is captured in", + self.ty_to_string(ty), + ty::local_var_name_str(self.tcx, + var_node_id)).as_slice()); + note_and_explain_region( + self.tcx, + "`proc()` is valid for ", + sub, + ""); + note_and_explain_region( + self.tcx, + format!("the type `{}` is only valid for ", + self.ty_to_string(ty)).as_slice(), + sup, + ""); + } + infer::RelateParamBound(span, param_ty, ty) => { + self.tcx.sess.span_err( + span, + format!("the type `{}` (provided as the value of \ + the parameter `{}`) does not fulfill the \ + required lifetime", + self.ty_to_string(ty), + param_ty.user_string(self.tcx)).as_slice()); + note_and_explain_region(self.tcx, + "type must outlive ", + sub, + ""); + } + infer::RelateRegionParamBound(span) => { + self.tcx.sess.span_err( + span, + "declared lifetime bound not satisfied"); + note_and_explain_region( + self.tcx, + "lifetime parameter instantiated with ", + sup, + ""); + note_and_explain_region( + self.tcx, + "but lifetime parameter must outlive ", + sub, + ""); + } + infer::RelateDefaultParamBound(span, ty) => { + self.tcx.sess.span_err( + span, + format!("the type `{}` (provided as the value of \ + a type parameter) is not valid at this point", + self.ty_to_string(ty)).as_slice()); + note_and_explain_region(self.tcx, + "type must outlive ", + sub, + ""); + } infer::CallRcvr(span) => { self.tcx.sess.span_err( span, @@ -593,6 +724,18 @@ impl<'a> ErrorReporting for InferCtxt<'a> { sup, ""); } + infer::ExprTypeIsNotInScope(t, span) => { + self.tcx.sess.span_err( + span, + format!("type of expression contains references \ + that are not valid during the expression: `{}`", + self.ty_to_string(t)).as_slice()); + note_and_explain_region( + self.tcx, + "type is only valid for ", + sup, + ""); + } infer::BindingTypeIsNotValidAtDecl(span) => { self.tcx.sess.span_err( span, @@ -606,9 +749,9 @@ impl<'a> ErrorReporting for InferCtxt<'a> { infer::ReferenceOutlivesReferent(ty, span) => { self.tcx.sess.span_err( span, - format!("in type `{}`, pointer has a longer lifetime than \ - the data it references", - ty.user_string(self.tcx)).as_slice()); + format!("in type `{}`, reference has a longer lifetime \ + than the data it references", + self.ty_to_string(ty)).as_slice()); note_and_explain_region( self.tcx, "the pointer is valid for ", @@ -620,6 +763,11 @@ impl<'a> ErrorReporting for InferCtxt<'a> { sup, ""); } + infer::Managed(span) => { + self.tcx.sess.span_err( + span, + format!("cannot put borrowed references into managed memory").as_slice()); + } } } @@ -637,7 +785,7 @@ impl<'a> ErrorReporting for InferCtxt<'a> { sup_region, "..."); - self.note_region_origin(sup_origin); + self.note_region_origin(&sup_origin); note_and_explain_region( self.tcx, @@ -645,7 +793,7 @@ impl<'a> ErrorReporting for InferCtxt<'a> { sub_region, "..."); - self.note_region_origin(sub_origin); + self.note_region_origin(&sub_origin); } fn report_sup_sup_conflict(&self, @@ -662,7 +810,7 @@ impl<'a> ErrorReporting for InferCtxt<'a> { region1, "..."); - self.note_region_origin(origin1); + self.note_region_origin(&origin1); note_and_explain_region( self.tcx, @@ -670,7 +818,7 @@ impl<'a> ErrorReporting for InferCtxt<'a> { region2, "..."); - self.note_region_origin(origin2); + self.note_region_origin(&origin2); } fn report_processed_errors(&self, @@ -920,8 +1068,12 @@ impl<'a> Rebuilder<'a> { -> OwnedSlice<ast::TyParamBound> { ty_param_bounds.map(|tpb| { match tpb { - &ast::StaticRegionTyParamBound => ast::StaticRegionTyParamBound, - &ast::OtherRegionTyParamBound(s) => ast::OtherRegionTyParamBound(s), + &ast::RegionTyParamBound(lt) => { + // FIXME -- it's unclear whether I'm supposed to + // substitute lifetime here. I suspect we need to + // be passing down a map. + ast::RegionTyParamBound(lt) + } &ast::UnboxedFnTyParamBound(unboxed_function_type) => { ast::UnboxedFnTyParamBound(unboxed_function_type) } @@ -1291,8 +1443,8 @@ impl<'a> ErrorReportingHelpers for InferCtxt<'a> { var_description).as_slice()); } - fn note_region_origin(&self, origin: SubregionOrigin) { - match origin { + fn note_region_origin(&self, origin: &SubregionOrigin) { + match *origin { infer::Subtype(ref trace) => { let desc = match trace.origin { infer::Misc(_) => { @@ -1384,8 +1536,16 @@ impl<'a> ErrorReportingHelpers for InferCtxt<'a> { infer::RelateObjectBound(span) => { self.tcx.sess.span_note( span, - "...so that source pointer does not outlive \ - lifetime bound of the object type"); + "...so that it can be closed over into an object"); + } + infer::RelateProcBound(span, var_node_id, _ty) => { + self.tcx.sess.span_err( + span, + format!( + "...so that the variable `{}` can be captured \ + into a proc", + ty::local_var_name_str(self.tcx, + var_node_id)).as_slice()); } infer::CallRcvr(span) => { self.tcx.sess.span_note( @@ -1411,19 +1571,55 @@ impl<'a> ErrorReportingHelpers for InferCtxt<'a> { infer::AutoBorrow(span) => { self.tcx.sess.span_note( span, - "...so that automatically reference is valid \ - at the time of borrow"); + "...so that reference is valid \ + at the time of implicit borrow"); + } + infer::ExprTypeIsNotInScope(t, span) => { + self.tcx.sess.span_note( + span, + format!("...so type `{}` of expression is valid during the \ + expression", + self.ty_to_string(t)).as_slice()); } infer::BindingTypeIsNotValidAtDecl(span) => { self.tcx.sess.span_note( span, "...so that variable is valid at time of its declaration"); } - infer::ReferenceOutlivesReferent(_, span) => { + infer::ReferenceOutlivesReferent(ty, span) => { + self.tcx.sess.span_note( + span, + format!("...so that the reference type `{}` \ + does not outlive the data it points at", + self.ty_to_string(ty)).as_slice()); + } + infer::Managed(span) => { + self.tcx.sess.span_note( + span, + "...so that the value can be stored in managed memory."); + } + infer::RelateParamBound(span, param_ty, t) => { + self.tcx.sess.span_note( + span, + format!("...so that the parameter `{}`, \ + when instantiated with `{}`, \ + will meet its declared lifetime bounds.", + param_ty.user_string(self.tcx), + self.ty_to_string(t)).as_slice()); + } + infer::RelateDefaultParamBound(span, t) => { + self.tcx.sess.span_note( + span, + format!("...so that type parameter \ + instantiated with `{}`, \ + will meet its declared lifetime bounds.", + self.ty_to_string(t)).as_slice()); + } + infer::RelateRegionParamBound(span) => { self.tcx.sess.span_note( span, - "...so that the pointer does not outlive the \ - data it points at"); + format!("...so that the declared lifetime parameter bounds \ + are satisfied").as_slice()); } } } diff --git a/src/librustc/middle/typeck/infer/glb.rs b/src/librustc/middle/typeck/infer/glb.rs index b6628c22ae6..00eaa4d235b 100644 --- a/src/librustc/middle/typeck/infer/glb.rs +++ b/src/librustc/middle/typeck/infer/glb.rs @@ -96,7 +96,10 @@ impl<'f> Combine for Glb<'f> { } } - fn bounds(&self, a: BuiltinBounds, b: BuiltinBounds) -> cres<BuiltinBounds> { + fn builtin_bounds(&self, + a: ty::BuiltinBounds, + b: ty::BuiltinBounds) + -> cres<ty::BuiltinBounds> { // More bounds is a subtype of fewer bounds, so // the GLB (mutual subtype) is the union. Ok(a.union(b)) diff --git a/src/librustc/middle/typeck/infer/lub.rs b/src/librustc/middle/typeck/infer/lub.rs index 6a50038afe7..8707efc622b 100644 --- a/src/librustc/middle/typeck/infer/lub.rs +++ b/src/librustc/middle/typeck/infer/lub.rs @@ -90,7 +90,10 @@ impl<'f> Combine for Lub<'f> { } } - fn bounds(&self, a: BuiltinBounds, b: BuiltinBounds) -> cres<BuiltinBounds> { + fn builtin_bounds(&self, + a: ty::BuiltinBounds, + b: ty::BuiltinBounds) + -> cres<ty::BuiltinBounds> { // More bounds is a subtype of fewer bounds, so // the LUB (mutual supertype) is the intersection. Ok(a.intersection(b)) diff --git a/src/librustc/middle/typeck/infer/mod.rs b/src/librustc/middle/typeck/infer/mod.rs index 30fffc42a3f..ed96effdd83 100644 --- a/src/librustc/middle/typeck/infer/mod.rs +++ b/src/librustc/middle/typeck/infer/mod.rs @@ -168,6 +168,22 @@ pub enum SubregionOrigin { // relating `'a` to `'b` RelateObjectBound(Span), + // When closing over a variable in a closure/proc, ensure that the + // type of the variable outlives the lifetime bound. + RelateProcBound(Span, ast::NodeId, ty::t), + + // The given type parameter was instantiated with the given type, + // and that type must outlive some region. + RelateParamBound(Span, ty::ParamTy, ty::t), + + // The given region parameter was instantiated with a region + // that must outlive some other region. + RelateRegionParamBound(Span), + + // A bound placed on type parameters that states that must outlive + // the moment of their instantiation. + RelateDefaultParamBound(Span, ty::t), + // Creating a pointer `b` to contents of another reference Reborrow(Span), @@ -177,6 +193,9 @@ pub enum SubregionOrigin { // (&'a &'b T) where a >= b ReferenceOutlivesReferent(ty::t, Span), + // The type T of an expression E must outlive the lifetime for E. + ExprTypeIsNotInScope(ty::t, Span), + // A `ref b` whose region does not enclose the decl site BindingTypeIsNotValidAtDecl(Span), @@ -194,6 +213,9 @@ pub enum SubregionOrigin { // An auto-borrow that does not enclose the expr where it occurs AutoBorrow(Span), + + // Managed data cannot contain borrowed pointers. + Managed(Span), } /// Reasons to create a region inference variable @@ -336,7 +358,6 @@ pub fn can_mk_subty(cx: &InferCtxt, a: ty::t, b: ty::t) -> ures { } pub fn mk_subr(cx: &InferCtxt, - _a_is_expected: bool, origin: SubregionOrigin, a: ty::Region, b: ty::Region) { @@ -346,6 +367,18 @@ pub fn mk_subr(cx: &InferCtxt, cx.region_vars.commit(snapshot); } +pub fn verify_param_bound(cx: &InferCtxt, + origin: SubregionOrigin, + param_ty: ty::ParamTy, + a: ty::Region, + bs: Vec<ty::Region>) { + debug!("verify_param_bound({}, {} <: {})", + param_ty.repr(cx.tcx), + a.repr(cx.tcx), + bs.repr(cx.tcx)); + + cx.region_vars.verify_param_bound(origin, param_ty, a, bs); +} pub fn mk_eqty(cx: &InferCtxt, a_is_expected: bool, origin: TypeOrigin, @@ -589,6 +622,13 @@ impl<'a> InferCtxt<'a> { self.rollback_to(snapshot); r } + + pub fn add_given(&self, + sub: ty::FreeRegion, + sup: ty::RegionVid) + { + self.region_vars.add_given(sub, sup); + } } impl<'a> InferCtxt<'a> { @@ -687,14 +727,13 @@ impl<'a> InferCtxt<'a> { } pub fn resolve_type_vars_in_trait_ref_if_possible(&self, - trait_ref: - &ty::TraitRef) + trait_ref: &ty::TraitRef) -> ty::TraitRef { // make up a dummy type just to reuse/abuse the resolve machinery let dummy0 = ty::mk_trait(self.tcx, trait_ref.def_id, trait_ref.substs.clone(), - ty::empty_builtin_bounds()); + ty::region_existential_bound(ty::ReStatic)); let dummy1 = self.resolve_type_vars_if_possible(dummy0); match ty::get(dummy1).sty { ty::ty_trait(box ty::TyTrait { ref def_id, ref substs, .. }) => { @@ -799,7 +838,7 @@ impl<'a> InferCtxt<'a> { _ => { // if I leave out : String, it infers &str and complains |actual: String| { - format!("mismatched types: expected `{}` but found `{}`", + format!("mismatched types: expected `{}`, found `{}`", self.ty_to_string(resolved_expected), actual) } @@ -896,15 +935,21 @@ impl SubregionOrigin { FreeVariable(a, _) => a, IndexSlice(a) => a, RelateObjectBound(a) => a, + RelateProcBound(a, _, _) => a, + RelateParamBound(a, _, _) => a, + RelateRegionParamBound(a) => a, + RelateDefaultParamBound(a, _) => a, Reborrow(a) => a, ReborrowUpvar(a, _) => a, ReferenceOutlivesReferent(_, a) => a, + ExprTypeIsNotInScope(_, a) => a, BindingTypeIsNotValidAtDecl(a) => a, CallRcvr(a) => a, CallArg(a) => a, CallReturn(a) => a, AddrOf(a) => a, AutoBorrow(a) => a, + Managed(a) => a, } } } @@ -933,6 +978,27 @@ impl Repr for SubregionOrigin { RelateObjectBound(a) => { format!("RelateObjectBound({})", a.repr(tcx)) } + RelateProcBound(a, b, c) => { + format!("RelateProcBound({},{},{})", + a.repr(tcx), + b, + c.repr(tcx)) + } + RelateParamBound(a, b, c) => { + format!("RelateParamBound({},{},{})", + a.repr(tcx), + b.repr(tcx), + c.repr(tcx)) + } + RelateRegionParamBound(a) => { + format!("RelateRegionParamBound({})", + a.repr(tcx)) + } + RelateDefaultParamBound(a, b) => { + format!("RelateDefaultParamBound({},{})", + a.repr(tcx), + b.repr(tcx)) + } Reborrow(a) => format!("Reborrow({})", a.repr(tcx)), ReborrowUpvar(a, b) => { format!("ReborrowUpvar({},{:?})", a.repr(tcx), b) @@ -940,6 +1006,11 @@ impl Repr for SubregionOrigin { ReferenceOutlivesReferent(_, a) => { format!("ReferenceOutlivesReferent({})", a.repr(tcx)) } + ExprTypeIsNotInScope(a, b) => { + format!("ExprTypeIsNotInScope({}, {})", + a.repr(tcx), + b.repr(tcx)) + } BindingTypeIsNotValidAtDecl(a) => { format!("BindingTypeIsNotValidAtDecl({})", a.repr(tcx)) } @@ -948,6 +1019,7 @@ impl Repr for SubregionOrigin { CallReturn(a) => format!("CallReturn({})", a.repr(tcx)), AddrOf(a) => format!("AddrOf({})", a.repr(tcx)), AutoBorrow(a) => format!("AutoBorrow({})", a.repr(tcx)), + Managed(a) => format!("Managed({})", a.repr(tcx)), } } } diff --git a/src/librustc/middle/typeck/infer/region_inference/mod.rs b/src/librustc/middle/typeck/infer/region_inference/mod.rs index dc674da38af..7c8d10dd994 100644 --- a/src/librustc/middle/typeck/infer/region_inference/mod.rs +++ b/src/librustc/middle/typeck/infer/region_inference/mod.rs @@ -30,12 +30,32 @@ use syntax::ast; mod doc; +// A constraint that influences the inference process. #[deriving(PartialEq, Eq, Hash)] pub enum Constraint { + // One region variable is subregion of another ConstrainVarSubVar(RegionVid, RegionVid), + + // Concrete region is subregion of region variable ConstrainRegSubVar(Region, RegionVid), + + // Region variable is subregion of concrete region ConstrainVarSubReg(RegionVid, Region), - ConstrainRegSubReg(Region, Region), +} + +// Something we have to verify after region inference is done, but +// which does not directly influence the inference process +pub enum Verify { + // VerifyRegSubReg(a, b): Verify that `a <= b`. Neither `a` nor + // `b` are inference variables. + VerifyRegSubReg(SubregionOrigin, Region, Region), + + // VerifyParamBound(T, _, R, RS): The parameter type `T` must + // outlive the region `R`. `T` is known to outlive `RS`. Therefore + // verify that `R <= RS[i]` for some `i`. Inference variables may + // be involved (but this verification step doesn't influence + // inference). + VerifyParamBound(ty::ParamTy, SubregionOrigin, Region, Vec<Region>), } #[deriving(PartialEq, Eq, Hash)] @@ -51,6 +71,8 @@ pub enum UndoLogEntry { Mark, AddVar(RegionVid), AddConstraint(Constraint), + AddVerify(uint), + AddGiven(ty::FreeRegion, ty::RegionVid), AddCombination(CombineMapType, TwoRegions) } @@ -66,6 +88,13 @@ pub enum RegionResolutionError { /// `o` requires that `a <= b`, but this does not hold ConcreteFailure(SubregionOrigin, Region, Region), + /// `ParamBoundFailure(p, s, a, bs) + /// + /// The parameter type `p` must be known to outlive the lifetime + /// `a`, but it is only known to outlive `bs` (and none of the + /// regions in `bs` outlive `a`). + ParamBoundFailure(SubregionOrigin, ty::ParamTy, Region, Vec<Region>), + /// `SubSupConflict(v, sub_origin, sub_r, sup_origin, sup_r)`: /// /// Could not infer a value for `v` because `sub_r <= v` (due to @@ -125,7 +154,38 @@ pub type CombineMap = HashMap<TwoRegions, RegionVid>; pub struct RegionVarBindings<'a> { tcx: &'a ty::ctxt, var_origins: RefCell<Vec<RegionVariableOrigin>>, + + // Constraints of the form `A <= B` introduced by the region + // checker. Here at least one of `A` and `B` must be a region + // variable. constraints: RefCell<HashMap<Constraint, SubregionOrigin>>, + + // A "verify" is something that we need to verify after inference is + // done, but which does not directly affect inference in any way. + // + // An example is a `A <= B` where neither `A` nor `B` are + // inference variables. + verifys: RefCell<Vec<Verify>>, + + // A "given" is a relationship that is known to hold. In particular, + // we often know from closure fn signatures that a particular free + // region must be a subregion of a region variable: + // + // foo.iter().filter(<'a> |x: &'a &'b T| ...) + // + // In situations like this, `'b` is in fact a region variable + // introduced by the call to `iter()`, and `'a` is a bound region + // on the closure (as indicated by the `<'a>` prefix). If we are + // naive, we wind up inferring that `'b` must be `'static`, + // because we require that it be greater than `'a` and we do not + // know what `'a` is precisely. + // + // This hashmap is used to avoid that naive scenario. Basically we + // record the fact that `'a <= 'b` is implied by the fn signature, + // and then ignore the constraint when solving equations. This is + // a bit of a hack but seems to work. + givens: RefCell<HashSet<(ty::FreeRegion, ty::RegionVid)>>, + lubs: RefCell<CombineMap>, glbs: RefCell<CombineMap>, skolemization_count: Cell<uint>, @@ -164,6 +224,8 @@ impl<'a> RegionVarBindings<'a> { var_origins: RefCell::new(Vec::new()), values: RefCell::new(None), constraints: RefCell::new(HashMap::new()), + verifys: RefCell::new(Vec::new()), + givens: RefCell::new(HashSet::new()), lubs: RefCell::new(HashMap::new()), glbs: RefCell::new(HashMap::new()), skolemization_count: Cell::new(0), @@ -216,12 +278,19 @@ impl<'a> RegionVarBindings<'a> { Mark | CommitedSnapshot => { } AddVar(vid) => { let mut var_origins = self.var_origins.borrow_mut(); - assert_eq!(var_origins.len(), vid.index + 1); var_origins.pop().unwrap(); + assert_eq!(var_origins.len(), vid.index); } AddConstraint(ref constraint) => { self.constraints.borrow_mut().remove(constraint); } + AddVerify(index) => { + self.verifys.borrow_mut().pop(); + assert_eq!(self.verifys.borrow().len(), index); + } + AddGiven(sub, sup) => { + self.givens.borrow_mut().remove(&(sub, sup)); + } AddCombination(Glb, ref regions) => { self.glbs.borrow_mut().remove(regions); } @@ -289,13 +358,14 @@ impl<'a> RegionVarBindings<'a> { self.values.borrow().is_none() } - pub fn add_constraint(&self, - constraint: Constraint, - origin: SubregionOrigin) { + fn add_constraint(&self, + constraint: Constraint, + origin: SubregionOrigin) { // cannot add constraints once regions are resolved assert!(self.values_are_none()); - debug!("RegionVarBindings: add_constraint({:?})", constraint); + debug!("RegionVarBindings: add_constraint({})", + constraint.repr(self.tcx)); if self.constraints.borrow_mut().insert(constraint, origin) { if self.in_snapshot() { @@ -304,6 +374,38 @@ impl<'a> RegionVarBindings<'a> { } } + fn add_verify(&self, + verify: Verify) { + // cannot add verifys once regions are resolved + assert!(self.values_are_none()); + + debug!("RegionVarBindings: add_verify({})", + verify.repr(self.tcx)); + + let mut verifys = self.verifys.borrow_mut(); + let index = verifys.len(); + verifys.push(verify); + if self.in_snapshot() { + self.undo_log.borrow_mut().push(AddVerify(index)); + } + } + + pub fn add_given(&self, + sub: ty::FreeRegion, + sup: ty::RegionVid) { + // cannot add givens once regions are resolved + assert!(self.values_are_none()); + + let mut givens = self.givens.borrow_mut(); + if givens.insert((sub, sup)) { + debug!("add_given({} <= {})", + sub.repr(self.tcx), + sup); + + self.undo_log.borrow_mut().push(AddGiven(sub, sup)); + } + } + pub fn make_subregion(&self, origin: SubregionOrigin, sub: Region, @@ -320,7 +422,9 @@ impl<'a> RegionVarBindings<'a> { (ReEarlyBound(..), ReEarlyBound(..)) => { // This case is used only to make sure that explicitly-specified // `Self` types match the real self type in implementations. - self.add_constraint(ConstrainRegSubReg(sub, sup), origin); + // + // FIXME(NDM) -- we really shouldn't be comparing bound things + self.add_verify(VerifyRegSubReg(origin, sub, sup)); } (ReEarlyBound(..), _) | (ReLateBound(..), _) | @@ -345,11 +449,19 @@ impl<'a> RegionVarBindings<'a> { self.add_constraint(ConstrainVarSubReg(sub_id, r), origin); } _ => { - self.add_constraint(ConstrainRegSubReg(sub, sup), origin); + self.add_verify(VerifyRegSubReg(origin, sub, sup)); } } } + pub fn verify_param_bound(&self, + origin: SubregionOrigin, + param_ty: ty::ParamTy, + sub: Region, + sups: Vec<Region>) { + self.add_verify(VerifyParamBound(param_ty, origin, sub, sups)); + } + pub fn lub_regions(&self, origin: SubregionOrigin, a: Region, @@ -358,7 +470,9 @@ impl<'a> RegionVarBindings<'a> { // cannot add constraints once regions are resolved assert!(self.values_are_none()); - debug!("RegionVarBindings: lub_regions({:?}, {:?})", a, b); + debug!("RegionVarBindings: lub_regions({}, {})", + a.repr(self.tcx), + b.repr(self.tcx)); match (a, b) { (ReStatic, _) | (_, ReStatic) => { ReStatic // nothing lives longer than static @@ -381,7 +495,9 @@ impl<'a> RegionVarBindings<'a> { // cannot add constraints once regions are resolved assert!(self.values_are_none()); - debug!("RegionVarBindings: glb_regions({:?}, {:?})", a, b); + debug!("RegionVarBindings: glb_regions({}, {})", + a.repr(self.tcx), + b.repr(self.tcx)); match (a, b) { (ReStatic, r) | (r, ReStatic) => { // static lives longer than everything else @@ -397,30 +513,29 @@ impl<'a> RegionVarBindings<'a> { } } + pub fn max_regions(&self, + a: Region, + b: Region) + -> Option<Region> + { + match self.glb_concrete_regions(a, b) { + Ok(r) => Some(r), + Err(_) => None + } + } + pub fn resolve_var(&self, rid: RegionVid) -> ty::Region { - let v = match *self.values.borrow() { + match *self.values.borrow() { None => { self.tcx.sess.span_bug( self.var_origins.borrow().get(rid.index).span(), "attempt to resolve region variable before values have \ been computed!") } - Some(ref values) => *values.get(rid.index) - }; - - debug!("RegionVarBindings: resolve_var({:?}={})={:?}", - rid, rid.index, v); - match v { - Value(r) => r, - - NoValue => { - // No constraints, return ty::ReEmpty - ReEmpty - } - - ErrorValue => { - // An error that has previously been reported. - ReStatic + Some(ref values) => { + let r = lookup(values, rid); + debug!("resolve_var({}) = {}", rid, r.repr(self.tcx)); + r } } } @@ -456,7 +571,7 @@ impl<'a> RegionVarBindings<'a> { } relate(self, a, ReInfer(ReVar(c))); relate(self, b, ReInfer(ReVar(c))); - debug!("combine_vars() c={:?}", c); + debug!("combine_vars() c={}", c); ReInfer(ReVar(c)) } @@ -493,40 +608,53 @@ impl<'a> RegionVarBindings<'a> { while result_index < result_set.len() { // nb: can't use uint::range() here because result_set grows let r = *result_set.get(result_index); - debug!("result_index={}, r={:?}", result_index, r); + debug!("result_index={}, r={}", result_index, r); for undo_entry in self.undo_log.borrow().slice_from(mark.length).iter() { - let regs = match undo_entry { - &AddConstraint(ConstrainVarSubVar(ref a, ref b)) => { - Some((ReInfer(ReVar(*a)), ReInfer(ReVar(*b)))) + match undo_entry { + &AddConstraint(ConstrainVarSubVar(a, b)) => { + consider_adding_bidirectional_edges( + &mut result_set, r, + ReInfer(ReVar(a)), ReInfer(ReVar(b))); + } + &AddConstraint(ConstrainRegSubVar(a, b)) => { + consider_adding_bidirectional_edges( + &mut result_set, r, + a, ReInfer(ReVar(b))); } - &AddConstraint(ConstrainRegSubVar(ref a, ref b)) => { - Some((*a, ReInfer(ReVar(*b)))) + &AddConstraint(ConstrainVarSubReg(a, b)) => { + consider_adding_bidirectional_edges( + &mut result_set, r, + ReInfer(ReVar(a)), b); } - &AddConstraint(ConstrainVarSubReg(ref a, ref b)) => { - Some((ReInfer(ReVar(*a)), *b)) + &AddGiven(a, b) => { + consider_adding_bidirectional_edges( + &mut result_set, r, + ReFree(a), ReInfer(ReVar(b))); } - &AddConstraint(ConstrainRegSubReg(a, b)) => { - Some((a, b)) + &AddVerify(i) => { + match self.verifys.borrow().get(i) { + &VerifyRegSubReg(_, a, b) => { + consider_adding_bidirectional_edges( + &mut result_set, r, + a, b); + } + &VerifyParamBound(_, _, a, ref bs) => { + for &b in bs.iter() { + consider_adding_bidirectional_edges( + &mut result_set, r, + a, b); + } + } + } } &AddCombination(..) | &Mark | &AddVar(..) | &OpenSnapshot | &CommitedSnapshot => { - None - } - }; - - match regs { - None => {} - Some((r1, r2)) => { - result_set = - consider_adding_edge(result_set, r, r1, r2); - result_set = - consider_adding_edge(result_set, r, r2, r1); } } } @@ -536,17 +664,24 @@ impl<'a> RegionVarBindings<'a> { return result_set; - fn consider_adding_edge(result_set: Vec<Region> , - r: Region, - r1: Region, - r2: Region) -> Vec<Region> { - let mut result_set = result_set; - if r == r1 { // Clearly, this is potentially inefficient. + fn consider_adding_bidirectional_edges(result_set: &mut Vec<Region>, + r: Region, + r1: Region, + r2: Region) { + consider_adding_directed_edge(result_set, r, r1, r2); + consider_adding_directed_edge(result_set, r, r2, r1); + } + + fn consider_adding_directed_edge(result_set: &mut Vec<Region>, + r: Region, + r1: Region, + r2: Region) { + if r == r1 { + // Clearly, this is potentially inefficient. if !result_set.iter().any(|x| *x == r2) { result_set.push(r2); } } - return result_set; } } @@ -595,7 +730,7 @@ impl<'a> RegionVarBindings<'a> { self.tcx.sess.span_bug( self.var_origins.borrow().get(v_id.index).span(), format!("lub_concrete_regions invoked with \ - non-concrete regions: {:?}, {:?}", + non-concrete regions: {}, {}", a, b).as_slice()); } @@ -675,7 +810,7 @@ impl<'a> RegionVarBindings<'a> { a: Region, b: Region) -> cres<Region> { - debug!("glb_concrete_regions({:?}, {:?})", a, b); + debug!("glb_concrete_regions({}, {})", a, b); match (a, b) { (ReLateBound(..), _) | (_, ReLateBound(..)) | @@ -702,7 +837,7 @@ impl<'a> RegionVarBindings<'a> { self.tcx.sess.span_bug( self.var_origins.borrow().get(v_id.index).span(), format!("glb_concrete_regions invoked with \ - non-concrete regions: {:?}, {:?}", + non-concrete regions: {}, {}", a, b).as_slice()); } @@ -783,7 +918,7 @@ impl<'a> RegionVarBindings<'a> { // scopes or two free regions. So, if one of // these scopes is a subscope of the other, return // it. Otherwise fail. - debug!("intersect_scopes(scope_a={:?}, scope_b={:?}, region_a={:?}, region_b={:?})", + debug!("intersect_scopes(scope_a={}, scope_b={}, region_a={}, region_b={})", scope_a, scope_b, region_a, region_b); match self.tcx.region_maps.nearest_common_ancestor(scope_a, scope_b) { Some(r_id) if scope_a == r_id => Ok(ReScope(scope_b)), @@ -815,12 +950,16 @@ type RegionGraph = graph::Graph<(), Constraint>; impl<'a> RegionVarBindings<'a> { fn infer_variable_values(&self, errors: &mut Vec<RegionResolutionError>) - -> Vec<VarValue> { + -> Vec<VarValue> + { let mut var_data = self.construct_var_data(); self.expansion(var_data.as_mut_slice()); self.contraction(var_data.as_mut_slice()); - self.collect_concrete_region_errors(&mut *errors); - self.extract_values_and_collect_conflicts(var_data.as_slice(), errors) + let values = + self.extract_values_and_collect_conflicts(var_data.as_slice(), + errors); + self.collect_concrete_region_errors(&values, errors); + values } fn construct_var_data(&self) -> Vec<VarData> { @@ -838,6 +977,12 @@ impl<'a> RegionVarBindings<'a> { fn expansion(&self, var_data: &mut [VarData]) { self.iterate_until_fixed_point("Expansion", |constraint| { + debug!("expansion: constraint={} origin={}", + constraint.repr(self.tcx), + self.constraints.borrow() + .find(constraint) + .unwrap() + .repr(self.tcx)); match *constraint { ConstrainRegSubVar(a_region, b_vid) => { let b_data = &mut var_data[b_vid.index]; @@ -856,10 +1001,6 @@ impl<'a> RegionVarBindings<'a> { // This is a contraction constraint. Ignore it. false } - ConstrainRegSubReg(..) => { - // No region variables involved. Ignore. - false - } } }) } @@ -868,14 +1009,29 @@ impl<'a> RegionVarBindings<'a> { a_region: Region, b_vid: RegionVid, b_data: &mut VarData) - -> bool { - debug!("expand_node({:?}, {:?} == {:?})", - a_region, b_vid, b_data.value); + -> bool + { + debug!("expand_node({}, {} == {})", + a_region.repr(self.tcx), + b_vid, + b_data.value.repr(self.tcx)); + + // Check if this relationship is implied by a given. + match a_region { + ty::ReFree(fr) => { + if self.givens.borrow().contains(&(fr, b_vid)) { + debug!("given"); + return false; + } + } + _ => { } + } b_data.classification = Expanding; match b_data.value { NoValue => { - debug!("Setting initial value of {:?} to {:?}", b_vid, a_region); + debug!("Setting initial value of {} to {}", + b_vid, a_region.repr(self.tcx)); b_data.value = Value(a_region); return true; @@ -887,8 +1043,10 @@ impl<'a> RegionVarBindings<'a> { return false; } - debug!("Expanding value of {:?} from {:?} to {:?}", - b_vid, cur_region, lub); + debug!("Expanding value of {} from {} to {}", + b_vid, + cur_region.repr(self.tcx), + lub.repr(self.tcx)); b_data.value = Value(lub); return true; @@ -903,6 +1061,12 @@ impl<'a> RegionVarBindings<'a> { fn contraction(&self, var_data: &mut [VarData]) { self.iterate_until_fixed_point("Contraction", |constraint| { + debug!("contraction: constraint={} origin={}", + constraint.repr(self.tcx), + self.constraints.borrow() + .find(constraint) + .unwrap() + .repr(self.tcx)); match *constraint { ConstrainRegSubVar(..) => { // This is an expansion constraint. Ignore. @@ -921,10 +1085,6 @@ impl<'a> RegionVarBindings<'a> { let a_data = &mut var_data[a_vid.index]; self.contract_node(a_vid, a_data, b_region) } - ConstrainRegSubReg(..) => { - // No region variables involved. Ignore. - false - } } }) } @@ -934,8 +1094,9 @@ impl<'a> RegionVarBindings<'a> { a_data: &mut VarData, b_region: Region) -> bool { - debug!("contract_node({:?} == {:?}/{:?}, {:?})", - a_vid, a_data.value, a_data.classification, b_region); + debug!("contract_node({} == {}/{}, {})", + a_vid, a_data.value.repr(self.tcx), + a_data.classification, b_region.repr(self.tcx)); return match a_data.value { NoValue => { @@ -967,8 +1128,10 @@ impl<'a> RegionVarBindings<'a> { b_region: Region) -> bool { if !this.is_subregion_of(a_region, b_region) { - debug!("Setting {:?} to ErrorValue: {:?} not subregion of {:?}", - a_vid, a_region, b_region); + debug!("Setting {} to ErrorValue: {} not subregion of {}", + a_vid, + a_region.repr(this.tcx), + b_region.repr(this.tcx)); a_data.value = ErrorValue; } false @@ -985,15 +1148,19 @@ impl<'a> RegionVarBindings<'a> { if glb == a_region { false } else { - debug!("Contracting value of {:?} from {:?} to {:?}", - a_vid, a_region, glb); + debug!("Contracting value of {} from {} to {}", + a_vid, + a_region.repr(this.tcx), + glb.repr(this.tcx)); a_data.value = Value(glb); true } } Err(_) => { - debug!("Setting {:?} to ErrorValue: no glb of {:?}, {:?}", - a_vid, a_region, b_region); + debug!("Setting {} to ErrorValue: no glb of {}, {}", + a_vid, + a_region.repr(this.tcx), + b_region.repr(this.tcx)); a_data.value = ErrorValue; false } @@ -1001,30 +1168,44 @@ impl<'a> RegionVarBindings<'a> { } } - fn collect_concrete_region_errors( - &self, - errors: &mut Vec<RegionResolutionError>) + fn collect_concrete_region_errors(&self, + values: &Vec<VarValue>, + errors: &mut Vec<RegionResolutionError>) { - for (constraint, _) in self.constraints.borrow().iter() { - let (sub, sup) = match *constraint { - ConstrainVarSubVar(..) | - ConstrainRegSubVar(..) | - ConstrainVarSubReg(..) => { - continue; - } - ConstrainRegSubReg(sub, sup) => { - (sub, sup) + let mut reg_reg_dups = HashSet::new(); + for verify in self.verifys.borrow().iter() { + match *verify { + VerifyRegSubReg(ref origin, sub, sup) => { + if self.is_subregion_of(sub, sup) { + continue; + } + + if !reg_reg_dups.insert((sub, sup)) { + continue; + } + + debug!("ConcreteFailure: !(sub <= sup): sub={}, sup={}", + sub.repr(self.tcx), + sup.repr(self.tcx)); + errors.push(ConcreteFailure((*origin).clone(), sub, sup)); } - }; - if self.is_subregion_of(sub, sup) { - continue; - } + VerifyParamBound(ref param_ty, ref origin, sub, ref sups) => { + let sub = normalize(values, sub); + if sups.iter() + .map(|&sup| normalize(values, sup)) + .any(|sup| self.is_subregion_of(sub, sup)) + { + continue; + } - debug!("ConcreteFailure: !(sub <= sup): sub={:?}, sup={:?}", - sub, sup); - let origin = self.constraints.borrow().get_copy(constraint); - errors.push(ConcreteFailure(origin, sub, sup)); + let sups = sups.iter().map(|&sup| normalize(values, sup)) + .collect(); + errors.push( + ParamBoundFailure( + (*origin).clone(), *param_ty, sub, sups)); + } + } } } @@ -1032,7 +1213,8 @@ impl<'a> RegionVarBindings<'a> { &self, var_data: &[VarData], errors: &mut Vec<RegionResolutionError>) - -> Vec<VarValue> { + -> Vec<VarValue> + { debug!("extract_values_and_collect_conflicts()"); // This is the best way that I have found to suppress @@ -1141,10 +1323,6 @@ impl<'a> RegionVarBindings<'a> { dummy_idx, *constraint); } - ConstrainRegSubReg(..) => { - // Relations between two concrete regions do not - // require an edge in the graph. - } } } @@ -1206,16 +1384,10 @@ impl<'a> RegionVarBindings<'a> { self.tcx.sess.span_bug( self.var_origins.borrow().get(node_idx.index).span(), format!("collect_error_for_expanding_node() could not find error \ - for var {:?}, lower_bounds={}, upper_bounds={}", - node_idx, - lower_bounds.iter() - .map(|x| x.region) - .collect::<Vec<ty::Region>>() - .repr(self.tcx), - upper_bounds.iter() - .map(|x| x.region) - .collect::<Vec<ty::Region>>() - .repr(self.tcx)).as_slice()); + for var {}, lower_bounds={}, upper_bounds={}", + node_idx, + lower_bounds.repr(self.tcx), + upper_bounds.repr(self.tcx)).as_slice()); } fn collect_error_for_contracting_node( @@ -1257,12 +1429,9 @@ impl<'a> RegionVarBindings<'a> { self.tcx.sess.span_bug( self.var_origins.borrow().get(node_idx.index).span(), format!("collect_error_for_contracting_node() could not find error \ - for var {:?}, upper_bounds={}", - node_idx, - upper_bounds.iter() - .map(|x| x.region) - .collect::<Vec<ty::Region>>() - .repr(self.tcx)).as_slice()); + for var {}, upper_bounds={}", + node_idx, + upper_bounds.repr(self.tcx)).as_slice()); } fn collect_concrete_regions(&self, @@ -1301,8 +1470,8 @@ impl<'a> RegionVarBindings<'a> { state.dup_found = true; } - debug!("collect_concrete_regions(orig_node_idx={:?}, node_idx={:?}, \ - classification={:?})", + debug!("collect_concrete_regions(orig_node_idx={}, node_idx={}, \ + classification={})", orig_node_idx, node_idx, classification); // figure out the direction from which this node takes its @@ -1323,7 +1492,7 @@ impl<'a> RegionVarBindings<'a> { graph: &RegionGraph, source_vid: RegionVid, dir: Direction) { - debug!("process_edges(source_vid={:?}, dir={:?})", source_vid, dir); + debug!("process_edges(source_vid={}, dir={})", source_vid, dir); let source_node_index = NodeIndex(source_vid.index); graph.each_adjacent_edge(source_node_index, dir, |_, edge| { @@ -1343,8 +1512,6 @@ impl<'a> RegionVarBindings<'a> { origin: this.constraints.borrow().get_copy(&edge.data) }); } - - ConstrainRegSubReg(..) => {} } true }); @@ -1386,9 +1553,53 @@ impl Repr for Constraint { ConstrainVarSubReg(a, b) => { format!("ConstrainVarSubReg({}, {})", a.repr(tcx), b.repr(tcx)) } - ConstrainRegSubReg(a, b) => { - format!("ConstrainRegSubReg({}, {})", a.repr(tcx), b.repr(tcx)) + } + } +} + +impl Repr for Verify { + fn repr(&self, tcx: &ty::ctxt) -> String { + match *self { + VerifyRegSubReg(_, ref a, ref b) => { + format!("VerifyRegSubReg({}, {})", a.repr(tcx), b.repr(tcx)) + } + VerifyParamBound(_, ref p, ref a, ref bs) => { + format!("VerifyParamBound({}, {}, {})", + p.repr(tcx), a.repr(tcx), bs.repr(tcx)) } } } } + +fn normalize(values: &Vec<VarValue>, r: ty::Region) -> ty::Region { + match r { + ty::ReInfer(ReVar(rid)) => lookup(values, rid), + _ => r + } +} + +fn lookup(values: &Vec<VarValue>, rid: ty::RegionVid) -> ty::Region { + match *values.get(rid.index) { + Value(r) => r, + NoValue => ReEmpty, // No constraints, return ty::ReEmpty + ErrorValue => ReStatic, // Previously reported error. + } +} + +impl Repr for VarValue { + fn repr(&self, tcx: &ty::ctxt) -> String { + match *self { + NoValue => format!("NoValue"), + Value(r) => format!("Value({})", r.repr(tcx)), + ErrorValue => format!("ErrorValue"), + } + } +} + +impl Repr for RegionAndOrigin { + fn repr(&self, tcx: &ty::ctxt) -> String { + format!("RegionAndOrigin({},{})", + self.region.repr(tcx), + self.origin.repr(tcx)) + } +} diff --git a/src/librustc/middle/typeck/infer/sub.rs b/src/librustc/middle/typeck/infer/sub.rs index 44c147bfe7f..a9e8d1e8603 100644 --- a/src/librustc/middle/typeck/infer/sub.rs +++ b/src/librustc/middle/typeck/infer/sub.rs @@ -102,7 +102,8 @@ impl<'f> Combine for Sub<'f> { }) } - fn bounds(&self, a: BuiltinBounds, b: BuiltinBounds) -> cres<BuiltinBounds> { + fn builtin_bounds(&self, a: BuiltinBounds, b: BuiltinBounds) + -> cres<BuiltinBounds> { // More bounds is a subtype of fewer bounds. // // e.g., fn:Copy() <: fn(), because the former is a function @@ -129,10 +130,23 @@ impl<'f> Combine for Sub<'f> { if_ok!(self.get_ref().var_sub_var(a_id, b_id)); Ok(a) } + // The vec/str check here and below is so that we don't unify + // T with [T], this is necessary so we reflect subtyping of references + // (&T does not unify with &[T]) where that in turn is to reflect + // the historical non-typedness of [T]. + (&ty::ty_infer(TyVar(_)), &ty::ty_str) | + (&ty::ty_infer(TyVar(_)), &ty::ty_vec(_, None)) => { + Err(ty::terr_sorts(expected_found(self, a, b))) + } (&ty::ty_infer(TyVar(a_id)), _) => { if_ok!(self.get_ref().var_sub_t(a_id, b)); Ok(a) } + + (&ty::ty_str, &ty::ty_infer(TyVar(_))) | + (&ty::ty_vec(_, None), &ty::ty_infer(TyVar(_))) => { + Err(ty::terr_sorts(expected_found(self, a, b))) + } (_, &ty::ty_infer(TyVar(b_id))) => { if_ok!(self.get_ref().t_sub_var(a, b_id)); Ok(a) diff --git a/src/librustc/middle/typeck/infer/unify.rs b/src/librustc/middle/typeck/infer/unify.rs index 44afc04d3f0..33bdded5234 100644 --- a/src/librustc/middle/typeck/infer/unify.rs +++ b/src/librustc/middle/typeck/infer/unify.rs @@ -555,3 +555,12 @@ impl SimplyUnifiable for ast::FloatTy { return ty::terr_float_mismatch(err); } } + +impl<K:Repr,V:Repr> Repr for VarValue<K,V> { + fn repr(&self, tcx: &ty::ctxt) -> String { + match *self { + Redirect(ref k) => format!("Redirect({})", k.repr(tcx)), + Root(ref v, r) => format!("Root({}, {})", v.repr(tcx), r) + } + } +} diff --git a/src/librustc/middle/typeck/rscope.rs b/src/librustc/middle/typeck/rscope.rs index cdb691073cd..530f65855d4 100644 --- a/src/librustc/middle/typeck/rscope.rs +++ b/src/librustc/middle/typeck/rscope.rs @@ -30,12 +30,19 @@ pub trait RegionScope { span: Span, count: uint) -> Result<Vec<ty::Region> , ()>; + + fn default_region_bound(&self, span: Span) -> Option<ty::Region>; } -// A scope in which all regions must be explicitly named +// A scope in which all regions must be explicitly named. This is used +// for types that appear in structs and so on. pub struct ExplicitRscope; impl RegionScope for ExplicitRscope { + fn default_region_bound(&self, _span: Span) -> Option<ty::Region> { + None + } + fn anon_regions(&self, _span: Span, _count: uint) @@ -44,6 +51,33 @@ impl RegionScope for ExplicitRscope { } } +// A scope in which any omitted region defaults to `default`. This is +// used after the `->` in function signatures, but also for backwards +// compatibility with object types. The latter use may go away. +pub struct SpecificRscope { + default: ty::Region +} + +impl SpecificRscope { + pub fn new(r: ty::Region) -> SpecificRscope { + SpecificRscope { default: r } + } +} + +impl RegionScope for SpecificRscope { + fn default_region_bound(&self, _span: Span) -> Option<ty::Region> { + Some(self.default) + } + + fn anon_regions(&self, + _span: Span, + count: uint) + -> Result<Vec<ty::Region> , ()> + { + Ok(Vec::from_elem(count, self.default)) + } +} + /// A scope in which we generate anonymous, late-bound regions for /// omitted regions. This occurs in function signatures. pub struct BindingRscope { @@ -58,30 +92,26 @@ impl BindingRscope { anon_bindings: Cell::new(0), } } -} -impl RegionScope for BindingRscope { - fn anon_regions(&self, - _: Span, - count: uint) - -> Result<Vec<ty::Region>, ()> { + fn next_region(&self) -> ty::Region { let idx = self.anon_bindings.get(); - self.anon_bindings.set(idx + count); - Ok(Vec::from_fn(count, |i| ty::ReLateBound(self.binder_id, - ty::BrAnon(idx + i)))) + self.anon_bindings.set(idx + 1); + ty::ReLateBound(self.binder_id, ty::BrAnon(idx)) } } -/// A scope in which we generate one specific region. This occurs after the -/// `->` (i.e. in the return type) of function signatures. -pub struct ImpliedSingleRscope { - pub region: ty::Region, -} +impl RegionScope for BindingRscope { + fn default_region_bound(&self, _span: Span) -> Option<ty::Region> + { + Some(self.next_region()) + } -impl RegionScope for ImpliedSingleRscope { - fn anon_regions(&self, _: Span, count: uint) - -> Result<Vec<ty::Region>,()> { - Ok(Vec::from_elem(count, self.region.clone())) + fn anon_regions(&self, + _: Span, + count: uint) + -> Result<Vec<ty::Region> , ()> + { + Ok(Vec::from_fn(count, |_| self.next_region())) } } diff --git a/src/librustc/middle/typeck/variance.rs b/src/librustc/middle/typeck/variance.rs index 97c11b92059..e6227b9c128 100644 --- a/src/librustc/middle/typeck/variance.rs +++ b/src/librustc/middle/typeck/variance.rs @@ -741,11 +741,7 @@ impl<'a> ConstraintContext<'a> { self.add_constraints_from_mt(mt, variance); } - ty::ty_vec(ref mt, _) => { - self.add_constraints_from_mt(mt, variance); - } - - ty::ty_uniq(typ) | ty::ty_box(typ) => { + ty::ty_uniq(typ) | ty::ty_box(typ) | ty::ty_vec(typ, _) | ty::ty_open(typ) => { self.add_constraints_from_ty(typ, variance); } diff --git a/src/librustc/util/common.rs b/src/librustc/util/common.rs index 9c9942d2628..61ef9df99e8 100644 --- a/src/librustc/util/common.rs +++ b/src/librustc/util/common.rs @@ -10,6 +10,8 @@ #![allow(non_camel_case_types)] +use std::hash::{Hash, Hasher}; +use std::collections::HashMap; use syntax::ast; use syntax::visit; use syntax::visit::Visitor; @@ -105,3 +107,51 @@ pub fn block_query(b: ast::P<ast::Block>, p: |&ast::Expr| -> bool) -> bool { visit::walk_block(&mut v, &*b, ()); return v.flag; } + +// K: Eq + Hash<S>, V, S, H: Hasher<S> +pub fn can_reach<S,H:Hasher<S>,T:Eq+Clone+Hash<S>>( + edges_map: &HashMap<T,Vec<T>,H>, + source: T, + destination: T) + -> bool +{ + /*! + * Determines whether there exists a path from `source` to + * `destination`. The graph is defined by the `edges_map`, which + * maps from a node `S` to a list of its adjacent nodes `T`. + * + * Efficiency note: This is implemented in an inefficient way + * because it is typically invoked on very small graphs. If the graphs + * become larger, a more efficient graph representation and algorithm + * would probably be advised. + */ + + if source == destination { + return true; + } + + // Do a little breadth-first-search here. The `queue` list + // doubles as a way to detect if we've seen a particular FR + // before. Note that we expect this graph to be an *extremely + // shallow* tree. + let mut queue = vec!(source); + let mut i = 0; + while i < queue.len() { + match edges_map.find(queue.get(i)) { + Some(edges) => { + for target in edges.iter() { + if *target == destination { + return true; + } + + if !queue.iter().any(|x| x == target) { + queue.push((*target).clone()); + } + } + } + None => {} + } + i += 1; + } + return false; +} diff --git a/src/librustc/util/ppaux.rs b/src/librustc/util/ppaux.rs index e0e9f0e6910..11f16f1ea95 100644 --- a/src/librustc/util/ppaux.rs +++ b/src/librustc/util/ppaux.rs @@ -19,15 +19,13 @@ use middle::ty::{ReSkolemized, ReVar}; use middle::ty::{mt, t, ParamTy}; use middle::ty::{ty_bool, ty_char, ty_bot, ty_box, ty_struct, ty_enum}; use middle::ty::{ty_err, ty_str, ty_vec, ty_float, ty_bare_fn, ty_closure}; -use middle::ty::{ty_nil, ty_param, ty_ptr, ty_rptr, ty_tup}; +use middle::ty::{ty_nil, ty_param, ty_ptr, ty_rptr, ty_tup, ty_open}; use middle::ty::{ty_unboxed_closure}; use middle::ty::{ty_uniq, ty_trait, ty_int, ty_uint, ty_infer}; use middle::ty; -use middle::typeck::infer::region_inference; -use middle::typeck::infer::unify::VarValue as VV; -use middle::typeck::infer::unify; -use middle::typeck::infer; use middle::typeck; +use middle::typeck::check::regionmanip; +use middle::typeck::infer; use std::gc::Gc; use std::rc::Rc; @@ -66,6 +64,22 @@ pub fn note_and_explain_region(cx: &ctxt, } } +fn item_scope_tag(item: &ast::Item) -> &'static str { + /*! + * When a free region is associated with `item`, how should we describe + * the item in the error message. + */ + + match item.node { + ast::ItemImpl(..) => "impl", + ast::ItemStruct(..) => "struct", + ast::ItemEnum(..) => "enum", + ast::ItemTrait(..) => "trait", + ast::ItemFn(..) => "function body", + _ => "item" + } +} + pub fn explain_region_and_span(cx: &ctxt, region: ty::Region) -> (String, Option<Span>) { return match region { @@ -87,9 +101,9 @@ pub fn explain_region_and_span(cx: &ctxt, region: ty::Region) Some(ast_map::NodeStmt(stmt)) => { explain_span(cx, "statement", stmt.span) } - Some(ast_map::NodeItem(it)) if (match it.node { - ast::ItemFn(..) => true, _ => false}) => { - explain_span(cx, "function body", it.span) + Some(ast_map::NodeItem(it)) => { + let tag = item_scope_tag(&*it); + explain_span(cx, tag, it.span) } Some(_) | None => { // this really should not happen @@ -112,17 +126,17 @@ pub fn explain_region_and_span(cx: &ctxt, region: ty::Region) match cx.map.find(fr.scope_id) { Some(ast_map::NodeBlock(ref blk)) => { - let (msg, opt_span) = explain_span(cx, "block", blk.span); - (format!("{} {}", prefix, msg), opt_span) + let (msg, opt_span) = explain_span(cx, "block", blk.span); + (format!("{} {}", prefix, msg), opt_span) } - Some(ast_map::NodeItem(it)) if match it.node { - ast::ItemImpl(..) => true, _ => false} => { - let (msg, opt_span) = explain_span(cx, "impl", it.span); - (format!("{} {}", prefix, msg), opt_span) + Some(ast_map::NodeItem(it)) => { + let tag = item_scope_tag(&*it); + let (msg, opt_span) = explain_span(cx, tag, it.span); + (format!("{} {}", prefix, msg), opt_span) } Some(_) | None => { - // this really should not happen - (format!("{} node {}", prefix, fr.scope_id), None) + // this really should not happen + (format!("{} node {}", prefix, fr.scope_id), None) } } } @@ -143,7 +157,7 @@ pub fn explain_region_and_span(cx: &ctxt, region: ty::Region) }; fn explain_span(cx: &ctxt, heading: &str, span: Span) - -> (String, Option<Span>) { + -> (String, Option<Span>) { let lo = cx.sess.codemap().lookup_char_pos_adj(span.lo); (format!("the {} at {}:{}", heading, lo.line, lo.col.to_uint()), Some(span)) @@ -273,7 +287,7 @@ pub fn ty_to_string(cx: &ctxt, typ: t) -> String { _ => { } } - push_sig_to_string(cx, &mut s, '(', ')', sig); + push_sig_to_string(cx, &mut s, '(', ')', sig, ""); s } @@ -296,34 +310,34 @@ pub fn ty_to_string(cx: &ctxt, typ: t) -> String { } }; + let bounds_str = cty.bounds.user_string(cx); + match cty.store { ty::UniqTraitStore => { assert_eq!(cty.onceness, ast::Once); s.push_str("proc"); - push_sig_to_string(cx, &mut s, '(', ')', &cty.sig); + push_sig_to_string(cx, &mut s, '(', ')', &cty.sig, + bounds_str.as_slice()); } ty::RegionTraitStore(..) => { match cty.onceness { ast::Many => {} ast::Once => s.push_str("once ") } - push_sig_to_string(cx, &mut s, '|', '|', &cty.sig); + push_sig_to_string(cx, &mut s, '|', '|', &cty.sig, + bounds_str.as_slice()); } } - if !cty.bounds.is_empty() { - s.push_str(":"); - s.push_str(cty.bounds.repr(cx).as_slice()); - } - - s + s.into_owned() } fn push_sig_to_string(cx: &ctxt, s: &mut String, bra: char, ket: char, - sig: &ty::FnSig) { + sig: &ty::FnSig, + bounds: &str) { s.push_char(bra); let strs: Vec<String> = sig.inputs.iter().map(|a| fn_input_to_string(cx, *a)).collect(); s.push_str(strs.connect(", ").as_slice()); @@ -332,6 +346,11 @@ pub fn ty_to_string(cx: &ctxt, typ: t) -> String { } s.push_char(ket); + if !bounds.is_empty() { + s.push_str(":"); + s.push_str(bounds); + } + if ty::get(sig.output).sty != ty_nil { s.push_str(" -> "); if ty::type_is_bot(sig.output) { @@ -370,6 +389,7 @@ pub fn ty_to_string(cx: &ctxt, typ: t) -> String { buf.push_str(mt_to_string(cx, tm).as_slice()); buf } + ty_open(typ) => format!("opened<{}>", ty_to_string(cx, typ)), ty_tup(ref elems) => { let strs: Vec<String> = elems.iter().map(|elem| ty_to_string(cx, *elem)).collect(); format!("({})", strs.connect(",")) @@ -382,18 +402,8 @@ pub fn ty_to_string(cx: &ctxt, typ: t) -> String { } ty_infer(infer_ty) => infer_ty.to_string(), ty_err => "[type error]".to_string(), - ty_param(ParamTy {idx: id, def_id: did, ..}) => { - let ident = match cx.ty_param_defs.borrow().find(&did.node) { - Some(def) => token::get_ident(def.ident).get().to_string(), - // This can only happen when a type mismatch error happens and - // the actual type has more type parameters than the expected one. - None => format!("<generic #{}>", id), - }; - if !cx.sess.verbose() { - ident - } else { - format!("{}:{:?}", ident, did) - } + ty_param(ref param_ty) => { + param_ty.repr(cx) } ty_enum(did, ref substs) | ty_struct(did, ref substs) => { let base = ty::item_path_str(cx, did); @@ -407,8 +417,8 @@ pub fn ty_to_string(cx: &ctxt, typ: t) -> String { let trait_def = ty::lookup_trait_def(cx, did); let ty = parameterized(cx, base.as_slice(), substs, &trait_def.generics); - let bound_sep = if bounds.is_empty() { "" } else { ":" }; - let bound_str = bounds.repr(cx); + let bound_str = bounds.user_string(cx); + let bound_sep = if bound_str.is_empty() { "" } else { "+" }; format!("{}{}{}", ty, bound_sep, @@ -416,12 +426,12 @@ pub fn ty_to_string(cx: &ctxt, typ: t) -> String { } ty_str => "str".to_string(), ty_unboxed_closure(..) => "closure".to_string(), - ty_vec(ref mt, sz) => { + ty_vec(t, sz) => { match sz { Some(n) => { - format!("[{}, .. {}]", mt_to_string(cx, mt), n) + format!("[{}, .. {}]", ty_to_string(cx, t), n) } - None => format!("[{}]", ty_to_string(cx, mt.ty)), + None => format!("[{}]", ty_to_string(cx, t)), } } } @@ -572,6 +582,14 @@ impl<T:Repr> Repr for Vec<T> { } } +impl<T:UserString> UserString for Vec<T> { + fn user_string(&self, tcx: &ctxt) -> String { + let strs: Vec<String> = + self.iter().map(|t| t.user_string(tcx)).collect(); + strs.connect(", ") + } +} + impl Repr for def::Def { fn repr(&self, _tcx: &ctxt) -> String { format!("{:?}", *self) @@ -580,16 +598,18 @@ impl Repr for def::Def { impl Repr for ty::TypeParameterDef { fn repr(&self, tcx: &ctxt) -> String { - format!("TypeParameterDef({:?}, {})", self.def_id, + format!("TypeParameterDef({}, {})", + self.def_id.repr(tcx), self.bounds.repr(tcx)) } } impl Repr for ty::RegionParameterDef { - fn repr(&self, _tcx: &ctxt) -> String { - format!("RegionParameterDef({}, {:?})", + fn repr(&self, tcx: &ctxt) -> String { + format!("RegionParameterDef(name={}, def_id={}, bounds={})", token::get_name(self.name), - self.def_id) + self.def_id.repr(tcx), + self.bounds.repr(tcx)) } } @@ -637,18 +657,31 @@ impl Repr for subst::RegionSubsts { } } -impl Repr for ty::ParamBounds { - fn repr(&self, tcx: &ctxt) -> String { +impl Repr for ty::BuiltinBounds { + fn repr(&self, _tcx: &ctxt) -> String { let mut res = Vec::new(); - for b in self.builtin_bounds.iter() { + for b in self.iter() { res.push(match b { - ty::BoundStatic => "'static".to_string(), - ty::BoundSend => "Send".to_string(), - ty::BoundSized => "Sized".to_string(), - ty::BoundCopy => "Copy".to_string(), - ty::BoundSync => "Sync".to_string(), + ty::BoundSend => "Send".to_owned(), + ty::BoundSized => "Sized".to_owned(), + ty::BoundCopy => "Copy".to_owned(), + ty::BoundSync => "Sync".to_owned(), }); } + res.connect("+") + } +} + +impl Repr for ty::ExistentialBounds { + fn repr(&self, tcx: &ctxt) -> String { + self.user_string(tcx) + } +} + +impl Repr for ty::ParamBounds { + fn repr(&self, tcx: &ctxt) -> String { + let mut res = Vec::new(); + res.push(self.builtin_bounds.repr(tcx)); for t in self.trait_bounds.iter() { res.push(t.repr(tcx)); } @@ -662,6 +695,15 @@ impl Repr for ty::TraitRef { } } +impl Repr for ty::TraitDef { + fn repr(&self, tcx: &ctxt) -> String { + format!("TraitDef(generics={}, bounds={}, trait_ref={})", + self.generics.repr(tcx), + self.bounds.repr(tcx), + self.trait_ref.repr(tcx)) + } +} + impl Repr for ast::Expr { fn repr(&self, _tcx: &ctxt) -> String { format!("expr({}: {})", self.id, pprust::expr_to_string(self)) @@ -674,12 +716,24 @@ impl Repr for ast::Path { } } +impl UserString for ast::Path { + fn user_string(&self, _tcx: &ctxt) -> String { + pprust::path_to_string(self) + } +} + impl Repr for ast::Item { fn repr(&self, tcx: &ctxt) -> String { format!("item({})", tcx.map.node_to_string(self.id)) } } +impl Repr for ast::Lifetime { + fn repr(&self, _tcx: &ctxt) -> String { + format!("lifetime({}: {})", self.id, pprust::lifetime_to_string(self)) + } +} + impl Repr for ast::Stmt { fn repr(&self, _tcx: &ctxt) -> String { format!("stmt({}: {})", @@ -723,11 +777,7 @@ impl Repr for ty::Region { bound_region.repr(tcx)) } - ty::ReFree(ref fr) => { - format!("ReFree({}, {})", - fr.scope_id, - fr.bound_region.repr(tcx)) - } + ty::ReFree(ref fr) => fr.repr(tcx), ty::ReScope(id) => { format!("ReScope({})", id) @@ -752,6 +802,20 @@ impl Repr for ty::Region { } } +impl UserString for ty::Region { + fn user_string(&self, tcx: &ctxt) -> String { + region_to_string(tcx, "", false, *self) + } +} + +impl Repr for ty::FreeRegion { + fn repr(&self, tcx: &ctxt) -> String { + format!("ReFree({}, {})", + self.scope_id, + self.bound_region.repr(tcx)) + } +} + impl Repr for ast::DefId { fn repr(&self, tcx: &ctxt) -> String { // Unfortunately, there seems to be no way to attempt to print @@ -832,6 +896,12 @@ impl Repr for ast::Name { } } +impl UserString for ast::Name { + fn user_string(&self, _tcx: &ctxt) -> String { + token::get_name(*self).get().to_string() + } +} + impl Repr for ast::Ident { fn repr(&self, _tcx: &ctxt) -> String { token::get_ident(*self).get().to_string() @@ -927,21 +997,14 @@ impl Repr for ty::BuiltinBound { impl UserString for ty::BuiltinBound { fn user_string(&self, _tcx: &ctxt) -> String { match *self { - ty::BoundStatic => "'static".to_string(), - ty::BoundSend => "Send".to_string(), - ty::BoundSized => "Sized".to_string(), - ty::BoundCopy => "Copy".to_string(), - ty::BoundSync => "Sync".to_string(), + ty::BoundSend => "Send".to_owned(), + ty::BoundSized => "Sized".to_owned(), + ty::BoundCopy => "Copy".to_owned(), + ty::BoundSync => "Sync".to_owned(), } } } -impl Repr for ty::BuiltinBounds { - fn repr(&self, tcx: &ctxt) -> String { - self.user_string(tcx) - } -} - impl Repr for Span { fn repr(&self, tcx: &ctxt) -> String { tcx.sess.codemap().span_to_string(*self).to_string() @@ -955,6 +1018,43 @@ impl<A:UserString> UserString for Rc<A> { } } +impl UserString for ty::ParamBounds { + fn user_string(&self, tcx: &ctxt) -> String { + let mut result = Vec::new(); + let s = self.builtin_bounds.user_string(tcx); + if !s.is_empty() { + result.push(s); + } + for n in self.trait_bounds.iter() { + result.push(n.user_string(tcx)); + } + result.connect("+") + } +} + +impl UserString for ty::ExistentialBounds { + fn user_string(&self, tcx: &ctxt) -> String { + if self.builtin_bounds.contains_elem(ty::BoundSend) && + self.region_bound == ty::ReStatic + { // Region bound is implied by builtin bounds: + return self.builtin_bounds.repr(tcx); + } + + let mut res = Vec::new(); + + let region_str = self.region_bound.user_string(tcx); + if !region_str.is_empty() { + res.push(region_str); + } + + for bound in self.builtin_bounds.iter() { + res.push(bound.user_string(tcx)); + } + + res.connect("+") + } +} + impl UserString for ty::BuiltinBounds { fn user_string(&self, tcx: &ctxt) -> String { self.iter() @@ -1082,33 +1182,55 @@ impl<T:Repr> Repr for infer::Bounds<T> { } } -impl<K:Repr,V:Repr> Repr for VV<K,V> { - fn repr(&self, tcx: &ctxt) -> String { - match *self { - unify::Redirect(ref k) => - format!("Redirect({})", k.repr(tcx)), - unify::Root(ref v, r) => - format!("Root({}, {})", v.repr(tcx), r) - } +impl Repr for ty::ExplicitSelfCategory { + fn repr(&self, _: &ctxt) -> String { + explicit_self_category_to_str(self).to_string() } } -impl Repr for region_inference::VarValue { + +impl Repr for regionmanip::WfConstraint { fn repr(&self, tcx: &ctxt) -> String { match *self { - infer::region_inference::NoValue => - format!("NoValue"), - infer::region_inference::Value(r) => - format!("Value({})", r.repr(tcx)), - infer::region_inference::ErrorValue => - format!("ErrorValue"), + regionmanip::RegionSubRegionConstraint(_, r_a, r_b) => { + format!("RegionSubRegionConstraint({}, {})", + r_a.repr(tcx), + r_b.repr(tcx)) + } + + regionmanip::RegionSubParamConstraint(_, r, p) => { + format!("RegionSubParamConstraint({}, {})", + r.repr(tcx), + p.repr(tcx)) + } } } } -impl Repr for ty::ExplicitSelfCategory { - fn repr(&self, _: &ctxt) -> String { - explicit_self_category_to_str(self).to_string() +impl UserString for ParamTy { + fn user_string(&self, tcx: &ctxt) -> String { + let id = self.idx; + let did = self.def_id; + let ident = match tcx.ty_param_defs.borrow().find(&did.node) { + Some(def) => token::get_ident(def.ident).get().to_string(), + + // This can only happen when a type mismatch error happens and + // the actual type has more type parameters than the expected one. + None => format!("<generic #{}>", id), + }; + ident + } +} + +impl Repr for ParamTy { + fn repr(&self, tcx: &ctxt) -> String { + self.user_string(tcx) } } +impl<A:Repr,B:Repr> Repr for (A,B) { + fn repr(&self, tcx: &ctxt) -> String { + let &(ref a, ref b) = self; + format!("({},{})", a.repr(tcx), b.repr(tcx)) + } +} diff --git a/src/librustc_back/abi.rs b/src/librustc_back/abi.rs index c722beb43ae..e859a5d21d7 100644 --- a/src/librustc_back/abi.rs +++ b/src/librustc_back/abi.rs @@ -20,14 +20,8 @@ pub static fn_field_box: uint = 1u; // The two fields of a trait object/trait instance: vtable and box. // The vtable contains the type descriptor as first element. -pub static trt_field_vtable: uint = 0u; -pub static trt_field_box: uint = 1u; - -pub static vec_elt_fill: uint = 0u; - -pub static vec_elt_alloc: uint = 1u; - -pub static vec_elt_elems: uint = 2u; +pub static trt_field_box: uint = 0u; +pub static trt_field_vtable: uint = 1u; pub static slice_elt_base: uint = 0u; pub static slice_elt_len: uint = 1u; diff --git a/src/librustc_back/svh.rs b/src/librustc_back/svh.rs index 29668795ed7..6af19d948e0 100644 --- a/src/librustc_back/svh.rs +++ b/src/librustc_back/svh.rs @@ -225,7 +225,6 @@ mod svh_visitor { SawExprBreak(Option<token::InternedString>), SawExprAgain(Option<token::InternedString>), - SawExprVstore, SawExprBox, SawExprVec, SawExprCall, @@ -257,7 +256,6 @@ mod svh_visitor { fn saw_expr<'a>(node: &'a Expr_) -> SawExprComponent<'a> { match *node { - ExprVstore(..) => SawExprVstore, ExprBox(..) => SawExprBox, ExprVec(..) => SawExprVec, ExprCall(..) => SawExprCall, diff --git a/src/librustc_llvm/lib.rs b/src/librustc_llvm/lib.rs index 1ac0aee85d4..522941cee8c 100644 --- a/src/librustc_llvm/lib.rs +++ b/src/librustc_llvm/lib.rs @@ -192,7 +192,7 @@ impl AttrHelper for SpecialAttribute { } pub struct AttrBuilder { - attrs: Vec<(uint, Box<AttrHelper>)> + attrs: Vec<(uint, Box<AttrHelper+'static>)> } impl AttrBuilder { @@ -203,12 +203,12 @@ impl AttrBuilder { } pub fn arg<'a, T: AttrHelper + 'static>(&'a mut self, idx: uint, a: T) -> &'a mut AttrBuilder { - self.attrs.push((idx, box a as Box<AttrHelper>)); + self.attrs.push((idx, box a as Box<AttrHelper+'static>)); self } pub fn ret<'a, T: AttrHelper + 'static>(&'a mut self, a: T) -> &'a mut AttrBuilder { - self.attrs.push((ReturnIndex as uint, box a as Box<AttrHelper>)); + self.attrs.push((ReturnIndex as uint, box a as Box<AttrHelper+'static>)); self } diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index 0e87be1c241..ee1b51683c2 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -124,14 +124,7 @@ fn try_inline_def(cx: &core::DocContext, pub fn load_attrs(tcx: &ty::ctxt, did: ast::DefId) -> Vec<clean::Attribute> { let mut attrs = Vec::new(); csearch::get_item_attrs(&tcx.sess.cstore, did, |v| { - attrs.extend(v.move_iter().map(|mut a| { - // FIXME this isn't quite always true, it's just true about 99% of - // the time when dealing with documentation. For example, - // this would treat doc comments of the form `#[doc = "foo"]` - // incorrectly. - if a.name().get() == "doc" && a.value_str().is_some() { - a.node.is_sugared_doc = true; - } + attrs.extend(v.move_iter().map(|a| { a.clean() })); }); @@ -166,18 +159,12 @@ pub fn build_external_trait(tcx: &ty::ctxt, did: ast::DefId) -> clean::Trait { clean::RequiredMethod(trait_item) } }); - let supertraits = ty::trait_supertraits(tcx, did); - let mut parents = supertraits.iter().map(|i| { - match i.clean() { - clean::TraitBound(ty) => ty, - clean::RegionBound => unreachable!() - } - }); - + let trait_def = ty::lookup_trait_def(tcx, did); + let bounds = trait_def.bounds.clean(); clean::Trait { generics: (&def.generics, subst::TypeSpace).clean(), items: items.collect(), - parents: parents.collect() + bounds: bounds, } } diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 68e1529fb17..af4df81f996 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -481,15 +481,14 @@ impl Clean<TyParam> for ty::TypeParameterDef { #[deriving(Clone, Encodable, Decodable, PartialEq)] pub enum TyParamBound { - RegionBound, + RegionBound, // FIXME(#16518) -- need to include name of actual region TraitBound(Type) } impl Clean<TyParamBound> for ast::TyParamBound { fn clean(&self) -> TyParamBound { match *self { - ast::StaticRegionTyParamBound => RegionBound, - ast::OtherRegionTyParamBound(_) => RegionBound, + ast::RegionTyParamBound(_) => RegionBound, ast::UnboxedFnTyParamBound(_) => { // FIXME(pcwalton): Wrong. RegionBound @@ -499,6 +498,16 @@ impl Clean<TyParamBound> for ast::TyParamBound { } } +impl Clean<Vec<TyParamBound>> for ty::ExistentialBounds { + fn clean(&self) -> Vec<TyParamBound> { + let mut vec = vec!(RegionBound); + for bb in self.builtin_bounds.iter() { + vec.push(bb.clean()); + } + vec + } +} + fn external_path(name: &str, substs: &subst::Substs) -> Path { let lifetimes = substs.regions().get_slice(subst::TypeSpace) .iter() @@ -525,7 +534,6 @@ impl Clean<TyParamBound> for ty::BuiltinBound { }; let empty = subst::Substs::empty(); let (did, path) = match *self { - ty::BoundStatic => return RegionBound, ty::BoundSend => (tcx.lang_items.send_trait().unwrap(), external_path("Send", &empty)), @@ -810,10 +818,7 @@ impl Clean<ClosureDecl> for ast::ClosureTy { decl: self.decl.clean(), onceness: self.onceness, fn_style: self.fn_style, - bounds: match self.bounds { - Some(ref x) => x.clean(), - None => Vec::new() - }, + bounds: self.bounds.clean() } } } @@ -909,7 +914,7 @@ impl Clean<RetStyle> for ast::RetStyle { pub struct Trait { pub items: Vec<TraitItem>, pub generics: Generics, - pub parents: Vec<Type>, + pub bounds: Vec<TyParamBound>, } impl Clean<Item> for doctree::Trait { @@ -924,7 +929,7 @@ impl Clean<Item> for doctree::Trait { inner: TraitItem(Trait { items: self.items.clean(), generics: self.generics.clean(), - parents: self.parents.clean(), + bounds: self.bounds.clean(), }), } } @@ -1060,7 +1065,7 @@ pub enum Type { Self(ast::DefId), /// Primitives are just the fixed-size numeric types (plus int/uint/float), and char. Primitive(Primitive), - Closure(Box<ClosureDecl>, Option<Lifetime>), + Closure(Box<ClosureDecl>), Proc(Box<ClosureDecl>), /// extern "ABI" fn BareFunction(Box<BareFunctionDecl>), @@ -1208,7 +1213,7 @@ impl Clean<Type> for ast::Ty { tpbs.clean().map(|x| x), id) } - TyClosure(ref c, region) => Closure(box c.clean(), region.clean()), + TyClosure(ref c) => Closure(box c.clean()), TyProc(ref c) => Proc(box c.clean()), TyBareFn(ref barefn) => BareFunction(box barefn.clean()), TyParen(ref ty) => ty.clean(), @@ -1250,8 +1255,8 @@ impl Clean<Type> for ty::t { }); lang_struct(box_did, t, "Box", Unique) } - ty::ty_vec(mt, None) => Vector(box mt.ty.clean()), - ty::ty_vec(mt, Some(i)) => FixedVector(box mt.ty.clean(), + ty::ty_vec(ty, None) => Vector(box ty.clean()), + ty::ty_vec(ty, Some(i)) => FixedVector(box ty.clean(), format!("{}", i)), ty::ty_ptr(mt) => RawPointer(mt.mutbl.clean(), box mt.ty.clean()), ty::ty_rptr(r, mt) => BorrowedRef { @@ -1273,11 +1278,11 @@ impl Clean<Type> for ty::t { decl: (ast_util::local_def(0), &fty.sig).clean(), onceness: fty.onceness, fn_style: fty.fn_style, - bounds: fty.bounds.iter().map(|i| i.clean()).collect(), + bounds: fty.bounds.clean(), }; match fty.store { ty::UniqTraitStore => Proc(decl), - ty::RegionTraitStore(ref r, _) => Closure(decl, r.clean()), + ty::RegionTraitStore(..) => Closure(decl), } } ty::ty_struct(did, ref substs) | @@ -1315,6 +1320,7 @@ impl Clean<Type> for ty::t { ty::ty_unboxed_closure(..) => Primitive(Unit), // FIXME(pcwalton) ty::ty_infer(..) => fail!("ty_infer"), + ty::ty_open(..) => fail!("ty_open"), ty::ty_err => fail!("ty_err"), } } diff --git a/src/librustdoc/doctree.rs b/src/librustdoc/doctree.rs index da45321e7fd..4d2cf852b8a 100644 --- a/src/librustdoc/doctree.rs +++ b/src/librustdoc/doctree.rs @@ -156,7 +156,7 @@ pub struct Trait { pub name: Ident, pub items: Vec<ast::TraitItem>, //should be TraitItem pub generics: ast::Generics, - pub parents: Vec<ast::TraitRef>, + pub bounds: Vec<ast::TyParamBound>, pub attrs: Vec<ast::Attribute>, pub id: ast::NodeId, pub whence: Span, diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index 37349388588..e526286ef46 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -355,7 +355,7 @@ impl fmt::Show for clean::Type { } clean::Self(..) => f.write("Self".as_bytes()), clean::Primitive(prim) => primitive_link(f, prim, prim.to_string()), - clean::Closure(ref decl, ref region) => { + clean::Closure(ref decl) => { write!(f, "{style}{lifetimes}|{args}|{bounds}{arrow}", style = FnStyleSpace(decl.fn_style), lifetimes = if decl.lifetimes.len() == 0 { @@ -370,13 +370,6 @@ impl fmt::Show for clean::Type { }, bounds = { let mut ret = String::new(); - match *region { - Some(ref lt) => { - ret.push_str(format!(": {}", - *lt).as_slice()); - } - None => {} - } for bound in decl.bounds.iter() { match *bound { clean::RegionBound => {} diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs index 97fa58d5077..305c18480f6 100644 --- a/src/librustdoc/html/markdown.rs +++ b/src/librustdoc/html/markdown.rs @@ -65,6 +65,7 @@ static HOEDOWN_EXTENSIONS: libc::c_uint = type hoedown_document = libc::c_void; // this is opaque to us +#[repr(C)] struct hoedown_renderer { opaque: *mut hoedown_html_renderer_state, blockcode: Option<extern "C" fn(*mut hoedown_buffer, *const hoedown_buffer, @@ -78,6 +79,7 @@ struct hoedown_renderer { other: [libc::size_t, ..28], } +#[repr(C)] struct hoedown_html_renderer_state { opaque: *mut libc::c_void, toc_data: html_toc_data, @@ -87,6 +89,7 @@ struct hoedown_html_renderer_state { *mut libc::c_void)>, } +#[repr(C)] struct html_toc_data { header_count: libc::c_int, current_level: libc::c_int, @@ -100,6 +103,7 @@ struct MyOpaque { toc_builder: Option<TocBuilder>, } +#[repr(C)] struct hoedown_buffer { data: *const u8, size: libc::size_t, diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs index be62b1cc36f..8342b6bd675 100644 --- a/src/librustdoc/html/render.rs +++ b/src/librustdoc/html/render.rs @@ -259,7 +259,8 @@ pub fn run(mut krate: clean::Crate, external_html: &ExternalHtml, dst: Path) -> // Crawl the crate attributes looking for attributes which control how we're // going to emit HTML - match krate.module.as_ref().map(|m| m.doc_list().unwrap_or(&[])) { + let default: &[_] = &[]; + match krate.module.as_ref().map(|m| m.doc_list().unwrap_or(default)) { Some(attrs) => { for attr in attrs.iter() { match *attr { @@ -1609,12 +1610,12 @@ fn item_function(w: &mut fmt::Formatter, it: &clean::Item, fn item_trait(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item, t: &clean::Trait) -> fmt::Result { - let mut parents = String::new(); - if t.parents.len() > 0 { - parents.push_str(": "); - for (i, p) in t.parents.iter().enumerate() { - if i > 0 { parents.push_str(" + "); } - parents.push_str(format!("{}", *p).as_slice()); + let mut bounds = String::new(); + if t.bounds.len() > 0 { + bounds.push_str(": "); + for (i, p) in t.bounds.iter().enumerate() { + if i > 0 { bounds.push_str(" + "); } + bounds.push_str(format!("{}", *p).as_slice()); } } @@ -1623,7 +1624,7 @@ fn item_trait(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item, VisSpace(it.visibility), it.name.get_ref().as_slice(), t.generics, - parents)); + bounds)); let required = t.items.iter() .filter(|m| { match **m { diff --git a/src/librustdoc/plugins.rs b/src/librustdoc/plugins.rs index b2edf5d6a2b..fe217a6d123 100644 --- a/src/librustdoc/plugins.rs +++ b/src/librustdoc/plugins.rs @@ -74,7 +74,6 @@ impl PluginManager { } #[cfg(target_os = "windows")] -#[cfg(stage0, target_os = "win32")] // NOTE: Remove after snapshot fn libname(mut n: String) -> String { n.push_str(".dll"); n @@ -86,8 +85,7 @@ fn libname(mut n: String) -> String { n } -#[cfg(not(stage0), not(target_os="windows"), not(target_os="macos"))] -#[cfg(stage0, not(target_os="win32"), not(target_os="macos"))] // NOTE: Remove after snapshot +#[cfg(not(target_os="windows"), not(target_os="macos"))] fn libname(n: String) -> String { let mut i = String::from_str("lib"); i.push_str(n.as_slice()); diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs index b67b3c394d6..1706ebfbcf4 100644 --- a/src/librustdoc/visit_ast.rs +++ b/src/librustdoc/visit_ast.rs @@ -317,12 +317,12 @@ impl<'a> RustdocVisitor<'a> { }; om.statics.push(s); }, - ast::ItemTrait(ref gen, _, ref tr, ref items) => { + ast::ItemTrait(ref gen, _, ref b, ref items) => { let t = Trait { name: item.ident, items: items.iter().map(|x| (*x).clone()).collect(), generics: gen.clone(), - parents: tr.iter().map(|x| (*x).clone()).collect(), + bounds: b.iter().map(|x| (*x).clone()).collect(), id: item.id, attrs: item.attrs.iter().map(|x| *x).collect(), whence: item.span, diff --git a/src/librustrt/args.rs b/src/librustrt/args.rs index 453a9307579..6ac36f8b856 100644 --- a/src/librustrt/args.rs +++ b/src/librustrt/args.rs @@ -148,7 +148,6 @@ mod imp { #[cfg(target_os = "macos")] #[cfg(target_os = "ios")] #[cfg(target_os = "windows")] -#[cfg(stage0, target_os = "win32")] // NOTE: Remove after snapshot mod imp { use core::prelude::*; use collections::vec::Vec; diff --git a/src/librustrt/exclusive.rs b/src/librustrt/exclusive.rs index 179d050f598..28514bec5b4 100644 --- a/src/librustrt/exclusive.rs +++ b/src/librustrt/exclusive.rs @@ -26,7 +26,8 @@ pub struct Exclusive<T> { data: UnsafeCell<T>, } -/// An RAII guard returned via `lock` +/// stage0 only +#[cfg(stage0)] pub struct ExclusiveGuard<'a, T> { // FIXME #12808: strange name to try to avoid interfering with // field accesses of the contained type via Deref @@ -34,6 +35,15 @@ pub struct ExclusiveGuard<'a, T> { _guard: mutex::LockGuard<'a>, } +/// An RAII guard returned via `lock` +#[cfg(not(stage0))] +pub struct ExclusiveGuard<'a, T:'a> { + // FIXME #12808: strange name to try to avoid interfering with + // field accesses of the contained type via Deref + _data: &'a mut T, + _guard: mutex::LockGuard<'a>, +} + impl<T: Send> Exclusive<T> { /// Creates a new `Exclusive` which will protect the data provided. pub fn new(user_data: T) -> Exclusive<T> { diff --git a/src/librustrt/lib.rs b/src/librustrt/lib.rs index 70ce85ee649..5ca46a728c3 100644 --- a/src/librustrt/lib.rs +++ b/src/librustrt/lib.rs @@ -19,6 +19,7 @@ #![feature(macro_rules, phase, globs, thread_local, managed_boxes, asm)] #![feature(linkage, lang_items, unsafe_destructor, default_type_params)] #![feature(import_shadowing)] +#![feature(issue_5723_bootstrap)] #![no_std] #![experimental] @@ -98,7 +99,7 @@ pub trait Runtime { fn can_block(&self) -> bool; // FIXME: This is a serious code smell and this should not exist at all. - fn wrap(self: Box<Self>) -> Box<Any>; + fn wrap(self: Box<Self>) -> Box<Any+'static>; } /// The default error code of the rust runtime if the main task fails instead diff --git a/src/librustrt/libunwind.rs b/src/librustrt/libunwind.rs index 72d78d84aa3..a43920d27a7 100644 --- a/src/librustrt/libunwind.rs +++ b/src/librustrt/libunwind.rs @@ -72,6 +72,7 @@ pub static unwinder_private_data_size: uint = 5; #[cfg(target_arch = "mipsel")] pub static unwinder_private_data_size: uint = 2; +#[repr(C)] pub struct _Unwind_Exception { pub exception_class: _Unwind_Exception_Class, pub exception_cleanup: _Unwind_Exception_Cleanup_Fn, @@ -87,7 +88,6 @@ pub type _Unwind_Exception_Cleanup_Fn = #[cfg(target_os = "linux")] #[cfg(target_os = "freebsd")] #[cfg(target_os = "windows")] -#[cfg(stage0, target_os = "win32")] // NOTE: Remove after snapshot #[link(name = "gcc_s")] extern {} diff --git a/src/librustrt/local_data.rs b/src/librustrt/local_data.rs index 6a0b599179c..fedea3c31e0 100644 --- a/src/librustrt/local_data.rs +++ b/src/librustrt/local_data.rs @@ -144,6 +144,16 @@ unsafe fn get_local_map<'a>() -> Option<&'a mut Map> { /// /// The task-local data can be accessed through this value, and when this /// structure is dropped it will return the borrow on the data. +#[cfg(not(stage0))] +pub struct Ref<T:'static> { + // FIXME #12808: strange names to try to avoid interfering with + // field accesses of the contained type via Deref + _inner: &'static TLDValueBox<T>, + _marker: marker::NoSend +} + +/// stage0 only +#[cfg(stage0)] pub struct Ref<T> { // FIXME #12808: strange names to try to avoid interfering with // field accesses of the contained type via Deref diff --git a/src/librustrt/rtio.rs b/src/librustrt/rtio.rs index 6525adf07f7..1afd88edbc2 100644 --- a/src/librustrt/rtio.rs +++ b/src/librustrt/rtio.rs @@ -120,7 +120,7 @@ pub struct ProcessConfig<'a> { } pub struct LocalIo<'a> { - factory: &'a mut IoFactory, + factory: &'a mut IoFactory+'a, } #[unsafe_destructor] @@ -174,7 +174,7 @@ impl<'a> LocalIo<'a> { } } - pub fn new<'a>(io: &'a mut IoFactory) -> LocalIo<'a> { + pub fn new<'a>(io: &'a mut IoFactory+'a) -> LocalIo<'a> { LocalIo { factory: io } } @@ -246,6 +246,8 @@ pub trait RtioTcpAcceptor : RtioSocket { fn accept_simultaneously(&mut self) -> IoResult<()>; fn dont_accept_simultaneously(&mut self) -> IoResult<()>; fn set_timeout(&mut self, timeout: Option<u64>); + fn clone(&self) -> Box<RtioTcpAcceptor + Send>; + fn close_accept(&mut self) -> IoResult<()>; } pub trait RtioTcpStream : RtioSocket { @@ -335,6 +337,8 @@ pub trait RtioUnixListener { pub trait RtioUnixAcceptor { fn accept(&mut self) -> IoResult<Box<RtioPipe + Send>>; fn set_timeout(&mut self, timeout: Option<u64>); + fn clone(&self) -> Box<RtioUnixAcceptor + Send>; + fn close_accept(&mut self) -> IoResult<()>; } pub trait RtioTTY { diff --git a/src/librustrt/stack.rs b/src/librustrt/stack.rs index 772b5da8cd5..3190e9f7841 100644 --- a/src/librustrt/stack.rs +++ b/src/librustrt/stack.rs @@ -200,7 +200,6 @@ pub unsafe fn record_sp_limit(limit: uint) { asm!("movq $0, %fs:112" :: "r"(limit) :: "volatile") } #[cfg(target_arch = "x86_64", target_os = "windows")] #[inline(always)] - #[cfg(stage0, target_arch = "x86_64", target_os = "win32")] // NOTE: Remove after snapshot unsafe fn target_record_sp_limit(limit: uint) { // see: http://en.wikipedia.org/wiki/Win32_Thread_Information_Block // store this inside of the "arbitrary data slot", but double the size @@ -229,7 +228,6 @@ pub unsafe fn record_sp_limit(limit: uint) { asm!("movl $0, %gs:48" :: "r"(limit) :: "volatile") } #[cfg(target_arch = "x86", target_os = "windows")] #[inline(always)] - #[cfg(stage0, target_arch = "x86", target_os = "win32")] // NOTE: Remove after snapshot unsafe fn target_record_sp_limit(limit: uint) { // see: http://en.wikipedia.org/wiki/Win32_Thread_Information_Block // store this inside of the "arbitrary data slot" @@ -283,7 +281,6 @@ pub unsafe fn get_sp_limit() -> uint { return limit; } #[cfg(target_arch = "x86_64", target_os = "windows")] #[inline(always)] - #[cfg(stage0, target_arch = "x86_64", target_os = "win32")] // NOTE: Remove after snapshot unsafe fn target_get_sp_limit() -> uint { let limit; asm!("movq %gs:0x28, $0" : "=r"(limit) ::: "volatile"); @@ -320,7 +317,6 @@ pub unsafe fn get_sp_limit() -> uint { return limit; } #[cfg(target_arch = "x86", target_os = "windows")] #[inline(always)] - #[cfg(stage0, target_arch = "x86", target_os = "win32")] // NOTE: Remove after snapshot unsafe fn target_get_sp_limit() -> uint { let limit; asm!("movl %fs:0x14, $0" : "=r"(limit) ::: "volatile"); diff --git a/src/librustrt/task.rs b/src/librustrt/task.rs index acd53535e3b..e39071864a7 100644 --- a/src/librustrt/task.rs +++ b/src/librustrt/task.rs @@ -103,7 +103,7 @@ pub struct Task { pub name: Option<SendStr>, state: TaskState, - imp: Option<Box<Runtime + Send>>, + imp: Option<Box<Runtime + Send + 'static>>, } // Once a task has entered the `Armed` state it must be destroyed via `drop`, @@ -353,14 +353,14 @@ impl Task { /// Inserts a runtime object into this task, transferring ownership to the /// task. It is illegal to replace a previous runtime object in this task /// with this argument. - pub fn put_runtime(&mut self, ops: Box<Runtime + Send>) { + pub fn put_runtime(&mut self, ops: Box<Runtime + Send + 'static>) { assert!(self.imp.is_none()); self.imp = Some(ops); } /// Removes the runtime from this task, transferring ownership to the /// caller. - pub fn take_runtime(&mut self) -> Box<Runtime + Send> { + pub fn take_runtime(&mut self) -> Box<Runtime + Send + 'static> { assert!(self.imp.is_some()); self.imp.take().unwrap() } @@ -390,7 +390,7 @@ impl Task { Ok(t) => Some(t), Err(t) => { let data = mem::transmute::<_, raw::TraitObject>(t).data; - let obj: Box<Runtime + Send> = + let obj: Box<Runtime + Send + 'static> = mem::transmute(raw::TraitObject { vtable: vtable, data: data, diff --git a/src/librustrt/unwind.rs b/src/librustrt/unwind.rs index 204128cf6be..08e182a6d6e 100644 --- a/src/librustrt/unwind.rs +++ b/src/librustrt/unwind.rs @@ -333,7 +333,10 @@ pub mod eabi { uw::_URC_HANDLER_FOUND // catch! } else { // cleanup phase - uw::_URC_INSTALL_CONTEXT + unsafe { + __gcc_personality_sj0(_version, actions, _exception_class, _ue_header, + _context) + } } } } @@ -399,8 +402,11 @@ pub mod eabi { use libunwind as uw; use libc::{c_void, c_int}; + #[repr(C)] struct EXCEPTION_RECORD; + #[repr(C)] struct CONTEXT; + #[repr(C)] struct DISPATCHER_CONTEXT; #[repr(C)] diff --git a/src/librustuv/access.rs b/src/librustuv/access.rs index 9bd8af6419e..0fa89ce989a 100644 --- a/src/librustuv/access.rs +++ b/src/librustuv/access.rs @@ -22,38 +22,47 @@ use std::cell::UnsafeCell; use homing::HomingMissile; -pub struct Access { - inner: Arc<UnsafeCell<Inner>>, +pub struct Access<T> { + inner: Arc<UnsafeCell<Inner<T>>>, } -pub struct Guard<'a> { - access: &'a mut Access, +#[cfg(stage0)] +pub struct Guard<'a, T> { + access: &'a mut Access<T>, missile: Option<HomingMissile>, } -struct Inner { +#[cfg(not(stage0))] +pub struct Guard<'a, T:'static> { + access: &'a mut Access<T>, + missile: Option<HomingMissile>, +} + +struct Inner<T> { queue: Vec<(BlockedTask, uint)>, held: bool, closed: bool, + data: T, } -impl Access { - pub fn new() -> Access { +impl<T: Send> Access<T> { + pub fn new(data: T) -> Access<T> { Access { inner: Arc::new(UnsafeCell::new(Inner { queue: vec![], held: false, closed: false, + data: data, })) } } pub fn grant<'a>(&'a mut self, token: uint, - missile: HomingMissile) -> Guard<'a> { + missile: HomingMissile) -> Guard<'a, T> { // This unsafety is actually OK because the homing missile argument // guarantees that we're on the same event loop as all the other objects // attempting to get access granted. - let inner: &mut Inner = unsafe { &mut *self.inner.get() }; + let inner = unsafe { &mut *self.inner.get() }; if inner.held { let t: Box<Task> = Local::take(); @@ -69,6 +78,15 @@ impl Access { Guard { access: self, missile: Some(missile) } } + pub fn unsafe_get(&self) -> *mut T { + unsafe { &mut (*self.inner.get()).data as *mut _ } + } + + // Safe version which requires proof that you are on the home scheduler. + pub fn get_mut<'a>(&'a mut self, _missile: &HomingMissile) -> &'a mut T { + unsafe { &mut *self.unsafe_get() } + } + pub fn close(&self, _missile: &HomingMissile) { // This unsafety is OK because with a homing missile we're guaranteed to // be the only task looking at the `closed` flag (and are therefore @@ -82,21 +100,27 @@ impl Access { // is only safe to invoke while on the home event loop, and there is no // guarantee that this i being invoked on the home event loop. pub unsafe fn dequeue(&mut self, token: uint) -> Option<BlockedTask> { - let inner: &mut Inner = &mut *self.inner.get(); + let inner = &mut *self.inner.get(); match inner.queue.iter().position(|&(_, t)| t == token) { Some(i) => Some(inner.queue.remove(i).unwrap().val0()), None => None, } } + + /// Test whether this access is closed, using a homing missile to prove + /// that it's safe + pub fn is_closed(&self, _missile: &HomingMissile) -> bool { + unsafe { (*self.inner.get()).closed } + } } -impl Clone for Access { - fn clone(&self) -> Access { +impl<T: Send> Clone for Access<T> { + fn clone(&self) -> Access<T> { Access { inner: self.inner.clone() } } } -impl<'a> Guard<'a> { +impl<'a, T: Send> Guard<'a, T> { pub fn is_closed(&self) -> bool { // See above for why this unsafety is ok, it just applies to the read // instead of the write. @@ -104,13 +128,27 @@ impl<'a> Guard<'a> { } } +impl<'a, T: Send> Deref<T> for Guard<'a, T> { + fn deref<'a>(&'a self) -> &'a T { + // A guard represents exclusive access to a piece of data, so it's safe + // to hand out shared and mutable references + unsafe { &(*self.access.inner.get()).data } + } +} + +impl<'a, T: Send> DerefMut<T> for Guard<'a, T> { + fn deref_mut<'a>(&'a mut self) -> &'a mut T { + unsafe { &mut (*self.access.inner.get()).data } + } +} + #[unsafe_destructor] -impl<'a> Drop for Guard<'a> { +impl<'a, T> Drop for Guard<'a, T> { fn drop(&mut self) { // This guard's homing missile is still armed, so we're guaranteed to be // on the same I/O event loop, so this unsafety should be ok. assert!(self.missile.is_some()); - let inner: &mut Inner = unsafe { + let inner: &mut Inner<T> = unsafe { mem::transmute(self.access.inner.get()) }; @@ -133,7 +171,8 @@ impl<'a> Drop for Guard<'a> { } } -impl Drop for Inner { +#[unsafe_destructor] +impl<T> Drop for Inner<T> { fn drop(&mut self) { assert!(!self.held); assert_eq!(self.queue.len(), 0); diff --git a/src/librustuv/homing.rs b/src/librustuv/homing.rs index 91614763ce5..55d9811ccad 100644 --- a/src/librustuv/homing.rs +++ b/src/librustuv/homing.rs @@ -72,13 +72,15 @@ impl Clone for HomeHandle { } pub fn local_id() -> uint { + use std::raw::TraitObject; + let mut io = match LocalIo::borrow() { Some(io) => io, None => return 0, }; let io = io.get(); unsafe { - let (_vtable, ptr): (uint, uint) = mem::transmute(io); - return ptr; + let obj: TraitObject = mem::transmute(io); + return mem::transmute(obj.data); } } diff --git a/src/librustuv/lib.rs b/src/librustuv/lib.rs index dd80ab3ee78..6e948992979 100644 --- a/src/librustuv/lib.rs +++ b/src/librustuv/lib.rs @@ -462,13 +462,14 @@ pub fn slice_to_uv_buf(v: &[u8]) -> Buf { // This function is full of lies! #[cfg(test)] fn local_loop() -> &'static mut uvio::UvIoFactory { + use std::raw::TraitObject; unsafe { mem::transmute({ let mut task = Local::borrow(None::<Task>); let mut io = task.local_io().unwrap(); - let (_vtable, uvio): (uint, &'static mut uvio::UvIoFactory) = + let obj: TraitObject = mem::transmute(io.get()); - uvio + obj.data }) } } diff --git a/src/librustuv/net.rs b/src/librustuv/net.rs index 0da8d0d2108..84ef9deaf92 100644 --- a/src/librustuv/net.rs +++ b/src/librustuv/net.rs @@ -22,7 +22,7 @@ use stream::StreamWatcher; use super::{Loop, Request, UvError, Buf, status_to_io_result, uv_error_to_io_error, UvHandle, slice_to_uv_buf, wait_until_woken_after, wakeup}; -use timeout::{AccessTimeout, AcceptTimeout, ConnectCtx}; +use timeout::{AccessTimeout, ConnectCtx, AcceptTimeout}; use uvio::UvIoFactory; use uvll; @@ -158,20 +158,20 @@ pub struct TcpWatcher { // stream object, so we use these access guards in order to arbitrate among // multiple concurrent reads and writes. Note that libuv *can* read and // write simultaneously, it just can't read and read simultaneously. - read_access: AccessTimeout, - write_access: AccessTimeout, + read_access: AccessTimeout<()>, + write_access: AccessTimeout<()>, } pub struct TcpListener { home: HomeHandle, - handle: *mut uvll::uv_pipe_t, - outgoing: Sender<Result<Box<rtio::RtioTcpStream + Send>, IoError>>, - incoming: Receiver<Result<Box<rtio::RtioTcpStream + Send>, IoError>>, + handle: *mut uvll::uv_tcp_t, } pub struct TcpAcceptor { - listener: Box<TcpListener>, - timeout: AcceptTimeout, + home: HomeHandle, + handle: *mut uvll::uv_tcp_t, + access: AcceptTimeout<Box<rtio::RtioTcpStream + Send>>, + refcount: Refcount, } // TCP watchers (clients/streams) @@ -192,8 +192,8 @@ impl TcpWatcher { handle: handle, stream: StreamWatcher::new(handle, true), refcount: Refcount::new(), - read_access: AccessTimeout::new(), - write_access: AccessTimeout::new(), + read_access: AccessTimeout::new(()), + write_access: AccessTimeout::new(()), } } @@ -291,7 +291,7 @@ impl rtio::RtioTcpStream for TcpWatcher { let task = { let m = self.fire_homing_missile(); self.read_access.access.close(&m); - self.stream.cancel_read(uvll::EOF as libc::ssize_t) + self.stream.cancel_read(uvll::EOF as libc::ssize_t) }; let _ = task.map(|t| t.reawaken()); Ok(()) @@ -354,12 +354,9 @@ impl TcpListener { assert_eq!(unsafe { uvll::uv_tcp_init(io.uv_loop(), handle) }, 0); - let (tx, rx) = channel(); let l = box TcpListener { home: io.make_handle(), handle: handle, - outgoing: tx, - incoming: rx, }; let mut storage = unsafe { mem::zeroed() }; let _len = addr_to_sockaddr(address, &mut storage); @@ -390,17 +387,21 @@ impl rtio::RtioSocket for TcpListener { } impl rtio::RtioTcpListener for TcpListener { - fn listen(self: Box<TcpListener>) + fn listen(mut self: Box<TcpListener>) -> Result<Box<rtio::RtioTcpAcceptor + Send>, IoError> { + let _m = self.fire_homing_missile(); + // create the acceptor object from ourselves - let mut acceptor = box TcpAcceptor { - listener: self, - timeout: AcceptTimeout::new(), - }; + let acceptor = (box TcpAcceptor { + handle: self.handle, + home: self.home.clone(), + access: AcceptTimeout::new(), + refcount: Refcount::new(), + }).install(); + self.handle = 0 as *mut _; - let _m = acceptor.fire_homing_missile(); // FIXME: the 128 backlog should be configurable - match unsafe { uvll::uv_listen(acceptor.listener.handle, 128, listen_cb) } { + match unsafe { uvll::uv_listen(acceptor.handle, 128, listen_cb) } { 0 => Ok(acceptor as Box<rtio::RtioTcpAcceptor + Send>), n => Err(uv_error_to_io_error(UvError(n))), } @@ -409,7 +410,7 @@ impl rtio::RtioTcpListener for TcpListener { extern fn listen_cb(server: *mut uvll::uv_stream_t, status: c_int) { assert!(status != uvll::ECANCELED); - let tcp: &mut TcpListener = unsafe { UvHandle::from_uv_handle(&server) }; + let tcp: &mut TcpAcceptor = unsafe { UvHandle::from_uv_handle(&server) }; let msg = match status { 0 => { let loop_ = Loop::wrap(unsafe { @@ -421,11 +422,15 @@ extern fn listen_cb(server: *mut uvll::uv_stream_t, status: c_int) { } n => Err(uv_error_to_io_error(UvError(n))) }; - tcp.outgoing.send(msg); + + // If we're running then we have exclusive access, so the unsafe_get() is ok + unsafe { tcp.access.push(msg); } } impl Drop for TcpListener { fn drop(&mut self) { + if self.handle.is_null() { return } + let _m = self.fire_homing_missile(); self.close(); } @@ -434,40 +439,68 @@ impl Drop for TcpListener { // TCP acceptors (bound servers) impl HomingIO for TcpAcceptor { - fn home<'r>(&'r mut self) -> &'r mut HomeHandle { self.listener.home() } + fn home<'r>(&'r mut self) -> &'r mut HomeHandle { &mut self.home } } impl rtio::RtioSocket for TcpAcceptor { fn socket_name(&mut self) -> Result<rtio::SocketAddr, IoError> { let _m = self.fire_homing_missile(); - socket_name(Tcp, self.listener.handle) + socket_name(Tcp, self.handle) } } +impl UvHandle<uvll::uv_tcp_t> for TcpAcceptor { + fn uv_handle(&self) -> *mut uvll::uv_tcp_t { self.handle } +} + impl rtio::RtioTcpAcceptor for TcpAcceptor { fn accept(&mut self) -> Result<Box<rtio::RtioTcpStream + Send>, IoError> { - self.timeout.accept(&self.listener.incoming) + let m = self.fire_homing_missile(); + let loop_ = self.uv_loop(); + self.access.accept(m, &loop_) } fn accept_simultaneously(&mut self) -> Result<(), IoError> { let _m = self.fire_homing_missile(); status_to_io_result(unsafe { - uvll::uv_tcp_simultaneous_accepts(self.listener.handle, 1) + uvll::uv_tcp_simultaneous_accepts(self.handle, 1) }) } fn dont_accept_simultaneously(&mut self) -> Result<(), IoError> { let _m = self.fire_homing_missile(); status_to_io_result(unsafe { - uvll::uv_tcp_simultaneous_accepts(self.listener.handle, 0) + uvll::uv_tcp_simultaneous_accepts(self.handle, 0) }) } fn set_timeout(&mut self, ms: Option<u64>) { let _m = self.fire_homing_missile(); - match ms { - None => self.timeout.clear(), - Some(ms) => self.timeout.set_timeout(ms, &mut *self.listener), + let loop_ = self.uv_loop(); + self.access.set_timeout(ms, &loop_, &self.home); + } + + fn clone(&self) -> Box<rtio::RtioTcpAcceptor + Send> { + box TcpAcceptor { + refcount: self.refcount.clone(), + home: self.home.clone(), + handle: self.handle, + access: self.access.clone(), + } as Box<rtio::RtioTcpAcceptor + Send> + } + + fn close_accept(&mut self) -> Result<(), IoError> { + let m = self.fire_homing_missile(); + self.access.close(m); + Ok(()) + } +} + +impl Drop for TcpAcceptor { + fn drop(&mut self) { + let _m = self.fire_homing_missile(); + if self.refcount.decrement() { + self.close(); } } } @@ -482,8 +515,8 @@ pub struct UdpWatcher { // See above for what these fields are refcount: Refcount, - read_access: AccessTimeout, - write_access: AccessTimeout, + read_access: AccessTimeout<()>, + write_access: AccessTimeout<()>, blocked_sender: Option<BlockedTask>, } @@ -507,8 +540,8 @@ impl UdpWatcher { handle: unsafe { uvll::malloc_handle(uvll::UV_UDP) }, home: io.make_handle(), refcount: Refcount::new(), - read_access: AccessTimeout::new(), - write_access: AccessTimeout::new(), + read_access: AccessTimeout::new(()), + write_access: AccessTimeout::new(()), blocked_sender: None, }; assert_eq!(unsafe { diff --git a/src/librustuv/pipe.rs b/src/librustuv/pipe.rs index f0a57546ed4..9ece6525e1e 100644 --- a/src/librustuv/pipe.rs +++ b/src/librustuv/pipe.rs @@ -31,20 +31,20 @@ pub struct PipeWatcher { refcount: Refcount, // see comments in TcpWatcher for why these exist - write_access: AccessTimeout, - read_access: AccessTimeout, + write_access: AccessTimeout<()>, + read_access: AccessTimeout<()>, } pub struct PipeListener { home: HomeHandle, pipe: *mut uvll::uv_pipe_t, - outgoing: Sender<IoResult<Box<rtio::RtioPipe + Send>>>, - incoming: Receiver<IoResult<Box<rtio::RtioPipe + Send>>>, } pub struct PipeAcceptor { - listener: Box<PipeListener>, - timeout: AcceptTimeout, + home: HomeHandle, + handle: *mut uvll::uv_pipe_t, + access: AcceptTimeout<Box<rtio::RtioPipe + Send>>, + refcount: Refcount, } // PipeWatcher implementation and traits @@ -71,8 +71,8 @@ impl PipeWatcher { home: home, defused: false, refcount: Refcount::new(), - read_access: AccessTimeout::new(), - write_access: AccessTimeout::new(), + read_access: AccessTimeout::new(()), + write_access: AccessTimeout::new(()), } } @@ -233,12 +233,9 @@ impl PipeListener { // If successful, unwrap the PipeWatcher because we control how // we close the pipe differently. We can't rely on // StreamWatcher's default close method. - let (tx, rx) = channel(); let p = box PipeListener { home: io.make_handle(), pipe: pipe.unwrap(), - incoming: rx, - outgoing: tx, }; Ok(p.install()) } @@ -248,17 +245,21 @@ impl PipeListener { } impl rtio::RtioUnixListener for PipeListener { - fn listen(self: Box<PipeListener>) + fn listen(mut self: Box<PipeListener>) -> IoResult<Box<rtio::RtioUnixAcceptor + Send>> { + let _m = self.fire_homing_missile(); + // create the acceptor object from ourselves - let mut acceptor = box PipeAcceptor { - listener: self, - timeout: AcceptTimeout::new(), - }; + let acceptor = (box PipeAcceptor { + handle: self.pipe, + home: self.home.clone(), + access: AcceptTimeout::new(), + refcount: Refcount::new(), + }).install(); + self.pipe = 0 as *mut _; - let _m = acceptor.fire_homing_missile(); // FIXME: the 128 backlog should be configurable - match unsafe { uvll::uv_listen(acceptor.listener.pipe, 128, listen_cb) } { + match unsafe { uvll::uv_listen(acceptor.handle, 128, listen_cb) } { 0 => Ok(acceptor as Box<rtio::RtioUnixAcceptor + Send>), n => Err(uv_error_to_io_error(UvError(n))), } @@ -276,7 +277,7 @@ impl UvHandle<uvll::uv_pipe_t> for PipeListener { extern fn listen_cb(server: *mut uvll::uv_stream_t, status: libc::c_int) { assert!(status != uvll::ECANCELED); - let pipe: &mut PipeListener = unsafe { UvHandle::from_uv_handle(&server) }; + let pipe: &mut PipeAcceptor = unsafe { UvHandle::from_uv_handle(&server) }; let msg = match status { 0 => { let loop_ = Loop::wrap(unsafe { @@ -288,11 +289,15 @@ extern fn listen_cb(server: *mut uvll::uv_stream_t, status: libc::c_int) { } n => Err(uv_error_to_io_error(UvError(n))) }; - pipe.outgoing.send(msg); + + // If we're running then we have exclusive access, so the unsafe_get() is ok + unsafe { pipe.access.push(msg); } } impl Drop for PipeListener { fn drop(&mut self) { + if self.pipe.is_null() { return } + let _m = self.fire_homing_missile(); self.close(); } @@ -302,19 +307,48 @@ impl Drop for PipeListener { impl rtio::RtioUnixAcceptor for PipeAcceptor { fn accept(&mut self) -> IoResult<Box<rtio::RtioPipe + Send>> { - self.timeout.accept(&self.listener.incoming) + let m = self.fire_homing_missile(); + let loop_ = self.uv_loop(); + self.access.accept(m, &loop_) } - fn set_timeout(&mut self, timeout_ms: Option<u64>) { - match timeout_ms { - None => self.timeout.clear(), - Some(ms) => self.timeout.set_timeout(ms, &mut *self.listener), - } + fn set_timeout(&mut self, ms: Option<u64>) { + let _m = self.fire_homing_missile(); + let loop_ = self.uv_loop(); + self.access.set_timeout(ms, &loop_, &self.home); + } + + fn clone(&self) -> Box<rtio::RtioUnixAcceptor + Send> { + box PipeAcceptor { + refcount: self.refcount.clone(), + home: self.home.clone(), + handle: self.handle, + access: self.access.clone(), + } as Box<rtio::RtioUnixAcceptor + Send> + } + + fn close_accept(&mut self) -> IoResult<()> { + let m = self.fire_homing_missile(); + self.access.close(m); + Ok(()) } } impl HomingIO for PipeAcceptor { - fn home<'r>(&'r mut self) -> &'r mut HomeHandle { &mut self.listener.home } + fn home<'r>(&'r mut self) -> &'r mut HomeHandle { &mut self.home } +} + +impl UvHandle<uvll::uv_pipe_t> for PipeAcceptor { + fn uv_handle(&self) -> *mut uvll::uv_pipe_t { self.handle } +} + +impl Drop for PipeAcceptor { + fn drop(&mut self) { + let _m = self.fire_homing_missile(); + if self.refcount.decrement() { + self.close(); + } + } } #[cfg(test)] diff --git a/src/librustuv/timeout.rs b/src/librustuv/timeout.rs index 1caaf5e0fc7..d2482ee6b60 100644 --- a/src/librustuv/timeout.rs +++ b/src/librustuv/timeout.rs @@ -14,7 +14,7 @@ use std::rt::task::BlockedTask; use std::rt::rtio::IoResult; use access; -use homing::{HomeHandle, HomingMissile, HomingIO}; +use homing::{HomeHandle, HomingMissile}; use timer::TimerWatcher; use uvll; use uvio::UvIoFactory; @@ -22,15 +22,23 @@ use {Loop, UvError, uv_error_to_io_error, Request, wakeup}; use {UvHandle, wait_until_woken_after}; /// Management of a timeout when gaining access to a portion of a duplex stream. -pub struct AccessTimeout { +pub struct AccessTimeout<T> { state: TimeoutState, timer: Option<Box<TimerWatcher>>, - pub access: access::Access, + pub access: access::Access<T>, } -pub struct Guard<'a> { +#[cfg(stage0)] +pub struct Guard<'a, T> { state: &'a mut TimeoutState, - pub access: access::Guard<'a>, + pub access: access::Guard<'a, T>, + pub can_timeout: bool, +} + +#[cfg(not(stage0))] +pub struct Guard<'a, T:'static> { + state: &'a mut TimeoutState, + pub access: access::Guard<'a, T>, pub can_timeout: bool, } @@ -49,17 +57,18 @@ enum ClientState { } struct TimerContext { - timeout: *mut AccessTimeout, - callback: fn(uint) -> Option<BlockedTask>, - payload: uint, + timeout: *mut AccessTimeout<()>, + callback: fn(*mut AccessTimeout<()>, &TimerContext), + user_unblock: fn(uint) -> Option<BlockedTask>, + user_payload: uint, } -impl AccessTimeout { - pub fn new() -> AccessTimeout { +impl<T: Send> AccessTimeout<T> { + pub fn new(data: T) -> AccessTimeout<T> { AccessTimeout { state: NoTimeout, timer: None, - access: access::Access::new(), + access: access::Access::new(data), } } @@ -68,7 +77,7 @@ impl AccessTimeout { /// On success, Ok(Guard) is returned and access has been granted to the /// stream. If a timeout occurs, then Err is returned with an appropriate /// error. - pub fn grant<'a>(&'a mut self, m: HomingMissile) -> IoResult<Guard<'a>> { + pub fn grant<'a>(&'a mut self, m: HomingMissile) -> IoResult<Guard<'a, T>> { // First, flag that we're attempting to acquire access. This will allow // us to cancel the pending grant if we timeout out while waiting for a // grant. @@ -94,6 +103,13 @@ impl AccessTimeout { }) } + pub fn timed_out(&self) -> bool { + match self.state { + TimedOut => true, + _ => false, + } + } + /// Sets the pending timeout to the value specified. /// /// The home/loop variables are used to construct a timer if one has not @@ -120,9 +136,10 @@ impl AccessTimeout { if self.timer.is_none() { let mut timer = box TimerWatcher::new_home(loop_, home.clone()); let mut cx = box TimerContext { - timeout: self as *mut _, - callback: cb, - payload: data, + timeout: self as *mut _ as *mut AccessTimeout<()>, + callback: real_cb::<T>, + user_unblock: cb, + user_payload: data, }; unsafe { timer.set_data(&mut *cx); @@ -135,8 +152,8 @@ impl AccessTimeout { unsafe { let cx = uvll::get_data_for_uv_handle(timer.handle); let cx = cx as *mut TimerContext; - (*cx).callback = cb; - (*cx).payload = data; + (*cx).user_unblock = cb; + (*cx).user_payload = data; } timer.stop(); timer.start(timer_cb, ms, 0); @@ -146,7 +163,12 @@ impl AccessTimeout { let cx: &TimerContext = unsafe { &*(uvll::get_data_for_uv_handle(timer) as *const TimerContext) }; - let me = unsafe { &mut *cx.timeout }; + (cx.callback)(cx.timeout, cx); + } + + fn real_cb<T: Send>(timeout: *mut AccessTimeout<()>, cx: &TimerContext) { + let timeout = timeout as *mut AccessTimeout<T>; + let me = unsafe { &mut *timeout }; match mem::replace(&mut me.state, TimedOut) { TimedOut | NoTimeout => unreachable!(), @@ -158,7 +180,7 @@ impl AccessTimeout { } } TimeoutPending(RequestPending) => { - match (cx.callback)(cx.payload) { + match (cx.user_unblock)(cx.user_payload) { Some(task) => task.reawaken(), None => unreachable!(), } @@ -168,8 +190,8 @@ impl AccessTimeout { } } -impl Clone for AccessTimeout { - fn clone(&self) -> AccessTimeout { +impl<T: Send> Clone for AccessTimeout<T> { + fn clone(&self) -> AccessTimeout<T> { AccessTimeout { access: self.access.clone(), state: NoTimeout, @@ -179,7 +201,7 @@ impl Clone for AccessTimeout { } #[unsafe_destructor] -impl<'a> Drop for Guard<'a> { +impl<'a, T> Drop for Guard<'a, T> { fn drop(&mut self) { match *self.state { TimeoutPending(NoWaiter) | TimeoutPending(AccessPending) => @@ -193,7 +215,8 @@ impl<'a> Drop for Guard<'a> { } } -impl Drop for AccessTimeout { +#[unsafe_destructor] +impl<T> Drop for AccessTimeout<T> { fn drop(&mut self) { match self.timer { Some(ref timer) => unsafe { @@ -215,12 +238,6 @@ pub struct ConnectCtx { pub timer: Option<Box<TimerWatcher>>, } -pub struct AcceptTimeout { - timer: Option<TimerWatcher>, - timeout_tx: Option<Sender<()>>, - timeout_rx: Option<Receiver<()>>, -} - impl ConnectCtx { pub fn connect<T>( mut self, obj: T, timeout: Option<u64>, io: &mut UvIoFactory, @@ -306,88 +323,97 @@ impl ConnectCtx { } } -impl AcceptTimeout { - pub fn new() -> AcceptTimeout { - AcceptTimeout { timer: None, timeout_tx: None, timeout_rx: None } - } +pub struct AcceptTimeout<T> { + access: AccessTimeout<AcceptorState<T>>, +} - pub fn accept<T: Send>(&mut self, c: &Receiver<IoResult<T>>) -> IoResult<T> { - match self.timeout_rx { - None => c.recv(), - Some(ref rx) => { - use std::comm::Select; - - // Poll the incoming channel first (don't rely on the order of - // select just yet). If someone's pending then we should return - // them immediately. - match c.try_recv() { - Ok(data) => return data, - Err(..) => {} - } +struct AcceptorState<T> { + blocked_acceptor: Option<BlockedTask>, + pending: Vec<IoResult<T>>, +} - // Use select to figure out which channel gets ready first. We - // do some custom handling of select to ensure that we never - // actually drain the timeout channel (we'll keep seeing the - // timeout message in the future). - let s = Select::new(); - let mut timeout = s.handle(rx); - let mut data = s.handle(c); - unsafe { - timeout.add(); - data.add(); - } - if s.wait() == timeout.id() { - Err(uv_error_to_io_error(UvError(uvll::ECANCELED))) - } else { - c.recv() - } - } +impl<T: Send> AcceptTimeout<T> { + pub fn new() -> AcceptTimeout<T> { + AcceptTimeout { + access: AccessTimeout::new(AcceptorState { + blocked_acceptor: None, + pending: Vec::new(), + }) } } - pub fn clear(&mut self) { - match self.timeout_rx { - Some(ref t) => { let _ = t.try_recv(); } - None => {} + pub fn accept(&mut self, + missile: HomingMissile, + loop_: &Loop) -> IoResult<T> { + // If we've timed out but we're not closed yet, poll the state of the + // queue to see if we can peel off a connection. + if self.access.timed_out() && !self.access.access.is_closed(&missile) { + let tmp = self.access.access.get_mut(&missile); + return match tmp.pending.remove(0) { + Some(msg) => msg, + None => Err(uv_error_to_io_error(UvError(uvll::ECANCELED))) + } } - match self.timer { - Some(ref mut t) => t.stop(), + + // Now that we're not polling, attempt to gain access and then peel off + // a connection. If we have no pending connections, then we need to go + // to sleep and wait for one. + // + // Note that if we're woken up for a pending connection then we're + // guaranteed that the check above will not steal our connection due to + // the single-threaded nature of the event loop. + let mut guard = try!(self.access.grant(missile)); + if guard.access.is_closed() { + return Err(uv_error_to_io_error(UvError(uvll::EOF))) + } + + match guard.access.pending.remove(0) { + Some(msg) => return msg, None => {} } + + wait_until_woken_after(&mut guard.access.blocked_acceptor, loop_, || {}); + + match guard.access.pending.remove(0) { + _ if guard.access.is_closed() => { + Err(uv_error_to_io_error(UvError(uvll::EOF))) + } + Some(msg) => msg, + None => Err(uv_error_to_io_error(UvError(uvll::ECANCELED))) + } } - pub fn set_timeout<U, T: UvHandle<U> + HomingIO>( - &mut self, ms: u64, t: &mut T - ) { - // If we have a timeout, lazily initialize the timer which will be used - // to fire when the timeout runs out. - if self.timer.is_none() { - let loop_ = Loop::wrap(unsafe { - uvll::get_loop_for_uv_handle(t.uv_handle()) - }); - let mut timer = TimerWatcher::new_home(&loop_, t.home().clone()); + pub unsafe fn push(&mut self, t: IoResult<T>) { + let state = self.access.access.unsafe_get(); + (*state).pending.push(t); + let _ = (*state).blocked_acceptor.take().map(|t| t.reawaken()); + } + + pub fn set_timeout(&mut self, + ms: Option<u64>, + loop_: &Loop, + home: &HomeHandle) { + self.access.set_timeout(ms, home, loop_, cancel_accept::<T>, + self as *mut _ as uint); + + fn cancel_accept<T: Send>(me: uint) -> Option<BlockedTask> { unsafe { - timer.set_data(self as *mut _); + let me: &mut AcceptTimeout<T> = mem::transmute(me); + (*me.access.access.unsafe_get()).blocked_acceptor.take() } - self.timer = Some(timer); } + } - // Once we've got a timer, stop any previous timeout, reset it for the - // current one, and install some new channels to send/receive data on - let timer = self.timer.get_mut_ref(); - timer.stop(); - timer.start(timer_cb, ms, 0); - let (tx, rx) = channel(); - self.timeout_tx = Some(tx); - self.timeout_rx = Some(rx); + pub fn close(&mut self, m: HomingMissile) { + self.access.access.close(&m); + let task = self.access.access.get_mut(&m).blocked_acceptor.take(); + drop(m); + let _ = task.map(|t| t.reawaken()); + } +} - extern fn timer_cb(timer: *mut uvll::uv_timer_t) { - let acceptor: &mut AcceptTimeout = unsafe { - &mut *(uvll::get_data_for_uv_handle(timer) as *mut AcceptTimeout) - }; - // This send can never fail because if this timer is active then the - // receiving channel is guaranteed to be alive - acceptor.timeout_tx.get_ref().send(()); - } +impl<T: Send> Clone for AcceptTimeout<T> { + fn clone(&self) -> AcceptTimeout<T> { + AcceptTimeout { access: self.access.clone() } } } diff --git a/src/librustuv/uvll.rs b/src/librustuv/uvll.rs index b06065659bd..c7c278675c1 100644 --- a/src/librustuv/uvll.rs +++ b/src/librustuv/uvll.rs @@ -103,6 +103,7 @@ pub type uv_buf_len_t = libc::size_t; pub type uv_buf_len_t = libc::c_ulong; // see libuv/include/uv-unix.h +#[repr(C)] #[cfg(unix)] pub struct uv_buf_t { pub base: *mut u8, @@ -114,6 +115,7 @@ pub type uv_os_socket_t = c_int; // see libuv/include/uv-win.h #[cfg(windows)] +#[repr(C)] pub struct uv_buf_t { pub len: uv_buf_len_t, pub base: *mut u8, @@ -135,6 +137,7 @@ pub enum uv_poll_event { UV_WRITABLE = 2, } +#[repr(C)] pub struct uv_process_options_t { pub exit_cb: uv_exit_cb, pub file: *const libc::c_char, @@ -178,11 +181,13 @@ pub type uv_tty_t = c_void; pub type uv_signal_t = c_void; pub type uv_shutdown_t = c_void; +#[repr(C)] pub struct uv_timespec_t { pub tv_sec: libc::c_long, pub tv_nsec: libc::c_long } +#[repr(C)] pub struct uv_stat_t { pub st_dev: libc::uint64_t, pub st_mode: libc::uint64_t, @@ -373,14 +378,14 @@ pub unsafe fn free_req(v: *mut c_void) { #[test] fn handle_sanity_check() { unsafe { - assert_eq!(UV_HANDLE_TYPE_MAX as uint, rust_uv_handle_type_max()); + assert_eq!(UV_HANDLE_TYPE_MAX as libc::uintptr_t, rust_uv_handle_type_max()); } } #[test] fn request_sanity_check() { unsafe { - assert_eq!(UV_REQ_TYPE_MAX as uint, rust_uv_req_type_max()); + assert_eq!(UV_REQ_TYPE_MAX as libc::uintptr_t, rust_uv_req_type_max()); } } @@ -728,7 +733,6 @@ extern {} extern {} #[cfg(target_os = "windows")] -#[cfg(stage0, target_os = "win32")] // NOTE: Remove after snapshot #[link(name = "ws2_32")] #[link(name = "psapi")] #[link(name = "iphlpapi")] diff --git a/src/libserialize/hex.rs b/src/libserialize/hex.rs index f33ecb5f19b..1d2fcc8b77b 100644 --- a/src/libserialize/hex.rs +++ b/src/libserialize/hex.rs @@ -183,16 +183,17 @@ mod tests { #[test] pub fn test_from_hex_all_bytes() { for i in range(0u, 256) { + let ii: &[u8] = &[i as u8]; assert_eq!(format!("{:02x}", i as uint).as_slice() .from_hex() .unwrap() .as_slice(), - &[i as u8]); + ii); assert_eq!(format!("{:02X}", i as uint).as_slice() .from_hex() .unwrap() .as_slice(), - &[i as u8]); + ii); } } diff --git a/src/libserialize/json.rs b/src/libserialize/json.rs index 51b8985e655..d70b6b4d57b 100644 --- a/src/libserialize/json.rs +++ b/src/libserialize/json.rs @@ -106,7 +106,7 @@ fn main() { ## Using the `ToJson` trait -The examples above use the `ToJson` trait to generate the JSON string, which required +The examples above use the `ToJson` trait to generate the JSON string, which is required for custom mappings. ### Simple example of `ToJson` usage @@ -209,7 +209,9 @@ use Encodable; /// Represents a json value #[deriving(Clone, PartialEq, PartialOrd)] pub enum Json { - Number(f64), + I64(i64), + U64(u64), + F64(f64), String(String), Boolean(bool), List(List), @@ -380,7 +382,7 @@ fn fmt_number_or_null(v: f64) -> String { /// A structure for implementing serialization to JSON. pub struct Encoder<'a> { - writer: &'a mut io::Writer, + writer: &'a mut io::Writer+'a, } impl<'a> Encoder<'a> { @@ -592,14 +594,24 @@ impl<'a> ::Encoder<io::IoError> for Encoder<'a> { /// Another encoder for JSON, but prints out human-readable JSON instead of /// compact data pub struct PrettyEncoder<'a> { - writer: &'a mut io::Writer, + writer: &'a mut io::Writer+'a, + curr_indent: uint, indent: uint, } impl<'a> PrettyEncoder<'a> { /// Creates a new encoder whose output will be written to the specified writer pub fn new<'a>(writer: &'a mut io::Writer) -> PrettyEncoder<'a> { - PrettyEncoder { writer: writer, indent: 0 } + PrettyEncoder { writer: writer, curr_indent: 0, indent: 2, } + } + + /// Set the number of spaces to indent for each level. + /// This is safe to set during encoding. + pub fn set_indent<'a>(&mut self, indent: uint) { + // self.indent very well could be 0 so we need to use checked division. + let level = self.curr_indent.checked_div(&self.indent).unwrap_or(0); + self.indent = indent; + self.curr_indent = level * self.indent; } } @@ -654,15 +666,15 @@ impl<'a> ::Encoder<io::IoError> for PrettyEncoder<'a> { if cnt == 0 { escape_str(self.writer, name) } else { - self.indent += 2; + self.curr_indent += self.indent; try!(write!(self.writer, "[\n")); - try!(spaces(self.writer, self.indent)); + try!(spaces(self.writer, self.curr_indent)); try!(escape_str(self.writer, name)); try!(write!(self.writer, ",\n")); try!(f(self)); - self.indent -= 2; + self.curr_indent -= self.indent; try!(write!(self.writer, "\n")); - try!(spaces(self.writer, self.indent)); + try!(spaces(self.writer, self.curr_indent)); write!(self.writer, "]") } } @@ -673,7 +685,7 @@ impl<'a> ::Encoder<io::IoError> for PrettyEncoder<'a> { if idx != 0 { try!(write!(self.writer, ",\n")); } - try!(spaces(self.writer, self.indent)); + try!(spaces(self.writer, self.curr_indent)); f(self) } @@ -701,11 +713,11 @@ impl<'a> ::Encoder<io::IoError> for PrettyEncoder<'a> { write!(self.writer, "{{}}") } else { try!(write!(self.writer, "{{")); - self.indent += 2; + self.curr_indent += self.indent; try!(f(self)); - self.indent -= 2; + self.curr_indent -= self.indent; try!(write!(self.writer, "\n")); - try!(spaces(self.writer, self.indent)); + try!(spaces(self.writer, self.curr_indent)); write!(self.writer, "}}") } } @@ -719,7 +731,7 @@ impl<'a> ::Encoder<io::IoError> for PrettyEncoder<'a> { } else { try!(write!(self.writer, ",\n")); } - try!(spaces(self.writer, self.indent)); + try!(spaces(self.writer, self.curr_indent)); try!(escape_str(self.writer, name)); try!(write!(self.writer, ": ")); f(self) @@ -763,11 +775,11 @@ impl<'a> ::Encoder<io::IoError> for PrettyEncoder<'a> { write!(self.writer, "[]") } else { try!(write!(self.writer, "[")); - self.indent += 2; + self.curr_indent += self.indent; try!(f(self)); - self.indent -= 2; + self.curr_indent -= self.indent; try!(write!(self.writer, "\n")); - try!(spaces(self.writer, self.indent)); + try!(spaces(self.writer, self.curr_indent)); write!(self.writer, "]") } } @@ -780,7 +792,7 @@ impl<'a> ::Encoder<io::IoError> for PrettyEncoder<'a> { } else { try!(write!(self.writer, ",\n")); } - try!(spaces(self.writer, self.indent)); + try!(spaces(self.writer, self.curr_indent)); f(self) } @@ -791,11 +803,11 @@ impl<'a> ::Encoder<io::IoError> for PrettyEncoder<'a> { write!(self.writer, "{{}}") } else { try!(write!(self.writer, "{{")); - self.indent += 2; + self.curr_indent += self.indent; try!(f(self)); - self.indent -= 2; + self.curr_indent -= self.indent; try!(write!(self.writer, "\n")); - try!(spaces(self.writer, self.indent)); + try!(spaces(self.writer, self.curr_indent)); write!(self.writer, "}}") } } @@ -808,7 +820,7 @@ impl<'a> ::Encoder<io::IoError> for PrettyEncoder<'a> { } else { try!(write!(self.writer, ",\n")); } - try!(spaces(self.writer, self.indent)); + try!(spaces(self.writer, self.curr_indent)); // ref #12967, make sure to wrap a key in double quotes, // in the event that its of a type that omits them (eg numbers) let mut buf = MemWriter::new(); @@ -836,7 +848,9 @@ impl<'a> ::Encoder<io::IoError> for PrettyEncoder<'a> { impl<E: ::Encoder<S>, S> Encodable<E, S> for Json { fn encode(&self, e: &mut E) -> Result<(), S> { match *self { - Number(v) => v.encode(e), + I64(v) => v.encode(e), + U64(v) => v.encode(e), + F64(v) => v.encode(e), String(ref v) => v.encode(e), Boolean(v) => v.encode(e), List(ref v) => v.encode(e), @@ -958,14 +972,63 @@ impl Json { /// Returns true if the Json value is a Number. Returns false otherwise. pub fn is_number(&self) -> bool { - self.as_number().is_some() + match *self { + I64(_) | U64(_) | F64(_) => true, + _ => false, + } + } + + /// Returns true if the Json value is a i64. Returns false otherwise. + pub fn is_i64(&self) -> bool { + match *self { + I64(_) => true, + _ => false, + } + } + + /// Returns true if the Json value is a u64. Returns false otherwise. + pub fn is_u64(&self) -> bool { + match *self { + U64(_) => true, + _ => false, + } + } + + /// Returns true if the Json value is a f64. Returns false otherwise. + pub fn is_f64(&self) -> bool { + match *self { + F64(_) => true, + _ => false, + } } - /// If the Json value is a Number, returns the associated f64. + /// If the Json value is a number, return or cast it to a i64. /// Returns None otherwise. - pub fn as_number(&self) -> Option<f64> { - match self { - &Number(n) => Some(n), + pub fn as_i64(&self) -> Option<i64> { + match *self { + I64(n) => Some(n), + U64(n) => num::cast(n), + _ => None + } + } + + /// If the Json value is a number, return or cast it to a u64. + /// Returns None otherwise. + pub fn as_u64(&self) -> Option<u64> { + match *self { + I64(n) => num::cast(n), + U64(n) => Some(n), + _ => None + } + } + + /// If the Json value is a number, return or cast it to a f64. + /// Returns None otherwise. + pub fn as_f64(&self) -> Option<f64> { + match *self { + I64(n) => num::cast(n), + U64(n) => num::cast(n), + F64(n) => Some(n), _ => None } } @@ -1007,7 +1070,9 @@ pub enum JsonEvent { ListStart, ListEnd, BooleanValue(bool), - NumberValue(f64), + I64Value(i64), + U64Value(u64), + F64Value(f64), StringValue(String), NullValue, Error(ParserError), @@ -1257,29 +1322,60 @@ impl<T: Iterator<char>> Parser<T> { self.ch_is('\r') { self.bump(); } } - fn parse_number(&mut self) -> Result<f64, ParserError> { - let mut neg = 1.0; + fn parse_number(&mut self) -> JsonEvent { + let mut neg = false; if self.ch_is('-') { self.bump(); - neg = -1.0; + neg = true; } - let mut res = try!(self.parse_integer()); + let res = match self.parse_u64() { + Ok(res) => res, + Err(e) => { return Error(e); } + }; - if self.ch_is('.') { - res = try!(self.parse_decimal(res)); - } + if self.ch_is('.') || self.ch_is('e') || self.ch_is('E') { + let mut res = res as f64; - if self.ch_is('e') || self.ch_is('E') { - res = try!(self.parse_exponent(res)); - } + if self.ch_is('.') { + res = match self.parse_decimal(res) { + Ok(res) => res, + Err(e) => { return Error(e); } + }; + } + + if self.ch_is('e') || self.ch_is('E') { + res = match self.parse_exponent(res) { + Ok(res) => res, + Err(e) => { return Error(e); } + }; + } + + if neg { + res *= -1.0; + } + + F64Value(res) + } else { + if neg { + let res = -(res as i64); - Ok(neg * res) + // Make sure we didn't underflow. + if res > 0 { + Error(SyntaxError(InvalidNumber, self.line, self.col)) + } else { + I64Value(res) + } + } else { + U64Value(res) + } + } } - fn parse_integer(&mut self) -> Result<f64, ParserError> { - let mut res = 0.0; + fn parse_u64(&mut self) -> Result<u64, ParserError> { + let mut accum = 0; + let last_accum = 0; // necessary to detect overflow. match self.ch_or_null() { '0' => { @@ -1295,8 +1391,12 @@ impl<T: Iterator<char>> Parser<T> { while !self.eof() { match self.ch_or_null() { c @ '0' .. '9' => { - res *= 10.0; - res += ((c as int) - ('0' as int)) as f64; + accum *= 10; + accum += (c as u64) - ('0' as u64); + + // Detect overflow by comparing to the last value. + if accum <= last_accum { return self.error(InvalidNumber); } + self.bump(); } _ => break, @@ -1305,7 +1405,8 @@ impl<T: Iterator<char>> Parser<T> { } _ => return self.error(InvalidNumber), } - Ok(res) + + Ok(accum) } fn parse_decimal(&mut self, mut res: f64) -> Result<f64, ParserError> { @@ -1654,10 +1755,7 @@ impl<T: Iterator<char>> Parser<T> { 'n' => { self.parse_ident("ull", NullValue) } 't' => { self.parse_ident("rue", BooleanValue(true)) } 'f' => { self.parse_ident("alse", BooleanValue(false)) } - '0' .. '9' | '-' => match self.parse_number() { - Ok(f) => NumberValue(f), - Err(e) => Error(e), - }, + '0' .. '9' | '-' => self.parse_number(), '"' => match self.parse_str() { Ok(s) => StringValue(s), Err(e) => Error(e), @@ -1721,7 +1819,9 @@ impl<T: Iterator<char>> Builder<T> { fn build_value(&mut self) -> Result<Json, BuilderError> { return match self.token { Some(NullValue) => { Ok(Null) } - Some(NumberValue(n)) => { Ok(Number(n)) } + Some(I64Value(n)) => { Ok(I64(n)) } + Some(U64Value(n)) => { Ok(U64(n)) } + Some(F64Value(n)) => { Ok(F64(n)) } Some(BooleanValue(b)) => { Ok(Boolean(b)) } Some(StringValue(ref mut s)) => { let mut temp = String::new(); @@ -1836,44 +1936,84 @@ macro_rules! expect( }) ) +macro_rules! read_primitive { + ($name:ident, $ty:ty) => { + fn $name(&mut self) -> DecodeResult<$ty> { + match self.pop() { + I64(f) => { + match num::cast(f) { + Some(f) => Ok(f), + None => Err(ExpectedError("Number".to_string(), format!("{}", f))), + } + } + U64(f) => { + match num::cast(f) { + Some(f) => Ok(f), + None => Err(ExpectedError("Number".to_string(), format!("{}", f))), + } + } + F64(f) => { + match num::cast(f) { + Some(f) => Ok(f), + None => Err(ExpectedError("Number".to_string(), format!("{}", f))), + } + } + String(s) => { + // re: #12967.. a type w/ numeric keys (ie HashMap<uint, V> etc) + // is going to have a string here, as per JSON spec. + match std::from_str::from_str(s.as_slice()) { + Some(f) => Ok(f), + None => Err(ExpectedError("Number".to_string(), s)), + } + }, + value => Err(ExpectedError("Number".to_string(), format!("{}", value))) + } + } + } +} + impl ::Decoder<DecoderError> for Decoder { fn read_nil(&mut self) -> DecodeResult<()> { debug!("read_nil"); expect!(self.pop(), Null) } - fn read_u64(&mut self) -> DecodeResult<u64 > { Ok(try!(self.read_f64()) as u64) } - fn read_u32(&mut self) -> DecodeResult<u32 > { Ok(try!(self.read_f64()) as u32) } - fn read_u16(&mut self) -> DecodeResult<u16 > { Ok(try!(self.read_f64()) as u16) } - fn read_u8 (&mut self) -> DecodeResult<u8 > { Ok(try!(self.read_f64()) as u8) } - fn read_uint(&mut self) -> DecodeResult<uint> { Ok(try!(self.read_f64()) as uint) } + read_primitive!(read_uint, uint) + read_primitive!(read_u8, u8) + read_primitive!(read_u16, u16) + read_primitive!(read_u32, u32) + read_primitive!(read_u64, u64) + read_primitive!(read_int, int) + read_primitive!(read_i8, i8) + read_primitive!(read_i16, i16) + read_primitive!(read_i32, i32) + read_primitive!(read_i64, i64) - fn read_i64(&mut self) -> DecodeResult<i64> { Ok(try!(self.read_f64()) as i64) } - fn read_i32(&mut self) -> DecodeResult<i32> { Ok(try!(self.read_f64()) as i32) } - fn read_i16(&mut self) -> DecodeResult<i16> { Ok(try!(self.read_f64()) as i16) } - fn read_i8 (&mut self) -> DecodeResult<i8 > { Ok(try!(self.read_f64()) as i8) } - fn read_int(&mut self) -> DecodeResult<int> { Ok(try!(self.read_f64()) as int) } - - fn read_bool(&mut self) -> DecodeResult<bool> { - debug!("read_bool"); - expect!(self.pop(), Boolean) - } + fn read_f32(&mut self) -> DecodeResult<f32> { self.read_f64().map(|x| x as f32) } fn read_f64(&mut self) -> DecodeResult<f64> { debug!("read_f64"); match self.pop() { - Number(f) => Ok(f), + I64(f) => Ok(f as f64), + U64(f) => Ok(f as f64), + F64(f) => Ok(f), String(s) => { // re: #12967.. a type w/ numeric keys (ie HashMap<uint, V> etc) // is going to have a string here, as per JSON spec. - Ok(std::from_str::from_str(s.as_slice()).unwrap()) + match std::from_str::from_str(s.as_slice()) { + Some(f) => Ok(f), + None => Err(ExpectedError("Number".to_string(), s)), + } }, Null => Ok(f64::NAN), value => Err(ExpectedError("Number".to_string(), format!("{}", value))) } } - fn read_f32(&mut self) -> DecodeResult<f32> { self.read_f64().map(|x| x as f32) } + fn read_bool(&mut self) -> DecodeResult<bool> { + debug!("read_bool"); + expect!(self.pop(), Boolean) + } fn read_char(&mut self) -> DecodeResult<char> { let s = try!(self.read_str()); @@ -2084,15 +2224,25 @@ pub trait ToJson { fn to_json(&self) -> Json; } -macro_rules! to_json_impl( +macro_rules! to_json_impl_i64( ($($t:ty), +) => ( $(impl ToJson for $t { - fn to_json(&self) -> Json { Number(*self as f64) } + fn to_json(&self) -> Json { I64(*self as i64) } })+ ) ) -to_json_impl!(int, i8, i16, i32, i64, uint, u8, u16, u32, u64) +to_json_impl_i64!(int, i8, i16, i32, i64) + +macro_rules! to_json_impl_u64( + ($($t:ty), +) => ( + $(impl ToJson for $t { + fn to_json(&self) -> Json { U64(*self as u64) } + })+ + ) +) + +to_json_impl_u64!(uint, u8, u16, u32, u64) impl ToJson for Json { fn to_json(&self) -> Json { self.clone() } @@ -2106,7 +2256,7 @@ impl ToJson for f64 { fn to_json(&self) -> Json { match self.classify() { FPNaN | FPInfinite => Null, - _ => Number(*self) + _ => F64(*self) } } } @@ -2210,16 +2360,16 @@ mod tests { extern crate test; use self::test::Bencher; use {Encodable, Decodable}; - use super::{Encoder, Decoder, Error, Boolean, Number, List, String, Null, + use super::{Encoder, Decoder, Error, Boolean, I64, U64, F64, List, String, Null, PrettyEncoder, Object, Json, from_str, ParseError, ExpectedError, MissingFieldError, UnknownVariantError, DecodeResult, DecoderError, JsonEvent, Parser, StackElement, - ObjectStart, ObjectEnd, ListStart, ListEnd, BooleanValue, NumberValue, StringValue, - NullValue, SyntaxError, Key, Index, Stack, + ObjectStart, ObjectEnd, ListStart, ListEnd, BooleanValue, U64Value, + F64Value, StringValue, NullValue, SyntaxError, Key, Index, Stack, InvalidSyntax, InvalidNumber, EOFWhileParsingObject, EOFWhileParsingList, EOFWhileParsingValue, EOFWhileParsingString, KeyMustBeAString, ExpectedColon, TrailingCharacters}; - use std::{f32, f64, io}; + use std::{i64, u64, f32, f64, io}; use std::collections::TreeMap; #[deriving(PartialEq, Encodable, Decodable, Show)] @@ -2264,29 +2414,40 @@ mod tests { assert_eq!(Null.to_pretty_str().into_string(), "null".to_string()); } + #[test] + fn test_write_i64() { + assert_eq!(U64(0).to_string().into_string(), "0".to_string()); + assert_eq!(U64(0).to_pretty_str().into_string(), "0".to_string()); + + assert_eq!(U64(1234).to_string().into_string(), "1234".to_string()); + assert_eq!(U64(1234).to_pretty_str().into_string(), "1234".to_string()); + + assert_eq!(I64(-5678).to_string().into_string(), "-5678".to_string()); + assert_eq!(I64(-5678).to_pretty_str().into_string(), "-5678".to_string()); + } #[test] - fn test_write_number() { - assert_eq!(Number(3.0).to_string().into_string(), "3".to_string()); - assert_eq!(Number(3.0).to_pretty_str().into_string(), "3".to_string()); + fn test_write_f64() { + assert_eq!(F64(3.0).to_string().into_string(), "3".to_string()); + assert_eq!(F64(3.0).to_pretty_str().into_string(), "3".to_string()); - assert_eq!(Number(3.1).to_string().into_string(), "3.1".to_string()); - assert_eq!(Number(3.1).to_pretty_str().into_string(), "3.1".to_string()); + assert_eq!(F64(3.1).to_string().into_string(), "3.1".to_string()); + assert_eq!(F64(3.1).to_pretty_str().into_string(), "3.1".to_string()); - assert_eq!(Number(-1.5).to_string().into_string(), "-1.5".to_string()); - assert_eq!(Number(-1.5).to_pretty_str().into_string(), "-1.5".to_string()); + assert_eq!(F64(-1.5).to_string().into_string(), "-1.5".to_string()); + assert_eq!(F64(-1.5).to_pretty_str().into_string(), "-1.5".to_string()); - assert_eq!(Number(0.5).to_string().into_string(), "0.5".to_string()); - assert_eq!(Number(0.5).to_pretty_str().into_string(), "0.5".to_string()); + assert_eq!(F64(0.5).to_string().into_string(), "0.5".to_string()); + assert_eq!(F64(0.5).to_pretty_str().into_string(), "0.5".to_string()); - assert_eq!(Number(f64::NAN).to_string().into_string(), "null".to_string()); - assert_eq!(Number(f64::NAN).to_pretty_str().into_string(), "null".to_string()); + assert_eq!(F64(f64::NAN).to_string().into_string(), "null".to_string()); + assert_eq!(F64(f64::NAN).to_pretty_str().into_string(), "null".to_string()); - assert_eq!(Number(f64::INFINITY).to_string().into_string(), "null".to_string()); - assert_eq!(Number(f64::INFINITY).to_pretty_str().into_string(), "null".to_string()); + assert_eq!(F64(f64::INFINITY).to_string().into_string(), "null".to_string()); + assert_eq!(F64(f64::INFINITY).to_pretty_str().into_string(), "null".to_string()); - assert_eq!(Number(f64::NEG_INFINITY).to_string().into_string(), "null".to_string()); - assert_eq!(Number(f64::NEG_INFINITY).to_pretty_str().into_string(), "null".to_string()); + assert_eq!(F64(f64::NEG_INFINITY).to_string().into_string(), "null".to_string()); + assert_eq!(F64(f64::NEG_INFINITY).to_pretty_str().into_string(), "null".to_string()); } #[test] @@ -2324,7 +2485,7 @@ mod tests { let long_test_list = List(vec![ Boolean(false), Null, - List(vec![String("foo\nbar".to_string()), Number(3.5)])]); + List(vec![String("foo\nbar".to_string()), F64(3.5)])]); assert_eq!(long_test_list.to_string().into_string(), "[false,null,[\"foo\\nbar\",3.5]]".to_string()); @@ -2539,14 +2700,21 @@ mod tests { assert_eq!(from_str("1e"), Err(SyntaxError(InvalidNumber, 1, 3))); assert_eq!(from_str("1e+"), Err(SyntaxError(InvalidNumber, 1, 4))); - assert_eq!(from_str("3"), Ok(Number(3.0))); - assert_eq!(from_str("3.1"), Ok(Number(3.1))); - assert_eq!(from_str("-1.2"), Ok(Number(-1.2))); - assert_eq!(from_str("0.4"), Ok(Number(0.4))); - assert_eq!(from_str("0.4e5"), Ok(Number(0.4e5))); - assert_eq!(from_str("0.4e+15"), Ok(Number(0.4e15))); - assert_eq!(from_str("0.4e-01"), Ok(Number(0.4e-01))); - assert_eq!(from_str(" 3 "), Ok(Number(3.0))); + assert_eq!(from_str("18446744073709551616"), Err(SyntaxError(InvalidNumber, 1, 20))); + assert_eq!(from_str("-9223372036854775809"), Err(SyntaxError(InvalidNumber, 1, 21))); + + assert_eq!(from_str("3"), Ok(U64(3))); + assert_eq!(from_str("3.1"), Ok(F64(3.1))); + assert_eq!(from_str("-1.2"), Ok(F64(-1.2))); + assert_eq!(from_str("0.4"), Ok(F64(0.4))); + assert_eq!(from_str("0.4e5"), Ok(F64(0.4e5))); + assert_eq!(from_str("0.4e+15"), Ok(F64(0.4e15))); + assert_eq!(from_str("0.4e-01"), Ok(F64(0.4e-01))); + assert_eq!(from_str(" 3 "), Ok(U64(3))); + + assert_eq!(from_str("-9223372036854775808"), Ok(I64(i64::MIN))); + assert_eq!(from_str("9223372036854775807"), Ok(U64(i64::MAX as u64))); + assert_eq!(from_str("18446744073709551615"), Ok(U64(u64::MAX))); } #[test] @@ -2571,6 +2739,18 @@ mod tests { let v: f64 = super::decode("0.4e-01").unwrap(); assert_eq!(v, 0.4e-01); + + let v: u64 = super::decode("0").unwrap(); + assert_eq!(v, 0); + + let v: u64 = super::decode("18446744073709551615").unwrap(); + assert_eq!(v, u64::MAX); + + let v: i64 = super::decode("-9223372036854775808").unwrap(); + assert_eq!(v, i64::MIN); + + let v: i64 = super::decode("9223372036854775807").unwrap(); + assert_eq!(v, i64::MAX); } #[test] @@ -2622,11 +2802,11 @@ mod tests { assert_eq!(from_str("[ false ]"), Ok(List(vec![Boolean(false)]))); assert_eq!(from_str("[null]"), Ok(List(vec![Null]))); assert_eq!(from_str("[3, 1]"), - Ok(List(vec![Number(3.0), Number(1.0)]))); + Ok(List(vec![U64(3), U64(1)]))); assert_eq!(from_str("\n[3, 2]\n"), - Ok(List(vec![Number(3.0), Number(2.0)]))); + Ok(List(vec![U64(3), U64(2)]))); assert_eq!(from_str("[2, [4, 1]]"), - Ok(List(vec![Number(2.0), List(vec![Number(4.0), Number(1.0)])]))); + Ok(List(vec![U64(2), List(vec![U64(4), U64(1)])]))); } #[test] @@ -2664,7 +2844,7 @@ mod tests { assert_eq!(from_str("{}").unwrap(), mk_object([])); assert_eq!(from_str("{\"a\": 3}").unwrap(), - mk_object([("a".to_string(), Number(3.0))])); + mk_object([("a".to_string(), U64(3))])); assert_eq!(from_str( "{ \"a\": null, \"b\" : true }").unwrap(), @@ -2678,7 +2858,7 @@ mod tests { assert_eq!(from_str( "{\"a\" : 1.0 ,\"b\": [ true ]}").unwrap(), mk_object([ - ("a".to_string(), Number(1.0)), + ("a".to_string(), F64(1.0)), ("b".to_string(), List(vec![Boolean(true)])) ])); assert_eq!(from_str( @@ -2691,7 +2871,7 @@ mod tests { ]\ }").unwrap(), mk_object([ - ("a".to_string(), Number(1.0)), + ("a".to_string(), F64(1.0)), ("b".to_string(), List(vec![ Boolean(true), String("foo\nbar".to_string()), @@ -2898,11 +3078,63 @@ mod tests { } #[test] - fn test_as_number(){ + fn test_is_i64(){ + let json_value = from_str("-12").unwrap(); + assert!(json_value.is_i64()); + + let json_value = from_str("12").unwrap(); + assert!(!json_value.is_i64()); + + let json_value = from_str("12.0").unwrap(); + assert!(!json_value.is_i64()); + } + + #[test] + fn test_is_u64(){ let json_value = from_str("12").unwrap(); - let json_num = json_value.as_number(); - let expected_num = 12f64; - assert!(json_num.is_some() && json_num.unwrap() == expected_num); + assert!(json_value.is_u64()); + + let json_value = from_str("-12").unwrap(); + assert!(!json_value.is_u64()); + + let json_value = from_str("12.0").unwrap(); + assert!(!json_value.is_u64()); + } + + #[test] + fn test_is_f64(){ + let json_value = from_str("12").unwrap(); + assert!(!json_value.is_f64()); + + let json_value = from_str("-12").unwrap(); + assert!(!json_value.is_f64()); + + let json_value = from_str("12.0").unwrap(); + assert!(json_value.is_f64()); + + let json_value = from_str("-12.0").unwrap(); + assert!(json_value.is_f64()); + } + + #[test] + fn test_as_i64(){ + let json_value = from_str("-12").unwrap(); + let json_num = json_value.as_i64(); + assert_eq!(json_num, Some(-12)); + } + + #[test] + fn test_as_u64(){ + let json_value = from_str("12").unwrap(); + let json_num = json_value.as_u64(); + assert_eq!(json_num, Some(12)); + } + + #[test] + fn test_as_f64(){ + let json_value = from_str("12.0").unwrap(); + let json_num = json_value.as_f64(); + assert_eq!(json_num, Some(12f64)); } #[test] @@ -2953,6 +3185,7 @@ mod tests { _ => {} // it parsed and we are good to go } } + #[test] fn test_prettyencode_hashmap_with_numeric_key() { use std::str::from_utf8; @@ -2973,6 +3206,64 @@ mod tests { _ => {} // it parsed and we are good to go } } + + #[test] + fn test_prettyencoder_indent_level_param() { + use std::str::from_utf8; + use std::io::MemWriter; + use std::collections::TreeMap; + + let mut tree = TreeMap::new(); + + tree.insert("hello".into_string(), String("guten tag".into_string())); + tree.insert("goodbye".into_string(), String("sayonara".into_string())); + + let json = List( + // The following layout below should look a lot like + // the pretty-printed JSON (indent * x) + vec! + ( // 0x + String("greetings".into_string()), // 1x + Object(tree), // 1x + 2x + 2x + 1x + ) // 0x + // End JSON list (7 lines) + ); + + // Helper function for counting indents + fn indents(source: &str) -> uint { + let trimmed = source.trim_left_chars(' '); + source.len() - trimmed.len() + } + + // Test up to 4 spaces of indents (more?) + for i in range(0, 4u) { + let mut writer = MemWriter::new(); + { + let ref mut encoder = PrettyEncoder::new(&mut writer); + encoder.set_indent(i); + json.encode(encoder).unwrap(); + } + + let bytes = writer.unwrap(); + let printed = from_utf8(bytes.as_slice()).unwrap(); + + // Check for indents at each line + let lines: Vec<&str> = printed.lines().collect(); + assert_eq!(lines.len(), 7); // JSON should be 7 lines + + assert_eq!(indents(lines[0]), 0 * i); // [ + assert_eq!(indents(lines[1]), 1 * i); // "greetings", + assert_eq!(indents(lines[2]), 1 * i); // { + assert_eq!(indents(lines[3]), 2 * i); // "hello": "guten tag", + assert_eq!(indents(lines[4]), 2 * i); // "goodbye": "sayonara" + assert_eq!(indents(lines[5]), 1 * i); // }, + assert_eq!(indents(lines[6]), 0 * i); // ] + + // Finally, test that the pretty-printed JSON is valid + from_str(printed).ok().expect("Pretty-printed JSON is invalid!"); + } + } + #[test] fn test_hashmap_with_numeric_key_can_handle_double_quote_delimited_key() { use std::collections::HashMap; @@ -2986,6 +3277,20 @@ mod tests { let _hm: HashMap<uint, bool> = Decodable::decode(&mut decoder).unwrap(); } + #[test] + fn test_hashmap_with_numeric_key_will_error_with_string_keys() { + use std::collections::HashMap; + use Decodable; + let json_str = "{\"a\":true}"; + let json_obj = match from_str(json_str) { + Err(_) => fail!("Unable to parse json_str: {}", json_str), + Ok(o) => o + }; + let mut decoder = Decoder::new(json_obj); + let result: Result<HashMap<uint, bool>, DecoderError> = Decodable::decode(&mut decoder); + assert_eq!(result, Err(ExpectedError("Number".to_string(), "a".to_string()))); + } + fn assert_stream_equal(src: &str, expected: Vec<(JsonEvent, Vec<StackElement>)>) { let mut parser = Parser::new(src.chars()); @@ -3007,17 +3312,17 @@ mod tests { #[ignore(cfg(target_word_size = "32"))] // FIXME(#14064) fn test_streaming_parser() { assert_stream_equal( - r#"{ "foo":"bar", "array" : [0, 1, 2,3 ,4,5], "idents":[null,true,false]}"#, + r#"{ "foo":"bar", "array" : [0, 1, 2, 3, 4, 5], "idents":[null,true,false]}"#, vec![ (ObjectStart, vec![]), (StringValue("bar".to_string()), vec![Key("foo")]), (ListStart, vec![Key("array")]), - (NumberValue(0.0), vec![Key("array"), Index(0)]), - (NumberValue(1.0), vec![Key("array"), Index(1)]), - (NumberValue(2.0), vec![Key("array"), Index(2)]), - (NumberValue(3.0), vec![Key("array"), Index(3)]), - (NumberValue(4.0), vec![Key("array"), Index(4)]), - (NumberValue(5.0), vec![Key("array"), Index(5)]), + (U64Value(0), vec![Key("array"), Index(0)]), + (U64Value(1), vec![Key("array"), Index(1)]), + (U64Value(2), vec![Key("array"), Index(2)]), + (U64Value(3), vec![Key("array"), Index(3)]), + (U64Value(4), vec![Key("array"), Index(4)]), + (U64Value(5), vec![Key("array"), Index(5)]), (ListEnd, vec![Key("array")]), (ListStart, vec![Key("idents")]), (NullValue, vec![Key("idents"), Index(0)]), @@ -3061,7 +3366,7 @@ mod tests { "{\"a\": 3}", vec![ (ObjectStart, vec![]), - (NumberValue(3.0), vec![Key("a")]), + (U64Value(3), vec![Key("a")]), (ObjectEnd, vec![]), ] ); @@ -3078,7 +3383,7 @@ mod tests { "{\"a\" : 1.0 ,\"b\": [ true ]}", vec![ (ObjectStart, vec![]), - (NumberValue(1.0), vec![Key("a")]), + (F64Value(1.0), vec![Key("a")]), (ListStart, vec![Key("b")]), (BooleanValue(true),vec![Key("b"), Index(0)]), (ListEnd, vec![Key("b")]), @@ -3096,7 +3401,7 @@ mod tests { }"#, vec![ (ObjectStart, vec![]), - (NumberValue(1.0), vec![Key("a")]), + (F64Value(1.0), vec![Key("a")]), (ListStart, vec![Key("b")]), (BooleanValue(true), vec![Key("b"), Index(0)]), (StringValue("foo\nbar".to_string()), vec![Key("b"), Index(1)]), @@ -3155,8 +3460,8 @@ mod tests { "[3, 1]", vec![ (ListStart, vec![]), - (NumberValue(3.0), vec![Index(0)]), - (NumberValue(1.0), vec![Index(1)]), + (U64Value(3), vec![Index(0)]), + (U64Value(1), vec![Index(1)]), (ListEnd, vec![]), ] ); @@ -3164,21 +3469,21 @@ mod tests { "\n[3, 2]\n", vec![ (ListStart, vec![]), - (NumberValue(3.0), vec![Index(0)]), - (NumberValue(2.0), vec![Index(1)]), + (U64Value(3), vec![Index(0)]), + (U64Value(2), vec![Index(1)]), (ListEnd, vec![]), ] ); assert_stream_equal( "[2, [4, 1]]", vec![ - (ListStart, vec![]), - (NumberValue(2.0), vec![Index(0)]), - (ListStart, vec![Index(1)]), - (NumberValue(4.0), vec![Index(1), Index(0)]), - (NumberValue(1.0), vec![Index(1), Index(1)]), - (ListEnd, vec![Index(1)]), - (ListEnd, vec![]), + (ListStart, vec![]), + (U64Value(2), vec![Index(0)]), + (ListStart, vec![Index(1)]), + (U64Value(4), vec![Index(1), Index(0)]), + (U64Value(1), vec![Index(1), Index(1)]), + (ListEnd, vec![Index(1)]), + (ListEnd, vec![]), ] ); @@ -3277,50 +3582,51 @@ mod tests { use std::collections::{HashMap,TreeMap}; use super::ToJson; - let list2 = List(vec!(Number(1.0_f64), Number(2.0_f64))); - let list3 = List(vec!(Number(1.0f64), Number(2.0f64), Number(3.0f64))); + let list2 = List(vec!(U64(1), U64(2))); + let list3 = List(vec!(U64(1), U64(2), U64(3))); let object = { let mut tree_map = TreeMap::new(); - tree_map.insert("a".to_string(), Number(1.0_f64)); - tree_map.insert("b".to_string(), Number(2.0_f64)); + tree_map.insert("a".to_string(), U64(1)); + tree_map.insert("b".to_string(), U64(2)); Object(tree_map) }; assert_eq!(list2.to_json(), list2); assert_eq!(object.to_json(), object); - assert_eq!(3_i.to_json(), Number(3.0_f64)); - assert_eq!(4_i8.to_json(), Number(4.0_f64)); - assert_eq!(5_i16.to_json(), Number(5.0_f64)); - assert_eq!(6_i32.to_json(), Number(6.0_f64)); - assert_eq!(7_i64.to_json(), Number(7.0_f64)); - assert_eq!(8_u.to_json(), Number(8.0_f64)); - assert_eq!(9_u8.to_json(), Number(9.0_f64)); - assert_eq!(10_u16.to_json(), Number(10.0_f64)); - assert_eq!(11_u32.to_json(), Number(11.0_f64)); - assert_eq!(12_u64.to_json(), Number(12.0_f64)); - assert_eq!(13.0_f32.to_json(), Number(13.0_f64)); - assert_eq!(14.0_f64.to_json(), Number(14.0_f64)); + assert_eq!(3_i.to_json(), I64(3)); + assert_eq!(4_i8.to_json(), I64(4)); + assert_eq!(5_i16.to_json(), I64(5)); + assert_eq!(6_i32.to_json(), I64(6)); + assert_eq!(7_i64.to_json(), I64(7)); + assert_eq!(8_u.to_json(), U64(8)); + assert_eq!(9_u8.to_json(), U64(9)); + assert_eq!(10_u16.to_json(), U64(10)); + assert_eq!(11_u32.to_json(), U64(11)); + assert_eq!(12_u64.to_json(), U64(12)); + assert_eq!(13.0_f32.to_json(), F64(13.0_f64)); + assert_eq!(14.0_f64.to_json(), F64(14.0_f64)); assert_eq!(().to_json(), Null); assert_eq!(f32::INFINITY.to_json(), Null); assert_eq!(f64::NAN.to_json(), Null); assert_eq!(true.to_json(), Boolean(true)); assert_eq!(false.to_json(), Boolean(false)); assert_eq!("abc".to_string().to_json(), String("abc".to_string())); - assert_eq!((1i, 2i).to_json(), list2); - assert_eq!((1i, 2i, 3i).to_json(), list3); - assert_eq!([1i, 2].to_json(), list2); - assert_eq!((&[1i, 2, 3]).to_json(), list3); - assert_eq!((vec![1i, 2]).to_json(), list2); - assert_eq!(vec!(1i, 2i, 3i).to_json(), list3); + assert_eq!((1u, 2u).to_json(), list2); + assert_eq!((1u, 2u, 3u).to_json(), list3); + assert_eq!([1u, 2].to_json(), list2); + assert_eq!((&[1u, 2, 3]).to_json(), list3); + assert_eq!((vec![1u, 2]).to_json(), list2); + assert_eq!(vec!(1u, 2, 3).to_json(), list3); let mut tree_map = TreeMap::new(); - tree_map.insert("a".to_string(), 1i); + tree_map.insert("a".to_string(), 1u); tree_map.insert("b".to_string(), 2); assert_eq!(tree_map.to_json(), object); let mut hash_map = HashMap::new(); - hash_map.insert("a".to_string(), 1i); + hash_map.insert("a".to_string(), 1u); hash_map.insert("b".to_string(), 2); assert_eq!(hash_map.to_json(), object); - assert_eq!(Some(15i).to_json(), Number(15f64)); + assert_eq!(Some(15i).to_json(), I64(15)); + assert_eq!(Some(15u).to_json(), U64(15)); assert_eq!(None::<int>.to_json(), Null); } diff --git a/src/libserialize/lib.rs b/src/libserialize/lib.rs index 5c35ad85233..44ea56f4c73 100644 --- a/src/libserialize/lib.rs +++ b/src/libserialize/lib.rs @@ -24,6 +24,7 @@ Core encoding and decoding interfaces. html_root_url = "http://doc.rust-lang.org/master/", html_playground_url = "http://play.rust-lang.org/")] #![feature(macro_rules, managed_boxes, default_type_params, phase)] +#![feature(issue_5723_bootstrap)] // test harness access #[cfg(test)] diff --git a/src/libstd/ascii.rs b/src/libstd/ascii.rs index b31baa88e0c..f7b23163dfe 100644 --- a/src/libstd/ascii.rs +++ b/src/libstd/ascii.rs @@ -609,11 +609,12 @@ mod tests { #[test] fn test_ascii_vec() { let test = &[40u8, 32u8, 59u8]; - assert_eq!(test.to_ascii(), v2ascii!([40, 32, 59])); - assert_eq!("( ;".to_ascii(), v2ascii!([40, 32, 59])); + let b: &[_] = v2ascii!([40, 32, 59]); + assert_eq!(test.to_ascii(), b); + assert_eq!("( ;".to_ascii(), b); let v = vec![40u8, 32u8, 59u8]; - assert_eq!(v.as_slice().to_ascii(), v2ascii!([40, 32, 59])); - assert_eq!("( ;".to_string().as_slice().to_ascii(), v2ascii!([40, 32, 59])); + assert_eq!(v.as_slice().to_ascii(), b); + assert_eq!("( ;".to_string().as_slice().to_ascii(), b); assert_eq!("abCDef&?#".to_ascii().to_lower().into_string(), "abcdef&?#".to_string()); assert_eq!("abCDef&?#".to_ascii().to_upper().into_string(), "ABCDEF&?#".to_string()); @@ -688,13 +689,12 @@ mod tests { assert_eq!((test1).to_ascii_opt(), None); let v = [40u8, 32u8, 59u8]; - let v2 = v2ascii!(&[40, 32, 59]); + let v2: &[_] = v2ascii!(&[40, 32, 59]); assert_eq!(v.to_ascii_opt(), Some(v2)); let v = [127u8, 128u8, 255u8]; assert_eq!(v.to_ascii_opt(), None); let v = "( ;"; - let v2 = v2ascii!(&[40, 32, 59]); assert_eq!(v.to_ascii_opt(), Some(v2)); assert_eq!("zoä华".to_ascii_opt(), None); diff --git a/src/libstd/collections/hashmap.rs b/src/libstd/collections/hashmap.rs index b8f8bd41a2d..714712d9eba 100644 --- a/src/libstd/collections/hashmap.rs +++ b/src/libstd/collections/hashmap.rs @@ -409,20 +409,38 @@ mod table { assert_eq!(size_of::<SafeHash>(), size_of::<u64>()) } - /// Iterator over shared references to entries in a table. + /// Note: stage0-specific version that lacks bound. + #[cfg(stage0)] pub struct Entries<'a, K, V> { table: &'a RawTable<K, V>, idx: uint, elems_seen: uint, } - /// Iterator over mutable references to entries in a table. + /// Iterator over shared references to entries in a table. + #[cfg(not(stage0))] + pub struct Entries<'a, K:'a, V:'a> { + table: &'a RawTable<K, V>, + idx: uint, + elems_seen: uint, + } + + /// Note: stage0-specific version that lacks bound. + #[cfg(stage0)] pub struct MutEntries<'a, K, V> { table: &'a mut RawTable<K, V>, idx: uint, elems_seen: uint, } + /// Iterator over mutable references to entries in a table. + #[cfg(not(stage0))] + pub struct MutEntries<'a, K:'a, V:'a> { + table: &'a mut RawTable<K, V>, + idx: uint, + elems_seen: uint, + } + /// Iterator over the entries in a table, consuming the table. pub struct MoveEntries<K, V> { table: RawTable<K, V>, diff --git a/src/libstd/dynamic_lib.rs b/src/libstd/dynamic_lib.rs index fb951d45c42..16c00d76c54 100644 --- a/src/libstd/dynamic_lib.rs +++ b/src/libstd/dynamic_lib.rs @@ -279,7 +279,6 @@ pub mod dl { } #[cfg(target_os = "windows")] -#[cfg(stage0, target_os = "win32")] // NOTE: Remove after snapshot pub mod dl { use c_str::ToCStr; use iter::Iterator; diff --git a/src/libstd/io/buffered.rs b/src/libstd/io/buffered.rs index a9b0b33c59a..1d638e498d4 100644 --- a/src/libstd/io/buffered.rs +++ b/src/libstd/io/buffered.rs @@ -415,21 +415,25 @@ mod test { let mut buf = [0, 0, 0]; let nread = reader.read(buf); assert_eq!(Ok(2), nread); - assert_eq!(buf.as_slice(), &[0, 1, 0]); + let b: &[_] = &[0, 1, 0]; + assert_eq!(buf.as_slice(), b); let mut buf = [0]; let nread = reader.read(buf); assert_eq!(Ok(1), nread); - assert_eq!(buf.as_slice(), &[2]); + let b: &[_] = &[2]; + assert_eq!(buf.as_slice(), b); let mut buf = [0, 0, 0]; let nread = reader.read(buf); assert_eq!(Ok(1), nread); - assert_eq!(buf.as_slice(), &[3, 0, 0]); + let b: &[_] = &[3, 0, 0]; + assert_eq!(buf.as_slice(), b); let nread = reader.read(buf); assert_eq!(Ok(1), nread); - assert_eq!(buf.as_slice(), &[4, 0, 0]); + let b: &[_] = &[4, 0, 0]; + assert_eq!(buf.as_slice(), b); assert!(reader.read(buf).is_err()); } @@ -440,35 +444,41 @@ mod test { let mut writer = BufferedWriter::with_capacity(2, inner); writer.write([0, 1]).unwrap(); - assert_eq!(writer.get_ref().get_ref(), &[]); + let b: &[_] = &[]; + assert_eq!(writer.get_ref().get_ref(), b); writer.write([2]).unwrap(); - assert_eq!(writer.get_ref().get_ref(), &[0, 1]); + let b: &[_] = &[0, 1]; + assert_eq!(writer.get_ref().get_ref(), b); writer.write([3]).unwrap(); - assert_eq!(writer.get_ref().get_ref(), &[0, 1]); + assert_eq!(writer.get_ref().get_ref(), b); writer.flush().unwrap(); - assert_eq!(&[0, 1, 2, 3], writer.get_ref().get_ref()); + let a: &[_] = &[0, 1, 2, 3]; + assert_eq!(a, writer.get_ref().get_ref()); writer.write([4]).unwrap(); writer.write([5]).unwrap(); - assert_eq!(&[0, 1, 2, 3], writer.get_ref().get_ref()); + assert_eq!(a, writer.get_ref().get_ref()); writer.write([6]).unwrap(); - assert_eq!(&[0, 1, 2, 3, 4, 5], + let a: &[_] = &[0, 1, 2, 3, 4, 5]; + assert_eq!(a, writer.get_ref().get_ref()); writer.write([7, 8]).unwrap(); - assert_eq!(&[0, 1, 2, 3, 4, 5, 6], + let a: &[_] = &[0, 1, 2, 3, 4, 5, 6]; + assert_eq!(a, writer.get_ref().get_ref()); writer.write([9, 10, 11]).unwrap(); - assert_eq!(&[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], + let a: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]; + assert_eq!(a, writer.get_ref().get_ref()); writer.flush().unwrap(); - assert_eq!(&[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], + assert_eq!(a, writer.get_ref().get_ref()); } @@ -476,9 +486,11 @@ mod test { fn test_buffered_writer_inner_flushes() { let mut w = BufferedWriter::with_capacity(3, MemWriter::new()); w.write([0, 1]).unwrap(); - assert_eq!(&[], w.get_ref().get_ref()); + let a: &[_] = &[]; + assert_eq!(a, w.get_ref().get_ref()); let w = w.unwrap(); - assert_eq!(&[0, 1], w.get_ref()); + let a: &[_] = &[0, 1]; + assert_eq!(a, w.get_ref()); } // This is just here to make sure that we don't infinite loop in the @@ -519,20 +531,22 @@ mod test { fn test_line_buffer() { let mut writer = LineBufferedWriter::new(MemWriter::new()); writer.write([0]).unwrap(); - assert_eq!(writer.get_ref().get_ref(), &[]); + let b: &[_] = &[]; + assert_eq!(writer.get_ref().get_ref(), b); writer.write([1]).unwrap(); - assert_eq!(writer.get_ref().get_ref(), &[]); + assert_eq!(writer.get_ref().get_ref(), b); writer.flush().unwrap(); - assert_eq!(writer.get_ref().get_ref(), &[0, 1]); + let b: &[_] = &[0, 1]; + assert_eq!(writer.get_ref().get_ref(), b); writer.write([0, b'\n', 1, b'\n', 2]).unwrap(); - assert_eq!(writer.get_ref().get_ref(), - &[0, 1, 0, b'\n', 1, b'\n']); + let b: &[_] = &[0, 1, 0, b'\n', 1, b'\n']; + assert_eq!(writer.get_ref().get_ref(), b); writer.flush().unwrap(); - assert_eq!(writer.get_ref().get_ref(), - &[0, 1, 0, b'\n', 1, b'\n', 2]); + let b: &[_] = &[0, 1, 0, b'\n', 1, b'\n', 2]; + assert_eq!(writer.get_ref().get_ref(), b); writer.write([3, b'\n']).unwrap(); - assert_eq!(writer.get_ref().get_ref(), - &[0, 1, 0, b'\n', 1, b'\n', 2, 3, b'\n']); + let b: &[_] = &[0, 1, 0, b'\n', 1, b'\n', 2, 3, b'\n']; + assert_eq!(writer.get_ref().get_ref(), b); } #[test] diff --git a/src/libstd/io/comm_adapters.rs b/src/libstd/io/comm_adapters.rs index 53b5fbe3894..1c8b047f56f 100644 --- a/src/libstd/io/comm_adapters.rs +++ b/src/libstd/io/comm_adapters.rs @@ -154,26 +154,29 @@ mod test { assert_eq!(Ok(0), reader.read([])); assert_eq!(Ok(3), reader.read(buf)); - assert_eq!(&[1,2,3], buf.as_slice()); + let a: &[u8] = &[1,2,3]; + assert_eq!(a, buf.as_slice()); assert_eq!(Ok(3), reader.read(buf)); - assert_eq!(&[4,5,6], buf.as_slice()); + let a: &[u8] = &[4,5,6]; + assert_eq!(a, buf.as_slice()); assert_eq!(Ok(2), reader.read(buf)); - assert_eq!(&[7,8,6], buf.as_slice()); + let a: &[u8] = &[7,8,6]; + assert_eq!(a, buf.as_slice()); match reader.read(buf.as_mut_slice()) { Ok(..) => fail!(), Err(e) => assert_eq!(e.kind, io::EndOfFile), } - assert_eq!(&[7,8,6], buf.as_slice()); + assert_eq!(a, buf.as_slice()); // Ensure it continues to fail in the same way. match reader.read(buf.as_mut_slice()) { Ok(..) => fail!(), Err(e) => assert_eq!(e.kind, io::EndOfFile), } - assert_eq!(&[7,8,6], buf.as_slice()); + assert_eq!(a, buf.as_slice()); } #[test] diff --git a/src/libstd/io/extensions.rs b/src/libstd/io/extensions.rs index 12caa715865..ffbcdd87bfe 100644 --- a/src/libstd/io/extensions.rs +++ b/src/libstd/io/extensions.rs @@ -37,10 +37,29 @@ use ptr::RawPtr; /// /// Any error other than `EndOfFile` that is produced by the underlying Reader /// is returned by the iterator and should be handled by the caller. +#[cfg(stage0)] pub struct Bytes<'r, T> { reader: &'r mut T, } +/// An iterator that reads a single byte on each iteration, +/// until `.read_byte()` returns `EndOfFile`. +/// +/// # Notes about the Iteration Protocol +/// +/// The `Bytes` may yield `None` and thus terminate +/// an iteration, but continue to yield elements if iteration +/// is attempted again. +/// +/// # Error +/// +/// Any error other than `EndOfFile` that is produced by the underlying Reader +/// is returned by the iterator and should be handled by the caller. +#[cfg(not(stage0))] +pub struct Bytes<'r, T:'r> { + reader: &'r mut T, +} + impl<'r, R: Reader> Bytes<'r, R> { /// Constructs a new byte iterator from the given Reader instance. pub fn new(r: &'r mut R) -> Bytes<'r, R> { diff --git a/src/libstd/io/mem.rs b/src/libstd/io/mem.rs index ea9d08171e6..21ab9c1fdd4 100644 --- a/src/libstd/io/mem.rs +++ b/src/libstd/io/mem.rs @@ -346,7 +346,8 @@ mod test { writer.write([0]).unwrap(); writer.write([1, 2, 3]).unwrap(); writer.write([4, 5, 6, 7]).unwrap(); - assert_eq!(writer.get_ref(), &[0, 1, 2, 3, 4, 5, 6, 7]); + let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7]; + assert_eq!(writer.get_ref(), b); } #[test] @@ -363,7 +364,8 @@ mod test { writer.write([]).unwrap(); assert_eq!(writer.tell(), Ok(8)); } - assert_eq!(buf.as_slice(), &[0, 1, 2, 3, 4, 5, 6, 7]); + let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7]; + assert_eq!(buf.as_slice(), b); } #[test] @@ -391,7 +393,8 @@ mod test { assert_eq!(writer.tell(), Ok(8)); } - assert_eq!(buf.as_slice(), &[1, 3, 2, 0, 0, 0, 0, 4]); + let b: &[_] = &[1, 3, 2, 0, 0, 0, 0, 4]; + assert_eq!(buf.as_slice(), b); } #[test] @@ -415,13 +418,16 @@ mod test { let mut buf = [0]; assert_eq!(reader.read(buf), Ok(1)); assert_eq!(reader.tell(), Ok(1)); - assert_eq!(buf.as_slice(), &[0]); + let b: &[_] = &[0]; + assert_eq!(buf.as_slice(), b); let mut buf = [0, ..4]; assert_eq!(reader.read(buf), Ok(4)); assert_eq!(reader.tell(), Ok(5)); - assert_eq!(buf.as_slice(), &[1, 2, 3, 4]); + let b: &[_] = &[1, 2, 3, 4]; + assert_eq!(buf.as_slice(), b); assert_eq!(reader.read(buf), Ok(3)); - assert_eq!(buf.slice(0, 3), &[5, 6, 7]); + let b: &[_] = &[5, 6, 7]; + assert_eq!(buf.slice(0, 3), b); assert!(reader.read(buf).is_err()); let mut reader = MemReader::new(vec!(0, 1, 2, 3, 4, 5, 6, 7)); assert_eq!(reader.read_until(3).unwrap(), vec!(0, 1, 2, 3)); @@ -439,13 +445,16 @@ mod test { let mut buf = [0]; assert_eq!(reader.read(buf), Ok(1)); assert_eq!(reader.tell(), Ok(1)); - assert_eq!(buf.as_slice(), &[0]); + let b: &[_] = &[0]; + assert_eq!(buf.as_slice(), b); let mut buf = [0, ..4]; assert_eq!(reader.read(buf), Ok(4)); assert_eq!(reader.tell(), Ok(5)); - assert_eq!(buf.as_slice(), &[1, 2, 3, 4]); + let b: &[_] = &[1, 2, 3, 4]; + assert_eq!(buf.as_slice(), b); assert_eq!(reader.read(buf), Ok(3)); - assert_eq!(buf.slice(0, 3), &[5, 6, 7]); + let b: &[_] = &[5, 6, 7]; + assert_eq!(buf.slice(0, 3), b); assert!(reader.read(buf).is_err()); let mut reader = BufReader::new(in_buf.as_slice()); assert_eq!(reader.read_until(3).unwrap(), vec!(0, 1, 2, 3)); @@ -537,13 +546,16 @@ mod test { let mut r = MemReader::new(vec![1, 2, 3, 4, 5, 6, 7, 8]); let mut buf = [0, ..3]; assert!(r.read_at_least(buf.len(), buf).is_ok()); - assert_eq!(buf.as_slice(), &[1, 2, 3]); + let b: &[_] = &[1, 2, 3]; + assert_eq!(buf.as_slice(), b); assert!(r.read_at_least(0, buf.mut_slice_to(0)).is_ok()); - assert_eq!(buf.as_slice(), &[1, 2, 3]); + assert_eq!(buf.as_slice(), b); assert!(r.read_at_least(buf.len(), buf).is_ok()); - assert_eq!(buf.as_slice(), &[4, 5, 6]); + let b: &[_] = &[4, 5, 6]; + assert_eq!(buf.as_slice(), b); assert!(r.read_at_least(buf.len(), buf).is_err()); - assert_eq!(buf.as_slice(), &[7, 8, 6]); + let b: &[_] = &[7, 8, 6]; + assert_eq!(buf.as_slice(), b); } fn do_bench_mem_writer(b: &mut Bencher, times: uint, len: uint) { diff --git a/src/libstd/io/mod.rs b/src/libstd/io/mod.rs index e93a958acc6..38aa58f1c6a 100644 --- a/src/libstd/io/mod.rs +++ b/src/libstd/io/mod.rs @@ -348,7 +348,7 @@ impl IoError { // libuv maps this error code to EISDIR. we do too. if it is found // to be incorrect, we can add in some more machinery to only // return this message when ERROR_INVALID_FUNCTION after certain - // win32 calls. + // Windows calls. libc::ERROR_INVALID_FUNCTION => (InvalidInput, "illegal operation on a directory"), @@ -945,11 +945,11 @@ pub trait Reader { } } -impl Reader for Box<Reader> { +impl Reader for Box<Reader+'static> { fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> { self.read(buf) } } -impl<'a> Reader for &'a mut Reader { +impl<'a> Reader for &'a mut Reader+'a { fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> { self.read(buf) } } @@ -976,6 +976,13 @@ unsafe fn slice_vec_capacity<'a, T>(v: &'a mut Vec<T>, start: uint, end: uint) - }) } +/// Note: stage0-specific version that lacks bound. +#[cfg(stage0)] +pub struct RefReader<'a, R> { + /// The underlying reader which this is referencing + inner: &'a mut R +} + /// A `RefReader` is a struct implementing `Reader` which contains a reference /// to another reader. This is often useful when composing streams. /// @@ -1000,7 +1007,8 @@ unsafe fn slice_vec_capacity<'a, T>(v: &'a mut Vec<T>, start: uint, end: uint) - /// /// # } /// ``` -pub struct RefReader<'a, R> { +#[cfg(not(stage0))] +pub struct RefReader<'a, R:'a> { /// The underlying reader which this is referencing inner: &'a mut R } @@ -1058,12 +1066,21 @@ pub trait Writer { /// /// This function will return any I/O error reported while formatting. fn write_fmt(&mut self, fmt: &fmt::Arguments) -> IoResult<()> { + // Note: stage0-specific version that lacks bound. + #[cfg(stage0)] + struct Adaptor<'a, T> { + inner: &'a mut T, + error: IoResult<()>, + } + // Create a shim which translates a Writer to a FormatWriter and saves // off I/O errors. instead of discarding them - struct Adaptor<'a, T> { + #[cfg(not(stage0))] + struct Adaptor<'a, T:'a> { inner: &'a mut T, error: IoResult<()>, } + impl<'a, T: Writer> fmt::FormatWriter for Adaptor<'a, T> { fn write(&mut self, bytes: &[u8]) -> fmt::Result { match self.inner.write(bytes) { @@ -1278,7 +1295,7 @@ pub trait Writer { } } -impl Writer for Box<Writer> { +impl Writer for Box<Writer+'static> { #[inline] fn write(&mut self, buf: &[u8]) -> IoResult<()> { self.write(buf) } @@ -1286,7 +1303,7 @@ impl Writer for Box<Writer> { fn flush(&mut self) -> IoResult<()> { self.flush() } } -impl<'a> Writer for &'a mut Writer { +impl<'a> Writer for &'a mut Writer+'a { #[inline] fn write(&mut self, buf: &[u8]) -> IoResult<()> { self.write(buf) } @@ -1318,11 +1335,42 @@ impl<'a> Writer for &'a mut Writer { /// println!("input processed: {}", output.unwrap()); /// # } /// ``` +#[cfg(stage0)] pub struct RefWriter<'a, W> { /// The underlying writer which this is referencing inner: &'a mut W } +/// A `RefWriter` is a struct implementing `Writer` which contains a reference +/// to another writer. This is often useful when composing streams. +/// +/// # Example +/// +/// ``` +/// # fn main() {} +/// # fn process_input<R: Reader>(r: R) {} +/// # fn foo () { +/// use std::io::util::TeeReader; +/// use std::io::{stdin, MemWriter}; +/// +/// let mut output = MemWriter::new(); +/// +/// { +/// // Don't give ownership of 'output' to the 'tee'. Instead we keep a +/// // handle to it in the outer scope +/// let mut tee = TeeReader::new(stdin(), output.by_ref()); +/// process_input(tee); +/// } +/// +/// println!("input processed: {}", output.unwrap()); +/// # } +/// ``` +#[cfg(not(stage0))] +pub struct RefWriter<'a, W:'a> { + /// The underlying writer which this is referencing + inner: &'a mut W +} + impl<'a, W: Writer> Writer for RefWriter<'a, W> { #[inline] fn write(&mut self, buf: &[u8]) -> IoResult<()> { self.inner.write(buf) } @@ -1351,10 +1399,29 @@ impl<T: Reader + Writer> Stream for T {} /// /// Any error other than `EndOfFile` that is produced by the underlying Reader /// is returned by the iterator and should be handled by the caller. +#[cfg(stage0)] pub struct Lines<'r, T> { buffer: &'r mut T, } +/// An iterator that reads a line on each iteration, +/// until `.read_line()` encounters `EndOfFile`. +/// +/// # Notes about the Iteration Protocol +/// +/// The `Lines` may yield `None` and thus terminate +/// an iteration, but continue to yield elements if iteration +/// is attempted again. +/// +/// # Error +/// +/// Any error other than `EndOfFile` that is produced by the underlying Reader +/// is returned by the iterator and should be handled by the caller. +#[cfg(not(stage0))] +pub struct Lines<'r, T:'r> { + buffer: &'r mut T, +} + impl<'r, T: Buffer> Iterator<IoResult<String>> for Lines<'r, T> { fn next(&mut self) -> Option<IoResult<String>> { match self.buffer.read_line() { @@ -1378,10 +1445,29 @@ impl<'r, T: Buffer> Iterator<IoResult<String>> for Lines<'r, T> { /// /// Any error other than `EndOfFile` that is produced by the underlying Reader /// is returned by the iterator and should be handled by the caller. +#[cfg(stage0)] pub struct Chars<'r, T> { buffer: &'r mut T } +/// An iterator that reads a utf8-encoded character on each iteration, +/// until `.read_char()` encounters `EndOfFile`. +/// +/// # Notes about the Iteration Protocol +/// +/// The `Chars` may yield `None` and thus terminate +/// an iteration, but continue to yield elements if iteration +/// is attempted again. +/// +/// # Error +/// +/// Any error other than `EndOfFile` that is produced by the underlying Reader +/// is returned by the iterator and should be handled by the caller. +#[cfg(not(stage0))] +pub struct Chars<'r, T:'r> { + buffer: &'r mut T +} + impl<'r, T: Buffer> Iterator<IoResult<char>> for Chars<'r, T> { fn next(&mut self) -> Option<IoResult<char>> { match self.buffer.read_char() { @@ -1611,6 +1697,12 @@ pub trait Acceptor<T> { } } +/// Note: stage0-specific version that lacks bound on A. +#[cfg(stage0)] +pub struct IncomingConnections<'a, A> { + inc: &'a mut A, +} + /// An infinite iterator over incoming connection attempts. /// Calling `next` will block the task until a connection is attempted. /// @@ -1618,7 +1710,8 @@ pub trait Acceptor<T> { /// `Some`. The `Some` contains the `IoResult` representing whether the /// connection attempt was successful. A successful connection will be wrapped /// in `Ok`. A failed connection is represented as an `Err`. -pub struct IncomingConnections<'a, A> { +#[cfg(not(stage0))] +pub struct IncomingConnections<'a, A:'a> { inc: &'a mut A, } diff --git a/src/libstd/io/net/tcp.rs b/src/libstd/io/net/tcp.rs index 8128c4466ce..d787f0e9854 100644 --- a/src/libstd/io/net/tcp.rs +++ b/src/libstd/io/net/tcp.rs @@ -442,6 +442,53 @@ impl TcpAcceptor { #[experimental = "the type of the argument and name of this function are \ subject to change"] pub fn set_timeout(&mut self, ms: Option<u64>) { self.obj.set_timeout(ms); } + + /// Closes the accepting capabilities of this acceptor. + /// + /// This function is similar to `TcpStream`'s `close_{read,write}` methods + /// in that it will affect *all* cloned handles of this acceptor's original + /// handle. + /// + /// Once this function succeeds, all future calls to `accept` will return + /// immediately with an error, preventing all future calls to accept. The + /// underlying socket will not be relinquished back to the OS until all + /// acceptors have been deallocated. + /// + /// This is useful for waking up a thread in an accept loop to indicate that + /// it should exit. + /// + /// # Example + /// + /// ``` + /// # #![allow(experimental)] + /// use std::io::{TcpListener, Listener, Acceptor, EndOfFile}; + /// + /// let mut a = TcpListener::bind("127.0.0.1", 8482).listen().unwrap(); + /// let a2 = a.clone(); + /// + /// spawn(proc() { + /// let mut a2 = a2; + /// for socket in a2.incoming() { + /// match socket { + /// Ok(s) => { /* handle s */ } + /// Err(ref e) if e.kind == EndOfFile => break, // closed + /// Err(e) => fail!("unexpected error: {}", e), + /// } + /// } + /// }); + /// + /// # fn wait_for_sigint() {} + /// // Now that our accept loop is running, wait for the program to be + /// // requested to exit. + /// wait_for_sigint(); + /// + /// // Signal our accept loop to exit + /// assert!(a.close_accept().is_ok()); + /// ``` + #[experimental] + pub fn close_accept(&mut self) -> IoResult<()> { + self.obj.close_accept().map_err(IoError::from_rtio_error) + } } impl Acceptor<TcpStream> for TcpAcceptor { @@ -453,6 +500,25 @@ impl Acceptor<TcpStream> for TcpAcceptor { } } +impl Clone for TcpAcceptor { + /// Creates a new handle to this TCP acceptor, allowing for simultaneous + /// accepts. + /// + /// The underlying TCP acceptor will not be closed until all handles to the + /// acceptor have been deallocated. Incoming connections will be received on + /// at most once acceptor, the same connection will not be accepted twice. + /// + /// The `close_accept` method will shut down *all* acceptors cloned from the + /// same original acceptor, whereas the `set_timeout` method only affects + /// the selector that it is called on. + /// + /// This function is useful for creating a handle to invoke `close_accept` + /// on to wake up any other task blocked in `accept`. + fn clone(&self) -> TcpAcceptor { + TcpAcceptor { obj: self.obj.clone() } + } +} + #[cfg(test)] #[allow(experimental)] mod test { @@ -1411,4 +1477,69 @@ mod test { rxdone.recv(); rxdone.recv(); }) + + iotest!(fn clone_accept_smoke() { + let addr = next_test_ip4(); + let l = TcpListener::bind(addr.ip.to_string().as_slice(), addr.port); + let mut a = l.listen().unwrap(); + let mut a2 = a.clone(); + + spawn(proc() { + let _ = TcpStream::connect(addr.ip.to_string().as_slice(), addr.port); + }); + spawn(proc() { + let _ = TcpStream::connect(addr.ip.to_string().as_slice(), addr.port); + }); + + assert!(a.accept().is_ok()); + assert!(a2.accept().is_ok()); + }) + + iotest!(fn clone_accept_concurrent() { + let addr = next_test_ip4(); + let l = TcpListener::bind(addr.ip.to_string().as_slice(), addr.port); + let a = l.listen().unwrap(); + let a2 = a.clone(); + + let (tx, rx) = channel(); + let tx2 = tx.clone(); + + spawn(proc() { let mut a = a; tx.send(a.accept()) }); + spawn(proc() { let mut a = a2; tx2.send(a.accept()) }); + + spawn(proc() { + let _ = TcpStream::connect(addr.ip.to_string().as_slice(), addr.port); + }); + spawn(proc() { + let _ = TcpStream::connect(addr.ip.to_string().as_slice(), addr.port); + }); + + assert!(rx.recv().is_ok()); + assert!(rx.recv().is_ok()); + }) + + iotest!(fn close_accept_smoke() { + let addr = next_test_ip4(); + let l = TcpListener::bind(addr.ip.to_string().as_slice(), addr.port); + let mut a = l.listen().unwrap(); + + a.close_accept().unwrap(); + assert_eq!(a.accept().err().unwrap().kind, EndOfFile); + }) + + iotest!(fn close_accept_concurrent() { + let addr = next_test_ip4(); + let l = TcpListener::bind(addr.ip.to_string().as_slice(), addr.port); + let a = l.listen().unwrap(); + let mut a2 = a.clone(); + + let (tx, rx) = channel(); + spawn(proc() { + let mut a = a; + tx.send(a.accept()); + }); + a2.close_accept().unwrap(); + + assert_eq!(rx.recv().err().unwrap().kind, EndOfFile); + }) } diff --git a/src/libstd/io/net/unix.rs b/src/libstd/io/net/unix.rs index 33235bd6b03..ea851d44531 100644 --- a/src/libstd/io/net/unix.rs +++ b/src/libstd/io/net/unix.rs @@ -212,6 +212,15 @@ impl UnixAcceptor { pub fn set_timeout(&mut self, timeout_ms: Option<u64>) { self.obj.set_timeout(timeout_ms) } + + /// Closes the accepting capabilities of this acceptor. + /// + /// This function has the same semantics as `TcpAcceptor::close_accept`, and + /// more information can be found in that documentation. + #[experimental] + pub fn close_accept(&mut self) -> IoResult<()> { + self.obj.close_accept().map_err(IoError::from_rtio_error) + } } impl Acceptor<UnixStream> for UnixAcceptor { @@ -222,6 +231,25 @@ impl Acceptor<UnixStream> for UnixAcceptor { } } +impl Clone for UnixAcceptor { + /// Creates a new handle to this unix acceptor, allowing for simultaneous + /// accepts. + /// + /// The underlying unix acceptor will not be closed until all handles to the + /// acceptor have been deallocated. Incoming connections will be received on + /// at most once acceptor, the same connection will not be accepted twice. + /// + /// The `close_accept` method will shut down *all* acceptors cloned from the + /// same original acceptor, whereas the `set_timeout` method only affects + /// the selector that it is called on. + /// + /// This function is useful for creating a handle to invoke `close_accept` + /// on to wake up any other task blocked in `accept`. + fn clone(&self) -> UnixAcceptor { + UnixAcceptor { obj: self.obj.clone() } + } +} + #[cfg(test)] #[allow(experimental)] mod tests { @@ -702,4 +730,73 @@ mod tests { rx2.recv(); }) + + #[cfg(not(windows))] + iotest!(fn clone_accept_smoke() { + let addr = next_test_unix(); + let l = UnixListener::bind(&addr); + let mut a = l.listen().unwrap(); + let mut a2 = a.clone(); + + let addr2 = addr.clone(); + spawn(proc() { + let _ = UnixStream::connect(&addr2); + }); + spawn(proc() { + let _ = UnixStream::connect(&addr); + }); + + assert!(a.accept().is_ok()); + drop(a); + assert!(a2.accept().is_ok()); + }) + + iotest!(fn clone_accept_concurrent() { + let addr = next_test_unix(); + let l = UnixListener::bind(&addr); + let a = l.listen().unwrap(); + let a2 = a.clone(); + + let (tx, rx) = channel(); + let tx2 = tx.clone(); + + spawn(proc() { let mut a = a; tx.send(a.accept()) }); + spawn(proc() { let mut a = a2; tx2.send(a.accept()) }); + + let addr2 = addr.clone(); + spawn(proc() { + let _ = UnixStream::connect(&addr2); + }); + spawn(proc() { + let _ = UnixStream::connect(&addr); + }); + + assert!(rx.recv().is_ok()); + assert!(rx.recv().is_ok()); + }) + + iotest!(fn close_accept_smoke() { + let addr = next_test_unix(); + let l = UnixListener::bind(&addr); + let mut a = l.listen().unwrap(); + + a.close_accept().unwrap(); + assert_eq!(a.accept().err().unwrap().kind, EndOfFile); + }) + + iotest!(fn close_accept_concurrent() { + let addr = next_test_unix(); + let l = UnixListener::bind(&addr); + let a = l.listen().unwrap(); + let mut a2 = a.clone(); + + let (tx, rx) = channel(); + spawn(proc() { + let mut a = a; + tx.send(a.accept()); + }); + a2.close_accept().unwrap(); + + assert_eq!(rx.recv().err().unwrap().kind, EndOfFile); + }) } diff --git a/src/libstd/io/result.rs b/src/libstd/io/result.rs index a69f6c10abf..03637079241 100644 --- a/src/libstd/io/result.rs +++ b/src/libstd/io/result.rs @@ -111,7 +111,8 @@ mod test { Ok(MemReader::new(vec!(0, 1, 2, 3))); let mut buf = [0, 0]; reader.read(buf).unwrap(); - assert_eq!(buf.as_slice(), &[0, 1]); + let b: &[_] = &[0, 1]; + assert_eq!(buf.as_slice(), b); } #[test] diff --git a/src/libstd/io/test.rs b/src/libstd/io/test.rs index 079a9aef648..769ad2a8468 100644 --- a/src/libstd/io/test.rs +++ b/src/libstd/io/test.rs @@ -146,6 +146,7 @@ mod darwin_fd_limit { use libc; type rlim_t = libc::uint64_t; + #[repr(C)] struct rlimit { rlim_cur: rlim_t, rlim_max: rlim_t diff --git a/src/libstd/io/util.rs b/src/libstd/io/util.rs index 1fe0ba780a6..7fba0bc85a6 100644 --- a/src/libstd/io/util.rs +++ b/src/libstd/io/util.rs @@ -126,12 +126,12 @@ impl Buffer for NullReader { /// The `Writer`s are delegated to in order. If any `Writer` returns an error, /// that error is returned immediately and remaining `Writer`s are not called. pub struct MultiWriter { - writers: Vec<Box<Writer>> + writers: Vec<Box<Writer+'static>> } impl MultiWriter { /// Creates a new `MultiWriter` - pub fn new(writers: Vec<Box<Writer>>) -> MultiWriter { + pub fn new(writers: Vec<Box<Writer+'static>>) -> MultiWriter { MultiWriter { writers: writers } } } diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index d35b644b643..8c1ed7cfa8f 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -108,6 +108,7 @@ #![feature(macro_rules, globs, managed_boxes, linkage)] #![feature(default_type_params, phase, lang_items, unsafe_destructor)] #![feature(import_shadowing)] +#![feature(issue_5723_bootstrap)] // Don't link to std. We are std. #![no_std] diff --git a/src/libstd/macros.rs b/src/libstd/macros.rs index 85e614ab47e..0ebca006c4c 100644 --- a/src/libstd/macros.rs +++ b/src/libstd/macros.rs @@ -96,7 +96,7 @@ macro_rules! fail( macro_rules! assert( ($cond:expr) => ( if !$cond { - fail!("assertion failed: {:s}", stringify!($cond)) + fail!(concat!("assertion failed: ", stringify!($cond))) } ); ($cond:expr, $($arg:expr),+) => ( diff --git a/src/libstd/os.rs b/src/libstd/os.rs index d8eb0979190..f452f8b23e7 100644 --- a/src/libstd/os.rs +++ b/src/libstd/os.rs @@ -59,7 +59,7 @@ use libc::c_char; /// Get the number of cores available pub fn num_cpus() -> uint { unsafe { - return rust_get_num_cpus(); + return rust_get_num_cpus() as uint; } extern { @@ -139,7 +139,7 @@ pub fn getcwd() -> Path { } #[cfg(windows)] -pub mod win32 { +pub mod windows { use libc::types::os::arch::extra::DWORD; use libc; use option::{None, Option}; @@ -295,7 +295,8 @@ pub fn env_as_bytes() -> Vec<(Vec<u8>,Vec<u8>)> { for p in input.iter() { let mut it = p.as_slice().splitn(1, |b| *b == b'='); let key = Vec::from_slice(it.next().unwrap()); - let val = Vec::from_slice(it.next().unwrap_or(&[])); + let default: &[u8] = &[]; + let val = Vec::from_slice(it.next().unwrap_or(default)); pairs.push((key, val)); } pairs @@ -362,7 +363,7 @@ pub fn getenv_as_bytes(n: &str) -> Option<Vec<u8>> { pub fn getenv(n: &str) -> Option<String> { unsafe { with_env_lock(|| { - use os::win32::{fill_utf16_buf_and_decode}; + use os::windows::{fill_utf16_buf_and_decode}; let n: Vec<u16> = n.utf16_units().collect(); let n = n.append_one(0); fill_utf16_buf_and_decode(|buf, sz| { @@ -707,7 +708,7 @@ pub fn self_exe_name() -> Option<Path> { #[cfg(windows)] fn load_self() -> Option<Vec<u8>> { unsafe { - use os::win32::fill_utf16_buf_and_decode; + use os::windows::fill_utf16_buf_and_decode; fill_utf16_buf_and_decode(|buf, sz| { libc::GetModuleFileNameW(0u as libc::DWORD, buf, sz) }).map(|s| s.into_string().into_bytes()) @@ -1315,7 +1316,7 @@ pub fn page_size() -> uint { /// A memory mapped file or chunk of memory. This is a very system-specific /// interface to the OS's memory mapping facilities (`mmap` on POSIX, -/// `VirtualAlloc`/`CreateFileMapping` on win32). It makes no attempt at +/// `VirtualAlloc`/`CreateFileMapping` on Windows). It makes no attempt at /// abstracting platform differences, besides in error values returned. Consider /// yourself warned. /// @@ -1385,7 +1386,7 @@ pub enum MapError { ErrZeroLength, /// Unrecognized error. The inner value is the unrecognized errno. ErrUnknown(int), - /// ## The following are win32-specific + /// ## The following are Windows-specific /// /// Unsupported combination of protection flags /// (`MapReadable`/`MapWritable`/`MapExecutable`). @@ -1843,14 +1844,13 @@ pub mod consts { } #[cfg(target_os = "windows")] -#[cfg(stage0, target_os = "win32")] // NOTE: Remove after snapshot pub mod consts { pub use os::arch_consts::ARCH; pub static FAMILY: &'static str = "windows"; /// A string describing the specific operating system in use: in this - /// case, `win32`. + /// case, `windows`. pub static SYSNAME: &'static str = "windows"; /// Specifies the filename prefix used for shared libraries on this diff --git a/src/libstd/path/mod.rs b/src/libstd/path/mod.rs index 38d04324fe4..6a10be84a62 100644 --- a/src/libstd/path/mod.rs +++ b/src/libstd/path/mod.rs @@ -825,12 +825,20 @@ pub trait GenericPathUnsafe { unsafe fn push_unchecked<T: BytesContainer>(&mut self, path: T); } -/// Helper struct for printing paths with format!() +/// Note: stage0-specific version that lacks bound. +#[cfg(stage0)] pub struct Display<'a, P> { path: &'a P, filename: bool } +/// Helper struct for printing paths with format!() +#[cfg(not(stage0))] +pub struct Display<'a, P:'a> { + path: &'a P, + filename: bool +} + impl<'a, P: GenericPath> fmt::Show for Display<'a, P> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.as_maybe_owned().as_slice().fmt(f) @@ -846,7 +854,10 @@ impl<'a, P: GenericPath> Display<'a, P> { pub fn as_maybe_owned(&self) -> MaybeOwned<'a> { String::from_utf8_lossy(if self.filename { match self.path.filename() { - None => &[], + None => { + let result: &[u8] = &[]; + result + } Some(v) => v } } else { diff --git a/src/libstd/path/posix.rs b/src/libstd/path/posix.rs index 0a7817c3e00..06eab31d7bf 100644 --- a/src/libstd/path/posix.rs +++ b/src/libstd/path/posix.rs @@ -464,6 +464,7 @@ static dot_dot_static: &'static [u8] = b".."; mod tests { use prelude::*; use super::*; + use mem; use str; use str::StrSlice; @@ -621,8 +622,10 @@ mod tests { macro_rules! t( (s: $path:expr, $op:ident, $exp:expr) => ( { - let path = Path::new($path); - assert!(path.$op() == ($exp).as_bytes()); + unsafe { + let path = Path::new($path); + assert!(path.$op() == mem::transmute(($exp).as_bytes())); + } } ); (s: $path:expr, $op:ident, $exp:expr, opt) => ( @@ -634,9 +637,11 @@ mod tests { ); (v: $path:expr, $op:ident, $exp:expr) => ( { - let arg = $path; - let path = Path::new(arg); - assert!(path.$op() == $exp); + unsafe { + let arg = $path; + let path = Path::new(arg); + assert!(path.$op() == mem::transmute($exp)); + } } ); ) @@ -684,8 +689,9 @@ mod tests { t!(v: b"hi/there.txt", extension, Some(b"txt")); t!(v: b"hi/there\x80.txt", extension, Some(b"txt")); t!(v: b"hi/there.t\x80xt", extension, Some(b"t\x80xt")); - t!(v: b"hi/there", extension, None); - t!(v: b"hi/there\x80", extension, None); + let no: Option<&'static [u8]> = None; + t!(v: b"hi/there", extension, no); + t!(v: b"hi/there\x80", extension, no); t!(s: "hi/there.txt", extension, Some("txt"), opt); t!(s: "hi/there", extension, None, opt); t!(s: "there.txt", extension, Some("txt"), opt); @@ -974,57 +980,62 @@ mod tests { macro_rules! t( (s: $path:expr, $filename:expr, $dirname:expr, $filestem:expr, $ext:expr) => ( { - let path = $path; - let filename = $filename; - assert!(path.filename_str() == filename, - "{}.filename_str(): Expected `{:?}`, found {:?}", - path.as_str().unwrap(), filename, path.filename_str()); - let dirname = $dirname; - assert!(path.dirname_str() == dirname, - "`{}`.dirname_str(): Expected `{:?}`, found `{:?}`", - path.as_str().unwrap(), dirname, path.dirname_str()); - let filestem = $filestem; - assert!(path.filestem_str() == filestem, - "`{}`.filestem_str(): Expected `{:?}`, found `{:?}`", - path.as_str().unwrap(), filestem, path.filestem_str()); - let ext = $ext; - assert!(path.extension_str() == ext, - "`{}`.extension_str(): Expected `{:?}`, found `{:?}`", - path.as_str().unwrap(), ext, path.extension_str()); + unsafe { + let path = $path; + let filename = $filename; + assert!(path.filename_str() == filename, + "{}.filename_str(): Expected `{:?}`, found {:?}", + path.as_str().unwrap(), filename, path.filename_str()); + let dirname = $dirname; + assert!(path.dirname_str() == dirname, + "`{}`.dirname_str(): Expected `{:?}`, found `{:?}`", + path.as_str().unwrap(), dirname, path.dirname_str()); + let filestem = $filestem; + assert!(path.filestem_str() == filestem, + "`{}`.filestem_str(): Expected `{:?}`, found `{:?}`", + path.as_str().unwrap(), filestem, path.filestem_str()); + let ext = $ext; + assert!(path.extension_str() == mem::transmute(ext), + "`{}`.extension_str(): Expected `{:?}`, found `{:?}`", + path.as_str().unwrap(), ext, path.extension_str()); + } } ); (v: $path:expr, $filename:expr, $dirname:expr, $filestem:expr, $ext:expr) => ( { - let path = $path; - assert!(path.filename() == $filename); - assert!(path.dirname() == $dirname); - assert!(path.filestem() == $filestem); - assert!(path.extension() == $ext); + unsafe { + let path = $path; + assert!(path.filename() == mem::transmute($filename)); + assert!(path.dirname() == mem::transmute($dirname)); + assert!(path.filestem() == mem::transmute($filestem)); + assert!(path.extension() == mem::transmute($ext)); + } } ) ) - t!(v: Path::new(b"a/b/c"), Some(b"c"), b"a/b", Some(b"c"), None); - t!(v: Path::new(b"a/b/\xFF"), Some(b"\xFF"), b"a/b", Some(b"\xFF"), None); + let no: Option<&'static str> = None; + t!(v: Path::new(b"a/b/c"), Some(b"c"), b"a/b", Some(b"c"), no); + t!(v: Path::new(b"a/b/\xFF"), Some(b"\xFF"), b"a/b", Some(b"\xFF"), no); t!(v: Path::new(b"hi/there.\xFF"), Some(b"there.\xFF"), b"hi", Some(b"there"), Some(b"\xFF")); - t!(s: Path::new("a/b/c"), Some("c"), Some("a/b"), Some("c"), None); - t!(s: Path::new("."), None, Some("."), None, None); - t!(s: Path::new("/"), None, Some("/"), None, None); - t!(s: Path::new(".."), None, Some(".."), None, None); - t!(s: Path::new("../.."), None, Some("../.."), None, None); + t!(s: Path::new("a/b/c"), Some("c"), Some("a/b"), Some("c"), no); + t!(s: Path::new("."), None, Some("."), None, no); + t!(s: Path::new("/"), None, Some("/"), None, no); + t!(s: Path::new(".."), None, Some(".."), None, no); + t!(s: Path::new("../.."), None, Some("../.."), None, no); t!(s: Path::new("hi/there.txt"), Some("there.txt"), Some("hi"), Some("there"), Some("txt")); - t!(s: Path::new("hi/there"), Some("there"), Some("hi"), Some("there"), None); + t!(s: Path::new("hi/there"), Some("there"), Some("hi"), Some("there"), no); t!(s: Path::new("hi/there."), Some("there."), Some("hi"), Some("there"), Some("")); - t!(s: Path::new("hi/.there"), Some(".there"), Some("hi"), Some(".there"), None); + t!(s: Path::new("hi/.there"), Some(".there"), Some("hi"), Some(".there"), no); t!(s: Path::new("hi/..there"), Some("..there"), Some("hi"), Some("."), Some("there")); - t!(s: Path::new(b"a/b/\xFF"), None, Some("a/b"), None, None); + t!(s: Path::new(b"a/b/\xFF"), None, Some("a/b"), None, no); t!(s: Path::new(b"a/b/\xFF.txt"), None, Some("a/b"), None, Some("txt")); - t!(s: Path::new(b"a/b/c.\x80"), None, Some("a/b"), Some("c"), None); - t!(s: Path::new(b"\xFF/b"), Some("b"), None, Some("b"), None); + t!(s: Path::new(b"a/b/c.\x80"), None, Some("a/b"), Some("c"), no); + t!(s: Path::new(b"\xFF/b"), Some("b"), None, Some("b"), no); } #[test] diff --git a/src/libstd/path/windows.rs b/src/libstd/path/windows.rs index 8402d751bf2..d9864cfaa61 100644 --- a/src/libstd/path/windows.rs +++ b/src/libstd/path/windows.rs @@ -628,18 +628,36 @@ impl GenericPath for Path { } impl Path { - /// Returns a new Path from a byte vector or string + /// Returns a new `Path` from a `BytesContainer`. /// /// # Failure /// - /// Fails the task if the vector contains a NUL. - /// Fails if invalid UTF-8. + /// Fails if the vector contains a `NUL`, or if it contains invalid UTF-8. + /// + /// # Example + /// + /// ``` + /// println!("{}", Path::new(r"C:\some\path").display()); + /// ``` #[inline] pub fn new<T: BytesContainer>(path: T) -> Path { GenericPath::new(path) } - /// Returns a new Path from a byte vector or string, if possible + /// Returns a new `Some(Path)` from a `BytesContainer`. + /// + /// Returns `None` if the vector contains a `NUL`, or if it contains invalid UTF-8. + /// + /// # Example + /// + /// ``` + /// let path = Path::new_opt(r"C:\some\path"); + /// + /// match path { + /// Some(path) => println!("{}", path.display()), + /// None => println!("There was a problem with your path."), + /// } + /// ``` #[inline] pub fn new_opt<T: BytesContainer>(path: T) -> Option<Path> { GenericPath::new_opt(path) @@ -1123,6 +1141,7 @@ fn prefix_len(p: Option<PathPrefix>) -> uint { #[cfg(test)] mod tests { + use mem; use prelude::*; use super::*; use super::parse_prefix; @@ -1365,9 +1384,11 @@ mod tests { macro_rules! t( (s: $path:expr, $op:ident, $exp:expr) => ( { - let path = $path; - let path = Path::new(path); - assert!(path.$op() == Some($exp)); + unsafe { + let path = $path; + let path = Path::new(path); + assert!(path.$op() == Some(mem::transmute($exp))); + } } ); (s: $path:expr, $op:ident, $exp:expr, opt) => ( @@ -1380,9 +1401,11 @@ mod tests { ); (v: $path:expr, $op:ident, $exp:expr) => ( { - let path = $path; - let path = Path::new(path); - assert!(path.$op() == $exp); + unsafe { + let path = $path; + let path = Path::new(path); + assert!(path.$op() == mem::transmute($exp)); + } } ) ) @@ -1467,7 +1490,8 @@ mod tests { // filestem is based on filename, so we don't need the full set of prefix tests t!(v: b"hi\\there.txt", extension, Some(b"txt")); - t!(v: b"hi\\there", extension, None); + let no: Option<&'static [u8]> = None; + t!(v: b"hi\\there", extension, no); t!(s: "hi\\there.txt", extension_str, Some("txt"), opt); t!(s: "hi\\there", extension_str, None, opt); t!(s: "there.txt", extension_str, Some("txt"), opt); @@ -1874,48 +1898,53 @@ mod tests { macro_rules! t( (s: $path:expr, $filename:expr, $dirname:expr, $filestem:expr, $ext:expr) => ( { - let path = $path; - let filename = $filename; - assert!(path.filename_str() == filename, - "`{}`.filename_str(): Expected `{:?}`, found `{:?}`", - path.as_str().unwrap(), filename, path.filename_str()); - let dirname = $dirname; - assert!(path.dirname_str() == dirname, - "`{}`.dirname_str(): Expected `{:?}`, found `{:?}`", - path.as_str().unwrap(), dirname, path.dirname_str()); - let filestem = $filestem; - assert!(path.filestem_str() == filestem, - "`{}`.filestem_str(): Expected `{:?}`, found `{:?}`", - path.as_str().unwrap(), filestem, path.filestem_str()); - let ext = $ext; - assert!(path.extension_str() == ext, - "`{}`.extension_str(): Expected `{:?}`, found `{:?}`", - path.as_str().unwrap(), ext, path.extension_str()); + unsafe { + let path = $path; + let filename = $filename; + assert!(path.filename_str() == filename, + "`{}`.filename_str(): Expected `{:?}`, found `{:?}`", + path.as_str().unwrap(), filename, path.filename_str()); + let dirname = $dirname; + assert!(path.dirname_str() == dirname, + "`{}`.dirname_str(): Expected `{:?}`, found `{:?}`", + path.as_str().unwrap(), dirname, path.dirname_str()); + let filestem = $filestem; + assert!(path.filestem_str() == filestem, + "`{}`.filestem_str(): Expected `{:?}`, found `{:?}`", + path.as_str().unwrap(), filestem, path.filestem_str()); + let ext = $ext; + assert!(path.extension_str() == mem::transmute(ext), + "`{}`.extension_str(): Expected `{:?}`, found `{:?}`", + path.as_str().unwrap(), ext, path.extension_str()); + } } ); (v: $path:expr, $filename:expr, $dirname:expr, $filestem:expr, $ext:expr) => ( { - let path = $path; - assert!(path.filename() == $filename); - assert!(path.dirname() == $dirname); - assert!(path.filestem() == $filestem); - assert!(path.extension() == $ext); + unsafe { + let path = $path; + assert!(path.filename() == mem::transmute($filename)); + assert!(path.dirname() == mem::transmute($dirname)); + assert!(path.filestem() == mem::transmute($filestem)); + assert!(path.extension() == mem::transmute($ext)); + } } ) ) - t!(v: Path::new(b"a\\b\\c"), Some(b"c"), b"a\\b", Some(b"c"), None); - t!(s: Path::new("a\\b\\c"), Some("c"), Some("a\\b"), Some("c"), None); - t!(s: Path::new("."), None, Some("."), None, None); - t!(s: Path::new("\\"), None, Some("\\"), None, None); - t!(s: Path::new(".."), None, Some(".."), None, None); - t!(s: Path::new("..\\.."), None, Some("..\\.."), None, None); + let no: Option<&'static str> = None; + t!(v: Path::new(b"a\\b\\c"), Some(b"c"), b"a\\b", Some(b"c"), no); + t!(s: Path::new("a\\b\\c"), Some("c"), Some("a\\b"), Some("c"), no); + t!(s: Path::new("."), None, Some("."), None, no); + t!(s: Path::new("\\"), None, Some("\\"), None, no); + t!(s: Path::new(".."), None, Some(".."), None, no); + t!(s: Path::new("..\\.."), None, Some("..\\.."), None, no); t!(s: Path::new("hi\\there.txt"), Some("there.txt"), Some("hi"), Some("there"), Some("txt")); - t!(s: Path::new("hi\\there"), Some("there"), Some("hi"), Some("there"), None); + t!(s: Path::new("hi\\there"), Some("there"), Some("hi"), Some("there"), no); t!(s: Path::new("hi\\there."), Some("there."), Some("hi"), Some("there"), Some("")); - t!(s: Path::new("hi\\.there"), Some(".there"), Some("hi"), Some(".there"), None); + t!(s: Path::new("hi\\.there"), Some(".there"), Some("hi"), Some(".there"), no); t!(s: Path::new("hi\\..there"), Some("..there"), Some("hi"), Some("."), Some("there")); diff --git a/src/libstd/prelude.rs b/src/libstd/prelude.rs index eb6dcc1f1a5..48ddfeaf5ff 100644 --- a/src/libstd/prelude.rs +++ b/src/libstd/prelude.rs @@ -46,6 +46,7 @@ #[doc(no_inline)] pub use ops::{Drop, Deref, DerefMut}; #[doc(no_inline)] pub use ops::{Shl, Shr}; #[doc(no_inline)] pub use ops::{Index, IndexMut}; +#[doc(no_inline)] pub use ops::{Fn, FnMut, FnOnce}; #[doc(no_inline)] pub use option::{Option, Some, None}; #[doc(no_inline)] pub use result::{Result, Ok, Err}; diff --git a/src/libstd/rand/mod.rs b/src/libstd/rand/mod.rs index c02a97e09a4..b9b7a02b62f 100644 --- a/src/libstd/rand/mod.rs +++ b/src/libstd/rand/mod.rs @@ -70,6 +70,49 @@ //! println!("{}", tuple) //! ``` //! +//! ## Monte Carlo estimation of π +//! +//! For this example, imagine we have a square with sides of length 2 and a unit +//! circle, both centered at the origin. Since the area of a unit circle is π, +//! we have: +//! +//! ```notrust +//! (area of unit circle) / (area of square) = π / 4 +//! ``` +//! +//! So if we sample many points randomly from the square, roughly π / 4 of them +//! should be inside the circle. +//! +//! We can use the above fact to estimate the value of π: pick many points in the +//! square at random, calculate the fraction that fall within the circle, and +//! multiply this fraction by 4. +//! +//! ``` +//! use std::rand; +//! use std::rand::distributions::{IndependentSample, Range}; +//! +//! fn main() { +//! let between = Range::new(-1f64, 1.); +//! let mut rng = rand::task_rng(); +//! +//! let total = 1_000_000u; +//! let mut in_circle = 0u; +//! +//! for _ in range(0u, total) { +//! let a = between.ind_sample(&mut rng); +//! let b = between.ind_sample(&mut rng); +//! if a*a + b*b <= 1. { +//! in_circle += 1; +//! } +//! } +//! +//! // prints something close to 3.14159... +//! println!("{}", 4. * (in_circle as f64) / (total as f64)); +//! } +//! ``` +//! +//! ## Monty Hall Problem +//! //! This is a simulation of the [Monty Hall Problem][]: //! //! > Suppose you're on a game show, and you're given the choice of three doors: @@ -488,7 +531,8 @@ mod test { r.shuffle(empty); let mut one = [1i]; r.shuffle(one); - assert_eq!(one.as_slice(), &[1]); + let b: &[_] = &[1]; + assert_eq!(one.as_slice(), b); let mut two = [1i, 2]; r.shuffle(two); @@ -496,7 +540,8 @@ mod test { let mut x = [1i, 1, 1]; r.shuffle(x); - assert_eq!(x.as_slice(), &[1, 1, 1]); + let b: &[_] = &[1, 1, 1]; + assert_eq!(x.as_slice(), b); } #[test] @@ -505,7 +550,8 @@ mod test { r.gen::<int>(); let mut v = [1i, 1, 1]; r.shuffle(v); - assert_eq!(v.as_slice(), &[1, 1, 1]); + let b: &[_] = &[1, 1, 1]; + assert_eq!(v.as_slice(), b); assert_eq!(r.gen_range(0u, 1u), 0u); } diff --git a/src/libstd/rand/os.rs b/src/libstd/rand/os.rs index 2be4129883f..07ad08e1b3c 100644 --- a/src/libstd/rand/os.rs +++ b/src/libstd/rand/os.rs @@ -85,6 +85,7 @@ mod imp { marker: marker::NoCopy } + #[repr(C)] struct SecRandom; static kSecRandomDefault: *const SecRandom = 0 as *const SecRandom; diff --git a/src/libstd/rt/backtrace.rs b/src/libstd/rt/backtrace.rs index a4491b2ab1d..58b3179a297 100644 --- a/src/libstd/rt/backtrace.rs +++ b/src/libstd/rt/backtrace.rs @@ -273,9 +273,9 @@ mod imp { try!(writeln!(w, "stack backtrace:")); // 100 lines should be enough - static SIZE: libc::c_int = 100; + static SIZE: uint = 100; let mut buf: [*mut libc::c_void, ..SIZE] = unsafe {mem::zeroed()}; - let cnt = unsafe { backtrace(buf.as_mut_ptr(), SIZE) as uint}; + let cnt = unsafe { backtrace(buf.as_mut_ptr(), SIZE as libc::c_int) as uint}; // skipping the first one as it is write itself result::fold_(range(1, cnt).map(|i| { @@ -291,7 +291,7 @@ mod imp { struct Context<'a> { idx: int, - writer: &'a mut Writer, + writer: &'a mut Writer+'a, last_error: Option<IoError>, } @@ -701,6 +701,7 @@ mod imp { static IMAGE_FILE_MACHINE_IA64: libc::DWORD = 0x0200; static IMAGE_FILE_MACHINE_AMD64: libc::DWORD = 0x8664; + #[cfg(stage0)] #[packed] struct SYMBOL_INFO { SizeOfStruct: libc::c_ulong, @@ -723,6 +724,30 @@ mod imp { Name: [libc::c_char, ..MAX_SYM_NAME], } + #[cfg(not(stage0))] + #[repr(C, packed)] + struct SYMBOL_INFO { + SizeOfStruct: libc::c_ulong, + TypeIndex: libc::c_ulong, + Reserved: [u64, ..2], + Index: libc::c_ulong, + Size: libc::c_ulong, + ModBase: u64, + Flags: libc::c_ulong, + Value: u64, + Address: u64, + Register: libc::c_ulong, + Scope: libc::c_ulong, + Tag: libc::c_ulong, + NameLen: libc::c_ulong, + MaxNameLen: libc::c_ulong, + // note that windows has this as 1, but it basically just means that + // the name is inline at the end of the struct. For us, we just bump + // the struct size up to MAX_SYM_NAME. + Name: [libc::c_char, ..MAX_SYM_NAME], + } + + #[repr(C)] enum ADDRESS_MODE { AddrMode1616, @@ -772,6 +797,7 @@ mod imp { static MAXIMUM_SUPPORTED_EXTENSION: uint = 512; + #[repr(C)] pub struct CONTEXT { ContextFlags: libc::DWORD, Dr0: libc::DWORD, @@ -800,6 +826,7 @@ mod imp { ExtendedRegisters: [u8, ..MAXIMUM_SUPPORTED_EXTENSION], } + #[repr(C)] pub struct FLOATING_SAVE_AREA { ControlWord: libc::DWORD, StatusWord: libc::DWORD, @@ -829,6 +856,7 @@ mod imp { use libc::{c_longlong, c_ulonglong}; use libc::types::os::arch::extra::{WORD, DWORD, DWORDLONG}; + #[repr(C)] pub struct CONTEXT { P1Home: DWORDLONG, P2Home: DWORDLONG, @@ -886,11 +914,13 @@ mod imp { LastExceptionFromRip: DWORDLONG, } + #[repr(C)] pub struct M128A { Low: c_ulonglong, High: c_longlong } + #[repr(C)] pub struct FLOATING_SAVE_AREA { _Dummy: [u8, ..512] // FIXME: Fill this out } @@ -907,6 +937,7 @@ mod imp { } } + #[repr(C)] struct Cleanup { handle: libc::HANDLE, SymCleanup: SymCleanupFn, diff --git a/src/libsync/comm/mod.rs b/src/libsync/comm/mod.rs index e4df661b562..16f6eea6144 100644 --- a/src/libsync/comm/mod.rs +++ b/src/libsync/comm/mod.rs @@ -86,6 +86,8 @@ //! //! ``` //! // Create a shared channel which can be sent along from many tasks +//! // where tx is the sending half (tx for transmission), and rx is the receiving +//! // half (rx for receiving). //! let (tx, rx) = channel(); //! for i in range(0i, 10i) { //! let tx = tx.clone(); @@ -384,6 +386,14 @@ pub struct Receiver<T> { /// whenever `next` is called, waiting for a new message, and `None` will be /// returned when the corresponding channel has hung up. #[unstable] +#[cfg(not(stage0))] +pub struct Messages<'a, T:'a> { + rx: &'a Receiver<T> +} + +/// Stage0 only +#[cfg(stage0)] +#[unstable] pub struct Messages<'a, T> { rx: &'a Receiver<T> } @@ -473,6 +483,8 @@ impl<T> UnsafeFlavor<T> for Receiver<T> { /// # Example /// /// ``` +/// // tx is is the sending half (tx for transmission), and rx is the receiving +/// // half (rx for receiving). /// let (tx, rx) = channel(); /// /// // Spawn off an expensive computation diff --git a/src/libsync/comm/select.rs b/src/libsync/comm/select.rs index 737a4bfe299..dc9891dd1ee 100644 --- a/src/libsync/comm/select.rs +++ b/src/libsync/comm/select.rs @@ -76,6 +76,24 @@ pub struct Select { /// A handle to a receiver which is currently a member of a `Select` set of /// receivers. This handle is used to keep the receiver in the set as well as /// interact with the underlying receiver. +#[cfg(not(stage0))] +pub struct Handle<'rx, T:'rx> { + /// The ID of this handle, used to compare against the return value of + /// `Select::wait()` + id: uint, + selector: &'rx Select, + next: *mut Handle<'static, ()>, + prev: *mut Handle<'static, ()>, + added: bool, + packet: &'rx Packet+'rx, + + // due to our fun transmutes, we be sure to place this at the end. (nothing + // previous relies on T) + rx: &'rx Receiver<T>, +} + +/// Stage0 only +#[cfg(stage0)] pub struct Handle<'rx, T> { /// The ID of this handle, used to compare against the return value of /// `Select::wait()` @@ -84,7 +102,7 @@ pub struct Handle<'rx, T> { next: *mut Handle<'static, ()>, prev: *mut Handle<'static, ()>, added: bool, - packet: &'rx Packet, + packet: &'rx Packet+'rx, // due to our fun transmutes, we be sure to place this at the end. (nothing // previous relies on T) diff --git a/src/libsync/lib.rs b/src/libsync/lib.rs index bed90743503..c2744751ee5 100644 --- a/src/libsync/lib.rs +++ b/src/libsync/lib.rs @@ -29,6 +29,7 @@ #![feature(phase, globs, macro_rules, unsafe_destructor)] #![feature(import_shadowing)] +#![feature(issue_5723_bootstrap)] #![deny(missing_doc)] #![no_std] diff --git a/src/libsync/lock.rs b/src/libsync/lock.rs index b07d06ca18e..e1cae6c62d5 100644 --- a/src/libsync/lock.rs +++ b/src/libsync/lock.rs @@ -180,6 +180,18 @@ pub struct Mutex<T> { /// An guard which is created by locking a mutex. Through this guard the /// underlying data can be accessed. +#[cfg(not(stage0))] +pub struct MutexGuard<'a, T:'a> { + // FIXME #12808: strange name to try to avoid interfering with + // field accesses of the contained type via Deref + _data: &'a mut T, + /// Inner condition variable connected to the locked mutex that this guard + /// was created from. This can be used for atomic-unlock-and-deschedule. + pub cond: Condvar<'a>, +} + +/// stage0 only +#[cfg(stage0)] pub struct MutexGuard<'a, T> { // FIXME #12808: strange name to try to avoid interfering with // field accesses of the contained type via Deref @@ -280,6 +292,18 @@ pub struct RWLock<T> { /// A guard which is created by locking an rwlock in write mode. Through this /// guard the underlying data can be accessed. +#[cfg(not(stage0))] +pub struct RWLockWriteGuard<'a, T:'a> { + // FIXME #12808: strange name to try to avoid interfering with + // field accesses of the contained type via Deref + _data: &'a mut T, + /// Inner condition variable that can be used to sleep on the write mode of + /// this rwlock. + pub cond: Condvar<'a>, +} + +/// stage0 only +#[cfg(stage0)] pub struct RWLockWriteGuard<'a, T> { // FIXME #12808: strange name to try to avoid interfering with // field accesses of the contained type via Deref @@ -291,6 +315,16 @@ pub struct RWLockWriteGuard<'a, T> { /// A guard which is created by locking an rwlock in read mode. Through this /// guard the underlying data can be accessed. +#[cfg(not(stage0))] +pub struct RWLockReadGuard<'a, T:'a> { + // FIXME #12808: strange names to try to avoid interfering with + // field accesses of the contained type via Deref + _data: &'a T, + _guard: raw::RWLockReadGuard<'a>, +} + +/// Stage0 only +#[cfg(stage0)] pub struct RWLockReadGuard<'a, T> { // FIXME #12808: strange names to try to avoid interfering with // field accesses of the contained type via Deref diff --git a/src/libsync/raw.rs b/src/libsync/raw.rs index c42d567fc18..98934d87474 100644 --- a/src/libsync/raw.rs +++ b/src/libsync/raw.rs @@ -103,10 +103,17 @@ struct SemInner<Q> { } #[must_use] +#[cfg(stage0)] struct SemGuard<'a, Q> { sem: &'a Sem<Q>, } +#[must_use] +#[cfg(not(stage0))] +struct SemGuard<'a, Q:'a> { + sem: &'a Sem<Q>, +} + impl<Q: Send> Sem<Q> { fn new(count: int, q: Q) -> Sem<Q> { assert!(count >= 0, diff --git a/src/libsync/spsc_queue.rs b/src/libsync/spsc_queue.rs index 578e518cb8f..32b77be78a4 100644 --- a/src/libsync/spsc_queue.rs +++ b/src/libsync/spsc_queue.rs @@ -315,27 +315,6 @@ mod test { assert_eq!(consumer.pop(), None); } - // This behaviour is blocked by the type system if using the safe constructor - #[test] - fn pop_peeked_unchecked() { - let q = unsafe { Queue::new(0) }; - q.push(vec![1i]); - q.push(vec![2]); - let peeked = q.peek().unwrap(); - - assert_eq!(*peeked, vec![1]); - assert_eq!(q.pop(), Some(vec![1])); - - assert_eq!(*peeked, vec![1]); - q.push(vec![7]); - - // Note: This should actually expect 1, but this test is to highlight - // the unsafety allowed by the unchecked usage. A Rust user would not - // expect their peeked value to mutate like this without the type system - // complaining. - assert_eq!(*peeked, vec![7]); - } - #[test] fn peek() { let (mut consumer, mut producer) = queue(0); diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index d0adc75e5c8..d574a02fded 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -224,16 +224,17 @@ pub static DUMMY_NODE_ID: NodeId = -1; #[deriving(Clone, PartialEq, Eq, Encodable, Decodable, Hash, Show)] pub enum TyParamBound { TraitTyParamBound(TraitRef), - StaticRegionTyParamBound, UnboxedFnTyParamBound(UnboxedFnTy), - OtherRegionTyParamBound(Span) // FIXME -- just here until work for #5723 lands + RegionTyParamBound(Lifetime) } +pub type TyParamBounds = OwnedSlice<TyParamBound>; + #[deriving(Clone, PartialEq, Eq, Encodable, Decodable, Hash, Show)] pub struct TyParam { pub ident: Ident, pub id: NodeId, - pub bounds: OwnedSlice<TyParamBound>, + pub bounds: TyParamBounds, pub unbound: Option<TyParamBound>, pub default: Option<P<Ty>>, pub span: Span @@ -395,16 +396,6 @@ pub enum Mutability { } #[deriving(Clone, PartialEq, Eq, Encodable, Decodable, Hash, Show)] -pub enum ExprVstore { - /// ~[1, 2, 3, 4] - ExprVstoreUniq, - /// &[1, 2, 3, 4] - ExprVstoreSlice, - /// &mut [1, 2, 3, 4] - ExprVstoreMutSlice, -} - -#[deriving(Clone, PartialEq, Eq, Encodable, Decodable, Hash, Show)] pub enum BinOp { BiAdd, BiSub, @@ -522,7 +513,6 @@ pub struct Expr { #[deriving(Clone, PartialEq, Eq, Encodable, Decodable, Hash, Show)] pub enum Expr_ { - ExprVstore(Gc<Expr>, ExprVstore), /// First expr is the place; second expr is the value. ExprBox(Gc<Expr>, Gc<Expr>), ExprVec(Vec<Gc<Expr>>), @@ -903,11 +893,7 @@ pub struct ClosureTy { pub fn_style: FnStyle, pub onceness: Onceness, pub decl: P<FnDecl>, - /// Optional optvec distinguishes between "fn()" and "fn:()" so we can - /// implement issue #7264. None means "fn()", which means infer a default - /// bound based on pointer sigil during typeck. Some(Empty) means "fn:()", - /// which means use no bounds (e.g., not even Owned on a ~fn()). - pub bounds: Option<OwnedSlice<TyParamBound>>, + pub bounds: TyParamBounds, } #[deriving(PartialEq, Eq, Encodable, Decodable, Hash, Show)] @@ -934,12 +920,12 @@ pub enum Ty_ { TyFixedLengthVec(P<Ty>, Gc<Expr>), TyPtr(MutTy), TyRptr(Option<Lifetime>, MutTy), - TyClosure(Gc<ClosureTy>, Option<Lifetime>), + TyClosure(Gc<ClosureTy>), TyProc(Gc<ClosureTy>), TyBareFn(Gc<BareFnTy>), TyUnboxedFn(Gc<UnboxedFnTy>), TyTup(Vec<P<Ty>> ), - TyPath(Path, Option<OwnedSlice<TyParamBound>>, NodeId), // for #7264; see above + TyPath(Path, Option<TyParamBounds>, NodeId), // for #7264; see above /// No-op; kept solely so that we can pretty-print faithfully TyParen(P<Ty>), TyTypeof(Gc<Expr>), @@ -958,9 +944,9 @@ pub enum AsmDialect { pub struct InlineAsm { pub asm: InternedString, pub asm_str_style: StrStyle, - pub clobbers: InternedString, + pub outputs: Vec<(InternedString, Gc<Expr>, bool)>, pub inputs: Vec<(InternedString, Gc<Expr>)>, - pub outputs: Vec<(InternedString, Gc<Expr>)>, + pub clobbers: InternedString, pub volatile: bool, pub alignstack: bool, pub dialect: AsmDialect @@ -1292,7 +1278,7 @@ pub enum Item_ { ItemTrait(Generics, Option<TyParamBound>, // (optional) default bound not required for Self. // Currently, only Sized makes sense here. - Vec<TraitRef> , + TyParamBounds, Vec<TraitItem>), ItemImpl(Generics, Option<TraitRef>, // (optional) trait this impl implements diff --git a/src/libsyntax/ast_map/mod.rs b/src/libsyntax/ast_map/mod.rs index 5fccf6cc3f0..993c5ce676a 100644 --- a/src/libsyntax/ast_map/mod.rs +++ b/src/libsyntax/ast_map/mod.rs @@ -68,10 +68,15 @@ impl<'a> Iterator<PathElem> for LinkedPath<'a> { } } -// HACK(eddyb) move this into libstd (value wrapper for slice::Items). +#[cfg(stage0)] #[deriving(Clone)] pub struct Values<'a, T>(pub slice::Items<'a, T>); +// HACK(eddyb) move this into libstd (value wrapper for slice::Items). +#[cfg(not(stage0))] +#[deriving(Clone)] +pub struct Values<'a, T:'a>(pub slice::Items<'a, T>); + impl<'a, T: Copy> Iterator<T> for Values<'a, T> { fn next(&mut self) -> Option<T> { let &Values(ref mut items) = self; @@ -478,6 +483,7 @@ impl Map { } } +#[cfg(stage0)] pub struct NodesMatchingSuffix<'a, S> { map: &'a Map, item_name: &'a S, @@ -485,6 +491,14 @@ pub struct NodesMatchingSuffix<'a, S> { idx: NodeId, } +#[cfg(not(stage0))] +pub struct NodesMatchingSuffix<'a, S:'a> { + map: &'a Map, + item_name: &'a S, + in_which: &'a [S], + idx: NodeId, +} + impl<'a,S:Str> NodesMatchingSuffix<'a,S> { /// Returns true only if some suffix of the module path for parent /// matches `self.in_which`. @@ -676,11 +690,7 @@ impl<'a, F: FoldOps> Folder for Ctx<'a, F> { None => {} } } - ItemTrait(_, _, ref traits, ref methods) => { - for t in traits.iter() { - self.insert(t.ref_id, EntryItem(self.parent, i)); - } - + ItemTrait(_, _, _, ref methods) => { for tm in methods.iter() { match *tm { RequiredMethod(ref m) => { diff --git a/src/libsyntax/ast_util.rs b/src/libsyntax/ast_util.rs index 1a4b41404be..cc586a3affa 100644 --- a/src/libsyntax/ast_util.rs +++ b/src/libsyntax/ast_util.rs @@ -349,12 +349,20 @@ pub trait IdVisitingOperation { /// A visitor that applies its operation to all of the node IDs /// in a visitable thing. +#[cfg(stage0)] pub struct IdVisitor<'a, O> { pub operation: &'a O, pub pass_through_items: bool, pub visited_outermost: bool, } +#[cfg(not(stage0))] +pub struct IdVisitor<'a, O:'a> { + pub operation: &'a O, + pub pass_through_items: bool, + pub visited_outermost: bool, +} + impl<'a, O: IdVisitingOperation> IdVisitor<'a, O> { fn visit_generics_helper(&self, generics: &Generics) { for type_parameter in generics.ty_params.iter() { diff --git a/src/libsyntax/attr.rs b/src/libsyntax/attr.rs index 21252619d11..4b2a3073755 100644 --- a/src/libsyntax/attr.rs +++ b/src/libsyntax/attr.rs @@ -418,19 +418,14 @@ pub fn require_unique_names(diagnostic: &SpanHandler, metas: &[Gc<MetaItem>]) { } -/// Fold this over attributes to parse #[repr(...)] forms. +/// Parse #[repr(...)] forms. /// /// Valid repr contents: any of the primitive integral type names (see -/// `int_type_of_word`, below) to specify the discriminant type; and `C`, to use -/// the same discriminant size that the corresponding C enum would. These are -/// not allowed on univariant or zero-variant enums, which have no discriminant. -/// -/// If a discriminant type is so specified, then the discriminant will be -/// present (before fields, if any) with that type; representation -/// optimizations which would remove it will not be done. -pub fn find_repr_attr(diagnostic: &SpanHandler, attr: &Attribute, acc: ReprAttr) - -> ReprAttr { - let mut acc = acc; +/// `int_type_of_word`, below) to specify enum discriminant type; `C`, to use +/// the same discriminant size that the corresponding C enum would or C +/// structure layout, and `packed` to remove padding. +pub fn find_repr_attrs(diagnostic: &SpanHandler, attr: &Attribute) -> Vec<ReprAttr> { + let mut acc = Vec::new(); match attr.node.value.node { ast::MetaList(ref s, ref items) if s.equiv(&("repr")) => { mark_used(attr); @@ -439,28 +434,26 @@ pub fn find_repr_attr(diagnostic: &SpanHandler, attr: &Attribute, acc: ReprAttr) ast::MetaWord(ref word) => { let hint = match word.get() { // Can't use "extern" because it's not a lexical identifier. - "C" => ReprExtern, + "C" => Some(ReprExtern), + "packed" => Some(ReprPacked), _ => match int_type_of_word(word.get()) { - Some(ity) => ReprInt(item.span, ity), + Some(ity) => Some(ReprInt(item.span, ity)), None => { // Not a word we recognize diagnostic.span_err(item.span, "unrecognized representation hint"); - ReprAny + None } } }; - if hint != ReprAny { - if acc == ReprAny { - acc = hint; - } else if acc != hint { - diagnostic.span_warn(item.span, - "conflicting representation hint ignored") - } + + match hint { + Some(h) => acc.push(h), + None => { } } } // Not a word: - _ => diagnostic.span_err(item.span, "unrecognized representation hint") + _ => diagnostic.span_err(item.span, "unrecognized enum representation hint") } } } @@ -490,7 +483,8 @@ fn int_type_of_word(s: &str) -> Option<IntType> { pub enum ReprAttr { ReprAny, ReprInt(Span, IntType), - ReprExtern + ReprExtern, + ReprPacked, } impl ReprAttr { @@ -498,7 +492,8 @@ impl ReprAttr { match *self { ReprAny => false, ReprInt(_sp, ity) => ity.is_ffi_safe(), - ReprExtern => true + ReprExtern => true, + ReprPacked => false } } } @@ -523,7 +518,7 @@ impl IntType { SignedInt(ast::TyI16) | UnsignedInt(ast::TyU16) | SignedInt(ast::TyI32) | UnsignedInt(ast::TyU32) | SignedInt(ast::TyI64) | UnsignedInt(ast::TyU64) => true, - _ => false + SignedInt(ast::TyI) | UnsignedInt(ast::TyU) => false } } } diff --git a/src/libsyntax/diagnostics/plugin.rs b/src/libsyntax/diagnostics/plugin.rs index 209296989fa..25a6a4c01bd 100644 --- a/src/libsyntax/diagnostics/plugin.rs +++ b/src/libsyntax/diagnostics/plugin.rs @@ -45,8 +45,10 @@ fn with_used_diagnostics<T>(f: |&mut HashMap<Name, Span>| -> T) -> T { } } -pub fn expand_diagnostic_used(ecx: &mut ExtCtxt, span: Span, - token_tree: &[TokenTree]) -> Box<MacResult> { +pub fn expand_diagnostic_used<'cx>(ecx: &'cx mut ExtCtxt, + span: Span, + token_tree: &[TokenTree]) + -> Box<MacResult+'cx> { let code = match token_tree { [ast::TTTok(_, token::IDENT(code, _))] => code, _ => unreachable!() @@ -75,8 +77,10 @@ pub fn expand_diagnostic_used(ecx: &mut ExtCtxt, span: Span, MacExpr::new(quote_expr!(ecx, ())) } -pub fn expand_register_diagnostic(ecx: &mut ExtCtxt, span: Span, - token_tree: &[TokenTree]) -> Box<MacResult> { +pub fn expand_register_diagnostic<'cx>(ecx: &'cx mut ExtCtxt, + span: Span, + token_tree: &[TokenTree]) + -> Box<MacResult+'cx> { let (code, description) = match token_tree { [ast::TTTok(_, token::IDENT(ref code, _))] => { (code, None) @@ -101,8 +105,10 @@ pub fn expand_register_diagnostic(ecx: &mut ExtCtxt, span: Span, MacItem::new(quote_item!(ecx, mod $sym {}).unwrap()) } -pub fn expand_build_diagnostic_array(ecx: &mut ExtCtxt, span: Span, - token_tree: &[TokenTree]) -> Box<MacResult> { +pub fn expand_build_diagnostic_array<'cx>(ecx: &'cx mut ExtCtxt, + span: Span, + token_tree: &[TokenTree]) + -> Box<MacResult+'cx> { let name = match token_tree { [ast::TTTok(_, token::IDENT(ref name, _))] => name, _ => unreachable!() diff --git a/src/libsyntax/ext/asm.rs b/src/libsyntax/ext/asm.rs index 13738e658e9..8028d51a7b5 100644 --- a/src/libsyntax/ext/asm.rs +++ b/src/libsyntax/ext/asm.rs @@ -45,8 +45,8 @@ impl State { static OPTIONS: &'static [&'static str] = &["volatile", "alignstack", "intel"]; -pub fn expand_asm(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree]) - -> Box<base::MacResult> { +pub fn expand_asm<'cx>(cx: &'cx mut ExtCtxt, sp: Span, tts: &[ast::TokenTree]) + -> Box<base::MacResult+'cx> { let mut p = cx.new_parser_from_tts(tts); let mut asm = InternedString::new(""); let mut asm_str_style = None; @@ -59,8 +59,6 @@ pub fn expand_asm(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree]) let mut state = Asm; - let mut read_write_operands = Vec::new(); - 'statement: loop { match state { Asm => { @@ -100,8 +98,6 @@ pub fn expand_asm(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree]) let output = match constraint.get().slice_shift_char() { (Some('='), _) => None, (Some('+'), operand) => { - // Save a reference to the output - read_write_operands.push((outputs.len(), out)); Some(token::intern_and_get_ident(format!( "={}", operand).as_slice())) @@ -112,7 +108,8 @@ pub fn expand_asm(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree]) } }; - outputs.push((output.unwrap_or(constraint), out)); + let is_rw = output.is_some(); + outputs.push((output.unwrap_or(constraint), out, is_rw)); } } Inputs => { @@ -154,7 +151,7 @@ pub fn expand_asm(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree]) clobs.push(clob); if OPTIONS.iter().any(|opt| s.equiv(opt)) { - cx.span_warn(p.last_span, "expected a clobber, but found an option"); + cx.span_warn(p.last_span, "expected a clobber, found an option"); } } @@ -202,21 +199,14 @@ pub fn expand_asm(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree]) } } - // Append an input operand, with the form of ("0", expr) - // that links to an output operand. - for &(i, out) in read_write_operands.iter() { - inputs.push((token::intern_and_get_ident(i.to_string().as_slice()), - out)); - } - MacExpr::new(box(GC) ast::Expr { id: ast::DUMMY_NODE_ID, node: ast::ExprInlineAsm(ast::InlineAsm { asm: token::intern_and_get_ident(asm.get()), asm_str_style: asm_str_style.unwrap(), - clobbers: token::intern_and_get_ident(cons.as_slice()), - inputs: inputs, outputs: outputs, + inputs: inputs, + clobbers: token::intern_and_get_ident(cons.as_slice()), volatile: volatile, alignstack: alignstack, dialect: dialect diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs index 01d3920a254..b3b66a6a604 100644 --- a/src/libsyntax/ext/base.rs +++ b/src/libsyntax/ext/base.rs @@ -52,23 +52,23 @@ pub struct BasicMacroExpander { /// Represents a thing that maps token trees to Macro Results pub trait TTMacroExpander { - fn expand(&self, - ecx: &mut ExtCtxt, - span: Span, - token_tree: &[ast::TokenTree]) - -> Box<MacResult>; + fn expand<'cx>(&self, + ecx: &'cx mut ExtCtxt, + span: Span, + token_tree: &[ast::TokenTree]) + -> Box<MacResult+'cx>; } pub type MacroExpanderFn = - fn(ecx: &mut ExtCtxt, span: codemap::Span, token_tree: &[ast::TokenTree]) - -> Box<MacResult>; + fn<'cx>(ecx: &'cx mut ExtCtxt, span: codemap::Span, token_tree: &[ast::TokenTree]) + -> Box<MacResult+'cx>; impl TTMacroExpander for BasicMacroExpander { - fn expand(&self, - ecx: &mut ExtCtxt, - span: Span, - token_tree: &[ast::TokenTree]) - -> Box<MacResult> { + fn expand<'cx>(&self, + ecx: &'cx mut ExtCtxt, + span: Span, + token_tree: &[ast::TokenTree]) + -> Box<MacResult+'cx> { (self.expander)(ecx, span, token_tree) } } @@ -79,27 +79,27 @@ pub struct BasicIdentMacroExpander { } pub trait IdentMacroExpander { - fn expand(&self, - cx: &mut ExtCtxt, - sp: Span, - ident: ast::Ident, - token_tree: Vec<ast::TokenTree> ) - -> Box<MacResult>; + fn expand<'cx>(&self, + cx: &'cx mut ExtCtxt, + sp: Span, + ident: ast::Ident, + token_tree: Vec<ast::TokenTree> ) + -> Box<MacResult+'cx>; } impl IdentMacroExpander for BasicIdentMacroExpander { - fn expand(&self, - cx: &mut ExtCtxt, - sp: Span, - ident: ast::Ident, - token_tree: Vec<ast::TokenTree> ) - -> Box<MacResult> { + fn expand<'cx>(&self, + cx: &'cx mut ExtCtxt, + sp: Span, + ident: ast::Ident, + token_tree: Vec<ast::TokenTree> ) + -> Box<MacResult+'cx> { (self.expander)(cx, sp, ident, token_tree) } } pub type IdentMacroExpanderFn = - fn(&mut ExtCtxt, Span, ast::Ident, Vec<ast::TokenTree>) -> Box<MacResult>; + fn<'cx>(&'cx mut ExtCtxt, Span, ast::Ident, Vec<ast::TokenTree>) -> Box<MacResult+'cx>; /// The result of a macro expansion. The return values of the various /// methods are spliced into the AST at the callsite of the macro (or @@ -146,8 +146,8 @@ pub struct MacExpr { e: Gc<ast::Expr>, } impl MacExpr { - pub fn new(e: Gc<ast::Expr>) -> Box<MacResult> { - box MacExpr { e: e } as Box<MacResult> + pub fn new(e: Gc<ast::Expr>) -> Box<MacResult+'static> { + box MacExpr { e: e } as Box<MacResult+'static> } } impl MacResult for MacExpr { @@ -160,8 +160,8 @@ pub struct MacPat { p: Gc<ast::Pat>, } impl MacPat { - pub fn new(p: Gc<ast::Pat>) -> Box<MacResult> { - box MacPat { p: p } as Box<MacResult> + pub fn new(p: Gc<ast::Pat>) -> Box<MacResult+'static> { + box MacPat { p: p } as Box<MacResult+'static> } } impl MacResult for MacPat { @@ -174,8 +174,8 @@ pub struct MacItem { i: Gc<ast::Item> } impl MacItem { - pub fn new(i: Gc<ast::Item>) -> Box<MacResult> { - box MacItem { i: i } as Box<MacResult> + pub fn new(i: Gc<ast::Item>) -> Box<MacResult+'static> { + box MacItem { i: i } as Box<MacResult+'static> } } impl MacResult for MacItem { @@ -203,8 +203,8 @@ impl DummyResult { /// /// Use this as a return value after hitting any errors and /// calling `span_err`. - pub fn any(sp: Span) -> Box<MacResult> { - box DummyResult { expr_only: false, span: sp } as Box<MacResult> + pub fn any(sp: Span) -> Box<MacResult+'static> { + box DummyResult { expr_only: false, span: sp } as Box<MacResult+'static> } /// Create a default MacResult that can only be an expression. @@ -212,8 +212,8 @@ impl DummyResult { /// Use this for macros that must expand to an expression, so even /// if an error is encountered internally, the user will receive /// an error that they also used it in the wrong place. - pub fn expr(sp: Span) -> Box<MacResult> { - box DummyResult { expr_only: true, span: sp } as Box<MacResult> + pub fn expr(sp: Span) -> Box<MacResult+'static> { + box DummyResult { expr_only: true, span: sp } as Box<MacResult+'static> } /// A plain dummy expression. diff --git a/src/libsyntax/ext/build.rs b/src/libsyntax/ext/build.rs index f7eddca4b7a..909f8f1e78c 100644 --- a/src/libsyntax/ext/build.rs +++ b/src/libsyntax/ext/build.rs @@ -143,12 +143,10 @@ pub trait AstBuilder { fn expr_u8(&self, sp: Span, u: u8) -> Gc<ast::Expr>; fn expr_bool(&self, sp: Span, value: bool) -> Gc<ast::Expr>; - fn expr_vstore(&self, sp: Span, expr: Gc<ast::Expr>, vst: ast::ExprVstore) -> Gc<ast::Expr>; fn expr_vec(&self, sp: Span, exprs: Vec<Gc<ast::Expr>> ) -> Gc<ast::Expr>; fn expr_vec_ng(&self, sp: Span) -> Gc<ast::Expr>; fn expr_vec_slice(&self, sp: Span, exprs: Vec<Gc<ast::Expr>> ) -> Gc<ast::Expr>; fn expr_str(&self, sp: Span, s: InternedString) -> Gc<ast::Expr>; - fn expr_str_uniq(&self, sp: Span, s: InternedString) -> Gc<ast::Expr>; fn expr_some(&self, sp: Span, expr: Gc<ast::Expr>) -> Gc<ast::Expr>; fn expr_none(&self, sp: Span) -> Gc<ast::Expr>; @@ -654,9 +652,6 @@ impl<'a> AstBuilder for ExtCtxt<'a> { self.expr_lit(sp, ast::LitBool(value)) } - fn expr_vstore(&self, sp: Span, expr: Gc<ast::Expr>, vst: ast::ExprVstore) -> Gc<ast::Expr> { - self.expr(sp, ast::ExprVstore(expr, vst)) - } fn expr_vec(&self, sp: Span, exprs: Vec<Gc<ast::Expr>> ) -> Gc<ast::Expr> { self.expr(sp, ast::ExprVec(exprs)) } @@ -669,15 +664,11 @@ impl<'a> AstBuilder for ExtCtxt<'a> { Vec::new()) } fn expr_vec_slice(&self, sp: Span, exprs: Vec<Gc<ast::Expr>> ) -> Gc<ast::Expr> { - self.expr_vstore(sp, self.expr_vec(sp, exprs), ast::ExprVstoreSlice) + self.expr_addr_of(sp, self.expr_vec(sp, exprs)) } fn expr_str(&self, sp: Span, s: InternedString) -> Gc<ast::Expr> { self.expr_lit(sp, ast::LitStr(s, ast::CookedStr)) } - fn expr_str_uniq(&self, sp: Span, s: InternedString) -> Gc<ast::Expr> { - self.expr_vstore(sp, self.expr_str(sp, s), ast::ExprVstoreUniq) - } - fn expr_cast(&self, sp: Span, expr: Gc<ast::Expr>, ty: P<ast::Ty>) -> Gc<ast::Expr> { self.expr(sp, ast::ExprCast(expr, ty)) diff --git a/src/libsyntax/ext/bytes.rs b/src/libsyntax/ext/bytes.rs index 6ea55096348..18367511495 100644 --- a/src/libsyntax/ext/bytes.rs +++ b/src/libsyntax/ext/bytes.rs @@ -17,8 +17,10 @@ use ext::base; use ext::build::AstBuilder; -pub fn expand_syntax_ext(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree]) - -> Box<base::MacResult> { +pub fn expand_syntax_ext<'cx>(cx: &'cx mut ExtCtxt, + sp: Span, + tts: &[ast::TokenTree]) + -> Box<base::MacResult+'cx> { cx.span_warn(sp, "`bytes!` is deprecated, use `b\"foo\"` literals instead"); cx.parse_sess.span_diagnostic.span_note(sp, "see http://doc.rust-lang.org/rust.html#byte-and-byte-string-literals \ diff --git a/src/libsyntax/ext/cfg.rs b/src/libsyntax/ext/cfg.rs index c2930662bc4..0c3a951c982 100644 --- a/src/libsyntax/ext/cfg.rs +++ b/src/libsyntax/ext/cfg.rs @@ -26,8 +26,10 @@ use parse::token::InternedString; use parse::token; -pub fn expand_cfg(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree]) - -> Box<base::MacResult> { +pub fn expand_cfg<'cx>(cx: &mut ExtCtxt, + sp: Span, + tts: &[ast::TokenTree]) + -> Box<base::MacResult+'static> { let mut p = cx.new_parser_from_tts(tts); let mut cfgs = Vec::new(); // parse `cfg!(meta_item, meta_item(x,y), meta_item="foo", ...)` diff --git a/src/libsyntax/ext/concat.rs b/src/libsyntax/ext/concat.rs index dd1153bf666..ea7a4d061c0 100644 --- a/src/libsyntax/ext/concat.rs +++ b/src/libsyntax/ext/concat.rs @@ -19,7 +19,7 @@ use std::string::String; pub fn expand_syntax_ext(cx: &mut base::ExtCtxt, sp: codemap::Span, tts: &[ast::TokenTree]) - -> Box<base::MacResult> { + -> Box<base::MacResult+'static> { let es = match base::get_exprs_from_tts(cx, sp, tts) { Some(e) => e, None => return base::DummyResult::expr(sp) diff --git a/src/libsyntax/ext/concat_idents.rs b/src/libsyntax/ext/concat_idents.rs index 7cf901bbd5e..0ac26a3a904 100644 --- a/src/libsyntax/ext/concat_idents.rs +++ b/src/libsyntax/ext/concat_idents.rs @@ -18,8 +18,8 @@ use parse::token::{str_to_ident}; use std::gc::GC; -pub fn expand_syntax_ext(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree]) - -> Box<base::MacResult> { +pub fn expand_syntax_ext<'cx>(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree]) + -> Box<base::MacResult+'cx> { let mut res_str = String::new(); for (i, e) in tts.iter().enumerate() { if i & 1 == 1 { diff --git a/src/libsyntax/ext/deriving/generic/mod.rs b/src/libsyntax/ext/deriving/generic/mod.rs index 4b185419b40..50bdc296aad 100644 --- a/src/libsyntax/ext/deriving/generic/mod.rs +++ b/src/libsyntax/ext/deriving/generic/mod.rs @@ -407,9 +407,15 @@ impl<'a> TraitDef<'a> { cx.typarambound(p.to_path(cx, self.span, type_ident, generics)) }).collect(); + // require the current trait bounds.push(cx.typarambound(trait_path.clone())); + // also add in any bounds from the declaration + for declared_bound in ty_param.bounds.iter() { + bounds.push((*declared_bound).clone()); + } + cx.typaram(self.span, ty_param.ident, OwnedSlice::from_vec(bounds), diff --git a/src/libsyntax/ext/env.rs b/src/libsyntax/ext/env.rs index b24cfb85794..aae92ae85fc 100644 --- a/src/libsyntax/ext/env.rs +++ b/src/libsyntax/ext/env.rs @@ -23,8 +23,8 @@ use parse::token; use std::os; -pub fn expand_option_env(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree]) - -> Box<base::MacResult> { +pub fn expand_option_env<'cx>(cx: &'cx mut ExtCtxt, sp: Span, tts: &[ast::TokenTree]) + -> Box<base::MacResult+'cx> { let var = match get_single_str_from_tts(cx, sp, tts, "option_env!") { None => return DummyResult::expr(sp), Some(v) => v @@ -59,8 +59,8 @@ pub fn expand_option_env(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree]) MacExpr::new(e) } -pub fn expand_env(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree]) - -> Box<base::MacResult> { +pub fn expand_env<'cx>(cx: &'cx mut ExtCtxt, sp: Span, tts: &[ast::TokenTree]) + -> Box<base::MacResult+'cx> { let exprs = match get_exprs_from_tts(cx, sp, tts) { Some(ref exprs) if exprs.len() == 0 => { cx.span_err(sp, "env! takes 1 or 2 arguments"); diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index d918b28d4dc..9dbea1c9ac2 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -31,6 +31,10 @@ use util::small_vector::SmallVector; use std::gc::{Gc, GC}; +enum Either<L,R> { + Left(L), + Right(R) +} fn expand_expr(e: Gc<ast::Expr>, fld: &mut MacroExpander) -> Gc<ast::Expr> { match e.node { @@ -102,7 +106,8 @@ fn expand_mac_invoc<T>(mac: &ast::Mac, span: &codemap::Span, parse_thunk: |Box<MacResult>|->Option<T>, mark_thunk: |T,Mrk|->T, fld: &mut MacroExpander) - -> Option<T> { + -> Option<T> +{ match (*mac).node { // it would almost certainly be cleaner to pass the whole // macro invocation in, rather than pulling it apart and @@ -149,10 +154,13 @@ fn expand_mac_invoc<T>(mac: &ast::Mac, span: &codemap::Span, // the macro. let mac_span = original_span(fld.cx); - let expanded = expandfun.expand(fld.cx, - mac_span.call_site, - marked_before.as_slice()); - let parsed = match parse_thunk(expanded) { + let opt_parsed = { + let expanded = expandfun.expand(fld.cx, + mac_span.call_site, + marked_before.as_slice()); + parse_thunk(expanded) + }; + let parsed = match opt_parsed { Some(e) => e, None => { fld.cx.span_err( @@ -358,7 +366,8 @@ fn contains_macro_escape(attrs: &[ast::Attribute]) -> bool { // Support for item-position macro invocations, exactly the same // logic as for expression-position macro invocations. fn expand_item_mac(it: Gc<ast::Item>, fld: &mut MacroExpander) - -> SmallVector<Gc<ast::Item>> { + -> SmallVector<Gc<ast::Item>> +{ let (pth, tts) = match it.node { ItemMac(codemap::Spanned { node: MacInvocTT(ref pth, ref tts, _), @@ -372,86 +381,93 @@ fn expand_item_mac(it: Gc<ast::Item>, fld: &mut MacroExpander) let extname = pth.segments.get(0).identifier; let extnamestr = token::get_ident(extname); let fm = fresh_mark(); - let expanded = match fld.cx.syntax_env.find(&extname.name) { - None => { - fld.cx.span_err(pth.span, - format!("macro undefined: '{}!'", - extnamestr).as_slice()); - // let compilation continue - return SmallVector::zero(); - } + let def_or_items = { + let expanded = match fld.cx.syntax_env.find(&extname.name) { + None => { + fld.cx.span_err(pth.span, + format!("macro undefined: '{}!'", + extnamestr).as_slice()); + // let compilation continue + return SmallVector::zero(); + } - Some(rc) => match *rc { - NormalTT(ref expander, span) => { - if it.ident.name != parse::token::special_idents::invalid.name { - fld.cx - .span_err(pth.span, - format!("macro {}! expects no ident argument, \ + Some(rc) => match *rc { + NormalTT(ref expander, span) => { + if it.ident.name != parse::token::special_idents::invalid.name { + fld.cx + .span_err(pth.span, + format!("macro {}! expects no ident argument, \ given '{}'", - extnamestr, - token::get_ident(it.ident)).as_slice()); - return SmallVector::zero(); + extnamestr, + token::get_ident(it.ident)).as_slice()); + return SmallVector::zero(); + } + fld.cx.bt_push(ExpnInfo { + call_site: it.span, + callee: NameAndSpan { + name: extnamestr.get().to_string(), + format: MacroBang, + span: span + } + }); + // mark before expansion: + let marked_before = mark_tts(tts.as_slice(), fm); + expander.expand(fld.cx, it.span, marked_before.as_slice()) } - fld.cx.bt_push(ExpnInfo { - call_site: it.span, - callee: NameAndSpan { - name: extnamestr.get().to_string(), - format: MacroBang, - span: span + IdentTT(ref expander, span) => { + if it.ident.name == parse::token::special_idents::invalid.name { + fld.cx.span_err(pth.span, + format!("macro {}! expects an ident argument", + extnamestr.get()).as_slice()); + return SmallVector::zero(); } - }); - // mark before expansion: - let marked_before = mark_tts(tts.as_slice(), fm); - expander.expand(fld.cx, it.span, marked_before.as_slice()) - } - IdentTT(ref expander, span) => { - if it.ident.name == parse::token::special_idents::invalid.name { - fld.cx.span_err(pth.span, - format!("macro {}! expects an ident argument", - extnamestr.get()).as_slice()); - return SmallVector::zero(); + fld.cx.bt_push(ExpnInfo { + call_site: it.span, + callee: NameAndSpan { + name: extnamestr.get().to_string(), + format: MacroBang, + span: span + } + }); + // mark before expansion: + let marked_tts = mark_tts(tts.as_slice(), fm); + expander.expand(fld.cx, it.span, it.ident, marked_tts) } - fld.cx.bt_push(ExpnInfo { - call_site: it.span, - callee: NameAndSpan { - name: extnamestr.get().to_string(), - format: MacroBang, - span: span + LetSyntaxTT(ref expander, span) => { + if it.ident.name == parse::token::special_idents::invalid.name { + fld.cx.span_err(pth.span, + format!("macro {}! expects an ident argument", + extnamestr.get()).as_slice()); + return SmallVector::zero(); } - }); - // mark before expansion: - let marked_tts = mark_tts(tts.as_slice(), fm); - expander.expand(fld.cx, it.span, it.ident, marked_tts) - } - LetSyntaxTT(ref expander, span) => { - if it.ident.name == parse::token::special_idents::invalid.name { - fld.cx.span_err(pth.span, - format!("macro {}! expects an ident argument", + fld.cx.bt_push(ExpnInfo { + call_site: it.span, + callee: NameAndSpan { + name: extnamestr.get().to_string(), + format: MacroBang, + span: span + } + }); + // DON'T mark before expansion: + expander.expand(fld.cx, it.span, it.ident, tts) + } + _ => { + fld.cx.span_err(it.span, + format!("{}! is not legal in item position", extnamestr.get()).as_slice()); return SmallVector::zero(); } - fld.cx.bt_push(ExpnInfo { - call_site: it.span, - callee: NameAndSpan { - name: extnamestr.get().to_string(), - format: MacroBang, - span: span - } - }); - // DON'T mark before expansion: - expander.expand(fld.cx, it.span, it.ident, tts) - } - _ => { - fld.cx.span_err(it.span, - format!("{}! is not legal in item position", - extnamestr.get()).as_slice()); - return SmallVector::zero(); } + }; + + match expanded.make_def() { + Some(def) => Left(def), + None => Right(expanded.make_items()) } }; - let items = match expanded.make_def() { - Some(MacroDef { name, ext }) => { + let items = match def_or_items { + Left(MacroDef { name, ext }) => { // hidden invariant: this should only be possible as the // result of expanding a LetSyntaxTT, and thus doesn't // need to be marked. Not that it could be marked anyway. @@ -462,23 +478,20 @@ fn expand_item_mac(it: Gc<ast::Item>, fld: &mut MacroExpander) } SmallVector::zero() } - None => { - match expanded.make_items() { - Some(items) => { - items.move_iter() - .map(|i| mark_item(i, fm)) - .flat_map(|i| fld.fold_item(i).move_iter()) - .collect() - } - None => { - fld.cx.span_err(pth.span, - format!("non-item macro in item position: {}", - extnamestr.get()).as_slice()); - return SmallVector::zero(); - } - } + Right(Some(items)) => { + items.move_iter() + .map(|i| mark_item(i, fm)) + .flat_map(|i| fld.fold_item(i).move_iter()) + .collect() + } + Right(None) => { + fld.cx.span_err(pth.span, + format!("non-item macro in item position: {}", + extnamestr.get()).as_slice()); + return SmallVector::zero(); } }; + fld.cx.bt_pop(); return items; } @@ -901,7 +914,7 @@ fn expand_and_rename_fn_decl_and_block(fn_decl: &ast::FnDecl, block: Gc<ast::Blo } /// A tree-folder that performs macro expansion -pub struct MacroExpander<'a, 'b> { +pub struct MacroExpander<'a, 'b:'a> { pub cx: &'a mut ExtCtxt<'b>, } diff --git a/src/libsyntax/ext/fmt.rs b/src/libsyntax/ext/fmt.rs index 4185458bfbe..5352cfaf749 100644 --- a/src/libsyntax/ext/fmt.rs +++ b/src/libsyntax/ext/fmt.rs @@ -19,7 +19,7 @@ use ext::build::AstBuilder; pub fn expand_syntax_ext(ecx: &mut base::ExtCtxt, sp: Span, _tts: &[ast::TokenTree]) - -> Box<base::MacResult> { + -> Box<base::MacResult+'static> { ecx.span_err(sp, "`fmt!` is deprecated, use `format!` instead"); ecx.parse_sess.span_diagnostic.span_note(sp, "see http://doc.rust-lang.org/std/fmt/ \ diff --git a/src/libsyntax/ext/format.rs b/src/libsyntax/ext/format.rs index 835181d55c4..0994abaadc7 100644 --- a/src/libsyntax/ext/format.rs +++ b/src/libsyntax/ext/format.rs @@ -33,7 +33,7 @@ enum Position { Named(String), } -struct Context<'a, 'b> { +struct Context<'a, 'b:'a> { ecx: &'a mut ExtCtxt<'b>, fmtsp: Span, @@ -128,7 +128,7 @@ fn parse_args(ecx: &mut ExtCtxt, sp: Span, allow_method: bool, } _ => { ecx.span_err(p.span, - format!("expected ident for named argument, but found `{}`", + format!("expected ident for named argument, found `{}`", p.this_token_to_string()).as_slice()); return (invocation, None); } @@ -668,8 +668,9 @@ impl<'a, 'b> Context<'a, 'b> { } } -pub fn expand_format_args(ecx: &mut ExtCtxt, sp: Span, - tts: &[ast::TokenTree]) -> Box<base::MacResult> { +pub fn expand_format_args<'cx>(ecx: &'cx mut ExtCtxt, sp: Span, + tts: &[ast::TokenTree]) + -> Box<base::MacResult+'cx> { match parse_args(ecx, sp, false, tts) { (invocation, Some((efmt, args, order, names))) => { @@ -680,8 +681,8 @@ pub fn expand_format_args(ecx: &mut ExtCtxt, sp: Span, } } -pub fn expand_format_args_method(ecx: &mut ExtCtxt, sp: Span, - tts: &[ast::TokenTree]) -> Box<base::MacResult> { +pub fn expand_format_args_method<'cx>(ecx: &'cx mut ExtCtxt, sp: Span, + tts: &[ast::TokenTree]) -> Box<base::MacResult+'cx> { match parse_args(ecx, sp, true, tts) { (invocation, Some((efmt, args, order, names))) => { diff --git a/src/libsyntax/ext/log_syntax.rs b/src/libsyntax/ext/log_syntax.rs index 1f4d087abd0..8df5746e412 100644 --- a/src/libsyntax/ext/log_syntax.rs +++ b/src/libsyntax/ext/log_syntax.rs @@ -15,10 +15,10 @@ use print; use std::rc::Rc; -pub fn expand_syntax_ext(cx: &mut base::ExtCtxt, - sp: codemap::Span, - tt: &[ast::TokenTree]) - -> Box<base::MacResult> { +pub fn expand_syntax_ext<'cx>(cx: &'cx mut base::ExtCtxt, + sp: codemap::Span, + tt: &[ast::TokenTree]) + -> Box<base::MacResult+'cx> { cx.print_backtrace(); println!("{}", print::pprust::tt_to_string(&ast::TTDelim( diff --git a/src/libsyntax/ext/quote.rs b/src/libsyntax/ext/quote.rs index d7d6c20b475..0c41db7ecd6 100644 --- a/src/libsyntax/ext/quote.rs +++ b/src/libsyntax/ext/quote.rs @@ -410,35 +410,36 @@ pub mod rt { } -pub fn expand_quote_tokens(cx: &mut ExtCtxt, - sp: Span, - tts: &[ast::TokenTree]) - -> Box<base::MacResult> { +pub fn expand_quote_tokens<'cx>(cx: &'cx mut ExtCtxt, + sp: Span, + tts: &[ast::TokenTree]) + -> Box<base::MacResult+'cx> { let (cx_expr, expr) = expand_tts(cx, sp, tts); let expanded = expand_wrapper(cx, sp, cx_expr, expr); base::MacExpr::new(expanded) } -pub fn expand_quote_expr(cx: &mut ExtCtxt, - sp: Span, - tts: &[ast::TokenTree]) -> Box<base::MacResult> { +pub fn expand_quote_expr<'cx>(cx: &'cx mut ExtCtxt, + sp: Span, + tts: &[ast::TokenTree]) + -> Box<base::MacResult+'cx> { let expanded = expand_parse_call(cx, sp, "parse_expr", Vec::new(), tts); base::MacExpr::new(expanded) } -pub fn expand_quote_item(cx: &mut ExtCtxt, - sp: Span, - tts: &[ast::TokenTree]) - -> Box<base::MacResult> { +pub fn expand_quote_item<'cx>(cx: &mut ExtCtxt, + sp: Span, + tts: &[ast::TokenTree]) + -> Box<base::MacResult+'cx> { let expanded = expand_parse_call(cx, sp, "parse_item_with_outer_attributes", vec!(), tts); base::MacExpr::new(expanded) } -pub fn expand_quote_pat(cx: &mut ExtCtxt, - sp: Span, - tts: &[ast::TokenTree]) - -> Box<base::MacResult> { +pub fn expand_quote_pat<'cx>(cx: &'cx mut ExtCtxt, + sp: Span, + tts: &[ast::TokenTree]) + -> Box<base::MacResult+'cx> { let expanded = expand_parse_call(cx, sp, "parse_pat", vec!(), tts); base::MacExpr::new(expanded) } @@ -446,7 +447,7 @@ pub fn expand_quote_pat(cx: &mut ExtCtxt, pub fn expand_quote_arm(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree]) - -> Box<base::MacResult> { + -> Box<base::MacResult+'static> { let expanded = expand_parse_call(cx, sp, "parse_arm", vec!(), tts); base::MacExpr::new(expanded) } @@ -454,7 +455,7 @@ pub fn expand_quote_arm(cx: &mut ExtCtxt, pub fn expand_quote_ty(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree]) - -> Box<base::MacResult> { + -> Box<base::MacResult+'static> { let e_param_colons = cx.expr_lit(sp, ast::LitBool(false)); let expanded = expand_parse_call(cx, sp, "parse_ty", vec!(e_param_colons), tts); @@ -464,7 +465,7 @@ pub fn expand_quote_ty(cx: &mut ExtCtxt, pub fn expand_quote_method(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree]) - -> Box<base::MacResult> { + -> Box<base::MacResult+'static> { let e_param_colons = cx.expr_none(sp); let expanded = expand_parse_call(cx, sp, "parse_method", vec!(e_param_colons), tts); @@ -474,7 +475,7 @@ pub fn expand_quote_method(cx: &mut ExtCtxt, pub fn expand_quote_stmt(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree]) - -> Box<base::MacResult> { + -> Box<base::MacResult+'static> { let e_attrs = cx.expr_vec_ng(sp); let expanded = expand_parse_call(cx, sp, "parse_stmt", vec!(e_attrs), tts); diff --git a/src/libsyntax/ext/source_util.rs b/src/libsyntax/ext/source_util.rs index 703adcbd335..5cc0ec4a122 100644 --- a/src/libsyntax/ext/source_util.rs +++ b/src/libsyntax/ext/source_util.rs @@ -29,7 +29,7 @@ use std::rc::Rc; /// line!(): expands to the current line number pub fn expand_line(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree]) - -> Box<base::MacResult> { + -> Box<base::MacResult+'static> { base::check_zero_tts(cx, sp, tts, "line!"); let topmost = topmost_expn_info(cx.backtrace().unwrap()); @@ -40,7 +40,7 @@ pub fn expand_line(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree]) /* col!(): expands to the current column number */ pub fn expand_col(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree]) - -> Box<base::MacResult> { + -> Box<base::MacResult+'static> { base::check_zero_tts(cx, sp, tts, "col!"); let topmost = topmost_expn_info(cx.backtrace().unwrap()); @@ -52,7 +52,7 @@ pub fn expand_col(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree]) /// The filemap (`loc.file`) contains a bunch more information we could spit /// out if we wanted. pub fn expand_file(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree]) - -> Box<base::MacResult> { + -> Box<base::MacResult+'static> { base::check_zero_tts(cx, sp, tts, "file!"); let topmost = topmost_expn_info(cx.backtrace().unwrap()); @@ -62,14 +62,14 @@ pub fn expand_file(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree]) } pub fn expand_stringify(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree]) - -> Box<base::MacResult> { + -> Box<base::MacResult+'static> { let s = pprust::tts_to_string(tts); base::MacExpr::new(cx.expr_str(sp, token::intern_and_get_ident(s.as_slice()))) } pub fn expand_mod(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree]) - -> Box<base::MacResult> { + -> Box<base::MacResult+'static> { base::check_zero_tts(cx, sp, tts, "module_path!"); let string = cx.mod_path() .iter() @@ -85,7 +85,7 @@ pub fn expand_mod(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree]) /// This is generally a bad idea because it's going to behave /// unhygienically. pub fn expand_include(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree]) - -> Box<base::MacResult> { + -> Box<base::MacResult+'static> { let file = match get_single_str_from_tts(cx, sp, tts, "include!") { Some(f) => f, None => return DummyResult::expr(sp), @@ -105,7 +105,7 @@ pub fn expand_include(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree]) // include_str! : read the given file, insert it as a literal string expr pub fn expand_include_str(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree]) - -> Box<base::MacResult> { + -> Box<base::MacResult+'static> { let file = match get_single_str_from_tts(cx, sp, tts, "include_str!") { Some(f) => f, None => return DummyResult::expr(sp) @@ -141,7 +141,7 @@ pub fn expand_include_str(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree]) } pub fn expand_include_bin(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree]) - -> Box<base::MacResult> { + -> Box<base::MacResult+'static> { let file = match get_single_str_from_tts(cx, sp, tts, "include_bin!") { Some(f) => f, None => return DummyResult::expr(sp) diff --git a/src/libsyntax/ext/trace_macros.rs b/src/libsyntax/ext/trace_macros.rs index 77324632664..1f50eb933bb 100644 --- a/src/libsyntax/ext/trace_macros.rs +++ b/src/libsyntax/ext/trace_macros.rs @@ -18,7 +18,7 @@ use parse::token::{keywords, is_keyword}; pub fn expand_trace_macros(cx: &mut ExtCtxt, sp: Span, tt: &[ast::TokenTree]) - -> Box<base::MacResult> { + -> Box<base::MacResult+'static> { match tt { [ast::TTTok(_, ref tok)] if is_keyword(keywords::True, tok) => { cx.set_trace_macros(true); diff --git a/src/libsyntax/ext/tt/macro_rules.rs b/src/libsyntax/ext/tt/macro_rules.rs index 1eb37abb781..d8f0eb32ad7 100644 --- a/src/libsyntax/ext/tt/macro_rules.rs +++ b/src/libsyntax/ext/tt/macro_rules.rs @@ -14,7 +14,6 @@ use ast; use codemap::{Span, Spanned, DUMMY_SP}; use ext::base::{ExtCtxt, MacResult, MacroDef}; use ext::base::{NormalTT, TTMacroExpander}; -use ext::base; use ext::tt::macro_parser::{Success, Error, Failure}; use ext::tt::macro_parser::{NamedMatch, MatchedSeq, MatchedNonterminal}; use ext::tt::macro_parser::{parse, parse_or_else}; @@ -113,11 +112,11 @@ struct MacroRulesMacroExpander { } impl TTMacroExpander for MacroRulesMacroExpander { - fn expand(&self, - cx: &mut ExtCtxt, - sp: Span, - arg: &[ast::TokenTree]) - -> Box<MacResult> { + fn expand<'cx>(&self, + cx: &'cx mut ExtCtxt, + sp: Span, + arg: &[ast::TokenTree]) + -> Box<MacResult+'cx> { generic_extension(cx, sp, self.name, @@ -137,13 +136,13 @@ impl MacResult for MacroRulesDefiner { } /// Given `lhses` and `rhses`, this is the new macro we create -fn generic_extension(cx: &ExtCtxt, - sp: Span, - name: Ident, - arg: &[ast::TokenTree], - lhses: &[Rc<NamedMatch>], - rhses: &[Rc<NamedMatch>]) - -> Box<MacResult> { +fn generic_extension<'cx>(cx: &'cx ExtCtxt, + sp: Span, + name: Ident, + arg: &[ast::TokenTree], + lhses: &[Rc<NamedMatch>], + rhses: &[Rc<NamedMatch>]) + -> Box<MacResult+'cx> { if cx.trace_macros() { println!("{}! {} {} {}", token::get_ident(name), @@ -195,7 +194,7 @@ fn generic_extension(cx: &ExtCtxt, // Weird, but useful for X-macros. return box ParserAnyMacro { parser: RefCell::new(p), - } as Box<MacResult> + } as Box<MacResult+'cx> } Failure(sp, ref msg) => if sp.lo >= best_fail_spot.lo { best_fail_spot = sp; @@ -213,11 +212,11 @@ fn generic_extension(cx: &ExtCtxt, /// This procedure performs the expansion of the /// macro_rules! macro. It parses the RHS and adds /// an extension to the current context. -pub fn add_new_extension(cx: &mut ExtCtxt, - sp: Span, - name: Ident, - arg: Vec<ast::TokenTree> ) - -> Box<base::MacResult> { +pub fn add_new_extension<'cx>(cx: &'cx mut ExtCtxt, + sp: Span, + name: Ident, + arg: Vec<ast::TokenTree> ) + -> Box<MacResult+'cx> { // these spans won't matter, anyways fn ms(m: Matcher_) -> Matcher { Spanned { @@ -274,5 +273,5 @@ pub fn add_new_extension(cx: &mut ExtCtxt, name: token::get_ident(name).to_string(), ext: NormalTT(exp, Some(sp)) })) - } as Box<MacResult> + } as Box<MacResult+'cx> } diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs index 9ad28f02e80..be1c0d96711 100644 --- a/src/libsyntax/fold.rs +++ b/src/libsyntax/fold.rs @@ -227,15 +227,20 @@ pub trait Folder { noop_fold_variant_arg(va, self) } - fn fold_ty_param_bound(&mut self, tpb: &TyParamBound) -> TyParamBound { - noop_fold_ty_param_bound(tpb, self) - } - fn fold_opt_bounds(&mut self, b: &Option<OwnedSlice<TyParamBound>>) -> Option<OwnedSlice<TyParamBound>> { noop_fold_opt_bounds(b, self) } + fn fold_bounds(&mut self, b: &OwnedSlice<TyParamBound>) + -> OwnedSlice<TyParamBound> { + noop_fold_bounds(b, self) + } + + fn fold_ty_param_bound(&mut self, tpb: &TyParamBound) -> TyParamBound { + noop_fold_ty_param_bound(tpb, self) + } + fn fold_mt(&mut self, mt: &MutTy) -> MutTy { noop_fold_mt(mt, self) } @@ -349,20 +354,20 @@ pub fn noop_fold_ty<T: Folder>(t: P<Ty>, fld: &mut T) -> P<Ty> { TyRptr(ref region, ref mt) => { TyRptr(fld.fold_opt_lifetime(region), fld.fold_mt(mt)) } - TyClosure(ref f, ref region) => { + TyClosure(ref f) => { TyClosure(box(GC) ClosureTy { fn_style: f.fn_style, onceness: f.onceness, - bounds: fld.fold_opt_bounds(&f.bounds), + bounds: fld.fold_bounds(&f.bounds), decl: fld.fold_fn_decl(&*f.decl), lifetimes: fld.fold_lifetime_defs(f.lifetimes.as_slice()), - }, fld.fold_opt_lifetime(region)) + }) } TyProc(ref f) => { TyProc(box(GC) ClosureTy { fn_style: f.fn_style, onceness: f.onceness, - bounds: fld.fold_opt_bounds(&f.bounds), + bounds: fld.fold_bounds(&f.bounds), decl: fld.fold_fn_decl(&*f.decl), lifetimes: fld.fold_lifetime_defs(f.lifetimes.as_slice()), }) @@ -648,14 +653,13 @@ pub fn noop_fold_ty_param_bound<T: Folder>(tpb: &TyParamBound, fld: &mut T) -> TyParamBound { match *tpb { TraitTyParamBound(ref ty) => TraitTyParamBound(fld.fold_trait_ref(ty)), - StaticRegionTyParamBound => StaticRegionTyParamBound, + RegionTyParamBound(ref lifetime) => RegionTyParamBound(fld.fold_lifetime(lifetime)), UnboxedFnTyParamBound(ref unboxed_function_type) => { UnboxedFnTyParamBound(UnboxedFnTy { decl: fld.fold_fn_decl(&*unboxed_function_type.decl), kind: unboxed_function_type.kind, }) } - OtherRegionTyParamBound(s) => OtherRegionTyParamBound(s) } } @@ -664,7 +668,7 @@ pub fn noop_fold_ty_param<T: Folder>(tp: &TyParam, fld: &mut T) -> TyParam { TyParam { ident: tp.ident, id: id, - bounds: tp.bounds.map(|x| fld.fold_ty_param_bound(x)), + bounds: fld.fold_bounds(&tp.bounds), unbound: tp.unbound.as_ref().map(|x| fld.fold_ty_param_bound(x)), default: tp.default.map(|x| fld.fold_ty(x)), span: tp.span @@ -792,11 +796,12 @@ pub fn noop_fold_mt<T: Folder>(mt: &MutTy, folder: &mut T) -> MutTy { pub fn noop_fold_opt_bounds<T: Folder>(b: &Option<OwnedSlice<TyParamBound>>, folder: &mut T) -> Option<OwnedSlice<TyParamBound>> { - b.as_ref().map(|bounds| { - bounds.map(|bound| { - folder.fold_ty_param_bound(bound) - }) - }) + b.as_ref().map(|bounds| folder.fold_bounds(bounds)) +} + +fn noop_fold_bounds<T: Folder>(bounds: &TyParamBounds, folder: &mut T) + -> TyParamBounds { + bounds.map(|bound| folder.fold_ty_param_bound(bound)) } pub fn noop_fold_variant_arg<T: Folder>(va: &VariantArg, folder: &mut T) -> VariantArg { @@ -889,7 +894,8 @@ pub fn noop_fold_item_underscore<T: Folder>(i: &Item_, folder: &mut T) -> Item_ }).collect() ) } - ItemTrait(ref generics, ref unbound, ref traits, ref methods) => { + ItemTrait(ref generics, ref unbound, ref bounds, ref methods) => { + let bounds = folder.fold_bounds(bounds); let methods = methods.iter().flat_map(|method| { let r = match *method { RequiredMethod(ref m) => { @@ -911,7 +917,7 @@ pub fn noop_fold_item_underscore<T: Folder>(i: &Item_, folder: &mut T) -> Item_ }).collect(); ItemTrait(folder.fold_generics(generics), unbound.clone(), - traits.iter().map(|p| folder.fold_trait_ref(p)).collect(), + bounds, methods) } ItemMac(ref m) => ItemMac(folder.fold_mac(m)), @@ -1088,9 +1094,6 @@ pub fn noop_fold_pat<T: Folder>(p: Gc<Pat>, folder: &mut T) -> Gc<Pat> { pub fn noop_fold_expr<T: Folder>(e: Gc<Expr>, folder: &mut T) -> Gc<Expr> { let id = folder.new_id(e.id); let node = match e.node { - ExprVstore(e, v) => { - ExprVstore(folder.fold_expr(e), v) - } ExprBox(p, e) => { ExprBox(folder.fold_expr(p), folder.fold_expr(e)) } @@ -1189,8 +1192,8 @@ pub fn noop_fold_expr<T: Folder>(e: Gc<Expr>, folder: &mut T) -> Gc<Expr> { inputs: a.inputs.iter().map(|&(ref c, input)| { ((*c).clone(), folder.fold_expr(input)) }).collect(), - outputs: a.outputs.iter().map(|&(ref c, out)| { - ((*c).clone(), folder.fold_expr(out)) + outputs: a.outputs.iter().map(|&(ref c, out, is_rw)| { + ((*c).clone(), folder.fold_expr(out), is_rw) }).collect(), .. (*a).clone() }) diff --git a/src/libsyntax/lib.rs b/src/libsyntax/lib.rs index 291c876082f..9bbd6b2a36e 100644 --- a/src/libsyntax/lib.rs +++ b/src/libsyntax/lib.rs @@ -25,6 +25,7 @@ #![feature(macro_rules, globs, managed_boxes, default_type_params, phase)] #![feature(quote, struct_variant, unsafe_destructor, import_shadowing)] +#![feature(issue_5723_bootstrap)] #![allow(deprecated)] // NOTE(stage0, pcwalton): Remove after snapshot. diff --git a/src/libsyntax/parse/attr.rs b/src/libsyntax/parse/attr.rs index 550dbfdab71..eca02d06ca9 100644 --- a/src/libsyntax/parse/attr.rs +++ b/src/libsyntax/parse/attr.rs @@ -92,7 +92,7 @@ impl<'a> ParserAttr for Parser<'a> { } _ => { let token_str = self.this_token_to_string(); - self.fatal(format!("expected `#` but found `{}`", + self.fatal(format!("expected `#`, found `{}`", token_str).as_slice()); } }; diff --git a/src/libsyntax/parse/mod.rs b/src/libsyntax/parse/mod.rs index e76e4adcd7f..585b98925cc 100644 --- a/src/libsyntax/parse/mod.rs +++ b/src/libsyntax/parse/mod.rs @@ -412,14 +412,21 @@ pub fn str_lit(lit: &str) -> String { loop { match chars.next() { Some((i, c)) => { - let em = error(i); match c { '\\' => { - if chars.peek().expect(em.as_slice()).val1() == '\n' { + let ch = chars.peek().unwrap_or_else(|| { + fail!("{}", error(i).as_slice()) + }).val1(); + + if ch == '\n' { eat(&mut chars); - } else if chars.peek().expect(em.as_slice()).val1() == '\r' { + } else if ch == '\r' { chars.next(); - if chars.peek().expect(em.as_slice()).val1() != '\n' { + let ch = chars.peek().unwrap_or_else(|| { + fail!("{}", error(i).as_slice()) + }).val1(); + + if ch != '\n' { fail!("lexer accepted bare CR"); } eat(&mut chars); @@ -433,7 +440,11 @@ pub fn str_lit(lit: &str) -> String { } }, '\r' => { - if chars.peek().expect(em.as_slice()).val1() != '\n' { + let ch = chars.peek().unwrap_or_else(|| { + fail!("{}", error(i).as_slice()) + }).val1(); + + if ch != '\n' { fail!("lexer accepted bare CR"); } chars.next(); @@ -1126,7 +1137,8 @@ mod test { let item = parse_item_from_source_str(name.clone(), source, Vec::new(), &sess).unwrap(); let docs = item.attrs.iter().filter(|a| a.name().get() == "doc") .map(|a| a.value_str().unwrap().get().to_string()).collect::<Vec<_>>(); - assert_eq!(docs.as_slice(), &["/// doc comment".to_string(), "/// line 2".to_string()]); + let b: &[_] = &["/// doc comment".to_string(), "/// line 2".to_string()]; + assert_eq!(docs.as_slice(), b); let source = "/** doc comment\r\n * with CRLF */\r\nfn foo() {}".to_string(); let item = parse_item_from_source_str(name, source, Vec::new(), &sess).unwrap(); diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 9e2829e6380..37bda15ac2c 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -12,7 +12,7 @@ use abi; use ast::{BareFnTy, ClosureTy}; -use ast::{StaticRegionTyParamBound, OtherRegionTyParamBound, TraitTyParamBound}; +use ast::{RegionTyParamBound, TraitTyParamBound}; use ast::{ProvidedMethod, Public, FnStyle}; use ast::{Mod, BiAdd, Arg, Arm, Attribute, BindByRef, BindByValue}; use ast::{BiBitAnd, BiBitOr, BiBitXor, Block}; @@ -27,9 +27,8 @@ use ast::{ExprField, ExprFnBlock, ExprIf, ExprIndex}; use ast::{ExprLit, ExprLoop, ExprMac}; use ast::{ExprMethodCall, ExprParen, ExprPath, ExprProc}; use ast::{ExprRepeat, ExprRet, ExprStruct, ExprTup, ExprUnary, ExprUnboxedFn}; -use ast::{ExprVec, ExprVstore, ExprVstoreSlice}; -use ast::{ExprVstoreMutSlice, ExprWhile, ExprForLoop, Field, FnDecl}; -use ast::{ExprVstoreUniq, Once, Many}; +use ast::{ExprVec, ExprWhile, ExprForLoop, Field, FnDecl}; +use ast::{Once, Many}; use ast::{FnUnboxedClosureKind, FnMutUnboxedClosureKind}; use ast::{FnOnceUnboxedClosureKind}; use ast::{ForeignItem, ForeignItemStatic, ForeignItemFn, ForeignMod}; @@ -62,7 +61,7 @@ use ast::{UnsafeFn, ViewItem, ViewItem_, ViewItemExternCrate, ViewItemUse}; use ast::{ViewPath, ViewPathGlob, ViewPathList, ViewPathSimple}; use ast::{Visibility, WhereClause, WherePredicate}; use ast; -use ast_util::{as_prec, ident_to_path, lit_is_str, operator_prec}; +use ast_util::{as_prec, ident_to_path, operator_prec}; use ast_util; use attr; use codemap::{Span, BytePos, Spanned, spanned, mk_sp}; @@ -71,7 +70,7 @@ use parse; use parse::attr::ParserAttr; use parse::classify; use parse::common::{SeqSep, seq_sep_none}; -use parse::common::{seq_sep_trailing_disallowed, seq_sep_trailing_allowed}; +use parse::common::{seq_sep_trailing_allowed}; use parse::lexer::Reader; use parse::lexer::TokenAndSpan; use parse::obsolete::*; @@ -86,6 +85,7 @@ use std::collections::HashSet; use std::mem::replace; use std::rc::Rc; use std::gc::{Gc, GC}; +use std::iter; #[allow(non_camel_case_types)] #[deriving(PartialEq)] @@ -120,7 +120,7 @@ pub enum PathParsingMode { /// A path paired with optional type bounds. pub struct PathAndBounds { pub path: ast::Path, - pub bounds: Option<OwnedSlice<TyParamBound>>, + pub bounds: Option<ast::TyParamBounds>, } enum ItemOrViewItem { @@ -309,7 +309,7 @@ pub struct Parser<'a> { pub tokens_consumed: uint, pub restriction: restriction, pub quote_depth: uint, // not (yet) related to the quasiquoter - pub reader: Box<Reader>, + pub reader: Box<Reader+'a>, pub interner: Rc<token::IdentInterner>, /// The set of seen errors about obsolete syntax. Used to suppress /// extra detail when the same error is seen twice @@ -346,8 +346,11 @@ fn real_token(rdr: &mut Reader) -> TokenAndSpan { } impl<'a> Parser<'a> { - pub fn new(sess: &'a ParseSess, cfg: ast::CrateConfig, - mut rdr: Box<Reader>) -> Parser<'a> { + pub fn new(sess: &'a ParseSess, + cfg: ast::CrateConfig, + mut rdr: Box<Reader+'a>) + -> Parser<'a> + { let tok0 = real_token(rdr); let span = tok0.sp; let placeholder = TokenAndSpan { @@ -413,7 +416,7 @@ impl<'a> Parser<'a> { } else { let token_str = Parser::token_to_string(t); let this_token_str = self.this_token_to_string(); - self.fatal(format!("expected `{}` but found `{}`", + self.fatal(format!("expected `{}`, found `{}`", token_str, this_token_str).as_slice()) } @@ -447,11 +450,11 @@ impl<'a> Parser<'a> { let actual = self.this_token_to_string(); self.fatal( (if expected.len() != 1 { - (format!("expected one of `{}` but found `{}`", + (format!("expected one of `{}`, found `{}`", expect, actual)) } else { - (format!("expected `{}` but found `{}`", + (format!("expected `{}`, found `{}`", expect, actual)) }).as_slice() @@ -762,20 +765,26 @@ impl<'a> Parser<'a> { sep: Option<token::Token>, f: |&mut Parser| -> T) -> OwnedSlice<T> { - let mut first = true; let mut v = Vec::new(); - while self.token != token::GT - && self.token != token::BINOP(token::SHR) - && self.token != token::GE - && self.token != token::BINOPEQ(token::SHR) { - match sep { - Some(ref t) => { - if first { first = false; } - else { self.expect(t); } - } - _ => () + // This loop works by alternating back and forth between parsing types + // and commas. For example, given a string `A, B,>`, the parser would + // first parse `A`, then a comma, then `B`, then a comma. After that it + // would encounter a `>` and stop. This lets the parser handle trailing + // commas in generic parameters, because it can stop either after + // parsing a type or after parsing a comma. + for i in iter::count(0u, 1) { + if self.token == token::GT + || self.token == token::BINOP(token::SHR) + || self.token == token::GE + || self.token == token::BINOPEQ(token::SHR) { + break; + } + + if i % 2 == 0 { + v.push(f(self)); + } else { + sep.as_ref().map(|t| self.expect(t)); } - v.push(f(self)); } return OwnedSlice::from_vec(v); } @@ -1067,14 +1076,7 @@ impl<'a> Parser<'a> { }; let (inputs, variadic) = self.parse_fn_args(false, false); - let bounds = { - if self.eat(&token::COLON) { - let (_, bounds) = self.parse_ty_param_bounds(false); - Some(bounds) - } else { - None - } - }; + let bounds = self.parse_colon_then_ty_param_bounds(); let (ret_style, ret_ty) = self.parse_ret_ty(); let decl = P(FnDecl { inputs: inputs, @@ -1162,14 +1164,7 @@ impl<'a> Parser<'a> { (optional_unboxed_closure_kind, inputs) }; - let (region, bounds) = { - if self.eat(&token::COLON) { - let (region, bounds) = self.parse_ty_param_bounds(true); - (region, Some(bounds)) - } else { - (None, None) - } - }; + let bounds = self.parse_colon_then_ty_param_bounds(); let (return_style, output) = self.parse_ret_ty(); let decl = P(FnDecl { @@ -1193,7 +1188,7 @@ impl<'a> Parser<'a> { bounds: bounds, decl: decl, lifetimes: lifetime_defs, - }, region) + }) } } } @@ -1314,7 +1309,7 @@ impl<'a> Parser<'a> { _ => { let token_str = p.this_token_to_string(); - p.fatal((format!("expected `;` or `{{` but found `{}`", + p.fatal((format!("expected `;` or `{{`, found `{}`", token_str)).as_slice()) } } @@ -1421,13 +1416,16 @@ impl<'a> Parser<'a> { } else if self.token == token::TILDE { // OWNED POINTER self.bump(); - let last_span = self.last_span; + let span = self.last_span; match self.token { - token::LBRACKET => - self.obsolete(last_span, ObsoleteOwnedVector), - _ => self.obsolete(last_span, ObsoleteOwnedType), - }; - TyUniq(self.parse_ty(true)) + token::IDENT(ref ident, _) + if "str" == token::get_ident(*ident).get() => { + // This is OK (for now). + } + token::LBRACKET => {} // Also OK. + _ => self.obsolete(span, ObsoleteOwnedType) + } + TyUniq(self.parse_ty(false)) } else if self.token == token::BINOP(token::STAR) { // STAR POINTER (bare pointer?) self.bump(); @@ -1678,7 +1676,7 @@ impl<'a> Parser<'a> { Some(INTERPOLATED(token::NtPath(box path))) => { return PathAndBounds { path: path, - bounds: None, + bounds: None } } _ => {} @@ -1735,25 +1733,31 @@ impl<'a> Parser<'a> { } } - // Next, parse a plus and bounded type parameters, if applicable. - let bounds = if mode == LifetimeAndTypesAndBounds { - let bounds = { - if self.eat(&token::BINOP(token::PLUS)) { - let (_, bounds) = self.parse_ty_param_bounds(false); - if bounds.len() == 0 { - let last_span = self.last_span; - self.span_err(last_span, - "at least one type parameter bound \ - must be specified after the `+`"); - } - Some(bounds) - } else { - None + // Next, parse a plus and bounded type parameters, if + // applicable. We need to remember whether the separate was + // present for later, because in some contexts it's a parse + // error. + let opt_bounds = { + if mode == LifetimeAndTypesAndBounds && + self.eat(&token::BINOP(token::PLUS)) + { + let bounds = self.parse_ty_param_bounds(); + + // For some reason that I do not fully understand, we + // do not permit an empty list in the case where it is + // introduced by a `+`, but we do for `:` and other + // separators. -nmatsakis + if bounds.len() == 0 { + let last_span = self.last_span; + self.span_err(last_span, + "at least one type parameter bound \ + must be specified"); } - }; - bounds - } else { - None + + Some(bounds) + } else { + None + } }; // Assemble the span. @@ -1766,7 +1770,7 @@ impl<'a> Parser<'a> { global: is_global, segments: segments, }, - bounds: bounds, + bounds: opt_bounds, } } @@ -2266,7 +2270,7 @@ impl<'a> Parser<'a> { let mut es = self.parse_unspanned_seq( &token::LPAREN, &token::RPAREN, - seq_sep_trailing_disallowed(token::COMMA), + seq_sep_trailing_allowed(token::COMMA), |p| p.parse_expr() ); hi = self.last_span.hi; @@ -2542,16 +2546,7 @@ impl<'a> Parser<'a> { let m = self.parse_mutability(); let e = self.parse_prefix_expr(); hi = e.span.hi; - // HACK: turn &[...] into a &-vec - ex = match e.node { - ExprVec(..) if m == MutImmutable => { - ExprVstore(e, ExprVstoreSlice) - } - ExprVec(..) if m == MutMutable => { - ExprVstore(e, ExprVstoreMutSlice) - } - _ => ExprAddrOf(m, e) - }; + ex = ExprAddrOf(m, e); } token::AT => { self.bump(); @@ -2563,61 +2558,43 @@ impl<'a> Parser<'a> { } token::TILDE => { self.bump(); + let span = self.last_span; + match self.token { + token::LIT_STR(_) => { + // This is OK (for now). + } + token::LBRACKET => {} // Also OK. + _ => self.obsolete(span, ObsoleteOwnedExpr) + } let e = self.parse_prefix_expr(); hi = e.span.hi; - // HACK: turn ~[...] into a ~-vec - let last_span = self.last_span; - ex = match e.node { - ExprVec(..) | ExprRepeat(..) => { - self.obsolete(last_span, ObsoleteOwnedVector); - ExprVstore(e, ExprVstoreUniq) - } - ExprLit(lit) if lit_is_str(lit) => { - self.obsolete(last_span, ObsoleteOwnedExpr); - ExprVstore(e, ExprVstoreUniq) - } - _ => { - self.obsolete(last_span, ObsoleteOwnedExpr); - self.mk_unary(UnUniq, e) - } - }; + ex = self.mk_unary(UnUniq, e); } token::IDENT(_, _) => { - if self.is_keyword(keywords::Box) { - self.bump(); + if !self.is_keyword(keywords::Box) { + return self.parse_dot_or_call_expr(); + } - // Check for a place: `box(PLACE) EXPR`. - if self.eat(&token::LPAREN) { - // Support `box() EXPR` as the default. - if !self.eat(&token::RPAREN) { - let place = self.parse_expr(); - self.expect(&token::RPAREN); - let subexpression = self.parse_prefix_expr(); - hi = subexpression.span.hi; - ex = ExprBox(place, subexpression); - return self.mk_expr(lo, hi, ex); - } + self.bump(); + + // Check for a place: `box(PLACE) EXPR`. + if self.eat(&token::LPAREN) { + // Support `box() EXPR` as the default. + if !self.eat(&token::RPAREN) { + let place = self.parse_expr(); + self.expect(&token::RPAREN); + let subexpression = self.parse_prefix_expr(); + hi = subexpression.span.hi; + ex = ExprBox(place, subexpression); + return self.mk_expr(lo, hi, ex); } + } - // Otherwise, we use the unique pointer default. - let subexpression = self.parse_prefix_expr(); - hi = subexpression.span.hi; - // HACK: turn `box [...]` into a boxed-vec - ex = match subexpression.node { - ExprVec(..) | ExprRepeat(..) => { - let last_span = self.last_span; - self.obsolete(last_span, ObsoleteOwnedVector); - ExprVstore(subexpression, ExprVstoreUniq) - } - ExprLit(lit) if lit_is_str(lit) => { - ExprVstore(subexpression, ExprVstoreUniq) - } - _ => self.mk_unary(UnUniq, subexpression) - }; - } else { - return self.parse_dot_or_call_expr() - } + // Otherwise, we use the unique pointer default. + let subexpression = self.parse_prefix_expr(); + hi = subexpression.span.hi; + ex = self.mk_unary(UnUniq, subexpression); } _ => return self.parse_dot_or_call_expr() } @@ -3196,7 +3173,7 @@ impl<'a> Parser<'a> { args = self.parse_enum_variant_seq( &token::LPAREN, &token::RPAREN, - seq_sep_trailing_disallowed(token::COMMA), + seq_sep_trailing_allowed(token::COMMA), |p| p.parse_pat() ); pat = PatEnum(enum_path, Some(args)); @@ -3375,7 +3352,7 @@ impl<'a> Parser<'a> { "" }; let tok_str = self.this_token_to_string(); - self.fatal(format!("expected {}`(` or `{{`, but found `{}`", + self.fatal(format!("expected {}`(` or `{{`, found `{}`", ident_str, tok_str).as_slice()) } @@ -3622,45 +3599,34 @@ impl<'a> Parser<'a> { } } - /// matches optbounds = ( ( : ( boundseq )? )? ) - /// where boundseq = ( bound + boundseq ) | bound - /// and bound = 'static | ty - /// Returns "None" if there's no colon (e.g. "T"); - /// Returns "Some(Empty)" if there's a colon but nothing after (e.g. "T:") - /// Returns "Some(stuff)" otherwise (e.g. "T:stuff"). - /// NB: The None/Some distinction is important for issue #7264. - /// - /// Note that the `allow_any_lifetime` argument is a hack for now while the - /// AST doesn't support arbitrary lifetimes in bounds on type parameters. In - /// the future, this flag should be removed, and the return value of this - /// function should be Option<~[TyParamBound]> - fn parse_ty_param_bounds(&mut self, allow_any_lifetime: bool) - -> (Option<ast::Lifetime>, - OwnedSlice<TyParamBound>) { - let mut ret_lifetime = None; + // Parses a sequence of bounds if a `:` is found, + // otherwise returns empty list. + fn parse_colon_then_ty_param_bounds(&mut self) + -> OwnedSlice<TyParamBound> + { + if !self.eat(&token::COLON) { + OwnedSlice::empty() + } else { + self.parse_ty_param_bounds() + } + } + + // matches bounds = ( boundseq )? + // where boundseq = ( bound + boundseq ) | bound + // and bound = 'region | ty + // NB: The None/Some distinction is important for issue #7264. + fn parse_ty_param_bounds(&mut self) + -> OwnedSlice<TyParamBound> + { let mut result = vec!(); loop { match self.token { token::LIFETIME(lifetime) => { - let lifetime_interned_string = token::get_ident(lifetime); - if lifetime_interned_string.equiv(&("'static")) { - result.push(StaticRegionTyParamBound); - if allow_any_lifetime && ret_lifetime.is_none() { - ret_lifetime = Some(ast::Lifetime { - id: ast::DUMMY_NODE_ID, - span: self.span, - name: lifetime.name - }); - } - } else if allow_any_lifetime && ret_lifetime.is_none() { - ret_lifetime = Some(ast::Lifetime { - id: ast::DUMMY_NODE_ID, - span: self.span, - name: lifetime.name - }); - } else { - result.push(OtherRegionTyParamBound(self.span)); - } + result.push(RegionTyParamBound(ast::Lifetime { + id: ast::DUMMY_NODE_ID, + span: self.span, + name: lifetime.name + })); self.bump(); } token::MOD_SEP | token::IDENT(..) => { @@ -3680,7 +3646,7 @@ impl<'a> Parser<'a> { } } - return (ret_lifetime, OwnedSlice::from_vec(result)); + return OwnedSlice::from_vec(result); } fn trait_ref_from_ident(ident: Ident, span: Span) -> ast::TraitRef { @@ -3717,16 +3683,7 @@ impl<'a> Parser<'a> { ident = self.parse_ident(); } - let opt_bounds = { - if self.eat(&token::COLON) { - let (_, bounds) = self.parse_ty_param_bounds(false); - Some(bounds) - } else { - None - } - }; - // For typarams we don't care about the difference b/w "<T>" and "<T:>". - let bounds = opt_bounds.unwrap_or_default(); + let bounds = self.parse_colon_then_ty_param_bounds(); let default = if self.token == token::EQ { self.bump(); @@ -3815,7 +3772,7 @@ impl<'a> Parser<'a> { }; self.expect(&token::COLON); - let (_, bounds) = self.parse_ty_param_bounds(false); + let bounds = self.parse_ty_param_bounds(); let hi = self.span.hi; let span = mk_sp(lo, hi); @@ -3924,7 +3881,7 @@ impl<'a> Parser<'a> { }, _ => { let token_str = self.this_token_to_string(); - self.fatal(format!("expected `self` but found `{}`", + self.fatal(format!("expected `self`, found `{}`", token_str).as_slice()) } } @@ -4068,7 +4025,7 @@ impl<'a> Parser<'a> { match self.token { token::COMMA => { self.bump(); - let sep = seq_sep_trailing_disallowed(token::COMMA); + let sep = seq_sep_trailing_allowed(token::COMMA); let mut fn_inputs = self.parse_seq_to_before_end( &token::RPAREN, sep, @@ -4091,7 +4048,7 @@ impl<'a> Parser<'a> { let fn_inputs = match explicit_self { SelfStatic => { - let sep = seq_sep_trailing_disallowed(token::COMMA); + let sep = seq_sep_trailing_allowed(token::COMMA); self.parse_seq_to_before_end(&token::RPAREN, sep, parse_arg_fn) } SelfValue(id) => parse_remaining_arguments!(id), @@ -4128,7 +4085,7 @@ impl<'a> Parser<'a> { self.parse_optional_unboxed_closure_kind(); let args = self.parse_seq_to_before_end( &token::BINOP(token::OR), - seq_sep_trailing_disallowed(token::COMMA), + seq_sep_trailing_allowed(token::COMMA), |p| p.parse_fn_block_arg() ); self.bump(); @@ -4291,19 +4248,13 @@ impl<'a> Parser<'a> { let mut tps = self.parse_generics(); let sized = self.parse_for_sized(); - // Parse traits, if necessary. - let traits; - if self.token == token::COLON { - self.bump(); - traits = self.parse_trait_ref_list(&token::LBRACE); - } else { - traits = Vec::new(); - } + // Parse supertrait bounds. + let bounds = self.parse_colon_then_ty_param_bounds(); self.parse_where_clause(&mut tps); let meths = self.parse_trait_methods(); - (ident, ItemTrait(tps, sized, traits, meths), None) + (ident, ItemTrait(tps, sized, bounds, meths), None) } fn parse_impl_items(&mut self) -> (Vec<ImplItem>, Vec<Attribute>) { @@ -4337,12 +4288,10 @@ impl<'a> Parser<'a> { // New-style trait. Reinterpret the type as a trait. let opt_trait_ref = match ty.node { TyPath(ref path, None, node_id) => { - Some(TraitRef { - path: /* bad */ (*path).clone(), - ref_id: node_id - }) + Some(TraitRef { path: (*path).clone(), + ref_id: node_id }) } - TyPath(..) => { + TyPath(_, Some(_), _) => { self.span_err(ty.span, "bounded traits are only valid in type position"); None @@ -4377,15 +4326,6 @@ impl<'a> Parser<'a> { } } - /// Parse B + C<String,int> + D - fn parse_trait_ref_list(&mut self, ket: &token::Token) -> Vec<TraitRef> { - self.parse_seq_to_before_end( - ket, - seq_sep_trailing_disallowed(token::BINOP(token::PLUS)), - |p| p.parse_trait_ref() - ) - } - /// Parse struct Foo { ... } fn parse_item_struct(&mut self, is_virtual: bool) -> ItemInfo { let class_name = self.parse_ident(); @@ -4455,7 +4395,7 @@ impl<'a> Parser<'a> { } else { let token_str = self.this_token_to_string(); self.fatal(format!("expected `{}`, `(`, or `;` after struct \ - name but found `{}`", "{", + name, found `{}`", "{", token_str).as_slice()) } @@ -4486,7 +4426,7 @@ impl<'a> Parser<'a> { let span = self.span; let token_str = self.this_token_to_string(); self.span_fatal(span, - format!("expected `,`, or `}}` but found `{}`", + format!("expected `,`, or `}}`, found `{}`", token_str).as_slice()) } } @@ -4566,7 +4506,7 @@ impl<'a> Parser<'a> { } _ => { let token_str = self.this_token_to_string(); - self.fatal(format!("expected item but found `{}`", + self.fatal(format!("expected item, found `{}`", token_str).as_slice()) } } @@ -4825,7 +4765,8 @@ impl<'a> Parser<'a> { /// # Example /// /// extern crate url; - /// extern crate foo = "bar"; + /// extern crate foo = "bar"; //deprecated + /// extern crate "bar" as foo; fn parse_item_extern_crate(&mut self, lo: BytePos, visibility: Visibility, @@ -4836,6 +4777,8 @@ impl<'a> Parser<'a> { token::IDENT(..) => { let the_ident = self.parse_ident(); self.expect_one_of(&[], &[token::EQ, token::SEMI]); + // NOTE - #16689 change this to a warning once + // the 'as' support is in stage0 let path = if self.token == token::EQ { self.bump(); Some(self.parse_str()) @@ -4843,7 +4786,14 @@ impl<'a> Parser<'a> { self.expect(&token::SEMI); (path, the_ident) - } + }, + token::LIT_STR(..) | token::LIT_STR_RAW(..) => { + let path = self.parse_str(); + self.expect_keyword(keywords::As); + let the_ident = self.parse_ident(); + self.expect(&token::SEMI); + (Some(path), the_ident) + }, _ => { let span = self.span; let token_str = self.this_token_to_string(); @@ -4950,7 +4900,7 @@ impl<'a> Parser<'a> { let arg_tys = self.parse_enum_variant_seq( &token::LPAREN, &token::RPAREN, - seq_sep_trailing_disallowed(token::COMMA), + seq_sep_trailing_allowed(token::COMMA), |p| p.parse_ty(true) ); for ty in arg_tys.move_iter() { @@ -5107,7 +5057,7 @@ impl<'a> Parser<'a> { let span = self.span; let token_str = self.this_token_to_string(); self.span_fatal(span, - format!("expected `{}` or `fn` but found `{}`", "{", + format!("expected `{}` or `fn`, found `{}`", "{", token_str).as_slice()); } diff --git a/src/libsyntax/print/pp.rs b/src/libsyntax/print/pp.rs index f28e6829b00..70da4e11961 100644 --- a/src/libsyntax/print/pp.rs +++ b/src/libsyntax/print/pp.rs @@ -155,7 +155,7 @@ pub struct PrintStackElem { static SIZE_INFINITY: int = 0xffff; -pub fn mk_printer(out: Box<io::Writer>, linewidth: uint) -> Printer { +pub fn mk_printer(out: Box<io::Writer+'static>, linewidth: uint) -> Printer { // Yes 3, it makes the ring buffers big enough to never // fall behind. let n: uint = 3 * linewidth; @@ -260,7 +260,7 @@ pub fn mk_printer(out: Box<io::Writer>, linewidth: uint) -> Printer { /// the method called 'pretty_print', and the 'PRINT' process is the method /// called 'print'. pub struct Printer { - pub out: Box<io::Writer>, + pub out: Box<io::Writer+'static>, buf_len: uint, /// Width of lines we're constrained to margin: int, diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index 26d425563d0..da265d81250 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -10,8 +10,8 @@ use abi; use ast::{FnMutUnboxedClosureKind, FnOnceUnboxedClosureKind}; -use ast::{FnUnboxedClosureKind, MethodImplItem, P, OtherRegionTyParamBound}; -use ast::{StaticRegionTyParamBound, TraitTyParamBound, UnboxedClosureKind}; +use ast::{FnUnboxedClosureKind, MethodImplItem, P}; +use ast::{RegionTyParamBound, TraitTyParamBound, UnboxedClosureKind}; use ast::{UnboxedFnTyParamBound, RequiredMethod, ProvidedMethod}; use ast; use ast_util; @@ -60,16 +60,16 @@ pub struct State<'a> { literals: Option<Vec<comments::Literal> >, cur_cmnt_and_lit: CurrentCommentAndLiteral, boxes: Vec<pp::Breaks>, - ann: &'a PpAnn, + ann: &'a PpAnn+'a, encode_idents_with_hygiene: bool, } -pub fn rust_printer(writer: Box<io::Writer>) -> State<'static> { +pub fn rust_printer(writer: Box<io::Writer+'static>) -> State<'static> { static NO_ANN: NoAnn = NoAnn; rust_printer_annotated(writer, &NO_ANN) } -pub fn rust_printer_annotated<'a>(writer: Box<io::Writer>, +pub fn rust_printer_annotated<'a>(writer: Box<io::Writer+'static>, ann: &'a PpAnn) -> State<'a> { State { s: pp::mk_printer(writer, default_columns), @@ -98,7 +98,7 @@ pub fn print_crate<'a>(cm: &'a CodeMap, krate: &ast::Crate, filename: String, input: &mut io::Reader, - out: Box<io::Writer>, + out: Box<io::Writer+'static>, ann: &'a PpAnn, is_expanded: bool) -> IoResult<()> { let mut s = State::new_from_input(cm, @@ -118,7 +118,7 @@ impl<'a> State<'a> { span_diagnostic: &diagnostic::SpanHandler, filename: String, input: &mut io::Reader, - out: Box<io::Writer>, + out: Box<io::Writer+'static>, ann: &'a PpAnn, is_expanded: bool) -> State<'a> { let (cmnts, lits) = comments::gather_comments_and_literals( @@ -138,7 +138,7 @@ impl<'a> State<'a> { } pub fn new(cm: &'a CodeMap, - out: Box<io::Writer>, + out: Box<io::Writer+'static>, ann: &'a PpAnn, comments: Option<Vec<comments::Comment>>, literals: Option<Vec<comments::Literal>>) -> State<'a> { @@ -159,6 +159,7 @@ impl<'a> State<'a> { } pub fn to_string(f: |&mut State| -> IoResult<()>) -> String { + use std::raw::TraitObject; let mut s = rust_printer(box MemWriter::new()); f(&mut s).unwrap(); eof(&mut s.s).unwrap(); @@ -166,7 +167,8 @@ pub fn to_string(f: |&mut State| -> IoResult<()>) -> String { // FIXME(pcwalton): A nasty function to extract the string from an `io::Writer` // that we "know" to be a `MemWriter` that works around the lack of checked // downcasts. - let (_, wr): (uint, Box<MemWriter>) = mem::transmute_copy(&s.s.out); + let obj: TraitObject = mem::transmute_copy(&s.s.out); + let wr: Box<MemWriter> = mem::transmute(obj.data); let result = String::from_utf8(Vec::from_slice(wr.get_ref().as_slice())).unwrap(); mem::forget(wr); @@ -592,17 +594,16 @@ impl<'a> State<'a> { }; try!(self.print_ty_fn(Some(f.abi), None, - &None, f.fn_style, ast::Many, &*f.decl, None, - &None, + &OwnedSlice::empty(), Some(&generics), None, None)); } - ast::TyClosure(f, ref region) => { + ast::TyClosure(f) => { let generics = ast::Generics { lifetimes: f.lifetimes.clone(), ty_params: OwnedSlice::empty(), @@ -613,7 +614,6 @@ impl<'a> State<'a> { }; try!(self.print_ty_fn(None, Some('&'), - region, f.fn_style, f.onceness, &*f.decl, @@ -634,7 +634,6 @@ impl<'a> State<'a> { }; try!(self.print_ty_fn(None, Some('~'), - &None, f.fn_style, f.onceness, &*f.decl, @@ -647,12 +646,11 @@ impl<'a> State<'a> { ast::TyUnboxedFn(f) => { try!(self.print_ty_fn(None, None, - &None, ast::NormalFn, ast::Many, &*f.decl, None, - &None, + &OwnedSlice::empty(), None, None, Some(f.kind))); @@ -835,7 +833,7 @@ impl<'a> State<'a> { } try!(self.bclose(item.span)); } - ast::ItemTrait(ref generics, ref unbound, ref traits, ref methods) => { + ast::ItemTrait(ref generics, ref unbound, ref bounds, ref methods) => { try!(self.head(visibility_qualified(item.vis, "trait").as_slice())); try!(self.print_ident(item.ident)); @@ -849,16 +847,7 @@ impl<'a> State<'a> { } _ => {} } - if traits.len() != 0u { - try!(word(&mut self.s, ":")); - for (i, trait_) in traits.iter().enumerate() { - try!(self.nbsp()); - if i != 0 { - try!(self.word_space("+")); - } - try!(self.print_path(&trait_.path, false)); - } - } + try!(self.print_bounds(":", bounds)); try!(self.print_where_clause(generics)); try!(word(&mut self.s, " ")); try!(self.bopen()); @@ -1071,12 +1060,11 @@ impl<'a> State<'a> { try!(self.print_outer_attributes(m.attrs.as_slice())); try!(self.print_ty_fn(None, None, - &None, m.fn_style, ast::Many, &*m.decl, Some(m.ident), - &None, + &OwnedSlice::empty(), Some(&m.generics), Some(m.explicit_self.node), None)); @@ -1321,16 +1309,6 @@ impl<'a> State<'a> { } } - pub fn print_expr_vstore(&mut self, t: ast::ExprVstore) -> IoResult<()> { - match t { - ast::ExprVstoreUniq => word(&mut self.s, "box "), - ast::ExprVstoreSlice => word(&mut self.s, "&"), - ast::ExprVstoreMutSlice => { - try!(word(&mut self.s, "&")); - word(&mut self.s, "mut") - } - } - } fn print_call_post(&mut self, args: &[Gc<ast::Expr>]) -> IoResult<()> { try!(self.popen()); @@ -1355,10 +1333,6 @@ impl<'a> State<'a> { try!(self.ibox(indent_unit)); try!(self.ann.pre(self, NodeExpr(expr))); match expr.node { - ast::ExprVstore(ref e, v) => { - try!(self.print_expr_vstore(v)); - try!(self.print_expr(&**e)); - }, ast::ExprBox(ref p, ref e) => { try!(word(&mut self.s, "box")); try!(word(&mut self.s, "(")); @@ -1671,8 +1645,14 @@ impl<'a> State<'a> { try!(self.word_space(":")); try!(self.commasep(Inconsistent, a.outputs.as_slice(), - |s, &(ref co, ref o)| { - try!(s.print_string(co.get(), ast::CookedStr)); + |s, &(ref co, ref o, is_rw)| { + match co.get().slice_shift_char() { + (Some('='), operand) if is_rw => { + try!(s.print_string(format!("+{}", operand).as_slice(), + ast::CookedStr)) + } + _ => try!(s.print_string(co.get(), ast::CookedStr)) + } try!(s.popen()); try!(s.print_expr(&**o)); try!(s.pclose()); @@ -1814,7 +1794,7 @@ impl<'a> State<'a> { match *opt_bounds { None => Ok(()), - Some(ref bounds) => self.print_bounds(&None, bounds, true, true), + Some(ref bounds) => self.print_bounds("+", bounds) } } @@ -2138,30 +2118,12 @@ impl<'a> State<'a> { } pub fn print_bounds(&mut self, - region: &Option<ast::Lifetime>, - bounds: &OwnedSlice<ast::TyParamBound>, - print_colon_anyway: bool, - print_plus_before_bounds: bool) + prefix: &str, + bounds: &OwnedSlice<ast::TyParamBound>) -> IoResult<()> { - let separator = if print_plus_before_bounds { - "+" - } else { - ":" - }; - if !bounds.is_empty() || region.is_some() { - try!(word(&mut self.s, separator)); + if !bounds.is_empty() { + try!(word(&mut self.s, prefix)); let mut first = true; - match *region { - Some(ref lt) => { - let token = token::get_name(lt.name); - if token.get() != "'static" { - try!(self.nbsp()); - first = false; - try!(self.print_lifetime(lt)); - } - } - None => {} - } for bound in bounds.iter() { try!(self.nbsp()); if first { @@ -2171,27 +2133,27 @@ impl<'a> State<'a> { } try!(match *bound { - TraitTyParamBound(ref tref) => self.print_trait_ref(tref), - StaticRegionTyParamBound => word(&mut self.s, "'static"), + TraitTyParamBound(ref tref) => { + self.print_trait_ref(tref) + } + RegionTyParamBound(ref lt) => { + self.print_lifetime(lt) + } UnboxedFnTyParamBound(ref unboxed_function_type) => { self.print_ty_fn(None, None, - &None, ast::NormalFn, ast::Many, &*unboxed_function_type.decl, None, - &None, + &OwnedSlice::empty(), None, None, Some(unboxed_function_type.kind)) } - OtherRegionTyParamBound(_) => Ok(()) }) } Ok(()) - } else if print_colon_anyway { - word(&mut self.s, separator) } else { Ok(()) } @@ -2218,23 +2180,29 @@ impl<'a> State<'a> { Ok(()) } - fn print_type_parameters(&mut self, - lifetimes: &[ast::LifetimeDef], - ty_params: &[ast::TyParam]) - -> IoResult<()> { - let total = lifetimes.len() + ty_params.len(); + pub fn print_generics(&mut self, + generics: &ast::Generics) + -> IoResult<()> + { + let total = generics.lifetimes.len() + generics.ty_params.len(); + if total == 0 { + return Ok(()); + } + + try!(word(&mut self.s, "<")); + let mut ints = Vec::new(); for i in range(0u, total) { ints.push(i); } - self.commasep(Inconsistent, ints.as_slice(), |s, &idx| { - if idx < lifetimes.len() { - let lifetime = &lifetimes[idx]; + try!(self.commasep(Inconsistent, ints.as_slice(), |s, &idx| { + if idx < generics.lifetimes.len() { + let lifetime = generics.lifetimes.get(idx); s.print_lifetime_def(lifetime) } else { - let idx = idx - lifetimes.len(); - let param = &ty_params[idx]; + let idx = idx - generics.lifetimes.len(); + let param = generics.ty_params.get(idx); match param.unbound { Some(TraitTyParamBound(ref tref)) => { try!(s.print_trait_ref(tref)); @@ -2243,10 +2211,7 @@ impl<'a> State<'a> { _ => {} } try!(s.print_ident(param.ident)); - try!(s.print_bounds(&None, - ¶m.bounds, - false, - false)); + try!(s.print_bounds(":", ¶m.bounds)); match param.default { Some(ref default) => { try!(space(&mut s.s)); @@ -2256,19 +2221,10 @@ impl<'a> State<'a> { _ => Ok(()) } } - }) - } + })); - pub fn print_generics(&mut self, generics: &ast::Generics) - -> IoResult<()> { - if generics.lifetimes.len() + generics.ty_params.len() > 0 { - try!(word(&mut self.s, "<")); - try!(self.print_type_parameters(generics.lifetimes.as_slice(), - generics.ty_params.as_slice())); - word(&mut self.s, ">") - } else { - Ok(()) - } + try!(word(&mut self.s, ">")); + Ok(()) } pub fn print_where_clause(&mut self, generics: &ast::Generics) @@ -2289,7 +2245,7 @@ impl<'a> State<'a> { } try!(self.print_ident(predicate.ident)); - try!(self.print_bounds(&None, &predicate.bounds, false, false)); + try!(self.print_bounds(":", &predicate.bounds)); } Ok(()) @@ -2369,13 +2325,13 @@ impl<'a> State<'a> { match item.node { ast::ViewItemExternCrate(id, ref optional_path, _) => { try!(self.head("extern crate")); - try!(self.print_ident(id)); for &(ref p, style) in optional_path.iter() { + try!(self.print_string(p.get(), style)); try!(space(&mut self.s)); - try!(word(&mut self.s, "=")); + try!(word(&mut self.s, "as")); try!(space(&mut self.s)); - try!(self.print_string(p.get(), style)); } + try!(self.print_ident(id)); } ast::ViewItemUse(ref vp) => { @@ -2427,12 +2383,11 @@ impl<'a> State<'a> { pub fn print_ty_fn(&mut self, opt_abi: Option<abi::Abi>, opt_sigil: Option<char>, - opt_region: &Option<ast::Lifetime>, fn_style: ast::FnStyle, onceness: ast::Onceness, decl: &ast::FnDecl, id: Option<ast::Ident>, - opt_bounds: &Option<OwnedSlice<ast::TyParamBound>>, + bounds: &OwnedSlice<ast::TyParamBound>, generics: Option<&ast::Generics>, opt_explicit_self: Option<ast::ExplicitSelf_>, opt_unboxed_closure_kind: @@ -2501,9 +2456,7 @@ impl<'a> State<'a> { try!(self.pclose()); } - opt_bounds.as_ref().map(|bounds| { - self.print_bounds(opt_region, bounds, true, false) - }); + try!(self.print_bounds(":", bounds)); try!(self.maybe_print_comment(decl.output.span.lo)); diff --git a/src/libsyntax/util/small_vector.rs b/src/libsyntax/util/small_vector.rs index 43367611ab2..517c5e5bf47 100644 --- a/src/libsyntax/util/small_vector.rs +++ b/src/libsyntax/util/small_vector.rs @@ -64,7 +64,10 @@ impl<T> SmallVector<T> { pub fn as_slice<'a>(&'a self) -> &'a [T] { match self.repr { - Zero => &[], + Zero => { + let result: &[T] = &[]; + result + } One(ref v) => slice::ref_slice(v), Many(ref vs) => vs.as_slice() } diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs index ac1dbdc439c..7a35d82b0e4 100644 --- a/src/libsyntax/visit.rs +++ b/src/libsyntax/visit.rs @@ -298,13 +298,9 @@ pub fn walk_item<E: Clone, V: Visitor<E>>(visitor: &mut V, item: &Item, env: E) item.id, env.clone()) } - ItemTrait(ref generics, _, ref trait_paths, ref methods) => { + ItemTrait(ref generics, _, ref bounds, ref methods) => { visitor.visit_generics(generics, env.clone()); - for trait_path in trait_paths.iter() { - visitor.visit_path(&trait_path.path, - trait_path.ref_id, - env.clone()) - } + walk_ty_param_bounds(visitor, bounds, env.clone()); for method in methods.iter() { visitor.visit_trait_item(method, env.clone()) } @@ -375,18 +371,13 @@ pub fn walk_ty<E: Clone, V: Visitor<E>>(visitor: &mut V, typ: &Ty, env: E) { visitor.visit_ty(&*tuple_element_type, env.clone()) } } - TyClosure(ref function_declaration, ref region) => { + TyClosure(ref function_declaration) => { for argument in function_declaration.decl.inputs.iter() { visitor.visit_ty(&*argument.ty, env.clone()) } visitor.visit_ty(&*function_declaration.decl.output, env.clone()); - for bounds in function_declaration.bounds.iter() { - walk_ty_param_bounds(visitor, bounds, env.clone()) - } - visitor.visit_opt_lifetime_ref( - typ.span, - region, - env.clone()); + walk_ty_param_bounds(visitor, &function_declaration.bounds, + env.clone()); walk_lifetime_decls(visitor, &function_declaration.lifetimes, env.clone()); } @@ -395,9 +386,8 @@ pub fn walk_ty<E: Clone, V: Visitor<E>>(visitor: &mut V, typ: &Ty, env: E) { visitor.visit_ty(&*argument.ty, env.clone()) } visitor.visit_ty(&*function_declaration.decl.output, env.clone()); - for bounds in function_declaration.bounds.iter() { - walk_ty_param_bounds(visitor, bounds, env.clone()) - } + walk_ty_param_bounds(visitor, &function_declaration.bounds, + env.clone()); walk_lifetime_decls(visitor, &function_declaration.lifetimes, env.clone()); } @@ -415,10 +405,13 @@ pub fn walk_ty<E: Clone, V: Visitor<E>>(visitor: &mut V, typ: &Ty, env: E) { } visitor.visit_ty(&*function_declaration.decl.output, env.clone()); } - TyPath(ref path, ref bounds, id) => { + TyPath(ref path, ref opt_bounds, id) => { visitor.visit_path(path, id, env.clone()); - for bounds in bounds.iter() { - walk_ty_param_bounds(visitor, bounds, env.clone()) + match *opt_bounds { + Some(ref bounds) => { + walk_ty_param_bounds(visitor, bounds, env.clone()); + } + None => { } } } TyFixedLengthVec(ref ty, ref expression) => { @@ -532,7 +525,6 @@ pub fn walk_ty_param_bounds<E: Clone, V: Visitor<E>>(visitor: &mut V, TraitTyParamBound(ref typ) => { walk_trait_ref_helper(visitor, typ, env.clone()) } - StaticRegionTyParamBound => {} UnboxedFnTyParamBound(ref function_declaration) => { for argument in function_declaration.decl.inputs.iter() { visitor.visit_ty(&*argument.ty, env.clone()) @@ -540,7 +532,9 @@ pub fn walk_ty_param_bounds<E: Clone, V: Visitor<E>>(visitor: &mut V, visitor.visit_ty(&*function_declaration.decl.output, env.clone()); } - OtherRegionTyParamBound(..) => {} + RegionTyParamBound(ref lifetime) => { + visitor.visit_lifetime_ref(lifetime, env.clone()); + } } } } @@ -729,9 +723,6 @@ pub fn walk_mac<E, V: Visitor<E>>(_: &mut V, _: &Mac, _: E) { pub fn walk_expr<E: Clone, V: Visitor<E>>(visitor: &mut V, expression: &Expr, env: E) { match expression.node { - ExprVstore(ref subexpression, _) => { - visitor.visit_expr(&**subexpression, env.clone()) - } ExprBox(ref place, ref subexpression) => { visitor.visit_expr(&**place, env.clone()); visitor.visit_expr(&**subexpression, env.clone()) @@ -854,11 +845,11 @@ pub fn walk_expr<E: Clone, V: Visitor<E>>(visitor: &mut V, expression: &Expr, en ExprParen(ref subexpression) => { visitor.visit_expr(&**subexpression, env.clone()) } - ExprInlineAsm(ref assembler) => { - for &(_, ref input) in assembler.inputs.iter() { + ExprInlineAsm(ref ia) => { + for &(_, ref input) in ia.inputs.iter() { visitor.visit_expr(&**input, env.clone()) } - for &(_, ref output) in assembler.outputs.iter() { + for &(_, ref output, _) in ia.outputs.iter() { visitor.visit_expr(&**output, env.clone()) } } diff --git a/src/libterm/terminfo/parser/compiled.rs b/src/libterm/terminfo/parser/compiled.rs index 94ed7fbbf30..2826ecc1a12 100644 --- a/src/libterm/terminfo/parser/compiled.rs +++ b/src/libterm/terminfo/parser/compiled.rs @@ -184,7 +184,7 @@ pub fn parse(file: &mut io::Reader, longnames: bool) // Check magic number let magic = try!(file.read_le_u16()); if magic != 0x011A { - return Err(format!("invalid magic number: expected {:x} but found {:x}", + return Err(format!("invalid magic number: expected {:x}, found {:x}", 0x011Au, magic as uint)); } diff --git a/src/libterm/win.rs b/src/libterm/win.rs index 00e90fc3cde..64a61cc6b3b 100644 --- a/src/libterm/win.rs +++ b/src/libterm/win.rs @@ -10,7 +10,7 @@ //! Windows console handling -// FIXME (#13400): this is only a tiny fraction of the win32 console api +// FIXME (#13400): this is only a tiny fraction of the Windows console api extern crate libc; diff --git a/src/libtest/lib.rs b/src/libtest/lib.rs index 46e2ca03ef6..4790e3833b7 100644 --- a/src/libtest/lib.rs +++ b/src/libtest/lib.rs @@ -135,10 +135,10 @@ pub trait TDynBenchFn { pub enum TestFn { StaticTestFn(fn()), StaticBenchFn(fn(&mut Bencher)), - StaticMetricFn(proc(&mut MetricMap)), + StaticMetricFn(proc(&mut MetricMap):'static), DynTestFn(proc():Send), - DynMetricFn(proc(&mut MetricMap)), - DynBenchFn(Box<TDynBenchFn>) + DynMetricFn(proc(&mut MetricMap):'static), + DynBenchFn(Box<TDynBenchFn+'static>) } impl TestFn { @@ -1103,8 +1103,8 @@ fn calc_result(desc: &TestDesc, task_succeeded: bool) -> TestResult { impl ToJson for Metric { fn to_json(&self) -> json::Json { let mut map = TreeMap::new(); - map.insert("value".to_string(), json::Number(self.value)); - map.insert("noise".to_string(), json::Number(self.noise)); + map.insert("value".to_string(), json::F64(self.value)); + map.insert("noise".to_string(), json::F64(self.noise)); json::Object(map) } } diff --git a/src/libtime/lib.rs b/src/libtime/lib.rs index d0b4c6b1e4c..429a16ec5c8 100644 --- a/src/libtime/lib.rs +++ b/src/libtime/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT +// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // @@ -1060,7 +1060,6 @@ pub fn strftime(format: &str, tm: &Tm) -> String { 'w' => (tm.tm_wday as int).to_string(), 'Y' => (tm.tm_year as int + 1900).to_string(), 'y' => format!("{:02d}", (tm.tm_year as int + 1900) % 100), - 'Z' => "".to_string(), // FIXME(pcwalton): Implement this. 'z' => { let sign = if tm.tm_gmtoff > 0_i32 { '+' } else { '-' }; let mut m = num::abs(tm.tm_gmtoff) / 60_i32; diff --git a/src/libunicode/u_char.rs b/src/libunicode/u_char.rs index a927c364ff9..0f75cf86c18 100644 --- a/src/libunicode/u_char.rs +++ b/src/libunicode/u_char.rs @@ -20,7 +20,13 @@ use tables::{derived_property, property, general_category, conversions, charwidt /// Returns whether the specified `char` is considered a Unicode alphabetic /// code point -pub fn is_alphabetic(c: char) -> bool { derived_property::Alphabetic(c) } +pub fn is_alphabetic(c: char) -> bool { + match c { + 'a' .. 'z' | 'A' .. 'Z' => true, + c if c > '\x7f' => derived_property::Alphabetic(c), + _ => false + } +} /// Returns whether the specified `char` satisfies the 'XID_Start' Unicode property /// @@ -44,7 +50,13 @@ pub fn is_XID_continue(c: char) -> bool { derived_property::XID_Continue(c) } /// This is defined according to the terms of the Unicode Derived Core Property 'Lowercase'. /// #[inline] -pub fn is_lowercase(c: char) -> bool { derived_property::Lowercase(c) } +pub fn is_lowercase(c: char) -> bool { + match c { + 'a' .. 'z' => true, + c if c > '\x7f' => derived_property::Lowercase(c), + _ => false + } +} /// /// Indicates whether a `char` is in upper case @@ -52,7 +64,13 @@ pub fn is_lowercase(c: char) -> bool { derived_property::Lowercase(c) } /// This is defined according to the terms of the Unicode Derived Core Property 'Uppercase'. /// #[inline] -pub fn is_uppercase(c: char) -> bool { derived_property::Uppercase(c) } +pub fn is_uppercase(c: char) -> bool { + match c { + 'A' .. 'Z' => true, + c if c > '\x7f' => derived_property::Uppercase(c), + _ => false + } +} /// /// Indicates whether a `char` is whitespace @@ -61,10 +79,11 @@ pub fn is_uppercase(c: char) -> bool { derived_property::Uppercase(c) } /// #[inline] pub fn is_whitespace(c: char) -> bool { - // As an optimization ASCII whitespace characters are checked separately - c == ' ' - || ('\x09' <= c && c <= '\x0d') - || property::White_Space(c) + match c { + ' ' | '\x09' .. '\x0d' => true, + c if c > '\x7f' => property::White_Space(c), + _ => false + } } /// @@ -75,8 +94,8 @@ pub fn is_whitespace(c: char) -> bool { /// #[inline] pub fn is_alphanumeric(c: char) -> bool { - derived_property::Alphabetic(c) - || general_category::N(c) + is_alphabetic(c) + || is_digit(c) } /// @@ -91,7 +110,11 @@ pub fn is_control(c: char) -> bool { general_category::Cc(c) } /// Indicates whether the `char` is numeric (Nd, Nl, or No) #[inline] pub fn is_digit(c: char) -> bool { - general_category::N(c) + match c { + '0' .. '9' => true, + c if c > '\x7f' => general_category::N(c), + _ => false + } } /// Convert a char to its uppercase equivalent diff --git a/src/libunicode/u_str.rs b/src/libunicode/u_str.rs index 262721bd636..c71cf5557e3 100644 --- a/src/libunicode/u_str.rs +++ b/src/libunicode/u_str.rs @@ -46,9 +46,11 @@ pub trait UnicodeStrSlice<'a> { /// /// ```rust /// let gr1 = "a\u0310e\u0301o\u0308\u0332".graphemes(true).collect::<Vec<&str>>(); - /// assert_eq!(gr1.as_slice(), &["a\u0310", "e\u0301", "o\u0308\u0332"]); + /// let b: &[_] = &["a\u0310", "e\u0301", "o\u0308\u0332"]; + /// assert_eq!(gr1.as_slice(), b); /// let gr2 = "a\r\nb🇷🇺🇸🇹".graphemes(true).collect::<Vec<&str>>(); - /// assert_eq!(gr2.as_slice(), &["a", "\r\n", "b", "🇷🇺🇸🇹"]); + /// let b: &[_] = &["a", "\r\n", "b", "🇷🇺🇸🇹"]; + /// assert_eq!(gr2.as_slice(), b); /// ``` fn graphemes(&self, is_extended: bool) -> Graphemes<'a>; @@ -59,7 +61,8 @@ pub trait UnicodeStrSlice<'a> { /// /// ```rust /// let gr_inds = "a̐éö̲\r\n".grapheme_indices(true).collect::<Vec<(uint, &str)>>(); - /// assert_eq!(gr_inds.as_slice(), &[(0u, "a̐"), (3, "é"), (6, "ö̲"), (11, "\r\n")]); + /// let b: &[_] = &[(0u, "a̐"), (3, "é"), (6, "ö̲"), (11, "\r\n")]; + /// assert_eq!(gr_inds.as_slice(), b); /// ``` fn grapheme_indices(&self, is_extended: bool) -> GraphemeIndices<'a>; diff --git a/src/liburl/lib.rs b/src/liburl/lib.rs index 9ced6cb62af..b5ffdef22e9 100644 --- a/src/liburl/lib.rs +++ b/src/liburl/lib.rs @@ -1095,7 +1095,8 @@ mod tests { t("\0", "%00"); t("\n", "%0A"); - t(&[0u8, 10, 37], "%00%0A%25"); + let a: &[_] = &[0u8, 10, 37]; + t(a, "%00%0A%25"); } #[test] @@ -1130,7 +1131,8 @@ mod tests { t("\0", "%00"); t("\n", "%0A"); - t(&[0u8, 10, 37], "%00%0A%25"); + let a: &[_] = &[0u8, 10, 37]; + t(a, "%00%0A%25"); } #[test] diff --git a/src/test/auxiliary/crateresolve4b-1.rs b/src/test/auxiliary/crateresolve4b-1.rs index 41eb0d604bd..54ee9c2d5a2 100644 --- a/src/test/auxiliary/crateresolve4b-1.rs +++ b/src/test/auxiliary/crateresolve4b-1.rs @@ -13,6 +13,6 @@ #![crate_id="crateresolve4b#0.1"] #![crate_type = "lib"] -extern crate crateresolve4a = "crateresolve4a#0.2"; +extern crate "crateresolve4a#0.2" as crateresolve4a; pub fn f() -> int { crateresolve4a::g() } diff --git a/src/test/auxiliary/crateresolve4b-2.rs b/src/test/auxiliary/crateresolve4b-2.rs index 51541c8764c..9221ecc697b 100644 --- a/src/test/auxiliary/crateresolve4b-2.rs +++ b/src/test/auxiliary/crateresolve4b-2.rs @@ -13,6 +13,6 @@ #![crate_id="crateresolve4b#0.2"] #![crate_type = "lib"] -extern crate crateresolve4a = "crateresolve4a#0.1"; +extern crate "crateresolve4a#0.1" as crateresolve4a; pub fn g() -> int { crateresolve4a::f() } diff --git a/src/test/auxiliary/extern-crosscrate-source.rs b/src/test/auxiliary/extern-crosscrate-source.rs index 5c83b327912..0e3b531e458 100644 --- a/src/test/auxiliary/extern-crosscrate-source.rs +++ b/src/test/auxiliary/extern-crosscrate-source.rs @@ -24,7 +24,7 @@ pub mod rustrt { } } -pub fn fact(n: uint) -> uint { +pub fn fact(n: libc::uintptr_t) -> libc::uintptr_t { unsafe { println!("n = {}", n); rustrt::rust_dbg_call(cb, n) @@ -32,9 +32,9 @@ pub fn fact(n: uint) -> uint { } pub extern fn cb(data: libc::uintptr_t) -> libc::uintptr_t { - if data == 1u { + if data == 1 { data } else { - fact(data - 1u) * data + fact(data - 1) * data } } diff --git a/src/test/auxiliary/foreign_lib.rs b/src/test/auxiliary/foreign_lib.rs index a6bbd40f810..500475091e0 100644 --- a/src/test/auxiliary/foreign_lib.rs +++ b/src/test/auxiliary/foreign_lib.rs @@ -15,6 +15,6 @@ pub mod rustrt { #[link(name = "rust_test_helpers")] extern { - fn rust_get_test_int() -> libc::intptr_t; + pub fn rust_get_test_int() -> libc::intptr_t; } } diff --git a/src/test/auxiliary/issue-12133-dylib2.rs b/src/test/auxiliary/issue-12133-dylib2.rs index a978eacbfd1..ee2b11da8f0 100644 --- a/src/test/auxiliary/issue-12133-dylib2.rs +++ b/src/test/auxiliary/issue-12133-dylib2.rs @@ -12,6 +12,6 @@ #![crate_type = "dylib"] -extern crate a = "issue-12133-rlib"; -extern crate b = "issue-12133-dylib"; +extern crate "issue-12133-rlib" as a; +extern crate "issue-12133-dylib" as b; diff --git a/src/test/auxiliary/issue-13560-3.rs b/src/test/auxiliary/issue-13560-3.rs index dfd13851774..c80a7643e01 100644 --- a/src/test/auxiliary/issue-13560-3.rs +++ b/src/test/auxiliary/issue-13560-3.rs @@ -13,6 +13,6 @@ #![crate_type = "rlib"] #![feature(phase)] -#[phase(plugin)] extern crate t1 = "issue-13560-1"; -#[phase(plugin, link)] extern crate t2 = "issue-13560-2"; +#[phase(plugin)] extern crate "issue-13560-1" as t1; +#[phase(plugin, link)] extern crate "issue-13560-2" as t2; diff --git a/src/test/auxiliary/issue-13620-2.rs b/src/test/auxiliary/issue-13620-2.rs index 6ad4a00a352..da47115e2b3 100644 --- a/src/test/auxiliary/issue-13620-2.rs +++ b/src/test/auxiliary/issue-13620-2.rs @@ -8,6 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -extern crate crate1 = "issue-13620-1"; +extern crate "issue-13620-1" as crate1; pub static FOO2: crate1::Foo = crate1::FOO; diff --git a/src/test/auxiliary/issue-13872-2.rs b/src/test/auxiliary/issue-13872-2.rs index ebfada18858..e2744b7910f 100644 --- a/src/test/auxiliary/issue-13872-2.rs +++ b/src/test/auxiliary/issue-13872-2.rs @@ -8,6 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -extern crate foo = "issue-13872-1"; +extern crate "issue-13872-1" as foo; pub use foo::B; diff --git a/src/test/auxiliary/issue-13872-3.rs b/src/test/auxiliary/issue-13872-3.rs index 7356b604a0e..827a9f18f48 100644 --- a/src/test/auxiliary/issue-13872-3.rs +++ b/src/test/auxiliary/issue-13872-3.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -extern crate bar = "issue-13872-2"; +extern crate "issue-13872-2" as bar; use bar::B; diff --git a/src/test/auxiliary/issue-15562.rs b/src/test/auxiliary/issue-15562.rs new file mode 100644 index 00000000000..76243d3bced --- /dev/null +++ b/src/test/auxiliary/issue-15562.rs @@ -0,0 +1,15 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![crate_type = "lib"] + +extern { + pub fn transmute(); +} diff --git a/src/test/auxiliary/issue-16643.rs b/src/test/auxiliary/issue-16643.rs new file mode 100644 index 00000000000..b30ccb4dded --- /dev/null +++ b/src/test/auxiliary/issue-16643.rs @@ -0,0 +1,27 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![crate_type = "lib"] + +pub struct TreeBuilder<H>; + +impl<H> TreeBuilder<H> { + pub fn process_token(&mut self) { + match self { + _ => for _y in *self {} + } + } +} + +impl<H> Iterator<H> for TreeBuilder<H> { + fn next(&mut self) -> Option<H> { + None + } +} diff --git a/src/test/auxiliary/issue-16725.rs b/src/test/auxiliary/issue-16725.rs new file mode 100644 index 00000000000..7f388c13e15 --- /dev/null +++ b/src/test/auxiliary/issue-16725.rs @@ -0,0 +1,14 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +extern { + fn bar(); +} + diff --git a/src/test/auxiliary/issue-2380.rs b/src/test/auxiliary/issue-2380.rs index c617c1b2d03..1cba738c564 100644 --- a/src/test/auxiliary/issue-2380.rs +++ b/src/test/auxiliary/issue-2380.rs @@ -14,8 +14,8 @@ pub trait i<T> { } -pub fn f<T>() -> Box<i<T>> { +pub fn f<T>() -> Box<i<T>+'static> { impl<T> i<T> for () { } - box() () as Box<i<T>> + box() () as Box<i<T>+'static> } diff --git a/src/test/auxiliary/issue-7178.rs b/src/test/auxiliary/issue-7178.rs index fe3842ef174..18b464bd924 100644 --- a/src/test/auxiliary/issue-7178.rs +++ b/src/test/auxiliary/issue-7178.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -pub struct Foo<'a, A>(&'a A); +pub struct Foo<'a, A:'a>(&'a A); impl<'a, A> Foo<'a, A> { pub fn new(a: &'a A) -> Foo<'a, A> { diff --git a/src/test/auxiliary/macro_crate_test.rs b/src/test/auxiliary/macro_crate_test.rs index b6283206676..0a9cfb5884f 100644 --- a/src/test/auxiliary/macro_crate_test.rs +++ b/src/test/auxiliary/macro_crate_test.rs @@ -40,7 +40,7 @@ pub fn plugin_registrar(reg: &mut Registry) { } fn expand_make_a_1(cx: &mut ExtCtxt, sp: Span, tts: &[TokenTree]) - -> Box<MacResult> { + -> Box<MacResult+'static> { if !tts.is_empty() { cx.span_fatal(sp, "make_a_1 takes no arguments"); } @@ -49,7 +49,7 @@ fn expand_make_a_1(cx: &mut ExtCtxt, sp: Span, tts: &[TokenTree]) // See Issue #15750 fn expand_identity(cx: &mut ExtCtxt, _span: Span, tts: &[TokenTree]) - -> Box<MacResult> { + -> Box<MacResult+'static> { // Parse an expression and emit it unchanged. let mut parser = parse::new_parser_from_tts(cx.parse_sess(), cx.cfg(), Vec::from_slice(tts)); @@ -65,7 +65,7 @@ fn expand_into_foo(cx: &mut ExtCtxt, sp: Span, attr: Gc<MetaItem>, it: Gc<Item>) } } -fn expand_forged_ident(cx: &mut ExtCtxt, sp: Span, tts: &[TokenTree]) -> Box<MacResult> { +fn expand_forged_ident(cx: &mut ExtCtxt, sp: Span, tts: &[TokenTree]) -> Box<MacResult+'static> { use syntax::ext::quote::rt::*; if !tts.is_empty() { diff --git a/src/test/auxiliary/packed.rs b/src/test/auxiliary/packed.rs index 54b2658e380..86f5f93e3cf 100644 --- a/src/test/auxiliary/packed.rs +++ b/src/test/auxiliary/packed.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#[packed] +#[repr(packed)] pub struct S { a: u8, b: u32 diff --git a/src/test/auxiliary/regions-bounded-method-type-parameters-cross-crate-lib.rs b/src/test/auxiliary/regions-bounded-method-type-parameters-cross-crate-lib.rs new file mode 100644 index 00000000000..a7429ca534b --- /dev/null +++ b/src/test/auxiliary/regions-bounded-method-type-parameters-cross-crate-lib.rs @@ -0,0 +1,33 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Check that method bounds declared on traits/impls in a cross-crate +// scenario work. This is the libary portion of the test. + +pub enum MaybeOwned<'a> { + Owned(int), + Borrowed(&'a int) +} + +struct Inv<'a> { // invariant w/r/t 'a + x: &'a mut &'a int +} + +// I encountered a bug at some point with encoding the IntoMaybeOwned +// trait, so I'll use that as the template for this test. +pub trait IntoMaybeOwned<'a> { + fn into_maybe_owned(self) -> MaybeOwned<'a>; + fn bigger_region<'b:'a>(self, b: Inv<'b>); +} + +impl<'a> IntoMaybeOwned<'a> for Inv<'a> { + fn into_maybe_owned(self) -> MaybeOwned<'a> { fail!() } + fn bigger_region<'b:'a>(self, b: Inv<'b>) { fail!() } +} diff --git a/src/test/auxiliary/syntax-extension-with-dll-deps-2.rs b/src/test/auxiliary/syntax-extension-with-dll-deps-2.rs index 04318fcae27..7a4339aa9f0 100644 --- a/src/test/auxiliary/syntax-extension-with-dll-deps-2.rs +++ b/src/test/auxiliary/syntax-extension-with-dll-deps-2.rs @@ -14,7 +14,7 @@ #![crate_type = "dylib"] #![feature(plugin_registrar, quote, globs)] -extern crate other = "syntax-extension-with-dll-deps-1"; +extern crate "syntax-extension-with-dll-deps-1" as other; extern crate syntax; extern crate rustc; @@ -29,7 +29,7 @@ pub fn plugin_registrar(reg: &mut Registry) { } fn expand_foo(cx: &mut ExtCtxt, sp: Span, tts: &[TokenTree]) - -> Box<MacResult> { + -> Box<MacResult+'static> { let answer = other::the_answer(); MacExpr::new(quote_expr!(cx, $answer)) } diff --git a/src/test/auxiliary/trait_default_method_xc_aux_2.rs b/src/test/auxiliary/trait_default_method_xc_aux_2.rs index 984264a969c..4239865d577 100644 --- a/src/test/auxiliary/trait_default_method_xc_aux_2.rs +++ b/src/test/auxiliary/trait_default_method_xc_aux_2.rs @@ -10,7 +10,7 @@ // aux-build:trait_default_method_xc_aux.rs -extern crate aux = "trait_default_method_xc_aux"; +extern crate "trait_default_method_xc_aux" as aux; use aux::A; pub struct a_struct { pub x: int } diff --git a/src/test/bench/core-map.rs b/src/test/bench/core-map.rs index 5044d82a6ea..f68ace395aa 100644 --- a/src/test/bench/core-map.rs +++ b/src/test/bench/core-map.rs @@ -100,7 +100,8 @@ fn main() { let mut rand = Vec::with_capacity(n_keys); { - let mut rng: IsaacRng = SeedableRng::from_seed(&[1, 1, 1, 1, 1, 1, 1]); + let seed: &[_] = &[1, 1, 1, 1, 1, 1, 1]; + let mut rng: IsaacRng = SeedableRng::from_seed(seed); let mut set = HashSet::new(); while set.len() != n_keys { let next = rng.gen(); diff --git a/src/test/bench/core-set.rs b/src/test/bench/core-set.rs index 1d2d02d7d59..7f85bc1d700 100644 --- a/src/test/bench/core-set.rs +++ b/src/test/bench/core-set.rs @@ -164,7 +164,7 @@ fn main() { } }; - let seed = &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + let seed: &[_] = &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; let max = 200000; { diff --git a/src/test/bench/shootout-fasta-redux.rs b/src/test/bench/shootout-fasta-redux.rs index adec9d31afe..03f98686324 100644 --- a/src/test/bench/shootout-fasta-redux.rs +++ b/src/test/bench/shootout-fasta-redux.rs @@ -77,7 +77,7 @@ struct AminoAcid { p: f32, } -struct RepeatFasta<'a, W> { +struct RepeatFasta<'a, W:'a> { alu: &'static str, out: &'a mut W } @@ -126,7 +126,7 @@ fn make_lookup(a: &[AminoAcid]) -> [AminoAcid, ..LOOKUP_SIZE] { lookup } -struct RandomFasta<'a, W> { +struct RandomFasta<'a, W:'a> { seed: u32, lookup: [AminoAcid, ..LOOKUP_SIZE], out: &'a mut W, diff --git a/src/test/bench/shootout-meteor.rs b/src/test/bench/shootout-meteor.rs index 9be111f55ae..c46c44abcd4 100644 --- a/src/test/bench/shootout-meteor.rs +++ b/src/test/bench/shootout-meteor.rs @@ -69,11 +69,11 @@ impl<'a, T> Iterator<T> for Iterate<'a, T> { } // a linked list using borrowed next. -enum List<'a, T> { +enum List<'a, T:'a> { Nil, Cons(T, &'a List<'a, T>) } -struct ListIterator<'a, T> { +struct ListIterator<'a, T:'a> { cur: &'a List<'a, T> } impl<'a, T> List<'a, T> { diff --git a/src/test/compile-fail/asm-misplaced-option.rs b/src/test/compile-fail/asm-misplaced-option.rs index d90e4068785..8e93b91276f 100644 --- a/src/test/compile-fail/asm-misplaced-option.rs +++ b/src/test/compile-fail/asm-misplaced-option.rs @@ -29,7 +29,7 @@ pub fn main() { unsafe { // comma in place of a colon asm!("add $2, $1; mov $1, $0" : "=r"(x) : "r"(x), "r"(8u) : "cc", "volatile"); - //~^ WARNING expected a clobber, but found an option + //~^ WARNING expected a clobber, found an option } assert_eq!(x, 13); } diff --git a/src/test/compile-fail/bad-bang-ann-3.rs b/src/test/compile-fail/bad-bang-ann-3.rs index d899459b70a..943169be004 100644 --- a/src/test/compile-fail/bad-bang-ann-3.rs +++ b/src/test/compile-fail/bad-bang-ann-3.rs @@ -12,7 +12,7 @@ fn bad_bang(i: uint) -> ! { return 7u; - //~^ ERROR expected `!` but found `uint` + //~^ ERROR expected `!`, found `uint` } fn main() { bad_bang(5u); } diff --git a/src/test/compile-fail/bad-bang-ann.rs b/src/test/compile-fail/bad-bang-ann.rs index 829253bde18..18b98eb3a38 100644 --- a/src/test/compile-fail/bad-bang-ann.rs +++ b/src/test/compile-fail/bad-bang-ann.rs @@ -12,7 +12,7 @@ fn bad_bang(i: uint) -> ! { if i < 0u { } else { fail!(); } - //~^ ERROR expected `!` but found `()` + //~^ ERROR expected `!`, found `()` } fn main() { bad_bang(5u); } diff --git a/src/test/compile-fail/bad-const-type.rs b/src/test/compile-fail/bad-const-type.rs index 144b02e9b3b..13b19141c16 100644 --- a/src/test/compile-fail/bad-const-type.rs +++ b/src/test/compile-fail/bad-const-type.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// error-pattern:expected `collections::string::String` but found `int` +// error-pattern:expected `collections::string::String`, found `int` static i: String = 10i; fn main() { println!("{}", i); } diff --git a/src/test/compile-fail/bad-crate-id.rs b/src/test/compile-fail/bad-crate-id.rs index 883bfd035f4..71e3ce9a35a 100644 --- a/src/test/compile-fail/bad-crate-id.rs +++ b/src/test/compile-fail/bad-crate-id.rs @@ -8,6 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -extern crate foo = ""; //~ ERROR: crate name must not be empty +extern crate "" as foo; //~ ERROR: crate name must not be empty fn main() {} diff --git a/src/test/compile-fail/bad-crate-id2.rs b/src/test/compile-fail/bad-crate-id2.rs index 22e98b61c61..4899252a1a4 100644 --- a/src/test/compile-fail/bad-crate-id2.rs +++ b/src/test/compile-fail/bad-crate-id2.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -extern crate bar = "#a"; //~ ERROR: invalid character `#` in crate name: `#a` +extern crate "#a" as bar; //~ ERROR: invalid character `#` in crate name: `#a` fn main() {} diff --git a/src/test/compile-fail/bad-method-typaram-kind.rs b/src/test/compile-fail/bad-method-typaram-kind.rs index db6db02ded5..b63ecc6b66f 100644 --- a/src/test/compile-fail/bad-method-typaram-kind.rs +++ b/src/test/compile-fail/bad-method-typaram-kind.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -fn foo<T>() { +fn foo<T:'static>() { 1u.bar::<T>(); //~ ERROR: does not fulfill `Send` } diff --git a/src/test/compile-fail/bang-tailexpr.rs b/src/test/compile-fail/bang-tailexpr.rs index af78e19e8c2..ff95f05279e 100644 --- a/src/test/compile-fail/bang-tailexpr.rs +++ b/src/test/compile-fail/bang-tailexpr.rs @@ -9,6 +9,6 @@ // except according to those terms. fn f() -> ! { - 3i //~ ERROR expected `!` but found `int` + 3i //~ ERROR expected `!`, found `int` } fn main() { } diff --git a/src/test/compile-fail/block-must-not-have-result-do.rs b/src/test/compile-fail/block-must-not-have-result-do.rs index abeefa4aac8..687171f8c1f 100644 --- a/src/test/compile-fail/block-must-not-have-result-do.rs +++ b/src/test/compile-fail/block-must-not-have-result-do.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// error-pattern:mismatched types: expected `()` but found `bool` +// error-pattern:mismatched types: expected `()`, found `bool` fn main() { loop { diff --git a/src/test/compile-fail/block-must-not-have-result-res.rs b/src/test/compile-fail/block-must-not-have-result-res.rs index df42244fd0f..328c032325e 100644 --- a/src/test/compile-fail/block-must-not-have-result-res.rs +++ b/src/test/compile-fail/block-must-not-have-result-res.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// error-pattern:mismatched types: expected `()` but found `bool` +// error-pattern:mismatched types: expected `()`, found `bool` struct r; diff --git a/src/test/compile-fail/block-must-not-have-result-while.rs b/src/test/compile-fail/block-must-not-have-result-while.rs index e4aceabf0c8..ed903f3fd65 100644 --- a/src/test/compile-fail/block-must-not-have-result-while.rs +++ b/src/test/compile-fail/block-must-not-have-result-while.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// error-pattern:mismatched types: expected `()` but found `bool` +// error-pattern:mismatched types: expected `()`, found `bool` fn main() { while true { diff --git a/src/test/compile-fail/borrowck-call-sendfn.rs b/src/test/compile-fail/borrowck-call-sendfn.rs index 57c0deb178d..eb2ea6b3de4 100644 --- a/src/test/compile-fail/borrowck-call-sendfn.rs +++ b/src/test/compile-fail/borrowck-call-sendfn.rs @@ -9,7 +9,7 @@ // except according to those terms. struct Foo { - f: proc() + f: proc():'static } fn call(x: Foo) { diff --git a/src/test/compile-fail/borrowck-object-lifetime.rs b/src/test/compile-fail/borrowck-object-lifetime.rs index c55a5a30538..bbb58e21198 100644 --- a/src/test/compile-fail/borrowck-object-lifetime.rs +++ b/src/test/compile-fail/borrowck-object-lifetime.rs @@ -1,4 +1,4 @@ -// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // @@ -8,17 +8,22 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// Test that borrows that occur due to calls to object methods +// properly "claim" the object path. trait Foo { fn borrowed(&self) -> &(); + fn mut_borrowed(&mut self) -> &(); } -fn borrowed_receiver(x: &Foo) -> &() { - x.borrowed() +fn borrowed_receiver(x: &Foo) { + let _y = x.borrowed(); + let _z = x.borrowed(); } -fn owned_receiver(x: Box<Foo>) -> &'static () { - x.borrowed() //~ ERROR `*x` does not live long enough +fn mut_borrowed_receiver(x: &mut Foo) { + let _y = x.borrowed(); + let _z = x.mut_borrowed(); //~ ERROR cannot borrow } fn mut_owned_receiver(mut x: Box<Foo>) { diff --git a/src/test/compile-fail/box-static-bound.rs b/src/test/compile-fail/box-static-bound.rs index 5ef52ab6645..29ee79b0079 100644 --- a/src/test/compile-fail/box-static-bound.rs +++ b/src/test/compile-fail/box-static-bound.rs @@ -12,7 +12,7 @@ use std::gc::{Gc, GC}; fn f<T>(x: T) -> Gc<T> { - box(GC) x //~ ERROR value may contain references + box(GC) x //~ ERROR the parameter type `T` may not live long enough } fn g<T:'static>(x: T) -> Gc<T> { diff --git a/src/test/compile-fail/builtin-superkinds-self-type.rs b/src/test/compile-fail/builtin-superkinds-self-type.rs index 67222bdafbf..726413981a5 100644 --- a/src/test/compile-fail/builtin-superkinds-self-type.rs +++ b/src/test/compile-fail/builtin-superkinds-self-type.rs @@ -11,7 +11,7 @@ // Tests (negatively) the ability for the Self type in default methods // to use capabilities granted by builtin kinds as supertraits. -trait Foo : Sync { +trait Foo : Sync+'static { fn foo(self, mut chan: Sender<Self>) { chan.send(self); //~ ERROR does not fulfill `Send` } diff --git a/src/test/compile-fail/cannot-mutate-captured-non-mut-var.rs b/src/test/compile-fail/cannot-mutate-captured-non-mut-var.rs index 6bc436d3c18..fcb09c20000 100644 --- a/src/test/compile-fail/cannot-mutate-captured-non-mut-var.rs +++ b/src/test/compile-fail/cannot-mutate-captured-non-mut-var.rs @@ -12,4 +12,8 @@ fn main() { let x = 1i; proc() { x = 2; }; //~^ ERROR: cannot assign to immutable captured outer variable in a proc `x` + + let s = std::io::stdin(); + proc() { s.lines(); }; + //~^ ERROR: cannot borrow immutable captured outer variable in a proc `s` as mutable } diff --git a/src/test/compile-fail/closure-bounds-cant-promote-superkind-in-struct.rs b/src/test/compile-fail/closure-bounds-cant-promote-superkind-in-struct.rs index 951354d964d..1ff9dc9dac4 100644 --- a/src/test/compile-fail/closure-bounds-cant-promote-superkind-in-struct.rs +++ b/src/test/compile-fail/closure-bounds-cant-promote-superkind-in-struct.rs @@ -13,7 +13,7 @@ struct X { } fn foo(blk: ||:'static) -> X { - return X { field: blk }; //~ ERROR expected bounds `'static+Send` + return X { field: blk }; //~ ERROR expected bounds `Send` } fn main() { diff --git a/src/test/compile-fail/closure-bounds-static-cant-capture-borrowed.rs b/src/test/compile-fail/closure-bounds-static-cant-capture-borrowed.rs index 9176412cd79..c0b463535d4 100644 --- a/src/test/compile-fail/closure-bounds-static-cant-capture-borrowed.rs +++ b/src/test/compile-fail/closure-bounds-static-cant-capture-borrowed.rs @@ -12,8 +12,8 @@ fn bar(blk: ||:'static) { } fn foo(x: &()) { - bar(|| { //~ ERROR cannot infer an appropriate lifetime - let _ = x; + bar(|| { + let _ = x; //~ ERROR captured variable `x` does not outlive }) } diff --git a/src/test/compile-fail/closure-bounds-subtype.rs b/src/test/compile-fail/closure-bounds-subtype.rs index 51188e5dce4..5bd9f20dd83 100644 --- a/src/test/compile-fail/closure-bounds-subtype.rs +++ b/src/test/compile-fail/closure-bounds-subtype.rs @@ -9,19 +9,19 @@ // except according to those terms. -fn take_any(_: ||:) { +fn take_any(_: ||) { } fn take_const_owned(_: ||:Sync+Send) { } -fn give_any(f: ||:) { +fn give_any(f: ||) { take_any(f); } fn give_owned(f: ||:Send) { take_any(f); - take_const_owned(f); //~ ERROR expected bounds `Send+Sync` but found bounds `Send` + take_const_owned(f); //~ ERROR expected bounds `Send+Sync`, found bounds `Send` } fn main() {} diff --git a/src/test/compile-fail/column-offset-1-based.rs b/src/test/compile-fail/column-offset-1-based.rs index 3a6594f64f3..a00ded61758 100644 --- a/src/test/compile-fail/column-offset-1-based.rs +++ b/src/test/compile-fail/column-offset-1-based.rs @@ -8,4 +8,4 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -# //~ ERROR 11:1: 11:2 error: expected `[` but found `<eof>` +# //~ ERROR 11:1: 11:2 error: expected `[`, found `<eof>` diff --git a/src/test/compile-fail/const-cast-different-types.rs b/src/test/compile-fail/const-cast-different-types.rs index f7d5ddb3145..6b8e126db77 100644 --- a/src/test/compile-fail/const-cast-different-types.rs +++ b/src/test/compile-fail/const-cast-different-types.rs @@ -9,8 +9,10 @@ // except according to those terms. static a: &'static str = "foo"; -static b: *const u8 = a as *const u8; //~ ERROR non-scalar cast -static c: *const u8 = &a as *const u8; //~ ERROR mismatched types +static b: *const u8 = a as *const u8; +//~^ ERROR mismatched types: expected `*const u8`, found `&'static str` +static c: *const u8 = &a as *const u8; +//~^ ERROR mismatched types: expected `*const u8`, found `&&'static str` fn main() { } diff --git a/src/test/compile-fail/drop-on-non-struct.rs b/src/test/compile-fail/drop-on-non-struct.rs index bf501ecfb70..af4c12c754b 100644 --- a/src/test/compile-fail/drop-on-non-struct.rs +++ b/src/test/compile-fail/drop-on-non-struct.rs @@ -14,7 +14,6 @@ type Foo = Vec<u8>; impl Drop for Foo { //~^ ERROR cannot provide an extension implementation -//~^^ ERROR multiple applicable methods fn drop(&mut self) { println!("kaboom"); } diff --git a/src/test/compile-fail/dst-bad-assign-2.rs b/src/test/compile-fail/dst-bad-assign-2.rs new file mode 100644 index 00000000000..08e51038104 --- /dev/null +++ b/src/test/compile-fail/dst-bad-assign-2.rs @@ -0,0 +1,46 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Forbid assignment into a dynamically sized type. + +struct Fat<Sized? T> { + f1: int, + f2: &'static str, + ptr: T +} + +#[deriving(PartialEq,Eq)] +struct Bar; + +#[deriving(PartialEq,Eq)] +struct Bar1 { + f: int +} + +trait ToBar { + fn to_bar(&self) -> Bar; + fn to_val(&self) -> int; +} + +impl ToBar for Bar1 { + fn to_bar(&self) -> Bar { + Bar + } + fn to_val(&self) -> int { + self.f + } +} + +pub fn main() { + // Assignment. + let f5: &mut Fat<ToBar> = &mut Fat { f1: 5, f2: "some str", ptr: Bar1 {f :42} }; + let z: Box<ToBar> = box Bar1 {f: 36}; + f5.ptr = *z; //~ ERROR dynamically sized type on lhs of assignment +} diff --git a/src/test/compile-fail/dst-bad-assign.rs b/src/test/compile-fail/dst-bad-assign.rs new file mode 100644 index 00000000000..17149941a7e --- /dev/null +++ b/src/test/compile-fail/dst-bad-assign.rs @@ -0,0 +1,46 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Forbid assignment into a dynamically sized type. + +struct Fat<Sized? T> { + f1: int, + f2: &'static str, + ptr: T +} + +#[deriving(PartialEq,Eq)] +struct Bar; + +#[deriving(PartialEq,Eq)] +struct Bar1 { + f: int +} + +trait ToBar { + fn to_bar(&self) -> Bar; + fn to_val(&self) -> int; +} + +impl ToBar for Bar1 { + fn to_bar(&self) -> Bar { + Bar + } + fn to_val(&self) -> int { + self.f + } +} + +pub fn main() { + // Assignment. + let f5: &mut Fat<ToBar> = &mut Fat { f1: 5, f2: "some str", ptr: Bar1 {f :42} }; + let z: Box<ToBar> = box Bar1 {f: 36}; + f5.ptr = Bar1 {f: 36}; //~ ERROR mismatched types: expected `ToBar`, found `Bar1` +} diff --git a/src/test/compile-fail/dst-bad-coerce1.rs b/src/test/compile-fail/dst-bad-coerce1.rs new file mode 100644 index 00000000000..a609740eaeb --- /dev/null +++ b/src/test/compile-fail/dst-bad-coerce1.rs @@ -0,0 +1,32 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Attempt to change the type as well as unsizing. + +struct Fat<Sized? T> { + ptr: T +} + +struct Foo; +trait Bar {} + +pub fn main() { + // With a vec of ints. + let f1 = Fat { ptr: [1, 2, 3] }; + let f2: &Fat<[int, ..3]> = &f1; + let f3: &Fat<[uint]> = f2; + //~^ ERROR mismatched types: expected `&Fat<[uint]>`, found `&Fat<[int, .. 3]>` + + // With a trait. + let f1 = Fat { ptr: Foo }; + let f2: &Fat<Foo> = &f1; + let f3: &Fat<Bar> = f2; + //~^ ERROR failed to find an implementation of trait Bar for Foo +} diff --git a/src/test/compile-fail/dst-bad-coerce2.rs b/src/test/compile-fail/dst-bad-coerce2.rs new file mode 100644 index 00000000000..118e4ce7e08 --- /dev/null +++ b/src/test/compile-fail/dst-bad-coerce2.rs @@ -0,0 +1,31 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Attempt to change the mutability as well as unsizing. + +struct Fat<Sized? T> { + ptr: T +} + +struct Foo; +trait Bar {} +impl Bar for Foo {} + +pub fn main() { + // With a vec of ints. + let f1 = Fat { ptr: [1, 2, 3] }; + let f2: &Fat<[int, ..3]> = &f1; + let f3: &mut Fat<[int]> = f2; //~ ERROR cannot borrow immutable dereference + + // With a trait. + let f1 = Fat { ptr: Foo }; + let f2: &Fat<Foo> = &f1; + let f3: &mut Fat<Bar> = f2; //~ ERROR cannot borrow immutable dereference +} diff --git a/src/test/compile-fail/dst-bad-coerce3.rs b/src/test/compile-fail/dst-bad-coerce3.rs new file mode 100644 index 00000000000..7cf647a26d7 --- /dev/null +++ b/src/test/compile-fail/dst-bad-coerce3.rs @@ -0,0 +1,35 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Attempt to extend the lifetime as well as unsizing. + +struct Fat<Sized? T> { + ptr: T +} + +struct Foo; +trait Bar {} +impl Bar for Foo {} + +fn baz<'a>() { + // With a vec of ints. + let f1 = Fat { ptr: [1, 2, 3] }; + let f2: &Fat<[int, ..3]> = &f1; //~ ERROR `f1` does not live long enough + let f3: &'a Fat<[int]> = f2; + + // With a trait. + let f1 = Fat { ptr: Foo }; + let f2: &Fat<Foo> = &f1; //~ ERROR `f1` does not live long enough + let f3: &'a Fat<Bar> = f2; +} + +pub fn main() { + baz(); +} diff --git a/src/test/compile-fail/dst-bad-coerce4.rs b/src/test/compile-fail/dst-bad-coerce4.rs new file mode 100644 index 00000000000..9a192334997 --- /dev/null +++ b/src/test/compile-fail/dst-bad-coerce4.rs @@ -0,0 +1,22 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Attempt to coerce from unsized to sized. + +struct Fat<Sized? T> { + ptr: T +} + +pub fn main() { + // With a vec of ints. + let f1: &Fat<[int]> = &Fat { ptr: [1, 2, 3] }; + let f2: &Fat<[int, ..3]> = f1; + //~^ ERROR mismatched types: expected `&Fat<[int, .. 3]>`, found `&Fat<[int]>` +} diff --git a/src/test/compile-fail/dst-bad-deep.rs b/src/test/compile-fail/dst-bad-deep.rs new file mode 100644 index 00000000000..cf526392283 --- /dev/null +++ b/src/test/compile-fail/dst-bad-deep.rs @@ -0,0 +1,25 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Try to initialise a DST struct where the lost information is deeply nested. +// This is an error because it requires an unsized rvalue. This is a problem +// because it would require stack allocation of an unsized temporary (*g in the +// test). + +struct Fat<Sized? T> { + ptr: T +} + +pub fn main() { + let f: Fat<[int, ..3]> = Fat { ptr: [5i, 6, 7] }; + let g: &Fat<[int]> = &f; + let h: &Fat<Fat<[int]>> = &Fat { ptr: *g }; + //~^ ERROR trying to initialise a dynamically sized struct +} diff --git a/src/test/compile-fail/empty-impl-semicolon.rs b/src/test/compile-fail/empty-impl-semicolon.rs index 1a8751cb91a..b5f17eef886 100644 --- a/src/test/compile-fail/empty-impl-semicolon.rs +++ b/src/test/compile-fail/empty-impl-semicolon.rs @@ -8,4 +8,4 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -impl Foo; //~ ERROR expected `{` but found `;` +impl Foo; //~ ERROR expected `{`, found `;` diff --git a/src/test/compile-fail/explicit-self-lifetime-mismatch.rs b/src/test/compile-fail/explicit-self-lifetime-mismatch.rs index 285792e26b1..b5346a1c5d1 100644 --- a/src/test/compile-fail/explicit-self-lifetime-mismatch.rs +++ b/src/test/compile-fail/explicit-self-lifetime-mismatch.rs @@ -16,10 +16,10 @@ struct Foo<'a,'b> { impl<'a,'b> Foo<'a,'b> { // The number of errors is related to the way invariance works. fn bar(self: Foo<'b,'a>) {} - //~^ ERROR mismatched types: expected `Foo<'a,'b>` but found `Foo<'b,'a>` - //~^^ ERROR mismatched types: expected `Foo<'a,'b>` but found `Foo<'b,'a>` - //~^^^ ERROR mismatched types: expected `Foo<'b,'a>` but found `Foo<'a,'b>` - //~^^^^ ERROR mismatched types: expected `Foo<'b,'a>` but found `Foo<'a,'b>` + //~^ ERROR mismatched types: expected `Foo<'a,'b>`, found `Foo<'b,'a>` + //~^^ ERROR mismatched types: expected `Foo<'a,'b>`, found `Foo<'b,'a>` + //~^^^ ERROR mismatched types: expected `Foo<'b,'a>`, found `Foo<'a,'b>` + //~^^^^ ERROR mismatched types: expected `Foo<'b,'a>`, found `Foo<'a,'b>` } fn main() {} diff --git a/src/test/compile-fail/extern-expected-fn-or-brace.rs b/src/test/compile-fail/extern-expected-fn-or-brace.rs index 638253f816a..7d1110cf6df 100644 --- a/src/test/compile-fail/extern-expected-fn-or-brace.rs +++ b/src/test/compile-fail/extern-expected-fn-or-brace.rs @@ -11,4 +11,4 @@ // Verifies that the expected token errors for `extern crate` are // raised -extern "C" mod foo; //~ERROR expected `{` or `fn` but found `mod` +extern "C" mod foo; //~ERROR expected `{` or `fn`, found `mod` diff --git a/src/test/compile-fail/extern-foreign-crate.rs b/src/test/compile-fail/extern-foreign-crate.rs index ffa6ffb042f..97a5f2a11e9 100644 --- a/src/test/compile-fail/extern-foreign-crate.rs +++ b/src/test/compile-fail/extern-foreign-crate.rs @@ -11,4 +11,4 @@ // Verifies that the expected token errors for `extern crate` are // raised -extern crate foo {} //~ERROR expected one of `=`, `;` but found `{` +extern crate foo {} //~ERROR expected one of `=`, `;`, found `{` diff --git a/src/test/compile-fail/fully-qualified-type-name2.rs b/src/test/compile-fail/fully-qualified-type-name2.rs index 986d19669b9..94af50dac0e 100644 --- a/src/test/compile-fail/fully-qualified-type-name2.rs +++ b/src/test/compile-fail/fully-qualified-type-name2.rs @@ -20,7 +20,7 @@ mod y { fn bar(x: x::foo) -> y::foo { return x; - //~^ ERROR mismatched types: expected `y::foo` but found `x::foo` + //~^ ERROR mismatched types: expected `y::foo`, found `x::foo` } fn main() { diff --git a/src/test/compile-fail/fully-qualified-type-name3.rs b/src/test/compile-fail/fully-qualified-type-name3.rs index d20ab27d6cf..0a5a54b9a27 100644 --- a/src/test/compile-fail/fully-qualified-type-name3.rs +++ b/src/test/compile-fail/fully-qualified-type-name3.rs @@ -17,7 +17,7 @@ type T2 = int; fn bar(x: T1) -> T2 { return x; - //~^ ERROR mismatched types: expected `T2` but found `T1` + //~^ ERROR mismatched types: expected `T2`, found `T1` } fn main() { diff --git a/src/test/compile-fail/generic-type-less-params-with-defaults.rs b/src/test/compile-fail/generic-type-less-params-with-defaults.rs index d1fa40bd22c..ec226061e2a 100644 --- a/src/test/compile-fail/generic-type-less-params-with-defaults.rs +++ b/src/test/compile-fail/generic-type-less-params-with-defaults.rs @@ -15,5 +15,5 @@ struct Heap; struct Vec<T, A = Heap>; fn main() { - let _: Vec; //~ ERROR wrong number of type arguments: expected at least 1 but found 0 + let _: Vec; //~ ERROR wrong number of type arguments: expected at least 1, found 0 } diff --git a/src/test/compile-fail/generic-type-more-params-with-defaults.rs b/src/test/compile-fail/generic-type-more-params-with-defaults.rs index 2f3f91a9c3d..b16abd17575 100644 --- a/src/test/compile-fail/generic-type-more-params-with-defaults.rs +++ b/src/test/compile-fail/generic-type-more-params-with-defaults.rs @@ -16,5 +16,5 @@ struct Vec<T, A = Heap>; fn main() { let _: Vec<int, Heap, bool>; - //~^ ERROR wrong number of type arguments: expected at most 2 but found 3 + //~^ ERROR wrong number of type arguments: expected at most 2, found 3 } diff --git a/src/test/compile-fail/generic-type-params-name-repr.rs b/src/test/compile-fail/generic-type-params-name-repr.rs index 6114c5958e0..87c865ab618 100644 --- a/src/test/compile-fail/generic-type-params-name-repr.rs +++ b/src/test/compile-fail/generic-type-params-name-repr.rs @@ -21,23 +21,23 @@ struct HashMap<K, V, H = Hash<K>>; fn main() { // Ensure that the printed type doesn't include the default type params... let _: Foo<int> = (); - //~^ ERROR mismatched types: expected `Foo<int>` but found `()` + //~^ ERROR mismatched types: expected `Foo<int>`, found `()` // ...even when they're present, but the same types as the defaults. let _: Foo<int, B, C> = (); - //~^ ERROR mismatched types: expected `Foo<int>` but found `()` + //~^ ERROR mismatched types: expected `Foo<int>`, found `()` // Including cases where the default is using previous type params. let _: HashMap<String, int> = (); - //~^ ERROR mismatched types: expected `HashMap<collections::string::String,int>` but found `()` + //~^ ERROR mismatched types: expected `HashMap<collections::string::String,int>`, found `()` let _: HashMap<String, int, Hash<String>> = (); - //~^ ERROR mismatched types: expected `HashMap<collections::string::String,int>` but found `()` + //~^ ERROR mismatched types: expected `HashMap<collections::string::String,int>`, found `()` // But not when there's a different type in between. let _: Foo<A, int, C> = (); - //~^ ERROR mismatched types: expected `Foo<A,int>` but found `()` + //~^ ERROR mismatched types: expected `Foo<A,int>`, found `()` // And don't print <> at all when there's just defaults. let _: Foo<A, B, C> = (); - //~^ ERROR mismatched types: expected `Foo` but found `()` + //~^ ERROR mismatched types: expected `Foo`, found `()` } diff --git a/src/test/compile-fail/if-branch-types.rs b/src/test/compile-fail/if-branch-types.rs index 1c6dd0ef9f6..4a8c72c3877 100644 --- a/src/test/compile-fail/if-branch-types.rs +++ b/src/test/compile-fail/if-branch-types.rs @@ -10,5 +10,5 @@ fn main() { let x = if true { 10i } else { 10u }; - //~^ ERROR if and else have incompatible types: expected `int` but found `uint` + //~^ ERROR if and else have incompatible types: expected `int`, found `uint` } diff --git a/src/test/compile-fail/if-without-else-result.rs b/src/test/compile-fail/if-without-else-result.rs index c15e28f9517..c22a8e3f782 100644 --- a/src/test/compile-fail/if-without-else-result.rs +++ b/src/test/compile-fail/if-without-else-result.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// error-pattern:mismatched types: expected `()` but found `bool` +// error-pattern:mismatched types: expected `()`, found `bool` extern crate debug; diff --git a/src/test/compile-fail/inherit-struct4.rs b/src/test/compile-fail/inherit-struct4.rs index e01ec2904a6..ec24be8bbe8 100644 --- a/src/test/compile-fail/inherit-struct4.rs +++ b/src/test/compile-fail/inherit-struct4.rs @@ -12,7 +12,7 @@ #![feature(struct_inherit)] // With lifetime parameters. -struct S5<'a> : S4 { //~ ERROR wrong number of lifetime parameters: expected 1 but found 0 +struct S5<'a> : S4 { //~ ERROR wrong number of lifetime parameters: expected 1, found 0 f4: int, } diff --git a/src/test/compile-fail/integer-literal-suffix-inference.rs b/src/test/compile-fail/integer-literal-suffix-inference.rs index 2f77497acc4..1e42a9447f6 100644 --- a/src/test/compile-fail/integer-literal-suffix-inference.rs +++ b/src/test/compile-fail/integer-literal-suffix-inference.rs @@ -39,62 +39,62 @@ fn main() { fn id_u64(n: u64) -> u64 { n } id_i8(a8); // ok - id_i8(a16); //~ ERROR mismatched types: expected `i8` but found `i16` - id_i8(a32); //~ ERROR mismatched types: expected `i8` but found `i32` - id_i8(a64); //~ ERROR mismatched types: expected `i8` but found `i64` + id_i8(a16); //~ ERROR mismatched types: expected `i8`, found `i16` + id_i8(a32); //~ ERROR mismatched types: expected `i8`, found `i32` + id_i8(a64); //~ ERROR mismatched types: expected `i8`, found `i64` - id_i16(a8); //~ ERROR mismatched types: expected `i16` but found `i8` + id_i16(a8); //~ ERROR mismatched types: expected `i16`, found `i8` id_i16(a16); // ok - id_i16(a32); //~ ERROR mismatched types: expected `i16` but found `i32` - id_i16(a64); //~ ERROR mismatched types: expected `i16` but found `i64` + id_i16(a32); //~ ERROR mismatched types: expected `i16`, found `i32` + id_i16(a64); //~ ERROR mismatched types: expected `i16`, found `i64` - id_i32(a8); //~ ERROR mismatched types: expected `i32` but found `i8` - id_i32(a16); //~ ERROR mismatched types: expected `i32` but found `i16` + id_i32(a8); //~ ERROR mismatched types: expected `i32`, found `i8` + id_i32(a16); //~ ERROR mismatched types: expected `i32`, found `i16` id_i32(a32); // ok - id_i32(a64); //~ ERROR mismatched types: expected `i32` but found `i64` + id_i32(a64); //~ ERROR mismatched types: expected `i32`, found `i64` - id_i64(a8); //~ ERROR mismatched types: expected `i64` but found `i8` - id_i64(a16); //~ ERROR mismatched types: expected `i64` but found `i16` - id_i64(a32); //~ ERROR mismatched types: expected `i64` but found `i32` + id_i64(a8); //~ ERROR mismatched types: expected `i64`, found `i8` + id_i64(a16); //~ ERROR mismatched types: expected `i64`, found `i16` + id_i64(a32); //~ ERROR mismatched types: expected `i64`, found `i32` id_i64(a64); // ok id_i8(c8); // ok - id_i8(c16); //~ ERROR mismatched types: expected `i8` but found `i16` - id_i8(c32); //~ ERROR mismatched types: expected `i8` but found `i32` - id_i8(c64); //~ ERROR mismatched types: expected `i8` but found `i64` + id_i8(c16); //~ ERROR mismatched types: expected `i8`, found `i16` + id_i8(c32); //~ ERROR mismatched types: expected `i8`, found `i32` + id_i8(c64); //~ ERROR mismatched types: expected `i8`, found `i64` - id_i16(c8); //~ ERROR mismatched types: expected `i16` but found `i8` + id_i16(c8); //~ ERROR mismatched types: expected `i16`, found `i8` id_i16(c16); // ok - id_i16(c32); //~ ERROR mismatched types: expected `i16` but found `i32` - id_i16(c64); //~ ERROR mismatched types: expected `i16` but found `i64` + id_i16(c32); //~ ERROR mismatched types: expected `i16`, found `i32` + id_i16(c64); //~ ERROR mismatched types: expected `i16`, found `i64` - id_i32(c8); //~ ERROR mismatched types: expected `i32` but found `i8` - id_i32(c16); //~ ERROR mismatched types: expected `i32` but found `i16` + id_i32(c8); //~ ERROR mismatched types: expected `i32`, found `i8` + id_i32(c16); //~ ERROR mismatched types: expected `i32`, found `i16` id_i32(c32); // ok - id_i32(c64); //~ ERROR mismatched types: expected `i32` but found `i64` + id_i32(c64); //~ ERROR mismatched types: expected `i32`, found `i64` - id_i64(a8); //~ ERROR mismatched types: expected `i64` but found `i8` - id_i64(a16); //~ ERROR mismatched types: expected `i64` but found `i16` - id_i64(a32); //~ ERROR mismatched types: expected `i64` but found `i32` + id_i64(a8); //~ ERROR mismatched types: expected `i64`, found `i8` + id_i64(a16); //~ ERROR mismatched types: expected `i64`, found `i16` + id_i64(a32); //~ ERROR mismatched types: expected `i64`, found `i32` id_i64(a64); // ok id_u8(b8); // ok - id_u8(b16); //~ ERROR mismatched types: expected `u8` but found `u16` - id_u8(b32); //~ ERROR mismatched types: expected `u8` but found `u32` - id_u8(b64); //~ ERROR mismatched types: expected `u8` but found `u64` + id_u8(b16); //~ ERROR mismatched types: expected `u8`, found `u16` + id_u8(b32); //~ ERROR mismatched types: expected `u8`, found `u32` + id_u8(b64); //~ ERROR mismatched types: expected `u8`, found `u64` - id_u16(b8); //~ ERROR mismatched types: expected `u16` but found `u8` + id_u16(b8); //~ ERROR mismatched types: expected `u16`, found `u8` id_u16(b16); // ok - id_u16(b32); //~ ERROR mismatched types: expected `u16` but found `u32` - id_u16(b64); //~ ERROR mismatched types: expected `u16` but found `u64` + id_u16(b32); //~ ERROR mismatched types: expected `u16`, found `u32` + id_u16(b64); //~ ERROR mismatched types: expected `u16`, found `u64` - id_u32(b8); //~ ERROR mismatched types: expected `u32` but found `u8` - id_u32(b16); //~ ERROR mismatched types: expected `u32` but found `u16` + id_u32(b8); //~ ERROR mismatched types: expected `u32`, found `u8` + id_u32(b16); //~ ERROR mismatched types: expected `u32`, found `u16` id_u32(b32); // ok - id_u32(b64); //~ ERROR mismatched types: expected `u32` but found `u64` + id_u32(b64); //~ ERROR mismatched types: expected `u32`, found `u64` - id_u64(b8); //~ ERROR mismatched types: expected `u64` but found `u8` - id_u64(b16); //~ ERROR mismatched types: expected `u64` but found `u16` - id_u64(b32); //~ ERROR mismatched types: expected `u64` but found `u32` + id_u64(b8); //~ ERROR mismatched types: expected `u64`, found `u8` + id_u64(b16); //~ ERROR mismatched types: expected `u64`, found `u16` + id_u64(b32); //~ ERROR mismatched types: expected `u64`, found `u32` id_u64(b64); // ok } diff --git a/src/test/compile-fail/issue-10291.rs b/src/test/compile-fail/issue-10291.rs index 71b98bb5f5a..8ae20dfde91 100644 --- a/src/test/compile-fail/issue-10291.rs +++ b/src/test/compile-fail/issue-10291.rs @@ -8,11 +8,10 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -fn test<'x>(x: &'x int) { //~ NOTE the lifetime 'x as defined +fn test<'x>(x: &'x int) { drop::< <'z>|&'z int| -> &'z int>(|z| { - //~^ ERROR mismatched types - //~^^ ERROR cannot infer an appropriate lifetime x + //~^ ERROR cannot infer an appropriate lifetime }); } diff --git a/src/test/compile-fail/issue-10764.rs b/src/test/compile-fail/issue-10764.rs index dfb38953ab3..0733744b652 100644 --- a/src/test/compile-fail/issue-10764.rs +++ b/src/test/compile-fail/issue-10764.rs @@ -12,4 +12,4 @@ fn f(_: extern "Rust" fn()) {} extern fn bar() {} fn main() { f(bar) } -//~^ ERROR: expected `fn()` but found `extern "C" fn()` +//~^ ERROR: expected `fn()`, found `extern "C" fn()` diff --git a/src/test/compile-fail/issue-11319.rs b/src/test/compile-fail/issue-11319.rs index 1d9250305ef..c818b3bb26c 100644 --- a/src/test/compile-fail/issue-11319.rs +++ b/src/test/compile-fail/issue-11319.rs @@ -10,7 +10,7 @@ fn main() { match Some(10) { - //~^ ERROR match arms have incompatible types: expected `bool` but found `()` + //~^ ERROR match arms have incompatible types: expected `bool`, found `()` Some(5) => false, Some(2) => true, None => (), //~ NOTE match arm with an incompatible type diff --git a/src/test/compile-fail/issue-11515.rs b/src/test/compile-fail/issue-11515.rs index 4d520b570b7..82cd38ad5b0 100644 --- a/src/test/compile-fail/issue-11515.rs +++ b/src/test/compile-fail/issue-11515.rs @@ -14,5 +14,5 @@ struct Test<'s> { fn main() { let test = box Test { func: proc() {} }; - //~^ ERROR: expected `||` but found `proc()` + //~^ ERROR: expected `||`, found `proc()` } diff --git a/src/test/compile-fail/issue-11680.rs b/src/test/compile-fail/issue-11680.rs index 9c9663a2f38..86a58783a2a 100644 --- a/src/test/compile-fail/issue-11680.rs +++ b/src/test/compile-fail/issue-11680.rs @@ -10,7 +10,7 @@ // aux-build:issue-11680.rs -extern crate other = "issue-11680"; +extern crate "issue-11680" as other; fn main() { let _b = other::Bar(1); diff --git a/src/test/compile-fail/isuue-12470.rs b/src/test/compile-fail/issue-12470.rs index bf13b7ebbdb..aa7e3cd3739 100644 --- a/src/test/compile-fail/isuue-12470.rs +++ b/src/test/compile-fail/issue-12470.rs @@ -24,7 +24,7 @@ impl X for B { } struct A<'a> { - p: &'a X + p: &'a X+'a } fn make_a<'a>(p: &'a X) -> A<'a> { diff --git a/src/test/compile-fail/issue-12612.rs b/src/test/compile-fail/issue-12612.rs index ee998e57c56..0550472dabb 100644 --- a/src/test/compile-fail/issue-12612.rs +++ b/src/test/compile-fail/issue-12612.rs @@ -10,7 +10,7 @@ // aux-build:issue-12612-1.rs -extern crate foo = "issue-12612-1"; +extern crate "issue-12612-1" as foo; use foo::bar; diff --git a/src/test/compile-fail/issue-12997-2.rs b/src/test/compile-fail/issue-12997-2.rs index f520ce0eabb..83f6fb07232 100644 --- a/src/test/compile-fail/issue-12997-2.rs +++ b/src/test/compile-fail/issue-12997-2.rs @@ -12,6 +12,6 @@ //! Test that makes sure wrongly-typed bench functions are rejected -// error-pattern:expected &-ptr but found int +// error-pattern:expected &-ptr, found int #[bench] fn bar(x: int) { } diff --git a/src/test/compile-fail/issue-13446.rs b/src/test/compile-fail/issue-13446.rs index 0bb6ded0012..162324b7c59 100644 --- a/src/test/compile-fail/issue-13446.rs +++ b/src/test/compile-fail/issue-13446.rs @@ -11,7 +11,9 @@ // Used to cause ICE -static VEC: [u32, ..256] = vec!(); //~ ERROR mismatched types +// error-pattern: mismatched types + +static VEC: [u32, ..256] = vec!(); fn main() {} diff --git a/src/test/compile-fail/issue-13482.rs b/src/test/compile-fail/issue-13482.rs index 2b769b9e499..7159aa17623 100644 --- a/src/test/compile-fail/issue-13482.rs +++ b/src/test/compile-fail/issue-13482.rs @@ -12,7 +12,7 @@ fn main() { let x = [1,2]; let y = match x { [] => None, -//~^ ERROR expected `[<generic integer #1>, .. 2]` but found a fixed vector pattern of size 0 +//~^ ERROR expected `[<generic integer #1>, .. 2]`, found a fixed vector pattern of size 0 [a,_] => Some(a) }; } diff --git a/src/test/compile-fail/issue-13599.rs b/src/test/compile-fail/issue-13599.rs index de1a9e13f9e..eee23f1feba 100644 --- a/src/test/compile-fail/issue-13599.rs +++ b/src/test/compile-fail/issue-13599.rs @@ -16,8 +16,8 @@ fn expect_proc(_: proc()) {} fn main() { expect_closure(proc() {}); - //~^ ERROR mismatched types: expected `||` but found `proc()` (expected closure, found proc) + //~^ ERROR mismatched types: expected `||`, found `proc()` (expected closure, found proc) expect_proc(|| {}); - //~^ ERROR mismatched types: expected `proc()` but found `||` (expected proc, found closure) + //~^ ERROR mismatched types: expected `proc()`, found `||` (expected proc, found closure) } diff --git a/src/test/compile-fail/issue-14285.rs b/src/test/compile-fail/issue-14285.rs index d5e608ecae3..624ddf0c8bb 100644 --- a/src/test/compile-fail/issue-14285.rs +++ b/src/test/compile-fail/issue-14285.rs @@ -14,7 +14,7 @@ struct A; impl Foo for A {} -struct B<'a>(&'a Foo); +struct B<'a>(&'a Foo+'a); fn foo<'a>(a: &Foo) -> B<'a> { B(a) //~ ERROR cannot infer an appropriate lifetime diff --git a/src/test/compile-fail/issue-14309.rs b/src/test/compile-fail/issue-14309.rs new file mode 100644 index 00000000000..d4a40ade72c --- /dev/null +++ b/src/test/compile-fail/issue-14309.rs @@ -0,0 +1,49 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![deny(ctypes)] +#![allow(dead_code)] + +struct A { + x: i32 +} + +#[repr(C, packed)] +struct B { + x: i32, + y: A +} + +#[repr(C)] +struct C { + x: i32 +} + +type A2 = A; +type B2 = B; +type C2 = C; + +#[repr(C)] +struct D { + x: C, + y: A +} + +extern "C" { + fn foo(x: A); //~ ERROR found type without foreign-function-safe + fn bar(x: B); //~ ERROR foreign-function-safe + fn baz(x: C); + fn qux(x: A2); //~ ERROR foreign-function-safe + fn quux(x: B2); //~ ERROR foreign-function-safe + fn corge(x: C2); + fn fred(x: D); //~ ERROR foreign-function-safe +} + +fn main() { } diff --git a/src/test/compile-fail/issue-14845.rs b/src/test/compile-fail/issue-14845.rs index 90366d09e2d..fc5a591ebd6 100644 --- a/src/test/compile-fail/issue-14845.rs +++ b/src/test/compile-fail/issue-14845.rs @@ -16,9 +16,9 @@ struct X { fn main() { let x = X { a: [0] }; let _f = &x.a as *mut u8; - //~^ ERROR mismatched types: expected `*mut u8` but found `&[u8, .. 1]` + //~^ ERROR mismatched types: expected `*mut u8`, found `&[u8, .. 1]` let local = [0u8]; let _v = &local as *mut u8; - //~^ ERROR mismatched types: expected `*mut u8` but found `&[u8, .. 1]` + //~^ ERROR mismatched types: expected `*mut u8`, found `&[u8, .. 1]` } diff --git a/src/test/compile-fail/issue-15094.rs b/src/test/compile-fail/issue-15094.rs index 2540c7edb0c..c9e47b74d51 100644 --- a/src/test/compile-fail/issue-15094.rs +++ b/src/test/compile-fail/issue-15094.rs @@ -18,7 +18,7 @@ struct Shower<T> { impl<T: fmt::Show> ops::Fn<(), ()> for Shower<T> { fn call(&self, _args: ()) { -//~^ ERROR `call` has an incompatible type for trait: expected "rust-call" fn but found "Rust" fn +//~^ ERROR `call` has an incompatible type for trait: expected "rust-call" fn, found "Rust" fn println!("{}", self.x); } } diff --git a/src/test/compile-fail/issue-16338.rs b/src/test/compile-fail/issue-16338.rs index db0a0487687..d4b31066e5a 100644 --- a/src/test/compile-fail/issue-16338.rs +++ b/src/test/compile-fail/issue-16338.rs @@ -12,6 +12,6 @@ use std::raw::Slice; fn main() { let Slice { data: data, len: len } = "foo"; - //~^ ERROR mismatched types: expected `&'static str` but found a structure pattern + //~^ ERROR mismatched types: expected `&'static str`, found a structure pattern } diff --git a/src/test/compile-fail/issue-16401.rs b/src/test/compile-fail/issue-16401.rs index 79a824bbf69..bece4381e41 100644 --- a/src/test/compile-fail/issue-16401.rs +++ b/src/test/compile-fail/issue-16401.rs @@ -13,7 +13,7 @@ use std::raw::Slice; fn main() { match () { Slice { data: data, len: len } => (), - //~^ ERROR mismatched types: expected `()` but found a structure pattern + //~^ ERROR mismatched types: expected `()`, found a structure pattern _ => unreachable!() } } diff --git a/src/test/compile-fail/issue-1655.rs b/src/test/compile-fail/issue-1655.rs index ce5a5a09e48..6bdcf5c5edc 100644 --- a/src/test/compile-fail/issue-1655.rs +++ b/src/test/compile-fail/issue-1655.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// error-pattern:expected `[` but found `vec` +// error-pattern:expected `[`, found `vec` mod blade_runner { #vec[doc( brief = "Blade Runner is probably the best movie ever", diff --git a/src/test/compile-fail/issue-16725.rs b/src/test/compile-fail/issue-16725.rs new file mode 100644 index 00000000000..f70d88a41cd --- /dev/null +++ b/src/test/compile-fail/issue-16725.rs @@ -0,0 +1,19 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// aux-build:issue-16725.rs + +extern crate foo = "issue-16725"; + +fn main() { + unsafe { foo::bar(); } + //~^ ERROR: function `bar` is private +} + diff --git a/src/test/compile-fail/issue-2951.rs b/src/test/compile-fail/issue-2951.rs index d979afbc55c..694bf05b210 100644 --- a/src/test/compile-fail/issue-2951.rs +++ b/src/test/compile-fail/issue-2951.rs @@ -10,7 +10,7 @@ fn foo<T, U>(x: T, y: U) { let mut xx = x; - xx = y; //~ ERROR expected `T` but found `U` + xx = y; //~ ERROR expected `T`, found `U` } fn main() { diff --git a/src/test/compile-fail/issue-3036.rs b/src/test/compile-fail/issue-3036.rs index 45b4ab1871d..5f56f6b8b6b 100644 --- a/src/test/compile-fail/issue-3036.rs +++ b/src/test/compile-fail/issue-3036.rs @@ -13,4 +13,4 @@ fn main() { let x = 3 -} //~ ERROR: expected `;` but found `}` +} //~ ERROR: expected `;`, found `}` diff --git a/src/test/compile-fail/issue-3154.rs b/src/test/compile-fail/issue-3154.rs index 141bf2b4279..5f55c550aeb 100644 --- a/src/test/compile-fail/issue-3154.rs +++ b/src/test/compile-fail/issue-3154.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -struct thing<'a, Q> { +struct thing<'a, Q:'a> { x: &'a Q } diff --git a/src/test/compile-fail/issue-3477.rs b/src/test/compile-fail/issue-3477.rs index df9af9c6bcc..798a8cfec9a 100644 --- a/src/test/compile-fail/issue-3477.rs +++ b/src/test/compile-fail/issue-3477.rs @@ -9,5 +9,5 @@ // except according to those terms. fn main() { - let _p: char = 100; //~ ERROR mismatched types: expected `char` but found + let _p: char = 100; //~ ERROR mismatched types: expected `char`, found } diff --git a/src/test/compile-fail/issue-3680.rs b/src/test/compile-fail/issue-3680.rs index 02c619f5f36..96a93c205fe 100644 --- a/src/test/compile-fail/issue-3680.rs +++ b/src/test/compile-fail/issue-3680.rs @@ -12,6 +12,6 @@ fn main() { match None { Err(_) => () //~^ ERROR mismatched types: expected `core::option::Option<<generic #1>>` - // but found `core::result::Result<<generic #2>,<generic #3>>` + // , found `core::result::Result<<generic #2>,<generic #3>>` } } diff --git a/src/test/compile-fail/issue-3907-2.rs b/src/test/compile-fail/issue-3907-2.rs index 795e48cb7b0..71f91050256 100644 --- a/src/test/compile-fail/issue-3907-2.rs +++ b/src/test/compile-fail/issue-3907-2.rs @@ -11,10 +11,12 @@ // aux-build:issue_3907.rs extern crate issue_3907; -type Foo = issue_3907::Foo; //~ ERROR: reference to trait +type Foo = issue_3907::Foo+'static; struct S { name: int } +fn bar(_x: Foo) {} //~ ERROR variable `_x` has dynamically sized type + fn main() {} diff --git a/src/test/compile-fail/issue-3953.rs b/src/test/compile-fail/issue-3953.rs index 4484a004251..ab2018af999 100644 --- a/src/test/compile-fail/issue-3953.rs +++ b/src/test/compile-fail/issue-3953.rs @@ -12,15 +12,9 @@ use std::cmp::PartialEq; -trait Hahaha: PartialEq + PartialEq + PartialEq + PartialEq + PartialEq + //~ ERROR duplicate supertrait - PartialEq + PartialEq + PartialEq + PartialEq + PartialEq + PartialEq + PartialEq + PartialEq + PartialEq + PartialEq + PartialEq + PartialEq + PartialEq + - PartialEq + PartialEq + PartialEq + PartialEq + PartialEq + PartialEq + PartialEq + PartialEq + PartialEq + PartialEq + PartialEq + PartialEq + PartialEq + - PartialEq + PartialEq + PartialEq + PartialEq + PartialEq + PartialEq + PartialEq + PartialEq + PartialEq + PartialEq + PartialEq + PartialEq + PartialEq + - PartialEq + PartialEq + PartialEq + PartialEq + PartialEq + PartialEq + PartialEq + PartialEq + PartialEq + PartialEq + PartialEq + PartialEq + PartialEq + - PartialEq + PartialEq + PartialEq + PartialEq + PartialEq + PartialEq + PartialEq + PartialEq + PartialEq + PartialEq + PartialEq + PartialEq + PartialEq + - PartialEq + PartialEq + PartialEq + PartialEq + PartialEq + PartialEq + PartialEq + PartialEq + PartialEq + PartialEq + PartialEq + PartialEq + PartialEq + - PartialEq + PartialEq + PartialEq + PartialEq + PartialEq + PartialEq + PartialEq + PartialEq + PartialEq + PartialEq + PartialEq + PartialEq + - PartialEq {} +trait Hahaha: PartialEq + PartialEq { + //~^ ERROR trait `PartialEq` already appears in the list of bounds +} struct Lol(int); diff --git a/src/test/compile-fail/issue-4517.rs b/src/test/compile-fail/issue-4517.rs index b90a7f233b6..d80f2d1263d 100644 --- a/src/test/compile-fail/issue-4517.rs +++ b/src/test/compile-fail/issue-4517.rs @@ -13,6 +13,6 @@ fn bar(int_param: int) {} fn main() { let foo: [u8, ..4] = [1u8, ..4u]; bar(foo); - //~^ ERROR mismatched types: expected `int` but found `[u8, .. 4]` - // (expected int but found vector) + //~^ ERROR mismatched types: expected `int`, found `[u8, .. 4]` + // (expected int, found vector) } diff --git a/src/test/compile-fail/issue-4523.rs b/src/test/compile-fail/issue-4523.rs index 026327a358a..5063a78e383 100644 --- a/src/test/compile-fail/issue-4523.rs +++ b/src/test/compile-fail/issue-4523.rs @@ -10,8 +10,8 @@ fn foopy() {} -static f: ||: 'static = foopy; //~ ERROR found extern fn +static f: ||: 'static = foopy; fn main () { - f(); + f(); //~ ERROR closure invocation in a static location } diff --git a/src/test/compile-fail/issue-4968.rs b/src/test/compile-fail/issue-4968.rs index ec1f62c33fc..220fb76411a 100644 --- a/src/test/compile-fail/issue-4968.rs +++ b/src/test/compile-fail/issue-4968.rs @@ -13,6 +13,6 @@ static A: (int,int) = (4,2); fn main() { match 42 { A => () } - //~^ ERROR mismatched types: expected `<generic integer #0>` but found `(int,int)` - // (expected integral variable but found tuple) + //~^ ERROR mismatched types: expected `<generic integer #0>`, found `(int,int)` + // (expected integral variable, found tuple) } diff --git a/src/test/compile-fail/issue-4972.rs b/src/test/compile-fail/issue-4972.rs index d684d1b376b..bff167fa391 100644 --- a/src/test/compile-fail/issue-4972.rs +++ b/src/test/compile-fail/issue-4972.rs @@ -12,7 +12,7 @@ trait MyTrait { } pub enum TraitWrapper { - A(Box<MyTrait>), + A(Box<MyTrait+'static>), } fn get_tw_map(tw: &TraitWrapper) -> &MyTrait { diff --git a/src/test/compile-fail/issue-5035-2.rs b/src/test/compile-fail/issue-5035-2.rs index 8d9116da81d..0251a06c5bd 100644 --- a/src/test/compile-fail/issue-5035-2.rs +++ b/src/test/compile-fail/issue-5035-2.rs @@ -9,6 +9,8 @@ // except according to those terms. trait I {} -type K = I; //~ ERROR: reference to trait +type K = I+'static; + +fn foo(_x: K) {} //~ ERROR: variable `_x` has dynamically sized type fn main() {} diff --git a/src/test/compile-fail/issue-5100.rs b/src/test/compile-fail/issue-5100.rs index 6734a546be5..5c4127c5bae 100644 --- a/src/test/compile-fail/issue-5100.rs +++ b/src/test/compile-fail/issue-5100.rs @@ -12,33 +12,33 @@ enum A { B, C } fn main() { match (true, false) { - B => (), //~ ERROR expected `(bool,bool)` but found an enum or structure pattern + B => (), //~ ERROR expected `(bool,bool)`, found an enum or structure pattern _ => () } match (true, false) { (true, false, false) => () - //~^ ERROR mismatched types: expected `(bool,bool)` but found tuple - // (expected a tuple with 2 elements but found one with 3 elements) + //~^ ERROR mismatched types: expected `(bool,bool)`, found tuple + // (expected a tuple with 2 elements, found one with 3 elements) } match (true, false) { box (true, false) => () - //~^ ERROR mismatched types: expected `(bool,bool)` but found a box pattern + //~^ ERROR mismatched types: expected `(bool,bool)`, found a box pattern } match (true, false) { &(true, false) => () - //~^ ERROR mismatched types: expected `(bool,bool)` but found an `&`-pointer pattern + //~^ ERROR mismatched types: expected `(bool,bool)`, found an `&`-pointer pattern } - let v = [('a', 'b') //~ ERROR expected function but found `(char,char)` + let v = [('a', 'b') //~ ERROR expected function, found `(char,char)` ('c', 'd'), ('e', 'f')]; for &(x,y) in v.iter() {} // should be OK // Make sure none of the errors above were fatal - let x: char = true; //~ ERROR expected `char` but found `bool` + let x: char = true; //~ ERROR expected `char`, found `bool` } diff --git a/src/test/compile-fail/issue-5216.rs b/src/test/compile-fail/issue-5216.rs index ec9ec9565c4..18af9736ed9 100644 --- a/src/test/compile-fail/issue-5216.rs +++ b/src/test/compile-fail/issue-5216.rs @@ -9,12 +9,12 @@ // except according to those terms. fn f() { } -struct S(||); //~ ERROR missing lifetime specifier +struct S(||); //~ ERROR explicit lifetime bound required pub static C: S = S(f); fn g() { } -type T = ||; //~ ERROR missing lifetime specifier +type T = ||; //~ ERROR explicit lifetime bound required pub static D: T = g; fn main() {} diff --git a/src/test/compile-fail/issue-5358-1.rs b/src/test/compile-fail/issue-5358-1.rs index 326d2e1c852..de715902f2a 100644 --- a/src/test/compile-fail/issue-5358-1.rs +++ b/src/test/compile-fail/issue-5358-1.rs @@ -13,7 +13,7 @@ struct S(Either<uint, uint>); fn main() { match S(Left(5)) { - Right(_) => {} //~ ERROR mismatched types: expected `S` but found `Either + Right(_) => {} //~ ERROR mismatched types: expected `S`, found `Either _ => {} } } diff --git a/src/test/compile-fail/issue-5883.rs b/src/test/compile-fail/issue-5883.rs index 831e165ad0e..f3bbb8051b7 100644 --- a/src/test/compile-fail/issue-5883.rs +++ b/src/test/compile-fail/issue-5883.rs @@ -11,15 +11,14 @@ trait A {} struct Struct { - r: A //~ ERROR reference to trait `A` where a type is expected; try `Box<A>` or `&A` + r: A+'static } -fn new_struct(r: A) -> Struct { - //~^ ERROR reference to trait `A` where a type is expected; try `Box<A>` or `&A` - Struct { r: r } +fn new_struct(r: A+'static) -> Struct { + //~^ ERROR variable `r` has dynamically sized type + Struct { r: r } //~ ERROR trying to initialise a dynamically sized struct } trait Curve {} -enum E {X(Curve)} -//~^ ERROR reference to trait `Curve` where a type is expected; try `Box<Curve>` or `&Curve` +enum E {X(Curve+'static)} fn main() {} diff --git a/src/test/compile-fail/issue-6610.rs b/src/test/compile-fail/issue-6610.rs index f90833a852b..166e91b27ac 100644 --- a/src/test/compile-fail/issue-6610.rs +++ b/src/test/compile-fail/issue-6610.rs @@ -8,6 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -trait Foo { fn a() } //~ ERROR expected `;` or `{` but found `}` +trait Foo { fn a() } //~ ERROR expected `;` or `{`, found `}` fn main() {} diff --git a/src/test/compile-fail/issue-7013.rs b/src/test/compile-fail/issue-7013.rs index ea332c1e252..ef585998aa5 100644 --- a/src/test/compile-fail/issue-7013.rs +++ b/src/test/compile-fail/issue-7013.rs @@ -32,7 +32,7 @@ struct A { fn main() { let a = A {v: box B{v: None} as Box<Foo+Send>}; - //~^ ERROR cannot pack type `Box<B>`, which does not fulfill `Send` + //~^ ERROR cannot pack type `Box<B>`, which does not fulfill `Send`, as a trait bounded by Send let v = Rc::new(RefCell::new(a)); let w = v.clone(); let b = &*v; diff --git a/src/test/compile-fail/issue-7061.rs b/src/test/compile-fail/issue-7061.rs index 99035c8906b..a9e9416beb3 100644 --- a/src/test/compile-fail/issue-7061.rs +++ b/src/test/compile-fail/issue-7061.rs @@ -15,7 +15,7 @@ struct BarStruct; impl<'a> BarStruct { fn foo(&'a mut self) -> Gc<BarStruct> { self } - //~^ ERROR: error: mismatched types: expected `Gc<BarStruct>` but found `&'a mut BarStruct + //~^ ERROR: error: mismatched types: expected `Gc<BarStruct>`, found `&'a mut BarStruct } fn main() {} diff --git a/src/test/compile-fail/issue-7092.rs b/src/test/compile-fail/issue-7092.rs index 4b1c6791874..8b3df6f9f95 100644 --- a/src/test/compile-fail/issue-7092.rs +++ b/src/test/compile-fail/issue-7092.rs @@ -14,7 +14,7 @@ enum Whatever { fn foo(x: Whatever) { match x { Some(field) => field.access(), - //~^ ERROR: mismatched types: expected `Whatever` but found + //~^ ERROR: mismatched types: expected `Whatever`, found } } diff --git a/src/test/compile-fail/issue-8761.rs b/src/test/compile-fail/issue-8761.rs index 183965f099c..27369f77e88 100644 --- a/src/test/compile-fail/issue-8761.rs +++ b/src/test/compile-fail/issue-8761.rs @@ -10,9 +10,9 @@ enum Foo { A = 1i64, - //~^ ERROR mismatched types: expected `int` but found `i64` + //~^ ERROR mismatched types: expected `int`, found `i64` B = 2u8 - //~^ ERROR mismatched types: expected `int` but found `u8` + //~^ ERROR mismatched types: expected `int`, found `u8` } fn main() {} diff --git a/src/test/compile-fail/issue13359.rs b/src/test/compile-fail/issue13359.rs index 07197bd3f3c..227ed3fb834 100644 --- a/src/test/compile-fail/issue13359.rs +++ b/src/test/compile-fail/issue13359.rs @@ -14,8 +14,8 @@ fn bar(_s: u32) { } fn main() { foo(1*(1 as int)); - //~^ ERROR: mismatched types: expected `i16` but found `int` (expected `i16` but found `int`) + //~^ ERROR: mismatched types: expected `i16`, found `int` (expected `i16`, found `int`) bar(1*(1 as uint)); - //~^ ERROR: mismatched types: expected `u32` but found `uint` (expected `u32` but found `uint`) + //~^ ERROR: mismatched types: expected `u32`, found `uint` (expected `u32`, found `uint`) } diff --git a/src/test/compile-fail/kindck-impl-type-params.rs b/src/test/compile-fail/kindck-impl-type-params.rs index 48e1bdd671a..4cc03ee3dcd 100644 --- a/src/test/compile-fail/kindck-impl-type-params.rs +++ b/src/test/compile-fail/kindck-impl-type-params.rs @@ -28,7 +28,7 @@ fn f<T>(val: T) { fn main() { let t: S<&int> = S; let a = &t as &Gettable<&int>; - //~^ ERROR instantiating a type parameter with an incompatible type `&int` + //~^ ERROR instantiating a type parameter with an incompatible type let t: Box<S<String>> = box S; let a = t as Box<Gettable<String>>; //~^ ERROR instantiating a type parameter with an incompatible type diff --git a/src/test/compile-fail/proc-bounds.rs b/src/test/compile-fail/kindck-proc-bounds.rs index e8c6a3ba191..57c8cc3da8a 100644 --- a/src/test/compile-fail/proc-bounds.rs +++ b/src/test/compile-fail/kindck-proc-bounds.rs @@ -10,16 +10,13 @@ fn is_send<T: Send>() {} fn is_freeze<T: Sync>() {} -fn is_static<T: 'static>() {} -fn main() { +fn foo<'a>() { is_send::<proc()>(); //~^ ERROR: instantiating a type parameter with an incompatible type is_freeze::<proc()>(); //~^ ERROR: instantiating a type parameter with an incompatible type - - is_static::<proc()>(); - //~^ ERROR: instantiating a type parameter with an incompatible type } +fn main() { } diff --git a/src/test/compile-fail/kindck-send-object.rs b/src/test/compile-fail/kindck-send-object.rs new file mode 100644 index 00000000000..99519263923 --- /dev/null +++ b/src/test/compile-fail/kindck-send-object.rs @@ -0,0 +1,44 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test which of the builtin types are considered sendable. The tests +// in this file all test the "kind" violates detected during kindck. +// See all `regions-bounded-by-send.rs` + +fn assert_send<T:Send>() { } +trait Dummy { } +trait Message : Send { } + +// careful with object types, who knows what they close over... + +fn object_ref_with_static_bound_not_ok() { + assert_send::<&'static Dummy+'static>(); //~ ERROR does not fulfill +} + +fn box_object_with_no_bound_not_ok<'a>() { + assert_send::<Box<Dummy>>(); //~ ERROR does not fulfill +} + +fn proc_with_no_bound_not_ok<'a>() { + assert_send::<proc()>(); //~ ERROR does not fulfill +} + +fn closure_with_no_bound_not_ok<'a>() { + assert_send::<||:'static>(); //~ ERROR does not fulfill +} + +fn object_with_send_bound_ok() { + assert_send::<&'static Dummy+Send>(); + assert_send::<Box<Dummy+Send>>(); + assert_send::<proc():Send>; + assert_send::<||:Send>; +} + +fn main() { } diff --git a/src/test/compile-fail/kindck-send.rs b/src/test/compile-fail/kindck-send.rs deleted file mode 100644 index 424c7a4e430..00000000000 --- a/src/test/compile-fail/kindck-send.rs +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright 2012 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or -// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license -// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -// Test which of the builtin types are considered sendable. - - -fn assert_send<T:Send>() { } -trait Dummy { } - -fn test<'a,T,U:Send>(_: &'a int) { - // lifetime pointers with 'static lifetime are ok - assert_send::<&'static int>(); - assert_send::<&'static str>(); - assert_send::<&'static [int]>(); - - // whether or not they are mutable - assert_send::<&'static mut int>(); - - // otherwise lifetime pointers are not ok - assert_send::<&'a int>(); //~ ERROR does not fulfill `Send` - assert_send::<&'a str>(); //~ ERROR does not fulfill `Send` - assert_send::<&'a [int]>(); //~ ERROR does not fulfill `Send` - - // boxes are ok - assert_send::<Box<int>>(); - assert_send::<String>(); - assert_send::<Vec<int> >(); - - // but not if they own a bad thing - assert_send::<Box<&'a int>>(); //~ ERROR does not fulfill `Send` - - // careful with object types, who knows what they close over... - assert_send::<&'static Dummy>(); //~ ERROR does not fulfill `Send` - assert_send::<&'a Dummy>(); //~ ERROR does not fulfill `Send` - assert_send::<&'a Dummy+Send>(); //~ ERROR does not fulfill `Send` - assert_send::<Box<Dummy>>(); //~ ERROR does not fulfill `Send` - - // ...unless they are properly bounded - assert_send::<&'static Dummy+Send>(); - assert_send::<Box<Dummy+Send>>(); - - // but closure and object types can have lifetime bounds which make - // them not ok (FIXME #5121) - // assert_send::<proc:'a()>(); // ERROR does not fulfill `Send` - // assert_send::<Box<Dummy+'a>>(); // ERROR does not fulfill `Send` - - // unsafe ptrs are ok unless they point at unsendable things - assert_send::<*const int>(); - assert_send::<*const &'a int>(); //~ ERROR does not fulfill `Send` -} - -fn main() { -} diff --git a/src/test/compile-fail/lifetime-inference-give-expl-lifetime-param-3.rs b/src/test/compile-fail/lifetime-inference-give-expl-lifetime-param-3.rs new file mode 100644 index 00000000000..21bd676a225 --- /dev/null +++ b/src/test/compile-fail/lifetime-inference-give-expl-lifetime-param-3.rs @@ -0,0 +1,30 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// ignore-tidy-linelength + +struct Bar<'x, 'y, 'z> { bar: &'y int, baz: int } +fn bar1<'a>(x: &Bar) -> (&'a int, &'a int, &'a int) { +//~^ NOTE: consider using an explicit lifetime parameter as shown: fn bar1<'b, 'c, 'a>(x: &'a Bar<'b, 'a, 'c>) -> (&'a int, &'a int, &'a int) + (x.bar, &x.baz, &x.baz) + //~^ ERROR: cannot infer + //~^^ ERROR: cannot infer + //~^^^ ERROR: cannot infer +} + +fn bar2<'a, 'b, 'c>(x: &Bar<'a, 'b, 'c>) -> (&'a int, &'a int, &'a int) { +//~^ NOTE: consider using an explicit lifetime parameter as shown: fn bar2<'a, 'c>(x: &'a Bar<'a, 'a, 'c>) -> (&'a int, &'a int, &'a int) + (x.bar, &x.baz, &x.baz) + //~^ ERROR: cannot infer + //~^^ ERROR: cannot infer + //~^^^ ERROR: cannot infer +} + +fn main() { } diff --git a/src/test/compile-fail/lifetime-inference-give-expl-lifetime-param.rs b/src/test/compile-fail/lifetime-inference-give-expl-lifetime-param.rs index ef5a45fcf70..b7da4d73489 100644 --- a/src/test/compile-fail/lifetime-inference-give-expl-lifetime-param.rs +++ b/src/test/compile-fail/lifetime-inference-give-expl-lifetime-param.rs @@ -33,41 +33,24 @@ fn foo4<'a, 'b>(x: &'a Foo) -> (&'b int, &'a int, &'b int) { //~^ ERROR: cannot infer } -struct Bar<'x, 'y, 'z> { bar: &'y int, baz: int } -fn bar1<'a>(x: &Bar) -> (&'a int, &'a int, &'a int) { -//~^ NOTE: consider using an explicit lifetime parameter as shown: fn bar1<'b, 'c, 'a>(x: &'a Bar<'b, 'a, 'c>) -> (&'a int, &'a int, &'a int) - (x.bar, &x.baz, &x.baz) //~ ERROR: mismatched types - //~^ ERROR: cannot infer - //~^^ ERROR: cannot infer -} - -fn bar2<'a, 'b, 'c>(x: &Bar<'a, 'b, 'c>) -> (&'a int, &'a int, &'a int) { -//~^ NOTE: consider using an explicit lifetime parameter as shown: fn bar2<'a, 'c>(x: &'a Bar<'a, 'a, 'c>) -> (&'a int, &'a int, &'a int) - (x.bar, &x.baz, &x.baz) //~ ERROR: mismatched types - //~^ ERROR: cannot infer - //~^^ ERROR: cannot infer -} - struct Cat<'x, T> { cat: &'x int, t: T } struct Dog<'y> { dog: &'y int } fn cat2<'x, 'y>(x: Cat<'x, Dog<'y>>) -> &'x int { //~^ NOTE: consider using an explicit lifetime parameter as shown: fn cat2<'x>(x: Cat<'x, Dog<'x>>) -> &'x int - x.t.dog //~ ERROR: mismatched types + x.t.dog //~ ERROR: cannot infer } struct Baz<'x> { bar: &'x int } - impl<'a> Baz<'a> { fn baz2<'b>(&self, x: &int) -> (&'b int, &'b int) { // The lifetime that gets assigned to `x` seems somewhat random. // I have disabled this test for the time being. --pcwalton (self.bar, x) //~ ERROR: cannot infer - //~^ ERROR: mismatched types - //~^^ ERROR: mismatched types + //~^ ERROR: cannot infer } } diff --git a/src/test/compile-fail/lint-ctypes-enum.rs b/src/test/compile-fail/lint-ctypes-enum.rs index e968bd601c5..d45a3b027a7 100644 --- a/src/test/compile-fail/lint-ctypes-enum.rs +++ b/src/test/compile-fail/lint-ctypes-enum.rs @@ -18,9 +18,9 @@ enum T { E, F, G } extern { fn zf(x: Z); - fn uf(x: U); - fn bf(x: B); //~ ERROR found enum type without foreign-function-safe - fn tf(x: T); //~ ERROR found enum type without foreign-function-safe + fn uf(x: U); //~ ERROR found type without foreign-function-safe + fn bf(x: B); //~ ERROR found type without foreign-function-safe + fn tf(x: T); //~ ERROR found type without foreign-function-safe } pub fn main() { } diff --git a/src/test/compile-fail/lint-non-camel-case-types.rs b/src/test/compile-fail/lint-non-camel-case-types.rs index 784930003d0..f33c7956a14 100644 --- a/src/test/compile-fail/lint-non-camel-case-types.rs +++ b/src/test/compile-fail/lint-non-camel-case-types.rs @@ -37,4 +37,6 @@ struct foo7 { bar: int, } +type __ = int; //~ ERROR type `__` should have a camel case name such as `CamelCase` + fn main() { } diff --git a/src/test/compile-fail/loop-does-not-diverge.rs b/src/test/compile-fail/loop-does-not-diverge.rs index 0a9d9fb20ab..d0e52493054 100644 --- a/src/test/compile-fail/loop-does-not-diverge.rs +++ b/src/test/compile-fail/loop-does-not-diverge.rs @@ -14,7 +14,7 @@ fn forever() -> ! { loop { break; } - return 42i; //~ ERROR expected `!` but found `int` + return 42i; //~ ERROR expected `!`, found `int` } fn main() { diff --git a/src/test/compile-fail/lub-if.rs b/src/test/compile-fail/lub-if.rs index 82938d63ce5..5440219e55e 100644 --- a/src/test/compile-fail/lub-if.rs +++ b/src/test/compile-fail/lub-if.rs @@ -31,18 +31,18 @@ pub fn opt_str1<'a>(maybestr: &'a Option<String>) -> &'a str { } pub fn opt_str2<'a>(maybestr: &'a Option<String>) -> &'static str { - if maybestr.is_none() { //~ ERROR mismatched types + if maybestr.is_none() { "(none)" } else { let s: &'a str = maybestr.get_ref().as_slice(); - s + s //~ ERROR cannot infer an appropriate lifetime for automatic coercion due to conflicting } } pub fn opt_str3<'a>(maybestr: &'a Option<String>) -> &'static str { - if maybestr.is_some() { //~ ERROR mismatched types + if maybestr.is_some() { let s: &'a str = maybestr.get_ref().as_slice(); - s + s //~ ERROR cannot infer an appropriate lifetime for automatic coercion due to conflicting } else { "(none)" } diff --git a/src/test/compile-fail/lub-match.rs b/src/test/compile-fail/lub-match.rs index 98bea422fb5..febe5f45d96 100644 --- a/src/test/compile-fail/lub-match.rs +++ b/src/test/compile-fail/lub-match.rs @@ -33,7 +33,7 @@ pub fn opt_str1<'a>(maybestr: &'a Option<String>) -> &'a str { } pub fn opt_str2<'a>(maybestr: &'a Option<String>) -> &'static str { - match *maybestr { //~ ERROR mismatched types + match *maybestr { //~ ERROR cannot infer an appropriate lifetime for automatic coercion due to None => "(none)", Some(ref s) => { let s: &'a str = s.as_slice(); @@ -43,7 +43,7 @@ pub fn opt_str2<'a>(maybestr: &'a Option<String>) -> &'static str { } pub fn opt_str3<'a>(maybestr: &'a Option<String>) -> &'static str { - match *maybestr { //~ ERROR mismatched types + match *maybestr { //~ ERROR cannot infer an appropriate lifetime for automatic coercion due to Some(ref s) => { let s: &'a str = s.as_slice(); s diff --git a/src/test/compile-fail/macro-bad-delimiter-ident.rs b/src/test/compile-fail/macro-bad-delimiter-ident.rs index 6f3b8bd421f..75f7b5d4dd8 100644 --- a/src/test/compile-fail/macro-bad-delimiter-ident.rs +++ b/src/test/compile-fail/macro-bad-delimiter-ident.rs @@ -9,5 +9,5 @@ // except according to those terms. fn main() { - foo! bar < //~ ERROR expected `(` or `{`, but found `<` + foo! bar < //~ ERROR expected `(` or `{`, found `<` } diff --git a/src/test/compile-fail/match-struct.rs b/src/test/compile-fail/match-struct.rs index 4956528856b..2491d0b3769 100644 --- a/src/test/compile-fail/match-struct.rs +++ b/src/test/compile-fail/match-struct.rs @@ -14,7 +14,7 @@ enum E { C(int) } fn main() { match (S { a: 1 }) { - C(_) => (), //~ ERROR mismatched types: expected `S` but found `E` + C(_) => (), //~ ERROR mismatched types: expected `S`, found `E` _ => () } } diff --git a/src/test/compile-fail/match-vec-mismatch-2.rs b/src/test/compile-fail/match-vec-mismatch-2.rs index c3a06aa0e2c..fee49ad8fdb 100644 --- a/src/test/compile-fail/match-vec-mismatch-2.rs +++ b/src/test/compile-fail/match-vec-mismatch-2.rs @@ -10,6 +10,6 @@ fn main() { match () { - [()] => { } //~ ERROR mismatched types: expected `()` but found a vector pattern + [()] => { } //~ ERROR mismatched types: expected `()`, found a vector pattern } } diff --git a/src/test/compile-fail/moves-sru-moved-field.rs b/src/test/compile-fail/moves-sru-moved-field.rs index 8b02740497d..74e5e6b1202 100644 --- a/src/test/compile-fail/moves-sru-moved-field.rs +++ b/src/test/compile-fail/moves-sru-moved-field.rs @@ -9,7 +9,7 @@ // except according to those terms. -type Noncopyable = proc(); +type Noncopyable = proc():'static; struct Foo { copied: int, diff --git a/src/test/compile-fail/multitrait.rs b/src/test/compile-fail/multitrait.rs index c7b0bc8822b..795e3807d5e 100644 --- a/src/test/compile-fail/multitrait.rs +++ b/src/test/compile-fail/multitrait.rs @@ -12,7 +12,7 @@ struct S { y: int } -impl Cmp, ToString for S { //~ ERROR: expected `{` but found `,` +impl Cmp, ToString for S { //~ ERROR: expected `{`, found `,` fn eq(&&other: S) { false } fn to_string(&self) -> String { "hi".to_string() } } diff --git a/src/test/compile-fail/mut-patterns.rs b/src/test/compile-fail/mut-patterns.rs index d9cdae4a499..a33a603f7f5 100644 --- a/src/test/compile-fail/mut-patterns.rs +++ b/src/test/compile-fail/mut-patterns.rs @@ -12,5 +12,5 @@ pub fn main() { struct Foo { x: int } - let mut Foo { x: x } = Foo { x: 3 }; //~ ERROR: expected `;` but found `{` + let mut Foo { x: x } = Foo { x: 3 }; //~ ERROR: expected `;`, found `{` } diff --git a/src/test/compile-fail/noexporttypeexe.rs b/src/test/compile-fail/noexporttypeexe.rs index 508fec069c1..7e113d4a8c4 100644 --- a/src/test/compile-fail/noexporttypeexe.rs +++ b/src/test/compile-fail/noexporttypeexe.rs @@ -18,5 +18,5 @@ fn main() { // because the def_id associated with the type was // not convertible to a path. let x: int = noexporttypelib::foo(); - //~^ ERROR expected `int` but found `core::option::Option<int>` + //~^ ERROR expected `int`, found `core::option::Option<int>` } diff --git a/src/test/compile-fail/non-constant-enum-for-vec-repeat.rs b/src/test/compile-fail/non-constant-enum-for-vec-repeat.rs index fd857129c35..c4d5d734a71 100644 --- a/src/test/compile-fail/non-constant-enum-for-vec-repeat.rs +++ b/src/test/compile-fail/non-constant-enum-for-vec-repeat.rs @@ -12,5 +12,5 @@ enum State { ST_NULL, ST_WHITESPACE } fn main() { [ST_NULL, ..(ST_WHITESPACE as uint)]; - //~^ ERROR expected constant integer for repeat count but found variable + //~^ ERROR expected constant integer for repeat count, found variable } diff --git a/src/test/compile-fail/non-constant-expr-for-vec-repeat.rs b/src/test/compile-fail/non-constant-expr-for-vec-repeat.rs index 41ea0c88e07..299e9d3dced 100644 --- a/src/test/compile-fail/non-constant-expr-for-vec-repeat.rs +++ b/src/test/compile-fail/non-constant-expr-for-vec-repeat.rs @@ -12,6 +12,6 @@ fn main() { fn bar(n: uint) { - let _x = [0, ..n]; //~ ERROR expected constant integer for repeat count but found variable + let _x = [0, ..n]; //~ ERROR expected constant integer for repeat count, found variable } } diff --git a/src/test/compile-fail/omitted-arg-in-item-fn.rs b/src/test/compile-fail/omitted-arg-in-item-fn.rs index fcbfb115af7..c5ff885997b 100644 --- a/src/test/compile-fail/omitted-arg-in-item-fn.rs +++ b/src/test/compile-fail/omitted-arg-in-item-fn.rs @@ -8,5 +8,5 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -fn foo(x) { //~ ERROR expected `:` but found `)` +fn foo(x) { //~ ERROR expected `:`, found `)` } diff --git a/src/test/compile-fail/packed-struct-generic-transmute.rs b/src/test/compile-fail/packed-struct-generic-transmute.rs index b5bb3314dcf..7a1ff574488 100644 --- a/src/test/compile-fail/packed-struct-generic-transmute.rs +++ b/src/test/compile-fail/packed-struct-generic-transmute.rs @@ -19,7 +19,7 @@ extern crate debug; use std::mem; -#[packed] +#[repr(packed)] struct Foo<T,S> { bar: T, baz: S diff --git a/src/test/compile-fail/packed-struct-transmute.rs b/src/test/compile-fail/packed-struct-transmute.rs index 0611c470d10..f92cc4b1344 100644 --- a/src/test/compile-fail/packed-struct-transmute.rs +++ b/src/test/compile-fail/packed-struct-transmute.rs @@ -19,7 +19,7 @@ extern crate debug; use std::mem; -#[packed] +#[repr(packed)] struct Foo { bar: u8, baz: uint diff --git a/src/test/compile-fail/pattern-error-continue.rs b/src/test/compile-fail/pattern-error-continue.rs index 47d0a226a56..01feda34e08 100644 --- a/src/test/compile-fail/pattern-error-continue.rs +++ b/src/test/compile-fail/pattern-error-continue.rs @@ -29,9 +29,9 @@ fn main() { _ => () } match 'c' { - S { .. } => (), //~ ERROR mismatched types: expected `char` but found a structure pattern + S { .. } => (), //~ ERROR mismatched types: expected `char`, found a structure pattern _ => () } - f(true); //~ ERROR mismatched types: expected `char` but found `bool` + f(true); //~ ERROR mismatched types: expected `char`, found `bool` } diff --git a/src/test/compile-fail/pptypedef.rs b/src/test/compile-fail/pptypedef.rs index 79f0cc352e4..4de56e32f56 100644 --- a/src/test/compile-fail/pptypedef.rs +++ b/src/test/compile-fail/pptypedef.rs @@ -12,8 +12,8 @@ fn let_in<T>(x: T, f: |T|) {} fn main() { let_in(3u, |i| { assert!(i == 3i); }); - //~^ ERROR expected `uint` but found `int` + //~^ ERROR expected `uint`, found `int` let_in(3i, |i| { assert!(i == 3u); }); - //~^ ERROR expected `int` but found `uint` + //~^ ERROR expected `int`, found `uint` } diff --git a/src/test/compile-fail/privacy-struct-variant.rs b/src/test/compile-fail/privacy-struct-variant.rs index d6fab326ba1..ba39d84cf78 100644 --- a/src/test/compile-fail/privacy-struct-variant.rs +++ b/src/test/compile-fail/privacy-struct-variant.rs @@ -12,7 +12,7 @@ #![feature(struct_variant)] -extern crate other = "privacy-struct-variant"; +extern crate "privacy-struct-variant" as other; mod a { pub enum Foo { diff --git a/src/test/compile-fail/privacy5.rs b/src/test/compile-fail/privacy5.rs index 2683022c4c8..555969b65ff 100644 --- a/src/test/compile-fail/privacy5.rs +++ b/src/test/compile-fail/privacy5.rs @@ -11,7 +11,7 @@ // aux-build:privacy-tuple-struct.rs // ignore-fast -extern crate other = "privacy-tuple-struct"; +extern crate "privacy-tuple-struct" as other; mod a { pub struct A(()); diff --git a/src/test/compile-fail/raw-str-unbalanced.rs b/src/test/compile-fail/raw-str-unbalanced.rs index 3e161041711..4f3fb7d5b8a 100644 --- a/src/test/compile-fail/raw-str-unbalanced.rs +++ b/src/test/compile-fail/raw-str-unbalanced.rs @@ -10,5 +10,5 @@ static s: &'static str = r#" - "## //~ ERROR expected `;` but found `#` + "## //~ ERROR expected `;`, found `#` ; diff --git a/src/test/compile-fail/region-bounds-on-objects-and-type-parameters.rs b/src/test/compile-fail/region-bounds-on-objects-and-type-parameters.rs new file mode 100644 index 00000000000..40cff3e466b --- /dev/null +++ b/src/test/compile-fail/region-bounds-on-objects-and-type-parameters.rs @@ -0,0 +1,44 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test related to when a region bound is required to be specified. + +trait IsStatic : 'static { } +trait IsSend : Send { } +trait Is<'a> : 'a { } +trait Is2<'a> : 'a { } +trait SomeTrait { } + +// Bounds on object types: + +struct Foo<'a,'b,'c> { + // All of these are ok, because we can derive exactly one bound: + a: Box<IsStatic>, + b: Box<Is<'static>>, + c: Box<Is<'a>>, + d: Box<IsSend>, + e: Box<Is<'a>+Send>, // we can derive two bounds, but one is 'static, so ok + f: Box<SomeTrait>, //~ ERROR explicit lifetime bound required + g: Box<SomeTrait+'a>, + + z: Box<Is<'a>+'b+'c>, //~ ERROR only a single explicit lifetime bound is permitted +} + +fn test< + 'a, + 'b, + A:IsStatic, + B:Is<'a>+Is2<'b>, //~ ERROR ambiguous lifetime bound + C:'b+Is<'a>+Is2<'b>, + D:Is<'a>+Is2<'static>, + E:'a+'b //~ ERROR only a single explicit lifetime bound is permitted +>() { } + +fn main() { } diff --git a/src/test/compile-fail/region-object-lifetime-1.rs b/src/test/compile-fail/region-object-lifetime-1.rs new file mode 100644 index 00000000000..01daeb628ef --- /dev/null +++ b/src/test/compile-fail/region-object-lifetime-1.rs @@ -0,0 +1,49 @@ +// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Various tests related to testing how region inference works +// with respect to the object receivers. + +trait Foo { + fn borrowed<'a>(&'a self) -> &'a (); +} + +// Here the receiver and return value all have the same lifetime, +// so no error results. +fn borrowed_receiver_same_lifetime<'a>(x: &'a Foo) -> &'a () { + x.borrowed() +} + +// Borrowed receiver but two distinct lifetimes, we get an error. +fn borrowed_receiver_different_lifetimes<'a,'b>(x: &'a Foo) -> &'b () { + x.borrowed() //~ ERROR cannot infer +} + +// Borrowed receiver with two distinct lifetimes, but we know that +// 'b:'a, hence &'a () is permitted. +fn borrowed_receiver_related_lifetimes<'a,'b>(x: &'a Foo+'b) -> &'a () { + x.borrowed() +} + +// Here we have two distinct lifetimes, but we try to return a pointer +// with the longer lifetime when (from the signature) we only know +// that it lives as long as the shorter lifetime. Therefore, error. +fn borrowed_receiver_related_lifetimes2<'a,'b>(x: &'a Foo+'b) -> &'b () { + x.borrowed() //~ ERROR cannot infer +} + +// Here, the object is bounded by an anonymous lifetime and returned +// as `&'static`, so you get an error. +fn owned_receiver(x: Box<Foo>) -> &'static () { + x.borrowed() //~ ERROR cannot infer +} + +fn main() {} + diff --git a/src/test/compile-fail/regionck-closure-lifetimes.rs b/src/test/compile-fail/regionck-closure-lifetimes.rs index 846e03d57c3..bb895a318ff 100644 --- a/src/test/compile-fail/regionck-closure-lifetimes.rs +++ b/src/test/compile-fail/regionck-closure-lifetimes.rs @@ -15,7 +15,7 @@ fn env<'a>(blk: |p: ||: 'a|) { let mut state = 0i; let statep = &mut state; - blk(|| *statep = 1i); //~ ERROR cannot infer + blk(|| *statep = 1i); //~ ERROR captured variable `statep` does not outlive } fn no_env_no_for<'a>(blk: |p: |||: 'a) { diff --git a/src/test/compile-fail/regionck-unboxed-closure-lifetimes.rs b/src/test/compile-fail/regionck-unboxed-closure-lifetimes.rs index 1c590db11e3..7520a4c125a 100644 --- a/src/test/compile-fail/regionck-unboxed-closure-lifetimes.rs +++ b/src/test/compile-fail/regionck-unboxed-closure-lifetimes.rs @@ -16,9 +16,8 @@ fn main() { let mut f; { let c = 1; - let c_ref = &c; + let c_ref = &c; //~ ERROR `c` does not live long enough f = |&mut: a: int, b: int| { a + b + *c_ref }; - //~^ ERROR cannot infer an appropriate lifetime } } diff --git a/src/test/compile-fail/regions-bound-missing-bound-in-impl.rs b/src/test/compile-fail/regions-bound-missing-bound-in-impl.rs new file mode 100644 index 00000000000..0c9f5004f57 --- /dev/null +++ b/src/test/compile-fail/regions-bound-missing-bound-in-impl.rs @@ -0,0 +1,57 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Check that explicit region bounds are allowed on the various +// nominal types (but not on other types) and that they are type +// checked. + +#![no_std] + +struct Inv<'a> { // invariant w/r/t 'a + x: &'a mut &'a int +} + +pub trait Foo<'a> { + fn no_bound<'b>(self, b: Inv<'b>); + fn has_bound<'b:'a>(self, b: Inv<'b>); + fn wrong_bound1<'b,'c,'d:'a+'b>(self, b: Inv<'b>, c: Inv<'c>, d: Inv<'d>); + fn wrong_bound2<'b,'c,'d:'a+'b+'c>(self, b: Inv<'b>, c: Inv<'c>, d: Inv<'d>); +} + + +impl<'a> Foo<'a> for &'a int { + fn no_bound<'b:'a>(self, b: Inv<'b>) { + //~^ ERROR lifetime parameters or bounds on method `no_bound` do not match + } + + fn has_bound<'b>(self, b: Inv<'b>) { + //~^ ERROR lifetime parameters or bounds on method `has_bound` do not match + } + + fn wrong_bound1<'b,'c,'d:'a+'c>(self, b: Inv<'b>, c: Inv<'c>, d: Inv<'d>) { + //~^ ERROR method `wrong_bound1` has an incompatible type for trait + // + // Note: This is a terrible error message. It is caused + // because, in the trait, 'b is early bound, and in the impl, + // 'c is early bound, so -- after substitution -- the + // lifetimes themselves look isomorphic. We fail because the + // lifetimes that appear in the types are in the wrong + // order. This should really be fixed by keeping more + // information about the lifetime declarations in the trait so + // that we can compare better to the impl, even in cross-crate + // cases. + } + + fn wrong_bound2<'b,'c,'e:'b+'c>(self, b: Inv<'b>, c: Inv<'c>, e: Inv<'e>) { + //~^ ERROR distinct set of bounds from its counterpart + } +} + +fn main() { } diff --git a/src/test/compile-fail/regions-bounded-by-send.rs b/src/test/compile-fail/regions-bounded-by-send.rs new file mode 100644 index 00000000000..3c7ffbc8d1f --- /dev/null +++ b/src/test/compile-fail/regions-bounded-by-send.rs @@ -0,0 +1,91 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test which of the builtin types are considered sendable. The tests +// in this file all test region bound and lifetime violations that are +// detected during type check. + +fn assert_send<T:Send>() { } +trait Dummy { } + +// lifetime pointers with 'static lifetime are ok + +fn static_lifime_ok<'a,T,U:Send>(_: &'a int) { + assert_send::<&'static int>(); + assert_send::<&'static str>(); + assert_send::<&'static [int]>(); + + // whether or not they are mutable + assert_send::<&'static mut int>(); +} + +// otherwise lifetime pointers are not ok + +fn param_not_ok<'a>(x: &'a int) { + assert_send::<&'a int>(); //~ ERROR does not fulfill +} + +fn param_not_ok1<'a>(_: &'a int) { + assert_send::<&'a str>(); //~ ERROR does not fulfill +} + +fn param_not_ok2<'a>(_: &'a int) { + assert_send::<&'a [int]>(); //~ ERROR does not fulfill +} + +// boxes are ok + +fn box_ok() { + assert_send::<Box<int>>(); + assert_send::<String>(); + assert_send::<Vec<int>>(); +} + +// but not if they own a bad thing + +fn box_with_region_not_ok<'a>() { + assert_send::<Box<&'a int>>(); //~ ERROR does not fulfill +} + +// objects with insufficient bounds no ok + +fn object_with_random_bound_not_ok<'a>() { + assert_send::<&'a Dummy+'a>(); //~ ERROR does not fulfill +} + +fn object_with_send_bound_not_ok<'a>() { + assert_send::<&'a Dummy+Send>(); //~ ERROR does not fulfill +} + +fn proc_with_lifetime_not_ok<'a>() { + assert_send::<proc():'a>(); //~ ERROR does not fulfill +} + +fn closure_with_lifetime_not_ok<'a>() { + assert_send::<||:'a>(); //~ ERROR does not fulfill +} + +// unsafe pointers are ok unless they point at unsendable things + +fn unsafe_ok1<'a>(_: &'a int) { + assert_send::<*const int>(); + assert_send::<*mut int>(); +} + +fn unsafe_ok2<'a>(_: &'a int) { + assert_send::<*const &'a int>(); //~ ERROR does not fulfill +} + +fn unsafe_ok3<'a>(_: &'a int) { + assert_send::<*mut &'a int>(); //~ ERROR does not fulfill +} + +fn main() { +} diff --git a/src/test/compile-fail/regions-bounded-by-trait-requiring-static.rs b/src/test/compile-fail/regions-bounded-by-trait-requiring-static.rs new file mode 100644 index 00000000000..04a94b75215 --- /dev/null +++ b/src/test/compile-fail/regions-bounded-by-trait-requiring-static.rs @@ -0,0 +1,73 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test which of the builtin types are considered sendable. The tests +// in this file all test region bound and lifetime violations that are +// detected during type check. + +trait Dummy : 'static { } +fn assert_send<T:'static>() { } + +// lifetime pointers with 'static lifetime are ok + +fn static_lifime_ok<'a,T,U:Send>(_: &'a int) { + assert_send::<&'static int>(); + assert_send::<&'static str>(); + assert_send::<&'static [int]>(); + + // whether or not they are mutable + assert_send::<&'static mut int>(); +} + +// otherwise lifetime pointers are not ok + +fn param_not_ok<'a>(x: &'a int) { + assert_send::<&'a int>(); //~ ERROR does not fulfill +} + +fn param_not_ok1<'a>(_: &'a int) { + assert_send::<&'a str>(); //~ ERROR does not fulfill +} + +fn param_not_ok2<'a>(_: &'a int) { + assert_send::<&'a [int]>(); //~ ERROR does not fulfill +} + +// boxes are ok + +fn box_ok() { + assert_send::<Box<int>>(); + assert_send::<String>(); + assert_send::<Vec<int>>(); +} + +// but not if they own a bad thing + +fn box_with_region_not_ok<'a>() { + assert_send::<Box<&'a int>>(); //~ ERROR does not fulfill +} + +// unsafe pointers are ok unless they point at unsendable things + +fn unsafe_ok1<'a>(_: &'a int) { + assert_send::<*const int>(); + assert_send::<*mut int>(); +} + +fn unsafe_ok2<'a>(_: &'a int) { + assert_send::<*const &'a int>(); //~ ERROR does not fulfill +} + +fn unsafe_ok3<'a>(_: &'a int) { + assert_send::<*mut &'a int>(); //~ ERROR does not fulfill +} + +fn main() { +} diff --git a/src/test/compile-fail/regions-bounded-method-type-parameters-cross-crate.rs b/src/test/compile-fail/regions-bounded-method-type-parameters-cross-crate.rs new file mode 100644 index 00000000000..ab97bad5bc2 --- /dev/null +++ b/src/test/compile-fail/regions-bounded-method-type-parameters-cross-crate.rs @@ -0,0 +1,33 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// aux-build:regions-bounded-method-type-parameters-cross-crate-lib.rs + +// Check explicit region bounds on methods in the cross crate case. + +extern crate lib = "regions-bounded-method-type-parameters-cross-crate-lib"; + +use lib::Inv; +use lib::MaybeOwned; +use lib::IntoMaybeOwned; + +fn call_into_maybe_owned<'a,F:IntoMaybeOwned<'a>>(f: F) { + // Exercise a code path I found to be buggy. We were not encoding + // the region parameters from the receiver correctly on trait + // methods. + f.into_maybe_owned(); +} + +fn call_bigger_region<'a, 'b>(a: Inv<'a>, b: Inv<'b>) { + // Here the value provided for 'y is 'b, and hence 'b:'a does not hold. + a.bigger_region(b) //~ ERROR cannot infer +} + +fn main() { } diff --git a/src/test/compile-fail/regions-bounded-method-type-parameters-trait-bound.rs b/src/test/compile-fail/regions-bounded-method-type-parameters-trait-bound.rs new file mode 100644 index 00000000000..e628eb3285a --- /dev/null +++ b/src/test/compile-fail/regions-bounded-method-type-parameters-trait-bound.rs @@ -0,0 +1,44 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![no_std] +#![feature(lang_items)] + +// Check that explicit region bounds are allowed on the various +// nominal types (but not on other types) and that they are type +// checked. + +#[lang="sized"] +trait Sized { } + +struct Inv<'a> { // invariant w/r/t 'a + x: &'a mut &'a int +} + +trait Foo<'x> { + fn method<'y:'x>(self, y: Inv<'y>); +} + +fn caller1<'a,'b,F:Foo<'a>>(a: Inv<'a>, b: Inv<'b>, f: F) { + // Here the value provided for 'y is 'a, and hence 'a:'a holds. + f.method(a); +} + +fn caller2<'a,'b,F:Foo<'a>>(a: Inv<'a>, b: Inv<'b>, f: F) { + // Here the value provided for 'y is 'b, and hence 'b:'a does not hold. + f.method(b); //~ ERROR cannot infer +} + +fn caller3<'a,'b:'a,F:Foo<'a>>(a: Inv<'a>, b: Inv<'b>, f: F) { + // Here the value provided for 'y is 'b, and hence 'b:'a holds. + f.method(b); +} + +fn main() { } diff --git a/src/test/compile-fail/regions-bounded-method-type-parameters.rs b/src/test/compile-fail/regions-bounded-method-type-parameters.rs new file mode 100644 index 00000000000..ba1993686d5 --- /dev/null +++ b/src/test/compile-fail/regions-bounded-method-type-parameters.rs @@ -0,0 +1,28 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![no_std] + +// Check that explicit region bounds are allowed on the various +// nominal types (but not on other types) and that they are type +// checked. + +struct Foo; + +impl Foo { + fn some_method<A:'static>(self) { } +} + +fn caller<'a>(x: &int) { + Foo.some_method::<&'a int>(); + //~^ ERROR does not fulfill the required lifetime +} + +fn main() { } diff --git a/src/test/compile-fail/regions-bounds.rs b/src/test/compile-fail/regions-bounds.rs index 5ef043634fb..68e198ea5b7 100644 --- a/src/test/compile-fail/regions-bounds.rs +++ b/src/test/compile-fail/regions-bounds.rs @@ -16,12 +16,12 @@ struct an_enum<'a>(&'a int); struct a_class<'a> { x:&'a int } fn a_fn1<'a,'b>(e: an_enum<'a>) -> an_enum<'b> { - return e; //~ ERROR mismatched types: expected `an_enum<'b>` but found `an_enum<'a>` + return e; //~ ERROR mismatched types: expected `an_enum<'b>`, found `an_enum<'a>` //~^ ERROR cannot infer } fn a_fn3<'a,'b>(e: a_class<'a>) -> a_class<'b> { - return e; //~ ERROR mismatched types: expected `a_class<'b>` but found `a_class<'a>` + return e; //~ ERROR mismatched types: expected `a_class<'b>`, found `a_class<'a>` //~^ ERROR cannot infer } diff --git a/src/test/compile-fail/owned-ptr-static-bound.rs b/src/test/compile-fail/regions-close-object-into-object.rs index dc6e8b1d6be..a45c8e1db54 100644 --- a/src/test/compile-fail/owned-ptr-static-bound.rs +++ b/src/test/compile-fail/regions-close-object-into-object.rs @@ -10,21 +10,25 @@ trait A<T> {} -struct B<'a, T>(&'a A<T>); +struct B<'a, T>(&'a A<T>+'a); trait X {} impl<'a, T> X for B<'a, T> {} -fn f<'a, T, U>(v: Box<A<T>>) -> Box<X> { - box B(v) as Box<X> //~ ERROR value may contain references; add `'static` bound to `T` +fn f<'a, T, U>(v: Box<A<T>+'static>) -> Box<X+'static> { + box B(v) as Box<X> } -fn g<'a, T, U>(v: Box<A<U>>) -> Box<X> { - box B(v) as Box<X> //~ ERROR value may contain references; add `'static` bound to `U` +fn g<'a, T: 'static>(v: Box<A<T>>) -> Box<X+'static> { + box B(v) as Box<X> //~ ERROR cannot infer } -fn h<'a, T: 'static>(v: Box<A<T>>) -> Box<X> { - box B(v) as Box<X> // ok +fn h<'a, T, U>(v: Box<A<U>+'static>) -> Box<X+'static> { + box B(v) as Box<X> +} + +fn i<'a, T, U>(v: Box<A<U>>) -> Box<X+'static> { + box B(v) as Box<X> //~ ERROR cannot infer } fn main() {} diff --git a/src/test/compile-fail/proc-static-bound.rs b/src/test/compile-fail/regions-close-over-borrowed-ref-in-obj.rs index f11ddc0151f..037514f45c7 100644 --- a/src/test/compile-fail/proc-static-bound.rs +++ b/src/test/compile-fail/regions-close-over-borrowed-ref-in-obj.rs @@ -8,19 +8,14 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +trait Foo { } + +impl<'a> Foo for &'a int { } + fn main() { - let mut x = Some(1); - let mut p: proc(&mut Option<int>) = proc(_) {}; - match x { - Some(ref y) => { - p = proc(z: &mut Option<int>) { - *z = None; - let _ = y; - //~^ ERROR cannot capture variable of type `&int`, which does not fulfill `'static` - }; - } - None => {} + let blah; + { + let ss: &int = &1; //~ ERROR borrowed value does not live long enough + blah = box ss as Box<Foo>; } - p(&mut x); } - diff --git a/src/test/compile-fail/regions-close-over-type-parameter-1.rs b/src/test/compile-fail/regions-close-over-type-parameter-1.rs new file mode 100644 index 00000000000..5465f199f40 --- /dev/null +++ b/src/test/compile-fail/regions-close-over-type-parameter-1.rs @@ -0,0 +1,31 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test for what happens when a type parameter `A` is closed over into +// an object. This should yield errors unless `A` (and the object) +// both have suitable bounds. + +trait SomeTrait { fn get(&self) -> int; } + +fn make_object1<A:SomeTrait>(v: A) -> Box<SomeTrait+'static> { + box v as Box<SomeTrait+'static> + //~^ ERROR the parameter type `A` may not live long enough +} + +fn make_object2<'a,A:SomeTrait+'a>(v: A) -> Box<SomeTrait+'a> { + box v as Box<SomeTrait+'a> +} + +fn make_object3<'a,'b,A:SomeTrait+'a>(v: A) -> Box<SomeTrait+'b> { + box v as Box<SomeTrait+'b> + //~^ ERROR the parameter type `A` may not live long enough +} + +fn main() { } diff --git a/src/test/compile-fail/kindck-owned-trait-contains.rs b/src/test/compile-fail/regions-close-over-type-parameter-2.rs index 50704a1afbf..0ee349aaebf 100644 --- a/src/test/compile-fail/kindck-owned-trait-contains.rs +++ b/src/test/compile-fail/regions-close-over-type-parameter-2.rs @@ -1,4 +1,4 @@ -// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // @@ -8,25 +8,27 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// Test for what happens when a type parameter `A` is closed over into +// an object. This should yield errors unless `A` (and the object) +// both have suitable bounds. -trait Repeat<A> { fn get(&self) -> A; } +trait Foo { fn get(&self); } -impl<A:Clone> Repeat<A> for A { - fn get(&self) -> A { self.clone() } +impl<A> Foo for A { + fn get(&self) { } } -fn repeater<A:Clone>(v: A) -> Box<Repeat<A>> { - box v as Box<Repeat<A>> // No +fn repeater3<'a,A:'a>(v: A) -> Box<Foo+'a> { + box v as Box<Foo+'a> } fn main() { // Error results because the type of is inferred to be // ~Repeat<&'blk int> where blk is the lifetime of the block below. - let y = { + let _ = { let tmp0 = 3i; let tmp1 = &tmp0; //~ ERROR `tmp0` does not live long enough - repeater(tmp1) + repeater3(tmp1) }; - assert!(3 == *(y.get())); } diff --git a/src/test/compile-fail/regions-early-bound-error-method.rs b/src/test/compile-fail/regions-early-bound-error-method.rs index 9c8f8f8c30c..0cb88b924f8 100644 --- a/src/test/compile-fail/regions-early-bound-error-method.rs +++ b/src/test/compile-fail/regions-early-bound-error-method.rs @@ -27,7 +27,7 @@ impl<'a> GetRef<'a> for Box<'a> { impl<'a> Box<'a> { fn or<'b,G:GetRef<'b>>(&self, g2: G) -> &'a int { - g2.get() //~ ERROR lifetime mismatch + g2.get() //~ ERROR cannot infer an appropriate lifetime for automatic coercion due to } } diff --git a/src/test/compile-fail/regions-early-bound-error.rs b/src/test/compile-fail/regions-early-bound-error.rs index 9cff4849cbe..5da281d93dd 100644 --- a/src/test/compile-fail/regions-early-bound-error.rs +++ b/src/test/compile-fail/regions-early-bound-error.rs @@ -15,7 +15,7 @@ trait GetRef<'a, T> { fn get(&self) -> &'a T; } -struct Box<'a, T> { +struct Box<'a, T:'a> { t: &'a T } @@ -26,7 +26,7 @@ impl<'a,T:Clone> GetRef<'a,T> for Box<'a,T> { } fn get<'a,'b,G:GetRef<'a, int>>(g1: G, b: &'b int) -> &'b int { - g1.get() //~ ERROR lifetime mismatch + g1.get() //~ ERROR cannot infer an appropriate lifetime for automatic coercion due to } fn main() { diff --git a/src/test/compile-fail/regions-enum-not-wf.rs b/src/test/compile-fail/regions-enum-not-wf.rs new file mode 100644 index 00000000000..6395ee62f16 --- /dev/null +++ b/src/test/compile-fail/regions-enum-not-wf.rs @@ -0,0 +1,37 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Various examples of structs whose fields are not well-formed. + +#![no_std] +#![allow(dead_code)] + +enum Ref1<'a, T> { //~ ERROR the parameter type `T` may not live long enough + Ref1Variant1(&'a T) +} + +enum Ref2<'a, T> { //~ ERROR the parameter type `T` may not live long enough + Ref2Variant1, + Ref2Variant2(int, &'a T), +} + +enum RefOk<'a, T:'a> { + RefOkVariant1(&'a T) +} + +enum RefIndirect<'a, T> { //~ ERROR the parameter type `T` may not live long enough + RefIndirectVariant1(int, RefOk<'a,T>) +} + +enum RefDouble<'a, 'b, T> { //~ ERROR reference has a longer lifetime than the data + RefDoubleVariant1(&'a &'b T) +} + +fn main() { } diff --git a/src/test/compile-fail/regions-escape-bound-fn-2.rs b/src/test/compile-fail/regions-escape-bound-fn-2.rs index d2551ec4fed..d752bc97cac 100644 --- a/src/test/compile-fail/regions-escape-bound-fn-2.rs +++ b/src/test/compile-fail/regions-escape-bound-fn-2.rs @@ -16,5 +16,6 @@ fn with_int(f: |x: &int|) { fn main() { let mut x = None; //~^ ERROR lifetime of variable does not enclose its declaration + //~^^ ERROR type of expression contains references that are not valid during the expression with_int(|y| x = Some(y)); } diff --git a/src/test/compile-fail/regions-escape-via-trait-or-not.rs b/src/test/compile-fail/regions-escape-via-trait-or-not.rs index 980a4aed34f..adef1f901fd 100644 --- a/src/test/compile-fail/regions-escape-via-trait-or-not.rs +++ b/src/test/compile-fail/regions-escape-via-trait-or-not.rs @@ -8,17 +8,21 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -trait deref { +#![no_std] + +#![allow(dead_code)] + +trait Deref { fn get(self) -> int; } -impl<'a> deref for &'a int { +impl<'a> Deref for &'a int { fn get(self) -> int { *self } } -fn with<R:deref>(f: |x: &int| -> R) -> int { +fn with<R:Deref>(f: |x: &int| -> R) -> int { f(&3).get() } diff --git a/src/test/compile-fail/regions-free-region-ordering-callee.rs b/src/test/compile-fail/regions-free-region-ordering-callee.rs index 9762e5c4690..26cf3be429b 100644 --- a/src/test/compile-fail/regions-free-region-ordering-callee.rs +++ b/src/test/compile-fail/regions-free-region-ordering-callee.rs @@ -31,6 +31,12 @@ fn ordering3<'a, 'b>(x: &'a uint, y: &'b uint) -> &'a &'b uint { } fn ordering4<'a, 'b>(a: &'a uint, b: &'b uint, x: |&'a &'b uint|) { + // Do not infer ordering from closure argument types. + let z: Option<&'a &'b uint> = None; + //~^ ERROR reference has a longer lifetime than the data it references +} + +fn ordering5<'a, 'b>(a: &'a uint, b: &'b uint, x: Option<&'a &'b uint>) { let z: Option<&'a &'b uint> = None; } diff --git a/src/test/compile-fail/regions-free-region-ordering-caller.rs b/src/test/compile-fail/regions-free-region-ordering-caller.rs index 2f8caabb7f8..55c0cf3bb26 100644 --- a/src/test/compile-fail/regions-free-region-ordering-caller.rs +++ b/src/test/compile-fail/regions-free-region-ordering-caller.rs @@ -16,18 +16,18 @@ struct Paramd<'a> { x: &'a uint } fn call2<'a, 'b>(a: &'a uint, b: &'b uint) { let z: Option<&'b &'a uint> = None; - //~^ ERROR pointer has a longer lifetime than the data it references + //~^ ERROR reference has a longer lifetime than the data it references } fn call3<'a, 'b>(a: &'a uint, b: &'b uint) { let y: Paramd<'a> = Paramd { x: a }; let z: Option<&'b Paramd<'a>> = None; - //~^ ERROR pointer has a longer lifetime than the data it references + //~^ ERROR reference has a longer lifetime than the data it references } fn call4<'a, 'b>(a: &'a uint, b: &'b uint) { - let z: Option<|&'a &'b uint|> = None; - //~^ ERROR pointer has a longer lifetime than the data it references + let z: Option<&'a &'b uint> = None; + //~^ ERROR reference has a longer lifetime than the data it references } diff --git a/src/test/compile-fail/regions-free-region-ordering-incorrect.rs b/src/test/compile-fail/regions-free-region-ordering-incorrect.rs index 6f6b6761735..9cb61c24922 100644 --- a/src/test/compile-fail/regions-free-region-ordering-incorrect.rs +++ b/src/test/compile-fail/regions-free-region-ordering-incorrect.rs @@ -15,7 +15,7 @@ // // This test began its life as a test for issue #4325. -struct Node<'b, T> { +struct Node<'b, T:'b> { val: T, next: Option<&'b Node<'b, T>> } diff --git a/src/test/compile-fail/regions-freevar.rs b/src/test/compile-fail/regions-freevar.rs index cef3b525997..76bbe71cf75 100644 --- a/src/test/compile-fail/regions-freevar.rs +++ b/src/test/compile-fail/regions-freevar.rs @@ -12,7 +12,7 @@ fn wants_static_fn(_x: ||: 'static) {} fn main() { let i = 3i; - wants_static_fn(|| { //~ ERROR cannot infer - println!("i={}", i); + wants_static_fn(|| { + println!("i={}", i); //~ ERROR captured variable `i` does not outlive }) } diff --git a/src/test/compile-fail/regions-glb-free-free.rs b/src/test/compile-fail/regions-glb-free-free.rs index 1d8c4cec655..0eb47da16b6 100644 --- a/src/test/compile-fail/regions-glb-free-free.rs +++ b/src/test/compile-fail/regions-glb-free-free.rs @@ -22,9 +22,9 @@ mod argparse { impl<'a> Flag<'a> { pub fn set_desc(self, s: &str) -> Flag<'a> { - Flag { //~ ERROR cannot infer + Flag { name: self.name, - desc: s, + desc: s, //~ ERROR cannot infer an appropriate lifetime for automatic coercion due t max_count: self.max_count, value: self.value } diff --git a/src/test/compile-fail/regions-infer-bound-from-trait-self.rs b/src/test/compile-fail/regions-infer-bound-from-trait-self.rs new file mode 100644 index 00000000000..25fd20b6ec5 --- /dev/null +++ b/src/test/compile-fail/regions-infer-bound-from-trait-self.rs @@ -0,0 +1,61 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test that we can derive lifetime bounds on `Self` from trait +// inheritance. + +trait Static : 'static { } + +trait Is<'a> : 'a { } + +struct Inv<'a> { + x: Option<&'a mut &'a int> +} + +fn check_bound<'a,A:'a>(x: Inv<'a>, a: A) { } + +// In these case, `Self` inherits `'static`. + +trait InheritsFromStatic : 'static { + fn foo1<'a>(self, x: Inv<'a>) { + check_bound(x, self) + } +} +trait InheritsFromStaticIndirectly : Static { + fn foo1<'a>(self, x: Inv<'a>) { + check_bound(x, self) + } +} + + +// In these case, `Self` inherits `'a`. + +trait InheritsFromIs<'a> : 'a { + fn foo(self, x: Inv<'a>) { + check_bound(x, self) + } +} + +trait InheritsFromIsIndirectly<'a> : Is<'a> { + fn foo(self, x: Inv<'a>) { + check_bound(x, self) + } +} + +// In this case, `Self` inherits nothing. + +trait InheritsFromNothing<'a> { + fn foo(self, x: Inv<'a>) { + check_bound(x, self) + //~^ ERROR parameter type `Self` may not live long enough + } +} + +fn main() { } diff --git a/src/test/compile-fail/regions-infer-bound-from-trait.rs b/src/test/compile-fail/regions-infer-bound-from-trait.rs new file mode 100644 index 00000000000..d1111377f1e --- /dev/null +++ b/src/test/compile-fail/regions-infer-bound-from-trait.rs @@ -0,0 +1,50 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test that we can derive lifetime bounds on type parameters +// from trait inheritance. + +trait Static : 'static { } + +trait Is<'a> : 'a { } + +struct Inv<'a> { + x: Option<&'a mut &'a int> +} + +fn check_bound<'a,A:'a>(x: Inv<'a>, a: A) { } + +// In all of these cases, we can derive a bound for A that is longer +// than 'a based on the trait bound of A: + +fn foo1<'a,A:Static>(x: Inv<'a>, a: A) { + check_bound(x, a) +} + +fn foo2<'a,A:Static>(x: Inv<'static>, a: A) { + check_bound(x, a) +} + +fn foo3<'a,A:Is<'a>>(x: Inv<'a>, a: A) { + check_bound(x, a) +} + +// In these cases, there is no trait bound, so we cannot derive any +// bound for A and we get an error: + +fn bar1<'a,A>(x: Inv<'a>, a: A) { + check_bound(x, a) //~ ERROR parameter type `A` may not live long enough +} + +fn bar2<'a,'b,A:Is<'b>>(x: Inv<'a>, y: Inv<'b>, a: A) { + check_bound(x, a) //~ ERROR parameter type `A` may not live long enough +} + +fn main() { } diff --git a/src/test/compile-fail/regions-infer-paramd-indirect.rs b/src/test/compile-fail/regions-infer-paramd-indirect.rs index 87820e88475..519223e9753 100644 --- a/src/test/compile-fail/regions-infer-paramd-indirect.rs +++ b/src/test/compile-fail/regions-infer-paramd-indirect.rs @@ -32,7 +32,7 @@ impl<'a> set_f<'a> for c<'a> { } fn set_f_bad(&self, b: Gc<b>) { - self.f = b; //~ ERROR mismatched types: expected `Gc<Gc<&'a int>>` but found `Gc<Gc<&int>>` + self.f = b; //~ ERROR mismatched types: expected `Gc<Gc<&'a int>>`, found `Gc<Gc<&int>>` //~^ ERROR cannot infer } } diff --git a/src/test/compile-fail/regions-infer-paramd-method.rs b/src/test/compile-fail/regions-infer-paramd-method.rs index d969a7d2663..772ccadda52 100644 --- a/src/test/compile-fail/regions-infer-paramd-method.rs +++ b/src/test/compile-fail/regions-infer-paramd-method.rs @@ -40,7 +40,7 @@ trait set_foo_foo { impl<'a> set_foo_foo for with_foo<'a> { fn set_foo(&mut self, f: @foo) { - self.f = f; //~ ERROR mismatched types: expected `@foo/&self` but found `@foo/&` + self.f = f; //~ ERROR mismatched types: expected `@foo/&self`, found `@foo/&` } } diff --git a/src/test/compile-fail/regions-infer-region-in-fn-but-not-type.rs b/src/test/compile-fail/regions-infer-region-in-fn-but-not-type.rs index a743ff81b30..0fa4969b54c 100644 --- a/src/test/compile-fail/regions-infer-region-in-fn-but-not-type.rs +++ b/src/test/compile-fail/regions-infer-region-in-fn-but-not-type.rs @@ -11,7 +11,7 @@ // check that the &int here does not cause us to think that `foo` // contains region pointers -struct foo(proc(x: &int)); +struct foo(proc(x: &int):'static); fn take_foo(x: foo<'static>) {} //~ ERROR wrong number of lifetime parameters diff --git a/src/test/compile-fail/regions-lifetime-bounds-on-fns.rs b/src/test/compile-fail/regions-lifetime-bounds-on-fns.rs new file mode 100644 index 00000000000..a52d2f9f9a0 --- /dev/null +++ b/src/test/compile-fail/regions-lifetime-bounds-on-fns.rs @@ -0,0 +1,41 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![no_std] + +fn a<'a, 'b:'a>(x: &mut &'a int, y: &mut &'b int) { + // Note: this is legal because of the `'b:'a` declaration. + *x = *y; +} + +fn b<'a, 'b>(x: &mut &'a int, y: &mut &'b int) { + // Illegal now because there is no `'b:'a` declaration. + *x = *y; //~ ERROR mismatched types +} + +fn c<'a,'b>(x: &mut &'a int, y: &mut &'b int) { + // Here we try to call `foo` but do not know that `'a` and `'b` are + // related as required. + a(x, y); //~ ERROR cannot infer +} + +fn d() { + // 'a and 'b are early bound in the function `a` because they appear + // inconstraints: + let _: fn(&mut &int, &mut &int) = a; //~ ERROR mismatched types +} + +fn e() { + // 'a and 'b are late bound in the function `b` because there are + // no constraints: + let _: fn(&mut &int, &mut &int) = b; +} + +fn main() { } diff --git a/src/test/compile-fail/regions-nested-fns.rs b/src/test/compile-fail/regions-nested-fns.rs index c66e5616b84..7dc57d37e24 100644 --- a/src/test/compile-fail/regions-nested-fns.rs +++ b/src/test/compile-fail/regions-nested-fns.rs @@ -21,8 +21,7 @@ fn nested<'x>(x: &'x int) { }); ignore::< <'z>|&'z int| -> &'z int>(|z| { - if false { return x; } //~ ERROR mismatched types - //~^ ERROR cannot infer + if false { return x; } //~ ERROR cannot infer an appropriate lifetime for automatic if false { return ay; } return z; }); diff --git a/src/test/compile-fail/regions-proc-bound-capture.rs b/src/test/compile-fail/regions-proc-bound-capture.rs new file mode 100644 index 00000000000..e32ef275256 --- /dev/null +++ b/src/test/compile-fail/regions-proc-bound-capture.rs @@ -0,0 +1,26 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +fn borrowed_proc<'a>(x: &'a int) -> proc():'a -> int { + // This is legal, because the region bound on `proc` + // states that it captures `x`. + proc() { + *x + } +} + +fn static_proc<'a>(x: &'a int) -> proc():'static -> int { + // This is illegal, because the region bound on `proc` is 'static. + proc() { //~ ERROR captured variable `x` outlives the `proc()` + *x + } +} + +fn main() { } diff --git a/src/test/compile-fail/regions-bound-lists-feature-gate.rs b/src/test/compile-fail/regions-proc-bounds.rs index de3b2faef86..db71bc4e15c 100644 --- a/src/test/compile-fail/regions-bound-lists-feature-gate.rs +++ b/src/test/compile-fail/regions-proc-bounds.rs @@ -8,13 +8,13 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +fn is_static<T: 'static>() {} -trait Foo { } +fn foo<'a>() { + is_static::<proc():'a>(); + //~^ ERROR does not fulfill the required lifetime -fn foo<'a>(x: Box<Foo + 'a>) { //~ ERROR only the 'static lifetime is accepted here -} - -fn bar<'a, T:'a>() { //~ ERROR only the 'static lifetime is accepted here + is_static::<proc():'static>(); } fn main() { } diff --git a/src/test/compile-fail/regions-reborrow-from-shorter-mut-ref-mut-ref.rs b/src/test/compile-fail/regions-reborrow-from-shorter-mut-ref-mut-ref.rs index d3e0740a0fe..50ea8b1f2ed 100644 --- a/src/test/compile-fail/regions-reborrow-from-shorter-mut-ref-mut-ref.rs +++ b/src/test/compile-fail/regions-reborrow-from-shorter-mut-ref-mut-ref.rs @@ -11,7 +11,7 @@ // Issue #8624. Test for reborrowing with 3 levels, not just two. fn copy_borrowed_ptr<'a, 'b, 'c>(p: &'a mut &'b mut &'c mut int) -> &'b mut int { - &mut ***p //~ ERROR cannot infer an appropriate lifetime + &mut ***p //~ ERROR lifetime of `p` is too short to guarantee its contents } fn main() { diff --git a/src/test/compile-fail/regions-ret-borrowed-1.rs b/src/test/compile-fail/regions-ret-borrowed-1.rs index 068ecb7118f..aac81a2af6b 100644 --- a/src/test/compile-fail/regions-ret-borrowed-1.rs +++ b/src/test/compile-fail/regions-ret-borrowed-1.rs @@ -17,9 +17,10 @@ fn with<R>(f: <'a>|x: &'a int| -> R) -> R { } fn return_it<'a>() -> &'a int { - with(|o| o) //~ ERROR mismatched types - //~^ ERROR lifetime of return value does not outlive the function call - //~^^ ERROR cannot infer + with(|o| o) + //~^ ERROR cannot infer + //~^^ ERROR not valid during the expression + //~^^^ ERROR not valid at this point } fn main() { diff --git a/src/test/compile-fail/regions-ret-borrowed.rs b/src/test/compile-fail/regions-ret-borrowed.rs index f9983bcf801..dd9421ee2ef 100644 --- a/src/test/compile-fail/regions-ret-borrowed.rs +++ b/src/test/compile-fail/regions-ret-borrowed.rs @@ -20,9 +20,10 @@ fn with<R>(f: |x: &int| -> R) -> R { } fn return_it<'a>() -> &'a int { - with(|o| o) //~ ERROR mismatched types - //~^ ERROR lifetime of return value does not outlive the function call - //~^^ ERROR cannot infer + with(|o| o) + //~^ ERROR cannot infer + //~^^ ERROR not valid during the expression + //~^^^ ERROR not valid at this point } fn main() { diff --git a/src/test/compile-fail/regions-return-stack-allocated-vec.rs b/src/test/compile-fail/regions-return-stack-allocated-vec.rs index b5f4fcadf89..3c5423c44d0 100644 --- a/src/test/compile-fail/regions-return-stack-allocated-vec.rs +++ b/src/test/compile-fail/regions-return-stack-allocated-vec.rs @@ -11,7 +11,7 @@ // Test that we cannot return a stack allocated slice fn function(x: int) -> &'static [int] { - &[x] //~ ERROR mismatched types + &[x] //~ ERROR borrowed value does not live long enough } fn main() { diff --git a/src/test/compile-fail/regions-struct-not-wf.rs b/src/test/compile-fail/regions-struct-not-wf.rs new file mode 100644 index 00000000000..3de137a9efb --- /dev/null +++ b/src/test/compile-fail/regions-struct-not-wf.rs @@ -0,0 +1,32 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Various examples of structs whose fields are not well-formed. + +#![no_std] +#![allow(dead_code)] + +struct Ref<'a, T> { //~ ERROR the parameter type `T` may not live long enough + field: &'a T +} + +struct RefOk<'a, T:'a> { + field: &'a T +} + +struct RefIndirect<'a, T> { //~ ERROR the parameter type `T` may not live long enough + field: RefOk<'a, T> +} + +struct DoubleRef<'a, 'b, T> { //~ ERROR reference has a longer lifetime than the data it references + field: &'a &'b T +} + +fn main() { } diff --git a/src/test/compile-fail/regions-trait-3.rs b/src/test/compile-fail/regions-trait-3.rs index 9222fde7789..78d84fb7c75 100644 --- a/src/test/compile-fail/regions-trait-3.rs +++ b/src/test/compile-fail/regions-trait-3.rs @@ -26,7 +26,7 @@ trait get_ctxt<'a> { } fn make_gc1(gc: @get_ctxt<'a>) -> @get_ctxt<'b> { - return gc; //~ ERROR mismatched types: expected `@get_ctxt/&b` but found `@get_ctxt/&a` + return gc; //~ ERROR mismatched types: expected `@get_ctxt/&b`, found `@get_ctxt/&a` } struct Foo { diff --git a/src/test/compile-fail/regions-trait-variance.rs b/src/test/compile-fail/regions-trait-variance.rs index 53cfd4e0324..3ceb4e3fef6 100644 --- a/src/test/compile-fail/regions-trait-variance.rs +++ b/src/test/compile-fail/regions-trait-variance.rs @@ -31,7 +31,7 @@ impl Drop for B { } struct A<'r> { - p: &'r X + p: &'r X+'r } fn make_a(p:&X) -> A { diff --git a/src/test/compile-fail/regions-variance-covariant-use-contravariant.rs b/src/test/compile-fail/regions-variance-covariant-use-contravariant.rs index 5cc3f1bdc37..7dcdc9875e3 100644 --- a/src/test/compile-fail/regions-variance-covariant-use-contravariant.rs +++ b/src/test/compile-fail/regions-variance-covariant-use-contravariant.rs @@ -31,7 +31,7 @@ fn use_<'short,'long>(c: Covariant<'long>, // contravariant with respect to its parameter 'a. let _: Covariant<'short> = c; //~ ERROR mismatched types - //~^ ERROR cannot infer an appropriate lifetime + //~^ ERROR cannot infer an appropriate lifetime } fn main() {} diff --git a/src/test/compile-fail/regions-variance-invariant-use-contravariant.rs b/src/test/compile-fail/regions-variance-invariant-use-contravariant.rs index 0790c3f956a..d09e6babe09 100644 --- a/src/test/compile-fail/regions-variance-invariant-use-contravariant.rs +++ b/src/test/compile-fail/regions-variance-invariant-use-contravariant.rs @@ -15,7 +15,7 @@ // variance inference works in the first place. struct Invariant<'a> { - f: &'static mut &'a int + f: &'a mut &'a int } fn use_<'short,'long>(c: Invariant<'long>, diff --git a/src/test/compile-fail/regions-variance-invariant-use-covariant.rs b/src/test/compile-fail/regions-variance-invariant-use-covariant.rs index 9cdd05f8ebe..861668ad50d 100644 --- a/src/test/compile-fail/regions-variance-invariant-use-covariant.rs +++ b/src/test/compile-fail/regions-variance-invariant-use-covariant.rs @@ -15,7 +15,7 @@ // variance inference works in the first place. struct Invariant<'a> { - f: &'static mut &'a int + f: &'a mut &'a int } fn use_<'b>(c: Invariant<'b>) { diff --git a/src/test/compile-fail/removed-syntax-closure-lifetime.rs b/src/test/compile-fail/removed-syntax-closure-lifetime.rs index c5c8aa043f3..a726e30b1de 100644 --- a/src/test/compile-fail/removed-syntax-closure-lifetime.rs +++ b/src/test/compile-fail/removed-syntax-closure-lifetime.rs @@ -8,4 +8,4 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -type closure = Box<lt/fn()>; //~ ERROR expected `,` but found `/` +type closure = Box<lt/fn()>; //~ ERROR expected `,`, found `/` diff --git a/src/test/compile-fail/removed-syntax-const-item.rs b/src/test/compile-fail/removed-syntax-const-item.rs index a87f6218941..841c1ec59fd 100644 --- a/src/test/compile-fail/removed-syntax-const-item.rs +++ b/src/test/compile-fail/removed-syntax-const-item.rs @@ -8,4 +8,4 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -const i: int = 42; //~ ERROR expected item but found `const` +const i: int = 42; //~ ERROR expected item, found `const` diff --git a/src/test/compile-fail/removed-syntax-enum-newtype.rs b/src/test/compile-fail/removed-syntax-enum-newtype.rs index e40ed28e93f..b9c9c5f0a53 100644 --- a/src/test/compile-fail/removed-syntax-enum-newtype.rs +++ b/src/test/compile-fail/removed-syntax-enum-newtype.rs @@ -8,4 +8,4 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -enum e = int; //~ ERROR expected `{` but found `=` +enum e = int; //~ ERROR expected `{`, found `=` diff --git a/src/test/compile-fail/removed-syntax-field-let.rs b/src/test/compile-fail/removed-syntax-field-let.rs index 48b0f008d22..2b76db4f160 100644 --- a/src/test/compile-fail/removed-syntax-field-let.rs +++ b/src/test/compile-fail/removed-syntax-field-let.rs @@ -11,5 +11,5 @@ struct s { let foo: (), //~^ ERROR found `let` in ident position - //~^^ ERROR expected `:` but found `foo` + //~^^ ERROR expected `:`, found `foo` } diff --git a/src/test/compile-fail/removed-syntax-field-semicolon.rs b/src/test/compile-fail/removed-syntax-field-semicolon.rs index 3efcf996f93..9bb3c649cdf 100644 --- a/src/test/compile-fail/removed-syntax-field-semicolon.rs +++ b/src/test/compile-fail/removed-syntax-field-semicolon.rs @@ -10,5 +10,5 @@ struct s { bar: (); - //~^ ERROR expected `,`, or `}` but found `;` + //~^ ERROR expected `,`, or `}`, found `;` } diff --git a/src/test/compile-fail/removed-syntax-fixed-vec.rs b/src/test/compile-fail/removed-syntax-fixed-vec.rs index b85880f1981..917b4e03ad0 100644 --- a/src/test/compile-fail/removed-syntax-fixed-vec.rs +++ b/src/test/compile-fail/removed-syntax-fixed-vec.rs @@ -8,4 +8,4 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -type v = [int * 3]; //~ ERROR expected `]` but found `*` +type v = [int * 3]; //~ ERROR expected `]`, found `*` diff --git a/src/test/compile-fail/removed-syntax-fn-pure.rs b/src/test/compile-fail/removed-syntax-fn-pure.rs index 865b5f7ad8b..d569ea25c46 100644 --- a/src/test/compile-fail/removed-syntax-fn-pure.rs +++ b/src/test/compile-fail/removed-syntax-fn-pure.rs @@ -8,4 +8,4 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -pure fn f() {} //~ ERROR expected item but found `pure` +pure fn f() {} //~ ERROR expected item, found `pure` diff --git a/src/test/compile-fail/removed-syntax-fn-sigil.rs b/src/test/compile-fail/removed-syntax-fn-sigil.rs index 4f21769fe25..83ebe7cc7a3 100644 --- a/src/test/compile-fail/removed-syntax-fn-sigil.rs +++ b/src/test/compile-fail/removed-syntax-fn-sigil.rs @@ -9,5 +9,5 @@ // except according to those terms. fn f() { - let x: fn~() = || (); //~ ERROR expected `(` but found `~` + let x: fn~() = || (); //~ ERROR expected `(`, found `~` } diff --git a/src/test/compile-fail/removed-syntax-larrow-init.rs b/src/test/compile-fail/removed-syntax-larrow-init.rs index 4f615af7c1c..b2e856750df 100644 --- a/src/test/compile-fail/removed-syntax-larrow-init.rs +++ b/src/test/compile-fail/removed-syntax-larrow-init.rs @@ -11,5 +11,5 @@ fn removed_moves() { let mut x = 0; let y <- x; - //~^ ERROR expected `;` but found `<-` + //~^ ERROR expected `;`, found `<-` } diff --git a/src/test/compile-fail/removed-syntax-larrow-move.rs b/src/test/compile-fail/removed-syntax-larrow-move.rs index af49f204e97..e39fbe0f950 100644 --- a/src/test/compile-fail/removed-syntax-larrow-move.rs +++ b/src/test/compile-fail/removed-syntax-larrow-move.rs @@ -12,5 +12,5 @@ fn removed_moves() { let mut x = 0; let y = 0; y <- x; - //~^ ERROR expected one of `;`, `}` but found `<-` + //~^ ERROR expected one of `;`, `}`, found `<-` } diff --git a/src/test/compile-fail/removed-syntax-mut-vec-expr.rs b/src/test/compile-fail/removed-syntax-mut-vec-expr.rs index 171cf03f711..9f0cc0107c1 100644 --- a/src/test/compile-fail/removed-syntax-mut-vec-expr.rs +++ b/src/test/compile-fail/removed-syntax-mut-vec-expr.rs @@ -11,5 +11,5 @@ fn f() { let v = [mut 1, 2, 3, 4]; //~^ ERROR found `mut` in ident position - //~^^ ERROR expected `]` but found `1` + //~^^ ERROR expected `]`, found `1` } diff --git a/src/test/compile-fail/removed-syntax-mut-vec-ty.rs b/src/test/compile-fail/removed-syntax-mut-vec-ty.rs index 4c0be562f92..912952892e4 100644 --- a/src/test/compile-fail/removed-syntax-mut-vec-ty.rs +++ b/src/test/compile-fail/removed-syntax-mut-vec-ty.rs @@ -10,4 +10,4 @@ type v = [mut int]; //~^ ERROR found `mut` in ident position - //~^^ ERROR expected `]` but found `int` + //~^^ ERROR expected `]`, found `int` diff --git a/src/test/compile-fail/removed-syntax-ptr-lifetime.rs b/src/test/compile-fail/removed-syntax-ptr-lifetime.rs index 5b969da4fd2..0468ddd389a 100644 --- a/src/test/compile-fail/removed-syntax-ptr-lifetime.rs +++ b/src/test/compile-fail/removed-syntax-ptr-lifetime.rs @@ -8,4 +8,4 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -type bptr = &lifetime/int; //~ ERROR expected `;` but found `/` +type bptr = &lifetime/int; //~ ERROR expected `;`, found `/` diff --git a/src/test/compile-fail/removed-syntax-uniq-mut-expr.rs b/src/test/compile-fail/removed-syntax-uniq-mut-expr.rs index 5603cd21f3b..f4fc5b696fa 100644 --- a/src/test/compile-fail/removed-syntax-uniq-mut-expr.rs +++ b/src/test/compile-fail/removed-syntax-uniq-mut-expr.rs @@ -11,5 +11,5 @@ fn f() { let a_box = box mut 42; //~^ ERROR found `mut` in ident position - //~^^ ERROR expected `;` but found `42` + //~^^ ERROR expected `;`, found `42` } diff --git a/src/test/compile-fail/removed-syntax-uniq-mut-ty.rs b/src/test/compile-fail/removed-syntax-uniq-mut-ty.rs index 128dbbd9cab..a3fc27d8cf2 100644 --- a/src/test/compile-fail/removed-syntax-uniq-mut-ty.rs +++ b/src/test/compile-fail/removed-syntax-uniq-mut-ty.rs @@ -10,4 +10,4 @@ type mut_box = Box<mut int>; //~^ ERROR found `mut` in ident position - //~^^ ERROR expected `,` but found `int` + //~^^ ERROR expected `,`, found `int` diff --git a/src/test/compile-fail/removed-syntax-with-1.rs b/src/test/compile-fail/removed-syntax-with-1.rs index a0a332af6f6..fd8cdb7b10e 100644 --- a/src/test/compile-fail/removed-syntax-with-1.rs +++ b/src/test/compile-fail/removed-syntax-with-1.rs @@ -16,5 +16,5 @@ fn removed_with() { let a = S { foo: (), bar: () }; let b = S { foo: () with a }; - //~^ ERROR expected one of `,`, `}` but found `with` + //~^ ERROR expected one of `,`, `}`, found `with` } diff --git a/src/test/compile-fail/removed-syntax-with-2.rs b/src/test/compile-fail/removed-syntax-with-2.rs index 3068c5804c8..83c6897dee3 100644 --- a/src/test/compile-fail/removed-syntax-with-2.rs +++ b/src/test/compile-fail/removed-syntax-with-2.rs @@ -16,5 +16,5 @@ fn removed_with() { let a = S { foo: (), bar: () }; let b = S { foo: (), with a }; - //~^ ERROR expected `:` but found `a` + //~^ ERROR expected `:`, found `a` } diff --git a/src/test/compile-fail/repeat_count.rs b/src/test/compile-fail/repeat_count.rs index e19c1921a90..8a28819a736 100644 --- a/src/test/compile-fail/repeat_count.rs +++ b/src/test/compile-fail/repeat_count.rs @@ -12,17 +12,17 @@ fn main() { let n = 1; - let a = [0, ..n]; //~ ERROR expected constant integer for repeat count but found variable - let b = [0, ..()]; //~ ERROR expected positive integer for repeat count but found () - //~^ ERROR: expected `uint` but found `()` - let c = [0, ..true]; //~ ERROR expected positive integer for repeat count but found boolean - //~^ ERROR: expected `uint` but found `bool` - let d = [0, ..0.5]; //~ ERROR expected positive integer for repeat count but found float - //~^ ERROR: expected `uint` but found `<generic float #0>` - let e = [0, .."foo"]; //~ ERROR expected positive integer for repeat count but found string - //~^ ERROR: expected `uint` but found `&'static str` + let a = [0, ..n]; //~ ERROR expected constant integer for repeat count, found variable + let b = [0, ..()]; //~ ERROR expected positive integer for repeat count, found () + //~^ ERROR: expected `uint`, found `()` + let c = [0, ..true]; //~ ERROR expected positive integer for repeat count, found boolean + //~^ ERROR: expected `uint`, found `bool` + let d = [0, ..0.5]; //~ ERROR expected positive integer for repeat count, found float + //~^ ERROR: expected `uint`, found `<generic float #0>` + let e = [0, .."foo"]; //~ ERROR expected positive integer for repeat count, found string + //~^ ERROR: expected `uint`, found `&'static str` let f = [0, ..-4]; - //~^ ERROR expected positive integer for repeat count but found negative integer + //~^ ERROR expected positive integer for repeat count, found negative integer let f = [0u, ..-1]; - //~^ ERROR expected positive integer for repeat count but found negative integer + //~^ ERROR expected positive integer for repeat count, found negative integer } diff --git a/src/test/compile-fail/selftype-traittype.rs b/src/test/compile-fail/selftype-traittype.rs index 3bf547e3aff..bf7c3b261fd 100644 --- a/src/test/compile-fail/selftype-traittype.rs +++ b/src/test/compile-fail/selftype-traittype.rs @@ -13,7 +13,7 @@ trait add { fn plus(&self, x: Self) -> Self; } -fn do_add(x: Box<add>, y: Box<add>) -> Box<add> { +fn do_add(x: Box<add+'static>, y: Box<add+'static>) -> Box<add+'static> { x.plus(y) //~ ERROR cannot call a method whose type contains a self-type through an object } diff --git a/src/test/compile-fail/slightly-nice-generic-literal-messages.rs b/src/test/compile-fail/slightly-nice-generic-literal-messages.rs index 6e5dc7cc102..aebe78b18e0 100644 --- a/src/test/compile-fail/slightly-nice-generic-literal-messages.rs +++ b/src/test/compile-fail/slightly-nice-generic-literal-messages.rs @@ -13,7 +13,7 @@ struct Foo<T,U>(T); fn main() { match Foo(1.1) { 1 => {} - //~^ ERROR expected `Foo<<generic float #0>,<generic #2>>` but found `<generic integer #0>` + //~^ ERROR expected `Foo<<generic float #0>,<generic #2>>`, found `<generic integer #0>` } } diff --git a/src/test/compile-fail/static-region-bound.rs b/src/test/compile-fail/static-region-bound.rs index c36610fc79e..2cb1f462e1d 100644 --- a/src/test/compile-fail/static-region-bound.rs +++ b/src/test/compile-fail/static-region-bound.rs @@ -16,6 +16,6 @@ fn f<T:'static>(_: T) {} fn main() { let x = box(GC) 3i; f(x); - let x = &3i; - f(x); //~ ERROR instantiating a type parameter with an incompatible type + let x = &3i; //~ ERROR borrowed value does not live long enough + f(x); } diff --git a/src/test/compile-fail/struct-base-wrong-type.rs b/src/test/compile-fail/struct-base-wrong-type.rs index adda356298d..af6fc645351 100644 --- a/src/test/compile-fail/struct-base-wrong-type.rs +++ b/src/test/compile-fail/struct-base-wrong-type.rs @@ -12,11 +12,11 @@ struct Foo { a: int, b: int } struct Bar { x: int } static bar: Bar = Bar { x: 5 }; -static foo: Foo = Foo { a: 2, ..bar }; //~ ERROR mismatched types: expected `Foo` but found `Bar` +static foo: Foo = Foo { a: 2, ..bar }; //~ ERROR mismatched types: expected `Foo`, found `Bar` static foo_i: Foo = Foo { a: 2, ..4 }; //~ ERROR mismatched types: expected `Foo` fn main() { let b = Bar { x: 5 }; - let f = Foo { a: 2, ..b }; //~ ERROR mismatched types: expected `Foo` but found `Bar` + let f = Foo { a: 2, ..b }; //~ ERROR mismatched types: expected `Foo`, found `Bar` let f_i = Foo { a: 2, ..4 }; //~ ERROR mismatched types: expected `Foo` } diff --git a/src/test/compile-fail/struct-field-privacy.rs b/src/test/compile-fail/struct-field-privacy.rs index 82ba9a02a30..ae421815e4d 100644 --- a/src/test/compile-fail/struct-field-privacy.rs +++ b/src/test/compile-fail/struct-field-privacy.rs @@ -10,7 +10,7 @@ // aux-build:struct-field-privacy.rs -extern crate xc = "struct-field-privacy"; +extern crate "struct-field-privacy" as xc; struct A { a: int, diff --git a/src/test/compile-fail/structure-constructor-type-mismatch.rs b/src/test/compile-fail/structure-constructor-type-mismatch.rs index 2c8bbee783b..a0a481401bd 100644 --- a/src/test/compile-fail/structure-constructor-type-mismatch.rs +++ b/src/test/compile-fail/structure-constructor-type-mismatch.rs @@ -24,25 +24,25 @@ type PairF<U> = Pair<f32,U>; fn main() { let pt = PointF { - //~^ ERROR expected f32 but found int + //~^ ERROR expected f32, found int x: 1i, y: 2i, }; let pt2 = Point::<f32> { - //~^ ERROR expected f32 but found int + //~^ ERROR expected f32, found int x: 3i, y: 4i, }; let pair = PairF { - //~^ ERROR expected f32 but found int + //~^ ERROR expected f32, found int x: 5i, y: 6i, }; let pair2 = PairF::<int> { - //~^ ERROR expected f32 but found int + //~^ ERROR expected f32, found int x: 7i, y: 8i, }; diff --git a/src/test/compile-fail/suppressed-error.rs b/src/test/compile-fail/suppressed-error.rs index e40bca58bad..f13aabe5259 100644 --- a/src/test/compile-fail/suppressed-error.rs +++ b/src/test/compile-fail/suppressed-error.rs @@ -9,6 +9,6 @@ // except according to those terms. fn main() { - let (x, y) = (); //~ ERROR expected `()` but found tuple (types differ) + let (x, y) = (); //~ ERROR expected `()`, found tuple (types differ) return x; } diff --git a/src/test/compile-fail/tag-that-dare-not-speak-its-name.rs b/src/test/compile-fail/tag-that-dare-not-speak-its-name.rs index 72b4153be2c..9615e32bb1a 100644 --- a/src/test/compile-fail/tag-that-dare-not-speak-its-name.rs +++ b/src/test/compile-fail/tag-that-dare-not-speak-its-name.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// error-pattern:mismatched types: expected `char` but found +// error-pattern:mismatched types: expected `char`, found // Issue #876 #![no_implicit_prelude] diff --git a/src/test/compile-fail/terr-in-field.rs b/src/test/compile-fail/terr-in-field.rs index 6474a58c1cd..88da7bc8542 100644 --- a/src/test/compile-fail/terr-in-field.rs +++ b/src/test/compile-fail/terr-in-field.rs @@ -20,7 +20,7 @@ struct bar { fn want_foo(f: foo) {} fn have_bar(b: bar) { - want_foo(b); //~ ERROR (expected struct foo but found struct bar) + want_foo(b); //~ ERROR (expected struct foo, found struct bar) } fn main() {} diff --git a/src/test/compile-fail/terr-sorts.rs b/src/test/compile-fail/terr-sorts.rs index c8059abfd74..0817d7c610a 100644 --- a/src/test/compile-fail/terr-sorts.rs +++ b/src/test/compile-fail/terr-sorts.rs @@ -20,7 +20,7 @@ type bar = Gc<foo>; fn want_foo(f: foo) {} fn have_bar(b: bar) { - want_foo(b); //~ ERROR (expected struct foo but found Gc-ptr) + want_foo(b); //~ ERROR (expected struct foo, found Gc-ptr) } fn main() {} diff --git a/src/test/compile-fail/trailing-plus-in-bounds.rs b/src/test/compile-fail/trailing-plus-in-bounds.rs index e8f9ed4d2cf..b189acb685a 100644 --- a/src/test/compile-fail/trailing-plus-in-bounds.rs +++ b/src/test/compile-fail/trailing-plus-in-bounds.rs @@ -11,7 +11,7 @@ use std::fmt::Show; fn main() { - let x: Box<Show+> = box 3 as Box<Show+>; + let x: Box<Show+> = box 3i as Box<Show+>; //~^ ERROR at least one type parameter bound must be specified //~^^ ERROR at least one type parameter bound must be specified } diff --git a/src/test/compile-fail/trait-bounds-not-on-bare-trait.rs b/src/test/compile-fail/trait-bounds-not-on-bare-trait.rs index f0f388a5a07..78b4ab817bf 100644 --- a/src/test/compile-fail/trait-bounds-not-on-bare-trait.rs +++ b/src/test/compile-fail/trait-bounds-not-on-bare-trait.rs @@ -14,7 +14,7 @@ trait Foo { // This should emit the less confusing error, not the more confusing one. fn foo(_x: Foo + Send) { - //~^ERROR reference to trait `Foo` where a type is expected; try `Box<Foo>` or `&Foo` + //~^ERROR variable `_x` has dynamically sized type `Foo+Send` } fn main() { } diff --git a/src/test/compile-fail/trait-bounds-sugar.rs b/src/test/compile-fail/trait-bounds-sugar.rs index c3d608b48f3..7ed8db4fcd2 100644 --- a/src/test/compile-fail/trait-bounds-sugar.rs +++ b/src/test/compile-fail/trait-bounds-sugar.rs @@ -16,15 +16,16 @@ trait Foo {} fn a(_x: Box<Foo+Send>) { } -fn b(_x: &'static Foo) { // should be same as &'static Foo+'static +fn b(_x: &'static Foo+'static) { } fn c(x: Box<Foo+Sync>) { - a(x); //~ ERROR expected bounds `Send` + a(x); //~ ERROR mismatched types } fn d(x: &'static Foo+Sync) { - b(x); //~ ERROR expected bounds `'static` + b(x); //~ ERROR cannot infer + //~^ ERROR mismatched types } fn main() {} diff --git a/src/test/compile-fail/trait-coercion-generic-bad.rs b/src/test/compile-fail/trait-coercion-generic-bad.rs index 7c4b633fa9e..55e5a02cfae 100644 --- a/src/test/compile-fail/trait-coercion-generic-bad.rs +++ b/src/test/compile-fail/trait-coercion-generic-bad.rs @@ -25,7 +25,7 @@ impl Trait<&'static str> for Struct { fn main() { let s: Box<Trait<int>> = box Struct { person: "Fred" }; - //~^ ERROR expected Trait<int>, but found Trait<&'static str> - //~^^ ERROR expected Trait<int>, but found Trait<&'static str> + //~^ ERROR expected Trait<int>, found Trait<&'static str> + //~^^ ERROR expected Trait<int>, found Trait<&'static str> s.f(1); } diff --git a/src/test/compile-fail/trait-impl-method-mismatch.rs b/src/test/compile-fail/trait-impl-method-mismatch.rs index 216da08a80f..bd844cc5860 100644 --- a/src/test/compile-fail/trait-impl-method-mismatch.rs +++ b/src/test/compile-fail/trait-impl-method-mismatch.rs @@ -18,7 +18,7 @@ trait Mumbo { impl Mumbo for uint { // Cannot have a larger effect than the trait: unsafe fn jumbo(&self, x: Gc<uint>) { *self + *x; } - //~^ ERROR expected normal fn but found unsafe fn + //~^ ERROR expected normal fn, found unsafe fn } fn main() {} diff --git a/src/test/compile-fail/tuple-arity-mismatch.rs b/src/test/compile-fail/tuple-arity-mismatch.rs index 677c4943043..1500a3cbe52 100644 --- a/src/test/compile-fail/tuple-arity-mismatch.rs +++ b/src/test/compile-fail/tuple-arity-mismatch.rs @@ -13,6 +13,6 @@ fn first((value, _): (int, f64)) -> int { value } fn main() { - let y = first ((1,2,3)); - //~^ ERROR expected a tuple with 2 elements but found one with 3 elements + let y = first ((1,2.0,3)); + //~^ ERROR expected a tuple with 2 elements, found one with 3 elements } diff --git a/src/test/compile-fail/tutorial-suffix-inference-test.rs b/src/test/compile-fail/tutorial-suffix-inference-test.rs index d92aa8d640a..c1be54b3f75 100644 --- a/src/test/compile-fail/tutorial-suffix-inference-test.rs +++ b/src/test/compile-fail/tutorial-suffix-inference-test.rs @@ -17,9 +17,9 @@ fn main() { identity_u8(x); // after this, `x` is assumed to have type `u8` identity_u16(x); - //~^ ERROR mismatched types: expected `u16` but found `u8` + //~^ ERROR mismatched types: expected `u16`, found `u8` identity_u16(y); - //~^ ERROR mismatched types: expected `u16` but found `i32` + //~^ ERROR mismatched types: expected `u16`, found `i32` let a = 3i; @@ -27,6 +27,6 @@ fn main() { identity_i(a); // ok identity_u16(a); - //~^ ERROR mismatched types: expected `u16` but found `int` + //~^ ERROR mismatched types: expected `u16`, found `int` } diff --git a/src/test/compile-fail/type-parameter-names.rs b/src/test/compile-fail/type-parameter-names.rs index da8478de985..e1a29afa737 100644 --- a/src/test/compile-fail/type-parameter-names.rs +++ b/src/test/compile-fail/type-parameter-names.rs @@ -11,6 +11,6 @@ // Test that we print out the names of type parameters correctly in // our error messages. -fn foo<Foo, Bar>(x: Foo) -> Bar { x } //~ ERROR expected `Bar` but found `Foo` +fn foo<Foo, Bar>(x: Foo) -> Bar { x } //~ ERROR expected `Bar`, found `Foo` fn main() {} diff --git a/src/test/compile-fail/type-params-in-different-spaces-2.rs b/src/test/compile-fail/type-params-in-different-spaces-2.rs index 955efeef30d..4143d9d9b84 100644 --- a/src/test/compile-fail/type-params-in-different-spaces-2.rs +++ b/src/test/compile-fail/type-params-in-different-spaces-2.rs @@ -15,12 +15,12 @@ trait Tr<T> { // these compile as if Self: Tr<U>, even tho only Self: Tr<Self or T> trait A: Tr<Self> { fn test<U>(u: U) -> Self { - Tr::op(u) //~ ERROR expected Tr<U>, but found Tr<Self> + Tr::op(u) //~ ERROR expected Tr<U>, found Tr<Self> } } trait B<T>: Tr<T> { fn test<U>(u: U) -> Self { - Tr::op(u) //~ ERROR expected Tr<U>, but found Tr<T> + Tr::op(u) //~ ERROR expected Tr<U>, found Tr<T> } } diff --git a/src/test/compile-fail/typeck_type_placeholder_lifetime_1.rs b/src/test/compile-fail/typeck_type_placeholder_lifetime_1.rs index fae4f541b17..9694c1d9f98 100644 --- a/src/test/compile-fail/typeck_type_placeholder_lifetime_1.rs +++ b/src/test/compile-fail/typeck_type_placeholder_lifetime_1.rs @@ -11,11 +11,11 @@ // This test checks that the `_` type placeholder does not react // badly if put as a lifetime parameter. -struct Foo<'a, T> { +struct Foo<'a, T:'a> { r: &'a T } pub fn main() { let c: Foo<_, _> = Foo { r: &5u }; - //~^ ERROR wrong number of type arguments: expected 1 but found 2 + //~^ ERROR wrong number of type arguments: expected 1, found 2 } diff --git a/src/test/compile-fail/typeck_type_placeholder_lifetime_2.rs b/src/test/compile-fail/typeck_type_placeholder_lifetime_2.rs index e879ddb951f..365b786cc1a 100644 --- a/src/test/compile-fail/typeck_type_placeholder_lifetime_2.rs +++ b/src/test/compile-fail/typeck_type_placeholder_lifetime_2.rs @@ -11,11 +11,11 @@ // This test checks that the `_` type placeholder does not react // badly if put as a lifetime parameter. -struct Foo<'a, T> { +struct Foo<'a, T:'a> { r: &'a T } pub fn main() { let c: Foo<_, uint> = Foo { r: &5 }; - //~^ ERROR wrong number of type arguments: expected 1 but found 2 + //~^ ERROR wrong number of type arguments: expected 1, found 2 } diff --git a/src/test/compile-fail/typeck_type_placeholder_mismatch.rs b/src/test/compile-fail/typeck_type_placeholder_mismatch.rs index f7e5964fa24..29d32b10539 100644 --- a/src/test/compile-fail/typeck_type_placeholder_mismatch.rs +++ b/src/test/compile-fail/typeck_type_placeholder_mismatch.rs @@ -19,11 +19,11 @@ pub fn main() { fn test1() { let x: Foo<_> = Bar::<uint>; - //~^ ERROR mismatched types: expected `Foo<<generic #0>>` but found `Bar<uint>` + //~^ ERROR mismatched types: expected `Foo<<generic #0>>`, found `Bar<uint>` let y: Foo<uint> = x; } fn test2() { let x: Foo<_> = Bar::<uint>; - //~^ ERROR mismatched types: expected `Foo<<generic #0>>` but found `Bar<uint>` + //~^ ERROR mismatched types: expected `Foo<<generic #0>>`, found `Bar<uint>` } diff --git a/src/test/compile-fail/unboxed-closures-static-call-wrong-trait.rs b/src/test/compile-fail/unboxed-closures-static-call-wrong-trait.rs new file mode 100644 index 00000000000..871889f26df --- /dev/null +++ b/src/test/compile-fail/unboxed-closures-static-call-wrong-trait.rs @@ -0,0 +1,17 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(unboxed_closures)] + +fn main() { + let mut_ = |&mut: x| x; + mut_.call_once((0i, )); //~ ERROR type `closure` does not implement +} + diff --git a/src/test/compile-fail/unconstrained-ref.rs b/src/test/compile-fail/unconstrained-ref.rs index a4125f94cd2..87647cdb546 100644 --- a/src/test/compile-fail/unconstrained-ref.rs +++ b/src/test/compile-fail/unconstrained-ref.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -struct S<'a, T> { +struct S<'a, T:'a> { o: &'a Option<T> } diff --git a/src/test/compile-fail/unreachable-variant.rs b/src/test/compile-fail/unreachable-variant.rs index 566b888099c..a6f17efe6b5 100644 --- a/src/test/compile-fail/unreachable-variant.rs +++ b/src/test/compile-fail/unreachable-variant.rs @@ -10,7 +10,7 @@ // aux-build:unreachable-variant.rs -extern crate other = "unreachable-variant"; +extern crate "unreachable-variant" as other; fn main() { let _x = other::super_sekrit::baz; //~ ERROR is private diff --git a/src/test/compile-fail/unsized.rs b/src/test/compile-fail/unsized.rs index d5c2bbb21ca..43db4dfd395 100644 --- a/src/test/compile-fail/unsized.rs +++ b/src/test/compile-fail/unsized.rs @@ -10,7 +10,7 @@ // Test syntax checks for `type` keyword. -struct S1 for type; //~ ERROR expected `{`, `(`, or `;` after struct name but found `for` +struct S1 for type; //~ ERROR expected `{`, `(`, or `;` after struct name, found `for` pub fn main() { } diff --git a/src/test/compile-fail/unsized4.rs b/src/test/compile-fail/unsized4.rs index e377c9d5f41..a66c1d85009 100644 --- a/src/test/compile-fail/unsized4.rs +++ b/src/test/compile-fail/unsized4.rs @@ -12,7 +12,7 @@ trait T {} fn f<Sized? Y: T>() { -//~^ERROR incompatible bounds on type parameter Y, bound T does not allow unsized type +//~^ERROR incompatible bounds on type parameter `Y`, bound `T` does not allow unsized type } pub fn main() { diff --git a/src/test/compile-fail/use-after-move-implicity-coerced-object.rs b/src/test/compile-fail/use-after-move-implicity-coerced-object.rs index 043f3a233a6..3f2f43b0c9b 100644 --- a/src/test/compile-fail/use-after-move-implicity-coerced-object.rs +++ b/src/test/compile-fail/use-after-move-implicity-coerced-object.rs @@ -23,10 +23,10 @@ impl fmt::Show for Number { } struct List { - list: Vec<Box<ToString>> } + list: Vec<Box<ToString+'static>> } impl List { - fn push(&mut self, n: Box<ToString>) { + fn push(&mut self, n: Box<ToString+'static>) { self.list.push(n); } } diff --git a/src/test/compile-fail/use-meta-mismatch.rs b/src/test/compile-fail/use-meta-mismatch.rs index 0d84a59fc9e..808deea226b 100644 --- a/src/test/compile-fail/use-meta-mismatch.rs +++ b/src/test/compile-fail/use-meta-mismatch.rs @@ -10,6 +10,6 @@ // error-pattern:can't find crate for `extra` -extern crate extra = "fake-crate"; +extern crate "fake-crate" as extra; fn main() { } diff --git a/src/test/compile-fail/variadic-ffi.rs b/src/test/compile-fail/variadic-ffi.rs index 2110d4c8009..17a17dcdcf8 100644 --- a/src/test/compile-fail/variadic-ffi.rs +++ b/src/test/compile-fail/variadic-ffi.rs @@ -25,13 +25,13 @@ fn main() { let x: unsafe extern "C" fn(f: int, x: u8) = foo; //~^ ERROR: mismatched types: expected `unsafe extern "C" fn(int, u8)` - // but found `unsafe extern "C" fn(int, u8, ...)` - // (expected non-variadic fn but found variadic function) + // , found `unsafe extern "C" fn(int, u8, ...)` + // (expected non-variadic fn, found variadic function) let y: unsafe extern "C" fn(f: int, x: u8, ...) = bar; //~^ ERROR: mismatched types: expected `unsafe extern "C" fn(int, u8, ...)` - // but found `extern "C" extern fn(int, u8)` - // (expected variadic fn but found non-variadic function) + // , found `extern "C" extern fn(int, u8)` + // (expected variadic fn, found non-variadic function) foo(1, 2, 3f32); //~ ERROR: can't pass an f32 to variadic function, cast to c_double foo(1, 2, true); //~ ERROR: can't pass bool to variadic function, cast to c_int diff --git a/src/test/compile-fail/variance-regions-direct.rs b/src/test/compile-fail/variance-regions-direct.rs index da4f9846187..fa38482b21c 100644 --- a/src/test/compile-fail/variance-regions-direct.rs +++ b/src/test/compile-fail/variance-regions-direct.rs @@ -32,7 +32,7 @@ struct Test3<'a, 'b, 'c> { //~ ERROR regions=[[+, +, +];[];[]] // Mutability induces invariance: #[rustc_variance] -struct Test4<'a, 'b> { //~ ERROR regions=[[-, o];[];[]] +struct Test4<'a, 'b:'a> { //~ ERROR regions=[[-, o];[];[]] x: &'a mut &'b int, } @@ -64,7 +64,7 @@ struct Test7<'a> { //~ ERROR regions=[[*];[];[]] // Try enums too. #[rustc_variance] -enum Test8<'a, 'b, 'c> { //~ ERROR regions=[[+, -, o];[];[]] +enum Test8<'a, 'b, 'c:'b> { //~ ERROR regions=[[+, -, o];[];[]] Test8A(extern "Rust" fn(&'a int)), Test8B(&'b [int]), Test8C(&'b mut &'c str), diff --git a/src/test/compile-fail/variance-regions-indirect.rs b/src/test/compile-fail/variance-regions-indirect.rs index 0d20f652496..c049fbc0fed 100644 --- a/src/test/compile-fail/variance-regions-indirect.rs +++ b/src/test/compile-fail/variance-regions-indirect.rs @@ -13,29 +13,29 @@ // Try enums too. #[rustc_variance] -enum Base<'a, 'b, 'c, 'd> { //~ ERROR regions=[[+, -, o, *];[];[]] +enum Base<'a, 'b, 'c:'b, 'd> { //~ ERROR regions=[[+, -, o, *];[];[]] Test8A(extern "Rust" fn(&'a int)), Test8B(&'b [int]), Test8C(&'b mut &'c str), } #[rustc_variance] -struct Derived1<'w, 'x, 'y, 'z> { //~ ERROR regions=[[*, o, -, +];[];[]] +struct Derived1<'w, 'x:'y, 'y, 'z> { //~ ERROR regions=[[*, o, -, +];[];[]] f: Base<'z, 'y, 'x, 'w> } #[rustc_variance] // Combine - and + to yield o -struct Derived2<'a, 'b, 'c> { //~ ERROR regions=[[o, o, *];[];[]] +struct Derived2<'a, 'b:'a, 'c> { //~ ERROR regions=[[o, o, *];[];[]] f: Base<'a, 'a, 'b, 'c> } #[rustc_variance] // Combine + and o to yield o (just pay attention to 'a here) -struct Derived3<'a, 'b, 'c> { //~ ERROR regions=[[o, -, *];[];[]] +struct Derived3<'a:'b, 'b, 'c> { //~ ERROR regions=[[o, -, *];[];[]] f: Base<'a, 'b, 'a, 'c> } #[rustc_variance] // Combine + and * to yield + (just pay attention to 'a here) -struct Derived4<'a, 'b, 'c> { //~ ERROR regions=[[+, -, o];[];[]] +struct Derived4<'a, 'b, 'c:'b> { //~ ERROR regions=[[+, -, o];[];[]] f: Base<'a, 'b, 'c, 'a> } diff --git a/src/test/compile-fail/weak-lang-item.rs b/src/test/compile-fail/weak-lang-item.rs index a1b64b77ac1..636adefb95e 100644 --- a/src/test/compile-fail/weak-lang-item.rs +++ b/src/test/compile-fail/weak-lang-item.rs @@ -16,4 +16,4 @@ #![no_std] extern crate core; -extern crate other = "weak-lang-items"; +extern crate "weak-lang-items" as other; diff --git a/src/test/compile-fail/wrong-mul-method-signature.rs b/src/test/compile-fail/wrong-mul-method-signature.rs index bb30715f75e..e3aed148a23 100644 --- a/src/test/compile-fail/wrong-mul-method-signature.rs +++ b/src/test/compile-fail/wrong-mul-method-signature.rs @@ -20,7 +20,7 @@ struct Vec1 { // Expecting ref in input signature impl Mul<f64, Vec1> for Vec1 { fn mul(&self, s: f64) -> Vec1 { - //~^ ERROR: method `mul` has an incompatible type for trait: expected &-ptr but found f64 + //~^ ERROR: method `mul` has an incompatible type for trait: expected &-ptr, found f64 Vec1 { x: self.x * s } @@ -35,7 +35,7 @@ struct Vec2 { // Wrong type parameter ordering impl Mul<Vec2, f64> for Vec2 { fn mul(&self, s: f64) -> Vec2 { - //~^ ERROR: method `mul` has an incompatible type for trait: expected &-ptr but found f64 + //~^ ERROR: method `mul` has an incompatible type for trait: expected &-ptr, found f64 Vec2 { x: self.x * s, y: self.y * s @@ -52,7 +52,7 @@ struct Vec3 { // Unexpected return type impl Mul<f64, i32> for Vec3 { fn mul(&self, s: &f64) -> f64 { - //~^ ERROR: method `mul` has an incompatible type for trait: expected i32 but found f64 + //~^ ERROR: method `mul` has an incompatible type for trait: expected i32, found f64 *s } } diff --git a/src/test/debuginfo/c-style-enum-in-composite.rs b/src/test/debuginfo/c-style-enum-in-composite.rs index f7351728290..ef23e7ee876 100644 --- a/src/test/debuginfo/c-style-enum-in-composite.rs +++ b/src/test/debuginfo/c-style-enum-in-composite.rs @@ -87,7 +87,7 @@ struct PaddedStruct { e: i16 } -#[packed] +#[repr(packed)] struct PackedStruct { a: i16, b: AnEnum, diff --git a/src/test/debuginfo/packed-struct-with-destructor.rs b/src/test/debuginfo/packed-struct-with-destructor.rs index 215d961b71b..00a560edbf0 100644 --- a/src/test/debuginfo/packed-struct-with-destructor.rs +++ b/src/test/debuginfo/packed-struct-with-destructor.rs @@ -77,7 +77,7 @@ #![allow(unused_variable)] -#[packed] +#[repr(packed)] struct Packed { x: i16, y: i32, @@ -88,7 +88,7 @@ impl Drop for Packed { fn drop(&mut self) {} } -#[packed] +#[repr(packed)] struct PackedInPacked { a: i32, b: Packed, @@ -113,7 +113,7 @@ impl Drop for Unpacked { fn drop(&mut self) {} } -#[packed] +#[repr(packed)] struct UnpackedInPacked { a: i16, b: Unpacked, @@ -121,7 +121,7 @@ struct UnpackedInPacked { d: i64 } -#[packed] +#[repr(packed)] struct PackedInPackedWithDrop { a: i32, b: Packed, @@ -144,7 +144,7 @@ impl Drop for PackedInUnpackedWithDrop { fn drop(&mut self) {} } -#[packed] +#[repr(packed)] struct UnpackedInPackedWithDrop { a: i16, b: Unpacked, diff --git a/src/test/debuginfo/packed-struct.rs b/src/test/debuginfo/packed-struct.rs index 8201afe3a01..bf2213509cf 100644 --- a/src/test/debuginfo/packed-struct.rs +++ b/src/test/debuginfo/packed-struct.rs @@ -63,14 +63,14 @@ #![allow(unused_variable)] -#[packed] +#[repr(packed)] struct Packed { x: i16, y: i32, z: i64 } -#[packed] +#[repr(packed)] struct PackedInPacked { a: i32, b: Packed, @@ -95,7 +95,7 @@ struct Unpacked { } // layout (64 bit): aabb bbbb bbbb bbbb bbbb bbbb bbcc cccc cccc cccc cccc cccc ccdd dddd dd -#[packed] +#[repr(packed)] struct UnpackedInPacked { a: i16, b: Unpacked, diff --git a/src/test/pretty/closure-reform-pretty.rs b/src/test/pretty/closure-reform-pretty.rs index 92fa8124d68..eb20a09477d 100644 --- a/src/test/pretty/closure-reform-pretty.rs +++ b/src/test/pretty/closure-reform-pretty.rs @@ -17,7 +17,7 @@ fn call_it(f: proc(String) -> String) { } fn call_this(f: |&str|: Send) { } -fn call_that(f: <'a>|&'a int, &'a int|: -> int) { } +fn call_that(f: <'a>|&'a int, &'a int| -> int) { } fn call_extern(f: fn() -> int) { } diff --git a/src/test/pretty/issue-4264.pp b/src/test/pretty/issue-4264.pp index e1ad27ec5c7..86e394e5408 100644 --- a/src/test/pretty/issue-4264.pp +++ b/src/test/pretty/issue-4264.pp @@ -2,8 +2,8 @@ #![no_std] #![feature(globs)] #[phase(plugin, link)] -extern crate std = "std"; -extern crate rt = "native"; +extern crate "std" as std; +extern crate "native" as rt; #[prelude_import] use std::prelude::*; // Copyright 2014 The Rust Project Developers. See the COPYRIGHT @@ -48,8 +48,8 @@ pub fn bar() { as core::fmt::rt::Piece<'static>)] as [core::fmt::rt::Piece<'static>, .. 1]); let __args_vec = - (&([] as &'static [core::fmt::Argument<'static>]) as - &'static [core::fmt::Argument<'static>]); + (&([] as [core::fmt::Argument<'_>, .. 0]) as + &[core::fmt::Argument<'_>, .. 0]); let __args = (unsafe { ((::std::fmt::Arguments::new as @@ -58,9 +58,9 @@ pub fn bar() { [core::fmt::rt::Piece<'static>, .. 1]), (__args_vec as - &'static [core::fmt::Argument<'static>])) - as core::fmt::Arguments<'static>) - } as core::fmt::Arguments<'static>); + &[core::fmt::Argument<'_>, .. 0])) + as core::fmt::Arguments<'_>) + } as core::fmt::Arguments<'_>); @@ -72,9 +72,9 @@ pub fn bar() { ((::std::fmt::format as fn(&core::fmt::Arguments<'_>) -> collections::string::String)((&(__args as - core::fmt::Arguments<'static>) + core::fmt::Arguments<'_>) as - &core::fmt::Arguments<'static>)) + &core::fmt::Arguments<'_>)) as collections::string::String) } } as collections::string::String); diff --git a/src/test/pretty/path-type-bounds.rs b/src/test/pretty/path-type-bounds.rs index 7c05e6d6065..f575fb32924 100644 --- a/src/test/pretty/path-type-bounds.rs +++ b/src/test/pretty/path-type-bounds.rs @@ -14,7 +14,7 @@ trait Tr { } impl Tr for int { } -fn foo(x: Box<Tr+ Sync>) -> Box<Tr+ Sync> { x } +fn foo<'a>(x: Box<Tr+ Sync + 'a>) -> Box<Tr+ Sync + 'a> { x } fn main() { let x: Box<Tr+ Sync>; diff --git a/src/test/pretty/raw-str-nonexpr.rs b/src/test/pretty/raw-str-nonexpr.rs index 5cc5705ed75..08d599b1e6f 100644 --- a/src/test/pretty/raw-str-nonexpr.rs +++ b/src/test/pretty/raw-str-nonexpr.rs @@ -13,6 +13,6 @@ #![feature(asm)] #[cfg = r#"just parse this"#] -extern crate blah = r##"blah"##; +extern crate r##"blah"## as blah; fn main() { unsafe { asm!(r###"blah"###); } } diff --git a/src/test/run-make/extern-fn-with-packed-struct/Makefile b/src/test/run-make/extern-fn-with-packed-struct/Makefile new file mode 100644 index 00000000000..ea6971853fe --- /dev/null +++ b/src/test/run-make/extern-fn-with-packed-struct/Makefile @@ -0,0 +1,7 @@ +-include ../tools.mk + +all: + $(CC) -std=c99 test.c -c -o $(TMPDIR)/test.o + $(AR) rcs $(TMPDIR)/libtest.a $(TMPDIR)/test.o + $(RUSTC) test.rs -L $(TMPDIR) + $(call RUN,test) || exit 1 diff --git a/src/test/run-make/extern-fn-with-packed-struct/test.c b/src/test/run-make/extern-fn-with-packed-struct/test.c new file mode 100644 index 00000000000..c3456a64b9b --- /dev/null +++ b/src/test/run-make/extern-fn-with-packed-struct/test.c @@ -0,0 +1,11 @@ +// Pragma needed cause of gcc bug on windows: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=52991 +#pragma pack(1) +struct __attribute__((packed)) Foo { + char a; + short b; + char c; +}; + +struct Foo foo(struct Foo foo) { + return foo; +} diff --git a/src/test/run-make/extern-fn-with-packed-struct/test.rs b/src/test/run-make/extern-fn-with-packed-struct/test.rs new file mode 100644 index 00000000000..8d8daed1393 --- /dev/null +++ b/src/test/run-make/extern-fn-with-packed-struct/test.rs @@ -0,0 +1,30 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[repr(packed)] +#[deriving(PartialEq, Show)] +struct Foo { + a: i8, + b: i16, + c: i8 +} + +#[link(name = "test", kind = "static")] +extern { + fn foo(f: Foo) -> Foo; +} + +fn main() { + unsafe { + let a = Foo { a: 1, b: 2, c: 3 }; + let b = foo(a); + assert_eq!(a, b); + } +} diff --git a/src/test/run-make/graphviz-flowgraph/f01.dot-expected.dot b/src/test/run-make/graphviz-flowgraph/f01.dot-expected.dot index a5239a6cc66..c5455ab90dc 100644 --- a/src/test/run-make/graphviz-flowgraph/f01.dot-expected.dot +++ b/src/test/run-make/graphviz-flowgraph/f01.dot-expected.dot @@ -2,8 +2,10 @@ digraph block { N0[label="entry"]; N1[label="exit"]; N2[label="expr 1i"]; - N3[label="block { 1i; }"]; + N3[label="stmt 1i;"]; + N4[label="block { 1i; }"]; N0 -> N2; N2 -> N3; - N3 -> N1; + N3 -> N4; + N4 -> N1; } diff --git a/src/test/run-make/graphviz-flowgraph/f02.dot-expected.dot b/src/test/run-make/graphviz-flowgraph/f02.dot-expected.dot index ada3f091808..230dcbaeb98 100644 --- a/src/test/run-make/graphviz-flowgraph/f02.dot-expected.dot +++ b/src/test/run-make/graphviz-flowgraph/f02.dot-expected.dot @@ -2,8 +2,10 @@ digraph block { N0[label="entry"]; N1[label="exit"]; N2[label="local _x"]; - N3[label="block { let _x: int; }"]; + N3[label="stmt let _x: int;"]; + N4[label="block { let _x: int; }"]; N0 -> N2; N2 -> N3; - N3 -> N1; + N3 -> N4; + N4 -> N1; } diff --git a/src/test/run-make/graphviz-flowgraph/f03.dot-expected.dot b/src/test/run-make/graphviz-flowgraph/f03.dot-expected.dot index 43462862f6e..e60d349ad14 100644 --- a/src/test/run-make/graphviz-flowgraph/f03.dot-expected.dot +++ b/src/test/run-make/graphviz-flowgraph/f03.dot-expected.dot @@ -4,10 +4,12 @@ digraph block { N2[label="expr 3i"]; N3[label="expr 33i"]; N4[label="expr 3i + 33i"]; - N5[label="block { 3i + 33i; }"]; + N5[label="stmt 3i + 33i;"]; + N6[label="block { 3i + 33i; }"]; N0 -> N2; N2 -> N3; N3 -> N4; N4 -> N5; - N5 -> N1; + N5 -> N6; + N6 -> N1; } diff --git a/src/test/run-make/graphviz-flowgraph/f04.dot-expected.dot b/src/test/run-make/graphviz-flowgraph/f04.dot-expected.dot index 26c858a0828..82cdcb39fbf 100644 --- a/src/test/run-make/graphviz-flowgraph/f04.dot-expected.dot +++ b/src/test/run-make/graphviz-flowgraph/f04.dot-expected.dot @@ -3,9 +3,11 @@ digraph block { N1[label="exit"]; N2[label="expr 4i"]; N3[label="local _x"]; - N4[label="block { let _x = 4i; }"]; + N4[label="stmt let _x = 4i;"]; + N5[label="block { let _x = 4i; }"]; N0 -> N2; N2 -> N3; N3 -> N4; - N4 -> N1; + N4 -> N5; + N5 -> N1; } diff --git a/src/test/run-make/graphviz-flowgraph/f05.dot-expected.dot b/src/test/run-make/graphviz-flowgraph/f05.dot-expected.dot index 850d04f430f..8a27d536ffc 100644 --- a/src/test/run-make/graphviz-flowgraph/f05.dot-expected.dot +++ b/src/test/run-make/graphviz-flowgraph/f05.dot-expected.dot @@ -7,7 +7,8 @@ digraph block { N5[label="local _x"]; N6[label="local _y"]; N7[label="pat (_x, _y)"]; - N8[label="block { let (_x, _y) = (5i, 55i); }"]; + N8[label="stmt let (_x, _y) = (5i, 55i);"]; + N9[label="block { let (_x, _y) = (5i, 55i); }"]; N0 -> N2; N2 -> N3; N3 -> N4; @@ -15,5 +16,6 @@ digraph block { N5 -> N6; N6 -> N7; N7 -> N8; - N8 -> N1; + N8 -> N9; + N9 -> N1; } diff --git a/src/test/run-make/graphviz-flowgraph/f06.dot-expected.dot b/src/test/run-make/graphviz-flowgraph/f06.dot-expected.dot index b431476f84a..54e9d89d3fb 100644 --- a/src/test/run-make/graphviz-flowgraph/f06.dot-expected.dot +++ b/src/test/run-make/graphviz-flowgraph/f06.dot-expected.dot @@ -5,11 +5,13 @@ digraph block { N3[label="expr S6{val: 6,}"]; N4[label="local _x"]; N5[label="pat S6 { val: _x }"]; - N6[label="block { let S6 { val: _x } = S6{val: 6,}; }"]; + N6[label="stmt let S6 { val: _x } = S6{val: 6,};"]; + N7[label="block { let S6 { val: _x } = S6{val: 6,}; }"]; N0 -> N2; N2 -> N3; N3 -> N4; N4 -> N5; N5 -> N6; - N6 -> N1; + N6 -> N7; + N7 -> N1; } diff --git a/src/test/run-make/graphviz-flowgraph/f07.dot-expected.dot b/src/test/run-make/graphviz-flowgraph/f07.dot-expected.dot index 2b7088fbc33..4c6383324e5 100644 --- a/src/test/run-make/graphviz-flowgraph/f07.dot-expected.dot +++ b/src/test/run-make/graphviz-flowgraph/f07.dot-expected.dot @@ -15,7 +15,8 @@ digraph block { N13[label="expr x"]; N14[label="expr y"]; N15[label="expr x + y"]; - N16[label="block { match [7i, 77i, 777i, 7777i] { [x, y, ..] => x + y, }; }"]; + N16[label="stmt match [7i, 77i, 777i, 7777i] { [x, y, ..] => x + y, };"]; + N17[label="block { match [7i, 77i, 777i, 7777i] { [x, y, ..] => x + y, }; }"]; N0 -> N2; N2 -> N3; N3 -> N4; @@ -31,5 +32,6 @@ digraph block { N14 -> N15; N15 -> N7; N7 -> N16; - N16 -> N1; + N16 -> N17; + N17 -> N1; } diff --git a/src/test/run-make/graphviz-flowgraph/f08.dot-expected.dot b/src/test/run-make/graphviz-flowgraph/f08.dot-expected.dot index f43beb025e3..27a240ed182 100644 --- a/src/test/run-make/graphviz-flowgraph/f08.dot-expected.dot +++ b/src/test/run-make/graphviz-flowgraph/f08.dot-expected.dot @@ -3,16 +3,19 @@ digraph block { N1[label="exit"]; N2[label="expr 8i"]; N3[label="local x"]; - N4[label="local _y"]; - N5[label="expr x"]; - N6[label="expr 88i"]; - N7[label="expr x > 88i"]; - N8[label="expr 888i"]; - N9[label="expr _y"]; - N10[label="expr _y = 888i"]; - N11[label="block { _y = 888i; }"]; - N12[label="expr if x > 88i { _y = 888i; }"]; - N13[label="block { let x = 8i; let _y; if x > 88i { _y = 888i; } }"]; + N4[label="stmt let x = 8i;"]; + N5[label="local _y"]; + N6[label="stmt let _y;"]; + N7[label="expr x"]; + N8[label="expr 88i"]; + N9[label="expr x > 88i"]; + N10[label="expr 888i"]; + N11[label="expr _y"]; + N12[label="expr _y = 888i"]; + N13[label="stmt _y = 888i;"]; + N14[label="block { _y = 888i; }"]; + N15[label="expr if x > 88i { _y = 888i; }"]; + N16[label="block { let x = 8i; let _y; if x > 88i { _y = 888i; } }"]; N0 -> N2; N2 -> N3; N3 -> N4; @@ -23,8 +26,11 @@ digraph block { N8 -> N9; N9 -> N10; N10 -> N11; - N7 -> N12; N11 -> N12; N12 -> N13; - N13 -> N1; + N13 -> N14; + N9 -> N15; + N14 -> N15; + N15 -> N16; + N16 -> N1; } diff --git a/src/test/run-make/graphviz-flowgraph/f09.dot-expected.dot b/src/test/run-make/graphviz-flowgraph/f09.dot-expected.dot index a3576b9c36b..d2c58c6d59a 100644 --- a/src/test/run-make/graphviz-flowgraph/f09.dot-expected.dot +++ b/src/test/run-make/graphviz-flowgraph/f09.dot-expected.dot @@ -3,23 +3,27 @@ digraph block { N1[label="exit"]; N2[label="expr 91i"]; N3[label="local x"]; - N4[label="local _y"]; - N5[label="expr x"]; - N6[label="expr 92i"]; - N7[label="expr x > 92i"]; - N8[label="expr 93i"]; - N9[label="expr _y"]; - N10[label="expr _y = 93i"]; - N11[label="block { _y = 93i; }"]; - N12[label="expr 94i"]; - N13[label="expr 95i"]; - N14[label="expr 94i + 95i"]; - N15[label="expr _y"]; - N16[label="expr _y = 94i + 95i"]; - N17[label="block { _y = 94i + 95i; }"]; - N18[label="expr { _y = 94i + 95i; }"]; - N19[label="expr if x > 92i { _y = 93i; } else { _y = 94i + 95i; }"]; - N20[label="block { let x = 91i; let _y; if x > 92i { _y = 93i; } else { _y = 94i + 95i; } }"]; + N4[label="stmt let x = 91i;"]; + N5[label="local _y"]; + N6[label="stmt let _y;"]; + N7[label="expr x"]; + N8[label="expr 92i"]; + N9[label="expr x > 92i"]; + N10[label="expr 93i"]; + N11[label="expr _y"]; + N12[label="expr _y = 93i"]; + N13[label="stmt _y = 93i;"]; + N14[label="block { _y = 93i; }"]; + N15[label="expr 94i"]; + N16[label="expr 95i"]; + N17[label="expr 94i + 95i"]; + N18[label="expr _y"]; + N19[label="expr _y = 94i + 95i"]; + N20[label="stmt _y = 94i + 95i;"]; + N21[label="block { _y = 94i + 95i; }"]; + N22[label="expr { _y = 94i + 95i; }"]; + N23[label="expr if x > 92i { _y = 93i; } else { _y = 94i + 95i; }"]; + N24[label="block { let x = 91i; let _y; if x > 92i { _y = 93i; } else { _y = 94i + 95i; } }"]; N0 -> N2; N2 -> N3; N3 -> N4; @@ -30,15 +34,19 @@ digraph block { N8 -> N9; N9 -> N10; N10 -> N11; - N7 -> N12; + N11 -> N12; N12 -> N13; N13 -> N14; - N14 -> N15; + N9 -> N15; N15 -> N16; N16 -> N17; N17 -> N18; - N11 -> N19; N18 -> N19; N19 -> N20; - N20 -> N1; + N20 -> N21; + N21 -> N22; + N14 -> N23; + N22 -> N23; + N23 -> N24; + N24 -> N1; } diff --git a/src/test/run-make/graphviz-flowgraph/f10.dot-expected.dot b/src/test/run-make/graphviz-flowgraph/f10.dot-expected.dot index 69b5bd6f58c..421a79fd136 100644 --- a/src/test/run-make/graphviz-flowgraph/f10.dot-expected.dot +++ b/src/test/run-make/graphviz-flowgraph/f10.dot-expected.dot @@ -3,16 +3,18 @@ digraph block { N1[label="exit"]; N2[label="expr 10i"]; N3[label="local mut x"]; - N4[label="(dummy_node)"]; - N5[label="expr x"]; - N6[label="expr 0i"]; - N7[label="expr x > 0i"]; - N8[label="expr while x > 0i { x -= 1i; }"]; - N9[label="expr 1i"]; - N10[label="expr x"]; - N11[label="expr x -= 1i"]; - N12[label="block { x -= 1i; }"]; - N13[label="block { let mut x = 10i; while x > 0i { x -= 1i; } }"]; + N4[label="stmt let mut x = 10i;"]; + N5[label="(dummy_node)"]; + N6[label="expr x"]; + N7[label="expr 0i"]; + N8[label="expr x > 0i"]; + N9[label="expr while x > 0i { x -= 1i; }"]; + N10[label="expr 1i"]; + N11[label="expr x"]; + N12[label="expr x -= 1i"]; + N13[label="stmt x -= 1i;"]; + N14[label="block { x -= 1i; }"]; + N15[label="block { let mut x = 10i; while x > 0i { x -= 1i; } }"]; N0 -> N2; N2 -> N3; N3 -> N4; @@ -20,11 +22,13 @@ digraph block { N5 -> N6; N6 -> N7; N7 -> N8; - N7 -> N9; - N9 -> N10; + N8 -> N9; + N8 -> N10; N10 -> N11; N11 -> N12; - N12 -> N4; - N8 -> N13; - N13 -> N1; + N12 -> N13; + N13 -> N14; + N14 -> N5; + N9 -> N15; + N15 -> N1; } diff --git a/src/test/run-make/graphviz-flowgraph/f11.dot-expected.dot b/src/test/run-make/graphviz-flowgraph/f11.dot-expected.dot index 44024cf76f3..b928058fed9 100644 --- a/src/test/run-make/graphviz-flowgraph/f11.dot-expected.dot +++ b/src/test/run-make/graphviz-flowgraph/f11.dot-expected.dot @@ -3,23 +3,31 @@ digraph block { N1[label="exit"]; N2[label="expr 11i"]; N3[label="local mut _x"]; - N4[label="(dummy_node)"]; - N5[label="expr loop { _x -= 1i; }"]; - N6[label="expr 1i"]; - N7[label="expr _x"]; - N8[label="expr _x -= 1i"]; - N9[label="block { _x -= 1i; }"]; - N10[label="expr \"unreachable\""]; - N11[label="block { let mut _x = 11i; loop { _x -= 1i; } \"unreachable\"; }"]; + N4[label="stmt let mut _x = 11i;"]; + N5[label="(dummy_node)"]; + N6[label="expr loop { _x -= 1i; }"]; + N7[label="expr 1i"]; + N8[label="expr _x"]; + N9[label="expr _x -= 1i"]; + N10[label="stmt _x -= 1i;"]; + N11[label="block { _x -= 1i; }"]; + N12[label="stmt loop { _x -= 1i; }"]; + N13[label="expr \"unreachable\""]; + N14[label="stmt \"unreachable\";"]; + N15[label="block { let mut _x = 11i; loop { _x -= 1i; } \"unreachable\"; }"]; N0 -> N2; N2 -> N3; N3 -> N4; - N4 -> N6; - N6 -> N7; + N4 -> N5; + N5 -> N7; N7 -> N8; N8 -> N9; - N9 -> N4; - N5 -> N10; + N9 -> N10; N10 -> N11; - N11 -> N1; + N11 -> N5; + N6 -> N12; + N12 -> N13; + N13 -> N14; + N14 -> N15; + N15 -> N1; } diff --git a/src/test/run-make/graphviz-flowgraph/f12.dot-expected.dot b/src/test/run-make/graphviz-flowgraph/f12.dot-expected.dot index ad257c19741..d89a37308de 100644 --- a/src/test/run-make/graphviz-flowgraph/f12.dot-expected.dot +++ b/src/test/run-make/graphviz-flowgraph/f12.dot-expected.dot @@ -3,38 +3,46 @@ digraph block { N1[label="exit"]; N2[label="expr 12i"]; N3[label="local mut x"]; - N4[label="(dummy_node)"]; - N5[label="expr loop { x -= 1i; if x == 2i { break ; \"unreachable\"; } }"]; - N6[label="expr 1i"]; - N7[label="expr x"]; - N8[label="expr x -= 1i"]; - N9[label="expr x"]; - N10[label="expr 2i"]; - N11[label="expr x == 2i"]; - N12[label="expr break"]; - N13[label="(dummy_node)"]; - N14[label="expr \"unreachable\""]; - N15[label="block { break ; \"unreachable\"; }"]; - N16[label="expr if x == 2i { break ; \"unreachable\"; }"]; - N17[label="block { x -= 1i; if x == 2i { break ; \"unreachable\"; } }"]; - N18[label="block { let mut x = 12i; loop { x -= 1i; if x == 2i { break ; \"unreachable\"; } } }"]; + N4[label="stmt let mut x = 12i;"]; + N5[label="(dummy_node)"]; + N6[label="expr loop { x -= 1i; if x == 2i { break ; \"unreachable\"; } }"]; + N7[label="expr 1i"]; + N8[label="expr x"]; + N9[label="expr x -= 1i"]; + N10[label="stmt x -= 1i;"]; + N11[label="expr x"]; + N12[label="expr 2i"]; + N13[label="expr x == 2i"]; + N14[label="expr break"]; + N15[label="(dummy_node)"]; + N16[label="stmt break ;"]; + N17[label="expr \"unreachable\""]; + N18[label="stmt \"unreachable\";"]; + N19[label="block { break ; \"unreachable\"; }"]; + N20[label="expr if x == 2i { break ; \"unreachable\"; }"]; + N21[label="block { x -= 1i; if x == 2i { break ; \"unreachable\"; } }"]; + N22[label="block { let mut x = 12i; loop { x -= 1i; if x == 2i { break ; \"unreachable\"; } } }"]; N0 -> N2; N2 -> N3; N3 -> N4; - N4 -> N6; - N6 -> N7; + N4 -> N5; + N5 -> N7; N7 -> N8; N8 -> N9; N9 -> N10; N10 -> N11; N11 -> N12; - N12 -> N5[label="exiting scope_0 expr break,\lexiting scope_1 stmt break ;,\lexiting scope_2 block { break ; \"unreachable\"; },\lexiting scope_3 expr if x == 2i { break ; \"unreachable\"; },\lexiting scope_4 block { x -= 1i; if x == 2i { break ; \"unreachable\"; } }"]; + N12 -> N13; N13 -> N14; - N14 -> N15; - N11 -> N16; + N14 -> N6[label="exiting scope_0 expr break,\lexiting scope_1 stmt break ;,\lexiting scope_2 block { break ; \"unreachable\"; },\lexiting scope_3 expr if x == 2i { break ; \"unreachable\"; },\lexiting scope_4 block { x -= 1i; if x == 2i { break ; \"unreachable\"; } }"]; N15 -> N16; N16 -> N17; - N17 -> N4; - N5 -> N18; - N18 -> N1; + N17 -> N18; + N18 -> N19; + N13 -> N20; + N19 -> N20; + N20 -> N21; + N21 -> N5; + N6 -> N22; + N22 -> N1; } diff --git a/src/test/run-make/graphviz-flowgraph/f13.dot-expected.dot b/src/test/run-make/graphviz-flowgraph/f13.dot-expected.dot index 5d1d1253b22..aa43ef51534 100644 --- a/src/test/run-make/graphviz-flowgraph/f13.dot-expected.dot +++ b/src/test/run-make/graphviz-flowgraph/f13.dot-expected.dot @@ -5,44 +5,48 @@ digraph block { N3[label="expr 13"]; N4[label="expr E13b(13)"]; N5[label="local x"]; - N6[label="local _y"]; - N7[label="expr x"]; - N8[label="expr match x { E13a => _y = 1, E13b(v) => _y = v + 1, }"]; - N9[label="(dummy_node)"]; - N10[label="local E13a"]; - N11[label="expr 1"]; - N12[label="expr _y"]; - N13[label="expr _y = 1"]; - N14[label="(dummy_node)"]; - N15[label="local v"]; - N16[label="pat E13b(v)"]; - N17[label="expr v"]; - N18[label="expr 1"]; - N19[label="expr v + 1"]; - N20[label="expr _y"]; - N21[label="expr _y = v + 1"]; - N22[label="block {\l let x = E13b(13);\l let _y;\l match x { E13a => _y = 1, E13b(v) => _y = v + 1, }\l}\l"]; + N6[label="stmt let x = E13b(13);"]; + N7[label="local _y"]; + N8[label="stmt let _y;"]; + N9[label="expr x"]; + N10[label="expr match x { E13a => _y = 1, E13b(v) => _y = v + 1, }"]; + N11[label="(dummy_node)"]; + N12[label="local E13a"]; + N13[label="expr 1"]; + N14[label="expr _y"]; + N15[label="expr _y = 1"]; + N16[label="(dummy_node)"]; + N17[label="local v"]; + N18[label="pat E13b(v)"]; + N19[label="expr v"]; + N20[label="expr 1"]; + N21[label="expr v + 1"]; + N22[label="expr _y"]; + N23[label="expr _y = v + 1"]; + N24[label="block {\l let x = E13b(13);\l let _y;\l match x { E13a => _y = 1, E13b(v) => _y = v + 1, }\l}\l"]; N0 -> N2; N2 -> N3; N3 -> N4; N4 -> N5; N5 -> N6; N6 -> N7; - N7 -> N9; - N9 -> N10; - N10 -> N11; + N7 -> N8; + N8 -> N9; + N9 -> N11; N11 -> N12; N12 -> N13; - N13 -> N8; - N9 -> N14; + N13 -> N14; N14 -> N15; - N15 -> N16; + N15 -> N10; + N11 -> N16; N16 -> N17; N17 -> N18; N18 -> N19; N19 -> N20; N20 -> N21; - N21 -> N8; - N8 -> N22; - N22 -> N1; + N21 -> N22; + N22 -> N23; + N23 -> N10; + N10 -> N24; + N24 -> N1; } diff --git a/src/test/run-make/graphviz-flowgraph/f14.dot-expected.dot b/src/test/run-make/graphviz-flowgraph/f14.dot-expected.dot index f8e4bd12bb0..bdb2c133bad 100644 --- a/src/test/run-make/graphviz-flowgraph/f14.dot-expected.dot +++ b/src/test/run-make/graphviz-flowgraph/f14.dot-expected.dot @@ -3,26 +3,32 @@ digraph block { N1[label="exit"]; N2[label="expr 14i"]; N3[label="local x"]; - N4[label="expr x"]; - N5[label="expr 1i"]; - N6[label="expr x > 1i"]; - N7[label="expr return"]; - N8[label="(dummy_node)"]; - N9[label="expr \"unreachable\""]; - N10[label="block { return; \"unreachable\"; }"]; - N11[label="expr if x > 1i { return; \"unreachable\"; }"]; - N12[label="block { let x = 14i; if x > 1i { return; \"unreachable\"; } }"]; + N4[label="stmt let x = 14i;"]; + N5[label="expr x"]; + N6[label="expr 1i"]; + N7[label="expr x > 1i"]; + N8[label="expr return"]; + N9[label="(dummy_node)"]; + N10[label="stmt return;"]; + N11[label="expr \"unreachable\""]; + N12[label="stmt \"unreachable\";"]; + N13[label="block { return; \"unreachable\"; }"]; + N14[label="expr if x > 1i { return; \"unreachable\"; }"]; + N15[label="block { let x = 14i; if x > 1i { return; \"unreachable\"; } }"]; N0 -> N2; N2 -> N3; N3 -> N4; N4 -> N5; N5 -> N6; N6 -> N7; - N7 -> N1; - N8 -> N9; + N7 -> N8; + N8 -> N1; N9 -> N10; - N6 -> N11; N10 -> N11; N11 -> N12; - N12 -> N1; + N12 -> N13; + N7 -> N14; + N13 -> N14; + N14 -> N15; + N15 -> N1; } diff --git a/src/test/run-make/graphviz-flowgraph/f15.dot-expected.dot b/src/test/run-make/graphviz-flowgraph/f15.dot-expected.dot index bc47d9aff81..4bd9fc9ec1a 100644 --- a/src/test/run-make/graphviz-flowgraph/f15.dot-expected.dot +++ b/src/test/run-make/graphviz-flowgraph/f15.dot-expected.dot @@ -3,77 +3,101 @@ digraph block { N1[label="exit"]; N2[label="expr 15i"]; N3[label="local mut x"]; - N4[label="expr 151i"]; - N5[label="local mut y"]; - N6[label="(dummy_node)"]; - N7[label="expr \'outer:\l loop {\l \'inner:\l loop {\l if x == 1i { break \'outer ; \"unreachable\" }\l if y >= 2i { break ; \"unreachable\" }\l y -= 3i;\l }\l y -= 4i;\l x -= 5i;\l }\l"]; + N4[label="stmt let mut x = 15i;"]; + N5[label="expr 151i"]; + N6[label="local mut y"]; + N7[label="stmt let mut y = 151i;"]; N8[label="(dummy_node)"]; - N9[label="expr \'inner:\l loop {\l if x == 1i { break \'outer ; \"unreachable\" }\l if y >= 2i { break ; \"unreachable\" }\l y -= 3i;\l }\l"]; - N10[label="expr x"]; - N11[label="expr 1i"]; - N12[label="expr x == 1i"]; - N13[label="expr break \'outer"]; - N14[label="(dummy_node)"]; - N15[label="expr \"unreachable\""]; - N16[label="block { break \'outer ; \"unreachable\" }"]; - N17[label="expr if x == 1i { break \'outer ; \"unreachable\" }"]; - N18[label="expr y"]; - N19[label="expr 2i"]; - N20[label="expr y >= 2i"]; - N21[label="expr break"]; - N22[label="(dummy_node)"]; - N23[label="expr \"unreachable\""]; - N24[label="block { break ; \"unreachable\" }"]; - N25[label="expr if y >= 2i { break ; \"unreachable\" }"]; - N26[label="expr 3i"]; - N27[label="expr y"]; - N28[label="expr y -= 3i"]; - N29[label="block {\l if x == 1i { break \'outer ; \"unreachable\" }\l if y >= 2i { break ; \"unreachable\" }\l y -= 3i;\l}\l"]; - N30[label="expr 4i"]; - N31[label="expr y"]; - N32[label="expr y -= 4i"]; - N33[label="expr 5i"]; - N34[label="expr x"]; - N35[label="expr x -= 5i"]; - N36[label="block {\l \'inner:\l loop {\l if x == 1i { break \'outer ; \"unreachable\" }\l if y >= 2i { break ; \"unreachable\" }\l y -= 3i;\l }\l y -= 4i;\l x -= 5i;\l}\l"]; - N37[label="block {\l let mut x = 15i;\l let mut y = 151i;\l \'outer:\l loop {\l \'inner:\l loop {\l if x == 1i { break \'outer ; \"unreachable\" }\l if y >= 2i { break ; \"unreachable\" }\l y -= 3i;\l }\l y -= 4i;\l x -= 5i;\l }\l}\l"]; + N9[label="expr \'outer:\l loop {\l \'inner:\l loop {\l if x == 1i { break \'outer ; \"unreachable\"; }\l if y >= 2i { break ; \"unreachable\"; }\l y -= 3i;\l }\l y -= 4i;\l x -= 5i;\l }\l"]; + N10[label="(dummy_node)"]; + N11[label="expr \'inner:\l loop {\l if x == 1i { break \'outer ; \"unreachable\"; }\l if y >= 2i { break ; \"unreachable\"; }\l y -= 3i;\l }\l"]; + N12[label="expr x"]; + N13[label="expr 1i"]; + N14[label="expr x == 1i"]; + N15[label="expr break \'outer"]; + N16[label="(dummy_node)"]; + N17[label="stmt break \'outer ;"]; + N18[label="expr \"unreachable\""]; + N19[label="stmt \"unreachable\";"]; + N20[label="block { break \'outer ; \"unreachable\"; }"]; + N21[label="expr if x == 1i { break \'outer ; \"unreachable\"; }"]; + N22[label="stmt if x == 1i { break \'outer ; \"unreachable\"; }"]; + N23[label="expr y"]; + N24[label="expr 2i"]; + N25[label="expr y >= 2i"]; + N26[label="expr break"]; + N27[label="(dummy_node)"]; + N28[label="stmt break ;"]; + N29[label="expr \"unreachable\""]; + N30[label="stmt \"unreachable\";"]; + N31[label="block { break ; \"unreachable\"; }"]; + N32[label="expr if y >= 2i { break ; \"unreachable\"; }"]; + N33[label="stmt if y >= 2i { break ; \"unreachable\"; }"]; + N34[label="expr 3i"]; + N35[label="expr y"]; + N36[label="expr y -= 3i"]; + N37[label="stmt y -= 3i;"]; + N38[label="block {\l if x == 1i { break \'outer ; \"unreachable\"; }\l if y >= 2i { break ; \"unreachable\"; }\l y -= 3i;\l}\l"]; + N39[label="stmt \'inner:\l loop {\l if x == 1i { break \'outer ; \"unreachable\"; }\l if y >= 2i { break ; \"unreachable\"; }\l y -= 3i;\l }\l"]; + N40[label="expr 4i"]; + N41[label="expr y"]; + N42[label="expr y -= 4i"]; + N43[label="stmt y -= 4i;"]; + N44[label="expr 5i"]; + N45[label="expr x"]; + N46[label="expr x -= 5i"]; + N47[label="stmt x -= 5i;"]; + N48[label="block {\l \'inner:\l loop {\l if x == 1i { break \'outer ; \"unreachable\"; }\l if y >= 2i { break ; \"unreachable\"; }\l y -= 3i;\l }\l y -= 4i;\l x -= 5i;\l}\l"]; + N49[label="block {\l let mut x = 15i;\l let mut y = 151i;\l \'outer:\l loop {\l \'inner:\l loop {\l if x == 1i { break \'outer ; \"unreachable\"; }\l if y >= 2i { break ; \"unreachable\"; }\l y -= 3i;\l }\l y -= 4i;\l x -= 5i;\l }\l}\l"]; N0 -> N2; N2 -> N3; N3 -> N4; N4 -> N5; N5 -> N6; - N6 -> N8; + N6 -> N7; + N7 -> N8; N8 -> N10; - N10 -> N11; - N11 -> N12; + N10 -> N12; N12 -> N13; - N13 -> N7[label="exiting scope_0 expr break \'outer,\lexiting scope_1 stmt break \'outer ;,\lexiting scope_2 block { break \'outer ; \"unreachable\" },\lexiting scope_3 expr if x == 1i { break \'outer ; \"unreachable\" },\lexiting scope_4 stmt if x == 1i { break \'outer ; \"unreachable\" },\lexiting scope_5 block {\l if x == 1i { break \'outer ; \"unreachable\" }\l if y >= 2i { break ; \"unreachable\" }\l y -= 3i;\l}\l,\lexiting scope_6 expr \'inner:\l loop {\l if x == 1i { break \'outer ; \"unreachable\" }\l if y >= 2i { break ; \"unreachable\" }\l y -= 3i;\l }\l,\lexiting scope_7 stmt \'inner:\l loop {\l if x == 1i { break \'outer ; \"unreachable\" }\l if y >= 2i { break ; \"unreachable\" }\l y -= 3i;\l }\l,\lexiting scope_8 block {\l \'inner:\l loop {\l if x == 1i { break \'outer ; \"unreachable\" }\l if y >= 2i { break ; \"unreachable\" }\l y -= 3i;\l }\l y -= 4i;\l x -= 5i;\l}\l"]; + N13 -> N14; N14 -> N15; - N15 -> N16; - N12 -> N17; + N15 -> N9[label="exiting scope_0 expr break \'outer,\lexiting scope_1 stmt break \'outer ;,\lexiting scope_2 block { break \'outer ; \"unreachable\"; },\lexiting scope_3 expr if x == 1i { break \'outer ; \"unreachable\"; },\lexiting scope_4 stmt if x == 1i { break \'outer ; \"unreachable\"; },\lexiting scope_5 block {\l if x == 1i { break \'outer ; \"unreachable\"; }\l if y >= 2i { break ; \"unreachable\"; }\l y -= 3i;\l}\l,\lexiting scope_6 expr \'inner:\l loop {\l if x == 1i { break \'outer ; \"unreachable\"; }\l if y >= 2i { break ; \"unreachable\"; }\l y -= 3i;\l }\l,\lexiting scope_7 stmt \'inner:\l loop {\l if x == 1i { break \'outer ; \"unreachable\"; }\l if y >= 2i { break ; \"unreachable\"; }\l y -= 3i;\l }\l,\lexiting scope_8 block {\l \'inner:\l loop {\l if x == 1i { break \'outer ; \"unreachable\"; }\l if y >= 2i { break ; \"unreachable\"; }\l y -= 3i;\l }\l y -= 4i;\l x -= 5i;\l}\l"]; N16 -> N17; N17 -> N18; N18 -> N19; N19 -> N20; + N14 -> N21; N20 -> N21; - N21 -> N9[label="exiting scope_0 expr break,\lexiting scope_1 stmt break ;,\lexiting scope_2 block { break ; \"unreachable\" },\lexiting scope_3 expr if y >= 2i { break ; \"unreachable\" },\lexiting scope_4 stmt if y >= 2i { break ; \"unreachable\" },\lexiting scope_5 block {\l if x == 1i { break \'outer ; \"unreachable\" }\l if y >= 2i { break ; \"unreachable\" }\l y -= 3i;\l}\l"]; + N21 -> N22; N22 -> N23; N23 -> N24; - N20 -> N25; N24 -> N25; N25 -> N26; - N26 -> N27; + N26 -> N11[label="exiting scope_0 expr break,\lexiting scope_1 stmt break ;,\lexiting scope_2 block { break ; \"unreachable\"; },\lexiting scope_3 expr if y >= 2i { break ; \"unreachable\"; },\lexiting scope_4 stmt if y >= 2i { break ; \"unreachable\"; },\lexiting scope_5 block {\l if x == 1i { break \'outer ; \"unreachable\"; }\l if y >= 2i { break ; \"unreachable\"; }\l y -= 3i;\l}\l"]; N27 -> N28; N28 -> N29; - N29 -> N8; - N9 -> N30; + N29 -> N30; N30 -> N31; + N25 -> N32; N31 -> N32; N32 -> N33; N33 -> N34; N34 -> N35; N35 -> N36; - N36 -> N6; - N7 -> N37; - N37 -> N1; + N36 -> N37; + N37 -> N38; + N38 -> N10; + N11 -> N39; + N39 -> N40; + N40 -> N41; + N41 -> N42; + N42 -> N43; + N43 -> N44; + N44 -> N45; + N45 -> N46; + N46 -> N47; + N47 -> N48; + N48 -> N8; + N9 -> N49; + N49 -> N1; } diff --git a/src/test/run-make/graphviz-flowgraph/f15.rs b/src/test/run-make/graphviz-flowgraph/f15.rs index e5ca1de3f2d..62233dcb7d8 100644 --- a/src/test/run-make/graphviz-flowgraph/f15.rs +++ b/src/test/run-make/graphviz-flowgraph/f15.rs @@ -16,11 +16,11 @@ pub fn expr_break_label_15() { 'inner: loop { if x == 1i { break 'outer; - "unreachable" + "unreachable"; } if y >= 2i { break; - "unreachable" + "unreachable"; } y -= 3i; } diff --git a/src/test/run-make/graphviz-flowgraph/f16.dot-expected.dot b/src/test/run-make/graphviz-flowgraph/f16.dot-expected.dot index 9c60e19f8b0..16b871bd844 100644 --- a/src/test/run-make/graphviz-flowgraph/f16.dot-expected.dot +++ b/src/test/run-make/graphviz-flowgraph/f16.dot-expected.dot @@ -3,79 +3,107 @@ digraph block { N1[label="exit"]; N2[label="expr 16i"]; N3[label="local mut x"]; - N4[label="expr 16i"]; - N5[label="local mut y"]; - N6[label="(dummy_node)"]; - N7[label="expr \'outer:\l loop {\l \'inner:\l loop {\l if x == 1i { continue \'outer ; \"unreachable\" }\l if y >= 1i { break ; \"unreachable\" }\l y -= 1i;\l }\l y -= 1i;\l x -= 1i;\l }\l"]; + N4[label="stmt let mut x = 16i;"]; + N5[label="expr 16i"]; + N6[label="local mut y"]; + N7[label="stmt let mut y = 16i;"]; N8[label="(dummy_node)"]; - N9[label="expr \'inner:\l loop {\l if x == 1i { continue \'outer ; \"unreachable\" }\l if y >= 1i { break ; \"unreachable\" }\l y -= 1i;\l }\l"]; - N10[label="expr x"]; - N11[label="expr 1i"]; - N12[label="expr x == 1i"]; - N13[label="expr continue \'outer"]; - N14[label="(dummy_node)"]; - N15[label="expr \"unreachable\""]; - N16[label="block { continue \'outer ; \"unreachable\" }"]; - N17[label="expr if x == 1i { continue \'outer ; \"unreachable\" }"]; - N18[label="expr y"]; - N19[label="expr 1i"]; - N20[label="expr y >= 1i"]; - N21[label="expr break"]; - N22[label="(dummy_node)"]; - N23[label="expr \"unreachable\""]; - N24[label="block { break ; \"unreachable\" }"]; - N25[label="expr if y >= 1i { break ; \"unreachable\" }"]; - N26[label="expr 1i"]; - N27[label="expr y"]; - N28[label="expr y -= 1i"]; - N29[label="block {\l if x == 1i { continue \'outer ; \"unreachable\" }\l if y >= 1i { break ; \"unreachable\" }\l y -= 1i;\l}\l"]; - N30[label="expr 1i"]; - N31[label="expr y"]; - N32[label="expr y -= 1i"]; - N33[label="expr 1i"]; - N34[label="expr x"]; - N35[label="expr x -= 1i"]; - N36[label="block {\l \'inner:\l loop {\l if x == 1i { continue \'outer ; \"unreachable\" }\l if y >= 1i { break ; \"unreachable\" }\l y -= 1i;\l }\l y -= 1i;\l x -= 1i;\l}\l"]; - N37[label="expr \"unreachable\""]; - N38[label="block {\l let mut x = 16i;\l let mut y = 16i;\l \'outer:\l loop {\l \'inner:\l loop {\l if x == 1i { continue \'outer ; \"unreachable\" }\l if y >= 1i { break ; \"unreachable\" }\l y -= 1i;\l }\l y -= 1i;\l x -= 1i;\l }\l \"unreachable\";\l}\l"]; + N9[label="expr \'outer:\l loop {\l \'inner:\l loop {\l if x == 1i { continue \'outer ; \"unreachable\"; }\l if y >= 1i { break ; \"unreachable\"; }\l y -= 1i;\l }\l y -= 1i;\l x -= 1i;\l }\l"]; + N10[label="(dummy_node)"]; + N11[label="expr \'inner:\l loop {\l if x == 1i { continue \'outer ; \"unreachable\"; }\l if y >= 1i { break ; \"unreachable\"; }\l y -= 1i;\l }\l"]; + N12[label="expr x"]; + N13[label="expr 1i"]; + N14[label="expr x == 1i"]; + N15[label="expr continue \'outer"]; + N16[label="(dummy_node)"]; + N17[label="stmt continue \'outer ;"]; + N18[label="expr \"unreachable\""]; + N19[label="stmt \"unreachable\";"]; + N20[label="block { continue \'outer ; \"unreachable\"; }"]; + N21[label="expr if x == 1i { continue \'outer ; \"unreachable\"; }"]; + N22[label="stmt if x == 1i { continue \'outer ; \"unreachable\"; }"]; + N23[label="expr y"]; + N24[label="expr 1i"]; + N25[label="expr y >= 1i"]; + N26[label="expr break"]; + N27[label="(dummy_node)"]; + N28[label="stmt break ;"]; + N29[label="expr \"unreachable\""]; + N30[label="stmt \"unreachable\";"]; + N31[label="block { break ; \"unreachable\"; }"]; + N32[label="expr if y >= 1i { break ; \"unreachable\"; }"]; + N33[label="stmt if y >= 1i { break ; \"unreachable\"; }"]; + N34[label="expr 1i"]; + N35[label="expr y"]; + N36[label="expr y -= 1i"]; + N37[label="stmt y -= 1i;"]; + N38[label="block {\l if x == 1i { continue \'outer ; \"unreachable\"; }\l if y >= 1i { break ; \"unreachable\"; }\l y -= 1i;\l}\l"]; + N39[label="stmt \'inner:\l loop {\l if x == 1i { continue \'outer ; \"unreachable\"; }\l if y >= 1i { break ; \"unreachable\"; }\l y -= 1i;\l }\l"]; + N40[label="expr 1i"]; + N41[label="expr y"]; + N42[label="expr y -= 1i"]; + N43[label="stmt y -= 1i;"]; + N44[label="expr 1i"]; + N45[label="expr x"]; + N46[label="expr x -= 1i"]; + N47[label="stmt x -= 1i;"]; + N48[label="block {\l \'inner:\l loop {\l if x == 1i { continue \'outer ; \"unreachable\"; }\l if y >= 1i { break ; \"unreachable\"; }\l y -= 1i;\l }\l y -= 1i;\l x -= 1i;\l}\l"]; + N49[label="stmt \'outer:\l loop {\l \'inner:\l loop {\l if x == 1i { continue \'outer ; \"unreachable\"; }\l if y >= 1i { break ; \"unreachable\"; }\l y -= 1i;\l }\l y -= 1i;\l x -= 1i;\l }\l"]; + N50[label="expr \"unreachable\""]; + N51[label="stmt \"unreachable\";"]; + N52[label="block {\l let mut x = 16i;\l let mut y = 16i;\l \'outer:\l loop {\l \'inner:\l loop {\l if x == 1i { continue \'outer ; \"unreachable\"; }\l if y >= 1i { break ; \"unreachable\"; }\l y -= 1i;\l }\l y -= 1i;\l x -= 1i;\l }\l \"unreachable\";\l}\l"]; N0 -> N2; N2 -> N3; N3 -> N4; N4 -> N5; N5 -> N6; - N6 -> N8; + N6 -> N7; + N7 -> N8; N8 -> N10; - N10 -> N11; - N11 -> N12; + N10 -> N12; N12 -> N13; - N13 -> N6[label="exiting scope_0 expr continue \'outer,\lexiting scope_1 stmt continue \'outer ;,\lexiting scope_2 block { continue \'outer ; \"unreachable\" },\lexiting scope_3 expr if x == 1i { continue \'outer ; \"unreachable\" },\lexiting scope_4 stmt if x == 1i { continue \'outer ; \"unreachable\" },\lexiting scope_5 block {\l if x == 1i { continue \'outer ; \"unreachable\" }\l if y >= 1i { break ; \"unreachable\" }\l y -= 1i;\l}\l,\lexiting scope_6 expr \'inner:\l loop {\l if x == 1i { continue \'outer ; \"unreachable\" }\l if y >= 1i { break ; \"unreachable\" }\l y -= 1i;\l }\l,\lexiting scope_7 stmt \'inner:\l loop {\l if x == 1i { continue \'outer ; \"unreachable\" }\l if y >= 1i { break ; \"unreachable\" }\l y -= 1i;\l }\l,\lexiting scope_8 block {\l \'inner:\l loop {\l if x == 1i { continue \'outer ; \"unreachable\" }\l if y >= 1i { break ; \"unreachable\" }\l y -= 1i;\l }\l y -= 1i;\l x -= 1i;\l}\l"]; + N13 -> N14; N14 -> N15; - N15 -> N16; - N12 -> N17; + N15 -> N8[label="exiting scope_0 expr continue \'outer,\lexiting scope_1 stmt continue \'outer ;,\lexiting scope_2 block { continue \'outer ; \"unreachable\"; },\lexiting scope_3 expr if x == 1i { continue \'outer ; \"unreachable\"; },\lexiting scope_4 stmt if x == 1i { continue \'outer ; \"unreachable\"; },\lexiting scope_5 block {\l if x == 1i { continue \'outer ; \"unreachable\"; }\l if y >= 1i { break ; \"unreachable\"; }\l y -= 1i;\l}\l,\lexiting scope_6 expr \'inner:\l loop {\l if x == 1i { continue \'outer ; \"unreachable\"; }\l if y >= 1i { break ; \"unreachable\"; }\l y -= 1i;\l }\l,\lexiting scope_7 stmt \'inner:\l loop {\l if x == 1i { continue \'outer ; \"unreachable\"; }\l if y >= 1i { break ; \"unreachable\"; }\l y -= 1i;\l }\l,\lexiting scope_8 block {\l \'inner:\l loop {\l if x == 1i { continue \'outer ; \"unreachable\"; }\l if y >= 1i { break ; \"unreachable\"; }\l y -= 1i;\l }\l y -= 1i;\l x -= 1i;\l}\l"]; N16 -> N17; N17 -> N18; N18 -> N19; N19 -> N20; + N14 -> N21; N20 -> N21; - N21 -> N9[label="exiting scope_0 expr break,\lexiting scope_1 stmt break ;,\lexiting scope_2 block { break ; \"unreachable\" },\lexiting scope_3 expr if y >= 1i { break ; \"unreachable\" },\lexiting scope_4 stmt if y >= 1i { break ; \"unreachable\" },\lexiting scope_5 block {\l if x == 1i { continue \'outer ; \"unreachable\" }\l if y >= 1i { break ; \"unreachable\" }\l y -= 1i;\l}\l"]; + N21 -> N22; N22 -> N23; N23 -> N24; - N20 -> N25; N24 -> N25; N25 -> N26; - N26 -> N27; + N26 -> N11[label="exiting scope_0 expr break,\lexiting scope_1 stmt break ;,\lexiting scope_2 block { break ; \"unreachable\"; },\lexiting scope_3 expr if y >= 1i { break ; \"unreachable\"; },\lexiting scope_4 stmt if y >= 1i { break ; \"unreachable\"; },\lexiting scope_5 block {\l if x == 1i { continue \'outer ; \"unreachable\"; }\l if y >= 1i { break ; \"unreachable\"; }\l y -= 1i;\l}\l"]; N27 -> N28; N28 -> N29; - N29 -> N8; - N9 -> N30; + N29 -> N30; N30 -> N31; + N25 -> N32; N31 -> N32; N32 -> N33; N33 -> N34; N34 -> N35; N35 -> N36; - N36 -> N6; - N7 -> N37; + N36 -> N37; N37 -> N38; - N38 -> N1; + N38 -> N10; + N11 -> N39; + N39 -> N40; + N40 -> N41; + N41 -> N42; + N42 -> N43; + N43 -> N44; + N44 -> N45; + N45 -> N46; + N46 -> N47; + N47 -> N48; + N48 -> N8; + N9 -> N49; + N49 -> N50; + N50 -> N51; + N51 -> N52; + N52 -> N1; } diff --git a/src/test/run-make/graphviz-flowgraph/f16.rs b/src/test/run-make/graphviz-flowgraph/f16.rs index 78de99d28fc..2683d8bd06b 100644 --- a/src/test/run-make/graphviz-flowgraph/f16.rs +++ b/src/test/run-make/graphviz-flowgraph/f16.rs @@ -16,11 +16,11 @@ pub fn expr_continue_label_16() { 'inner: loop { if x == 1i { continue 'outer; - "unreachable" + "unreachable"; } if y >= 1i { break; - "unreachable" + "unreachable"; } y -= 1i; } diff --git a/src/test/run-make/graphviz-flowgraph/f17.dot-expected.dot b/src/test/run-make/graphviz-flowgraph/f17.dot-expected.dot index d3e098a71f2..c78224c00df 100644 --- a/src/test/run-make/graphviz-flowgraph/f17.dot-expected.dot +++ b/src/test/run-make/graphviz-flowgraph/f17.dot-expected.dot @@ -6,12 +6,14 @@ digraph block { N4[label="expr 17i"]; N5[label="expr [1i, 7i, 17i]"]; N6[label="local _v"]; - N7[label="block { let _v = [1i, 7i, 17i]; }"]; + N7[label="stmt let _v = [1i, 7i, 17i];"]; + N8[label="block { let _v = [1i, 7i, 17i]; }"]; N0 -> N2; N2 -> N3; N3 -> N4; N4 -> N5; N5 -> N6; N6 -> N7; - N7 -> N1; + N7 -> N8; + N8 -> N1; } diff --git a/src/test/run-make/graphviz-flowgraph/f18.dot-expected.dot b/src/test/run-make/graphviz-flowgraph/f18.dot-expected.dot index 6345b4effaf..c4a39a519ed 100644 --- a/src/test/run-make/graphviz-flowgraph/f18.dot-expected.dot +++ b/src/test/run-make/graphviz-flowgraph/f18.dot-expected.dot @@ -1,17 +1,21 @@ digraph block { N0[label="entry"]; N1[label="exit"]; - N2[label="expr inner"]; + N2[label="stmt fn inner(x: int) -> int { x + x }"]; N3[label="expr inner"]; - N4[label="expr 18"]; - N5[label="expr inner(18)"]; - N6[label="expr inner(inner(18))"]; - N7[label="block {\l fn inner(x: int) -> int { x + x }\l inner(inner(18));\l}\l"]; + N4[label="expr inner"]; + N5[label="expr 18"]; + N6[label="expr inner(18)"]; + N7[label="expr inner(inner(18))"]; + N8[label="stmt inner(inner(18));"]; + N9[label="block {\l fn inner(x: int) -> int { x + x }\l inner(inner(18));\l}\l"]; N0 -> N2; N2 -> N3; N3 -> N4; N4 -> N5; N5 -> N6; N6 -> N7; - N7 -> N1; + N7 -> N8; + N8 -> N9; + N9 -> N1; } diff --git a/src/test/run-make/graphviz-flowgraph/f19.dot-expected.dot b/src/test/run-make/graphviz-flowgraph/f19.dot-expected.dot index 5fad18536e5..8d21ef80917 100644 --- a/src/test/run-make/graphviz-flowgraph/f19.dot-expected.dot +++ b/src/test/run-make/graphviz-flowgraph/f19.dot-expected.dot @@ -1,13 +1,17 @@ digraph block { N0[label="entry"]; N1[label="exit"]; - N2[label="expr 19"]; - N3[label="expr S19{x: 19,}"]; - N4[label="local s"]; - N5[label="expr s"]; - N6[label="expr s.inner()"]; - N7[label="expr s.inner().inner()"]; - N8[label="block {\l struct S19 {\l x: int,\l }\l impl S19 {\l fn inner(self) -> S19 { S19{x: self.x + self.x,} }\l }\l let s = S19{x: 19,};\l s.inner().inner();\l}\l"]; + N2[label="stmt struct S19 {\l x: int,\l}\l"]; + N3[label="stmt impl S19 {\l fn inner(self) -> S19 { S19{x: self.x + self.x,} }\l}\l"]; + N4[label="expr 19"]; + N5[label="expr S19{x: 19,}"]; + N6[label="local s"]; + N7[label="stmt let s = S19{x: 19,};"]; + N8[label="expr s"]; + N9[label="expr s.inner()"]; + N10[label="expr s.inner().inner()"]; + N11[label="stmt s.inner().inner();"]; + N12[label="block {\l struct S19 {\l x: int,\l }\l impl S19 {\l fn inner(self) -> S19 { S19{x: self.x + self.x,} }\l }\l let s = S19{x: 19,};\l s.inner().inner();\l}\l"]; N0 -> N2; N2 -> N3; N3 -> N4; @@ -15,5 +19,9 @@ digraph block { N5 -> N6; N6 -> N7; N7 -> N8; - N8 -> N1; + N8 -> N9; + N9 -> N10; + N10 -> N11; + N11 -> N12; + N12 -> N1; } diff --git a/src/test/run-make/graphviz-flowgraph/f20.dot-expected.dot b/src/test/run-make/graphviz-flowgraph/f20.dot-expected.dot index 716ec469fb0..a625a1a0026 100644 --- a/src/test/run-make/graphviz-flowgraph/f20.dot-expected.dot +++ b/src/test/run-make/graphviz-flowgraph/f20.dot-expected.dot @@ -6,10 +6,12 @@ digraph block { N4[label="expr 20u"]; N5[label="expr [2u, 0u, 20u]"]; N6[label="local v"]; - N7[label="expr v"]; - N8[label="expr 20u"]; - N9[label="expr v[20u]"]; - N10[label="block { let v = [2u, 0u, 20u]; v[20u]; }"]; + N7[label="stmt let v = [2u, 0u, 20u];"]; + N8[label="expr v"]; + N9[label="expr 20u"]; + N10[label="expr v[20u]"]; + N11[label="stmt v[20u];"]; + N12[label="block { let v = [2u, 0u, 20u]; v[20u]; }"]; N0 -> N2; N2 -> N3; N3 -> N4; @@ -19,5 +21,7 @@ digraph block { N7 -> N8; N8 -> N9; N9 -> N10; - N10 -> N1; + N10 -> N11; + N11 -> N12; + N12 -> N1; } diff --git a/src/test/run-make/graphviz-flowgraph/f21.dot-expected.dot b/src/test/run-make/graphviz-flowgraph/f21.dot-expected.dot index 2bbc3e7e5c8..ad2ef60ce29 100644 --- a/src/test/run-make/graphviz-flowgraph/f21.dot-expected.dot +++ b/src/test/run-make/graphviz-flowgraph/f21.dot-expected.dot @@ -3,73 +3,97 @@ digraph block { N1[label="exit"]; N2[label="expr 15i"]; N3[label="local mut x"]; - N4[label="expr 151i"]; - N5[label="local mut y"]; - N6[label="(dummy_node)"]; - N7[label="expr \'outer:\l loop {\l \'inner:\l loop {\l if x == 1i { break \'outer ; \"unreachable\"; }\l if y >= 2i { return; \"unreachable\"; }\l y -= 3i;\l x -= 5i;\l }\l \"unreachable\";\l }\l"]; + N4[label="stmt let mut x = 15i;"]; + N5[label="expr 151i"]; + N6[label="local mut y"]; + N7[label="stmt let mut y = 151i;"]; N8[label="(dummy_node)"]; - N9[label="expr \'inner:\l loop {\l if x == 1i { break \'outer ; \"unreachable\"; }\l if y >= 2i { return; \"unreachable\"; }\l y -= 3i;\l x -= 5i;\l }\l"]; - N10[label="expr x"]; - N11[label="expr 1i"]; - N12[label="expr x == 1i"]; - N13[label="expr break \'outer"]; - N14[label="(dummy_node)"]; - N15[label="expr \"unreachable\""]; - N16[label="block { break \'outer ; \"unreachable\"; }"]; - N17[label="expr if x == 1i { break \'outer ; \"unreachable\"; }"]; - N18[label="expr y"]; - N19[label="expr 2i"]; - N20[label="expr y >= 2i"]; - N21[label="expr return"]; - N22[label="(dummy_node)"]; - N23[label="expr \"unreachable\""]; - N24[label="block { return; \"unreachable\"; }"]; - N25[label="expr if y >= 2i { return; \"unreachable\"; }"]; - N26[label="expr 3i"]; - N27[label="expr y"]; - N28[label="expr y -= 3i"]; - N29[label="expr 5i"]; - N30[label="expr x"]; - N31[label="expr x -= 5i"]; - N32[label="block {\l if x == 1i { break \'outer ; \"unreachable\"; }\l if y >= 2i { return; \"unreachable\"; }\l y -= 3i;\l x -= 5i;\l}\l"]; - N33[label="expr \"unreachable\""]; - N34[label="block {\l \'inner:\l loop {\l if x == 1i { break \'outer ; \"unreachable\"; }\l if y >= 2i { return; \"unreachable\"; }\l y -= 3i;\l x -= 5i;\l }\l \"unreachable\";\l}\l"]; - N35[label="block {\l let mut x = 15i;\l let mut y = 151i;\l \'outer:\l loop {\l \'inner:\l loop {\l if x == 1i { break \'outer ; \"unreachable\"; }\l if y >= 2i { return; \"unreachable\"; }\l y -= 3i;\l x -= 5i;\l }\l \"unreachable\";\l }\l}\l"]; + N9[label="expr \'outer:\l loop {\l \'inner:\l loop {\l if x == 1i { break \'outer ; \"unreachable\"; }\l if y >= 2i { return; \"unreachable\"; }\l y -= 3i;\l x -= 5i;\l }\l \"unreachable\";\l }\l"]; + N10[label="(dummy_node)"]; + N11[label="expr \'inner:\l loop {\l if x == 1i { break \'outer ; \"unreachable\"; }\l if y >= 2i { return; \"unreachable\"; }\l y -= 3i;\l x -= 5i;\l }\l"]; + N12[label="expr x"]; + N13[label="expr 1i"]; + N14[label="expr x == 1i"]; + N15[label="expr break \'outer"]; + N16[label="(dummy_node)"]; + N17[label="stmt break \'outer ;"]; + N18[label="expr \"unreachable\""]; + N19[label="stmt \"unreachable\";"]; + N20[label="block { break \'outer ; \"unreachable\"; }"]; + N21[label="expr if x == 1i { break \'outer ; \"unreachable\"; }"]; + N22[label="stmt if x == 1i { break \'outer ; \"unreachable\"; }"]; + N23[label="expr y"]; + N24[label="expr 2i"]; + N25[label="expr y >= 2i"]; + N26[label="expr return"]; + N27[label="(dummy_node)"]; + N28[label="stmt return;"]; + N29[label="expr \"unreachable\""]; + N30[label="stmt \"unreachable\";"]; + N31[label="block { return; \"unreachable\"; }"]; + N32[label="expr if y >= 2i { return; \"unreachable\"; }"]; + N33[label="stmt if y >= 2i { return; \"unreachable\"; }"]; + N34[label="expr 3i"]; + N35[label="expr y"]; + N36[label="expr y -= 3i"]; + N37[label="stmt y -= 3i;"]; + N38[label="expr 5i"]; + N39[label="expr x"]; + N40[label="expr x -= 5i"]; + N41[label="stmt x -= 5i;"]; + N42[label="block {\l if x == 1i { break \'outer ; \"unreachable\"; }\l if y >= 2i { return; \"unreachable\"; }\l y -= 3i;\l x -= 5i;\l}\l"]; + N43[label="stmt \'inner:\l loop {\l if x == 1i { break \'outer ; \"unreachable\"; }\l if y >= 2i { return; \"unreachable\"; }\l y -= 3i;\l x -= 5i;\l }\l"]; + N44[label="expr \"unreachable\""]; + N45[label="stmt \"unreachable\";"]; + N46[label="block {\l \'inner:\l loop {\l if x == 1i { break \'outer ; \"unreachable\"; }\l if y >= 2i { return; \"unreachable\"; }\l y -= 3i;\l x -= 5i;\l }\l \"unreachable\";\l}\l"]; + N47[label="block {\l let mut x = 15i;\l let mut y = 151i;\l \'outer:\l loop {\l \'inner:\l loop {\l if x == 1i { break \'outer ; \"unreachable\"; }\l if y >= 2i { return; \"unreachable\"; }\l y -= 3i;\l x -= 5i;\l }\l \"unreachable\";\l }\l}\l"]; N0 -> N2; N2 -> N3; N3 -> N4; N4 -> N5; N5 -> N6; - N6 -> N8; + N6 -> N7; + N7 -> N8; N8 -> N10; - N10 -> N11; - N11 -> N12; + N10 -> N12; N12 -> N13; - N13 -> N7[label="exiting scope_0 expr break \'outer,\lexiting scope_1 stmt break \'outer ;,\lexiting scope_2 block { break \'outer ; \"unreachable\"; },\lexiting scope_3 expr if x == 1i { break \'outer ; \"unreachable\"; },\lexiting scope_4 stmt if x == 1i { break \'outer ; \"unreachable\"; },\lexiting scope_5 block {\l if x == 1i { break \'outer ; \"unreachable\"; }\l if y >= 2i { return; \"unreachable\"; }\l y -= 3i;\l x -= 5i;\l}\l,\lexiting scope_6 expr \'inner:\l loop {\l if x == 1i { break \'outer ; \"unreachable\"; }\l if y >= 2i { return; \"unreachable\"; }\l y -= 3i;\l x -= 5i;\l }\l,\lexiting scope_7 stmt \'inner:\l loop {\l if x == 1i { break \'outer ; \"unreachable\"; }\l if y >= 2i { return; \"unreachable\"; }\l y -= 3i;\l x -= 5i;\l }\l,\lexiting scope_8 block {\l \'inner:\l loop {\l if x == 1i { break \'outer ; \"unreachable\"; }\l if y >= 2i { return; \"unreachable\"; }\l y -= 3i;\l x -= 5i;\l }\l \"unreachable\";\l}\l"]; + N13 -> N14; N14 -> N15; - N15 -> N16; - N12 -> N17; + N15 -> N9[label="exiting scope_0 expr break \'outer,\lexiting scope_1 stmt break \'outer ;,\lexiting scope_2 block { break \'outer ; \"unreachable\"; },\lexiting scope_3 expr if x == 1i { break \'outer ; \"unreachable\"; },\lexiting scope_4 stmt if x == 1i { break \'outer ; \"unreachable\"; },\lexiting scope_5 block {\l if x == 1i { break \'outer ; \"unreachable\"; }\l if y >= 2i { return; \"unreachable\"; }\l y -= 3i;\l x -= 5i;\l}\l,\lexiting scope_6 expr \'inner:\l loop {\l if x == 1i { break \'outer ; \"unreachable\"; }\l if y >= 2i { return; \"unreachable\"; }\l y -= 3i;\l x -= 5i;\l }\l,\lexiting scope_7 stmt \'inner:\l loop {\l if x == 1i { break \'outer ; \"unreachable\"; }\l if y >= 2i { return; \"unreachable\"; }\l y -= 3i;\l x -= 5i;\l }\l,\lexiting scope_8 block {\l \'inner:\l loop {\l if x == 1i { break \'outer ; \"unreachable\"; }\l if y >= 2i { return; \"unreachable\"; }\l y -= 3i;\l x -= 5i;\l }\l \"unreachable\";\l}\l"]; N16 -> N17; N17 -> N18; N18 -> N19; N19 -> N20; + N14 -> N21; N20 -> N21; - N21 -> N1[label="exiting scope_0 expr \'inner:\l loop {\l if x == 1i { break \'outer ; \"unreachable\"; }\l if y >= 2i { return; \"unreachable\"; }\l y -= 3i;\l x -= 5i;\l }\l,\lexiting scope_1 expr \'outer:\l loop {\l \'inner:\l loop {\l if x == 1i { break \'outer ; \"unreachable\"; }\l if y >= 2i { return; \"unreachable\"; }\l y -= 3i;\l x -= 5i;\l }\l \"unreachable\";\l }\l"]; + N21 -> N22; N22 -> N23; N23 -> N24; - N20 -> N25; N24 -> N25; N25 -> N26; - N26 -> N27; + N26 -> N1[label="exiting scope_0 expr \'inner:\l loop {\l if x == 1i { break \'outer ; \"unreachable\"; }\l if y >= 2i { return; \"unreachable\"; }\l y -= 3i;\l x -= 5i;\l }\l,\lexiting scope_1 expr \'outer:\l loop {\l \'inner:\l loop {\l if x == 1i { break \'outer ; \"unreachable\"; }\l if y >= 2i { return; \"unreachable\"; }\l y -= 3i;\l x -= 5i;\l }\l \"unreachable\";\l }\l"]; N27 -> N28; N28 -> N29; N29 -> N30; N30 -> N31; + N25 -> N32; N31 -> N32; - N32 -> N8; - N9 -> N33; + N32 -> N33; N33 -> N34; - N34 -> N6; - N7 -> N35; - N35 -> N1; + N34 -> N35; + N35 -> N36; + N36 -> N37; + N37 -> N38; + N38 -> N39; + N39 -> N40; + N40 -> N41; + N41 -> N42; + N42 -> N10; + N11 -> N43; + N43 -> N44; + N44 -> N45; + N45 -> N46; + N46 -> N8; + N9 -> N47; + N47 -> N1; } diff --git a/src/test/run-make/graphviz-flowgraph/f22.dot-expected.dot b/src/test/run-make/graphviz-flowgraph/f22.dot-expected.dot index 8ecddba21fc..dcceb5bb937 100644 --- a/src/test/run-make/graphviz-flowgraph/f22.dot-expected.dot +++ b/src/test/run-make/graphviz-flowgraph/f22.dot-expected.dot @@ -3,75 +3,103 @@ digraph block { N1[label="exit"]; N2[label="expr 15i"]; N3[label="local mut x"]; - N4[label="expr 151i"]; - N5[label="local mut y"]; - N6[label="(dummy_node)"]; - N7[label="expr \'outer:\l loop {\l \'inner:\l loop {\l if x == 1i { continue \'outer ; \"unreachable\"; }\l if y >= 2i { return; \"unreachable\"; }\l x -= 1i;\l y -= 3i;\l }\l \"unreachable\";\l }\l"]; + N4[label="stmt let mut x = 15i;"]; + N5[label="expr 151i"]; + N6[label="local mut y"]; + N7[label="stmt let mut y = 151i;"]; N8[label="(dummy_node)"]; - N9[label="expr \'inner:\l loop {\l if x == 1i { continue \'outer ; \"unreachable\"; }\l if y >= 2i { return; \"unreachable\"; }\l x -= 1i;\l y -= 3i;\l }\l"]; - N10[label="expr x"]; - N11[label="expr 1i"]; - N12[label="expr x == 1i"]; - N13[label="expr continue \'outer"]; - N14[label="(dummy_node)"]; - N15[label="expr \"unreachable\""]; - N16[label="block { continue \'outer ; \"unreachable\"; }"]; - N17[label="expr if x == 1i { continue \'outer ; \"unreachable\"; }"]; - N18[label="expr y"]; - N19[label="expr 2i"]; - N20[label="expr y >= 2i"]; - N21[label="expr return"]; - N22[label="(dummy_node)"]; - N23[label="expr \"unreachable\""]; - N24[label="block { return; \"unreachable\"; }"]; - N25[label="expr if y >= 2i { return; \"unreachable\"; }"]; - N26[label="expr 1i"]; - N27[label="expr x"]; - N28[label="expr x -= 1i"]; - N29[label="expr 3i"]; - N30[label="expr y"]; - N31[label="expr y -= 3i"]; - N32[label="block {\l if x == 1i { continue \'outer ; \"unreachable\"; }\l if y >= 2i { return; \"unreachable\"; }\l x -= 1i;\l y -= 3i;\l}\l"]; - N33[label="expr \"unreachable\""]; - N34[label="block {\l \'inner:\l loop {\l if x == 1i { continue \'outer ; \"unreachable\"; }\l if y >= 2i { return; \"unreachable\"; }\l x -= 1i;\l y -= 3i;\l }\l \"unreachable\";\l}\l"]; - N35[label="expr \"unreachable\""]; - N36[label="block {\l let mut x = 15i;\l let mut y = 151i;\l \'outer:\l loop {\l \'inner:\l loop {\l if x == 1i { continue \'outer ; \"unreachable\"; }\l if y >= 2i { return; \"unreachable\"; }\l x -= 1i;\l y -= 3i;\l }\l \"unreachable\";\l }\l \"unreachable\";\l}\l"]; + N9[label="expr \'outer:\l loop {\l \'inner:\l loop {\l if x == 1i { continue \'outer ; \"unreachable\"; }\l if y >= 2i { return; \"unreachable\"; }\l x -= 1i;\l y -= 3i;\l }\l \"unreachable\";\l }\l"]; + N10[label="(dummy_node)"]; + N11[label="expr \'inner:\l loop {\l if x == 1i { continue \'outer ; \"unreachable\"; }\l if y >= 2i { return; \"unreachable\"; }\l x -= 1i;\l y -= 3i;\l }\l"]; + N12[label="expr x"]; + N13[label="expr 1i"]; + N14[label="expr x == 1i"]; + N15[label="expr continue \'outer"]; + N16[label="(dummy_node)"]; + N17[label="stmt continue \'outer ;"]; + N18[label="expr \"unreachable\""]; + N19[label="stmt \"unreachable\";"]; + N20[label="block { continue \'outer ; \"unreachable\"; }"]; + N21[label="expr if x == 1i { continue \'outer ; \"unreachable\"; }"]; + N22[label="stmt if x == 1i { continue \'outer ; \"unreachable\"; }"]; + N23[label="expr y"]; + N24[label="expr 2i"]; + N25[label="expr y >= 2i"]; + N26[label="expr return"]; + N27[label="(dummy_node)"]; + N28[label="stmt return;"]; + N29[label="expr \"unreachable\""]; + N30[label="stmt \"unreachable\";"]; + N31[label="block { return; \"unreachable\"; }"]; + N32[label="expr if y >= 2i { return; \"unreachable\"; }"]; + N33[label="stmt if y >= 2i { return; \"unreachable\"; }"]; + N34[label="expr 1i"]; + N35[label="expr x"]; + N36[label="expr x -= 1i"]; + N37[label="stmt x -= 1i;"]; + N38[label="expr 3i"]; + N39[label="expr y"]; + N40[label="expr y -= 3i"]; + N41[label="stmt y -= 3i;"]; + N42[label="block {\l if x == 1i { continue \'outer ; \"unreachable\"; }\l if y >= 2i { return; \"unreachable\"; }\l x -= 1i;\l y -= 3i;\l}\l"]; + N43[label="stmt \'inner:\l loop {\l if x == 1i { continue \'outer ; \"unreachable\"; }\l if y >= 2i { return; \"unreachable\"; }\l x -= 1i;\l y -= 3i;\l }\l"]; + N44[label="expr \"unreachable\""]; + N45[label="stmt \"unreachable\";"]; + N46[label="block {\l \'inner:\l loop {\l if x == 1i { continue \'outer ; \"unreachable\"; }\l if y >= 2i { return; \"unreachable\"; }\l x -= 1i;\l y -= 3i;\l }\l \"unreachable\";\l}\l"]; + N47[label="stmt \'outer:\l loop {\l \'inner:\l loop {\l if x == 1i { continue \'outer ; \"unreachable\"; }\l if y >= 2i { return; \"unreachable\"; }\l x -= 1i;\l y -= 3i;\l }\l \"unreachable\";\l }\l"]; + N48[label="expr \"unreachable\""]; + N49[label="stmt \"unreachable\";"]; + N50[label="block {\l let mut x = 15i;\l let mut y = 151i;\l \'outer:\l loop {\l \'inner:\l loop {\l if x == 1i { continue \'outer ; \"unreachable\"; }\l if y >= 2i { return; \"unreachable\"; }\l x -= 1i;\l y -= 3i;\l }\l \"unreachable\";\l }\l \"unreachable\";\l}\l"]; N0 -> N2; N2 -> N3; N3 -> N4; N4 -> N5; N5 -> N6; - N6 -> N8; + N6 -> N7; + N7 -> N8; N8 -> N10; - N10 -> N11; - N11 -> N12; + N10 -> N12; N12 -> N13; - N13 -> N6[label="exiting scope_0 expr continue \'outer,\lexiting scope_1 stmt continue \'outer ;,\lexiting scope_2 block { continue \'outer ; \"unreachable\"; },\lexiting scope_3 expr if x == 1i { continue \'outer ; \"unreachable\"; },\lexiting scope_4 stmt if x == 1i { continue \'outer ; \"unreachable\"; },\lexiting scope_5 block {\l if x == 1i { continue \'outer ; \"unreachable\"; }\l if y >= 2i { return; \"unreachable\"; }\l x -= 1i;\l y -= 3i;\l}\l,\lexiting scope_6 expr \'inner:\l loop {\l if x == 1i { continue \'outer ; \"unreachable\"; }\l if y >= 2i { return; \"unreachable\"; }\l x -= 1i;\l y -= 3i;\l }\l,\lexiting scope_7 stmt \'inner:\l loop {\l if x == 1i { continue \'outer ; \"unreachable\"; }\l if y >= 2i { return; \"unreachable\"; }\l x -= 1i;\l y -= 3i;\l }\l,\lexiting scope_8 block {\l \'inner:\l loop {\l if x == 1i { continue \'outer ; \"unreachable\"; }\l if y >= 2i { return; \"unreachable\"; }\l x -= 1i;\l y -= 3i;\l }\l \"unreachable\";\l}\l"]; + N13 -> N14; N14 -> N15; - N15 -> N16; - N12 -> N17; + N15 -> N8[label="exiting scope_0 expr continue \'outer,\lexiting scope_1 stmt continue \'outer ;,\lexiting scope_2 block { continue \'outer ; \"unreachable\"; },\lexiting scope_3 expr if x == 1i { continue \'outer ; \"unreachable\"; },\lexiting scope_4 stmt if x == 1i { continue \'outer ; \"unreachable\"; },\lexiting scope_5 block {\l if x == 1i { continue \'outer ; \"unreachable\"; }\l if y >= 2i { return; \"unreachable\"; }\l x -= 1i;\l y -= 3i;\l}\l,\lexiting scope_6 expr \'inner:\l loop {\l if x == 1i { continue \'outer ; \"unreachable\"; }\l if y >= 2i { return; \"unreachable\"; }\l x -= 1i;\l y -= 3i;\l }\l,\lexiting scope_7 stmt \'inner:\l loop {\l if x == 1i { continue \'outer ; \"unreachable\"; }\l if y >= 2i { return; \"unreachable\"; }\l x -= 1i;\l y -= 3i;\l }\l,\lexiting scope_8 block {\l \'inner:\l loop {\l if x == 1i { continue \'outer ; \"unreachable\"; }\l if y >= 2i { return; \"unreachable\"; }\l x -= 1i;\l y -= 3i;\l }\l \"unreachable\";\l}\l"]; N16 -> N17; N17 -> N18; N18 -> N19; N19 -> N20; + N14 -> N21; N20 -> N21; - N21 -> N1[label="exiting scope_0 expr \'inner:\l loop {\l if x == 1i { continue \'outer ; \"unreachable\"; }\l if y >= 2i { return; \"unreachable\"; }\l x -= 1i;\l y -= 3i;\l }\l,\lexiting scope_1 expr \'outer:\l loop {\l \'inner:\l loop {\l if x == 1i { continue \'outer ; \"unreachable\"; }\l if y >= 2i { return; \"unreachable\"; }\l x -= 1i;\l y -= 3i;\l }\l \"unreachable\";\l }\l"]; + N21 -> N22; N22 -> N23; N23 -> N24; - N20 -> N25; N24 -> N25; N25 -> N26; - N26 -> N27; + N26 -> N1[label="exiting scope_0 expr \'inner:\l loop {\l if x == 1i { continue \'outer ; \"unreachable\"; }\l if y >= 2i { return; \"unreachable\"; }\l x -= 1i;\l y -= 3i;\l }\l,\lexiting scope_1 expr \'outer:\l loop {\l \'inner:\l loop {\l if x == 1i { continue \'outer ; \"unreachable\"; }\l if y >= 2i { return; \"unreachable\"; }\l x -= 1i;\l y -= 3i;\l }\l \"unreachable\";\l }\l"]; N27 -> N28; N28 -> N29; N29 -> N30; N30 -> N31; + N25 -> N32; N31 -> N32; - N32 -> N8; - N9 -> N33; + N32 -> N33; N33 -> N34; - N34 -> N6; - N7 -> N35; + N34 -> N35; N35 -> N36; - N36 -> N1; + N36 -> N37; + N37 -> N38; + N38 -> N39; + N39 -> N40; + N40 -> N41; + N41 -> N42; + N42 -> N10; + N11 -> N43; + N43 -> N44; + N44 -> N45; + N45 -> N46; + N46 -> N8; + N9 -> N47; + N47 -> N48; + N48 -> N49; + N49 -> N50; + N50 -> N1; } diff --git a/src/test/run-make/graphviz-flowgraph/f23.dot-expected.dot b/src/test/run-make/graphviz-flowgraph/f23.dot-expected.dot index 718d4687ef9..034ecfb7f20 100644 --- a/src/test/run-make/graphviz-flowgraph/f23.dot-expected.dot +++ b/src/test/run-make/graphviz-flowgraph/f23.dot-expected.dot @@ -3,46 +3,55 @@ digraph block { N1[label="exit"]; N2[label="expr 23i"]; N3[label="local mut x"]; - N4[label="expr 23i"]; - N5[label="local mut y"]; - N6[label="expr 23i"]; - N7[label="local mut z"]; - N8[label="(dummy_node)"]; - N9[label="expr x"]; - N10[label="expr 0i"]; - N11[label="expr x > 0i"]; - N12[label="expr while x > 0i {\l x -= 1i;\l while y > 0i {\l y -= 1i;\l while z > 0i { z -= 1i; }\l if x > 10i { return; \"unreachable\"; }\l }\l}\l"]; - N13[label="expr 1i"]; - N14[label="expr x"]; - N15[label="expr x -= 1i"]; - N16[label="(dummy_node)"]; - N17[label="expr y"]; - N18[label="expr 0i"]; - N19[label="expr y > 0i"]; - N20[label="expr while y > 0i {\l y -= 1i;\l while z > 0i { z -= 1i; }\l if x > 10i { return; \"unreachable\"; }\l}\l"]; - N21[label="expr 1i"]; - N22[label="expr y"]; - N23[label="expr y -= 1i"]; - N24[label="(dummy_node)"]; - N25[label="expr z"]; - N26[label="expr 0i"]; - N27[label="expr z > 0i"]; - N28[label="expr while z > 0i { z -= 1i; }"]; - N29[label="expr 1i"]; + N4[label="stmt let mut x = 23i;"]; + N5[label="expr 23i"]; + N6[label="local mut y"]; + N7[label="stmt let mut y = 23i;"]; + N8[label="expr 23i"]; + N9[label="local mut z"]; + N10[label="stmt let mut z = 23i;"]; + N11[label="(dummy_node)"]; + N12[label="expr x"]; + N13[label="expr 0i"]; + N14[label="expr x > 0i"]; + N15[label="expr while x > 0i {\l x -= 1i;\l while y > 0i {\l y -= 1i;\l while z > 0i { z -= 1i; }\l if x > 10i { return; \"unreachable\"; }\l }\l}\l"]; + N16[label="expr 1i"]; + N17[label="expr x"]; + N18[label="expr x -= 1i"]; + N19[label="stmt x -= 1i;"]; + N20[label="(dummy_node)"]; + N21[label="expr y"]; + N22[label="expr 0i"]; + N23[label="expr y > 0i"]; + N24[label="expr while y > 0i {\l y -= 1i;\l while z > 0i { z -= 1i; }\l if x > 10i { return; \"unreachable\"; }\l}\l"]; + N25[label="expr 1i"]; + N26[label="expr y"]; + N27[label="expr y -= 1i"]; + N28[label="stmt y -= 1i;"]; + N29[label="(dummy_node)"]; N30[label="expr z"]; - N31[label="expr z -= 1i"]; - N32[label="block { z -= 1i; }"]; - N33[label="expr x"]; - N34[label="expr 10i"]; - N35[label="expr x > 10i"]; - N36[label="expr return"]; - N37[label="(dummy_node)"]; - N38[label="expr \"unreachable\""]; - N39[label="block { return; \"unreachable\"; }"]; - N40[label="expr if x > 10i { return; \"unreachable\"; }"]; - N41[label="block { y -= 1i; while z > 0i { z -= 1i; } if x > 10i { return; \"unreachable\"; } }"]; - N42[label="block {\l x -= 1i;\l while y > 0i {\l y -= 1i;\l while z > 0i { z -= 1i; }\l if x > 10i { return; \"unreachable\"; }\l }\l}\l"]; - N43[label="block {\l let mut x = 23i;\l let mut y = 23i;\l let mut z = 23i;\l while x > 0i {\l x -= 1i;\l while y > 0i {\l y -= 1i;\l while z > 0i { z -= 1i; }\l if x > 10i { return; \"unreachable\"; }\l }\l }\l}\l"]; + N31[label="expr 0i"]; + N32[label="expr z > 0i"]; + N33[label="expr while z > 0i { z -= 1i; }"]; + N34[label="expr 1i"]; + N35[label="expr z"]; + N36[label="expr z -= 1i"]; + N37[label="stmt z -= 1i;"]; + N38[label="block { z -= 1i; }"]; + N39[label="stmt while z > 0i { z -= 1i; }"]; + N40[label="expr x"]; + N41[label="expr 10i"]; + N42[label="expr x > 10i"]; + N43[label="expr return"]; + N44[label="(dummy_node)"]; + N45[label="stmt return;"]; + N46[label="expr \"unreachable\""]; + N47[label="stmt \"unreachable\";"]; + N48[label="block { return; \"unreachable\"; }"]; + N49[label="expr if x > 10i { return; \"unreachable\"; }"]; + N50[label="block { y -= 1i; while z > 0i { z -= 1i; } if x > 10i { return; \"unreachable\"; } }"]; + N51[label="block {\l x -= 1i;\l while y > 0i {\l y -= 1i;\l while z > 0i { z -= 1i; }\l if x > 10i { return; \"unreachable\"; }\l }\l}\l"]; + N52[label="block {\l let mut x = 23i;\l let mut y = 23i;\l let mut z = 23i;\l while x > 0i {\l x -= 1i;\l while y > 0i {\l y -= 1i;\l while z > 0i { z -= 1i; }\l if x > 10i { return; \"unreachable\"; }\l }\l }\l}\l"]; N0 -> N2; N2 -> N3; N3 -> N4; @@ -54,40 +63,49 @@ digraph block { N9 -> N10; N10 -> N11; N11 -> N12; - N11 -> N13; + N12 -> N13; N13 -> N14; N14 -> N15; - N15 -> N16; + N14 -> N16; N16 -> N17; N17 -> N18; N18 -> N19; N19 -> N20; - N19 -> N21; + N20 -> N21; N21 -> N22; N22 -> N23; N23 -> N24; - N24 -> N25; + N23 -> N25; N25 -> N26; N26 -> N27; N27 -> N28; - N27 -> N29; + N28 -> N29; N29 -> N30; N30 -> N31; N31 -> N32; - N32 -> N24; - N28 -> N33; - N33 -> N34; + N32 -> N33; + N32 -> N34; N34 -> N35; N35 -> N36; - N36 -> N1[label="exiting scope_0 expr while y > 0i {\l y -= 1i;\l while z > 0i { z -= 1i; }\l if x > 10i { return; \"unreachable\"; }\l}\l,\lexiting scope_1 expr while x > 0i {\l x -= 1i;\l while y > 0i {\l y -= 1i;\l while z > 0i { z -= 1i; }\l if x > 10i { return; \"unreachable\"; }\l }\l}\l"]; + N36 -> N37; N37 -> N38; - N38 -> N39; - N35 -> N40; + N38 -> N29; + N33 -> N39; N39 -> N40; N40 -> N41; - N41 -> N16; - N20 -> N42; - N42 -> N8; - N12 -> N43; - N43 -> N1; + N41 -> N42; + N42 -> N43; + N43 -> N1[label="exiting scope_0 expr while y > 0i {\l y -= 1i;\l while z > 0i { z -= 1i; }\l if x > 10i { return; \"unreachable\"; }\l}\l,\lexiting scope_1 expr while x > 0i {\l x -= 1i;\l while y > 0i {\l y -= 1i;\l while z > 0i { z -= 1i; }\l if x > 10i { return; \"unreachable\"; }\l }\l}\l"]; + N44 -> N45; + N45 -> N46; + N46 -> N47; + N47 -> N48; + N42 -> N49; + N48 -> N49; + N49 -> N50; + N50 -> N20; + N24 -> N51; + N51 -> N11; + N15 -> N52; + N52 -> N1; } diff --git a/src/test/run-make/graphviz-flowgraph/f24.dot-expected.dot b/src/test/run-make/graphviz-flowgraph/f24.dot-expected.dot index 646d98a54a7..ddb5b865c2e 100644 --- a/src/test/run-make/graphviz-flowgraph/f24.dot-expected.dot +++ b/src/test/run-make/graphviz-flowgraph/f24.dot-expected.dot @@ -3,61 +3,79 @@ digraph block { N1[label="exit"]; N2[label="expr 24i"]; N3[label="local mut x"]; - N4[label="expr 24i"]; - N5[label="local mut y"]; - N6[label="expr 24i"]; - N7[label="local mut z"]; - N8[label="(dummy_node)"]; - N9[label="expr loop {\l if x == 0i { break ; \"unreachable\"; }\l x -= 1i;\l loop {\l if y == 0i { break ; \"unreachable\"; }\l y -= 1i;\l loop { if z == 0i { break ; \"unreachable\"; } z -= 1i; }\l if x > 10i { return; \"unreachable\"; }\l }\l}\l"]; - N10[label="expr x"]; - N11[label="expr 0i"]; - N12[label="expr x == 0i"]; - N13[label="expr break"]; - N14[label="(dummy_node)"]; - N15[label="expr \"unreachable\""]; - N16[label="block { break ; \"unreachable\"; }"]; - N17[label="expr if x == 0i { break ; \"unreachable\"; }"]; - N18[label="expr 1i"]; - N19[label="expr x"]; - N20[label="expr x -= 1i"]; - N21[label="(dummy_node)"]; - N22[label="expr loop {\l if y == 0i { break ; \"unreachable\"; }\l y -= 1i;\l loop { if z == 0i { break ; \"unreachable\"; } z -= 1i; }\l if x > 10i { return; \"unreachable\"; }\l}\l"]; - N23[label="expr y"]; - N24[label="expr 0i"]; - N25[label="expr y == 0i"]; - N26[label="expr break"]; - N27[label="(dummy_node)"]; - N28[label="expr \"unreachable\""]; - N29[label="block { break ; \"unreachable\"; }"]; - N30[label="expr if y == 0i { break ; \"unreachable\"; }"]; - N31[label="expr 1i"]; - N32[label="expr y"]; - N33[label="expr y -= 1i"]; + N4[label="stmt let mut x = 24i;"]; + N5[label="expr 24i"]; + N6[label="local mut y"]; + N7[label="stmt let mut y = 24i;"]; + N8[label="expr 24i"]; + N9[label="local mut z"]; + N10[label="stmt let mut z = 24i;"]; + N11[label="(dummy_node)"]; + N12[label="expr loop {\l if x == 0i { break ; \"unreachable\"; }\l x -= 1i;\l loop {\l if y == 0i { break ; \"unreachable\"; }\l y -= 1i;\l loop { if z == 0i { break ; \"unreachable\"; } z -= 1i; }\l if x > 10i { return; \"unreachable\"; }\l }\l}\l"]; + N13[label="expr x"]; + N14[label="expr 0i"]; + N15[label="expr x == 0i"]; + N16[label="expr break"]; + N17[label="(dummy_node)"]; + N18[label="stmt break ;"]; + N19[label="expr \"unreachable\""]; + N20[label="stmt \"unreachable\";"]; + N21[label="block { break ; \"unreachable\"; }"]; + N22[label="expr if x == 0i { break ; \"unreachable\"; }"]; + N23[label="stmt if x == 0i { break ; \"unreachable\"; }"]; + N24[label="expr 1i"]; + N25[label="expr x"]; + N26[label="expr x -= 1i"]; + N27[label="stmt x -= 1i;"]; + N28[label="(dummy_node)"]; + N29[label="expr loop {\l if y == 0i { break ; \"unreachable\"; }\l y -= 1i;\l loop { if z == 0i { break ; \"unreachable\"; } z -= 1i; }\l if x > 10i { return; \"unreachable\"; }\l}\l"]; + N30[label="expr y"]; + N31[label="expr 0i"]; + N32[label="expr y == 0i"]; + N33[label="expr break"]; N34[label="(dummy_node)"]; - N35[label="expr loop { if z == 0i { break ; \"unreachable\"; } z -= 1i; }"]; - N36[label="expr z"]; - N37[label="expr 0i"]; - N38[label="expr z == 0i"]; - N39[label="expr break"]; - N40[label="(dummy_node)"]; - N41[label="expr \"unreachable\""]; - N42[label="block { break ; \"unreachable\"; }"]; - N43[label="expr if z == 0i { break ; \"unreachable\"; }"]; - N44[label="expr 1i"]; - N45[label="expr z"]; - N46[label="expr z -= 1i"]; - N47[label="block { if z == 0i { break ; \"unreachable\"; } z -= 1i; }"]; - N48[label="expr x"]; - N49[label="expr 10i"]; - N50[label="expr x > 10i"]; - N51[label="expr return"]; - N52[label="(dummy_node)"]; + N35[label="stmt break ;"]; + N36[label="expr \"unreachable\""]; + N37[label="stmt \"unreachable\";"]; + N38[label="block { break ; \"unreachable\"; }"]; + N39[label="expr if y == 0i { break ; \"unreachable\"; }"]; + N40[label="stmt if y == 0i { break ; \"unreachable\"; }"]; + N41[label="expr 1i"]; + N42[label="expr y"]; + N43[label="expr y -= 1i"]; + N44[label="stmt y -= 1i;"]; + N45[label="(dummy_node)"]; + N46[label="expr loop { if z == 0i { break ; \"unreachable\"; } z -= 1i; }"]; + N47[label="expr z"]; + N48[label="expr 0i"]; + N49[label="expr z == 0i"]; + N50[label="expr break"]; + N51[label="(dummy_node)"]; + N52[label="stmt break ;"]; N53[label="expr \"unreachable\""]; - N54[label="block { return; \"unreachable\"; }"]; - N55[label="expr if x > 10i { return; \"unreachable\"; }"]; - N56[label="block {\l if y == 0i { break ; \"unreachable\"; }\l y -= 1i;\l loop { if z == 0i { break ; \"unreachable\"; } z -= 1i; }\l if x > 10i { return; \"unreachable\"; }\l}\l"]; - N57[label="block {\l if x == 0i { break ; \"unreachable\"; }\l x -= 1i;\l loop {\l if y == 0i { break ; \"unreachable\"; }\l y -= 1i;\l loop { if z == 0i { break ; \"unreachable\"; } z -= 1i; }\l if x > 10i { return; \"unreachable\"; }\l }\l}\l"]; - N58[label="block {\l let mut x = 24i;\l let mut y = 24i;\l let mut z = 24i;\l loop {\l if x == 0i { break ; \"unreachable\"; }\l x -= 1i;\l loop {\l if y == 0i { break ; \"unreachable\"; }\l y -= 1i;\l loop { if z == 0i { break ; \"unreachable\"; } z -= 1i; }\l if x > 10i { return; \"unreachable\"; }\l }\l }\l}\l"]; + N54[label="stmt \"unreachable\";"]; + N55[label="block { break ; \"unreachable\"; }"]; + N56[label="expr if z == 0i { break ; \"unreachable\"; }"]; + N57[label="stmt if z == 0i { break ; \"unreachable\"; }"]; + N58[label="expr 1i"]; + N59[label="expr z"]; + N60[label="expr z -= 1i"]; + N61[label="stmt z -= 1i;"]; + N62[label="block { if z == 0i { break ; \"unreachable\"; } z -= 1i; }"]; + N63[label="stmt loop { if z == 0i { break ; \"unreachable\"; } z -= 1i; }"]; + N64[label="expr x"]; + N65[label="expr 10i"]; + N66[label="expr x > 10i"]; + N67[label="expr return"]; + N68[label="(dummy_node)"]; + N69[label="stmt return;"]; + N70[label="expr \"unreachable\""]; + N71[label="stmt \"unreachable\";"]; + N72[label="block { return; \"unreachable\"; }"]; + N73[label="expr if x > 10i { return; \"unreachable\"; }"]; + N74[label="block {\l if y == 0i { break ; \"unreachable\"; }\l y -= 1i;\l loop { if z == 0i { break ; \"unreachable\"; } z -= 1i; }\l if x > 10i { return; \"unreachable\"; }\l}\l"]; + N75[label="block {\l if x == 0i { break ; \"unreachable\"; }\l x -= 1i;\l loop {\l if y == 0i { break ; \"unreachable\"; }\l y -= 1i;\l loop { if z == 0i { break ; \"unreachable\"; } z -= 1i; }\l if x > 10i { return; \"unreachable\"; }\l }\l}\l"]; + N76[label="block {\l let mut x = 24i;\l let mut y = 24i;\l let mut z = 24i;\l loop {\l if x == 0i { break ; \"unreachable\"; }\l x -= 1i;\l loop {\l if y == 0i { break ; \"unreachable\"; }\l y -= 1i;\l loop { if z == 0i { break ; \"unreachable\"; } z -= 1i; }\l if x > 10i { return; \"unreachable\"; }\l }\l }\l}\l"]; N0 -> N2; N2 -> N3; N3 -> N4; @@ -65,59 +83,77 @@ digraph block { N5 -> N6; N6 -> N7; N7 -> N8; - N8 -> N10; + N8 -> N9; + N9 -> N10; N10 -> N11; - N11 -> N12; - N12 -> N13; - N13 -> N9[label="exiting scope_0 expr break,\lexiting scope_1 stmt break ;,\lexiting scope_2 block { break ; \"unreachable\"; },\lexiting scope_3 expr if x == 0i { break ; \"unreachable\"; },\lexiting scope_4 stmt if x == 0i { break ; \"unreachable\"; },\lexiting scope_5 block {\l if x == 0i { break ; \"unreachable\"; }\l x -= 1i;\l loop {\l if y == 0i { break ; \"unreachable\"; }\l y -= 1i;\l loop { if z == 0i { break ; \"unreachable\"; } z -= 1i; }\l if x > 10i { return; \"unreachable\"; }\l }\l}\l"]; + N11 -> N13; + N13 -> N14; N14 -> N15; N15 -> N16; - N12 -> N17; - N16 -> N17; + N16 -> N12[label="exiting scope_0 expr break,\lexiting scope_1 stmt break ;,\lexiting scope_2 block { break ; \"unreachable\"; },\lexiting scope_3 expr if x == 0i { break ; \"unreachable\"; },\lexiting scope_4 stmt if x == 0i { break ; \"unreachable\"; },\lexiting scope_5 block {\l if x == 0i { break ; \"unreachable\"; }\l x -= 1i;\l loop {\l if y == 0i { break ; \"unreachable\"; }\l y -= 1i;\l loop { if z == 0i { break ; \"unreachable\"; } z -= 1i; }\l if x > 10i { return; \"unreachable\"; }\l }\l}\l"]; N17 -> N18; N18 -> N19; N19 -> N20; N20 -> N21; - N21 -> N23; + N15 -> N22; + N21 -> N22; + N22 -> N23; N23 -> N24; N24 -> N25; N25 -> N26; - N26 -> N22[label="exiting scope_0 expr break,\lexiting scope_1 stmt break ;,\lexiting scope_2 block { break ; \"unreachable\"; },\lexiting scope_3 expr if y == 0i { break ; \"unreachable\"; },\lexiting scope_4 stmt if y == 0i { break ; \"unreachable\"; },\lexiting scope_5 block {\l if y == 0i { break ; \"unreachable\"; }\l y -= 1i;\l loop { if z == 0i { break ; \"unreachable\"; } z -= 1i; }\l if x > 10i { return; \"unreachable\"; }\l}\l"]; + N26 -> N27; N27 -> N28; - N28 -> N29; - N25 -> N30; - N29 -> N30; + N28 -> N30; N30 -> N31; N31 -> N32; N32 -> N33; - N33 -> N34; - N34 -> N36; + N33 -> N29[label="exiting scope_0 expr break,\lexiting scope_1 stmt break ;,\lexiting scope_2 block { break ; \"unreachable\"; },\lexiting scope_3 expr if y == 0i { break ; \"unreachable\"; },\lexiting scope_4 stmt if y == 0i { break ; \"unreachable\"; },\lexiting scope_5 block {\l if y == 0i { break ; \"unreachable\"; }\l y -= 1i;\l loop { if z == 0i { break ; \"unreachable\"; } z -= 1i; }\l if x > 10i { return; \"unreachable\"; }\l}\l"]; + N34 -> N35; + N35 -> N36; N36 -> N37; N37 -> N38; + N32 -> N39; N38 -> N39; - N39 -> N35[label="exiting scope_0 expr break,\lexiting scope_1 stmt break ;,\lexiting scope_2 block { break ; \"unreachable\"; },\lexiting scope_3 expr if z == 0i { break ; \"unreachable\"; },\lexiting scope_4 stmt if z == 0i { break ; \"unreachable\"; },\lexiting scope_5 block { if z == 0i { break ; \"unreachable\"; } z -= 1i; }"]; + N39 -> N40; N40 -> N41; N41 -> N42; - N38 -> N43; N42 -> N43; N43 -> N44; N44 -> N45; - N45 -> N46; - N46 -> N47; - N47 -> N34; - N35 -> N48; + N45 -> N47; + N47 -> N48; N48 -> N49; N49 -> N50; - N50 -> N51; - N51 -> N1[label="exiting scope_0 expr loop {\l if y == 0i { break ; \"unreachable\"; }\l y -= 1i;\l loop { if z == 0i { break ; \"unreachable\"; } z -= 1i; }\l if x > 10i { return; \"unreachable\"; }\l}\l,\lexiting scope_1 expr loop {\l if x == 0i { break ; \"unreachable\"; }\l x -= 1i;\l loop {\l if y == 0i { break ; \"unreachable\"; }\l y -= 1i;\l loop { if z == 0i { break ; \"unreachable\"; } z -= 1i; }\l if x > 10i { return; \"unreachable\"; }\l }\l}\l"]; + N50 -> N46[label="exiting scope_0 expr break,\lexiting scope_1 stmt break ;,\lexiting scope_2 block { break ; \"unreachable\"; },\lexiting scope_3 expr if z == 0i { break ; \"unreachable\"; },\lexiting scope_4 stmt if z == 0i { break ; \"unreachable\"; },\lexiting scope_5 block { if z == 0i { break ; \"unreachable\"; } z -= 1i; }"]; + N51 -> N52; N52 -> N53; N53 -> N54; - N50 -> N55; N54 -> N55; + N49 -> N56; N55 -> N56; - N56 -> N21; - N22 -> N57; - N57 -> N8; - N9 -> N58; - N58 -> N1; + N56 -> N57; + N57 -> N58; + N58 -> N59; + N59 -> N60; + N60 -> N61; + N61 -> N62; + N62 -> N45; + N46 -> N63; + N63 -> N64; + N64 -> N65; + N65 -> N66; + N66 -> N67; + N67 -> N1[label="exiting scope_0 expr loop {\l if y == 0i { break ; \"unreachable\"; }\l y -= 1i;\l loop { if z == 0i { break ; \"unreachable\"; } z -= 1i; }\l if x > 10i { return; \"unreachable\"; }\l}\l,\lexiting scope_1 expr loop {\l if x == 0i { break ; \"unreachable\"; }\l x -= 1i;\l loop {\l if y == 0i { break ; \"unreachable\"; }\l y -= 1i;\l loop { if z == 0i { break ; \"unreachable\"; } z -= 1i; }\l if x > 10i { return; \"unreachable\"; }\l }\l}\l"]; + N68 -> N69; + N69 -> N70; + N70 -> N71; + N71 -> N72; + N66 -> N73; + N72 -> N73; + N73 -> N74; + N74 -> N28; + N29 -> N75; + N75 -> N11; + N12 -> N76; + N76 -> N1; } diff --git a/src/test/run-make/graphviz-flowgraph/f25.dot-expected.dot b/src/test/run-make/graphviz-flowgraph/f25.dot-expected.dot index 11b9c7ef05e..9fd4dbfc395 100644 --- a/src/test/run-make/graphviz-flowgraph/f25.dot-expected.dot +++ b/src/test/run-make/graphviz-flowgraph/f25.dot-expected.dot @@ -3,61 +3,79 @@ digraph block { N1[label="exit"]; N2[label="expr 25i"]; N3[label="local mut x"]; - N4[label="expr 25i"]; - N5[label="local mut y"]; - N6[label="expr 25i"]; - N7[label="local mut z"]; - N8[label="(dummy_node)"]; - N9[label="expr \'a:\l loop {\l if x == 0i { break ; \"unreachable\"; }\l x -= 1i;\l \'a:\l loop {\l if y == 0i { break ; \"unreachable\"; }\l y -= 1i;\l \'a: loop { if z == 0i { break ; \"unreachable\"; } z -= 1i; }\l if x > 10i { continue \'a ; \"unreachable\"; }\l }\l }\l"]; - N10[label="expr x"]; - N11[label="expr 0i"]; - N12[label="expr x == 0i"]; - N13[label="expr break"]; - N14[label="(dummy_node)"]; - N15[label="expr \"unreachable\""]; - N16[label="block { break ; \"unreachable\"; }"]; - N17[label="expr if x == 0i { break ; \"unreachable\"; }"]; - N18[label="expr 1i"]; - N19[label="expr x"]; - N20[label="expr x -= 1i"]; - N21[label="(dummy_node)"]; - N22[label="expr \'a:\l loop {\l if y == 0i { break ; \"unreachable\"; }\l y -= 1i;\l \'a: loop { if z == 0i { break ; \"unreachable\"; } z -= 1i; }\l if x > 10i { continue \'a ; \"unreachable\"; }\l }\l"]; - N23[label="expr y"]; - N24[label="expr 0i"]; - N25[label="expr y == 0i"]; - N26[label="expr break"]; - N27[label="(dummy_node)"]; - N28[label="expr \"unreachable\""]; - N29[label="block { break ; \"unreachable\"; }"]; - N30[label="expr if y == 0i { break ; \"unreachable\"; }"]; - N31[label="expr 1i"]; - N32[label="expr y"]; - N33[label="expr y -= 1i"]; + N4[label="stmt let mut x = 25i;"]; + N5[label="expr 25i"]; + N6[label="local mut y"]; + N7[label="stmt let mut y = 25i;"]; + N8[label="expr 25i"]; + N9[label="local mut z"]; + N10[label="stmt let mut z = 25i;"]; + N11[label="(dummy_node)"]; + N12[label="expr \'a:\l loop {\l if x == 0i { break ; \"unreachable\"; }\l x -= 1i;\l \'a:\l loop {\l if y == 0i { break ; \"unreachable\"; }\l y -= 1i;\l \'a: loop { if z == 0i { break ; \"unreachable\"; } z -= 1i; }\l if x > 10i { continue \'a ; \"unreachable\"; }\l }\l }\l"]; + N13[label="expr x"]; + N14[label="expr 0i"]; + N15[label="expr x == 0i"]; + N16[label="expr break"]; + N17[label="(dummy_node)"]; + N18[label="stmt break ;"]; + N19[label="expr \"unreachable\""]; + N20[label="stmt \"unreachable\";"]; + N21[label="block { break ; \"unreachable\"; }"]; + N22[label="expr if x == 0i { break ; \"unreachable\"; }"]; + N23[label="stmt if x == 0i { break ; \"unreachable\"; }"]; + N24[label="expr 1i"]; + N25[label="expr x"]; + N26[label="expr x -= 1i"]; + N27[label="stmt x -= 1i;"]; + N28[label="(dummy_node)"]; + N29[label="expr \'a:\l loop {\l if y == 0i { break ; \"unreachable\"; }\l y -= 1i;\l \'a: loop { if z == 0i { break ; \"unreachable\"; } z -= 1i; }\l if x > 10i { continue \'a ; \"unreachable\"; }\l }\l"]; + N30[label="expr y"]; + N31[label="expr 0i"]; + N32[label="expr y == 0i"]; + N33[label="expr break"]; N34[label="(dummy_node)"]; - N35[label="expr \'a: loop { if z == 0i { break ; \"unreachable\"; } z -= 1i; }"]; - N36[label="expr z"]; - N37[label="expr 0i"]; - N38[label="expr z == 0i"]; - N39[label="expr break"]; - N40[label="(dummy_node)"]; - N41[label="expr \"unreachable\""]; - N42[label="block { break ; \"unreachable\"; }"]; - N43[label="expr if z == 0i { break ; \"unreachable\"; }"]; - N44[label="expr 1i"]; - N45[label="expr z"]; - N46[label="expr z -= 1i"]; - N47[label="block { if z == 0i { break ; \"unreachable\"; } z -= 1i; }"]; - N48[label="expr x"]; - N49[label="expr 10i"]; - N50[label="expr x > 10i"]; - N51[label="expr continue \'a"]; - N52[label="(dummy_node)"]; + N35[label="stmt break ;"]; + N36[label="expr \"unreachable\""]; + N37[label="stmt \"unreachable\";"]; + N38[label="block { break ; \"unreachable\"; }"]; + N39[label="expr if y == 0i { break ; \"unreachable\"; }"]; + N40[label="stmt if y == 0i { break ; \"unreachable\"; }"]; + N41[label="expr 1i"]; + N42[label="expr y"]; + N43[label="expr y -= 1i"]; + N44[label="stmt y -= 1i;"]; + N45[label="(dummy_node)"]; + N46[label="expr \'a: loop { if z == 0i { break ; \"unreachable\"; } z -= 1i; }"]; + N47[label="expr z"]; + N48[label="expr 0i"]; + N49[label="expr z == 0i"]; + N50[label="expr break"]; + N51[label="(dummy_node)"]; + N52[label="stmt break ;"]; N53[label="expr \"unreachable\""]; - N54[label="block { continue \'a ; \"unreachable\"; }"]; - N55[label="expr if x > 10i { continue \'a ; \"unreachable\"; }"]; - N56[label="block {\l if y == 0i { break ; \"unreachable\"; }\l y -= 1i;\l \'a: loop { if z == 0i { break ; \"unreachable\"; } z -= 1i; }\l if x > 10i { continue \'a ; \"unreachable\"; }\l}\l"]; - N57[label="block {\l if x == 0i { break ; \"unreachable\"; }\l x -= 1i;\l \'a:\l loop {\l if y == 0i { break ; \"unreachable\"; }\l y -= 1i;\l \'a: loop { if z == 0i { break ; \"unreachable\"; } z -= 1i; }\l if x > 10i { continue \'a ; \"unreachable\"; }\l }\l}\l"]; - N58[label="block {\l let mut x = 25i;\l let mut y = 25i;\l let mut z = 25i;\l \'a:\l loop {\l if x == 0i { break ; \"unreachable\"; }\l x -= 1i;\l \'a:\l loop {\l if y == 0i { break ; \"unreachable\"; }\l y -= 1i;\l \'a:\l loop {\l if z == 0i { break ; \"unreachable\"; }\l z -= 1i;\l }\l if x > 10i { continue \'a ; \"unreachable\"; }\l }\l }\l}\l"]; + N54[label="stmt \"unreachable\";"]; + N55[label="block { break ; \"unreachable\"; }"]; + N56[label="expr if z == 0i { break ; \"unreachable\"; }"]; + N57[label="stmt if z == 0i { break ; \"unreachable\"; }"]; + N58[label="expr 1i"]; + N59[label="expr z"]; + N60[label="expr z -= 1i"]; + N61[label="stmt z -= 1i;"]; + N62[label="block { if z == 0i { break ; \"unreachable\"; } z -= 1i; }"]; + N63[label="stmt \'a: loop { if z == 0i { break ; \"unreachable\"; } z -= 1i; }"]; + N64[label="expr x"]; + N65[label="expr 10i"]; + N66[label="expr x > 10i"]; + N67[label="expr continue \'a"]; + N68[label="(dummy_node)"]; + N69[label="stmt continue \'a ;"]; + N70[label="expr \"unreachable\""]; + N71[label="stmt \"unreachable\";"]; + N72[label="block { continue \'a ; \"unreachable\"; }"]; + N73[label="expr if x > 10i { continue \'a ; \"unreachable\"; }"]; + N74[label="block {\l if y == 0i { break ; \"unreachable\"; }\l y -= 1i;\l \'a: loop { if z == 0i { break ; \"unreachable\"; } z -= 1i; }\l if x > 10i { continue \'a ; \"unreachable\"; }\l}\l"]; + N75[label="block {\l if x == 0i { break ; \"unreachable\"; }\l x -= 1i;\l \'a:\l loop {\l if y == 0i { break ; \"unreachable\"; }\l y -= 1i;\l \'a: loop { if z == 0i { break ; \"unreachable\"; } z -= 1i; }\l if x > 10i { continue \'a ; \"unreachable\"; }\l }\l}\l"]; + N76[label="block {\l let mut x = 25i;\l let mut y = 25i;\l let mut z = 25i;\l \'a:\l loop {\l if x == 0i { break ; \"unreachable\"; }\l x -= 1i;\l \'a:\l loop {\l if y == 0i { break ; \"unreachable\"; }\l y -= 1i;\l \'a:\l loop {\l if z == 0i { break ; \"unreachable\"; }\l z -= 1i;\l }\l if x > 10i { continue \'a ; \"unreachable\"; }\l }\l }\l}\l"]; N0 -> N2; N2 -> N3; N3 -> N4; @@ -65,59 +83,77 @@ digraph block { N5 -> N6; N6 -> N7; N7 -> N8; - N8 -> N10; + N8 -> N9; + N9 -> N10; N10 -> N11; - N11 -> N12; - N12 -> N13; - N13 -> N9[label="exiting scope_0 expr break,\lexiting scope_1 stmt break ;,\lexiting scope_2 block { break ; \"unreachable\"; },\lexiting scope_3 expr if x == 0i { break ; \"unreachable\"; },\lexiting scope_4 stmt if x == 0i { break ; \"unreachable\"; },\lexiting scope_5 block {\l if x == 0i { break ; \"unreachable\"; }\l x -= 1i;\l \'a:\l loop {\l if y == 0i { break ; \"unreachable\"; }\l y -= 1i;\l \'a: loop { if z == 0i { break ; \"unreachable\"; } z -= 1i; }\l if x > 10i { continue \'a ; \"unreachable\"; }\l }\l}\l"]; + N11 -> N13; + N13 -> N14; N14 -> N15; N15 -> N16; - N12 -> N17; - N16 -> N17; + N16 -> N12[label="exiting scope_0 expr break,\lexiting scope_1 stmt break ;,\lexiting scope_2 block { break ; \"unreachable\"; },\lexiting scope_3 expr if x == 0i { break ; \"unreachable\"; },\lexiting scope_4 stmt if x == 0i { break ; \"unreachable\"; },\lexiting scope_5 block {\l if x == 0i { break ; \"unreachable\"; }\l x -= 1i;\l \'a:\l loop {\l if y == 0i { break ; \"unreachable\"; }\l y -= 1i;\l \'a: loop { if z == 0i { break ; \"unreachable\"; } z -= 1i; }\l if x > 10i { continue \'a ; \"unreachable\"; }\l }\l}\l"]; N17 -> N18; N18 -> N19; N19 -> N20; N20 -> N21; - N21 -> N23; + N15 -> N22; + N21 -> N22; + N22 -> N23; N23 -> N24; N24 -> N25; N25 -> N26; - N26 -> N22[label="exiting scope_0 expr break,\lexiting scope_1 stmt break ;,\lexiting scope_2 block { break ; \"unreachable\"; },\lexiting scope_3 expr if y == 0i { break ; \"unreachable\"; },\lexiting scope_4 stmt if y == 0i { break ; \"unreachable\"; },\lexiting scope_5 block {\l if y == 0i { break ; \"unreachable\"; }\l y -= 1i;\l \'a: loop { if z == 0i { break ; \"unreachable\"; } z -= 1i; }\l if x > 10i { continue \'a ; \"unreachable\"; }\l}\l"]; + N26 -> N27; N27 -> N28; - N28 -> N29; - N25 -> N30; - N29 -> N30; + N28 -> N30; N30 -> N31; N31 -> N32; N32 -> N33; - N33 -> N34; - N34 -> N36; + N33 -> N29[label="exiting scope_0 expr break,\lexiting scope_1 stmt break ;,\lexiting scope_2 block { break ; \"unreachable\"; },\lexiting scope_3 expr if y == 0i { break ; \"unreachable\"; },\lexiting scope_4 stmt if y == 0i { break ; \"unreachable\"; },\lexiting scope_5 block {\l if y == 0i { break ; \"unreachable\"; }\l y -= 1i;\l \'a: loop { if z == 0i { break ; \"unreachable\"; } z -= 1i; }\l if x > 10i { continue \'a ; \"unreachable\"; }\l}\l"]; + N34 -> N35; + N35 -> N36; N36 -> N37; N37 -> N38; + N32 -> N39; N38 -> N39; - N39 -> N35[label="exiting scope_0 expr break,\lexiting scope_1 stmt break ;,\lexiting scope_2 block { break ; \"unreachable\"; },\lexiting scope_3 expr if z == 0i { break ; \"unreachable\"; },\lexiting scope_4 stmt if z == 0i { break ; \"unreachable\"; },\lexiting scope_5 block { if z == 0i { break ; \"unreachable\"; } z -= 1i; }"]; + N39 -> N40; N40 -> N41; N41 -> N42; - N38 -> N43; N42 -> N43; N43 -> N44; N44 -> N45; - N45 -> N46; - N46 -> N47; - N47 -> N34; - N35 -> N48; + N45 -> N47; + N47 -> N48; N48 -> N49; N49 -> N50; - N50 -> N51; - N51 -> N21[label="exiting scope_0 expr continue \'a,\lexiting scope_1 stmt continue \'a ;,\lexiting scope_2 block { continue \'a ; \"unreachable\"; },\lexiting scope_3 expr if x > 10i { continue \'a ; \"unreachable\"; },\lexiting scope_4 block {\l if y == 0i { break ; \"unreachable\"; }\l y -= 1i;\l \'a: loop { if z == 0i { break ; \"unreachable\"; } z -= 1i; }\l if x > 10i { continue \'a ; \"unreachable\"; }\l}\l"]; + N50 -> N46[label="exiting scope_0 expr break,\lexiting scope_1 stmt break ;,\lexiting scope_2 block { break ; \"unreachable\"; },\lexiting scope_3 expr if z == 0i { break ; \"unreachable\"; },\lexiting scope_4 stmt if z == 0i { break ; \"unreachable\"; },\lexiting scope_5 block { if z == 0i { break ; \"unreachable\"; } z -= 1i; }"]; + N51 -> N52; N52 -> N53; N53 -> N54; - N50 -> N55; N54 -> N55; + N49 -> N56; N55 -> N56; - N56 -> N21; - N22 -> N57; - N57 -> N8; - N9 -> N58; - N58 -> N1; + N56 -> N57; + N57 -> N58; + N58 -> N59; + N59 -> N60; + N60 -> N61; + N61 -> N62; + N62 -> N45; + N46 -> N63; + N63 -> N64; + N64 -> N65; + N65 -> N66; + N66 -> N67; + N67 -> N28[label="exiting scope_0 expr continue \'a,\lexiting scope_1 stmt continue \'a ;,\lexiting scope_2 block { continue \'a ; \"unreachable\"; },\lexiting scope_3 expr if x > 10i { continue \'a ; \"unreachable\"; },\lexiting scope_4 block {\l if y == 0i { break ; \"unreachable\"; }\l y -= 1i;\l \'a: loop { if z == 0i { break ; \"unreachable\"; } z -= 1i; }\l if x > 10i { continue \'a ; \"unreachable\"; }\l}\l"]; + N68 -> N69; + N69 -> N70; + N70 -> N71; + N71 -> N72; + N66 -> N73; + N72 -> N73; + N73 -> N74; + N74 -> N28; + N29 -> N75; + N75 -> N11; + N12 -> N76; + N76 -> N1; } diff --git a/src/test/run-make/unicode-input/multiple_files.rs b/src/test/run-make/unicode-input/multiple_files.rs index 295af0964b4..31802dd9f06 100644 --- a/src/test/run-make/unicode-input/multiple_files.rs +++ b/src/test/run-make/unicode-input/multiple_files.rs @@ -65,6 +65,6 @@ fn main() { // positive test so that this test will be updated when the // compiler changes. - assert!(err.as_slice().contains("expected item but found")) + assert!(err.as_slice().contains("expected item, found")) } } diff --git a/src/test/run-pass-fulldeps/issue-13560.rs b/src/test/run-pass-fulldeps/issue-13560.rs index d72c82852dd..cd79a95dace 100644 --- a/src/test/run-pass-fulldeps/issue-13560.rs +++ b/src/test/run-pass-fulldeps/issue-13560.rs @@ -16,7 +16,7 @@ // Regression test for issue #13560, the test itself is all in the dependent // libraries. The fail which previously failed to compile is the one numbered 3. -extern crate t2 = "issue-13560-2"; -extern crate t3 = "issue-13560-3"; +extern crate "issue-13560-2" as t2; +extern crate "issue-13560-3" as t3; fn main() {} diff --git a/src/test/run-pass-fulldeps/syntax-extension-with-dll-deps.rs b/src/test/run-pass-fulldeps/syntax-extension-with-dll-deps.rs index 275463f5d7f..b3fae671c52 100644 --- a/src/test/run-pass-fulldeps/syntax-extension-with-dll-deps.rs +++ b/src/test/run-pass-fulldeps/syntax-extension-with-dll-deps.rs @@ -15,7 +15,7 @@ #![feature(phase)] #[phase(plugin)] -extern crate extension = "syntax-extension-with-dll-deps-2"; +extern crate "syntax-extension-with-dll-deps-2" as extension; fn main() { foo!(); diff --git a/src/test/run-pass/alignment-gep-tup-like-1.rs b/src/test/run-pass/alignment-gep-tup-like-1.rs index dec53672d87..6e67b3f6add 100644 --- a/src/test/run-pass/alignment-gep-tup-like-1.rs +++ b/src/test/run-pass/alignment-gep-tup-like-1.rs @@ -29,11 +29,11 @@ impl<A:Clone> Invokable<A> for Invoker<A> { } } -fn f<A:Clone + 'static>(a: A, b: u16) -> Box<Invokable<A>> { +fn f<A:Clone + 'static>(a: A, b: u16) -> Box<Invokable<A>+'static> { box Invoker { a: a, b: b, - } as (Box<Invokable<A>>) + } as (Box<Invokable<A>+'static>) } pub fn main() { diff --git a/src/test/run-pass/borrowck-freeze-frozen-mut.rs b/src/test/run-pass/borrowck-freeze-frozen-mut.rs index 4044e9f06af..f224042bc79 100644 --- a/src/test/run-pass/borrowck-freeze-frozen-mut.rs +++ b/src/test/run-pass/borrowck-freeze-frozen-mut.rs @@ -10,7 +10,7 @@ // Test that a `&mut` inside of an `&` is freezable. -struct MutSlice<'a, T> { +struct MutSlice<'a, T:'a> { data: &'a mut [T] } diff --git a/src/test/run-pass/byte-literals.rs b/src/test/run-pass/byte-literals.rs index 7fd7e3dbf00..058dc426766 100644 --- a/src/test/run-pass/byte-literals.rs +++ b/src/test/run-pass/byte-literals.rs @@ -37,13 +37,16 @@ pub fn main() { _ => fail!() } - assert_eq!(b"a\n\r\t\\\'\"\0\xF0", - &[97u8, 10u8, 13u8, 9u8, 92u8, 39u8, 34u8, 0u8, 240u8]); + let expected: &[_] = &[97u8, 10u8, 13u8, 9u8, 92u8, 39u8, 34u8, 0u8, 240u8]; + assert_eq!(b"a\n\r\t\\\'\"\0\xF0", expected); + let expected: &[_] = &[97u8, 98u8]; assert_eq!(b"a\ - b", &[97u8, 98u8]); - assert_eq!(BAR, &[97u8, 240u8, 9u8]); + b", expected); + let expected: &[_] = &[97u8, 240u8, 9u8]; + assert_eq!(BAR, expected); - match &[97u8, 10u8] { + let val: &[_] = &[97u8, 10u8]; + match val { b"a\n" => {}, _ => fail!(), } @@ -55,9 +58,12 @@ pub fn main() { _ => 3u }, 2); - assert_eq!(BAZ, &[97u8, 92u8, 110u8]); - assert_eq!(br"a\n", &[97u8, 92u8, 110u8]); + let expected: &[_] = &[97u8, 92u8, 110u8]; + assert_eq!(BAZ, expected); + let expected: &[_] = &[97u8, 92u8, 110u8]; + assert_eq!(br"a\n", expected); assert_eq!(br"a\n", b"a\\n"); - assert_eq!(br###"a"##b"###, &[97u8, 34u8, 35u8, 35u8, 98u8]); + let expected: &[_] = &[97u8, 34u8, 35u8, 35u8, 98u8]; + assert_eq!(br###"a"##b"###, expected); assert_eq!(br###"a"##b"###, b"a\"##b"); } diff --git a/src/test/run-pass/cast-in-array-size.rs b/src/test/run-pass/cast-in-array-size.rs new file mode 100644 index 00000000000..13e5b89b84e --- /dev/null +++ b/src/test/run-pass/cast-in-array-size.rs @@ -0,0 +1,20 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + + +// issues #10618 and #16382 +static SIZE: int = 25; + +fn main() { + let _a: [bool, ..1 as uint]; + let _b: [int, ..SIZE as uint] = [1, ..SIZE as uint]; + let _c: [bool, ..'\n' as uint] = [true, ..'\n' as uint]; + let _d: [bool, ..true as uint] = [true, ..true as uint]; +} diff --git a/src/test/run-pass/check-static-slice.rs b/src/test/run-pass/check-static-slice.rs new file mode 100644 index 00000000000..17957dbcc13 --- /dev/null +++ b/src/test/run-pass/check-static-slice.rs @@ -0,0 +1,40 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Check that the various ways of getting to a reference to a vec (both sized +// and unsized) work properly. + +static aa: [int, ..3] = [1, 2, 3]; +static ab: &'static [int, ..3] = &aa; +static ac: &'static [int] = ab; +static ad: &'static [int] = &aa; +static ae: &'static [int, ..3] = &[1, 2, 3]; +static af: &'static [int] = &[1, 2, 3]; + +static ca: int = aa[0]; +static cb: int = ab[1]; +static cc: int = ac[2]; +static cd: int = ad[0]; +static ce: int = ae[1]; +static cf: int = af[2]; + +fn main () { + let b: &[int] = &[1, 2, 3]; + assert!(ac == b); + assert!(ad == b); + assert!(af == b); + + assert!(ca == 1); + assert!(cb == 2); + assert!(cc == 3); + assert!(cd == 1); + assert!(ce == 2); + assert!(cf == 3); +} diff --git a/src/test/run-pass/close-over-big-then-small-data.rs b/src/test/run-pass/close-over-big-then-small-data.rs index 59d532a40e7..69878d4a06b 100644 --- a/src/test/run-pass/close-over-big-then-small-data.rs +++ b/src/test/run-pass/close-over-big-then-small-data.rs @@ -33,11 +33,11 @@ impl<A:Clone> Invokable<A> for Invoker<A> { } } -fn f<A:Clone + 'static>(a: A, b: u16) -> Box<Invokable<A>> { +fn f<A:Clone + 'static>(a: A, b: u16) -> Box<Invokable<A>+'static> { box Invoker { a: a, b: b, - } as (Box<Invokable<A>>) + } as (Box<Invokable<A>+'static>) } pub fn main() { diff --git a/src/test/run-pass/closure-reform.rs b/src/test/run-pass/closure-reform.rs index c05f2502a89..aa4d48e5ae0 100644 --- a/src/test/run-pass/closure-reform.rs +++ b/src/test/run-pass/closure-reform.rs @@ -26,7 +26,7 @@ fn call_this(f: |&str|:Send) { f("Hello!"); } -fn call_that(f: <'a>|&'a int, &'a int|: -> int) { +fn call_that(f: <'a>|&'a int, &'a int| -> int) { let (ten, forty_two) = (10, 42); println!("Your lucky number is {}", f(&ten, &forty_two)); } diff --git a/src/test/run-pass/closure-syntax.rs b/src/test/run-pass/closure-syntax.rs index 7b8d1dd7576..c2fbc2a4bf2 100644 --- a/src/test/run-pass/closure-syntax.rs +++ b/src/test/run-pass/closure-syntax.rs @@ -13,16 +13,16 @@ fn foo<T>() {} trait Bar1 {} -impl Bar1 for proc() {} +impl Bar1 for proc():'static {} trait Bar2 {} -impl Bar2 for proc(): Send {} +impl Bar2 for proc():Send {} trait Bar3 {} impl<'b> Bar3 for <'a>|&'a int|: 'b + Send -> &'a int {} trait Bar4 {} -impl Bar4 for proc<'a>(&'a int) -> &'a int {} +impl Bar4 for proc<'a>(&'a int):'static -> &'a int {} struct Foo<'a> { a: ||: 'a, @@ -30,9 +30,9 @@ struct Foo<'a> { c: <'b>||: 'a, d: ||: 'a + Sync, e: <'b>|int|: 'a + Sync -> &'b f32, - f: proc(), - g: proc(): 'static + Sync, - h: proc<'b>(int): Sync -> &'b f32, + f: proc():'static, + g: proc():'static+Sync, + h: proc<'b>(int):'static+Sync -> &'b f32, } fn f<'a>(a: &'a int, f: <'b>|&'b int| -> &'b int) -> &'a int { diff --git a/src/test/run-pass/colorful-write-macros.rs b/src/test/run-pass/colorful-write-macros.rs index 724e57bdef2..60b6d8f8be0 100644 --- a/src/test/run-pass/colorful-write-macros.rs +++ b/src/test/run-pass/colorful-write-macros.rs @@ -18,7 +18,7 @@ use std::fmt; use std::fmt::FormatWriter; struct Foo<'a> { - writer: &'a mut Writer, + writer: &'a mut Writer+'a, other: &'a str, } diff --git a/src/test/run-pass/const-enum-vec-index.rs b/src/test/run-pass/const-enum-vec-index.rs index 4c81eaae1d8..5be21696bd1 100644 --- a/src/test/run-pass/const-enum-vec-index.rs +++ b/src/test/run-pass/const-enum-vec-index.rs @@ -12,6 +12,9 @@ enum E { V1(int), V0 } static C: &'static [E] = &[V0, V1(0xDEADBEE)]; static C0: E = C[0]; static C1: E = C[1]; +static D: &'static [E, ..2] = &[V0, V1(0xDEADBEE)]; +static D0: E = C[0]; +static D1: E = C[1]; pub fn main() { match C0 { @@ -22,4 +25,13 @@ pub fn main() { V1(n) => assert!(n == 0xDEADBEE), _ => fail!() } + + match D0 { + V0 => (), + _ => fail!() + } + match D1 { + V1(n) => assert!(n == 0xDEADBEE), + _ => fail!() + } } diff --git a/src/test/run-pass/dst-dtor-1.rs b/src/test/run-pass/dst-dtor-1.rs new file mode 100644 index 00000000000..6e2ae117ce7 --- /dev/null +++ b/src/test/run-pass/dst-dtor-1.rs @@ -0,0 +1,34 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +static mut DROP_RAN: bool = false; + +struct Foo; +impl Drop for Foo { + fn drop(&mut self) { + unsafe { DROP_RAN = true; } + } +} + +trait Trait {} +impl Trait for Foo {} + +struct Fat<Sized? T> { + f: T +} + +pub fn main() { + { + let _x: Box<Fat<Trait>> = box Fat { f: Foo }; + } + unsafe { + assert!(DROP_RAN); + } +} diff --git a/src/test/run-pass/dst-dtor-2.rs b/src/test/run-pass/dst-dtor-2.rs new file mode 100644 index 00000000000..deaf49228bc --- /dev/null +++ b/src/test/run-pass/dst-dtor-2.rs @@ -0,0 +1,31 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +static mut DROP_RAN: int = 0; + +struct Foo; +impl Drop for Foo { + fn drop(&mut self) { + unsafe { DROP_RAN += 1; } + } +} + +struct Fat<Sized? T> { + f: T +} + +pub fn main() { + { + let _x: Box<Fat<[Foo]>> = box Fat { f: [Foo, Foo, Foo] }; + } + unsafe { + assert!(DROP_RAN == 3); + } +} diff --git a/src/test/run-pass/dst-struct-reflect.rs b/src/test/run-pass/dst-struct-reflect.rs new file mode 100644 index 00000000000..2028ebf64c2 --- /dev/null +++ b/src/test/run-pass/dst-struct-reflect.rs @@ -0,0 +1,61 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// FIXME(15049) Re-enable this test. +// ignore-test +// Test that structs with unsized fields work with {:?} reflection. + +extern crate debug; + +struct Fat<Sized? T> { + f1: int, + f2: &'static str, + ptr: T +} + +// x is a fat pointer +fn reflect(x: &Fat<[int]>, cmp: &str) { + // Don't test this result because reflecting unsized fields is undefined for now. + let _s = format!("{:?}", x); + let s = format!("{:?}", &x.ptr); + assert!(s == cmp.to_string()) + + println!("{:?}", x); + println!("{:?}", &x.ptr); +} + +fn reflect_0(x: &Fat<[int]>) { + let _s = format!("{:?}", x.ptr[0]); + println!("{:?}", x.ptr[0]); +} + +pub fn main() { + // With a vec of ints. + let f1 = Fat { f1: 5, f2: "some str", ptr: [1, 2, 3] }; + reflect(&f1, "&[1, 2, 3]"); + reflect_0(&f1); + let f2 = &f1; + reflect(f2, "&[1, 2, 3]"); + reflect_0(f2); + let f3: &Fat<[int]> = f2; + reflect(f3, "&[1, 2, 3]"); + reflect_0(f3); + let f4: &Fat<[int]> = &f1; + reflect(f4, "&[1, 2, 3]"); + reflect_0(f4); + let f5: &Fat<[int]> = &Fat { f1: 5, f2: "some str", ptr: [1, 2, 3] }; + reflect(f5, "&[1, 2, 3]"); + reflect_0(f5); + + // Zero size vec. + let f5: &Fat<[int]> = &Fat { f1: 5, f2: "some str", ptr: [] }; + reflect(f5, "&[]"); +} + diff --git a/src/test/run-pass/dst-struct-sole.rs b/src/test/run-pass/dst-struct-sole.rs new file mode 100644 index 00000000000..04fe6d5cefd --- /dev/null +++ b/src/test/run-pass/dst-struct-sole.rs @@ -0,0 +1,84 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// As dst-struct.rs, but the unsized field is the only field in the struct. + +struct Fat<Sized? T> { + ptr: T +} + +// x is a fat pointer +fn foo(x: &Fat<[int]>) { + let y = &x.ptr; + assert!(x.ptr.len() == 3); + assert!(y[0] == 1); + assert!(x.ptr[1] == 2); +} + +fn foo2<T:ToBar>(x: &Fat<[T]>) { + let y = &x.ptr; + let bar = Bar; + assert!(x.ptr.len() == 3); + assert!(y[0].to_bar() == bar); + assert!(x.ptr[1].to_bar() == bar); +} + +#[deriving(PartialEq,Eq)] +struct Bar; + +trait ToBar { + fn to_bar(&self) -> Bar; +} + +impl ToBar for Bar { + fn to_bar(&self) -> Bar { + *self + } +} + +pub fn main() { + // With a vec of ints. + let f1 = Fat { ptr: [1, 2, 3] }; + foo(&f1); + let f2 = &f1; + foo(f2); + let f3: &Fat<[int]> = f2; + foo(f3); + let f4: &Fat<[int]> = &f1; + foo(f4); + let f5: &Fat<[int]> = &Fat { ptr: [1, 2, 3] }; + foo(f5); + + // With a vec of Bars. + let bar = Bar; + let f1 = Fat { ptr: [bar, bar, bar] }; + foo2(&f1); + let f2 = &f1; + foo2(f2); + let f3: &Fat<[Bar]> = f2; + foo2(f3); + let f4: &Fat<[Bar]> = &f1; + foo2(f4); + let f5: &Fat<[Bar]> = &Fat { ptr: [bar, bar, bar] }; + foo2(f5); + + // Assignment. + let f5: &mut Fat<[int]> = &mut Fat { ptr: [1, 2, 3] }; + f5.ptr[1] = 34; + assert!(f5.ptr[0] == 1); + assert!(f5.ptr[1] == 34); + assert!(f5.ptr[2] == 3); + + // Zero size vec. + let f5: &Fat<[int]> = &Fat { ptr: [] }; + assert!(f5.ptr.len() == 0); + let f5: &Fat<[Bar]> = &Fat { ptr: [] }; + assert!(f5.ptr.len() == 0); +} diff --git a/src/test/run-pass/dst-struct.rs b/src/test/run-pass/dst-struct.rs new file mode 100644 index 00000000000..6b8e25e8559 --- /dev/null +++ b/src/test/run-pass/dst-struct.rs @@ -0,0 +1,127 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +struct Fat<Sized? T> { + f1: int, + f2: &'static str, + ptr: T +} + +// x is a fat pointer +fn foo(x: &Fat<[int]>) { + let y = &x.ptr; + assert!(x.ptr.len() == 3); + assert!(y[0] == 1); + assert!(x.ptr[1] == 2); + assert!(x.f1 == 5); + assert!(x.f2 == "some str"); +} + +fn foo2<T:ToBar>(x: &Fat<[T]>) { + let y = &x.ptr; + let bar = Bar; + assert!(x.ptr.len() == 3); + assert!(y[0].to_bar() == bar); + assert!(x.ptr[1].to_bar() == bar); + assert!(x.f1 == 5); + assert!(x.f2 == "some str"); +} + +fn foo3(x: &Fat<Fat<[int]>>) { + let y = &x.ptr.ptr; + assert!(x.f1 == 5); + assert!(x.f2 == "some str"); + assert!(x.ptr.f1 == 8); + assert!(x.ptr.f2 == "deep str"); + assert!(x.ptr.ptr.len() == 3); + assert!(y[0] == 1); + assert!(x.ptr.ptr[1] == 2); +} + + +#[deriving(PartialEq,Eq)] +struct Bar; + +trait ToBar { + fn to_bar(&self) -> Bar; +} + +impl ToBar for Bar { + fn to_bar(&self) -> Bar { + *self + } +} + +pub fn main() { + // With a vec of ints. + let f1 = Fat { f1: 5, f2: "some str", ptr: [1, 2, 3] }; + foo(&f1); + let f2 = &f1; + foo(f2); + let f3: &Fat<[int]> = f2; + foo(f3); + let f4: &Fat<[int]> = &f1; + foo(f4); + let f5: &Fat<[int]> = &Fat { f1: 5, f2: "some str", ptr: [1, 2, 3] }; + foo(f5); + + // With a vec of Bars. + let bar = Bar; + let f1 = Fat { f1: 5, f2: "some str", ptr: [bar, bar, bar] }; + foo2(&f1); + let f2 = &f1; + foo2(f2); + let f3: &Fat<[Bar]> = f2; + foo2(f3); + let f4: &Fat<[Bar]> = &f1; + foo2(f4); + let f5: &Fat<[Bar]> = &Fat { f1: 5, f2: "some str", ptr: [bar, bar, bar] }; + foo2(f5); + + // Assignment. + let f5: &mut Fat<[int]> = &mut Fat { f1: 5, f2: "some str", ptr: [1, 2, 3] }; + f5.ptr[1] = 34; + assert!(f5.ptr[0] == 1); + assert!(f5.ptr[1] == 34); + assert!(f5.ptr[2] == 3); + + // Zero size vec. + let f5: &Fat<[int]> = &Fat { f1: 5, f2: "some str", ptr: [] }; + assert!(f5.ptr.len() == 0); + let f5: &Fat<[Bar]> = &Fat { f1: 5, f2: "some str", ptr: [] }; + assert!(f5.ptr.len() == 0); + + // Deeply nested. + let f1 = Fat { f1: 5, f2: "some str", ptr: Fat { f1: 8, f2: "deep str", ptr: [1, 2, 3]} }; + foo3(&f1); + let f2 = &f1; + foo3(f2); + let f3: &Fat<Fat<[int]>> = f2; + foo3(f3); + let f4: &Fat<Fat<[int]>> = &f1; + foo3(f4); + let f5: &Fat<Fat<[int]>> = + &Fat { f1: 5, f2: "some str", ptr: Fat { f1: 8, f2: "deep str", ptr: [1, 2, 3]} }; + foo3(f5); + + // Box. + let f1 = box [1i, 2, 3]; + assert!((*f1)[1] == 2); + let f2: Box<[int]> = f1; + assert!((*f2)[1] == 2); + + // Nested Box. + let f1 : Box<Fat<[int, ..3]>> = box Fat { f1: 5, f2: "some str", ptr: [1, 2, 3] }; + foo(&*f1); + let f2 : Box<Fat<[int]>> = f1; + foo(&*f2); + let f3 : Box<Fat<[int]>> = box Fat { f1: 5, f2: "some str", ptr: [1, 2, 3] }; + foo(&*f3); +} diff --git a/src/test/run-pass/dst-trait.rs b/src/test/run-pass/dst-trait.rs new file mode 100644 index 00000000000..97627309551 --- /dev/null +++ b/src/test/run-pass/dst-trait.rs @@ -0,0 +1,111 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +struct Fat<Sized? T> { + f1: int, + f2: &'static str, + ptr: T +} + +#[deriving(PartialEq,Eq)] +struct Bar; + +#[deriving(PartialEq,Eq)] +struct Bar1 { + f: int +} + +trait ToBar { + fn to_bar(&self) -> Bar; + fn to_val(&self) -> int; +} + +impl ToBar for Bar { + fn to_bar(&self) -> Bar { + *self + } + fn to_val(&self) -> int { + 0 + } +} +impl ToBar for Bar1 { + fn to_bar(&self) -> Bar { + Bar + } + fn to_val(&self) -> int { + self.f + } +} + +// x is a fat pointer +fn foo(x: &Fat<ToBar>) { + assert!(x.f1 == 5); + assert!(x.f2 == "some str"); + assert!(x.ptr.to_bar() == Bar); + assert!(x.ptr.to_val() == 42); + + let y = &x.ptr; + assert!(y.to_bar() == Bar); + assert!(y.to_val() == 42); +} + +fn bar(x: &ToBar) { + assert!(x.to_bar() == Bar); + assert!(x.to_val() == 42); +} + +fn baz(x: &Fat<Fat<ToBar>>) { + assert!(x.f1 == 5); + assert!(x.f2 == "some str"); + assert!(x.ptr.f1 == 8); + assert!(x.ptr.f2 == "deep str"); + assert!(x.ptr.ptr.to_bar() == Bar); + assert!(x.ptr.ptr.to_val() == 42); + + let y = &x.ptr.ptr; + assert!(y.to_bar() == Bar); + assert!(y.to_val() == 42); + +} + +pub fn main() { + let f1 = Fat { f1: 5, f2: "some str", ptr: Bar1 {f :42} }; + foo(&f1); + let f2 = &f1; + foo(f2); + let f3: &Fat<ToBar> = f2; + foo(f3); + let f4: &Fat<ToBar> = &f1; + foo(f4); + let f5: &Fat<ToBar> = &Fat { f1: 5, f2: "some str", ptr: Bar1 {f :42} }; + foo(f5); + + // Zero size object. + let f6: &Fat<ToBar> = &Fat { f1: 5, f2: "some str", ptr: Bar }; + assert!(f6.ptr.to_bar() == Bar); + + // &* + let f7: Box<ToBar> = box Bar1 {f :42}; + bar(&*f7); + + // Deep nesting + let f1 = + Fat { f1: 5, f2: "some str", ptr: Fat { f1: 8, f2: "deep str", ptr: Bar1 {f :42}} }; + baz(&f1); + let f2 = &f1; + baz(f2); + let f3: &Fat<Fat<ToBar>> = f2; + baz(f3); + let f4: &Fat<Fat<ToBar>> = &f1; + baz(f4); + let f5: &Fat<Fat<ToBar>> = + &Fat { f1: 5, f2: "some str", ptr: Fat { f1: 8, f2: "deep str", ptr: Bar1 {f :42}} }; + baz(f5); +} diff --git a/src/test/run-pass/dupe-first-attr.rc b/src/test/run-pass/dupe-first-attr.rc index f987446c6ad..30d6b934fa2 100644 --- a/src/test/run-pass/dupe-first-attr.rc +++ b/src/test/run-pass/dupe-first-attr.rc @@ -18,7 +18,6 @@ mod hello; mod hello; #[cfg(target_os = "windows")] -/* NOTE: Remove after snapshot */#[cfg(stage0, target_os = "win32")] mod hello; #[cfg(target_os = "freebsd")] diff --git a/src/test/run-pass/evec-slice.rs b/src/test/run-pass/evec-slice.rs index 9c5995b7ba0..4a112f145c3 100644 --- a/src/test/run-pass/evec-slice.rs +++ b/src/test/run-pass/evec-slice.rs @@ -14,7 +14,7 @@ extern crate debug; pub fn main() { let x : &[int] = &[1,2,3,4,5]; - let mut z = &[1,2,3,4,5]; + let mut z : &[int] = &[1,2,3,4,5]; z = x; assert_eq!(z[0], 1); assert_eq!(z[4], 5); diff --git a/src/test/run-pass/explicit-self-generic.rs b/src/test/run-pass/explicit-self-generic.rs index ac2922e92d4..e95b1ed2f0e 100644 --- a/src/test/run-pass/explicit-self-generic.rs +++ b/src/test/run-pass/explicit-self-generic.rs @@ -13,8 +13,8 @@ * * The hash should concentrate entropy in the lower bits. */ -type HashFn<K> = proc(K) -> uint; -type EqFn<K> = proc(K, K) -> bool; +type HashFn<K> = proc(K):'static -> uint; +type EqFn<K> = proc(K, K):'static -> bool; struct LM { resize_at: uint, size: uint } diff --git a/src/test/run-pass/extern-call-deep.rs b/src/test/run-pass/extern-call-deep.rs index d05057ea251..93a5752d004 100644 --- a/src/test/run-pass/extern-call-deep.rs +++ b/src/test/run-pass/extern-call-deep.rs @@ -22,14 +22,14 @@ mod rustrt { } extern fn cb(data: libc::uintptr_t) -> libc::uintptr_t { - if data == 1u { + if data == 1 { data } else { - count(data - 1u) + 1u + count(data - 1) + 1 } } -fn count(n: uint) -> uint { +fn count(n: libc::uintptr_t) -> libc::uintptr_t { unsafe { println!("n = {}", n); rustrt::rust_dbg_call(cb, n) @@ -37,7 +37,7 @@ fn count(n: uint) -> uint { } pub fn main() { - let result = count(1000u); + let result = count(1000); println!("result = {}", result); - assert_eq!(result, 1000u); + assert_eq!(result, 1000); } diff --git a/src/test/run-pass/extern-call-deep2.rs b/src/test/run-pass/extern-call-deep2.rs index 654541dcde0..bc5ccc30c52 100644 --- a/src/test/run-pass/extern-call-deep2.rs +++ b/src/test/run-pass/extern-call-deep2.rs @@ -23,14 +23,14 @@ mod rustrt { } extern fn cb(data: libc::uintptr_t) -> libc::uintptr_t { - if data == 1u { + if data == 1 { data } else { - count(data - 1u) + 1u + count(data - 1) + 1 } } -fn count(n: uint) -> uint { +fn count(n: libc::uintptr_t) -> libc::uintptr_t { unsafe { println!("n = {}", n); rustrt::rust_dbg_call(cb, n) @@ -41,8 +41,8 @@ pub fn main() { // Make sure we're on a task with small Rust stacks (main currently // has a large stack) task::spawn(proc() { - let result = count(1000u); + let result = count(1000); println!("result = {}", result); - assert_eq!(result, 1000u); + assert_eq!(result, 1000); }); } diff --git a/src/test/run-pass/extern-call-indirect.rs b/src/test/run-pass/extern-call-indirect.rs index 8db745424b2..52697d96b32 100644 --- a/src/test/run-pass/extern-call-indirect.rs +++ b/src/test/run-pass/extern-call-indirect.rs @@ -22,14 +22,14 @@ mod rustrt { } extern fn cb(data: libc::uintptr_t) -> libc::uintptr_t { - if data == 1u { + if data == 1 { data } else { - fact(data - 1u) * data + fact(data - 1) * data } } -fn fact(n: uint) -> uint { +fn fact(n: libc::uintptr_t) -> libc::uintptr_t { unsafe { println!("n = {}", n); rustrt::rust_dbg_call(cb, n) @@ -37,7 +37,7 @@ fn fact(n: uint) -> uint { } pub fn main() { - let result = fact(10u); + let result = fact(10); println!("result = {}", result); - assert_eq!(result, 3628800u); + assert_eq!(result, 3628800); } diff --git a/src/test/run-pass/extern-call-scrub.rs b/src/test/run-pass/extern-call-scrub.rs index 07d425dcdad..ae9430370d5 100644 --- a/src/test/run-pass/extern-call-scrub.rs +++ b/src/test/run-pass/extern-call-scrub.rs @@ -27,14 +27,14 @@ mod rustrt { } extern fn cb(data: libc::uintptr_t) -> libc::uintptr_t { - if data == 1u { + if data == 1 { data } else { - count(data - 1u) + count(data - 1u) + count(data - 1) + count(data - 1) } } -fn count(n: uint) -> uint { +fn count(n: libc::uintptr_t) -> libc::uintptr_t { unsafe { println!("n = {}", n); rustrt::rust_dbg_call(cb, n) @@ -45,8 +45,8 @@ pub fn main() { // Make sure we're on a task with small Rust stacks (main currently // has a large stack) task::spawn(proc() { - let result = count(12u); + let result = count(12); println!("result = {}", result); - assert_eq!(result, 2048u); + assert_eq!(result, 2048); }); } diff --git a/src/test/run-pass/extern-crosscrate.rs b/src/test/run-pass/extern-crosscrate.rs index 5dc25c85325..18e20332adc 100644 --- a/src/test/run-pass/extern-crosscrate.rs +++ b/src/test/run-pass/extern-crosscrate.rs @@ -11,8 +11,9 @@ //aux-build:extern-crosscrate-source.rs extern crate externcallback; +extern crate libc; -fn fact(n: uint) -> uint { +fn fact(n: libc::uintptr_t) -> libc::uintptr_t { unsafe { println!("n = {}", n); externcallback::rustrt::rust_dbg_call(externcallback::cb, n) @@ -20,7 +21,7 @@ fn fact(n: uint) -> uint { } pub fn main() { - let result = fact(10u); + let result = fact(10); println!("result = {}", result); - assert_eq!(result, 3628800u); + assert_eq!(result, 3628800); } diff --git a/src/test/run-pass/extern-foreign-crate.rs b/src/test/run-pass/extern-foreign-crate.rs index 434b684756b..59ee9a14e2e 100644 --- a/src/test/run-pass/extern-foreign-crate.rs +++ b/src/test/run-pass/extern-foreign-crate.rs @@ -8,6 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -extern crate mystd = "std"; +extern crate "std" as mystd; pub fn main() {} diff --git a/src/test/run-pass/extern-stress.rs b/src/test/run-pass/extern-stress.rs index 18e771b9b1e..87d96880e4b 100644 --- a/src/test/run-pass/extern-stress.rs +++ b/src/test/run-pass/extern-stress.rs @@ -26,24 +26,24 @@ mod rustrt { } extern fn cb(data: libc::uintptr_t) -> libc::uintptr_t { - if data == 1u { + if data == 1 { data } else { task::deschedule(); - count(data - 1u) + count(data - 1u) + count(data - 1) + count(data - 1) } } -fn count(n: uint) -> uint { +fn count(n: libc::uintptr_t) -> libc::uintptr_t { unsafe { rustrt::rust_dbg_call(cb, n) } } pub fn main() { - for _ in range(0, 100u) { + for _ in range(0u, 100) { task::spawn(proc() { - assert_eq!(count(5u), 16u); + assert_eq!(count(5), 16); }); } } diff --git a/src/test/run-pass/extern-yield.rs b/src/test/run-pass/extern-yield.rs index 0735cca1864..7fb68e9f8a0 100644 --- a/src/test/run-pass/extern-yield.rs +++ b/src/test/run-pass/extern-yield.rs @@ -23,14 +23,14 @@ mod rustrt { } extern fn cb(data: libc::uintptr_t) -> libc::uintptr_t { - if data == 1u { + if data == 1 { data } else { - count(data - 1u) + count(data - 1u) + count(data - 1) + count(data - 1) } } -fn count(n: uint) -> uint { +fn count(n: libc::uintptr_t) -> libc::uintptr_t { unsafe { task::deschedule(); rustrt::rust_dbg_call(cb, n) @@ -40,9 +40,9 @@ fn count(n: uint) -> uint { pub fn main() { for _ in range(0, 10u) { task::spawn(proc() { - let result = count(5u); + let result = count(5); println!("result = {}", result); - assert_eq!(result, 16u); + assert_eq!(result, 16); }); } } diff --git a/src/test/run-pass/gc-vec.rs b/src/test/run-pass/gc-vec.rs new file mode 100644 index 00000000000..430ee16bc8a --- /dev/null +++ b/src/test/run-pass/gc-vec.rs @@ -0,0 +1,22 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::gc::{GC}; + +fn main() { + // A fixed-size array allocated in a garbage-collected box + let x = box(GC) [1i, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + assert_eq!(x[0], 1); + assert_eq!(x[6], 7); + assert_eq!(x[9], 10); + + let y = x; + assert!(*y == [1i, 2, 3, 4, 5, 6, 7, 8, 9, 10]); +} diff --git a/src/test/run-pass/glob-std.rs b/src/test/run-pass/glob-std.rs deleted file mode 100644 index 2ed6b82cad2..00000000000 --- a/src/test/run-pass/glob-std.rs +++ /dev/null @@ -1,211 +0,0 @@ -// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or -// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license -// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -// ignore-windows TempDir may cause IoError on windows: #10462 - -#![feature(macro_rules)] - -extern crate glob; - -use glob::glob; -use std::os; -use std::io; -use std::io::TempDir; - -macro_rules! assert_eq ( ($e1:expr, $e2:expr) => ( - if $e1 != $e2 { - fail!("{} != {}", stringify!($e1), stringify!($e2)) - } -) ) - -pub fn main() { - fn mk_file(path: &str, directory: bool) { - if directory { - io::fs::mkdir(&Path::new(path), io::UserRWX).unwrap(); - } else { - io::File::create(&Path::new(path)).unwrap(); - } - } - - fn abs_path(path: &str) -> Path { - os::getcwd().join(&Path::new(path)) - } - - fn glob_vec(pattern: &str) -> Vec<Path> { - glob(pattern).collect() - } - - let root = TempDir::new("glob-tests"); - let root = root.expect("Should have created a temp directory"); - assert!(os::change_dir(root.path())); - - mk_file("aaa", true); - mk_file("aaa/apple", true); - mk_file("aaa/orange", true); - mk_file("aaa/tomato", true); - mk_file("aaa/tomato/tomato.txt", false); - mk_file("aaa/tomato/tomoto.txt", false); - mk_file("bbb", true); - mk_file("bbb/specials", true); - mk_file("bbb/specials/!", false); - - // windows does not allow `*` or `?` characters to exist in filenames - if os::consts::FAMILY != "windows" { - mk_file("bbb/specials/*", false); - mk_file("bbb/specials/?", false); - } - - mk_file("bbb/specials/[", false); - mk_file("bbb/specials/]", false); - mk_file("ccc", true); - mk_file("xyz", true); - mk_file("xyz/x", false); - mk_file("xyz/y", false); - mk_file("xyz/z", false); - - assert_eq!(glob_vec(""), Vec::new()); - assert_eq!(glob_vec("."), vec!(os::getcwd())); - assert_eq!(glob_vec(".."), vec!(os::getcwd().join(".."))); - - assert_eq!(glob_vec("aaa"), vec!(abs_path("aaa"))); - assert_eq!(glob_vec("aaa/"), vec!(abs_path("aaa"))); - assert_eq!(glob_vec("a"), Vec::new()); - assert_eq!(glob_vec("aa"), Vec::new()); - assert_eq!(glob_vec("aaaa"), Vec::new()); - - assert_eq!(glob_vec("aaa/apple"), vec!(abs_path("aaa/apple"))); - assert_eq!(glob_vec("aaa/apple/nope"), Vec::new()); - - // windows should support both / and \ as directory separators - if os::consts::FAMILY == "windows" { - assert_eq!(glob_vec("aaa\\apple"), vec!(abs_path("aaa/apple"))); - } - - assert_eq!(glob_vec("???/"), vec!( - abs_path("aaa"), - abs_path("bbb"), - abs_path("ccc"), - abs_path("xyz"))); - - assert_eq!(glob_vec("aaa/tomato/tom?to.txt"), vec!( - abs_path("aaa/tomato/tomato.txt"), - abs_path("aaa/tomato/tomoto.txt"))); - - assert_eq!(glob_vec("xyz/?"), vec!( - abs_path("xyz/x"), - abs_path("xyz/y"), - abs_path("xyz/z"))); - - assert_eq!(glob_vec("a*"), vec!(abs_path("aaa"))); - assert_eq!(glob_vec("*a*"), vec!(abs_path("aaa"))); - assert_eq!(glob_vec("a*a"), vec!(abs_path("aaa"))); - assert_eq!(glob_vec("aaa*"), vec!(abs_path("aaa"))); - assert_eq!(glob_vec("*aaa"), vec!(abs_path("aaa"))); - assert_eq!(glob_vec("*aaa*"), vec!(abs_path("aaa"))); - assert_eq!(glob_vec("*a*a*a*"), vec!(abs_path("aaa"))); - assert_eq!(glob_vec("aaa*/"), vec!(abs_path("aaa"))); - - assert_eq!(glob_vec("aaa/*"), vec!( - abs_path("aaa/apple"), - abs_path("aaa/orange"), - abs_path("aaa/tomato"))); - - assert_eq!(glob_vec("aaa/*a*"), vec!( - abs_path("aaa/apple"), - abs_path("aaa/orange"), - abs_path("aaa/tomato"))); - - assert_eq!(glob_vec("*/*/*.txt"), vec!( - abs_path("aaa/tomato/tomato.txt"), - abs_path("aaa/tomato/tomoto.txt"))); - - assert_eq!(glob_vec("*/*/t[aob]m?to[.]t[!y]t"), vec!( - abs_path("aaa/tomato/tomato.txt"), - abs_path("aaa/tomato/tomoto.txt"))); - - assert_eq!(glob_vec("./aaa"), vec!(abs_path("aaa"))); - assert_eq!(glob_vec("./*"), glob_vec("*")); - assert_eq!(glob_vec("*/..").pop().unwrap(), abs_path(".")); - assert_eq!(glob_vec("aaa/../bbb"), vec!(abs_path("bbb"))); - assert_eq!(glob_vec("nonexistent/../bbb"), Vec::new()); - assert_eq!(glob_vec("aaa/tomato/tomato.txt/.."), Vec::new()); - - assert_eq!(glob_vec("aaa/tomato/tomato.txt/"), Vec::new()); - - assert_eq!(glob_vec("aa[a]"), vec!(abs_path("aaa"))); - assert_eq!(glob_vec("aa[abc]"), vec!(abs_path("aaa"))); - assert_eq!(glob_vec("a[bca]a"), vec!(abs_path("aaa"))); - assert_eq!(glob_vec("aa[b]"), Vec::new()); - assert_eq!(glob_vec("aa[xyz]"), Vec::new()); - assert_eq!(glob_vec("aa[]]"), Vec::new()); - - assert_eq!(glob_vec("aa[!b]"), vec!(abs_path("aaa"))); - assert_eq!(glob_vec("aa[!bcd]"), vec!(abs_path("aaa"))); - assert_eq!(glob_vec("a[!bcd]a"), vec!(abs_path("aaa"))); - assert_eq!(glob_vec("aa[!a]"), Vec::new()); - assert_eq!(glob_vec("aa[!abc]"), Vec::new()); - - assert_eq!(glob_vec("bbb/specials/[[]"), vec!(abs_path("bbb/specials/["))); - assert_eq!(glob_vec("bbb/specials/!"), vec!(abs_path("bbb/specials/!"))); - assert_eq!(glob_vec("bbb/specials/[]]"), vec!(abs_path("bbb/specials/]"))); - - if os::consts::FAMILY != "windows" { - assert_eq!(glob_vec("bbb/specials/[*]"), vec!(abs_path("bbb/specials/*"))); - assert_eq!(glob_vec("bbb/specials/[?]"), vec!(abs_path("bbb/specials/?"))); - } - - if os::consts::FAMILY == "windows" { - - assert_eq!(glob_vec("bbb/specials/[![]"), vec!( - abs_path("bbb/specials/!"), - abs_path("bbb/specials/]"))); - - assert_eq!(glob_vec("bbb/specials/[!]]"), vec!( - abs_path("bbb/specials/!"), - abs_path("bbb/specials/["))); - - assert_eq!(glob_vec("bbb/specials/[!!]"), vec!( - abs_path("bbb/specials/["), - abs_path("bbb/specials/]"))); - - } else { - - assert_eq!(glob_vec("bbb/specials/[![]"), vec!( - abs_path("bbb/specials/!"), - abs_path("bbb/specials/*"), - abs_path("bbb/specials/?"), - abs_path("bbb/specials/]"))); - - assert_eq!(glob_vec("bbb/specials/[!]]"), vec!( - abs_path("bbb/specials/!"), - abs_path("bbb/specials/*"), - abs_path("bbb/specials/?"), - abs_path("bbb/specials/["))); - - assert_eq!(glob_vec("bbb/specials/[!!]"), vec!( - abs_path("bbb/specials/*"), - abs_path("bbb/specials/?"), - abs_path("bbb/specials/["), - abs_path("bbb/specials/]"))); - - assert_eq!(glob_vec("bbb/specials/[!*]"), vec!( - abs_path("bbb/specials/!"), - abs_path("bbb/specials/?"), - abs_path("bbb/specials/["), - abs_path("bbb/specials/]"))); - - assert_eq!(glob_vec("bbb/specials/[!?]"), vec!( - abs_path("bbb/specials/!"), - abs_path("bbb/specials/*"), - abs_path("bbb/specials/["), - abs_path("bbb/specials/]"))); - - } -} diff --git a/src/test/run-pass/intrinsic-alignment.rs b/src/test/run-pass/intrinsic-alignment.rs index 5d0a3c085b4..4873dc13c40 100644 --- a/src/test/run-pass/intrinsic-alignment.rs +++ b/src/test/run-pass/intrinsic-alignment.rs @@ -43,7 +43,6 @@ mod m { } #[cfg(target_os = "windows")] -/* NOTE: Remove after snapshot */#[cfg(stage0, target_os = "win32")] mod m { #[main] #[cfg(target_arch = "x86")] diff --git a/src/test/run-pass/issue-10028.rs b/src/test/run-pass/issue-10028.rs index f4f7e24d156..826c23a782b 100644 --- a/src/test/run-pass/issue-10028.rs +++ b/src/test/run-pass/issue-10028.rs @@ -10,7 +10,7 @@ // aux-build:issue-10028.rs -extern crate issue10028 = "issue-10028"; +extern crate "issue-10028" as issue10028; use issue10028::ZeroLengthThingWithDestructor; diff --git a/src/test/run-pass/issue-10802.rs b/src/test/run-pass/issue-10802.rs index 4fda506ae64..7487ea81fa8 100644 --- a/src/test/run-pass/issue-10802.rs +++ b/src/test/run-pass/issue-10802.rs @@ -30,9 +30,9 @@ trait MyTrait { } impl MyTrait for Box<DroppableStruct> {} impl MyTrait for Box<DroppableEnum> {} -struct Whatever { w: Box<MyTrait> } +struct Whatever { w: Box<MyTrait+'static> } impl Whatever { - fn new(w: Box<MyTrait>) -> Whatever { + fn new(w: Box<MyTrait+'static>) -> Whatever { Whatever { w: w } } } diff --git a/src/test/run-pass/issue-11205.rs b/src/test/run-pass/issue-11205.rs index 5b52bc34d2b..c2c291c0bec 100644 --- a/src/test/run-pass/issue-11205.rs +++ b/src/test/run-pass/issue-11205.rs @@ -49,7 +49,7 @@ fn main() { foog(x, &[box 1i]); struct T<'a> { - t: [&'a Foo, ..2] + t: [&'a Foo+'a, ..2] } let _n = T { t: [&1i, &2i] @@ -64,7 +64,7 @@ fn main() { }; struct F<'b> { - t: &'b [&'b Foo] + t: &'b [&'b Foo+'b] } let _n = F { t: &[&1i, &2i] @@ -80,7 +80,7 @@ fn main() { }; struct M<'a> { - t: &'a [Box<Foo>] + t: &'a [Box<Foo+'static>] } let _n = M { t: &[box 1i, box 2i] diff --git a/src/test/run-pass/issue-11224.rs b/src/test/run-pass/issue-11224.rs index c247893ef6b..02c82d9c947 100644 --- a/src/test/run-pass/issue-11224.rs +++ b/src/test/run-pass/issue-11224.rs @@ -10,6 +10,6 @@ // aux-build:issue-11224.rs -extern crate unused = "issue-11224"; +extern crate "issue-11224" as unused; pub fn main() {} diff --git a/src/test/run-pass/issue-11225-1.rs b/src/test/run-pass/issue-11225-1.rs index bcdbfb27753..7d1c93fe9a4 100644 --- a/src/test/run-pass/issue-11225-1.rs +++ b/src/test/run-pass/issue-11225-1.rs @@ -10,7 +10,7 @@ // aux-build:issue-11225-1.rs -extern crate foo = "issue-11225-1"; +extern crate "issue-11225-1" as foo; pub fn main() { foo::foo(1i); diff --git a/src/test/run-pass/issue-11225-2.rs b/src/test/run-pass/issue-11225-2.rs index a9b70b1d7c2..7f36c38283b 100644 --- a/src/test/run-pass/issue-11225-2.rs +++ b/src/test/run-pass/issue-11225-2.rs @@ -10,7 +10,7 @@ // aux-build:issue-11225-2.rs -extern crate foo = "issue-11225-2"; +extern crate "issue-11225-2" as foo; pub fn main() { foo::foo(1i); diff --git a/src/test/run-pass/issue-11508.rs b/src/test/run-pass/issue-11508.rs index 25d3a241ebd..1fc72fd2cbe 100644 --- a/src/test/run-pass/issue-11508.rs +++ b/src/test/run-pass/issue-11508.rs @@ -10,7 +10,7 @@ // aux-build:issue-11508.rs -extern crate rand = "issue-11508"; +extern crate "issue-11508" as rand; use rand::{Closed01, random}; diff --git a/src/test/run-pass/issue-11529.rs b/src/test/run-pass/issue-11529.rs index c11f7c79db1..4a74e4be4ce 100644 --- a/src/test/run-pass/issue-11529.rs +++ b/src/test/run-pass/issue-11529.rs @@ -10,7 +10,7 @@ // aux-build:issue-11529.rs -extern crate a = "issue-11529"; +extern crate "issue-11529" as a; fn main() { let one = 1; diff --git a/src/test/run-pass/issue-11612.rs b/src/test/run-pass/issue-11612.rs index 5fb2274a446..fa25d25df05 100644 --- a/src/test/run-pass/issue-11612.rs +++ b/src/test/run-pass/issue-11612.rs @@ -14,7 +14,7 @@ trait A {} -struct B<'a, T> { +struct B<'a, T:'a> { f: &'a T } diff --git a/src/test/run-pass/issue-11677.rs b/src/test/run-pass/issue-11677.rs index 5a244250852..14c1b1b06ea 100644 --- a/src/test/run-pass/issue-11677.rs +++ b/src/test/run-pass/issue-11677.rs @@ -14,7 +14,8 @@ trait X<T> {} -struct S<T> {f: Box<X<T>>, g: Box<X<T>>} +struct S<T> {f: Box<X<T>+'static>, + g: Box<X<T>+'static>} struct F; impl X<int> for F {} diff --git a/src/test/run-pass/issue-12133-1.rs b/src/test/run-pass/issue-12133-1.rs index 91003672f3a..bf5976e9217 100644 --- a/src/test/run-pass/issue-12133-1.rs +++ b/src/test/run-pass/issue-12133-1.rs @@ -11,7 +11,7 @@ // aux-build:issue-12133-rlib.rs // aux-build:issue-12133-dylib.rs -extern crate a = "issue-12133-rlib"; -extern crate b = "issue-12133-dylib"; +extern crate "issue-12133-rlib" as a; +extern crate "issue-12133-dylib" as b; fn main() {} diff --git a/src/test/run-pass/issue-12133-2.rs b/src/test/run-pass/issue-12133-2.rs index 877d4f706e9..50977a7e039 100644 --- a/src/test/run-pass/issue-12133-2.rs +++ b/src/test/run-pass/issue-12133-2.rs @@ -12,7 +12,7 @@ // aux-build:issue-12133-dylib.rs // no-prefer-dynamic -extern crate a = "issue-12133-rlib"; -extern crate b = "issue-12133-dylib"; +extern crate "issue-12133-rlib" as a; +extern crate "issue-12133-dylib" as b; fn main() {} diff --git a/src/test/run-pass/issue-12133-3.rs b/src/test/run-pass/issue-12133-3.rs index 35f4d86efe0..ab990e55295 100644 --- a/src/test/run-pass/issue-12133-3.rs +++ b/src/test/run-pass/issue-12133-3.rs @@ -12,6 +12,6 @@ // aux-build:issue-12133-dylib.rs // aux-build:issue-12133-dylib2.rs -extern crate other = "issue-12133-dylib2"; +extern crate "issue-12133-dylib2" as other; fn main() {} diff --git a/src/test/run-pass/issue-12612.rs b/src/test/run-pass/issue-12612.rs index 3e8ac2f2783..9ceb7366e40 100644 --- a/src/test/run-pass/issue-12612.rs +++ b/src/test/run-pass/issue-12612.rs @@ -11,8 +11,8 @@ // aux-build:issue-12612-1.rs // aux-build:issue-12612-2.rs -extern crate foo = "issue-12612-1"; -extern crate bar = "issue-12612-2"; +extern crate "issue-12612-1" as foo; +extern crate "issue-12612-2" as bar; mod test { use bar::baz; diff --git a/src/test/run-pass/issue-13620.rs b/src/test/run-pass/issue-13620.rs index 13baa80781b..c67dd4b93a0 100644 --- a/src/test/run-pass/issue-13620.rs +++ b/src/test/run-pass/issue-13620.rs @@ -11,7 +11,7 @@ // aux-build:issue-13620-1.rs // aux-build:issue-13620-2.rs -extern crate crate2 = "issue-13620-2"; +extern crate "issue-13620-2" as crate2; fn main() { (crate2::FOO2.foo)(); diff --git a/src/test/run-pass/issue-13872.rs b/src/test/run-pass/issue-13872.rs index 3c19e908e16..a58477e647f 100644 --- a/src/test/run-pass/issue-13872.rs +++ b/src/test/run-pass/issue-13872.rs @@ -12,7 +12,7 @@ // aux-build:issue-13872-2.rs // aux-build:issue-13872-3.rs -extern crate other = "issue-13872-3"; +extern crate "issue-13872-3" as other; fn main() { other::foo(); diff --git a/src/test/run-pass/issue-14330.rs b/src/test/run-pass/issue-14330.rs index 26b282b7180..bac846dfa20 100644 --- a/src/test/run-pass/issue-14330.rs +++ b/src/test/run-pass/issue-14330.rs @@ -10,6 +10,6 @@ #![feature(phase)] -#[phase(plugin, link)] extern crate std2 = "std"; +#[phase(plugin, link)] extern crate "std" as std2; fn main() {} diff --git a/src/test/run-pass/issue-14421.rs b/src/test/run-pass/issue-14421.rs index cf88131048e..c595f98cd0e 100644 --- a/src/test/run-pass/issue-14421.rs +++ b/src/test/run-pass/issue-14421.rs @@ -10,7 +10,7 @@ // aux-build:issue-14421.rs -extern crate bug_lib = "issue-14421"; +extern crate "issue-14421" as bug_lib; use bug_lib::B; use bug_lib::make; diff --git a/src/test/run-pass/issue-14422.rs b/src/test/run-pass/issue-14422.rs index b4d5a377b99..439998c597d 100644 --- a/src/test/run-pass/issue-14422.rs +++ b/src/test/run-pass/issue-14422.rs @@ -10,7 +10,7 @@ // aux-build:issue-14422.rs -extern crate bug_lib = "issue-14422"; +extern crate "issue-14422" as bug_lib; use bug_lib::B; use bug_lib::make; diff --git a/src/test/run-pass/issue-14936.rs b/src/test/run-pass/issue-14936.rs new file mode 100644 index 00000000000..0aca74c76c3 --- /dev/null +++ b/src/test/run-pass/issue-14936.rs @@ -0,0 +1,55 @@ +// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(asm, macro_rules)] + +type History = Vec<&'static str>; + +fn wrap<A>(x:A, which: &'static str, history: &mut History) -> A { + history.push(which); + x +} + +macro_rules! demo { + ( $output_constraint:tt ) => { + { + let mut x: int = 0; + let y: int = 1; + + let mut history: History = vec!(); + unsafe { + asm!("mov ($1), $0" + : $output_constraint (*wrap(&mut x, "out", &mut history)) + : "r"(&wrap(y, "in", &mut history))); + } + assert_eq!((x,y), (1,1)); + let b: &[_] = &["out", "in"]; + assert_eq!(history.as_slice(), b); + } + } +} + +#[cfg(target_arch = "x86")] +#[cfg(target_arch = "x86_64")] +fn main() { + fn out_write_only_expr_then_in_expr() { + demo!("=r") + } + + fn out_read_write_expr_then_in_expr() { + demo!("+r") + } + + out_write_only_expr_then_in_expr(); + out_read_write_expr_then_in_expr(); +} + +#[cfg(not(target_arch = "x86"), not(target_arch = "x86_64"))] +pub fn main() {} diff --git a/src/test/run-pass/issue-14958.rs b/src/test/run-pass/issue-14958.rs index b53c2258736..c2bd8c5b3e5 100644 --- a/src/test/run-pass/issue-14958.rs +++ b/src/test/run-pass/issue-14958.rs @@ -14,7 +14,7 @@ trait Foo {} struct Bar; -impl<'a> std::ops::Fn<(&'a Foo,), ()> for Bar { +impl<'a> std::ops::Fn<(&'a Foo+'a,), ()> for Bar { extern "rust-call" fn call(&self, _: (&'a Foo,)) {} } diff --git a/src/test/run-pass/issue-14959.rs b/src/test/run-pass/issue-14959.rs index af0bc78094e..74b9df9b88d 100644 --- a/src/test/run-pass/issue-14959.rs +++ b/src/test/run-pass/issue-14959.rs @@ -33,8 +33,8 @@ impl Alloy { } } -impl<'a, 'b> Fn<(&'b mut Response,),()> for SendFile<'a> { - extern "rust-call" fn call(&self, (_res,): (&'b mut Response,)) {} +impl<'a, 'b> Fn<(&'b mut Response+'b,),()> for SendFile<'a> { + extern "rust-call" fn call(&self, (_res,): (&'b mut Response+'b,)) {} } impl<Rq: Request, Rs: Response> Ingot<Rq, Rs> for HelloWorld { diff --git a/src/test/run-pass/issue-15080.rs b/src/test/run-pass/issue-15080.rs index b0741391d80..444e8bd3770 100644 --- a/src/test/run-pass/issue-15080.rs +++ b/src/test/run-pass/issue-15080.rs @@ -9,7 +9,7 @@ // except according to those terms. fn main() { - let mut x = &[1i, 2, 3, 4]; + let mut x: &[_] = &[1i, 2, 3, 4]; let mut result = vec!(); loop { diff --git a/src/test/run-pass/issue-15562.rs b/src/test/run-pass/issue-15562.rs new file mode 100644 index 00000000000..96de6c90b66 --- /dev/null +++ b/src/test/run-pass/issue-15562.rs @@ -0,0 +1,29 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// aux-build:issue-15562.rs + +extern crate i = "issue-15562"; + +pub fn main() { + extern { + fn transmute(); + } + unsafe { + transmute(); + i::transmute(); + } +} + +// We declare this so we don't run into unresolved symbol errors +// The above extern is NOT `extern "rust-intrinsic"` and thus +// means it'll try to find a corresponding symbol to link to. +#[no_mangle] +pub extern fn transmute() {} diff --git a/src/test/compile-fail/regions-bound-lists-feature-gate-2.rs b/src/test/run-pass/issue-16643.rs index 0f79716f370..db877aaafca 100644 --- a/src/test/compile-fail/regions-bound-lists-feature-gate-2.rs +++ b/src/test/run-pass/issue-16643.rs @@ -8,11 +8,10 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// ignore-pretty +// aux-build:issue-16643.rs -trait Foo { } +extern crate i = "issue-16643"; -fn foo<'a, 'b:'a>() { //~ ERROR region bounds require `issue_5723_bootstrap` +pub fn main() { + i::TreeBuilder::<uint>.process_token(); } - -pub fn main() { } diff --git a/src/test/run-pass/issue-16671.rs b/src/test/run-pass/issue-16671.rs new file mode 100644 index 00000000000..a0d384418f9 --- /dev/null +++ b/src/test/run-pass/issue-16671.rs @@ -0,0 +1,24 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![forbid(warnings)] + +// Pretty printing tests complain about `use std::predule::*` +#![allow(unused_imports)] + +// A var moved into a proc, that has a mutable loan path should +// not trigger a misleading unused_mut warning. + +pub fn main() { + let mut stdin = std::io::stdin(); + spawn(proc() { + let _ = stdin.lines(); + }); +} diff --git a/src/test/run-pass/issue-2734.rs b/src/test/run-pass/issue-2734.rs index 11ebf014bc6..9eec2d048d4 100644 --- a/src/test/run-pass/issue-2734.rs +++ b/src/test/run-pass/issue-2734.rs @@ -12,8 +12,8 @@ trait hax { } impl<A> hax for A { } -fn perform_hax<T: 'static>(x: Box<T>) -> Box<hax> { - box x as Box<hax> +fn perform_hax<T: 'static>(x: Box<T>) -> Box<hax+'static> { + box x as Box<hax+'static> } fn deadcode() { diff --git a/src/test/run-pass/issue-2735.rs b/src/test/run-pass/issue-2735.rs index 1a5b175cffc..74b64bb87cf 100644 --- a/src/test/run-pass/issue-2735.rs +++ b/src/test/run-pass/issue-2735.rs @@ -12,8 +12,8 @@ trait hax { } impl<A> hax for A { } -fn perform_hax<T: 'static>(x: Box<T>) -> Box<hax> { - box x as Box<hax> +fn perform_hax<T: 'static>(x: Box<T>) -> Box<hax+'static> { + box x as Box<hax+'static> } fn deadcode() { diff --git a/src/test/run-pass/issue-2804.rs b/src/test/run-pass/issue-2804.rs index a0a800e0890..d4b77ea4976 100644 --- a/src/test/run-pass/issue-2804.rs +++ b/src/test/run-pass/issue-2804.rs @@ -50,7 +50,7 @@ fn add_interface(_store: int, managed_ip: String, data: json::Json) -> (String, (label, bool_value(false)) } _ => { - println!("Expected dict for {} interfaces but found {:?}", managed_ip, data); + println!("Expected dict for {} interfaces, found {:?}", managed_ip, data); ("gnos:missing-interface".to_string(), bool_value(true)) } } @@ -68,7 +68,7 @@ fn add_interfaces(store: int, managed_ip: String, device: HashMap<String, json:: } _ => { - println!("Expected list for {} interfaces but found {:?}", managed_ip, + println!("Expected list for {} interfaces, found {:?}", managed_ip, device.get(&"interfaces".to_string())); Vec::new() } diff --git a/src/test/run-pass/issue-3424.rs b/src/test/run-pass/issue-3424.rs index b7818ea732a..2dac64b2ec8 100644 --- a/src/test/run-pass/issue-3424.rs +++ b/src/test/run-pass/issue-3424.rs @@ -15,7 +15,7 @@ use std::path::{Path}; use std::path; use std::result; -type rsrc_loader = proc(path: &Path) -> result::Result<String, String>; +type rsrc_loader = proc(path: &Path):'static -> result::Result<String, String>; fn tester() { diff --git a/src/test/run-pass/issue-4545.rs b/src/test/run-pass/issue-4545.rs index fdcee2e3b61..bf03b4c4532 100644 --- a/src/test/run-pass/issue-4545.rs +++ b/src/test/run-pass/issue-4545.rs @@ -10,5 +10,5 @@ // aux-build:issue-4545.rs -extern crate somelib = "issue-4545"; +extern crate "issue-4545" as somelib; pub fn main() { somelib::mk::<int>(); } diff --git a/src/test/run-pass/issue-5192.rs b/src/test/run-pass/issue-5192.rs index 0dd6623a349..3b1a8c4a190 100644 --- a/src/test/run-pass/issue-5192.rs +++ b/src/test/run-pass/issue-5192.rs @@ -28,12 +28,12 @@ impl EventLoop for UvEventLoop { } pub struct Scheduler { - event_loop: Box<EventLoop>, + event_loop: Box<EventLoop+'static>, } impl Scheduler { - pub fn new(event_loop: Box<EventLoop>) -> Scheduler { + pub fn new(event_loop: Box<EventLoop+'static>) -> Scheduler { Scheduler { event_loop: event_loop, } diff --git a/src/test/run-pass/issue-5518.rs b/src/test/run-pass/issue-5518.rs index bc24870e5df..a28bc9f825d 100644 --- a/src/test/run-pass/issue-5518.rs +++ b/src/test/run-pass/issue-5518.rs @@ -10,6 +10,6 @@ // aux-build:issue-5518.rs -extern crate other = "issue-5518"; +extern crate "issue-5518" as other; fn main() {} diff --git a/src/test/run-pass/issue-5521.rs b/src/test/run-pass/issue-5521.rs index 150bd9a74a9..8bf87e7f483 100644 --- a/src/test/run-pass/issue-5521.rs +++ b/src/test/run-pass/issue-5521.rs @@ -11,7 +11,7 @@ // aux-build:issue-5521.rs -extern crate foo = "issue-5521"; +extern crate "issue-5521" as foo; fn bar(a: foo::map) { if false { diff --git a/src/test/run-pass/issue-5554.rs b/src/test/run-pass/issue-5554.rs index 9151fb2b764..24dcc3838c5 100644 --- a/src/test/run-pass/issue-5554.rs +++ b/src/test/run-pass/issue-5554.rs @@ -17,7 +17,10 @@ pub struct X<T> { } // reordering these bounds stops the ICE -impl<T: Default + PartialEq + Default> Default for X<T> { +// +// nmatsakis: This test used to have the bounds Default + PartialEq + +// Default, but having duplicate bounds became illegal. +impl<T: Default + PartialEq> Default for X<T> { fn default() -> X<T> { X { a: Default::default() } } diff --git a/src/test/run-pass/issue-5708.rs b/src/test/run-pass/issue-5708.rs index 2bb320e5562..6168753b6d7 100644 --- a/src/test/run-pass/issue-5708.rs +++ b/src/test/run-pass/issue-5708.rs @@ -29,7 +29,7 @@ impl Inner for int { } struct Outer<'a> { - inner: &'a Inner + inner: &'a Inner+'a } impl<'a> Outer<'a> { @@ -51,7 +51,7 @@ pub fn main() { trait MyTrait<T> { } pub struct MyContainer<'a, T> { - foos: Vec<&'a MyTrait<T>> , + foos: Vec<&'a MyTrait<T>+'a> , } impl<'a, T> MyContainer<'a, T> { diff --git a/src/test/run-pass/issue-6318.rs b/src/test/run-pass/issue-6318.rs index 6512db3b1c5..a4576bc7c8c 100644 --- a/src/test/run-pass/issue-6318.rs +++ b/src/test/run-pass/issue-6318.rs @@ -10,7 +10,7 @@ pub enum Thing { - A(Box<Foo>) + A(Box<Foo+'static>) } pub trait Foo {} @@ -20,7 +20,7 @@ pub struct Struct; impl Foo for Struct {} pub fn main() { - match A(box Struct as Box<Foo>) { + match A(box Struct as Box<Foo+'static>) { A(_a) => 0i, }; } diff --git a/src/test/run-pass/issue-7012.rs b/src/test/run-pass/issue-7012.rs index 4e74a7a1ecf..96db28f4a10 100644 --- a/src/test/run-pass/issue-7012.rs +++ b/src/test/run-pass/issue-7012.rs @@ -22,6 +22,7 @@ static test1: signature<'static> = signature { }; pub fn main() { - let test = &[0x243f6a88u32,0x85a308d3u32,0x13198a2eu32,0x03707344u32,0xa4093822u32,0x299f31d0u32]; + let test: &[u32] = &[0x243f6a88u32,0x85a308d3u32,0x13198a2eu32, + 0x03707344u32,0xa4093822u32,0x299f31d0u32]; println!("{}",test==test1.pattern); } diff --git a/src/test/run-pass/issue-7178.rs b/src/test/run-pass/issue-7178.rs index b11521b23b2..4acb4959724 100644 --- a/src/test/run-pass/issue-7178.rs +++ b/src/test/run-pass/issue-7178.rs @@ -10,7 +10,7 @@ // aux-build:issue-7178.rs -extern crate cross_crate_self = "issue-7178"; +extern crate "issue-7178" as cross_crate_self; pub fn main() { let _ = cross_crate_self::Foo::new(&1i); diff --git a/src/test/run-pass/issue-7899.rs b/src/test/run-pass/issue-7899.rs index 84c7cce2276..4b3d46f4833 100644 --- a/src/test/run-pass/issue-7899.rs +++ b/src/test/run-pass/issue-7899.rs @@ -10,7 +10,7 @@ // aux-build:issue-7899.rs -extern crate testcrate = "issue-7899"; +extern crate "issue-7899" as testcrate; fn main() { let f = testcrate::V2(1.0f32, 2.0f32); diff --git a/src/test/run-pass/issue-8044.rs b/src/test/run-pass/issue-8044.rs index e5949e23467..c50cf845d00 100644 --- a/src/test/run-pass/issue-8044.rs +++ b/src/test/run-pass/issue-8044.rs @@ -10,7 +10,7 @@ // aux-build:issue-8044.rs -extern crate minimal = "issue-8044"; +extern crate "issue-8044" as minimal; use minimal::{BTree, leaf}; pub fn main() { diff --git a/src/test/run-pass/issue-8249.rs b/src/test/run-pass/issue-8249.rs index 3ca6c806ef5..dae5db11b0a 100644 --- a/src/test/run-pass/issue-8249.rs +++ b/src/test/run-pass/issue-8249.rs @@ -13,7 +13,7 @@ struct B; impl A for B {} struct C<'a> { - foo: &'a mut A, + foo: &'a mut A+'a, } fn foo(a: &mut A) { diff --git a/src/test/run-pass/issue-8259.rs b/src/test/run-pass/issue-8259.rs index 484df474403..4805b7713ee 100644 --- a/src/test/run-pass/issue-8259.rs +++ b/src/test/run-pass/issue-8259.rs @@ -10,7 +10,7 @@ // aux-build:issue-8259.rs -extern crate other = "issue-8259"; +extern crate "issue-8259" as other; static a: other::Foo<'static> = other::A; pub fn main() {} diff --git a/src/test/run-pass/issue-9259.rs b/src/test/run-pass/issue-9259.rs index 35f51efe135..2818b824969 100644 --- a/src/test/run-pass/issue-9259.rs +++ b/src/test/run-pass/issue-9259.rs @@ -14,7 +14,7 @@ struct A<'a> { } pub fn main() { - let b = &["foo".to_string()]; + let b: &[String] = &["foo".to_string()]; let a = A { a: &["test".to_string()], b: Some(b), diff --git a/src/test/run-pass/issue-9719.rs b/src/test/run-pass/issue-9719.rs index ac200e6c1ac..0f054a3083d 100644 --- a/src/test/run-pass/issue-9719.rs +++ b/src/test/run-pass/issue-9719.rs @@ -16,7 +16,7 @@ mod a { pub trait X {} impl X for int {} - pub struct Z<'a>(Enum<&'a X>); + pub struct Z<'a>(Enum<&'a X+'a>); fn foo() { let x = 42i; let z = Z(A(&x as &X)); let _ = z; } } @@ -24,7 +24,7 @@ mod b { trait X {} impl X for int {} struct Y<'a>{ - x:Option<&'a X>, + x:Option<&'a X+'a>, } fn bar() { @@ -36,7 +36,7 @@ mod b { mod c { pub trait X { fn f(&self); } impl X for int { fn f(&self) {} } - pub struct Z<'a>(Option<&'a X>); + pub struct Z<'a>(Option<&'a X+'a>); fn main() { let x = 42i; let z = Z(Some(&x as &X)); let _ = z; } } diff --git a/src/test/run-pass/issue-9906.rs b/src/test/run-pass/issue-9906.rs index dd12ea8b765..d8e28e449ee 100644 --- a/src/test/run-pass/issue-9906.rs +++ b/src/test/run-pass/issue-9906.rs @@ -10,7 +10,7 @@ // aux-build:issue-9906.rs -extern crate testmod = "issue-9906"; +extern crate "issue-9906" as testmod; pub fn main() { testmod::foo(); diff --git a/src/test/run-pass/issue-9968.rs b/src/test/run-pass/issue-9968.rs index 8768a76a9a2..2c9382be9b1 100644 --- a/src/test/run-pass/issue-9968.rs +++ b/src/test/run-pass/issue-9968.rs @@ -10,7 +10,7 @@ // aux-build:issue-9968.rs -extern crate lib = "issue-9968"; +extern crate "issue-9968" as lib; use lib::{Trait, Struct}; diff --git a/src/test/run-pass/kindck-owned-trait-contains-1.rs b/src/test/run-pass/kindck-owned-trait-contains-1.rs index 511629a6d7a..fbd6c92a020 100644 --- a/src/test/run-pass/kindck-owned-trait-contains-1.rs +++ b/src/test/run-pass/kindck-owned-trait-contains-1.rs @@ -17,9 +17,8 @@ impl<A:Clone + 'static> repeat<A> for Box<A> { } } -fn repeater<A:Clone + 'static>(v: Box<A>) -> Box<repeat<A>> { - // Note: owned kind is not necessary as A appears in the trait type - box v as Box<repeat<A>> // No +fn repeater<A:Clone + 'static>(v: Box<A>) -> Box<repeat<A>+'static> { + box v as Box<repeat<A>+'static> // No } pub fn main() { diff --git a/src/test/run-pass/lang-item-public.rs b/src/test/run-pass/lang-item-public.rs index f12c66ae7db..abd5f8326b0 100644 --- a/src/test/run-pass/lang-item-public.rs +++ b/src/test/run-pass/lang-item-public.rs @@ -14,7 +14,7 @@ #![no_std] -extern crate lang_lib = "lang-item-public"; +extern crate "lang-item-public" as lang_lib; #[cfg(target_os = "linux")] #[link(name = "c")] diff --git a/src/test/run-pass/linkage-visibility.rs b/src/test/run-pass/linkage-visibility.rs index 5e9e252cefa..80a859c03bc 100644 --- a/src/test/run-pass/linkage-visibility.rs +++ b/src/test/run-pass/linkage-visibility.rs @@ -10,9 +10,9 @@ // aux-build:linkage-visibility.rs // ignore-android: FIXME(#10379) -// ignore-windows: std::dynamic_lib does not work on win32 well +// ignore-windows: std::dynamic_lib does not work on Windows well -extern crate foo = "linkage-visibility"; +extern crate "linkage-visibility" as foo; pub fn main() { foo::test(); diff --git a/src/test/run-pass/linkage1.rs b/src/test/run-pass/linkage1.rs index 6d750ac1913..0ba7dcb013b 100644 --- a/src/test/run-pass/linkage1.rs +++ b/src/test/run-pass/linkage1.rs @@ -15,7 +15,7 @@ #![feature(linkage)] -extern crate other = "linkage1"; +extern crate "linkage1" as other; extern { #[linkage = "extern_weak"] diff --git a/src/test/run-pass/match-vec-alternatives.rs b/src/test/run-pass/match-vec-alternatives.rs index ffbc4e85cb6..de1bb02bfef 100644 --- a/src/test/run-pass/match-vec-alternatives.rs +++ b/src/test/run-pass/match-vec-alternatives.rs @@ -68,15 +68,21 @@ fn main() { assert_eq!(match_vecs_snoc::<uint>(&[], &[]), "both empty"); assert_eq!(match_vecs_snoc(&[1i, 2, 3], &[]), "one empty"); - assert_eq!(match_nested_vecs_cons(None, Ok(&[4u, 2u])), "None, Ok(at least two elements)"); + assert_eq!(match_nested_vecs_cons(None, Ok::<&[_], ()>(&[4u, 2u])), + "None, Ok(at least two elements)"); assert_eq!(match_nested_vecs_cons::<uint>(None, Err(())), "None, Ok(less than one element)"); - assert_eq!(match_nested_vecs_cons::<bool>(Some(&[]), Ok(&[])), "Some(empty), Ok(empty)"); - assert_eq!(match_nested_vecs_cons(Some(&[1i]), Err(())), "Some(non-empty), any"); - assert_eq!(match_nested_vecs_cons(Some(&[(42i, ())]), Ok(&[(1i, ())])), "Some(non-empty), any"); + assert_eq!(match_nested_vecs_cons::<bool>(Some::<&[_]>(&[]), Ok::<&[_], ()>(&[])), + "Some(empty), Ok(empty)"); + assert_eq!(match_nested_vecs_cons(Some::<&[_]>(&[1i]), Err(())), "Some(non-empty), any"); + assert_eq!(match_nested_vecs_cons(Some::<&[_]>(&[(42i, ())]), Ok::<&[_], ()>(&[(1i, ())])), + "Some(non-empty), any"); - assert_eq!(match_nested_vecs_snoc(None, Ok(&[4u, 2u])), "None, Ok(at least two elements)"); + assert_eq!(match_nested_vecs_snoc(None, Ok::<&[_], ()>(&[4u, 2u])), + "None, Ok(at least two elements)"); assert_eq!(match_nested_vecs_snoc::<uint>(None, Err(())), "None, Ok(less than one element)"); - assert_eq!(match_nested_vecs_snoc::<bool>(Some(&[]), Ok(&[])), "Some(empty), Ok(empty)"); - assert_eq!(match_nested_vecs_snoc(Some(&[1i]), Err(())), "Some(non-empty), any"); - assert_eq!(match_nested_vecs_snoc(Some(&[(42i, ())]), Ok(&[(1i, ())])), "Some(non-empty), any"); + assert_eq!(match_nested_vecs_snoc::<bool>(Some::<&[_]>(&[]), Ok::<&[_], ()>(&[])), + "Some(empty), Ok(empty)"); + assert_eq!(match_nested_vecs_snoc(Some::<&[_]>(&[1i]), Err(())), "Some(non-empty), any"); + assert_eq!(match_nested_vecs_snoc(Some::<&[_]>(&[(42i, ())]), Ok::<&[_], ()>(&[(1i, ())])), + "Some(non-empty), any"); } diff --git a/src/test/run-pass/newlambdas-ret-infer.rs b/src/test/run-pass/newlambdas-ret-infer.rs index 84d49820239..f704545af33 100644 --- a/src/test/run-pass/newlambdas-ret-infer.rs +++ b/src/test/run-pass/newlambdas-ret-infer.rs @@ -11,7 +11,7 @@ // Test that the lambda kind is inferred correctly as a return // expression -fn unique() -> proc() { return proc() (); } +fn unique() -> proc():'static { return proc() (); } pub fn main() { } diff --git a/src/test/run-pass/newlambdas-ret-infer2.rs b/src/test/run-pass/newlambdas-ret-infer2.rs index 86ad53c0228..22e51ea9a75 100644 --- a/src/test/run-pass/newlambdas-ret-infer2.rs +++ b/src/test/run-pass/newlambdas-ret-infer2.rs @@ -11,7 +11,7 @@ // Test that the lambda kind is inferred correctly as a return // expression -fn unique() -> proc() { proc() () } +fn unique() -> proc():'static { proc() () } pub fn main() { } diff --git a/src/test/run-pass/order-drop-with-match.rs b/src/test/run-pass/order-drop-with-match.rs index ed5cff36c8b..9a76beac9e5 100644 --- a/src/test/run-pass/order-drop-with-match.rs +++ b/src/test/run-pass/order-drop-with-match.rs @@ -59,6 +59,7 @@ fn main() { } } unsafe { - assert_eq!(&[1, 2, 3], ORDER.as_slice()); + let expected: &[_] = &[1, 2, 3]; + assert_eq!(expected, ORDER.as_slice()); } } diff --git a/src/test/run-pass/overloaded-autoderef-indexing.rs b/src/test/run-pass/overloaded-autoderef-indexing.rs index 37e7ee6c216..5c4befcd0c8 100644 --- a/src/test/run-pass/overloaded-autoderef-indexing.rs +++ b/src/test/run-pass/overloaded-autoderef-indexing.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -struct DerefArray<'a, T> { +struct DerefArray<'a, T:'a> { inner: &'a [T] } diff --git a/src/test/run-pass/overloaded-calls-param-vtables.rs b/src/test/run-pass/overloaded-calls-param-vtables.rs new file mode 100644 index 00000000000..6f870f0afd5 --- /dev/null +++ b/src/test/run-pass/overloaded-calls-param-vtables.rs @@ -0,0 +1,29 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Tests that nested vtables work with overloaded calls. + +#![feature(overloaded_calls)] + +use std::ops::Fn; + +struct G; + +impl<'a, A: Add<int, int>> Fn<(A,), int> for G { + extern "rust-call" fn call(&self, (arg,): (A,)) -> int { + arg.add(&1) + } +} + +fn main() { + // ICE trigger + G(1i); +} + diff --git a/src/test/run-pass/overloaded-deref-count.rs b/src/test/run-pass/overloaded-deref-count.rs index 0b5406b8f67..2d546639939 100644 --- a/src/test/run-pass/overloaded-deref-count.rs +++ b/src/test/run-pass/overloaded-deref-count.rs @@ -81,5 +81,6 @@ pub fn main() { // Check the final states. assert_eq!(*n, 2); - assert_eq!((*v).as_slice(), &[1, 2]); + let expected: &[_] = &[1, 2]; + assert_eq!((*v).as_slice(), expected); } diff --git a/src/test/run-pass/packed-struct-borrow-element.rs b/src/test/run-pass/packed-struct-borrow-element.rs index 1434e1da4c7..c6c74fe3fda 100644 --- a/src/test/run-pass/packed-struct-borrow-element.rs +++ b/src/test/run-pass/packed-struct-borrow-element.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#[packed] +#[repr(packed)] struct Foo { bar: u8, baz: uint diff --git a/src/test/run-pass/packed-struct-generic-layout.rs b/src/test/run-pass/packed-struct-generic-layout.rs index 20269766158..999e4aeeb59 100644 --- a/src/test/run-pass/packed-struct-generic-layout.rs +++ b/src/test/run-pass/packed-struct-generic-layout.rs @@ -10,7 +10,7 @@ use std::mem; -#[packed] +#[repr(packed)] struct S<T, S> { a: T, b: u8, diff --git a/src/test/run-pass/packed-struct-generic-size.rs b/src/test/run-pass/packed-struct-generic-size.rs index 80f37a0fa3d..45791332bbe 100644 --- a/src/test/run-pass/packed-struct-generic-size.rs +++ b/src/test/run-pass/packed-struct-generic-size.rs @@ -10,7 +10,7 @@ use std::mem; -#[packed] +#[repr(packed)] struct S<T, S> { a: T, b: u8, diff --git a/src/test/run-pass/packed-struct-layout.rs b/src/test/run-pass/packed-struct-layout.rs index 7f9bf8e7d57..b4fbf0820cd 100644 --- a/src/test/run-pass/packed-struct-layout.rs +++ b/src/test/run-pass/packed-struct-layout.rs @@ -10,13 +10,13 @@ use std::mem; -#[packed] +#[repr(packed)] struct S4 { a: u8, b: [u8, .. 3], } -#[packed] +#[repr(packed)] struct S5 { a: u8, b: u32 diff --git a/src/test/run-pass/packed-struct-match.rs b/src/test/run-pass/packed-struct-match.rs index 27ab2c83e55..46ffed0cba9 100644 --- a/src/test/run-pass/packed-struct-match.rs +++ b/src/test/run-pass/packed-struct-match.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#[packed] +#[repr(packed)] struct Foo { bar: u8, baz: uint diff --git a/src/test/run-pass/packed-struct-size.rs b/src/test/run-pass/packed-struct-size.rs index 8dbc7cf327d..cfea444d7ff 100644 --- a/src/test/run-pass/packed-struct-size.rs +++ b/src/test/run-pass/packed-struct-size.rs @@ -12,19 +12,19 @@ use std::mem; use std::gc::Gc; -#[packed] +#[repr(packed)] struct S4 { a: u8, b: [u8, .. 3], } -#[packed] +#[repr(packed)] struct S5 { a: u8, b: u32 } -#[packed] +#[repr(packed)] struct S13 { a: i64, b: f32, @@ -36,14 +36,14 @@ enum Foo { Baz = 2 } -#[packed] +#[repr(packed)] struct S3_Foo { a: u8, b: u16, c: Foo } -#[packed] +#[repr(packed)] struct S7_Option { a: f32, b: u8, diff --git a/src/test/run-pass/packed-struct-vec.rs b/src/test/run-pass/packed-struct-vec.rs index 8309e95820c..c20e62351a6 100644 --- a/src/test/run-pass/packed-struct-vec.rs +++ b/src/test/run-pass/packed-struct-vec.rs @@ -12,7 +12,7 @@ use std::mem; -#[packed] +#[repr(packed)] #[deriving(PartialEq, Show)] struct Foo { bar: u8, diff --git a/src/test/run-pass/packed-tuple-struct-layout.rs b/src/test/run-pass/packed-tuple-struct-layout.rs index 3ec6182beb2..5fb43503ccb 100644 --- a/src/test/run-pass/packed-tuple-struct-layout.rs +++ b/src/test/run-pass/packed-tuple-struct-layout.rs @@ -10,10 +10,10 @@ use std::mem; -#[packed] +#[repr(packed)] struct S4(u8,[u8, .. 3]); -#[packed] +#[repr(packed)] struct S5(u8,u32); pub fn main() { diff --git a/src/test/run-pass/packed-tuple-struct-size.rs b/src/test/run-pass/packed-tuple-struct-size.rs index da6e9a9dac0..f23166288fb 100644 --- a/src/test/run-pass/packed-tuple-struct-size.rs +++ b/src/test/run-pass/packed-tuple-struct-size.rs @@ -12,13 +12,13 @@ use std::gc::Gc; use std::mem; -#[packed] +#[repr(packed)] struct S4(u8,[u8, .. 3]); -#[packed] +#[repr(packed)] struct S5(u8, u32); -#[packed] +#[repr(packed)] struct S13(i64, f32, u8); enum Foo { @@ -26,10 +26,10 @@ enum Foo { Baz = 2 } -#[packed] +#[repr(packed)] struct S3_Foo(u8, u16, Foo); -#[packed] +#[repr(packed)] struct S7_Option(f32, u8, u16, Option<Gc<f64>>); pub fn main() { diff --git a/src/test/run-pass/priv-impl-prim-ty.rs b/src/test/run-pass/priv-impl-prim-ty.rs index 6af18992636..a8cd5e789b1 100644 --- a/src/test/run-pass/priv-impl-prim-ty.rs +++ b/src/test/run-pass/priv-impl-prim-ty.rs @@ -10,7 +10,7 @@ // aux-build:priv-impl-prim-ty.rs -extern crate bar = "priv-impl-prim-ty"; +extern crate "priv-impl-prim-ty" as bar; pub fn main() { bar::frob(1i); diff --git a/src/test/run-pass/realloc-16687.rs b/src/test/run-pass/realloc-16687.rs new file mode 100644 index 00000000000..2e8c23fe5ba --- /dev/null +++ b/src/test/run-pass/realloc-16687.rs @@ -0,0 +1,167 @@ +// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// alloc::heap::reallocate test. +// +// Ideally this would be revised to use no_std, but for now it serves +// well enough to reproduce (and illustrate) the bug from #16687. + +extern crate alloc; + +use alloc::heap; +use std::ptr; + +fn main() { + unsafe { + assert!(test_triangle()); + } +} + +unsafe fn test_triangle() -> bool { + static COUNT : uint = 16; + let mut ascend = Vec::from_elem(COUNT, ptr::mut_null()); + let ascend = ascend.as_mut_slice(); + static ALIGN : uint = 1; + + // Checks that `ascend` forms triangle of acending size formed + // from pairs of rows (where each pair of rows is equally sized), + // and the elements of the triangle match their row-pair index. + unsafe fn sanity_check(ascend: &[*mut u8]) { + for i in range(0u, COUNT / 2) { + let (p0, p1, size) = (ascend[2*i], ascend[2*i+1], idx_to_size(i)); + for j in range(0u, size) { + assert_eq!(*p0.offset(j as int), i as u8); + assert_eq!(*p1.offset(j as int), i as u8); + } + } + } + + static PRINT : bool = false; + + unsafe fn allocate(size: uint, align: uint) -> *mut u8 { + if PRINT { println!("allocate(size={:u} align={:u})", size, align); } + + let ret = heap::allocate(size, align); + + if PRINT { println!("allocate(size={:u} align={:u}) ret: 0x{:010x}", + size, align, ret as uint); + } + + ret + } + unsafe fn reallocate(ptr: *mut u8, size: uint, align: uint, + old_size: uint) -> *mut u8 { + if PRINT { + println!("reallocate(ptr=0x{:010x} size={:u} align={:u} old_size={:u})", + ptr as uint, size, align, old_size); + } + + let ret = heap::reallocate(ptr, size, align, old_size); + + if PRINT { + println!("reallocate(ptr=0x{:010x} size={:u} align={:u} old_size={:u}) \ + ret: 0x{:010x}", + ptr as uint, size, align, old_size, ret as uint); + } + ret + } + + fn idx_to_size(i: uint) -> uint { (i+1) * 10 } + + // Allocate pairs of rows that form a triangle shape. (Hope is + // that at least two rows will be allocated near each other, so + // that we trigger the bug (a buffer overrun) in an observable + // way.) + for i in range(0u, COUNT / 2) { + let size = idx_to_size(i); + ascend[2*i] = allocate(size, ALIGN); + ascend[2*i+1] = allocate(size, ALIGN); + } + + // Initialize each pair of rows to distinct value. + for i in range(0u, COUNT / 2) { + let (p0, p1, size) = (ascend[2*i], ascend[2*i+1], idx_to_size(i)); + for j in range(0, size) { + *p0.offset(j as int) = i as u8; + *p1.offset(j as int) = i as u8; + } + } + + sanity_check(ascend.as_slice()); + test_1(ascend); + test_2(ascend); + test_3(ascend); + test_4(ascend); + + return true; + + // Test 1: turn the triangle into a square (in terms of + // allocation; initialized portion remains a triangle) by + // realloc'ing each row from top to bottom, and checking all the + // rows as we go. + unsafe fn test_1(ascend: &mut [*mut u8]) { + let new_size = idx_to_size(COUNT-1); + for i in range(0u, COUNT / 2) { + let (p0, p1, old_size) = (ascend[2*i], ascend[2*i+1], idx_to_size(i)); + assert!(old_size < new_size); + + ascend[2*i] = reallocate(p0, new_size, ALIGN, old_size); + sanity_check(ascend.as_slice()); + + ascend[2*i+1] = reallocate(p1, new_size, ALIGN, old_size); + sanity_check(ascend.as_slice()); + } + } + + // Test 2: turn the square back into a triangle, top to bottom. + unsafe fn test_2(ascend: &mut [*mut u8]) { + let old_size = idx_to_size(COUNT-1); + for i in range(0u, COUNT / 2) { + let (p0, p1, new_size) = (ascend[2*i], ascend[2*i+1], idx_to_size(i)); + assert!(new_size < old_size); + + ascend[2*i] = reallocate(p0, new_size, ALIGN, old_size); + sanity_check(ascend.as_slice()); + + ascend[2*i+1] = reallocate(p1, new_size, ALIGN, old_size); + sanity_check(ascend.as_slice()); + } + } + + // Test 3: turn triangle into a square, bottom to top. + unsafe fn test_3(ascend: &mut [*mut u8]) { + let new_size = idx_to_size(COUNT-1); + for i in range(0u, COUNT / 2).rev() { + let (p0, p1, old_size) = (ascend[2*i], ascend[2*i+1], idx_to_size(i)); + assert!(old_size < new_size); + + ascend[2*i+1] = reallocate(p1, new_size, ALIGN, old_size); + sanity_check(ascend.as_slice()); + + ascend[2*i] = reallocate(p0, new_size, ALIGN, old_size); + sanity_check(ascend.as_slice()); + } + } + + // Test 4: turn the square back into a triangle, bottom to top. + unsafe fn test_4(ascend: &mut [*mut u8]) { + let old_size = idx_to_size(COUNT-1); + for i in range(0u, COUNT / 2).rev() { + let (p0, p1, new_size) = (ascend[2*i], ascend[2*i+1], idx_to_size(i)); + assert!(new_size < old_size); + + ascend[2*i+1] = reallocate(p1, new_size, ALIGN, old_size); + sanity_check(ascend.as_slice()); + + ascend[2*i] = reallocate(p0, new_size, ALIGN, old_size); + sanity_check(ascend.as_slice()); + } + } +} diff --git a/src/test/run-pass/rec-align-u64.rs b/src/test/run-pass/rec-align-u64.rs index 3a4f8770122..8bd7a499de6 100644 --- a/src/test/run-pass/rec-align-u64.rs +++ b/src/test/run-pass/rec-align-u64.rs @@ -56,7 +56,6 @@ mod m { } #[cfg(target_os = "windows")] -/* NOTE: Remove after snapshot */#[cfg(stage0, target_os = "win32")] mod m { #[cfg(target_arch = "x86")] pub mod m { diff --git a/src/test/run-pass/reexport-should-still-link.rs b/src/test/run-pass/reexport-should-still-link.rs index c63d50d898c..ba74386f975 100644 --- a/src/test/run-pass/reexport-should-still-link.rs +++ b/src/test/run-pass/reexport-should-still-link.rs @@ -10,7 +10,7 @@ // aux-build:reexport-should-still-link.rs -extern crate foo = "reexport-should-still-link"; +extern crate "reexport-should-still-link" as foo; pub fn main() { foo::bar(); diff --git a/src/test/run-pass/reflect-visit-type.rs b/src/test/run-pass/reflect-visit-type.rs index 596e56b424a..f599a2b7ad5 100644 --- a/src/test/run-pass/reflect-visit-type.rs +++ b/src/test/run-pass/reflect-visit-type.rs @@ -61,6 +61,8 @@ impl TyVisitor for MyVisitor { fn visit_char(&mut self) -> bool { true } fn visit_estr_slice(&mut self) -> bool { true } + // NOTE: remove after snapshot + #[cfg(stage0)] fn visit_estr_fixed(&mut self, _sz: uint, _sz2: uint, _align: uint) -> bool { true } @@ -72,7 +74,7 @@ impl TyVisitor for MyVisitor { fn visit_evec_slice(&mut self, _mtbl: uint, _inner: *const TyDesc) -> bool { true } fn visit_evec_fixed(&mut self, _n: uint, _sz: uint, _align: uint, - _mtbl: uint, _inner: *const TyDesc) -> bool { true } + _inner: *const TyDesc) -> bool { true } fn visit_enter_rec(&mut self, _n_fields: uint, _sz: uint, _align: uint) -> bool { true } diff --git a/src/test/run-pass/regions-early-bound-trait-param.rs b/src/test/run-pass/regions-early-bound-trait-param.rs index 6deae8618fa..27ef90d7376 100644 --- a/src/test/run-pass/regions-early-bound-trait-param.rs +++ b/src/test/run-pass/regions-early-bound-trait-param.rs @@ -30,7 +30,7 @@ fn object_invoke1<'d>(x: &'d Trait<'d>) -> (int, int) { } struct Struct1<'e> { - f: &'e Trait<'e> + f: &'e Trait<'e>+'e } fn field_invoke1<'f, 'g>(x: &'g Struct1<'f>) -> (int,int) { @@ -40,7 +40,7 @@ fn field_invoke1<'f, 'g>(x: &'g Struct1<'f>) -> (int,int) { } struct Struct2<'h, 'i> { - f: &'h Trait<'i> + f: &'h Trait<'i>+'h } fn object_invoke2<'j, 'k>(x: &'k Trait<'j>) -> int { @@ -78,8 +78,8 @@ impl<'s> Trait<'s> for (int,int) { } } -impl<'t> MakerTrait<'t> for Box<Trait<'t>> { - fn mk() -> Box<Trait<'t>> { box() (4i,5i) as Box<Trait> } +impl<'t> MakerTrait<'t> for Box<Trait<'t>+'static> { + fn mk() -> Box<Trait<'t>+'static> { box() (4i,5i) as Box<Trait> } } enum List<'l> { diff --git a/src/test/run-pass/regions-early-bound-used-in-bound.rs b/src/test/run-pass/regions-early-bound-used-in-bound.rs index c262370ac5d..58de2e0e20e 100644 --- a/src/test/run-pass/regions-early-bound-used-in-bound.rs +++ b/src/test/run-pass/regions-early-bound-used-in-bound.rs @@ -15,7 +15,7 @@ trait GetRef<'a, T> { fn get(&self) -> &'a T; } -struct Box<'a, T> { +struct Box<'a, T:'a> { t: &'a T } diff --git a/src/test/run-pass/running-with-no-runtime.rs b/src/test/run-pass/running-with-no-runtime.rs index 44435dc2398..3f559df4b7e 100644 --- a/src/test/run-pass/running-with-no-runtime.rs +++ b/src/test/run-pass/running-with-no-runtime.rs @@ -43,14 +43,22 @@ fn main() { let args = os::args(); let me = args.get(0).as_slice(); - pass(Command::new(me).arg(&[1u8]).output().unwrap()); - pass(Command::new(me).arg(&[2u8]).output().unwrap()); - pass(Command::new(me).arg(&[3u8]).output().unwrap()); - pass(Command::new(me).arg(&[4u8]).output().unwrap()); - pass(Command::new(me).arg(&[5u8]).output().unwrap()); - pass(Command::new(me).arg(&[6u8]).output().unwrap()); - pass(Command::new(me).arg(&[7u8]).output().unwrap()); - pass(Command::new(me).arg(&[8u8]).output().unwrap()); + let x: &[u8] = &[1u8]; + pass(Command::new(me).arg(x).output().unwrap()); + let x: &[u8] = &[2u8]; + pass(Command::new(me).arg(x).output().unwrap()); + let x: &[u8] = &[3u8]; + pass(Command::new(me).arg(x).output().unwrap()); + let x: &[u8] = &[4u8]; + pass(Command::new(me).arg(x).output().unwrap()); + let x: &[u8] = &[5u8]; + pass(Command::new(me).arg(x).output().unwrap()); + let x: &[u8] = &[6u8]; + pass(Command::new(me).arg(x).output().unwrap()); + let x: &[u8] = &[7u8]; + pass(Command::new(me).arg(x).output().unwrap()); + let x: &[u8] = &[8u8]; + pass(Command::new(me).arg(x).output().unwrap()); } fn pass(output: ProcessOutput) { diff --git a/src/test/run-pass/slowparse-bstring.rs b/src/test/run-pass/slowparse-bstring.rs new file mode 100644 index 00000000000..b2bbd7e9103 --- /dev/null +++ b/src/test/run-pass/slowparse-bstring.rs @@ -0,0 +1,15 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// ignore-tidy-linelength + +// Issue #16624 + +fn main() { b"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; } diff --git a/src/test/run-pass/slowparse-string.rs b/src/test/run-pass/slowparse-string.rs new file mode 100644 index 00000000000..46b47c8537f --- /dev/null +++ b/src/test/run-pass/slowparse-string.rs @@ -0,0 +1,15 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// ignore-tidy-linelength + +// Issue #16624 + +fn main() { "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; } diff --git a/src/test/run-pass/smallest-hello-world.rs b/src/test/run-pass/smallest-hello-world.rs index 4f1a3817fab..52e71186537 100644 --- a/src/test/run-pass/smallest-hello-world.rs +++ b/src/test/run-pass/smallest-hello-world.rs @@ -22,6 +22,7 @@ extern "rust-intrinsic" { fn transmute<T, U>(t: T) -> U; } #[lang = "stack_exhausted"] extern fn stack_exhausted() {} #[lang = "eh_personality"] extern fn eh_personality() {} +#[lang = "sized"] pub trait Sized {} #[start] #[no_split_stack] diff --git a/src/test/run-pass/static-fn-inline-xc.rs b/src/test/run-pass/static-fn-inline-xc.rs index 0d591998bc9..dbe9221066f 100644 --- a/src/test/run-pass/static-fn-inline-xc.rs +++ b/src/test/run-pass/static-fn-inline-xc.rs @@ -10,7 +10,7 @@ // aux-build:static_fn_inline_xc_aux.rs -extern crate mycore = "static_fn_inline_xc_aux"; +extern crate "static_fn_inline_xc_aux" as mycore; use mycore::num; diff --git a/src/test/run-pass/static-fn-trait-xc.rs b/src/test/run-pass/static-fn-trait-xc.rs index 32a4046884d..5dd6b6dbc53 100644 --- a/src/test/run-pass/static-fn-trait-xc.rs +++ b/src/test/run-pass/static-fn-trait-xc.rs @@ -10,7 +10,7 @@ // aux-build:static_fn_trait_xc_aux.rs -extern crate mycore = "static_fn_trait_xc_aux"; +extern crate "static_fn_trait_xc_aux" as mycore; use mycore::num; diff --git a/src/test/run-pass/static-function-pointer-xc.rs b/src/test/run-pass/static-function-pointer-xc.rs index 8e5539ff8dc..6e12c5fa73d 100644 --- a/src/test/run-pass/static-function-pointer-xc.rs +++ b/src/test/run-pass/static-function-pointer-xc.rs @@ -9,7 +9,7 @@ // except according to those terms. // aux-build:static-function-pointer-aux.rs -extern crate aux = "static-function-pointer-aux"; +extern crate "static-function-pointer-aux" as aux; fn f(x: int) -> int { x } diff --git a/src/test/run-pass/swap-overlapping.rs b/src/test/run-pass/swap-overlapping.rs index 1a96af43d23..f33483d31bf 100644 --- a/src/test/run-pass/swap-overlapping.rs +++ b/src/test/run-pass/swap-overlapping.rs @@ -34,8 +34,8 @@ pub enum TestName { } pub enum TestFn { - DynTestFn(proc()), - DynBenchFn(proc(&mut int)) + DynTestFn(proc():'static), + DynBenchFn(proc(&mut int):'static) } pub struct TestDesc { diff --git a/src/test/run-pass/syntax-extension-bytes.rs b/src/test/run-pass/syntax-extension-bytes.rs index 5b66d5f28a9..8d5333e5b3f 100644 --- a/src/test/run-pass/syntax-extension-bytes.rs +++ b/src/test/run-pass/syntax-extension-bytes.rs @@ -12,13 +12,17 @@ static static_vec: &'static [u8] = bytes!("abc", 0xFF, '!'); pub fn main() { let vec = bytes!("abc"); - assert_eq!(vec, &[97_u8, 98_u8, 99_u8]); + let expected: &[u8] = &[97_u8, 98_u8, 99_u8]; + assert_eq!(vec, expected); let vec = bytes!("null", 0); - assert_eq!(vec, &[110_u8, 117_u8, 108_u8, 108_u8, 0_u8]); + let expected: &[u8] = &[110_u8, 117_u8, 108_u8, 108_u8, 0_u8]; + assert_eq!(vec, expected); let vec = bytes!(' ', " ", 32, 32u8); - assert_eq!(vec, &[32_u8, 32_u8, 32_u8, 32_u8]); + let expected: &[u8] = &[32_u8, 32_u8, 32_u8, 32_u8]; + assert_eq!(vec, expected); - assert_eq!(static_vec, &[97_u8, 98_u8, 99_u8, 255_u8, 33_u8]); + let expected: &[u8] = &[97_u8, 98_u8, 99_u8, 255_u8, 33_u8]; + assert_eq!(static_vec, expected); } diff --git a/src/test/run-pass/tcp-accept-stress.rs b/src/test/run-pass/tcp-accept-stress.rs new file mode 100644 index 00000000000..b8470ef7b8f --- /dev/null +++ b/src/test/run-pass/tcp-accept-stress.rs @@ -0,0 +1,94 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(phase)] + +#[phase(plugin)] +extern crate green; +extern crate native; + +use std::io::{TcpListener, Listener, Acceptor, EndOfFile, TcpStream}; +use std::sync::{atomic, Arc}; +use std::task::TaskBuilder; +use native::NativeTaskBuilder; + +static N: uint = 8; +static M: uint = 100; + +green_start!(main) + +fn main() { + test(); + + let (tx, rx) = channel(); + TaskBuilder::new().native().spawn(proc() { + tx.send(test()); + }); + rx.recv(); +} + +fn test() { + let mut l = TcpListener::bind("127.0.0.1", 0).unwrap(); + let addr = l.socket_name().unwrap(); + let mut a = l.listen().unwrap(); + let cnt = Arc::new(atomic::AtomicUint::new(0)); + + let (tx, rx) = channel(); + for _ in range(0, N) { + let a = a.clone(); + let cnt = cnt.clone(); + let tx = tx.clone(); + spawn(proc() { + let mut a = a; + let mut mycnt = 0u; + loop { + match a.accept() { + Ok(..) => { + mycnt += 1; + if cnt.fetch_add(1, atomic::SeqCst) == N * M - 1 { + break + } + } + Err(ref e) if e.kind == EndOfFile => break, + Err(e) => fail!("{}", e), + } + } + assert!(mycnt > 0); + tx.send(()); + }); + } + + for _ in range(0, N) { + let tx = tx.clone(); + spawn(proc() { + for _ in range(0, M) { + let _s = TcpStream::connect(addr.ip.to_string().as_slice(), + addr.port).unwrap(); + } + tx.send(()); + }); + } + + // wait for senders + assert_eq!(rx.iter().take(N).count(), N); + + // wait for one acceptor to die + let _ = rx.recv(); + + // Notify other receivers should die + a.close_accept().unwrap(); + + // wait for receivers + assert_eq!(rx.iter().take(N - 1).count(), N - 1); + + // Everything should have been accepted. + assert_eq!(cnt.load(atomic::SeqCst), N * M); +} + diff --git a/src/test/run-pass/trailing-comma.rs b/src/test/run-pass/trailing-comma.rs index 8d580729da9..394ff831d9b 100644 --- a/src/test/run-pass/trailing-comma.rs +++ b/src/test/run-pass/trailing-comma.rs @@ -8,9 +8,31 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -fn f(_: int,) {} +fn f<T,>(_: T,) {} + +struct Foo<T,>; + +struct Bar; + +impl Bar { + fn f(_: int,) {} + fn g(self, _: int,) {} + fn h(self,) {} +} + +enum Baz { + Qux(int,), +} pub fn main() { - f(0i,); + f::<int,>(0i,); let (_, _,) = (1i, 1i,); + + let x: Foo<int,> = Foo::<int,>; + + Bar::f(0i,); + Bar.g(0i,); + Bar.h(); + + let x = Qux(1,); } diff --git a/src/test/run-pass/trait-bounds-impl-comparison-duplicates.rs b/src/test/run-pass/trait-bounds-impl-comparison-duplicates.rs index 55ad22dd0bf..5b744a44132 100644 --- a/src/test/run-pass/trait-bounds-impl-comparison-duplicates.rs +++ b/src/test/run-pass/trait-bounds-impl-comparison-duplicates.rs @@ -17,7 +17,7 @@ trait A { } impl A for int { - fn foo<T: Ord + Ord>(&self) {} + fn foo<T: Ord>(&self) {} // Ord implies Eq, so this is ok. } fn main() {} diff --git a/src/test/run-pass/trait-bounds-on-structs-and-enums.rs b/src/test/run-pass/trait-bounds-on-structs-and-enums.rs index ebcaf772db4..e3234f03754 100644 --- a/src/test/run-pass/trait-bounds-on-structs-and-enums.rs +++ b/src/test/run-pass/trait-bounds-on-structs-and-enums.rs @@ -12,11 +12,11 @@ trait U {} trait T<X: U> {} trait S2<Y: U> { - fn m(x: Box<T<Y>>) {} + fn m(x: Box<T<Y>+'static>) {} } struct St<X: U> { - f: Box<T<X>>, + f: Box<T<X>+'static>, } impl<X: U> St<X> { diff --git a/src/test/run-pass/trait-default-method-xc-2.rs b/src/test/run-pass/trait-default-method-xc-2.rs index 447968eb8c4..1aa958dafc7 100644 --- a/src/test/run-pass/trait-default-method-xc-2.rs +++ b/src/test/run-pass/trait-default-method-xc-2.rs @@ -12,8 +12,8 @@ // aux-build:trait_default_method_xc_aux_2.rs -extern crate aux = "trait_default_method_xc_aux"; -extern crate aux2 = "trait_default_method_xc_aux_2"; +extern crate "trait_default_method_xc_aux" as aux; +extern crate "trait_default_method_xc_aux_2" as aux2; use aux::A; use aux2::{a_struct, welp}; diff --git a/src/test/run-pass/trait-default-method-xc.rs b/src/test/run-pass/trait-default-method-xc.rs index 8201c7ec347..f88522facdf 100644 --- a/src/test/run-pass/trait-default-method-xc.rs +++ b/src/test/run-pass/trait-default-method-xc.rs @@ -10,7 +10,7 @@ // aux-build:trait_default_method_xc_aux.rs -extern crate aux = "trait_default_method_xc_aux"; +extern crate "trait_default_method_xc_aux" as aux; use aux::{A, TestEquality, Something}; use aux::B; diff --git a/src/test/run-pass/trait-inheritance-auto-xc-2.rs b/src/test/run-pass/trait-inheritance-auto-xc-2.rs index 5944106ad50..d45d7ebe90a 100644 --- a/src/test/run-pass/trait-inheritance-auto-xc-2.rs +++ b/src/test/run-pass/trait-inheritance-auto-xc-2.rs @@ -10,7 +10,7 @@ // aux-build:trait_inheritance_auto_xc_2_aux.rs -extern crate aux = "trait_inheritance_auto_xc_2_aux"; +extern crate "trait_inheritance_auto_xc_2_aux" as aux; // aux defines impls of Foo, Bar and Baz for A use aux::{Foo, Bar, Baz, A}; diff --git a/src/test/run-pass/trait-inheritance-auto-xc.rs b/src/test/run-pass/trait-inheritance-auto-xc.rs index 09c58316611..f4e1908aaee 100644 --- a/src/test/run-pass/trait-inheritance-auto-xc.rs +++ b/src/test/run-pass/trait-inheritance-auto-xc.rs @@ -10,7 +10,7 @@ // aux-build:trait_inheritance_auto_xc_aux.rs -extern crate aux = "trait_inheritance_auto_xc_aux"; +extern crate "trait_inheritance_auto_xc_aux" as aux; use aux::{Foo, Bar, Baz, Quux}; diff --git a/src/test/run-pass/trait-inheritance-cross-trait-call-xc.rs b/src/test/run-pass/trait-inheritance-cross-trait-call-xc.rs index 0afd91c5857..3ee046e8bfe 100644 --- a/src/test/run-pass/trait-inheritance-cross-trait-call-xc.rs +++ b/src/test/run-pass/trait-inheritance-cross-trait-call-xc.rs @@ -10,7 +10,7 @@ // aux-build:trait_inheritance_cross_trait_call_xc_aux.rs -extern crate aux = "trait_inheritance_cross_trait_call_xc_aux"; +extern crate "trait_inheritance_cross_trait_call_xc_aux" as aux; use aux::Foo; diff --git a/src/test/run-pass/trait-object-generics.rs b/src/test/run-pass/trait-object-generics.rs index 3c5ae6b57a3..b20915763f2 100644 --- a/src/test/run-pass/trait-object-generics.rs +++ b/src/test/run-pass/trait-object-generics.rs @@ -21,7 +21,7 @@ pub struct Impl<A1, A2, A3> { * task <unnamed> failed at 'index out of bounds: the len is 1 but the index is 1', * src/librustc/middle/subst.rs:58 */ - t: Box<Trait2<A2>> + t: Box<Trait2<A2>+'static> } impl<A1, A2, A3> Impl<A1, A2, A3> { diff --git a/src/test/run-pass/typeck_type_placeholder_1.rs b/src/test/run-pass/typeck_type_placeholder_1.rs index f95e9ad7d83..10c91274f10 100644 --- a/src/test/run-pass/typeck_type_placeholder_1.rs +++ b/src/test/run-pass/typeck_type_placeholder_1.rs @@ -15,10 +15,11 @@ static CONSTEXPR: *const int = &413 as *const _; pub fn main() { let x: Vec<_> = range(0u, 5).collect(); - assert_eq!(x.as_slice(), &[0u,1,2,3,4]); + let expected: &[uint] = &[0,1,2,3,4]; + assert_eq!(x.as_slice(), expected); let x = range(0u, 5).collect::<Vec<_>>(); - assert_eq!(x.as_slice(), &[0u,1,2,3,4]); + assert_eq!(x.as_slice(), expected); let y: _ = "hello"; assert_eq!(y.len(), 5); diff --git a/src/test/run-pass/typeid-intrinsic.rs b/src/test/run-pass/typeid-intrinsic.rs index 90f0767534b..bba043ea8f8 100644 --- a/src/test/run-pass/typeid-intrinsic.rs +++ b/src/test/run-pass/typeid-intrinsic.rs @@ -11,8 +11,8 @@ // aux-build:typeid-intrinsic.rs // aux-build:typeid-intrinsic2.rs -extern crate other1 = "typeid-intrinsic"; -extern crate other2 = "typeid-intrinsic2"; +extern crate "typeid-intrinsic" as other1; +extern crate "typeid-intrinsic2" as other2; use std::hash; use std::intrinsics; diff --git a/src/test/run-pass/unboxed-closures-boxed.rs b/src/test/run-pass/unboxed-closures-boxed.rs index c4b990abf7e..746af1b9cf5 100644 --- a/src/test/run-pass/unboxed-closures-boxed.rs +++ b/src/test/run-pass/unboxed-closures-boxed.rs @@ -12,8 +12,8 @@ use std::ops::FnMut; -fn make_adder(x: int) -> Box<FnMut<(int,),int>> { - (box |&mut: y: int| -> int { x + y }) as Box<FnMut<(int,),int>> + fn make_adder(x: int) -> Box<FnMut<(int,),int>+'static> { + (box |&mut: y: int| -> int { x + y }) as Box<FnMut<(int,),int>+'static> } pub fn main() { diff --git a/src/test/run-pass/unboxed-closures-direct-sugary-call.rs b/src/test/run-pass/unboxed-closures-direct-sugary-call.rs new file mode 100644 index 00000000000..c77ee9914ef --- /dev/null +++ b/src/test/run-pass/unboxed-closures-direct-sugary-call.rs @@ -0,0 +1,17 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(unboxed_closures, overloaded_calls)] + +fn main() { + let mut unboxed = |&mut:| {}; + unboxed(); +} + diff --git a/src/test/run-pass/unboxed-closures-prelude.rs b/src/test/run-pass/unboxed-closures-prelude.rs new file mode 100644 index 00000000000..4226ed427e7 --- /dev/null +++ b/src/test/run-pass/unboxed-closures-prelude.rs @@ -0,0 +1,19 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Tests that the reexports of `FnOnce` et al from the prelude work. + +#![feature(unboxed_closures, unboxed_closure_sugar)] + +fn main() { + let task: Box<|: int| -> int> = box |: x| x; + task.call_once((0i, )); +} + diff --git a/src/test/run-pass/unboxed-closures-static-call-fn-once.rs b/src/test/run-pass/unboxed-closures-static-call-fn-once.rs new file mode 100644 index 00000000000..beab82e804b --- /dev/null +++ b/src/test/run-pass/unboxed-closures-static-call-fn-once.rs @@ -0,0 +1,17 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(unboxed_closures)] + +fn main() { + let onetime = |: x| x; + onetime.call_once((0i,)); +} + diff --git a/src/test/run-pass/unboxed-closures-unboxing-shim.rs b/src/test/run-pass/unboxed-closures-unboxing-shim.rs new file mode 100644 index 00000000000..0a7baa3ba36 --- /dev/null +++ b/src/test/run-pass/unboxed-closures-unboxing-shim.rs @@ -0,0 +1,19 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(unboxed_closures, unboxed_closure_sugar)] + +use std::ops::FnOnce; + +fn main() { + let task: Box<|: int| -> int> = box |: x| x; + assert!(task.call_once((1234i,)) == 1234i); +} + diff --git a/src/test/run-pass/unboxed-closures-zero-args.rs b/src/test/run-pass/unboxed-closures-zero-args.rs new file mode 100644 index 00000000000..6d6d81fd0ef --- /dev/null +++ b/src/test/run-pass/unboxed-closures-zero-args.rs @@ -0,0 +1,17 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(unboxed_closures)] + +fn main() { + let mut zero = |&mut:| {}; + zero.call_mut(()); +} + diff --git a/src/test/run-pass/use-crate-name-alias.rs b/src/test/run-pass/use-crate-name-alias.rs index bf9c5838de8..4751b4666f1 100644 --- a/src/test/run-pass/use-crate-name-alias.rs +++ b/src/test/run-pass/use-crate-name-alias.rs @@ -9,6 +9,6 @@ // except according to those terms. // Issue #1706 -extern crate stdlib = "std"; +extern crate "std" as stdlib; pub fn main() {} diff --git a/src/test/run-pass/use.rs b/src/test/run-pass/use.rs index cdc0ffed7a1..67083f53623 100644 --- a/src/test/run-pass/use.rs +++ b/src/test/run-pass/use.rs @@ -14,7 +14,7 @@ #![no_std] extern crate std; -extern crate zed = "std"; +extern crate "std" as zed; use std::str; diff --git a/src/test/run-pass/vec-dst.rs b/src/test/run-pass/vec-dst.rs new file mode 100644 index 00000000000..809dde38eb4 --- /dev/null +++ b/src/test/run-pass/vec-dst.rs @@ -0,0 +1,115 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +extern crate debug; + +fn reflect() { + // Tests for reflective printing. + // Also tests drop glue. + let x = [1, 2, 3, 4]; + let x2 = [(), (), ()]; + let e1: [uint, ..0] = []; + let e2: [&'static str, ..0] = []; + let e3: [(), ..0] = []; + assert!(format!("{:?}", x) == "[1u, 2u, 3u, 4u]".to_string()); + assert!(format!("{:?}", x2) == "[(), (), ()]".to_string()); + assert!(format!("{:?}", e1) == "[]".to_string()); + assert!(format!("{:?}", e2) == "[]".to_string()); + assert!(format!("{:?}", e3) == "[]".to_string()); + + let rx: &[uint, ..4] = &x; + let rx2: &[(), ..3] = &x2; + let re1: &[uint, ..0] = &e1; + let re2: &[&'static str, ..0] = &e2; + let re3: &[(), ..0] = &e3; + assert!(format!("{:?}", rx) == "&[1u, 2u, 3u, 4u]".to_string()); + assert!(format!("{:?}", rx2) == "&[(), (), ()]".to_string()); + assert!(format!("{:?}", re1) == "&[]".to_string()); + assert!(format!("{:?}", re2) == "&[]".to_string()); + assert!(format!("{:?}", re3) == "&[]".to_string()); + + let rx: &[uint] = &x; + let rx2: &[()] = &x2; + let re1: &[uint] = &e1; + let re2: &[&'static str] = &e2; + let re3: &[()] = &e3; + assert!(format!("{:?}", rx) == "&[1u, 2u, 3u, 4u]".to_string()); + assert!(format!("{:?}", rx2) == "&[(), (), ()]".to_string()); + assert!(format!("{:?}", re1) == "&[]".to_string()); + assert!(format!("{:?}", re2) == "&[]".to_string()); + assert!(format!("{:?}", re3) == "&[]".to_string()); + + // FIXME(15049) These should all work some day. + /*let rx: Box<[uint, ..4]> = box x; + let rx2: Box<[(), ..3]> = box x2; + let re1: Box<[uint, ..0]> = box e1; + let re2: Box<[&'static str, ..0]> = box e2; + let re3: Box<[(), ..0]> = box e3; + assert!(format!("{:?}", rx) == "box [1u, 2u, 3u, 4u]".to_string()); + assert!(format!("{:?}", rx2) == "box [(), (), ()]".to_string()); + assert!(format!("{:?}", re1) == "box []".to_string()); + assert!(format!("{:?}", re2) == "box []".to_string()); + assert!(format!("{:?}", re3) == "box []".to_string()); + + let x = [1, 2, 3, 4]; + let x2 = [(), (), ()]; + let e1: [uint, ..0] = []; + let e2: [&'static str, ..0] = []; + let e3: [(), ..0] = []; + let rx: Box<[uint]> = box x; + let rx2: Box<[()]> = box x2; + let re1: Box<[uint]> = box e1; + let re2: Box<[&'static str]> = box e2; + let re3: Box<[()]> = box e3; + assert!(format!("{:?}", rx) == "box [1u, 2u, 3u, 4u]".to_string()); + assert!(format!("{:?}", rx2) == "box [(), (), ()]".to_string()); + assert!(format!("{:?}", re1) == "box []".to_string()); + assert!(format!("{:?}", re2) == "box []".to_string()); + assert!(format!("{:?}", re3) == "box []".to_string());*/ +} + +fn sub_expr() { + // Test for a &[T] => &&[T] coercion in sub-expression position + // (surpisingly, this can cause errors which are not caused by either of: + // `let x = vec.mut_slice(0, 2);` + // `foo(vec.mut_slice(0, 2));` ). + let mut vec: Vec<int> = vec!(1, 2, 3, 4); + let b: &mut [int] = [1, 2]; + assert!(vec.mut_slice(0, 2) == b); +} + +fn index() { + // Tests for indexing into box/& [T, ..n] + let x: [int, ..3] = [1, 2, 3]; + let mut x: Box<[int, ..3]> = box x; + assert!(x[0] == 1); + assert!(x[1] == 2); + assert!(x[2] == 3); + x[1] = 45; + assert!(x[0] == 1); + assert!(x[1] == 45); + assert!(x[2] == 3); + + let mut x: [int, ..3] = [1, 2, 3]; + let x: &mut [int, ..3] = &mut x; + assert!(x[0] == 1); + assert!(x[1] == 2); + assert!(x[2] == 3); + x[1] = 45; + assert!(x[0] == 1); + assert!(x[1] == 45); + assert!(x[2] == 3); +} + +pub fn main() { + reflect(); + sub_expr(); + index(); +} diff --git a/src/test/run-pass/vec-matching-legal-tail-element-borrow.rs b/src/test/run-pass/vec-matching-legal-tail-element-borrow.rs index c070e5dab77..2fd8a4ab256 100644 --- a/src/test/run-pass/vec-matching-legal-tail-element-borrow.rs +++ b/src/test/run-pass/vec-matching-legal-tail-element-borrow.rs @@ -10,6 +10,7 @@ pub fn main() { let x = &[1i, 2, 3, 4, 5]; + let x: &[int] = &[1, 2, 3, 4, 5]; if !x.is_empty() { let el = match x { [1, ..ref tail] => &tail[0], diff --git a/src/test/run-pass/vec-matching.rs b/src/test/run-pass/vec-matching.rs index 8ba8ba4482e..e95495a42d2 100644 --- a/src/test/run-pass/vec-matching.rs +++ b/src/test/run-pass/vec-matching.rs @@ -23,12 +23,14 @@ fn b() { [a, b, ..c] => { assert_eq!(a, 1); assert_eq!(b, 2); - assert_eq!(c, &[3]); + let expected: &[_] = &[3]; + assert_eq!(c, expected); } } match x { [..a, b, c] => { - assert_eq!(a, &[1]); + let expected: &[_] = &[1]; + assert_eq!(a, expected); assert_eq!(b, 2); assert_eq!(c, 3); } @@ -36,7 +38,8 @@ fn b() { match x { [a, ..b, c] => { assert_eq!(a, 1); - assert_eq!(b, &[2]); + let expected: &[_] = &[2]; + assert_eq!(b, expected); assert_eq!(c, 3); } } @@ -69,7 +72,8 @@ fn d() { } fn e() { - match &[1i, 2, 3] { + let x: &[int] = &[1i, 2, 3]; + match x { [1, 2] => (), [..] => () } diff --git a/src/test/run-pass/vec-to_str.rs b/src/test/run-pass/vec-to_str.rs index deb08a4608c..52e0ba89479 100644 --- a/src/test/run-pass/vec-to_str.rs +++ b/src/test/run-pass/vec-to_str.rs @@ -10,10 +10,9 @@ pub fn main() { assert_eq!((vec!(0i, 1)).to_string(), "[0, 1]".to_string()); - assert_eq!((&[1i, 2]).to_string(), "[1, 2]".to_string()); let foo = vec!(3i, 4); - let bar = &[4i, 5]; + let bar: &[int] = &[4, 5]; assert_eq!(foo.to_string(), "[3, 4]".to_string()); assert_eq!(bar.to_string(), "[4, 5]".to_string()); diff --git a/src/test/run-pass/weak-lang-item.rs b/src/test/run-pass/weak-lang-item.rs index 889259b6acd..d44e25550ef 100644 --- a/src/test/run-pass/weak-lang-item.rs +++ b/src/test/run-pass/weak-lang-item.rs @@ -10,7 +10,7 @@ // aux-build:weak-lang-items.rs -extern crate other = "weak-lang-items"; +extern crate "weak-lang-items" as other; use std::task; diff --git a/src/test/run-pass/where-clauses-lifetimes.rs b/src/test/run-pass/where-clauses-lifetimes.rs new file mode 100644 index 00000000000..4adaf7a11e3 --- /dev/null +++ b/src/test/run-pass/where-clauses-lifetimes.rs @@ -0,0 +1,15 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +fn foo<'a, I>(mut it: I) where I: Iterator<&'a int> {} + +fn main() { + foo([1i, 2].iter()); +} diff --git a/src/test/run-pass/where-clauses-unboxed-closures.rs b/src/test/run-pass/where-clauses-unboxed-closures.rs new file mode 100644 index 00000000000..ae005b4ae53 --- /dev/null +++ b/src/test/run-pass/where-clauses-unboxed-closures.rs @@ -0,0 +1,26 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(unboxed_closures)] + +struct Bencher; + +// ICE +fn warm_up<'a, F>(f: F) where F: |&: &'a mut Bencher| { +} + +fn main() { + // ICE trigger + warm_up(|&: b: &mut Bencher| () ); + + // OK + warm_up(|&: b| () ); +} + diff --git a/src/test/run-pass/xcrate-address-insignificant.rs b/src/test/run-pass/xcrate-address-insignificant.rs index ddb83dedcc0..236ff0838e5 100644 --- a/src/test/run-pass/xcrate-address-insignificant.rs +++ b/src/test/run-pass/xcrate-address-insignificant.rs @@ -10,7 +10,7 @@ // aux-build:xcrate_address_insignificant.rs -extern crate foo = "xcrate_address_insignificant"; +extern crate "xcrate_address_insignificant" as foo; pub fn main() { assert_eq!(foo::foo::<f64>(), foo::bar()); diff --git a/src/test/run-pass/xcrate-trait-lifetime-param.rs b/src/test/run-pass/xcrate-trait-lifetime-param.rs index 56d6b4eae94..aa61237417e 100644 --- a/src/test/run-pass/xcrate-trait-lifetime-param.rs +++ b/src/test/run-pass/xcrate-trait-lifetime-param.rs @@ -10,7 +10,7 @@ // aux-build:xcrate-trait-lifetime-param.rs -extern crate other = "xcrate-trait-lifetime-param"; +extern crate "xcrate-trait-lifetime-param" as other; struct Reader<'a> { b : &'a [u8] |
