# email addresses.
#
-Elly Jones <elly@leptoquark.net>
-ILyoan <ilyoan@gmail.com>
+Aaron Todd <github@opprobrio.us>
+Ahmed Charles <ahmedcharles@gmail.com> <acharles@outlook.com>
+Alex Lyon <arcterus@mail.com> <Arcterus@mail.com>
+Alex Rønne Petersen <alex@lycus.org>
+Andreas Gal <gal@mozilla.com> <andreas.gal@gmail.com>
+Andrew Poelstra <asp11@sfu.ca> <apoelstra@wpsoftware.net>
+Anton Löfgren <anton.lofgren@gmail.com> <alofgren@op5.com>
+Ariel Ben-Yehuda <arielb1@mail.tau.ac.il> <ariel.byd@gmail.com>
+Austin Seipp <mad.one@gmail.com> <as@hacks.yi.org>
+Ben Alpert <ben@benalpert.com> <spicyjalapeno@gmail.com>
+Benjamin Jackman <ben@jackman.biz>
+Björn Steinbrink <bsteinbr@gmail.com> <B.Steinbrink@gmx.de>
+blake2-ppc <ulrik.sverdrup@gmail.com> <blake2-ppc>
+Boris Egorov <jightuse@gmail.com> <egorov@linux.com>
+Brian Anderson <banderson@mozilla.com> <andersrb@gmail.com>
+Brian Dawn <brian.t.dawn@gmail.com>
+Carl-Anton Ingmarsson <mail@carlanton.se> <ca.ingmarsson@gmail.com>
+Carol Willing <carolcode@willingconsulting.com>
+Chris Pressey <cpressey@gmail.com>
+Clark Gaebel <cg.wowus.cg@gmail.com> <cgaebel@mozilla.com>
+David Klein <david.klein@baesystemsdetica.com>
+David Manescu <david.manescu@gmail.com> <dman2626@uni.sydney.edu.au>
+Damien Schoof <damien.schoof@gmail.com>
+Derek Chiang <derekchiang93@gmail.com> Derek Chiang (Enchi Jiang) <derekchiang93@gmail.com>
+Dylan Braithwaite <dylanbraithwaite1@gmail.com> <mail@dylanb.me>
+Eduardo Bautista <me@eduardobautista.com> <mail@eduardobautista.com>
+Eduardo Bautista <me@eduardobautista.com> <=>
+Elliott Slaughter <elliottslaughter@gmail.com> <eslaughter@mozilla.com>
+Elly Fong-Jones <elly@leptoquark.net>
+Eric Holk <eric.holk@gmail.com> <eholk@mozilla.com>
+Eric Holk <eric.holk@gmail.com> <eholk@cs.indiana.edu>
+Eric Holmes <eric@ejholmes.net>
+Eric Reed <ecreed@cs.washington.edu> <ereed@mozilla.com>
+Erick Tryzelaar <erick.tryzelaar@gmail.com> <etryzelaar@iqt.org>
+Evgeny Sologubov
+Falco Hirschenberger <falco.hirschenberger@gmail.com> <hirschen@itwm.fhg.de>
+Gareth Daniel Smith <garethdanielsmith@gmail.com>
+Georges Dubus <georges.dubus@gmail.com> <georges.dubus@compiletoi.net>
+Graham Fawcett <fawcett@uwindsor.ca> <graham.fawcett@gmail.com>
+Graydon Hoare <graydon@mozilla.com> <graydon@pobox.com>
+Heather <heather@cynede.net> <Heather@cynede.net>
+Heather <heather@cynede.net> <Cynede@Gentoo.org>
+Ilyong Cho <ilyoan@gmail.com>
+J. J. Weber <jjweber@gmail.com>
+Jakub Bukaj <jakub@jakub.cc>
+Jakub Bukaj <jakub@jakub.cc> <jakubw@jakubw.net>
+James Deng <cnjamesdeng@gmail.com> <cnJamesDeng@gmail.com>
+James Miller <bladeon@gmail.com> <james@aatch.net>
+Jason Orendorff <jorendorff@mozilla.com> <jason@mozmac-2.local>
+Jason Orendorff <jorendorff@mozilla.com> <jason.orendorff@gmail.com>
+Jeremy Letang <letang.jeremy@gmail.com>
+Jihyun Yu <jihyun@nclab.kaist.ac.kr> jihyun <jihyun@nablecomm.com>
+Jihyun Yu <jihyun@nclab.kaist.ac.kr> <yjh0502@gmail.com>
+John Clements <clements@racket-lang.org> <clements@brinckerhoff.org>
+Jorge Aparicio <japaric@linux.com> <japaricious@gmail.com>
+Jonathan Bailey <jbailey@mozilla.com> <jbailey@jbailey-20809.local>
Junyoung Cho <june0.cho@samsung.com>
+Jyun-Yan You <jyyou.tw@gmail.com> <jyyou@cs.nctu.edu.tw>
+Kang Seonghoon <kang.seonghoon@mearie.org> <public+git@mearie.org>
+Keegan McAllister <kmcallister@mozilla.com> <mcallister.keegan@gmail.com>
+Kyeongwoon Lee <kyeongwoon.lee@samsung.com>
+Lee Wondong <wdlee91@gmail.com>
+Lennart Kudling <github@kudling.de>
+Lindsey Kuper <lindsey@composition.al> <lindsey@rockstargirl.org>
+Lindsey Kuper <lindsey@composition.al> <lkuper@mozilla.com>
+Luqman Aden <me@luqman.ca> <laden@mozilla.com>
+Luqman Aden <me@luqman.ca> <laden@csclub.uwaterloo.ca>
+Luke Metz <luke.metz@students.olin.edu>
+Makoto Nakashima <makoto.nksm+github@gmail.com> <makoto.nksm@gmail.com>
+Makoto Nakashima <makoto.nksm+github@gmail.com> gifnksm <makoto.nksm+github@gmail.com>
+Margaret Meyerhofer <mmeyerho@andrew.cmu.edu> <mmeyerho@andrew>
+Mark Sinclair <mark.edward.x@gmail.com>
+Mark Sinclair <mark.edward.x@gmail.com> =Mark Sinclair <=125axel125@gmail.com>
+Matt Brubeck <mbrubeck@limpet.net> <mbrubeck@cs.hmc.edu>
+Matthew Auld <matthew.auld@intel.com>
+Matthew McPherrin <matthew@mcpherrin.ca> <matt@mcpherrin.ca>
Matthijs Hofstra <thiezz@gmail.com>
+Michael Williams <m.t.williams@live.com>
+Michael Woerister <michaelwoerister@gmail> <michaelwoerister@gmail.com>
+Michael Woerister <michaelwoerister@gmail> <michaelwoerister@posteo>
+Neil Pankey <npankey@gmail.com> <neil@wire.im>
+Philipp Brüschweiler <blei42@gmail.com> <blei42@gmail.com>
+Philipp Brüschweiler <blei42@gmail.com> <bruphili@student.ethz.ch>
+Pradeep Kumar <gohanpra@gmail.com>
+Richard Diamond <wichard@vitalitystudios.com> <wichard@hahbee.co>
Rob Arnold <robarnold@cs.cmu.edu>
+Robert Gawdzik <rgawdzik@hotmail.com> Robert Gawdzik ☢ <rgawdzik@hotmail.com>
+Robert Millar <robert.millar@cantab.net>
+Ryan Scheel <ryan.havvy@gmail.com>
+Seonghyun Kim <sh8281.kim@samsung.com>
+Simon Barber-Dueck <sbarberdueck@gmail.com> Simon BD <simon@server>
+Simon Sapin <simon@exyr.org> <simon.sapin@exyr.org>
+startling <tdixon51793@gmail.com>
+Steven Fackler <sfackler@gmail.com> <sfackler@palantir.com>
+Steven Stewart-Gallus <sstewartgallus00@langara.bc.ca> <sstewartgallus00@mylangara.bc.ca>
+Tim Chevalier <chevalier@alum.wellesley.edu> <catamorphism@gmail.com>
+Torsten Weber <TorstenWeber12@gmail.com> <torstenweber12@gmail.com>
+William Ting <io@williamting.com> <william.h.ting@gmail.com>
+Youngsoo Son <ysson83@gmail.com> <ysoo.son@samsung.com>
+Zack Corr <zack@z0w0.me> <zackcorr95@gmail.com>
+Zack Slayton <zack.slayton@gmail.com>
done
}
-valopt() {
- VAL_OPTIONS="$VAL_OPTIONS $1"
+# `valopt OPTION_NAME DEFAULT DOC` extracts a string-valued option
+# from command line, using provided default value for the option if
+# not present, and saves it to the generated config.mk.
+#
+# `valopt_nosave` is much the same, except that it does not save the
+# result to config.mk (instead the script should use `putvar` itself
+# later on to save it). `valopt_core` is the core upon which the
+# other two are built.
- local OP=$1
- local DEFAULT=$2
+valopt_core() {
+ VAL_OPTIONS="$VAL_OPTIONS $2"
+
+ local SAVE=$1
+ local OP=$2
+ local DEFAULT=$3
+ shift
shift
shift
local DOC="$*"
eval $V=$val
fi
done
- putvar $V
+ if [ "$SAVE" = "save" ]
+ then
+ putvar $V
+ fi
else
if [ -z "$DEFAULT" ]
then
fi
}
-opt() {
- BOOL_OPTIONS="$BOOL_OPTIONS $1"
+valopt_nosave() {
+ valopt_core nosave "$@"
+}
+
+valopt() {
+ valopt_core save "$@"
+}
- local OP=$1
- local DEFAULT=$2
+# `opt OPTION_NAME DEFAULT DOC` extracts a boolean-valued option from
+# command line, using the provided default value (0/1) for the option
+# if not present, and saves it to the generated config.mk.
+#
+# `opt_nosave` is much the same, except that it does not save the
+# result to config.mk (instead the script should use `putvar` itself
+# later on to save it). `opt_core` is the core upon which the other
+# two are built.
+
+opt_core() {
+ BOOL_OPTIONS="$BOOL_OPTIONS $2"
+
+ local SAVE=$1
+ local OP=$2
+ local DEFAULT=$3
+ shift
shift
shift
local DOC="$*"
FLAG=$(echo $FLAG | tr 'a-z' 'A-Z')
local V="CFG_${FLAG}_${OP}"
eval $V=1
- putvar $V
+ if [ "$SAVE" = "save" ]
+ then
+ putvar $V
+ fi
fi
done
else
fi
}
+opt_nosave() {
+ opt_core nosave "$@"
+}
+
+opt() {
+ opt_core save "$@"
+}
+
envopt() {
local NAME=$1
local V="CFG_${NAME}"
opt debug 1 "build with extra debug fun"
opt ratchet-bench 0 "ratchet benchmarks"
opt fast-make 0 "use .gitmodules as timestamp for submodule deps"
-opt manage-submodules 1 "let the build manage the git submodules"
opt mingw-cross 0 "cross-compile for win32 using mingw"
-opt clang 0 "prefer clang to gcc for building the runtime"
opt ccache 0 "invoke gcc/clang via ccache to reuse object files between builds"
opt local-rust 0 "use an installed rustc rather than downloading a snapshot"
-opt inject-std-version 1 "inject the current compiler version of libstd into programs"
opt llvm-static-stdcpp 0 "statically link to libstdc++ for LLVM"
opt rpath 0 "build rpaths into rustc itself"
opt nightly 0 "build nightly packages"
opt verify-install 1 "verify installed binaries work"
-opt jemalloc 1 "build liballoc with jemalloc"
# This is used by the automation to produce single-target nightlies
opt dist-host-only 0 "only install bins for the host architecture"
-valopt prefix "/usr/local" "set installation prefix"
-valopt local-rust-root "/usr/local" "set prefix for local rust binary"
-valopt llvm-root "" "set LLVM root"
-valopt jemalloc-root "" "set directory where libjemalloc_pic.a is located"
-valopt android-cross-path "/opt/ndk_standalone" "Android NDK standalone path"
-valopt mingw32-cross-path "" "MinGW32 cross compiler path"
-
-valopt build "${DEFAULT_BUILD}" "GNUs ./configure syntax LLVM build triple"
-valopt host "${CFG_BUILD}" "GNUs ./configure syntax LLVM host triples"
-valopt target "${CFG_HOST}" "GNUs ./configure syntax LLVM target triples"
+opt inject-std-version 1 "inject the current compiler version of libstd into programs"
+opt jemalloc 1 "build liballoc with jemalloc"
valopt localstatedir "/var/lib" "local state directory"
valopt sysconfdir "/etc" "install system configuration files"
valopt datadir "${CFG_PREFIX}/share" "install data"
valopt infodir "${CFG_PREFIX}/share/info" "install additional info"
-valopt mandir "${CFG_PREFIX}/share/man" "install man pages in PATH"
+valopt llvm-root "" "set LLVM root"
+valopt jemalloc-root "" "set directory where libjemalloc_pic.a is located"
+valopt build "${DEFAULT_BUILD}" "GNUs ./configure syntax LLVM build triple"
+valopt android-cross-path "/opt/ndk_standalone" "Android NDK standalone path"
+valopt mingw32-cross-path "" "MinGW32 cross compiler path"
+
+# Many of these are saved below during the "writing configuration" step
+# (others are conditionally saved).
+opt_nosave manage-submodules 1 "let the build manage the git submodules"
+opt_nosave clang 0 "prefer clang to gcc for building the runtime"
-valopt release-channel "dev" "the name of the release channel to build"
+valopt_nosave prefix "/usr/local" "set installation prefix"
+valopt_nosave local-rust-root "/usr/local" "set prefix for local rust binary"
+valopt_nosave host "${CFG_BUILD}" "GNUs ./configure syntax LLVM host triples"
+valopt_nosave target "${CFG_HOST}" "GNUs ./configure syntax LLVM target triples"
+valopt_nosave mandir "${CFG_PREFIX}/share/man" "install man pages in PATH"
+valopt_nosave release-channel "dev" "the name of the release channel to build"
# On windows we just store the libraries in the bin directory because
# there's no rpath. This is where the build system itself puts libraries;
if [ ! -z "$CFG_ENABLE_NIGHTLY" ]
then
CFG_RELEASE_CHANNEL=nightly
- putvar CFG_RELEASE_CHANNEL
fi
+putvar CFG_RELEASE_CHANNEL
step_msg "looking for build programs"
err "no local rust to use"
fi
- LRV=`${CFG_LOCAL_RUST_ROOT}/bin/rustc${BIN_SUF} --version`
+ CMD="${CFG_LOCAL_RUST_ROOT}/bin/rustc${BIN_SUF}"
+ LRV=`$CMD --version`
+ if [ $? -ne 0 ]
+ then
+ step_msg "failure while running $CMD --version"
+ exit 1
+ fi
step_msg "using rustc at: ${CFG_LOCAL_RUST_ROOT} with version: $LRV"
putvar CFG_LOCAL_RUST_ROOT
+else
+ if [ ! -z "$CFG_LOCAL_RUST_ROOT" ]
+ then
+ warn "Use of --local-rust-root without --enable-local-rust"
+ fi
fi
# Force freebsd to build with clang; gcc doesn't like us there
then
step_msg "on FreeBSD, forcing use of clang"
CFG_ENABLE_CLANG=1
- putvar CFG_ENABLE_CLANG
fi
if [ -z "$CFG_ENABLE_CLANG" -a -z "$CFG_GCC" ]
then
step_msg "on OS X 10.9, forcing use of clang"
CFG_ENABLE_CLANG=1
- putvar CFG_ENABLE_CLANG
else
if [ $("$CFG_GCC" --version 2>&1 | grep -c ' 4\.[0-6]') -ne 0 ]; then
step_msg "older GCC found, using clang instead"
CFG_ENABLE_CLANG=1
- putvar CFG_ENABLE_CLANG
else
# on OS X, with xcode 5 and newer, certain developers may have
# cc, gcc and g++ point to a mixture of clang and gcc
fi
fi
+# Okay, at this point, we have made up our minds about whether we are
+# going to force CFG_ENABLE_CLANG or not; save the setting if so.
+if [ ! -z "$CFG_ENABLE_CLANG" ]
+then
+ putvar CFG_ENABLE_CLANG
+fi
+
if [ ! -z "$CFG_LLVM_ROOT" -a -e "$CFG_LLVM_ROOT/bin/llvm-config" ]
then
step_msg "using custom LLVM at $CFG_LLVM_ROOT"
make_dir $h/test/doc-guide-container
make_dir $h/test/doc-guide-tasks
make_dir $h/test/doc-guide-plugin
+ make_dir $h/test/doc-guide-crates
make_dir $h/test/doc-rust
done
putvar CFG_CPUTYPE
putvar CFG_CONFIGURE_ARGS
putvar CFG_PREFIX
-putvar CFG_BUILD
putvar CFG_HOST
putvar CFG_TARGET
-putvar CFG_LIBDIR
putvar CFG_LIBDIR_RELATIVE
putvar CFG_DISABLE_MANAGE_SUBMODULES
-putvar CFG_ANDROID_CROSS_PATH
-putvar CFG_MINGW32_CROSS_PATH
putvar CFG_MANDIR
-putvar CFG_DISABLE_INJECT_STD_VERSION
-putvar CFG_JEMALLOC_ROOT
-putvar CFG_DISABLE_JEMALLOC
# Avoid spurious warnings from clang by feeding it original source on
# ccache-miss rather than preprocessed input.
putvar CFG_PANDOC
fi
-putvar CFG_LLVM_ROOT
putvar CFG_LLVM_SRC_DIR
for t in $CFG_HOST
######################################################################
DOCS := index intro tutorial guide guide-ffi guide-macros guide-lifetimes \
guide-tasks guide-container guide-pointers guide-testing \
- guide-plugin complement-bugreport \
+ guide-plugin guide-crates complement-bugreport \
complement-lang-faq complement-design-faq complement-project-faq rust \
rustdoc guide-unsafe guide-strings reference
use std::from_str::FromStr;
use getopts::{optopt, optflag, reqopt};
use common::Config;
-use common::{Pretty, DebugInfoGdb, Codegen};
+use common::{Pretty, DebugInfoGdb, DebugInfoLldb, Codegen};
use util::logv;
use regex::Regex;
os::setenv("RUST_TEST_TASKS","1");
}
+ match config.mode {
+ DebugInfoLldb => {
+ // Some older versions of LLDB seem to have problems with multiple
+ // instances running in parallel, so only run one test task at a
+ // time.
+ os::setenv("RUST_TEST_TASKS", "1");
+ }
+ _ => { /* proceed */ }
+ }
+
let opts = test_opts(config);
let tests = make_tests(config);
// sadly osx needs some file descriptor limits raised for running tests in
Rust will give us a compile-time error:
-```{rust,ignore}
+```{notrust,ignore}
Compiling phrases v0.0.1 (file:///home/you/projects/phrases)
/home/you/projects/phrases/src/main.rs:4:5: 4:40 error: a value named `hello` has already been imported in this module
/home/you/projects/phrases/src/main.rs:4 use phrases::japanese::greetings::hello;
use syntax::codemap::Span;
use syntax::parse::token::{IDENT, get_ident};
-use syntax::ast::{TokenTree, TTTok};
+use syntax::ast::{TokenTree, TtToken};
use syntax::ext::base::{ExtCtxt, MacResult, DummyResult, MacExpr};
use syntax::ext::build::AstBuilder; // trait for expr_uint
use rustc::plugin::Registry;
("I", 1)];
let text = match args {
- [TTTok(_, IDENT(s, _))] => get_ident(s).to_string(),
+ [TtToken(_, IDENT(s, _))] => get_ident(s).to_string(),
_ => {
cx.span_err(sp, "argument should be a single identifier");
return DummyResult::any(sp);
fn expand_foo(cx: &mut ExtCtxt, sp: Span, args: &[TokenTree])
-> Box<MacResult+'static> {
- let mut parser =
- parse::new_parser_from_tts(cx.parse_sess(), cx.cfg(), args.to_slice())
+ let mut parser = cx.new_parser_from_tts(args);
let expr: P<Expr> = parser.parse_expr();
```
# &str
-The first kind is a `&str`. This is pronounced a 'string slice.' String literals
-are of the type `&str`:
+The first kind is a `&str`. This is pronounced a 'string slice'.
+String literals are of the type `&str`:
```{rust}
let string = "Hello there.";
To compare a String to a constant string, prefer `as_slice()`...
```{rust}
-fn compare(string: String) {
- if string.as_slice() == "Hello" {
+fn compare(x: String) {
+ if x.as_slice() == "Hello" {
println!("yes");
}
}
... over `to_string()`:
```{rust}
-fn compare(string: String) {
- if string == "Hello".to_string() {
+fn compare(x: String) {
+ if x == "Hello".to_string() {
println!("yes");
}
}
let y = Missing;
match x {
- Value(n) => println!("x is {:d}", n),
+ Value(n) => println!("x is {}", n),
Missing => println!("x is missing!"),
}
match y {
- Value(n) => println!("y is {:d}", n),
+ Value(n) => println!("y is {}", n),
Missing => println!("y is missing!"),
}
}
```{rust}
for x in range(0i, 10i) {
- println!("{:d}", x);
+ println!("{}", x);
}
```
for x in range(0i, 10i) {
if x % 2 == 0 { continue; }
- println!("{:d}", x);
+ println!("{}", x);
}
```
let y = Missing;
match x {
- Value(n) => println!("x is {:d}", n),
+ Value(n) => println!("x is {}", n),
Missing => println!("x is missing!"),
}
match y {
- Value(n) => println!("y is {:d}", n),
+ Value(n) => println!("y is {}", n),
Missing => println!("y is missing!"),
}
}
```{rust}
fn main() {
- println!("Hello, world!");
+ println!("Hello, world!")
}
```
```
fn main() {
- println!("Hello, world!");
+ println!("Hello, world!")
}
mod hello {
fn print_hello() {
- println!("Hello, world!");
+ println!("Hello, world!")
}
}
```
mod hello {
fn print_hello() {
- println!("Hello, world!");
+ println!("Hello, world!")
}
}
```
mod hello {
pub fn print_hello() {
- println!("Hello, world!");
+ println!("Hello, world!")
}
}
```
```{notrust,ignore}
$ cargo test
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:1: 3:2 warning: function 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");
+/home/you/projects/testing/src/main.rs:2 println!("Hello, world!")
/home/you/projects/testing/src/main.rs:3 }
-
-running 0 tests
-
-test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured
-
+ Running target/lib-654ce120f310a3a5
running 1 test
test foo ... FAILED
test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured
-task '<main>' failed at 'Some tests failed', /home/you/src/rust/src/libtest/lib.rs:242
+task '<main>' failed at 'Some tests failed', /home/you/src/rust/src/libtest/lib.rs:243
```
Lots of output! Let's break this down:
`tests`, as well as the tests you put inside of your crate.
```{notrust,ignore}
-/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:1: 3:2 warning: function 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");
+/home/you/projects/testing/src/main.rs:2 println!("Hello, world!")
/home/you/projects/testing/src/main.rs:3 }
```
output.
```{notrust,ignore}
-running 0 tests
-
-test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured
-```
+ Running target/lib-654ce120f310a3a5
-Wait a minute, zero tests? Didn't we define one? Yup. This output is from
-attempting to run the tests in our crate, of which we don't have any.
-You'll note that Rust reports on several kinds of tests: passed, failed,
-ignored, and measured. The 'measured' tests refer to benchmark tests, which
-we'll cover soon enough!
-
-```{notrust,ignore}
running 1 test
test foo ... FAILED
```
test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured
-task '<main>' failed at 'Some tests failed', /home/you/src/rust/src/libtest/lib.rs:242
+task '<main>' failed at 'Some tests failed', /home/you/src/rust/src/libtest/lib.rs:243
```
After all the tests run, Rust will show us any output from our failed tests.
```{notrust,ignore}
$ cargo test
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");
-/home/you/projects/testing/src/main.rs:3 }
-
-running 0 tests
-
-test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured
-
+ Running target/lib-654ce120f310a3a5
running 1 test
test foo ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured
+
+ Running target/testing-6d7518593c7c3ee5
+
+running 0 tests
+
+test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured
```
-Nice! Our test passes, as we expected. Let's get rid of that warning for our `main`
-function. Change your `src/main.rs` to look like this:
+Nice! Our test passes, as we expected. Note how we didn't get the
+`main` warning this time? This is because `src/main.rs` didn't
+need recompiling, but we'll get that warning again if we
+change (and recompile) that file. Let's get rid of that
+warning; change your `src/main.rs` to look like this:
```{rust}
#[cfg(not(test))]
fn main() {
- println!("Hello, world");
+ println!("Hello, world!")
}
```
include `main` when it's _not_ true. So we use `not` to negate things:
`cfg(not(test))` will only compile our code when the `cfg(test)` is false.
-With this attribute, we won't get the warning:
+With this attribute we won't get the warning (even
+though `src/main.rs` gets recompiled this time):
```{notrust,ignore}
$ cargo test
Compiling testing v0.0.1 (file:///home/you/projects/testing)
-
-running 0 tests
-
-test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured
-
+ Running target/lib-654ce120f310a3a5
running 1 test
test foo ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured
+
+ Running target/testing-6d7518593c7c3ee5
+
+running 0 tests
+
+test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured
```
Nice. Okay, let's write a real test now. Change your `tests/lib.rs`
#[cfg(not(test))]
fn main() {
- println!("Hello, world");
+ println!("Hello, world!")
}
```
```{ignore,notrust}
$ cargo test
Compiling testing v0.0.1 (file:///home/you/projects/testing)
+ Running target/lib-654ce120f310a3a5
+
+running 1 test
+test math_checks_out ... ok
+
+test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured
+
+ Running target/testing-6d7518593c7c3ee5
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured
+ Running target/testing-8a94b31f7fd2e8fe
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured
+ Doc-tests testing
-running 1 test
-test math_checks_out ... ok
+running 0 tests
-test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured
+test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured
```
Great! One test passed. We've got an integration test showing that our public
```{ignore,notrust}
$ cargo test
Compiling testing v0.0.1 (file:///home/you/projects/testing)
+ Running target/lib-654ce120f310a3a5
+
+running 1 test
+test math_checks_out ... ok
+
+test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured
+
+ Running target/testing-6d7518593c7c3ee5
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured
+ Running target/testing-8a94b31f7fd2e8fe
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured
+ Doc-tests testing
-running 1 test
-test math_checks_out ... ok
+running 0 tests
-test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured
+test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured
```
If we tried to write a test for these two new functions, it wouldn't
```{ignore,notrust}
$ cargo test
Compiling testing v0.0.1 (file:///home/you/projects/testing)
+ Running target/lib-654ce120f310a3a5
+
+running 1 test
+test math_checks_out ... ok
+
+test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured
+
+ Running target/testing-6d7518593c7c3ee5
+
+running 0 tests
+
+test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured
+
+ Running target/testing-8a94b31f7fd2e8fe
running 2 tests
test test::test_times_four ... ok
test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured
+ Doc-tests testing
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured
-
-
-running 1 test
-test math_checks_out ... ok
-
-test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured
```
Cool! We now have two tests of our internal functions. You'll note that there
## Boxes
-All of our references so far have been to variables we've created on the stack.
-In Rust, the simplest way to allocate heap variables is using a *box*. To
-create a box, use the `box` keyword:
+Most of the types we've seen so far have a fixed size or number of components.
+The compiler needs this fact to lay out values in memory. However, some data
+structures, such as a linked list, do not have a fixed size. You might think to
+implement a linked list with an enum that's either a `Node` or the end of the
+list (`Nil`), like this:
+
+```{rust,ignore}
+enum List { // error: illegal recursive enum type
+ Node(u32, List),
+ Nil
+}
+```
+
+But the compiler complains that the type is recursive, that is, it could be
+arbitrarily large. To remedy this, Rust provides a fixed-size container called
+a **box** that can hold any type. You can box up any value with the `box`
+keyword. Our boxed List gets the type `Box<List>` (more on the notation when we
+get to generics):
```{rust}
-let x = box 5i;
+enum List {
+ Node(u32, Box<List>),
+ Nil
+}
+
+fn main() {
+ let list = Node(0, box Node(1, box Nil));
+}
```
-This allocates an integer `5` on the heap, and creates a binding `x` that
-refers to it. The great thing about boxed pointers is that we don't have to
-manually free this allocation! If we write
+A box dynamically allocates memory to hold its contents. The great thing about
+Rust is that that memory is *automatically*, *efficiently*, and *predictably*
+deallocated when you're done with the box.
+
+A box is a pointer type, and you access what's inside using the `*` operator,
+just like regular references. This (rather silly) example dynamically allocates
+an integer `5` and makes `x` a pointer to it:
```{rust}
{
let x = box 5i;
- // do stuff
+ println!("{}", *x); // Prints 5
}
```
-then Rust will automatically free `x` at the end of the block. This isn't
-because Rust has a garbage collector -- it doesn't. Instead, when `x` goes out
-of scope, Rust `free`s `x`. This Rust code will do the same thing as the
-following C code:
+The great thing about boxes is that we don't have to manually free this
+allocation! Instead, when `x` reaches the end of its lifetime -- in this case,
+when it goes out of scope at the end of the block -- Rust `free`s `x`. This
+isn't because Rust has a garbage collector (it doesn't). Instead, by tracking
+the ownership and lifetime of a variable (with a little help from you, the
+programmer), the compiler knows precisely when it is no longer used.
+
+The Rust code above will do the same thing as the following C code:
```{c,ignore}
{
int *x = (int *)malloc(sizeof(int));
- // do stuff
+ if (!x) abort();
+ *x = 5;
+ printf("%d\n", *x);
free(x);
}
```
-This means we get the benefits of manual memory management, but the compiler
-ensures that we don't do something wrong. We can't forget to `free` our memory.
+We get the benefits of manual memory management, while ensuring we don't
+introduce any bugs. We can't forget to `free` our memory.
Boxes are the sole owner of their contents, so you cannot take a mutable
reference to them and then use the original box:
*x;
```
-## Rc and Arc
-
-Sometimes, you need to allocate something on the heap, but give out multiple
-references to the memory. Rust's `Rc<T>` (pronounced 'arr cee tee') and
-`Arc<T>` types (again, the `T` is for generics, we'll learn more later) provide
-you with this ability. **Rc** stands for 'reference counted,' and **Arc** for
-'atomically reference counted.' This is how Rust keeps track of the multiple
-owners: every time we make a new reference to the `Rc<T>`, we add one to its
-internal 'reference count.' Every time a reference goes out of scope, we
-subtract one from the count. When the count is zero, the `Rc<T>` can be safely
-deallocated. `Arc<T>` is almost identical to `Rc<T>`, except for one thing: The
-'atomically' in 'Arc' means that increasing and decreasing the count uses a
-thread-safe mechanism to do so. Why two types? `Rc<T>` is faster, so if you're
-not in a multi-threaded scenario, you can have that advantage. Since we haven't
-talked about threading yet in Rust, we'll show you `Rc<T>` for the rest of this
-section.
+Boxes are simple and efficient pointers to dynamically allocated values with a
+single owner. They are useful for tree-like structures where the lifetime of a
+child depends solely on the lifetime of its (single) parent. If you need a
+value that must persist as long as any of several referrers, read on.
-To create an `Rc<T>`, use `Rc::new()`:
+## Rc and Arc
-```{rust}
-use std::rc::Rc;
+Sometimes, you need a variable that is referenced from multiple places
+(immutably!), lasting as long as any of those places, and disappearing when it
+is no longer referenced. For instance, in a graph-like data structure, a node
+might be referenced from all of its neighbors. In this case, it is not possible
+for the compiler to determine ahead of time when the value can be freed -- it
+needs a little run-time support.
-let x = Rc::new(5i);
-```
+Rust's **Rc** type provides shared ownership of a dynamically allocated value
+that is automatically freed at the end of its last owner's lifetime. (`Rc`
+stands for 'reference counted,' referring to the way these library types are
+implemented.) This provides more flexibility than single-owner boxes, but has
+some runtime overhead.
-To create a second reference, use the `.clone()` method:
+To create an `Rc` value, use `Rc::new()`. To create a second owner, use the
+`.clone()` method:
```{rust}
use std::rc::Rc;
let x = Rc::new(5i);
let y = x.clone();
+
+println!("{} {}", *x, *y); // Prints 5 5
```
-The `Rc<T>` will live as long as any of its references are alive. After they
-all go out of scope, the memory will be `free`d.
+The `Rc` will live as long as any of its owners are alive. After that, the
+memory will be `free`d.
+
+**Arc** is an 'atomically reference counted' value, identical to `Rc` except
+that ownership can be safely shared among multiple threads. Why two types?
+`Arc` has more overhead, so if you're not in a multi-threaded scenario, you
+don't have to pay the price.
-If you use `Rc<T>` or `Arc<T>`, you have to be careful about introducing
-cycles. If you have two `Rc<T>`s that point to each other, the reference counts
-will never drop to zero, and you'll have a memory leak. To learn more, check
-out [the section on `Rc<T>` and `Arc<T>` in the pointers
-guide](guide-pointers.html#rc-and-arc).
+If you use `Rc` or `Arc`, you have to be careful about introducing cycles. If
+you have two `Rc`s that point to each other, they will happily keep each other
+alive forever, creating a memory leak. To learn more, check out [the section on
+`Rc` and `Arc` in the pointers guide](guide-pointers.html#rc-and-arc).
# Patterns
```{rust}
for x in range(0i, 10i) {
- println!("{:d}", x);
+ println!("{}", x);
}
```
```{rust}
let greater_than_forty_two = range(0i, 100i)
- .find(|x| *x >= 42);
+ .find(|x| *x > 42);
match greater_than_forty_two {
Some(_) => println!("We got some numbers!"),
`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
+So this would give us the numbers from `2-100`. Well, almost! If you
compile the example, you'll get a warning:
```{notrust,ignore}
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`
+Rust has support for [FFI](http://en.wikipedia.org/wiki/Foreign_function_interface)
+(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
* [Strings](guide-strings.html)
* [Pointers](guide-pointers.html)
* [References and Lifetimes](guide-lifetimes.html)
+* [Crates and modules](guide-crates.html)
* [Tasks and Communication](guide-tasks.html)
* [Foreign Function Interface](guide-ffi.html)
* [Writing Unsafe and Low-Level Code](guide-unsafe.html)
[type: text] src/doc/guide-tasks.md $lang:doc/l10n/$lang/guide-tasks.md
[type: text] src/doc/guide-testing.md $lang:doc/l10n/$lang/guide-testing.md
[type: text] src/doc/guide-unsafe.md $lang:doc/l10n/$lang/guide-unsafe.md
+[type: text] src/doc/guide-unsafe.md $lang:doc/l10n/$lang/guide-crates.md
[type: text] src/doc/guide.md $lang:doc/l10n/$lang/guide.md
[type: text] src/doc/index.md $lang:doc/l10n/$lang/index.md
[type: text] src/doc/intro.md $lang:doc/l10n/$lang/intro.md
-> int
{ }
-fn baz( a:int, // shoudl work with a comment here
+fn baz( a:int, // should work with a comment here
b:char)
-> int
{ }
use core::atomic;
use core::clone::Clone;
+use core::fmt::{mod, Show};
use core::kinds::{Sync, Send};
use core::mem::{min_align_of, size_of, drop};
use core::mem;
}
}
+impl<T: Send + Sync + Show> Show for Arc<T> {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ (**self).fmt(f)
+ }
+}
+
impl<T: Send + Sync + Clone> Arc<T> {
/// Acquires a mutable pointer to the inner contents by guaranteeing that
/// the reference count is one (no sharing is possible).
use std::mem::drop;
use std::ops::Drop;
use std::option::{Option, Some, None};
+ use std::str::Str;
use std::sync::atomic;
use std::task;
use std::vec::Vec;
assert!(canary.load(atomic::Acquire) == 1);
drop(arc_weak);
}
+
+ #[test]
+ fn show_arc() {
+ let a = Arc::new(5u32);
+ assert!(format!("{}", a).as_slice() == "5")
+ }
}
/// size on the platform.
///
/// The `old_size` and `align` parameters are the parameters that were used to
-/// create the allocation referenced by `ptr`. The `old_size` parameter may also
-/// be the value returned by `usable_size` for the requested size.
+/// create the allocation referenced by `ptr`. The `old_size` parameter may be
+/// any value in range_inclusive(requested_size, usable_size).
#[inline]
pub unsafe fn reallocate(ptr: *mut u8, old_size: uint, size: uint, align: uint) -> *mut u8 {
imp::reallocate(ptr, old_size, size, align)
/// Extends or shrinks the allocation referenced by `ptr` to `size` bytes of
/// memory in-place.
///
-/// Returns true if successful, otherwise false if the allocation was not
-/// altered.
+/// If the operation succeeds, it returns `usable_size(size, align)` and if it
+/// fails (or is a no-op) it returns `usable_size(old_size, align)`.
///
/// 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
/// create the allocation referenced by `ptr`. The `old_size` parameter may be
/// any value in range_inclusive(requested_size, usable_size).
#[inline]
-pub unsafe fn reallocate_inplace(ptr: *mut u8, old_size: uint, size: uint, align: uint) -> bool {
+pub unsafe fn reallocate_inplace(ptr: *mut u8, old_size: uint, size: uint, align: uint) -> uint {
imp::reallocate_inplace(ptr, old_size, size, align)
}
///
/// The `ptr` parameter must not be null.
///
-/// The `size` and `align` parameters are the parameters that were used to
-/// create the allocation referenced by `ptr`. The `size` parameter may also be
-/// the value returned by `usable_size` for the requested size.
+/// The `old_size` and `align` parameters are the parameters that were used to
+/// create the allocation referenced by `ptr`. The `old_size` parameter may be
+/// any value in range_inclusive(requested_size, usable_size).
#[inline]
-pub unsafe fn deallocate(ptr: *mut u8, size: uint, align: uint) {
- imp::deallocate(ptr, size, align)
+pub unsafe fn deallocate(ptr: *mut u8, old_size: uint, align: uint) {
+ imp::deallocate(ptr, old_size, align)
}
/// Returns the usable size of an allocation created with the specified the
#[cfg(not(test))]
#[lang="exchange_free"]
#[inline]
-unsafe fn exchange_free(ptr: *mut u8, size: uint, align: uint) {
- deallocate(ptr, size, align);
+unsafe fn exchange_free(ptr: *mut u8, old_size: uint, align: uint) {
+ deallocate(ptr, old_size, align);
}
// The minimum alignment guaranteed by the architecture. This value is used to
#[cfg(any(target_arch = "arm",
target_arch = "mips",
target_arch = "mipsel"))]
-static MIN_ALIGN: uint = 8;
+const MIN_ALIGN: uint = 8;
#[cfg(any(target_arch = "x86",
target_arch = "x86_64"))]
-static MIN_ALIGN: uint = 16;
+const MIN_ALIGN: uint = 16;
#[cfg(jemalloc)]
mod imp {
}
#[inline]
- pub unsafe fn reallocate_inplace(ptr: *mut u8, old_size: uint, size: uint,
- align: uint) -> bool {
+ pub unsafe fn reallocate_inplace(ptr: *mut u8, _old_size: uint, size: uint,
+ align: uint) -> uint {
let flags = align_to_flags(align);
- let new_size = je_xallocx(ptr as *mut c_void, size as size_t, 0, flags) as uint;
- // checking for failure to shrink is tricky
- if size < old_size {
- usable_size(size, align) == new_size as uint
- } else {
- new_size >= size
- }
+ je_xallocx(ptr as *mut c_void, size as size_t, 0, flags) as uint
}
#[inline]
- pub unsafe fn deallocate(ptr: *mut u8, size: uint, align: uint) {
+ pub unsafe fn deallocate(ptr: *mut u8, old_size: uint, align: uint) {
let flags = align_to_flags(align);
- je_sdallocx(ptr as *mut c_void, size as size_t, flags)
+ je_sdallocx(ptr as *mut c_void, old_size as size_t, flags)
}
#[inline]
mod imp {
use core::cmp;
use core::ptr;
+ use core::ptr::RawPtr;
use libc;
- use libc_heap;
use super::MIN_ALIGN;
extern {
#[inline]
pub unsafe fn allocate(size: uint, align: uint) -> *mut u8 {
if align <= MIN_ALIGN {
- libc_heap::malloc_raw(size)
+ let ptr = libc::malloc(size as libc::size_t);
+ if ptr.is_null() {
+ ::oom();
+ }
+ ptr as *mut u8
} else {
let mut out = 0 as *mut libc::c_void;
let ret = posix_memalign(&mut out,
#[inline]
pub unsafe fn reallocate(ptr: *mut u8, old_size: uint, size: uint, align: uint) -> *mut u8 {
if align <= MIN_ALIGN {
- libc_heap::realloc_raw(ptr, size)
+ let ptr = libc::realloc(ptr as *mut libc::c_void, size as libc::size_t);
+ if ptr.is_null() {
+ ::oom();
+ }
+ ptr as *mut u8
} else {
let new_ptr = allocate(size, align);
ptr::copy_memory(new_ptr, ptr as *const u8, cmp::min(size, old_size));
}
#[inline]
- pub unsafe fn reallocate_inplace(_ptr: *mut u8, old_size: uint, size: uint,
- _align: uint) -> bool {
- size == old_size
+ pub unsafe fn reallocate_inplace(_ptr: *mut u8, old_size: uint, _size: uint,
+ _align: uint) -> uint {
+ old_size
}
#[inline]
- pub unsafe fn deallocate(ptr: *mut u8, _size: uint, _align: uint) {
+ pub unsafe fn deallocate(ptr: *mut u8, _old_size: uint, _align: uint) {
libc::free(ptr as *mut libc::c_void)
}
mod imp {
use libc::{c_void, size_t};
use libc;
- use libc_heap;
use core::ptr::RawPtr;
use super::MIN_ALIGN;
#[inline]
pub unsafe fn allocate(size: uint, align: uint) -> *mut u8 {
if align <= MIN_ALIGN {
- libc_heap::malloc_raw(size)
+ let ptr = libc::malloc(size as size_t);
+ if ptr.is_null() {
+ ::oom();
+ }
+ ptr as *mut u8
} else {
let ptr = _aligned_malloc(size as size_t, align as size_t);
if ptr.is_null() {
#[inline]
pub unsafe fn reallocate(ptr: *mut u8, _old_size: uint, size: uint, align: uint) -> *mut u8 {
if align <= MIN_ALIGN {
- libc_heap::realloc_raw(ptr, size)
+ let ptr = libc::realloc(ptr as *mut c_void, size as size_t);
+ if ptr.is_null() {
+ ::oom();
+ }
+ ptr as *mut u8
} else {
let ptr = _aligned_realloc(ptr as *mut c_void, size as size_t,
align as size_t);
}
#[inline]
- pub unsafe fn reallocate_inplace(_ptr: *mut u8, old_size: uint, size: uint,
- _align: uint) -> bool {
- size == old_size
+ pub unsafe fn reallocate_inplace(_ptr: *mut u8, old_size: uint, _size: uint,
+ _align: uint) -> uint {
+ old_size
}
#[inline]
- pub unsafe fn deallocate(ptr: *mut u8, _size: uint, align: uint) {
+ pub unsafe fn deallocate(ptr: *mut u8, _old_size: uint, align: uint) {
if align <= MIN_ALIGN {
libc::free(ptr as *mut libc::c_void)
} else {
let ptr = heap::allocate(size, 8);
let ret = heap::reallocate_inplace(ptr, size, size, 8);
heap::deallocate(ptr, size, 8);
- assert!(ret);
+ assert_eq!(ret, heap::usable_size(size, 8));
}
}
//!
//! ## Heap interfaces
//!
-//! The [`heap`](heap/index.html) and [`libc_heap`](libc_heap/index.html)
-//! modules are the unsafe interfaces to the underlying allocation systems. The
-//! `heap` module is considered the default heap, and is not necessarily backed
-//! by libc malloc/free. The `libc_heap` module is defined to be wired up to
-//! the system malloc/free.
+//! The [`heap`](heap/index.html) module defines the low-level interface to the
+//! default global allocator. It is not compatible with the libc allocator API.
#![crate_name = "alloc"]
#![experimental]
// Heaps provided for low-level allocation strategies
pub mod heap;
-pub mod libc_heap;
// Primitive types using the heaps above
+++ /dev/null
-// 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.
-
-
-//! The global (exchange) heap.
-
-use libc::{c_void, size_t, free, malloc, realloc};
-use core::ptr::{RawPtr, null_mut};
-
-/// 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
- // http://pubs.opengroup.org/onlinepubs/9699919799/functions/malloc.html
- if size == 0 {
- null_mut()
- } else {
- let p = malloc(size as size_t);
- if p.is_null() {
- ::oom();
- }
- p as *mut u8
- }
-}
-
-/// 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
- // http://pubs.opengroup.org/onlinepubs/9699919799/functions/realloc.html
- if size == 0 {
- free(ptr as *mut c_void);
- null_mut()
- } else {
- let p = realloc(ptr as *mut c_void, size as size_t);
- if p.is_null() {
- ::oom();
- }
- p as *mut u8
- }
-}
/// the BST strategy.
///
/// A B-Tree instead makes each node contain B-1 to 2B-1 elements in a contiguous array. By doing
-/// this, we reduce the number of allocations by a factor of B, and improve cache effeciency in
+/// this, we reduce the number of allocations by a factor of B, and improve cache efficiency in
/// searches. However, this does mean that searches will have to do *more* comparisons on average.
/// The precise number of comparisons depends on the node search strategy used. For optimal cache
-/// effeciency, one could search the nodes linearly. For optimal comparisons, one could search
+/// efficiency, one could search the nodes linearly. For optimal comparisons, one could search
/// the node using binary search. As a compromise, one could also perform a linear search
/// that initially only checks every i<sup>th</sup> element for some choice of i.
///
// hard. For now, we accept this cost in the name of correctness and simplicity.
//
// As a compromise, keys and vals could be merged into one Vec<(K, V)>, which would shave
- // off 3 words, but possibly hurt our cache effeciency during search, which only cares about
+ // off 3 words, but possibly hurt our cache efficiency during search, which only cares about
// keys. This would also avoid the Zip we use in our iterator implementations. This is
// probably worth investigating.
//
/// `GoDown` will be yielded with the index of the subtree the key must lie in.
pub fn search(&self, key: &K) -> SearchResult {
// FIXME(Gankro): Tune when to search linear or binary based on B (and maybe K/V).
- // For the B configured as of this writing (B = 6), binary search was *singnificantly*
+ // For the B configured as of this writing (B = 6), binary search was *significantly*
// worse for uints.
self.search_linear(key)
}
}
}
- /// Steal! Stealing is roughly analagous to a binary tree rotation.
+ /// Steal! Stealing is roughly analogous to a binary tree rotation.
/// In this case, we're "rotating" right.
unsafe fn steal_to_left(&mut self, underflowed_child_index: uint) {
// Take the biggest stuff off left
}
};
- // Swap the parent's seperating key-value pair with left's
+ // Swap the parent's separating key-value pair with left's
self.unsafe_swap(underflowed_child_index - 1, &mut key, &mut val);
// Put them at the start of right
}
}
- /// Steal! Stealing is roughly analagous to a binary tree rotation.
+ /// Steal! Stealing is roughly analogous to a binary tree rotation.
/// In this case, we're "rotating" left.
unsafe fn steal_to_right(&mut self, underflowed_child_index: uint) {
// Take the smallest stuff off right
}
};
- // Swap the parent's seperating key-value pair with right's
+ // Swap the parent's separating key-value pair with right's
self.unsafe_swap(underflowed_child_index, &mut key, &mut val);
// Put them at the end of left
}
/// Merge! Left and right will be smooshed into one node, along with the key-value
- /// pair that seperated them in their parent.
+ /// pair that separated them in their parent.
unsafe fn merge_children(&mut self, left_index: uint) {
- // Permanently remove right's index, and the key-value pair that seperates
+ // Permanently remove right's index, and the key-value pair that separates
// left and right
let (key, val, right) = {
match (self.keys.remove(left_index),
left.absorb(key, val, right);
}
- /// Take all the values from right, seperated by the given key and value
+ /// Take all the values from right, separated by the given key and value
fn absorb(&mut self, key: K, val: V, right: Node<K, V>) {
// Just as a sanity check, make sure we can fit this guy in
debug_assert!(self.len() + right.len() <= self.capacity())
#[experimental]
fn into_vec(mut self) -> Vec<T> {
unsafe {
- let xs = Vec::from_raw_parts(self.len(), self.len(), self.as_mut_ptr());
+ let xs = Vec::from_raw_parts(self.as_mut_ptr(), self.len(), self.len());
mem::forget(self);
xs
}
#[inline]
pub unsafe fn from_parts(buf: *mut u8, length: uint, capacity: uint) -> String {
String {
- vec: Vec::from_raw_parts(length, capacity, buf),
+ vec: Vec::from_raw_parts(buf, length, capacity),
}
}
#[unsafe_no_drop_flag]
#[stable]
pub struct Vec<T> {
+ ptr: *mut T,
len: uint,
cap: uint,
- ptr: *mut T
}
impl<T> Vec<T> {
// non-null value which is fine since we never call deallocate on the ptr
// if cap is 0. The reason for this is because the pointer of a slice
// being NULL would break the null pointer optimization for enums.
- Vec { len: 0, cap: 0, ptr: EMPTY as *mut T }
+ Vec { ptr: EMPTY as *mut T, len: 0, cap: 0 }
}
/// Constructs a new, empty `Vec` with the specified capacity.
#[stable]
pub fn with_capacity(capacity: uint) -> Vec<T> {
if mem::size_of::<T>() == 0 {
- Vec { len: 0, cap: uint::MAX, ptr: EMPTY as *mut T }
+ Vec { ptr: EMPTY as *mut T, len: 0, cap: uint::MAX }
} else if capacity == 0 {
Vec::new()
} else {
let size = capacity.checked_mul(&mem::size_of::<T>())
.expect("capacity overflow");
let ptr = unsafe { allocate(size, mem::min_align_of::<T>()) };
- Vec { len: 0, cap: capacity, ptr: ptr as *mut T }
+ Vec { ptr: ptr as *mut T, len: 0, cap: capacity }
}
}
/// }
/// ```
#[experimental]
- pub unsafe fn from_raw_parts(length: uint, capacity: uint,
- ptr: *mut T) -> Vec<T> {
- Vec { len: length, cap: capacity, ptr: ptr }
+ pub unsafe fn from_raw_parts(ptr: *mut T, length: uint,
+ capacity: uint) -> Vec<T> {
+ Vec { ptr: ptr, len: length, cap: capacity }
}
/// Consumes the `Vec`, partitioning it based on a predicate.
pub fn as_vec<'a, T>(x: &'a [T]) -> DerefVec<'a, T> {
unsafe {
DerefVec {
- x: Vec::from_raw_parts(x.len(), x.len(), x.as_ptr() as *mut T),
+ x: Vec::from_raw_parts(x.as_ptr() as *mut T, x.len(), x.len()),
l: ContravariantLifetime::<'a>
}
}
let vec_cap = pv.vec.capacity();
let vec_ptr = pv.vec.as_mut_ptr() as *mut U;
mem::forget(pv);
- Vec::from_raw_parts(vec_len, vec_cap, vec_ptr)
+ Vec::from_raw_parts(vec_ptr, vec_len, vec_cap)
}
} else {
// Put the `Vec` into the `PartialVecZeroSized` structure and
*self.value.get() = value;
}
}
+
+ /// Get a reference to the underlying `UnsafeCell`.
+ ///
+ /// This can be used to circumvent `Cell`'s safety checks.
+ ///
+ /// This function is `unsafe` because `UnsafeCell`'s field is public.
+ #[inline]
+ #[experimental]
+ pub unsafe fn as_unsafe_cell<'a>(&'a self) -> &'a UnsafeCell<T> {
+ &self.value
+ }
}
#[unstable = "waiting for `Clone` trait to become stable"]
None => fail!("RefCell<T> already borrowed")
}
}
+
+ /// Get a reference to the underlying `UnsafeCell`.
+ ///
+ /// This can be used to circumvent `RefCell`'s safety checks.
+ ///
+ /// This function is `unsafe` because `UnsafeCell`'s field is public.
+ #[inline]
+ #[experimental]
+ pub unsafe fn as_unsafe_cell<'a>(&'a self) -> &'a UnsafeCell<T> {
+ &self.value
+ }
}
#[unstable = "waiting for `Clone` to become stable"]
use mem;
/// The representation of a Rust slice
+#[repr(C)]
pub struct Slice<T> {
pub data: *const T,
pub len: uint,
}
/// The representation of a Rust closure
+#[repr(C)]
pub struct Closure {
pub code: *mut (),
pub env: *mut (),
}
/// The representation of a Rust procedure (`proc()`)
+#[repr(C)]
pub struct Procedure {
pub code: *mut (),
pub env: *mut (),
///
/// This struct does not have a `Repr` implementation
/// because there is no way to refer to all trait objects generically.
+#[repr(C)]
pub struct TraitObject {
pub data: *mut (),
pub vtable: *mut (),
impl<'a, T> Repr<Slice<T>> for &'a [T] {}
impl<'a> Repr<Slice<u8>> for &'a str {}
-
}
assert!(x.try_borrow_mut().is_some());
}
+
+#[test]
+fn as_unsafe_cell() {
+ let c1: Cell<uint> = Cell::new(0u);
+ c1.set(1u);
+ assert_eq!(1u, unsafe { *c1.as_unsafe_cell().get() });
+
+ let c2: Cell<uint> = Cell::new(0u);
+ unsafe { *c2.as_unsafe_cell().get() = 1u; }
+ assert_eq!(1u, c2.get());
+
+ let r1: RefCell<uint> = RefCell::new(0u);
+ *r1.borrow_mut() = 1u;
+ assert_eq!(1u, unsafe { *r1.as_unsafe_cell().get() });
+
+ let r2: RefCell<uint> = RefCell::new(0u);
+ unsafe { *r2.as_unsafe_cell().get() = 1u; }
+ assert_eq!(1u, *r2.borrow());
+}
fn reseed(&mut self, seed: &'a [u32]) {
// reset state
self.init(&[0u32, ..KEY_WORDS]);
- // set key inplace
+ // set key in place
let key = self.state.slice_mut(4, 4+KEY_WORDS);
for (k, s) in key.iter_mut().zip(seed.iter()) {
*k = *s;
.stdout(::std::io::process::InheritFd(1))
.stderr(::std::io::process::InheritFd(2));
match cmd.status() {
- Ok(_) => {},
+ Ok(status) => {
+ if !status.success() {
+ sess.err(format!("linking of {} with `{}` failed",
+ output_path.display(), cmd).as_slice());
+ sess.abort_if_errors();
+ }
+ },
Err(e) => {
sess.err(format!("could not exec the linker `{}`: {}",
pname,
E0015,
E0016,
E0017,
+ E0018,
E0019,
E0020,
E0022,
// Otherwise, just a plain error.
match assignee_cmt.note {
mc::NoteClosureEnv(upvar_id) => {
- self.bccx.span_err(
- assignment_span,
- format!("cannot assign to {}",
- self.bccx.cmt_to_string(&*assignee_cmt)).as_slice());
- self.bccx.span_note(
- self.tcx().map.span(upvar_id.closure_expr_id),
- "consider changing this closure to take self by mutable reference");
+ // If this is an `Fn` closure, it simply can't mutate upvars.
+ // If it's an `FnMut` closure, the original variable was declared immutable.
+ // We need to determine which is the case here.
+ let kind = match assignee_cmt.upvar().unwrap().cat {
+ mc::cat_upvar(mc::Upvar { kind, .. }) => kind,
+ _ => unreachable!()
+ };
+ if kind == ty::FnUnboxedClosureKind {
+ self.bccx.span_err(
+ assignment_span,
+ format!("cannot assign to {}",
+ self.bccx.cmt_to_string(&*assignee_cmt)).as_slice());
+ self.bccx.span_note(
+ self.tcx().map.span(upvar_id.closure_expr_id),
+ "consider changing this closure to take self by mutable reference");
+ } else {
+ self.bccx.span_err(
+ assignment_span,
+ format!("cannot assign to {} {}",
+ assignee_cmt.mutbl.to_user_str(),
+ self.bccx.cmt_to_string(&*assignee_cmt)).as_slice());
+ }
}
_ => match opt_loan_path(&assignee_cmt) {
Some(lp) => {
mc::cat_rvalue(..) |
mc::cat_static_item |
mc::cat_deref(_, _, mc::UnsafePtr(..)) |
- mc::cat_deref(_, _, mc::BorrowedPtr(..)) |
mc::cat_deref(_, _, mc::Implicit(..)) => {
assert_eq!(cmt.mutbl, mc::McDeclared);
return;
}
- mc::cat_discr(b, _) |
+ mc::cat_deref(_, _, mc::BorrowedPtr(..)) => {
+ assert_eq!(cmt.mutbl, mc::McDeclared);
+ // We need to drill down to upvar if applicable
+ match cmt.upvar() {
+ Some(b) => cmt = b,
+ None => return
+ }
+ }
+
mc::cat_deref(b, _, mc::OwnedPtr) => {
assert_eq!(cmt.mutbl, mc::McInherited);
cmt = b;
}
}
- mc::cat_deref(ref b, _, mc::OwnedPtr) |
- mc::cat_discr(ref b, _) => {
+ mc::cat_deref(ref b, _, mc::OwnedPtr) => {
check_and_get_illegal_move_origin(bccx, b)
}
}
mc::cat_interior(ref base, _) => { // L-Field
self.check(base, discr_scope)
}
-
- mc::cat_discr(ref base, new_discr_scope) => {
- // Subtle: in a match, we must ensure that each binding
- // variable remains valid for the duration of the arm in
- // which it appears, presuming that this arm is taken.
- // But it is inconvenient in trans to root something just
- // for one arm. Therefore, we insert a cat_discr(),
- // basically a special kind of category that says "if this
- // value must be dynamically rooted, root it for the scope
- // `match_id`".
- //
- // As an example, consider this scenario:
- //
- // let mut x = @Some(3);
- // match *x { Some(y) {...} None {...} }
- //
- // Technically, the value `x` need only be rooted
- // in the `some` arm. However, we evaluate `x` in trans
- // before we know what arm will be taken, so we just
- // always root it for the duration of the match.
- //
- // As a second example, consider *this* scenario:
- //
- // let x = @@Some(3);
- // match x { @@Some(y) {...} @@None {...} }
- //
- // Here again, `x` need only be rooted in the `some` arm.
- // In this case, the value which needs to be rooted is
- // found only when checking which pattern matches: but
- // this check is done before entering the arm. Therefore,
- // even in this case we just choose to keep the value
- // rooted for the entire match. This means the value will be
- // rooted even if the none arm is taken. Oh well.
- //
- // At first, I tried to optimize the second case to only
- // root in one arm, but the result was suboptimal: first,
- // it interfered with the construction of phi nodes in the
- // arm, as we were adding code to root values before the
- // phi nodes were added. This could have been addressed
- // with a second basic block. However, the naive approach
- // also yielded suboptimal results for patterns like:
- //
- // let x = @@...;
- // match x { @@some_variant(y) | @@some_other_variant(y) =>
- //
- // The reason is that we would root the value once for
- // each pattern and not once per arm. This is also easily
- // fixed, but it's yet more code for what is really quite
- // the corner case.
- //
- // Nonetheless, if you decide to optimize this case in the
- // future, you need only adjust where the cat_discr()
- // node appears to draw the line between what will be rooted
- // in the *arm* vs the *match*.
- self.check(base, Some(new_discr_scope))
- }
}
}
}
mc::cat_downcast(ref cmt) |
mc::cat_deref(ref cmt, _, mc::OwnedPtr) |
- mc::cat_interior(ref cmt, _) |
- mc::cat_discr(ref cmt, _) => {
+ mc::cat_interior(ref cmt, _) => {
self.scope(cmt)
}
}
/*!
* Guarantees that `addr_of(cmt)` will be valid for the duration of
* `static_scope_r`, or reports an error. This may entail taking
- * out loans, which will be added to the `req_loan_map`. This can
- * also entail "rooting" GC'd pointers, which means ensuring
- * dynamically that they are not freed.
+ * out loans, which will be added to the `req_loan_map`.
*/
debug!("guarantee_valid(borrow_id={}, cmt={}, \
self.extend(result, cmt.mutbl, LpInterior(i))
}
- mc::cat_deref(cmt_base, _, pk @ mc::OwnedPtr) => {
- // R-Deref-Send-Pointer
- //
- // When we borrow the interior of an owned pointer, we
- // cannot permit the base to be mutated, because that
- // would cause the unique pointer to be freed.
- //
- // Eventually we should make these non-special and
- // just rely on Deref<T> implementation.
- let result = self.restrict(cmt_base);
- self.extend(result, cmt.mutbl, LpDeref(pk))
- }
mc::cat_static_item(..) => {
Safe
}
- mc::cat_deref(cmt_base, _, mc::BorrowedPtr(ty::ImmBorrow, lt)) |
- mc::cat_deref(cmt_base, _, mc::Implicit(ty::ImmBorrow, lt)) => {
- // R-Deref-Imm-Borrowed
- if !self.bccx.is_subregion_of(self.loan_region, lt) {
- self.bccx.report(
- BckError {
- span: self.span,
- cause: self.cause,
- cmt: cmt_base,
- code: err_borrowed_pointer_too_short(
- self.loan_region, lt)});
- return Safe;
- }
- Safe
- }
-
mc::cat_deref(cmt_base, _, pk) => {
match pk {
- mc::BorrowedPtr(ty::MutBorrow, lt) |
- mc::BorrowedPtr(ty::UniqueImmBorrow, lt) |
- mc::Implicit(ty::MutBorrow, lt) |
- mc::Implicit(ty::UniqueImmBorrow, lt) => {
- // R-Deref-Mut-Borrowed
+ mc::OwnedPtr => {
+ // R-Deref-Send-Pointer
+ //
+ // When we borrow the interior of an owned pointer, we
+ // cannot permit the base to be mutated, because that
+ // would cause the unique pointer to be freed.
+ //
+ // Eventually we should make these non-special and
+ // just rely on Deref<T> implementation.
+ let result = self.restrict(cmt_base);
+ self.extend(result, cmt.mutbl, LpDeref(pk))
+ }
+ mc::Implicit(bk, lt) | mc::BorrowedPtr(bk, lt) => {
+ // R-Deref-[Mut-]Borrowed
if !self.bccx.is_subregion_of(self.loan_region, lt) {
self.bccx.report(
BckError {
return Safe;
}
- let result = self.restrict(cmt_base);
- self.extend(result, cmt.mutbl, LpDeref(pk))
- }
- mc::UnsafePtr(..) => {
- // We are very trusting when working with unsafe
- // pointers.
- Safe
- }
- _ => {
- self.bccx.tcx.sess.span_bug(self.span,
- "unhandled memcat in \
- cat_deref")
+ match bk {
+ ty::ImmBorrow => Safe,
+ ty::MutBorrow | ty::UniqueImmBorrow => {
+ // R-Deref-Mut-Borrowed
+ //
+ // The referent can be aliased after the
+ // references lifetime ends (by a newly-unfrozen
+ // borrow).
+ let result = self.restrict(cmt_base);
+ self.extend(result, cmt.mutbl, LpDeref(pk))
+ }
+ }
}
+ // Borrowck is not relevant for unsafe pointers
+ mc::UnsafePtr(..) => Safe
}
}
-
- mc::cat_discr(cmt_base, _) => {
- self.restrict(cmt_base)
- }
}
}
})
}
- mc::cat_downcast(ref cmt_base) |
- mc::cat_discr(ref cmt_base, _) => {
+ mc::cat_downcast(ref cmt_base) => {
opt_loan_path(cmt_base)
}
}
match err.code {
err_mutbl => {
let descr = match err.cmt.note {
- mc::NoteClosureEnv(_) => {
+ mc::NoteClosureEnv(_) | mc::NoteUpvarRef(_) => {
self.cmt_to_string(&*err.cmt)
}
_ => match opt_loan_path(&err.cmt) {
match code {
err_mutbl(..) => {
match err.cmt.note {
- mc::NoteClosureEnv(upvar_id) => {
- self.tcx.sess.span_note(
- self.tcx.map.span(upvar_id.closure_expr_id),
- "consider changing this closure to take \
- self by mutable reference");
+ mc::NoteClosureEnv(upvar_id) | mc::NoteUpvarRef(upvar_id) => {
+ // If this is an `Fn` closure, it simply can't mutate upvars.
+ // If it's an `FnMut` closure, the original variable was declared immutable.
+ // We need to determine which is the case here.
+ let kind = match err.cmt.upvar().unwrap().cat {
+ mc::cat_upvar(mc::Upvar { kind, .. }) => kind,
+ _ => unreachable!()
+ };
+ if kind == ty::FnUnboxedClosureKind {
+ self.tcx.sess.span_note(
+ self.tcx.map.span(upvar_id.closure_expr_id),
+ "consider changing this closure to take \
+ self by mutable reference");
+ }
}
_ => {}
}
}
}
ExprLit(_) => (),
- ExprCast(_, _) => {
- let ety = ty::expr_ty(v.tcx, e);
- if !ty::type_is_numeric(ety) && !ty::type_is_unsafe_ptr(ety) {
+ ExprCast(ref from, _) => {
+ let toty = ty::expr_ty(v.tcx, e);
+ let fromty = ty::expr_ty(v.tcx, &**from);
+ if !ty::type_is_numeric(toty) && !ty::type_is_unsafe_ptr(toty) {
span_err!(v.tcx.sess, e.span, E0012,
"can not cast to `{}` in a constant expression",
- ppaux::ty_to_string(v.tcx, ety));
+ ppaux::ty_to_string(v.tcx, toty));
+ }
+ if ty::type_is_unsafe_ptr(fromty) && ty::type_is_numeric(toty) {
+ span_err!(v.tcx.sess, e.span, E0018,
+ "can not cast a pointer to an integer in a constant \
+ expression");
}
}
ExprPath(ref pth) => {
break
}
mc::cat_deref(ref cmt, _, _) |
- mc::cat_discr(ref cmt, _) |
mc::cat_downcast(ref cmt) |
mc::cat_interior(ref cmt, _) => cur = cmt,
}
mc::cat_downcast(..) |
- mc::cat_discr(..) |
mc::cat_upvar(..) => unreachable!(),
mc::cat_local(..) => {
_cmt: mc::cmt,
_mode: euv::ConsumeMode) {}
}
-
cat_deref(cmt, uint, PointerKind), // deref of a ptr
cat_interior(cmt, InteriorKind), // something interior: field, tuple, etc
cat_downcast(cmt), // selects a particular enum variant (*1)
- cat_discr(cmt, ast::NodeId), // match discriminant (see preserve())
// (*1) downcast is only required if the enum has more than one variant
}
// FnOnce | copied | upvar -> &'up bk
// old stack | N/A | upvar -> &'env mut -> &'up bk
// old proc/once | copied | N/A
+ let var_ty = if_ok!(self.node_ty(var_id));
+
let upvar_id = ty::UpvarId { var_id: var_id,
closure_expr_id: fn_node_id };
- // Do we need to deref through an env reference?
- let has_env_deref = kind != ty::FnOnceUnboxedClosureKind;
-
// Mutability of original variable itself
let var_mutbl = MutabilityCategory::from_local(self.tcx(), var_id);
- // Mutability of environment dereference
- let env_mutbl = match kind {
- ty::FnOnceUnboxedClosureKind => var_mutbl,
- ty::FnMutUnboxedClosureKind => McInherited,
- ty::FnUnboxedClosureKind => McImmutable
+ // Construct information about env pointer dereference, if any
+ let mutbl = match kind {
+ ty::FnOnceUnboxedClosureKind => None, // None, env is by-value
+ ty::FnMutUnboxedClosureKind => match mode { // Depends on capture type
+ ast::CaptureByValue => Some(var_mutbl), // Mutable if the original var is
+ ast::CaptureByRef => Some(McDeclared) // Mutable regardless
+ },
+ ty::FnUnboxedClosureKind => Some(McImmutable) // Never mutable
};
+ let env_info = mutbl.map(|env_mutbl| {
+ // Look up the node ID of the closure body so we can construct
+ // a free region within it
+ let fn_body_id = {
+ let fn_expr = match self.tcx().map.find(fn_node_id) {
+ Some(ast_map::NodeExpr(e)) => e,
+ _ => unreachable!()
+ };
- // Look up the node ID of the closure body so we can construct
- // a free region within it
- let fn_body_id = {
- let fn_expr = match self.tcx().map.find(fn_node_id) {
- Some(ast_map::NodeExpr(e)) => e,
- _ => unreachable!()
+ match fn_expr.node {
+ ast::ExprFnBlock(_, _, ref body) |
+ ast::ExprProc(_, ref body) |
+ ast::ExprUnboxedFn(_, _, _, ref body) => body.id,
+ _ => unreachable!()
+ }
};
- match fn_expr.node {
- ast::ExprFnBlock(_, _, ref body) |
- ast::ExprProc(_, ref body) |
- ast::ExprUnboxedFn(_, _, _, ref body) => body.id,
- _ => unreachable!()
- }
- };
-
- // Region of environment pointer
- let env_region = ty::ReFree(ty::FreeRegion {
- scope_id: fn_body_id,
- bound_region: ty::BrEnv
- });
+ // Region of environment pointer
+ let env_region = ty::ReFree(ty::FreeRegion {
+ scope_id: fn_body_id,
+ bound_region: ty::BrEnv
+ });
- let env_ptr = BorrowedPtr(if env_mutbl.is_mutable() {
- ty::MutBorrow
- } else {
- ty::ImmBorrow
- }, env_region);
+ let env_ptr = BorrowedPtr(if env_mutbl.is_mutable() {
+ ty::MutBorrow
+ } else {
+ ty::ImmBorrow
+ }, env_region);
- let var_ty = if_ok!(self.node_ty(var_id));
+ (env_mutbl, env_ptr)
+ });
// First, switch by capture mode
Ok(match mode {
note: NoteNone
};
- if has_env_deref {
- // We need to add the env deref. This means that
- // the above is actually immutable and has a ref
- // type. However, nothing should actually look at
- // the type, so we can get away with stuffing a
- // `ty_err` in there instead of bothering to
- // construct a proper one.
- base.mutbl = McImmutable;
- base.ty = ty::mk_err();
- Rc::new(cmt_ {
- id: id,
- span: span,
- cat: cat_deref(Rc::new(base), 0, env_ptr),
- mutbl: env_mutbl,
- ty: var_ty,
- note: NoteClosureEnv(upvar_id)
- })
- } else {
- Rc::new(base)
+ match env_info {
+ Some((env_mutbl, env_ptr)) => {
+ // We need to add the env deref. This means
+ // that the above is actually immutable and
+ // has a ref type. However, nothing should
+ // actually look at the type, so we can get
+ // away with stuffing a `ty_err` in there
+ // instead of bothering to construct a proper
+ // one.
+ base.mutbl = McImmutable;
+ base.ty = ty::mk_err();
+ Rc::new(cmt_ {
+ id: id,
+ span: span,
+ cat: cat_deref(Rc::new(base), 0, env_ptr),
+ mutbl: env_mutbl,
+ ty: var_ty,
+ note: NoteClosureEnv(upvar_id)
+ })
+ }
+ None => Rc::new(base)
}
},
ast::CaptureByRef => {
note: NoteNone
};
- // As in the by-value case, add env deref if needed
- if has_env_deref {
- base = cmt_ {
- id: id,
- span: span,
- cat: cat_deref(Rc::new(base), 0, env_ptr),
- mutbl: env_mutbl,
- ty: ty::mk_err(),
- note: NoteClosureEnv(upvar_id)
- };
+ match env_info {
+ Some((env_mutbl, env_ptr)) => {
+ base = cmt_ {
+ id: id,
+ span: span,
+ cat: cat_deref(Rc::new(base), 0, env_ptr),
+ mutbl: env_mutbl,
+ ty: ty::mk_err(),
+ note: NoteClosureEnv(upvar_id)
+ };
+ }
+ None => {}
}
// Look up upvar borrow so we can get its region
cat_upvar(ref var) => {
upvar_to_string(var, true)
}
- cat_discr(ref cmt, _) => {
- self.cmt_to_string(&**cmt)
- }
cat_downcast(ref cmt) => {
self.cmt_to_string(&**cmt)
}
Rc::new((*self).clone())
}
cat_downcast(ref b) |
- cat_discr(ref b, _) |
cat_interior(ref b, _) |
cat_deref(ref b, _, OwnedPtr) => {
b.guarantor()
cat_deref(ref b, _, Implicit(ty::UniqueImmBorrow, _)) |
cat_downcast(ref b) |
cat_deref(ref b, _, OwnedPtr) |
- cat_interior(ref b, _) |
- cat_discr(ref b, _) => {
+ cat_interior(ref b, _) => {
// Aliasability depends on base cmt
b.freely_aliasable(ctxt)
}
cat_downcast(ref cmt) => {
format!("{}->(enum)", cmt.cat.repr(tcx))
}
- cat_discr(ref cmt, _) => {
- cmt.cat.repr(tcx)
- }
}
}
}
type of the receiver and various other complications. The procedure is
described in `select.rs` in the "METHOD MATCHING" section.
+# Caching and subtle considerations therewith
+
+In general we attempt to cache the results of trait selection. This
+is a somewhat complex process. Part of the reason for this is that we
+want to be able to cache results even when all the types in the trait
+reference are not fully known. In that case, it may happen that the
+trait selection process is also influencing type variables, so we have
+to be able to not only cache the *result* of the selection process,
+but *replay* its effects on the type variables.
+
+## An example
+
+The high-level idea of how the cache works is that we first replace
+all unbound inference variables with skolemized versions. Therefore,
+if we had a trait reference `uint : Foo<$1>`, where `$n` is an unbound
+inference variable, we might replace it with `uint : Foo<%0>`, where
+`%n` is a skolemized type. We would then look this up in the cache.
+If we found a hit, the hit would tell us the immediate next step to
+take in the selection process: i.e., apply impl #22, or apply where
+clause `X : Foo<Y>`. Let's say in this case there is no hit.
+Therefore, we search through impls and where clauses and so forth, and
+we come to the conclusion that the only possible impl is this one,
+with def-id 22:
+
+ impl Foo<int> for uint { ... } // Impl #22
+
+We would then record in the cache `uint : Foo<%0> ==>
+ImplCandidate(22)`. Next we would confirm `ImplCandidate(22)`, which
+would (as a side-effect) unify `$1` with `int`.
+
+Now, at some later time, we might come along and see a `uint :
+Foo<$3>`. When skolemized, this would yield `uint : Foo<%0>`, just as
+before, and hence the cache lookup would succeed, yielding
+`ImplCandidate(22)`. We would confirm `ImplCandidate(22)` which would
+(as a side-effect) unify `$3` with `int`.
+
+## Where clauses and the local vs global cache
+
+One subtle interaction is that the results of trait lookup will vary
+depending on what where clauses are in scope. Therefore, we actually
+have *two* caches, a local and a global cache. The local cache is
+attached to the `ParameterEnvironment` and the global cache attached
+to the `tcx`. We use the local cache whenever the result might depend
+on the where clauses that are in scope. The determination of which
+cache to use is done by the method `pick_candidate_cache` in
+`select.rs`.
+
+There are two cases where we currently use the local cache. The
+current rules are probably more conservative than necessary.
+
+### Trait references that involve parameter types
+
+The most obvious case where you need the local environment is
+when the trait reference includes parameter types. For example,
+consider the following function:
+
+ impl<T> Vec<T> {
+ fn foo(x: T)
+ where T : Foo
+ { ... }
+
+ fn bar(x: T)
+ { ... }
+ }
+
+If there is an obligation `T : Foo`, or `int : Bar<T>`, or whatever,
+clearly the results from `foo` and `bar` are potentially different,
+since the set of where clauses in scope are different.
+
+### Trait references with unbound variables when where clauses are in scope
+
+There is another less obvious interaction which involves unbound variables
+where *only* where clauses are in scope (no impls). This manifested as
+issue #18209 (`run-pass/trait-cache-issue-18209.rs`). Consider
+this snippet:
+
+```
+pub trait Foo {
+ fn load_from() -> Box<Self>;
+ fn load() -> Box<Self> {
+ Foo::load_from()
+ }
+}
+```
+
+The default method will incur an obligation `$0 : Foo` from the call
+to `load_from`. If there are no impls, this can be eagerly resolved to
+`VtableParam(Self : Foo)` and cached. Because the trait reference
+doesn't involve any parameters types (only the resolution does), this
+result was stored in the global cache, causing later calls to
+`Foo::load_from()` to get nonsense.
+
+To fix this, we always use the local cache if there are unbound
+variables and where clauses in scope. This is more conservative than
+necessary as far as I can tell. However, it still seems to be a simple
+rule and I observe ~99% hit rate on rustc, so it doesn't seem to hurt
+us in particular.
+
+Here is an example of the kind of subtle case that I would be worried
+about with a more complex rule (although this particular case works
+out ok). Imagine the trait reference doesn't directly reference a
+where clause, but the where clause plays a role in the winnowing
+phase. Something like this:
+
+```
+pub trait Foo<T> { ... }
+pub trait Bar { ... }
+impl<U,T:Bar> Foo<U> for T { ... } // Impl A
+impl Foo<char> for uint { ... } // Impl B
+```
+
+Now, in some function, we have no where clauses in scope, and we have
+an obligation `$1 : Foo<$0>`. We might then conclude that `$0=char`
+and `$1=uint`: this is because for impl A to apply, `uint:Bar` would
+have to hold, and we know it does not or else the coherence check
+would have failed. So we might enter into our global cache: `$1 :
+Foo<$0> => Impl B`. Then we come along in a different scope, where a
+generic type `A` is around with the bound `A:Bar`. Now suddenly the
+impl is viable.
+
+The flaw in this imaginary DOOMSDAY SCENARIO is that we would not
+currently conclude that `$1 : Foo<$0>` implies that `$0 == uint` and
+`$1 == char`, even though it is true that (absent type parameters)
+there is no other type the user could enter. However, it is not
+*completely* implausible that we *could* draw this conclusion in the
+future; we wouldn't have to guess types, in particular, we could be
+led by the impls.
+
*/
// can be applied to particular types. It skips the "confirmation"
// step and hence completely ignores output type parameters.
//
- // The result is "true" if the obliation *may* hold and "false" if
+ // The result is "true" if the obligation *may* hold and "false" if
// we can be sure it does not.
pub fn evaluate_obligation_intercrate(&mut self,
cache_skol_trait_ref: &Rc<ty::TraitRef>)
-> &SelectionCache
{
+ // High-level idea: we have to decide whether to consult the
+ // cache that is specific to this scope, or to consult the
+ // global cache. We want the cache that is specific to this
+ // scope whenever where clauses might affect the result.
+
// If the trait refers to any parameters in scope, then use
- // the cache of the param-environment. This is because the
- // result will depend on the where clauses that are in
- // scope. Otherwise, use the generic tcx cache, since the
- // result holds across all environments.
+ // the cache of the param-environment.
if
cache_skol_trait_ref.input_types().iter().any(
|&t| ty::type_has_self(t) || ty::type_has_params(t))
{
- &self.param_env.selection_cache
- } else {
- &self.tcx().selection_cache
+ return &self.param_env.selection_cache;
}
+
+ // If the trait refers to unbound type variables, and there
+ // are where clauses in scope, then use the local environment.
+ // If there are no where clauses in scope, which is a very
+ // common case, then we can use the global environment.
+ // See the discussion in doc.rs for more details.
+ if
+ !self.param_env.caller_obligations.is_empty()
+ &&
+ cache_skol_trait_ref.input_types().iter().any(
+ |&t| ty::type_has_ty_infer(t))
+ {
+ return &self.param_env.selection_cache;
+ }
+
+ // Otherwise, we can use the global cache.
+ &self.tcx().selection_cache
}
fn check_candidate_cache(&mut self,
util::obligations_for_generics(self.tcx(), cause, recursion_depth,
&impl_generics, impl_substs)
}
-
- fn contains_skolemized_types(&self,
- ty: ty::t)
- -> bool
- {
- /*!
- * True if the type contains skolemized variables.
- */
-
- let mut found_skol = false;
-
- ty::walk_ty(ty, |t| {
- match ty::get(t).sty {
- ty::ty_infer(ty::SkolemizedTy(_)) => { found_skol = true; }
- _ => { }
- }
- });
-
- found_skol
- }
}
impl Repr for Candidate {
controlflow::trans_loop(bcx, expr.id, &**body)
}
ast::ExprAssign(ref dst, ref src) => {
+ let src_datum = unpack_datum!(bcx, trans(bcx, &**src));
let dst_datum = unpack_datum!(bcx, trans_to_lvalue(bcx, &**dst, "assign"));
if ty::type_needs_drop(bcx.tcx(), dst_datum.ty) {
// We could avoid this intermediary with some analysis
// to determine whether `dst` may possibly own `src`.
debuginfo::set_source_location(bcx.fcx, expr.id, expr.span);
- let src_datum = unpack_datum!(bcx, trans(bcx, &**src));
let src_datum = unpack_datum!(
bcx, src_datum.to_rvalue_datum(bcx, "ExprAssign"));
bcx = glue::drop_ty(bcx,
Some(NodeInfo { id: expr.id, span: expr.span }));
src_datum.store_to(bcx, dst_datum.val)
} else {
- trans_into(bcx, &**src, SaveIn(dst_datum.to_llref()))
+ src_datum.store_to(bcx, dst_datum.val)
}
}
ast::ExprAssignOp(op, ref dst, ref src) => {
deref_owned_pointer(bcx, expr, datum, content_ty)
} else {
// A fat pointer and an opened DST value have the same
- // represenation just different types. Since there is no
+ // representation just different types. Since there is no
// temporary for `*e` here (because it is unsized), we cannot
// emulate the sized object code path for running drop glue and
// free. Instead, we schedule cleanup for `e`, turning it into
// 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
+ // A fat pointer and an opened DST value have the same representation
// just different types.
DatumBlock::new(bcx, Datum::new(datum.val,
ty::mk_open(bcx.tcx(), content_ty),
pub repr_hint_cache: RefCell<DefIdMap<Rc<Vec<attr::ReprAttr>>>>,
}
-pub enum tbox_flag {
- has_params = 1,
- has_self = 2,
- needs_infer = 4,
- has_regions = 8,
- has_ty_err = 16,
- has_ty_bot = 32,
-
- // a meta-pub flag: subst may be required if the type has parameters, a self
- // type, or references bound regions
- needs_subst = 1 | 2 | 8
+// Flags that we track on types. These flags are propagated upwards
+// through the type during type construction, so that we can quickly
+// check whether the type has various kinds of types in it without
+// recursing over the type itself.
+bitflags! {
+ flags TypeFlags: u32 {
+ const NO_TYPE_FLAGS = 0b0,
+ const HAS_PARAMS = 0b1,
+ const HAS_SELF = 0b10,
+ const HAS_TY_INFER = 0b100,
+ const HAS_RE_INFER = 0b1000,
+ const HAS_REGIONS = 0b10000,
+ const HAS_TY_ERR = 0b100000,
+ const HAS_TY_BOT = 0b1000000,
+ const NEEDS_SUBST = HAS_PARAMS.bits | HAS_SELF.bits | HAS_REGIONS.bits,
+ }
}
pub type t_box = &'static t_box_;
pub struct t_box_ {
pub sty: sty,
pub id: uint,
- pub flags: uint,
+ pub flags: TypeFlags,
+}
+
+impl fmt::Show for TypeFlags {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "{}", self.bits)
+ }
}
// To reduce refcounting cost, we're representing types as unsafe pointers
}
}
-pub fn tbox_has_flag(tb: t_box, flag: tbox_flag) -> bool {
- (tb.flags & (flag as uint)) != 0u
+fn tbox_has_flag(tb: t_box, flag: TypeFlags) -> bool {
+ tb.flags.intersects(flag)
}
pub fn type_has_params(t: t) -> bool {
- tbox_has_flag(get(t), has_params)
+ tbox_has_flag(get(t), HAS_PARAMS)
}
-pub fn type_has_self(t: t) -> bool { tbox_has_flag(get(t), has_self) }
+pub fn type_has_self(t: t) -> bool { tbox_has_flag(get(t), HAS_SELF) }
+pub fn type_has_ty_infer(t: t) -> bool { tbox_has_flag(get(t), HAS_TY_INFER) }
pub fn type_needs_infer(t: t) -> bool {
- tbox_has_flag(get(t), needs_infer)
+ tbox_has_flag(get(t), HAS_TY_INFER | HAS_RE_INFER)
}
pub fn type_id(t: t) -> uint { get(t).id }
pub static $name: t_box_ = t_box_ {
sty: $sty,
id: $id,
- flags: 0,
+ flags: super::NO_TYPE_FLAGS,
};
)
)
pub static TY_BOT: t_box_ = t_box_ {
sty: super::ty_bot,
id: 16,
- flags: super::has_ty_bot as uint,
+ flags: super::HAS_TY_BOT,
};
pub static TY_ERR: t_box_ = t_box_ {
sty: super::ty_err,
id: 17,
- flags: super::has_ty_err as uint,
+ flags: super::HAS_TY_ERR,
};
pub const LAST_PRIMITIVE_ID: uint = 18;
_ => ()
}
- let mut flags = 0u;
- fn rflags(r: Region) -> uint {
- (has_regions as uint) | {
+ let mut flags = NO_TYPE_FLAGS;
+ fn rflags(r: Region) -> TypeFlags {
+ HAS_REGIONS | {
match r {
- ty::ReInfer(_) => needs_infer as uint,
- _ => 0u
+ ty::ReInfer(_) => HAS_RE_INFER,
+ _ => NO_TYPE_FLAGS,
}
}
}
- fn sflags(substs: &Substs) -> uint {
- let mut f = 0u;
+ fn sflags(substs: &Substs) -> TypeFlags {
+ let mut f = NO_TYPE_FLAGS;
let mut i = substs.types.iter();
for tt in i {
- f |= get(*tt).flags;
+ f = f | get(*tt).flags;
}
match substs.regions {
subst::ErasedRegions => {}
subst::NonerasedRegions(ref regions) => {
for r in regions.iter() {
- f |= rflags(*r)
+ f = f | rflags(*r)
}
}
}
return f;
}
- fn flags_for_bounds(bounds: &ExistentialBounds) -> uint {
+ fn flags_for_bounds(bounds: &ExistentialBounds) -> TypeFlags {
rflags(bounds.region_bound)
}
match &st {
&ty_str => {}
// You might think that we could just return ty_err for
// any type containing ty_err as a component, and get
- // rid of the has_ty_err flag -- likewise for ty_bot (with
+ // rid of the HAS_TY_ERR flag -- likewise for ty_bot (with
// the exception of function types that return bot).
// But doing so caused sporadic memory corruption, and
// neither I (tjc) nor nmatsakis could figure out why,
// so we're doing it this way.
- &ty_bot => flags |= has_ty_bot as uint,
- &ty_err => flags |= has_ty_err as uint,
+ &ty_bot => flags = flags | HAS_TY_BOT,
+ &ty_err => flags = flags | HAS_TY_ERR,
&ty_param(ref p) => {
if p.space == subst::SelfSpace {
- flags |= has_self as uint;
+ flags = flags | HAS_SELF;
} else {
- flags |= has_params as uint;
+ flags = flags | HAS_PARAMS;
}
}
- &ty_unboxed_closure(_, ref region) => flags |= rflags(*region),
- &ty_infer(_) => flags |= needs_infer as uint,
+ &ty_unboxed_closure(_, ref region) => flags = flags | rflags(*region),
+ &ty_infer(_) => flags = flags | HAS_TY_INFER,
&ty_enum(_, ref substs) | &ty_struct(_, ref substs) => {
- flags |= sflags(substs);
+ flags = flags | sflags(substs);
}
&ty_trait(box TyTrait { ref substs, ref bounds, .. }) => {
- flags |= sflags(substs);
- flags |= flags_for_bounds(bounds);
+ flags = flags | sflags(substs);
+ flags = flags | flags_for_bounds(bounds);
}
&ty_uniq(tt) | &ty_vec(tt, _) | &ty_open(tt) => {
- flags |= get(tt).flags
+ flags = flags | get(tt).flags
}
&ty_ptr(ref m) => {
- flags |= get(m.ty).flags;
+ flags = flags | get(m.ty).flags;
}
&ty_rptr(r, ref m) => {
- flags |= rflags(r);
- flags |= get(m.ty).flags;
+ flags = flags | rflags(r);
+ flags = flags | get(m.ty).flags;
}
- &ty_tup(ref ts) => for tt in ts.iter() { flags |= get(*tt).flags; },
+ &ty_tup(ref ts) => for tt in ts.iter() { flags = flags | get(*tt).flags; },
&ty_bare_fn(ref f) => {
- for a in f.sig.inputs.iter() { flags |= get(*a).flags; }
- flags |= get(f.sig.output).flags;
+ for a in f.sig.inputs.iter() { flags = flags | get(*a).flags; }
+ flags = flags | get(f.sig.output).flags;
// T -> _|_ is *not* _|_ !
- flags &= !(has_ty_bot as uint);
+ flags = flags - HAS_TY_BOT;
}
&ty_closure(ref f) => {
match f.store {
RegionTraitStore(r, _) => {
- flags |= rflags(r);
+ flags = flags | rflags(r);
}
_ => {}
}
- for a in f.sig.inputs.iter() { flags |= get(*a).flags; }
- flags |= get(f.sig.output).flags;
+ for a in f.sig.inputs.iter() { flags = flags | get(*a).flags; }
+ flags = flags | get(f.sig.output).flags;
// T -> _|_ is *not* _|_ !
- flags &= !(has_ty_bot as uint);
- flags |= flags_for_bounds(&f.bounds);
+ flags = flags - HAS_TY_BOT;
+ flags = flags | flags_for_bounds(&f.bounds);
}
}
// Type utilities
-pub fn type_is_nil(ty: t) -> bool { get(ty).sty == ty_nil }
+pub fn type_is_nil(ty: t) -> bool {
+ get(ty).sty == ty_nil
+}
pub fn type_is_bot(ty: t) -> bool {
- (get(ty).flags & (has_ty_bot as uint)) != 0
+ get(ty).flags.intersects(HAS_TY_BOT)
}
pub fn type_is_error(ty: t) -> bool {
- (get(ty).flags & (has_ty_err as uint)) != 0
+ get(ty).flags.intersects(HAS_TY_ERR)
}
pub fn type_needs_subst(ty: t) -> bool {
- tbox_has_flag(get(ty), needs_subst)
+ tbox_has_flag(get(ty), NEEDS_SUBST)
}
pub fn trait_ref_contains_error(tref: &ty::TraitRef) -> bool {
// Special case: A unit like struct's constructor must be called without () at the
// end (like `UnitStruct`) which means this is an ExprPath to a DefFn. But in case
- // of unit structs this is should not be interpretet as function pointer but as
+ // of unit structs this is should not be interpreted as function pointer but as
// call to the constructor.
def::DefFn(_, _, true) => RvalueDpsExpr,
MutBorrow => ast::MutMutable,
ImmBorrow => ast::MutImmutable,
- // We have no type correponding to a unique imm borrow, so
+ // We have no type corresponding to a unique imm borrow, so
// use `&mut`. It gives all the capabilities of an `&uniq`
// and hence is a safe "over approximation".
UniqueImmBorrow => ast::MutMutable,
use middle::typeck::astconv::AstConv;
use middle::typeck::check::{FnCtxt, NoPreference, PreferMutLvalue};
use middle::typeck::check::{impl_self_ty};
-use middle::typeck::check::vtable2::select_fcx_obligations_where_possible;
+use middle::typeck::check::vtable::select_fcx_obligations_where_possible;
use middle::typeck::check;
use middle::typeck::infer;
use middle::typeck::{MethodCall, MethodCallee};
{
match error {
NoMatch(static_sources) => {
+ let cx = fcx.tcx();
+ let method_ustring = method_name.user_string(cx);
+
+ // True if the type is a struct and contains a field with
+ // the same name as the not-found method
+ let is_field = match ty::get(rcvr_ty).sty {
+ ty_struct(did, _) =>
+ ty::lookup_struct_fields(cx, did)
+ .iter()
+ .any(|f| f.name.user_string(cx) == method_ustring),
+ _ => false
+ };
+
fcx.type_error_message(
span,
|actual| {
format!("type `{}` does not implement any \
method in scope named `{}`",
actual,
- method_name.user_string(fcx.tcx()))
+ method_ustring)
},
rcvr_ty,
None);
+ // If the method has the name of a field, give a help note
+ if is_field {
+ cx.sess.span_note(span,
+ format!("use `(s.{0})(...)` if you meant to call the \
+ function stored in the `{0}` field", method_ustring).as_slice());
+ }
+
if static_sources.len() > 0 {
fcx.tcx().sess.fileline_note(
span,
use syntax;
pub mod _match;
-pub mod vtable2; // New trait code
+pub mod vtable;
pub mod writeback;
pub mod regionmanip;
pub mod regionck;
let fcx = check_fn(ccx, fn_ty.fn_style, id, &fn_ty.sig,
decl, id, body, &inh);
- vtable2::select_all_fcx_obligations_or_error(&fcx);
+ vtable::select_all_fcx_obligations_or_error(&fcx);
regionck::regionck_fn(&fcx, id, body);
writeback::resolve_type_vars_in_fn(&fcx, decl, body);
}
if ty::type_is_trait(t_1) {
// This will be looked up later on.
- vtable2::check_object_cast(fcx, cast_expr, e, t_1);
+ vtable::check_object_cast(fcx, cast_expr, e, t_1);
fcx.write_ty(id, t_1);
return
}
ty::UnsizeVtable(ref ty_trait, self_ty) => {
// If the type is `Foo+'a`, ensures that the type
// being cast to `Foo+'a` implements `Foo`:
- vtable2::register_object_cast_obligations(self,
+ vtable::register_object_cast_obligations(self,
span,
ty_trait,
self_ty);
// an "opportunistic" vtable resolution of any trait
// bounds on the call.
if check_blocks {
- vtable2::select_fcx_obligations_where_possible(fcx);
+ vtable::select_fcx_obligations_where_possible(fcx);
}
// For variadic functions, we don't have a declared type for all of
ast::ExprForLoop(ref pat, ref head, ref block, _) => {
check_expr(fcx, &**head);
let typ = lookup_method_for_for_loop(fcx, &**head, expr.id);
- vtable2::select_fcx_obligations_where_possible(fcx);
+ vtable::select_fcx_obligations_where_possible(fcx);
let pcx = pat_ctxt {
fcx: fcx,
check_expr_with_hint(fcx, e, declty);
demand::coerce(fcx, e.span, declty, e);
- vtable2::select_all_fcx_obligations_or_error(fcx);
+ vtable::select_all_fcx_obligations_or_error(fcx);
regionck::regionck_expr(fcx, e);
writeback::resolve_type_vars_in_expr(fcx, e);
}
use middle::typeck::astconv::AstConv;
use middle::typeck::check::FnCtxt;
use middle::typeck::check::regionmanip;
-use middle::typeck::check::vtable2;
+use middle::typeck::check::vtable;
use middle::typeck::infer::resolve_and_force_all_but_regions;
use middle::typeck::infer::resolve_type;
use middle::typeck::infer;
// Region checking a fn can introduce new trait obligations,
// particularly around closure bounds.
- vtable2::select_all_fcx_obligations_or_error(fcx);
+ vtable::select_all_fcx_obligations_or_error(fcx);
fcx.infcx().resolve_regions_and_report_errors();
}
}
}
- mc::cat_discr(cmt_base, _) |
mc::cat_downcast(cmt_base) |
mc::cat_deref(cmt_base, _, mc::OwnedPtr) |
mc::cat_interior(cmt_base, _) => {
//
// If mutability was inferred from an upvar, we may be
// forced to revisit this decision later if processing
- // another borrow or nested closure ends up coverting the
+ // another borrow or nested closure ends up converting the
// upvar borrow kind to mutable/unique. Record the
// information needed to perform the recursive link in the
// maybe link map.
match cmt.cat.clone() {
mc::cat_deref(base, _, mc::OwnedPtr) |
mc::cat_interior(base, _) |
- mc::cat_downcast(base) |
- mc::cat_discr(base, _) => {
+ mc::cat_downcast(base) => {
// Interior or owned data is mutable if base is
// mutable, so iterate to the base.
cmt = base;
match cmt.cat.clone() {
mc::cat_deref(base, _, mc::OwnedPtr) |
mc::cat_interior(base, _) |
- mc::cat_downcast(base) |
- mc::cat_discr(base, _) => {
+ mc::cat_downcast(base) => {
// Interior or owned data is unique if base is
// unique.
cmt = base;
--- /dev/null
+// 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 middle::subst::{SelfSpace};
+use middle::traits;
+use middle::traits::{SelectionError, OutputTypeParameterMismatch, Overflow, Unimplemented};
+use middle::traits::{Obligation, obligation_for_builtin_bound};
+use middle::traits::{FulfillmentError, CodeSelectionError, CodeAmbiguity};
+use middle::traits::{ObligationCause};
+use middle::ty;
+use middle::typeck::check::{FnCtxt,
+ structurally_resolved_type};
+use middle::typeck::infer;
+use std::rc::Rc;
+use syntax::ast;
+use syntax::codemap::Span;
+use util::ppaux::UserString;
+use util::ppaux::Repr;
+
+pub fn check_object_cast(fcx: &FnCtxt,
+ cast_expr: &ast::Expr,
+ source_expr: &ast::Expr,
+ target_object_ty: ty::t)
+{
+ debug!("check_object_cast(cast_expr={}, target_object_ty={})",
+ cast_expr.repr(fcx.tcx()),
+ target_object_ty.repr(fcx.tcx()));
+
+ // 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>`
+ let source_ty = fcx.expr_ty(source_expr);
+ let source_ty = structurally_resolved_type(fcx, source_expr.span, source_ty);
+ debug!("source_ty={}", source_ty.repr(fcx.tcx()));
+ match (&ty::get(source_ty).sty, &ty::get(target_object_ty).sty) {
+ (&ty::ty_uniq(referent_ty), &ty::ty_uniq(object_trait_ty)) => {
+ let object_trait = object_trait(&object_trait_ty);
+
+ // Ensure that if ~T is cast to ~Trait, then T : Trait
+ push_cast_obligation(fcx, cast_expr, object_trait, referent_ty);
+ }
+
+ (&ty::ty_rptr(referent_region, ty::mt { ty: referent_ty,
+ mutbl: referent_mutbl }),
+ &ty::ty_rptr(target_region, ty::mt { ty: object_trait_ty,
+ mutbl: target_mutbl })) =>
+ {
+ let object_trait = object_trait(&object_trait_ty);
+ if !mutability_allowed(referent_mutbl, target_mutbl) {
+ fcx.tcx().sess.span_err(source_expr.span,
+ "types differ in mutability");
+ } else {
+ // Ensure that if &'a T is cast to &'b Trait, then T : Trait
+ push_cast_obligation(fcx, cast_expr,
+ object_trait,
+ referent_ty);
+
+ // Ensure that if &'a T is cast to &'b Trait, then 'b <= 'a
+ infer::mk_subr(fcx.infcx(),
+ infer::RelateObjectBound(source_expr.span),
+ target_region,
+ referent_region);
+ }
+ }
+
+ (_, &ty::ty_uniq(..)) => {
+ fcx.ccx.tcx.sess.span_err(
+ source_expr.span,
+ format!("can only cast an boxed pointer \
+ to a boxed object, not a {}",
+ ty::ty_sort_string(fcx.tcx(), source_ty)).as_slice());
+ }
+
+ (_, &ty::ty_rptr(..)) => {
+ fcx.ccx.tcx.sess.span_err(
+ source_expr.span,
+ format!("can only cast a &-pointer \
+ to an &-object, not a {}",
+ ty::ty_sort_string(fcx.tcx(), source_ty)).as_slice());
+ }
+
+ _ => {
+ fcx.tcx().sess.span_bug(
+ source_expr.span,
+ "expected object type");
+ }
+ }
+
+ // Because we currently give unsound lifetimes to the "t_box", I
+ // could have written &'static ty::TyTrait here, but it seems
+ // gratuitously unsafe.
+ fn object_trait<'a>(t: &'a ty::t) -> &'a ty::TyTrait {
+ match ty::get(*t).sty {
+ ty::ty_trait(ref ty_trait) => &**ty_trait,
+ _ => fail!("expected ty_trait")
+ }
+ }
+
+ fn mutability_allowed(a_mutbl: ast::Mutability,
+ b_mutbl: ast::Mutability)
+ -> bool {
+ a_mutbl == b_mutbl ||
+ (a_mutbl == ast::MutMutable && b_mutbl == ast::MutImmutable)
+ }
+
+ fn push_cast_obligation(fcx: &FnCtxt,
+ cast_expr: &ast::Expr,
+ object_trait: &ty::TyTrait,
+ referent_ty: ty::t) {
+ let object_trait_ref =
+ register_object_cast_obligations(fcx,
+ cast_expr.span,
+ object_trait,
+ referent_ty);
+
+ // Finally record the object_trait_ref for use during trans
+ // (it would prob be better not to do this, but it's just kind
+ // of a pain to have to reconstruct it).
+ fcx.write_object_cast(cast_expr.id, object_trait_ref);
+ }
+}
+
+pub fn register_object_cast_obligations(fcx: &FnCtxt,
+ span: Span,
+ object_trait: &ty::TyTrait,
+ referent_ty: ty::t)
+ -> Rc<ty::TraitRef>
+{
+ // This is just for better error reporting. Kinda goofy. The object type stuff
+ // needs some refactoring so there is a more convenient type to pass around.
+ let object_trait_ty =
+ ty::mk_trait(fcx.tcx(),
+ object_trait.def_id,
+ object_trait.substs.clone(),
+ object_trait.bounds);
+
+ debug!("register_object_cast_obligations: referent_ty={} object_trait_ty={}",
+ referent_ty.repr(fcx.tcx()),
+ object_trait_ty.repr(fcx.tcx()));
+
+ // 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 object_substs = object_trait.substs.clone();
+ assert!(object_substs.self_ty().is_none());
+ object_substs.types.push(SelfSpace, referent_ty);
+
+ // Create the obligation for casting from T to Trait.
+ let object_trait_ref =
+ Rc::new(ty::TraitRef { def_id: object_trait.def_id,
+ substs: object_substs });
+ let object_obligation =
+ Obligation::new(
+ ObligationCause::new(span,
+ traits::ObjectCastObligation(object_trait_ty)),
+ object_trait_ref.clone());
+ fcx.register_obligation(object_obligation);
+
+ // Create additional obligations for all the various builtin
+ // bounds attached to the object cast. (In other words, if the
+ // object type is Foo+Send, this would create an obligation
+ // for the Send check.)
+ for builtin_bound in object_trait.bounds.builtin_bounds.iter() {
+ let obligation = obligation_for_builtin_bound(
+ fcx.tcx(),
+ ObligationCause::new(span,
+ traits::ObjectCastObligation(object_trait_ty)),
+ referent_ty,
+ builtin_bound);
+ match obligation {
+ Ok(obligation) => fcx.register_obligation(obligation),
+ _ => {}
+ }
+ }
+
+ object_trait_ref
+}
+
+pub fn select_all_fcx_obligations_or_error(fcx: &FnCtxt) {
+ debug!("select_all_fcx_obligations_or_error");
+
+ let mut fulfillment_cx = fcx.inh.fulfillment_cx.borrow_mut();
+ let r = fulfillment_cx.select_all_or_error(fcx.infcx(),
+ &fcx.inh.param_env,
+ fcx);
+ match r {
+ Ok(()) => { }
+ Err(errors) => { report_fulfillment_errors(fcx, &errors); }
+ }
+}
+
+fn resolve_trait_ref(fcx: &FnCtxt, obligation: &Obligation)
+ -> (ty::TraitRef, ty::t)
+{
+ let trait_ref =
+ fcx.infcx().resolve_type_vars_in_trait_ref_if_possible(
+ &*obligation.trait_ref);
+ let self_ty =
+ trait_ref.substs.self_ty().unwrap();
+ (trait_ref, self_ty)
+}
+
+pub fn report_fulfillment_errors(fcx: &FnCtxt,
+ errors: &Vec<FulfillmentError>) {
+ for error in errors.iter() {
+ report_fulfillment_error(fcx, error);
+ }
+}
+
+pub fn report_fulfillment_error(fcx: &FnCtxt,
+ error: &FulfillmentError) {
+ match error.code {
+ CodeSelectionError(ref e) => {
+ report_selection_error(fcx, &error.obligation, e);
+ }
+ CodeAmbiguity => {
+ maybe_report_ambiguity(fcx, &error.obligation);
+ }
+ }
+}
+
+pub fn report_selection_error(fcx: &FnCtxt,
+ obligation: &Obligation,
+ error: &SelectionError)
+{
+ match *error {
+ Overflow => {
+ let (trait_ref, self_ty) = resolve_trait_ref(fcx, obligation);
+ fcx.tcx().sess.span_err(
+ obligation.cause.span,
+ format!(
+ "overflow evaluating the trait `{}` for the type `{}`",
+ trait_ref.user_string(fcx.tcx()),
+ self_ty.user_string(fcx.tcx())).as_slice());
+ note_obligation_cause(fcx, obligation);
+ }
+ Unimplemented => {
+ let (trait_ref, self_ty) = resolve_trait_ref(fcx, obligation);
+ if !ty::type_is_error(self_ty) {
+ fcx.tcx().sess.span_err(
+ obligation.cause.span,
+ format!(
+ "the trait `{}` is not implemented for the type `{}`",
+ trait_ref.user_string(fcx.tcx()),
+ self_ty.user_string(fcx.tcx())).as_slice());
+ note_obligation_cause(fcx, obligation);
+ }
+ }
+ OutputTypeParameterMismatch(ref expected_trait_ref, ref e) => {
+ let expected_trait_ref =
+ fcx.infcx().resolve_type_vars_in_trait_ref_if_possible(
+ &**expected_trait_ref);
+ let (trait_ref, self_ty) = resolve_trait_ref(fcx, obligation);
+ if !ty::type_is_error(self_ty) {
+ fcx.tcx().sess.span_err(
+ obligation.cause.span,
+ format!(
+ "type mismatch: the type `{}` implements the trait `{}`, \
+ but the trait `{}` is required ({})",
+ self_ty.user_string(fcx.tcx()),
+ expected_trait_ref.user_string(fcx.tcx()),
+ trait_ref.user_string(fcx.tcx()),
+ ty::type_err_to_str(fcx.tcx(), e)).as_slice());
+ note_obligation_cause(fcx, obligation);
+ }
+ }
+ }
+}
+
+pub fn maybe_report_ambiguity(fcx: &FnCtxt, obligation: &Obligation) {
+ // Unable to successfully determine, probably means
+ // insufficient type information, but could mean
+ // ambiguous impls. The latter *ought* to be a
+ // coherence violation, so we don't report it here.
+ let (trait_ref, self_ty) = resolve_trait_ref(fcx, obligation);
+ debug!("maybe_report_ambiguity(trait_ref={}, self_ty={}, obligation={})",
+ trait_ref.repr(fcx.tcx()),
+ self_ty.repr(fcx.tcx()),
+ obligation.repr(fcx.tcx()));
+ let all_types = &trait_ref.substs.types;
+ if all_types.iter().any(|&t| ty::type_is_error(t)) {
+ } else if all_types.iter().any(|&t| ty::type_needs_infer(t)) {
+ // This is kind of a hack: it frequently happens that some earlier
+ // error prevents types from being fully inferred, and then we get
+ // a bunch of uninteresting errors saying something like "<generic
+ // #0> doesn't implement Sized". It may even be true that we
+ // could just skip over all checks where the self-ty is an
+ // inference variable, but I was afraid that there might be an
+ // inference variable created, registered as an obligation, and
+ // then never forced by writeback, and hence by skipping here we'd
+ // be ignoring the fact that we don't KNOW the type works
+ // out. Though even that would probably be harmless, given that
+ // we're only talking about builtin traits, which are known to be
+ // inhabited. But in any case I just threw in this check for
+ // has_errors() to be sure that compilation isn't happening
+ // anyway. In that case, why inundate the user.
+ if !fcx.tcx().sess.has_errors() {
+ fcx.tcx().sess.span_err(
+ obligation.cause.span,
+ format!(
+ "unable to infer enough type information to \
+ locate the impl of the trait `{}` for \
+ the type `{}`; type annotations required",
+ trait_ref.user_string(fcx.tcx()),
+ self_ty.user_string(fcx.tcx())).as_slice());
+ note_obligation_cause(fcx, obligation);
+ }
+ } else if !fcx.tcx().sess.has_errors() {
+ // Ambiguity. Coherence should have reported an error.
+ fcx.tcx().sess.span_bug(
+ obligation.cause.span,
+ format!(
+ "coherence failed to report ambiguity: \
+ cannot locate the impl of the trait `{}` for \
+ the type `{}`",
+ trait_ref.user_string(fcx.tcx()),
+ self_ty.user_string(fcx.tcx())).as_slice());
+ }
+}
+
+pub fn select_fcx_obligations_where_possible(fcx: &FnCtxt) {
+ /*! Select as many obligations as we can at present. */
+
+ match
+ fcx.inh.fulfillment_cx
+ .borrow_mut()
+ .select_where_possible(fcx.infcx(), &fcx.inh.param_env, fcx)
+ {
+ Ok(()) => { }
+ Err(errors) => { report_fulfillment_errors(fcx, &errors); }
+ }
+}
+
+fn note_obligation_cause(fcx: &FnCtxt,
+ obligation: &Obligation) {
+ let tcx = fcx.tcx();
+ let trait_name = ty::item_path_str(tcx, obligation.trait_ref.def_id);
+ match obligation.cause.code {
+ traits::MiscObligation => { }
+ traits::ItemObligation(item_def_id) => {
+ let item_name = ty::item_path_str(tcx, item_def_id);
+ tcx.sess.span_note(
+ obligation.cause.span,
+ format!(
+ "the trait `{}` must be implemented because it is required by `{}`",
+ trait_name,
+ item_name).as_slice());
+ }
+ traits::ObjectCastObligation(object_ty) => {
+ tcx.sess.span_note(
+ obligation.cause.span,
+ format!(
+ "the trait `{}` must be implemented for the cast \
+ to the object type `{}`",
+ trait_name,
+ fcx.infcx().ty_to_string(object_ty)).as_slice());
+ }
+ traits::RepeatVec => {
+ tcx.sess.span_note(
+ obligation.cause.span,
+ "the `Copy` trait is required because the \
+ repeated element will be copied");
+ }
+ traits::VariableType(_) => {
+ tcx.sess.span_note(
+ obligation.cause.span,
+ "all local variables must have a statically known size");
+ }
+ traits::ReturnType => {
+ tcx.sess.span_note(
+ obligation.cause.span,
+ "the return type of a function must have a \
+ statically known size");
+ }
+ traits::AssignmentLhsSized => {
+ tcx.sess.span_note(
+ obligation.cause.span,
+ "the left-hand-side of an assignment must have a statically known size");
+ }
+ traits::StructInitializerSized => {
+ tcx.sess.span_note(
+ obligation.cause.span,
+ "structs must have a statically known size to be initialized");
+ }
+ traits::DropTrait => {
+ span_note!(tcx.sess, obligation.cause.span,
+ "cannot implement a destructor on a \
+ structure or enumeration that does not satisfy Send");
+ span_note!(tcx.sess, obligation.cause.span,
+ "use \"#[unsafe_destructor]\" on the implementation \
+ to force the compiler to allow this");
+ }
+ traits::ClosureCapture(var_id, closure_span) => {
+ let name = ty::local_var_name_str(tcx, var_id);
+ span_note!(tcx.sess, closure_span,
+ "the closure that captures `{}` requires that all captured variables \"
+ implement the trait `{}`",
+ name,
+ trait_name);
+ }
+ traits::FieldSized => {
+ span_note!(tcx.sess, obligation.cause.span,
+ "only the last field of a struct or enum variant \
+ may have a dynamically sized type")
+ }
+ }
+}
+++ /dev/null
-// 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 middle::subst::{SelfSpace};
-use middle::traits;
-use middle::traits::{SelectionError, OutputTypeParameterMismatch, Overflow, Unimplemented};
-use middle::traits::{Obligation, obligation_for_builtin_bound};
-use middle::traits::{FulfillmentError, CodeSelectionError, CodeAmbiguity};
-use middle::traits::{ObligationCause};
-use middle::ty;
-use middle::typeck::check::{FnCtxt,
- structurally_resolved_type};
-use middle::typeck::infer;
-use std::rc::Rc;
-use syntax::ast;
-use syntax::codemap::Span;
-use util::ppaux::UserString;
-use util::ppaux::Repr;
-
-pub fn check_object_cast(fcx: &FnCtxt,
- cast_expr: &ast::Expr,
- source_expr: &ast::Expr,
- target_object_ty: ty::t)
-{
- debug!("check_object_cast(cast_expr={}, target_object_ty={})",
- cast_expr.repr(fcx.tcx()),
- target_object_ty.repr(fcx.tcx()));
-
- // 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>`
- let source_ty = fcx.expr_ty(source_expr);
- let source_ty = structurally_resolved_type(fcx, source_expr.span, source_ty);
- debug!("source_ty={}", source_ty.repr(fcx.tcx()));
- match (&ty::get(source_ty).sty, &ty::get(target_object_ty).sty) {
- (&ty::ty_uniq(referent_ty), &ty::ty_uniq(object_trait_ty)) => {
- let object_trait = object_trait(&object_trait_ty);
-
- // Ensure that if ~T is cast to ~Trait, then T : Trait
- push_cast_obligation(fcx, cast_expr, object_trait, referent_ty);
- }
-
- (&ty::ty_rptr(referent_region, ty::mt { ty: referent_ty,
- mutbl: referent_mutbl }),
- &ty::ty_rptr(target_region, ty::mt { ty: object_trait_ty,
- mutbl: target_mutbl })) =>
- {
- let object_trait = object_trait(&object_trait_ty);
- if !mutability_allowed(referent_mutbl, target_mutbl) {
- fcx.tcx().sess.span_err(source_expr.span,
- "types differ in mutability");
- } else {
- // Ensure that if &'a T is cast to &'b Trait, then T : Trait
- push_cast_obligation(fcx, cast_expr,
- object_trait,
- referent_ty);
-
- // Ensure that if &'a T is cast to &'b Trait, then 'b <= 'a
- infer::mk_subr(fcx.infcx(),
- infer::RelateObjectBound(source_expr.span),
- target_region,
- referent_region);
- }
- }
-
- (_, &ty::ty_uniq(..)) => {
- fcx.ccx.tcx.sess.span_err(
- source_expr.span,
- format!("can only cast an boxed pointer \
- to a boxed object, not a {}",
- ty::ty_sort_string(fcx.tcx(), source_ty)).as_slice());
- }
-
- (_, &ty::ty_rptr(..)) => {
- fcx.ccx.tcx.sess.span_err(
- source_expr.span,
- format!("can only cast a &-pointer \
- to an &-object, not a {}",
- ty::ty_sort_string(fcx.tcx(), source_ty)).as_slice());
- }
-
- _ => {
- fcx.tcx().sess.span_bug(
- source_expr.span,
- "expected object type");
- }
- }
-
- // Because we currently give unsound lifetimes to the "t_box", I
- // could have written &'static ty::TyTrait here, but it seems
- // gratuitously unsafe.
- fn object_trait<'a>(t: &'a ty::t) -> &'a ty::TyTrait {
- match ty::get(*t).sty {
- ty::ty_trait(ref ty_trait) => &**ty_trait,
- _ => fail!("expected ty_trait")
- }
- }
-
- fn mutability_allowed(a_mutbl: ast::Mutability,
- b_mutbl: ast::Mutability)
- -> bool {
- a_mutbl == b_mutbl ||
- (a_mutbl == ast::MutMutable && b_mutbl == ast::MutImmutable)
- }
-
- fn push_cast_obligation(fcx: &FnCtxt,
- cast_expr: &ast::Expr,
- object_trait: &ty::TyTrait,
- referent_ty: ty::t) {
- let object_trait_ref =
- register_object_cast_obligations(fcx,
- cast_expr.span,
- object_trait,
- referent_ty);
-
- // Finally record the object_trait_ref for use during trans
- // (it would prob be better not to do this, but it's just kind
- // of a pain to have to reconstruct it).
- fcx.write_object_cast(cast_expr.id, object_trait_ref);
- }
-}
-
-pub fn register_object_cast_obligations(fcx: &FnCtxt,
- span: Span,
- object_trait: &ty::TyTrait,
- referent_ty: ty::t)
- -> Rc<ty::TraitRef>
-{
- // This is just for better error reporting. Kinda goofy. The object type stuff
- // needs some refactoring so there is a more convenient type to pass around.
- let object_trait_ty =
- ty::mk_trait(fcx.tcx(),
- object_trait.def_id,
- object_trait.substs.clone(),
- object_trait.bounds);
-
- debug!("register_object_cast_obligations: referent_ty={} object_trait_ty={}",
- referent_ty.repr(fcx.tcx()),
- object_trait_ty.repr(fcx.tcx()));
-
- // 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 object_substs = object_trait.substs.clone();
- assert!(object_substs.self_ty().is_none());
- object_substs.types.push(SelfSpace, referent_ty);
-
- // Create the obligation for casting from T to Trait.
- let object_trait_ref =
- Rc::new(ty::TraitRef { def_id: object_trait.def_id,
- substs: object_substs });
- let object_obligation =
- Obligation::new(
- ObligationCause::new(span,
- traits::ObjectCastObligation(object_trait_ty)),
- object_trait_ref.clone());
- fcx.register_obligation(object_obligation);
-
- // Create additional obligations for all the various builtin
- // bounds attached to the object cast. (In other words, if the
- // object type is Foo+Send, this would create an obligation
- // for the Send check.)
- for builtin_bound in object_trait.bounds.builtin_bounds.iter() {
- let obligation = obligation_for_builtin_bound(
- fcx.tcx(),
- ObligationCause::new(span,
- traits::ObjectCastObligation(object_trait_ty)),
- referent_ty,
- builtin_bound);
- match obligation {
- Ok(obligation) => fcx.register_obligation(obligation),
- _ => {}
- }
- }
-
- object_trait_ref
-}
-
-pub fn select_all_fcx_obligations_or_error(fcx: &FnCtxt) {
- debug!("select_all_fcx_obligations_or_error");
-
- let mut fulfillment_cx = fcx.inh.fulfillment_cx.borrow_mut();
- let r = fulfillment_cx.select_all_or_error(fcx.infcx(),
- &fcx.inh.param_env,
- fcx);
- match r {
- Ok(()) => { }
- Err(errors) => { report_fulfillment_errors(fcx, &errors); }
- }
-}
-
-fn resolve_trait_ref(fcx: &FnCtxt, obligation: &Obligation)
- -> (ty::TraitRef, ty::t)
-{
- let trait_ref =
- fcx.infcx().resolve_type_vars_in_trait_ref_if_possible(
- &*obligation.trait_ref);
- let self_ty =
- trait_ref.substs.self_ty().unwrap();
- (trait_ref, self_ty)
-}
-
-pub fn report_fulfillment_errors(fcx: &FnCtxt,
- errors: &Vec<FulfillmentError>) {
- for error in errors.iter() {
- report_fulfillment_error(fcx, error);
- }
-}
-
-pub fn report_fulfillment_error(fcx: &FnCtxt,
- error: &FulfillmentError) {
- match error.code {
- CodeSelectionError(ref e) => {
- report_selection_error(fcx, &error.obligation, e);
- }
- CodeAmbiguity => {
- maybe_report_ambiguity(fcx, &error.obligation);
- }
- }
-}
-
-pub fn report_selection_error(fcx: &FnCtxt,
- obligation: &Obligation,
- error: &SelectionError)
-{
- match *error {
- Overflow => {
- let (trait_ref, self_ty) = resolve_trait_ref(fcx, obligation);
- fcx.tcx().sess.span_err(
- obligation.cause.span,
- format!(
- "overflow evaluating the trait `{}` for the type `{}`",
- trait_ref.user_string(fcx.tcx()),
- self_ty.user_string(fcx.tcx())).as_slice());
- note_obligation_cause(fcx, obligation);
- }
- Unimplemented => {
- let (trait_ref, self_ty) = resolve_trait_ref(fcx, obligation);
- if !ty::type_is_error(self_ty) {
- fcx.tcx().sess.span_err(
- obligation.cause.span,
- format!(
- "the trait `{}` is not implemented for the type `{}`",
- trait_ref.user_string(fcx.tcx()),
- self_ty.user_string(fcx.tcx())).as_slice());
- note_obligation_cause(fcx, obligation);
- }
- }
- OutputTypeParameterMismatch(ref expected_trait_ref, ref e) => {
- let expected_trait_ref =
- fcx.infcx().resolve_type_vars_in_trait_ref_if_possible(
- &**expected_trait_ref);
- let (trait_ref, self_ty) = resolve_trait_ref(fcx, obligation);
- if !ty::type_is_error(self_ty) {
- fcx.tcx().sess.span_err(
- obligation.cause.span,
- format!(
- "type mismatch: the type `{}` implements the trait `{}`, \
- but the trait `{}` is required ({})",
- self_ty.user_string(fcx.tcx()),
- expected_trait_ref.user_string(fcx.tcx()),
- trait_ref.user_string(fcx.tcx()),
- ty::type_err_to_str(fcx.tcx(), e)).as_slice());
- note_obligation_cause(fcx, obligation);
- }
- }
- }
-}
-
-pub fn maybe_report_ambiguity(fcx: &FnCtxt, obligation: &Obligation) {
- // Unable to successfully determine, probably means
- // insufficient type information, but could mean
- // ambiguous impls. The latter *ought* to be a
- // coherence violation, so we don't report it here.
- let (trait_ref, self_ty) = resolve_trait_ref(fcx, obligation);
- debug!("maybe_report_ambiguity(trait_ref={}, self_ty={}, obligation={})",
- trait_ref.repr(fcx.tcx()),
- self_ty.repr(fcx.tcx()),
- obligation.repr(fcx.tcx()));
- let all_types = &trait_ref.substs.types;
- if all_types.iter().any(|&t| ty::type_is_error(t)) {
- } else if all_types.iter().any(|&t| ty::type_needs_infer(t)) {
- // This is kind of a hack: it frequently happens that some earlier
- // error prevents types from being fully inferred, and then we get
- // a bunch of uninteresting errors saying something like "<generic
- // #0> doesn't implement Sized". It may even be true that we
- // could just skip over all checks where the self-ty is an
- // inference variable, but I was afraid that there might be an
- // inference variable created, registered as an obligation, and
- // then never forced by writeback, and hence by skipping here we'd
- // be ignoring the fact that we don't KNOW the type works
- // out. Though even that would probably be harmless, given that
- // we're only talking about builtin traits, which are known to be
- // inhabited. But in any case I just threw in this check for
- // has_errors() to be sure that compilation isn't happening
- // anyway. In that case, why inundate the user.
- if !fcx.tcx().sess.has_errors() {
- fcx.tcx().sess.span_err(
- obligation.cause.span,
- format!(
- "unable to infer enough type information to \
- locate the impl of the trait `{}` for \
- the type `{}`; type annotations required",
- trait_ref.user_string(fcx.tcx()),
- self_ty.user_string(fcx.tcx())).as_slice());
- note_obligation_cause(fcx, obligation);
- }
- } else if !fcx.tcx().sess.has_errors() {
- // Ambiguity. Coherence should have reported an error.
- fcx.tcx().sess.span_bug(
- obligation.cause.span,
- format!(
- "coherence failed to report ambiguity: \
- cannot locate the impl of the trait `{}` for \
- the type `{}`",
- trait_ref.user_string(fcx.tcx()),
- self_ty.user_string(fcx.tcx())).as_slice());
- }
-}
-
-pub fn select_fcx_obligations_where_possible(fcx: &FnCtxt) {
- /*! Select as many obligations as we can at present. */
-
- match
- fcx.inh.fulfillment_cx
- .borrow_mut()
- .select_where_possible(fcx.infcx(), &fcx.inh.param_env, fcx)
- {
- Ok(()) => { }
- Err(errors) => { report_fulfillment_errors(fcx, &errors); }
- }
-}
-
-fn note_obligation_cause(fcx: &FnCtxt,
- obligation: &Obligation) {
- let tcx = fcx.tcx();
- let trait_name = ty::item_path_str(tcx, obligation.trait_ref.def_id);
- match obligation.cause.code {
- traits::MiscObligation => { }
- traits::ItemObligation(item_def_id) => {
- let item_name = ty::item_path_str(tcx, item_def_id);
- tcx.sess.span_note(
- obligation.cause.span,
- format!(
- "the trait `{}` must be implemented because it is required by `{}`",
- trait_name,
- item_name).as_slice());
- }
- traits::ObjectCastObligation(object_ty) => {
- tcx.sess.span_note(
- obligation.cause.span,
- format!(
- "the trait `{}` must be implemented for the cast \
- to the object type `{}`",
- trait_name,
- fcx.infcx().ty_to_string(object_ty)).as_slice());
- }
- traits::RepeatVec => {
- tcx.sess.span_note(
- obligation.cause.span,
- "the `Copy` trait is required because the \
- repeated element will be copied");
- }
- traits::VariableType(_) => {
- tcx.sess.span_note(
- obligation.cause.span,
- "all local variables must have a statically known size");
- }
- traits::ReturnType => {
- tcx.sess.span_note(
- obligation.cause.span,
- "the return type of a function must have a \
- statically known size");
- }
- traits::AssignmentLhsSized => {
- tcx.sess.span_note(
- obligation.cause.span,
- "the left-hand-side of an assignment must have a statically known size");
- }
- traits::StructInitializerSized => {
- tcx.sess.span_note(
- obligation.cause.span,
- "structs must have a statically known size to be initialized");
- }
- traits::DropTrait => {
- span_note!(tcx.sess, obligation.cause.span,
- "cannot implement a destructor on a \
- structure or enumeration that does not satisfy Send");
- span_note!(tcx.sess, obligation.cause.span,
- "use \"#[unsafe_destructor]\" on the implementation \
- to force the compiler to allow this");
- }
- traits::ClosureCapture(var_id, closure_span) => {
- let name = ty::local_var_name_str(tcx, var_id);
- span_note!(tcx.sess, closure_span,
- "the closure that captures `{}` requires that all captured variables \"
- implement the trait `{}`",
- name,
- trait_name);
- }
- traits::FieldSized => {
- span_note!(tcx.sess, obligation.cause.span,
- "only the last field of a struct or enum variant \
- may have a dynamically sized type")
- }
- }
-}
use middle::ty;
use middle::ty_fold::{TypeFolder, TypeFoldable};
use middle::typeck::astconv::AstConv;
-use middle::typeck::check::{FnCtxt, Inherited, blank_fn_ctxt, vtable2, regionck};
+use middle::typeck::check::{FnCtxt, Inherited, blank_fn_ctxt, vtable, regionck};
use middle::typeck::check::regionmanip::replace_late_bound_regions;
use middle::typeck::CrateCtxt;
use util::ppaux::Repr;
let inh = Inherited::new(ccx.tcx, param_env);
let fcx = blank_fn_ctxt(ccx, &inh, polytype.ty, item.id);
f(self, &fcx);
- vtable2::select_all_fcx_obligations_or_error(&fcx);
+ vtable::select_all_fcx_obligations_or_error(&fcx);
regionck::regionck_item(&fcx, item);
}
*/
-use alloc::libc_heap::malloc_raw;
use collections::string::String;
use collections::hash;
use core::fmt;
/// with C's allocator API, rather than the usual shallow clone.
fn clone(&self) -> CString {
let len = self.len() + 1;
- let buf = unsafe { malloc_raw(len) } as *mut libc::c_char;
+ let buf = unsafe { libc::malloc(len as libc::size_t) } as *mut libc::c_char;
+ if buf.is_null() { fail!("out of memory") }
unsafe { ptr::copy_nonoverlapping_memory(buf, self.buf, len); }
CString { buf: buf as *const libc::c_char, owns_buffer_: true }
}
unsafe fn to_c_str_unchecked(&self) -> CString {
let self_len = self.len();
- let buf = malloc_raw(self_len + 1);
+ let buf = libc::malloc(self_len as libc::size_t + 1) as *mut u8;
+ if buf.is_null() { fail!("out of memory") }
ptr::copy_memory(buf, self.as_ptr(), self_len);
*buf.offset(self_len as int) = 0;
#[cfg(windows)]
mod imp {
- use alloc::libc_heap::malloc_raw;
+ use alloc::heap;
use core::atomic;
use core::ptr;
use libc::{HANDLE, BOOL, LPSECURITY_ATTRIBUTES, c_void, DWORD, LPCSTR};
}
pub unsafe fn init_lock() -> uint {
- let block = malloc_raw(CRIT_SECTION_SIZE as uint) as *mut c_void;
+ let block = heap::allocate(CRIT_SECTION_SIZE, 8) as *mut c_void;
InitializeCriticalSectionAndSpinCount(block, SPIN_COUNT);
return block as uint;
}
pub unsafe fn free_lock(h: uint) {
DeleteCriticalSection(h as LPCRITICAL_SECTION);
- libc::free(h as *mut c_void);
+ heap::deallocate(h as *mut u8, CRIT_SECTION_SIZE, 8);
}
pub unsafe fn free_cond(h: uint) {
use super::CVec;
use libc;
use ptr;
- use rt::libc_heap::malloc_raw;
fn malloc(n: uint) -> CVec<u8> {
unsafe {
- let mem = malloc_raw(n);
+ let mem = libc::malloc(n as libc::size_t);
+ if mem.is_null() { fail!("out of memory") }
CVec::new_with_dtor(mem as *mut u8, n,
proc() { libc::free(mem as *mut libc::c_void); })
/// # Example
///
/// This is a slightly silly example where we define the number's
- /// parity as the equivilance class. It is important that the
+ /// parity as the equivalance class. It is important that the
/// values hash the same, which is why we implement `Hash`.
///
/// ```
use slice::AsSlice;
use vec::Vec;
-static BUF_CAPACITY: uint = 128;
+const BUF_CAPACITY: uint = 128;
fn combine(seek: SeekStyle, cur: uint, end: uint, offset: i64) -> IoResult<u64> {
// compute offset as signed and clamp to prevent overflow
/// the internal buffer.
#[inline]
pub fn with_capacity(n: uint) -> MemWriter {
- MemWriter { buf: Vec::with_capacity(n) }
+ MemWriter::from_vec(Vec::with_capacity(n))
+ }
+ /// Create a new `MemWriter` that will append to an existing `Vec`.
+ #[inline]
+ pub fn from_vec(buf: Vec<u8>) -> MemWriter {
+ MemWriter { buf: buf }
}
/// Acquires an immutable reference to the underlying buffer of this
///
/// Note that this call does not perform any actual network communication,
/// because UDP is a datagram protocol.
+ #[deprecated = "`UdpStream` has been deprecated"]
+ #[allow(deprecated)]
pub fn connect(self, other: SocketAddr) -> UdpStream {
UdpStream {
socket: self,
/// A type that allows convenient usage of a UDP stream connected to one
/// address via the `Reader` and `Writer` traits.
+///
+/// # Note
+///
+/// This structure has been deprecated because `Reader` is a stream-oriented API but UDP
+/// is a packet-oriented protocol. Every `Reader` method will read a whole packet and
+/// throw all superfluous bytes away so that they are no longer available for further
+/// method calls.
+#[deprecated]
pub struct UdpStream {
socket: UdpSocket,
connected_to: SocketAddr
}
impl Reader for UdpStream {
+ /// Returns the next non-empty message from the specified address.
fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
let peer = self.connected_to;
self.as_socket(|sock| {
- match sock.recv_from(buf) {
- Ok((_nread, src)) if src != peer => Ok(0),
- Ok((nread, _src)) => Ok(nread),
- Err(e) => Err(e),
+ loop {
+ let (nread, src) = try!(sock.recv_from(buf));
+ if nread > 0 && src == peer {
+ return Ok(nread);
+ }
}
})
}
}
#[test]
+ #[allow(deprecated)]
fn stream_smoke_test_ip4() {
let server_ip = next_test_ip4();
let client_ip = next_test_ip4();
+ let dummy_ip = next_test_ip4();
let (tx1, rx1) = channel();
let (tx2, rx2) = channel();
spawn(proc() {
- match UdpSocket::bind(client_ip) {
- Ok(client) => {
- let client = box client;
- let mut stream = client.connect(server_ip);
- rx1.recv();
- stream.write([99]).unwrap();
+ let send_as = |ip, val: &[u8]| {
+ match UdpSocket::bind(ip) {
+ Ok(client) => {
+ let client = box client;
+ let mut stream = client.connect(server_ip);
+ stream.write(val).unwrap();
+ }
+ Err(..) => fail!()
}
- Err(..) => fail!()
- }
+ };
+ rx1.recv();
+ send_as(dummy_ip, [98]);
+ send_as(client_ip, [99]);
tx2.send(());
});
assert_eq!(nread, 1);
assert_eq!(buf[0], 99);
}
- Err(..) => fail!()
+ Err(..) => fail!(),
}
}
Err(..) => fail!()
}
#[test]
+ #[allow(deprecated)]
fn stream_smoke_test_ip6() {
let server_ip = next_test_ip6();
let client_ip = next_test_ip6();
// Reexport functionality from librustrt and other crates underneath the
// standard library which work together to create the entire runtime.
-pub use alloc::{heap, libc_heap};
+pub use alloc::heap;
pub use rustrt::{task, local, mutex, exclusive, stack, args, rtio, thread};
pub use rustrt::{Stdio, Stdout, Stderr, begin_unwind, begin_unwind_fmt};
pub use rustrt::{bookkeeping, at_exit, unwind, DEFAULT_ERROR_CODE, Runtime};
use std::rc::Rc;
use serialize::{Encodable, Decodable, Encoder, Decoder};
+#[cfg(stage0)]
+pub use self::TtToken as TTTok;
+
// FIXME #6993: in librustc, uses of "ident" should be replaced
// by just "Name".
/// Expr with trailing semi-colon (may have any type):
StmtSemi(P<Expr>, NodeId),
- /// bool: is there a trailing sem-colon?
+ /// bool: is there a trailing semi-colon?
StmtMac(Mac, bool),
}
CaptureByRef,
}
+/// A token that delimits a sequence of token trees
+#[deriving(Clone, PartialEq, Eq, Encodable, Decodable, Hash, Show)]
+pub struct Delimiter {
+ pub span: Span,
+ pub token: ::parse::token::Token,
+}
+
+impl Delimiter {
+ /// Convert the delimiter to a `TtToken`
+ pub fn to_tt(&self) -> TokenTree {
+ TtToken(self.span, self.token.clone())
+ }
+}
+
+/// A Kleene-style [repetition operator](http://en.wikipedia.org/wiki/Kleene_star)
+/// for token sequences.
+#[deriving(Clone, PartialEq, Eq, Encodable, Decodable, Hash, Show)]
+pub enum KleeneOp {
+ ZeroOrMore,
+ OneOrMore,
+}
+
/// When the main rust parser encounters a syntax-extension invocation, it
/// parses the arguments to the invocation as a token-tree. This is a very
/// loose structure, such that all sorts of different AST-fragments can
/// If the syntax extension is an MBE macro, it will attempt to match its
/// LHS "matchers" against the provided token tree, and if it finds a
/// match, will transcribe the RHS token tree, splicing in any captured
-/// macro_parser::matched_nonterminals into the TTNonterminals it finds.
+/// `macro_parser::matched_nonterminals` into the `TtNonterminal`s it finds.
///
-/// The RHS of an MBE macro is the only place a TTNonterminal or TTSeq
+/// The RHS of an MBE macro is the only place a `TtNonterminal` or `TtSequence`
/// makes any real sense. You could write them elsewhere but nothing
/// else knows what to do with them, so you'll probably get a syntax
/// error.
#[doc="For macro invocations; parsing is delegated to the macro"]
pub enum TokenTree {
/// A single token
- TTTok(Span, ::parse::token::Token),
- /// A delimited sequence (the delimiters appear as the first
- /// and last elements of the vector)
- // FIXME(eddyb) #6308 Use Rc<[TokenTree]> after DST.
- TTDelim(Rc<Vec<TokenTree>>),
+ TtToken(Span, ::parse::token::Token),
+ /// A delimited sequence of token trees
+ TtDelimited(Span, Rc<(Delimiter, Vec<TokenTree>, Delimiter)>),
// These only make sense for right-hand-sides of MBE macros:
- /// A kleene-style repetition sequence with a span, a TTForest,
- /// an optional separator, and a boolean where true indicates
- /// zero or more (..), and false indicates one or more (+).
+ /// A Kleene-style repetition sequence with an optional separator.
// FIXME(eddyb) #6308 Use Rc<[TokenTree]> after DST.
- TTSeq(Span, Rc<Vec<TokenTree>>, Option<::parse::token::Token>, bool),
-
+ TtSequence(Span, Rc<Vec<TokenTree>>, Option<::parse::token::Token>, KleeneOp),
/// A syntactic variable that will be filled in by macro expansion.
- TTNonterminal(Span, Ident)
+ TtNonterminal(Span, Ident)
+}
+
+impl TokenTree {
+ /// Returns the `Span` corresponding to this token tree.
+ pub fn get_span(&self) -> Span {
+ match *self {
+ TtToken(span, _) => span,
+ TtDelimited(span, _) => span,
+ TtSequence(span, _, _, _) => span,
+ TtNonterminal(span, _) => span,
+ }
+ }
}
// Matchers are nodes defined-by and recognized-by the main rust parser and
pub enum Matcher_ {
/// Match one token
MatchTok(::parse::token::Token),
- /// Match repetitions of a sequence: body, separator, zero ok?,
+ /// Match repetitions of a sequence: body, separator, Kleene operator,
/// lo, hi position-in-match-array used:
- MatchSeq(Vec<Matcher> , Option<::parse::token::Token>, bool, uint, uint),
+ MatchSeq(Vec<Matcher> , Option<::parse::token::Token>, KleeneOp, uint, uint),
/// Parse a Rust NT: name to bind, name of NT, position in match array:
MatchNonterminal(Ident, Ident, uint)
}
// Determine if an item should be translated in the current crate
// configuration based on the item's attributes
fn in_cfg(diagnostic: &SpanHandler, cfg: &[P<ast::MetaItem>], attrs: &[ast::Attribute]) -> bool {
- let mut in_cfg = false;
- let mut seen_cfg = false;
- for attr in attrs.iter() {
+ attrs.iter().all(|attr| {
let mis = match attr.node.value.node {
ast::MetaList(_, ref mis) if attr.check_name("cfg") => mis,
- _ => continue
+ _ => return true
};
if mis.len() != 1 {
diagnostic.span_err(attr.span, "expected 1 cfg-pattern");
- return false;
+ return true;
}
- if seen_cfg {
- diagnostic.span_err(attr.span, "The semantics of multiple `#[cfg(..)]` attributes on \
- same item are changing from the union of the cfgs to \
- the intersection of the cfgs. Change `#[cfg(a)] \
- #[cfg(b)]` to `#[cfg(any(a, b))]`.");
- return false;
- }
-
- seen_cfg = true;
- in_cfg |= attr::cfg_matches(diagnostic, cfg, &*mis[0]);
- }
- in_cfg | !seen_cfg
+ attr::cfg_matches(diagnostic, cfg, &*mis[0])
+ })
}
token_tree: &[TokenTree])
-> Box<MacResult+'cx> {
let code = match token_tree {
- [ast::TTTok(_, token::IDENT(code, _))] => code,
+ [ast::TtToken(_, token::IDENT(code, _))] => code,
_ => unreachable!()
};
with_registered_diagnostics(|diagnostics| {
token_tree: &[TokenTree])
-> Box<MacResult+'cx> {
let (code, description) = match token_tree {
- [ast::TTTok(_, token::IDENT(ref code, _))] => {
+ [ast::TtToken(_, token::IDENT(ref code, _))] => {
(code, None)
},
- [ast::TTTok(_, token::IDENT(ref code, _)),
- ast::TTTok(_, token::COMMA),
- ast::TTTok(_, token::LIT_STR_RAW(description, _))] => {
+ [ast::TtToken(_, token::IDENT(ref code, _)),
+ ast::TtToken(_, token::COMMA),
+ ast::TtToken(_, token::LIT_STR_RAW(description, _))] => {
(code, Some(description))
}
_ => unreachable!()
token_tree: &[TokenTree])
-> Box<MacResult+'cx> {
let name = match token_tree {
- [ast::TTTok(_, token::IDENT(ref name, _))] => name,
+ [ast::TtToken(_, token::IDENT(ref name, _))] => name,
_ => unreachable!()
};
cx.span_err(sp, format!("{} takes 1 argument.", name).as_slice());
} else {
match tts[0] {
- ast::TTTok(_, token::LIT_STR(ident)) => return Some(parse::str_lit(ident.as_str())),
- ast::TTTok(_, token::LIT_STR_RAW(ident, _)) => {
+ ast::TtToken(_, token::LIT_STR(ident)) => return Some(parse::str_lit(ident.as_str())),
+ ast::TtToken(_, token::LIT_STR_RAW(ident, _)) => {
return Some(parse::raw_str_lit(ident.as_str()))
}
_ => {
for (i, e) in tts.iter().enumerate() {
if i & 1 == 1 {
match *e {
- ast::TTTok(_, token::COMMA) => (),
+ ast::TtToken(_, token::COMMA) => (),
_ => {
cx.span_err(sp, "concat_idents! expecting comma.");
return DummyResult::expr(sp);
}
} else {
match *e {
- ast::TTTok(_, token::IDENT(ident,_)) => {
+ ast::TtToken(_, token::IDENT(ident,_)) => {
res_str.push_str(token::get_ident(ident).get())
}
_ => {
use ext::base;
use print;
-use std::rc::Rc;
-
pub fn expand_syntax_ext<'cx>(cx: &'cx mut base::ExtCtxt,
sp: codemap::Span,
- tt: &[ast::TokenTree])
+ tts: &[ast::TokenTree])
-> Box<base::MacResult+'cx> {
cx.print_backtrace();
- println!("{}", print::pprust::tt_to_string(&ast::TTDelim(
- Rc::new(tt.iter().map(|x| (*x).clone()).collect()))));
+
+ println!("{}", print::pprust::tts_to_string(tts));
// any so that `log_syntax` can be invoked as an expression and item.
base::DummyResult::any(sp)
*
* This is registered as a set of expression syntax extension called quote!
* that lifts its argument token-tree to an AST representing the
-* construction of the same token tree, with ast::TTNonterminal nodes
+* construction of the same token tree, with ast::TtNonterminal nodes
* interpreted as antiquotes (splices).
*
*/
}
-fn mk_tt(cx: &ExtCtxt, sp: Span, tt: &ast::TokenTree) -> Vec<P<ast::Stmt>> {
+fn mk_tt(cx: &ExtCtxt, _: Span, tt: &ast::TokenTree) -> Vec<P<ast::Stmt>> {
match *tt {
- ast::TTTok(sp, ref tok) => {
+ ast::TtToken(sp, ref tok) => {
let e_sp = cx.expr_ident(sp, id_ext("_sp"));
let e_tok = cx.expr_call(sp,
- mk_ast_path(cx, sp, "TTTok"),
+ mk_ast_path(cx, sp, "TtToken"),
vec!(e_sp, mk_token(cx, sp, tok)));
let e_push =
cx.expr_method_call(sp,
id_ext("push"),
vec!(e_tok));
vec!(cx.stmt_expr(e_push))
- }
-
- ast::TTDelim(ref tts) => mk_tts(cx, sp, tts.as_slice()),
- ast::TTSeq(..) => fail!("TTSeq in quote!"),
-
- ast::TTNonterminal(sp, ident) => {
-
+ },
+ ast::TtDelimited(sp, ref delimed) => {
+ let (ref open, ref tts, ref close) = **delimed;
+ mk_tt(cx, sp, &open.to_tt()).into_iter()
+ .chain(tts.iter().flat_map(|tt| mk_tt(cx, sp, tt).into_iter()))
+ .chain(mk_tt(cx, sp, &close.to_tt()).into_iter())
+ .collect()
+ },
+ ast::TtSequence(..) => fail!("TtSequence in quote!"),
+ ast::TtNonterminal(sp, ident) => {
// tt.extend($ident.to_tokens(ext_cx).into_iter())
let e_to_toks =
vec!(e_to_toks));
vec!(cx.stmt_expr(e_push))
- }
+ },
}
}
fn expand_tts(cx: &ExtCtxt, sp: Span, tts: &[ast::TokenTree])
-> (P<ast::Expr>, P<ast::Expr>) {
// NB: It appears that the main parser loses its mind if we consider
- // $foo as a TTNonterminal during the main parse, so we have to re-parse
+ // $foo as a TtNonterminal during the main parse, so we have to re-parse
// under quote_depth > 0. This is silly and should go away; the _guess_ is
// it has to do with transition away from supporting old-style macros, so
// try removing it when enough of them are gone.
tt: &[ast::TokenTree])
-> Box<base::MacResult+'static> {
match tt {
- [ast::TTTok(_, ref tok)] if is_keyword(keywords::True, tok) => {
+ [ast::TtToken(_, ref tok)] if is_keyword(keywords::True, tok) => {
cx.set_trace_macros(true);
}
- [ast::TTTok(_, ref tok)] if is_keyword(keywords::False, tok) => {
+ [ast::TtToken(_, ref tok)] if is_keyword(keywords::False, tok) => {
cx.set_trace_macros(false);
}
_ => cx.span_err(sp, "trace_macros! accepts only `true` or `false`"),
} else {
match ei.elts[idx].node.clone() {
/* need to descend into sequence */
- MatchSeq(ref matchers, ref sep, zero_ok,
+ MatchSeq(ref matchers, ref sep, kleene_op,
match_idx_lo, match_idx_hi) => {
- if zero_ok {
+ if kleene_op == ast::ZeroOrMore {
let mut new_ei = ei.clone();
new_ei.idx += 1u;
//we specifically matched zero repeats.
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-use ast::{Ident, Matcher_, Matcher, MatchTok, MatchNonterminal, MatchSeq, TTDelim};
+use ast::{Ident, Matcher_, Matcher, MatchTok, MatchNonterminal, MatchSeq, TtDelimited};
use ast;
use codemap::{Span, Spanned, DUMMY_SP};
use ext::base::{ExtCtxt, MacResult, MacroDef};
rhses: &[Rc<NamedMatch>])
-> Box<MacResult+'cx> {
if cx.trace_macros() {
- println!("{}! {} {} {}",
+ println!("{}! {{ {} }}",
token::get_ident(name),
- "{",
- print::pprust::tt_to_string(&TTDelim(Rc::new(arg.iter()
- .map(|x| (*x).clone())
- .collect()))),
- "}");
+ print::pprust::tts_to_string(arg));
}
// Which arm's failure should we report? (the one furthest along)
// okay, what's your transcriber?
MatchedNonterminal(NtTT(ref tt)) => {
match **tt {
- // cut off delimiters; don't parse 'em
- TTDelim(ref tts) => {
- (*tts).slice(1u,(*tts).len()-1u)
- .iter()
- .map(|x| (*x).clone())
- .collect()
- }
- _ => cx.span_fatal(
- sp, "macro rhs must be delimited")
+ // ignore delimiters
+ TtDelimited(_, ref delimed) => {
+ let (_, ref tts, _) = **delimed;
+ tts.clone()
+ },
+ _ => cx.span_fatal(sp, "macro rhs must be delimited"),
}
},
_ => cx.span_bug(sp, "bad thing in rhs")
ms(MatchSeq(vec!(
ms(MatchNonterminal(lhs_nm, special_idents::matchers, 0u)),
ms(MatchTok(FAT_ARROW)),
- ms(MatchNonterminal(rhs_nm, special_idents::tt, 1u))), Some(SEMI), false, 0u, 2u)),
+ ms(MatchNonterminal(rhs_nm, special_idents::tt, 1u))), Some(SEMI),
+ ast::OneOrMore, 0u, 2u)),
//to phase into semicolon-termination instead of
//semicolon-separation
- ms(MatchSeq(vec!(ms(MatchTok(SEMI))), None, true, 2u, 2u)));
+ ms(MatchSeq(vec!(ms(MatchTok(SEMI))), None, ast::ZeroOrMore, 2u, 2u)));
// Parse the macro_rules! invocation (`none` is for no interpolations):
// except according to those terms.
use ast;
-use ast::{TokenTree, TTDelim, TTTok, TTSeq, TTNonterminal, Ident};
+use ast::{TokenTree, TtDelimited, TtToken, TtSequence, TtNonterminal, Ident};
use codemap::{Span, DUMMY_SP};
use diagnostic::SpanHandler;
use ext::tt::macro_parser::{NamedMatch, MatchedSeq, MatchedNonterminal};
use parse::lexer::TokenAndSpan;
use std::rc::Rc;
+use std::ops::Add;
use std::collections::HashMap;
///an unzipping of `TokenTree`s
}
/// This can do Macro-By-Example transcription. On the other hand, if
-/// `src` contains no `TTSeq`s and `TTNonterminal`s, `interp` can (and
+/// `src` contains no `TtSequence`s and `TtNonterminal`s, `interp` can (and
/// should) be none.
pub fn new_tt_reader<'a>(sp_diag: &'a SpanHandler,
interp: Option<HashMap<Ident, Rc<NamedMatch>>>,
LisContradiction(String),
}
-fn lis_merge(lhs: LockstepIterSize, rhs: LockstepIterSize) -> LockstepIterSize {
- match lhs {
- LisUnconstrained => rhs.clone(),
- LisContradiction(_) => lhs.clone(),
- LisConstraint(l_len, l_id) => match rhs {
- LisUnconstrained => lhs.clone(),
- LisContradiction(_) => rhs.clone(),
- LisConstraint(r_len, _) if l_len == r_len => lhs.clone(),
- LisConstraint(r_len, r_id) => {
- let l_n = token::get_ident(l_id);
- let r_n = token::get_ident(r_id);
- LisContradiction(format!("inconsistent lockstep iteration: \
- '{}' has {} items, but '{}' has {}",
- l_n, l_len, r_n, r_len).to_string())
- }
+impl Add<LockstepIterSize, LockstepIterSize> for LockstepIterSize {
+ fn add(&self, other: &LockstepIterSize) -> LockstepIterSize {
+ match *self {
+ LisUnconstrained => other.clone(),
+ LisContradiction(_) => self.clone(),
+ LisConstraint(l_len, l_id) => match *other {
+ LisUnconstrained => self.clone(),
+ LisContradiction(_) => other.clone(),
+ LisConstraint(r_len, _) if l_len == r_len => self.clone(),
+ LisConstraint(r_len, r_id) => {
+ let l_n = token::get_ident(l_id);
+ let r_n = token::get_ident(r_id);
+ LisContradiction(format!("inconsistent lockstep iteration: \
+ '{}' has {} items, but '{}' has {}",
+ l_n, l_len, r_n, r_len).to_string())
+ }
+ },
}
}
}
fn lockstep_iter_size(t: &TokenTree, r: &TtReader) -> LockstepIterSize {
match *t {
- TTDelim(ref tts) | TTSeq(_, ref tts, _, _) => {
- tts.iter().fold(LisUnconstrained, |lis, tt| {
- lis_merge(lis, lockstep_iter_size(tt, r))
+ TtDelimited(_, ref delimed) => {
+ let (_, ref tts, _) = **delimed;
+ tts.iter().fold(LisUnconstrained, |size, tt| {
+ size + lockstep_iter_size(tt, r)
})
- }
- TTTok(..) => LisUnconstrained,
- TTNonterminal(_, name) => match *lookup_cur_matched(r, name) {
+ },
+ TtSequence(_, ref tts, _, _) => {
+ tts.iter().fold(LisUnconstrained, |size, tt| {
+ size + lockstep_iter_size(tt, r)
+ })
+ },
+ TtToken(..) => LisUnconstrained,
+ TtNonterminal(_, name) => match *lookup_cur_matched(r, name) {
MatchedNonterminal(_) => LisUnconstrained,
MatchedSeq(ref ads, _) => LisConstraint(ads.len(), name)
- }
+ },
}
}
}
}
}
- loop { /* because it's easiest, this handles `TTDelim` not starting
- with a `TTTok`, even though it won't happen */
+ loop { /* because it's easiest, this handles `TtDelimited` not starting
+ with a `TtToken`, even though it won't happen */
let t = {
let frame = r.stack.last().unwrap();
// FIXME(pcwalton): Bad copy.
(*frame.forest)[frame.idx].clone()
};
match t {
- TTDelim(tts) => {
+ TtDelimited(_, ref delimed) => {
+ let (ref open, ref tts, ref close) = **delimed;
+ let mut forest = Vec::with_capacity(1 + tts.len() + 1);
+ forest.push(open.to_tt());
+ forest.extend(tts.iter().map(|x| (*x).clone()));
+ forest.push(close.to_tt());
+
r.stack.push(TtFrame {
- forest: tts,
+ forest: Rc::new(forest),
idx: 0,
dotdotdoted: false,
sep: None
});
// if this could be 0-length, we'd need to potentially recur here
}
- TTTok(sp, tok) => {
+ TtToken(sp, tok) => {
r.cur_span = sp;
r.cur_tok = tok;
r.stack.last_mut().unwrap().idx += 1;
return ret_val;
}
- TTSeq(sp, tts, sep, zerok) => {
+ TtSequence(sp, tts, sep, kleene_op) => {
// FIXME(pcwalton): Bad copy.
- match lockstep_iter_size(&TTSeq(sp, tts.clone(), sep.clone(), zerok), r) {
+ match lockstep_iter_size(&TtSequence(sp, tts.clone(), sep.clone(), kleene_op), r) {
LisUnconstrained => {
r.sp_diag.span_fatal(
sp.clone(), /* blame macro writer */
}
LisConstraint(len, _) => {
if len == 0 {
- if !zerok {
+ if kleene_op == ast::OneOrMore {
// FIXME #2887 blame invoker
r.sp_diag.span_fatal(sp.clone(),
"this must repeat at least once");
}
}
// FIXME #2887: think about span stuff here
- TTNonterminal(sp, ident) => {
+ TtNonterminal(sp, ident) => {
r.stack.last_mut().unwrap().idx += 1;
match *lookup_cur_matched(r, ident) {
/* sidestep the interpolation tricks for ident because
pub fn noop_fold_tt<T: Folder>(tt: &TokenTree, fld: &mut T) -> TokenTree {
match *tt {
- TTTok(span, ref tok) =>
- TTTok(span, fld.fold_token(tok.clone())),
- TTDelim(ref tts) => TTDelim(Rc::new(fld.fold_tts(tts.as_slice()))),
- TTSeq(span, ref pattern, ref sep, is_optional) =>
- TTSeq(span,
- Rc::new(fld.fold_tts(pattern.as_slice())),
- sep.clone().map(|tok| fld.fold_token(tok)),
- is_optional),
- TTNonterminal(sp,ref ident) =>
- TTNonterminal(sp,fld.fold_ident(*ident))
+ TtToken(span, ref tok) =>
+ TtToken(span, fld.fold_token(tok.clone())),
+ TtDelimited(span, ref delimed) => {
+ let (ref open, ref tts, ref close) = **delimed;
+ TtDelimited(span, Rc::new((
+ Delimiter {
+ span: open.span,
+ token: fld.fold_token(open.token.clone())
+ },
+ fld.fold_tts(tts.as_slice()),
+ Delimiter {
+ span: close.span,
+ token: fld.fold_token(close.token.clone())
+ },
+ )))
+ },
+ TtSequence(span, ref pattern, ref sep, is_optional) =>
+ TtSequence(span,
+ Rc::new(fld.fold_tts(pattern.as_slice())),
+ sep.clone().map(|tok| fld.fold_token(tok)),
+ is_optional),
+ TtNonterminal(sp,ref ident) =>
+ TtNonterminal(sp,fld.fold_ident(*ident))
}
}
pub fn into_vec(self) -> Vec<T> {
// null is ok, because len == 0 in that case, as required by Vec.
unsafe {
- let ret = Vec::from_raw_parts(self.len, self.len, self.data);
+ let ret = Vec::from_raw_parts(self.data, self.len, self.len);
// the vector owns the allocation now
mem::forget(self);
ret
}
// check the token-tree-ization of macros
- #[test] fn string_to_tts_macro () {
+ #[test]
+ fn string_to_tts_macro () {
let tts = string_to_tts("macro_rules! zip (($a)=>($a))".to_string());
let tts: &[ast::TokenTree] = tts.as_slice();
match tts {
- [ast::TTTok(_,_),
- ast::TTTok(_,token::NOT),
- ast::TTTok(_,_),
- ast::TTDelim(ref delim_elts)] => {
- let delim_elts: &[ast::TokenTree] = delim_elts.as_slice();
- match delim_elts {
- [ast::TTTok(_,token::LPAREN),
- ast::TTDelim(ref first_set),
- ast::TTTok(_,token::FAT_ARROW),
- ast::TTDelim(ref second_set),
- ast::TTTok(_,token::RPAREN)] => {
- let first_set: &[ast::TokenTree] =
- first_set.as_slice();
- match first_set {
- [ast::TTTok(_,token::LPAREN),
- ast::TTTok(_,token::DOLLAR),
- ast::TTTok(_,_),
- ast::TTTok(_,token::RPAREN)] => {
- let second_set: &[ast::TokenTree] =
- second_set.as_slice();
- match second_set {
- [ast::TTTok(_,token::LPAREN),
- ast::TTTok(_,token::DOLLAR),
- ast::TTTok(_,_),
- ast::TTTok(_,token::RPAREN)] => {
- assert_eq!("correct","correct")
- }
- _ => assert_eq!("wrong 4","correct")
- }
- },
- _ => {
- error!("failing value 3: {}",first_set);
- assert_eq!("wrong 3","correct")
- }
+ [ast::TtToken(_, token::IDENT(name_macro_rules, false)),
+ ast::TtToken(_, token::NOT),
+ ast::TtToken(_, token::IDENT(name_zip, false)),
+ ast::TtDelimited(_, ref macro_delimed)]
+ if name_macro_rules.as_str() == "macro_rules"
+ && name_zip.as_str() == "zip" => {
+ let (ref macro_open, ref macro_tts, ref macro_close) = **macro_delimed;
+ match (macro_open, macro_tts.as_slice(), macro_close) {
+ (&ast::Delimiter { token: token::LPAREN, .. },
+ [ast::TtDelimited(_, ref first_delimed),
+ ast::TtToken(_, token::FAT_ARROW),
+ ast::TtDelimited(_, ref second_delimed)],
+ &ast::Delimiter { token: token::RPAREN, .. }) => {
+ let (ref first_open, ref first_tts, ref first_close) = **first_delimed;
+ match (first_open, first_tts.as_slice(), first_close) {
+ (&ast::Delimiter { token: token::LPAREN, .. },
+ [ast::TtToken(_, token::DOLLAR),
+ ast::TtToken(_, token::IDENT(name, false))],
+ &ast::Delimiter { token: token::RPAREN, .. })
+ if name.as_str() == "a" => {},
+ _ => fail!("value 3: {}", **first_delimed),
+ }
+ let (ref second_open, ref second_tts, ref second_close) = **second_delimed;
+ match (second_open, second_tts.as_slice(), second_close) {
+ (&ast::Delimiter { token: token::LPAREN, .. },
+ [ast::TtToken(_, token::DOLLAR),
+ ast::TtToken(_, token::IDENT(name, false))],
+ &ast::Delimiter { token: token::RPAREN, .. })
+ if name.as_str() == "a" => {},
+ _ => fail!("value 4: {}", **second_delimed),
}
},
- _ => {
- error!("failing value 2: {}",delim_elts);
- assert_eq!("wrong","correct");
- }
+ _ => fail!("value 2: {}", **macro_delimed),
}
},
- _ => {
- error!("failing value: {}",tts);
- assert_eq!("wrong 1","correct");
- }
+ _ => fail!("value: {}",tts),
}
}
- #[test] fn string_to_tts_1 () {
+ #[test]
+ fn string_to_tts_1 () {
let tts = string_to_tts("fn a (b : int) { b; }".to_string());
assert_eq!(json::encode(&tts),
"[\
{\
- \"variant\":\"TTTok\",\
+ \"variant\":\"TtToken\",\
\"fields\":[\
null,\
{\
]\
},\
{\
- \"variant\":\"TTTok\",\
+ \"variant\":\"TtToken\",\
\"fields\":[\
null,\
{\
]\
},\
{\
- \"variant\":\"TTDelim\",\
+ \"variant\":\"TtDelimited\",\
\"fields\":[\
+ null,\
[\
{\
- \"variant\":\"TTTok\",\
- \"fields\":[\
- null,\
- \"LPAREN\"\
- ]\
- },\
- {\
- \"variant\":\"TTTok\",\
- \"fields\":[\
- null,\
- {\
- \"variant\":\"IDENT\",\
- \"fields\":[\
- \"b\",\
- false\
- ]\
- }\
- ]\
+ \"span\":null,\
+ \"token\":\"LPAREN\"\
},\
+ [\
+ {\
+ \"variant\":\"TtToken\",\
+ \"fields\":[\
+ null,\
+ {\
+ \"variant\":\"IDENT\",\
+ \"fields\":[\
+ \"b\",\
+ false\
+ ]\
+ }\
+ ]\
+ },\
+ {\
+ \"variant\":\"TtToken\",\
+ \"fields\":[\
+ null,\
+ \"COLON\"\
+ ]\
+ },\
+ {\
+ \"variant\":\"TtToken\",\
+ \"fields\":[\
+ null,\
+ {\
+ \"variant\":\"IDENT\",\
+ \"fields\":[\
+ \"int\",\
+ false\
+ ]\
+ }\
+ ]\
+ }\
+ ],\
{\
- \"variant\":\"TTTok\",\
- \"fields\":[\
- null,\
- \"COLON\"\
- ]\
- },\
- {\
- \"variant\":\"TTTok\",\
- \"fields\":[\
- null,\
- {\
- \"variant\":\"IDENT\",\
- \"fields\":[\
- \"int\",\
- false\
- ]\
- }\
- ]\
- },\
- {\
- \"variant\":\"TTTok\",\
- \"fields\":[\
- null,\
- \"RPAREN\"\
- ]\
+ \"span\":null,\
+ \"token\":\"RPAREN\"\
}\
]\
]\
},\
{\
- \"variant\":\"TTDelim\",\
+ \"variant\":\"TtDelimited\",\
\"fields\":[\
+ null,\
[\
{\
- \"variant\":\"TTTok\",\
- \"fields\":[\
- null,\
- \"LBRACE\"\
- ]\
- },\
- {\
- \"variant\":\"TTTok\",\
- \"fields\":[\
- null,\
- {\
- \"variant\":\"IDENT\",\
- \"fields\":[\
- \"b\",\
- false\
- ]\
- }\
- ]\
- },\
- {\
- \"variant\":\"TTTok\",\
- \"fields\":[\
- null,\
- \"SEMI\"\
- ]\
+ \"span\":null,\
+ \"token\":\"LBRACE\"\
},\
+ [\
+ {\
+ \"variant\":\"TtToken\",\
+ \"fields\":[\
+ null,\
+ {\
+ \"variant\":\"IDENT\",\
+ \"fields\":[\
+ \"b\",\
+ false\
+ ]\
+ }\
+ ]\
+ },\
+ {\
+ \"variant\":\"TtToken\",\
+ \"fields\":[\
+ null,\
+ \"SEMI\"\
+ ]\
+ }\
+ ],\
{\
- \"variant\":\"TTTok\",\
- \"fields\":[\
- null,\
- \"RBRACE\"\
- ]\
+ \"span\":null,\
+ \"token\":\"RBRACE\"\
}\
]\
]\
use ast::{StructVariantKind, BiSub};
use ast::StrStyle;
use ast::{SelfExplicit, SelfRegion, SelfStatic, SelfValue};
-use ast::{TokenTree, TraitItem, TraitRef, TTDelim, TTSeq, TTTok};
-use ast::{TTNonterminal, TupleVariantKind, Ty, Ty_, TyBot};
+use ast::{Delimiter, TokenTree, TraitItem, TraitRef, TtDelimited, TtSequence, TtToken};
+use ast::{TtNonterminal, TupleVariantKind, Ty, Ty_, TyBot};
use ast::{TypeField, TyFixedLengthVec, TyClosure, TyProc, TyBareFn};
use ast::{TyTypeof, TyInfer, TypeMethod};
use ast::{TyNil, TyParam, TyParamBound, TyParen, TyPath, TyPtr, TyQPath};
return e;
}
- /// Parse an optional separator followed by a kleene-style
+ /// Parse an optional separator followed by a Kleene-style
/// repetition token (+ or *).
- pub fn parse_sep_and_zerok(&mut self) -> (Option<token::Token>, bool) {
- fn parse_zerok(parser: &mut Parser) -> Option<bool> {
+ pub fn parse_sep_and_kleene_op(&mut self) -> (Option<token::Token>, ast::KleeneOp) {
+ fn parse_kleene_op(parser: &mut Parser) -> Option<ast::KleeneOp> {
match parser.token {
- token::BINOP(token::STAR) | token::BINOP(token::PLUS) => {
- let zerok = parser.token == token::BINOP(token::STAR);
+ token::BINOP(token::STAR) => {
parser.bump();
- Some(zerok)
+ Some(ast::ZeroOrMore)
+ },
+ token::BINOP(token::PLUS) => {
+ parser.bump();
+ Some(ast::OneOrMore)
},
_ => None
}
};
- match parse_zerok(self) {
- Some(zerok) => return (None, zerok),
+ match parse_kleene_op(self) {
+ Some(kleene_op) => return (None, kleene_op),
None => {}
}
let separator = self.bump_and_get();
- match parse_zerok(self) {
+ match parse_kleene_op(self) {
Some(zerok) => (Some(separator), zerok),
None => self.fatal("expected `*` or `+`")
}
/// parse a single token tree from the input.
pub fn parse_token_tree(&mut self) -> TokenTree {
// FIXME #6994: currently, this is too eager. It
- // parses token trees but also identifies TTSeq's
- // and TTNonterminal's; it's too early to know yet
+ // parses token trees but also identifies TtSequence's
+ // and TtNonterminal's; it's too early to know yet
// whether something will be a nonterminal or a seq
// yet.
maybe_whole!(deref self, NtTT);
seq_sep_none(),
|p| p.parse_token_tree()
);
- let (s, z) = p.parse_sep_and_zerok();
+ let (sep, repeat) = p.parse_sep_and_kleene_op();
let seq = match seq {
Spanned { node, .. } => node,
};
- TTSeq(mk_sp(sp.lo, p.span.hi), Rc::new(seq), s, z)
+ TtSequence(mk_sp(sp.lo, p.span.hi), Rc::new(seq), sep, repeat)
} else {
- TTNonterminal(sp, p.parse_ident())
+ TtNonterminal(sp, p.parse_ident())
}
}
_ => {
- parse_any_tt_tok(p)
+ TtToken(p.span, p.bump_and_get())
}
}
}
- // turn the next token into a TTTok:
- fn parse_any_tt_tok(p: &mut Parser) -> TokenTree {
- TTTok(p.span, p.bump_and_get())
- }
-
match (&self.token, token::close_delimiter_for(&self.token)) {
(&token::EOF, _) => {
let open_braces = self.open_braces.clone();
self.fatal("this file contains an un-closed delimiter ");
}
(_, Some(close_delim)) => {
+ // The span for beginning of the delimited section
+ let pre_span = self.span;
+
// Parse the open delimiter.
self.open_braces.push(self.span);
- let mut result = vec!(parse_any_tt_tok(self));
+ let open = Delimiter {
+ span: self.span,
+ token: self.bump_and_get(),
+ };
- let trees =
- self.parse_seq_to_before_end(&close_delim,
- seq_sep_none(),
- |p| p.parse_token_tree());
- result.extend(trees.into_iter());
+ // Parse the token trees within the delimeters
+ let tts = self.parse_seq_to_before_end(
+ &close_delim, seq_sep_none(), |p| p.parse_token_tree()
+ );
// Parse the close delimiter.
- result.push(parse_any_tt_tok(self));
+ let close = Delimiter {
+ span: self.span,
+ token: self.bump_and_get(),
+ };
self.open_braces.pop().unwrap();
- TTDelim(Rc::new(result))
+ // Expand to cover the entire delimited token tree
+ let span = Span { hi: self.span.hi, ..pre_span };
+
+ TtDelimited(span, Rc::new((open, tts, close)))
}
_ => parse_non_delim_tt_tok(self)
}
if ms.len() == 0u {
self.fatal("repetition body must be nonempty");
}
- let (sep, zerok) = self.parse_sep_and_zerok();
- MatchSeq(ms, sep, zerok, name_idx_lo, *name_idx)
+ let (sep, kleene_op) = self.parse_sep_and_kleene_op();
+ MatchSeq(ms, sep, kleene_op, name_idx_lo, *name_idx)
} else {
let bound_to = self.parse_ident();
self.expect(&token::COLON);
$to_string(|s| s.print_item(i))
}
+pub fn view_item_to_string(i: &ast::ViewItem) -> String {
+ $to_string(|s| s.print_view_item(i))
+}
+
pub fn generics_to_string(generics: &ast::Generics) -> String {
$to_string(|s| s.print_generics(generics))
}
/// expression arguments as expressions). It can be done! I think.
pub fn print_tt(&mut self, tt: &ast::TokenTree) -> IoResult<()> {
match *tt {
- ast::TTDelim(ref tts) => self.print_tts(tts.as_slice()),
- ast::TTTok(_, ref tk) => {
+ ast::TtDelimited(_, ref delimed) => {
+ let (ref open, ref tts, ref close) = **delimed;
+ try!(word(&mut self.s, parse::token::to_string(&open.token).as_slice()));
+ try!(space(&mut self.s));
+ try!(self.print_tts(tts.as_slice()));
+ try!(space(&mut self.s));
+ word(&mut self.s, parse::token::to_string(&close.token).as_slice())
+ },
+ ast::TtToken(_, ref tk) => {
try!(word(&mut self.s, parse::token::to_string(tk).as_slice()));
match *tk {
parse::token::DOC_COMMENT(..) => {
_ => Ok(())
}
}
- ast::TTSeq(_, ref tts, ref sep, zerok) => {
+ ast::TtSequence(_, ref tts, ref separator, kleene_op) => {
try!(word(&mut self.s, "$("));
for tt_elt in (*tts).iter() {
try!(self.print_tt(tt_elt));
}
try!(word(&mut self.s, ")"));
- match *sep {
+ match *separator {
Some(ref tk) => {
try!(word(&mut self.s,
parse::token::to_string(tk).as_slice()));
}
None => ()
}
- word(&mut self.s, if zerok { "*" } else { "+" })
+ match kleene_op {
+ ast::ZeroOrMore => word(&mut self.s, "*"),
+ ast::OneOrMore => word(&mut self.s, "+"),
+ }
}
- ast::TTNonterminal(_, name) => {
+ ast::TtNonterminal(_, name) => {
try!(word(&mut self.s, "$"));
self.print_ident(name)
}
}
fn bits_to_color(bits: u16) -> color::Color {
- let color = match bits & 0x7 {
+ let bits = bits & 0x7;
+ let color = match bits {
0 => color::BLACK,
0x1 => color::BLUE,
0x2 => color::GREEN,
use syntax::codemap::Span;
use syntax::parse::token::{IDENT, get_ident};
-use syntax::ast::{TokenTree, TTTok};
+use syntax::ast::{TokenTree, TtToken};
use syntax::ext::base::{ExtCtxt, MacResult, DummyResult, MacExpr};
use syntax::ext::build::AstBuilder; // trait for expr_uint
use rustc::plugin::Registry;
("I", 1)];
let text = match args {
- [TTTok(_, IDENT(s, _))] => get_ident(s).to_string(),
+ [TtToken(_, IDENT(s, _))] => get_ident(s).to_string(),
_ => {
cx.span_err(sp, "argument should be a single identifier");
return DummyResult::any(sp);
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
// OF THE POSSIBILITY OF SUCH DAMAGE.
-// ignore-pretty very bad with line comments
// ignore-android doesn't terminate?
-#![feature(slicing_syntax)]
+#![feature(slicing_syntax, asm, if_let, tuple_indexing)]
-use std::iter::range_step;
-use std::io::{stdin, stdout, File};
+extern crate libc;
-static LINE_LEN: uint = 60;
+use std::io::stdio::{stdin_raw, stdout_raw};
+use std::sync::{Future};
+use std::num::{div_rem};
+use std::ptr::{copy_memory};
+use std::io::{IoResult, EndOfFile};
+use std::slice::raw::{mut_buf_as_slice};
-fn make_complements() -> [u8, ..256] {
- let transforms = [
- ('A', 'T'), ('C', 'G'), ('G', 'C'), ('T', 'A'),
- ('U', 'A'), ('M', 'K'), ('R', 'Y'), ('W', 'W'),
- ('S', 'S'), ('Y', 'R'), ('K', 'M'), ('V', 'B'),
- ('H', 'D'), ('D', 'H'), ('B', 'V'), ('N', 'N'),
- ('\n', '\n')];
- let mut complements: [u8, ..256] = [0, ..256];
- for (i, c) in complements.iter_mut().enumerate() {
- *c = i as u8;
+use shared_memory::{SharedMemory};
+
+mod tables {
+ use std::sync::{Once, ONCE_INIT};
+
+ /// Lookup tables.
+ static mut CPL16: [u16, ..1 << 16] = [0, ..1 << 16];
+ static mut CPL8: [u8, ..1 << 8] = [0, ..1 << 8];
+
+ /// Generates the tables.
+ pub fn get() -> Tables {
+ /// To make sure we initialize the tables only once.
+ static INIT: Once = ONCE_INIT;
+ INIT.doit(|| {
+ unsafe {
+ for i in range(0, 1 << 8) {
+ CPL8[i] = match i as u8 {
+ b'A' | b'a' => b'T',
+ b'C' | b'c' => b'G',
+ b'G' | b'g' => b'C',
+ b'T' | b't' => b'A',
+ b'U' | b'u' => b'A',
+ b'M' | b'm' => b'K',
+ b'R' | b'r' => b'Y',
+ b'W' | b'w' => b'W',
+ b'S' | b's' => b'S',
+ b'Y' | b'y' => b'R',
+ b'K' | b'k' => b'M',
+ b'V' | b'v' => b'B',
+ b'H' | b'h' => b'D',
+ b'D' | b'd' => b'H',
+ b'B' | b'b' => b'V',
+ b'N' | b'n' => b'N',
+ i => i,
+ };
+ }
+
+ for (i, v) in CPL16.iter_mut().enumerate() {
+ *v = *CPL8.unsafe_get(i & 255) as u16 << 8 |
+ *CPL8.unsafe_get(i >> 8) as u16;
+ }
+ }
+ });
+ Tables { _dummy: () }
}
- let lower = 'A' as u8 - 'a' as u8;
- for &(from, to) in transforms.iter() {
- complements[from as uint] = to as u8;
- complements[(from as u8 - lower) as uint] = to as u8;
+
+ /// Accessor for the static arrays.
+ ///
+ /// To make sure that the tables can't be accessed without having been initialized.
+ pub struct Tables {
+ _dummy: ()
+ }
+
+ impl Tables {
+ /// Retreives the complement for `i`.
+ pub fn cpl8(self, i: u8) -> u8 {
+ // Not really unsafe.
+ unsafe { CPL8[i as uint] }
+ }
+
+ /// Retreives the complement for `i`.
+ pub fn cpl16(self, i: u16) -> u16 {
+ unsafe { CPL16[i as uint] }
+ }
}
- complements
}
-fn main() {
- let complements = make_complements();
- let data = if std::os::getenv("RUST_BENCH").is_some() {
- File::open(&Path::new("shootout-k-nucleotide.data")).read_to_end()
- } else {
- stdin().read_to_end()
- };
- let mut data = data.unwrap();
+mod shared_memory {
+ use std::sync::{Arc};
+ use std::mem::{transmute};
+ use std::raw::{Slice};
- for seq in data.as_mut_slice().split_mut(|c| *c == '>' as u8) {
- // skip header and last \n
- let begin = match seq.iter().position(|c| *c == '\n' as u8) {
- None => continue,
- Some(c) => c
- };
- let len = seq.len();
- let seq = seq[mut begin+1..len-1];
-
- // arrange line breaks
- let len = seq.len();
- let off = LINE_LEN - len % (LINE_LEN + 1);
- for i in range_step(LINE_LEN, len, LINE_LEN + 1) {
- for j in std::iter::count(i, -1).take(off) {
- seq[j] = seq[j - 1];
+ /// Structure for sharing disjoint parts of a vector mutably across tasks.
+ pub struct SharedMemory {
+ ptr: Arc<Vec<u8>>,
+ start: uint,
+ len: uint,
+ }
+
+ impl SharedMemory {
+ pub fn new(ptr: Vec<u8>) -> SharedMemory {
+ let len = ptr.len();
+ SharedMemory {
+ ptr: Arc::new(ptr),
+ start: 0,
+ len: len,
}
- seq[i - off] = '\n' as u8;
}
- // reverse complement, as
- // seq.reverse(); for c in seq.iter_mut() { *c = complements[*c] }
- // but faster:
- for (front, back) in two_side_iter(seq) {
- let tmp = complements[*front as uint];
- *front = complements[*back as uint];
- *back = tmp;
+ pub fn as_mut_slice(&mut self) -> &mut [u8] {
+ unsafe {
+ transmute(Slice {
+ data: self.ptr.as_ptr().offset(self.start as int) as *const u8,
+ len: self.len,
+ })
+ }
}
- if seq.len() % 2 == 1 {
- let middle = &mut seq[seq.len() / 2];
- *middle = complements[*middle as uint];
+
+ pub fn len(&self) -> uint {
+ self.len
}
- }
- stdout().write(data.as_slice()).unwrap();
+ pub fn split_at(self, mid: uint) -> (SharedMemory, SharedMemory) {
+ assert!(mid <= self.len);
+ let left = SharedMemory {
+ ptr: self.ptr.clone(),
+ start: self.start,
+ len: mid,
+ };
+ let right = SharedMemory {
+ ptr: self.ptr,
+ start: self.start + mid,
+ len: self.len - mid,
+ };
+ (left, right)
+ }
+
+ /// Resets the object so that it covers the whole range of the contained vector.
+ ///
+ /// You must not call this method if `self` is not the only reference to the
+ /// shared memory.
+ ///
+ /// FIXME: If `Arc` had a method to check if the reference is unique, then we
+ /// wouldn't need the `unsafe` here.
+ ///
+ /// FIXME: If `Arc` had a method to unwrap the contained value, then we could
+ /// simply unwrap here.
+ pub unsafe fn reset(self) -> SharedMemory {
+ let len = self.ptr.len();
+ SharedMemory {
+ ptr: self.ptr,
+ start: 0,
+ len: len,
+ }
+ }
+ }
}
-pub struct TwoSideIter<'a, T: 'a> {
- first: *mut T,
- last: *mut T,
- marker: std::kinds::marker::ContravariantLifetime<'a>,
- marker2: std::kinds::marker::NoCopy
+
+/// Reads all remaining bytes from the stream.
+fn read_to_end<R: Reader>(r: &mut R) -> IoResult<Vec<u8>> {
+ const CHUNK: uint = 64 * 1024;
+
+ let mut vec = Vec::with_capacity(1024 * 1024);
+ loop {
+ if vec.capacity() - vec.len() < CHUNK {
+ let cap = vec.capacity();
+ let mult = if cap < 256 * 1024 * 1024 {
+ // FIXME (mahkoh): Temporary workaround for jemalloc on linux. Replace
+ // this by 2x once the jemalloc preformance issue has been fixed.
+ 16
+ } else {
+ 2
+ };
+ vec.reserve_exact(mult * cap);
+ }
+ unsafe {
+ let ptr = vec.as_mut_ptr().offset(vec.len() as int);
+ match mut_buf_as_slice(ptr, CHUNK, |s| r.read(s)) {
+ Ok(n) => {
+ let len = vec.len();
+ vec.set_len(len + n);
+ },
+ Err(ref e) if e.kind == EndOfFile => break,
+ Err(e) => return Err(e),
+ }
+ }
+ }
+ Ok(vec)
}
-pub fn two_side_iter<'a, T>(slice: &'a mut [T]) -> TwoSideIter<'a, T> {
- let len = slice.len();
- let first = slice.as_mut_ptr();
- let last = if len == 0 {
- first
- } else {
- unsafe { first.offset(len as int - 1) }
+/// Finds the first position at which `b` occurs in `s`.
+fn memchr(h: &[u8], n: u8) -> Option<uint> {
+ use libc::{c_void, c_int, size_t};
+ extern {
+ fn memchr(h: *const c_void, n: c_int, s: size_t) -> *mut c_void;
+ }
+ let res = unsafe {
+ memchr(h.as_ptr() as *const c_void, n as c_int, h.len() as size_t)
};
-
- TwoSideIter {
- first: first,
- last: last,
- marker: std::kinds::marker::ContravariantLifetime,
- marker2: std::kinds::marker::NoCopy
+ if res.is_null() {
+ None
+ } else {
+ Some(res as uint - h.as_ptr() as uint)
}
}
-impl<'a, T> Iterator<(&'a mut T, &'a mut T)> for TwoSideIter<'a, T> {
- fn next(&mut self) -> Option<(&'a mut T, &'a mut T)> {
- if self.first < self.last {
- let result = unsafe { (&mut *self.first, &mut *self.last) };
- self.first = unsafe { self.first.offset(1) };
- self.last = unsafe { self.last.offset(-1) };
- Some(result)
- } else {
- None
+/// Length of a normal line without the terminating \n.
+const LINE_LEN: uint = 60;
+
+/// Compute the reverse complement.
+fn reverse_complement(mut view: SharedMemory, tables: tables::Tables) {
+ // Drop the last newline
+ let seq = view.as_mut_slice().init_mut();
+ let len = seq.len();
+ let off = LINE_LEN - len % (LINE_LEN + 1);
+ let mut i = LINE_LEN;
+ while i < len {
+ unsafe {
+ copy_memory(seq.as_mut_ptr().offset((i - off + 1) as int),
+ seq.as_ptr().offset((i - off) as int), off);
+ *seq.unsafe_mut(i - off) = b'\n';
+ }
+ i += LINE_LEN + 1;
+ }
+
+ let (div, rem) = div_rem(len, 4);
+ unsafe {
+ let mut left = seq.as_mut_ptr() as *mut u16;
+ // This is slow if len % 2 != 0 but still faster than bytewise operations.
+ let mut right = seq.as_mut_ptr().offset(len as int - 2) as *mut u16;
+ let end = left.offset(div as int);
+ while left != end {
+ let tmp = tables.cpl16(*left);
+ *left = tables.cpl16(*right);
+ *right = tmp;
+ left = left.offset(1);
+ right = right.offset(-1);
+ }
+
+ let end = end as *mut u8;
+ match rem {
+ 1 => *end = tables.cpl8(*end),
+ 2 => {
+ let tmp = tables.cpl8(*end);
+ *end = tables.cpl8(*end.offset(1));
+ *end.offset(1) = tmp;
+ },
+ 3 => {
+ *end.offset(1) = tables.cpl8(*end.offset(1));
+ let tmp = tables.cpl8(*end);
+ *end = tables.cpl8(*end.offset(2));
+ *end.offset(2) = tmp;
+ },
+ _ => { },
}
}
}
+
+fn main() {
+ let mut data = SharedMemory::new(read_to_end(&mut stdin_raw()).unwrap());
+ let tables = tables::get();
+
+ let mut futures = vec!();
+ loop {
+ let (_, mut tmp_data) = match memchr(data.as_mut_slice(), b'\n') {
+ Some(i) => data.split_at(i + 1),
+ _ => break,
+ };
+ let (view, tmp_data) = match memchr(tmp_data.as_mut_slice(), b'>') {
+ Some(i) => tmp_data.split_at(i),
+ None => {
+ let len = tmp_data.len();
+ tmp_data.split_at(len)
+ },
+ };
+ futures.push(Future::spawn(proc() reverse_complement(view, tables)));
+ data = tmp_data;
+ }
+
+ for f in futures.iter_mut() {
+ f.get();
+ }
+
+ // Not actually unsafe. If Arc had a way to check uniqueness then we could do that in
+ // `reset` and it would tell us that, yes, it is unique at this point.
+ data = unsafe { data.reset() };
+
+ stdout_raw().write(data.as_mut_slice()).unwrap();
+}
--- /dev/null
+// 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.
+
+mod foo { pub fn bar() {} }
+
+fn main() {
+ match () {
+ foo::bar => {} //~ ERROR `bar` is not an enum variant, struct or const
+ }
+}
--- /dev/null
+// 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 main() {
+ let foo = "str";
+ println!("{}", foo.desc); //~ ERROR attempted access of field `desc` on type `&str`,
+ // but no field with that name was found
+}
--- /dev/null
+// 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.
+
+trait T<'a> {
+ fn a(&'a self) -> &'a bool;
+ fn b(&self) {
+ self.a(); //~ ERROR mismatched types: expected `&'a Self`, found `&Self` (lifetime mismatch)
+ }
+}
+
+fn main() {}
--- /dev/null
+// 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 B<T>;
+
+fn main() {
+ let foo = B; //~ ERROR unable to infer enough type information to locate the impl of the trait
+ let closure = |:| foo;
+}
--- /dev/null
+// 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.
+
+pub fn main() {
+ static z: &'static int = {
+ let p = 3;
+ &p
+//~^ ERROR cannot borrow a local variable inside a static block, define a separate static instead
+ };
+}
--- /dev/null
+// 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(struct_variant)]
+
+enum Foo {
+ Variant { x: uint }
+}
+
+fn main() {
+ let f = Variant(42u); //~ ERROR expected function, found `Foo`
+}
--- /dev/null
+// 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 main() {
+ const X: u32 = 1;
+ const Y: uint = &X as *const u32 as uint; //~ ERROR E0018
+ println!("{}", Y);
+}
--- /dev/null
+// 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 Obj<'a> {
+ closure: ||: 'a -> u32
+}
+
+fn main() {
+ let o = Obj { closure: || 42 };
+ o.closure(); //~ ERROR type `Obj<'_>` does not implement any method in scope named `closure`
+ //~^ NOTE use `(s.closure)(...)` if you meant to call the function stored in the `closure` field
+}
--- /dev/null
+// 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 x: &'static uint = &1;
+static y: uint = *x;
+//~^ ERROR cannot refer to other statics by value,
+// use the address-of operator or a constant instead
+fn main() {}
--- /dev/null
+// 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.
+
+enum A { B, C }
+
+mod foo { pub fn bar() {} }
+
+fn main() {
+ match (true, false) {
+ B => (), //~ ERROR expected `(bool,bool)`, found `A` (expected tuple, found enum A)
+ _ => ()
+ }
+
+ match &Some(42i) {
+ Some(x) => (), //~ ERROR expected `&core::option::Option<int>`,
+ // found `core::option::Option<<generic #4>>`
+ None => () //~ ERROR expected `&core::option::Option<int>`,
+ // found `core::option::Option<<generic #5>>`
+ }
+}
--- /dev/null
+// 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)]
+
+// Test that even unboxed closures that are capable of mutating their
+// environment cannot mutate captured variables that have not been
+// declared mutable (#18335)
+
+fn set(x: &mut uint) { *x = 0; }
+
+fn main() {
+ let x = 0u;
+ move |&mut:| x = 1; //~ ERROR cannot assign
+ move |&mut:| set(&mut x); //~ ERROR cannot borrow
+ move |:| x = 1; //~ ERROR cannot assign
+ move |:| set(&mut x); //~ ERROR cannot borrow
+ |&mut:| x = 1; //~ ERROR cannot assign
+ // FIXME: this should be `cannot borrow` (issue #18330)
+ |&mut:| set(&mut x); //~ ERROR cannot assign
+ |:| x = 1; //~ ERROR cannot assign
+ // FIXME: this should be `cannot borrow` (issue #18330)
+ |:| set(&mut x); //~ ERROR cannot assign
+}
// except according to those terms.
#![allow(dead_code)]
+#![feature(unboxed_closures, unboxed_closure_sugar)]
+
+// compile-flags:-g
fn foo<T>() {}
// issue #13490
let _ = || -> ! loop {};
let _ = proc() -> ! loop {};
+
+ // issue #17021
+ let c = box |&:| {};
}
struct B<T>;
let x: *mut S = &mut S;
- // Test we can chnage the mutability from mut to const.
+ // Test we can change the mutability from mut to const.
let x: &T = &mut S;
let x: *const T = &mut S;
}
--- /dev/null
+// 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.
+
+trait Hash<H> {
+ fn hash2(&self, hasher: &H) -> u64;
+}
+
+trait Stream {
+ fn input(&mut self, bytes: &[u8]);
+ fn result(&self) -> u64;
+}
+
+trait StreamHasher<S: Stream> {
+ fn stream(&self) -> S;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+trait StreamHash<S: Stream, H: StreamHasher<S>>: Hash<H> {
+ fn input_stream(&self, stream: &mut S);
+}
+
+impl<S: Stream, H: StreamHasher<S>> Hash<H> for u8 {
+ fn hash2(&self, hasher: &H) -> u64 {
+ let mut stream = hasher.stream();
+ self.input_stream(&mut stream);
+ stream.result()
+ }
+}
+
+impl<S: Stream, H: StreamHasher<S>> StreamHash<S, H> for u8 {
+ fn input_stream(&self, stream: &mut S) {
+ stream.input([*self]);
+ }
+}
+
+fn main() {}
--- /dev/null
+// 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::io::Reader;
+
+enum Wrapper<'a> {
+ WrapReader(&'a Reader + 'a)
+}
+
+trait Wrap<'a> {
+ fn wrap(self) -> Wrapper<'a>;
+}
+
+impl<'a, R: Reader> Wrap<'a> for &'a mut R {
+ fn wrap(self) -> Wrapper<'a> {
+ WrapReader(self as &'a mut Reader)
+ }
+}
+
+pub fn main() {}
--- /dev/null
+// 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)]
+
+use std::mem;
+
+fn main() {
+ let y = 0u8;
+ let closure = move |&: x| y + x;
+
+ // Check that both closures are capturing by value
+ assert_eq!(1, mem::size_of_val(&closure));
+
+ spawn(proc() {
+ let ok = closure;
+ })
+}
--- /dev/null
+// 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 Parser<'a, I, O> {
+ parse: Box<FnMut<(I,), Result<O, String>> + 'a>
+}
+
+impl<'a, I, O: 'a> Parser<'a, I, O> {
+ fn compose<K: 'a>(mut self, mut rhs: Parser<O, K>) -> Parser<'a, I, K> {
+ Parser {
+ parse: box move |&mut: x: I| {
+ match self.parse.call_mut((x,)) {
+ Ok(r) => rhs.parse.call_mut((r,)),
+ Err(e) => Err(e)
+ }
+ }
+ }
+ }
+}
+
+fn main() {}
--- /dev/null
+// 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.
+
+pub struct Foo {
+ f1: int,
+ _f2: int,
+}
+
+#[inline(never)]
+pub fn foo(f: &mut Foo) -> Foo {
+ let ret = *f;
+ f.f1 = 0;
+ ret
+}
+
+pub fn main() {
+ let mut f = Foo {
+ f1: 8,
+ _f2: 9,
+ };
+ f = foo(&mut f);
+ assert_eq!(f.f1, 8);
+}
let ascend = ascend.as_mut_slice();
static ALIGN : uint = 1;
- // Checks that `ascend` forms triangle of acending size formed
+ // Checks that `ascend` forms triangle of ascending 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]) {
--- /dev/null
+// 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 the cache results from the default method do not pollute
+// the cache for the later call in `load()`.
+//
+// See issue #18209.
+
+pub trait Foo {
+ fn load_from() -> Box<Self>;
+ fn load() -> Box<Self> {
+ Foo::load_from()
+ }
+}
+
+pub fn load<M: Foo>() -> Box<M> {
+ Foo::load()
+}
+
+fn main() { }
--- /dev/null
+// 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)]
+#![deny(unused_mut)]
+
+// Test that mutating a mutable upvar in a capture-by-value unboxed
+// closure does not ice (issue #18238) and marks the upvar as used
+// mutably so we do not get a spurious warning about it not needing to
+// be declared mutable (issue #18336).
+
+fn main() {
+ {
+ let mut x = 0u;
+ move |&mut:| x += 1;
+ }
+ {
+ let mut x = 0u;
+ move |:| x += 1;
+ }
+}
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:
+ // (surprisingly, this can cause errors which are not caused by either of:
// `let x = vec.slice_mut(0, 2);`
// `foo(vec.slice_mut(0, 2));` ).
let mut vec: Vec<int> = vec!(1, 2, 3, 4);