# Linux builders, all docker images
- env: IMAGE=arm-android
- env: IMAGE=cross
+ - env: IMAGE=dist-arm-unknown-linux-gnueabi
+ - env: IMAGE=dist-x86_64-unknown-freebsd
- env: IMAGE=i686-gnu
- env: IMAGE=i686-gnu-nopt
- - env: IMAGE=x86_64-freebsd
- env: IMAGE=x86_64-gnu
- env: IMAGE=x86_64-gnu-full-bootstrap
- env: IMAGE=x86_64-gnu-aux
then
LLVM_BUILD_DIR=${CFG_BUILD_DIR}$t/llvm
LLVM_INST_DIR=$LLVM_BUILD_DIR
- # For some crazy reason the MSVC output dir is different than Unix
+ # For some weird reason the MSVC output dir is different than Unix
if [ ${is_msvc} -ne 0 ]; then
if [ -n "$CFG_DISABLE_OPTIMIZE_LLVM" ]
then
[lib]
name = "bootstrap"
path = "lib.rs"
+doctest = false
[[bin]]
name = "bootstrap"
path = "bin/main.rs"
+test = false
[[bin]]
name = "rustc"
path = "bin/rustc.rs"
+test = false
[[bin]]
name = "rustdoc"
path = "bin/rustdoc.rs"
+test = false
[dependencies]
build_helper = { path = "../build_helper" }
.arg("check")
.current_dir(&dir));
}
+
+/// Test the build system itself
+pub fn bootstrap(build: &Build) {
+ let mut cmd = Command::new(&build.cargo);
+ cmd.arg("test")
+ .current_dir(build.src.join("src/bootstrap"))
+ .env("CARGO_TARGET_DIR", build.out.join("bootstrap"))
+ .env("RUSTC", &build.rustc);
+ cmd.arg("--").args(&build.flags.cmd.test_args());
+ build.run(&mut cmd);
+}
}
pub fn create_sysroot(build: &Build, compiler: &Compiler) {
- // nothing to do in stage0
- if compiler.stage == 0 {
- return
- }
-
let sysroot = build.sysroot(compiler);
let _ = fs::remove_dir_all(&sysroot);
t!(fs::create_dir_all(&sysroot));
/// all files in a directory and updating the stamp if any are newer.
fn update_mtime(path: &Path) {
let mut max = None;
- if let Ok(entries) = path.parent().unwrap().read_dir() {
+ if let Ok(entries) = path.parent().unwrap().join("deps").read_dir() {
for entry in entries.map(|e| t!(e)) {
if t!(entry.file_type()).is_file() {
let meta = t!(entry.metadata());
return
}
+ // Package save-analysis from stage1 if not doing a full bootstrap, as the
+ // stage2 artifacts is simply copied from stage1 in that case.
+ let compiler = if build.force_use_stage1(compiler, target) {
+ Compiler::new(1, compiler.host)
+ } else {
+ compiler.clone()
+ };
+
let name = format!("rust-analysis-{}", package_vers(build));
let image = tmpdir(build).join(format!("{}-{}-image", name, target));
- let src = build.stage_out(compiler, Mode::Libstd).join(target).join("release").join("deps");
+ let src = build.stage_out(&compiler, Mode::Libstd).join(target).join("release").join("deps");
let image_src = src.join("save-analysis");
let dst = image.join("lib/rustlib").join(target).join("analysis");
}
/// Creates the `rust-src` installer component and the plain source tarball
-pub fn rust_src(build: &Build, host: &str) {
+pub fn rust_src(build: &Build) {
println!("Dist src");
- if host != build.config.build {
- println!("\tskipping, not a build host");
- return
- }
-
let plain_name = format!("rustc-{}-src", package_vers(build));
let name = format!("rust-src-{}", package_vers(build));
let image = tmpdir(build).join(format!("{}-image", name));
/// `STAMP` alongw ith providing the various header/footer HTML we've cutomized.
///
/// In the end, this is just a glorified wrapper around rustdoc!
-pub fn standalone(build: &Build, stage: u32, target: &str) {
- println!("Documenting stage{} standalone ({})", stage, target);
+pub fn standalone(build: &Build, target: &str) {
+ println!("Documenting standalone ({})", target);
let out = build.doc_out(target);
t!(fs::create_dir_all(&out));
- let compiler = Compiler::new(stage, &build.config.build);
+ let compiler = Compiler::new(0, &build.config.build);
let favicon = build.src.join("src/doc/favicon.inc");
let footer = build.src.join("src/doc/footer.inc");
suite("check-rpass-full", "src/test/run-pass-fulldeps",
"run-pass", "run-pass-fulldeps");
+ suite("check-rfail-full", "src/test/run-fail-fulldeps",
+ "run-fail", "run-fail-fulldeps");
suite("check-cfail-full", "src/test/compile-fail-fulldeps",
"compile-fail", "compile-fail-fulldeps");
suite("check-rmake", "src/test/run-make", "run-make", "run-make");
.dep(|s| s.name("tool-tidy").stage(0))
.default(true)
.host(true)
+ .only_build(true)
.run(move |s| check::tidy(build, s.target));
rules.test("check-error-index", "src/tools/error_index_generator")
.dep(|s| s.name("libstd"))
.dep(|s| s.name("libtest"))
.run(move |s| check::android_copy_libs(build, &s.compiler(), s.target));
+ rules.test("check-bootstrap", "src/bootstrap")
+ .default(true)
+ .host(true)
+ .only_build(true)
+ .run(move |_| check::bootstrap(build));
+
// ========================================================================
// Build tools
//
.default(build.config.docs)
.run(move |s| doc::rustbook(build, s.target, "nomicon"));
rules.doc("doc-standalone", "src/doc")
- .dep(move |s| s.name("rustc").host(&build.config.build).target(&build.config.build))
+ .dep(move |s| {
+ s.name("rustc")
+ .host(&build.config.build)
+ .target(&build.config.build)
+ .stage(0)
+ })
.default(build.config.docs)
- .run(move |s| doc::standalone(build, s.stage, s.target));
+ .run(move |s| doc::standalone(build, s.target));
rules.doc("doc-error-index", "src/tools/error_index_generator")
.dep(move |s| s.name("tool-error-index").target(&build.config.build).stage(0))
.dep(move |s| s.name("librustc-link").stage(0))
rules.dist("dist-rustc", "src/librustc")
.dep(move |s| s.name("rustc").host(&build.config.build))
.host(true)
+ .only_host_build(true)
.default(true)
.run(move |s| dist::rustc(build, s.stage, s.target));
rules.dist("dist-std", "src/libstd")
}
})
.default(true)
+ .only_host_build(true)
.run(move |s| dist::std(build, &s.compiler(), s.target));
rules.dist("dist-mingw", "path/to/nowhere")
.default(true)
+ .only_host_build(true)
.run(move |s| {
if s.target.contains("pc-windows-gnu") {
dist::mingw(build, s.target)
rules.dist("dist-src", "src")
.default(true)
.host(true)
- .run(move |s| dist::rust_src(build, s.target));
+ .only_build(true)
+ .only_host_build(true)
+ .run(move |_| dist::rust_src(build));
rules.dist("dist-docs", "src/doc")
.default(true)
+ .only_host_build(true)
.dep(|s| s.name("default:doc"))
.run(move |s| dist::docs(build, s.stage, s.target));
rules.dist("dist-analysis", "analysis")
.dep(|s| s.name("dist-std"))
.default(true)
+ .only_host_build(true)
.run(move |s| dist::analysis(build, &s.compiler(), s.target));
rules.dist("install", "src")
.dep(|s| s.name("default:dist"))
/// only intended for compiler hosts and not for targets that are being
/// generated.
host: bool,
+
+ /// Whether this rule is only for steps where the host is the build triple,
+ /// not anything in hosts or targets.
+ only_host_build: bool,
+
+ /// Whether this rule is only for the build triple, not anything in hosts or
+ /// targets.
+ only_build: bool,
}
#[derive(PartialEq)]
kind: kind,
default: false,
host: false,
+ only_host_build: false,
+ only_build: false,
}
}
}
self.rule.host = host;
self
}
+
+ fn only_build(&mut self, only_build: bool) -> &mut Self {
+ self.rule.only_build = only_build;
+ self
+ }
+
+ fn only_host_build(&mut self, only_host_build: bool) -> &mut Self {
+ self.rule.only_host_build = only_host_build;
+ self
+ }
}
impl<'a, 'b> Drop for RuleBuilder<'a, 'b> {
path.ends_with(rule.path)
})
}).flat_map(|rule| {
- let hosts = if self.build.flags.host.len() > 0 {
+ let hosts = if rule.only_host_build || rule.only_build {
+ &self.build.config.host[..1]
+ } else if self.build.flags.host.len() > 0 {
&self.build.flags.host
} else {
- if kind == Kind::Dist {
- // For 'dist' steps we only distribute artifacts built from
- // the build platform, so only consider that in the hosts
- // array.
- // NOTE: This relies on the fact that the build triple is
- // always placed first, as done in `config.rs`.
- &self.build.config.host[..1]
- } else {
- &self.build.config.host
- }
+ &self.build.config.host
};
let targets = if self.build.flags.target.len() > 0 {
&self.build.flags.target
&self.build.flags.host[..]
} else if self.build.flags.target.len() > 0 {
&[]
+ } else if rule.only_build {
+ &self.build.config.host[..1]
} else {
&self.build.config.host[..]
}
// Using `steps` as the top-level targets, make a topological ordering
// of what we need to do.
- let mut order = Vec::new();
- let mut added = HashSet::new();
- added.insert(Step::noop());
- for step in steps.iter().cloned() {
- self.fill(step, &mut order, &mut added);
- }
+ let order = self.expand(steps);
// Print out what we're doing for debugging
self.build.verbose("bootstrap build plan:");
}
}
+ /// From the top level targets `steps` generate a topological ordering of
+ /// all steps needed to run those steps.
+ fn expand(&self, steps: &[Step<'a>]) -> Vec<Step<'a>> {
+ let mut order = Vec::new();
+ let mut added = HashSet::new();
+ added.insert(Step::noop());
+ for step in steps.iter().cloned() {
+ self.fill(step, &mut order, &mut added);
+ }
+ return order
+ }
+
/// Performs topological sort of dependencies rooted at the `step`
/// specified, pushing all results onto the `order` vector provided.
///
order.push(step);
}
}
+
+#[cfg(test)]
+mod tests {
+ use std::env;
+
+ use Build;
+ use config::Config;
+ use flags::Flags;
+
+ macro_rules! a {
+ ($($a:expr),*) => (vec![$($a.to_string()),*])
+ }
+
+ fn build(args: &[&str],
+ extra_host: &[&str],
+ extra_target: &[&str]) -> Build {
+ let mut args = args.iter().map(|s| s.to_string()).collect::<Vec<_>>();
+ args.push("--build".to_string());
+ args.push("A".to_string());
+ let flags = Flags::parse(&args);
+
+ let mut config = Config::default();
+ config.docs = true;
+ config.build = "A".to_string();
+ config.host = vec![config.build.clone()];
+ config.host.extend(extra_host.iter().map(|s| s.to_string()));
+ config.target = config.host.clone();
+ config.target.extend(extra_target.iter().map(|s| s.to_string()));
+
+ let mut build = Build::new(flags, config);
+ let cwd = env::current_dir().unwrap();
+ build.crates.insert("std_shim".to_string(), ::Crate {
+ name: "std_shim".to_string(),
+ deps: Vec::new(),
+ path: cwd.join("src/std_shim"),
+ doc_step: "doc-std_shim".to_string(),
+ build_step: "build-crate-std_shim".to_string(),
+ test_step: "test-std_shim".to_string(),
+ bench_step: "bench-std_shim".to_string(),
+ });
+ build.crates.insert("test_shim".to_string(), ::Crate {
+ name: "test_shim".to_string(),
+ deps: Vec::new(),
+ path: cwd.join("src/test_shim"),
+ doc_step: "doc-test_shim".to_string(),
+ build_step: "build-crate-test_shim".to_string(),
+ test_step: "test-test_shim".to_string(),
+ bench_step: "bench-test_shim".to_string(),
+ });
+ build.crates.insert("rustc-main".to_string(), ::Crate {
+ name: "rustc-main".to_string(),
+ deps: Vec::new(),
+ path: cwd.join("src/rustc-main"),
+ doc_step: "doc-rustc-main".to_string(),
+ build_step: "build-crate-rustc-main".to_string(),
+ test_step: "test-rustc-main".to_string(),
+ bench_step: "bench-rustc-main".to_string(),
+ });
+ return build
+ }
+
+ #[test]
+ fn dist_baseline() {
+ let build = build(&["dist"], &[], &[]);
+ let rules = super::build_rules(&build);
+ let plan = rules.plan();
+ println!("rules: {:#?}", plan);
+ assert!(plan.iter().all(|s| s.stage == 2));
+ assert!(plan.iter().all(|s| s.host == "A" ));
+ assert!(plan.iter().all(|s| s.target == "A" ));
+
+ let step = super::Step {
+ name: "",
+ stage: 2,
+ host: &build.config.build,
+ target: &build.config.build,
+ };
+
+ assert!(plan.contains(&step.name("dist-docs")));
+ assert!(plan.contains(&step.name("dist-mingw")));
+ assert!(plan.contains(&step.name("dist-rustc")));
+ assert!(plan.contains(&step.name("dist-std")));
+ assert!(plan.contains(&step.name("dist-src")));
+ }
+
+ #[test]
+ fn dist_with_targets() {
+ let build = build(&["dist"], &[], &["B"]);
+ let rules = super::build_rules(&build);
+ let plan = rules.plan();
+ println!("rules: {:#?}", plan);
+ assert!(plan.iter().all(|s| s.stage == 2));
+ assert!(plan.iter().all(|s| s.host == "A" ));
+
+ let step = super::Step {
+ name: "",
+ stage: 2,
+ host: &build.config.build,
+ target: &build.config.build,
+ };
+
+ assert!(plan.contains(&step.name("dist-docs")));
+ assert!(plan.contains(&step.name("dist-mingw")));
+ assert!(plan.contains(&step.name("dist-rustc")));
+ assert!(plan.contains(&step.name("dist-std")));
+ assert!(plan.contains(&step.name("dist-src")));
+
+ assert!(plan.contains(&step.target("B").name("dist-docs")));
+ assert!(plan.contains(&step.target("B").name("dist-mingw")));
+ assert!(!plan.contains(&step.target("B").name("dist-rustc")));
+ assert!(plan.contains(&step.target("B").name("dist-std")));
+ assert!(!plan.contains(&step.target("B").name("dist-src")));
+ }
+
+ #[test]
+ fn dist_with_hosts() {
+ let build = build(&["dist"], &["B"], &[]);
+ let rules = super::build_rules(&build);
+ let plan = rules.plan();
+ println!("rules: {:#?}", plan);
+ assert!(plan.iter().all(|s| s.stage == 2));
+
+ let step = super::Step {
+ name: "",
+ stage: 2,
+ host: &build.config.build,
+ target: &build.config.build,
+ };
+
+ assert!(!plan.iter().any(|s| s.host == "B"));
+
+ assert!(plan.contains(&step.name("dist-docs")));
+ assert!(plan.contains(&step.name("dist-mingw")));
+ assert!(plan.contains(&step.name("dist-rustc")));
+ assert!(plan.contains(&step.name("dist-std")));
+ assert!(plan.contains(&step.name("dist-src")));
+
+ assert!(plan.contains(&step.target("B").name("dist-docs")));
+ assert!(plan.contains(&step.target("B").name("dist-mingw")));
+ assert!(plan.contains(&step.target("B").name("dist-rustc")));
+ assert!(plan.contains(&step.target("B").name("dist-std")));
+ assert!(!plan.contains(&step.target("B").name("dist-src")));
+ }
+
+ #[test]
+ fn dist_with_targets_and_hosts() {
+ let build = build(&["dist"], &["B"], &["C"]);
+ let rules = super::build_rules(&build);
+ let plan = rules.plan();
+ println!("rules: {:#?}", plan);
+ assert!(plan.iter().all(|s| s.stage == 2));
+
+ let step = super::Step {
+ name: "",
+ stage: 2,
+ host: &build.config.build,
+ target: &build.config.build,
+ };
+
+ assert!(!plan.iter().any(|s| s.host == "B"));
+ assert!(!plan.iter().any(|s| s.host == "C"));
+
+ assert!(plan.contains(&step.name("dist-docs")));
+ assert!(plan.contains(&step.name("dist-mingw")));
+ assert!(plan.contains(&step.name("dist-rustc")));
+ assert!(plan.contains(&step.name("dist-std")));
+ assert!(plan.contains(&step.name("dist-src")));
+
+ assert!(plan.contains(&step.target("B").name("dist-docs")));
+ assert!(plan.contains(&step.target("B").name("dist-mingw")));
+ assert!(plan.contains(&step.target("B").name("dist-rustc")));
+ assert!(plan.contains(&step.target("B").name("dist-std")));
+ assert!(!plan.contains(&step.target("B").name("dist-src")));
+
+ assert!(plan.contains(&step.target("C").name("dist-docs")));
+ assert!(plan.contains(&step.target("C").name("dist-mingw")));
+ assert!(!plan.contains(&step.target("C").name("dist-rustc")));
+ assert!(plan.contains(&step.target("C").name("dist-std")));
+ assert!(!plan.contains(&step.target("C").name("dist-src")));
+ }
+
+ #[test]
+ fn dist_target_with_target_flag() {
+ let build = build(&["dist", "--target=C"], &["B"], &["C"]);
+ let rules = super::build_rules(&build);
+ let plan = rules.plan();
+ println!("rules: {:#?}", plan);
+ assert!(plan.iter().all(|s| s.stage == 2));
+
+ let step = super::Step {
+ name: "",
+ stage: 2,
+ host: &build.config.build,
+ target: &build.config.build,
+ };
+
+ assert!(!plan.iter().any(|s| s.target == "A"));
+ assert!(!plan.iter().any(|s| s.target == "B"));
+ assert!(!plan.iter().any(|s| s.host == "B"));
+ assert!(!plan.iter().any(|s| s.host == "C"));
+
+ assert!(plan.contains(&step.target("C").name("dist-docs")));
+ assert!(plan.contains(&step.target("C").name("dist-mingw")));
+ assert!(!plan.contains(&step.target("C").name("dist-rustc")));
+ assert!(plan.contains(&step.target("C").name("dist-std")));
+ assert!(!plan.contains(&step.target("C").name("dist-src")));
+ }
+
+ #[test]
+ fn dist_host_with_target_flag() {
+ let build = build(&["dist", "--host=B", "--target=B"], &["B"], &["C"]);
+ let rules = super::build_rules(&build);
+ let plan = rules.plan();
+ println!("rules: {:#?}", plan);
+ assert!(plan.iter().all(|s| s.stage == 2));
+
+ let step = super::Step {
+ name: "",
+ stage: 2,
+ host: &build.config.build,
+ target: &build.config.build,
+ };
+
+ assert!(!plan.iter().any(|s| s.target == "A"));
+ assert!(!plan.iter().any(|s| s.target == "C"));
+ assert!(!plan.iter().any(|s| s.host == "B"));
+ assert!(!plan.iter().any(|s| s.host == "C"));
+
+ assert!(plan.contains(&step.target("B").name("dist-docs")));
+ assert!(plan.contains(&step.target("B").name("dist-mingw")));
+ assert!(plan.contains(&step.target("B").name("dist-rustc")));
+ assert!(plan.contains(&step.target("B").name("dist-std")));
+ assert!(plan.contains(&step.target("B").name("dist-src")));
+
+ let all = rules.expand(&plan);
+ println!("all rules: {:#?}", all);
+ assert!(!all.contains(&step.name("rustc")));
+ assert!(!all.contains(&step.name("build-crate-std_shim").stage(1)));
+ }
+
+ #[test]
+ fn build_default() {
+ let build = build(&["build"], &["B"], &["C"]);
+ let rules = super::build_rules(&build);
+ let plan = rules.plan();
+ println!("rules: {:#?}", plan);
+ assert!(plan.iter().all(|s| s.stage == 2));
+
+ let step = super::Step {
+ name: "",
+ stage: 2,
+ host: &build.config.build,
+ target: &build.config.build,
+ };
+
+ // rustc built for all for of (A, B) x (A, B)
+ assert!(plan.contains(&step.name("librustc")));
+ assert!(plan.contains(&step.target("B").name("librustc")));
+ assert!(plan.contains(&step.host("B").target("A").name("librustc")));
+ assert!(plan.contains(&step.host("B").target("B").name("librustc")));
+
+ // rustc never built for C
+ assert!(!plan.iter().any(|s| {
+ s.name.contains("rustc") && (s.host == "C" || s.target == "C")
+ }));
+
+ // test built for everything
+ assert!(plan.contains(&step.name("libtest")));
+ assert!(plan.contains(&step.target("B").name("libtest")));
+ assert!(plan.contains(&step.host("B").target("A").name("libtest")));
+ assert!(plan.contains(&step.host("B").target("B").name("libtest")));
+ assert!(plan.contains(&step.host("A").target("C").name("libtest")));
+ assert!(plan.contains(&step.host("B").target("C").name("libtest")));
+
+ let all = rules.expand(&plan);
+ println!("all rules: {:#?}", all);
+ assert!(all.contains(&step.name("rustc")));
+ assert!(all.contains(&step.name("libstd")));
+ }
+
+ #[test]
+ fn build_filtered() {
+ let build = build(&["build", "--target=C"], &["B"], &["C"]);
+ let rules = super::build_rules(&build);
+ let plan = rules.plan();
+ println!("rules: {:#?}", plan);
+ assert!(plan.iter().all(|s| s.stage == 2));
+
+ assert!(!plan.iter().any(|s| s.name.contains("rustc")));
+ assert!(plan.iter().all(|s| {
+ !s.name.contains("test_shim") || s.target == "C"
+ }));
+ }
+
+ #[test]
+ fn test_default() {
+ let build = build(&["test"], &[], &[]);
+ let rules = super::build_rules(&build);
+ let plan = rules.plan();
+ println!("rules: {:#?}", plan);
+ assert!(plan.iter().all(|s| s.stage == 2));
+ assert!(plan.iter().all(|s| s.host == "A"));
+ assert!(plan.iter().all(|s| s.target == "A"));
+
+ assert!(plan.iter().any(|s| s.name.contains("-ui")));
+ assert!(plan.iter().any(|s| s.name.contains("cfail")));
+ assert!(plan.iter().any(|s| s.name.contains("cfail")));
+ assert!(plan.iter().any(|s| s.name.contains("cfail-full")));
+ assert!(plan.iter().any(|s| s.name.contains("codegen-units")));
+ assert!(plan.iter().any(|s| s.name.contains("debuginfo")));
+ assert!(plan.iter().any(|s| s.name.contains("docs")));
+ assert!(plan.iter().any(|s| s.name.contains("error-index")));
+ assert!(plan.iter().any(|s| s.name.contains("incremental")));
+ assert!(plan.iter().any(|s| s.name.contains("linkchecker")));
+ assert!(plan.iter().any(|s| s.name.contains("mir-opt")));
+ assert!(plan.iter().any(|s| s.name.contains("pfail")));
+ assert!(plan.iter().any(|s| s.name.contains("rfail")));
+ assert!(plan.iter().any(|s| s.name.contains("rfail-full")));
+ assert!(plan.iter().any(|s| s.name.contains("rmake")));
+ assert!(plan.iter().any(|s| s.name.contains("rpass")));
+ assert!(plan.iter().any(|s| s.name.contains("rpass-full")));
+ assert!(plan.iter().any(|s| s.name.contains("rustc-all")));
+ assert!(plan.iter().any(|s| s.name.contains("rustdoc")));
+ assert!(plan.iter().any(|s| s.name.contains("std-all")));
+ assert!(plan.iter().any(|s| s.name.contains("test-all")));
+ assert!(plan.iter().any(|s| s.name.contains("tidy")));
+ assert!(plan.iter().any(|s| s.name.contains("valgrind")));
+ }
+
+ #[test]
+ fn test_with_a_target() {
+ let build = build(&["test", "--target=C"], &[], &["C"]);
+ let rules = super::build_rules(&build);
+ let plan = rules.plan();
+ println!("rules: {:#?}", plan);
+ assert!(plan.iter().all(|s| s.stage == 2));
+ assert!(plan.iter().all(|s| s.host == "A"));
+ assert!(plan.iter().all(|s| s.target == "C"));
+
+ assert!(plan.iter().any(|s| s.name.contains("-ui")));
+ assert!(plan.iter().any(|s| s.name.contains("cfail")));
+ assert!(plan.iter().any(|s| s.name.contains("cfail")));
+ assert!(!plan.iter().any(|s| s.name.contains("cfail-full")));
+ assert!(plan.iter().any(|s| s.name.contains("codegen-units")));
+ assert!(plan.iter().any(|s| s.name.contains("debuginfo")));
+ assert!(!plan.iter().any(|s| s.name.contains("docs")));
+ assert!(!plan.iter().any(|s| s.name.contains("error-index")));
+ assert!(plan.iter().any(|s| s.name.contains("incremental")));
+ assert!(!plan.iter().any(|s| s.name.contains("linkchecker")));
+ assert!(plan.iter().any(|s| s.name.contains("mir-opt")));
+ assert!(plan.iter().any(|s| s.name.contains("pfail")));
+ assert!(plan.iter().any(|s| s.name.contains("rfail")));
+ assert!(!plan.iter().any(|s| s.name.contains("rfail-full")));
+ assert!(!plan.iter().any(|s| s.name.contains("rmake")));
+ assert!(plan.iter().any(|s| s.name.contains("rpass")));
+ assert!(!plan.iter().any(|s| s.name.contains("rpass-full")));
+ assert!(!plan.iter().any(|s| s.name.contains("rustc-all")));
+ assert!(!plan.iter().any(|s| s.name.contains("rustdoc")));
+ assert!(plan.iter().any(|s| s.name.contains("std-all")));
+ assert!(plan.iter().any(|s| s.name.contains("test-all")));
+ assert!(!plan.iter().any(|s| s.name.contains("tidy")));
+ assert!(plan.iter().any(|s| s.name.contains("valgrind")));
+ }
+}
--- /dev/null
+FROM ubuntu:16.04
+
+RUN apt-get update && apt-get install -y --no-install-recommends \
+ g++ \
+ make \
+ file \
+ curl \
+ ca-certificates \
+ python2.7 \
+ git \
+ cmake \
+ sudo \
+ gdb \
+ xz-utils \
+ g++-arm-linux-gnueabi
+
+ENV SCCACHE_DIGEST=7237e38e029342fa27b7ac25412cb9d52554008b12389727320bd533fd7f05b6a96d55485f305caf95e5c8f5f97c3313e10012ccad3e752aba2518f3522ba783
+RUN curl -L https://api.pub.build.mozilla.org/tooltool/sha512/$SCCACHE_DIGEST | \
+ tar xJf - -C /usr/local/bin --strip-components=1
+
+RUN curl -OL https://github.com/Yelp/dumb-init/releases/download/v1.2.0/dumb-init_1.2.0_amd64.deb && \
+ dpkg -i dumb-init_*.deb && \
+ rm dumb-init_*.deb
+ENTRYPOINT ["/usr/bin/dumb-init", "--"]
+
+ENV RUST_CONFIGURE_ARGS --host=arm-unknown-linux-gnueabi
+ENV XPY_RUN \
+ dist \
+ --host arm-unknown-linux-gnueabi \
+ --target arm-unknown-linux-gnueabi
--- /dev/null
+FROM ubuntu:16.04
+
+RUN apt-get update && apt-get install -y --no-install-recommends \
+ g++ \
+ make \
+ file \
+ curl \
+ ca-certificates \
+ python2.7 \
+ git \
+ cmake \
+ sudo \
+ bzip2 \
+ xz-utils \
+ wget
+
+COPY build-toolchain.sh /tmp/
+RUN sh /tmp/build-toolchain.sh
+
+RUN curl -OL https://github.com/Yelp/dumb-init/releases/download/v1.2.0/dumb-init_1.2.0_amd64.deb && \
+ dpkg -i dumb-init_*.deb && \
+ rm dumb-init_*.deb
+ENTRYPOINT ["/usr/bin/dumb-init", "--"]
+
+ENV SCCACHE_DIGEST=7237e38e029342fa27b7ac25412cb9d52554008b12389727320bd533fd7f05b6a96d55485f305caf95e5c8f5f97c3313e10012ccad3e752aba2518f3522ba783
+RUN curl -L https://api.pub.build.mozilla.org/tooltool/sha512/$SCCACHE_DIGEST | \
+ tar xJf - -C /usr/local/bin --strip-components=1
+
+ENV \
+ AR_x86_64_unknown_freebsd=x86_64-unknown-freebsd10-ar \
+ CC_x86_64_unknown_freebsd=x86_64-unknown-freebsd10-gcc \
+ CXX_x86_64_unknown_freebsd=x86_64-unknown-freebsd10-g++
+
+ENV RUST_CONFIGURE_ARGS --host=x86_64-unknown-freebsd
+ENV XPY_RUN \
+ dist \
+ --host x86_64-unknown-freebsd \
+ --target x86_64-unknown-freebsd
--- /dev/null
+#!/bin/bash
+# Copyright 2016 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.
+
+set -ex
+
+ARCH=x86_64
+BINUTILS=2.25.1
+GCC=5.3.0
+
+mkdir binutils
+cd binutils
+
+# First up, build binutils
+curl https://ftp.gnu.org/gnu/binutils/binutils-$BINUTILS.tar.bz2 | tar xjf -
+mkdir binutils-build
+cd binutils-build
+../binutils-$BINUTILS/configure \
+ --target=$ARCH-unknown-freebsd10
+make -j10
+make install
+cd ../..
+rm -rf binutils
+
+# Next, download the FreeBSD libc and relevant header files
+
+mkdir freebsd
+case "$ARCH" in
+ x86_64)
+ URL=ftp://ftp.freebsd.org/pub/FreeBSD/releases/amd64/10.2-RELEASE/base.txz
+ ;;
+ i686)
+ URL=ftp://ftp.freebsd.org/pub/FreeBSD/releases/i386/10.2-RELEASE/base.txz
+ ;;
+esac
+curl $URL | tar xJf - -C freebsd ./usr/include ./usr/lib ./lib
+
+dst=/usr/local/$ARCH-unknown-freebsd10
+
+cp -r freebsd/usr/include $dst/
+cp freebsd/usr/lib/crt1.o $dst/lib
+cp freebsd/usr/lib/Scrt1.o $dst/lib
+cp freebsd/usr/lib/crti.o $dst/lib
+cp freebsd/usr/lib/crtn.o $dst/lib
+cp freebsd/usr/lib/libc.a $dst/lib
+cp freebsd/usr/lib/libutil.a $dst/lib
+cp freebsd/usr/lib/libutil_p.a $dst/lib
+cp freebsd/usr/lib/libm.a $dst/lib
+cp freebsd/usr/lib/librt.so.1 $dst/lib
+cp freebsd/usr/lib/libexecinfo.so.1 $dst/lib
+cp freebsd/lib/libc.so.7 $dst/lib
+cp freebsd/lib/libm.so.5 $dst/lib
+cp freebsd/lib/libutil.so.9 $dst/lib
+cp freebsd/lib/libthr.so.3 $dst/lib/libpthread.so
+
+ln -s libc.so.7 $dst/lib/libc.so
+ln -s libm.so.5 $dst/lib/libm.so
+ln -s librt.so.1 $dst/lib/librt.so
+ln -s libutil.so.9 $dst/lib/libutil.so
+ln -s libexecinfo.so.1 $dst/lib/libexecinfo.so
+rm -rf freebsd
+
+# Finally, download and build gcc to target FreeBSD
+mkdir gcc
+cd gcc
+curl https://ftp.gnu.org/gnu/gcc/gcc-$GCC/gcc-$GCC.tar.bz2 | tar xjf -
+cd gcc-$GCC
+./contrib/download_prerequisites
+
+mkdir ../gcc-build
+cd ../gcc-build
+../gcc-$GCC/configure \
+ --enable-languages=c,c++ \
+ --target=$ARCH-unknown-freebsd10 \
+ --disable-multilib \
+ --disable-nls \
+ --disable-libgomp \
+ --disable-libquadmath \
+ --disable-libssp \
+ --disable-libvtv \
+ --disable-libcilkrts \
+ --disable-libada \
+ --disable-libsanitizer \
+ --disable-libquadmath-support \
+ --disable-lto
+make -j10
+make install
+cd ../..
+rm -rf gcc
+++ /dev/null
-FROM ubuntu:16.04
-
-RUN apt-get update && apt-get install -y --no-install-recommends \
- g++ \
- make \
- file \
- curl \
- ca-certificates \
- python2.7 \
- git \
- cmake \
- sudo \
- bzip2 \
- xz-utils \
- wget
-
-COPY build-toolchain.sh /tmp/
-RUN sh /tmp/build-toolchain.sh
-
-RUN curl -OL https://github.com/Yelp/dumb-init/releases/download/v1.2.0/dumb-init_1.2.0_amd64.deb && \
- dpkg -i dumb-init_*.deb && \
- rm dumb-init_*.deb
-ENTRYPOINT ["/usr/bin/dumb-init", "--"]
-
-ENV SCCACHE_DIGEST=7237e38e029342fa27b7ac25412cb9d52554008b12389727320bd533fd7f05b6a96d55485f305caf95e5c8f5f97c3313e10012ccad3e752aba2518f3522ba783
-RUN curl -L https://api.pub.build.mozilla.org/tooltool/sha512/$SCCACHE_DIGEST | \
- tar xJf - -C /usr/local/bin --strip-components=1
-
-ENV \
- AR_x86_64_unknown_freebsd=x86_64-unknown-freebsd10-ar \
- CC_x86_64_unknown_freebsd=x86_64-unknown-freebsd10-gcc
-
-ENV RUST_CONFIGURE_ARGS --target=x86_64-unknown-freebsd
-ENV RUST_CHECK_TARGET ""
+++ /dev/null
-#!/bin/bash
-# Copyright 2016 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.
-
-set -ex
-
-ARCH=x86_64
-BINUTILS=2.25.1
-GCC=5.3.0
-
-mkdir binutils
-cd binutils
-
-# First up, build binutils
-curl https://ftp.gnu.org/gnu/binutils/binutils-$BINUTILS.tar.bz2 | tar xjf -
-mkdir binutils-build
-cd binutils-build
-../binutils-$BINUTILS/configure \
- --target=$ARCH-unknown-freebsd10
-make -j10
-make install
-cd ../..
-rm -rf binutils
-
-# Next, download the FreeBSD libc and relevant header files
-
-mkdir freebsd
-case "$ARCH" in
- x86_64)
- URL=ftp://ftp.freebsd.org/pub/FreeBSD/releases/amd64/10.2-RELEASE/base.txz
- ;;
- i686)
- URL=ftp://ftp.freebsd.org/pub/FreeBSD/releases/i386/10.2-RELEASE/base.txz
- ;;
-esac
-curl $URL | tar xJf - -C freebsd ./usr/include ./usr/lib ./lib
-
-dst=/usr/local/$ARCH-unknown-freebsd10
-
-cp -r freebsd/usr/include $dst/
-cp freebsd/usr/lib/crt1.o $dst/lib
-cp freebsd/usr/lib/Scrt1.o $dst/lib
-cp freebsd/usr/lib/crti.o $dst/lib
-cp freebsd/usr/lib/crtn.o $dst/lib
-cp freebsd/usr/lib/libc.a $dst/lib
-cp freebsd/usr/lib/libutil.a $dst/lib
-cp freebsd/usr/lib/libutil_p.a $dst/lib
-cp freebsd/usr/lib/libm.a $dst/lib
-cp freebsd/usr/lib/librt.so.1 $dst/lib
-cp freebsd/usr/lib/libexecinfo.so.1 $dst/lib
-cp freebsd/lib/libc.so.7 $dst/lib
-cp freebsd/lib/libm.so.5 $dst/lib
-cp freebsd/lib/libutil.so.9 $dst/lib
-cp freebsd/lib/libthr.so.3 $dst/lib/libpthread.so
-
-ln -s libc.so.7 $dst/lib/libc.so
-ln -s libm.so.5 $dst/lib/libm.so
-ln -s librt.so.1 $dst/lib/librt.so
-ln -s libutil.so.9 $dst/lib/libutil.so
-ln -s libexecinfo.so.1 $dst/lib/libexecinfo.so
-rm -rf freebsd
-
-# Finally, download and build gcc to target FreeBSD
-mkdir gcc
-cd gcc
-curl https://ftp.gnu.org/gnu/gcc/gcc-$GCC/gcc-$GCC.tar.bz2 | tar xjf -
-cd gcc-$GCC
-./contrib/download_prerequisites
-
-mkdir ../gcc-build
-cd ../gcc-build
-../gcc-$GCC/configure \
- --enable-languages=c \
- --target=$ARCH-unknown-freebsd10 \
- --disable-multilib \
- --disable-nls \
- --disable-libgomp \
- --disable-libquadmath \
- --disable-libssp \
- --disable-libvtv \
- --disable-libcilkrts \
- --disable-libada \
- --disable-libsanitizer \
- --disable-libquadmath-support \
- --disable-lto
-make -j10
-make install
-cd ../..
-rm -rf gcc
* [Borrow and AsRef](borrow-and-asref.md)
* [Release Channels](release-channels.md)
* [Using Rust without the standard library](using-rust-without-the-standard-library.md)
+ * [Procedural Macros (and custom derive)](procedural-macros.md)
* [Nightly Rust](nightly-rust.md)
* [Compiler Plugins](compiler-plugins.md)
* [Inline Assembly](inline-assembly.md)
--- /dev/null
+% Procedural Macros (and custom Derive)
+
+As you've seen throughout the rest of the book, Rust provides a mechanism
+called "derive" that lets you implement traits easily. For example,
+
+```rust
+#[derive(Debug)]
+struct Point {
+ x: i32,
+ y: i32,
+}
+```
+
+is a lot simpler than
+
+```rust
+struct Point {
+ x: i32,
+ y: i32,
+}
+
+use std::fmt;
+
+impl fmt::Debug for Point {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "Point {{ x: {}, y: {} }}", self.x, self.y)
+ }
+}
+```
+
+Rust includes several traits that you can derive, but it also lets you define
+your own. We can accomplish this task through a feature of Rust called
+"procedural macros." Eventually, procedural macros will allow for all sorts of
+advanced metaprogramming in Rust, but today, they're only for custom derive.
+
+Let's build a very simple trait, and derive it with custom derive.
+
+## Hello World
+
+So the first thing we need to do is start a new crate for our project.
+
+```bash
+$ cargo new --bin hello-world
+```
+
+All we want is to be able to call `hello_world()` on a derived type. Something
+like this:
+
+```rust,ignore
+#[derive(HelloWorld)]
+struct Pancakes;
+
+fn main() {
+ Pancakes::hello_world();
+}
+```
+
+With some kind of nice output, like `Hello, World! My name is Pancakes.`.
+
+Let's go ahead and write up what we think our macro will look like from a user
+perspective. In `src/main.rs` we write:
+
+```rust,ignore
+#[macro_use]
+extern crate hello_world_derive;
+
+trait HelloWorld {
+ fn hello_world();
+}
+
+#[derive(HelloWorld)]
+struct FrenchToast;
+
+#[derive(HelloWorld)]
+struct Waffles;
+
+fn main() {
+ FrenchToast::hello_world();
+ Waffles::hello_world();
+}
+```
+
+Great. So now we just need to actually write the procedural macro. At the
+moment, procedural macros need to be in their own crate. Eventually, this
+restriction may be lifted, but for now, it's required. As such, there's a
+convention; for a crate named `foo`, a custom derive procedural macro is called
+`foo-derive`. Let's start a new crate called `hello-world-derive` inside our
+`hello-world` project.
+
+```bash
+$ cargo new hello-world-derive
+```
+
+To make sure that our `hello-world` crate is able to find this new crate we've
+created, we'll add it to our toml:
+
+```toml
+[dependencies]
+hello-world-derive = { path = "hello-world-derive" }
+```
+
+As for our the source of our `hello-world-derive` crate, here's an example:
+
+```rust,ignore
+extern crate proc_macro;
+extern crate syn;
+#[macro_use]
+extern crate quote;
+
+use proc_macro::TokenStream;
+
+#[proc_macro_derive(HelloWorld)]
+pub fn hello_world(input: TokenStream) -> TokenStream {
+ // Construct a string representation of the type definition
+ let s = input.to_string();
+
+ // Parse the string representation
+ let ast = syn::parse_macro_input(&s).unwrap();
+
+ // Build the impl
+ let gen = impl_hello_world(&ast);
+
+ // Return the generated impl
+ gen.parse().unwrap()
+}
+```
+
+So there is a lot going on here. We have introduced two new crates: [`syn`] and
+[`quote`]. As you may have noticed, `input: TokenSteam` is immediately converted
+to a `String`. This `String` is a string representation of the Rust code for which
+we are deriving `HelloWorld` for. At the moment, the only thing you can do with a
+`TokenStream` is convert it to a string. A richer API will exist in the future.
+
+So what we really need is to be able to _parse_ Rust code into something
+usable. This is where `syn` comes to play. `syn` is a crate for parsing Rust
+code. The other crate we've introduced is `quote`. It's essentially the dual of
+`syn` as it will make generating Rust code really easy. We could write this
+stuff on our own, but it's much simpler to use these libraries. Writing a full
+parser for Rust code is no simple task.
+
+[`syn`]: https://crates.io/crates/syn
+[`quote`]: https://crates.io/crates/quote
+
+The comments seem to give us a pretty good idea of our overall strategy. We
+are going to take a `String` of the Rust code for the type we are deriving, parse
+it using `syn`, construct the implementation of `hello_world` (using `quote`),
+then pass it back to Rust compiler.
+
+One last note: you'll see some `unwrap()`s there. If you want to provide an
+error for a procedural macro, then you should `panic!` with the error message.
+In this case, we're keeping it as simple as possible.
+
+Great, so let's write `impl_hello_world(&ast)`.
+
+```rust,ignore
+fn impl_hello_world(ast: &syn::MacroInput) -> quote::Tokens {
+ let name = &ast.ident;
+ quote! {
+ impl HelloWorld for #name {
+ fn hello_world() {
+ println!("Hello, World! My name is {}", stringify!(#name));
+ }
+ }
+ }
+}
+```
+
+So this is where quotes comes in. The `ast` argument is a struct that gives us
+a representation of our type (which can be either a `struct` or an `enum`).
+Check out the [docs](https://docs.rs/syn/0.10.5/syn/struct.MacroInput.html),
+there is some useful information there. We are able to get the name of the
+type using `ast.ident`. The `quote!` macro let's us write up the Rust code
+that we wish to return and convert it into `Tokens`. `quote!` let's us use some
+really cool templating mechanics; we simply write `#name` and `quote!` will
+replace it with the variable named `name`. You can even do some repetition
+similar to regular macros work. You should check out the
+[docs](https://docs.rs/quote) for a good introduction.
+
+So I think that's it. Oh, well, we do need to add dependencies for `syn` and
+`quote` in the `cargo.toml` for `hello-world-derive`.
+
+```toml
+[dependencies]
+syn = "0.10.5"
+quote = "0.3.10"
+```
+
+That should be it. Let's try to compile `hello-world`.
+
+```bash
+error: the `#[proc_macro_derive]` attribute is only usable with crates of the `proc-macro` crate type
+ --> hello-world-derive/src/lib.rs:8:3
+ |
+8 | #[proc_macro_derive(HelloWorld)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+```
+
+Oh, so it appears that we need to declare that our `hello-world-derive` crate is
+a `proc-macro` crate type. How do we do this? Like this:
+
+```toml
+[lib]
+proc-macro = true
+```
+
+Ok so now, let's compile `hello-world`. Executing `cargo run` now yields:
+
+```bash
+Hello, World! My name is FrenchToast
+Hello, World! My name is Waffles
+```
+
+We've done it!
with this error:
```text
-thread 'main' panicked at 'index 0 and/or 2 in `忠犬ハチ公` do not lie on
-character boundary'
+thread 'main' panicked at 'byte index 2 is not a char boundary; it is inside '忠'
+(bytes 0..3) of `忠犬ハチ公`'
```
## Concatenation
# Compiler Reordering
-Compilers fundamentally want to be able to do all sorts of crazy transformations
-to reduce data dependencies and eliminate dead code. In particular, they may
-radically change the actual order of events, or make events never occur! If we
-write something like
+Compilers fundamentally want to be able to do all sorts of complicated
+transformations to reduce data dependencies and eliminate dead code. In
+particular, they may radically change the actual order of events, or make events
+never occur! If we write something like
```rust,ignore
x = 1;
Rust can be thought of as being composed of two programming languages: *Safe
Rust* and *Unsafe Rust*. Safe Rust is For Reals Totally Safe. Unsafe Rust,
unsurprisingly, is *not* For Reals Totally Safe. In fact, Unsafe Rust lets you
-do some really crazy unsafe things.
+do some really, *really* unsafe things.
Safe Rust is the *true* Rust programming language. If all you do is write Safe
Rust, you will never have to worry about type-safety or memory-safety. You will
incorrect.
So it's perfectly "fine" for a Safe Rust program to get deadlocked or do
-something incredibly stupid with incorrect synchronization. Obviously such a
-program isn't very good, but Rust can only hold your hand so far. Still, a
-race condition can't violate memory safety in a Rust program on
-its own. Only in conjunction with some other unsafe code can a race condition
-actually violate memory safety. For instance:
+something nonsensical with incorrect synchronization. Obviously such a program
+isn't very good, but Rust can only hold your hand so far. Still, a race
+condition can't violate memory safety in a Rust program on its own. Only in
+conjunction with some other unsafe code can a race condition actually violate
+memory safety. For instance:
```rust,no_run
use std::thread;
# fn main() {}
```
-# Syntax extensions
+# Macros
A number of minor features of Rust are not central enough to have their own
syntax, and yet are not implementable as functions. Instead, they are given
names, and invoked through a consistent syntax: `some_extension!(...)`.
-Users of `rustc` can define new syntax extensions in two ways:
-
-* [Compiler plugins][plugin] can include arbitrary Rust code that
- manipulates syntax trees at compile time. Note that the interface
- for compiler plugins is considered highly unstable.
+Users of `rustc` can define new macros in two ways:
* [Macros](book/macros.html) define new syntax in a higher-level,
declarative way.
+* [Procedural Macros][procedural macros] can be used to implement custom derive.
+
+And one unstable way: [compiler plugins][plugin].
## Macros
`macro_rules` allows users to define syntax extension in a declarative way. We
-call such extensions "macros by example" or simply "macros" — to be distinguished
-from the "procedural macros" defined in [compiler plugins][plugin].
+call such extensions "macros by example" or simply "macros".
Currently, macros can expand to expressions, statements, items, or patterns.
[RFC 550]: https://github.com/rust-lang/rfcs/blob/master/text/0550-macro-future-proofing.md
+## Procedrual Macros
+
+"Procedrual macros" are the second way to implement a macro. For now, the only
+thing they can be used for is to implement derive on your own types. See
+[the book][procedural macros] for a tutorial.
+
+Procedural macros involve a few different parts of the language and its
+standard libraries. First is the `proc_macro` crate, included with Rust,
+that defines an interface for building a procedrual macro. The
+`#[proc_macro_derive(Foo)]` attribute is used to mark the the deriving
+function. This function must have the type signature:
+
+```rust,ignore
+use proc_macro::TokenStream;
+
+#[proc_macro_derive(Hello)]
+pub fn hello_world(input: TokenStream) -> TokenStream
+```
+
+Finally, procedural macros must be in their own crate, with the `proc-macro`
+crate type.
+
# Crates and source files
Although Rust, like any other language, can be implemented by an interpreter as
}
```
+You can implement `derive` for your own type through [procedural
+macros](#procedural-macros).
+
### Compiler Features
Certain aspects of Rust may be implemented in the compiler, but they're not
in dynamic libraries. This form of output is used to produce statically linked
executables as well as `staticlib` outputs.
+* `--crate-type=proc-macro`, `#[crate_type = "proc-macro"]` - The output
+ produced is not specified, but if a `-L` path is provided to it then the
+ compiler will recognize the output artifacts as a macro and it can be loaded
+ for a program. If a crate is compiled with the `proc-macro` crate type it
+ will forbid exporting any items in the crate other than those functions
+ tagged `#[proc_macro_derive]` and those functions must also be placed at the
+ crate root. Finally, the compiler will automatically set the
+ `cfg(proc_macro)` annotation whenever any crate type of a compilation is the
+ `proc-macro` crate type.
+
Note that these outputs are stackable in the sense that if multiple are
specified, then the compiler will produce each form of output at once without
having to recompile. However, this only applies for outputs specified by the
[ffi]: book/ffi.html
[plugin]: book/compiler-plugins.html
+[procedural macros]: book/procedural-macros.html
TYPE_KIND_CSTYLE_ENUM = 14
TYPE_KIND_PTR = 15
TYPE_KIND_FIXED_SIZE_VEC = 16
+TYPE_KIND_REGULAR_UNION = 17
ENCODED_ENUM_PREFIX = "RUST$ENCODED$ENUM$"
ENUM_DISR_FIELD_NAME = "RUST$ENUM$DISR"
union_member_count = len(union_members)
if union_member_count == 0:
return TYPE_KIND_EMPTY
- elif union_member_count == 1:
- first_variant_name = union_members[0].name
- if first_variant_name is None:
+
+ first_variant_name = union_members[0].name
+ if first_variant_name is None:
+ if union_member_count == 1:
return TYPE_KIND_SINGLETON_ENUM
else:
- assert first_variant_name.startswith(ENCODED_ENUM_PREFIX)
- return TYPE_KIND_COMPRESSED_ENUM
+ return TYPE_KIND_REGULAR_ENUM
+ elif first_variant_name.startswith(ENCODED_ENUM_PREFIX):
+ assert union_member_count == 1
+ return TYPE_KIND_COMPRESSED_ENUM
else:
- return TYPE_KIND_REGULAR_ENUM
+ return TYPE_KIND_REGULAR_UNION
def __conforms_to_field_layout(self, expected_fields):
type_kind = val.type.get_type_kind()
if (type_kind == rustpp.TYPE_KIND_REGULAR_STRUCT or
+ type_kind == rustpp.TYPE_KIND_REGULAR_UNION or
type_kind == rustpp.TYPE_KIND_EMPTY):
return print_struct_val(val,
internal_dict,
Prints a struct, tuple, or tuple struct value with Rust syntax.
Ignores any fields before field_start_index.
"""
- assert val.type.get_dwarf_type_kind() == rustpp.DWARF_TYPE_CODE_STRUCT
+ assert (val.type.get_dwarf_type_kind() == rustpp.DWARF_TYPE_CODE_STRUCT or
+ val.type.get_dwarf_type_kind() == rustpp.DWARF_TYPE_CODE_UNION)
if omit_type_name:
type_name = ""
// check the panic includes the prefix of the sliced string
#[test]
-#[should_panic(expected="Lorem ipsum dolor sit amet")]
+#[should_panic(expected="byte index 1024 is out of bounds of `Lorem ipsum dolor sit amet")]
fn test_slice_fail_truncated_1() {
&LOREM_PARAGRAPH[..1024];
}
// check the truncation in the panic message
#[test]
-#[should_panic(expected="luctus, im`[...] do not lie on character boundary")]
+#[should_panic(expected="luctus, im`[...]")]
fn test_slice_fail_truncated_2() {
&LOREM_PARAGRAPH[..1024];
}
+#[test]
+#[should_panic(expected="byte index 4 is not a char boundary; it is inside 'α' (bytes 3..5) of")]
+fn test_slice_fail_boundary_1() {
+ &"abcαβγ"[4..];
+}
+
+#[test]
+#[should_panic(expected="byte index 6 is not a char boundary; it is inside 'β' (bytes 5..7) of")]
+fn test_slice_fail_boundary_2() {
+ &"abcαβγ"[2..6];
+}
+
#[test]
fn test_slice_from() {
assert_eq!(&"abcd"[0..], "abcd");
/// Generalizing the latter case, any type implementing [`Drop`] can't be `Copy`, because it's
/// managing some resource besides its own [`size_of::<T>()`] bytes.
///
-/// If you try to implement `Copy` on a struct or enum containing non-`Copy` data, you will get a
-/// compile-time error. Specifically, with structs you'll get [E0204] and with enums you'll get
-/// [E0205].
+/// If you try to implement `Copy` on a struct or enum containing non-`Copy` data, you will get
+/// the error [E0204].
///
/// [E0204]: ../../error-index.html#E0204
-/// [E0205]: ../../error-index.html#E0205
///
/// ## When *should* my type be `Copy`?
///
#[cold]
fn slice_error_fail(s: &str, begin: usize, end: usize) -> ! {
const MAX_DISPLAY_LENGTH: usize = 256;
- let (truncated, s) = truncate_to_char_boundary(s, MAX_DISPLAY_LENGTH);
+ let (truncated, s_trunc) = truncate_to_char_boundary(s, MAX_DISPLAY_LENGTH);
let ellipsis = if truncated { "[...]" } else { "" };
+ // 1. out of bounds
+ if begin > s.len() || end > s.len() {
+ let oob_index = if begin > s.len() { begin } else { end };
+ panic!("byte index {} is out of bounds of `{}`{}", oob_index, s_trunc, ellipsis);
+ }
+
+ // 2. begin <= end
assert!(begin <= end, "begin <= end ({} <= {}) when slicing `{}`{}",
- begin, end, s, ellipsis);
- panic!("index {} and/or {} in `{}`{} do not lie on character boundary",
- begin, end, s, ellipsis);
+ begin, end, s_trunc, ellipsis);
+
+ // 3. character boundary
+ let index = if !s.is_char_boundary(begin) { begin } else { end };
+ // find the character
+ let mut char_start = index;
+ while !s.is_char_boundary(char_start) {
+ char_start -= 1;
+ }
+ // `char_start` must be less than len and a char boundary
+ let ch = s[char_start..].chars().next().unwrap();
+ let char_range = char_start .. char_start + ch.len_utf8();
+ panic!("byte index {} is not a char boundary; it is inside {:?} (bytes {:?}) of `{}`{}",
+ index, ch, char_range, s_trunc, ellipsis);
}
#[stable(feature = "core", since = "1.6.0")]
-Subproject commit 98589876259e19f13eab81b033ced95bbb6deca0
+Subproject commit 7d57bdcdbb56540f37afe5a934ce12d33a6ca7fc
//! Currently the primary use of this crate is to provide the ability to define
//! new custom derive modes through `#[proc_macro_derive]`.
//!
-//! Added recently as part of [RFC 1681] this crate is currently *unstable* and
-//! requires the `#![feature(proc_macro_lib)]` directive to use.
-//!
-//! [RFC 1681]: https://github.com/rust-lang/rfcs/blob/master/text/1681-macros-1.1.md
-//!
//! Note that this crate is intentionally very bare-bones currently. The main
//! type, `TokenStream`, only supports `fmt::Display` and `FromStr`
//! implementations, indicating that it can only go to and come from a string.
//! This functionality is intended to be expanded over time as more surface
//! area for macro authors is stabilized.
+//!
+//! See [the book](../../book/procedural-macros.html) for more.
#![crate_name = "proc_macro"]
-#![unstable(feature = "proc_macro_lib", issue = "27812")]
+#![stable(feature = "proc_macro_lib", since = "1.15.0")]
#![crate_type = "rlib"]
#![crate_type = "dylib"]
#![cfg_attr(not(stage0), deny(warnings))]
///
/// The API of this type is intentionally bare-bones, but it'll be expanded over
/// time!
+#[stable(feature = "proc_macro_lib", since = "1.15.0")]
pub struct TokenStream {
inner: Vec<P<ast::Item>>,
}
/// Error returned from `TokenStream::from_str`.
#[derive(Debug)]
+#[stable(feature = "proc_macro_lib", since = "1.15.0")]
pub struct LexError {
_inner: (),
}
}
}
+#[stable(feature = "proc_macro_lib", since = "1.15.0")]
impl FromStr for TokenStream {
type Err = LexError;
}
}
+#[stable(feature = "proc_macro_lib", since = "1.15.0")]
impl fmt::Display for TokenStream {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
for item in self.inner.iter() {
}
}
+ fn trait_item_scope_tag(item: &hir::TraitItem) -> &'static str {
+ match item.node {
+ hir::TraitItemKind::Method(..) => "method body",
+ hir::TraitItemKind::Const(..) |
+ hir::TraitItemKind::Type(..) => "associated item"
+ }
+ }
+
+ fn impl_item_scope_tag(item: &hir::ImplItem) -> &'static str {
+ match item.node {
+ hir::ImplItemKind::Method(..) => "method body",
+ hir::ImplItemKind::Const(..) |
+ hir::ImplItemKind::Type(_) => "associated item"
+ }
+ }
+
fn explain_span<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
heading: &str, span: Span)
-> (String, Option<Span>) {
},
Some(ast_map::NodeStmt(_)) => "statement",
Some(ast_map::NodeItem(it)) => item_scope_tag(&it),
+ Some(ast_map::NodeTraitItem(it)) => trait_item_scope_tag(&it),
+ Some(ast_map::NodeImplItem(it)) => impl_item_scope_tag(&it),
Some(_) | None => {
err.span_note(span, &unknown_scope());
return;
}
};
- match self.map.find(fr.scope.node_id(&self.region_maps)) {
- Some(ast_map::NodeBlock(ref blk)) => {
- let (msg, opt_span) = explain_span(self, "block", blk.span);
- (format!("{} {}", prefix, msg), opt_span)
- }
- Some(ast_map::NodeItem(it)) => {
- let tag = item_scope_tag(&it);
- let (msg, opt_span) = explain_span(self, tag, it.span);
- (format!("{} {}", prefix, msg), opt_span)
+ let node = fr.scope.node_id(&self.region_maps);
+ let unknown;
+ let tag = match self.map.find(node) {
+ Some(ast_map::NodeBlock(_)) |
+ Some(ast_map::NodeExpr(_)) => "body",
+ Some(ast_map::NodeItem(it)) => item_scope_tag(&it),
+ Some(ast_map::NodeTraitItem(it)) => trait_item_scope_tag(&it),
+ Some(ast_map::NodeImplItem(it)) => impl_item_scope_tag(&it),
+
+ // this really should not happen, but it does:
+ // FIXME(#27942)
+ Some(_) => {
+ unknown = format!("unexpected node ({}) for scope {:?}. \
+ Please report a bug.",
+ self.map.node_to_string(node), fr.scope);
+ &unknown
}
- Some(_) | None => {
- // this really should not happen, but it does:
- // FIXME(#27942)
- (format!("{} unknown free region bounded by scope {:?}",
- prefix, fr.scope), None)
+ None => {
+ unknown = format!("unknown node for scope {:?}. \
+ Please report a bug.", fr.scope);
+ &unknown
}
- }
+ };
+ let (msg, opt_span) = explain_span(self, tag, self.map.span(node));
+ (format!("{} {}", prefix, msg), opt_span)
}
ty::ReStatic => ("the static lifetime".to_owned(), None),
"detects unreachable code paths"
}
+declare_lint! {
+ pub UNREACHABLE_PATTERNS,
+ Warn,
+ "detects unreachable patterns"
+}
+
declare_lint! {
pub WARNINGS,
Warn,
UNUSED_ASSIGNMENTS,
DEAD_CODE,
UNREACHABLE_CODE,
+ UNREACHABLE_PATTERNS,
WARNINGS,
UNUSED_FEATURES,
STABLE_FEATURES,
&& !has_allow_dead_code_or_lang_attr(&variant.attrs)
}
+ fn should_warn_about_foreign_item(&mut self, fi: &hir::ForeignItem) -> bool {
+ !self.symbol_is_live(fi.id, None)
+ && !has_allow_dead_code_or_lang_attr(&fi.attrs)
+ }
+
// id := node id of an item's definition.
// ctor_id := `Some` if the item is a struct_ctor (tuple struct),
// `None` otherwise.
}
fn visit_foreign_item(&mut self, fi: &'tcx hir::ForeignItem) {
- if !self.symbol_is_live(fi.id, None) {
+ if self.should_warn_about_foreign_item(fi) {
self.warn_dead_code(fi.id, fi.span, fi.name, fi.node.descriptive_variant());
}
intravisit::walk_foreign_item(self, fi);
use lint;
use hir::def::Def;
use hir::def_id::{CrateNum, CRATE_DEF_INDEX, DefId, DefIndex, LOCAL_CRATE};
-use ty::TyCtxt;
+use ty::{self, TyCtxt};
use middle::privacy::AccessLevels;
use syntax::symbol::Symbol;
use syntax_pos::{Span, DUMMY_SP};
}
impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
+ // (See issue #38412)
+ fn skip_stability_check_due_to_privacy(self, def_id: DefId) -> bool {
+ let visibility = {
+ // Check if `def_id` is a trait method.
+ match self.sess.cstore.associated_item(def_id) {
+ Some(ty::AssociatedItem { container: ty::TraitContainer(trait_def_id), .. }) => {
+ // Trait methods do not declare visibility (even
+ // for visibility info in cstore). Use containing
+ // trait instead, so methods of pub traits are
+ // themselves considered pub.
+ self.sess.cstore.visibility(trait_def_id)
+ }
+ _ => {
+ // Otherwise, cstore info works directly.
+ self.sess.cstore.visibility(def_id)
+ }
+ }
+ };
+
+ match visibility {
+ // must check stability for pub items.
+ ty::Visibility::Public => false,
+
+ // these are not visible outside crate; therefore
+ // stability markers are irrelevant, if even present.
+ ty::Visibility::Restricted(..) |
+ ty::Visibility::Invisible => true,
+ }
+ }
+
pub fn check_stability(self, def_id: DefId, id: NodeId, span: Span) {
if self.sess.codemap().span_allows_unstable(span) {
debug!("stability: \
self.stability.borrow_mut().used_features.insert(feature.clone(), level.clone());
}
+ // Issue 38412: private items lack stability markers.
+ if self.skip_stability_check_due_to_privacy(def_id) {
+ return
+ }
+
match stability {
Some(&Stability { level: attr::Unstable {ref reason, issue}, ref feature, .. }) => {
if !self.stability.borrow().active_features.contains(feature) {
self.elem(ProjectionElem::Deref)
}
+ pub fn downcast(self, adt_def: &'tcx AdtDef, variant_index: usize) -> Lvalue<'tcx> {
+ self.elem(ProjectionElem::Downcast(adt_def, variant_index))
+ }
+
pub fn index(self, index: Operand<'tcx>) -> Lvalue<'tcx> {
self.elem(ProjectionElem::Index(index))
}
the compiler to emit",
"[asm|llvm-bc|llvm-ir|obj|metadata|link|dep-info]"),
opt::multi_s("", "print", "Comma separated list of compiler information to \
- print on stdout", &print_opts.join("|")),
+ print on stdout", &format!("[{}]",
+ &print_opts.join("|"))),
opt::flagmulti_s("g", "", "Equivalent to -C debuginfo=2"),
opt::flagmulti_s("O", "", "Equivalent to -C opt-level=2"),
opt::opt_s("o", "", "Write output to <filename>", "FILENAME"),
use ty::{TyVar, TyVid, IntVar, IntVid, FloatVar, FloatVid};
use ty::TypeVariants::*;
use ty::layout::{Layout, TargetDataLayout};
+use ty::inhabitedness::DefIdForest;
use ty::maps;
use util::common::MemoizationMap;
use util::nodemap::{NodeMap, NodeSet, DefIdMap, DefIdSet};
// FIXME dep tracking -- should be harmless enough
pub normalized_cache: RefCell<FxHashMap<Ty<'tcx>, Ty<'tcx>>>,
+ pub inhabitedness_cache: RefCell<FxHashMap<Ty<'tcx>, DefIdForest>>,
+
pub lang_items: middle::lang_items::LanguageItems,
/// Maps from def-id of a type or region parameter to its
associated_item_def_ids: RefCell::new(DepTrackingMap::new(dep_graph.clone())),
ty_param_defs: RefCell::new(NodeMap()),
normalized_cache: RefCell::new(FxHashMap()),
+ inhabitedness_cache: RefCell::new(FxHashMap()),
lang_items: lang_items,
inherent_impls: RefCell::new(DepTrackingMap::new(dep_graph.clone())),
used_unsafe: RefCell::new(NodeSet()),
--- /dev/null
+// Copyright 2012-2015 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::mem;
+use rustc_data_structures::small_vec::SmallVec;
+use syntax::ast::CRATE_NODE_ID;
+use ty::context::TyCtxt;
+use ty::{DefId, DefIdTree};
+
+/// Represents a forest of DefIds closed under the ancestor relation. That is,
+/// if a DefId representing a module is contained in the forest then all
+/// DefIds defined in that module or submodules are also implicitly contained
+/// in the forest.
+///
+/// This is used to represent a set of modules in which a type is visibly
+/// uninhabited.
+#[derive(Clone)]
+pub struct DefIdForest {
+ /// The minimal set of DefIds required to represent the whole set.
+ /// If A and B are DefIds in the DefIdForest, and A is a desecendant
+ /// of B, then only B will be in root_ids.
+ /// We use a SmallVec here because (for its use for cacheing inhabitedness)
+ /// its rare that this will contain even two ids.
+ root_ids: SmallVec<[DefId; 1]>,
+}
+
+impl<'a, 'gcx, 'tcx> DefIdForest {
+ /// Create an empty forest.
+ pub fn empty() -> DefIdForest {
+ DefIdForest {
+ root_ids: SmallVec::new(),
+ }
+ }
+
+ /// Create a forest consisting of a single tree representing the entire
+ /// crate.
+ #[inline]
+ pub fn full(tcx: TyCtxt<'a, 'gcx, 'tcx>) -> DefIdForest {
+ let crate_id = tcx.map.local_def_id(CRATE_NODE_ID);
+ DefIdForest::from_id(crate_id)
+ }
+
+ /// Create a forest containing a DefId and all its descendants.
+ pub fn from_id(id: DefId) -> DefIdForest {
+ let mut root_ids = SmallVec::new();
+ root_ids.push(id);
+ DefIdForest {
+ root_ids: root_ids,
+ }
+ }
+
+ /// Test whether the forest is empty.
+ pub fn is_empty(&self) -> bool {
+ self.root_ids.is_empty()
+ }
+
+ /// Test whether the forest conains a given DefId.
+ pub fn contains(&self,
+ tcx: TyCtxt<'a, 'gcx, 'tcx>,
+ id: DefId) -> bool
+ {
+ for root_id in self.root_ids.iter() {
+ if tcx.is_descendant_of(id, *root_id) {
+ return true;
+ }
+ }
+ false
+ }
+
+ /// Calculate the intersection of a collection of forests.
+ pub fn intersection<I>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
+ iter: I) -> DefIdForest
+ where I: IntoIterator<Item=DefIdForest>
+ {
+ let mut ret = DefIdForest::full(tcx);
+ let mut next_ret = SmallVec::new();
+ let mut old_ret: SmallVec<[DefId; 1]> = SmallVec::new();
+ for next_forest in iter {
+ for id in ret.root_ids.drain(..) {
+ if next_forest.contains(tcx, id) {
+ next_ret.push(id);
+ } else {
+ old_ret.push(id);
+ }
+ }
+ ret.root_ids.extend(old_ret.drain(..));
+
+ for id in next_forest.root_ids {
+ if ret.contains(tcx, id) {
+ next_ret.push(id);
+ }
+ }
+
+ mem::swap(&mut next_ret, &mut ret.root_ids);
+ next_ret.drain(..);
+ }
+ ret
+ }
+
+ /// Calculate the union of a collection of forests.
+ pub fn union<I>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
+ iter: I) -> DefIdForest
+ where I: IntoIterator<Item=DefIdForest>
+ {
+ let mut ret = DefIdForest::empty();
+ let mut next_ret = SmallVec::new();
+ for next_forest in iter {
+ for id in ret.root_ids.drain(..) {
+ if !next_forest.contains(tcx, id) {
+ next_ret.push(id);
+ }
+ }
+
+ for id in next_forest.root_ids {
+ if !next_ret.contains(&id) {
+ next_ret.push(id);
+ }
+ }
+
+ mem::swap(&mut next_ret, &mut ret.root_ids);
+ next_ret.drain(..);
+ }
+ ret
+ }
+}
+
--- /dev/null
+// Copyright 2012-2015 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 util::nodemap::FxHashSet;
+use ty::context::TyCtxt;
+use ty::{AdtDef, VariantDef, FieldDef, TyS};
+use ty::{DefId, Substs};
+use ty::{AdtKind, Visibility};
+use ty::TypeVariants::*;
+
+pub use self::def_id_forest::DefIdForest;
+
+mod def_id_forest;
+
+// The methods in this module calculate DefIdForests of modules in which a
+// AdtDef/VariantDef/FieldDef is visibly uninhabited.
+//
+// # Example
+// ```rust
+// enum Void {}
+// mod a {
+// pub mod b {
+// pub struct SecretlyUninhabited {
+// _priv: !,
+// }
+// }
+// }
+//
+// mod c {
+// pub struct AlsoSecretlyUninhabited {
+// _priv: Void,
+// }
+// mod d {
+// }
+// }
+//
+// struct Foo {
+// x: a::b::SecretlyUninhabited,
+// y: c::AlsoSecretlyUninhabited,
+// }
+// ```
+// In this code, the type Foo will only be visibly uninhabited inside the
+// modules b, c and d. Calling uninhabited_from on Foo or its AdtDef will
+// return the forest of modules {b, c->d} (represented in a DefIdForest by the
+// set {b, c})
+//
+// We need this information for pattern-matching on Foo or types that contain
+// Foo.
+//
+// # Example
+// ```rust
+// let foo_result: Result<T, Foo> = ... ;
+// let Ok(t) = foo_result;
+// ```
+// This code should only compile in modules where the uninhabitedness of Foo is
+// visible.
+
+impl<'a, 'gcx, 'tcx> AdtDef {
+ /// Calculate the forest of DefIds from which this adt is visibly uninhabited.
+ pub fn uninhabited_from(
+ &self,
+ visited: &mut FxHashSet<(DefId, &'tcx Substs<'tcx>)>,
+ tcx: TyCtxt<'a, 'gcx, 'tcx>,
+ substs: &'tcx Substs<'tcx>) -> DefIdForest
+ {
+ if !visited.insert((self.did, substs)) {
+ return DefIdForest::empty();
+ }
+
+ let ret = DefIdForest::intersection(tcx, self.variants.iter().map(|v| {
+ v.uninhabited_from(visited, tcx, substs, self.adt_kind())
+ }));
+ visited.remove(&(self.did, substs));
+ ret
+ }
+}
+
+impl<'a, 'gcx, 'tcx> VariantDef {
+ /// Calculate the forest of DefIds from which this variant is visibly uninhabited.
+ pub fn uninhabited_from(
+ &self,
+ visited: &mut FxHashSet<(DefId, &'tcx Substs<'tcx>)>,
+ tcx: TyCtxt<'a, 'gcx, 'tcx>,
+ substs: &'tcx Substs<'tcx>,
+ adt_kind: AdtKind) -> DefIdForest
+ {
+ match adt_kind {
+ AdtKind::Union => {
+ DefIdForest::intersection(tcx, self.fields.iter().map(|f| {
+ f.uninhabited_from(visited, tcx, substs, false)
+ }))
+ },
+ AdtKind::Struct => {
+ DefIdForest::union(tcx, self.fields.iter().map(|f| {
+ f.uninhabited_from(visited, tcx, substs, false)
+ }))
+ },
+ AdtKind::Enum => {
+ DefIdForest::union(tcx, self.fields.iter().map(|f| {
+ f.uninhabited_from(visited, tcx, substs, true)
+ }))
+ },
+ }
+ }
+}
+
+impl<'a, 'gcx, 'tcx> FieldDef {
+ /// Calculate the forest of DefIds from which this field is visibly uninhabited.
+ pub fn uninhabited_from(
+ &self,
+ visited: &mut FxHashSet<(DefId, &'tcx Substs<'tcx>)>,
+ tcx: TyCtxt<'a, 'gcx, 'tcx>,
+ substs: &'tcx Substs<'tcx>,
+ is_enum: bool) -> DefIdForest
+ {
+ let mut data_uninhabitedness = move || self.ty(tcx, substs).uninhabited_from(visited, tcx);
+ // FIXME(canndrew): Currently enum fields are (incorrectly) stored with
+ // Visibility::Invisible so we need to override self.vis if we're
+ // dealing with an enum.
+ if is_enum {
+ data_uninhabitedness()
+ } else {
+ match self.vis {
+ Visibility::Invisible => DefIdForest::empty(),
+ Visibility::Restricted(from) => {
+ let forest = DefIdForest::from_id(from);
+ let iter = Some(forest).into_iter().chain(Some(data_uninhabitedness()));
+ DefIdForest::intersection(tcx, iter)
+ },
+ Visibility::Public => data_uninhabitedness(),
+ }
+ }
+ }
+}
+
+impl<'a, 'gcx, 'tcx> TyS<'tcx> {
+ /// Calculate the forest of DefIds from which this type is visibly uninhabited.
+ pub fn uninhabited_from(
+ &self,
+ visited: &mut FxHashSet<(DefId, &'tcx Substs<'tcx>)>,
+ tcx: TyCtxt<'a, 'gcx, 'tcx>) -> DefIdForest
+ {
+ match tcx.lift_to_global(&self) {
+ Some(global_ty) => {
+ {
+ let cache = tcx.inhabitedness_cache.borrow();
+ if let Some(forest) = cache.get(&global_ty) {
+ return forest.clone();
+ }
+ }
+ let forest = global_ty.uninhabited_from_inner(visited, tcx);
+ let mut cache = tcx.inhabitedness_cache.borrow_mut();
+ cache.insert(global_ty, forest.clone());
+ forest
+ },
+ None => {
+ let forest = self.uninhabited_from_inner(visited, tcx);
+ forest
+ },
+ }
+ }
+
+ fn uninhabited_from_inner(
+ &self,
+ visited: &mut FxHashSet<(DefId, &'tcx Substs<'tcx>)>,
+ tcx: TyCtxt<'a, 'gcx, 'tcx>) -> DefIdForest
+ {
+ match self.sty {
+ TyAdt(def, substs) => {
+ def.uninhabited_from(visited, tcx, substs)
+ },
+
+ TyNever => DefIdForest::full(tcx),
+ TyTuple(ref tys) => {
+ DefIdForest::union(tcx, tys.iter().map(|ty| {
+ ty.uninhabited_from(visited, tcx)
+ }))
+ },
+ TyArray(ty, len) => {
+ if len == 0 {
+ DefIdForest::empty()
+ } else {
+ ty.uninhabited_from(visited, tcx)
+ }
+ }
+ TyRef(_, ref tm) => tm.ty.uninhabited_from(visited, tcx),
+
+ _ => DefIdForest::empty(),
+ }
+ }
+}
+
use ty::subst::{Subst, Substs};
use ty::walk::TypeWalker;
use util::common::MemoizationMap;
-use util::nodemap::{NodeSet, NodeMap, FxHashMap, FxHashSet};
+use util::nodemap::{NodeSet, NodeMap, FxHashMap};
use serialize::{self, Encodable, Encoder};
use std::borrow::Cow;
pub mod error;
pub mod fast_reject;
pub mod fold;
+pub mod inhabitedness;
pub mod item_path;
pub mod layout;
pub mod _match;
pub trait DefIdTree: Copy {
fn parent(self, id: DefId) -> Option<DefId>;
+
+ fn is_descendant_of(self, mut descendant: DefId, ancestor: DefId) -> bool {
+ if descendant.krate != ancestor.krate {
+ return false;
+ }
+
+ while descendant != ancestor {
+ match self.parent(descendant) {
+ Some(parent) => descendant = parent,
+ None => return false,
+ }
+ }
+ true
+ }
}
impl<'a, 'gcx, 'tcx> DefIdTree for TyCtxt<'a, 'gcx, 'tcx> {
}
/// Returns true if an item with this visibility is accessible from the given block.
- pub fn is_accessible_from<T: DefIdTree>(self, mut module: DefId, tree: T) -> bool {
+ pub fn is_accessible_from<T: DefIdTree>(self, module: DefId, tree: T) -> bool {
let restriction = match self {
// Public items are visible everywhere.
Visibility::Public => return true,
Visibility::Restricted(module) => module,
};
- while module != restriction {
- match tree.parent(module) {
- Some(parent) => module = parent,
- None => return false,
- }
- }
-
- true
+ tree.is_descendant_of(module, restriction)
}
/// Returns true if this visibility is at least as accessible as the given visibility
self.flags.set(self.flags.get() | AdtFlags::IS_DTORCK_VALID)
}
- #[inline]
- pub fn is_uninhabited_recurse(&self,
- visited: &mut FxHashSet<(DefId, &'tcx Substs<'tcx>)>,
- block: Option<NodeId>,
- tcx: TyCtxt<'a, 'gcx, 'tcx>,
- substs: &'tcx Substs<'tcx>) -> bool {
- if !visited.insert((self.did, substs)) {
- return false;
- };
- self.variants.iter().all(|v| {
- v.is_uninhabited_recurse(visited, block, tcx, substs, self.is_union())
- })
- }
-
#[inline]
pub fn is_struct(&self) -> bool {
!self.is_union() && !self.is_enum()
pub fn field_named(&self, name: ast::Name) -> &FieldDef {
self.find_field_named(name).unwrap()
}
-
- #[inline]
- pub fn is_uninhabited_recurse(&self,
- visited: &mut FxHashSet<(DefId, &'tcx Substs<'tcx>)>,
- block: Option<NodeId>,
- tcx: TyCtxt<'a, 'gcx, 'tcx>,
- substs: &'tcx Substs<'tcx>,
- is_union: bool) -> bool {
- if is_union {
- self.fields.iter().all(|f| f.is_uninhabited_recurse(visited, block, tcx, substs))
- } else {
- self.fields.iter().any(|f| f.is_uninhabited_recurse(visited, block, tcx, substs))
- }
- }
}
impl<'a, 'gcx, 'tcx> FieldDef {
pub fn ty(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>, subst: &Substs<'tcx>) -> Ty<'tcx> {
tcx.item_type(self.did).subst(tcx, subst)
}
-
- #[inline]
- pub fn is_uninhabited_recurse(&self,
- visited: &mut FxHashSet<(DefId, &'tcx Substs<'tcx>)>,
- block: Option<NodeId>,
- tcx: TyCtxt<'a, 'gcx, 'tcx>,
- substs: &'tcx Substs<'tcx>) -> bool {
- block.map_or(true, |b| tcx.vis_is_accessible_from(self.vis, b)) &&
- self.ty(tcx, substs).is_uninhabited_recurse(visited, block, tcx)
- }
}
/// Records the substitutions used to translate the polytype for an
use std::iter;
use std::cmp::Ordering;
use syntax::abi;
-use syntax::ast::{self, Name, NodeId};
+use syntax::ast::{self, Name};
use syntax::symbol::{keywords, InternedString};
use util::nodemap::FxHashSet;
}
}
- /// Checks whether a type is uninhabited.
- /// If `block` is `Some(id)` it also checks that the uninhabited-ness is visible from `id`.
- pub fn is_uninhabited(&self, block: Option<NodeId>, cx: TyCtxt<'a, 'gcx, 'tcx>) -> bool {
+ /// Checks whether a type is visibly uninhabited from a particular module.
+ /// # Example
+ /// ```rust
+ /// enum Void {}
+ /// mod a {
+ /// pub mod b {
+ /// pub struct SecretlyUninhabited {
+ /// _priv: !,
+ /// }
+ /// }
+ /// }
+ ///
+ /// mod c {
+ /// pub struct AlsoSecretlyUninhabited {
+ /// _priv: Void,
+ /// }
+ /// mod d {
+ /// }
+ /// }
+ ///
+ /// struct Foo {
+ /// x: a::b::SecretlyUninhabited,
+ /// y: c::AlsoSecretlyUninhabited,
+ /// }
+ /// ```
+ /// In this code, the type `Foo` will only be visibly uninhabited inside the
+ /// modules b, c and d. This effects pattern-matching on `Foo` or types that
+ /// contain `Foo`.
+ ///
+ /// # Example
+ /// ```rust
+ /// let foo_result: Result<T, Foo> = ... ;
+ /// let Ok(t) = foo_result;
+ /// ```
+ /// This code should only compile in modules where the uninhabitedness of Foo is
+ /// visible.
+ pub fn is_uninhabited_from(&self, module: DefId, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> bool {
let mut visited = FxHashSet::default();
- self.is_uninhabited_recurse(&mut visited, block, cx)
- }
-
- pub fn is_uninhabited_recurse(&self,
- visited: &mut FxHashSet<(DefId, &'tcx Substs<'tcx>)>,
- block: Option<NodeId>,
- cx: TyCtxt<'a, 'gcx, 'tcx>) -> bool {
- match self.sty {
- TyAdt(def, substs) => {
- def.is_uninhabited_recurse(visited, block, cx, substs)
- },
-
- TyNever => true,
- TyTuple(ref tys) => tys.iter().any(|ty| ty.is_uninhabited_recurse(visited, block, cx)),
- TyArray(ty, len) => len > 0 && ty.is_uninhabited_recurse(visited, block, cx),
- TyRef(_, ref tm) => tm.ty.is_uninhabited_recurse(visited, block, cx),
-
- _ => false,
- }
+ let forest = self.uninhabited_from(&mut visited, tcx);
+
+ // To check whether this type is uninhabited at all (not just from the
+ // given node) you could check whether the forest is empty.
+ // ```
+ // forest.is_empty()
+ // ```
+ forest.contains(tcx, module)
}
pub fn is_primitive(&self) -> bool {
use infer::InferCtxt;
use hir::map as ast_map;
use traits::{self, Reveal};
-use ty::{self, Ty, AdtKind, TyCtxt, TypeAndMut, TypeFlags, TypeFoldable};
+use ty::{self, Ty, TyCtxt, TypeAndMut, TypeFlags, TypeFoldable};
use ty::{Disr, ParameterEnvironment};
use ty::fold::TypeVisitor;
use ty::layout::{Layout, LayoutError};
#[derive(Copy, Clone)]
-pub enum CopyImplementationError {
- InfrigingField(Name),
- InfrigingVariant(Name),
+pub enum CopyImplementationError<'tcx> {
+ InfrigingField(&'tcx ty::FieldDef),
NotAnAdt,
HasDestructor
}
impl<'tcx> ParameterEnvironment<'tcx> {
pub fn can_type_implement_copy<'a>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>,
self_type: Ty<'tcx>, span: Span)
- -> Result<(),CopyImplementationError> {
+ -> Result<(), CopyImplementationError> {
// FIXME: (@jroesch) float this code up
- tcx.infer_ctxt(None, Some(self.clone()), Reveal::ExactMatch).enter(|infcx| {
- let adt = match self_type.sty {
- ty::TyAdt(adt, substs) => match adt.adt_kind() {
- AdtKind::Struct | AdtKind::Union => {
- for field in adt.all_fields() {
- let field_ty = field.ty(tcx, substs);
- if infcx.type_moves_by_default(field_ty, span) {
- return Err(CopyImplementationError::InfrigingField(
- field.name))
- }
- }
- adt
- }
- AdtKind::Enum => {
- for variant in &adt.variants {
- for field in &variant.fields {
- let field_ty = field.ty(tcx, substs);
- if infcx.type_moves_by_default(field_ty, span) {
- return Err(CopyImplementationError::InfrigingVariant(
- variant.name))
- }
- }
- }
- adt
- }
- },
+ tcx.infer_ctxt(None, Some(self.clone()), Reveal::NotSpecializable).enter(|infcx| {
+ let (adt, substs) = match self_type.sty {
+ ty::TyAdt(adt, substs) => (adt, substs),
_ => return Err(CopyImplementationError::NotAnAdt)
};
+ let field_implements_copy = |field: &ty::FieldDef| {
+ let cause = traits::ObligationCause::dummy();
+ match traits::fully_normalize(&infcx, cause, &field.ty(tcx, substs)) {
+ Ok(ty) => !infcx.type_moves_by_default(ty, span),
+ Err(..) => false
+ }
+ };
+
+ for variant in &adt.variants {
+ for field in &variant.fields {
+ if !field_implements_copy(field) {
+ return Err(CopyImplementationError::InfrigingField(field));
+ }
+ }
+ }
+
if adt.has_dtor() {
return Err(CopyImplementationError::HasDestructor);
}
"-Wl,--as-needed".to_string(),
// Always enable NX protection when it is available
- "-Wl,-z,noexecstack".to_string(),
-
- // Static link
- "-static".to_string()
- ],
- late_link_args: vec![
- "-lc".to_string(),
- "-lm".to_string()
+ "-Wl,-z,noexecstack".to_string()
],
executables: true,
relocation_model: "static".to_string(),
eliminate_frame_pointer: false,
target_family: None,
linker_is_gnu: true,
- no_default_libraries: true,
lib_allocation_crate: "alloc_system".to_string(),
exe_allocation_crate: "alloc_system".to_string(),
has_elf_tls: true,
use rustc_const_math::ConstInt;
-use rustc_data_structures::fx::FxHashMap;
+use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_data_structures::indexed_vec::Idx;
use pattern::{FieldPattern, Pattern, PatternKind};
use pattern::{PatternFoldable, PatternFolder};
-use rustc::hir::def::Def;
use rustc::hir::def_id::DefId;
-use rustc::ty::{self, Ty, TyCtxt, TypeFoldable};
+use rustc::ty::{self, AdtKind, Ty, TyCtxt, TypeFoldable};
-use rustc::hir;
-use rustc::hir::def::CtorKind;
-use rustc::hir::{Pat, PatKind};
+use rustc::mir::Field;
use rustc::util::common::ErrorReported;
-use syntax::ast::{self, DUMMY_NODE_ID};
-use syntax::codemap::Spanned;
-use syntax::ptr::P;
use syntax_pos::{Span, DUMMY_SP};
use arena::TypedArena;
}
}
-pub const DUMMY_WILD_PAT: &'static Pat = &Pat {
- id: DUMMY_NODE_ID,
- node: PatKind::Wild,
- span: DUMMY_SP
-};
-
impl<'tcx> Pattern<'tcx> {
fn is_wildcard(&self) -> bool {
match *self.kind {
//NOTE: appears to be the only place other then InferCtxt to contain a ParamEnv
pub struct MatchCheckCtxt<'a, 'tcx: 'a> {
pub tcx: TyCtxt<'a, 'tcx, 'tcx>,
- /// A wild pattern with an error type - it exists to avoid having to normalize
- /// associated types to get field types.
- pub wild_pattern: &'a Pattern<'tcx>,
+ /// The module in which the match occurs. This is necessary for
+ /// checking inhabited-ness of types because whether a type is (visibly)
+ /// inhabited can depend on whether it was defined in the current module or
+ /// not. eg. `struct Foo { _private: ! }` cannot be seen to be empty
+ /// outside it's module and should not be matchable with an empty match
+ /// statement.
+ pub module: DefId,
pub pattern_arena: &'a TypedArena<Pattern<'tcx>>,
pub byte_array_map: FxHashMap<*const Pattern<'tcx>, Vec<&'a Pattern<'tcx>>>,
}
impl<'a, 'tcx> MatchCheckCtxt<'a, 'tcx> {
pub fn create_and_enter<F, R>(
tcx: TyCtxt<'a, 'tcx, 'tcx>,
+ module: DefId,
f: F) -> R
where F: for<'b> FnOnce(MatchCheckCtxt<'b, 'tcx>) -> R
{
- let wild_pattern = Pattern {
- ty: tcx.types.err,
- span: DUMMY_SP,
- kind: box PatternKind::Wild
- };
-
let pattern_arena = TypedArena::new();
f(MatchCheckCtxt {
tcx: tcx,
- wild_pattern: &wild_pattern,
+ module: module,
pattern_arena: &pattern_arena,
byte_array_map: FxHashMap(),
})
}
// convert a byte-string pattern to a list of u8 patterns.
- fn lower_byte_str_pattern(&mut self, pat: &'a Pattern<'tcx>) -> Vec<&'a Pattern<'tcx>> {
+ fn lower_byte_str_pattern<'p>(&mut self, pat: &'p Pattern<'tcx>) -> Vec<&'p Pattern<'tcx>>
+ where 'a: 'p
+ {
let pattern_arena = &*self.pattern_arena;
let tcx = self.tcx;
self.byte_array_map.entry(pat).or_insert_with(|| {
}
impl<'tcx> Constructor {
- fn variant_for_adt(&self, adt: &'tcx ty::AdtDef) -> &'tcx ty::VariantDef {
+ fn variant_index_for_adt(&self, adt: &'tcx ty::AdtDef) -> usize {
match self {
- &Variant(vid) => adt.variant_with_id(vid),
+ &Variant(vid) => adt.variant_index_with_id(vid),
&Single => {
assert_eq!(adt.variants.len(), 1);
- &adt.variants[0]
+ 0
}
_ => bug!("bad constructor {:?} for adt {:?}", self, adt)
}
}
}
-#[derive(Clone, PartialEq)]
-pub enum Usefulness {
+#[derive(Clone)]
+pub enum Usefulness<'tcx> {
Useful,
- UsefulWithWitness(Vec<Witness>),
+ UsefulWithWitness(Vec<Witness<'tcx>>),
NotUseful
}
+impl<'tcx> Usefulness<'tcx> {
+ fn is_useful(&self) -> bool {
+ match *self {
+ NotUseful => false,
+ _ => true
+ }
+ }
+}
+
#[derive(Copy, Clone)]
pub enum WitnessPreference {
ConstructWitness,
max_slice_length: usize,
}
-
-fn const_val_to_expr(value: &ConstVal) -> P<hir::Expr> {
- let node = match value {
- &ConstVal::Bool(b) => ast::LitKind::Bool(b),
- _ => bug!()
- };
- P(hir::Expr {
- id: DUMMY_NODE_ID,
- node: hir::ExprLit(P(Spanned { node: node, span: DUMMY_SP })),
- span: DUMMY_SP,
- attrs: ast::ThinVec::new(),
- })
-}
-
/// A stack of patterns in reverse order of construction
-#[derive(Clone, PartialEq, Eq)]
-pub struct Witness(Vec<P<Pat>>);
+#[derive(Clone)]
+pub struct Witness<'tcx>(Vec<Pattern<'tcx>>);
-impl Witness {
- pub fn single_pattern(&self) -> &Pat {
+impl<'tcx> Witness<'tcx> {
+ pub fn single_pattern(&self) -> &Pattern<'tcx> {
assert_eq!(self.0.len(), 1);
&self.0[0]
}
- fn push_wild_constructor<'a, 'tcx>(
+ fn push_wild_constructor<'a>(
mut self,
cx: &MatchCheckCtxt<'a, 'tcx>,
ctor: &Constructor,
ty: Ty<'tcx>)
-> Self
{
- let arity = constructor_arity(cx, ctor, ty);
- self.0.extend(repeat(DUMMY_WILD_PAT).take(arity).map(|p| P(p.clone())));
+ let sub_pattern_tys = constructor_sub_pattern_tys(cx, ctor, ty);
+ self.0.extend(sub_pattern_tys.into_iter().map(|ty| {
+ Pattern {
+ ty: ty,
+ span: DUMMY_SP,
+ kind: box PatternKind::Wild,
+ }
+ }));
self.apply_constructor(cx, ctor, ty)
}
///
/// left_ty: struct X { a: (bool, &'static str), b: usize}
/// pats: [(false, "foo"), 42] => X { a: (false, "foo"), b: 42 }
- fn apply_constructor<'a, 'tcx>(
+ fn apply_constructor<'a>(
mut self,
cx: &MatchCheckCtxt<'a,'tcx>,
ctor: &Constructor,
let mut pats = self.0.drain(len-arity..).rev();
match ty.sty {
- ty::TyTuple(..) => PatKind::Tuple(pats.collect(), None),
-
- ty::TyAdt(adt, _) => {
- let v = ctor.variant_for_adt(adt);
- let qpath = hir::QPath::Resolved(None, P(hir::Path {
- span: DUMMY_SP,
- def: Def::Err,
- segments: vec![hir::PathSegment::from_name(v.name)].into(),
- }));
- match v.ctor_kind {
- CtorKind::Fictive => {
- let field_pats: hir::HirVec<_> = v.fields.iter()
- .zip(pats)
- .filter(|&(_, ref pat)| pat.node != PatKind::Wild)
- .map(|(field, pat)| Spanned {
- span: DUMMY_SP,
- node: hir::FieldPat {
- name: field.name,
- pat: pat,
- is_shorthand: false,
- }
- }).collect();
- let has_more_fields = field_pats.len() < arity;
- PatKind::Struct(qpath, field_pats, has_more_fields)
+ ty::TyAdt(..) |
+ ty::TyTuple(..) => {
+ let pats = pats.enumerate().map(|(i, p)| {
+ FieldPattern {
+ field: Field::new(i),
+ pattern: p
}
- CtorKind::Fn => {
- PatKind::TupleStruct(qpath, pats.collect(), None)
+ }).collect();
+
+ if let ty::TyAdt(adt, substs) = ty.sty {
+ if adt.variants.len() > 1 {
+ PatternKind::Variant {
+ adt_def: adt,
+ substs: substs,
+ variant_index: ctor.variant_index_for_adt(adt),
+ subpatterns: pats
+ }
+ } else {
+ PatternKind::Leaf { subpatterns: pats }
}
- CtorKind::Const => PatKind::Path(qpath)
+ } else {
+ PatternKind::Leaf { subpatterns: pats }
}
}
- ty::TyRef(_, ty::TypeAndMut { mutbl, .. }) => {
- PatKind::Ref(pats.nth(0).unwrap(), mutbl)
+ ty::TyRef(..) => {
+ PatternKind::Deref { subpattern: pats.nth(0).unwrap() }
}
ty::TySlice(_) | ty::TyArray(..) => {
- PatKind::Slice(pats.collect(), None, hir::HirVec::new())
+ PatternKind::Slice {
+ prefix: pats.collect(),
+ slice: None,
+ suffix: vec![]
+ }
}
_ => {
match *ctor {
- ConstantValue(ref v) => PatKind::Lit(const_val_to_expr(v)),
- _ => PatKind::Wild,
+ ConstantValue(ref v) => PatternKind::Constant { value: v.clone() },
+ _ => PatternKind::Wild,
}
}
}
};
- self.0.push(P(hir::Pat {
- id: DUMMY_NODE_ID,
- node: pat,
- span: DUMMY_SP
- }));
+ self.0.push(Pattern {
+ ty: ty,
+ span: DUMMY_SP,
+ kind: Box::new(pat),
+ });
self
}
}
-/// Return the set of constructors from the same type as the first column of `matrix`,
-/// that are matched only by wildcard patterns from that first column.
-///
-/// Therefore, if there is some pattern that is unmatched by `matrix`, it will
-/// still be unmatched if the first constructor is replaced by any of the constructors
-/// in the return value.
-fn missing_constructors(cx: &mut MatchCheckCtxt,
- matrix: &Matrix,
- pcx: PatternContext) -> Vec<Constructor> {
- let used_constructors: Vec<Constructor> =
- matrix.0.iter()
- .flat_map(|row| pat_constructors(cx, row[0], pcx).unwrap_or(vec![]))
- .collect();
- debug!("used_constructors = {:?}", used_constructors);
- all_constructors(cx, pcx).into_iter()
- .filter(|c| !used_constructors.contains(c))
- .collect()
-}
-
/// This determines the set of all possible constructors of a pattern matching
/// values of type `left_ty`. For vectors, this would normally be an infinite set
+/// but is instead bounded by the maximum fixed length of slice patterns in
+/// the column of patterns being analyzed.
///
/// This intentionally does not list ConstantValue specializations for
/// non-booleans, because we currently assume that there is always a
/// "non-standard constant" that matches. See issue #12483.
///
-/// but is instead bounded by the maximum fixed length of slice patterns in
-/// the column of patterns being analyzed.
-fn all_constructors(_cx: &mut MatchCheckCtxt, pcx: PatternContext) -> Vec<Constructor> {
+/// We make sure to omit constructors that are statically impossible. eg for
+/// Option<!> we do not include Some(_) in the returned list of constructors.
+fn all_constructors<'a, 'tcx: 'a>(cx: &mut MatchCheckCtxt<'a, 'tcx>,
+ pcx: PatternContext<'tcx>) -> Vec<Constructor>
+{
+ debug!("all_constructors({:?})", pcx.ty);
match pcx.ty.sty {
ty::TyBool =>
[true, false].iter().map(|b| ConstantValue(ConstVal::Bool(*b))).collect(),
- ty::TySlice(_) =>
- (0..pcx.max_slice_length+1).map(|length| Slice(length)).collect(),
- ty::TyArray(_, length) => vec![Slice(length)],
- ty::TyAdt(def, _) if def.is_enum() && def.variants.len() > 1 =>
- def.variants.iter().map(|v| Variant(v.did)).collect(),
- _ => vec![Single]
+ ty::TySlice(ref sub_ty) => {
+ if sub_ty.is_uninhabited_from(cx.module, cx.tcx) {
+ vec![Slice(0)]
+ } else {
+ (0..pcx.max_slice_length+1).map(|length| Slice(length)).collect()
+ }
+ }
+ ty::TyArray(ref sub_ty, length) => {
+ if length == 0 || !sub_ty.is_uninhabited_from(cx.module, cx.tcx) {
+ vec![Slice(length)]
+ } else {
+ vec![]
+ }
+ }
+ ty::TyAdt(def, substs) if def.is_enum() && def.variants.len() != 1 => {
+ def.variants.iter().filter_map(|v| {
+ let mut visited = FxHashSet::default();
+ let forest = v.uninhabited_from(&mut visited,
+ cx.tcx, substs,
+ AdtKind::Enum);
+ if forest.contains(cx.tcx, cx.module) {
+ None
+ } else {
+ Some(Variant(v.did))
+ }
+ }).collect()
+ }
+ _ => {
+ if pcx.ty.is_uninhabited_from(cx.module, cx.tcx) {
+ vec![]
+ } else {
+ vec![Single]
+ }
+ }
}
}
-fn max_slice_length<'a, 'tcx, I>(
+fn max_slice_length<'p, 'a: 'p, 'tcx: 'a, I>(
_cx: &mut MatchCheckCtxt<'a, 'tcx>,
patterns: I) -> usize
- where I: Iterator<Item=&'a Pattern<'tcx>>
+ where I: Iterator<Item=&'p Pattern<'tcx>>
{
// The exhaustiveness-checking paper does not include any details on
// checking variable-length slice patterns. However, they are matched
}
/// Algorithm from http://moscova.inria.fr/~maranget/papers/warn/index.html
+/// The algorithm from the paper has been modified to correctly handle empty
+/// types. The changes are:
+/// (0) We don't exit early if the pattern matrix has zero rows. We just
+/// continue to recurse over columns.
+/// (1) all_constructors will only return constructors that are statically
+/// possible. eg. it will only return Ok for Result<T, !>
///
/// Whether a vector `v` of patterns is 'useful' in relation to a set of such
/// vectors `m` is defined as there being a set of inputs that will match `v`
/// relation to preceding patterns, it is not reachable) and exhaustiveness
/// checking (if a wildcard pattern is useful in relation to a matrix, the
/// matrix isn't exhaustive).
-///
-/// Note: is_useful doesn't work on empty types, as the paper notes.
-/// So it assumes that v is non-empty.
-pub fn is_useful<'a, 'tcx>(cx: &mut MatchCheckCtxt<'a, 'tcx>,
- matrix: &Matrix<'a, 'tcx>,
- v: &[&'a Pattern<'tcx>],
+pub fn is_useful<'p, 'a: 'p, 'tcx: 'a>(cx: &mut MatchCheckCtxt<'a, 'tcx>,
+ matrix: &Matrix<'p, 'tcx>,
+ v: &[&'p Pattern<'tcx>],
witness: WitnessPreference)
- -> Usefulness {
+ -> Usefulness<'tcx> {
let &Matrix(ref rows) = matrix;
debug!("is_useful({:?}, {:?})", matrix, v);
- if rows.is_empty() {
- return match witness {
- ConstructWitness => UsefulWithWitness(vec![Witness(
- repeat(DUMMY_WILD_PAT).take(v.len()).map(|p| P(p.clone())).collect()
- )]),
- LeaveOutWitness => Useful
- };
- }
- if rows[0].is_empty() {
- return NotUseful;
- }
- let &Matrix(ref rows) = matrix;
+ // The base case. We are pattern-matching on () and the return value is
+ // based on whether our matrix has a row or not.
+ // NOTE: This could potentially be optimized by checking rows.is_empty()
+ // first and then, if v is non-empty, the return value is based on whether
+ // the type of the tuple we're checking is inhabited or not.
+ if v.is_empty() {
+ return if rows.is_empty() {
+ match witness {
+ ConstructWitness => UsefulWithWitness(vec![Witness(vec![])]),
+ LeaveOutWitness => Useful,
+ }
+ } else {
+ NotUseful
+ }
+ };
+
assert!(rows.iter().all(|r| r.len() == v.len()));
debug!("is_useful - expanding constructors: {:?}", constructors);
constructors.into_iter().map(|c|
is_useful_specialized(cx, matrix, v, c.clone(), pcx.ty, witness)
- ).find(|result| result != &NotUseful).unwrap_or(NotUseful)
+ ).find(|result| result.is_useful()).unwrap_or(NotUseful)
} else {
debug!("is_useful - expanding wildcard");
- let constructors = missing_constructors(cx, matrix, pcx);
- debug!("is_useful - missing_constructors = {:?}", constructors);
- if constructors.is_empty() {
- all_constructors(cx, pcx).into_iter().map(|c| {
+
+ let used_ctors: Vec<Constructor> = rows.iter().flat_map(|row| {
+ pat_constructors(cx, row[0], pcx).unwrap_or(vec![])
+ }).collect();
+ debug!("used_ctors = {:?}", used_ctors);
+ let all_ctors = all_constructors(cx, pcx);
+ debug!("all_ctors = {:?}", all_ctors);
+ let missing_ctors: Vec<Constructor> = all_ctors.iter().filter(|c| {
+ !used_ctors.contains(*c)
+ }).cloned().collect();
+ debug!("missing_ctors = {:?}", missing_ctors);
+
+ // `missing_ctors` is the set of constructors from the same type as the
+ // first column of `matrix` that are matched only by wildcard patterns
+ // from the first column.
+ //
+ // Therefore, if there is some pattern that is unmatched by `matrix`,
+ // it will still be unmatched if the first constructor is replaced by
+ // any of the constructors in `missing_ctors`
+
+ if missing_ctors.is_empty() {
+ all_ctors.into_iter().map(|c| {
is_useful_specialized(cx, matrix, v, c.clone(), pcx.ty, witness)
- }).find(|result| result != &NotUseful).unwrap_or(NotUseful)
+ }).find(|result| result.is_useful()).unwrap_or(NotUseful)
} else {
let matrix = rows.iter().filter_map(|r| {
if r[0].is_wildcard() {
match is_useful(cx, &matrix, &v[1..], witness) {
UsefulWithWitness(pats) => {
let cx = &*cx;
- UsefulWithWitness(pats.into_iter().flat_map(|witness| {
- constructors.iter().map(move |ctor| {
- witness.clone().push_wild_constructor(cx, ctor, pcx.ty)
- })
- }).collect())
+ let new_witnesses = if used_ctors.is_empty() {
+ // All constructors are unused. Add wild patterns
+ // rather than each individual constructor
+ pats.into_iter().map(|mut witness| {
+ witness.0.push(Pattern {
+ ty: pcx.ty,
+ span: DUMMY_SP,
+ kind: box PatternKind::Wild,
+ });
+ witness
+ }).collect()
+ } else {
+ pats.into_iter().flat_map(|witness| {
+ missing_ctors.iter().map(move |ctor| {
+ witness.clone().push_wild_constructor(cx, ctor, pcx.ty)
+ })
+ }).collect()
+ };
+ UsefulWithWitness(new_witnesses)
}
result => result
}
}
}
-fn is_useful_specialized<'a, 'tcx>(
+fn is_useful_specialized<'p, 'a:'p, 'tcx: 'a>(
cx: &mut MatchCheckCtxt<'a, 'tcx>,
- &Matrix(ref m): &Matrix<'a, 'tcx>,
- v: &[&'a Pattern<'tcx>],
+ &Matrix(ref m): &Matrix<'p, 'tcx>,
+ v: &[&'p Pattern<'tcx>],
ctor: Constructor,
lty: Ty<'tcx>,
- witness: WitnessPreference) -> Usefulness
+ witness: WitnessPreference) -> Usefulness<'tcx>
{
- let arity = constructor_arity(cx, &ctor, lty);
+ let sub_pat_tys = constructor_sub_pattern_tys(cx, &ctor, lty);
+ let wild_patterns_owned: Vec<_> = sub_pat_tys.iter().map(|ty| {
+ Pattern {
+ ty: ty,
+ span: DUMMY_SP,
+ kind: box PatternKind::Wild,
+ }
+ }).collect();
+ let wild_patterns: Vec<_> = wild_patterns_owned.iter().collect();
let matrix = Matrix(m.iter().flat_map(|r| {
- specialize(cx, &r[..], &ctor, 0, arity)
+ specialize(cx, &r[..], &ctor, &wild_patterns)
}).collect());
- match specialize(cx, v, &ctor, 0, arity) {
+ match specialize(cx, v, &ctor, &wild_patterns) {
Some(v) => match is_useful(cx, &matrix, &v[..], witness) {
UsefulWithWitness(witnesses) => UsefulWithWitness(
witnesses.into_iter()
},
ty::TyRef(..) => 1,
ty::TyAdt(adt, _) => {
- ctor.variant_for_adt(adt).fields.len()
+ adt.variants[ctor.variant_index_for_adt(adt)].fields.len()
}
_ => 0
}
}
+/// This computes the types of the sub patterns that a constructor should be
+/// expanded to.
+///
+/// For instance, a tuple pattern (43u32, 'a') has sub pattern types [u32, char].
+fn constructor_sub_pattern_tys<'a, 'tcx: 'a>(cx: &MatchCheckCtxt<'a, 'tcx>,
+ ctor: &Constructor,
+ ty: Ty<'tcx>) -> Vec<Ty<'tcx>>
+{
+ debug!("constructor_sub_pattern_tys({:?}, {:?})", ctor, ty);
+ match ty.sty {
+ ty::TyTuple(ref fs) => fs.into_iter().map(|t| *t).collect(),
+ ty::TyBox(ty) => vec![ty],
+ ty::TySlice(ty) | ty::TyArray(ty, _) => match *ctor {
+ Slice(length) => repeat(ty).take(length).collect(),
+ ConstantValue(_) => vec![],
+ _ => bug!("bad slice pattern {:?} {:?}", ctor, ty)
+ },
+ ty::TyRef(_, ref ty_and_mut) => vec![ty_and_mut.ty],
+ ty::TyAdt(adt, substs) => {
+ adt.variants[ctor.variant_index_for_adt(adt)].fields.iter().map(|field| {
+ field.ty(cx.tcx, substs)
+ }).collect()
+ }
+ _ => vec![],
+ }
+}
+
fn slice_pat_covered_by_constructor(_tcx: TyCtxt, _span: Span,
ctor: &Constructor,
prefix: &[Pattern],
Ok(cmp_from != Ordering::Less && cmp_to != Ordering::Greater)
}
-fn patterns_for_variant<'a, 'tcx>(
- cx: &mut MatchCheckCtxt<'a, 'tcx>,
- subpatterns: &'a [FieldPattern<'tcx>],
- arity: usize)
- -> Vec<&'a Pattern<'tcx>>
+fn patterns_for_variant<'p, 'a: 'p, 'tcx: 'a>(
+ subpatterns: &'p [FieldPattern<'tcx>],
+ wild_patterns: &[&'p Pattern<'tcx>])
+ -> Vec<&'p Pattern<'tcx>>
{
- let mut result = vec![cx.wild_pattern; arity];
+ let mut result = wild_patterns.to_owned();
for subpat in subpatterns {
result[subpat.field.index()] = &subpat.pattern;
}
- debug!("patterns_for_variant({:?}, {:?}) = {:?}", subpatterns, arity, result);
+ debug!("patterns_for_variant({:?}, {:?}) = {:?}", subpatterns, wild_patterns, result);
result
}
/// different patterns.
/// Structure patterns with a partial wild pattern (Foo { a: 42, .. }) have their missing
/// fields filled with wild patterns.
-fn specialize<'a, 'tcx>(
+fn specialize<'p, 'a: 'p, 'tcx: 'a>(
cx: &mut MatchCheckCtxt<'a, 'tcx>,
- r: &[&'a Pattern<'tcx>],
- constructor: &Constructor, col: usize, arity: usize)
- -> Option<Vec<&'a Pattern<'tcx>>>
+ r: &[&'p Pattern<'tcx>],
+ constructor: &Constructor,
+ wild_patterns: &[&'p Pattern<'tcx>])
+ -> Option<Vec<&'p Pattern<'tcx>>>
{
- let pat = &r[col];
+ let pat = &r[0];
let head: Option<Vec<&Pattern>> = match *pat.kind {
- PatternKind::Binding { .. } | PatternKind::Wild =>
- Some(vec![cx.wild_pattern; arity]),
+ PatternKind::Binding { .. } | PatternKind::Wild => {
+ Some(wild_patterns.to_owned())
+ },
- PatternKind::Variant { adt_def, variant_index, ref subpatterns } => {
+ PatternKind::Variant { adt_def, variant_index, ref subpatterns, .. } => {
let ref variant = adt_def.variants[variant_index];
if *constructor == Variant(variant.did) {
- Some(patterns_for_variant(cx, subpatterns, arity))
+ Some(patterns_for_variant(subpatterns, wild_patterns))
} else {
None
}
}
- PatternKind::Leaf { ref subpatterns } => Some(patterns_for_variant(cx, subpatterns, arity)),
- PatternKind::Deref { ref subpattern } => Some(vec![subpattern]),
+ PatternKind::Leaf { ref subpatterns } => {
+ Some(patterns_for_variant(subpatterns, wild_patterns))
+ }
+ PatternKind::Deref { ref subpattern } => {
+ Some(vec![subpattern])
+ }
PatternKind::Constant { ref value } => {
match *constructor {
Slice(..) => match *value {
ConstVal::ByteStr(ref data) => {
- if arity == data.len() {
+ if wild_patterns.len() == data.len() {
Some(cx.lower_byte_str_pattern(pat))
} else {
None
match *constructor {
Slice(..) => {
let pat_len = prefix.len() + suffix.len();
- if let Some(slice_count) = arity.checked_sub(pat_len) {
+ if let Some(slice_count) = wild_patterns.len().checked_sub(pat_len) {
if slice_count == 0 || slice.is_some() {
Some(
prefix.iter().chain(
- repeat(cx.wild_pattern).take(slice_count).chain(
+ wild_patterns.iter().map(|p| *p)
+ .skip(prefix.len())
+ .take(slice_count)
+ .chain(
suffix.iter()
)).collect())
} else {
}
}
};
- debug!("specialize({:?}, {:?}) = {:?}", r[col], arity, head);
+ debug!("specialize({:?}, {:?}) = {:?}", r[0], wild_patterns, head);
head.map(|mut head| {
- head.extend_from_slice(&r[..col]);
- head.extend_from_slice(&r[col + 1..]);
+ head.extend_from_slice(&r[1 ..]);
head
})
}
// except according to those terms.
use _match::{MatchCheckCtxt, Matrix, expand_pattern, is_useful};
-use _match::{DUMMY_WILD_PAT};
use _match::Usefulness::*;
use _match::WitnessPreference::*;
-use pattern::{Pattern, PatternContext, PatternError};
+use pattern::{Pattern, PatternContext, PatternError, PatternKind};
use eval::report_const_eval_err;
use rustc::middle::mem_categorization::{cmt};
use rustc::session::Session;
use rustc::traits::Reveal;
-use rustc::ty::{self, TyCtxt};
-use rustc_errors::DiagnosticBuilder;
+use rustc::ty::{self, Ty, TyCtxt};
+use rustc::lint;
+use rustc_errors::{Diagnostic, Level, DiagnosticBuilder};
use rustc::hir::def::*;
use rustc::hir::intravisit::{self, Visitor, FnKind, NestedVisitorMap};
use syntax::ast;
use syntax::ptr::P;
-use syntax_pos::Span;
+use syntax_pos::{Span, DUMMY_SP};
struct OuterVisitor<'a, 'tcx: 'a> { tcx: TyCtxt<'a, 'tcx, 'tcx> }
match ex.node {
hir::ExprMatch(ref scrut, ref arms, source) => {
- self.check_match(scrut, arms, source, ex.span);
+ self.check_match(scrut, arms, source);
}
_ => {}
}
fn report_inlining_errors(&self, patcx: PatternContext, pat_span: Span) {
for error in patcx.errors {
match error {
- PatternError::BadConstInPattern(span, def_id) => {
- self.tcx.sess.span_err(
- span,
- &format!("constants of the type `{}` \
- cannot be used in patterns",
- self.tcx.item_path_str(def_id)));
- }
PatternError::StaticInPattern(span) => {
span_err!(self.tcx.sess, span, E0158,
"statics cannot be referenced in patterns");
&self,
scrut: &hir::Expr,
arms: &[hir::Arm],
- source: hir::MatchSource,
- span: Span)
+ source: hir::MatchSource)
{
for arm in arms {
// First, check legality of move bindings.
}
}
- MatchCheckCtxt::create_and_enter(self.tcx, |ref mut cx| {
+ let module = self.tcx.map.local_def_id(self.tcx.map.get_module_parent(scrut.id));
+ MatchCheckCtxt::create_and_enter(self.tcx, module, |ref mut cx| {
let mut have_errors = false;
let inlined_arms : Vec<(Vec<_>, _)> = arms.iter().map(|arm| (
// Fourth, check for unreachable arms.
check_arms(cx, &inlined_arms, source);
- // Finally, check if the whole match expression is exhaustive.
- // Check for empty enum, because is_useful only works on inhabited types.
- let pat_ty = self.tcx.tables().node_id_to_type(scrut.id);
- if inlined_arms.is_empty() {
- if !pat_ty.is_uninhabited(Some(scrut.id), self.tcx) {
- // We know the type is inhabited, so this must be wrong
- let mut err = create_e0004(self.tcx.sess, span,
- format!("non-exhaustive patterns: type {} \
- is non-empty",
- pat_ty));
- span_help!(&mut err, span,
- "Please ensure that all possible cases are being handled; \
- possibly adding wildcards or more match arms.");
- err.emit();
- }
- // If the type *is* uninhabited, it's vacuously exhaustive
- return;
- }
-
let matrix: Matrix = inlined_arms
.iter()
.filter(|&&(_, guard)| guard.is_none())
.flat_map(|arm| &arm.0)
.map(|pat| vec![pat.0])
.collect();
- check_exhaustive(cx, scrut.span, &matrix, source);
+ let scrut_ty = cx.tcx.tables().node_id_to_type(scrut.id);
+ check_exhaustive(cx, scrut_ty, scrut.span, &matrix, source);
})
}
"local binding"
};
- MatchCheckCtxt::create_and_enter(self.tcx, |ref mut cx| {
+ let module = self.tcx.map.local_def_id(self.tcx.map.get_module_parent(pat.id));
+ MatchCheckCtxt::create_and_enter(self.tcx, module, |ref mut cx| {
let mut patcx = PatternContext::new(self.tcx);
+ let pattern = patcx.lower_pattern(pat);
+ let pattern_ty = pattern.ty;
let pats : Matrix = vec![vec![
- expand_pattern(cx, patcx.lower_pattern(pat))
+ expand_pattern(cx, pattern)
]].into_iter().collect();
- let witness = match is_useful(cx, &pats, &[cx.wild_pattern], ConstructWitness) {
+ let wild_pattern = Pattern {
+ ty: pattern_ty,
+ span: DUMMY_SP,
+ kind: box PatternKind::Wild,
+ };
+ let witness = match is_useful(cx, &pats, &[&wild_pattern], ConstructWitness) {
UsefulWithWitness(witness) => witness,
NotUseful => return,
Useful => bug!()
};
- let pattern_string = hir::print::to_string(&self.tcx.map, |s| {
- s.print_pat(witness[0].single_pattern())
- });
+ let pattern_string = witness[0].single_pattern().to_string();
let mut diag = struct_span_err!(
self.tcx.sess, pat.span, E0005,
"refutable pattern in {}: `{}` not covered",
},
hir::MatchSource::Normal => {
- let mut err = struct_span_err!(cx.tcx.sess, pat.span, E0001,
- "unreachable pattern");
- err.span_label(pat.span, &"this is an unreachable pattern");
+ let mut diagnostic = Diagnostic::new(Level::Warning,
+ "unreachable pattern");
+ diagnostic.set_span(pat.span);
// if we had a catchall pattern, hint at that
if let Some(catchall) = catchall {
- err.span_note(catchall, "this pattern matches any value");
+ diagnostic.span_label(pat.span, &"this is an unreachable pattern");
+ diagnostic.span_note(catchall, "this pattern matches any value");
}
- err.emit();
+ cx.tcx.sess.add_lint_diagnostic(lint::builtin::UNREACHABLE_PATTERNS,
+ hir_pat.id, diagnostic);
},
hir::MatchSource::TryDesugar => {
}
fn check_exhaustive<'a, 'tcx>(cx: &mut MatchCheckCtxt<'a, 'tcx>,
+ scrut_ty: Ty<'tcx>,
sp: Span,
matrix: &Matrix<'a, 'tcx>,
source: hir::MatchSource) {
- match is_useful(cx, matrix, &[cx.wild_pattern], ConstructWitness) {
+ let wild_pattern = Pattern {
+ ty: scrut_ty,
+ span: DUMMY_SP,
+ kind: box PatternKind::Wild,
+ };
+ match is_useful(cx, matrix, &[&wild_pattern], ConstructWitness) {
UsefulWithWitness(pats) => {
let witnesses = if pats.is_empty() {
- vec![DUMMY_WILD_PAT]
+ vec![&wild_pattern]
} else {
pats.iter().map(|w| w.single_pattern()).collect()
};
match source {
hir::MatchSource::ForLoopDesugar => {
// `witnesses[0]` has the form `Some(<head>)`, peel off the `Some`
- let witness = match witnesses[0].node {
- PatKind::TupleStruct(_, ref pats, _) => match &pats[..] {
- &[ref pat] => &**pat,
+ let witness = match *witnesses[0].kind {
+ PatternKind::Variant { ref subpatterns, .. } => match &subpatterns[..] {
+ &[ref pat] => &pat.pattern,
_ => bug!(),
},
_ => bug!(),
};
- let pattern_string = hir::print::to_string(&cx.tcx.map, |s| {
- s.print_pat(witness)
- });
+ let pattern_string = witness.to_string();
struct_span_err!(cx.tcx.sess, sp, E0297,
"refutable pattern in `for` loop binding: \
`{}` not covered",
.emit();
},
_ => {
- let pattern_strings: Vec<_> = witnesses.iter().map(|w| {
- hir::print::to_string(&cx.tcx.map, |s| s.print_pat(w))
- }).collect();
const LIMIT: usize = 3;
- let joined_patterns = match pattern_strings.len() {
+ let joined_patterns = match witnesses.len() {
0 => bug!(),
- 1 => format!("`{}`", pattern_strings[0]),
+ 1 => format!("`{}`", witnesses[0]),
2...LIMIT => {
- let (tail, head) = pattern_strings.split_last().unwrap();
- format!("`{}`", head.join("`, `") + "` and `" + tail)
+ let (tail, head) = witnesses.split_last().unwrap();
+ let head: Vec<_> = head.iter().map(|w| w.to_string()).collect();
+ format!("`{}` and `{}`", head.join("`, `"), tail)
},
_ => {
- let (head, tail) = pattern_strings.split_at(LIMIT);
+ let (head, tail) = witnesses.split_at(LIMIT);
+ let head: Vec<_> = head.iter().map(|w| w.to_string()).collect();
format!("`{}` and {} more", head.join("`, `"), tail.len())
}
};
- let label_text = match pattern_strings.len(){
+ let label_text = match witnesses.len() {
1 => format!("pattern {} not covered", joined_patterns),
_ => format!("patterns {} not covered", joined_patterns)
};
register_long_diagnostics! {
E0001: r##"
+## Note: this error code is no longer emitted by the compiler.
+
This error suggests that the expression arm corresponding to the noted pattern
will never be reached as for all possible values of the expression being
matched, one of the preceding patterns will match.
For example, the following `match` block has too many arms:
-```compile_fail,E0001
+```
match Some(0) {
Some(bar) => {/* ... */}
- None => {/* ... */}
+ x => {/* ... */} // This handles the `None` case
_ => {/* ... */} // All possible cases have already been handled
}
```
use rustc::hir::map as ast_map;
use rustc::hir::map::blocks::FnLikeNode;
use rustc::traits;
-use rustc::hir::def::{Def, CtorKind};
+use rustc::hir::def::Def;
use rustc::hir::def_id::DefId;
use rustc::ty::{self, Ty, TyCtxt};
use rustc::ty::util::IntTypeExt;
use rustc::traits::Reveal;
use rustc::util::common::ErrorReported;
use rustc::util::nodemap::DefIdMap;
-use rustc::lint;
use graphviz::IntoCow;
use syntax::ast;
-use rustc::hir::{Expr, PatKind};
-use rustc::hir;
-use syntax::ptr::P;
-use syntax::codemap;
+use rustc::hir::{self, Expr};
use syntax::attr::IntType;
-use syntax_pos::{self, Span};
+use syntax_pos::Span;
use std::borrow::Cow;
use std::cmp::Ordering;
}
}
-pub fn const_expr_to_pat<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
- expr: &Expr,
- pat_id: ast::NodeId,
- span: Span)
- -> Result<P<hir::Pat>, DefId> {
- let pat_ty = tcx.tables().expr_ty(expr);
- debug!("expr={:?} pat_ty={:?} pat_id={}", expr, pat_ty, pat_id);
- match pat_ty.sty {
- ty::TyFloat(_) => {
- tcx.sess.add_lint(
- lint::builtin::ILLEGAL_FLOATING_POINT_CONSTANT_PATTERN,
- pat_id,
- span,
- format!("floating point constants cannot be used in patterns"));
- }
- ty::TyAdt(adt_def, _) if adt_def.is_union() => {
- // Matching on union fields is unsafe, we can't hide it in constants
- tcx.sess.span_err(span, "cannot use unions in constant patterns");
- }
- ty::TyAdt(adt_def, _) => {
- if !tcx.has_attr(adt_def.did, "structural_match") {
- tcx.sess.add_lint(
- lint::builtin::ILLEGAL_STRUCT_OR_ENUM_CONSTANT_PATTERN,
- pat_id,
- span,
- format!("to use a constant of type `{}` \
- in a pattern, \
- `{}` must be annotated with `#[derive(PartialEq, Eq)]`",
- tcx.item_path_str(adt_def.did),
- tcx.item_path_str(adt_def.did)));
- }
- }
- _ => { }
- }
- let pat = match expr.node {
- hir::ExprTup(ref exprs) =>
- PatKind::Tuple(exprs.iter()
- .map(|expr| const_expr_to_pat(tcx, &expr, pat_id, span))
- .collect::<Result<_, _>>()?, None),
-
- hir::ExprCall(ref callee, ref args) => {
- let qpath = match callee.node {
- hir::ExprPath(ref qpath) => qpath,
- _ => bug!()
- };
- let def = tcx.tables().qpath_def(qpath, callee.id);
- let ctor_path = if let hir::QPath::Resolved(_, ref path) = *qpath {
- match def {
- Def::StructCtor(_, CtorKind::Fn) |
- Def::VariantCtor(_, CtorKind::Fn) => {
- Some(path.clone())
- }
- _ => None
- }
- } else {
- None
- };
- match (def, ctor_path) {
- (Def::Fn(..), None) | (Def::Method(..), None) => {
- PatKind::Lit(P(expr.clone()))
- }
- (_, Some(ctor_path)) => {
- let pats = args.iter()
- .map(|expr| const_expr_to_pat(tcx, expr, pat_id, span))
- .collect::<Result<_, _>>()?;
- PatKind::TupleStruct(hir::QPath::Resolved(None, ctor_path), pats, None)
- }
- _ => bug!()
- }
- }
-
- hir::ExprStruct(ref qpath, ref fields, None) => {
- let field_pats =
- fields.iter()
- .map(|field| Ok(codemap::Spanned {
- span: syntax_pos::DUMMY_SP,
- node: hir::FieldPat {
- name: field.name.node,
- pat: const_expr_to_pat(tcx, &field.expr, pat_id, span)?,
- is_shorthand: false,
- },
- }))
- .collect::<Result<_, _>>()?;
- PatKind::Struct(qpath.clone(), field_pats, false)
- }
-
- hir::ExprArray(ref exprs) => {
- let pats = exprs.iter()
- .map(|expr| const_expr_to_pat(tcx, &expr, pat_id, span))
- .collect::<Result<_, _>>()?;
- PatKind::Slice(pats, None, hir::HirVec::new())
- }
-
- hir::ExprPath(ref qpath) => {
- let def = tcx.tables().qpath_def(qpath, expr.id);
- match def {
- Def::StructCtor(_, CtorKind::Const) |
- Def::VariantCtor(_, CtorKind::Const) => {
- match expr.node {
- hir::ExprPath(hir::QPath::Resolved(_, ref path)) => {
- PatKind::Path(hir::QPath::Resolved(None, path.clone()))
- }
- _ => bug!()
- }
- }
- Def::Const(def_id) | Def::AssociatedConst(def_id) => {
- let substs = Some(tcx.tables().node_id_item_substs(expr.id)
- .unwrap_or_else(|| tcx.intern_substs(&[])));
- let (expr, _ty) = lookup_const_by_id(tcx, def_id, substs).unwrap();
- return const_expr_to_pat(tcx, expr, pat_id, span);
- },
- _ => bug!(),
- }
- }
-
- _ => PatKind::Lit(P(expr.clone()))
- };
- Ok(P(hir::Pat { id: expr.id, node: pat, span: span }))
-}
-
pub fn report_const_eval_err<'a, 'tcx>(
tcx: TyCtxt<'a, 'tcx, 'tcx>,
err: &ConstEvalErr,
use eval;
+use rustc::lint;
use rustc::middle::const_val::ConstVal;
use rustc::mir::{Field, BorrowKind, Mutability};
-use rustc::ty::{self, TyCtxt, AdtDef, Ty, Region};
+use rustc::ty::{self, TyCtxt, AdtDef, Ty, TypeVariants, Region};
+use rustc::ty::subst::{Substs, Kind};
use rustc::hir::{self, PatKind};
-use rustc::hir::def::Def;
-use rustc::hir::def_id::DefId;
+use rustc::hir::def::{Def, CtorKind};
use rustc::hir::pat_util::EnumerateAndAdjustIterator;
use rustc_data_structures::indexed_vec::Idx;
+use std::fmt;
use syntax::ast;
use syntax::ptr::P;
use syntax_pos::Span;
#[derive(Clone, Debug)]
pub enum PatternError {
StaticInPattern(Span),
- BadConstInPattern(Span, DefId),
ConstEval(eval::ConstEvalErr),
}
/// Foo(...) or Foo{...} or Foo, where `Foo` is a variant name from an adt with >1 variants
Variant {
adt_def: &'tcx AdtDef,
+ substs: &'tcx Substs<'tcx>,
variant_index: usize,
subpatterns: Vec<FieldPattern<'tcx>>,
},
},
}
+fn print_const_val(value: &ConstVal, f: &mut fmt::Formatter) -> fmt::Result {
+ match *value {
+ ConstVal::Float(ref x) => write!(f, "{}", x),
+ ConstVal::Integral(ref i) => write!(f, "{}", i),
+ ConstVal::Str(ref s) => write!(f, "{:?}", &s[..]),
+ ConstVal::ByteStr(ref b) => write!(f, "{:?}", &b[..]),
+ ConstVal::Bool(b) => write!(f, "{:?}", b),
+ ConstVal::Char(c) => write!(f, "{:?}", c),
+ ConstVal::Struct(_) |
+ ConstVal::Tuple(_) |
+ ConstVal::Function(_) |
+ ConstVal::Array(..) |
+ ConstVal::Repeat(..) |
+ ConstVal::Dummy => bug!("{:?} not printable in a pattern", value)
+ }
+}
+
+impl<'tcx> fmt::Display for Pattern<'tcx> {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match *self.kind {
+ PatternKind::Wild => write!(f, "_"),
+ PatternKind::Binding { mutability, name, mode, ref subpattern, .. } => {
+ let is_mut = match mode {
+ BindingMode::ByValue => mutability == Mutability::Mut,
+ BindingMode::ByRef(_, bk) => {
+ write!(f, "ref ")?;
+ bk == BorrowKind::Mut
+ }
+ };
+ if is_mut {
+ write!(f, "mut ")?;
+ }
+ write!(f, "{}", name)?;
+ if let Some(ref subpattern) = *subpattern {
+ write!(f, " @ {}", subpattern)?;
+ }
+ Ok(())
+ }
+ PatternKind::Variant { ref subpatterns, .. } |
+ PatternKind::Leaf { ref subpatterns } => {
+ let variant = match *self.kind {
+ PatternKind::Variant { adt_def, variant_index, .. } => {
+ Some(&adt_def.variants[variant_index])
+ }
+ _ => if let ty::TyAdt(adt, _) = self.ty.sty {
+ Some(adt.struct_variant())
+ } else {
+ None
+ }
+ };
+
+ let mut first = true;
+ let mut start_or_continue = || if first { first = false; "" } else { ", " };
+
+ if let Some(variant) = variant {
+ write!(f, "{}", variant.name)?;
+
+ // Only for TyAdt we can have `S {...}`,
+ // which we handle separately here.
+ if variant.ctor_kind == CtorKind::Fictive {
+ write!(f, " {{ ")?;
+
+ let mut printed = 0;
+ for p in subpatterns {
+ if let PatternKind::Wild = *p.pattern.kind {
+ continue;
+ }
+ let name = variant.fields[p.field.index()].name;
+ write!(f, "{}{}: {}", start_or_continue(), name, p.pattern)?;
+ printed += 1;
+ }
+
+ if printed < variant.fields.len() {
+ write!(f, "{}..", start_or_continue())?;
+ }
+
+ return write!(f, " }}");
+ }
+ }
+
+ let num_fields = variant.map_or(subpatterns.len(), |v| v.fields.len());
+ if num_fields != 0 || variant.is_none() {
+ write!(f, "(")?;
+ for i in 0..num_fields {
+ write!(f, "{}", start_or_continue())?;
+
+ // Common case: the field is where we expect it.
+ if let Some(p) = subpatterns.get(i) {
+ if p.field.index() == i {
+ write!(f, "{}", p.pattern)?;
+ continue;
+ }
+ }
+
+ // Otherwise, we have to go looking for it.
+ if let Some(p) = subpatterns.iter().find(|p| p.field.index() == i) {
+ write!(f, "{}", p.pattern)?;
+ } else {
+ write!(f, "_")?;
+ }
+ }
+ write!(f, ")")?;
+ }
+
+ Ok(())
+ }
+ PatternKind::Deref { ref subpattern } => {
+ match self.ty.sty {
+ ty::TyBox(_) => write!(f, "box ")?,
+ ty::TyRef(_, mt) => {
+ write!(f, "&")?;
+ if mt.mutbl == hir::MutMutable {
+ write!(f, "mut ")?;
+ }
+ }
+ _ => bug!("{} is a bad Deref pattern type", self.ty)
+ }
+ write!(f, "{}", subpattern)
+ }
+ PatternKind::Constant { ref value } => {
+ print_const_val(value, f)
+ }
+ PatternKind::Range { ref lo, ref hi } => {
+ print_const_val(lo, f)?;
+ write!(f, "...")?;
+ print_const_val(hi, f)
+ }
+ PatternKind::Slice { ref prefix, ref slice, ref suffix } |
+ PatternKind::Array { ref prefix, ref slice, ref suffix } => {
+ let mut first = true;
+ let mut start_or_continue = || if first { first = false; "" } else { ", " };
+ write!(f, "[")?;
+ for p in prefix {
+ write!(f, "{}{}", start_or_continue(), p)?;
+ }
+ if let Some(ref slice) = *slice {
+ write!(f, "{}", start_or_continue())?;
+ match *slice.kind {
+ PatternKind::Wild => {}
+ _ => write!(f, "{}", slice)?
+ }
+ write!(f, "..")?;
+ }
+ for p in suffix {
+ write!(f, "{}{}", start_or_continue(), p)?;
+ }
+ write!(f, "]")
+ }
+ }
+ }
+}
+
pub struct PatternContext<'a, 'gcx: 'tcx, 'tcx: 'a> {
pub tcx: TyCtxt<'a, 'gcx, 'tcx>,
pub errors: Vec<PatternError>,
let kind = match pat.node {
PatKind::Wild => PatternKind::Wild,
- PatKind::Lit(ref value) => {
- match eval::eval_const_expr_checked(self.tcx.global_tcx(), value) {
- Ok(value) => {
- PatternKind::Constant { value: value }
- }
- Err(e) => {
- self.errors.push(PatternError::ConstEval(e));
- PatternKind::Wild
- }
- }
- }
+ PatKind::Lit(ref value) => self.lower_lit(value),
PatKind::Range(ref lo, ref hi) => {
- let r_lo = eval::eval_const_expr_checked(self.tcx.global_tcx(), lo);
- if let Err(ref e_lo) = r_lo {
- self.errors.push(PatternError::ConstEval(e_lo.clone()));
- }
-
- let r_hi = eval::eval_const_expr_checked(self.tcx.global_tcx(), hi);
- if let Err(ref e_hi) = r_hi {
- self.errors.push(PatternError::ConstEval(e_hi.clone()));
- }
-
- if let (Ok(lo), Ok(hi)) = (r_lo, r_hi) {
- PatternKind::Range { lo: lo, hi: hi }
- } else {
- PatternKind::Wild
+ match (self.lower_lit(lo), self.lower_lit(hi)) {
+ (PatternKind::Constant { value: lo },
+ PatternKind::Constant { value: hi }) => {
+ PatternKind::Range { lo: lo, hi: hi }
+ }
+ _ => PatternKind::Wild
}
}
PatKind::Path(ref qpath) => {
- let def = self.tcx.tables().qpath_def(qpath, pat.id);
- match def {
- Def::Const(def_id) | Def::AssociatedConst(def_id) => {
- let tcx = self.tcx.global_tcx();
- let substs = tcx.tables().node_id_item_substs(pat.id)
- .unwrap_or_else(|| tcx.intern_substs(&[]));
- match eval::lookup_const_by_id(tcx, def_id, Some(substs)) {
- Some((const_expr, _const_ty)) => {
- match eval::const_expr_to_pat(
- tcx, const_expr, pat.id, pat.span)
- {
- Ok(pat) => return self.lower_pattern(&pat),
- Err(_) => {
- self.errors.push(PatternError::BadConstInPattern(
- pat.span, def_id));
- PatternKind::Wild
- }
- }
- }
- None => {
- self.errors.push(PatternError::StaticInPattern(pat.span));
- PatternKind::Wild
- }
- }
- }
- _ => self.lower_variant_or_leaf(def, vec![])
- }
+ return self.lower_path(qpath, pat.id, pat.id, pat.span);
}
PatKind::Ref(ref subpattern, _) |
PatKind::TupleStruct(ref qpath, ref subpatterns, ddpos) => {
let def = self.tcx.tables().qpath_def(qpath, pat.id);
- let pat_ty = self.tcx.tables().node_id_to_type(pat.id);
- let adt_def = match pat_ty.sty {
+ let adt_def = match ty.sty {
ty::TyAdt(adt_def, _) => adt_def,
_ => span_bug!(pat.span, "tuple struct pattern not applied to an ADT"),
};
pattern: self.lower_pattern(field),
})
.collect();
- self.lower_variant_or_leaf(def, subpatterns)
+ self.lower_variant_or_leaf(def, ty, subpatterns)
}
PatKind::Struct(ref qpath, ref fields, _) => {
let def = self.tcx.tables().qpath_def(qpath, pat.id);
- let pat_ty = self.tcx.tables().node_id_to_type(pat.id);
- let adt_def = match pat_ty.sty {
+ let adt_def = match ty.sty {
ty::TyAdt(adt_def, _) => adt_def,
_ => {
span_bug!(
})
.collect();
- self.lower_variant_or_leaf(def, subpatterns)
+ self.lower_variant_or_leaf(def, ty, subpatterns)
}
};
fn lower_variant_or_leaf(
&mut self,
def: Def,
+ ty: Ty<'tcx>,
subpatterns: Vec<FieldPattern<'tcx>>)
-> PatternKind<'tcx>
{
let enum_id = self.tcx.parent_def_id(variant_id).unwrap();
let adt_def = self.tcx.lookup_adt_def(enum_id);
if adt_def.variants.len() > 1 {
+ let substs = match ty.sty {
+ TypeVariants::TyAdt(_, substs) => substs,
+ TypeVariants::TyFnDef(_, substs, _) => substs,
+ _ => bug!("inappropriate type for def: {:?}", ty.sty),
+ };
PatternKind::Variant {
adt_def: adt_def,
+ substs: substs,
variant_index: adt_def.variant_index_with_id(variant_id),
subpatterns: subpatterns,
}
_ => bug!()
}
}
+
+ fn lower_path(&mut self,
+ qpath: &hir::QPath,
+ id: ast::NodeId,
+ pat_id: ast::NodeId,
+ span: Span)
+ -> Pattern<'tcx> {
+ let ty = self.tcx.tables().node_id_to_type(id);
+ let def = self.tcx.tables().qpath_def(qpath, id);
+ let kind = match def {
+ Def::Const(def_id) | Def::AssociatedConst(def_id) => {
+ let tcx = self.tcx.global_tcx();
+ let substs = tcx.tables().node_id_item_substs(id)
+ .unwrap_or_else(|| tcx.intern_substs(&[]));
+ match eval::lookup_const_by_id(tcx, def_id, Some(substs)) {
+ Some((const_expr, _const_ty)) => {
+ return self.lower_const_expr(const_expr, pat_id, span);
+ }
+ None => {
+ self.errors.push(PatternError::StaticInPattern(span));
+ PatternKind::Wild
+ }
+ }
+ }
+ _ => self.lower_variant_or_leaf(def, ty, vec![]),
+ };
+
+ Pattern {
+ span: span,
+ ty: ty,
+ kind: Box::new(kind),
+ }
+ }
+
+ fn lower_lit(&mut self, expr: &hir::Expr) -> PatternKind<'tcx> {
+ match eval::eval_const_expr_checked(self.tcx.global_tcx(), expr) {
+ Ok(value) => {
+ PatternKind::Constant { value: value }
+ }
+ Err(e) => {
+ self.errors.push(PatternError::ConstEval(e));
+ PatternKind::Wild
+ }
+ }
+ }
+
+ fn lower_const_expr(&mut self,
+ expr: &hir::Expr,
+ pat_id: ast::NodeId,
+ span: Span)
+ -> Pattern<'tcx> {
+ let pat_ty = self.tcx.tables().expr_ty(expr);
+ debug!("expr={:?} pat_ty={:?} pat_id={}", expr, pat_ty, pat_id);
+ match pat_ty.sty {
+ ty::TyFloat(_) => {
+ self.tcx.sess.add_lint(
+ lint::builtin::ILLEGAL_FLOATING_POINT_CONSTANT_PATTERN,
+ pat_id,
+ span,
+ format!("floating point constants cannot be used in patterns"));
+ }
+ ty::TyAdt(adt_def, _) if adt_def.is_union() => {
+ // Matching on union fields is unsafe, we can't hide it in constants
+ self.tcx.sess.span_err(span, "cannot use unions in constant patterns");
+ }
+ ty::TyAdt(adt_def, _) => {
+ if !self.tcx.has_attr(adt_def.did, "structural_match") {
+ self.tcx.sess.add_lint(
+ lint::builtin::ILLEGAL_STRUCT_OR_ENUM_CONSTANT_PATTERN,
+ pat_id,
+ span,
+ format!("to use a constant of type `{}` \
+ in a pattern, \
+ `{}` must be annotated with `#[derive(PartialEq, Eq)]`",
+ self.tcx.item_path_str(adt_def.did),
+ self.tcx.item_path_str(adt_def.did)));
+ }
+ }
+ _ => { }
+ }
+ let kind = match expr.node {
+ hir::ExprTup(ref exprs) => {
+ PatternKind::Leaf {
+ subpatterns: exprs.iter().enumerate().map(|(i, expr)| {
+ FieldPattern {
+ field: Field::new(i),
+ pattern: self.lower_const_expr(expr, pat_id, span)
+ }
+ }).collect()
+ }
+ }
+
+ hir::ExprCall(ref callee, ref args) => {
+ let qpath = match callee.node {
+ hir::ExprPath(ref qpath) => qpath,
+ _ => bug!()
+ };
+ let ty = self.tcx.tables().node_id_to_type(callee.id);
+ let def = self.tcx.tables().qpath_def(qpath, callee.id);
+ match def {
+ Def::Fn(..) | Def::Method(..) => self.lower_lit(expr),
+ _ => {
+ let subpatterns = args.iter().enumerate().map(|(i, expr)| {
+ FieldPattern {
+ field: Field::new(i),
+ pattern: self.lower_const_expr(expr, pat_id, span)
+ }
+ }).collect();
+ self.lower_variant_or_leaf(def, ty, subpatterns)
+ }
+ }
+ }
+
+ hir::ExprStruct(ref qpath, ref fields, None) => {
+ let def = self.tcx.tables().qpath_def(qpath, expr.id);
+ let pat_ty = self.tcx.tables().node_id_to_type(expr.id);
+ let adt_def = match pat_ty.sty {
+ ty::TyAdt(adt_def, _) => adt_def,
+ _ => {
+ span_bug!(
+ expr.span,
+ "struct expr without ADT type");
+ }
+ };
+ let variant_def = adt_def.variant_of_def(def);
+
+ let subpatterns =
+ fields.iter()
+ .map(|field| {
+ let index = variant_def.index_of_field_named(field.name.node);
+ let index = index.unwrap_or_else(|| {
+ span_bug!(
+ expr.span,
+ "no field with name {:?}",
+ field.name);
+ });
+ FieldPattern {
+ field: Field::new(index),
+ pattern: self.lower_const_expr(&field.expr, pat_id, span),
+ }
+ })
+ .collect();
+
+ self.lower_variant_or_leaf(def, pat_ty, subpatterns)
+ }
+
+ hir::ExprArray(ref exprs) => {
+ let pats = exprs.iter()
+ .map(|expr| self.lower_const_expr(expr, pat_id, span))
+ .collect();
+ PatternKind::Array {
+ prefix: pats,
+ slice: None,
+ suffix: vec![]
+ }
+ }
+
+ hir::ExprPath(ref qpath) => {
+ return self.lower_path(qpath, expr.id, pat_id, span);
+ }
+
+ _ => self.lower_lit(expr)
+ };
+
+ Pattern {
+ span: span,
+ ty: pat_ty,
+ kind: Box::new(kind),
+ }
+ }
}
pub trait PatternFoldable<'tcx> : Sized {
}
CloneImpls!{ <'tcx>
- Span, Field, Mutability, ast::Name, ast::NodeId, usize, ConstVal,
- Ty<'tcx>, BindingMode<'tcx>, &'tcx AdtDef
+ Span, Field, Mutability, ast::Name, ast::NodeId, usize, ConstVal, Region,
+ Ty<'tcx>, BindingMode<'tcx>, &'tcx AdtDef,
+ &'tcx Substs<'tcx>, &'tcx Kind<'tcx>
}
impl<'tcx> PatternFoldable<'tcx> for FieldPattern<'tcx> {
},
PatternKind::Variant {
adt_def,
+ substs,
variant_index,
ref subpatterns,
} => PatternKind::Variant {
adt_def: adt_def.fold_with(folder),
+ substs: substs.fold_with(folder),
variant_index: variant_index.fold_with(folder),
subpatterns: subpatterns.fold_with(folder)
},
use std::iter::{self, IntoIterator, FromIterator};
use std::slice;
use std::vec;
+use std::collections::range::RangeArgument;
use rustc_serialize::{Encodable, Encoder, Decodable, Decoder};
AccumulateVec::Heap(ref mut vec) => vec.pop(),
}
}
+
+ pub fn drain<R>(&mut self, range: R) -> Drain<A>
+ where R: RangeArgument<usize>
+ {
+ match *self {
+ AccumulateVec::Array(ref mut v) => {
+ Drain::Array(v.drain(range))
+ },
+ AccumulateVec::Heap(ref mut v) => {
+ Drain::Heap(v.drain(range))
+ },
+ }
+ }
}
impl<A: Array> Deref for AccumulateVec<A> {
}
}
+pub enum Drain<'a, A: Array>
+ where A::Element: 'a
+{
+ Array(array_vec::Drain<'a, A>),
+ Heap(vec::Drain<'a, A::Element>),
+}
+
+impl<'a, A: Array> Iterator for Drain<'a, A> {
+ type Item = A::Element;
+
+ fn next(&mut self) -> Option<A::Element> {
+ match *self {
+ Drain::Array(ref mut drain) => drain.next(),
+ Drain::Heap(ref mut drain) => drain.next(),
+ }
+ }
+
+ fn size_hint(&self) -> (usize, Option<usize>) {
+ match *self {
+ Drain::Array(ref drain) => drain.size_hint(),
+ Drain::Heap(ref drain) => drain.size_hint(),
+ }
+ }
+}
+
impl<A: Array> IntoIterator for AccumulateVec<A> {
type Item = A::Element;
type IntoIter = IntoIter<A>;
use std::marker::Unsize;
use std::iter::Extend;
-use std::ptr::{self, drop_in_place};
+use std::ptr::{self, drop_in_place, Shared};
use std::ops::{Deref, DerefMut, Range};
use std::hash::{Hash, Hasher};
use std::slice;
use std::fmt;
use std::mem;
+use std::collections::range::RangeArgument;
pub unsafe trait Array {
type Element;
None
}
}
+
+ pub fn drain<R>(&mut self, range: R) -> Drain<A>
+ where R: RangeArgument<usize>
+ {
+ // Memory safety
+ //
+ // When the Drain is first created, it shortens the length of
+ // the source vector to make sure no uninitalized or moved-from elements
+ // are accessible at all if the Drain's destructor never gets to run.
+ //
+ // Drain will ptr::read out the values to remove.
+ // When finished, remaining tail of the vec is copied back to cover
+ // the hole, and the vector length is restored to the new length.
+ //
+ let len = self.len();
+ let start = *range.start().unwrap_or(&0);
+ let end = *range.end().unwrap_or(&len);
+ assert!(start <= end);
+ assert!(end <= len);
+
+ unsafe {
+ // set self.vec length's to start, to be safe in case Drain is leaked
+ self.set_len(start);
+ // Use the borrow in the IterMut to indicate borrowing behavior of the
+ // whole Drain iterator (like &mut T).
+ let range_slice = {
+ let arr = &mut self.values as &mut [ManuallyDrop<_>];
+ slice::from_raw_parts_mut(arr.as_mut_ptr().offset(start as isize),
+ end - start)
+ };
+ Drain {
+ tail_start: end,
+ tail_len: len - end,
+ iter: range_slice.iter(),
+ array_vec: Shared::new(self as *mut _),
+ }
+ }
+ }
}
impl<A> Default for ArrayVec<A>
}
}
+pub struct Drain<'a, A: Array>
+ where A::Element: 'a
+{
+ tail_start: usize,
+ tail_len: usize,
+ iter: slice::Iter<'a, ManuallyDrop<A::Element>>,
+ array_vec: Shared<ArrayVec<A>>,
+}
+
+impl<'a, A: Array> Iterator for Drain<'a, A> {
+ type Item = A::Element;
+
+ #[inline]
+ fn next(&mut self) -> Option<A::Element> {
+ self.iter.next().map(|elt| unsafe { ptr::read(elt as *const ManuallyDrop<_>).value })
+ }
+
+ fn size_hint(&self) -> (usize, Option<usize>) {
+ self.iter.size_hint()
+ }
+}
+
+impl<'a, A: Array> Drop for Drain<'a, A> {
+ fn drop(&mut self) {
+ // exhaust self first
+ while let Some(_) = self.next() {}
+
+ if self.tail_len > 0 {
+ unsafe {
+ let source_array_vec = &mut **self.array_vec;
+ // memmove back untouched tail, update to new length
+ let start = source_array_vec.len();
+ let tail = self.tail_start;
+ {
+ let mut arr = &mut source_array_vec.values as &mut [ManuallyDrop<_>];
+ let src = arr.as_ptr().offset(tail as isize);
+ let dst = arr.as_mut_ptr().offset(start as isize);
+ ptr::copy(src, dst, self.tail_len);
+ };
+ source_array_vec.set_len(start + self.tail_len);
+ }
+ }
+ }
+}
+
impl<A: Array> IntoIterator for ArrayVec<A> {
type Item = A::Element;
type IntoIter = Iter<A>;
html_root_url = "https://doc.rust-lang.org/nightly/")]
#![cfg_attr(not(stage0), deny(warnings))]
+#![feature(shared)]
+#![feature(collections_range)]
#![feature(nonzero)]
#![feature(rustc_private)]
#![feature(staged_api)]
is_proc_macro_crate,
is_test_crate,
num_crate_types,
- sess.diagnostic(),
- &sess.features.borrow())
+ sess.diagnostic())
});
}
DEAD_CODE,
UNUSED_MUT,
UNREACHABLE_CODE,
+ UNREACHABLE_PATTERNS,
UNUSED_MUST_USE,
UNUSED_UNSAFE,
PATH_STATEMENTS,
use rustc_i128::{i128, u128};
-register_long_diagnostics! {
-E0519: r##"
-It is not allowed to negate an unsigned integer.
-You can negate a signed integer and cast it to an
-unsigned integer or use the `!` operator.
-
-```
-let x: usize = -1isize as usize;
-let y: usize = !0;
-assert_eq!(x, y);
-```
-
-Alternatively you can use the `Wrapping` newtype
-or the `wrapping_neg` operation that all
-integral types support:
-
-```
-use std::num::Wrapping;
-let x: Wrapping<usize> = -Wrapping(1);
-let Wrapping(x) = x;
-let y: usize = 1.wrapping_neg();
-assert_eq!(x, y);
-```
-"##
-}
-
declare_lint! {
UNUSED_COMPARISONS,
Warn,
fn check_expr(&mut self, cx: &LateContext, e: &hir::Expr) {
match e.node {
hir::ExprUnary(hir::UnNeg, ref expr) => {
- if let hir::ExprLit(ref lit) = expr.node {
- match lit.node {
- ast::LitKind::Int(_, ast::LitIntType::Unsigned(_)) => {
- forbid_unsigned_negation(cx, e.span);
- }
- ast::LitKind::Int(_, ast::LitIntType::Unsuffixed) => {
- if let ty::TyUint(_) = cx.tcx.tables().node_id_to_type(e.id).sty {
- forbid_unsigned_negation(cx, e.span);
- }
- }
- _ => (),
- }
- } else {
- let t = cx.tcx.tables().node_id_to_type(expr.id);
- if let ty::TyUint(_) = t.sty {
- forbid_unsigned_negation(cx, e.span);
- }
- }
// propagate negation, if the negation itself isn't negated
if self.negated_expr_id != e.id {
self.negated_expr_id = expr.id;
_ => false,
}
}
-
- fn forbid_unsigned_negation(cx: &LateContext, span: Span) {
- cx.sess()
- .struct_span_err_with_code(span, "unary negation of unsigned integer", "E0519")
- .span_help(span, "use a cast or the `!` operator")
- .emit();
- }
}
}
#![feature(conservative_impl_trait)]
#![feature(core_intrinsics)]
#![feature(proc_macro_internals)]
-#![feature(proc_macro_lib)]
#![feature(quote)]
#![feature(rustc_diagnostic_macros)]
#![feature(rustc_private)]
use build::matches::{Binding, MatchPair, Candidate};
use hair::*;
use rustc::mir::*;
+use rustc_data_structures::fx::FxHashSet;
use std::mem;
}
PatternKind::Range { .. } |
- PatternKind::Variant { .. } |
PatternKind::Slice { .. } => {
Err(match_pair)
}
+ PatternKind::Variant { adt_def, substs, variant_index, ref subpatterns } => {
+ let irrefutable = adt_def.variants.iter().enumerate().all(|(i, v)| {
+ i == variant_index || {
+ let mut visited = FxHashSet::default();
+ let node_set = v.uninhabited_from(&mut visited,
+ self.hir.tcx(),
+ substs,
+ adt_def.adt_kind());
+ !node_set.is_empty()
+ }
+ });
+ if irrefutable {
+ let lvalue = match_pair.lvalue.downcast(adt_def, variant_index);
+ candidate.match_pairs.extend(self.field_match_pairs(lvalue, subpatterns));
+ Ok(())
+ } else {
+ Err(match_pair)
+ }
+ },
+
PatternKind::Array { ref prefix, ref slice, ref suffix } => {
self.prefix_slice_suffix(&mut candidate.match_pairs,
&match_pair.lvalue,
/// It is a bug to call this with a simplifyable pattern.
pub fn test<'pat>(&mut self, match_pair: &MatchPair<'pat, 'tcx>) -> Test<'tcx> {
match *match_pair.pattern.kind {
- PatternKind::Variant { ref adt_def, variant_index: _, subpatterns: _ } => {
+ PatternKind::Variant { ref adt_def, substs: _, variant_index: _, subpatterns: _ } => {
Test {
span: match_pair.pattern.span,
kind: TestKind::Switch {
// If we are performing a variant switch, then this
// informs variant patterns, but nothing else.
(&TestKind::Switch { adt_def: tested_adt_def, .. },
- &PatternKind::Variant { adt_def, variant_index, ref subpatterns }) => {
+ &PatternKind::Variant { adt_def, variant_index, ref subpatterns, .. }) => {
assert_eq!(adt_def, tested_adt_def);
let new_candidate =
self.candidate_after_variant_switch(match_pair_index,
/// A "root candidate" for promotion, which will become the
/// returned value in a promoted MIR, unless it's a subset
/// of a larger candidate.
+#[derive(Debug)]
pub enum Candidate {
/// Borrow of a constant temporary.
Ref(Location),
/// promoted MIR, recursing through temps.
fn promote_temp(&mut self, temp: Local) -> Local {
let old_keep_original = self.keep_original;
- let (bb, stmt_idx) = match self.temps[temp] {
- TempState::Defined {
- location: Location { block, statement_index },
- uses
- } if uses > 0 => {
+ let loc = match self.temps[temp] {
+ TempState::Defined { location, uses } if uses > 0 => {
if uses > 1 {
self.keep_original = true;
}
- (block, statement_index)
+ location
}
state => {
span_bug!(self.promoted.span, "{:?} not promotable: {:?}",
self.temps[temp] = TempState::PromotedOut;
}
- let no_stmts = self.source[bb].statements.len();
+ let no_stmts = self.source[loc.block].statements.len();
+ let new_temp = self.promoted.local_decls.push(
+ LocalDecl::new_temp(self.source.local_decls[temp].ty));
+
+ debug!("promote({:?} @ {:?}/{:?}, {:?})",
+ temp, loc, no_stmts, self.keep_original);
// First, take the Rvalue or Call out of the source MIR,
// or duplicate it, depending on keep_original.
- let (mut rvalue, mut call) = (None, None);
- let source_info = if stmt_idx < no_stmts {
- let statement = &mut self.source[bb].statements[stmt_idx];
- let rhs = match statement.kind {
- StatementKind::Assign(_, ref mut rhs) => rhs,
- _ => {
- span_bug!(statement.source_info.span, "{:?} is not an assignment",
- statement);
- }
+ if loc.statement_index < no_stmts {
+ let (mut rvalue, source_info) = {
+ let statement = &mut self.source[loc.block].statements[loc.statement_index];
+ let rhs = match statement.kind {
+ StatementKind::Assign(_, ref mut rhs) => rhs,
+ _ => {
+ span_bug!(statement.source_info.span, "{:?} is not an assignment",
+ statement);
+ }
+ };
+
+ (if self.keep_original {
+ rhs.clone()
+ } else {
+ let unit = Rvalue::Aggregate(AggregateKind::Tuple, vec![]);
+ mem::replace(rhs, unit)
+ }, statement.source_info)
};
- if self.keep_original {
- rvalue = Some(rhs.clone());
- } else {
- let unit = Rvalue::Aggregate(AggregateKind::Tuple, vec![]);
- rvalue = Some(mem::replace(rhs, unit));
- }
- statement.source_info
- } else if self.keep_original {
- let terminator = self.source[bb].terminator().clone();
- call = Some(terminator.kind);
- terminator.source_info
+
+ self.visit_rvalue(&mut rvalue, loc);
+ self.assign(new_temp, rvalue, source_info.span);
} else {
- let terminator = self.source[bb].terminator_mut();
- let target = match terminator.kind {
- TerminatorKind::Call {
- destination: ref mut dest @ Some(_),
- ref mut cleanup, ..
- } => {
- // No cleanup necessary.
- cleanup.take();
-
- // We'll put a new destination in later.
- dest.take().unwrap().1
- }
- ref kind => {
- span_bug!(terminator.source_info.span, "{:?} not promotable", kind);
+ let mut terminator = if self.keep_original {
+ self.source[loc.block].terminator().clone()
+ } else {
+ let terminator = self.source[loc.block].terminator_mut();
+ let target = match terminator.kind {
+ TerminatorKind::Call { destination: Some((_, target)), .. } => target,
+ ref kind => {
+ span_bug!(terminator.source_info.span, "{:?} not promotable", kind);
+ }
+ };
+ Terminator {
+ source_info: terminator.source_info,
+ kind: mem::replace(&mut terminator.kind, TerminatorKind::Goto {
+ target: target
+ })
}
};
- call = Some(mem::replace(&mut terminator.kind, TerminatorKind::Goto {
- target: target
- }));
- terminator.source_info
- };
- // Then, recurse for components in the Rvalue or Call.
- if stmt_idx < no_stmts {
- self.visit_rvalue(rvalue.as_mut().unwrap(), Location {
- block: bb,
- statement_index: stmt_idx
- });
- } else {
- self.visit_terminator_kind(bb, call.as_mut().unwrap(), Location {
- block: bb,
- statement_index: no_stmts
- });
- }
-
- let new_temp = self.promoted.local_decls.push(
- LocalDecl::new_temp(self.source.local_decls[temp].ty));
-
- // Inject the Rvalue or Call into the promoted MIR.
- if stmt_idx < no_stmts {
- self.assign(new_temp, rvalue.unwrap(), source_info.span);
- } else {
let last = self.promoted.basic_blocks().last().unwrap();
let new_target = self.new_block();
- let mut call = call.unwrap();
- match call {
- TerminatorKind::Call { ref mut destination, ..} => {
- *destination = Some((Lvalue::Local(new_temp), new_target));
+
+ terminator.kind = match terminator.kind {
+ TerminatorKind::Call { mut func, mut args, .. } => {
+ self.visit_operand(&mut func, loc);
+ for arg in &mut args {
+ self.visit_operand(arg, loc);
+ }
+ TerminatorKind::Call {
+ func: func,
+ args: args,
+ cleanup: None,
+ destination: Some((Lvalue::Local(new_temp), new_target))
+ }
}
- _ => bug!()
- }
- let terminator = self.promoted[last].terminator_mut();
- terminator.source_info.span = source_info.span;
- terminator.kind = call;
- }
+ ref kind => {
+ span_bug!(terminator.source_info.span, "{:?} not promotable", kind);
+ }
+ };
- // Restore the old duplication state.
- self.keep_original = old_keep_original;
+ *self.promoted[last].terminator_mut() = terminator;
+ };
+ self.keep_original = old_keep_original;
new_temp
}
mut temps: IndexVec<Local, TempState>,
candidates: Vec<Candidate>) {
// Visit candidates in reverse, in case they're nested.
+ debug!("promote_candidates({:?})", candidates);
for candidate in candidates.into_iter().rev() {
let (span, ty) = match candidate {
Candidate::Ref(Location { block: bb, statement_index: stmt_idx }) => {
Entry::Vacant(entry) => {
// Guard against `const` recursion.
entry.insert(Qualif::RECURSIVE);
+ Mode::Const
}
}
- Mode::Const
}
MirSource::Static(_, hir::MutImmutable) => Mode::Static,
MirSource::Static(_, hir::MutMutable) => Mode::StaticMut,
PathResult::Indeterminate => bug!("indetermined path result in resolve_qpath"),
};
- if path.len() == 1 || global_by_default || result.base_def == Def::Err {
- return Some(result);
- }
-
- let unqualified_result = {
- match self.resolve_path(&[*path.last().unwrap()], Some(ns), None) {
- PathResult::NonModule(path_res) => path_res.base_def,
- PathResult::Module(module) => module.def().unwrap(),
- _ => return Some(result),
+ if path.len() > 1 && !global_by_default && result.base_def != Def::Err &&
+ path[0].name != keywords::CrateRoot.name() && path[0].name != "$crate" {
+ let unqualified_result = {
+ match self.resolve_path(&[*path.last().unwrap()], Some(ns), None) {
+ PathResult::NonModule(path_res) => path_res.base_def,
+ PathResult::Module(module) => module.def().unwrap(),
+ _ => return Some(result),
+ }
+ };
+ if result.base_def == unqualified_result {
+ let lint = lint::builtin::UNUSED_QUALIFICATIONS;
+ self.session.add_lint(lint, id, span, "unnecessary qualification".to_string());
}
- };
- if result.base_def == unqualified_result && path[0].name != "$crate" {
- let lint = lint::builtin::UNUSED_QUALIFICATIONS;
- self.session.add_lint(lint, id, span, "unnecessary qualification".to_string());
}
Some(result)
use rustc::lint::builtin::PRIVATE_IN_PUBLIC;
use rustc::hir::def_id::DefId;
use rustc::hir::def::*;
+use rustc::util::nodemap::FxHashSet;
use syntax::ast::{Ident, NodeId};
use syntax::ext::base::Determinacy::{self, Determined, Undetermined};
let mut reexports = Vec::new();
if module as *const _ == self.graph_root as *const _ {
- reexports = mem::replace(&mut self.macro_exports, Vec::new());
+ let mut exported_macro_names = FxHashSet();
+ for export in mem::replace(&mut self.macro_exports, Vec::new()).into_iter().rev() {
+ if exported_macro_names.insert(export.name) {
+ reexports.push(export);
+ }
+ }
}
for (&(ident, ns), resolution) in module.resolutions.borrow().iter() {
use llvm::{self, ValueRef, Integer, Pointer, Float, Double, Struct, Array, Vector, AttributePlace};
use base;
-use common::{type_is_fat_ptr, BlockAndBuilder, C_uint};
+use builder::Builder;
+use common::{type_is_fat_ptr, C_uint};
use context::CrateContext;
use cabi_x86;
use cabi_x86_64;
/// lvalue for the original Rust type of this argument/return.
/// Can be used for both storing formal arguments into Rust variables
/// or results of call/invoke instructions into their destinations.
- pub fn store(&self, bcx: &BlockAndBuilder, mut val: ValueRef, dst: ValueRef) {
+ pub fn store(&self, bcx: &Builder, mut val: ValueRef, dst: ValueRef) {
if self.is_ignore() {
return;
}
let can_store_through_cast_ptr = false;
if can_store_through_cast_ptr {
let cast_dst = bcx.pointercast(dst, ty.ptr_to());
- let store = bcx.store(val, cast_dst);
let llalign = llalign_of_min(ccx, self.ty);
- unsafe {
- llvm::LLVMSetAlignment(store, llalign);
- }
+ bcx.store(val, cast_dst, Some(llalign));
} else {
// The actual return type is a struct, but the ABI
// adaptation code has cast it into some scalar type. The
// bitcasting to the struct type yields invalid cast errors.
// We instead thus allocate some scratch space...
- let llscratch = bcx.fcx().alloca(ty, "abi_cast");
+ let llscratch = bcx.alloca(ty, "abi_cast");
base::Lifetime::Start.call(bcx, llscratch);
// ...where we first store the value...
- bcx.store(val, llscratch);
+ bcx.store(val, llscratch, None);
// ...and then memcpy it to the intended destination.
base::call_memcpy(bcx,
if self.original_ty == Type::i1(ccx) {
val = bcx.zext(val, Type::i8(ccx));
}
- bcx.store(val, dst);
+ bcx.store(val, dst, None);
}
}
- pub fn store_fn_arg(&self, bcx: &BlockAndBuilder, idx: &mut usize, dst: ValueRef) {
+ pub fn store_fn_arg(&self, bcx: &Builder, idx: &mut usize, dst: ValueRef) {
if self.pad.is_some() {
*idx += 1;
}
if self.is_ignore() {
return;
}
- let val = llvm::get_param(bcx.fcx().llfn, *idx as c_uint);
+ let val = llvm::get_param(bcx.llfn(), *idx as c_uint);
*idx += 1;
self.store(bcx, val, dst);
}
use rustc::ty::layout;
use rustc::ty::{self, Ty, AdtKind};
use common::*;
-use glue;
+use builder::Builder;
use base;
use machine;
use monomorphize;
use type_::Type;
use type_of;
-use value::Value;
-
-#[derive(Copy, Clone, PartialEq)]
-pub enum BranchKind {
- Switch,
- Single
-}
-
-#[derive(Copy, Clone)]
-pub struct MaybeSizedValue {
- pub value: ValueRef,
- pub meta: ValueRef,
-}
-
-impl MaybeSizedValue {
- pub fn sized(value: ValueRef) -> MaybeSizedValue {
- MaybeSizedValue {
- value: value,
- meta: std::ptr::null_mut()
- }
- }
-
- pub fn unsized_(value: ValueRef, meta: ValueRef) -> MaybeSizedValue {
- MaybeSizedValue {
- value: value,
- meta: meta
- }
- }
-
- pub fn has_meta(&self) -> bool {
- !self.meta.is_null()
- }
-}
/// Given an enum, struct, closure, or tuple, extracts fields.
/// Treats closures as a struct with one variant.
/// `empty_if_no_variants` is a switch to deal with empty enums.
/// If true, `variant_index` is disregarded and an empty Vec returned in this case.
-fn compute_fields<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>,
- variant_index: usize,
- empty_if_no_variants: bool) -> Vec<Ty<'tcx>> {
+pub fn compute_fields<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>,
+ variant_index: usize,
+ empty_if_no_variants: bool) -> Vec<Ty<'tcx>> {
match t.sty {
ty::TyAdt(ref def, _) if def.variants.len() == 0 && empty_if_no_variants => {
Vec::default()
}
}
-/// Obtain a representation of the discriminant sufficient to translate
-/// destructuring; this may or may not involve the actual discriminant.
-pub fn trans_switch<'a, 'tcx>(
- bcx: &BlockAndBuilder<'a, 'tcx>,
- t: Ty<'tcx>,
- scrutinee: ValueRef,
- range_assert: bool
-) -> (BranchKind, Option<ValueRef>) {
- let l = bcx.ccx.layout_of(t);
- match *l {
- layout::CEnum { .. } | layout::General { .. } |
- layout::RawNullablePointer { .. } | layout::StructWrappedNullablePointer { .. } => {
- (BranchKind::Switch, Some(trans_get_discr(bcx, t, scrutinee, None, range_assert)))
- }
- layout::Univariant { .. } | layout::UntaggedUnion { .. } => {
- // N.B.: Univariant means <= 1 enum variants (*not* == 1 variants).
- (BranchKind::Single, None)
- },
- _ => bug!("{} is not an enum.", t)
- }
-}
-
pub fn is_discr_signed<'tcx>(l: &layout::Layout) -> bool {
match *l {
layout::CEnum { signed, .. }=> signed,
/// Obtain the actual discriminant of a value.
pub fn trans_get_discr<'a, 'tcx>(
- bcx: &BlockAndBuilder<'a, 'tcx>,
+ bcx: &Builder<'a, 'tcx>,
t: Ty<'tcx>,
scrutinee: ValueRef,
cast_to: Option<Type>,
layout::RawNullablePointer { nndiscr, .. } => {
let cmp = if nndiscr == 0 { IntEQ } else { IntNE };
let llptrty = type_of::sizing_type_of(bcx.ccx,
- monomorphize::field_ty(bcx.ccx.tcx(), substs,
+ monomorphize::field_ty(bcx.tcx(), substs,
&def.variants[nndiscr as usize].fields[0]));
bcx.icmp(cmp, bcx.load(scrutinee), C_null(llptrty))
}
}
fn struct_wrapped_nullable_bitdiscr(
- bcx: &BlockAndBuilder,
+ bcx: &Builder,
nndiscr: u64,
discrfield: &layout::FieldPath,
scrutinee: ValueRef
}
/// Helper for cases where the discriminant is simply loaded.
-fn load_discr(bcx: &BlockAndBuilder, ity: layout::Integer, ptr: ValueRef, min: u64, max: u64,
+fn load_discr(bcx: &Builder, ity: layout::Integer, ptr: ValueRef, min: u64, max: u64,
range_assert: bool)
-> ValueRef {
let llty = Type::from_integer(bcx.ccx, ity);
/// discriminant-like value returned by `trans_switch`.
///
/// This should ideally be less tightly tied to `_match`.
-pub fn trans_case<'a, 'tcx>(bcx: &BlockAndBuilder<'a, 'tcx>, t: Ty<'tcx>, value: Disr) -> ValueRef {
+pub fn trans_case<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, t: Ty<'tcx>, value: Disr) -> ValueRef {
let l = bcx.ccx.layout_of(t);
match *l {
layout::CEnum { discr, .. }
/// Set the discriminant for a new value of the given case of the given
/// representation.
-pub fn trans_set_discr<'a, 'tcx>(
- bcx: &BlockAndBuilder<'a, 'tcx>, t: Ty<'tcx>, val: ValueRef, to: Disr
-) {
+pub fn trans_set_discr<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, t: Ty<'tcx>, val: ValueRef, to: Disr) {
let l = bcx.ccx.layout_of(t);
match *l {
layout::CEnum{ discr, min, max, .. } => {
assert_discr_in_range(Disr(min), Disr(max), to);
bcx.store(C_integral(Type::from_integer(bcx.ccx, discr), to.0, true),
- val);
+ val, None);
}
layout::General{ discr, .. } => {
bcx.store(C_integral(Type::from_integer(bcx.ccx, discr), to.0, true),
- bcx.struct_gep(val, 0));
+ bcx.struct_gep(val, 0), None);
}
layout::Univariant { .. }
| layout::UntaggedUnion { .. }
let nnty = compute_fields(bcx.ccx, t, nndiscr as usize, false)[0];
if to.0 != nndiscr {
let llptrty = type_of::sizing_type_of(bcx.ccx, nnty);
- bcx.store(C_null(llptrty), val);
+ bcx.store(C_null(llptrty), val, None);
}
}
layout::StructWrappedNullablePointer { nndiscr, ref discrfield, ref nonnull, .. } => {
let path = discrfield.iter().map(|&i| i as usize).collect::<Vec<_>>();
let llptrptr = bcx.gepi(val, &path[..]);
let llptrty = val_ty(llptrptr).element_type();
- bcx.store(C_null(llptrty), llptrptr);
+ bcx.store(C_null(llptrty), llptrptr, None);
}
}
}
}
}
-fn target_sets_discr_via_memset<'a, 'tcx>(bcx: &BlockAndBuilder<'a, 'tcx>) -> bool {
+fn target_sets_discr_via_memset<'a, 'tcx>(bcx: &Builder<'a, 'tcx>) -> bool {
bcx.sess().target.target.arch == "arm" || bcx.sess().target.target.arch == "aarch64"
}
-fn assert_discr_in_range(min: Disr, max: Disr, discr: Disr) {
+pub fn assert_discr_in_range(min: Disr, max: Disr, discr: Disr) {
if min <= max {
assert!(min <= discr && discr <= max)
} else {
}
}
-/// Access a field, at a point when the value's case is known.
-pub fn trans_field_ptr<'a, 'tcx>(
- bcx: &BlockAndBuilder<'a, 'tcx>,
- t: Ty<'tcx>,
- val: MaybeSizedValue,
- discr: Disr,
- ix: usize
-) -> ValueRef {
- let l = bcx.ccx.layout_of(t);
- debug!("trans_field_ptr on {} represented as {:#?}", t, l);
- // Note: if this ever needs to generate conditionals (e.g., if we
- // decide to do some kind of cdr-coding-like non-unique repr
- // someday), it will need to return a possibly-new bcx as well.
- match *l {
- layout::Univariant { ref variant, .. } => {
- assert_eq!(discr, Disr(0));
- struct_field_ptr(bcx, &variant,
- &compute_fields(bcx.ccx, t, 0, false),
- val, ix, false)
- }
- layout::Vector { count, .. } => {
- assert_eq!(discr.0, 0);
- assert!((ix as u64) < count);
- bcx.struct_gep(val.value, ix)
- }
- layout::General { discr: d, ref variants, .. } => {
- let mut fields = compute_fields(bcx.ccx, t, discr.0 as usize, false);
- fields.insert(0, d.to_ty(&bcx.ccx.tcx(), false));
- struct_field_ptr(bcx, &variants[discr.0 as usize],
- &fields,
- val, ix + 1, true)
- }
- layout::UntaggedUnion { .. } => {
- let fields = compute_fields(bcx.ccx, t, 0, false);
- let ty = type_of::in_memory_type_of(bcx.ccx, fields[ix]);
- bcx.pointercast(val.value, ty.ptr_to())
- }
- layout::RawNullablePointer { nndiscr, .. } |
- layout::StructWrappedNullablePointer { nndiscr, .. } if discr.0 != nndiscr => {
- let nullfields = compute_fields(bcx.ccx, t, (1-nndiscr) as usize, false);
- // The unit-like case might have a nonzero number of unit-like fields.
- // (e.d., Result of Either with (), as one side.)
- let ty = type_of::type_of(bcx.ccx, nullfields[ix]);
- assert_eq!(machine::llsize_of_alloc(bcx.ccx, ty), 0);
- bcx.pointercast(val.value, ty.ptr_to())
- }
- layout::RawNullablePointer { nndiscr, .. } => {
- let nnty = compute_fields(bcx.ccx, t, nndiscr as usize, false)[0];
- assert_eq!(ix, 0);
- assert_eq!(discr.0, nndiscr);
- let ty = type_of::type_of(bcx.ccx, nnty);
- bcx.pointercast(val.value, ty.ptr_to())
- }
- layout::StructWrappedNullablePointer { ref nonnull, nndiscr, .. } => {
- assert_eq!(discr.0, nndiscr);
- struct_field_ptr(bcx, &nonnull,
- &compute_fields(bcx.ccx, t, discr.0 as usize, false),
- val, ix, false)
- }
- _ => bug!("element access in type without elements: {} represented as {:#?}", t, l)
- }
-}
-
-fn struct_field_ptr<'a, 'tcx>(
- bcx: &BlockAndBuilder<'a, 'tcx>,
- st: &layout::Struct,
- fields: &Vec<Ty<'tcx>>,
- val: MaybeSizedValue,
- ix: usize,
- needs_cast: bool
-) -> ValueRef {
- let fty = fields[ix];
- let ccx = bcx.ccx;
-
- let ptr_val = if needs_cast {
- let fields = st.field_index_by_increasing_offset().map(|i| {
- type_of::in_memory_type_of(ccx, fields[i])
- }).collect::<Vec<_>>();
- let real_ty = Type::struct_(ccx, &fields[..], st.packed);
- bcx.pointercast(val.value, real_ty.ptr_to())
- } else {
- val.value
- };
-
- // Simple case - we can just GEP the field
- // * First field - Always aligned properly
- // * Packed struct - There is no alignment padding
- // * Field is sized - pointer is properly aligned already
- if st.offsets[ix] == layout::Size::from_bytes(0) || st.packed ||
- bcx.ccx.shared().type_is_sized(fty) {
- return bcx.struct_gep(ptr_val, st.memory_index[ix] as usize);
- }
-
- // If the type of the last field is [T] or str, then we don't need to do
- // any adjusments
- match fty.sty {
- ty::TySlice(..) | ty::TyStr => {
- return bcx.struct_gep(ptr_val, st.memory_index[ix] as usize);
- }
- _ => ()
- }
-
- // There's no metadata available, log the case and just do the GEP.
- if !val.has_meta() {
- debug!("Unsized field `{}`, of `{:?}` has no metadata for adjustment",
- ix, Value(ptr_val));
- return bcx.struct_gep(ptr_val, ix);
- }
-
- // We need to get the pointer manually now.
- // We do this by casting to a *i8, then offsetting it by the appropriate amount.
- // We do this instead of, say, simply adjusting the pointer from the result of a GEP
- // because the field may have an arbitrary alignment in the LLVM representation
- // anyway.
- //
- // To demonstrate:
- // struct Foo<T: ?Sized> {
- // x: u16,
- // y: T
- // }
- //
- // The type Foo<Foo<Trait>> is represented in LLVM as { u16, { u16, u8 }}, meaning that
- // the `y` field has 16-bit alignment.
-
- let meta = val.meta;
-
-
- let offset = st.offsets[ix].bytes();
- let unaligned_offset = C_uint(bcx.ccx, offset);
-
- // Get the alignment of the field
- let (_, align) = glue::size_and_align_of_dst(bcx, fty, meta);
-
- // Bump the unaligned offset up to the appropriate alignment using the
- // following expression:
- //
- // (unaligned offset + (align - 1)) & -align
-
- // Calculate offset
- let align_sub_1 = bcx.sub(align, C_uint(bcx.ccx, 1u64));
- let offset = bcx.and(bcx.add(unaligned_offset, align_sub_1),
- bcx.neg(align));
-
- debug!("struct_field_ptr: DST field offset: {:?}", Value(offset));
-
- // Cast and adjust pointer
- let byte_ptr = bcx.pointercast(ptr_val, Type::i8p(bcx.ccx));
- let byte_ptr = bcx.gep(byte_ptr, &[offset]);
-
- // Finally, cast back to the type expected
- let ll_fty = type_of::in_memory_type_of(bcx.ccx, fty);
- debug!("struct_field_ptr: Field type is {:?}", ll_fty);
- bcx.pointercast(byte_ptr, ll_fty.ptr_to())
-}
-
-/// Construct a constant value, suitable for initializing a
-/// GlobalVariable, given a case and constant values for its fields.
-/// Note that this may have a different LLVM type (and different
-/// alignment!) from the representation's `type_of`, so it needs a
-/// pointer cast before use.
-///
-/// The LLVM type system does not directly support unions, and only
-/// pointers can be bitcast, so a constant (and, by extension, the
-/// GlobalVariable initialized by it) will have a type that can vary
-/// depending on which case of an enum it is.
-///
-/// To understand the alignment situation, consider `enum E { V64(u64),
-/// V32(u32, u32) }` on Windows. The type has 8-byte alignment to
-/// accommodate the u64, but `V32(x, y)` would have LLVM type `{i32,
-/// i32, i32}`, which is 4-byte aligned.
-///
-/// Currently the returned value has the same size as the type, but
-/// this could be changed in the future to avoid allocating unnecessary
-/// space after values of shorter-than-maximum cases.
-pub fn trans_const<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>, discr: Disr,
- vals: &[ValueRef]) -> ValueRef {
- let l = ccx.layout_of(t);
- let dl = &ccx.tcx().data_layout;
- match *l {
- layout::CEnum { discr: d, min, max, .. } => {
- assert_eq!(vals.len(), 0);
- assert_discr_in_range(Disr(min), Disr(max), discr);
- C_integral(Type::from_integer(ccx, d), discr.0, true)
- }
- layout::General { discr: d, ref variants, .. } => {
- let variant = &variants[discr.0 as usize];
- let lldiscr = C_integral(Type::from_integer(ccx, d), discr.0 as u64, true);
- let mut vals_with_discr = vec![lldiscr];
- vals_with_discr.extend_from_slice(vals);
- let mut contents = build_const_struct(ccx, &variant, &vals_with_discr[..]);
- let needed_padding = l.size(dl).bytes() - variant.stride().bytes();
- if needed_padding > 0 {
- contents.push(padding(ccx, needed_padding));
- }
- C_struct(ccx, &contents[..], false)
- }
- layout::UntaggedUnion { ref variants, .. }=> {
- assert_eq!(discr, Disr(0));
- let contents = build_const_union(ccx, variants, vals[0]);
- C_struct(ccx, &contents, variants.packed)
- }
- layout::Univariant { ref variant, .. } => {
- assert_eq!(discr, Disr(0));
- let contents = build_const_struct(ccx, &variant, vals);
- C_struct(ccx, &contents[..], variant.packed)
- }
- layout::Vector { .. } => {
- C_vector(vals)
- }
- layout::RawNullablePointer { nndiscr, .. } => {
- let nnty = compute_fields(ccx, t, nndiscr as usize, false)[0];
- if discr.0 == nndiscr {
- assert_eq!(vals.len(), 1);
- vals[0]
- } else {
- C_null(type_of::sizing_type_of(ccx, nnty))
- }
- }
- layout::StructWrappedNullablePointer { ref nonnull, nndiscr, .. } => {
- if discr.0 == nndiscr {
- C_struct(ccx, &build_const_struct(ccx, &nonnull, vals), false)
- } else {
- let fields = compute_fields(ccx, t, nndiscr as usize, false);
- let vals = fields.iter().map(|&ty| {
- // Always use null even if it's not the `discrfield`th
- // field; see #8506.
- C_null(type_of::sizing_type_of(ccx, ty))
- }).collect::<Vec<ValueRef>>();
- C_struct(ccx, &build_const_struct(ccx, &nonnull, &vals[..]), false)
- }
- }
- _ => bug!("trans_const: cannot handle type {} repreented as {:#?}", t, l)
- }
-}
-
-/// Building structs is a little complicated, because we might need to
-/// insert padding if a field's value is less aligned than its type.
-///
-/// Continuing the example from `trans_const`, a value of type `(u32,
-/// E)` should have the `E` at offset 8, but if that field's
-/// initializer is 4-byte aligned then simply translating the tuple as
-/// a two-element struct will locate it at offset 4, and accesses to it
-/// will read the wrong memory.
-fn build_const_struct<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
- st: &layout::Struct,
- vals: &[ValueRef])
- -> Vec<ValueRef> {
- assert_eq!(vals.len(), st.offsets.len());
-
- if vals.len() == 0 {
- return Vec::new();
- }
-
- // offset of current value
- let mut offset = 0;
- let mut cfields = Vec::new();
- cfields.reserve(st.offsets.len()*2);
-
- let parts = st.field_index_by_increasing_offset().map(|i| {
- (&vals[i], st.offsets[i].bytes())
- });
- for (&val, target_offset) in parts {
- if offset < target_offset {
- cfields.push(padding(ccx, target_offset - offset));
- offset = target_offset;
- }
- assert!(!is_undef(val));
- cfields.push(val);
- offset += machine::llsize_of_alloc(ccx, val_ty(val));
- }
-
- if offset < st.stride().bytes() {
- cfields.push(padding(ccx, st.stride().bytes() - offset));
- }
-
- cfields
-}
-
-fn build_const_union<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
- un: &layout::Union,
- field_val: ValueRef)
- -> Vec<ValueRef> {
- let mut cfields = vec![field_val];
-
- let offset = machine::llsize_of_alloc(ccx, val_ty(field_val));
- let size = un.stride().bytes();
- if offset != size {
- cfields.push(padding(ccx, size - offset));
- }
-
- cfields
-}
-
-fn padding(ccx: &CrateContext, size: u64) -> ValueRef {
- C_undef(Type::array(&Type::i8(ccx), size))
-}
-
// FIXME this utility routine should be somewhere more general
#[inline]
fn roundup(x: u64, a: u32) -> u64 { let a = a as u64; ((x + (a - 1)) / a) * a }
use common::*;
use type_of;
use type_::Type;
+use builder::Builder;
use rustc::hir;
use rustc::ty::Ty;
// Take an inline assembly expression and splat it out via LLVM
pub fn trans_inline_asm<'a, 'tcx>(
- bcx: &BlockAndBuilder<'a, 'tcx>,
+ bcx: &Builder<'a, 'tcx>,
ia: &hir::InlineAsm,
outputs: Vec<(ValueRef, Ty<'tcx>)>,
mut inputs: Vec<ValueRef>
let outputs = ia.outputs.iter().zip(&outputs).filter(|&(ref o, _)| !o.is_indirect);
for (i, (_, &(val, _))) in outputs.enumerate() {
let v = if num_outputs == 1 { r } else { bcx.extract_value(r, i) };
- bcx.store(v, val);
+ bcx.store(v, val, None);
}
// Store expn_id in a metadata node so we can map LLVM errors
use rustc::hir::def_id::{DefId, LOCAL_CRATE};
use middle::lang_items::StartFnLangItem;
use rustc::ty::subst::Substs;
+use rustc::mir::tcx::LvalueTy;
use rustc::traits;
-use rustc::ty::{self, Ty, TyCtxt, TypeFoldable};
+use rustc::ty::{self, Ty, TyCtxt};
use rustc::ty::adjustment::CustomCoerceUnsized;
use rustc::dep_graph::{DepNode, WorkProduct};
use rustc::hir::map as hir_map;
use rustc_incremental::IncrementalHashesMap;
use session::{self, DataTypeKind, Session};
use abi::{self, Abi, FnType};
+use mir::lvalue::LvalueRef;
use adt;
use attributes;
use builder::Builder;
use callee::{Callee};
-use common::{BlockAndBuilder, C_bool, C_bytes_in_context, C_i32, C_uint};
+use common::{C_bool, C_bytes_in_context, C_i32, C_uint};
use collector::{self, TransItemCollectionMode};
use common::{C_struct_in_context, C_u64, C_undef};
-use common::{CrateContext, FunctionContext};
+use common::CrateContext;
use common::{fulfill_obligation};
use common::{type_is_zero_size, val_ty};
use common;
}
pub fn compare_simd_types<'a, 'tcx>(
- bcx: &BlockAndBuilder<'a, 'tcx>,
+ bcx: &Builder<'a, 'tcx>,
lhs: ValueRef,
rhs: ValueRef,
t: Ty<'tcx>,
/// Coerce `src` to `dst_ty`. `src_ty` must be a thin pointer.
pub fn unsize_thin_ptr<'a, 'tcx>(
- bcx: &BlockAndBuilder<'a, 'tcx>,
+ bcx: &Builder<'a, 'tcx>,
src: ValueRef,
src_ty: Ty<'tcx>,
dst_ty: Ty<'tcx>
/// Coerce `src`, which is a reference to a value of type `src_ty`,
/// to a value of type `dst_ty` and store the result in `dst`
-pub fn coerce_unsized_into<'a, 'tcx>(bcx: &BlockAndBuilder<'a, 'tcx>,
+pub fn coerce_unsized_into<'a, 'tcx>(bcx: &Builder<'a, 'tcx>,
src: ValueRef,
src_ty: Ty<'tcx>,
dst: ValueRef,
monomorphize::field_ty(bcx.tcx(), substs_b, f)
});
- let src = adt::MaybeSizedValue::sized(src);
- let dst = adt::MaybeSizedValue::sized(dst);
+ let src = LvalueRef::new_sized_ty(src, src_ty);
+ let dst = LvalueRef::new_sized_ty(dst, dst_ty);
let iter = src_fields.zip(dst_fields).enumerate();
for (i, (src_fty, dst_fty)) in iter {
continue;
}
- let src_f = adt::trans_field_ptr(bcx, src_ty, src, Disr(0), i);
- let dst_f = adt::trans_field_ptr(bcx, dst_ty, dst, Disr(0), i);
+ let src_f = src.trans_field_ptr(bcx, i);
+ let dst_f = dst.trans_field_ptr(bcx, i);
if src_fty == dst_fty {
- memcpy_ty(bcx, dst_f, src_f, src_fty);
+ memcpy_ty(bcx, dst_f, src_f, src_fty, None);
} else {
coerce_unsized_into(bcx, src_f, src_fty, dst_f, dst_fty);
}
}
pub fn cast_shift_expr_rhs(
- cx: &BlockAndBuilder, op: hir::BinOp_, lhs: ValueRef, rhs: ValueRef
+ cx: &Builder, op: hir::BinOp_, lhs: ValueRef, rhs: ValueRef
) -> ValueRef {
cast_shift_rhs(op, lhs, rhs, |a, b| cx.trunc(a, b), |a, b| cx.zext(a, b))
}
/// Helper for storing values in memory. Does the necessary conversion if the in-memory type
/// differs from the type used for SSA values.
-pub fn store_ty<'a, 'tcx>(cx: &BlockAndBuilder<'a, 'tcx>, v: ValueRef, dst: ValueRef, t: Ty<'tcx>) {
+pub fn store_ty<'a, 'tcx>(cx: &Builder<'a, 'tcx>, v: ValueRef, dst: ValueRef, t: Ty<'tcx>) {
debug!("store_ty: {:?} : {:?} <- {:?}", Value(dst), t, Value(v));
if common::type_is_fat_ptr(cx.ccx, t) {
let llextra = cx.extract_value(v, abi::FAT_PTR_EXTRA);
store_fat_ptr(cx, lladdr, llextra, dst, t);
} else {
- cx.store(from_immediate(cx, v), dst);
+ cx.store(from_immediate(cx, v), dst, None);
}
}
-pub fn store_fat_ptr<'a, 'tcx>(cx: &BlockAndBuilder<'a, 'tcx>,
+pub fn store_fat_ptr<'a, 'tcx>(cx: &Builder<'a, 'tcx>,
data: ValueRef,
extra: ValueRef,
dst: ValueRef,
_ty: Ty<'tcx>) {
// FIXME: emit metadata
- cx.store(data, get_dataptr(cx, dst));
- cx.store(extra, get_meta(cx, dst));
+ cx.store(data, get_dataptr(cx, dst), None);
+ cx.store(extra, get_meta(cx, dst), None);
}
pub fn load_fat_ptr<'a, 'tcx>(
(ptr, meta)
}
-pub fn from_immediate(bcx: &BlockAndBuilder, val: ValueRef) -> ValueRef {
+pub fn from_immediate(bcx: &Builder, val: ValueRef) -> ValueRef {
if val_ty(val) == Type::i1(bcx.ccx) {
bcx.zext(val, Type::i8(bcx.ccx))
} else {
}
}
-pub fn to_immediate(bcx: &BlockAndBuilder, val: ValueRef, ty: Ty) -> ValueRef {
+pub fn to_immediate(bcx: &Builder, val: ValueRef, ty: Ty) -> ValueRef {
if ty.is_bool() {
bcx.trunc(val, Type::i1(bcx.ccx))
} else {
}
pub fn memcpy_ty<'a, 'tcx>(
- bcx: &BlockAndBuilder<'a, 'tcx>, dst: ValueRef, src: ValueRef, t: Ty<'tcx>
+ bcx: &Builder<'a, 'tcx>,
+ dst: ValueRef,
+ src: ValueRef,
+ t: Ty<'tcx>,
+ align: Option<u32>,
) {
let ccx = bcx.ccx;
return;
}
- if t.is_structural() {
- let llty = type_of::type_of(ccx, t);
- let llsz = llsize_of(ccx, llty);
- let llalign = type_of::align_of(ccx, t);
- call_memcpy(bcx, dst, src, llsz, llalign as u32);
- } else if common::type_is_fat_ptr(bcx.ccx, t) {
- let (data, extra) = load_fat_ptr(bcx, src, t);
- store_fat_ptr(bcx, data, extra, dst, t);
- } else {
- store_ty(bcx, load_ty(bcx, src, t), dst, t);
- }
+ let llty = type_of::type_of(ccx, t);
+ let llsz = llsize_of(ccx, llty);
+ let llalign = align.unwrap_or_else(|| type_of::align_of(ccx, t));
+ call_memcpy(bcx, dst, src, llsz, llalign as u32);
}
pub fn call_memset<'a, 'tcx>(b: &Builder<'a, 'tcx>,
b.call(llintrinsicfn, &[ptr, fill_byte, size, align, volatile], None)
}
-pub fn alloc_ty<'a, 'tcx>(bcx: &BlockAndBuilder<'a, 'tcx>, ty: Ty<'tcx>, name: &str) -> ValueRef {
- assert!(!ty.has_param_types());
- bcx.fcx().alloca(type_of::type_of(bcx.ccx, ty), name)
-}
-
pub fn trans_instance<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, instance: Instance<'tcx>) {
let _s = if ccx.sess().trans_stats() {
let mut instance_name = String::new();
let fn_ty = FnType::new(ccx, abi, &sig, &[]);
- let fcx = FunctionContext::new(ccx, lldecl);
let mir = ccx.tcx().item_mir(instance.def);
- mir::trans_mir(&fcx, fn_ty, &mir, instance, &sig, abi);
+ mir::trans_mir(ccx, lldecl, fn_ty, &mir, instance, &sig, abi);
}
pub fn trans_ctor_shim<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
def_id: DefId,
substs: &'tcx Substs<'tcx>,
disr: Disr,
- llfndecl: ValueRef) {
- attributes::inline(llfndecl, attributes::InlineAttr::Hint);
- attributes::set_frame_pointer_elimination(ccx, llfndecl);
+ llfn: ValueRef) {
+ attributes::inline(llfn, attributes::InlineAttr::Hint);
+ attributes::set_frame_pointer_elimination(ccx, llfn);
let ctor_ty = ccx.tcx().item_type(def_id);
let ctor_ty = monomorphize::apply_param_substs(ccx.shared(), substs, &ctor_ty);
let sig = ccx.tcx().erase_late_bound_regions_and_normalize(&ctor_ty.fn_sig());
let fn_ty = FnType::new(ccx, Abi::Rust, &sig, &[]);
- let fcx = FunctionContext::new(ccx, llfndecl);
- let bcx = fcx.get_entry_block();
+ let bcx = Builder::new_block(ccx, llfn, "entry-block");
if !fn_ty.ret.is_ignore() {
// But if there are no nested returns, we skip the indirection
// and have a single retslot
let dest = if fn_ty.ret.is_indirect() {
- get_param(fcx.llfn, 0)
+ get_param(llfn, 0)
} else {
// We create an alloca to hold a pointer of type `ret.original_ty`
// which will hold the pointer to the right alloca which has the
// final ret value
- fcx.alloca(fn_ty.ret.memory_ty(ccx), "sret_slot")
+ bcx.alloca(fn_ty.ret.memory_ty(ccx), "sret_slot")
+ };
+ // Can return unsized value
+ let mut dest_val = LvalueRef::new_sized_ty(dest, sig.output());
+ dest_val.ty = LvalueTy::Downcast {
+ adt_def: sig.output().ty_adt_def().unwrap(),
+ substs: substs,
+ variant_index: disr.0 as usize,
};
- let dest_val = adt::MaybeSizedValue::sized(dest); // Can return unsized value
let mut llarg_idx = fn_ty.ret.is_indirect() as usize;
let mut arg_idx = 0;
for (i, arg_ty) in sig.inputs().iter().enumerate() {
- let lldestptr = adt::trans_field_ptr(&bcx, sig.output(), dest_val, Disr::from(disr), i);
+ let lldestptr = dest_val.trans_field_ptr(&bcx, i);
let arg = &fn_ty.args[arg_idx];
arg_idx += 1;
if common::type_is_fat_ptr(bcx.ccx, arg_ty) {
// `main` should respect same config for frame pointer elimination as rest of code
attributes::set_frame_pointer_elimination(ccx, llfn);
- let llbb = unsafe {
- let name = CString::new("top").unwrap();
- llvm::LLVMAppendBasicBlockInContext(ccx.llcx(), llfn, name.as_ptr())
- };
- let bld = Builder::with_ccx(ccx);
- bld.position_at_end(llbb);
+ let bld = Builder::new_block(ccx, llfn, "top");
debuginfo::gdb::insert_reference_to_gdb_debug_scripts_section_global(ccx, &bld);
use type_::Type;
use value::Value;
use libc::{c_uint, c_char};
+use rustc::ty::{Ty, TyCtxt, TypeFoldable};
+use rustc::session::Session;
+use type_of;
use std::borrow::Cow;
use std::ffi::CString;
use std::ptr;
use syntax_pos::Span;
+// All Builders must have an llfn associated with them
+#[must_use]
pub struct Builder<'a, 'tcx: 'a> {
pub llbuilder: BuilderRef,
pub ccx: &'a CrateContext<'a, 'tcx>,
}
impl<'a, 'tcx> Builder<'a, 'tcx> {
+ pub fn new_block<'b>(ccx: &'a CrateContext<'a, 'tcx>, llfn: ValueRef, name: &'b str) -> Self {
+ let builder = Builder::with_ccx(ccx);
+ let llbb = unsafe {
+ let name = CString::new(name).unwrap();
+ llvm::LLVMAppendBasicBlockInContext(
+ ccx.llcx(),
+ llfn,
+ name.as_ptr()
+ )
+ };
+ builder.position_at_end(llbb);
+ builder
+ }
+
pub fn with_ccx(ccx: &'a CrateContext<'a, 'tcx>) -> Self {
// Create a fresh builder from the crate context.
let llbuilder = unsafe {
}
}
+ pub fn build_sibling_block<'b>(&self, name: &'b str) -> Builder<'a, 'tcx> {
+ Builder::new_block(self.ccx, self.llfn(), name)
+ }
+
+ pub fn sess(&self) -> &Session {
+ self.ccx.sess()
+ }
+
+ pub fn tcx(&self) -> TyCtxt<'a, 'tcx, 'tcx> {
+ self.ccx.tcx()
+ }
+
+ pub fn llfn(&self) -> ValueRef {
+ unsafe {
+ llvm::LLVMGetBasicBlockParent(self.llbb())
+ }
+ }
+
+ pub fn llbb(&self) -> BasicBlockRef {
+ unsafe {
+ llvm::LLVMGetInsertBlock(self.llbuilder)
+ }
+ }
+
fn count_insn(&self, category: &str) {
if self.ccx.sess().trans_stats() {
self.ccx.stats().n_llvm_insns.set(self.ccx.stats().n_llvm_insns.get() + 1);
}
}
+ pub fn alloca(&self, ty: Type, name: &str) -> ValueRef {
+ let builder = Builder::with_ccx(self.ccx);
+ builder.position_at_start(unsafe {
+ llvm::LLVMGetFirstBasicBlock(self.llfn())
+ });
+ builder.dynamic_alloca(ty, name)
+ }
+
+ pub fn alloca_ty(&self, ty: Ty<'tcx>, name: &str) -> ValueRef {
+ assert!(!ty.has_param_types());
+ self.alloca(type_of::type_of(self.ccx, ty), name)
+ }
+
pub fn dynamic_alloca(&self, ty: Type, name: &str) -> ValueRef {
self.count_insn("alloca");
unsafe {
value
}
- pub fn store(&self, val: ValueRef, ptr: ValueRef) -> ValueRef {
+ pub fn store(&self, val: ValueRef, ptr: ValueRef, align: Option<u32>) -> ValueRef {
debug!("Store {:?} -> {:?}", Value(val), Value(ptr));
assert!(!self.llbuilder.is_null());
self.count_insn("store");
let ptr = self.check_store(val, ptr);
unsafe {
- llvm::LLVMBuildStore(self.llbuilder, val, ptr)
+ let store = llvm::LLVMBuildStore(self.llbuilder, val, ptr);
+ if let Some(align) = align {
+ llvm::LLVMSetAlignment(store, align as c_uint);
+ }
+ store
}
}
use abi::{Abi, FnType};
use attributes;
use base;
-use base::*;
-use common::{
- self, CrateContext, FunctionContext, SharedCrateContext
-};
-use adt::MaybeSizedValue;
+use builder::Builder;
+use common::{self, CrateContext, SharedCrateContext};
+use cleanup::CleanupScope;
+use mir::lvalue::LvalueRef;
use consts;
use declare;
use value::Value;
attributes::set_frame_pointer_elimination(ccx, lloncefn);
let orig_fn_ty = fn_ty;
- let fcx = FunctionContext::new(ccx, lloncefn);
- let mut bcx = fcx.get_entry_block();
+ let mut bcx = Builder::new_block(ccx, lloncefn, "entry-block");
let callee = Callee {
data: Fn(llreffn),
// the first argument (`self`) will be the (by value) closure env.
- let mut llargs = get_params(fcx.llfn);
+ let mut llargs = get_params(lloncefn);
let fn_ret = callee.ty.fn_ret();
let fn_ty = callee.direct_fn_type(bcx.ccx, &[]);
let self_idx = fn_ty.ret.is_indirect() as usize;
let llenv = if env_arg.is_indirect() {
llargs[self_idx]
} else {
- let scratch = alloc_ty(&bcx, closure_ty, "self");
+ let scratch = bcx.alloca_ty(closure_ty, "self");
let mut llarg_idx = self_idx;
env_arg.store_fn_arg(&bcx, &mut llarg_idx, scratch);
scratch
// Call the by-ref closure body with `self` in a cleanup scope,
// to drop `self` when the body returns, or in case it unwinds.
- let self_scope = fcx.schedule_drop_mem(MaybeSizedValue::sized(llenv), closure_ty);
+ let self_scope = CleanupScope::schedule_drop_mem(
+ &bcx, LvalueRef::new_sized_ty(llenv, closure_ty)
+ );
let llfn = callee.reify(bcx.ccx);
let llret;
if let Some(landing_pad) = self_scope.landing_pad {
- let normal_bcx = bcx.fcx().build_new_block("normal-return");
+ let normal_bcx = bcx.build_sibling_block("normal-return");
llret = bcx.invoke(llfn, &llargs[..], normal_bcx.llbb(), landing_pad, None);
bcx = normal_bcx;
} else {
let llfn = declare::define_internal_fn(ccx, &function_name, tuple_fn_ty);
attributes::set_frame_pointer_elimination(ccx, llfn);
//
- let fcx = FunctionContext::new(ccx, llfn);
- let bcx = fcx.get_entry_block();
+ let bcx = Builder::new_block(ccx, llfn, "entry-block");
- let mut llargs = get_params(fcx.llfn);
+ let mut llargs = get_params(llfn);
let self_arg = llargs.remove(fn_ty.ret.is_indirect() as usize);
let llfnpointer = llfnpointer.unwrap_or_else(|| {
use llvm::BasicBlockRef;
use base;
-use adt::MaybeSizedValue;
-use common::{BlockAndBuilder, FunctionContext, Funclet};
+use mir::lvalue::LvalueRef;
+use rustc::mir::tcx::LvalueTy;
+use builder::Builder;
+use common::Funclet;
use glue;
use type_::Type;
-use rustc::ty::Ty;
pub struct CleanupScope<'tcx> {
// Cleanup to run upon scope exit.
#[derive(Copy, Clone)]
pub struct DropValue<'tcx> {
- val: MaybeSizedValue,
- ty: Ty<'tcx>,
+ val: LvalueRef<'tcx>,
skip_dtor: bool,
}
impl<'tcx> DropValue<'tcx> {
- fn trans<'a>(&self, funclet: Option<&'a Funclet>, bcx: &BlockAndBuilder<'a, 'tcx>) {
- glue::call_drop_glue(bcx, self.val, self.ty, self.skip_dtor, funclet)
+ fn trans<'a>(&self, funclet: Option<&'a Funclet>, bcx: &Builder<'a, 'tcx>) {
+ glue::call_drop_glue(bcx, self.val, self.skip_dtor, funclet)
}
/// Creates a landing pad for the top scope. The landing pad will perform all cleanups necessary
/// landing_pad -> ... cleanups ... -> [resume]
///
/// This should only be called once per function, as it creates an alloca for the landingpad.
- fn get_landing_pad<'a>(&self, fcx: &FunctionContext<'a, 'tcx>) -> BasicBlockRef {
+ fn get_landing_pad<'a>(&self, bcx: &Builder<'a, 'tcx>) -> BasicBlockRef {
debug!("get_landing_pad");
- let bcx = fcx.build_new_block("cleanup_unwind");
+ let bcx = bcx.build_sibling_block("cleanup_unwind");
let llpersonality = bcx.ccx.eh_personality();
bcx.set_personality_fn(llpersonality);
- if base::wants_msvc_seh(fcx.ccx.sess()) {
+ if base::wants_msvc_seh(bcx.sess()) {
let pad = bcx.cleanup_pad(None, &[]);
let funclet = Some(Funclet::new(pad));
self.trans(funclet.as_ref(), &bcx);
// The landing pad return type (the type being propagated). Not sure
// what this represents but it's determined by the personality
// function and this is what the EH proposal example uses.
- let llretty = Type::struct_(fcx.ccx, &[Type::i8p(fcx.ccx), Type::i32(fcx.ccx)], false);
+ let llretty = Type::struct_(bcx.ccx, &[Type::i8p(bcx.ccx), Type::i32(bcx.ccx)], false);
// The only landing pad clause will be 'cleanup'
- let llretval = bcx.landing_pad(llretty, llpersonality, 1, bcx.fcx().llfn);
+ let llretval = bcx.landing_pad(llretty, llpersonality, 1, bcx.llfn());
// The landing pad block is a cleanup
bcx.set_cleanup(llretval);
}
}
-impl<'a, 'tcx> FunctionContext<'a, 'tcx> {
+impl<'a, 'tcx> CleanupScope<'tcx> {
/// Schedules a (deep) drop of `val`, which is a pointer to an instance of `ty`
- pub fn schedule_drop_mem(&self, val: MaybeSizedValue, ty: Ty<'tcx>) -> CleanupScope<'tcx> {
- if !self.ccx.shared().type_needs_drop(ty) { return CleanupScope::noop(); }
+ pub fn schedule_drop_mem(
+ bcx: &Builder<'a, 'tcx>, val: LvalueRef<'tcx>
+ ) -> CleanupScope<'tcx> {
+ if let LvalueTy::Downcast { .. } = val.ty {
+ bug!("Cannot drop downcast ty yet");
+ }
+ if !bcx.ccx.shared().type_needs_drop(val.ty.to_ty(bcx.tcx())) {
+ return CleanupScope::noop();
+ }
let drop = DropValue {
val: val,
- ty: ty,
skip_dtor: false,
};
- CleanupScope::new(self, drop)
+ CleanupScope::new(bcx, drop)
}
/// Issue #23611: Schedules a (deep) drop of the contents of
/// `ty`. The scheduled code handles extracting the discriminant
/// and dropping the contents associated with that variant
/// *without* executing any associated drop implementation.
- pub fn schedule_drop_adt_contents(&self, val: MaybeSizedValue, ty: Ty<'tcx>)
- -> CleanupScope<'tcx> {
+ pub fn schedule_drop_adt_contents(
+ bcx: &Builder<'a, 'tcx>, val: LvalueRef<'tcx>
+ ) -> CleanupScope<'tcx> {
+ if let LvalueTy::Downcast { .. } = val.ty {
+ bug!("Cannot drop downcast ty yet");
+ }
// `if` below could be "!contents_needs_drop"; skipping drop
// is just an optimization, so sound to be conservative.
- if !self.ccx.shared().type_needs_drop(ty) { return CleanupScope::noop(); }
+ if !bcx.ccx.shared().type_needs_drop(val.ty.to_ty(bcx.tcx())) {
+ return CleanupScope::noop();
+ }
let drop = DropValue {
val: val,
- ty: ty,
skip_dtor: true,
};
- CleanupScope::new(self, drop)
+ CleanupScope::new(bcx, drop)
}
-}
-impl<'tcx> CleanupScope<'tcx> {
- fn new<'a>(fcx: &FunctionContext<'a, 'tcx>, drop_val: DropValue<'tcx>) -> CleanupScope<'tcx> {
+ fn new(bcx: &Builder<'a, 'tcx>, drop_val: DropValue<'tcx>) -> CleanupScope<'tcx> {
CleanupScope {
cleanup: Some(drop_val),
- landing_pad: if !fcx.ccx.sess().no_landing_pads() {
- Some(drop_val.get_landing_pad(fcx))
+ landing_pad: if !bcx.sess().no_landing_pads() {
+ Some(drop_val.get_landing_pad(bcx))
} else {
None
},
}
}
- pub fn trans<'a>(self, bcx: &'a BlockAndBuilder<'a, 'tcx>) {
+ pub fn trans(self, bcx: &'a Builder<'a, 'tcx>) {
if let Some(cleanup) = self.cleanup {
cleanup.trans(None, &bcx);
}
//! Code that is useful in various trans modules.
-use session::Session;
use llvm;
-use llvm::{ValueRef, BasicBlockRef, ContextRef, TypeKind};
+use llvm::{ValueRef, ContextRef, TypeKind};
use llvm::{True, False, Bool, OperandBundleDef};
-use rustc::hir::def::Def;
use rustc::hir::def_id::DefId;
use rustc::hir::map::DefPathData;
use rustc::util::common::MemoizationMap;
use libc::{c_uint, c_char};
use std::borrow::Cow;
use std::iter;
-use std::ops::Deref;
-use std::ffi::CString;
use syntax::ast;
-use syntax::symbol::{Symbol, InternedString};
+use syntax::symbol::InternedString;
use syntax_pos::Span;
use rustc_i128::u128;
*
*/
-use Disr;
-
-/// The concrete version of ty::FieldDef. The name is the field index if
-/// the field is numeric.
-pub struct Field<'tcx>(pub ast::Name, pub Ty<'tcx>);
-
-/// The concrete version of ty::VariantDef
-pub struct VariantInfo<'tcx> {
- pub discr: Disr,
- pub fields: Vec<Field<'tcx>>
-}
-
-impl<'a, 'tcx> VariantInfo<'tcx> {
- pub fn from_ty(tcx: TyCtxt<'a, 'tcx, 'tcx>,
- ty: Ty<'tcx>,
- opt_def: Option<Def>)
- -> Self
- {
- match ty.sty {
- ty::TyAdt(adt, substs) => {
- let variant = match opt_def {
- None => adt.struct_variant(),
- Some(def) => adt.variant_of_def(def)
- };
-
- VariantInfo {
- discr: Disr::from(variant.disr_val),
- fields: variant.fields.iter().map(|f| {
- Field(f.name, monomorphize::field_ty(tcx, substs, f))
- }).collect()
- }
- }
-
- ty::TyTuple(ref v) => {
- VariantInfo {
- discr: Disr(0),
- fields: v.iter().enumerate().map(|(i, &t)| {
- Field(Symbol::intern(&i.to_string()), t)
- }).collect()
- }
- }
-
- _ => {
- bug!("cannot get field types from the type {:?}", ty);
- }
- }
- }
-}
-
-// Function context. Every LLVM function we create will have one of these.
-pub struct FunctionContext<'a, 'tcx: 'a> {
- // The ValueRef returned from a call to llvm::LLVMAddFunction; the
- // address of the first instruction in the sequence of
- // instructions for this function that will go in the .text
- // section of the executable we're generating.
- pub llfn: ValueRef,
-
- // A marker for the place where we want to insert the function's static
- // allocas, so that LLVM will coalesce them into a single alloca call.
- alloca_insert_pt: Option<ValueRef>,
-
- // This function's enclosing crate context.
- pub ccx: &'a CrateContext<'a, 'tcx>,
-
- alloca_builder: Builder<'a, 'tcx>,
-}
-
-impl<'a, 'tcx> FunctionContext<'a, 'tcx> {
- /// Create a function context for the given function.
- /// Call FunctionContext::get_entry_block for the first entry block.
- pub fn new(ccx: &'a CrateContext<'a, 'tcx>, llfndecl: ValueRef) -> FunctionContext<'a, 'tcx> {
- let mut fcx = FunctionContext {
- llfn: llfndecl,
- alloca_insert_pt: None,
- ccx: ccx,
- alloca_builder: Builder::with_ccx(ccx),
- };
-
- let val = {
- let entry_bcx = fcx.build_new_block("entry-block");
- let val = entry_bcx.load(C_null(Type::i8p(ccx)));
- fcx.alloca_builder.position_at_start(entry_bcx.llbb());
- val
- };
-
- // Use a dummy instruction as the insertion point for all allocas.
- // This is later removed in the drop of FunctionContext.
- fcx.alloca_insert_pt = Some(val);
-
- fcx
- }
-
- pub fn get_entry_block(&'a self) -> BlockAndBuilder<'a, 'tcx> {
- BlockAndBuilder::new(unsafe {
- llvm::LLVMGetFirstBasicBlock(self.llfn)
- }, self)
- }
-
- pub fn new_block(&'a self, name: &str) -> BasicBlockRef {
- unsafe {
- let name = CString::new(name).unwrap();
- llvm::LLVMAppendBasicBlockInContext(
- self.ccx.llcx(),
- self.llfn,
- name.as_ptr()
- )
- }
- }
-
- pub fn build_new_block(&'a self, name: &str) -> BlockAndBuilder<'a, 'tcx> {
- BlockAndBuilder::new(self.new_block(name), self)
- }
-
- pub fn alloca(&self, ty: Type, name: &str) -> ValueRef {
- self.alloca_builder.dynamic_alloca(ty, name)
- }
-}
-
-impl<'a, 'tcx> Drop for FunctionContext<'a, 'tcx> {
- fn drop(&mut self) {
- unsafe {
- llvm::LLVMInstructionEraseFromParent(self.alloca_insert_pt.unwrap());
- }
- }
-}
-
-#[must_use]
-pub struct BlockAndBuilder<'a, 'tcx: 'a> {
- // The BasicBlockRef returned from a call to
- // llvm::LLVMAppendBasicBlock(llfn, name), which adds a basic
- // block to the function pointed to by llfn. We insert
- // instructions into that block by way of this block context.
- // The block pointing to this one in the function's digraph.
- llbb: BasicBlockRef,
-
- // The function context for the function to which this block is
- // attached.
- fcx: &'a FunctionContext<'a, 'tcx>,
-
- builder: Builder<'a, 'tcx>,
-}
-
-impl<'a, 'tcx> BlockAndBuilder<'a, 'tcx> {
- pub fn new(llbb: BasicBlockRef, fcx: &'a FunctionContext<'a, 'tcx>) -> Self {
- let builder = Builder::with_ccx(fcx.ccx);
- // Set the builder's position to this block's end.
- builder.position_at_end(llbb);
- BlockAndBuilder {
- llbb: llbb,
- fcx: fcx,
- builder: builder,
- }
- }
-
- pub fn at_start<F, R>(&self, f: F) -> R
- where F: FnOnce(&BlockAndBuilder<'a, 'tcx>) -> R
- {
- self.position_at_start(self.llbb);
- let r = f(self);
- self.position_at_end(self.llbb);
- r
- }
-
- pub fn fcx(&self) -> &'a FunctionContext<'a, 'tcx> {
- self.fcx
- }
- pub fn tcx(&self) -> TyCtxt<'a, 'tcx, 'tcx> {
- self.ccx.tcx()
- }
- pub fn sess(&self) -> &'a Session {
- self.ccx.sess()
- }
-
- pub fn llbb(&self) -> BasicBlockRef {
- self.llbb
- }
-}
-
-impl<'a, 'tcx> Deref for BlockAndBuilder<'a, 'tcx> {
- type Target = Builder<'a, 'tcx>;
- fn deref(&self) -> &Self::Target {
- &self.builder
- }
-}
-
/// A structure representing an active landing pad for the duration of a basic
/// block.
///
// of Java. (See related discussion on #1877 and #10183.)
pub fn build_unchecked_lshift<'a, 'tcx>(
- bcx: &BlockAndBuilder<'a, 'tcx>,
+ bcx: &Builder<'a, 'tcx>,
lhs: ValueRef,
rhs: ValueRef
) -> ValueRef {
}
pub fn build_unchecked_rshift<'a, 'tcx>(
- bcx: &BlockAndBuilder<'a, 'tcx>, lhs_t: Ty<'tcx>, lhs: ValueRef, rhs: ValueRef
+ bcx: &Builder<'a, 'tcx>, lhs_t: Ty<'tcx>, lhs: ValueRef, rhs: ValueRef
) -> ValueRef {
let rhs = base::cast_shift_expr_rhs(bcx, hir::BinOp_::BiShr, lhs, rhs);
// #1877, #10183: Ensure that input is always valid
}
}
-fn shift_mask_rhs<'a, 'tcx>(bcx: &BlockAndBuilder<'a, 'tcx>, rhs: ValueRef) -> ValueRef {
+fn shift_mask_rhs<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, rhs: ValueRef) -> ValueRef {
let rhs_llty = val_ty(rhs);
bcx.and(rhs, shift_mask_val(bcx, rhs_llty, rhs_llty, false))
}
pub fn shift_mask_val<'a, 'tcx>(
- bcx: &BlockAndBuilder<'a, 'tcx>,
+ bcx: &Builder<'a, 'tcx>,
llty: Type,
mask_llty: Type,
invert: bool
use llvm;
use llvm::debuginfo::{DIScope, DISubprogram};
-use common::{CrateContext, FunctionContext};
+use common::CrateContext;
use rustc::mir::{Mir, VisibilityScope};
use libc::c_uint;
/// Produce DIScope DIEs for each MIR Scope which has variables defined in it.
/// If debuginfo is disabled, the returned vector is empty.
-pub fn create_mir_scopes(fcx: &FunctionContext, mir: &Mir, debug_context: &FunctionDebugContext)
+pub fn create_mir_scopes(ccx: &CrateContext, mir: &Mir, debug_context: &FunctionDebugContext)
-> IndexVec<VisibilityScope, MirDebugScope> {
let null_scope = MirDebugScope {
scope_metadata: ptr::null_mut(),
// Instantiate all scopes.
for idx in 0..mir.visibility_scopes.len() {
let scope = VisibilityScope::new(idx);
- make_mir_scope(fcx.ccx, &mir, &has_variables, fn_metadata, scope, &mut scopes);
+ make_mir_scope(ccx, &mir, &has_variables, fn_metadata, scope, &mut scopes);
}
scopes
//!
//! All private state used by the module is stored within either the
//! CrateDebugContext struct (owned by the CrateContext) or the
-//! FunctionDebugContext (owned by the FunctionContext).
+//! FunctionDebugContext (owned by the MirContext).
//!
//! This file consists of three conceptual sections:
//! 1. The public interface of the module
MemberDescription {
name: name,
- llvm_type: type_of::type_of(cx, fty),
+ llvm_type: type_of::in_memory_type_of(cx, fty),
type_metadata: type_metadata(cx, fty, self.span),
offset: offset,
flags: DIFlags::FlagZero,
use rustc::ty::subst::Substs;
use abi::Abi;
-use common::{CrateContext, BlockAndBuilder};
+use common::CrateContext;
+use builder::Builder;
use monomorphize::{self, Instance};
use rustc::ty::{self, Ty};
use rustc::mir;
}
}
-pub fn declare_local<'a, 'tcx>(bcx: &BlockAndBuilder<'a, 'tcx>,
+pub fn declare_local<'a, 'tcx>(bcx: &Builder<'a, 'tcx>,
dbg_context: &FunctionDebugContext,
variable_name: ast::Name,
variable_type: Ty<'tcx>,
};
let dbg_loc = if function_debug_context.source_locations_enabled.get() {
- debug!("set_source_location: {}", builder.ccx.sess().codemap().span_to_string(span));
+ debug!("set_source_location: {}", builder.sess().codemap().span_to_string(span));
let loc = span_start(builder.ccx, span);
InternalDebugLocation::new(scope, loc.line, loc.col.to_usize())
} else {
// Code relating to drop glue.
use std;
+use std::ptr;
use std::iter;
use llvm;
use middle::lang_items::BoxFreeFnLangItem;
use rustc::ty::subst::{Substs};
use rustc::traits;
-use rustc::ty::{self, AdtKind, Ty, TypeFoldable};
+use rustc::ty::{self, layout, AdtDef, AdtKind, Ty, TypeFoldable};
use rustc::ty::subst::Kind;
-use adt::{self, MaybeSizedValue};
+use rustc::mir::tcx::LvalueTy;
+use mir::lvalue::LvalueRef;
+use adt;
use base::*;
use callee::Callee;
+use cleanup::CleanupScope;
use common::*;
use machine::*;
use monomorphize;
use type_::Type;
use value::Value;
use Disr;
-use cleanup::CleanupScope;
+use builder::Builder;
use syntax_pos::DUMMY_SP;
-pub fn trans_exchange_free_ty<'a, 'tcx>(
- bcx: &BlockAndBuilder<'a, 'tcx>,
- ptr: MaybeSizedValue,
- content_ty: Ty<'tcx>
-) {
+pub fn trans_exchange_free_ty<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, ptr: LvalueRef<'tcx>) {
+ let content_ty = ptr.ty.to_ty(bcx.tcx());
let def_id = langcall(bcx.tcx(), None, "", BoxFreeFnLangItem);
let substs = bcx.tcx().mk_substs(iter::once(Kind::from(content_ty)));
let callee = Callee::def(bcx.ccx, def_id, substs);
let fn_ty = callee.direct_fn_type(bcx.ccx, &[]);
let llret = bcx.call(callee.reify(bcx.ccx),
- &[ptr.value, ptr.meta][..1 + ptr.has_meta() as usize], None);
+ &[ptr.llval, ptr.llextra][..1 + ptr.has_extra() as usize], None);
fn_ty.apply_attrs_callsite(llret);
}
}
}
-fn drop_ty<'a, 'tcx>(bcx: &BlockAndBuilder<'a, 'tcx>, args: MaybeSizedValue, t: Ty<'tcx>) {
- call_drop_glue(bcx, args, t, false, None)
+fn drop_ty<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, args: LvalueRef<'tcx>) {
+ call_drop_glue(bcx, args, false, None)
}
pub fn call_drop_glue<'a, 'tcx>(
- bcx: &BlockAndBuilder<'a, 'tcx>,
- mut args: MaybeSizedValue,
- t: Ty<'tcx>,
+ bcx: &Builder<'a, 'tcx>,
+ mut args: LvalueRef<'tcx>,
skip_dtor: bool,
funclet: Option<&'a Funclet>,
) {
+ let t = args.ty.to_ty(bcx.tcx());
// NB: v is an *alias* of type t here, not a direct value.
debug!("call_drop_glue(t={:?}, skip_dtor={})", t, skip_dtor);
if bcx.ccx.shared().type_needs_drop(t) {
let glue = get_drop_glue_core(ccx, g);
let glue_type = get_drop_glue_type(ccx.shared(), t);
if glue_type != t {
- args.value = bcx.pointercast(args.value, type_of(ccx, glue_type).ptr_to());
+ args.llval = bcx.pointercast(args.llval, type_of(ccx, glue_type).ptr_to());
}
// No drop-hint ==> call standard drop glue
- bcx.call(glue, &[args.value, args.meta][..1 + args.has_meta() as usize],
+ bcx.call(glue, &[args.llval, args.llextra][..1 + args.has_extra() as usize],
funclet.map(|b| b.bundle()));
}
}
assert_eq!(g.ty(), get_drop_glue_type(ccx.shared(), g.ty()));
let (llfn, _) = ccx.drop_glues().borrow().get(&g).unwrap().clone();
- let fcx = FunctionContext::new(ccx, llfn);
- let mut bcx = fcx.get_entry_block();
+ let mut bcx = Builder::new_block(ccx, llfn, "entry-block");
ccx.stats().n_glues_created.set(ccx.stats().n_glues_created.get() + 1);
// All glue functions take values passed *by alias*; this is a
let value = get_param(llfn, 0);
let ptr = if ccx.shared().type_is_sized(t) {
- MaybeSizedValue::sized(value)
+ LvalueRef::new_sized_ty(value, t)
} else {
- MaybeSizedValue::unsized_(value, get_param(llfn, 1))
+ LvalueRef::new_unsized_ty(value, get_param(llfn, 1), t)
};
let skip_dtor = match g {
// a safe-guard, assert TyBox not used with TyContents.
assert!(!skip_dtor);
let ptr = if !bcx.ccx.shared().type_is_sized(content_ty) {
- let llbox = bcx.load(get_dataptr(&bcx, ptr.value));
- let info = bcx.load(get_meta(&bcx, ptr.value));
- MaybeSizedValue::unsized_(llbox, info)
+ let llbox = bcx.load(get_dataptr(&bcx, ptr.llval));
+ let info = bcx.load(get_meta(&bcx, ptr.llval));
+ LvalueRef::new_unsized_ty(llbox, info, content_ty)
} else {
- MaybeSizedValue::sized(bcx.load(ptr.value))
+ LvalueRef::new_sized_ty(bcx.load(ptr.llval), content_ty)
};
- drop_ty(&bcx, ptr, content_ty);
- trans_exchange_free_ty(&bcx, ptr, content_ty);
+ drop_ty(&bcx, ptr);
+ trans_exchange_free_ty(&bcx, ptr);
bcx
}
ty::TyDynamic(..) => {
// versus without calling Drop::drop. Assert caller is
// okay with always calling the Drop impl, if any.
assert!(!skip_dtor);
- let dtor = bcx.load(ptr.meta);
- bcx.call(dtor, &[ptr.value], None);
+ let dtor = bcx.load(ptr.llextra);
+ bcx.call(dtor, &[ptr.llval], None);
bcx
}
ty::TyAdt(def, ..) if def.dtor_kind().is_present() && !skip_dtor => {
// Issue #23611: schedule cleanup of contents, re-inspecting the
// discriminant (if any) in case of variant swap in drop code.
let contents_scope = if !shallow_drop {
- bcx.fcx().schedule_drop_adt_contents(ptr, t)
+ CleanupScope::schedule_drop_adt_contents(&bcx, ptr)
} else {
CleanupScope::noop()
};
let callee = Callee::def(bcx.ccx, dtor_did, vtbl.substs);
let fn_ty = callee.direct_fn_type(bcx.ccx, &[]);
let llret;
- let args = &[ptr.value, ptr.meta][..1 + ptr.has_meta() as usize];
+ let args = &[ptr.llval, ptr.llextra][..1 + ptr.has_extra() as usize];
if let Some(landing_pad) = contents_scope.landing_pad {
- let normal_bcx = bcx.fcx().build_new_block("normal-return");
+ let normal_bcx = bcx.build_sibling_block("normal-return");
llret = bcx.invoke(callee.reify(ccx), args, normal_bcx.llbb(), landing_pad, None);
bcx = normal_bcx;
} else {
}
_ => {
if bcx.ccx.shared().type_needs_drop(t) {
- drop_structural_ty(bcx, ptr, t)
+ drop_structural_ty(bcx, ptr)
} else {
bcx
}
bcx.ret_void();
}
-pub fn size_and_align_of_dst<'a, 'tcx>(bcx: &BlockAndBuilder<'a, 'tcx>,
- t: Ty<'tcx>, info: ValueRef)
+pub fn size_and_align_of_dst<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, t: Ty<'tcx>, info: ValueRef)
-> (ValueRef, ValueRef) {
debug!("calculate size of DST: {}; with lost info: {:?}",
t, Value(info));
}
// Iterates through the elements of a structural type, dropping them.
-fn drop_structural_ty<'a, 'tcx>(cx: BlockAndBuilder<'a, 'tcx>,
- ptr: MaybeSizedValue,
- t: Ty<'tcx>)
- -> BlockAndBuilder<'a, 'tcx> {
- fn iter_variant<'a, 'tcx>(cx: &BlockAndBuilder<'a, 'tcx>,
- t: Ty<'tcx>,
- av: adt::MaybeSizedValue,
- variant: &'tcx ty::VariantDef,
- substs: &Substs<'tcx>) {
+fn drop_structural_ty<'a, 'tcx>(
+ cx: Builder<'a, 'tcx>,
+ mut ptr: LvalueRef<'tcx>
+) -> Builder<'a, 'tcx> {
+ fn iter_variant_fields<'a, 'tcx>(
+ cx: &'a Builder<'a, 'tcx>,
+ av: LvalueRef<'tcx>,
+ adt_def: &'tcx AdtDef,
+ variant_index: usize,
+ substs: &'tcx Substs<'tcx>
+ ) {
+ let variant = &adt_def.variants[variant_index];
let tcx = cx.tcx();
for (i, field) in variant.fields.iter().enumerate() {
let arg = monomorphize::field_ty(tcx, substs, field);
- let field_ptr = adt::trans_field_ptr(&cx, t, av, Disr::from(variant.disr_val), i);
- drop_ty(&cx, MaybeSizedValue::sized(field_ptr), arg);
+ let field_ptr = av.trans_field_ptr(&cx, i);
+ drop_ty(&cx, LvalueRef::new_sized_ty(field_ptr, arg));
}
}
let mut cx = cx;
+ let t = ptr.ty.to_ty(cx.tcx());
match t.sty {
ty::TyClosure(def_id, substs) => {
for (i, upvar_ty) in substs.upvar_tys(def_id, cx.tcx()).enumerate() {
- let llupvar = adt::trans_field_ptr(&cx, t, ptr, Disr(0), i);
- drop_ty(&cx, MaybeSizedValue::sized(llupvar), upvar_ty);
+ let llupvar = ptr.trans_field_ptr(&cx, i);
+ drop_ty(&cx, LvalueRef::new_sized_ty(llupvar, upvar_ty));
}
}
ty::TyArray(_, n) => {
- let base = get_dataptr(&cx, ptr.value);
+ let base = get_dataptr(&cx, ptr.llval);
let len = C_uint(cx.ccx, n);
let unit_ty = t.sequence_element_type(cx.tcx());
cx = tvec::slice_for_each(&cx, base, unit_ty, len,
- |bb, vv| drop_ty(bb, MaybeSizedValue::sized(vv), unit_ty));
+ |bb, vv| drop_ty(bb, LvalueRef::new_sized_ty(vv, unit_ty)));
}
ty::TySlice(_) | ty::TyStr => {
let unit_ty = t.sequence_element_type(cx.tcx());
- cx = tvec::slice_for_each(&cx, ptr.value, unit_ty, ptr.meta,
- |bb, vv| drop_ty(bb, MaybeSizedValue::sized(vv), unit_ty));
+ cx = tvec::slice_for_each(&cx, ptr.llval, unit_ty, ptr.llextra,
+ |bb, vv| drop_ty(bb, LvalueRef::new_sized_ty(vv, unit_ty)));
}
ty::TyTuple(ref args) => {
for (i, arg) in args.iter().enumerate() {
- let llfld_a = adt::trans_field_ptr(&cx, t, ptr, Disr(0), i);
- drop_ty(&cx, MaybeSizedValue::sized(llfld_a), *arg);
+ let llfld_a = ptr.trans_field_ptr(&cx, i);
+ drop_ty(&cx, LvalueRef::new_sized_ty(llfld_a, *arg));
}
}
ty::TyAdt(adt, substs) => match adt.adt_kind() {
AdtKind::Struct => {
- let VariantInfo { fields, discr } = VariantInfo::from_ty(cx.tcx(), t, None);
- for (i, &Field(_, field_ty)) in fields.iter().enumerate() {
- let llfld_a = adt::trans_field_ptr(&cx, t, ptr, Disr::from(discr), i);
- let ptr = if cx.ccx.shared().type_is_sized(field_ty) {
- MaybeSizedValue::sized(llfld_a)
- } else {
- MaybeSizedValue::unsized_(llfld_a, ptr.meta)
- };
- drop_ty(&cx, ptr, field_ty);
+ for (i, field) in adt.variants[0].fields.iter().enumerate() {
+ let field_ty = monomorphize::field_ty(cx.tcx(), substs, field);
+ let mut field_ptr = ptr.clone();
+ field_ptr.llval = ptr.trans_field_ptr(&cx, i);
+ field_ptr.ty = LvalueTy::from_ty(field_ty);
+ if cx.ccx.shared().type_is_sized(field_ty) {
+ field_ptr.llextra = ptr::null_mut();
+ }
+ drop_ty(&cx, field_ptr);
}
}
AdtKind::Union => {
// NB: we must hit the discriminant first so that structural
// comparison know not to proceed when the discriminants differ.
- match adt::trans_switch(&cx, t, ptr.value, false) {
- (adt::BranchKind::Single, None) => {
+ // Obtain a representation of the discriminant sufficient to translate
+ // destructuring; this may or may not involve the actual discriminant.
+ let l = cx.ccx.layout_of(t);
+ match *l {
+ layout::Univariant { .. } |
+ layout::UntaggedUnion { .. } => {
if n_variants != 0 {
assert!(n_variants == 1);
- iter_variant(&cx, t, ptr, &adt.variants[0], substs);
+ ptr.ty = LvalueTy::Downcast {
+ adt_def: adt,
+ substs: substs,
+ variant_index: 0,
+ };
+ iter_variant_fields(&cx, ptr, &adt, 0, substs);
}
}
- (adt::BranchKind::Switch, Some(lldiscrim_a)) => {
+ layout::CEnum { .. } |
+ layout::General { .. } |
+ layout::RawNullablePointer { .. } |
+ layout::StructWrappedNullablePointer { .. } => {
+ let lldiscrim_a = adt::trans_get_discr(&cx, t, ptr.llval, None, false);
let tcx = cx.tcx();
- drop_ty(&cx, MaybeSizedValue::sized(lldiscrim_a), tcx.types.isize);
+ drop_ty(&cx, LvalueRef::new_sized_ty(lldiscrim_a, tcx.types.isize));
// Create a fall-through basic block for the "else" case of
// the switch instruction we're about to generate. Note that
// from the outer function, and any other use case will only
// call this for an already-valid enum in which case the `ret
// void` will never be hit.
- let ret_void_cx = cx.fcx().build_new_block("enum-iter-ret-void");
+ let ret_void_cx = cx.build_sibling_block("enum-iter-ret-void");
ret_void_cx.ret_void();
let llswitch = cx.switch(lldiscrim_a, ret_void_cx.llbb(), n_variants);
- let next_cx = cx.fcx().build_new_block("enum-iter-next");
+ let next_cx = cx.build_sibling_block("enum-iter-next");
- for variant in &adt.variants {
+ for (i, variant) in adt.variants.iter().enumerate() {
let variant_cx_name = format!("enum-iter-variant-{}",
&variant.disr_val.to_string());
- let variant_cx = cx.fcx().build_new_block(&variant_cx_name);
+ let variant_cx = cx.build_sibling_block(&variant_cx_name);
let case_val = adt::trans_case(&cx, t, Disr::from(variant.disr_val));
variant_cx.add_case(llswitch, case_val, variant_cx.llbb());
- iter_variant(&variant_cx, t, ptr, variant, substs);
+ ptr.ty = LvalueTy::Downcast {
+ adt_def: adt,
+ substs: substs,
+ variant_index: i,
+ };
+ iter_variant_fields(&variant_cx, ptr, &adt, i, substs);
variant_cx.br(next_cx.llbb());
}
cx = next_cx;
}
- _ => cx.ccx.sess().unimpl("value from adt::trans_switch in drop_structural_ty"),
+ _ => bug!("{} is not an enum.", t),
}
}
},
use llvm::{ValueRef};
use abi::{Abi, FnType};
use adt;
+use mir::lvalue::LvalueRef;
use base::*;
use common::*;
use declare;
use machine;
use type_::Type;
use rustc::ty::{self, Ty};
-use Disr;
use rustc::hir;
use syntax::ast;
use syntax::symbol::Symbol;
+use builder::Builder;
use rustc::session::Session;
use syntax_pos::Span;
/// Remember to add all intrinsics here, in librustc_typeck/check/mod.rs,
/// and in libcore/intrinsics.rs; if you need access to any llvm intrinsics,
/// add them to librustc_trans/trans/context.rs
-pub fn trans_intrinsic_call<'a, 'tcx>(bcx: &BlockAndBuilder<'a, 'tcx>,
+pub fn trans_intrinsic_call<'a, 'tcx>(bcx: &Builder<'a, 'tcx>,
callee_ty: Ty<'tcx>,
fn_ty: &FnType,
llargs: &[ValueRef],
llresult: ValueRef,
span: Span) {
let ccx = bcx.ccx;
- let tcx = bcx.tcx();
+ let tcx = ccx.tcx();
let (def_id, substs, fty) = match callee_ty.sty {
ty::TyFnDef(def_id, substs, ref fty) => (def_id, substs, fty),
bcx.call(expect, &[llargs[0], C_bool(ccx, false)], None)
}
"try" => {
- try_intrinsic(bcx, llargs[0], llargs[1], llargs[2], llresult);
+ try_intrinsic(bcx, ccx, llargs[0], llargs[1], llargs[2], llresult);
C_nil(ccx)
}
"breakpoint" => {
let val = bcx.call(llfn, &[llargs[0], llargs[1]], None);
let result = bcx.extract_value(val, 0);
let overflow = bcx.zext(bcx.extract_value(val, 1), Type::bool(ccx));
- bcx.store(result, bcx.struct_gep(llresult, 0));
- bcx.store(overflow, bcx.struct_gep(llresult, 1));
+ bcx.store(result, bcx.struct_gep(llresult, 0), None);
+ bcx.store(overflow, bcx.struct_gep(llresult, 1), None);
C_nil(bcx.ccx)
},
failorder, weak);
let result = bcx.extract_value(val, 0);
let success = bcx.zext(bcx.extract_value(val, 1), Type::bool(bcx.ccx));
- bcx.store(result, bcx.struct_gep(llresult, 0));
- bcx.store(success, bcx.struct_gep(llresult, 1));
+ bcx.store(result, bcx.struct_gep(llresult, 0), None);
+ bcx.store(success, bcx.struct_gep(llresult, 1), None);
} else {
invalid_monomorphization(sty);
}
// qux` to be converted into `foo, bar, baz, qux`, integer
// arguments to be truncated as needed and pointers to be
// cast.
- fn modify_as_needed<'a, 'tcx>(bcx: &BlockAndBuilder<'a, 'tcx>,
+ fn modify_as_needed<'a, 'tcx>(bcx: &Builder<'a, 'tcx>,
t: &intrinsics::Type,
arg_type: Ty<'tcx>,
llarg: ValueRef)
// destructors, and the contents are SIMD
// etc.
assert!(!bcx.ccx.shared().type_needs_drop(arg_type));
- let arg = adt::MaybeSizedValue::sized(llarg);
- (0..contents.len())
- .map(|i| {
- bcx.load(adt::trans_field_ptr(bcx, arg_type, arg, Disr(0), i))
- })
- .collect()
+ let arg = LvalueRef::new_sized_ty(llarg, arg_type);
+ (0..contents.len()).map(|i| bcx.load(arg.trans_field_ptr(bcx, i))).collect()
}
intrinsics::Type::Pointer(_, Some(ref llvm_elem), _) => {
let llvm_elem = one(ty_to_type(bcx.ccx, llvm_elem, &mut false));
for i in 0..elems.len() {
let val = bcx.extract_value(val, i);
- bcx.store(val, bcx.struct_gep(llresult, i));
+ bcx.store(val, bcx.struct_gep(llresult, i), None);
}
C_nil(ccx)
}
if val_ty(llval) != Type::void(ccx) && machine::llsize_of_alloc(ccx, val_ty(llval)) != 0 {
if let Some(ty) = fn_ty.ret.cast {
let ptr = bcx.pointercast(llresult, ty.ptr_to());
- let store = bcx.store(llval, ptr);
- unsafe {
- llvm::LLVMSetAlignment(store, type_of::align_of(ccx, ret_ty));
- }
+ bcx.store(llval, ptr, Some(type_of::align_of(ccx, ret_ty)));
} else {
store_ty(bcx, llval, llresult, ret_ty);
}
}
}
-fn copy_intrinsic<'a, 'tcx>(bcx: &BlockAndBuilder<'a, 'tcx>,
+fn copy_intrinsic<'a, 'tcx>(bcx: &Builder<'a, 'tcx>,
allow_overlap: bool,
volatile: bool,
tp_ty: Ty<'tcx>,
}
fn memset_intrinsic<'a, 'tcx>(
- bcx: &BlockAndBuilder<'a, 'tcx>,
+ bcx: &Builder<'a, 'tcx>,
volatile: bool,
ty: Ty<'tcx>,
dst: ValueRef,
}
fn try_intrinsic<'a, 'tcx>(
- bcx: &BlockAndBuilder<'a, 'tcx>,
+ bcx: &Builder<'a, 'tcx>,
+ ccx: &CrateContext,
func: ValueRef,
data: ValueRef,
local_ptr: ValueRef,
) {
if bcx.sess().no_landing_pads() {
bcx.call(func, &[data], None);
- bcx.store(C_null(Type::i8p(&bcx.ccx)), dest);
+ bcx.store(C_null(Type::i8p(&bcx.ccx)), dest, None);
} else if wants_msvc_seh(bcx.sess()) {
- trans_msvc_try(bcx, func, data, local_ptr, dest);
+ trans_msvc_try(bcx, ccx, func, data, local_ptr, dest);
} else {
- trans_gnu_try(bcx, func, data, local_ptr, dest);
+ trans_gnu_try(bcx, ccx, func, data, local_ptr, dest);
}
}
// instructions are meant to work for all targets, as of the time of this
// writing, however, LLVM does not recommend the usage of these new instructions
// as the old ones are still more optimized.
-fn trans_msvc_try<'a, 'tcx>(bcx: &BlockAndBuilder<'a, 'tcx>,
+fn trans_msvc_try<'a, 'tcx>(bcx: &Builder<'a, 'tcx>,
+ ccx: &CrateContext,
func: ValueRef,
data: ValueRef,
local_ptr: ValueRef,
dest: ValueRef) {
- let llfn = get_rust_try_fn(bcx.fcx(), &mut |bcx| {
+ let llfn = get_rust_try_fn(ccx, &mut |bcx| {
let ccx = bcx.ccx;
bcx.set_personality_fn(bcx.ccx.eh_personality());
- let normal = bcx.fcx().build_new_block("normal");
- let catchswitch = bcx.fcx().build_new_block("catchswitch");
- let catchpad = bcx.fcx().build_new_block("catchpad");
- let caught = bcx.fcx().build_new_block("caught");
+ let normal = bcx.build_sibling_block("normal");
+ let catchswitch = bcx.build_sibling_block("catchswitch");
+ let catchpad = bcx.build_sibling_block("catchpad");
+ let caught = bcx.build_sibling_block("caught");
- let func = llvm::get_param(bcx.fcx().llfn, 0);
- let data = llvm::get_param(bcx.fcx().llfn, 1);
- let local_ptr = llvm::get_param(bcx.fcx().llfn, 2);
+ let func = llvm::get_param(bcx.llfn(), 0);
+ let data = llvm::get_param(bcx.llfn(), 1);
+ let local_ptr = llvm::get_param(bcx.llfn(), 2);
// We're generating an IR snippet that looks like:
//
//
// More information can be found in libstd's seh.rs implementation.
let i64p = Type::i64(ccx).ptr_to();
- let slot = bcx.fcx().alloca(i64p, "slot");
+ let slot = bcx.alloca(i64p, "slot");
bcx.invoke(func, &[data], normal.llbb(), catchswitch.llbb(),
None);
let val1 = C_i32(ccx, 1);
let arg2 = catchpad.load(catchpad.inbounds_gep(addr, &[val1]));
let local_ptr = catchpad.bitcast(local_ptr, i64p);
- catchpad.store(arg1, local_ptr);
- catchpad.store(arg2, catchpad.inbounds_gep(local_ptr, &[val1]));
+ catchpad.store(arg1, local_ptr, None);
+ catchpad.store(arg2, catchpad.inbounds_gep(local_ptr, &[val1]), None);
catchpad.catch_ret(tok, caught.llbb());
caught.ret(C_i32(ccx, 1));
// Note that no invoke is used here because by definition this function
// can't panic (that's what it's catching).
let ret = bcx.call(llfn, &[func, data, local_ptr], None);
- bcx.store(ret, dest);
+ bcx.store(ret, dest, None);
}
// Definition of the standard "try" function for Rust using the GNU-like model
// function calling it, and that function may already have other personality
// functions in play. By calling a shim we're guaranteed that our shim will have
// the right personality function.
-fn trans_gnu_try<'a, 'tcx>(bcx: &BlockAndBuilder<'a, 'tcx>,
+fn trans_gnu_try<'a, 'tcx>(bcx: &Builder<'a, 'tcx>,
+ ccx: &CrateContext,
func: ValueRef,
data: ValueRef,
local_ptr: ValueRef,
dest: ValueRef) {
- let llfn = get_rust_try_fn(bcx.fcx(), &mut |bcx| {
+ let llfn = get_rust_try_fn(ccx, &mut |bcx| {
let ccx = bcx.ccx;
// Translates the shims described above:
// expected to be `*mut *mut u8` for this to actually work, but that's
// managed by the standard library.
- let then = bcx.fcx().build_new_block("then");
- let catch = bcx.fcx().build_new_block("catch");
+ let then = bcx.build_sibling_block("then");
+ let catch = bcx.build_sibling_block("catch");
- let func = llvm::get_param(bcx.fcx().llfn, 0);
- let data = llvm::get_param(bcx.fcx().llfn, 1);
- let local_ptr = llvm::get_param(bcx.fcx().llfn, 2);
+ let func = llvm::get_param(bcx.llfn(), 0);
+ let data = llvm::get_param(bcx.llfn(), 1);
+ let local_ptr = llvm::get_param(bcx.llfn(), 2);
bcx.invoke(func, &[data], then.llbb(), catch.llbb(), None);
then.ret(C_i32(ccx, 0));
// rust_try ignores the selector.
let lpad_ty = Type::struct_(ccx, &[Type::i8p(ccx), Type::i32(ccx)],
false);
- let vals = catch.landing_pad(lpad_ty, bcx.ccx.eh_personality(), 1, catch.fcx().llfn);
+ let vals = catch.landing_pad(lpad_ty, bcx.ccx.eh_personality(), 1, catch.llfn());
catch.add_clause(vals, C_null(Type::i8p(ccx)));
let ptr = catch.extract_value(vals, 0);
- catch.store(ptr, catch.bitcast(local_ptr, Type::i8p(ccx).ptr_to()));
+ catch.store(ptr, catch.bitcast(local_ptr, Type::i8p(ccx).ptr_to()), None);
catch.ret(C_i32(ccx, 1));
});
// Note that no invoke is used here because by definition this function
// can't panic (that's what it's catching).
let ret = bcx.call(llfn, &[func, data, local_ptr], None);
- bcx.store(ret, dest);
+ bcx.store(ret, dest, None);
}
// Helper function to give a Block to a closure to translate a shim function.
// This is currently primarily used for the `try` intrinsic functions above.
-fn gen_fn<'a, 'tcx>(fcx: &FunctionContext<'a, 'tcx>,
+fn gen_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
name: &str,
inputs: Vec<Ty<'tcx>>,
output: Ty<'tcx>,
- trans: &mut for<'b> FnMut(BlockAndBuilder<'b, 'tcx>))
+ trans: &mut for<'b> FnMut(Builder<'b, 'tcx>))
-> ValueRef {
- let ccx = fcx.ccx;
let sig = ccx.tcx().mk_fn_sig(inputs.into_iter(), output, false);
let rust_fn_ty = ccx.tcx().mk_fn_ptr(ccx.tcx().mk_bare_fn(ty::BareFnTy {
sig: ty::Binder(sig)
}));
let llfn = declare::define_internal_fn(ccx, name, rust_fn_ty);
- let fcx = FunctionContext::new(ccx, llfn);
- trans(fcx.get_entry_block());
+ let bcx = Builder::new_block(ccx, llfn, "entry-block");
+ trans(bcx);
llfn
}
// catch exceptions.
//
// This function is only generated once and is then cached.
-fn get_rust_try_fn<'a, 'tcx>(fcx: &FunctionContext<'a, 'tcx>,
- trans: &mut for<'b> FnMut(BlockAndBuilder<'b, 'tcx>))
+fn get_rust_try_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
+ trans: &mut for<'b> FnMut(Builder<'b, 'tcx>))
-> ValueRef {
- let ccx = fcx.ccx;
if let Some(llfn) = ccx.rust_try_fn().get() {
return llfn;
}
sig: ty::Binder(tcx.mk_fn_sig(iter::once(i8p), tcx.mk_nil(), false)),
}));
let output = tcx.types.i32;
- let rust_try = gen_fn(fcx, "__rust_try", vec![fn_ty, i8p, i8p], output, trans);
+ let rust_try = gen_fn(ccx, "__rust_try", vec![fn_ty, i8p, i8p], output, trans);
ccx.rust_try_fn().set(Some(rust_try));
return rust_try
}
}
fn generic_simd_intrinsic<'a, 'tcx>(
- bcx: &BlockAndBuilder<'a, 'tcx>,
+ bcx: &Builder<'a, 'tcx>,
name: &str,
callee_ty: Ty<'tcx>,
llargs: &[ValueRef],
use rustc::traits;
use callee::{Callee, CalleeData};
use common::*;
+use builder::Builder;
use consts;
use declare;
use glue;
const VTABLE_OFFSET: usize = 3;
/// Extracts a method from a trait object's vtable, at the specified index.
-pub fn get_virtual_method<'a, 'tcx>(bcx: &BlockAndBuilder<'a, 'tcx>,
+pub fn get_virtual_method<'a, 'tcx>(bcx: &Builder<'a, 'tcx>,
llvtable: ValueRef,
vtable_index: usize)
-> ValueRef {
let llfn = declare::define_internal_fn(ccx, &function_name, callee.ty);
attributes::set_frame_pointer_elimination(ccx, llfn);
- let fcx = FunctionContext::new(ccx, llfn);
- let bcx = fcx.get_entry_block();
+ let bcx = Builder::new_block(ccx, llfn, "entry-block");
- let mut llargs = get_params(fcx.llfn);
+ let mut llargs = get_params(llfn);
let fn_ret = callee.ty.fn_ret();
let fn_ty = callee.direct_fn_type(ccx, &[]);
use rustc::ty::{self, layout};
use rustc::mir;
use abi::{Abi, FnType, ArgType};
-use adt::{self, MaybeSizedValue};
+use adt;
use base::{self, Lifetime};
use callee::{Callee, CalleeData, Fn, Intrinsic, NamedTupleConstructor, Virtual};
-use common::{self, BlockAndBuilder, Funclet};
+use builder::Builder;
+use common::{self, Funclet};
use common::{C_bool, C_str_slice, C_struct, C_u32, C_undef};
use consts;
use Disr;
use machine::{llalign_of_min, llbitsize_of_real};
use meth;
-use type_of;
+use type_of::{self, align_of};
use glue;
use type_::Type;
use rustc_data_structures::fx::FxHashMap;
use syntax::symbol::Symbol;
+use std::cmp;
+
use super::{MirContext, LocalRef};
use super::analyze::CleanupKind;
use super::constant::Const;
-use super::lvalue::{LvalueRef};
+use super::lvalue::LvalueRef;
use super::operand::OperandRef;
use super::operand::OperandValue::{Pair, Ref, Immediate};
impl<'a, 'tcx> MirContext<'a, 'tcx> {
pub fn trans_block(&mut self, bb: mir::BasicBlock,
funclets: &IndexVec<mir::BasicBlock, Option<Funclet>>) {
- let mut bcx = self.build_block(bb);
+ let mut bcx = self.get_builder(bb);
let data = &self.mir[bb];
debug!("trans_block({:?}={:?})", bb, data);
let cleanup_pad = funclet.map(|lp| lp.cleanuppad());
let cleanup_bundle = funclet.map(|l| l.bundle());
- let funclet_br = |this: &Self, bcx: BlockAndBuilder, bb: mir::BasicBlock| {
+ let funclet_br = |this: &Self, bcx: Builder, bb: mir::BasicBlock| {
let lltarget = this.blocks[bb];
if let Some(cp) = cleanup_pad {
match this.cleanup_kinds[bb] {
debug!("llblock: creating cleanup trampoline for {:?}", target);
let name = &format!("{:?}_cleanup_trampoline_{:?}", bb, target);
- let trampoline = this.fcx.build_new_block(name);
+ let trampoline = this.new_block(name);
trampoline.cleanup_ret(cp, Some(lltarget));
trampoline.llbb()
}
};
let llslot = match op.val {
Immediate(_) | Pair(..) => {
- let llscratch = bcx.fcx().alloca(ret.original_ty, "ret");
- self.store_operand(&bcx, llscratch, op);
+ let llscratch = bcx.alloca(ret.original_ty, "ret");
+ self.store_operand(&bcx, llscratch, op, None);
llscratch
}
Ref(llval) => llval
load
} else {
let op = self.trans_consume(&bcx, &mir::Lvalue::Local(mir::RETURN_POINTER));
- op.pack_if_pair(&bcx).immediate()
+ if let Ref(llval) = op.val {
+ base::load_ty(&bcx, llval, op.ty)
+ } else {
+ op.pack_if_pair(&bcx).immediate()
+ }
};
bcx.ret(llval);
}
return;
}
- let lvalue = self.trans_lvalue(&bcx, location);
+ let mut lvalue = self.trans_lvalue(&bcx, location);
let drop_fn = glue::get_drop_glue(bcx.ccx, ty);
let drop_ty = glue::get_drop_glue_type(bcx.ccx.shared(), ty);
- let ptr = if bcx.ccx.shared().type_is_sized(ty) {
- let value = if drop_ty != ty {
- bcx.pointercast(lvalue.llval, type_of::type_of(bcx.ccx, drop_ty).ptr_to())
- } else {
- lvalue.llval
- };
- MaybeSizedValue::sized(value)
- } else {
- MaybeSizedValue::unsized_(lvalue.llval, lvalue.llextra)
- };
- let args = &[ptr.value, ptr.meta][..1 + ptr.has_meta() as usize];
+ if bcx.ccx.shared().type_is_sized(ty) && drop_ty != ty {
+ lvalue.llval = bcx.pointercast(
+ lvalue.llval, type_of::type_of(bcx.ccx, drop_ty).ptr_to());
+ }
+ let args = &[lvalue.llval, lvalue.llextra][..1 + lvalue.has_extra() as usize];
if let Some(unwind) = unwind {
bcx.invoke(
drop_fn,
// Create the failure block and the conditional branch to it.
let lltarget = llblock(self, target);
- let panic_block = self.fcx.build_new_block("panic");
+ let panic_block = self.new_block("panic");
if expected {
bcx.cond_br(cond, lltarget, panic_block.llbb());
} else {
// The first argument is a thin destination pointer.
let llptr = self.trans_operand(&bcx, &args[0]).immediate();
let val = self.trans_operand(&bcx, &args[1]);
- self.store_operand(&bcx, llptr, val);
+ self.store_operand(&bcx, llptr, val, None);
funclet_br(self, bcx, target);
return;
}
fn_ty.apply_attrs_callsite(invokeret);
if let Some((_, target)) = *destination {
- let ret_bcx = self.build_block(target);
- ret_bcx.at_start(|ret_bcx| {
- self.set_debug_loc(&ret_bcx, terminator.source_info);
- let op = OperandRef {
- val: Immediate(invokeret),
- ty: sig.output(),
- };
- self.store_return(&ret_bcx, ret_dest, fn_ty.ret, op);
- });
+ let ret_bcx = self.get_builder(target);
+ self.set_debug_loc(&ret_bcx, terminator.source_info);
+ let op = OperandRef {
+ val: Immediate(invokeret),
+ ty: sig.output(),
+ };
+ self.store_return(&ret_bcx, ret_dest, fn_ty.ret, op);
}
} else {
let llret = bcx.call(fn_ptr, &llargs, cleanup_bundle);
}
fn trans_argument(&mut self,
- bcx: &BlockAndBuilder<'a, 'tcx>,
+ bcx: &Builder<'a, 'tcx>,
op: OperandRef<'tcx>,
llargs: &mut Vec<ValueRef>,
fn_ty: &FnType,
let (mut llval, by_ref) = match op.val {
Immediate(_) | Pair(..) => {
if arg.is_indirect() || arg.cast.is_some() {
- let llscratch = bcx.fcx().alloca(arg.original_ty, "arg");
- self.store_operand(bcx, llscratch, op);
+ let llscratch = bcx.alloca(arg.original_ty, "arg");
+ self.store_operand(bcx, llscratch, op, None);
(llscratch, true)
} else {
(op.pack_if_pair(bcx).immediate(), false)
}
fn trans_arguments_untupled(&mut self,
- bcx: &BlockAndBuilder<'a, 'tcx>,
+ bcx: &Builder<'a, 'tcx>,
operand: &mir::Operand<'tcx>,
llargs: &mut Vec<ValueRef>,
fn_ty: &FnType,
// Handle both by-ref and immediate tuples.
match tuple.val {
Ref(llval) => {
- let base = adt::MaybeSizedValue::sized(llval);
for (n, &ty) in arg_types.iter().enumerate() {
- let ptr = adt::trans_field_ptr(bcx, tuple.ty, base, Disr(0), n);
+ let ptr = LvalueRef::new_sized_ty(llval, tuple.ty);
+ let ptr = ptr.trans_field_ptr(bcx, n);
let val = if common::type_is_fat_ptr(bcx.ccx, ty) {
let (lldata, llextra) = base::load_fat_ptr(bcx, ptr, ty);
Pair(lldata, llextra)
}
- fn get_personality_slot(&mut self, bcx: &BlockAndBuilder<'a, 'tcx>) -> ValueRef {
+ fn get_personality_slot(&mut self, bcx: &Builder<'a, 'tcx>) -> ValueRef {
let ccx = bcx.ccx;
if let Some(slot) = self.llpersonalityslot {
slot
} else {
let llretty = Type::struct_(ccx, &[Type::i8p(ccx), Type::i32(ccx)], false);
- let slot = bcx.fcx().alloca(llretty, "personalityslot");
+ let slot = bcx.alloca(llretty, "personalityslot");
self.llpersonalityslot = Some(slot);
Lifetime::Start.call(bcx, slot);
slot
return self.blocks[target_bb];
}
- let target = self.build_block(target_bb);
+ let target = self.get_builder(target_bb);
- let bcx = self.fcx.build_new_block("cleanup");
+ let bcx = self.new_block("cleanup");
self.landing_pads[target_bb] = Some(bcx.llbb());
let ccx = bcx.ccx;
let llpersonality = self.ccx.eh_personality();
let llretty = Type::struct_(ccx, &[Type::i8p(ccx), Type::i32(ccx)], false);
- let llretval = bcx.landing_pad(llretty, llpersonality, 1, self.fcx.llfn);
+ let llretval = bcx.landing_pad(llretty, llpersonality, 1, self.llfn);
bcx.set_cleanup(llretval);
let slot = self.get_personality_slot(&bcx);
- bcx.store(llretval, slot);
+ bcx.store(llretval, slot, None);
bcx.br(target.llbb());
bcx.llbb()
}
fn unreachable_block(&mut self) -> BasicBlockRef {
self.unreachable_block.unwrap_or_else(|| {
- let bl = self.fcx.build_new_block("unreachable");
+ let bl = self.new_block("unreachable");
bl.unreachable();
self.unreachable_block = Some(bl.llbb());
bl.llbb()
})
}
- pub fn build_block(&self, bb: mir::BasicBlock) -> BlockAndBuilder<'a, 'tcx> {
- BlockAndBuilder::new(self.blocks[bb], self.fcx)
+ pub fn new_block(&self, name: &str) -> Builder<'a, 'tcx> {
+ Builder::new_block(self.ccx, self.llfn, name)
+ }
+
+ pub fn get_builder(&self, bb: mir::BasicBlock) -> Builder<'a, 'tcx> {
+ let builder = Builder::with_ccx(self.ccx);
+ builder.position_at_end(self.blocks[bb]);
+ builder
}
- fn make_return_dest(&mut self, bcx: &BlockAndBuilder<'a, 'tcx>,
+ fn make_return_dest(&mut self, bcx: &Builder<'a, 'tcx>,
dest: &mir::Lvalue<'tcx>, fn_ret_ty: &ArgType,
llargs: &mut Vec<ValueRef>, is_intrinsic: bool) -> ReturnDest {
// If the return is ignored, we can just return a do-nothing ReturnDest
return if fn_ret_ty.is_indirect() {
// Odd, but possible, case, we have an operand temporary,
// but the calling convention has an indirect return.
- let tmp = base::alloc_ty(bcx, ret_ty, "tmp_ret");
+ let tmp = bcx.alloca_ty(ret_ty, "tmp_ret");
llargs.push(tmp);
ReturnDest::IndirectOperand(tmp, index)
} else if is_intrinsic {
// Currently, intrinsics always need a location to store
// the result. so we create a temporary alloca for the
// result
- let tmp = base::alloc_ty(bcx, ret_ty, "tmp_ret");
+ let tmp = bcx.alloca_ty(ret_ty, "tmp_ret");
ReturnDest::IndirectOperand(tmp, index)
} else {
ReturnDest::DirectOperand(index)
}
}
- fn trans_transmute(&mut self, bcx: &BlockAndBuilder<'a, 'tcx>,
+ fn trans_transmute(&mut self, bcx: &Builder<'a, 'tcx>,
src: &mir::Operand<'tcx>, dst: LvalueRef<'tcx>) {
let mut val = self.trans_operand(bcx, src);
if let ty::TyFnDef(def_id, substs, _) = val.ty.sty {
let llty = type_of::type_of(bcx.ccx, val.ty);
let cast_ptr = bcx.pointercast(dst.llval, llty.ptr_to());
- self.store_operand(bcx, cast_ptr, val);
+ let in_type = val.ty;
+ let out_type = dst.ty.to_ty(bcx.tcx());;
+ let llalign = cmp::min(align_of(bcx.ccx, in_type), align_of(bcx.ccx, out_type));
+ self.store_operand(bcx, cast_ptr, val, Some(llalign));
}
// Stores the return value of a function call into it's final location.
fn store_return(&mut self,
- bcx: &BlockAndBuilder<'a, 'tcx>,
+ bcx: &Builder<'a, 'tcx>,
dest: ReturnDest,
ret_ty: ArgType,
op: OperandRef<'tcx>) {
DirectOperand(index) => {
// If there is a cast, we have to store and reload.
let op = if ret_ty.cast.is_some() {
- let tmp = base::alloc_ty(bcx, op.ty, "tmp_ret");
+ let tmp = bcx.alloca_ty(op.ty, "tmp_ret");
ret_ty.store(bcx, op.immediate(), tmp);
self.trans_load(bcx, tmp, op.ty)
} else {
use rustc::infer::TransNormalize;
use rustc::mir;
use rustc::mir::tcx::LvalueTy;
-use rustc::ty::{self, Ty, TyCtxt, TypeFoldable};
+use rustc::ty::{self, layout, Ty, TyCtxt, TypeFoldable};
use rustc::ty::cast::{CastTy, IntTy};
use rustc::ty::subst::Substs;
use rustc_data_structures::indexed_vec::{Idx, IndexVec};
use {abi, adt, base, Disr, machine};
use callee::Callee;
-use common::{self, BlockAndBuilder, CrateContext, const_get_elt, val_ty};
+use builder::Builder;
+use common::{self, CrateContext, const_get_elt, val_ty};
use common::{C_array, C_bool, C_bytes, C_floating_f64, C_integral, C_big_integral};
-use common::{C_null, C_struct, C_str_slice, C_undef, C_uint};
-use common::{const_to_opt_u128};
+use common::{C_null, C_struct, C_str_slice, C_undef, C_uint, C_vector, is_undef};
+use common::const_to_opt_u128;
use consts;
use monomorphize::{self, Instance};
use type_of;
mir::AggregateKind::Adt(..) |
mir::AggregateKind::Closure(..) |
mir::AggregateKind::Tuple => {
- let disr = match *kind {
- mir::AggregateKind::Adt(adt_def, index, _, _) => {
- Disr::from(adt_def.variants[index].disr_val)
- }
- _ => Disr(0)
- };
- Const::new(
- adt::trans_const(self.ccx, dest_ty, disr, &fields),
- dest_ty
- )
+ Const::new(trans_const(self.ccx, dest_ty, kind, &fields), dest_ty)
}
}
}
impl<'a, 'tcx> MirContext<'a, 'tcx> {
pub fn trans_constant(&mut self,
- bcx: &BlockAndBuilder<'a, 'tcx>,
+ bcx: &Builder<'a, 'tcx>,
constant: &mir::Constant<'tcx>)
-> Const<'tcx>
{
let instance = Instance::mono(ccx.shared(), def_id);
MirConstContext::trans_def(ccx, instance, IndexVec::new()).map(|c| c.llval)
}
+
+/// Construct a constant value, suitable for initializing a
+/// GlobalVariable, given a case and constant values for its fields.
+/// Note that this may have a different LLVM type (and different
+/// alignment!) from the representation's `type_of`, so it needs a
+/// pointer cast before use.
+///
+/// The LLVM type system does not directly support unions, and only
+/// pointers can be bitcast, so a constant (and, by extension, the
+/// GlobalVariable initialized by it) will have a type that can vary
+/// depending on which case of an enum it is.
+///
+/// To understand the alignment situation, consider `enum E { V64(u64),
+/// V32(u32, u32) }` on Windows. The type has 8-byte alignment to
+/// accommodate the u64, but `V32(x, y)` would have LLVM type `{i32,
+/// i32, i32}`, which is 4-byte aligned.
+///
+/// Currently the returned value has the same size as the type, but
+/// this could be changed in the future to avoid allocating unnecessary
+/// space after values of shorter-than-maximum cases.
+fn trans_const<'a, 'tcx>(
+ ccx: &CrateContext<'a, 'tcx>,
+ t: Ty<'tcx>,
+ kind: &mir::AggregateKind,
+ vals: &[ValueRef]
+) -> ValueRef {
+ let l = ccx.layout_of(t);
+ let dl = &ccx.tcx().data_layout;
+ let variant_index = match *kind {
+ mir::AggregateKind::Adt(_, index, _, _) => index,
+ _ => 0,
+ };
+ match *l {
+ layout::CEnum { discr: d, min, max, .. } => {
+ let discr = match *kind {
+ mir::AggregateKind::Adt(adt_def, _, _, _) => {
+ Disr::from(adt_def.variants[variant_index].disr_val)
+ },
+ _ => Disr(0),
+ };
+ assert_eq!(vals.len(), 0);
+ adt::assert_discr_in_range(Disr(min), Disr(max), discr);
+ C_integral(Type::from_integer(ccx, d), discr.0, true)
+ }
+ layout::General { discr: d, ref variants, .. } => {
+ let variant = &variants[variant_index];
+ let lldiscr = C_integral(Type::from_integer(ccx, d), variant_index as u64, true);
+ let mut vals_with_discr = vec![lldiscr];
+ vals_with_discr.extend_from_slice(vals);
+ let mut contents = build_const_struct(ccx, &variant, &vals_with_discr[..]);
+ let needed_padding = l.size(dl).bytes() - variant.stride().bytes();
+ if needed_padding > 0 {
+ contents.push(padding(ccx, needed_padding));
+ }
+ C_struct(ccx, &contents[..], false)
+ }
+ layout::UntaggedUnion { ref variants, .. }=> {
+ assert_eq!(variant_index, 0);
+ let contents = build_const_union(ccx, variants, vals[0]);
+ C_struct(ccx, &contents, variants.packed)
+ }
+ layout::Univariant { ref variant, .. } => {
+ assert_eq!(variant_index, 0);
+ let contents = build_const_struct(ccx, &variant, vals);
+ C_struct(ccx, &contents[..], variant.packed)
+ }
+ layout::Vector { .. } => {
+ C_vector(vals)
+ }
+ layout::RawNullablePointer { nndiscr, .. } => {
+ let nnty = adt::compute_fields(ccx, t, nndiscr as usize, false)[0];
+ if variant_index as u64 == nndiscr {
+ assert_eq!(vals.len(), 1);
+ vals[0]
+ } else {
+ C_null(type_of::sizing_type_of(ccx, nnty))
+ }
+ }
+ layout::StructWrappedNullablePointer { ref nonnull, nndiscr, .. } => {
+ if variant_index as u64 == nndiscr {
+ C_struct(ccx, &build_const_struct(ccx, &nonnull, vals), false)
+ } else {
+ let fields = adt::compute_fields(ccx, t, nndiscr as usize, false);
+ let vals = fields.iter().map(|&ty| {
+ // Always use null even if it's not the `discrfield`th
+ // field; see #8506.
+ C_null(type_of::sizing_type_of(ccx, ty))
+ }).collect::<Vec<ValueRef>>();
+ C_struct(ccx, &build_const_struct(ccx, &nonnull, &vals[..]), false)
+ }
+ }
+ _ => bug!("trans_const: cannot handle type {} repreented as {:#?}", t, l)
+ }
+}
+
+/// Building structs is a little complicated, because we might need to
+/// insert padding if a field's value is less aligned than its type.
+///
+/// Continuing the example from `trans_const`, a value of type `(u32,
+/// E)` should have the `E` at offset 8, but if that field's
+/// initializer is 4-byte aligned then simply translating the tuple as
+/// a two-element struct will locate it at offset 4, and accesses to it
+/// will read the wrong memory.
+fn build_const_struct<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
+ st: &layout::Struct,
+ vals: &[ValueRef])
+ -> Vec<ValueRef> {
+ assert_eq!(vals.len(), st.offsets.len());
+
+ if vals.len() == 0 {
+ return Vec::new();
+ }
+
+ // offset of current value
+ let mut offset = 0;
+ let mut cfields = Vec::new();
+ cfields.reserve(st.offsets.len()*2);
+
+ let parts = st.field_index_by_increasing_offset().map(|i| {
+ (&vals[i], st.offsets[i].bytes())
+ });
+ for (&val, target_offset) in parts {
+ if offset < target_offset {
+ cfields.push(padding(ccx, target_offset - offset));
+ offset = target_offset;
+ }
+ assert!(!is_undef(val));
+ cfields.push(val);
+ offset += machine::llsize_of_alloc(ccx, val_ty(val));
+ }
+
+ if offset < st.stride().bytes() {
+ cfields.push(padding(ccx, st.stride().bytes() - offset));
+ }
+
+ cfields
+}
+
+fn build_const_union<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
+ un: &layout::Union,
+ field_val: ValueRef)
+ -> Vec<ValueRef> {
+ let mut cfields = vec![field_val];
+
+ let offset = machine::llsize_of_alloc(ccx, val_ty(field_val));
+ let size = un.stride().bytes();
+ if offset != size {
+ cfields.push(padding(ccx, size - offset));
+ }
+
+ cfields
+}
+
+fn padding(ccx: &CrateContext, size: u64) -> ValueRef {
+ C_undef(Type::array(&Type::i8(ccx), size))
+}
// except according to those terms.
use llvm::ValueRef;
-use rustc::ty::{self, Ty, TypeFoldable};
+use rustc::ty::{self, layout, Ty, TypeFoldable};
use rustc::mir;
use rustc::mir::tcx::LvalueTy;
use rustc_data_structures::indexed_vec::Idx;
use adt;
-use base;
-use common::{self, BlockAndBuilder, CrateContext, C_uint, C_undef};
+use builder::Builder;
+use common::{self, CrateContext, C_uint, C_undef};
use consts;
use machine;
use type_of::type_of;
use type_of;
-use Disr;
+use type_::Type;
+use value::Value;
+use glue;
use std::ptr;
pub ty: LvalueTy<'tcx>,
}
-impl<'tcx> LvalueRef<'tcx> {
+impl<'a, 'tcx> LvalueRef<'tcx> {
pub fn new_sized(llval: ValueRef, lvalue_ty: LvalueTy<'tcx>) -> LvalueRef<'tcx> {
LvalueRef { llval: llval, llextra: ptr::null_mut(), ty: lvalue_ty }
}
- pub fn alloca<'a>(bcx: &BlockAndBuilder<'a, 'tcx>,
- ty: Ty<'tcx>,
- name: &str)
- -> LvalueRef<'tcx>
- {
- assert!(!ty.has_erasable_regions());
- let lltemp = base::alloc_ty(bcx, ty, name);
- LvalueRef::new_sized(lltemp, LvalueTy::from_ty(ty))
+ pub fn new_sized_ty(llval: ValueRef, ty: Ty<'tcx>) -> LvalueRef<'tcx> {
+ LvalueRef::new_sized(llval, LvalueTy::from_ty(ty))
+ }
+
+ pub fn new_unsized_ty(llval: ValueRef, llextra: ValueRef, ty: Ty<'tcx>) -> LvalueRef<'tcx> {
+ LvalueRef {
+ llval: llval,
+ llextra: llextra,
+ ty: LvalueTy::from_ty(ty),
+ }
}
- pub fn len<'a>(&self, ccx: &CrateContext<'a, 'tcx>) -> ValueRef {
+ pub fn len(&self, ccx: &CrateContext<'a, 'tcx>) -> ValueRef {
let ty = self.ty.to_ty(ccx.tcx());
match ty.sty {
ty::TyArray(_, n) => common::C_uint(ccx, n),
_ => bug!("unexpected type `{}` in LvalueRef::len", ty)
}
}
+
+ pub fn has_extra(&self) -> bool {
+ !self.llextra.is_null()
+ }
+
+ fn struct_field_ptr(
+ self,
+ bcx: &Builder<'a, 'tcx>,
+ st: &layout::Struct,
+ fields: &Vec<Ty<'tcx>>,
+ ix: usize,
+ needs_cast: bool
+ ) -> ValueRef {
+ let fty = fields[ix];
+ let ccx = bcx.ccx;
+
+ let ptr_val = if needs_cast {
+ let fields = st.field_index_by_increasing_offset().map(|i| {
+ type_of::in_memory_type_of(ccx, fields[i])
+ }).collect::<Vec<_>>();
+ let real_ty = Type::struct_(ccx, &fields[..], st.packed);
+ bcx.pointercast(self.llval, real_ty.ptr_to())
+ } else {
+ self.llval
+ };
+
+ // Simple case - we can just GEP the field
+ // * First field - Always aligned properly
+ // * Packed struct - There is no alignment padding
+ // * Field is sized - pointer is properly aligned already
+ if st.offsets[ix] == layout::Size::from_bytes(0) || st.packed ||
+ bcx.ccx.shared().type_is_sized(fty) {
+ return bcx.struct_gep(ptr_val, st.memory_index[ix] as usize);
+ }
+
+ // If the type of the last field is [T] or str, then we don't need to do
+ // any adjusments
+ match fty.sty {
+ ty::TySlice(..) | ty::TyStr => {
+ return bcx.struct_gep(ptr_val, st.memory_index[ix] as usize);
+ }
+ _ => ()
+ }
+
+ // There's no metadata available, log the case and just do the GEP.
+ if !self.has_extra() {
+ debug!("Unsized field `{}`, of `{:?}` has no metadata for adjustment",
+ ix, Value(ptr_val));
+ return bcx.struct_gep(ptr_val, ix);
+ }
+
+ // We need to get the pointer manually now.
+ // We do this by casting to a *i8, then offsetting it by the appropriate amount.
+ // We do this instead of, say, simply adjusting the pointer from the result of a GEP
+ // because the field may have an arbitrary alignment in the LLVM representation
+ // anyway.
+ //
+ // To demonstrate:
+ // struct Foo<T: ?Sized> {
+ // x: u16,
+ // y: T
+ // }
+ //
+ // The type Foo<Foo<Trait>> is represented in LLVM as { u16, { u16, u8 }}, meaning that
+ // the `y` field has 16-bit alignment.
+
+ let meta = self.llextra;
+
+
+ let offset = st.offsets[ix].bytes();
+ let unaligned_offset = C_uint(bcx.ccx, offset);
+
+ // Get the alignment of the field
+ let (_, align) = glue::size_and_align_of_dst(bcx, fty, meta);
+
+ // Bump the unaligned offset up to the appropriate alignment using the
+ // following expression:
+ //
+ // (unaligned offset + (align - 1)) & -align
+
+ // Calculate offset
+ let align_sub_1 = bcx.sub(align, C_uint(bcx.ccx, 1u64));
+ let offset = bcx.and(bcx.add(unaligned_offset, align_sub_1),
+ bcx.neg(align));
+
+ debug!("struct_field_ptr: DST field offset: {:?}", Value(offset));
+
+ // Cast and adjust pointer
+ let byte_ptr = bcx.pointercast(ptr_val, Type::i8p(bcx.ccx));
+ let byte_ptr = bcx.gep(byte_ptr, &[offset]);
+
+ // Finally, cast back to the type expected
+ let ll_fty = type_of::in_memory_type_of(bcx.ccx, fty);
+ debug!("struct_field_ptr: Field type is {:?}", ll_fty);
+ bcx.pointercast(byte_ptr, ll_fty.ptr_to())
+ }
+
+ /// Access a field, at a point when the value's case is known.
+ pub fn trans_field_ptr(self, bcx: &Builder<'a, 'tcx>, ix: usize) -> ValueRef {
+ let discr = match self.ty {
+ LvalueTy::Ty { .. } => 0,
+ LvalueTy::Downcast { variant_index, .. } => variant_index,
+ };
+ let t = self.ty.to_ty(bcx.tcx());
+ let l = bcx.ccx.layout_of(t);
+ // Note: if this ever needs to generate conditionals (e.g., if we
+ // decide to do some kind of cdr-coding-like non-unique repr
+ // someday), it will need to return a possibly-new bcx as well.
+ match *l {
+ layout::Univariant { ref variant, .. } => {
+ assert_eq!(discr, 0);
+ self.struct_field_ptr(bcx, &variant,
+ &adt::compute_fields(bcx.ccx, t, 0, false), ix, false)
+ }
+ layout::Vector { count, .. } => {
+ assert_eq!(discr, 0);
+ assert!((ix as u64) < count);
+ bcx.struct_gep(self.llval, ix)
+ }
+ layout::General { discr: d, ref variants, .. } => {
+ let mut fields = adt::compute_fields(bcx.ccx, t, discr, false);
+ fields.insert(0, d.to_ty(&bcx.tcx(), false));
+ self.struct_field_ptr(bcx, &variants[discr], &fields, ix + 1, true)
+ }
+ layout::UntaggedUnion { .. } => {
+ let fields = adt::compute_fields(bcx.ccx, t, 0, false);
+ let ty = type_of::in_memory_type_of(bcx.ccx, fields[ix]);
+ bcx.pointercast(self.llval, ty.ptr_to())
+ }
+ layout::RawNullablePointer { nndiscr, .. } |
+ layout::StructWrappedNullablePointer { nndiscr, .. } if discr as u64 != nndiscr => {
+ let nullfields = adt::compute_fields(bcx.ccx, t, (1-nndiscr) as usize, false);
+ // The unit-like case might have a nonzero number of unit-like fields.
+ // (e.d., Result of Either with (), as one side.)
+ let ty = type_of::type_of(bcx.ccx, nullfields[ix]);
+ assert_eq!(machine::llsize_of_alloc(bcx.ccx, ty), 0);
+ bcx.pointercast(self.llval, ty.ptr_to())
+ }
+ layout::RawNullablePointer { nndiscr, .. } => {
+ let nnty = adt::compute_fields(bcx.ccx, t, nndiscr as usize, false)[0];
+ assert_eq!(ix, 0);
+ assert_eq!(discr as u64, nndiscr);
+ let ty = type_of::type_of(bcx.ccx, nnty);
+ bcx.pointercast(self.llval, ty.ptr_to())
+ }
+ layout::StructWrappedNullablePointer { ref nonnull, nndiscr, .. } => {
+ assert_eq!(discr as u64, nndiscr);
+ self.struct_field_ptr(bcx, &nonnull,
+ &adt::compute_fields(bcx.ccx, t, discr, false), ix, false)
+ }
+ _ => bug!("element access in type without elements: {} represented as {:#?}", t, l)
+ }
+ }
}
impl<'a, 'tcx> MirContext<'a, 'tcx> {
pub fn trans_lvalue(&mut self,
- bcx: &BlockAndBuilder<'a, 'tcx>,
+ bcx: &Builder<'a, 'tcx>,
lvalue: &mir::Lvalue<'tcx>)
-> LvalueRef<'tcx> {
debug!("trans_lvalue(lvalue={:?})", lvalue);
let ccx = bcx.ccx;
- let tcx = bcx.tcx();
+ let tcx = ccx.tcx();
if let mir::Lvalue::Local(index) = *lvalue {
match self.locals[index] {
let (llprojected, llextra) = match projection.elem {
mir::ProjectionElem::Deref => bug!(),
mir::ProjectionElem::Field(ref field, _) => {
- let base_ty = tr_base.ty.to_ty(tcx);
- let discr = match tr_base.ty {
- LvalueTy::Ty { .. } => 0,
- LvalueTy::Downcast { adt_def: _, substs: _, variant_index: v } => v,
- };
- let discr = discr as u64;
- let is_sized = self.ccx.shared().type_is_sized(projected_ty.to_ty(tcx));
- let base = if is_sized {
- adt::MaybeSizedValue::sized(tr_base.llval)
- } else {
- adt::MaybeSizedValue::unsized_(tr_base.llval, tr_base.llextra)
- };
- let llprojected = adt::trans_field_ptr(bcx, base_ty, base, Disr(discr),
- field.index());
- let llextra = if is_sized {
+ let llextra = if self.ccx.shared().type_is_sized(projected_ty.to_ty(tcx)) {
ptr::null_mut()
} else {
tr_base.llextra
};
- (llprojected, llextra)
+ (tr_base.trans_field_ptr(bcx, field.index()), llextra)
}
mir::ProjectionElem::Index(ref index) => {
let index = self.trans_operand(bcx, index);
// Perform an action using the given Lvalue.
// If the Lvalue is an empty LocalRef::Operand, then a temporary stack slot
// is created first, then used as an operand to update the Lvalue.
- pub fn with_lvalue_ref<F, U>(&mut self, bcx: &BlockAndBuilder<'a, 'tcx>,
+ pub fn with_lvalue_ref<F, U>(&mut self, bcx: &Builder<'a, 'tcx>,
lvalue: &mir::Lvalue<'tcx>, f: F) -> U
where F: FnOnce(&mut Self, LvalueRef<'tcx>) -> U
{
LocalRef::Lvalue(lvalue) => f(self, lvalue),
LocalRef::Operand(None) => {
let lvalue_ty = self.monomorphized_lvalue_ty(lvalue);
- let lvalue = LvalueRef::alloca(bcx,
- lvalue_ty,
- "lvalue_temp");
+ assert!(!lvalue_ty.has_erasable_regions());
+ let lltemp = bcx.alloca_ty(lvalue_ty, "lvalue_temp");
+ let lvalue = LvalueRef::new_sized(lltemp, LvalueTy::from_ty(lvalue_ty));
let ret = f(self, lvalue);
let op = self.trans_load(bcx, lvalue.llval, lvalue_ty);
self.locals[index] = LocalRef::Operand(Some(op));
/// than we are.
///
/// nmatsakis: is this still necessary? Not sure.
- fn prepare_index(&mut self,
- bcx: &BlockAndBuilder<'a, 'tcx>,
- llindex: ValueRef)
- -> ValueRef
- {
- let ccx = bcx.ccx;
+ fn prepare_index(&mut self, bcx: &Builder<'a, 'tcx>, llindex: ValueRef) -> ValueRef {
let index_size = machine::llbitsize_of_real(bcx.ccx, common::val_ty(llindex));
- let int_size = machine::llbitsize_of_real(bcx.ccx, ccx.int_type());
+ let int_size = machine::llbitsize_of_real(bcx.ccx, bcx.ccx.int_type());
if index_size < int_size {
- bcx.zext(llindex, ccx.int_type())
+ bcx.zext(llindex, bcx.ccx.int_type())
} else if index_size > int_size {
- bcx.trunc(llindex, ccx.int_type())
+ bcx.trunc(llindex, bcx.ccx.int_type())
} else {
llindex
}
use rustc::ty::TypeFoldable;
use session::config::FullDebugInfo;
use base;
-use common::{self, BlockAndBuilder, CrateContext, FunctionContext, C_null, Funclet};
+use builder::Builder;
+use common::{self, CrateContext, C_null, Funclet};
use debuginfo::{self, declare_local, VariableAccess, VariableKind, FunctionDebugContext};
use monomorphize::{self, Instance};
use abi::FnType;
pub use self::constant::trans_static_initializer;
use self::analyze::CleanupKind;
-use self::lvalue::{LvalueRef};
+use self::lvalue::LvalueRef;
use rustc::mir::traversal;
use self::operand::{OperandRef, OperandValue};
debug_context: debuginfo::FunctionDebugContext,
- fcx: &'a common::FunctionContext<'a, 'tcx>,
+ llfn: ValueRef,
ccx: &'a CrateContext<'a, 'tcx>,
monomorphize::apply_param_substs(self.ccx.shared(), self.param_substs, value)
}
- pub fn set_debug_loc(&mut self, bcx: &BlockAndBuilder, source_info: mir::SourceInfo) {
+ pub fn set_debug_loc(&mut self, bcx: &Builder, source_info: mir::SourceInfo) {
let (scope, span) = self.debug_loc(source_info);
debuginfo::set_source_location(&self.debug_context, bcx, scope, span);
}
///////////////////////////////////////////////////////////////////////////
pub fn trans_mir<'a, 'tcx: 'a>(
- fcx: &'a FunctionContext<'a, 'tcx>,
+ ccx: &'a CrateContext<'a, 'tcx>,
+ llfn: ValueRef,
fn_ty: FnType,
mir: &'a Mir<'tcx>,
instance: Instance<'tcx>,
) {
debug!("fn_ty: {:?}", fn_ty);
let debug_context =
- debuginfo::create_function_debug_context(fcx.ccx, instance, sig, abi, fcx.llfn, mir);
- let bcx = fcx.get_entry_block();
+ debuginfo::create_function_debug_context(ccx, instance, sig, abi, llfn, mir);
+ let bcx = Builder::new_block(ccx, llfn, "entry-block");
let cleanup_kinds = analyze::cleanup_kinds(&mir);
let block_bcxs: IndexVec<mir::BasicBlock, BasicBlockRef> =
mir.basic_blocks().indices().map(|bb| {
if bb == mir::START_BLOCK {
- fcx.new_block("start")
+ bcx.build_sibling_block("start").llbb()
} else {
- fcx.new_block(&format!("{:?}", bb))
+ bcx.build_sibling_block(&format!("{:?}", bb)).llbb()
}
}).collect();
// Compute debuginfo scopes from MIR scopes.
- let scopes = debuginfo::create_mir_scopes(fcx, mir, &debug_context);
+ let scopes = debuginfo::create_mir_scopes(ccx, mir, &debug_context);
let mut mircx = MirContext {
mir: mir,
- fcx: fcx,
+ llfn: llfn,
fn_ty: fn_ty,
- ccx: fcx.ccx,
+ ccx: ccx,
llpersonalityslot: None,
blocks: block_bcxs,
unreachable_block: None,
}
debug!("alloc: {:?} ({}) -> lvalue", local, name);
- let lvalue = LvalueRef::alloca(&bcx, ty, &name.as_str());
+ assert!(!ty.has_erasable_regions());
+ let lltemp = bcx.alloca_ty(ty, &name.as_str());
+ let lvalue = LvalueRef::new_sized(lltemp, LvalueTy::from_ty(ty));
if dbg {
let (scope, span) = mircx.debug_loc(source_info);
declare_local(&bcx, &mircx.debug_context, name, ty, scope,
// Temporary or return pointer
if local == mir::RETURN_POINTER && mircx.fn_ty.ret.is_indirect() {
debug!("alloc: {:?} (return pointer) -> lvalue", local);
- let llretptr = llvm::get_param(fcx.llfn, 0);
+ let llretptr = llvm::get_param(llfn, 0);
LocalRef::Lvalue(LvalueRef::new_sized(llretptr, LvalueTy::from_ty(ty)))
} else if lvalue_locals.contains(local.index()) {
debug!("alloc: {:?} -> lvalue", local);
- LocalRef::Lvalue(LvalueRef::alloca(&bcx, ty, &format!("{:?}", local)))
+ assert!(!ty.has_erasable_regions());
+ let lltemp = bcx.alloca_ty(ty, &format!("{:?}", local));
+ LocalRef::Lvalue(LvalueRef::new_sized(lltemp, LvalueTy::from_ty(ty)))
} else {
// If this is an immediate local, we do not create an
// alloca in advance. Instead we wait until we see the
let funclets: IndexVec<mir::BasicBlock, Option<Funclet>> =
mircx.cleanup_kinds.iter_enumerated().map(|(bb, cleanup_kind)| {
if let CleanupKind::Funclet = *cleanup_kind {
- let bcx = mircx.build_block(bb);
+ let bcx = mircx.get_builder(bb);
bcx.set_personality_fn(mircx.ccx.eh_personality());
- if base::wants_msvc_seh(fcx.ccx.sess()) {
+ if base::wants_msvc_seh(ccx.sess()) {
return Some(Funclet::new(bcx.cleanup_pad(None, &[])));
}
}
/// Produce, for each argument, a `ValueRef` pointing at the
/// argument's value. As arguments are lvalues, these are always
/// indirect.
-fn arg_local_refs<'a, 'tcx>(bcx: &BlockAndBuilder<'a, 'tcx>,
+fn arg_local_refs<'a, 'tcx>(bcx: &Builder<'a, 'tcx>,
mircx: &MirContext<'a, 'tcx>,
scopes: &IndexVec<mir::VisibilityScope, debuginfo::MirDebugScope>,
lvalue_locals: &BitVector)
-> Vec<LocalRef<'tcx>> {
let mir = mircx.mir;
- let fcx = bcx.fcx();
let tcx = bcx.tcx();
let mut idx = 0;
let mut llarg_idx = mircx.fn_ty.ret.is_indirect() as usize;
_ => bug!("spread argument isn't a tuple?!")
};
- let lltemp = base::alloc_ty(&bcx, arg_ty, &format!("arg{}", arg_index));
+ let lltemp = bcx.alloca_ty(arg_ty, &format!("arg{}", arg_index));
for (i, &tupled_arg_ty) in tupled_arg_tys.iter().enumerate() {
let dst = bcx.struct_gep(lltemp, i);
let arg = &mircx.fn_ty.args[idx];
if arg.pad.is_some() {
llarg_idx += 1;
}
- let llarg = llvm::get_param(fcx.llfn, llarg_idx as c_uint);
+ let llarg = llvm::get_param(bcx.llfn(), llarg_idx as c_uint);
llarg_idx += 1;
llarg
} else if !lvalue_locals.contains(local.index()) &&
if arg.pad.is_some() {
llarg_idx += 1;
}
- let llarg = llvm::get_param(fcx.llfn, llarg_idx as c_uint);
+ let llarg = llvm::get_param(bcx.llfn(), llarg_idx as c_uint);
llarg_idx += 1;
let val = if common::type_is_fat_ptr(bcx.ccx, arg_ty) {
let meta = &mircx.fn_ty.args[idx];
idx += 1;
assert_eq!((meta.cast, meta.pad), (None, None));
- let llmeta = llvm::get_param(fcx.llfn, llarg_idx as c_uint);
+ let llmeta = llvm::get_param(bcx.llfn(), llarg_idx as c_uint);
llarg_idx += 1;
OperandValue::Pair(llarg, llmeta)
} else {
};
return LocalRef::Operand(Some(operand.unpack_if_pair(bcx)));
} else {
- let lltemp = base::alloc_ty(&bcx, arg_ty, &format!("arg{}", arg_index));
+ let lltemp = bcx.alloca_ty(arg_ty, &format!("arg{}", arg_index));
if common::type_is_fat_ptr(bcx.ccx, arg_ty) {
// we pass fat pointers as two words, but we want to
// represent them internally as a pointer to two words,
// doesn't actually strip the offset when splitting the closure
// environment into its components so it ends up out of bounds.
let env_ptr = if !env_ref {
- let alloc = bcx.fcx().alloca(common::val_ty(llval), "__debuginfo_env_ptr");
- bcx.store(llval, alloc);
+ let alloc = bcx.alloca(common::val_ty(llval), "__debuginfo_env_ptr");
+ bcx.store(llval, alloc, None);
alloc
} else {
llval
mod analyze;
mod block;
mod constant;
-mod lvalue;
+pub mod lvalue;
mod operand;
mod rvalue;
mod statement;
use rustc_data_structures::indexed_vec::Idx;
use base;
-use common::{self, BlockAndBuilder};
+use common;
+use builder::Builder;
use value::Value;
use type_of;
use type_::Type;
/// If this operand is a Pair, we return an
/// Immediate aggregate with the two values.
- pub fn pack_if_pair(mut self, bcx: &BlockAndBuilder<'a, 'tcx>)
- -> OperandRef<'tcx> {
+ pub fn pack_if_pair(mut self, bcx: &Builder<'a, 'tcx>) -> OperandRef<'tcx> {
if let OperandValue::Pair(a, b) = self.val {
// Reconstruct the immediate aggregate.
let llty = type_of::type_of(bcx.ccx, self.ty);
/// If this operand is a pair in an Immediate,
/// we return a Pair with the two halves.
- pub fn unpack_if_pair(mut self, bcx: &BlockAndBuilder<'a, 'tcx>)
- -> OperandRef<'tcx> {
+ pub fn unpack_if_pair(mut self, bcx: &Builder<'a, 'tcx>) -> OperandRef<'tcx> {
if let OperandValue::Immediate(llval) = self.val {
// Deconstruct the immediate aggregate.
if common::type_is_imm_pair(bcx.ccx, self.ty) {
impl<'a, 'tcx> MirContext<'a, 'tcx> {
pub fn trans_load(&mut self,
- bcx: &BlockAndBuilder<'a, 'tcx>,
+ bcx: &Builder<'a, 'tcx>,
llval: ValueRef,
ty: Ty<'tcx>)
-> OperandRef<'tcx>
}
pub fn trans_consume(&mut self,
- bcx: &BlockAndBuilder<'a, 'tcx>,
+ bcx: &Builder<'a, 'tcx>,
lvalue: &mir::Lvalue<'tcx>)
-> OperandRef<'tcx>
{
}
pub fn trans_operand(&mut self,
- bcx: &BlockAndBuilder<'a, 'tcx>,
+ bcx: &Builder<'a, 'tcx>,
operand: &mir::Operand<'tcx>)
-> OperandRef<'tcx>
{
}
pub fn store_operand(&mut self,
- bcx: &BlockAndBuilder<'a, 'tcx>,
+ bcx: &Builder<'a, 'tcx>,
lldest: ValueRef,
- operand: OperandRef<'tcx>) {
- debug!("store_operand: operand={:?}", operand);
+ operand: OperandRef<'tcx>,
+ align: Option<u32>) {
+ debug!("store_operand: operand={:?}, align={:?}", operand, align);
// Avoid generating stores of zero-sized values, because the only way to have a zero-sized
// value is through `undef`, and store itself is useless.
if common::type_is_zero_size(bcx.ccx, operand.ty) {
return;
}
match operand.val {
- OperandValue::Ref(r) => base::memcpy_ty(bcx, lldest, r, operand.ty),
- OperandValue::Immediate(s) => base::store_ty(bcx, s, lldest, operand.ty),
+ OperandValue::Ref(r) => base::memcpy_ty(bcx, lldest, r, operand.ty, align),
+ OperandValue::Immediate(s) => {
+ bcx.store(base::from_immediate(bcx, s), lldest, align);
+ }
OperandValue::Pair(a, b) => {
let a = base::from_immediate(bcx, a);
let b = base::from_immediate(bcx, b);
- bcx.store(a, bcx.struct_gep(lldest, 0));
- bcx.store(b, bcx.struct_gep(lldest, 1));
+ bcx.store(a, bcx.struct_gep(lldest, 0), align);
+ bcx.store(b, bcx.struct_gep(lldest, 1), align);
}
}
}
use rustc::ty::{self, Ty};
use rustc::ty::cast::{CastTy, IntTy};
use rustc::ty::layout::Layout;
+use rustc::mir::tcx::LvalueTy;
use rustc::mir;
use middle::lang_items::ExchangeMallocFnLangItem;
use asm;
use base;
+use builder::Builder;
use callee::Callee;
-use common::{self, val_ty, C_bool, C_null, C_uint, BlockAndBuilder};
+use common::{self, val_ty, C_bool, C_null, C_uint};
use common::{C_integral};
use adt;
use machine;
impl<'a, 'tcx> MirContext<'a, 'tcx> {
pub fn trans_rvalue(&mut self,
- bcx: BlockAndBuilder<'a, 'tcx>,
+ bcx: Builder<'a, 'tcx>,
dest: LvalueRef<'tcx>,
rvalue: &mir::Rvalue<'tcx>)
- -> BlockAndBuilder<'a, 'tcx>
+ -> Builder<'a, 'tcx>
{
debug!("trans_rvalue(dest.llval={:?}, rvalue={:?})",
Value(dest.llval), rvalue);
let tr_operand = self.trans_operand(&bcx, operand);
// FIXME: consider not copying constants through stack. (fixable by translating
// constants into OperandValue::Ref, why don’t we do that yet if we don’t?)
- self.store_operand(&bcx, dest.llval, tr_operand);
+ self.store_operand(&bcx, dest.llval, tr_operand, None);
bcx
}
// into-coerce of a thin pointer to a fat pointer - just
// use the operand path.
let (bcx, temp) = self.trans_rvalue_operand(bcx, rvalue);
- self.store_operand(&bcx, dest.llval, temp);
+ self.store_operand(&bcx, dest.llval, temp, None);
return bcx;
}
// index into the struct, and this case isn't
// important enough for it.
debug!("trans_rvalue: creating ugly alloca");
- let lltemp = base::alloc_ty(&bcx, operand.ty, "__unsize_temp");
+ let lltemp = bcx.alloca_ty(operand.ty, "__unsize_temp");
base::store_ty(&bcx, llval, lltemp, operand.ty);
lltemp
}
let size = C_uint(bcx.ccx, size);
let base = base::get_dataptr(&bcx, dest.llval);
tvec::slice_for_each(&bcx, base, tr_elem.ty, size, |bcx, llslot| {
- self.store_operand(bcx, llslot, tr_elem);
+ self.store_operand(bcx, llslot, tr_elem, None);
})
}
mir::Rvalue::Aggregate(ref kind, ref operands) => {
match *kind {
- mir::AggregateKind::Adt(adt_def, variant_index, _, active_field_index) => {
+ mir::AggregateKind::Adt(adt_def, variant_index, substs, active_field_index) => {
let disr = Disr::from(adt_def.variants[variant_index].disr_val);
let dest_ty = dest.ty.to_ty(bcx.tcx());
adt::trans_set_discr(&bcx, dest_ty, dest.llval, Disr::from(disr));
let op = self.trans_operand(&bcx, operand);
// Do not generate stores and GEPis for zero-sized fields.
if !common::type_is_zero_size(bcx.ccx, op.ty) {
- let val = adt::MaybeSizedValue::sized(dest.llval);
+ let mut val = LvalueRef::new_sized(dest.llval, dest.ty);
let field_index = active_field_index.unwrap_or(i);
- let lldest_i = adt::trans_field_ptr(&bcx, dest_ty, val, disr,
- field_index);
- self.store_operand(&bcx, lldest_i, op);
+ val.ty = LvalueTy::Downcast {
+ adt_def: adt_def,
+ substs: self.monomorphize(&substs),
+ variant_index: disr.0 as usize,
+ };
+ let lldest_i = val.trans_field_ptr(&bcx, field_index);
+ self.store_operand(&bcx, lldest_i, op, None);
}
}
},
i
};
let dest = bcx.gepi(dest.llval, &[0, i]);
- self.store_operand(&bcx, dest, op);
+ self.store_operand(&bcx, dest, op, None);
}
}
}
_ => {
assert!(rvalue_creates_operand(rvalue));
let (bcx, temp) = self.trans_rvalue_operand(bcx, rvalue);
- self.store_operand(&bcx, dest.llval, temp);
+ self.store_operand(&bcx, dest.llval, temp, None);
bcx
}
}
}
pub fn trans_rvalue_operand(&mut self,
- bcx: BlockAndBuilder<'a, 'tcx>,
+ bcx: Builder<'a, 'tcx>,
rvalue: &mir::Rvalue<'tcx>)
- -> (BlockAndBuilder<'a, 'tcx>, OperandRef<'tcx>)
+ -> (Builder<'a, 'tcx>, OperandRef<'tcx>)
{
assert!(rvalue_creates_operand(rvalue), "cannot trans {:?} to operand", rvalue);
}
pub fn trans_scalar_binop(&mut self,
- bcx: &BlockAndBuilder<'a, 'tcx>,
+ bcx: &Builder<'a, 'tcx>,
op: mir::BinOp,
lhs: ValueRef,
rhs: ValueRef,
}
pub fn trans_fat_ptr_binop(&mut self,
- bcx: &BlockAndBuilder<'a, 'tcx>,
+ bcx: &Builder<'a, 'tcx>,
op: mir::BinOp,
lhs_addr: ValueRef,
lhs_extra: ValueRef,
}
pub fn trans_scalar_checked_binop(&mut self,
- bcx: &BlockAndBuilder<'a, 'tcx>,
+ bcx: &Builder<'a, 'tcx>,
op: mir::BinOp,
lhs: ValueRef,
rhs: ValueRef,
Add, Sub, Mul
}
-fn get_overflow_intrinsic(oop: OverflowOp, bcx: &BlockAndBuilder, ty: Ty) -> ValueRef {
+fn get_overflow_intrinsic(oop: OverflowOp, bcx: &Builder, ty: Ty) -> ValueRef {
use syntax::ast::IntTy::*;
use syntax::ast::UintTy::*;
use rustc::ty::{TyInt, TyUint};
use rustc::mir;
use base;
-use common::{self, BlockAndBuilder};
+use common;
+use builder::Builder;
use super::MirContext;
use super::LocalRef;
impl<'a, 'tcx> MirContext<'a, 'tcx> {
pub fn trans_statement(&mut self,
- bcx: BlockAndBuilder<'a, 'tcx>,
+ bcx: Builder<'a, 'tcx>,
statement: &mir::Statement<'tcx>)
- -> BlockAndBuilder<'a, 'tcx> {
+ -> Builder<'a, 'tcx> {
debug!("trans_statement(statement={:?})", statement);
self.set_debug_loc(&bcx, statement.source_info);
}
fn trans_storage_liveness(&self,
- bcx: BlockAndBuilder<'a, 'tcx>,
+ bcx: Builder<'a, 'tcx>,
lvalue: &mir::Lvalue<'tcx>,
intrinsic: base::Lifetime)
- -> BlockAndBuilder<'a, 'tcx> {
+ -> Builder<'a, 'tcx> {
if let mir::Lvalue::Local(index) = *lvalue {
if let LocalRef::Lvalue(tr_lval) = self.locals[index] {
intrinsic.call(&bcx, tr_lval.llval);
// except according to those terms.
use llvm;
+use builder::Builder;
use llvm::ValueRef;
use common::*;
use rustc::ty::Ty;
pub fn slice_for_each<'a, 'tcx, F>(
- bcx: &BlockAndBuilder<'a, 'tcx>,
+ bcx: &Builder<'a, 'tcx>,
data_ptr: ValueRef,
unit_ty: Ty<'tcx>,
len: ValueRef,
f: F
-) -> BlockAndBuilder<'a, 'tcx> where F: FnOnce(&BlockAndBuilder<'a, 'tcx>, ValueRef) {
+) -> Builder<'a, 'tcx> where F: FnOnce(&Builder<'a, 'tcx>, ValueRef) {
// Special-case vectors with elements of size 0 so they don't go out of bounds (#9890)
let zst = type_is_zero_size(bcx.ccx, unit_ty);
- let add = |bcx: &BlockAndBuilder, a, b| if zst {
+ let add = |bcx: &Builder, a, b| if zst {
bcx.add(a, b)
} else {
bcx.inbounds_gep(a, &[b])
};
- let body_bcx = bcx.fcx().build_new_block("slice_loop_body");
- let next_bcx = bcx.fcx().build_new_block("slice_loop_next");
- let header_bcx = bcx.fcx().build_new_block("slice_loop_header");
+ let body_bcx = bcx.build_sibling_block("slice_loop_body");
+ let next_bcx = bcx.build_sibling_block("slice_loop_next");
+ let header_bcx = bcx.build_sibling_block("slice_loop_header");
let start = if zst {
C_uint(bcx.ccx, 0usize)
self.check_pat(&p, discrim_ty);
all_pats_diverge &= self.diverges.get();
}
- all_pats_diverge
+ // As discussed with @eddyb, this is for disabling unreachable_code
+ // warnings on patterns (they're now subsumed by unreachable_patterns
+ // warnings).
+ match all_pats_diverge {
+ Diverges::Maybe => Diverges::Maybe,
+ Diverges::Always | Diverges::WarnedAlways => Diverges::WarnedAlways,
+ }
}).collect();
// Now typecheck the blocks.
hir::UnNot => {
oprnd_t = self.structurally_resolved_type(oprnd.span,
oprnd_t);
+ let result = self.check_user_unop("!", "not",
+ tcx.lang_items.not_trait(),
+ expr, &oprnd, oprnd_t, unop);
+ // If it's builtin, we can reuse the type, this helps inference.
if !(oprnd_t.is_integral() || oprnd_t.sty == ty::TyBool) {
- oprnd_t = self.check_user_unop("!", "not",
- tcx.lang_items.not_trait(),
- expr, &oprnd, oprnd_t, unop);
+ oprnd_t = result;
}
}
hir::UnNeg => {
oprnd_t = self.structurally_resolved_type(oprnd.span,
oprnd_t);
+ let result = self.check_user_unop("-", "neg",
+ tcx.lang_items.neg_trait(),
+ expr, &oprnd, oprnd_t, unop);
+ // If it's builtin, we can reuse the type, this helps inference.
if !(oprnd_t.is_integral() || oprnd_t.is_fp()) {
- oprnd_t = self.check_user_unop("-", "neg",
- tcx.lang_items.neg_trait(),
- expr, &oprnd, oprnd_t, unop);
+ oprnd_t = result;
}
}
}
// as potentially overloaded. But then, during writeback, if
// we observe that something like `a+b` is (known to be)
// operating on scalars, we clear the overload.
- fn fix_scalar_binary_expr(&mut self, e: &hir::Expr) {
+ fn fix_scalar_builtin_expr(&mut self, e: &hir::Expr) {
match e.node {
+ hir::ExprUnary(hir::UnNeg, ref inner) |
+ hir::ExprUnary(hir::UnNot, ref inner) => {
+ let inner_ty = self.fcx.node_ty(inner.id);
+ let inner_ty = self.fcx.resolve_type_vars_if_possible(&inner_ty);
+
+ if inner_ty.is_scalar() {
+ self.fcx.tables.borrow_mut().method_map.remove(&MethodCall::expr(e.id));
+ }
+ }
hir::ExprBinary(ref op, ref lhs, ref rhs) |
hir::ExprAssignOp(ref op, ref lhs, ref rhs) => {
let lhs_ty = self.fcx.node_ty(lhs.id);
return;
}
- self.fix_scalar_binary_expr(e);
+ self.fix_scalar_builtin_expr(e);
self.visit_node_id(ResolvingExpr(e.span), e.id);
self.visit_method_map_entry(ResolvingExpr(e.span),
--- /dev/null
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! Check properties that are required by built-in traits and set
+//! up data structures required by type-checking/translation.
+
+use rustc::middle::free_region::FreeRegionMap;
+use rustc::middle::lang_items::UnsizeTraitLangItem;
+
+use rustc::traits::{self, ObligationCause, Reveal};
+use rustc::ty::{self, Ty, TyCtxt};
+use rustc::ty::ParameterEnvironment;
+use rustc::ty::TypeFoldable;
+use rustc::ty::subst::Subst;
+use rustc::ty::util::CopyImplementationError;
+use rustc::infer;
+
+use rustc::hir::def_id::DefId;
+use rustc::hir::map as hir_map;
+use rustc::hir::{self, ItemImpl};
+
+pub fn check<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) {
+ check_trait(tcx, tcx.lang_items.drop_trait(), visit_implementation_of_drop);
+ check_trait(tcx, tcx.lang_items.copy_trait(), visit_implementation_of_copy);
+ check_trait(
+ tcx,
+ tcx.lang_items.coerce_unsized_trait(),
+ visit_implementation_of_coerce_unsized);
+}
+
+fn check_trait<'a, 'tcx, F>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
+ trait_def_id: Option<DefId>,
+ mut f: F)
+ where F: FnMut(TyCtxt<'a, 'tcx, 'tcx>, DefId, DefId)
+{
+ if let Some(trait_def_id) = trait_def_id {
+ let mut impls = vec![];
+ tcx.lookup_trait_def(trait_def_id).for_each_impl(tcx, |did| {
+ impls.push(did);
+ });
+ impls.sort();
+ for impl_def_id in impls {
+ f(tcx, trait_def_id, impl_def_id);
+ }
+ }
+}
+
+fn visit_implementation_of_drop<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
+ _drop_did: DefId,
+ impl_did: DefId) {
+ let items = tcx.associated_item_def_ids(impl_did);
+ if items.is_empty() {
+ // We'll error out later. For now, just don't ICE.
+ return;
+ }
+ let method_def_id = items[0];
+
+ let self_type = tcx.item_type(impl_did);
+ match self_type.sty {
+ ty::TyAdt(type_def, _) => {
+ type_def.set_destructor(method_def_id);
+ }
+ _ => {
+ // Destructors only work on nominal types.
+ if let Some(impl_node_id) = tcx.map.as_local_node_id(impl_did) {
+ match tcx.map.find(impl_node_id) {
+ Some(hir_map::NodeItem(item)) => {
+ let span = match item.node {
+ ItemImpl(.., ref ty, _) => ty.span,
+ _ => item.span,
+ };
+ struct_span_err!(tcx.sess,
+ span,
+ E0120,
+ "the Drop trait may only be implemented on \
+ structures")
+ .span_label(span, &format!("implementing Drop requires a struct"))
+ .emit();
+ }
+ _ => {
+ bug!("didn't find impl in ast map");
+ }
+ }
+ } else {
+ bug!("found external impl of Drop trait on \
+ something other than a struct");
+ }
+ }
+ }
+}
+
+fn visit_implementation_of_copy<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
+ _copy_did: DefId,
+ impl_did: DefId) {
+ debug!("visit_implementation_of_copy: impl_did={:?}", impl_did);
+
+ let impl_node_id = if let Some(n) = tcx.map.as_local_node_id(impl_did) {
+ n
+ } else {
+ debug!("visit_implementation_of_copy(): impl not in this \
+ crate");
+ return;
+ };
+
+ let self_type = tcx.item_type(impl_did);
+ debug!("visit_implementation_of_copy: self_type={:?} (bound)",
+ self_type);
+
+ let span = tcx.map.span(impl_node_id);
+ let param_env = ParameterEnvironment::for_item(tcx, impl_node_id);
+ let self_type = self_type.subst(tcx, ¶m_env.free_substs);
+ assert!(!self_type.has_escaping_regions());
+
+ debug!("visit_implementation_of_copy: self_type={:?} (free)",
+ self_type);
+
+ match param_env.can_type_implement_copy(tcx, self_type, span) {
+ Ok(()) => {}
+ Err(CopyImplementationError::InfrigingField(field)) => {
+ let item = tcx.map.expect_item(impl_node_id);
+ let span = if let ItemImpl(.., Some(ref tr), _, _) = item.node {
+ tr.path.span
+ } else {
+ span
+ };
+
+ struct_span_err!(tcx.sess,
+ span,
+ E0204,
+ "the trait `Copy` may not be implemented for this type")
+ .span_label(
+ tcx.def_span(field.did),
+ &"this field does not implement `Copy`")
+ .emit()
+ }
+ Err(CopyImplementationError::NotAnAdt) => {
+ let item = tcx.map.expect_item(impl_node_id);
+ let span = if let ItemImpl(.., ref ty, _) = item.node {
+ ty.span
+ } else {
+ span
+ };
+
+ struct_span_err!(tcx.sess,
+ span,
+ E0206,
+ "the trait `Copy` may not be implemented for this type")
+ .span_label(span, &format!("type is not a structure or enumeration"))
+ .emit();
+ }
+ Err(CopyImplementationError::HasDestructor) => {
+ struct_span_err!(tcx.sess,
+ span,
+ E0184,
+ "the trait `Copy` may not be implemented for this type; the \
+ type has a destructor")
+ .span_label(span, &format!("Copy not allowed on types with destructors"))
+ .emit();
+ }
+ }
+}
+
+fn visit_implementation_of_coerce_unsized<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
+ coerce_unsized_trait: DefId,
+ impl_did: DefId) {
+ debug!("visit_implementation_of_coerce_unsized: impl_did={:?}",
+ impl_did);
+
+ let unsize_trait = match tcx.lang_items.require(UnsizeTraitLangItem) {
+ Ok(id) => id,
+ Err(err) => {
+ tcx.sess.fatal(&format!("`CoerceUnsized` implementation {}", err));
+ }
+ };
+
+ let impl_node_id = if let Some(n) = tcx.map.as_local_node_id(impl_did) {
+ n
+ } else {
+ debug!("visit_implementation_of_coerce_unsized(): impl not \
+ in this crate");
+ return;
+ };
+
+ let source = tcx.item_type(impl_did);
+ let trait_ref = tcx.impl_trait_ref(impl_did).unwrap();
+ let target = trait_ref.substs.type_at(1);
+ debug!("visit_implementation_of_coerce_unsized: {:?} -> {:?} (bound)",
+ source,
+ target);
+
+ let span = tcx.map.span(impl_node_id);
+ let param_env = ParameterEnvironment::for_item(tcx, impl_node_id);
+ let source = source.subst(tcx, ¶m_env.free_substs);
+ let target = target.subst(tcx, ¶m_env.free_substs);
+ assert!(!source.has_escaping_regions());
+
+ debug!("visit_implementation_of_coerce_unsized: {:?} -> {:?} (free)",
+ source,
+ target);
+
+ tcx.infer_ctxt(None, Some(param_env), Reveal::ExactMatch).enter(|infcx| {
+ let cause = ObligationCause::misc(span, impl_node_id);
+ let check_mutbl = |mt_a: ty::TypeAndMut<'tcx>,
+ mt_b: ty::TypeAndMut<'tcx>,
+ mk_ptr: &Fn(Ty<'tcx>) -> Ty<'tcx>| {
+ if (mt_a.mutbl, mt_b.mutbl) == (hir::MutImmutable, hir::MutMutable) {
+ infcx.report_mismatched_types(&cause,
+ mk_ptr(mt_b.ty),
+ target,
+ ty::error::TypeError::Mutability)
+ .emit();
+ }
+ (mt_a.ty, mt_b.ty, unsize_trait, None)
+ };
+ let (source, target, trait_def_id, kind) = match (&source.sty, &target.sty) {
+ (&ty::TyBox(a), &ty::TyBox(b)) => (a, b, unsize_trait, None),
+
+ (&ty::TyRef(r_a, mt_a), &ty::TyRef(r_b, mt_b)) => {
+ infcx.sub_regions(infer::RelateObjectBound(span), r_b, r_a);
+ check_mutbl(mt_a, mt_b, &|ty| tcx.mk_imm_ref(r_b, ty))
+ }
+
+ (&ty::TyRef(_, mt_a), &ty::TyRawPtr(mt_b)) |
+ (&ty::TyRawPtr(mt_a), &ty::TyRawPtr(mt_b)) => {
+ check_mutbl(mt_a, mt_b, &|ty| tcx.mk_imm_ptr(ty))
+ }
+
+ (&ty::TyAdt(def_a, substs_a), &ty::TyAdt(def_b, substs_b)) if def_a.is_struct() &&
+ def_b.is_struct() => {
+ if def_a != def_b {
+ let source_path = tcx.item_path_str(def_a.did);
+ let target_path = tcx.item_path_str(def_b.did);
+ span_err!(tcx.sess,
+ span,
+ E0377,
+ "the trait `CoerceUnsized` may only be implemented \
+ for a coercion between structures with the same \
+ definition; expected {}, found {}",
+ source_path,
+ target_path);
+ return;
+ }
+
+ let fields = &def_a.struct_variant().fields;
+ let diff_fields = fields.iter()
+ .enumerate()
+ .filter_map(|(i, f)| {
+ let (a, b) = (f.ty(tcx, substs_a), f.ty(tcx, substs_b));
+
+ if tcx.item_type(f.did).is_phantom_data() {
+ // Ignore PhantomData fields
+ return None;
+ }
+
+ // Ignore fields that aren't significantly changed
+ if let Ok(ok) = infcx.sub_types(false, &cause, b, a) {
+ if ok.obligations.is_empty() {
+ return None;
+ }
+ }
+
+ // Collect up all fields that were significantly changed
+ // i.e. those that contain T in coerce_unsized T -> U
+ Some((i, a, b))
+ })
+ .collect::<Vec<_>>();
+
+ if diff_fields.is_empty() {
+ span_err!(tcx.sess,
+ span,
+ E0374,
+ "the trait `CoerceUnsized` may only be implemented \
+ for a coercion between structures with one field \
+ being coerced, none found");
+ return;
+ } else if diff_fields.len() > 1 {
+ let item = tcx.map.expect_item(impl_node_id);
+ let span = if let ItemImpl(.., Some(ref t), _, _) = item.node {
+ t.path.span
+ } else {
+ tcx.map.span(impl_node_id)
+ };
+
+ let mut err = struct_span_err!(tcx.sess,
+ span,
+ E0375,
+ "implementing the trait \
+ `CoerceUnsized` requires multiple \
+ coercions");
+ err.note("`CoerceUnsized` may only be implemented for \
+ a coercion between structures with one field being coerced");
+ err.note(&format!("currently, {} fields need coercions: {}",
+ diff_fields.len(),
+ diff_fields.iter()
+ .map(|&(i, a, b)| {
+ format!("{} ({} to {})", fields[i].name, a, b)
+ })
+ .collect::<Vec<_>>()
+ .join(", ")));
+ err.span_label(span, &format!("requires multiple coercions"));
+ err.emit();
+ return;
+ }
+
+ let (i, a, b) = diff_fields[0];
+ let kind = ty::adjustment::CustomCoerceUnsized::Struct(i);
+ (a, b, coerce_unsized_trait, Some(kind))
+ }
+
+ _ => {
+ span_err!(tcx.sess,
+ span,
+ E0376,
+ "the trait `CoerceUnsized` may only be implemented \
+ for a coercion between structures");
+ return;
+ }
+ };
+
+ let mut fulfill_cx = traits::FulfillmentContext::new();
+
+ // Register an obligation for `A: Trait<B>`.
+ let cause = traits::ObligationCause::misc(span, impl_node_id);
+ let predicate = tcx.predicate_for_trait_def(cause, trait_def_id, 0, source, &[target]);
+ fulfill_cx.register_predicate_obligation(&infcx, predicate);
+
+ // Check that all transitive obligations are satisfied.
+ if let Err(errors) = fulfill_cx.select_all_or_error(&infcx) {
+ infcx.report_fulfillment_errors(&errors);
+ }
+
+ // Finally, resolve all regions.
+ let mut free_regions = FreeRegionMap::new();
+ free_regions.relate_free_regions_from_predicates(&infcx.parameter_environment
+ .caller_bounds);
+ infcx.resolve_regions_and_report_errors(&free_regions, impl_node_id);
+
+ if let Some(kind) = kind {
+ tcx.custom_coerce_unsized_kinds.borrow_mut().insert(impl_did, kind);
+ }
+ });
+}
// mappings. That mapping code resides here.
use hir::def_id::DefId;
-use middle::lang_items::UnsizeTraitLangItem;
-use rustc::ty::subst::Subst;
use rustc::ty::{self, TyCtxt, TypeFoldable};
-use rustc::traits::{self, ObligationCause, Reveal};
-use rustc::ty::ParameterEnvironment;
use rustc::ty::{Ty, TyBool, TyChar, TyError};
use rustc::ty::{TyParam, TyRawPtr};
use rustc::ty::{TyRef, TyAdt, TyDynamic, TyNever, TyTuple};
use rustc::ty::{TyStr, TyArray, TySlice, TyFloat, TyInfer, TyInt};
use rustc::ty::{TyUint, TyClosure, TyBox, TyFnDef, TyFnPtr};
use rustc::ty::{TyProjection, TyAnon};
-use rustc::ty::util::CopyImplementationError;
-use middle::free_region::FreeRegionMap;
use CrateCtxt;
-use rustc::infer::{self, InferCtxt};
use syntax_pos::Span;
use rustc::dep_graph::DepNode;
-use rustc::hir::map as hir_map;
use rustc::hir::itemlikevisit::ItemLikeVisitor;
use rustc::hir::{Item, ItemImpl};
use rustc::hir;
+mod builtin;
mod orphan;
mod overlap;
mod unsafety;
-struct CoherenceChecker<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> {
- crate_context: &'a CrateCtxt<'a, 'gcx>,
- inference_context: InferCtxt<'a, 'gcx, 'tcx>,
+struct CoherenceChecker<'a, 'tcx: 'a> {
+ tcx: TyCtxt<'a, 'tcx, 'tcx>,
}
-struct CoherenceCheckVisitor<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> {
- cc: &'a CoherenceChecker<'a, 'gcx, 'tcx>,
-}
-
-impl<'a, 'gcx, 'tcx, 'v> ItemLikeVisitor<'v> for CoherenceCheckVisitor<'a, 'gcx, 'tcx> {
+impl<'a, 'tcx, 'v> ItemLikeVisitor<'v> for CoherenceChecker<'a, 'tcx> {
fn visit_item(&mut self, item: &Item) {
if let ItemImpl(..) = item.node {
- self.cc.check_implementation(item)
+ self.check_implementation(item)
}
}
}
}
-impl<'a, 'gcx, 'tcx> CoherenceChecker<'a, 'gcx, 'tcx> {
+impl<'a, 'tcx> CoherenceChecker<'a, 'tcx> {
// Returns the def ID of the base type, if there is one.
fn get_base_type_def_id(&self, span: Span, ty: Ty<'tcx>) -> Option<DefId> {
match ty.sty {
TyDynamic(ref t, ..) => t.principal().map(|p| p.def_id()),
- TyBox(_) => self.inference_context.tcx.lang_items.owned_box(),
+ TyBox(_) => self.tcx.lang_items.owned_box(),
TyBool | TyChar | TyInt(..) | TyUint(..) | TyFloat(..) | TyStr | TyArray(..) |
TySlice(..) | TyFnDef(..) | TyFnPtr(_) | TyTuple(..) | TyParam(..) | TyError |
}
}
- fn check(&self) {
+ fn check(&mut self) {
// Check implementations and traits. This populates the tables
// containing the inherent methods and extension methods. It also
// builds up the trait inheritance table.
- self.crate_context.tcx.visit_all_item_likes_in_krate(
- DepNode::CoherenceCheckImpl,
- &mut CoherenceCheckVisitor { cc: self });
-
- // Populate the table of destructors. It might seem a bit strange to
- // do this here, but it's actually the most convenient place, since
- // the coherence tables contain the trait -> type mappings.
- self.populate_destructors();
-
- // Check to make sure implementations of `Copy` are legal.
- self.check_implementations_of_copy();
-
- // Check to make sure implementations of `CoerceUnsized` are legal
- // and collect the necessary information from them.
- self.check_implementations_of_coerce_unsized();
+ self.tcx.visit_all_item_likes_in_krate(DepNode::CoherenceCheckImpl, self);
}
fn check_implementation(&self, item: &Item) {
- let tcx = self.crate_context.tcx;
+ let tcx = self.tcx;
let impl_did = tcx.map.local_def_id(item.id);
let self_type = tcx.item_type(impl_did);
// If there are no traits, then this implementation must have a
// base type.
- if let Some(trait_ref) = self.crate_context.tcx.impl_trait_ref(impl_did) {
+ if let Some(trait_ref) = self.tcx.impl_trait_ref(impl_did) {
debug!("(checking implementation) adding impl for trait '{:?}', item '{}'",
trait_ref,
item.name);
return;
}
- enforce_trait_manually_implementable(self.crate_context.tcx,
- item.span,
- trait_ref.def_id);
+ enforce_trait_manually_implementable(self.tcx, item.span, trait_ref.def_id);
self.add_trait_impl(trait_ref, impl_did);
} else {
// Skip inherent impls where the self type is an error
}
fn add_inherent_impl(&self, base_def_id: DefId, impl_def_id: DefId) {
- let tcx = self.crate_context.tcx;
- tcx.inherent_impls.borrow_mut().push(base_def_id, impl_def_id);
+ self.tcx.inherent_impls.borrow_mut().push(base_def_id, impl_def_id);
}
- fn add_trait_impl(&self, impl_trait_ref: ty::TraitRef<'gcx>, impl_def_id: DefId) {
+ fn add_trait_impl(&self, impl_trait_ref: ty::TraitRef<'tcx>, impl_def_id: DefId) {
debug!("add_trait_impl: impl_trait_ref={:?} impl_def_id={:?}",
impl_trait_ref,
impl_def_id);
- let trait_def = self.crate_context.tcx.lookup_trait_def(impl_trait_ref.def_id);
- trait_def.record_local_impl(self.crate_context.tcx, impl_def_id, impl_trait_ref);
- }
-
- // Destructors
- //
-
- fn populate_destructors(&self) {
- let tcx = self.crate_context.tcx;
- let drop_trait = match tcx.lang_items.drop_trait() {
- Some(id) => id,
- None => return,
- };
- tcx.populate_implementations_for_trait_if_necessary(drop_trait);
- let drop_trait = tcx.lookup_trait_def(drop_trait);
-
- drop_trait.for_each_impl(tcx, |impl_did| {
- let items = tcx.associated_item_def_ids(impl_did);
- if items.is_empty() {
- // We'll error out later. For now, just don't ICE.
- return;
- }
- let method_def_id = items[0];
-
- let self_type = tcx.item_type(impl_did);
- match self_type.sty {
- ty::TyAdt(type_def, _) => {
- type_def.set_destructor(method_def_id);
- }
- _ => {
- // Destructors only work on nominal types.
- if let Some(impl_node_id) = tcx.map.as_local_node_id(impl_did) {
- match tcx.map.find(impl_node_id) {
- Some(hir_map::NodeItem(item)) => {
- let span = match item.node {
- ItemImpl(.., ref ty, _) => ty.span,
- _ => item.span,
- };
- struct_span_err!(tcx.sess,
- span,
- E0120,
- "the Drop trait may only be implemented on \
- structures")
- .span_label(span,
- &format!("implementing Drop requires a struct"))
- .emit();
- }
- _ => {
- bug!("didn't find impl in ast map");
- }
- }
- } else {
- bug!("found external impl of Drop trait on \
- something other than a struct");
- }
- }
- }
- });
- }
-
- /// Ensures that implementations of the built-in trait `Copy` are legal.
- fn check_implementations_of_copy(&self) {
- let tcx = self.crate_context.tcx;
- let copy_trait = match tcx.lang_items.copy_trait() {
- Some(id) => id,
- None => return,
- };
- tcx.populate_implementations_for_trait_if_necessary(copy_trait);
- let copy_trait = tcx.lookup_trait_def(copy_trait);
-
- copy_trait.for_each_impl(tcx, |impl_did| {
- debug!("check_implementations_of_copy: impl_did={:?}", impl_did);
-
- let impl_node_id = if let Some(n) = tcx.map.as_local_node_id(impl_did) {
- n
- } else {
- debug!("check_implementations_of_copy(): impl not in this \
- crate");
- return;
- };
-
- let self_type = tcx.item_type(impl_did);
- debug!("check_implementations_of_copy: self_type={:?} (bound)",
- self_type);
-
- let span = tcx.map.span(impl_node_id);
- let param_env = ParameterEnvironment::for_item(tcx, impl_node_id);
- let self_type = self_type.subst(tcx, ¶m_env.free_substs);
- assert!(!self_type.has_escaping_regions());
-
- debug!("check_implementations_of_copy: self_type={:?} (free)",
- self_type);
-
- match param_env.can_type_implement_copy(tcx, self_type, span) {
- Ok(()) => {}
- Err(CopyImplementationError::InfrigingField(name)) => {
- struct_span_err!(tcx.sess,
- span,
- E0204,
- "the trait `Copy` may not be implemented for this type")
- .span_label(span, &format!("field `{}` does not implement `Copy`", name))
- .emit()
- }
- Err(CopyImplementationError::InfrigingVariant(name)) => {
- let item = tcx.map.expect_item(impl_node_id);
- let span = if let ItemImpl(.., Some(ref tr), _, _) = item.node {
- tr.path.span
- } else {
- span
- };
-
- struct_span_err!(tcx.sess,
- span,
- E0205,
- "the trait `Copy` may not be implemented for this type")
- .span_label(span,
- &format!("variant `{}` does not implement `Copy`", name))
- .emit()
- }
- Err(CopyImplementationError::NotAnAdt) => {
- let item = tcx.map.expect_item(impl_node_id);
- let span = if let ItemImpl(.., ref ty, _) = item.node {
- ty.span
- } else {
- span
- };
-
- struct_span_err!(tcx.sess,
- span,
- E0206,
- "the trait `Copy` may not be implemented for this type")
- .span_label(span, &format!("type is not a structure or enumeration"))
- .emit();
- }
- Err(CopyImplementationError::HasDestructor) => {
- struct_span_err!(tcx.sess,
- span,
- E0184,
- "the trait `Copy` may not be implemented for this type; the \
- type has a destructor")
- .span_label(span, &format!("Copy not allowed on types with destructors"))
- .emit();
- }
- }
- });
- }
-
- /// Process implementations of the built-in trait `CoerceUnsized`.
- fn check_implementations_of_coerce_unsized(&self) {
- let tcx = self.crate_context.tcx;
- let coerce_unsized_trait = match tcx.lang_items.coerce_unsized_trait() {
- Some(id) => id,
- None => return,
- };
- let unsize_trait = match tcx.lang_items.require(UnsizeTraitLangItem) {
- Ok(id) => id,
- Err(err) => {
- tcx.sess.fatal(&format!("`CoerceUnsized` implementation {}", err));
- }
- };
-
- let trait_def = tcx.lookup_trait_def(coerce_unsized_trait);
-
- trait_def.for_each_impl(tcx, |impl_did| {
- debug!("check_implementations_of_coerce_unsized: impl_did={:?}",
- impl_did);
-
- let impl_node_id = if let Some(n) = tcx.map.as_local_node_id(impl_did) {
- n
- } else {
- debug!("check_implementations_of_coerce_unsized(): impl not \
- in this crate");
- return;
- };
-
- let source = tcx.item_type(impl_did);
- let trait_ref = self.crate_context.tcx.impl_trait_ref(impl_did).unwrap();
- let target = trait_ref.substs.type_at(1);
- debug!("check_implementations_of_coerce_unsized: {:?} -> {:?} (bound)",
- source,
- target);
-
- let span = tcx.map.span(impl_node_id);
- let param_env = ParameterEnvironment::for_item(tcx, impl_node_id);
- let source = source.subst(tcx, ¶m_env.free_substs);
- let target = target.subst(tcx, ¶m_env.free_substs);
- assert!(!source.has_escaping_regions());
-
- debug!("check_implementations_of_coerce_unsized: {:?} -> {:?} (free)",
- source,
- target);
-
- tcx.infer_ctxt(None, Some(param_env), Reveal::ExactMatch).enter(|infcx| {
- let cause = ObligationCause::misc(span, impl_node_id);
- let check_mutbl = |mt_a: ty::TypeAndMut<'gcx>,
- mt_b: ty::TypeAndMut<'gcx>,
- mk_ptr: &Fn(Ty<'gcx>) -> Ty<'gcx>| {
- if (mt_a.mutbl, mt_b.mutbl) == (hir::MutImmutable, hir::MutMutable) {
- infcx.report_mismatched_types(&cause,
- mk_ptr(mt_b.ty),
- target,
- ty::error::TypeError::Mutability).emit();
- }
- (mt_a.ty, mt_b.ty, unsize_trait, None)
- };
- let (source, target, trait_def_id, kind) = match (&source.sty, &target.sty) {
- (&ty::TyBox(a), &ty::TyBox(b)) => (a, b, unsize_trait, None),
-
- (&ty::TyRef(r_a, mt_a), &ty::TyRef(r_b, mt_b)) => {
- infcx.sub_regions(infer::RelateObjectBound(span), r_b, r_a);
- check_mutbl(mt_a, mt_b, &|ty| tcx.mk_imm_ref(r_b, ty))
- }
-
- (&ty::TyRef(_, mt_a), &ty::TyRawPtr(mt_b)) |
- (&ty::TyRawPtr(mt_a), &ty::TyRawPtr(mt_b)) => {
- check_mutbl(mt_a, mt_b, &|ty| tcx.mk_imm_ptr(ty))
- }
-
- (&ty::TyAdt(def_a, substs_a), &ty::TyAdt(def_b, substs_b))
- if def_a.is_struct() && def_b.is_struct() => {
- if def_a != def_b {
- let source_path = tcx.item_path_str(def_a.did);
- let target_path = tcx.item_path_str(def_b.did);
- span_err!(tcx.sess,
- span,
- E0377,
- "the trait `CoerceUnsized` may only be implemented \
- for a coercion between structures with the same \
- definition; expected {}, found {}",
- source_path,
- target_path);
- return;
- }
-
- let fields = &def_a.struct_variant().fields;
- let diff_fields = fields.iter()
- .enumerate()
- .filter_map(|(i, f)| {
- let (a, b) = (f.ty(tcx, substs_a), f.ty(tcx, substs_b));
-
- if tcx.item_type(f.did).is_phantom_data() {
- // Ignore PhantomData fields
- return None;
- }
-
- // Ignore fields that aren't significantly changed
- if let Ok(ok) = infcx.sub_types(false, &cause, b, a) {
- if ok.obligations.is_empty() {
- return None;
- }
- }
-
- // Collect up all fields that were significantly changed
- // i.e. those that contain T in coerce_unsized T -> U
- Some((i, a, b))
- })
- .collect::<Vec<_>>();
-
- if diff_fields.is_empty() {
- span_err!(tcx.sess,
- span,
- E0374,
- "the trait `CoerceUnsized` may only be implemented \
- for a coercion between structures with one field \
- being coerced, none found");
- return;
- } else if diff_fields.len() > 1 {
- let item = tcx.map.expect_item(impl_node_id);
- let span = if let ItemImpl(.., Some(ref t), _, _) = item.node {
- t.path.span
- } else {
- tcx.map.span(impl_node_id)
- };
-
- let mut err = struct_span_err!(tcx.sess,
- span,
- E0375,
- "implementing the trait \
- `CoerceUnsized` requires multiple \
- coercions");
- err.note("`CoerceUnsized` may only be implemented for \
- a coercion between structures with one field being coerced");
- err.note(&format!("currently, {} fields need coercions: {}",
- diff_fields.len(),
- diff_fields.iter()
- .map(|&(i, a, b)| {
- format!("{} ({} to {})", fields[i].name, a, b)
- })
- .collect::<Vec<_>>()
- .join(", ")));
- err.span_label(span, &format!("requires multiple coercions"));
- err.emit();
- return;
- }
-
- let (i, a, b) = diff_fields[0];
- let kind = ty::adjustment::CustomCoerceUnsized::Struct(i);
- (a, b, coerce_unsized_trait, Some(kind))
- }
-
- _ => {
- span_err!(tcx.sess,
- span,
- E0376,
- "the trait `CoerceUnsized` may only be implemented \
- for a coercion between structures");
- return;
- }
- };
-
- let mut fulfill_cx = traits::FulfillmentContext::new();
-
- // Register an obligation for `A: Trait<B>`.
- let cause = traits::ObligationCause::misc(span, impl_node_id);
- let predicate =
- tcx.predicate_for_trait_def(cause, trait_def_id, 0, source, &[target]);
- fulfill_cx.register_predicate_obligation(&infcx, predicate);
-
- // Check that all transitive obligations are satisfied.
- if let Err(errors) = fulfill_cx.select_all_or_error(&infcx) {
- infcx.report_fulfillment_errors(&errors);
- }
-
- // Finally, resolve all regions.
- let mut free_regions = FreeRegionMap::new();
- free_regions.relate_free_regions_from_predicates(&infcx.parameter_environment
- .caller_bounds);
- infcx.resolve_regions_and_report_errors(&free_regions, impl_node_id);
-
- if let Some(kind) = kind {
- tcx.custom_coerce_unsized_kinds.borrow_mut().insert(impl_did, kind);
- }
- });
- });
+ let trait_def = self.tcx.lookup_trait_def(impl_trait_ref.def_id);
+ trait_def.record_local_impl(self.tcx, impl_def_id, impl_trait_ref);
}
}
pub fn check_coherence(ccx: &CrateCtxt) {
let _task = ccx.tcx.dep_graph.in_task(DepNode::Coherence);
- ccx.tcx.infer_ctxt(None, None, Reveal::ExactMatch).enter(|infcx| {
- CoherenceChecker {
- crate_context: ccx,
- inference_context: infcx,
- }
- .check();
- });
+ CoherenceChecker { tcx: ccx.tcx }.check();
unsafety::check(ccx.tcx);
orphan::check(ccx.tcx);
overlap::check(ccx.tcx);
+ builtin::check(ccx.tcx);
}
differs from the behavior for `&T`, which is always `Copy`).
"##,
+/*
E0205: r##"
An attempt to implement the `Copy` trait for an enum failed because one of the
variants does not implement `Copy`. To fix this, you must implement `Copy` for
This fails because `&mut T` is not `Copy`, even when `T` is `Copy` (this
differs from the behavior for `&T`, which is always `Copy`).
"##,
+*/
E0206: r##"
You can only implement `Copy` for a struct or enum. Both of the following
}
}
- pub fn last_name(&self) -> String {
- self.segments.last().unwrap().name.clone()
+ pub fn last_name(&self) -> &str {
+ self.segments.last().unwrap().name.as_str()
}
}
let name = link::find_crate_name(Some(&sess), &krate.attrs, &input);
let driver::ExpansionResult { defs, analysis, resolutions, mut hir_forest, .. } = {
- driver::phase_2_configure_and_expand(
- &sess, &cstore, krate, None, &name, None, resolve::MakeGlobMap::No, |_| Ok(()),
- ).expect("phase_2_configure_and_expand aborted in rustdoc!")
+ let result = driver::phase_2_configure_and_expand(&sess,
+ &cstore,
+ krate,
+ None,
+ &name,
+ None,
+ resolve::MakeGlobMap::No,
+ |_| Ok(()));
+ abort_on_err(result, &sess)
};
let arena = DroplessArena::new();
/// Used when rendering a `ResolvedPath` structure. This invokes the `path`
/// rendering function with the necessary arguments for linking to a local path.
fn resolved_path(w: &mut fmt::Formatter, did: DefId, path: &clean::Path,
- print_all: bool) -> fmt::Result {
+ print_all: bool, use_absolute: bool) -> fmt::Result {
let last = path.segments.last().unwrap();
let rel_root = match &*path.segments[0].name {
"self" => Some("./".to_string()),
if w.alternate() {
write!(w, "{:#}{:#}", HRef::new(did, &last.name), last.params)?;
} else {
- write!(w, "{}{}", HRef::new(did, &last.name), last.params)?;
+ let path = if use_absolute {
+ match href(did) {
+ Some((_, _, fqp)) => format!("{}::{}",
+ fqp[..fqp.len()-1].join("::"),
+ HRef::new(did, fqp.last().unwrap())),
+ None => format!("{}", HRef::new(did, &last.name)),
+ }
+ } else {
+ format!("{}", HRef::new(did, &last.name))
+ };
+ write!(w, "{}{}", path, last.params)?;
}
Ok(())
}
}
}
-impl fmt::Display for clean::Type {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- match *self {
- clean::Generic(ref name) => {
- f.write_str(name)
- }
- clean::ResolvedPath{ did, ref typarams, ref path, is_generic } => {
- // Paths like T::Output and Self::Output should be rendered with all segments
- resolved_path(f, did, path, is_generic)?;
- tybounds(f, typarams)
+fn fmt_type(t: &clean::Type, f: &mut fmt::Formatter, use_absolute: bool) -> fmt::Result {
+ match *t {
+ clean::Generic(ref name) => {
+ f.write_str(name)
+ }
+ clean::ResolvedPath{ did, ref typarams, ref path, is_generic } => {
+ // Paths like T::Output and Self::Output should be rendered with all segments
+ resolved_path(f, did, path, is_generic, use_absolute)?;
+ tybounds(f, typarams)
+ }
+ clean::Infer => write!(f, "_"),
+ clean::Primitive(prim) => primitive_link(f, prim, prim.as_str()),
+ clean::BareFunction(ref decl) => {
+ if f.alternate() {
+ write!(f, "{}{}fn{:#}{:#}",
+ UnsafetySpace(decl.unsafety),
+ AbiSpace(decl.abi),
+ decl.generics,
+ decl.decl)
+ } else {
+ write!(f, "{}{}fn{}{}",
+ UnsafetySpace(decl.unsafety),
+ AbiSpace(decl.abi),
+ decl.generics,
+ decl.decl)
}
- clean::Infer => write!(f, "_"),
- clean::Primitive(prim) => primitive_link(f, prim, prim.as_str()),
- clean::BareFunction(ref decl) => {
- if f.alternate() {
- write!(f, "{}{}fn{:#}{:#}",
- UnsafetySpace(decl.unsafety),
- AbiSpace(decl.abi),
- decl.generics,
- decl.decl)
- } else {
- write!(f, "{}{}fn{}{}",
- UnsafetySpace(decl.unsafety),
- AbiSpace(decl.abi),
- decl.generics,
- decl.decl)
+ }
+ clean::Tuple(ref typs) => {
+ match &typs[..] {
+ &[] => primitive_link(f, PrimitiveType::Tuple, "()"),
+ &[ref one] => {
+ primitive_link(f, PrimitiveType::Tuple, "(")?;
+ //carry f.alternate() into this display w/o branching manually
+ fmt::Display::fmt(one, f)?;
+ primitive_link(f, PrimitiveType::Tuple, ",)")
}
- }
- clean::Tuple(ref typs) => {
- match &typs[..] {
- &[] => primitive_link(f, PrimitiveType::Tuple, "()"),
- &[ref one] => {
- primitive_link(f, PrimitiveType::Tuple, "(")?;
- //carry f.alternate() into this display w/o branching manually
- fmt::Display::fmt(one, f)?;
- primitive_link(f, PrimitiveType::Tuple, ",)")
- }
- many => {
- primitive_link(f, PrimitiveType::Tuple, "(")?;
- fmt::Display::fmt(&CommaSep(&many), f)?;
- primitive_link(f, PrimitiveType::Tuple, ")")
- }
+ many => {
+ primitive_link(f, PrimitiveType::Tuple, "(")?;
+ fmt::Display::fmt(&CommaSep(&many), f)?;
+ primitive_link(f, PrimitiveType::Tuple, ")")
}
}
- clean::Vector(ref t) => {
- primitive_link(f, PrimitiveType::Slice, &format!("["))?;
- fmt::Display::fmt(t, f)?;
- primitive_link(f, PrimitiveType::Slice, &format!("]"))
- }
- clean::FixedVector(ref t, ref s) => {
- primitive_link(f, PrimitiveType::Array, "[")?;
- fmt::Display::fmt(t, f)?;
- if f.alternate() {
- primitive_link(f, PrimitiveType::Array,
- &format!("; {}]", s))
- } else {
- primitive_link(f, PrimitiveType::Array,
- &format!("; {}]", Escape(s)))
- }
+ }
+ clean::Vector(ref t) => {
+ primitive_link(f, PrimitiveType::Slice, &format!("["))?;
+ fmt::Display::fmt(t, f)?;
+ primitive_link(f, PrimitiveType::Slice, &format!("]"))
+ }
+ clean::FixedVector(ref t, ref s) => {
+ primitive_link(f, PrimitiveType::Array, "[")?;
+ fmt::Display::fmt(t, f)?;
+ if f.alternate() {
+ primitive_link(f, PrimitiveType::Array,
+ &format!("; {}]", s))
+ } else {
+ primitive_link(f, PrimitiveType::Array,
+ &format!("; {}]", Escape(s)))
}
- clean::Never => f.write_str("!"),
- clean::RawPointer(m, ref t) => {
- match **t {
- clean::Generic(_) | clean::ResolvedPath {is_generic: true, ..} => {
- if f.alternate() {
- primitive_link(f, clean::PrimitiveType::RawPointer,
- &format!("*{}{:#}", RawMutableSpace(m), t))
- } else {
- primitive_link(f, clean::PrimitiveType::RawPointer,
- &format!("*{}{}", RawMutableSpace(m), t))
- }
- }
- _ => {
+ }
+ clean::Never => f.write_str("!"),
+ clean::RawPointer(m, ref t) => {
+ match **t {
+ clean::Generic(_) | clean::ResolvedPath {is_generic: true, ..} => {
+ if f.alternate() {
primitive_link(f, clean::PrimitiveType::RawPointer,
- &format!("*{}", RawMutableSpace(m)))?;
- fmt::Display::fmt(t, f)
+ &format!("*{}{:#}", RawMutableSpace(m), t))
+ } else {
+ primitive_link(f, clean::PrimitiveType::RawPointer,
+ &format!("*{}{}", RawMutableSpace(m), t))
}
}
+ _ => {
+ primitive_link(f, clean::PrimitiveType::RawPointer,
+ &format!("*{}", RawMutableSpace(m)))?;
+ fmt::Display::fmt(t, f)
+ }
}
- clean::BorrowedRef{ lifetime: ref l, mutability, type_: ref ty} => {
- let lt = match *l {
- Some(ref l) => format!("{} ", *l),
- _ => "".to_string(),
- };
- let m = MutableSpace(mutability);
- match **ty {
- clean::Vector(ref bt) => { // BorrowedRef{ ... Vector(T) } is &[T]
- match **bt {
- clean::Generic(_) =>
- if f.alternate() {
- primitive_link(f, PrimitiveType::Slice,
- &format!("&{}{}[{:#}]", lt, m, **bt))
- } else {
- primitive_link(f, PrimitiveType::Slice,
- &format!("&{}{}[{}]", lt, m, **bt))
- },
- _ => {
- if f.alternate() {
- primitive_link(f, PrimitiveType::Slice,
- &format!("&{}{}[", lt, m))?;
- write!(f, "{:#}", **bt)?;
- } else {
- primitive_link(f, PrimitiveType::Slice,
- &format!("&{}{}[", lt, m))?;
- write!(f, "{}", **bt)?;
- }
- primitive_link(f, PrimitiveType::Slice, "]")
+ }
+ clean::BorrowedRef{ lifetime: ref l, mutability, type_: ref ty} => {
+ let lt = match *l {
+ Some(ref l) => format!("{} ", *l),
+ _ => "".to_string(),
+ };
+ let m = MutableSpace(mutability);
+ match **ty {
+ clean::Vector(ref bt) => { // BorrowedRef{ ... Vector(T) } is &[T]
+ match **bt {
+ clean::Generic(_) =>
+ if f.alternate() {
+ primitive_link(f, PrimitiveType::Slice,
+ &format!("&{}{}[{:#}]", lt, m, **bt))
+ } else {
+ primitive_link(f, PrimitiveType::Slice,
+ &format!("&{}{}[{}]", lt, m, **bt))
+ },
+ _ => {
+ if f.alternate() {
+ primitive_link(f, PrimitiveType::Slice,
+ &format!("&{}{}[", lt, m))?;
+ write!(f, "{:#}", **bt)?;
+ } else {
+ primitive_link(f, PrimitiveType::Slice,
+ &format!("&{}{}[", lt, m))?;
+ write!(f, "{}", **bt)?;
}
- }
- }
- _ => {
- if f.alternate() {
- write!(f, "&{}{}{:#}", lt, m, **ty)
- } else {
- write!(f, "&{}{}{}", lt, m, **ty)
+ primitive_link(f, PrimitiveType::Slice, "]")
}
}
}
- }
- clean::PolyTraitRef(ref bounds) => {
- for (i, bound) in bounds.iter().enumerate() {
- if i != 0 {
- write!(f, " + ")?;
- }
+ _ => {
if f.alternate() {
- write!(f, "{:#}", *bound)?;
+ write!(f, "&{}{}{:#}", lt, m, **ty)
} else {
- write!(f, "{}", *bound)?;
+ write!(f, "&{}{}{}", lt, m, **ty)
}
}
- Ok(())
}
- clean::ImplTrait(ref bounds) => {
- write!(f, "impl ")?;
- for (i, bound) in bounds.iter().enumerate() {
- if i != 0 {
- write!(f, " + ")?;
- }
- if f.alternate() {
- write!(f, "{:#}", *bound)?;
- } else {
- write!(f, "{}", *bound)?;
- }
+ }
+ clean::PolyTraitRef(ref bounds) => {
+ for (i, bound) in bounds.iter().enumerate() {
+ if i != 0 {
+ write!(f, " + ")?;
}
- Ok(())
- }
- // It's pretty unsightly to look at `<A as B>::C` in output, and
- // we've got hyperlinking on our side, so try to avoid longer
- // notation as much as possible by making `C` a hyperlink to trait
- // `B` to disambiguate.
- //
- // FIXME: this is still a lossy conversion and there should probably
- // be a better way of representing this in general? Most of
- // the ugliness comes from inlining across crates where
- // everything comes in as a fully resolved QPath (hard to
- // look at).
- clean::QPath {
- ref name,
- ref self_type,
- trait_: box clean::ResolvedPath { did, ref typarams, .. },
- } => {
if f.alternate() {
- write!(f, "{:#}::", self_type)?;
+ write!(f, "{:#}", *bound)?;
} else {
- write!(f, "{}::", self_type)?;
+ write!(f, "{}", *bound)?;
}
- let path = clean::Path::singleton(name.clone());
- resolved_path(f, did, &path, false)?;
-
- // FIXME: `typarams` are not rendered, and this seems bad?
- drop(typarams);
- Ok(())
}
- clean::QPath { ref name, ref self_type, ref trait_ } => {
+ Ok(())
+ }
+ clean::ImplTrait(ref bounds) => {
+ write!(f, "impl ")?;
+ for (i, bound) in bounds.iter().enumerate() {
+ if i != 0 {
+ write!(f, " + ")?;
+ }
if f.alternate() {
- write!(f, "<{:#} as {:#}>::{}", self_type, trait_, name)
+ write!(f, "{:#}", *bound)?;
} else {
- write!(f, "<{} as {}>::{}", self_type, trait_, name)
+ write!(f, "{}", *bound)?;
}
}
- clean::Unique(..) => {
- panic!("should have been cleaned")
+ Ok(())
+ }
+ // It's pretty unsightly to look at `<A as B>::C` in output, and
+ // we've got hyperlinking on our side, so try to avoid longer
+ // notation as much as possible by making `C` a hyperlink to trait
+ // `B` to disambiguate.
+ //
+ // FIXME: this is still a lossy conversion and there should probably
+ // be a better way of representing this in general? Most of
+ // the ugliness comes from inlining across crates where
+ // everything comes in as a fully resolved QPath (hard to
+ // look at).
+ clean::QPath {
+ ref name,
+ ref self_type,
+ trait_: box clean::ResolvedPath { did, ref typarams, .. },
+ } => {
+ if f.alternate() {
+ write!(f, "{:#}::", self_type)?;
+ } else {
+ write!(f, "{}::", self_type)?;
+ }
+ let path = clean::Path::singleton(name.clone());
+ resolved_path(f, did, &path, true, use_absolute)?;
+
+ // FIXME: `typarams` are not rendered, and this seems bad?
+ drop(typarams);
+ Ok(())
+ }
+ clean::QPath { ref name, ref self_type, ref trait_ } => {
+ if f.alternate() {
+ write!(f, "<{:#} as {:#}>::{}", self_type, trait_, name)
+ } else {
+ write!(f, "<{} as {}>::{}", self_type, trait_, name)
}
}
+ clean::Unique(..) => {
+ panic!("should have been cleaned")
+ }
+ }
+}
+
+impl fmt::Display for clean::Type {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ fmt_type(self, f, false)
}
}
-fn fmt_impl(i: &clean::Impl, f: &mut fmt::Formatter, link_trait: bool) -> fmt::Result {
+fn fmt_impl(i: &clean::Impl,
+ f: &mut fmt::Formatter,
+ link_trait: bool,
+ use_absolute: bool) -> fmt::Result {
let mut plain = String::new();
if f.alternate() {
plain.push_str(&format!("{:#}", ty));
} else {
match *ty {
- clean::ResolvedPath{ typarams: None, ref path, is_generic: false, .. } => {
+ clean::ResolvedPath { typarams: None, ref path, is_generic: false, .. } => {
let last = path.segments.last().unwrap();
fmt::Display::fmt(&last.name, f)?;
fmt::Display::fmt(&last.params, f)?;
plain.push_str(" for ");
}
- fmt::Display::fmt(&i.for_, f)?;
+ fmt_type(&i.for_, f, use_absolute)?;
plain.push_str(&format!("{:#}", i.for_));
fmt::Display::fmt(&WhereClause(&i.generics, plain.len() + 1), f)?;
impl fmt::Display for clean::Impl {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- fmt_impl(self, f, true)
+ fmt_impl(self, f, true, false)
}
}
// The difference from above is that trait is not hyperlinked.
-pub fn fmt_impl_for_trait_page(i: &clean::Impl, f: &mut fmt::Formatter) -> fmt::Result {
- fmt_impl(i, f, false)
+pub fn fmt_impl_for_trait_page(i: &clean::Impl,
+ f: &mut fmt::Formatter,
+ use_absolute: bool) -> fmt::Result {
+ fmt_impl(i, f, false, use_absolute)
}
impl fmt::Display for clean::Arguments {
impl fmt::Display for clean::ImportSource {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.did {
- Some(did) => resolved_path(f, did, &self.path, true),
+ Some(did) => resolved_path(f, did, &self.path, true, false),
_ => {
for (i, seg) in self.path.segments.iter().enumerate() {
if i > 0 {
<ul class='item-list' id='implementors-list'>
")?;
if let Some(implementors) = cache.implementors.get(&it.def_id) {
- for i in implementors {
+ let mut implementor_count: FxHashMap<&str, usize> = FxHashMap();
+ for implementor in implementors {
+ if let clean::Type::ResolvedPath {ref path, ..} = implementor.impl_.for_ {
+ *implementor_count.entry(path.last_name()).or_insert(0) += 1;
+ }
+ }
+
+ for implementor in implementors {
write!(w, "<li><code>")?;
- fmt_impl_for_trait_page(&i.impl_, w)?;
+ // If there's already another implementor that has the same abbridged name, use the
+ // full path, for example in `std::iter::ExactSizeIterator`
+ let use_absolute = if let clean::Type::ResolvedPath {
+ ref path, ..
+ } = implementor.impl_.for_ {
+ implementor_count[path.last_name()] > 1
+ } else {
+ false
+ };
+ fmt_impl_for_trait_page(&implementor.impl_, w, use_absolute)?;
writeln!(w, "</code></li>")?;
}
}
self.0.ttl()
}
- /// Sets the value for the `IPV6_V6ONLY` option on this socket.
- ///
- /// If this is set to `true` then the socket is restricted to sending and
- /// receiving IPv6 packets only. In this case two IPv4 and IPv6 applications
- /// can bind the same port at the same time.
- ///
- /// If this is set to `false` then the socket can be used to send and
- /// receive packets from an IPv4-mapped IPv6 address.
- ///
- /// # Examples
- ///
- /// ```no_run
- /// use std::net::TcpListener;
- ///
- /// let listener = TcpListener::bind("127.0.0.1:80").unwrap();
- /// listener.set_only_v6(true).expect("Cannot set to IPv6");
- /// ```
#[stable(feature = "net2_mutators", since = "1.9.0")]
+ #[rustc_deprecated(since = "1.16.0",
+ reason = "this option can only be set before the socket is bound")]
+ #[allow(missing_docs)]
pub fn set_only_v6(&self, only_v6: bool) -> io::Result<()> {
self.0.set_only_v6(only_v6)
}
- /// Gets the value of the `IPV6_V6ONLY` option for this socket.
- ///
- /// For more information about this option, see [`set_only_v6`][link].
- ///
- /// [link]: #method.set_only_v6
- ///
- /// # Examples
- ///
- /// ```no_run
- /// use std::net::TcpListener;
- ///
- /// let listener = TcpListener::bind("127.0.0.1:80").unwrap();
- /// listener.set_only_v6(true).expect("Cannot set to IPv6");
- /// assert_eq!(listener.only_v6().unwrap_or(false), true);
- /// ```
#[stable(feature = "net2_mutators", since = "1.9.0")]
+ #[rustc_deprecated(since = "1.16.0",
+ reason = "this option can only be set before the socket is bound")]
+ #[allow(missing_docs)]
pub fn only_v6(&self) -> io::Result<bool> {
self.0.only_v6()
}
use str::FromStr;
use string::{String, ToString};
use sys::syscall::EINVAL;
-use time;
+use time::{self, Duration};
use vec::{IntoIter, Vec};
use self::dns::{Dns, DnsQuery};
let my_ip = Ipv4Addr::new(ip[0], ip[1], ip[2], ip[3]);
let dns_ip = Ipv4Addr::new(dns[0], dns[1], dns[2], dns[3]);
let socket = UdpSocket::bind(&SocketAddr::V4(SocketAddrV4::new(my_ip, 0)))?;
+ socket.set_read_timeout(Some(Duration::new(5, 0)))?;
+ socket.set_write_timeout(Some(Duration::new(5, 0)))?;
socket.connect(&SocketAddr::V4(SocketAddrV4::new(dns_ip, 53)))?;
socket.send(&packet_data)?;
// option. This file may not be copied, modified, or distributed
// except according to those terms.
+use cmp;
use io::{Error, ErrorKind, Result};
+use mem;
use net::{SocketAddr, Shutdown};
use path::Path;
use sys::fs::{File, OpenOptions};
+use sys::syscall::TimeSpec;
use sys_common::{AsInner, FromInner, IntoInner};
use time::Duration;
use vec::Vec;
}
pub fn ttl(&self) -> Result<u32> {
- Err(Error::new(ErrorKind::Other, "TcpStream::ttl not implemented"))
+ let mut ttl = [0];
+ let file = self.0.dup(b"ttl")?;
+ file.read(&mut ttl)?;
+ Ok(ttl[0] as u32)
}
pub fn read_timeout(&self) -> Result<Option<Duration>> {
- Err(Error::new(ErrorKind::Other, "TcpStream::read_timeout not implemented"))
+ let mut time = TimeSpec::default();
+ let file = self.0.dup(b"read_timeout")?;
+ if file.read(&mut time)? >= mem::size_of::<TimeSpec>() {
+ Ok(Some(Duration::new(time.tv_sec as u64, time.tv_nsec as u32)))
+ } else {
+ Ok(None)
+ }
}
pub fn write_timeout(&self) -> Result<Option<Duration>> {
- Err(Error::new(ErrorKind::Other, "TcpStream::write_timeout not implemented"))
+ let mut time = TimeSpec::default();
+ let file = self.0.dup(b"write_timeout")?;
+ if file.read(&mut time)? >= mem::size_of::<TimeSpec>() {
+ Ok(Some(Duration::new(time.tv_sec as u64, time.tv_nsec as u32)))
+ } else {
+ Ok(None)
+ }
}
pub fn set_nodelay(&self, _nodelay: bool) -> Result<()> {
Err(Error::new(ErrorKind::Other, "TcpStream::set_only_v6 not implemented"))
}
- pub fn set_ttl(&self, _ttl: u32) -> Result<()> {
- Err(Error::new(ErrorKind::Other, "TcpStream::set_ttl not implemented"))
- }
-
- pub fn set_read_timeout(&self, _dur: Option<Duration>) -> Result<()> {
- Err(Error::new(ErrorKind::Other, "TcpStream::set_read_timeout not implemented"))
- }
-
- pub fn set_write_timeout(&self, _dur: Option<Duration>) -> Result<()> {
- Err(Error::new(ErrorKind::Other, "TcpStream::set_write_timeout not implemented"))
+ pub fn set_ttl(&self, ttl: u32) -> Result<()> {
+ let file = self.0.dup(b"ttl")?;
+ file.write(&[cmp::min(ttl, 255) as u8])?;
+ Ok(())
+ }
+
+ pub fn set_read_timeout(&self, duration_option: Option<Duration>) -> Result<()> {
+ let file = self.0.dup(b"read_timeout")?;
+ if let Some(duration) = duration_option {
+ file.write(&TimeSpec {
+ tv_sec: duration.as_secs() as i64,
+ tv_nsec: duration.subsec_nanos() as i32
+ })?;
+ } else {
+ file.write(&[])?;
+ }
+ Ok(())
+ }
+
+ pub fn set_write_timeout(&self, duration_option: Option<Duration>) -> Result<()> {
+ let file = self.0.dup(b"write_timeout")?;
+ if let Some(duration) = duration_option {
+ file.write(&TimeSpec {
+ tv_sec: duration.as_secs() as i64,
+ tv_nsec: duration.subsec_nanos() as i32
+ })?;
+ } else {
+ file.write(&[])?;
+ }
+ Ok(())
}
}
}
pub fn ttl(&self) -> Result<u32> {
- Err(Error::new(ErrorKind::Other, "TcpListener::ttl not implemented"))
+ let mut ttl = [0];
+ let file = self.0.dup(b"ttl")?;
+ file.read(&mut ttl)?;
+ Ok(ttl[0] as u32)
}
pub fn set_nonblocking(&self, _nonblocking: bool) -> Result<()> {
Err(Error::new(ErrorKind::Other, "TcpListener::set_only_v6 not implemented"))
}
- pub fn set_ttl(&self, _ttl: u32) -> Result<()> {
- Err(Error::new(ErrorKind::Other, "TcpListener::set_ttl not implemented"))
+ pub fn set_ttl(&self, ttl: u32) -> Result<()> {
+ let file = self.0.dup(b"ttl")?;
+ file.write(&[cmp::min(ttl, 255) as u8])?;
+ Ok(())
}
}
// except according to those terms.
use cell::UnsafeCell;
+use cmp;
use io::{Error, ErrorKind, Result};
+use mem;
use net::{SocketAddr, Ipv4Addr, Ipv6Addr};
use path::Path;
use sys::fs::{File, OpenOptions};
+use sys::syscall::TimeSpec;
use sys_common::{AsInner, FromInner, IntoInner};
use time::Duration;
}
pub fn ttl(&self) -> Result<u32> {
- Err(Error::new(ErrorKind::Other, "UdpSocket::ttl not implemented"))
+ let mut ttl = [0];
+ let file = self.0.dup(b"ttl")?;
+ file.read(&mut ttl)?;
+ Ok(ttl[0] as u32)
}
pub fn read_timeout(&self) -> Result<Option<Duration>> {
- Err(Error::new(ErrorKind::Other, "UdpSocket::read_timeout not implemented"))
+ let mut time = TimeSpec::default();
+ let file = self.0.dup(b"read_timeout")?;
+ if file.read(&mut time)? >= mem::size_of::<TimeSpec>() {
+ Ok(Some(Duration::new(time.tv_sec as u64, time.tv_nsec as u32)))
+ } else {
+ Ok(None)
+ }
}
pub fn write_timeout(&self) -> Result<Option<Duration>> {
- Err(Error::new(ErrorKind::Other, "UdpSocket::write_timeout not implemented"))
+ let mut time = TimeSpec::default();
+ let file = self.0.dup(b"write_timeout")?;
+ if file.read(&mut time)? >= mem::size_of::<TimeSpec>() {
+ Ok(Some(Duration::new(time.tv_sec as u64, time.tv_nsec as u32)))
+ } else {
+ Ok(None)
+ }
}
pub fn set_broadcast(&self, _broadcast: bool) -> Result<()> {
Err(Error::new(ErrorKind::Other, "UdpSocket::set_only_v6 not implemented"))
}
- pub fn set_ttl(&self, _ttl: u32) -> Result<()> {
- Err(Error::new(ErrorKind::Other, "UdpSocket::set_ttl not implemented"))
+ pub fn set_ttl(&self, ttl: u32) -> Result<()> {
+ let file = self.0.dup(b"ttl")?;
+ file.write(&[cmp::min(ttl, 255) as u8])?;
+ Ok(())
}
- pub fn set_read_timeout(&self, _dur: Option<Duration>) -> Result<()> {
- Err(Error::new(ErrorKind::Other, "UdpSocket::set_read_timeout not implemented"))
+ pub fn set_read_timeout(&self, duration_option: Option<Duration>) -> Result<()> {
+ let file = self.0.dup(b"read_timeout")?;
+ if let Some(duration) = duration_option {
+ file.write(&TimeSpec {
+ tv_sec: duration.as_secs() as i64,
+ tv_nsec: duration.subsec_nanos() as i32
+ })?;
+ } else {
+ file.write(&[])?;
+ }
+ Ok(())
}
- pub fn set_write_timeout(&self, _dur: Option<Duration>) -> Result<()> {
- Err(Error::new(ErrorKind::Other, "UdpSocket::set_write_timeout not implemented"))
+ pub fn set_write_timeout(&self, duration_option: Option<Duration>) -> Result<()> {
+ let file = self.0.dup(b"write_timeout")?;
+ if let Some(duration) = duration_option {
+ file.write(&TimeSpec {
+ tv_sec: duration.as_secs() as i64,
+ tv_nsec: duration.subsec_nanos() as i32
+ })?;
+ } else {
+ file.write(&[])?;
+ }
+ Ok(())
}
pub fn join_multicast_v4(&self, _multiaddr: &Ipv4Addr, _interface: &Ipv4Addr) -> Result<()> {
pub tv_sec: i64,
pub tv_nsec: i32,
}
+
+impl Deref for TimeSpec {
+ type Target = [u8];
+ fn deref(&self) -> &[u8] {
+ unsafe {
+ slice::from_raw_parts(self as *const TimeSpec as *const u8,
+ mem::size_of::<TimeSpec>()) as &[u8]
+ }
+ }
+}
+
+impl DerefMut for TimeSpec {
+ fn deref_mut(&mut self) -> &mut [u8] {
+ unsafe {
+ slice::from_raw_parts_mut(self as *mut TimeSpec as *mut u8,
+ mem::size_of::<TimeSpec>()) as &mut [u8]
+ }
+ }
+}
pub const EXCEPTION_MAXIMUM_PARAMETERS: usize = 15;
pub const PIPE_ACCESS_INBOUND: DWORD = 0x00000001;
+pub const PIPE_ACCESS_OUTBOUND: DWORD = 0x00000002;
pub const FILE_FLAG_FIRST_PIPE_INSTANCE: DWORD = 0x00080000;
pub const FILE_FLAG_OVERLAPPED: DWORD = 0x40000000;
pub const PIPE_WAIT: DWORD = 0x00000000;
inner: Handle,
}
-pub fn anon_pipe() -> io::Result<(AnonPipe, AnonPipe)> {
+pub struct Pipes {
+ pub ours: AnonPipe,
+ pub theirs: AnonPipe,
+}
+
+/// Although this looks similar to `anon_pipe` in the Unix module it's actually
+/// subtly different. Here we'll return two pipes in the `Pipes` return value,
+/// but one is intended for "us" where as the other is intended for "someone
+/// else".
+///
+/// Currently the only use case for this function is pipes for stdio on
+/// processes in the standard library, so "ours" is the one that'll stay in our
+/// process whereas "theirs" will be inherited to a child.
+///
+/// The ours/theirs pipes are *not* specifically readable or writable. Each
+/// one only supports a read or a write, but which is which depends on the
+/// boolean flag given. If `ours_readable` is true then `ours` is readable where
+/// `theirs` is writable. Conversely if `ours_readable` is false then `ours` is
+/// writable where `theirs` is readable.
+///
+/// Also note that the `ours` pipe is always a handle opened up in overlapped
+/// mode. This means that technically speaking it should only ever be used
+/// with `OVERLAPPED` instances, but also works out ok if it's only ever used
+/// once at a time (which we do indeed guarantee).
+pub fn anon_pipe(ours_readable: bool) -> io::Result<Pipes> {
// Note that we specifically do *not* use `CreatePipe` here because
// unfortunately the anonymous pipes returned do not support overlapped
- // operations.
- //
- // Instead, we create a "hopefully unique" name and create a named pipe
- // which has overlapped operations enabled.
+ // operations. Instead, we create a "hopefully unique" name and create a
+ // named pipe which has overlapped operations enabled.
//
- // Once we do this, we connect do it as usual via `CreateFileW`, and then we
- // return those reader/writer halves.
+ // Once we do this, we connect do it as usual via `CreateFileW`, and then
+ // we return those reader/writer halves. Note that the `ours` pipe return
+ // value is always the named pipe, whereas `theirs` is just the normal file.
+ // This should hopefully shield us from child processes which assume their
+ // stdout is a named pipe, which would indeed be odd!
unsafe {
- let reader;
+ let ours;
let mut name;
let mut tries = 0;
let mut reject_remote_clients_flag = c::PIPE_REJECT_REMOTE_CLIENTS;
.encode_wide()
.chain(Some(0))
.collect::<Vec<_>>();
+ let mut flags = c::FILE_FLAG_FIRST_PIPE_INSTANCE |
+ c::FILE_FLAG_OVERLAPPED;
+ if ours_readable {
+ flags |= c::PIPE_ACCESS_INBOUND;
+ } else {
+ flags |= c::PIPE_ACCESS_OUTBOUND;
+ }
let handle = c::CreateNamedPipeW(wide_name.as_ptr(),
- c::PIPE_ACCESS_INBOUND |
- c::FILE_FLAG_FIRST_PIPE_INSTANCE |
- c::FILE_FLAG_OVERLAPPED,
+ flags,
c::PIPE_TYPE_BYTE |
c::PIPE_READMODE_BYTE |
c::PIPE_WAIT |
}
return Err(err)
}
- reader = Handle::new(handle);
+ ours = Handle::new(handle);
break
}
- // Connect to the named pipe we just created in write-only mode (also
- // overlapped for async I/O below).
+ // Connect to the named pipe we just created. This handle is going to be
+ // returned in `theirs`, so if `ours` is readable we want this to be
+ // writable, otherwise if `ours` is writable we want this to be
+ // readable.
+ //
+ // Additionally we don't enable overlapped mode on this because most
+ // client processes aren't enabled to work with that.
let mut opts = OpenOptions::new();
- opts.write(true);
- opts.read(false);
+ opts.write(ours_readable);
+ opts.read(!ours_readable);
opts.share_mode(0);
- opts.attributes(c::FILE_FLAG_OVERLAPPED);
- let writer = File::open(Path::new(&name), &opts)?;
- let writer = AnonPipe { inner: writer.into_handle() };
+ let theirs = File::open(Path::new(&name), &opts)?;
+ let theirs = AnonPipe { inner: theirs.into_handle() };
- Ok((AnonPipe { inner: reader }, AnonPipe { inner: writer.into_handle() }))
+ Ok(Pipes {
+ ours: AnonPipe { inner: ours },
+ theirs: AnonPipe { inner: theirs.into_handle() },
+ })
}
}
}
Stdio::MakePipe => {
- let (reader, writer) = pipe::anon_pipe()?;
- let (ours, theirs) = if stdio_id == c::STD_INPUT_HANDLE {
- (writer, reader)
- } else {
- (reader, writer)
- };
- *pipe = Some(ours);
+ let ours_readable = stdio_id != c::STD_INPUT_HANDLE;
+ let pipes = pipe::anon_pipe(ours_readable)?;
+ *pipe = Some(pipes.ours);
cvt(unsafe {
- c::SetHandleInformation(theirs.handle().raw(),
+ c::SetHandleInformation(pipes.theirs.handle().raw(),
c::HANDLE_FLAG_INHERIT,
c::HANDLE_FLAG_INHERIT)
})?;
- Ok(theirs.into_handle())
+ Ok(pipes.theirs.into_handle())
}
Stdio::Handle(ref handle) => {
// loop to basically match Unix semantics. If we don't reach a fixed point
// after a short while then we just inevitably leak something most likely.
//
-// # The article mentions crazy stuff about "/INCLUDE"?
+// # The article mentions weird stuff about "/INCLUDE"?
//
// It sure does! Specifically we're talking about this quote:
//
/// A `ThreadId` is an opaque object that has a unique value for each thread
/// that creates one. `ThreadId`s do not correspond to a thread's system-
/// designated identifier.
+///
+/// # Examples
+///
+/// ```
+/// #![feature(thread_id)]
+///
+/// use std::thread;
+///
+/// let handler = thread::Builder::new()
+/// .spawn(|| {
+/// let thread = thread::current();
+/// let thread_id = thread.id();
+/// })
+/// .unwrap();
+///
+/// handler.join().unwrap();
+/// ```
#[unstable(feature = "thread_id", issue = "21507")]
#[derive(Eq, PartialEq, Copy, Clone)]
pub struct ThreadId(u64);
#[derive(Clone)]
#[stable(feature = "rust1", since = "1.0.0")]
/// A handle to a thread.
+///
+/// # Examples
+///
+/// ```
+/// use std::thread;
+///
+/// let handler = thread::Builder::new()
+/// .name("foo".into())
+/// .spawn(|| {
+/// let thread = thread::current();
+/// println!("thread name: {}", thread.name().unwrap());
+/// })
+/// .unwrap();
+///
+/// handler.join().unwrap();
+/// ```
pub struct Thread {
inner: Arc<Inner>,
}
/// Atomically makes the handle's token available if it is not already.
///
/// See the module doc for more detail.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::thread;
+ ///
+ /// let handler = thread::Builder::new()
+ /// .spawn(|| {
+ /// let thread = thread::current();
+ /// thread.unpark();
+ /// })
+ /// .unwrap();
+ ///
+ /// handler.join().unwrap();
+ /// ```
#[stable(feature = "rust1", since = "1.0.0")]
pub fn unpark(&self) {
let mut guard = self.inner.lock.lock().unwrap();
}
/// Gets the thread's unique identifier.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// #![feature(thread_id)]
+ ///
+ /// use std::thread;
+ ///
+ /// let handler = thread::Builder::new()
+ /// .spawn(|| {
+ /// let thread = thread::current();
+ /// println!("thread id: {:?}", thread.id());
+ /// })
+ /// .unwrap();
+ ///
+ /// handler.join().unwrap();
+ /// ```
#[unstable(feature = "thread_id", issue = "21507")]
pub fn id(&self) -> ThreadId {
self.inner.id
fn enable_allow_internal_unstable = allow_internal_unstable,
fn enable_custom_derive = custom_derive,
fn enable_pushpop_unsafe = pushpop_unsafe,
- fn enable_proc_macro = proc_macro,
}
}
// instead of just the platforms on which it is the C ABI
(active, abi_sysv64, "1.13.0", Some(36167)),
- // Macros 1.1
- (active, proc_macro, "1.13.0", Some(35900)),
-
// Allows untagged unions `union U { ... }`
(active, untagged_unions, "1.13.0", Some(32836)),
// Allows `..` in tuple (struct) patterns
(accepted, dotdot_in_tuple_patterns, "1.14.0", Some(33627)),
(accepted, item_like_imports, "1.14.0", Some(35120)),
+ // Macros 1.1
+ (accepted, proc_macro, "1.15.0", Some(35900)),
);
// (changing above list without updating src/doc/reference.md makes @cmr sad)
is an experimental feature",
cfg_fn!(fundamental))),
- ("proc_macro_derive", Normal, Gated(Stability::Unstable,
- "proc_macro",
- "the `#[proc_macro_derive]` attribute \
- is an experimental feature",
- cfg_fn!(proc_macro))),
+ ("proc_macro_derive", Normal, Ungated),
("rustc_copy_clone_marker", Whitelisted, Gated(Stability::Unstable,
"rustc_attrs",
("target_vendor", "cfg_target_vendor", cfg_fn!(cfg_target_vendor)),
("target_thread_local", "cfg_target_thread_local", cfg_fn!(cfg_target_thread_local)),
("target_has_atomic", "cfg_target_has_atomic", cfg_fn!(cfg_target_has_atomic)),
- ("proc_macro", "proc_macro", cfg_fn!(proc_macro)),
];
#[derive(Debug, Eq, PartialEq)]
pub fn noop_fold_tt<T: Folder>(tt: &TokenTree, fld: &mut T) -> TokenTree {
match *tt {
TokenTree::Token(span, ref tok) =>
- TokenTree::Token(span, fld.fold_token(tok.clone())),
+ TokenTree::Token(fld.new_span(span), fld.fold_token(tok.clone())),
TokenTree::Delimited(span, ref delimed) => {
- TokenTree::Delimited(span, Rc::new(
+ TokenTree::Delimited(fld.new_span(span), Rc::new(
Delimited {
delim: delimed.delim,
- open_span: delimed.open_span,
+ open_span: fld.new_span(delimed.open_span),
tts: fld.fold_tts(&delimed.tts),
- close_span: delimed.close_span,
+ close_span: fld.new_span(delimed.close_span),
}
))
},
TokenTree::Sequence(span, ref seq) =>
- TokenTree::Sequence(span,
+ TokenTree::Sequence(fld.new_span(span),
Rc::new(SequenceRepetition {
tts: fld.fold_tts(&seq.tts),
separator: seq.separator.clone().map(|tok| fld.fold_token(tok)),
inputs: inputs.move_map(|x| fld.fold_arg(x)),
output: match output {
FunctionRetTy::Ty(ty) => FunctionRetTy::Ty(fld.fold_ty(ty)),
- FunctionRetTy::Default(span) => FunctionRetTy::Default(span),
+ FunctionRetTy::Default(span) => FunctionRetTy::Default(fld.new_span(span)),
},
variadic: variadic
})
ident: fld.fold_ident(ident),
bounds: fld.fold_bounds(bounds),
default: default.map(|x| fld.fold_ty(x)),
- span: span
+ span: fld.new_span(span),
}
}
let prelude_import_meta = attr::mk_list_word_item(Symbol::intern("prelude_import"));
let list = attr::mk_list_item(Symbol::intern("feature"), vec![prelude_import_meta]);
let fake_attr = attr::mk_attr_inner(attr::mk_attr_id(), list);
- try!(s.print_attribute(&fake_attr));
+ s.print_attribute(&fake_attr)?;
// #![no_std]
let no_std_meta = attr::mk_word_item(Symbol::intern("no_std"));
let fake_attr = attr::mk_attr_inner(attr::mk_attr_id(), no_std_meta);
- try!(s.print_attribute(&fake_attr));
+ s.print_attribute(&fake_attr)?;
}
- try!(s.print_mod(&krate.module, &krate.attrs));
- try!(s.print_remaining_comments());
+ s.print_mod(&krate.module, &krate.attrs)?;
+ s.print_remaining_comments()?;
eof(&mut s.s)
}
generics: &ast::Generics)
-> String {
to_string(|s| {
- try!(s.head(""));
- try!(s.print_fn(decl, unsafety, constness, Abi::Rust, Some(name),
- generics, &ast::Visibility::Inherited));
- try!(s.end()); // Close the head box
+ s.head("")?;
+ s.print_fn(decl, unsafety, constness, Abi::Rust, Some(name),
+ generics, &ast::Visibility::Inherited)?;
+ s.end()?; // Close the head box
s.end() // Close the outer box
})
}
pub fn block_to_string(blk: &ast::Block) -> String {
to_string(|s| {
// containing cbox, will be closed by print-block at }
- try!(s.cbox(INDENT_UNIT));
+ s.cbox(INDENT_UNIT)?;
// head-ibox, will be closed by print-block after {
- try!(s.ibox(0));
+ s.ibox(0)?;
s.print_block(blk)
})
}
fn literals(&self) -> &Option<Vec<comments::Literal>>;
fn word_space(&mut self, w: &str) -> io::Result<()> {
- try!(word(self.writer(), w));
+ word(self.writer(), w)?;
space(self.writer())
}
fn hardbreak_if_not_bol(&mut self) -> io::Result<()> {
if !self.is_bol() {
- try!(hardbreak(self.writer()))
+ hardbreak(self.writer())?
}
Ok(())
}
fn commasep<T, F>(&mut self, b: Breaks, elts: &[T], mut op: F) -> io::Result<()>
where F: FnMut(&mut Self, &T) -> io::Result<()>,
{
- try!(self.rbox(0, b));
+ self.rbox(0, b)?;
let mut first = true;
for elt in elts {
- if first { first = false; } else { try!(self.word_space(",")); }
- try!(op(self, elt));
+ if first { first = false; } else { self.word_space(",")?; }
+ op(self, elt)?;
}
self.end()
}
fn maybe_print_comment(&mut self, pos: BytePos) -> io::Result<()> {
while let Some(ref cmnt) = self.next_comment() {
if cmnt.pos < pos {
- try!(self.print_comment(cmnt));
+ self.print_comment(cmnt)?;
self.cur_cmnt_and_lit().cur_cmnt += 1;
} else {
break
match cmnt.style {
comments::Mixed => {
assert_eq!(cmnt.lines.len(), 1);
- try!(zerobreak(self.writer()));
- try!(word(self.writer(), &cmnt.lines[0]));
+ zerobreak(self.writer())?;
+ word(self.writer(), &cmnt.lines[0])?;
zerobreak(self.writer())
}
comments::Isolated => {
- try!(self.hardbreak_if_not_bol());
+ self.hardbreak_if_not_bol()?;
for line in &cmnt.lines {
// Don't print empty lines because they will end up as trailing
// whitespace
if !line.is_empty() {
- try!(word(self.writer(), &line[..]));
+ word(self.writer(), &line[..])?;
}
- try!(hardbreak(self.writer()));
+ hardbreak(self.writer())?;
}
Ok(())
}
comments::Trailing => {
if !self.is_bol() {
- try!(word(self.writer(), " "));
+ word(self.writer(), " ")?;
}
if cmnt.lines.len() == 1 {
- try!(word(self.writer(), &cmnt.lines[0]));
+ word(self.writer(), &cmnt.lines[0])?;
hardbreak(self.writer())
} else {
- try!(self.ibox(0));
+ self.ibox(0)?;
for line in &cmnt.lines {
if !line.is_empty() {
- try!(word(self.writer(), &line[..]));
+ word(self.writer(), &line[..])?;
}
- try!(hardbreak(self.writer()));
+ hardbreak(self.writer())?;
}
self.end()
}
_ => false
};
if is_semi || self.is_begin() || self.is_end() {
- try!(hardbreak(self.writer()));
+ hardbreak(self.writer())?;
}
hardbreak(self.writer())
}
}
fn print_literal(&mut self, lit: &ast::Lit) -> io::Result<()> {
- try!(self.maybe_print_comment(lit.span.lo));
+ self.maybe_print_comment(lit.span.lo)?;
match self.next_lit(lit.span.lo) {
Some(ref ltrl) => {
return word(self.writer(), &(*ltrl).lit);
let mut count = 0;
for attr in attrs {
if attr.style == kind {
- try!(self.print_attribute_inline(attr, is_inline));
+ self.print_attribute_inline(attr, is_inline)?;
if is_inline {
- try!(self.nbsp());
+ self.nbsp()?;
}
count += 1;
}
}
if count > 0 && trailing_hardbreak && !is_inline {
- try!(self.hardbreak_if_not_bol());
+ self.hardbreak_if_not_bol()?;
}
Ok(())
}
fn print_attribute_inline(&mut self, attr: &ast::Attribute,
is_inline: bool) -> io::Result<()> {
if !is_inline {
- try!(self.hardbreak_if_not_bol());
+ self.hardbreak_if_not_bol()?;
}
- try!(self.maybe_print_comment(attr.span.lo));
+ self.maybe_print_comment(attr.span.lo)?;
if attr.is_sugared_doc {
- try!(word(self.writer(), &attr.value_str().unwrap().as_str()));
+ word(self.writer(), &attr.value_str().unwrap().as_str())?;
hardbreak(self.writer())
} else {
match attr.style {
- ast::AttrStyle::Inner => try!(word(self.writer(), "#![")),
- ast::AttrStyle::Outer => try!(word(self.writer(), "#[")),
+ ast::AttrStyle::Inner => word(self.writer(), "#![")?,
+ ast::AttrStyle::Outer => word(self.writer(), "#[")?,
}
- try!(self.print_meta_item(&attr.meta()));
+ self.print_meta_item(&attr.meta())?;
word(self.writer(), "]")
}
}
}
fn print_meta_item(&mut self, item: &ast::MetaItem) -> io::Result<()> {
- try!(self.ibox(INDENT_UNIT));
+ self.ibox(INDENT_UNIT)?;
match item.node {
ast::MetaItemKind::Word => {
- try!(word(self.writer(), &item.name.as_str()));
+ word(self.writer(), &item.name.as_str())?;
}
ast::MetaItemKind::NameValue(ref value) => {
- try!(self.word_space(&item.name.as_str()));
- try!(self.word_space("="));
- try!(self.print_literal(value));
+ self.word_space(&item.name.as_str())?;
+ self.word_space("=")?;
+ self.print_literal(value)?;
}
ast::MetaItemKind::List(ref items) => {
- try!(word(self.writer(), &item.name.as_str()));
- try!(self.popen());
- try!(self.commasep(Consistent,
+ word(self.writer(), &item.name.as_str())?;
+ self.popen()?;
+ self.commasep(Consistent,
&items[..],
- |s, i| s.print_meta_list_item(&i)));
- try!(self.pclose());
+ |s, i| s.print_meta_list_item(&i))?;
+ self.pclose()?;
}
}
self.end()
}
fn space_if_not_bol(&mut self) -> io::Result<()> {
- if !self.is_bol() { try!(space(self.writer())); }
+ if !self.is_bol() { space(self.writer())?; }
Ok(())
}
}
pub fn word_nbsp(&mut self, w: &str) -> io::Result<()> {
- try!(word(&mut self.s, w));
+ word(&mut self.s, w)?;
self.nbsp()
}
pub fn head(&mut self, w: &str) -> io::Result<()> {
// outer-box is consistent
- try!(self.cbox(INDENT_UNIT));
+ self.cbox(INDENT_UNIT)?;
// head-box is inconsistent
- try!(self.ibox(w.len() + 1));
+ self.ibox(w.len() + 1)?;
// keyword that starts the head
if !w.is_empty() {
- try!(self.word_nbsp(w));
+ self.word_nbsp(w)?;
}
Ok(())
}
pub fn bopen(&mut self) -> io::Result<()> {
- try!(word(&mut self.s, "{"));
+ word(&mut self.s, "{")?;
self.end() // close the head-box
}
}
pub fn bclose_maybe_open(&mut self, span: syntax_pos::Span,
indented: usize, close_box: bool) -> io::Result<()> {
- try!(self.maybe_print_comment(span.hi));
- try!(self.break_offset_if_not_bol(1, -(indented as isize)));
- try!(word(&mut self.s, "}"));
+ self.maybe_print_comment(span.hi)?;
+ self.break_offset_if_not_bol(1, -(indented as isize))?;
+ word(&mut self.s, "}")?;
if close_box {
- try!(self.end()); // close the outer-box
+ self.end()?; // close the outer-box
}
Ok(())
}
// Synthesizes a comment that was not textually present in the original source
// file.
pub fn synth_comment(&mut self, text: String) -> io::Result<()> {
- try!(word(&mut self.s, "/*"));
- try!(space(&mut self.s));
- try!(word(&mut self.s, &text[..]));
- try!(space(&mut self.s));
+ word(&mut self.s, "/*")?;
+ space(&mut self.s)?;
+ word(&mut self.s, &text[..])?;
+ space(&mut self.s)?;
word(&mut self.s, "*/")
}
F: FnMut(&mut State, &T) -> io::Result<()>,
G: FnMut(&T) -> syntax_pos::Span,
{
- try!(self.rbox(0, b));
+ self.rbox(0, b)?;
let len = elts.len();
let mut i = 0;
for elt in elts {
- try!(self.maybe_print_comment(get_span(elt).hi));
- try!(op(self, elt));
+ self.maybe_print_comment(get_span(elt).hi)?;
+ op(self, elt)?;
i += 1;
if i < len {
- try!(word(&mut self.s, ","));
- try!(self.maybe_print_trailing_comment(get_span(elt),
- Some(get_span(&elts[i]).hi)));
- try!(self.space_if_not_bol());
+ word(&mut self.s, ",")?;
+ self.maybe_print_trailing_comment(get_span(elt),
+ Some(get_span(&elts[i]).hi))?;
+ self.space_if_not_bol()?;
}
}
self.end()
pub fn print_mod(&mut self, _mod: &ast::Mod,
attrs: &[ast::Attribute]) -> io::Result<()> {
- try!(self.print_inner_attributes(attrs));
+ self.print_inner_attributes(attrs)?;
for item in &_mod.items {
- try!(self.print_item(&item));
+ self.print_item(&item)?;
}
Ok(())
}
pub fn print_foreign_mod(&mut self, nmod: &ast::ForeignMod,
attrs: &[ast::Attribute]) -> io::Result<()> {
- try!(self.print_inner_attributes(attrs));
+ self.print_inner_attributes(attrs)?;
for item in &nmod.items {
- try!(self.print_foreign_item(item));
+ self.print_foreign_item(item)?;
}
Ok(())
}
pub fn print_opt_lifetime(&mut self,
lifetime: &Option<ast::Lifetime>) -> io::Result<()> {
if let Some(l) = *lifetime {
- try!(self.print_lifetime(&l));
- try!(self.nbsp());
+ self.print_lifetime(&l)?;
+ self.nbsp()?;
}
Ok(())
}
pub fn print_type(&mut self, ty: &ast::Ty) -> io::Result<()> {
- try!(self.maybe_print_comment(ty.span.lo));
- try!(self.ibox(0));
+ self.maybe_print_comment(ty.span.lo)?;
+ self.ibox(0)?;
match ty.node {
ast::TyKind::Slice(ref ty) => {
- try!(word(&mut self.s, "["));
- try!(self.print_type(&ty));
- try!(word(&mut self.s, "]"));
+ word(&mut self.s, "[")?;
+ self.print_type(&ty)?;
+ word(&mut self.s, "]")?;
}
ast::TyKind::Ptr(ref mt) => {
- try!(word(&mut self.s, "*"));
+ word(&mut self.s, "*")?;
match mt.mutbl {
- ast::Mutability::Mutable => try!(self.word_nbsp("mut")),
- ast::Mutability::Immutable => try!(self.word_nbsp("const")),
+ ast::Mutability::Mutable => self.word_nbsp("mut")?,
+ ast::Mutability::Immutable => self.word_nbsp("const")?,
}
- try!(self.print_type(&mt.ty));
+ self.print_type(&mt.ty)?;
}
ast::TyKind::Rptr(ref lifetime, ref mt) => {
- try!(word(&mut self.s, "&"));
- try!(self.print_opt_lifetime(lifetime));
- try!(self.print_mt(mt));
+ word(&mut self.s, "&")?;
+ self.print_opt_lifetime(lifetime)?;
+ self.print_mt(mt)?;
}
ast::TyKind::Never => {
- try!(word(&mut self.s, "!"));
+ word(&mut self.s, "!")?;
},
ast::TyKind::Tup(ref elts) => {
- try!(self.popen());
- try!(self.commasep(Inconsistent, &elts[..],
- |s, ty| s.print_type(&ty)));
+ self.popen()?;
+ self.commasep(Inconsistent, &elts[..],
+ |s, ty| s.print_type(&ty))?;
if elts.len() == 1 {
- try!(word(&mut self.s, ","));
+ word(&mut self.s, ",")?;
}
- try!(self.pclose());
+ self.pclose()?;
}
ast::TyKind::Paren(ref typ) => {
- try!(self.popen());
- try!(self.print_type(&typ));
- try!(self.pclose());
+ self.popen()?;
+ self.print_type(&typ)?;
+ self.pclose()?;
}
ast::TyKind::BareFn(ref f) => {
let generics = ast::Generics {
},
span: syntax_pos::DUMMY_SP,
};
- try!(self.print_ty_fn(f.abi,
+ self.print_ty_fn(f.abi,
f.unsafety,
&f.decl,
None,
- &generics));
+ &generics)?;
}
ast::TyKind::Path(None, ref path) => {
- try!(self.print_path(path, false, 0, false));
+ self.print_path(path, false, 0, false)?;
}
ast::TyKind::Path(Some(ref qself), ref path) => {
- try!(self.print_qpath(path, qself, false))
+ self.print_qpath(path, qself, false)?
}
ast::TyKind::ObjectSum(ref ty, ref bounds) => {
- try!(self.print_type(&ty));
- try!(self.print_bounds("+", &bounds[..]));
+ self.print_type(&ty)?;
+ self.print_bounds("+", &bounds[..])?;
}
ast::TyKind::PolyTraitRef(ref bounds) => {
- try!(self.print_bounds("", &bounds[..]));
+ self.print_bounds("", &bounds[..])?;
}
ast::TyKind::ImplTrait(ref bounds) => {
- try!(self.print_bounds("impl ", &bounds[..]));
+ self.print_bounds("impl ", &bounds[..])?;
}
ast::TyKind::Array(ref ty, ref v) => {
- try!(word(&mut self.s, "["));
- try!(self.print_type(&ty));
- try!(word(&mut self.s, "; "));
- try!(self.print_expr(&v));
- try!(word(&mut self.s, "]"));
+ word(&mut self.s, "[")?;
+ self.print_type(&ty)?;
+ word(&mut self.s, "; ")?;
+ self.print_expr(&v)?;
+ word(&mut self.s, "]")?;
}
ast::TyKind::Typeof(ref e) => {
- try!(word(&mut self.s, "typeof("));
- try!(self.print_expr(&e));
- try!(word(&mut self.s, ")"));
+ word(&mut self.s, "typeof(")?;
+ self.print_expr(&e)?;
+ word(&mut self.s, ")")?;
}
ast::TyKind::Infer => {
- try!(word(&mut self.s, "_"));
+ word(&mut self.s, "_")?;
}
ast::TyKind::ImplicitSelf => {
- try!(word(&mut self.s, "Self"));
+ word(&mut self.s, "Self")?;
}
ast::TyKind::Mac(ref m) => {
- try!(self.print_mac(m, token::Paren));
+ self.print_mac(m, token::Paren)?;
}
}
self.end()
pub fn print_foreign_item(&mut self,
item: &ast::ForeignItem) -> io::Result<()> {
- try!(self.hardbreak_if_not_bol());
- try!(self.maybe_print_comment(item.span.lo));
- try!(self.print_outer_attributes(&item.attrs));
+ self.hardbreak_if_not_bol()?;
+ self.maybe_print_comment(item.span.lo)?;
+ self.print_outer_attributes(&item.attrs)?;
match item.node {
ast::ForeignItemKind::Fn(ref decl, ref generics) => {
- try!(self.head(""));
- try!(self.print_fn(decl, ast::Unsafety::Normal,
+ self.head("")?;
+ self.print_fn(decl, ast::Unsafety::Normal,
ast::Constness::NotConst,
Abi::Rust, Some(item.ident),
- generics, &item.vis));
- try!(self.end()); // end head-ibox
- try!(word(&mut self.s, ";"));
+ generics, &item.vis)?;
+ self.end()?; // end head-ibox
+ word(&mut self.s, ";")?;
self.end() // end the outer fn box
}
ast::ForeignItemKind::Static(ref t, m) => {
- try!(self.head(&visibility_qualified(&item.vis, "static")));
+ self.head(&visibility_qualified(&item.vis, "static"))?;
if m {
- try!(self.word_space("mut"));
+ self.word_space("mut")?;
}
- try!(self.print_ident(item.ident));
- try!(self.word_space(":"));
- try!(self.print_type(&t));
- try!(word(&mut self.s, ";"));
- try!(self.end()); // end the head-ibox
+ self.print_ident(item.ident)?;
+ self.word_space(":")?;
+ self.print_type(&t)?;
+ word(&mut self.s, ";")?;
+ self.end()?; // end the head-ibox
self.end() // end the outer cbox
}
}
vis: &ast::Visibility)
-> io::Result<()>
{
- try!(word(&mut self.s, &visibility_qualified(vis, "")));
- try!(self.word_space("const"));
- try!(self.print_ident(ident));
- try!(self.word_space(":"));
- try!(self.print_type(ty));
+ word(&mut self.s, &visibility_qualified(vis, ""))?;
+ self.word_space("const")?;
+ self.print_ident(ident)?;
+ self.word_space(":")?;
+ self.print_type(ty)?;
if let Some(expr) = default {
- try!(space(&mut self.s));
- try!(self.word_space("="));
- try!(self.print_expr(expr));
+ space(&mut self.s)?;
+ self.word_space("=")?;
+ self.print_expr(expr)?;
}
word(&mut self.s, ";")
}
bounds: Option<&ast::TyParamBounds>,
ty: Option<&ast::Ty>)
-> io::Result<()> {
- try!(self.word_space("type"));
- try!(self.print_ident(ident));
+ self.word_space("type")?;
+ self.print_ident(ident)?;
if let Some(bounds) = bounds {
- try!(self.print_bounds(":", bounds));
+ self.print_bounds(":", bounds)?;
}
if let Some(ty) = ty {
- try!(space(&mut self.s));
- try!(self.word_space("="));
- try!(self.print_type(ty));
+ space(&mut self.s)?;
+ self.word_space("=")?;
+ self.print_type(ty)?;
}
word(&mut self.s, ";")
}
/// Pretty-print an item
pub fn print_item(&mut self, item: &ast::Item) -> io::Result<()> {
- try!(self.hardbreak_if_not_bol());
- try!(self.maybe_print_comment(item.span.lo));
- try!(self.print_outer_attributes(&item.attrs));
- try!(self.ann.pre(self, NodeItem(item)));
+ self.hardbreak_if_not_bol()?;
+ self.maybe_print_comment(item.span.lo)?;
+ self.print_outer_attributes(&item.attrs)?;
+ self.ann.pre(self, NodeItem(item))?;
match item.node {
ast::ItemKind::ExternCrate(ref optional_path) => {
- try!(self.head(&visibility_qualified(&item.vis, "extern crate")));
+ self.head(&visibility_qualified(&item.vis, "extern crate"))?;
if let Some(p) = *optional_path {
let val = p.as_str();
if val.contains("-") {
- try!(self.print_string(&val, ast::StrStyle::Cooked));
+ self.print_string(&val, ast::StrStyle::Cooked)?;
} else {
- try!(self.print_name(p));
+ self.print_name(p)?;
}
- try!(space(&mut self.s));
- try!(word(&mut self.s, "as"));
- try!(space(&mut self.s));
+ space(&mut self.s)?;
+ word(&mut self.s, "as")?;
+ space(&mut self.s)?;
}
- try!(self.print_ident(item.ident));
- try!(word(&mut self.s, ";"));
- try!(self.end()); // end inner head-block
- try!(self.end()); // end outer head-block
+ self.print_ident(item.ident)?;
+ word(&mut self.s, ";")?;
+ self.end()?; // end inner head-block
+ self.end()?; // end outer head-block
}
ast::ItemKind::Use(ref vp) => {
- try!(self.head(&visibility_qualified(&item.vis, "use")));
- try!(self.print_view_path(&vp));
- try!(word(&mut self.s, ";"));
- try!(self.end()); // end inner head-block
- try!(self.end()); // end outer head-block
+ self.head(&visibility_qualified(&item.vis, "use"))?;
+ self.print_view_path(&vp)?;
+ word(&mut self.s, ";")?;
+ self.end()?; // end inner head-block
+ self.end()?; // end outer head-block
}
ast::ItemKind::Static(ref ty, m, ref expr) => {
- try!(self.head(&visibility_qualified(&item.vis, "static")));
+ self.head(&visibility_qualified(&item.vis, "static"))?;
if m == ast::Mutability::Mutable {
- try!(self.word_space("mut"));
+ self.word_space("mut")?;
}
- try!(self.print_ident(item.ident));
- try!(self.word_space(":"));
- try!(self.print_type(&ty));
- try!(space(&mut self.s));
- try!(self.end()); // end the head-ibox
+ self.print_ident(item.ident)?;
+ self.word_space(":")?;
+ self.print_type(&ty)?;
+ space(&mut self.s)?;
+ self.end()?; // end the head-ibox
- try!(self.word_space("="));
- try!(self.print_expr(&expr));
- try!(word(&mut self.s, ";"));
- try!(self.end()); // end the outer cbox
+ self.word_space("=")?;
+ self.print_expr(&expr)?;
+ word(&mut self.s, ";")?;
+ self.end()?; // end the outer cbox
}
ast::ItemKind::Const(ref ty, ref expr) => {
- try!(self.head(&visibility_qualified(&item.vis, "const")));
- try!(self.print_ident(item.ident));
- try!(self.word_space(":"));
- try!(self.print_type(&ty));
- try!(space(&mut self.s));
- try!(self.end()); // end the head-ibox
-
- try!(self.word_space("="));
- try!(self.print_expr(&expr));
- try!(word(&mut self.s, ";"));
- try!(self.end()); // end the outer cbox
+ self.head(&visibility_qualified(&item.vis, "const"))?;
+ self.print_ident(item.ident)?;
+ self.word_space(":")?;
+ self.print_type(&ty)?;
+ space(&mut self.s)?;
+ self.end()?; // end the head-ibox
+
+ self.word_space("=")?;
+ self.print_expr(&expr)?;
+ word(&mut self.s, ";")?;
+ self.end()?; // end the outer cbox
}
ast::ItemKind::Fn(ref decl, unsafety, constness, abi, ref typarams, ref body) => {
- try!(self.head(""));
- try!(self.print_fn(
+ self.head("")?;
+ self.print_fn(
decl,
unsafety,
constness.node,
Some(item.ident),
typarams,
&item.vis
- ));
- try!(word(&mut self.s, " "));
- try!(self.print_block_with_attrs(&body, &item.attrs));
+ )?;
+ word(&mut self.s, " ")?;
+ self.print_block_with_attrs(&body, &item.attrs)?;
}
ast::ItemKind::Mod(ref _mod) => {
- try!(self.head(&visibility_qualified(&item.vis, "mod")));
- try!(self.print_ident(item.ident));
- try!(self.nbsp());
- try!(self.bopen());
- try!(self.print_mod(_mod, &item.attrs));
- try!(self.bclose(item.span));
+ self.head(&visibility_qualified(&item.vis, "mod"))?;
+ self.print_ident(item.ident)?;
+ self.nbsp()?;
+ self.bopen()?;
+ self.print_mod(_mod, &item.attrs)?;
+ self.bclose(item.span)?;
}
ast::ItemKind::ForeignMod(ref nmod) => {
- try!(self.head("extern"));
- try!(self.word_nbsp(&nmod.abi.to_string()));
- try!(self.bopen());
- try!(self.print_foreign_mod(nmod, &item.attrs));
- try!(self.bclose(item.span));
+ self.head("extern")?;
+ self.word_nbsp(&nmod.abi.to_string())?;
+ self.bopen()?;
+ self.print_foreign_mod(nmod, &item.attrs)?;
+ self.bclose(item.span)?;
}
ast::ItemKind::Ty(ref ty, ref params) => {
- try!(self.ibox(INDENT_UNIT));
- try!(self.ibox(0));
- try!(self.word_nbsp(&visibility_qualified(&item.vis, "type")));
- try!(self.print_ident(item.ident));
- try!(self.print_generics(params));
- try!(self.end()); // end the inner ibox
-
- try!(self.print_where_clause(¶ms.where_clause));
- try!(space(&mut self.s));
- try!(self.word_space("="));
- try!(self.print_type(&ty));
- try!(word(&mut self.s, ";"));
- try!(self.end()); // end the outer ibox
+ self.ibox(INDENT_UNIT)?;
+ self.ibox(0)?;
+ self.word_nbsp(&visibility_qualified(&item.vis, "type"))?;
+ self.print_ident(item.ident)?;
+ self.print_generics(params)?;
+ self.end()?; // end the inner ibox
+
+ self.print_where_clause(¶ms.where_clause)?;
+ space(&mut self.s)?;
+ self.word_space("=")?;
+ self.print_type(&ty)?;
+ word(&mut self.s, ";")?;
+ self.end()?; // end the outer ibox
}
ast::ItemKind::Enum(ref enum_definition, ref params) => {
- try!(self.print_enum_def(
+ self.print_enum_def(
enum_definition,
params,
item.ident,
item.span,
&item.vis
- ));
+ )?;
}
ast::ItemKind::Struct(ref struct_def, ref generics) => {
- try!(self.head(&visibility_qualified(&item.vis, "struct")));
- try!(self.print_struct(&struct_def, generics, item.ident, item.span, true));
+ self.head(&visibility_qualified(&item.vis, "struct"))?;
+ self.print_struct(&struct_def, generics, item.ident, item.span, true)?;
}
ast::ItemKind::Union(ref struct_def, ref generics) => {
- try!(self.head(&visibility_qualified(&item.vis, "union")));
- try!(self.print_struct(&struct_def, generics, item.ident, item.span, true));
+ self.head(&visibility_qualified(&item.vis, "union"))?;
+ self.print_struct(&struct_def, generics, item.ident, item.span, true)?;
}
ast::ItemKind::DefaultImpl(unsafety, ref trait_ref) => {
- try!(self.head(""));
- try!(self.print_visibility(&item.vis));
- try!(self.print_unsafety(unsafety));
- try!(self.word_nbsp("impl"));
- try!(self.print_trait_ref(trait_ref));
- try!(space(&mut self.s));
- try!(self.word_space("for"));
- try!(self.word_space(".."));
- try!(self.bopen());
- try!(self.bclose(item.span));
+ self.head("")?;
+ self.print_visibility(&item.vis)?;
+ self.print_unsafety(unsafety)?;
+ self.word_nbsp("impl")?;
+ self.print_trait_ref(trait_ref)?;
+ space(&mut self.s)?;
+ self.word_space("for")?;
+ self.word_space("..")?;
+ self.bopen()?;
+ self.bclose(item.span)?;
}
ast::ItemKind::Impl(unsafety,
polarity,
ref opt_trait,
ref ty,
ref impl_items) => {
- try!(self.head(""));
- try!(self.print_visibility(&item.vis));
- try!(self.print_unsafety(unsafety));
- try!(self.word_nbsp("impl"));
+ self.head("")?;
+ self.print_visibility(&item.vis)?;
+ self.print_unsafety(unsafety)?;
+ self.word_nbsp("impl")?;
if generics.is_parameterized() {
- try!(self.print_generics(generics));
- try!(space(&mut self.s));
+ self.print_generics(generics)?;
+ space(&mut self.s)?;
}
match polarity {
ast::ImplPolarity::Negative => {
- try!(word(&mut self.s, "!"));
+ word(&mut self.s, "!")?;
},
_ => {}
}
if let Some(ref t) = *opt_trait {
- try!(self.print_trait_ref(t));
- try!(space(&mut self.s));
- try!(self.word_space("for"));
+ self.print_trait_ref(t)?;
+ space(&mut self.s)?;
+ self.word_space("for")?;
}
- try!(self.print_type(&ty));
- try!(self.print_where_clause(&generics.where_clause));
+ self.print_type(&ty)?;
+ self.print_where_clause(&generics.where_clause)?;
- try!(space(&mut self.s));
- try!(self.bopen());
- try!(self.print_inner_attributes(&item.attrs));
+ space(&mut self.s)?;
+ self.bopen()?;
+ self.print_inner_attributes(&item.attrs)?;
for impl_item in impl_items {
- try!(self.print_impl_item(impl_item));
+ self.print_impl_item(impl_item)?;
}
- try!(self.bclose(item.span));
+ self.bclose(item.span)?;
}
ast::ItemKind::Trait(unsafety, ref generics, ref bounds, ref trait_items) => {
- try!(self.head(""));
- try!(self.print_visibility(&item.vis));
- try!(self.print_unsafety(unsafety));
- try!(self.word_nbsp("trait"));
- try!(self.print_ident(item.ident));
- try!(self.print_generics(generics));
+ self.head("")?;
+ self.print_visibility(&item.vis)?;
+ self.print_unsafety(unsafety)?;
+ self.word_nbsp("trait")?;
+ self.print_ident(item.ident)?;
+ self.print_generics(generics)?;
let mut real_bounds = Vec::with_capacity(bounds.len());
for b in bounds.iter() {
if let TraitTyParamBound(ref ptr, ast::TraitBoundModifier::Maybe) = *b {
- try!(space(&mut self.s));
- try!(self.word_space("for ?"));
- try!(self.print_trait_ref(&ptr.trait_ref));
+ space(&mut self.s)?;
+ self.word_space("for ?")?;
+ self.print_trait_ref(&ptr.trait_ref)?;
} else {
real_bounds.push(b.clone());
}
}
- try!(self.print_bounds(":", &real_bounds[..]));
- try!(self.print_where_clause(&generics.where_clause));
- try!(word(&mut self.s, " "));
- try!(self.bopen());
+ self.print_bounds(":", &real_bounds[..])?;
+ self.print_where_clause(&generics.where_clause)?;
+ word(&mut self.s, " ")?;
+ self.bopen()?;
for trait_item in trait_items {
- try!(self.print_trait_item(trait_item));
+ self.print_trait_item(trait_item)?;
}
- try!(self.bclose(item.span));
+ self.bclose(item.span)?;
}
ast::ItemKind::Mac(codemap::Spanned { ref node, .. }) => {
- try!(self.print_visibility(&item.vis));
- try!(self.print_path(&node.path, false, 0, false));
- try!(word(&mut self.s, "! "));
- try!(self.print_ident(item.ident));
- try!(self.cbox(INDENT_UNIT));
- try!(self.popen());
- try!(self.print_tts(&node.tts[..]));
- try!(self.pclose());
- try!(word(&mut self.s, ";"));
- try!(self.end());
+ self.print_visibility(&item.vis)?;
+ self.print_path(&node.path, false, 0, false)?;
+ word(&mut self.s, "! ")?;
+ self.print_ident(item.ident)?;
+ self.cbox(INDENT_UNIT)?;
+ self.popen()?;
+ self.print_tts(&node.tts[..])?;
+ self.pclose()?;
+ word(&mut self.s, ";")?;
+ self.end()?;
}
}
self.ann.post(self, NodeItem(item))
fn print_formal_lifetime_list(&mut self, lifetimes: &[ast::LifetimeDef]) -> io::Result<()> {
if !lifetimes.is_empty() {
- try!(word(&mut self.s, "for<"));
+ word(&mut self.s, "for<")?;
let mut comma = false;
for lifetime_def in lifetimes {
if comma {
- try!(self.word_space(","))
+ self.word_space(",")?
}
- try!(self.print_outer_attributes_inline(&lifetime_def.attrs));
- try!(self.print_lifetime_bounds(&lifetime_def.lifetime, &lifetime_def.bounds));
+ self.print_outer_attributes_inline(&lifetime_def.attrs)?;
+ self.print_lifetime_bounds(&lifetime_def.lifetime, &lifetime_def.bounds)?;
comma = true;
}
- try!(word(&mut self.s, ">"));
+ word(&mut self.s, ">")?;
}
Ok(())
}
fn print_poly_trait_ref(&mut self, t: &ast::PolyTraitRef) -> io::Result<()> {
- try!(self.print_formal_lifetime_list(&t.bound_lifetimes));
+ self.print_formal_lifetime_list(&t.bound_lifetimes)?;
self.print_trait_ref(&t.trait_ref)
}
generics: &ast::Generics, ident: ast::Ident,
span: syntax_pos::Span,
visibility: &ast::Visibility) -> io::Result<()> {
- try!(self.head(&visibility_qualified(visibility, "enum")));
- try!(self.print_ident(ident));
- try!(self.print_generics(generics));
- try!(self.print_where_clause(&generics.where_clause));
- try!(space(&mut self.s));
+ self.head(&visibility_qualified(visibility, "enum"))?;
+ self.print_ident(ident)?;
+ self.print_generics(generics)?;
+ self.print_where_clause(&generics.where_clause)?;
+ space(&mut self.s)?;
self.print_variants(&enum_definition.variants, span)
}
pub fn print_variants(&mut self,
variants: &[ast::Variant],
span: syntax_pos::Span) -> io::Result<()> {
- try!(self.bopen());
+ self.bopen()?;
for v in variants {
- try!(self.space_if_not_bol());
- try!(self.maybe_print_comment(v.span.lo));
- try!(self.print_outer_attributes(&v.node.attrs));
- try!(self.ibox(INDENT_UNIT));
- try!(self.print_variant(v));
- try!(word(&mut self.s, ","));
- try!(self.end());
- try!(self.maybe_print_trailing_comment(v.span, None));
+ self.space_if_not_bol()?;
+ self.maybe_print_comment(v.span.lo)?;
+ self.print_outer_attributes(&v.node.attrs)?;
+ self.ibox(INDENT_UNIT)?;
+ self.print_variant(v)?;
+ word(&mut self.s, ",")?;
+ self.end()?;
+ self.maybe_print_trailing_comment(v.span, None)?;
}
self.bclose(span)
}
ident: ast::Ident,
span: syntax_pos::Span,
print_finalizer: bool) -> io::Result<()> {
- try!(self.print_ident(ident));
- try!(self.print_generics(generics));
+ self.print_ident(ident)?;
+ self.print_generics(generics)?;
if !struct_def.is_struct() {
if struct_def.is_tuple() {
- try!(self.popen());
- try!(self.commasep(
+ self.popen()?;
+ self.commasep(
Inconsistent, struct_def.fields(),
|s, field| {
- try!(s.maybe_print_comment(field.span.lo));
- try!(s.print_outer_attributes(&field.attrs));
- try!(s.print_visibility(&field.vis));
+ s.maybe_print_comment(field.span.lo)?;
+ s.print_outer_attributes(&field.attrs)?;
+ s.print_visibility(&field.vis)?;
s.print_type(&field.ty)
}
- ));
- try!(self.pclose());
+ )?;
+ self.pclose()?;
}
- try!(self.print_where_clause(&generics.where_clause));
+ self.print_where_clause(&generics.where_clause)?;
if print_finalizer {
- try!(word(&mut self.s, ";"));
+ word(&mut self.s, ";")?;
}
- try!(self.end());
+ self.end()?;
self.end() // close the outer-box
} else {
- try!(self.print_where_clause(&generics.where_clause));
- try!(self.nbsp());
- try!(self.bopen());
- try!(self.hardbreak_if_not_bol());
+ self.print_where_clause(&generics.where_clause)?;
+ self.nbsp()?;
+ self.bopen()?;
+ self.hardbreak_if_not_bol()?;
for field in struct_def.fields() {
- try!(self.hardbreak_if_not_bol());
- try!(self.maybe_print_comment(field.span.lo));
- try!(self.print_outer_attributes(&field.attrs));
- try!(self.print_visibility(&field.vis));
- try!(self.print_ident(field.ident.unwrap()));
- try!(self.word_nbsp(":"));
- try!(self.print_type(&field.ty));
- try!(word(&mut self.s, ","));
+ self.hardbreak_if_not_bol()?;
+ self.maybe_print_comment(field.span.lo)?;
+ self.print_outer_attributes(&field.attrs)?;
+ self.print_visibility(&field.vis)?;
+ self.print_ident(field.ident.unwrap())?;
+ self.word_nbsp(":")?;
+ self.print_type(&field.ty)?;
+ word(&mut self.s, ",")?;
}
self.bclose(span)
pub fn print_tt(&mut self, tt: &tokenstream::TokenTree) -> io::Result<()> {
match *tt {
TokenTree::Token(_, ref tk) => {
- try!(word(&mut self.s, &token_to_string(tk)));
+ word(&mut self.s, &token_to_string(tk))?;
match *tk {
parse::token::DocComment(..) => {
hardbreak(&mut self.s)
}
}
TokenTree::Delimited(_, ref delimed) => {
- try!(word(&mut self.s, &token_to_string(&delimed.open_token())));
- try!(space(&mut self.s));
- try!(self.print_tts(&delimed.tts));
- try!(space(&mut self.s));
+ word(&mut self.s, &token_to_string(&delimed.open_token()))?;
+ space(&mut self.s)?;
+ self.print_tts(&delimed.tts)?;
+ space(&mut self.s)?;
word(&mut self.s, &token_to_string(&delimed.close_token()))
},
TokenTree::Sequence(_, ref seq) => {
- try!(word(&mut self.s, "$("));
+ word(&mut self.s, "$(")?;
for tt_elt in &seq.tts {
- try!(self.print_tt(tt_elt));
+ self.print_tt(tt_elt)?;
}
- try!(word(&mut self.s, ")"));
+ word(&mut self.s, ")")?;
if let Some(ref tk) = seq.separator {
- try!(word(&mut self.s, &token_to_string(tk)));
+ word(&mut self.s, &token_to_string(tk))?;
}
match seq.op {
tokenstream::KleeneOp::ZeroOrMore => word(&mut self.s, "*"),
}
pub fn print_tts(&mut self, tts: &[tokenstream::TokenTree]) -> io::Result<()> {
- try!(self.ibox(0));
+ self.ibox(0)?;
for (i, tt) in tts.iter().enumerate() {
if i != 0 {
- try!(space(&mut self.s));
+ space(&mut self.s)?;
}
- try!(self.print_tt(tt));
+ self.print_tt(tt)?;
}
self.end()
}
pub fn print_variant(&mut self, v: &ast::Variant) -> io::Result<()> {
- try!(self.head(""));
+ self.head("")?;
let generics = ast::Generics::default();
- try!(self.print_struct(&v.node.data, &generics, v.node.name, v.span, false));
+ self.print_struct(&v.node.data, &generics, v.node.name, v.span, false)?;
match v.node.disr_expr {
Some(ref d) => {
- try!(space(&mut self.s));
- try!(self.word_space("="));
+ space(&mut self.s)?;
+ self.word_space("=")?;
self.print_expr(&d)
}
_ => Ok(())
pub fn print_trait_item(&mut self, ti: &ast::TraitItem)
-> io::Result<()> {
- try!(self.ann.pre(self, NodeSubItem(ti.id)));
- try!(self.hardbreak_if_not_bol());
- try!(self.maybe_print_comment(ti.span.lo));
- try!(self.print_outer_attributes(&ti.attrs));
+ self.ann.pre(self, NodeSubItem(ti.id))?;
+ self.hardbreak_if_not_bol()?;
+ self.maybe_print_comment(ti.span.lo)?;
+ self.print_outer_attributes(&ti.attrs)?;
match ti.node {
ast::TraitItemKind::Const(ref ty, ref default) => {
- try!(self.print_associated_const(ti.ident, &ty,
+ self.print_associated_const(ti.ident, &ty,
default.as_ref().map(|expr| &**expr),
- &ast::Visibility::Inherited));
+ &ast::Visibility::Inherited)?;
}
ast::TraitItemKind::Method(ref sig, ref body) => {
if body.is_some() {
- try!(self.head(""));
+ self.head("")?;
}
- try!(self.print_method_sig(ti.ident, sig, &ast::Visibility::Inherited));
+ self.print_method_sig(ti.ident, sig, &ast::Visibility::Inherited)?;
if let Some(ref body) = *body {
- try!(self.nbsp());
- try!(self.print_block_with_attrs(body, &ti.attrs));
+ self.nbsp()?;
+ self.print_block_with_attrs(body, &ti.attrs)?;
} else {
- try!(word(&mut self.s, ";"));
+ word(&mut self.s, ";")?;
}
}
ast::TraitItemKind::Type(ref bounds, ref default) => {
- try!(self.print_associated_type(ti.ident, Some(bounds),
- default.as_ref().map(|ty| &**ty)));
+ self.print_associated_type(ti.ident, Some(bounds),
+ default.as_ref().map(|ty| &**ty))?;
}
ast::TraitItemKind::Macro(codemap::Spanned { ref node, .. }) => {
// code copied from ItemKind::Mac:
}
pub fn print_impl_item(&mut self, ii: &ast::ImplItem) -> io::Result<()> {
- try!(self.ann.pre(self, NodeSubItem(ii.id)));
- try!(self.hardbreak_if_not_bol());
- try!(self.maybe_print_comment(ii.span.lo));
- try!(self.print_outer_attributes(&ii.attrs));
+ self.ann.pre(self, NodeSubItem(ii.id))?;
+ self.hardbreak_if_not_bol()?;
+ self.maybe_print_comment(ii.span.lo)?;
+ self.print_outer_attributes(&ii.attrs)?;
if let ast::Defaultness::Default = ii.defaultness {
- try!(self.word_nbsp("default"));
+ self.word_nbsp("default")?;
}
match ii.node {
ast::ImplItemKind::Const(ref ty, ref expr) => {
- try!(self.print_associated_const(ii.ident, &ty, Some(&expr), &ii.vis));
+ self.print_associated_const(ii.ident, &ty, Some(&expr), &ii.vis)?;
}
ast::ImplItemKind::Method(ref sig, ref body) => {
- try!(self.head(""));
- try!(self.print_method_sig(ii.ident, sig, &ii.vis));
- try!(self.nbsp());
- try!(self.print_block_with_attrs(body, &ii.attrs));
+ self.head("")?;
+ self.print_method_sig(ii.ident, sig, &ii.vis)?;
+ self.nbsp()?;
+ self.print_block_with_attrs(body, &ii.attrs)?;
}
ast::ImplItemKind::Type(ref ty) => {
- try!(self.print_associated_type(ii.ident, None, Some(ty)));
+ self.print_associated_type(ii.ident, None, Some(ty))?;
}
ast::ImplItemKind::Macro(codemap::Spanned { ref node, .. }) => {
// code copied from ItemKind::Mac:
- try!(self.print_path(&node.path, false, 0, false));
- try!(word(&mut self.s, "! "));
- try!(self.cbox(INDENT_UNIT));
- try!(self.popen());
- try!(self.print_tts(&node.tts[..]));
- try!(self.pclose());
- try!(word(&mut self.s, ";"));
- try!(self.end())
+ self.print_path(&node.path, false, 0, false)?;
+ word(&mut self.s, "! ")?;
+ self.cbox(INDENT_UNIT)?;
+ self.popen()?;
+ self.print_tts(&node.tts[..])?;
+ self.pclose()?;
+ word(&mut self.s, ";")?;
+ self.end()?
}
}
self.ann.post(self, NodeSubItem(ii.id))
}
pub fn print_stmt(&mut self, st: &ast::Stmt) -> io::Result<()> {
- try!(self.maybe_print_comment(st.span.lo));
+ self.maybe_print_comment(st.span.lo)?;
match st.node {
ast::StmtKind::Local(ref loc) => {
- try!(self.print_outer_attributes(&loc.attrs));
- try!(self.space_if_not_bol());
- try!(self.ibox(INDENT_UNIT));
- try!(self.word_nbsp("let"));
-
- try!(self.ibox(INDENT_UNIT));
- try!(self.print_local_decl(&loc));
- try!(self.end());
+ self.print_outer_attributes(&loc.attrs)?;
+ self.space_if_not_bol()?;
+ self.ibox(INDENT_UNIT)?;
+ self.word_nbsp("let")?;
+
+ self.ibox(INDENT_UNIT)?;
+ self.print_local_decl(&loc)?;
+ self.end()?;
if let Some(ref init) = loc.init {
- try!(self.nbsp());
- try!(self.word_space("="));
- try!(self.print_expr(&init));
+ self.nbsp()?;
+ self.word_space("=")?;
+ self.print_expr(&init)?;
}
- try!(word(&mut self.s, ";"));
+ word(&mut self.s, ";")?;
self.end()?;
}
ast::StmtKind::Item(ref item) => self.print_item(&item)?,
ast::StmtKind::Expr(ref expr) => {
- try!(self.space_if_not_bol());
- try!(self.print_expr_outer_attr_style(&expr, false));
+ self.space_if_not_bol()?;
+ self.print_expr_outer_attr_style(&expr, false)?;
if parse::classify::expr_requires_semi_to_be_stmt(expr) {
- try!(word(&mut self.s, ";"));
+ word(&mut self.s, ";")?;
}
}
ast::StmtKind::Semi(ref expr) => {
- try!(self.space_if_not_bol());
- try!(self.print_expr_outer_attr_style(&expr, false));
- try!(word(&mut self.s, ";"));
+ self.space_if_not_bol()?;
+ self.print_expr_outer_attr_style(&expr, false)?;
+ word(&mut self.s, ";")?;
}
ast::StmtKind::Mac(ref mac) => {
let (ref mac, style, ref attrs) = **mac;
- try!(self.space_if_not_bol());
- try!(self.print_outer_attributes(&attrs));
+ self.space_if_not_bol()?;
+ self.print_outer_attributes(&attrs)?;
let delim = match style {
ast::MacStmtStyle::Braces => token::Brace,
_ => token::Paren
};
- try!(self.print_mac(&mac, delim));
+ self.print_mac(&mac, delim)?;
if style == ast::MacStmtStyle::Semicolon {
- try!(word(&mut self.s, ";"));
+ word(&mut self.s, ";")?;
}
}
}
attrs: &[ast::Attribute],
close_box: bool) -> io::Result<()> {
match blk.rules {
- BlockCheckMode::Unsafe(..) => try!(self.word_space("unsafe")),
+ BlockCheckMode::Unsafe(..) => self.word_space("unsafe")?,
BlockCheckMode::Default => ()
}
- try!(self.maybe_print_comment(blk.span.lo));
- try!(self.ann.pre(self, NodeBlock(blk)));
- try!(self.bopen());
+ self.maybe_print_comment(blk.span.lo)?;
+ self.ann.pre(self, NodeBlock(blk))?;
+ self.bopen()?;
- try!(self.print_inner_attributes(attrs));
+ self.print_inner_attributes(attrs)?;
for (i, st) in blk.stmts.iter().enumerate() {
match st.node {
ast::StmtKind::Expr(ref expr) if i == blk.stmts.len() - 1 => {
- try!(self.maybe_print_comment(st.span.lo));
- try!(self.space_if_not_bol());
- try!(self.print_expr_outer_attr_style(&expr, false));
- try!(self.maybe_print_trailing_comment(expr.span, Some(blk.span.hi)));
+ self.maybe_print_comment(st.span.lo)?;
+ self.space_if_not_bol()?;
+ self.print_expr_outer_attr_style(&expr, false)?;
+ self.maybe_print_trailing_comment(expr.span, Some(blk.span.hi))?;
}
- _ => try!(self.print_stmt(st)),
+ _ => self.print_stmt(st)?,
}
}
- try!(self.bclose_maybe_open(blk.span, indented, close_box));
+ self.bclose_maybe_open(blk.span, indented, close_box)?;
self.ann.post(self, NodeBlock(blk))
}
match _else.node {
// "another else-if"
ast::ExprKind::If(ref i, ref then, ref e) => {
- try!(self.cbox(INDENT_UNIT - 1));
- try!(self.ibox(0));
- try!(word(&mut self.s, " else if "));
- try!(self.print_expr(&i));
- try!(space(&mut self.s));
- try!(self.print_block(&then));
+ self.cbox(INDENT_UNIT - 1)?;
+ self.ibox(0)?;
+ word(&mut self.s, " else if ")?;
+ self.print_expr(&i)?;
+ space(&mut self.s)?;
+ self.print_block(&then)?;
self.print_else(e.as_ref().map(|e| &**e))
}
// "another else-if-let"
ast::ExprKind::IfLet(ref pat, ref expr, ref then, ref e) => {
- try!(self.cbox(INDENT_UNIT - 1));
- try!(self.ibox(0));
- try!(word(&mut self.s, " else if let "));
- try!(self.print_pat(&pat));
- try!(space(&mut self.s));
- try!(self.word_space("="));
- try!(self.print_expr(&expr));
- try!(space(&mut self.s));
- try!(self.print_block(&then));
+ self.cbox(INDENT_UNIT - 1)?;
+ self.ibox(0)?;
+ word(&mut self.s, " else if let ")?;
+ self.print_pat(&pat)?;
+ space(&mut self.s)?;
+ self.word_space("=")?;
+ self.print_expr(&expr)?;
+ space(&mut self.s)?;
+ self.print_block(&then)?;
self.print_else(e.as_ref().map(|e| &**e))
}
// "final else"
ast::ExprKind::Block(ref b) => {
- try!(self.cbox(INDENT_UNIT - 1));
- try!(self.ibox(0));
- try!(word(&mut self.s, " else "));
+ self.cbox(INDENT_UNIT - 1)?;
+ self.ibox(0)?;
+ word(&mut self.s, " else ")?;
self.print_block(&b)
}
// BLEAH, constraints would be great here
pub fn print_if(&mut self, test: &ast::Expr, blk: &ast::Block,
elseopt: Option<&ast::Expr>) -> io::Result<()> {
- try!(self.head("if"));
- try!(self.print_expr(test));
- try!(space(&mut self.s));
- try!(self.print_block(blk));
+ self.head("if")?;
+ self.print_expr(test)?;
+ space(&mut self.s)?;
+ self.print_block(blk)?;
self.print_else(elseopt)
}
pub fn print_if_let(&mut self, pat: &ast::Pat, expr: &ast::Expr, blk: &ast::Block,
elseopt: Option<&ast::Expr>) -> io::Result<()> {
- try!(self.head("if let"));
- try!(self.print_pat(pat));
- try!(space(&mut self.s));
- try!(self.word_space("="));
- try!(self.print_expr(expr));
- try!(space(&mut self.s));
- try!(self.print_block(blk));
+ self.head("if let")?;
+ self.print_pat(pat)?;
+ space(&mut self.s)?;
+ self.word_space("=")?;
+ self.print_expr(expr)?;
+ space(&mut self.s)?;
+ self.print_block(blk)?;
self.print_else(elseopt)
}
pub fn print_mac(&mut self, m: &ast::Mac, delim: token::DelimToken)
-> io::Result<()> {
- try!(self.print_path(&m.node.path, false, 0, false));
- try!(word(&mut self.s, "!"));
+ self.print_path(&m.node.path, false, 0, false)?;
+ word(&mut self.s, "!")?;
match delim {
- token::Paren => try!(self.popen()),
- token::Bracket => try!(word(&mut self.s, "[")),
+ token::Paren => self.popen()?,
+ token::Bracket => word(&mut self.s, "[")?,
token::Brace => {
- try!(self.head(""));
- try!(self.bopen());
+ self.head("")?;
+ self.bopen()?;
}
token::NoDelim => {}
}
- try!(self.print_tts(&m.node.tts));
+ self.print_tts(&m.node.tts)?;
match delim {
token::Paren => self.pclose(),
token::Bracket => word(&mut self.s, "]"),
fn print_call_post(&mut self, args: &[P<ast::Expr>]) -> io::Result<()> {
- try!(self.popen());
- try!(self.commasep_exprs(Inconsistent, args));
+ self.popen()?;
+ self.commasep_exprs(Inconsistent, args)?;
self.pclose()
}
pub fn print_expr_maybe_paren(&mut self, expr: &ast::Expr) -> io::Result<()> {
let needs_par = needs_parentheses(expr);
if needs_par {
- try!(self.popen());
+ self.popen()?;
}
- try!(self.print_expr(expr));
+ self.print_expr(expr)?;
if needs_par {
- try!(self.pclose());
+ self.pclose()?;
}
Ok(())
}
fn print_expr_in_place(&mut self,
place: &ast::Expr,
expr: &ast::Expr) -> io::Result<()> {
- try!(self.print_expr_maybe_paren(place));
- try!(space(&mut self.s));
- try!(self.word_space("<-"));
+ self.print_expr_maybe_paren(place)?;
+ space(&mut self.s)?;
+ self.word_space("<-")?;
self.print_expr_maybe_paren(expr)
}
fn print_expr_vec(&mut self, exprs: &[P<ast::Expr>],
attrs: &[Attribute]) -> io::Result<()> {
- try!(self.ibox(INDENT_UNIT));
- try!(word(&mut self.s, "["));
- try!(self.print_inner_attributes_inline(attrs));
- try!(self.commasep_exprs(Inconsistent, &exprs[..]));
- try!(word(&mut self.s, "]"));
+ self.ibox(INDENT_UNIT)?;
+ word(&mut self.s, "[")?;
+ self.print_inner_attributes_inline(attrs)?;
+ self.commasep_exprs(Inconsistent, &exprs[..])?;
+ word(&mut self.s, "]")?;
self.end()
}
element: &ast::Expr,
count: &ast::Expr,
attrs: &[Attribute]) -> io::Result<()> {
- try!(self.ibox(INDENT_UNIT));
- try!(word(&mut self.s, "["));
- try!(self.print_inner_attributes_inline(attrs));
- try!(self.print_expr(element));
- try!(self.word_space(";"));
- try!(self.print_expr(count));
- try!(word(&mut self.s, "]"));
+ self.ibox(INDENT_UNIT)?;
+ word(&mut self.s, "[")?;
+ self.print_inner_attributes_inline(attrs)?;
+ self.print_expr(element)?;
+ self.word_space(";")?;
+ self.print_expr(count)?;
+ word(&mut self.s, "]")?;
self.end()
}
fields: &[ast::Field],
wth: &Option<P<ast::Expr>>,
attrs: &[Attribute]) -> io::Result<()> {
- try!(self.print_path(path, true, 0, false));
- try!(word(&mut self.s, "{"));
- try!(self.print_inner_attributes_inline(attrs));
- try!(self.commasep_cmnt(
+ self.print_path(path, true, 0, false)?;
+ word(&mut self.s, "{")?;
+ self.print_inner_attributes_inline(attrs)?;
+ self.commasep_cmnt(
Consistent,
&fields[..],
|s, field| {
- try!(s.ibox(INDENT_UNIT));
+ s.ibox(INDENT_UNIT)?;
if !field.is_shorthand {
- try!(s.print_ident(field.ident.node));
- try!(s.word_space(":"));
+ s.print_ident(field.ident.node)?;
+ s.word_space(":")?;
}
- try!(s.print_expr(&field.expr));
+ s.print_expr(&field.expr)?;
s.end()
},
- |f| f.span));
+ |f| f.span)?;
match *wth {
Some(ref expr) => {
- try!(self.ibox(INDENT_UNIT));
+ self.ibox(INDENT_UNIT)?;
if !fields.is_empty() {
- try!(word(&mut self.s, ","));
- try!(space(&mut self.s));
+ word(&mut self.s, ",")?;
+ space(&mut self.s)?;
}
- try!(word(&mut self.s, ".."));
- try!(self.print_expr(&expr));
- try!(self.end());
+ word(&mut self.s, "..")?;
+ self.print_expr(&expr)?;
+ self.end()?;
}
_ => if !fields.is_empty() {
- try!(word(&mut self.s, ","))
+ word(&mut self.s, ",")?
}
}
- try!(word(&mut self.s, "}"));
+ word(&mut self.s, "}")?;
Ok(())
}
fn print_expr_tup(&mut self, exprs: &[P<ast::Expr>],
attrs: &[Attribute]) -> io::Result<()> {
- try!(self.popen());
- try!(self.print_inner_attributes_inline(attrs));
- try!(self.commasep_exprs(Inconsistent, &exprs[..]));
+ self.popen()?;
+ self.print_inner_attributes_inline(attrs)?;
+ self.commasep_exprs(Inconsistent, &exprs[..])?;
if exprs.len() == 1 {
- try!(word(&mut self.s, ","));
+ word(&mut self.s, ",")?;
}
self.pclose()
}
fn print_expr_call(&mut self,
func: &ast::Expr,
args: &[P<ast::Expr>]) -> io::Result<()> {
- try!(self.print_expr_maybe_paren(func));
+ self.print_expr_maybe_paren(func)?;
self.print_call_post(args)
}
tys: &[P<ast::Ty>],
args: &[P<ast::Expr>]) -> io::Result<()> {
let base_args = &args[1..];
- try!(self.print_expr(&args[0]));
- try!(word(&mut self.s, "."));
- try!(self.print_ident(ident.node));
+ self.print_expr(&args[0])?;
+ word(&mut self.s, ".")?;
+ self.print_ident(ident.node)?;
if !tys.is_empty() {
- try!(word(&mut self.s, "::<"));
- try!(self.commasep(Inconsistent, tys,
- |s, ty| s.print_type(&ty)));
- try!(word(&mut self.s, ">"));
+ word(&mut self.s, "::<")?;
+ self.commasep(Inconsistent, tys,
+ |s, ty| s.print_type(&ty))?;
+ word(&mut self.s, ">")?;
}
self.print_call_post(base_args)
}
lhs: &ast::Expr,
rhs: &ast::Expr) -> io::Result<()> {
if self.check_expr_bin_needs_paren(lhs, op) {
- try!(self.print_expr_maybe_paren(lhs));
+ self.print_expr_maybe_paren(lhs)?;
} else {
- try!(self.print_expr(lhs));
+ self.print_expr(lhs)?;
}
- try!(space(&mut self.s));
- try!(self.word_space(op.node.to_string()));
+ space(&mut self.s)?;
+ self.word_space(op.node.to_string())?;
if self.check_expr_bin_needs_paren(rhs, op) {
self.print_expr_maybe_paren(rhs)
} else {
fn print_expr_unary(&mut self,
op: ast::UnOp,
expr: &ast::Expr) -> io::Result<()> {
- try!(word(&mut self.s, ast::UnOp::to_string(op)));
+ word(&mut self.s, ast::UnOp::to_string(op))?;
self.print_expr_maybe_paren(expr)
}
fn print_expr_addr_of(&mut self,
mutability: ast::Mutability,
expr: &ast::Expr) -> io::Result<()> {
- try!(word(&mut self.s, "&"));
- try!(self.print_mutability(mutability));
+ word(&mut self.s, "&")?;
+ self.print_mutability(mutability)?;
self.print_expr_maybe_paren(expr)
}
fn print_expr_outer_attr_style(&mut self,
expr: &ast::Expr,
is_inline: bool) -> io::Result<()> {
- try!(self.maybe_print_comment(expr.span.lo));
+ self.maybe_print_comment(expr.span.lo)?;
let attrs = &expr.attrs;
if is_inline {
- try!(self.print_outer_attributes_inline(attrs));
+ self.print_outer_attributes_inline(attrs)?;
} else {
- try!(self.print_outer_attributes(attrs));
+ self.print_outer_attributes(attrs)?;
}
- try!(self.ibox(INDENT_UNIT));
- try!(self.ann.pre(self, NodeExpr(expr)));
+ self.ibox(INDENT_UNIT)?;
+ self.ann.pre(self, NodeExpr(expr))?;
match expr.node {
ast::ExprKind::Box(ref expr) => {
- try!(self.word_space("box"));
- try!(self.print_expr(expr));
+ self.word_space("box")?;
+ self.print_expr(expr)?;
}
ast::ExprKind::InPlace(ref place, ref expr) => {
- try!(self.print_expr_in_place(place, expr));
+ self.print_expr_in_place(place, expr)?;
}
ast::ExprKind::Vec(ref exprs) => {
- try!(self.print_expr_vec(&exprs[..], attrs));
+ self.print_expr_vec(&exprs[..], attrs)?;
}
ast::ExprKind::Repeat(ref element, ref count) => {
- try!(self.print_expr_repeat(&element, &count, attrs));
+ self.print_expr_repeat(&element, &count, attrs)?;
}
ast::ExprKind::Struct(ref path, ref fields, ref wth) => {
- try!(self.print_expr_struct(path, &fields[..], wth, attrs));
+ self.print_expr_struct(path, &fields[..], wth, attrs)?;
}
ast::ExprKind::Tup(ref exprs) => {
- try!(self.print_expr_tup(&exprs[..], attrs));
+ self.print_expr_tup(&exprs[..], attrs)?;
}
ast::ExprKind::Call(ref func, ref args) => {
- try!(self.print_expr_call(&func, &args[..]));
+ self.print_expr_call(&func, &args[..])?;
}
ast::ExprKind::MethodCall(ident, ref tys, ref args) => {
- try!(self.print_expr_method_call(ident, &tys[..], &args[..]));
+ self.print_expr_method_call(ident, &tys[..], &args[..])?;
}
ast::ExprKind::Binary(op, ref lhs, ref rhs) => {
- try!(self.print_expr_binary(op, &lhs, &rhs));
+ self.print_expr_binary(op, &lhs, &rhs)?;
}
ast::ExprKind::Unary(op, ref expr) => {
- try!(self.print_expr_unary(op, &expr));
+ self.print_expr_unary(op, &expr)?;
}
ast::ExprKind::AddrOf(m, ref expr) => {
- try!(self.print_expr_addr_of(m, &expr));
+ self.print_expr_addr_of(m, &expr)?;
}
ast::ExprKind::Lit(ref lit) => {
- try!(self.print_literal(&lit));
+ self.print_literal(&lit)?;
}
ast::ExprKind::Cast(ref expr, ref ty) => {
if let ast::ExprKind::Cast(..) = expr.node {
- try!(self.print_expr(&expr));
+ self.print_expr(&expr)?;
} else {
- try!(self.print_expr_maybe_paren(&expr));
+ self.print_expr_maybe_paren(&expr)?;
}
- try!(space(&mut self.s));
- try!(self.word_space("as"));
- try!(self.print_type(&ty));
+ space(&mut self.s)?;
+ self.word_space("as")?;
+ self.print_type(&ty)?;
}
ast::ExprKind::Type(ref expr, ref ty) => {
- try!(self.print_expr(&expr));
- try!(self.word_space(":"));
- try!(self.print_type(&ty));
+ self.print_expr(&expr)?;
+ self.word_space(":")?;
+ self.print_type(&ty)?;
}
ast::ExprKind::If(ref test, ref blk, ref elseopt) => {
- try!(self.print_if(&test, &blk, elseopt.as_ref().map(|e| &**e)));
+ self.print_if(&test, &blk, elseopt.as_ref().map(|e| &**e))?;
}
ast::ExprKind::IfLet(ref pat, ref expr, ref blk, ref elseopt) => {
- try!(self.print_if_let(&pat, &expr, &blk, elseopt.as_ref().map(|e| &**e)));
+ self.print_if_let(&pat, &expr, &blk, elseopt.as_ref().map(|e| &**e))?;
}
ast::ExprKind::While(ref test, ref blk, opt_ident) => {
if let Some(ident) = opt_ident {
- try!(self.print_ident(ident.node));
- try!(self.word_space(":"));
+ self.print_ident(ident.node)?;
+ self.word_space(":")?;
}
- try!(self.head("while"));
- try!(self.print_expr(&test));
- try!(space(&mut self.s));
- try!(self.print_block_with_attrs(&blk, attrs));
+ self.head("while")?;
+ self.print_expr(&test)?;
+ space(&mut self.s)?;
+ self.print_block_with_attrs(&blk, attrs)?;
}
ast::ExprKind::WhileLet(ref pat, ref expr, ref blk, opt_ident) => {
if let Some(ident) = opt_ident {
- try!(self.print_ident(ident.node));
- try!(self.word_space(":"));
+ self.print_ident(ident.node)?;
+ self.word_space(":")?;
}
- try!(self.head("while let"));
- try!(self.print_pat(&pat));
- try!(space(&mut self.s));
- try!(self.word_space("="));
- try!(self.print_expr(&expr));
- try!(space(&mut self.s));
- try!(self.print_block_with_attrs(&blk, attrs));
+ self.head("while let")?;
+ self.print_pat(&pat)?;
+ space(&mut self.s)?;
+ self.word_space("=")?;
+ self.print_expr(&expr)?;
+ space(&mut self.s)?;
+ self.print_block_with_attrs(&blk, attrs)?;
}
ast::ExprKind::ForLoop(ref pat, ref iter, ref blk, opt_ident) => {
if let Some(ident) = opt_ident {
- try!(self.print_ident(ident.node));
- try!(self.word_space(":"));
+ self.print_ident(ident.node)?;
+ self.word_space(":")?;
}
- try!(self.head("for"));
- try!(self.print_pat(&pat));
- try!(space(&mut self.s));
- try!(self.word_space("in"));
- try!(self.print_expr(&iter));
- try!(space(&mut self.s));
- try!(self.print_block_with_attrs(&blk, attrs));
+ self.head("for")?;
+ self.print_pat(&pat)?;
+ space(&mut self.s)?;
+ self.word_space("in")?;
+ self.print_expr(&iter)?;
+ space(&mut self.s)?;
+ self.print_block_with_attrs(&blk, attrs)?;
}
ast::ExprKind::Loop(ref blk, opt_ident) => {
if let Some(ident) = opt_ident {
- try!(self.print_ident(ident.node));
- try!(self.word_space(":"));
+ self.print_ident(ident.node)?;
+ self.word_space(":")?;
}
- try!(self.head("loop"));
- try!(space(&mut self.s));
- try!(self.print_block_with_attrs(&blk, attrs));
+ self.head("loop")?;
+ space(&mut self.s)?;
+ self.print_block_with_attrs(&blk, attrs)?;
}
ast::ExprKind::Match(ref expr, ref arms) => {
- try!(self.cbox(INDENT_UNIT));
- try!(self.ibox(4));
- try!(self.word_nbsp("match"));
- try!(self.print_expr(&expr));
- try!(space(&mut self.s));
- try!(self.bopen());
- try!(self.print_inner_attributes_no_trailing_hardbreak(attrs));
+ self.cbox(INDENT_UNIT)?;
+ self.ibox(4)?;
+ self.word_nbsp("match")?;
+ self.print_expr(&expr)?;
+ space(&mut self.s)?;
+ self.bopen()?;
+ self.print_inner_attributes_no_trailing_hardbreak(attrs)?;
for arm in arms {
- try!(self.print_arm(arm));
+ self.print_arm(arm)?;
}
- try!(self.bclose_(expr.span, INDENT_UNIT));
+ self.bclose_(expr.span, INDENT_UNIT)?;
}
ast::ExprKind::Closure(capture_clause, ref decl, ref body, _) => {
- try!(self.print_capture_clause(capture_clause));
+ self.print_capture_clause(capture_clause)?;
- try!(self.print_fn_block_args(&decl));
- try!(space(&mut self.s));
- try!(self.print_expr(body));
- try!(self.end()); // need to close a box
+ self.print_fn_block_args(&decl)?;
+ space(&mut self.s)?;
+ self.print_expr(body)?;
+ self.end()?; // need to close a box
// a box will be closed by print_expr, but we didn't want an overall
// wrapper so we closed the corresponding opening. so create an
// empty box to satisfy the close.
- try!(self.ibox(0));
+ self.ibox(0)?;
}
ast::ExprKind::Block(ref blk) => {
// containing cbox, will be closed by print-block at }
- try!(self.cbox(INDENT_UNIT));
+ self.cbox(INDENT_UNIT)?;
// head-box, will be closed by print-block after {
- try!(self.ibox(0));
- try!(self.print_block_with_attrs(&blk, attrs));
+ self.ibox(0)?;
+ self.print_block_with_attrs(&blk, attrs)?;
}
ast::ExprKind::Assign(ref lhs, ref rhs) => {
- try!(self.print_expr(&lhs));
- try!(space(&mut self.s));
- try!(self.word_space("="));
- try!(self.print_expr(&rhs));
+ self.print_expr(&lhs)?;
+ space(&mut self.s)?;
+ self.word_space("=")?;
+ self.print_expr(&rhs)?;
}
ast::ExprKind::AssignOp(op, ref lhs, ref rhs) => {
- try!(self.print_expr(&lhs));
- try!(space(&mut self.s));
- try!(word(&mut self.s, op.node.to_string()));
- try!(self.word_space("="));
- try!(self.print_expr(&rhs));
+ self.print_expr(&lhs)?;
+ space(&mut self.s)?;
+ word(&mut self.s, op.node.to_string())?;
+ self.word_space("=")?;
+ self.print_expr(&rhs)?;
}
ast::ExprKind::Field(ref expr, id) => {
- try!(self.print_expr(&expr));
- try!(word(&mut self.s, "."));
- try!(self.print_ident(id.node));
+ self.print_expr(&expr)?;
+ word(&mut self.s, ".")?;
+ self.print_ident(id.node)?;
}
ast::ExprKind::TupField(ref expr, id) => {
- try!(self.print_expr(&expr));
- try!(word(&mut self.s, "."));
- try!(self.print_usize(id.node));
+ self.print_expr(&expr)?;
+ word(&mut self.s, ".")?;
+ self.print_usize(id.node)?;
}
ast::ExprKind::Index(ref expr, ref index) => {
- try!(self.print_expr(&expr));
- try!(word(&mut self.s, "["));
- try!(self.print_expr(&index));
- try!(word(&mut self.s, "]"));
+ self.print_expr(&expr)?;
+ word(&mut self.s, "[")?;
+ self.print_expr(&index)?;
+ word(&mut self.s, "]")?;
}
ast::ExprKind::Range(ref start, ref end, limits) => {
if let &Some(ref e) = start {
- try!(self.print_expr(&e));
+ self.print_expr(&e)?;
}
if limits == ast::RangeLimits::HalfOpen {
- try!(word(&mut self.s, ".."));
+ word(&mut self.s, "..")?;
} else {
- try!(word(&mut self.s, "..."));
+ word(&mut self.s, "...")?;
}
if let &Some(ref e) = end {
- try!(self.print_expr(&e));
+ self.print_expr(&e)?;
}
}
ast::ExprKind::Path(None, ref path) => {
- try!(self.print_path(path, true, 0, false))
+ self.print_path(path, true, 0, false)?
}
ast::ExprKind::Path(Some(ref qself), ref path) => {
- try!(self.print_qpath(path, qself, true))
+ self.print_qpath(path, qself, true)?
}
ast::ExprKind::Break(opt_ident, ref opt_expr) => {
- try!(word(&mut self.s, "break"));
- try!(space(&mut self.s));
+ word(&mut self.s, "break")?;
+ space(&mut self.s)?;
if let Some(ident) = opt_ident {
- try!(self.print_ident(ident.node));
- try!(space(&mut self.s));
+ self.print_ident(ident.node)?;
+ space(&mut self.s)?;
}
if let Some(ref expr) = *opt_expr {
- try!(self.print_expr(expr));
- try!(space(&mut self.s));
+ self.print_expr(expr)?;
+ space(&mut self.s)?;
}
}
ast::ExprKind::Continue(opt_ident) => {
- try!(word(&mut self.s, "continue"));
- try!(space(&mut self.s));
+ word(&mut self.s, "continue")?;
+ space(&mut self.s)?;
if let Some(ident) = opt_ident {
- try!(self.print_ident(ident.node));
- try!(space(&mut self.s))
+ self.print_ident(ident.node)?;
+ space(&mut self.s)?
}
}
ast::ExprKind::Ret(ref result) => {
- try!(word(&mut self.s, "return"));
+ word(&mut self.s, "return")?;
match *result {
Some(ref expr) => {
- try!(word(&mut self.s, " "));
- try!(self.print_expr(&expr));
+ word(&mut self.s, " ")?;
+ self.print_expr(&expr)?;
}
_ => ()
}
}
ast::ExprKind::InlineAsm(ref a) => {
- try!(word(&mut self.s, "asm!"));
- try!(self.popen());
- try!(self.print_string(&a.asm.as_str(), a.asm_str_style));
- try!(self.word_space(":"));
+ word(&mut self.s, "asm!")?;
+ self.popen()?;
+ self.print_string(&a.asm.as_str(), a.asm_str_style)?;
+ self.word_space(":")?;
- try!(self.commasep(Inconsistent, &a.outputs, |s, out| {
+ self.commasep(Inconsistent, &a.outputs, |s, out| {
let constraint = out.constraint.as_str();
let mut ch = constraint.chars();
match ch.next() {
Some('=') if out.is_rw => {
- try!(s.print_string(&format!("+{}", ch.as_str()),
- ast::StrStyle::Cooked))
+ s.print_string(&format!("+{}", ch.as_str()),
+ ast::StrStyle::Cooked)?
}
- _ => try!(s.print_string(&constraint, ast::StrStyle::Cooked))
+ _ => s.print_string(&constraint, ast::StrStyle::Cooked)?
}
- try!(s.popen());
- try!(s.print_expr(&out.expr));
- try!(s.pclose());
+ s.popen()?;
+ s.print_expr(&out.expr)?;
+ s.pclose()?;
Ok(())
- }));
- try!(space(&mut self.s));
- try!(self.word_space(":"));
-
- try!(self.commasep(Inconsistent, &a.inputs, |s, &(co, ref o)| {
- try!(s.print_string(&co.as_str(), ast::StrStyle::Cooked));
- try!(s.popen());
- try!(s.print_expr(&o));
- try!(s.pclose());
+ })?;
+ space(&mut self.s)?;
+ self.word_space(":")?;
+
+ self.commasep(Inconsistent, &a.inputs, |s, &(co, ref o)| {
+ s.print_string(&co.as_str(), ast::StrStyle::Cooked)?;
+ s.popen()?;
+ s.print_expr(&o)?;
+ s.pclose()?;
Ok(())
- }));
- try!(space(&mut self.s));
- try!(self.word_space(":"));
+ })?;
+ space(&mut self.s)?;
+ self.word_space(":")?;
- try!(self.commasep(Inconsistent, &a.clobbers,
+ self.commasep(Inconsistent, &a.clobbers,
|s, co| {
- try!(s.print_string(&co.as_str(), ast::StrStyle::Cooked));
+ s.print_string(&co.as_str(), ast::StrStyle::Cooked)?;
Ok(())
- }));
+ })?;
let mut options = vec![];
if a.volatile {
}
if !options.is_empty() {
- try!(space(&mut self.s));
- try!(self.word_space(":"));
- try!(self.commasep(Inconsistent, &options,
+ space(&mut self.s)?;
+ self.word_space(":")?;
+ self.commasep(Inconsistent, &options,
|s, &co| {
- try!(s.print_string(co, ast::StrStyle::Cooked));
+ s.print_string(co, ast::StrStyle::Cooked)?;
Ok(())
- }));
+ })?;
}
- try!(self.pclose());
+ self.pclose()?;
}
- ast::ExprKind::Mac(ref m) => try!(self.print_mac(m, token::Paren)),
+ ast::ExprKind::Mac(ref m) => self.print_mac(m, token::Paren)?,
ast::ExprKind::Paren(ref e) => {
- try!(self.popen());
- try!(self.print_inner_attributes_inline(attrs));
- try!(self.print_expr(&e));
- try!(self.pclose());
+ self.popen()?;
+ self.print_inner_attributes_inline(attrs)?;
+ self.print_expr(&e)?;
+ self.pclose()?;
},
ast::ExprKind::Try(ref e) => {
- try!(self.print_expr(e));
- try!(word(&mut self.s, "?"))
+ self.print_expr(e)?;
+ word(&mut self.s, "?")?
}
}
- try!(self.ann.post(self, NodeExpr(expr)));
+ self.ann.post(self, NodeExpr(expr))?;
self.end()
}
pub fn print_local_decl(&mut self, loc: &ast::Local) -> io::Result<()> {
- try!(self.print_pat(&loc.pat));
+ self.print_pat(&loc.pat)?;
if let Some(ref ty) = loc.ty {
- try!(self.word_space(":"));
- try!(self.print_type(&ty));
+ self.word_space(":")?;
+ self.print_type(&ty)?;
}
Ok(())
}
pub fn print_ident(&mut self, ident: ast::Ident) -> io::Result<()> {
- try!(word(&mut self.s, &ident.name.as_str()));
+ word(&mut self.s, &ident.name.as_str())?;
self.ann.post(self, NodeIdent(&ident))
}
}
pub fn print_name(&mut self, name: ast::Name) -> io::Result<()> {
- try!(word(&mut self.s, &name.as_str()));
+ word(&mut self.s, &name.as_str())?;
self.ann.post(self, NodeName(&name))
}
pub fn print_for_decl(&mut self, loc: &ast::Local,
coll: &ast::Expr) -> io::Result<()> {
- try!(self.print_local_decl(loc));
- try!(space(&mut self.s));
- try!(self.word_space("in"));
+ self.print_local_decl(loc)?;
+ space(&mut self.s)?;
+ self.word_space("in")?;
self.print_expr(coll)
}
defaults_to_global: bool)
-> io::Result<()>
{
- try!(self.maybe_print_comment(path.span.lo));
+ self.maybe_print_comment(path.span.lo)?;
let mut segments = path.segments[..path.segments.len()-depth].iter();
if defaults_to_global && path.is_global() {
}
for (i, segment) in segments.enumerate() {
if i > 0 {
- try!(word(&mut self.s, "::"))
+ word(&mut self.s, "::")?
}
if segment.identifier.name != keywords::CrateRoot.name() &&
segment.identifier.name != "$crate" {
- try!(self.print_ident(segment.identifier));
+ self.print_ident(segment.identifier)?;
if let Some(ref parameters) = segment.parameters {
- try!(self.print_path_parameters(parameters, colons_before_params));
+ self.print_path_parameters(parameters, colons_before_params)?;
}
}
}
colons_before_params: bool)
-> io::Result<()>
{
- try!(word(&mut self.s, "<"));
- try!(self.print_type(&qself.ty));
+ word(&mut self.s, "<")?;
+ self.print_type(&qself.ty)?;
if qself.position > 0 {
- try!(space(&mut self.s));
- try!(self.word_space("as"));
+ space(&mut self.s)?;
+ self.word_space("as")?;
let depth = path.segments.len() - qself.position;
- try!(self.print_path(&path, false, depth, false));
+ self.print_path(&path, false, depth, false)?;
}
- try!(word(&mut self.s, ">"));
- try!(word(&mut self.s, "::"));
+ word(&mut self.s, ">")?;
+ word(&mut self.s, "::")?;
let item_segment = path.segments.last().unwrap();
- try!(self.print_ident(item_segment.identifier));
+ self.print_ident(item_segment.identifier)?;
match item_segment.parameters {
Some(ref parameters) => self.print_path_parameters(parameters, colons_before_params),
None => Ok(()),
-> io::Result<()>
{
if colons_before_params {
- try!(word(&mut self.s, "::"))
+ word(&mut self.s, "::")?
}
match *parameters {
ast::PathParameters::AngleBracketed(ref data) => {
- try!(word(&mut self.s, "<"));
+ word(&mut self.s, "<")?;
let mut comma = false;
for lifetime in &data.lifetimes {
if comma {
- try!(self.word_space(","))
+ self.word_space(",")?
}
- try!(self.print_lifetime(lifetime));
+ self.print_lifetime(lifetime)?;
comma = true;
}
if !data.types.is_empty() {
if comma {
- try!(self.word_space(","))
+ self.word_space(",")?
}
- try!(self.commasep(
+ self.commasep(
Inconsistent,
&data.types,
- |s, ty| s.print_type(&ty)));
+ |s, ty| s.print_type(&ty))?;
comma = true;
}
for binding in data.bindings.iter() {
if comma {
- try!(self.word_space(","))
+ self.word_space(",")?
}
- try!(self.print_ident(binding.ident));
- try!(space(&mut self.s));
- try!(self.word_space("="));
- try!(self.print_type(&binding.ty));
+ self.print_ident(binding.ident)?;
+ space(&mut self.s)?;
+ self.word_space("=")?;
+ self.print_type(&binding.ty)?;
comma = true;
}
- try!(word(&mut self.s, ">"))
+ word(&mut self.s, ">")?
}
ast::PathParameters::Parenthesized(ref data) => {
- try!(word(&mut self.s, "("));
- try!(self.commasep(
+ word(&mut self.s, "(")?;
+ self.commasep(
Inconsistent,
&data.inputs,
- |s, ty| s.print_type(&ty)));
- try!(word(&mut self.s, ")"));
+ |s, ty| s.print_type(&ty))?;
+ word(&mut self.s, ")")?;
if let Some(ref ty) = data.output {
- try!(self.space_if_not_bol());
- try!(self.word_space("->"));
- try!(self.print_type(&ty));
+ self.space_if_not_bol()?;
+ self.word_space("->")?;
+ self.print_type(&ty)?;
}
}
}
}
pub fn print_pat(&mut self, pat: &ast::Pat) -> io::Result<()> {
- try!(self.maybe_print_comment(pat.span.lo));
- try!(self.ann.pre(self, NodePat(pat)));
+ self.maybe_print_comment(pat.span.lo)?;
+ self.ann.pre(self, NodePat(pat))?;
/* Pat isn't normalized, but the beauty of it
is that it doesn't matter */
match pat.node {
- PatKind::Wild => try!(word(&mut self.s, "_")),
+ PatKind::Wild => word(&mut self.s, "_")?,
PatKind::Ident(binding_mode, ref path1, ref sub) => {
match binding_mode {
ast::BindingMode::ByRef(mutbl) => {
- try!(self.word_nbsp("ref"));
- try!(self.print_mutability(mutbl));
+ self.word_nbsp("ref")?;
+ self.print_mutability(mutbl)?;
}
ast::BindingMode::ByValue(ast::Mutability::Immutable) => {}
ast::BindingMode::ByValue(ast::Mutability::Mutable) => {
- try!(self.word_nbsp("mut"));
+ self.word_nbsp("mut")?;
}
}
- try!(self.print_ident(path1.node));
+ self.print_ident(path1.node)?;
if let Some(ref p) = *sub {
- try!(word(&mut self.s, "@"));
- try!(self.print_pat(&p));
+ word(&mut self.s, "@")?;
+ self.print_pat(&p)?;
}
}
PatKind::TupleStruct(ref path, ref elts, ddpos) => {
- try!(self.print_path(path, true, 0, false));
- try!(self.popen());
+ self.print_path(path, true, 0, false)?;
+ self.popen()?;
if let Some(ddpos) = ddpos {
- try!(self.commasep(Inconsistent, &elts[..ddpos], |s, p| s.print_pat(&p)));
+ self.commasep(Inconsistent, &elts[..ddpos], |s, p| s.print_pat(&p))?;
if ddpos != 0 {
- try!(self.word_space(","));
+ self.word_space(",")?;
}
- try!(word(&mut self.s, ".."));
+ word(&mut self.s, "..")?;
if ddpos != elts.len() {
- try!(word(&mut self.s, ","));
- try!(self.commasep(Inconsistent, &elts[ddpos..], |s, p| s.print_pat(&p)));
+ word(&mut self.s, ",")?;
+ self.commasep(Inconsistent, &elts[ddpos..], |s, p| s.print_pat(&p))?;
}
} else {
- try!(self.commasep(Inconsistent, &elts[..], |s, p| s.print_pat(&p)));
+ self.commasep(Inconsistent, &elts[..], |s, p| s.print_pat(&p))?;
}
- try!(self.pclose());
+ self.pclose()?;
}
PatKind::Path(None, ref path) => {
- try!(self.print_path(path, true, 0, false));
+ self.print_path(path, true, 0, false)?;
}
PatKind::Path(Some(ref qself), ref path) => {
- try!(self.print_qpath(path, qself, false));
+ self.print_qpath(path, qself, false)?;
}
PatKind::Struct(ref path, ref fields, etc) => {
- try!(self.print_path(path, true, 0, false));
- try!(self.nbsp());
- try!(self.word_space("{"));
- try!(self.commasep_cmnt(
+ self.print_path(path, true, 0, false)?;
+ self.nbsp()?;
+ self.word_space("{")?;
+ self.commasep_cmnt(
Consistent, &fields[..],
|s, f| {
- try!(s.cbox(INDENT_UNIT));
+ s.cbox(INDENT_UNIT)?;
if !f.node.is_shorthand {
- try!(s.print_ident(f.node.ident));
- try!(s.word_nbsp(":"));
+ s.print_ident(f.node.ident)?;
+ s.word_nbsp(":")?;
}
- try!(s.print_pat(&f.node.pat));
+ s.print_pat(&f.node.pat)?;
s.end()
},
- |f| f.node.pat.span));
+ |f| f.node.pat.span)?;
if etc {
- if !fields.is_empty() { try!(self.word_space(",")); }
- try!(word(&mut self.s, ".."));
+ if !fields.is_empty() { self.word_space(",")?; }
+ word(&mut self.s, "..")?;
}
- try!(space(&mut self.s));
- try!(word(&mut self.s, "}"));
+ space(&mut self.s)?;
+ word(&mut self.s, "}")?;
}
PatKind::Tuple(ref elts, ddpos) => {
- try!(self.popen());
+ self.popen()?;
if let Some(ddpos) = ddpos {
- try!(self.commasep(Inconsistent, &elts[..ddpos], |s, p| s.print_pat(&p)));
+ self.commasep(Inconsistent, &elts[..ddpos], |s, p| s.print_pat(&p))?;
if ddpos != 0 {
- try!(self.word_space(","));
+ self.word_space(",")?;
}
- try!(word(&mut self.s, ".."));
+ word(&mut self.s, "..")?;
if ddpos != elts.len() {
- try!(word(&mut self.s, ","));
- try!(self.commasep(Inconsistent, &elts[ddpos..], |s, p| s.print_pat(&p)));
+ word(&mut self.s, ",")?;
+ self.commasep(Inconsistent, &elts[ddpos..], |s, p| s.print_pat(&p))?;
}
} else {
- try!(self.commasep(Inconsistent, &elts[..], |s, p| s.print_pat(&p)));
+ self.commasep(Inconsistent, &elts[..], |s, p| s.print_pat(&p))?;
if elts.len() == 1 {
- try!(word(&mut self.s, ","));
+ word(&mut self.s, ",")?;
}
}
- try!(self.pclose());
+ self.pclose()?;
}
PatKind::Box(ref inner) => {
- try!(word(&mut self.s, "box "));
- try!(self.print_pat(&inner));
+ word(&mut self.s, "box ")?;
+ self.print_pat(&inner)?;
}
PatKind::Ref(ref inner, mutbl) => {
- try!(word(&mut self.s, "&"));
+ word(&mut self.s, "&")?;
if mutbl == ast::Mutability::Mutable {
- try!(word(&mut self.s, "mut "));
+ word(&mut self.s, "mut ")?;
}
- try!(self.print_pat(&inner));
+ self.print_pat(&inner)?;
}
- PatKind::Lit(ref e) => try!(self.print_expr(&**e)),
+ PatKind::Lit(ref e) => self.print_expr(&**e)?,
PatKind::Range(ref begin, ref end) => {
- try!(self.print_expr(&begin));
- try!(space(&mut self.s));
- try!(word(&mut self.s, "..."));
- try!(self.print_expr(&end));
+ self.print_expr(&begin)?;
+ space(&mut self.s)?;
+ word(&mut self.s, "...")?;
+ self.print_expr(&end)?;
}
PatKind::Slice(ref before, ref slice, ref after) => {
- try!(word(&mut self.s, "["));
- try!(self.commasep(Inconsistent,
+ word(&mut self.s, "[")?;
+ self.commasep(Inconsistent,
&before[..],
- |s, p| s.print_pat(&p)));
+ |s, p| s.print_pat(&p))?;
if let Some(ref p) = *slice {
- if !before.is_empty() { try!(self.word_space(",")); }
+ if !before.is_empty() { self.word_space(",")?; }
if p.node != PatKind::Wild {
- try!(self.print_pat(&p));
+ self.print_pat(&p)?;
}
- try!(word(&mut self.s, ".."));
- if !after.is_empty() { try!(self.word_space(",")); }
+ word(&mut self.s, "..")?;
+ if !after.is_empty() { self.word_space(",")?; }
}
- try!(self.commasep(Inconsistent,
+ self.commasep(Inconsistent,
&after[..],
- |s, p| s.print_pat(&p)));
- try!(word(&mut self.s, "]"));
+ |s, p| s.print_pat(&p))?;
+ word(&mut self.s, "]")?;
}
- PatKind::Mac(ref m) => try!(self.print_mac(m, token::Paren)),
+ PatKind::Mac(ref m) => self.print_mac(m, token::Paren)?,
}
self.ann.post(self, NodePat(pat))
}
// I have no idea why this check is necessary, but here it
// is :(
if arm.attrs.is_empty() {
- try!(space(&mut self.s));
+ space(&mut self.s)?;
}
- try!(self.cbox(INDENT_UNIT));
- try!(self.ibox(0));
- try!(self.maybe_print_comment(arm.pats[0].span.lo));
- try!(self.print_outer_attributes(&arm.attrs));
+ self.cbox(INDENT_UNIT)?;
+ self.ibox(0)?;
+ self.maybe_print_comment(arm.pats[0].span.lo)?;
+ self.print_outer_attributes(&arm.attrs)?;
let mut first = true;
for p in &arm.pats {
if first {
first = false;
} else {
- try!(space(&mut self.s));
- try!(self.word_space("|"));
+ space(&mut self.s)?;
+ self.word_space("|")?;
}
- try!(self.print_pat(&p));
+ self.print_pat(&p)?;
}
- try!(space(&mut self.s));
+ space(&mut self.s)?;
if let Some(ref e) = arm.guard {
- try!(self.word_space("if"));
- try!(self.print_expr(&e));
- try!(space(&mut self.s));
+ self.word_space("if")?;
+ self.print_expr(&e)?;
+ space(&mut self.s)?;
}
- try!(self.word_space("=>"));
+ self.word_space("=>")?;
match arm.body.node {
ast::ExprKind::Block(ref blk) => {
// the block will close the pattern's ibox
- try!(self.print_block_unclosed_indent(&blk, INDENT_UNIT));
+ self.print_block_unclosed_indent(&blk, INDENT_UNIT)?;
// If it is a user-provided unsafe block, print a comma after it
if let BlockCheckMode::Unsafe(ast::UserProvided) = blk.rules {
- try!(word(&mut self.s, ","));
+ word(&mut self.s, ",")?;
}
}
_ => {
- try!(self.end()); // close the ibox for the pattern
- try!(self.print_expr(&arm.body));
- try!(word(&mut self.s, ","));
+ self.end()?; // close the ibox for the pattern
+ self.print_expr(&arm.body)?;
+ word(&mut self.s, ",")?;
}
}
self.end() // close enclosing cbox
fn print_explicit_self(&mut self, explicit_self: &ast::ExplicitSelf) -> io::Result<()> {
match explicit_self.node {
SelfKind::Value(m) => {
- try!(self.print_mutability(m));
+ self.print_mutability(m)?;
word(&mut self.s, "self")
}
SelfKind::Region(ref lt, m) => {
- try!(word(&mut self.s, "&"));
- try!(self.print_opt_lifetime(lt));
- try!(self.print_mutability(m));
+ word(&mut self.s, "&")?;
+ self.print_opt_lifetime(lt)?;
+ self.print_mutability(m)?;
word(&mut self.s, "self")
}
SelfKind::Explicit(ref typ, m) => {
- try!(self.print_mutability(m));
- try!(word(&mut self.s, "self"));
- try!(self.word_space(":"));
+ self.print_mutability(m)?;
+ word(&mut self.s, "self")?;
+ self.word_space(":")?;
self.print_type(&typ)
}
}
name: Option<ast::Ident>,
generics: &ast::Generics,
vis: &ast::Visibility) -> io::Result<()> {
- try!(self.print_fn_header_info(unsafety, constness, abi, vis));
+ self.print_fn_header_info(unsafety, constness, abi, vis)?;
if let Some(name) = name {
- try!(self.nbsp());
- try!(self.print_ident(name));
+ self.nbsp()?;
+ self.print_ident(name)?;
}
- try!(self.print_generics(generics));
- try!(self.print_fn_args_and_ret(decl));
+ self.print_generics(generics)?;
+ self.print_fn_args_and_ret(decl)?;
self.print_where_clause(&generics.where_clause)
}
pub fn print_fn_args_and_ret(&mut self, decl: &ast::FnDecl)
-> io::Result<()> {
- try!(self.popen());
- try!(self.commasep(Inconsistent, &decl.inputs, |s, arg| s.print_arg(arg, false)));
+ self.popen()?;
+ self.commasep(Inconsistent, &decl.inputs, |s, arg| s.print_arg(arg, false))?;
if decl.variadic {
- try!(word(&mut self.s, ", ..."));
+ word(&mut self.s, ", ...")?;
}
- try!(self.pclose());
+ self.pclose()?;
self.print_fn_output(decl)
}
&mut self,
decl: &ast::FnDecl)
-> io::Result<()> {
- try!(word(&mut self.s, "|"));
- try!(self.commasep(Inconsistent, &decl.inputs, |s, arg| s.print_arg(arg, true)));
- try!(word(&mut self.s, "|"));
+ word(&mut self.s, "|")?;
+ self.commasep(Inconsistent, &decl.inputs, |s, arg| s.print_arg(arg, true))?;
+ word(&mut self.s, "|")?;
if let ast::FunctionRetTy::Default(..) = decl.output {
return Ok(());
}
- try!(self.space_if_not_bol());
- try!(self.word_space("->"));
+ self.space_if_not_bol()?;
+ self.word_space("->")?;
match decl.output {
ast::FunctionRetTy::Ty(ref ty) => {
- try!(self.print_type(&ty));
+ self.print_type(&ty)?;
self.maybe_print_comment(ty.span.lo)
}
ast::FunctionRetTy::Default(..) => unreachable!(),
bounds: &[ast::TyParamBound])
-> io::Result<()> {
if !bounds.is_empty() {
- try!(word(&mut self.s, prefix));
+ word(&mut self.s, prefix)?;
let mut first = true;
for bound in bounds {
- try!(self.nbsp());
+ self.nbsp()?;
if first {
first = false;
} else {
- try!(self.word_space("+"));
+ self.word_space("+")?;
}
- try!(match *bound {
+ (match *bound {
TraitTyParamBound(ref tref, TraitBoundModifier::None) => {
self.print_poly_trait_ref(tref)
}
TraitTyParamBound(ref tref, TraitBoundModifier::Maybe) => {
- try!(word(&mut self.s, "?"));
+ word(&mut self.s, "?")?;
self.print_poly_trait_ref(tref)
}
RegionTyParamBound(ref lt) => {
self.print_lifetime(lt)
}
- })
+ })?
}
Ok(())
} else {
bounds: &[ast::Lifetime])
-> io::Result<()>
{
- try!(self.print_lifetime(lifetime));
+ self.print_lifetime(lifetime)?;
if !bounds.is_empty() {
- try!(word(&mut self.s, ": "));
+ word(&mut self.s, ": ")?;
for (i, bound) in bounds.iter().enumerate() {
if i != 0 {
- try!(word(&mut self.s, " + "));
+ word(&mut self.s, " + ")?;
}
- try!(self.print_lifetime(bound));
+ self.print_lifetime(bound)?;
}
}
Ok(())
return Ok(());
}
- try!(word(&mut self.s, "<"));
+ word(&mut self.s, "<")?;
let mut ints = Vec::new();
for i in 0..total {
ints.push(i);
}
- try!(self.commasep(Inconsistent, &ints[..], |s, &idx| {
+ self.commasep(Inconsistent, &ints[..], |s, &idx| {
if idx < generics.lifetimes.len() {
let lifetime_def = &generics.lifetimes[idx];
- try!(s.print_outer_attributes_inline(&lifetime_def.attrs));
+ s.print_outer_attributes_inline(&lifetime_def.attrs)?;
s.print_lifetime_bounds(&lifetime_def.lifetime, &lifetime_def.bounds)
} else {
let idx = idx - generics.lifetimes.len();
let param = &generics.ty_params[idx];
s.print_ty_param(param)
}
- }));
+ })?;
- try!(word(&mut self.s, ">"));
+ word(&mut self.s, ">")?;
Ok(())
}
pub fn print_ty_param(&mut self, param: &ast::TyParam) -> io::Result<()> {
- try!(self.print_outer_attributes_inline(¶m.attrs));
- try!(self.print_ident(param.ident));
- try!(self.print_bounds(":", ¶m.bounds));
+ self.print_outer_attributes_inline(¶m.attrs)?;
+ self.print_ident(param.ident)?;
+ self.print_bounds(":", ¶m.bounds)?;
match param.default {
Some(ref default) => {
- try!(space(&mut self.s));
- try!(self.word_space("="));
+ space(&mut self.s)?;
+ self.word_space("=")?;
self.print_type(&default)
}
_ => Ok(())
return Ok(())
}
- try!(space(&mut self.s));
- try!(self.word_space("where"));
+ space(&mut self.s)?;
+ self.word_space("where")?;
for (i, predicate) in where_clause.predicates.iter().enumerate() {
if i != 0 {
- try!(self.word_space(","));
+ self.word_space(",")?;
}
match *predicate {
ref bounded_ty,
ref bounds,
..}) => {
- try!(self.print_formal_lifetime_list(bound_lifetimes));
- try!(self.print_type(&bounded_ty));
- try!(self.print_bounds(":", bounds));
+ self.print_formal_lifetime_list(bound_lifetimes)?;
+ self.print_type(&bounded_ty)?;
+ self.print_bounds(":", bounds)?;
}
ast::WherePredicate::RegionPredicate(ast::WhereRegionPredicate{ref lifetime,
ref bounds,
..}) => {
- try!(self.print_lifetime_bounds(lifetime, bounds));
+ self.print_lifetime_bounds(lifetime, bounds)?;
}
ast::WherePredicate::EqPredicate(ast::WhereEqPredicate{ref path, ref ty, ..}) => {
- try!(self.print_path(path, false, 0, false));
- try!(space(&mut self.s));
- try!(self.word_space("="));
- try!(self.print_type(&ty));
+ self.print_path(path, false, 0, false)?;
+ space(&mut self.s)?;
+ self.word_space("=")?;
+ self.print_type(&ty)?;
}
}
}
pub fn print_view_path(&mut self, vp: &ast::ViewPath) -> io::Result<()> {
match vp.node {
ast::ViewPathSimple(ident, ref path) => {
- try!(self.print_path(path, false, 0, true));
+ self.print_path(path, false, 0, true)?;
if path.segments.last().unwrap().identifier.name !=
ident.name {
- try!(space(&mut self.s));
- try!(self.word_space("as"));
- try!(self.print_ident(ident));
+ space(&mut self.s)?;
+ self.word_space("as")?;
+ self.print_ident(ident)?;
}
Ok(())
}
ast::ViewPathGlob(ref path) => {
- try!(self.print_path(path, false, 0, true));
+ self.print_path(path, false, 0, true)?;
word(&mut self.s, "::*")
}
ast::ViewPathList(ref path, ref idents) => {
if path.segments.is_empty() {
- try!(word(&mut self.s, "{"));
+ word(&mut self.s, "{")?;
} else {
- try!(self.print_path(path, false, 0, true));
- try!(word(&mut self.s, "::{"));
+ self.print_path(path, false, 0, true)?;
+ word(&mut self.s, "::{")?;
}
- try!(self.commasep(Inconsistent, &idents[..], |s, w| {
- try!(s.print_ident(w.node.name));
+ self.commasep(Inconsistent, &idents[..], |s, w| {
+ s.print_ident(w.node.name)?;
if let Some(ident) = w.node.rename {
- try!(space(&mut s.s));
- try!(s.word_space("as"));
- try!(s.print_ident(ident));
+ space(&mut s.s)?;
+ s.word_space("as")?;
+ s.print_ident(ident)?;
}
Ok(())
- }));
+ })?;
word(&mut self.s, "}")
}
}
}
pub fn print_mt(&mut self, mt: &ast::MutTy) -> io::Result<()> {
- try!(self.print_mutability(mt.mutbl));
+ self.print_mutability(mt.mutbl)?;
self.print_type(&mt.ty)
}
pub fn print_arg(&mut self, input: &ast::Arg, is_closure: bool) -> io::Result<()> {
- try!(self.ibox(INDENT_UNIT));
+ self.ibox(INDENT_UNIT)?;
match input.ty.node {
- ast::TyKind::Infer if is_closure => try!(self.print_pat(&input.pat)),
+ ast::TyKind::Infer if is_closure => self.print_pat(&input.pat)?,
_ => {
if let Some(eself) = input.to_self() {
- try!(self.print_explicit_self(&eself));
+ self.print_explicit_self(&eself)?;
} else {
let invalid = if let PatKind::Ident(_, ident, _) = input.pat.node {
ident.node.name == keywords::Invalid.name()
false
};
if !invalid {
- try!(self.print_pat(&input.pat));
- try!(word(&mut self.s, ":"));
- try!(space(&mut self.s));
+ self.print_pat(&input.pat)?;
+ word(&mut self.s, ":")?;
+ space(&mut self.s)?;
}
- try!(self.print_type(&input.ty));
+ self.print_type(&input.ty)?;
}
}
}
return Ok(());
}
- try!(self.space_if_not_bol());
- try!(self.ibox(INDENT_UNIT));
- try!(self.word_space("->"));
+ self.space_if_not_bol()?;
+ self.ibox(INDENT_UNIT)?;
+ self.word_space("->")?;
match decl.output {
ast::FunctionRetTy::Default(..) => unreachable!(),
ast::FunctionRetTy::Ty(ref ty) =>
- try!(self.print_type(&ty))
+ self.print_type(&ty)?
}
- try!(self.end());
+ self.end()?;
match decl.output {
ast::FunctionRetTy::Ty(ref output) => self.maybe_print_comment(output.span.lo),
name: Option<ast::Ident>,
generics: &ast::Generics)
-> io::Result<()> {
- try!(self.ibox(INDENT_UNIT));
+ self.ibox(INDENT_UNIT)?;
if !generics.lifetimes.is_empty() || !generics.ty_params.is_empty() {
- try!(word(&mut self.s, "for"));
- try!(self.print_generics(generics));
+ word(&mut self.s, "for")?;
+ self.print_generics(generics)?;
}
let generics = ast::Generics {
lifetimes: Vec::new(),
},
span: syntax_pos::DUMMY_SP,
};
- try!(self.print_fn(decl,
+ self.print_fn(decl,
unsafety,
ast::Constness::NotConst,
abi,
name,
&generics,
- &ast::Visibility::Inherited));
+ &ast::Visibility::Inherited)?;
self.end()
}
// If there aren't any remaining comments, then we need to manually
// make sure there is a line break at the end.
if self.next_comment().is_none() {
- try!(hardbreak(&mut self.s));
+ hardbreak(&mut self.s)?;
}
loop {
match self.next_comment() {
Some(ref cmnt) => {
- try!(self.print_comment(cmnt));
+ self.print_comment(cmnt)?;
self.cur_cmnt_and_lit.cur_cmnt += 1;
}
_ => break
match opt_abi {
Some(Abi::Rust) => Ok(()),
Some(abi) => {
- try!(self.word_nbsp("extern"));
+ self.word_nbsp("extern")?;
self.word_nbsp(&abi.to_string())
}
None => Ok(())
opt_abi: Option<Abi>) -> io::Result<()> {
match opt_abi {
Some(abi) => {
- try!(self.word_nbsp("extern"));
+ self.word_nbsp("extern")?;
self.word_nbsp(&abi.to_string())
}
None => Ok(())
constness: ast::Constness,
abi: Abi,
vis: &ast::Visibility) -> io::Result<()> {
- try!(word(&mut self.s, &visibility_qualified(vis, "")));
+ word(&mut self.s, &visibility_qualified(vis, ""))?;
match constness {
ast::Constness::NotConst => {}
- ast::Constness::Const => try!(self.word_nbsp("const"))
+ ast::Constness::Const => self.word_nbsp("const")?
}
- try!(self.print_unsafety(unsafety));
+ self.print_unsafety(unsafety)?;
if abi != Abi::Rust {
- try!(self.word_nbsp("extern"));
- try!(self.word_nbsp(&abi.to_string()));
+ self.word_nbsp("extern")?;
+ self.word_nbsp(&abi.to_string())?;
}
word(&mut self.s, "fn")
use syntax::codemap;
use syntax::ext::base::{Annotatable, ExtCtxt, SyntaxExtension};
use syntax::ext::build::AstBuilder;
-use syntax::feature_gate::{self, emit_feature_err};
+use syntax::feature_gate;
use syntax::ptr::P;
use syntax::symbol::Symbol;
use syntax_pos::Span;
.filter(|&(_, ref name)| !is_builtin_trait(name.name().unwrap()))
.next();
if let Some((i, titem)) = macros_11_derive {
- if !cx.ecfg.features.unwrap().proc_macro {
- let issue = feature_gate::GateIssue::Language;
- let msg = "custom derive macros are experimentally supported";
- emit_feature_err(cx.parse_sess, "proc_macro", titem.span, issue, msg);
- }
-
let tname = ast::Ident::with_empty_ctxt(titem.name().unwrap());
let path = ast::Path::from_ident(titem.span, tname);
let ext = cx.resolver.resolve_macro(cx.current_expansion.mark, &path, false).unwrap();
html_root_url = "https://doc.rust-lang.org/nightly/")]
#![cfg_attr(not(stage0), deny(warnings))]
-#![feature(proc_macro_lib)]
#![feature(proc_macro_internals)]
#![feature(rustc_private)]
#![feature(staged_api)]
use syntax::ext::build::AstBuilder;
use syntax::ext::expand::ExpansionConfig;
use syntax::parse::ParseSess;
-use syntax::feature_gate::Features;
use syntax::fold::Folder;
use syntax::ptr::P;
use syntax::symbol::Symbol;
is_proc_macro_crate: bool,
is_test_crate: bool,
num_crate_types: usize,
- handler: &errors::Handler,
- features: &Features) -> ast::Crate {
+ handler: &errors::Handler) -> ast::Crate {
let ecfg = ExpansionConfig::default("proc_macro".to_string());
let mut cx = ExtCtxt::new(sess, ecfg, resolver);
if !is_proc_macro_crate {
return krate
- } else if !features.proc_macro {
- let mut err = handler.struct_err("the `proc-macro` crate type is \
- experimental");
- err.help("add #![feature(proc_macro)] to the crate attributes to \
- enable");
- err.emit();
}
if num_crate_types > 1 {
li.QuadPart += 1;
return li;
}
+
+#define DO_INT128_TEST !(defined(WIN32) || defined(_WIN32) || defined(__WIN32)) && \
+ defined(__amd64__)
+
+#if DO_INT128_TEST
+
+unsigned __int128 identity(unsigned __int128 a) {
+ return a;
+}
+
+__int128 square(__int128 a) {
+ return a * a;
+}
+
+__int128 sub(__int128 a, __int128 b) {
+ return a - b;
+}
+
+#endif
// dependent alignment
#[no_mangle]
pub fn small_array_alignment(x: &mut [i8; 4], y: [i8; 4]) {
-// CHECK: %arg1 = alloca [4 x i8]
// CHECK: [[TMP:%.+]] = alloca i32
+// CHECK: %arg1 = alloca [4 x i8]
// CHECK: store i32 %1, i32* [[TMP]]
// CHECK: [[Y8:%[0-9]+]] = bitcast [4 x i8]* %arg1 to i8*
// CHECK: [[TMP8:%[0-9]+]] = bitcast i32* [[TMP]] to i8*
// dependent alignment
#[no_mangle]
pub fn small_struct_alignment(x: &mut Bytes, y: Bytes) {
-// CHECK: %arg1 = alloca %Bytes
// CHECK: [[TMP:%.+]] = alloca i32
+// CHECK: %arg1 = alloca %Bytes
// CHECK: store i32 %1, i32* [[TMP]]
// CHECK: [[Y8:%[0-9]+]] = bitcast %Bytes* %arg1 to i8*
// CHECK: [[TMP8:%[0-9]+]] = bitcast i32* [[TMP]] to i8*
--- /dev/null
+// Copyright 2017 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.
+
+// This crate attempts to enumerate the various scenarios for how a
+// type can define fields and methods with various visiblities and
+// stabilities.
+//
+// The basic stability pattern in this file has four cases:
+// 1. no stability attribute at all
+// 2. a stable attribute (feature "unit_test")
+// 3. an unstable attribute that unit test declares (feature "unstable_declared")
+// 4. an unstable attribute that unit test fails to declare (feature "unstable_undeclared")
+//
+// This file also covers four kinds of visibility: private,
+// pub(module), pub(crate), and pub.
+//
+// However, since stability attributes can only be observed in
+// cross-crate linkage scenarios, there is little reason to take the
+// cross-product (4 stability cases * 4 visiblity cases), because the
+// first three visibility cases cannot be accessed outside this crate,
+// and therefore stability is only relevant when the visibility is pub
+// to the whole universe.
+//
+// (The only reason to do so would be if one were worried about the
+// compiler having some subtle bug where adding a stability attribute
+// introduces a privacy violation. As a way to provide evidence that
+// this is not occurring, I have put stability attributes on some
+// non-pub fields, marked with SILLY below)
+
+#![feature(staged_api)]
+#![feature(pub_restricted)]
+
+#![stable(feature = "unit_test", since = "0.0.0")]
+
+#[stable(feature = "unit_test", since = "0.0.0")]
+pub use m::{Record, Trait, Tuple};
+
+mod m {
+ #[derive(Default)]
+ #[stable(feature = "unit_test", since = "0.0.0")]
+ pub struct Record {
+ #[stable(feature = "unit_test", since = "0.0.0")]
+ pub a_stable_pub: i32,
+ #[unstable(feature = "unstable_declared", issue = "38412")]
+ pub a_unstable_declared_pub: i32,
+ #[unstable(feature = "unstable_undeclared", issue = "38412")]
+ pub a_unstable_undeclared_pub: i32,
+ #[unstable(feature = "unstable_undeclared", issue = "38412")] // SILLY
+ pub(crate) b_crate: i32,
+ #[unstable(feature = "unstable_declared", issue = "38412")] // SILLY
+ pub(m) c_mod: i32,
+ #[stable(feature = "unit_test", since = "0.0.0")] // SILLY
+ d_priv: i32
+ }
+
+ #[derive(Default)]
+ #[stable(feature = "unit_test", since = "1.0.0")]
+ pub struct Tuple(
+ #[stable(feature = "unit_test", since = "0.0.0")]
+ pub i32,
+ #[unstable(feature = "unstable_declared", issue = "38412")]
+ pub i32,
+ #[unstable(feature = "unstable_undeclared", issue = "38412")]
+ pub i32,
+
+ pub(crate) i32,
+ pub(m) i32,
+ i32);
+
+ impl Record {
+ #[stable(feature = "unit_test", since = "1.0.0")]
+ pub fn new() -> Self { Default::default() }
+ }
+
+ impl Tuple {
+ #[stable(feature = "unit_test", since = "1.0.0")]
+ pub fn new() -> Self { Default::default() }
+ }
+
+
+ #[stable(feature = "unit_test", since = "0.0.0")]
+ pub trait Trait {
+ #[stable(feature = "unit_test", since = "0.0.0")]
+ type Type;
+ #[stable(feature = "unit_test", since = "0.0.0")]
+ fn stable_trait_method(&self) -> Self::Type;
+ #[unstable(feature = "unstable_undeclared", issue = "38412")]
+ fn unstable_undeclared_trait_method(&self) -> Self::Type;
+ #[unstable(feature = "unstable_declared", issue = "38412")]
+ fn unstable_declared_trait_method(&self) -> Self::Type;
+ }
+
+ #[stable(feature = "unit_test", since = "0.0.0")]
+ impl Trait for Record {
+ type Type = i32;
+ fn stable_trait_method(&self) -> i32 { self.d_priv }
+ fn unstable_undeclared_trait_method(&self) -> i32 { self.d_priv }
+ fn unstable_declared_trait_method(&self) -> i32 { self.d_priv }
+ }
+
+ #[stable(feature = "unit_test", since = "0.0.0")]
+ impl Trait for Tuple {
+ type Type = i32;
+ fn stable_trait_method(&self) -> i32 { self.3 }
+ fn unstable_undeclared_trait_method(&self) -> i32 { self.3 }
+ fn unstable_declared_trait_method(&self) -> i32 { self.3 }
+ }
+
+ impl Record {
+ #[unstable(feature = "unstable_undeclared", issue = "38412")]
+ pub fn unstable_undeclared(&self) -> i32 { self.d_priv }
+ #[unstable(feature = "unstable_declared", issue = "38412")]
+ pub fn unstable_declared(&self) -> i32 { self.d_priv }
+ #[stable(feature = "unit_test", since = "0.0.0")]
+ pub fn stable(&self) -> i32 { self.d_priv }
+
+ #[unstable(feature = "unstable_undeclared", issue = "38412")] // SILLY
+ pub(crate) fn pub_crate(&self) -> i32 { self.d_priv }
+ #[unstable(feature = "unstable_declared", issue = "38412")] // SILLY
+ pub(m) fn pub_mod(&self) -> i32 { self.d_priv }
+ #[stable(feature = "unit_test", since = "0.0.0")] // SILLY
+ fn private(&self) -> i32 { self.d_priv }
+ }
+
+ impl Tuple {
+ #[unstable(feature = "unstable_undeclared", issue = "38412")]
+ pub fn unstable_undeclared(&self) -> i32 { self.0 }
+ #[unstable(feature = "unstable_declared", issue = "38412")]
+ pub fn unstable_declared(&self) -> i32 { self.0 }
+ #[stable(feature = "unit_test", since = "0.0.0")]
+ pub fn stable(&self) -> i32 { self.0 }
+
+ pub(crate) fn pub_crate(&self) -> i32 { self.0 }
+ pub(m) fn pub_mod(&self) -> i32 { self.0 }
+ fn private(&self) -> i32 { self.0 }
+ }
+}
--- /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.
+
+// aux-build:pub_and_stability.rs
+
+#![feature(staged_api)]
+#![feature(unused_feature)]
+
+// A big point of this test is that we *declare* `unstable_declared`,
+// but do *not* declare `unstable_undeclared`. This way we can check
+// that the compiler is letting in uses of declared feature-gated
+// stuff but still rejecting uses of undeclared feature-gated stuff.
+#![feature(unstable_declared)]
+
+extern crate pub_and_stability;
+use pub_and_stability::{Record, Trait, Tuple};
+
+fn main() {
+ // Okay
+ let Record { .. } = Record::new();
+ // Okay (for now; see RFC Issue #902)
+ let Tuple(..) = Tuple::new();
+
+ // Okay
+ let Record { a_stable_pub: _, a_unstable_declared_pub: _, .. } = Record::new();
+ // Okay (for now; see RFC Issue #902)
+ let Tuple(_, _, ..) = Tuple::new(); // analogous to above
+
+ let Record { a_stable_pub: _, a_unstable_declared_pub: _, a_unstable_undeclared_pub: _, .. } =
+ Record::new();
+ //~^^ ERROR use of unstable library feature 'unstable_undeclared'
+
+ let Tuple(_, _, _, ..) = Tuple::new(); // analogous to previous
+ //~^ ERROR use of unstable library feature 'unstable_undeclared'
+
+ let r = Record::new();
+ let t = Tuple::new();
+
+ r.a_stable_pub;
+ r.a_unstable_declared_pub;
+ r.a_unstable_undeclared_pub; //~ ERROR use of unstable library feature
+ r.b_crate; //~ ERROR is private
+ r.c_mod; //~ ERROR is private
+ r.d_priv; //~ ERROR is private
+
+ t.0;
+ t.1;
+ t.2; //~ ERROR use of unstable library feature
+ t.3; //~ ERROR is private
+ t.4; //~ ERROR is private
+ t.5; //~ ERROR is private
+
+ r.stable_trait_method();
+ r.unstable_declared_trait_method();
+ r.unstable_undeclared_trait_method(); //~ ERROR use of unstable library feature
+
+ r.stable();
+ r.unstable_declared();
+ r.unstable_undeclared(); //~ ERROR use of unstable library feature
+
+ r.pub_crate(); //~ ERROR `pub_crate` is private
+ r.pub_mod(); //~ ERROR `pub_mod` is private
+ r.private(); //~ ERROR `private` is private
+
+ let t = Tuple::new();
+ t.stable_trait_method();
+ t.unstable_declared_trait_method();
+ t.unstable_undeclared_trait_method(); //~ ERROR use of unstable library feature
+
+ t.stable();
+ t.unstable_declared();
+ t.unstable_undeclared(); //~ ERROR use of unstable library feature
+
+ t.pub_crate(); //~ ERROR `pub_crate` is private
+ t.pub_mod(); //~ ERROR `pub_mod` is private
+ t.private(); //~ ERROR `private` is private
+
+}
// except according to those terms.
#![crate_type = "proc-macro"]
-#![feature(proc_macro, proc_macro_lib)]
extern crate proc_macro;
// force-host
// no-prefer-dynamic
-#![feature(proc_macro, proc_macro_lib)]
#![crate_type = "proc-macro"]
extern crate proc_macro;
// force-host
// no-prefer-dynamic
-#![feature(proc_macro)]
-#![feature(proc_macro_lib)]
#![crate_type = "proc-macro"]
extern crate proc_macro;
// force-host
// no-prefer-dynamic
-#![feature(proc_macro)]
-#![feature(proc_macro_lib)]
#![crate_type = "proc-macro"]
extern crate proc_macro;
// no-prefer-dynamic
// force-host
-#![feature(proc_macro)]
-#![feature(proc_macro_lib)]
#![crate_type = "proc-macro"]
extern crate proc_macro;
// no-prefer-dynamic
// force-host
-#![feature(proc_macro)]
-#![feature(proc_macro_lib)]
#![crate_type = "proc-macro"]
extern crate proc_macro;
// force-host
// no-prefer-dynamic
-#![feature(proc_macro)]
-#![feature(proc_macro_lib)]
#![crate_type = "proc-macro"]
extern crate proc_macro;
// force-host
// no-prefer-dynamic
-#![feature(proc_macro)]
-#![feature(proc_macro_lib)]
#![crate_type = "proc-macro"]
extern crate proc_macro;
--- /dev/null
+// Copyright 2016 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.
+
+// force-host
+// no-prefer-dynamic
+
+#![feature(proc_macro, proc_macro_lib)]
+#![crate_type = "proc-macro"]
+
+extern crate proc_macro;
+
+#[proc_macro_derive(A)]
+pub fn derive_a(_: proc_macro::TokenStream) -> proc_macro::TokenStream {
+ "fn f() { println!(\"{}\", foo); }".parse().unwrap()
+}
// no-prefer-dynamic
#![crate_type = "proc-macro"]
-#![feature(proc_macro, proc_macro_lib)]
extern crate proc_macro;
// aux-build:derive-bad.rs
-#![feature(proc_macro)]
-
#[macro_use]
extern crate derive_bad;
// aux-build:derive-a.rs
-#![feature(proc_macro)]
#![allow(warnings)]
#[macro_use]
// aux-build:derive-unstable-2.rs
-#![feature(proc_macro)]
#![allow(warnings)]
#[macro_use]
// aux-build:derive-unstable.rs
-#![feature(proc_macro)]
#![allow(warnings)]
#[macro_use]
// error-pattern: cannot export macro_rules! macros from a `proc-macro` crate
#![crate_type = "proc-macro"]
-#![feature(proc_macro)]
#[macro_export]
macro_rules! foo {
+++ /dev/null
-// Copyright 2016 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.
-
-// error-pattern: the `proc-macro` crate type is experimental
-
-#![crate_type = "proc-macro"]
+++ /dev/null
-// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-extern crate proc_macro; //~ ERROR: use of unstable library feature
-
-fn main() {}
+++ /dev/null
-// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-#![crate_type = "proc-macro"]
-
-#[proc_macro_derive(Foo)] //~ ERROR: is an experimental feature
-pub fn foo() {
-}
+++ /dev/null
-// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-// aux-build:derive-a.rs
-
-#[macro_use]
-extern crate derive_a;
-
-#[derive(A)] //~ ERROR custom derive macros are experimentally supported
-struct S;
+++ /dev/null
-// Copyright 2016 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.
-
-#[cfg(proc_macro)] //~ ERROR: experimental and subject to change
-fn foo() {}
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-#![feature(proc_macro, proc_macro_lib)]
-
extern crate proc_macro;
#[proc_macro_derive(Foo)]
// aux-build:derive-a.rs
-#![feature(proc_macro)]
#![allow(warnings)]
#[macro_use]
// aux-build:derive-a-b.rs
-#![feature(proc_macro)]
-
#[macro_use]
extern crate derive_a_b;
--- /dev/null
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// aux-build:issue_38586.rs
+
+#![feature(proc_macro)]
+
+#[macro_use]
+extern crate issue_38586;
+
+#[derive(A)] //~ ERROR `foo`
+struct A;
+
+fn main() {}
// aux-build:derive-b.rs
-#![feature(proc_macro)]
#![allow(warnings)]
#[macro_use]
// aux-build:derive-panic.rs
-#![feature(proc_macro)]
-
#[macro_use]
extern crate derive_panic;
// aux-build:derive-b.rs
-#![feature(proc_macro)]
#![allow(warnings)]
#[macro_use]
// except according to those terms.
#![crate_type = "proc-macro"]
-#![feature(proc_macro, proc_macro_lib)]
extern crate proc_macro;
// except according to those terms.
#![crate_type = "proc-macro"]
-#![feature(proc_macro, proc_macro_lib)]
extern crate proc_macro;
// aux-build:derive-a.rs
-#![feature(proc_macro)]
-
#[macro_use]
extern crate derive_a;
#[macro_use]
// except according to those terms.
#![crate_type = "proc-macro"]
-#![feature(proc_macro, proc_macro_lib)]
#![allow(warnings)]
extern crate proc_macro;
// option. This file may not be copied, modified, or distributed
// except according to those terms.
+#![deny(unreachable_patterns)]
+
fn main() {
let foo = Some(1);
match foo {
- Some(bar) => {/* ... */}
+ Some(_) => {/* ... */}
None => {/* ... */}
- _ => {/* ... */} //~ ERROR E0001
+ _ => {/* ... */} //~ ERROR unreachable pattern
}
}
+++ /dev/null
-// Copyright 2016 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 Foo {
- foo: Vec<u32>,
-}
-
-impl Copy for Foo { }
-//~^ ERROR E0204
-//~| NOTE field `foo` does not implement `Copy`
-
-#[derive(Copy)]
-//~^ ERROR E0204
-//~| NOTE field `ty` does not implement `Copy`
-//~| NOTE in this expansion of #[derive(Copy)]
-struct Foo2<'a> {
- ty: &'a mut bool,
-}
-
-fn main() {
-}
+++ /dev/null
-// Copyright 2016 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 Foo {
- Bar(Vec<u32>),
- Baz,
-}
-
-impl Copy for Foo { }
-//~^ ERROR the trait `Copy` may not be implemented for this type
-//~| NOTE variant `Bar` does not implement `Copy`
-
-#[derive(Copy)]
-//~^ ERROR the trait `Copy` may not be implemented for this type
-//~| NOTE variant `Bar` does not implement `Copy`
-//~| NOTE in this expansion of #[derive(Copy)]
-enum Foo2<'a> {
- Bar(&'a mut bool),
- Baz,
-}
-
-fn main() {
-}
+++ /dev/null
-// Copyright 2015 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.
-
-#![allow(unused_imports)]
-
-// Note: the relevant lint pass here runs before some of the constant
-// evaluation below (e.g. that performed by trans and llvm), so if you
-// change this warn to a deny, then the compiler will exit before
-// those errors are detected.
-
-use std::fmt;
-use std::{i8, i16, i32, i64, isize};
-use std::{u8, u16, u32, u64, usize};
-
-const VALS_I8: (i8, i8, i8, i8) =
- (-i8::MIN,
- i8::MIN - 1,
- i8::MAX + 1,
- i8::MIN * 2,
- );
-
-const VALS_I16: (i16, i16, i16, i16) =
- (-i16::MIN,
- i16::MIN - 1,
- i16::MAX + 1,
- i16::MIN * 2,
- );
-
-const VALS_I32: (i32, i32, i32, i32) =
- (-i32::MIN,
- i32::MIN - 1,
- i32::MAX + 1,
- i32::MIN * 2,
- );
-
-const VALS_I64: (i64, i64, i64, i64) =
- (-i64::MIN,
- i64::MIN - 1,
- i64::MAX + 1,
- i64::MAX * 2,
- );
-
-const VALS_U8: (u8, u8, u8, u8) =
- (-u8::MIN,
- //~^ ERROR unary negation of unsigned integer
- //~| HELP use a cast or the `!` operator
- u8::MIN - 1,
- u8::MAX + 1,
- u8::MAX * 2,
- );
-
-const VALS_U16: (u16, u16, u16, u16) =
- (-u16::MIN,
- //~^ ERROR unary negation of unsigned integer
- //~| HELP use a cast or the `!` operator
- u16::MIN - 1,
- u16::MAX + 1,
- u16::MAX * 2,
- );
-
-const VALS_U32: (u32, u32, u32, u32) =
- (-u32::MIN,
- //~^ ERROR unary negation of unsigned integer
- //~| HELP use a cast or the `!` operator
- u32::MIN - 1,
- u32::MAX + 1,
- u32::MAX * 2,
- );
-
-const VALS_U64: (u64, u64, u64, u64) =
- (-u64::MIN,
- //~^ ERROR unary negation of unsigned integer
- //~| HELP use a cast or the `!` operator
- u64::MIN - 1,
- u64::MAX + 1,
- u64::MAX * 2,
- );
-
-fn main() {
- foo(VALS_I8);
- foo(VALS_I16);
- foo(VALS_I32);
- foo(VALS_I64);
-
- foo(VALS_U8);
- foo(VALS_U16);
- foo(VALS_U32);
- foo(VALS_U64);
-}
-
-fn foo<T:fmt::Debug>(x: T) {
- println!("{:?}", x);
-}
fn neg(self) -> u32 { 0 }
}
-// FIXME(eddyb) move this back to a `-1` literal when
-// MIR building stops eagerly erroring in that case.
-const _MAX: usize = -(2 - 1);
-//~^ WARN unary negation of unsigned integer
-//~| ERROR unary negation of unsigned integer
-//~| HELP use a cast or the `!` operator
-
fn main() {
+ let _max: usize = -1;
+ //~^ ERROR cannot apply unary operator `-` to type `usize`
+
let x = 5u8;
- let _y = -x; //~ ERROR unary negation of unsigned integer
- //~^ HELP use a cast or the `!` operator
+ let _y = -x;
+ //~^ ERROR cannot apply unary operator `-` to type `u8`
+
-S; // should not trigger the gate; issue 26840
}
+++ /dev/null
-// Copyright 2015 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 negating unsigned integers doesn't compile
-
-struct S;
-impl std::ops::Neg for S {
- type Output = u32;
- fn neg(self) -> u32 { 0 }
-}
-
-fn main() {
- let a = -1;
- //~^ ERROR E0080
- //~| unary negation of unsigned integer
- let _b : u8 = a; // for infering variable a to u8.
-
- let _d = -1u8;
- //~^ ERROR E0080
- //~| unary negation of unsigned integer
-
- for _ in -10..10u8 {}
- //~^ ERROR E0080
- //~| unary negation of unsigned integer
-
- -S; // should not trigger the gate; issue 26840
-}
#![feature(box_patterns)]
#![feature(box_syntax)]
+#![allow(dead_code)]
+#![allow(unused_variables)]
+#![deny(unreachable_patterns)]
enum IntList {
Cons(isize, Box<IntList>),
fn tail(source_list: &IntList) -> IntList {
match source_list {
&IntList::Cons(val, box ref next_list) => tail(next_list),
- &IntList::Cons(val, box Nil) => IntList::Cons(val, box Nil),
+ &IntList::Cons(val, box IntList::Nil) => IntList::Cons(val, box IntList::Nil),
//~^ ERROR unreachable pattern
-//~^^ WARN pattern binding `Nil` is named the same as one of the variants of the type `IntList`
_ => panic!()
}
}
// except according to those terms.
#![feature(slice_patterns)]
+#![allow(unused_variables)]
+#![deny(unreachable_patterns)]
fn main() {
let sl = vec![1,2,3];
// option. This file may not be copied, modified, or distributed
// except according to those terms.
+#![allow(overflowing_literals)]
+#![deny(unreachable_patterns)]
+
fn test(val: u8) {
match val {
256 => print!("0b1110\n"),
// option. This file may not be copied, modified, or distributed
// except according to those terms.
+#![deny(unreachable_patterns)]
+#![allow(unused_variables)]
+#![allow(non_snake_case)]
+
pub enum E {
A,
B,
--- /dev/null
+// Copyright 2016 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 trait Resources<'a> {}
+
+pub trait Buffer<'a, R: Resources<'a>> {
+ fn select(&self) -> BufferViewHandle<R>;
+ //~^ ERROR mismatched types
+ //~| lifetime mismatch
+ //~| NOTE expected type `Resources<'_>`
+ //~| NOTE found type `Resources<'a>`
+ //~| NOTE the lifetime 'a as defined on the method body at 14:4...
+ //~| NOTE ...does not necessarily outlive the anonymous lifetime #1 defined on the method body
+ //~| ERROR mismatched types
+ //~| lifetime mismatch
+ //~| NOTE expected type `Resources<'_>`
+ //~| NOTE found type `Resources<'a>`
+ //~| NOTE the anonymous lifetime #1 defined on the method body at 14:4...
+ //~| NOTE ...does not necessarily outlive the lifetime 'a as defined on the method body
+}
+
+pub struct BufferViewHandle<'a, R: 'a+Resources<'a>>(&'a R);
+
+fn main() {}
--- /dev/null
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![deny(unreachable_patterns)]
+
+fn main() {
+ match "world" {
+ "hello" => {}
+ _ => {},
+ }
+
+ match "world" {
+ ref _x if false => {}
+ "hello" => {}
+ "hello" => {} //~ ERROR unreachable pattern
+ _ => {},
+ }
+}
+
match "world" { //~ ERROR non-exhaustive patterns: `&_`
ref _x if false => {}
"hello" => {}
- "hello" => {} //~ ERROR unreachable pattern
}
}
// option. This file may not be copied, modified, or distributed
// except according to those terms.
+#![allow(dead_code)]
+#![allow(unused_variables)]
+#![allow(non_snake_case)]
+#![deny(unreachable_patterns)]
+
enum Stack<T> {
Nil,
Cons(T, Box<Stack<T>>)
// option. This file may not be copied, modified, or distributed
// except according to those terms.
+#![allow(dead_code)]
+#![allow(unused_variables)]
+#![allow(non_snake_case)]
+#![deny(unreachable_patterns)]
+//~^ NOTE lint level defined here
+//~^^ NOTE lint level defined here
+//~^^^ NOTE lint level defined here
+
+#[derive(Clone, Copy)]
enum Enum {
Var1,
Var2,
//~^ ERROR unreachable pattern
//~^^ NOTE this is an unreachable pattern
};
- // `_` need not emit a note, it is pretty obvious already.
- let t = (Var1, Var1);
- match t {
- (Var1, b) => (),
- _ => (),
- anything => ()
- //~^ ERROR unreachable pattern
- //~^^ NOTE this is an unreachable pattern
- };
}
box NodeKind::Element(ed) => match ed.kind { //~ ERROR non-exhaustive patterns
box ElementKind::HTMLImageElement(ref d) if d.image.is_some() => { true }
},
- _ => panic!("WAT") //~ ERROR unreachable pattern
};
}
--- /dev/null
+// Copyright 2016 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 RepeatMut<'a, T>(T, &'a ());
+
+impl<'a, T: 'a> Iterator for RepeatMut<'a, T> {
+ type Item = &'a mut T;
+ fn next(&'a mut self) -> Option<Self::Item>
+ //~^ ERROR method not compatible with trait
+ //~| lifetime mismatch
+ //~| NOTE expected type `fn(&mut RepeatMut<'a, T>) -> std::option::Option<&mut T>`
+ //~| NOTE found type `fn(&'a mut RepeatMut<'a, T>) -> std::option::Option<&mut T>`
+ {
+ //~^ NOTE the anonymous lifetime #1 defined on the body
+ //~| NOTE ...does not necessarily outlive the lifetime 'a as defined on the body
+ Some(&mut self.0)
+ }
+}
+
+fn main() {}
--- /dev/null
+// Copyright 2016 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 Box(a) = loop { };
+ //~^ ERROR field `0` of struct `std::boxed::Box` is private
+
+ // (The below is a trick to allow compiler to infer a type for
+ // variable `a` without attempting to ascribe a type to the
+ // pattern or otherwise attempting to name the Box type, which
+ // would run afoul of issue #22207)
+ let _b: *mut i32 = *a;
+}
let _ = || -> Result<(), ()> { try!(Ok(())); Ok(()) }; // issue #37345
- macro_rules! m {
- () => { $crate::foo::bar(); }
- }
- m!(); // issue #37357
+ macro_rules! m { () => {
+ $crate::foo::bar(); // issue #37357
+ ::foo::bar(); // issue #38682
+ } }
+ m!();
}
--- /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 self::Direction::{North, East, South, West};
+
+struct NewBool(bool);
+
+enum Direction {
+ North,
+ East,
+ South,
+ West
+}
+
+const TRUE_TRUE: (bool, bool) = (true, true);
+
+fn nonexhaustive_1() {
+ match (true, false) {
+ //~^ ERROR non-exhaustive patterns: `(true, false)` not covered
+ TRUE_TRUE => (),
+ (false, false) => (),
+ (false, true) => ()
+ }
+}
+
+const NONE: Option<Direction> = None;
+const EAST: Direction = East;
+
+fn nonexhaustive_2() {
+ match Some(Some(North)) {
+ //~^ ERROR non-exhaustive patterns: `Some(Some(West))` not covered
+ Some(NONE) => (),
+ Some(Some(North)) => (),
+ Some(Some(EAST)) => (),
+ Some(Some(South)) => (),
+ None => ()
+ }
+}
+
+const NEW_FALSE: NewBool = NewBool(false);
+struct Foo {
+ bar: Option<Direction>,
+ baz: NewBool
+}
+
+const STATIC_FOO: Foo = Foo { bar: None, baz: NEW_FALSE };
+
+fn nonexhaustive_3() {
+ match (Foo { bar: Some(North), baz: NewBool(true) }) {
+ //~^ ERROR non-exhaustive patterns: `Foo { bar: Some(North), baz: NewBool(true) }`
+ Foo { bar: None, baz: NewBool(true) } => (),
+ Foo { bar: _, baz: NEW_FALSE } => (),
+ Foo { bar: Some(West), baz: NewBool(true) } => (),
+ Foo { bar: Some(South), .. } => (),
+ Foo { bar: Some(EAST), .. } => ()
+ }
+}
+
+fn main() {
+ nonexhaustive_1();
+ nonexhaustive_2();
+ nonexhaustive_3();
+}
+
// <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.
+
+#![allow(dead_code)]
+#![deny(unreachable_patterns)]
+
use self::Direction::{North, East, South, West};
+#[derive(PartialEq, Eq)]
struct NewBool(bool);
+#[derive(PartialEq, Eq)]
enum Direction {
North,
East,
const TRUE_TRUE: (bool, bool) = (true, true);
-fn nonexhaustive_1() {
- match (true, false) {
- //~^ ERROR non-exhaustive patterns: `(true, false)` not covered
- TRUE_TRUE => (),
- (false, false) => (),
- (false, true) => ()
- }
-}
-
fn unreachable_1() {
match (true, false) {
TRUE_TRUE => (),
const NONE: Option<Direction> = None;
const EAST: Direction = East;
-fn nonexhaustive_2() {
- match Some(Some(North)) {
- //~^ ERROR non-exhaustive patterns: `Some(Some(West))` not covered
- Some(NONE) => (),
- Some(Some(North)) => (),
- Some(Some(EAST)) => (),
- Some(Some(South)) => (),
- None => ()
- }
-}
-
fn unreachable_2() {
match Some(Some(North)) {
Some(NONE) => (),
baz: NewBool
}
-const STATIC_FOO: Foo = Foo { bar: None, baz: NEW_FALSE };
-
-fn nonexhaustive_3() {
- match (Foo { bar: Some(North), baz: NewBool(true) }) {
- //~^ ERROR non-exhaustive patterns: `Foo { bar: Some(North), baz: NewBool(true) }`
- Foo { bar: None, baz: NewBool(true) } => (),
- Foo { bar: _, baz: NEW_FALSE } => (),
- Foo { bar: Some(West), baz: NewBool(true) } => (),
- Foo { bar: Some(South), .. } => (),
- Foo { bar: Some(EAST), .. } => ()
- }
-}
-
fn unreachable_3() {
match (Foo { bar: Some(EAST), baz: NewBool(true) }) {
Foo { bar: None, baz: NewBool(true) } => (),
}
fn main() {
- nonexhaustive_1();
- nonexhaustive_2();
- nonexhaustive_3();
unreachable_1();
unreachable_2();
unreachable_3();
--- /dev/null
+// Copyright 2016 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(advanced_slice_patterns, slice_patterns)]
+
+fn main() {
+ let buf = &[0, 1, 2, 3];
+
+ match buf { //~ ERROR non-exhaustive
+ b"AAAA" => {}
+ }
+
+ let buf: &[u8] = buf;
+
+ match buf { //~ ERROR non-exhaustive
+ b"AAAA" => {}
+ }
+}
+
// except according to those terms.
#![feature(advanced_slice_patterns, slice_patterns)]
+#![deny(unreachable_patterns)]
fn main() {
let buf = &[0, 1, 2, 3];
_ => {}
}
- match buf { //~ ERROR non-exhaustive
- b"AAAA" => {}
- }
-
let buf: &[u8] = buf;
match buf {
b"AAAA" => {}, //~ ERROR unreachable pattern
_ => {}
}
-
- match buf { //~ ERROR non-exhaustive
- b"AAAA" => {}
- }
}
//error-pattern: unreachable
//error-pattern: unreachable
+#![deny(unreachable_patterns)]
+
fn main() {
match 5 {
1 ... 10 => { }
// except according to those terms.
#![feature(slice_patterns)]
+#![deny(unreachable_patterns)]
// The arity of `ref x` is always 1. If the pattern is compared to some non-structural type whose
// arity is always 0, an ICE occurs.
let homura = [1, 2, 3];
match homura {
- [1, ref madoka, 3] => (),
+ [1, ref _madoka, 3] => (),
[1, 2, 3] => (), //~ ERROR unreachable pattern
[_, _, _] => (),
}
fn check(list: &[Option<()>]) {
match list {
- //~^ ERROR `&[None, Some(_), None, _]` and `&[Some(_), Some(_), None, _]` not covered
+ //~^ ERROR `&[_, Some(_), None, _]` not covered
&[] => {},
&[_] => {},
&[_, _] => {},
// except according to those terms.
#![feature(slice_patterns)]
+#![deny(unreachable_patterns)]
fn a() {
let v = [1, 2, 3];
// except according to those terms.
#![feature(slice_patterns)]
+#![deny(unreachable_patterns)]
+#![allow(unused_variables)]
fn main() {
let x: Vec<(isize, isize)> = Vec::new();
"baz".to_string()];
let x: &[String] = &x;
match *x {
- [a, _, _, ..] => { println!("{}", a); }
+ [ref a, _, _, ..] => { println!("{}", a); }
[_, _, _, _, _] => { } //~ ERROR unreachable pattern
_ => { }
}
// option. This file may not be copied, modified, or distributed
// except according to those terms.
+#![deny(unreachable_patterns)]
+
struct Foo {
x: isize,
y: isize,
pub fn main() {
let a = Foo { x: 1, y: 2 };
match a {
- Foo { x: x, y: y } => (),
+ Foo { x: _x, y: _y } => (),
Foo { .. } => () //~ ERROR unreachable pattern
}
--- /dev/null
+// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(never_type)]
+
+mod foo {
+ pub struct SecretlyEmpty {
+ _priv: !,
+ }
+
+ pub struct NotSoSecretlyEmpty {
+ pub _pub: !,
+ }
+}
+
+struct NotSoSecretlyEmpty {
+ _priv: !,
+}
+
+enum Foo {
+ A(foo::SecretlyEmpty),
+ B(foo::NotSoSecretlyEmpty),
+ C(NotSoSecretlyEmpty),
+ D(u32),
+}
+
+fn main() {
+ let x: Foo = Foo::D(123);
+ let Foo::D(_y) = x; //~ ERROR refutable pattern in local binding: `A(_)` not covered
+}
+
--- /dev/null
+// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(box_patterns)]
+#![feature(slice_patterns)]
+#![feature(box_syntax)]
+#![feature(never_type)]
+#![deny(unreachable_patterns)]
+
+mod foo {
+ pub struct SecretlyEmpty {
+ _priv: !,
+ }
+}
+
+struct NotSoSecretlyEmpty {
+ _priv: !,
+}
+
+fn main() {
+ let x: &[!] = &[];
+
+ match x {
+ &[] => (),
+ &[..] => (), //~ ERROR unreachable pattern
+ };
+
+ let x: Result<Box<NotSoSecretlyEmpty>, &[Result<!, !>]> = Err(&[]);
+ match x {
+ Ok(box _) => (), //~ ERROR unreachable pattern
+ Err(&[]) => (),
+ Err(&[..]) => (), //~ ERROR unreachable pattern
+ }
+
+ let x: Result<foo::SecretlyEmpty, Result<NotSoSecretlyEmpty, u32>> = Err(Err(123));
+ match x {
+ Ok(_y) => (),
+ Err(Err(_y)) => (),
+ Err(Ok(_y)) => (), //~ ERROR unreachable pattern
+ }
+}
+
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-// error-pattern:unreachable pattern
-
#![feature(box_patterns)]
#![feature(box_syntax)]
+#![allow(dead_code)]
+#![deny(unreachable_patterns)]
+
+enum Foo { A(Box<Foo>, isize), B(usize), }
-enum foo { a(Box<foo>, isize), b(usize), }
+fn main() {
+ match Foo::B(1) {
+ Foo::B(_) | Foo::A(box _, 1) => { }
+ Foo::A(_, 1) => { } //~ ERROR unreachable pattern
+ _ => { }
+ }
+}
-fn main() { match foo::b(1) { foo::b(_) | foo::a(box _, 1) => { } foo::a(_, 1) => { } } }
--- /dev/null
+// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// ignore-windows failing on 64-bit bots FIXME #17638
+// ignore-lldb
+// ignore-aarch64
+
+// compile-flags:-g
+
+// gdb-command:run
+// gdb-command:next
+// gdb-check:[...]35[...]s
+// gdb-command:continue
+
+#![feature(omit_gdb_pretty_printer_section)]
+#![omit_gdb_pretty_printer_section]
+
+// IF YOU MODIFY THIS FILE, BE CAREFUL TO ADAPT THE LINE NUMBERS IN THE DEBUGGER COMMANDS
+
+// This test makes sure that gdb does not set unwanted breakpoints in inlined functions. If a
+// breakpoint existed in unwrap(), then calling `next` would (when stopped at `let s = ...`) stop
+// in unwrap() instead of stepping over the function invocation. By making sure that `s` is
+// contained in the output, after calling `next` just once, we can be sure that we did not stop in
+// unwrap(). (The testing framework doesn't allow for checking that some text is *not* contained in
+// the output, which is why we have to make the test in this kind of roundabout way)
+fn bar() -> isize {
+ let s = Some(5).unwrap(); // #break
+ s
+}
+
+fn main() {
+ let _ = bar();
+}
--- /dev/null
+// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// min-lldb-version: 310
+
+// aux-build:issue13213aux.rs
+
+extern crate issue13213aux;
+
+// compile-flags:-g
+
+// This tests make sure that we get no linker error when using a completely inlined static. Some
+// statics that are marked with AvailableExternallyLinkage in the importing crate, may actually not
+// be available because they have been optimized out from the exporting crate.
+fn main() {
+ let b: issue13213aux::S = issue13213aux::A;
+ println!("Nothing to do here...");
+}
--- /dev/null
+// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// min-lldb-version: 310
+
+// compile-flags:-g
+
+// No debugger interaction required: just make sure it compiles without
+// crashing.
+
+fn test(a: &Vec<u8>) {
+ print!("{}", a.len());
+}
+
+pub fn main() {
+ let data = vec![];
+ test(&data);
+}
--- /dev/null
+// Copyright 2013-2015 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.
+
+
+// This test makes sure that the LLDB pretty printer does not throw an exception
+// when trying to handle a Vec<> or anything else that contains zero-sized
+// fields.
+
+// min-lldb-version: 310
+// ignore-gdb
+// ignore-tidy-linelength
+
+// compile-flags:-g
+
+// === LLDB TESTS ==================================================================================
+// lldb-command:run
+
+// lldb-command:print v
+// lldb-check:[...]$0 = vec![1, 2, 3]
+// lldb-command:print zs
+// lldb-check:[...]$1 = StructWithZeroSizedField { x: ZeroSizedStruct, y: 123, z: ZeroSizedStruct, w: 456 }
+// lldb-command:continue
+
+#![allow(unused_variables)]
+#![allow(dead_code)]
+#![feature(omit_gdb_pretty_printer_section)]
+#![omit_gdb_pretty_printer_section]
+
+struct ZeroSizedStruct;
+
+struct StructWithZeroSizedField {
+ x: ZeroSizedStruct,
+ y: u32,
+ z: ZeroSizedStruct,
+ w: u64
+}
+
+fn main() {
+ let v = vec![1,2,3];
+
+ let zs = StructWithZeroSizedField {
+ x: ZeroSizedStruct,
+ y: 123,
+ z: ZeroSizedStruct,
+ w: 456
+ };
+
+ zzz(); // #break
+}
+
+fn zzz() { () }
--- /dev/null
+// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <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.
+
+// compile-flags:-C debuginfo=1
+// min-lldb-version: 310
+
+pub trait TraitWithDefaultMethod : Sized {
+ fn method(self) {
+ ()
+ }
+}
+
+struct MyStruct;
+
+impl TraitWithDefaultMethod for MyStruct { }
+
+pub fn main() {
+ MyStruct.method();
+}
+++ /dev/null
-// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-// ignore-windows failing on 64-bit bots FIXME #17638
-// ignore-lldb
-// ignore-aarch64
-
-// compile-flags:-g
-
-// gdb-command:run
-// gdb-command:next
-// gdb-check:[...]35[...]s
-// gdb-command:continue
-
-#![feature(omit_gdb_pretty_printer_section)]
-#![omit_gdb_pretty_printer_section]
-
-// IF YOU MODIFY THIS FILE, BE CAREFUL TO ADAPT THE LINE NUMBERS IN THE DEBUGGER COMMANDS
-
-// This test makes sure that gdb does not set unwanted breakpoints in inlined functions. If a
-// breakpoint existed in unwrap(), then calling `next` would (when stopped at `let s = ...`) stop
-// in unwrap() instead of stepping over the function invocation. By making sure that `s` is
-// contained in the output, after calling `next` just once, we can be sure that we did not stop in
-// unwrap(). (The testing framework doesn't allow for checking that some text is *not* contained in
-// the output, which is why we have to make the test in this kind of roundabout way)
-fn bar() -> isize {
- let s = Some(5).unwrap(); // #break
- s
-}
-
-fn main() {
- let _ = bar();
-}
+++ /dev/null
-// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-// min-lldb-version: 310
-
-// aux-build:issue13213aux.rs
-
-extern crate issue13213aux;
-
-// compile-flags:-g
-
-// This tests make sure that we get no linker error when using a completely inlined static. Some
-// statics that are marked with AvailableExternallyLinkage in the importing crate, may actually not
-// be available because they have been optimized out from the exporting crate.
-fn main() {
- let b: issue13213aux::S = issue13213aux::A;
- println!("Nothing to do here...");
-}
+++ /dev/null
-// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-// min-lldb-version: 310
-
-// compile-flags:-g
-
-// No debugger interaction required: just make sure it compiles without
-// crashing.
-
-fn test(a: &Vec<u8>) {
- print!("{}", a.len());
-}
-
-pub fn main() {
- let data = vec![];
- test(&data);
-}
+++ /dev/null
-// Copyright 2013-2015 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.
-
-
-// This test makes sure that the LLDB pretty printer does not throw an exception
-// when trying to handle a Vec<> or anything else that contains zero-sized
-// fields.
-
-// min-lldb-version: 310
-// ignore-gdb
-// ignore-tidy-linelength
-
-// compile-flags:-g
-
-// === LLDB TESTS ==================================================================================
-// lldb-command:run
-
-// lldb-command:print v
-// lldb-check:[...]$0 = vec![1, 2, 3]
-// lldb-command:print zs
-// lldb-check:[...]$1 = StructWithZeroSizedField { x: ZeroSizedStruct, y: 123, z: ZeroSizedStruct, w: 456 }
-// lldb-command:continue
-
-#![allow(unused_variables)]
-#![allow(dead_code)]
-#![feature(omit_gdb_pretty_printer_section)]
-#![omit_gdb_pretty_printer_section]
-
-struct ZeroSizedStruct;
-
-struct StructWithZeroSizedField {
- x: ZeroSizedStruct,
- y: u32,
- z: ZeroSizedStruct,
- w: u64
-}
-
-fn main() {
- let v = vec![1,2,3];
-
- let zs = StructWithZeroSizedField {
- x: ZeroSizedStruct,
- y: 123,
- z: ZeroSizedStruct,
- w: 456
- };
-
- zzz(); // #break
-}
-
-fn zzz() { () }
+++ /dev/null
-// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <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.
-
-// compile-flags:-C debuginfo=1
-// min-lldb-version: 310
-
-pub trait TraitWithDefaultMethod : Sized {
- fn method(self) {
- ()
- }
-}
-
-struct MyStruct;
-
-impl TraitWithDefaultMethod for MyStruct { }
-
-pub fn main() {
- MyStruct.method();
-}
// except according to those terms.
// min-lldb-version: 310
-// ignore-macos FIXME(#37479)
// compile-flags:-g
// lldb-command:run
// lldb-command:print u
-// lldb-check:[...]$0 = { a = ('\x02', '\x02') b = 514 }
+// lldb-check:[...]$0 = U { a: ('\x02', '\x02'), b: 514 }
// lldb-command:print union_smoke::SU
-// lldb-check:[...]$1 = 257
+// lldb-check:[...]$1 = U { a: ('\x01', '\x01'), b: 257 }
#![allow(unused)]
#![feature(omit_gdb_pretty_printer_section)]
--- /dev/null
+// Copyright 2016 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.
+
+// compile-flags:-g
+
+// === GDB TESTS ===================================================================================
+
+// gdb-command:run
+
+// gdb-command:print *a
+// gdbg-check:$1 = {value = [...] "abc"}
+// gdbr-check:$1 = unsized::Foo<[u8]> {value: [...]}
+
+// gdb-command:print *b
+// gdbg-check:$2 = {value = {value = [...] "abc"}}
+// gdbr-check:$2 = unsized::Foo<unsized::Foo<[u8]>> {value: unsized::Foo<[u8]> {value: [...]}}
+
+
+#![feature(omit_gdb_pretty_printer_section)]
+#![omit_gdb_pretty_printer_section]
+
+struct Foo<T: ?Sized> {
+ value: T
+}
+
+fn main() {
+ let foo: Foo<Foo<[u8; 4]>> = Foo {
+ value: Foo {
+ value: *b"abc\0"
+ }
+ };
+ let a: &Foo<[u8]> = &foo.value;
+ let b: &Foo<Foo<[u8]>> = &foo;
+
+ zzz(); // #break
+}
+
+fn zzz() { () }
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-#![feature(proc_macro)]
#![allow(unused)]
#![crate_type = "proc-macro"]
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-#![feature(proc_macro)]
#![allow(unused)]
#![crate_type = "proc-macro"]
// except according to those terms.
#![crate_type = "proc-macro"]
-#![feature(proc_macro, proc_macro_lib)]
extern crate proc_macro;
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-#![feature(proc_macro)]
-
#[macro_use]
extern crate foo;
// except according to those terms.
#![crate_type = "proc-macro"]
-#![feature(proc_macro)]
-#![feature(proc_macro_lib)]
extern crate proc_macro;
-include ../tools.mk
all:
- # check that #[ignore(cfg(...))] does the right thing.
+ # check that #[cfg_attr(..., ignore)] does the right thing.
$(RUSTC) --test test-ignore-cfg.rs --cfg ignorecfg
$(call RUN,test-ignore-cfg) | grep 'shouldnotignore ... ok'
$(call RUN,test-ignore-cfg) | grep 'shouldignore ... ignored'
// aux-build:add-impl.rs
-#![feature(proc_macro)]
-
#[macro_use]
extern crate add_impl;
// aux-build:append-impl.rs
-#![feature(proc_macro)]
#![allow(warnings)]
#[macro_use]
// no-prefer-dynamic
#![crate_type = "proc-macro"]
-#![feature(proc_macro)]
-#![feature(proc_macro_lib)]
extern crate proc_macro;
// force-host
// no-prefer-dynamic
-#![feature(proc_macro)]
-#![feature(proc_macro_lib)]
#![crate_type = "proc-macro"]
extern crate proc_macro;
// no-prefer-dynamic
#![crate_type = "proc-macro"]
-#![feature(proc_macro)]
-#![feature(proc_macro_lib)]
extern crate proc_macro;
// no-prefer-dynamic
#![crate_type = "proc-macro"]
-#![feature(proc_macro)]
-#![feature(proc_macro_lib)]
extern crate proc_macro;
// no-prefer-dynamic
#![crate_type = "proc-macro"]
-#![feature(proc_macro)]
-#![feature(proc_macro_lib)]
extern crate proc_macro;
// no-prefer-dynamic
#![crate_type = "proc-macro"]
-#![feature(proc_macro)]
-#![feature(proc_macro_lib)]
extern crate proc_macro;
// no-prefer-dynamic
#![crate_type = "proc-macro"]
-#![feature(proc_macro)]
-#![feature(proc_macro_lib)]
extern crate proc_macro;
// ignore-test
-#![feature(macro_reexport, proc_macro)]
+#![feature(macro_reexport)]
#[macro_reexport(A)]
extern crate derive_a;
// no-prefer-dynamic
// compile-flags:--crate-type proc-macro
-#![feature(proc_macro)]
-#![feature(proc_macro_lib)]
-
extern crate proc_macro;
use proc_macro::TokenStream;
// no-prefer-dynamic
#![crate_type = "proc-macro"]
-#![feature(proc_macro)]
-#![feature(proc_macro_lib)]
extern crate proc_macro;
// no-prefer-dynamic
#![crate_type = "proc-macro"]
-#![feature(proc_macro)]
-#![feature(proc_macro_lib)]
#![deny(warnings)]
extern crate proc_macro;
// aux-build:double.rs
-#![feature(proc_macro)]
#![allow(unused)]
#[macro_use]
// aux-build:derive-b.rs
// ignore-stage1
-#![feature(proc_macro)]
-
#[macro_use]
extern crate derive_b;
// aux-build:derive-same-struct.rs
-#![feature(proc_macro)]
-
#[macro_use]
extern crate derive_same_struct;
// compile-flags: --test
#![crate_type = "proc-macro"]
-#![feature(proc_macro)]
-#![feature(proc_macro, proc_macro_lib)]
extern crate proc_macro;
// aux-build:expand-with-a-macro.rs
// ignore-stage1
-#![feature(proc_macro)]
#![deny(warnings)]
#[macro_use]
// aux-build:derive-atob.rs
// aux-build:derive-ctod.rs
-#![feature(proc_macro)]
-
#[macro_use]
extern crate derive_atob;
#[macro_use]
// aux-build:derive-a.rs
// ignore-stage1
-#![feature(proc_macro)]
-
#[macro_use]
extern crate derive_a;
// aux-build:derive-nothing.rs
// ignore-stage1
-#![feature(proc_macro)]
-
#[macro_use]
extern crate derive_nothing;
// aux-build:derive-a.rs
// aux-build:derive-reexport.rs
-#![feature(proc_macro)]
-
#[macro_use]
extern crate derive_reexport;
--- /dev/null
+// Copyright 2016 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.
+
+#[macro_export]
+macro_rules! foo { ($i:ident) => {} }
+
+#[macro_export]
+macro_rules! foo { () => {} }
--- /dev/null
+// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(never_type)]
+#![feature(slice_patterns)]
+#![allow(unreachable_patterns)]
+#![allow(unreachable_code)]
+
+#[allow(dead_code)]
+fn foo(z: !) {
+ let x: Result<!, !> = Ok(z);
+
+ let Ok(_y) = x;
+ let Err(_y) = x;
+
+ let x = [z; 1];
+
+ match x {};
+ match x {
+ [q] => q,
+ };
+}
+
+fn bar(nevers: &[!]) {
+ match nevers {
+ &[] => (),
+ };
+
+ match nevers {
+ &[] => (),
+ &[_] => (),
+ &[_, _, _, ..] => (),
+ };
+}
+
+fn main() {
+ let x: Result<u32, !> = Ok(123);
+ let Ok(y) = x;
+
+ assert_eq!(123, y);
+
+ match x {
+ Ok(y) => y,
+ };
+
+ match x {
+ Ok(y) => y,
+ Err(e) => match e {},
+ };
+
+ bar(&[]);
+}
+
--- /dev/null
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// ignore-stage0
+// ignore-stage1
+
+// MSVC doesn't support 128 bit integers, and other Windows
+// C compilers have very inconsistent views on how the ABI
+// should look like.
+
+// ignore-windows
+
+// Ignore 32 bit targets:
+// ignore-x86, ignore-arm
+
+#![feature(i128_type)]
+
+#[link(name = "rust_test_helpers", kind = "static")]
+extern "C" {
+ fn identity(f: u128) -> u128;
+ fn square(f: i128) -> i128;
+ fn sub(f: i128, f: i128) -> i128;
+}
+
+fn main() {
+ unsafe {
+ let a = 0x734C_C2F2_A521;
+ let b = 0x33EE_0E2A_54E2_59DA_A0E7_8E41;
+ let b_out = identity(b);
+ assert_eq!(b, b_out);
+ let a_square = square(a);
+ assert_eq!(b, a_square as u128);
+ let k = 0x1234_5678_9ABC_DEFF_EDCB_A987_6543_210;
+ let k_d = 0x2468_ACF1_3579_BDFF_DB97_530E_CA86_420;
+ let k_out = sub(k_d, k);
+ assert_eq!(k, k_out);
+ }
+}
assert_eq!((-z).checked_mul(-z), Some(0x734C_C2F2_A521));
assert_eq!((z).checked_mul(z), Some(0x734C_C2F2_A521));
assert_eq!((k).checked_mul(k), None);
+ let l: i128 = b(i128::min_value());
+ let o: i128 = b(17);
+ assert_eq!(l.checked_sub(b(2)), None);
+ assert_eq!(l.checked_add(l), None);
+ assert_eq!((-(l + 1)).checked_add(2), None);
+ assert_eq!(l.checked_sub(l), Some(0));
+ assert_eq!(b(1u128).checked_shl(b(127)), Some(1 << 127));
+ assert_eq!(o.checked_shl(b(128)), None);
}
--- /dev/null
+// Copyright 2015 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 Foo {
+ type T;
+}
+
+// should be able to use a trait with an associated type without specifying it as an argument
+trait Bar<F: Foo> {
+ fn bar(foo: &F);
+}
+
+pub fn main() {
+}
--- /dev/null
+// Copyright 2015 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() {
+ let command = "a";
+
+ match command {
+ "foo" => println!("foo"),
+ _ => println!("{}", command),
+ }
+}
--- /dev/null
+// Copyright 2015 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.
+
+// pretty-expanded FIXME #23616
+
+#![feature(core)]
+
+// This used to cause an ICE because the retslot for the "return" had the wrong type
+fn testcase<'a>() -> Box<Iterator<Item=usize> + 'a> {
+ return Box::new((0..3).map(|i| { return i; }));
+}
+
+fn main() {
+}
--- /dev/null
+// Copyright 2015 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() {
+ return ();
+
+ let x = ();
+ x
+}
--- /dev/null
+// Copyright 2015 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 Tr { type T; }
+impl Tr for u8 { type T=(); }
+struct S<I: Tr>(I::T);
+
+fn foo<I: Tr>(i: I::T) {
+ S::<I>(i);
+}
+
+fn main() {
+ foo::<u8>(());
+}
--- /dev/null
+// Copyright 2015 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(const_fn)]
+struct A {
+ field: usize,
+}
+const fn f() -> usize {
+ 5
+}
+fn main() {
+ let _ = [0; f()];
+}
--- /dev/null
+// Copyright 2016 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(repr_simd, test)]
+
+extern crate test;
+
+#[repr(simd)]
+pub struct Mu64(pub u64, pub u64, pub u64, pub u64);
+
+fn main() {
+ // This ensures an unaligned pointer even in optimized builds, though LLVM
+ // gets enough type information to actually not mess things up in that case,
+ // but at the time of writing this, it's enough to trigger the bug in
+ // non-optimized builds
+ unsafe {
+ let memory = &mut [0u64; 8] as *mut _ as *mut u8;
+ let misaligned_ptr: &mut [u8; 32] = {
+ std::mem::transmute(memory.offset(1))
+ };
+ *misaligned_ptr = std::mem::transmute(Mu64(1, 1, 1, 1));
+ test::black_box(memory);
+ }
+}
--- /dev/null
+// Copyright 2016 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 Foo<A: Repr>(<A as Repr>::Data);
+
+impl<A> Copy for Foo<A> where <A as Repr>::Data: Copy { }
+impl<A> Clone for Foo<A> where <A as Repr>::Data: Clone {
+ fn clone(&self) -> Self { Foo(self.0.clone()) }
+}
+
+trait Repr {
+ type Data;
+}
+
+impl<A> Repr for A {
+ type Data = u32;
+}
+
+fn main() {
+}
--- /dev/null
+// Copyright 2016 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.
+
+// compile-flags:-g
+
+// In this test we just want to make sure that the code below does not lead to
+// a debuginfo verification assertion during compilation. This was caused by the
+// closure in the guard being translated twice due to how match expressions are
+// handled.
+//
+// See https://github.com/rust-lang/rust/issues/34569 for details.
+
+fn main() {
+ match 0 {
+ e if (|| { e == 0 })() => {},
+ 1 => {},
+ _ => {}
+ }
+}
--- /dev/null
+// Copyright 2016 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.
+
+// This test case exposes conditions where the encoding of a trait object type
+// with projection predicates would differ between this crate and the upstream
+// crate, because the predicates were encoded in different order within each
+// crate. This led to different symbol hashes of functions using these type,
+// which in turn led to linker errors because the two crates would not agree on
+// the symbol name.
+// The fix was to make the order in which predicates get encoded stable.
+
+// aux-build:issue34796aux.rs
+extern crate issue34796aux;
+
+fn mk<T>() -> T { loop {} }
+
+struct Data<T, E> {
+ data: T,
+ error: E,
+}
+
+fn main() {
+ issue34796aux::bar(|()| {
+ Data::<(), std::io::Error> {
+ data: mk(),
+ error: mk(),
+ }
+ })
+}
--- /dev/null
+// Copyright 2012-2015 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.
+
+// Make sure this compiles without getting a linker error because of missing
+// drop-glue because the collector missed adding drop-glue for the closure:
+
+fn create_fn() -> Box<Fn()> {
+ let text = String::new();
+
+ Box::new(move || { let _ = &text; })
+}
+
+fn main() {
+ let _ = create_fn();
+}
--- /dev/null
+// Copyright 2016 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(const_fn)]
+
+const fn foo() -> i64 {
+ 3
+}
+
+fn main() {
+ let val = &(foo() % 2);
+ assert_eq!(*val, 1);
+}
--- /dev/null
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// aux-build:issue_38715.rs
+
+// Test that `#[macro_export] macro_rules!` shadow earlier `#[macro_export] macro_rules!`
+
+#[macro_use]
+extern crate issue_38715;
+
+fn main() {
+ foo!();
+}
--- /dev/null
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#[repr(u64)]
+enum A {
+ A = 0u64,
+ B = !0u64,
+}
+
+fn cmp() -> A {
+ A::B
+}
+
+fn main() {}
+++ /dev/null
-// Copyright 2015 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 Foo {
- type T;
-}
-
-// should be able to use a trait with an associated type without specifying it as an argument
-trait Bar<F: Foo> {
- fn bar(foo: &F);
-}
-
-pub fn main() {
-}
+++ /dev/null
-// Copyright 2015 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() {
- let command = "a";
-
- match command {
- "foo" => println!("foo"),
- _ => println!("{}", command),
- }
-}
+++ /dev/null
-// Copyright 2015 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.
-
-// pretty-expanded FIXME #23616
-
-#![feature(core)]
-
-// This used to cause an ICE because the retslot for the "return" had the wrong type
-fn testcase<'a>() -> Box<Iterator<Item=usize> + 'a> {
- return Box::new((0..3).map(|i| { return i; }));
-}
-
-fn main() {
-}
+++ /dev/null
-// Copyright 2015 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() {
- return ();
-
- let x = ();
- x
-}
+++ /dev/null
-// Copyright 2015 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 Tr { type T; }
-impl Tr for u8 { type T=(); }
-struct S<I: Tr>(I::T);
-
-fn foo<I: Tr>(i: I::T) {
- S::<I>(i);
-}
-
-fn main() {
- foo::<u8>(());
-}
+++ /dev/null
-// Copyright 2015 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(const_fn)]
-struct A {
- field: usize,
-}
-const fn f() -> usize {
- 5
-}
-fn main() {
- let _ = [0; f()];
-}
+++ /dev/null
-// Copyright 2016 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.
-
-// compile-flags:-g
-
-// In this test we just want to make sure that the code below does not lead to
-// a debuginfo verification assertion during compilation. This was caused by the
-// closure in the guard being translated twice due to how match expressions are
-// handled.
-//
-// See https://github.com/rust-lang/rust/issues/34569 for details.
-
-fn main() {
- match 0 {
- e if (|| { e == 0 })() => {},
- 1 => {},
- _ => {}
- }
-}
+++ /dev/null
-// Copyright 2016 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.
-
-// This test case exposes conditions where the encoding of a trait object type
-// with projection predicates would differ between this crate and the upstream
-// crate, because the predicates were encoded in different order within each
-// crate. This led to different symbol hashes of functions using these type,
-// which in turn led to linker errors because the two crates would not agree on
-// the symbol name.
-// The fix was to make the order in which predicates get encoded stable.
-
-// aux-build:issue34796aux.rs
-extern crate issue34796aux;
-
-fn mk<T>() -> T { loop {} }
-
-struct Data<T, E> {
- data: T,
- error: E,
-}
-
-fn main() {
- issue34796aux::bar(|()| {
- Data::<(), std::io::Error> {
- data: mk(),
- error: mk(),
- }
- })
-}
+++ /dev/null
-// Copyright 2012-2015 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.
-
-// Make sure this compiles without getting a linker error because of missing
-// drop-glue because the collector missed adding drop-glue for the closure:
-
-fn create_fn() -> Box<Fn()> {
- let text = String::new();
-
- Box::new(move || { let _ = &text; })
-}
-
-fn main() {
- let _ = create_fn();
-}
--- /dev/null
+// Copyright 2017 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::env;
+use std::io::prelude::*;
+use std::process::Command;
+use std::thread;
+
+const THREADS: usize = 20;
+const WRITES: usize = 100;
+const WRITE_SIZE: usize = 1024 * 32;
+
+fn main() {
+ let args = env::args().collect::<Vec<_>>();
+ if args.len() == 1 {
+ parent();
+ } else {
+ child();
+ }
+}
+
+fn parent() {
+ let me = env::current_exe().unwrap();
+ let mut cmd = Command::new(me);
+ cmd.arg("run-the-test");
+ let output = cmd.output().unwrap();
+ assert!(output.status.success());
+ assert_eq!(output.stderr.len(), 0);
+ assert_eq!(output.stdout.len(), WRITES * THREADS * WRITE_SIZE);
+ for byte in output.stdout.iter() {
+ assert_eq!(*byte, b'a');
+ }
+}
+
+fn child() {
+ let threads = (0..THREADS).map(|_| {
+ thread::spawn(|| {
+ let buf = [b'a'; WRITE_SIZE];
+ for _ in 0..WRITES {
+ write_all(&buf);
+ }
+ })
+ }).collect::<Vec<_>>();
+
+ for thread in threads {
+ thread.join().unwrap();
+ }
+}
+
+#[cfg(unix)]
+fn write_all(buf: &[u8]) {
+ use std::fs::File;
+ use std::mem;
+ use std::os::unix::prelude::*;
+
+ let mut file = unsafe { File::from_raw_fd(1) };
+ let res = file.write_all(buf);
+ mem::forget(file);
+ res.unwrap();
+}
+
+#[cfg(windows)]
+fn write_all(buf: &[u8]) {
+ use std::fs::File;
+ use std::mem;
+ use std::os::windows::raw::*;
+ use std::os::windows::prelude::*;
+
+ const STD_OUTPUT_HANDLE: u32 = (-11i32) as u32;
+
+ extern "system" {
+ fn GetStdHandle(handle: u32) -> HANDLE;
+ }
+
+ let mut file = unsafe {
+ let handle = GetStdHandle(STD_OUTPUT_HANDLE);
+ assert!(!handle.is_null());
+ File::from_raw_handle(handle)
+ };
+ let res = file.write_all(buf);
+ mem::forget(file);
+ res.unwrap();
+}
--- /dev/null
+// Copyright 2017 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.
+
+// compile-flags: --test
+
+#![deny(dead_code)]
+
+extern "C" {
+ #[allow(dead_code)]
+ static Qt: u64;
+}
+
+fn main() {}
// ignore-stage0
// ignore-stage1
-#![feature(i128_type)]
+#![feature(i128_type, test)]
+
+extern crate test;
+use test::black_box as b;
fn main() {
let x: u128 = 0xFFFF_FFFF_FFFF_FFFF__FFFF_FFFF_FFFF_FFFF;
format!("{}", u128::max_value()));
assert_eq!("147573952589676412928", format!("{:?}", j));
// common traits
- x.clone();
+ assert_eq!(x, b(x.clone()));
+ // overflow checks
+ assert_eq!((z).checked_mul(z), Some(0x734C_C2F2_A521));
+ assert_eq!((k).checked_mul(k), None);
+ let l: u128 = b(u128::max_value() - 10);
+ let o: u128 = b(17);
+ assert_eq!(l.checked_add(b(11)), None);
+ assert_eq!(l.checked_sub(l), Some(0));
+ assert_eq!(o.checked_sub(b(18)), None);
+ assert_eq!(b(1u128).checked_shl(b(127)), Some(1 << 127));
+ assert_eq!(o.checked_shl(b(128)), None);
}
// no-prefer-dynamic
-#![feature(proc_macro)]
-#![feature(proc_macro_lib)]
#![crate_type = "proc-macro"]
extern crate proc_macro;
20 | match (A, ()) {
| ^^^^^^^ patterns `(B, _)`, `(C, _)`, `(D, _)` and 2 more not covered
-error[E0004]: non-exhaustive patterns: `(A, B)`, `(B, B)`, `(C, B)` and 27 more not covered
+error[E0004]: non-exhaustive patterns: `(_, B)`, `(_, C)`, `(_, D)` and 2 more not covered
--> $DIR/issue-35609.rs:24:11
|
24 | match (A, A) {
- | ^^^^^^ patterns `(A, B)`, `(B, B)`, `(C, B)` and 27 more not covered
+ | ^^^^^^ patterns `(_, B)`, `(_, C)`, `(_, D)` and 2 more not covered
error[E0004]: non-exhaustive patterns: `((B, _), _)`, `((C, _), _)`, `((D, _), _)` and 2 more not covered
--> $DIR/issue-35609.rs:28:11
--- /dev/null
+// Copyright 2016 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 Foo {
+ foo: Vec<u32>,
+}
+
+impl Copy for Foo { }
+
+#[derive(Copy)]
+struct Foo2<'a> {
+ ty: &'a mut bool,
+}
+
+enum EFoo {
+ Bar { x: Vec<u32> },
+ Baz,
+}
+
+impl Copy for EFoo { }
+
+#[derive(Copy)]
+enum EFoo2<'a> {
+ Bar(&'a mut bool),
+ Baz,
+}
+
+fn main() {
+}
--- /dev/null
+error[E0204]: the trait `Copy` may not be implemented for this type
+ --> $DIR/E0204.rs:15:6
+ |
+12 | foo: Vec<u32>,
+ | ------------- this field does not implement `Copy`
+...
+15 | impl Copy for Foo { }
+ | ^^^^
+
+error[E0204]: the trait `Copy` may not be implemented for this type
+ --> $DIR/E0204.rs:27:6
+ |
+23 | Bar { x: Vec<u32> },
+ | ----------- this field does not implement `Copy`
+...
+27 | impl Copy for EFoo { }
+ | ^^^^
+
+error[E0204]: the trait `Copy` may not be implemented for this type
+ --> $DIR/E0204.rs:17:10
+ |
+17 | #[derive(Copy)]
+ | ^^^^
+18 | struct Foo2<'a> {
+19 | ty: &'a mut bool,
+ | ---------------- this field does not implement `Copy`
+
+error[E0204]: the trait `Copy` may not be implemented for this type
+ --> $DIR/E0204.rs:29:10
+ |
+29 | #[derive(Copy)]
+ | ^^^^
+30 | enum EFoo2<'a> {
+31 | Bar(&'a mut bool),
+ | ------------- this field does not implement `Copy`
+
+error: aborting due to 4 previous errors
+