getopts collections test rand \
core alloc \
rustc_unicode rustc_bitflags \
- alloc_system alloc_jemalloc
+ alloc_system alloc_jemalloc rustc_const_eval
RUSTC_CRATES := rustc rustc_typeck rustc_mir rustc_borrowck rustc_resolve rustc_driver \
rustc_trans rustc_back rustc_llvm rustc_privacy rustc_lint \
rustc_data_structures rustc_front rustc_platform_intrinsics \
DEPS_syntax := std term serialize log arena libc rustc_bitflags rustc_unicode
DEPS_syntax_ext := syntax fmt_macros
+DEPS_rustc_const_eval := std syntax
+
DEPS_rustc := syntax fmt_macros flate arena serialize getopts rbml rustc_front\
- log graphviz rustc_llvm rustc_back rustc_data_structures
+ log graphviz rustc_llvm rustc_back rustc_data_structures\
+ rustc_const_eval
DEPS_rustc_back := std syntax rustc_llvm rustc_front flate log libc
DEPS_rustc_borrowck := rustc rustc_front log graphviz syntax
DEPS_rustc_data_structures := std log serialize
DEPS_rustc_front := std syntax log serialize
DEPS_rustc_lint := rustc log syntax
DEPS_rustc_llvm := native:rustllvm libc std rustc_bitflags
-DEPS_rustc_metadata := rustc rustc_front syntax rbml
+DEPS_rustc_metadata := rustc rustc_front syntax rbml rustc_const_eval
DEPS_rustc_passes := syntax rustc core rustc_front
-DEPS_rustc_mir := rustc rustc_front syntax
+DEPS_rustc_mir := rustc rustc_front syntax rustc_const_eval
DEPS_rustc_resolve := arena rustc rustc_front log syntax
DEPS_rustc_platform_intrinsics := rustc rustc_llvm
DEPS_rustc_plugin := rustc rustc_metadata syntax rustc_mir
DEPS_rustc_privacy := rustc rustc_front log syntax
DEPS_rustc_trans := arena flate getopts graphviz libc rustc rustc_back rustc_mir \
- log syntax serialize rustc_llvm rustc_front rustc_platform_intrinsics
-DEPS_rustc_typeck := rustc syntax rustc_front rustc_platform_intrinsics
+ log syntax serialize rustc_llvm rustc_front rustc_platform_intrinsics \
+ rustc_const_eval
+DEPS_rustc_typeck := rustc syntax rustc_front rustc_platform_intrinsics rustc_const_eval
DEPS_rustdoc := rustc rustc_driver native:hoedown serialize getopts \
test rustc_lint rustc_front
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-use std::process::Command;
-
use build::{Build, Compiler};
pub fn linkcheck(build: &Build, stage: u32, host: &str) {
println!("Linkcheck stage{} ({})", stage, host);
let compiler = Compiler::new(stage, host);
- let linkchecker = build.tool(&compiler, "linkchecker");
- build.run(Command::new(&linkchecker)
- .arg(build.out.join(host).join("doc")));
+ build.run(build.tool_cmd(&compiler, "linkchecker")
+ .arg(build.out.join(host).join("doc")));
}
/// This will build the standard library for a particular stage of the build
/// using the `compiler` targeting the `target` architecture. The artifacts
/// created will also be linked into the sysroot directory.
-pub fn std<'a>(build: &'a Build, stage: u32, target: &str,
- compiler: &Compiler<'a>) {
- let host = compiler.host;
- println!("Building stage{} std artifacts ({} -> {})", stage,
- host, target);
+pub fn std<'a>(build: &'a Build, target: &str, compiler: &Compiler<'a>) {
+ println!("Building stage{} std artifacts ({} -> {})", compiler.stage,
+ compiler.host, target);
// Move compiler-rt into place as it'll be required by the compiler when
// building the standard library to link the dylib of libstd
- let libdir = build.sysroot_libdir(stage, &host, target);
+ let libdir = build.sysroot_libdir(compiler, target);
let _ = fs::remove_dir_all(&libdir);
t!(fs::create_dir_all(&libdir));
t!(fs::hard_link(&build.compiler_rt_built.borrow()[target],
build_startup_objects(build, target, &libdir);
- let out_dir = build.cargo_out(stage, &host, Mode::Libstd, target);
+ let out_dir = build.cargo_out(compiler, Mode::Libstd, target);
build.clear_if_dirty(&out_dir, &build.compiler_path(compiler));
- let mut cargo = build.cargo(stage, compiler, Mode::Libstd, Some(target),
- "build");
+ let mut cargo = build.cargo(compiler, Mode::Libstd, target, "build");
cargo.arg("--features").arg(build.std_features())
.arg("--manifest-path")
.arg(build.src.join("src/rustc/std_shim/Cargo.toml"));
}
build.run(&mut cargo);
- std_link(build, stage, target, compiler, host);
+ std_link(build, target, compiler, compiler.host);
}
/// Link all libstd rlibs/dylibs into the sysroot location.
/// Links those artifacts generated in the given `stage` for `target` produced
/// by `compiler` into `host`'s sysroot.
pub fn std_link(build: &Build,
- stage: u32,
target: &str,
compiler: &Compiler,
host: &str) {
- let libdir = build.sysroot_libdir(stage, host, target);
- let out_dir = build.cargo_out(stage, compiler.host, Mode::Libstd, target);
+ let target_compiler = Compiler::new(compiler.stage, host);
+ let libdir = build.sysroot_libdir(&target_compiler, target);
+ let out_dir = build.cargo_out(compiler, Mode::Libstd, target);
// If we're linking one compiler host's output into another, then we weren't
// called from the `std` method above. In that case we clean out what's
}
add_to_sysroot(&out_dir, &libdir);
- if target.contains("musl") && (target.contains("x86_64") || target.contains("i686")) {
+ if target.contains("musl") &&
+ (target.contains("x86_64") || target.contains("i686")) {
copy_third_party_objects(build, target, &libdir);
}
}
/// This will build the compiler for a particular stage of the build using
/// the `compiler` targeting the `target` architecture. The artifacts
/// created will also be linked into the sysroot directory.
-pub fn rustc<'a>(build: &'a Build, stage: u32, target: &str,
- compiler: &Compiler<'a>) {
- let host = compiler.host;
- println!("Building stage{} compiler artifacts ({} -> {})", stage,
- host, target);
+pub fn rustc<'a>(build: &'a Build, target: &str, compiler: &Compiler<'a>) {
+ println!("Building stage{} compiler artifacts ({} -> {})",
+ compiler.stage, compiler.host, target);
- let out_dir = build.cargo_out(stage, &host, Mode::Librustc, target);
- build.clear_if_dirty(&out_dir, &libstd_shim(build, stage, &host, target));
+ let out_dir = build.cargo_out(compiler, Mode::Librustc, target);
+ build.clear_if_dirty(&out_dir, &libstd_shim(build, compiler, target));
- let mut cargo = build.cargo(stage, compiler, Mode::Librustc, Some(target),
- "build");
+ let mut cargo = build.cargo(compiler, Mode::Librustc, target, "build");
cargo.arg("--features").arg(build.rustc_features())
.arg("--manifest-path")
.arg(build.src.join("src/rustc/Cargo.toml"));
}
build.run(&mut cargo);
- rustc_link(build, stage, target, compiler, compiler.host);
+ rustc_link(build, target, compiler, compiler.host);
}
/// Link all librustc rlibs/dylibs into the sysroot location.
/// Links those artifacts generated in the given `stage` for `target` produced
/// by `compiler` into `host`'s sysroot.
pub fn rustc_link(build: &Build,
- stage: u32,
target: &str,
compiler: &Compiler,
host: &str) {
- let libdir = build.sysroot_libdir(stage, host, target);
- let out_dir = build.cargo_out(stage, compiler.host, Mode::Librustc, target);
+ let target_compiler = Compiler::new(compiler.stage, host);
+ let libdir = build.sysroot_libdir(&target_compiler, target);
+ let out_dir = build.cargo_out(compiler, Mode::Librustc, target);
add_to_sysroot(&out_dir, &libdir);
}
/// Cargo's output path for the standard library in a given stage, compiled
/// by a particular compiler for the specified target.
-fn libstd_shim(build: &Build, stage: u32, host: &str, target: &str) -> PathBuf {
- build.cargo_out(stage, host, Mode::Libstd, target).join("libstd_shim.rlib")
+fn libstd_shim(build: &Build, compiler: &Compiler, target: &str) -> PathBuf {
+ build.cargo_out(compiler, Mode::Libstd, target).join("libstd_shim.rlib")
}
fn compiler_file(compiler: &Path, file: &str) -> String {
/// compiler.
pub fn assemble_rustc(build: &Build, stage: u32, host: &str) {
assert!(stage > 0, "the stage0 compiler isn't assembled, it's downloaded");
+ // The compiler that we're assembling
+ let target_compiler = Compiler::new(stage, host);
+
+ // The compiler that compiled the compiler we're assembling
+ let build_compiler = Compiler::new(stage - 1, &build.config.build);
// Clear out old files
- let sysroot = build.sysroot(stage, host);
+ let sysroot = build.sysroot(&target_compiler);
let _ = fs::remove_dir_all(&sysroot);
t!(fs::create_dir_all(&sysroot));
// Link in all dylibs to the libdir
let sysroot_libdir = sysroot.join(libdir(host));
t!(fs::create_dir_all(&sysroot_libdir));
- let src_libdir = build.sysroot_libdir(stage - 1, &build.config.build, host);
+ let src_libdir = build.sysroot_libdir(&build_compiler, host);
for f in t!(fs::read_dir(&src_libdir)).map(|f| t!(f)) {
let filename = f.file_name().into_string().unwrap();
if is_dylib(&filename) {
}
}
- let out_dir = build.cargo_out(stage - 1, &build.config.build,
- Mode::Librustc, host);
+ let out_dir = build.cargo_out(&build_compiler, Mode::Librustc, host);
// Link the compiler binary itself into place
let rustc = out_dir.join(exe("rustc", host));
// let out_dir = build.cargo_out(stage, &host, Mode::Librustc, target);
// build.clear_if_dirty(&out_dir, &libstd_shim(build, stage, &host, target));
- let mut cargo = build.cargo(stage, &compiler, Mode::Tool, None, "build");
+ let mut cargo = build.cargo(&compiler, Mode::Tool, host, "build");
cargo.arg("--manifest-path")
.arg(build.src.join(format!("src/tools/{}/Cargo.toml", tool)));
build.run(&mut cargo);
}
let mut cmd = Command::new(&rustdoc);
+ build.add_rustc_lib_path(&compiler, &mut cmd);
cmd.arg("--html-after-content").arg(&footer)
.arg("--html-before-content").arg(&version_info)
.arg("--html-in-header").arg(&favicon)
pub fn std(build: &Build, stage: u32, host: &str, out: &Path) {
println!("Documenting stage{} std ({})", stage, host);
let compiler = Compiler::new(stage, host);
- let out_dir = build.stage_out(stage, host, Mode::Libstd)
+ let out_dir = build.stage_out(&compiler, Mode::Libstd)
.join(host).join("doc");
let rustdoc = build.rustdoc(&compiler);
build.clear_if_dirty(&out_dir, &rustdoc);
- let mut cargo = build.cargo(stage, &compiler, Mode::Libstd, Some(host),
- "doc");
+ let mut cargo = build.cargo(&compiler, Mode::Libstd, host, "doc");
cargo.arg("--manifest-path")
.arg(build.src.join("src/rustc/std_shim/Cargo.toml"))
.arg("--features").arg(build.std_features());
pub fn rustc(build: &Build, stage: u32, host: &str, out: &Path) {
println!("Documenting stage{} compiler ({})", stage, host);
let compiler = Compiler::new(stage, host);
- let out_dir = build.stage_out(stage, host, Mode::Librustc)
+ let out_dir = build.stage_out(&compiler, Mode::Librustc)
.join(host).join("doc");
let rustdoc = build.rustdoc(&compiler);
if !up_to_date(&rustdoc, &out_dir.join("rustc/index.html")) {
t!(fs::remove_dir_all(&out_dir));
}
- let mut cargo = build.cargo(stage, &compiler, Mode::Librustc, Some(host),
- "doc");
+ let mut cargo = build.cargo(&compiler, Mode::Librustc, host, "doc");
cargo.arg("--manifest-path")
.arg(build.src.join("src/rustc/Cargo.toml"))
.arg("--features").arg(build.rustc_features());
pub fn error_index(build: &Build, stage: u32, host: &str, out: &Path) {
println!("Documenting stage{} error index ({})", stage, host);
let compiler = Compiler::new(stage, host);
- let mut index = Command::new(build.tool(&compiler, "error_index_generator"));
+ let mut index = build.tool_cmd(&compiler, "error_index_generator");
index.arg("html");
index.arg(out.join("error-index.html"));
CompilerRt { _dummy } => {
native::compiler_rt(self, target.target);
}
- Libstd { stage, compiler } => {
- compile::std(self, stage, target.target, &compiler);
+ Libstd { compiler } => {
+ compile::std(self, target.target, &compiler);
}
- Librustc { stage, compiler } => {
- compile::rustc(self, stage, target.target, &compiler);
+ Librustc { compiler } => {
+ compile::rustc(self, target.target, &compiler);
}
- LibstdLink { stage, compiler, host } => {
- compile::std_link(self, stage, target.target,
- &compiler, host);
+ LibstdLink { compiler, host } => {
+ compile::std_link(self, target.target, &compiler, host);
}
- LibrustcLink { stage, compiler, host } => {
- compile::rustc_link(self, stage, target.target,
- &compiler, host);
+ LibrustcLink { compiler, host } => {
+ compile::rustc_link(self, target.target, &compiler, host);
}
Rustc { stage: 0 } => {
// nothing to do...
/// This will create a `Command` that represents a pending execution of
/// Cargo for the specified stage, whether or not the standard library is
/// being built, and using the specified compiler targeting `target`.
- // FIXME: aren't stage/compiler duplicated?
fn cargo(&self,
- stage: u32,
compiler: &Compiler,
mode: Mode,
- target: Option<&str>,
+ target: &str,
cmd: &str) -> Command {
let mut cargo = Command::new(&self.cargo);
- let host = compiler.host;
- let out_dir = self.stage_out(stage, host, mode);
+ let out_dir = self.stage_out(compiler, mode);
cargo.env("CARGO_TARGET_DIR", out_dir)
.arg(cmd)
- .arg("-j").arg(self.jobs().to_string());
+ .arg("-j").arg(self.jobs().to_string())
+ .arg("--target").arg(target);
// Customize the compiler we're running. Specify the compiler to cargo
// as our shim and then pass it some various options used to configure
// how the actual compiler itself is called.
cargo.env("RUSTC", self.out.join("bootstrap/debug/rustc"))
.env("RUSTC_REAL", self.compiler_path(compiler))
- .env("RUSTC_STAGE", self.stage_arg(stage, compiler).to_string())
+ .env("RUSTC_STAGE", compiler.stage.to_string())
.env("RUSTC_DEBUGINFO", self.config.rust_debuginfo.to_string())
.env("RUSTC_CODEGEN_UNITS",
self.config.rust_codegen_units.to_string())
.env("RUSTC_DEBUG_ASSERTIONS",
self.config.rust_debug_assertions.to_string())
.env("RUSTC_SNAPSHOT", &self.rustc)
- .env("RUSTC_SYSROOT", self.sysroot(stage, host))
+ .env("RUSTC_SYSROOT", self.sysroot(compiler))
.env("RUSTC_SNAPSHOT_LIBDIR", self.rustc_snapshot_libdir())
.env("RUSTC_RPATH", self.config.rust_rpath.to_string())
.env("RUSTDOC", self.out.join("bootstrap/debug/rustdoc"))
- .env("RUSTDOC_REAL", self.rustdoc(compiler));
-
- if let Some(target) = target {
- cargo.env("RUSTC_FLAGS", self.rustc_flags(target).join(" "));
- cargo.arg("--target").arg(target);
-
- // Specify some various options for build scripts used throughout
- // the build.
- //
- // FIXME: the guard against msvc shouldn't need to be here
- if !target.contains("msvc") {
- cargo.env(format!("CC_{}", target), self.cc(target))
- .env(format!("AR_{}", target), self.ar(target))
- .env(format!("CFLAGS_{}", target), self.cflags(target));
- }
-
- // Environment variables *required* needed throughout the build
- //
- // FIXME: should update code to not require this env vars
- cargo.env("CFG_COMPILER_HOST_TRIPLE", target);
+ .env("RUSTDOC_REAL", self.rustdoc(compiler))
+ .env("RUSTC_FLAGS", self.rustc_flags(target).join(" "));
+
+ // Specify some various options for build scripts used throughout
+ // the build.
+ //
+ // FIXME: the guard against msvc shouldn't need to be here
+ if !target.contains("msvc") {
+ cargo.env(format!("CC_{}", target), self.cc(target))
+ .env(format!("AR_{}", target), self.ar(target))
+ .env(format!("CFLAGS_{}", target), self.cflags(target));
}
+ // Environment variables *required* needed throughout the build
+ //
+ // FIXME: should update code to not require this env vars
+ cargo.env("CFG_COMPILER_HOST_TRIPLE", target);
+
if self.config.verbose || self.flags.verbose {
cargo.arg("-v");
}
if compiler.is_snapshot(self) {
self.rustc.clone()
} else {
- self.sysroot(compiler.stage, compiler.host).join("bin")
- .join(exe("rustc", compiler.host))
+ self.sysroot(compiler).join("bin").join(exe("rustc", compiler.host))
}
}
/// Get the specified tool built by the specified compiler
fn tool(&self, compiler: &Compiler, tool: &str) -> PathBuf {
- self.stage_out(compiler.stage, compiler.host, Mode::Tool)
- .join(self.cargo_dir())
+ self.cargo_out(compiler, Mode::Tool, compiler.host)
.join(exe(tool, compiler.host))
}
/// Get the `rustdoc` executable next to the specified compiler
fn rustdoc(&self, compiler: &Compiler) -> PathBuf {
- let root = if compiler.is_snapshot(self) {
- let mut rustdoc = self.rustc.clone();
- rustdoc.pop();
- rustdoc
- } else {
- let (stage, host) = (compiler.stage, compiler.host);
- self.cargo_out(stage - 1, host, Mode::Librustc, host)
- };
- root.join(exe("rustdoc", compiler.host))
+ let mut rustdoc = self.compiler_path(compiler);
+ rustdoc.pop();
+ rustdoc.push(exe("rustdoc", compiler.host));
+ return rustdoc
}
/// Get a `Command` which is ready to run `tool` in `stage` built for
/// `host`.
- #[allow(dead_code)] // this will be used soon
fn tool_cmd(&self, compiler: &Compiler, tool: &str) -> Command {
let mut cmd = Command::new(self.tool(&compiler, tool));
let host = compiler.host;
- let stage = compiler.stage;
let paths = vec![
- self.cargo_out(stage, host, Mode::Libstd, host).join("deps"),
- self.cargo_out(stage, host, Mode::Librustc, host).join("deps"),
+ self.cargo_out(compiler, Mode::Libstd, host).join("deps"),
+ self.cargo_out(compiler, Mode::Librustc, host).join("deps"),
];
add_lib_path(paths, &mut cmd);
return cmd
}
- fn stage_arg(&self, stage: u32, compiler: &Compiler) -> u32 {
- if stage == 0 && compiler.host != self.config.build {1} else {stage}
- }
-
/// Get the space-separated set of activated features for the standard
/// library.
fn std_features(&self) -> String {
if self.config.rust_optimize {"release"} else {"debug"}
}
- fn sysroot(&self, stage: u32, host: &str) -> PathBuf {
- if stage == 0 {
- self.stage_out(stage, host, Mode::Librustc)
+ fn sysroot(&self, compiler: &Compiler) -> PathBuf {
+ if compiler.stage == 0 {
+ self.out.join(compiler.host).join("stage0-sysroot")
} else {
- self.out.join(host).join(format!("stage{}", stage))
+ self.out.join(compiler.host).join(format!("stage{}", compiler.stage))
}
}
- fn sysroot_libdir(&self, stage: u32, host: &str, target: &str) -> PathBuf {
- self.sysroot(stage, host).join("lib").join("rustlib")
+ fn sysroot_libdir(&self, compiler: &Compiler, target: &str) -> PathBuf {
+ self.sysroot(compiler).join("lib").join("rustlib")
.join(target).join("lib")
}
/// stage when running with a particular host compiler.
///
/// The mode indicates what the root directory is for.
- fn stage_out(&self, stage: u32, host: &str, mode: Mode) -> PathBuf {
+ fn stage_out(&self, compiler: &Compiler, mode: Mode) -> PathBuf {
let suffix = match mode {
Mode::Libstd => "-std",
_ => "-rustc",
};
- self.out.join(host).join(format!("stage{}{}", stage, suffix))
+ self.out.join(compiler.host)
+ .join(format!("stage{}{}", compiler.stage, suffix))
}
/// Returns the root output directory for all Cargo output in a given stage,
/// running a particular comipler, wehther or not we're building the
/// standard library, and targeting the specified architecture.
- fn cargo_out(&self, stage: u32, host: &str, mode: Mode,
+ fn cargo_out(&self,
+ compiler: &Compiler,
+ mode: Mode,
target: &str) -> PathBuf {
- self.stage_out(stage, host, mode).join(target).join(self.cargo_dir())
+ self.stage_out(compiler, mode).join(target).join(self.cargo_dir())
}
/// Root output directory for LLVM compiled for `target`
if compiler.is_snapshot(self) {
self.rustc_snapshot_libdir()
} else {
- self.sysroot(compiler.stage, compiler.host)
- .join(libdir(compiler.host))
+ self.sysroot(compiler).join(libdir(compiler.host))
}
}
// and one for the compiler itself. These are parameterized over the
// stage output they're going to be placed in along with the
// compiler which is producing the copy of libstd or librustc
- (libstd, Libstd { stage: u32, compiler: Compiler<'a> }),
- (librustc, Librustc { stage: u32, compiler: Compiler<'a> }),
+ (libstd, Libstd { compiler: Compiler<'a> }),
+ (librustc, Librustc { compiler: Compiler<'a> }),
// Links the standard library/librustc produced by the compiler
// provided into the host's directory also provided.
(libstd_link, LibstdLink {
- stage: u32,
compiler: Compiler<'a>,
host: &'a str
}),
(librustc_link, LibrustcLink {
- stage: u32,
compiler: Compiler<'a>,
host: &'a str
}),
}
let host = t.target(host);
if host.target == build.config.build {
- targets.push(host.librustc(stage, host.compiler(stage)));
+ targets.push(host.librustc(host.compiler(stage)));
} else {
- targets.push(host.librustc_link(stage, t.compiler(stage),
- host.target));
+ targets.push(host.librustc_link(t.compiler(stage), host.target));
}
for target in build.config.target.iter() {
if !build.flags.target.contains(target) {
if host.target == build.config.build {
targets.push(host.target(target)
- .libstd(stage, host.compiler(stage)));
+ .libstd(host.compiler(stage)));
} else {
targets.push(host.target(target)
- .libstd_link(stage, t.compiler(stage),
- host.target));
+ .libstd_link(t.compiler(stage), host.target));
}
}
}
}
Source::Rustc { stage } => {
let compiler = Compiler::new(stage - 1, &build.config.build);
- vec![self.librustc(stage - 1, compiler)]
+ vec![self.librustc(compiler)]
}
- Source::Librustc { stage, compiler } => {
- vec![self.libstd(stage, compiler), self.llvm(())]
+ Source::Librustc { compiler } => {
+ vec![self.libstd(compiler), self.llvm(())]
}
- Source::Libstd { stage: _, compiler } => {
+ Source::Libstd { compiler } => {
vec![self.compiler_rt(()),
self.rustc(compiler.stage).target(compiler.host)]
}
- Source::LibrustcLink { stage, compiler, host } => {
- vec![self.librustc(stage, compiler),
- self.libstd_link(stage, compiler, host)]
+ Source::LibrustcLink { compiler, host } => {
+ vec![self.librustc(compiler),
+ self.libstd_link(compiler, host)]
}
- Source::LibstdLink { stage, compiler, host } => {
- vec![self.libstd(stage, compiler),
- self.target(host).rustc(stage)]
+ Source::LibstdLink { compiler, host } => {
+ vec![self.libstd(compiler),
+ self.target(host).rustc(compiler.stage)]
}
Source::CompilerRt { _dummy } => {
vec![self.llvm(()).target(&build.config.build)]
}
Source::Llvm { _dummy } => Vec::new(),
Source::DocStd { stage } => {
- vec![self.libstd(stage, self.compiler(stage))]
+ vec![self.libstd(self.compiler(stage))]
}
Source::DocBook { stage } |
Source::DocNomicon { stage } |
}
Source::ToolLinkchecker { stage } => {
- vec![self.libstd(stage, self.compiler(stage))]
+ vec![self.libstd(self.compiler(stage))]
}
Source::ToolErrorIndex { stage } |
Source::ToolRustbook { stage } => {
- vec![self.librustc(stage, self.compiler(stage))]
+ vec![self.librustc(self.compiler(stage))]
}
}
}
}
}
- let filter = if !matches.free.is_empty() {
- Some(matches.free[0].clone())
- } else {
- None
- };
-
Config {
compile_lib_path: matches.opt_str("compile-lib-path").unwrap(),
run_lib_path: matches.opt_str("run-lib-path").unwrap(),
stage_id: matches.opt_str("stage-id").unwrap(),
mode: matches.opt_str("mode").unwrap().parse().ok().expect("invalid mode"),
run_ignored: matches.opt_present("ignored"),
- filter: filter,
+ filter: matches.free.first().cloned(),
logfile: matches.opt_str("logfile").map(|s| PathBuf::from(&s)),
runtool: matches.opt_str("runtool"),
host_rustcflags: matches.opt_str("host-rustcflags"),
pub fn test_opts(config: &Config) -> test::TestOpts {
test::TestOpts {
- filter: match config.filter {
- None => None,
- Some(ref filter) => Some(filter.clone()),
- },
+ filter: config.filter.clone(),
run_ignored: config.run_ignored,
logfile: config.logfile.clone(),
run_tests: true,
"ret": "f(32-64)",
"args": ["0", "0"]
},
+ {
+ "intrinsic": "256_blendv_{0.data_type}",
+ "width": [256],
+ "llvm": "blendv.{0.data_type}.256",
+ "ret": "f(32-64)",
+ "args": ["0", "0", "0"]
+ },
{
"intrinsic": "256_broadcast_{0.data_type}",
"width": [256],
"ret": "f(32-64)",
"args": ["s8SPc"]
},
+ {
+ "intrinsic": "256_cmp_{0.data_type}",
+ "width": [256],
+ "llvm": "cmp.{1.data_type}.256",
+ "ret": "f(32-64)",
+ "args": ["0", "0", "s8S"]
+ },
{
"intrinsic": "256_cvtepi32_pd",
"width": [256],
rbml = { path = "../librbml" }
rustc_back = { path = "../librustc_back" }
rustc_bitflags = { path = "../librustc_bitflags" }
+rustc_const_eval = { path = "../librustc_const_eval" }
rustc_data_structures = { path = "../librustc_data_structures" }
rustc_front = { path = "../librustc_front" }
rustc_llvm = { path = "../librustc_llvm" }
not shared state, because if it changes it does not itself
invalidate other functions (though it may be that it causes new
monomorphizations to occur, but that's handled independently).
-
+
Put another way: if the HIR for an item changes, we are going to
recompile that item for sure. But we need the dep tracking map to tell
us what *else* we have to recompile. Shared state is anything that is
| ^
| |
+---------------------------------+ // added by `visit_all_items_in_krate`
-
+
In particular, the edge from `Hir(X)` to `ItemSignature(X)` is only
present because we called `read` ourselves when entering the `ItemSignature(X)`
task.
... -> MapVariant(key) -> A
|
+----------> B
-
-which is much cleaner.
+
+which is much cleaner.
**Be aware though that the closure is executed with `MapVariant(key)`
pushed onto the stack as the current task!** That means that you must
This will dump out all the nodes that lead from `Hir(foo)` to
`TypeckItemBody(bar)`, from which you can (hopefully) see the source
of the erroneous edge.
-
extern crate rustc_data_structures;
extern crate serialize;
extern crate collections;
+extern crate rustc_const_eval;
#[macro_use] extern crate log;
#[macro_use] extern crate syntax;
#[macro_use] #[no_link] extern crate rustc_bitflags;
use middle::infer;
use middle::mem_categorization::{cmt};
use middle::pat_util::*;
+use middle::traits::ProjectionMode;
use middle::ty::*;
use middle::ty;
use std::cmp::Ordering;
Some(Def::AssociatedConst(did)) |
Some(Def::Const(did)) => match lookup_const_by_id(self.tcx, did,
Some(pat.id), None) {
- Some(const_expr) => {
+ Some((const_expr, _const_ty)) => {
const_expr_to_pat(self.tcx, const_expr, pat.span).map(|new_pat| {
if let Some(ref mut renaming_map) = self.renaming_map {
//FIXME: (@jroesch) this code should be floated up as well
let infcx = infer::new_infer_ctxt(cx.tcx,
&cx.tcx.tables,
- Some(cx.param_env.clone()));
+ Some(cx.param_env.clone()),
+ ProjectionMode::AnyFinal);
if infcx.type_moves_by_default(pat_ty, pat.span) {
check_move(p, sub.as_ref().map(|p| &**p));
}
let infcx = infer::new_infer_ctxt(cx.tcx,
&cx.tcx.tables,
- Some(checker.cx.param_env.clone()));
+ Some(checker.cx.param_env.clone()),
+ ProjectionMode::AnyFinal);
let mut visitor = ExprUseVisitor::new(&mut checker, &infcx);
visitor.walk_expr(guard);
use middle::def_id::DefId;
use middle::pat_util::def_to_path;
use middle::ty::{self, Ty, TyCtxt};
+use middle::ty::util::IntTypeExt;
+use middle::traits::ProjectionMode;
use middle::astconv_util::ast_ty_to_prim_ty;
-use util::num::ToPrimitive;
use util::nodemap::NodeMap;
-use session::Session;
use graphviz::IntoCow;
use syntax::ast;
use syntax::parse::token::InternedString;
use syntax::ptr::P;
use syntax::codemap;
+use syntax::attr::IntType;
use std::borrow::Cow;
use std::cmp::Ordering;
use std::collections::hash_map::Entry::Vacant;
use std::hash;
use std::mem::transmute;
-use std::{i8, i16, i32, i64, u8, u16, u32, u64};
use std::rc::Rc;
-fn lookup_variant_by_id<'a>(tcx: &'a TyCtxt,
+use rustc_const_eval::*;
+
+macro_rules! math {
+ ($e:expr, $op:expr) => {
+ match $op {
+ Ok(val) => val,
+ Err(e) => signal!($e, Math(e)),
+ }
+ }
+}
+
+fn lookup_variant_by_id<'a>(tcx: &'a ty::TyCtxt,
enum_def: DefId,
variant_def: DefId)
-> Option<&'a Expr> {
def_id: DefId,
maybe_ref_id: Option<ast::NodeId>,
param_substs: Option<&'tcx subst::Substs<'tcx>>)
- -> Option<&'tcx Expr> {
+ -> Option<(&'tcx Expr, Option<ty::Ty<'tcx>>)> {
if let Some(node_id) = tcx.map.as_local_node_id(def_id) {
match tcx.map.find(node_id) {
None => None,
Some(ast_map::NodeItem(it)) => match it.node {
- hir::ItemConst(_, ref const_expr) => {
- Some(&const_expr)
+ hir::ItemConst(ref ty, ref const_expr) => {
+ Some((&const_expr, ast_ty_to_prim_ty(tcx, ty)))
}
_ => None
},
if let Some(param_substs) = param_substs {
substs = substs.subst(tcx, param_substs);
}
- resolve_trait_associated_const(tcx, ti, trait_id,
- substs)
+ resolve_trait_associated_const(tcx, ti, trait_id, substs)
}
// Technically, without knowing anything about the
// expression that generates the obligation, we could
_ => None
},
Some(ast_map::NodeImplItem(ii)) => match ii.node {
- hir::ImplItemKind::Const(_, ref expr) => {
- Some(&expr)
+ hir::ImplItemKind::Const(ref ty, ref expr) => {
+ Some((&expr, ast_ty_to_prim_ty(tcx, ty)))
}
_ => None
},
}
} else {
match tcx.extern_const_statics.borrow().get(&def_id) {
- Some(&ast::DUMMY_NODE_ID) => return None,
- Some(&expr_id) => {
- return Some(tcx.map.expect_expr(expr_id));
+ Some(&None) => return None,
+ Some(&Some((expr_id, ty))) => {
+ return Some((tcx.map.expect_expr(expr_id), ty));
}
None => {}
}
let mut used_ref_id = false;
- let expr_id = match tcx.sess.cstore.maybe_get_item_ast(tcx, def_id) {
+ let expr_ty = match tcx.sess.cstore.maybe_get_item_ast(tcx, def_id) {
cstore::FoundAst::Found(&InlinedItem::Item(ref item)) => match item.node {
- hir::ItemConst(_, ref const_expr) => Some(const_expr.id),
+ hir::ItemConst(ref ty, ref const_expr) => {
+ Some((&**const_expr, ast_ty_to_prim_ty(tcx, ty)))
+ },
_ => None
},
cstore::FoundAst::Found(&InlinedItem::TraitItem(trait_id, ref ti)) => match ti.node {
if let Some(param_substs) = param_substs {
substs = substs.subst(tcx, param_substs);
}
- resolve_trait_associated_const(tcx, ti, trait_id,
- substs).map(|e| e.id)
+ resolve_trait_associated_const(tcx, ti, trait_id, substs)
}
None => None
}
_ => None
},
cstore::FoundAst::Found(&InlinedItem::ImplItem(_, ref ii)) => match ii.node {
- hir::ImplItemKind::Const(_, ref expr) => Some(expr.id),
+ hir::ImplItemKind::Const(ref ty, ref expr) => {
+ Some((&**expr, ast_ty_to_prim_ty(tcx, ty)))
+ },
_ => None
},
_ => None
// lookup with the same def_id may yield a different result.
if !used_ref_id {
tcx.extern_const_statics
- .borrow_mut().insert(def_id,
- expr_id.unwrap_or(ast::DUMMY_NODE_ID));
+ .borrow_mut()
+ .insert(def_id, expr_ty.map(|(e, t)| (e.id, t)));
}
- expr_id.map(|id| tcx.map.expect_expr(id))
+ expr_ty
}
}
#[derive(Clone, Debug, RustcEncodable, RustcDecodable)]
pub enum ConstVal {
Float(f64),
- Int(i64),
- Uint(u64),
+ Integral(ConstInt),
Str(InternedString),
ByteStr(Rc<Vec<u8>>),
Bool(bool),
Function(DefId),
Array(ast::NodeId, u64),
Repeat(ast::NodeId, u64),
+ Char(char),
+ /// A value that only occurs in case `eval_const_expr` reported an error. You should never
+ /// handle this case. Its sole purpose is to allow more errors to be reported instead of
+ /// causing a fatal error.
+ Dummy,
}
impl hash::Hash for ConstVal {
fn hash<H: hash::Hasher>(&self, state: &mut H) {
match *self {
Float(a) => unsafe { transmute::<_,u64>(a) }.hash(state),
- Int(a) => a.hash(state),
- Uint(a) => a.hash(state),
+ Integral(a) => a.hash(state),
Str(ref a) => a.hash(state),
ByteStr(ref a) => a.hash(state),
Bool(a) => a.hash(state),
Function(a) => a.hash(state),
Array(a, n) => { a.hash(state); n.hash(state) },
Repeat(a, n) => { a.hash(state); n.hash(state) },
+ Char(c) => c.hash(state),
+ Dummy => ().hash(state),
}
}
}
fn eq(&self, other: &ConstVal) -> bool {
match (self, other) {
(&Float(a), &Float(b)) => unsafe{transmute::<_,u64>(a) == transmute::<_,u64>(b)},
- (&Int(a), &Int(b)) => a == b,
- (&Uint(a), &Uint(b)) => a == b,
+ (&Integral(a), &Integral(b)) => a == b,
(&Str(ref a), &Str(ref b)) => a == b,
(&ByteStr(ref a), &ByteStr(ref b)) => a == b,
(&Bool(a), &Bool(b)) => a == b,
(&Function(a), &Function(b)) => a == b,
(&Array(a, an), &Array(b, bn)) => (a == b) && (an == bn),
(&Repeat(a, an), &Repeat(b, bn)) => (a == b) && (an == bn),
+ (&Char(a), &Char(b)) => a == b,
+ (&Dummy, &Dummy) => true, // FIXME: should this be false?
_ => false,
}
}
pub fn description(&self) -> &'static str {
match *self {
Float(_) => "float",
- Int(i) if i < 0 => "negative integer",
- Int(_) => "positive integer",
- Uint(_) => "unsigned integer",
+ Integral(i) => i.description(),
Str(_) => "string literal",
ByteStr(_) => "byte string literal",
Bool(_) => "boolean",
Function(_) => "function definition",
Array(..) => "array",
Repeat(..) => "repeat",
+ Char(..) => "char",
+ Dummy => "dummy value",
}
}
}
PatKind::Path(path.clone()),
Some(Def::Const(def_id)) |
Some(Def::AssociatedConst(def_id)) => {
- let expr = lookup_const_by_id(tcx, def_id, Some(expr.id), None).unwrap();
+ let (expr, _ty) = lookup_const_by_id(tcx, def_id, Some(expr.id), None).unwrap();
return const_expr_to_pat(tcx, expr, span);
},
_ => unreachable!(),
pub fn eval_const_expr(tcx: &TyCtxt, e: &Expr) -> ConstVal {
match eval_const_expr_partial(tcx, e, ExprTypeChecked, None) {
Ok(r) => r,
- Err(s) => tcx.sess.span_fatal(s.span, &s.description())
+ // non-const path still needs to be a fatal error, because enums are funky
+ Err(ref s) if s.kind == NonConstPath => tcx.sess.span_fatal(s.span, &s.description()),
+ Err(s) => {
+ tcx.sess.span_err(s.span, &s.description());
+ Dummy
+ },
}
}
pub kind: ErrKind,
}
-#[derive(Clone)]
+#[derive(Clone, PartialEq)]
pub enum ErrKind {
CannotCast,
CannotCastTo(&'static str),
InvalidOpForInts(hir::BinOp_),
- InvalidOpForUInts(hir::BinOp_),
InvalidOpForBools(hir::BinOp_),
InvalidOpForFloats(hir::BinOp_),
InvalidOpForIntUint(hir::BinOp_),
MiscCatchAll,
IndexOpFeatureGated,
+ Math(ConstMathErr),
+
+ IntermediateUnsignedNegative,
+ /// Expected, Got
+ TypeMismatch(String, ConstInt),
+ BadType(ConstVal),
+}
+
+impl From<ConstMathErr> for ErrKind {
+ fn from(err: ConstMathErr) -> ErrKind {
+ Math(err)
+ }
}
impl ConstEvalErr {
match self.kind {
CannotCast => "can't cast this type".into_cow(),
CannotCastTo(s) => format!("can't cast this type to {}", s).into_cow(),
- InvalidOpForInts(_) => "can't do this op on signed integrals".into_cow(),
- InvalidOpForUInts(_) => "can't do this op on unsigned integrals".into_cow(),
+ InvalidOpForInts(_) => "can't do this op on integrals".into_cow(),
InvalidOpForBools(_) => "can't do this op on bools".into_cow(),
InvalidOpForFloats(_) => "can't do this op on floats".into_cow(),
InvalidOpForIntUint(..) => "can't do this op on an isize and usize".into_cow(),
MiscBinaryOp => "bad operands for binary".into_cow(),
MiscCatchAll => "unsupported constant expr".into_cow(),
IndexOpFeatureGated => "the index operation on const values is unstable".into_cow(),
+ Math(ref err) => err.description().into_cow(),
+
+ IntermediateUnsignedNegative => "during the computation of an unsigned a negative \
+ number was encountered. This is most likely a bug in\
+ the constant evaluator".into_cow(),
+
+ TypeMismatch(ref expected, ref got) => {
+ format!("mismatched types: expected `{}`, found `{}`",
+ expected, got.description()).into_cow()
+ },
+ BadType(ref i) => format!("value of wrong type: {:?}", i).into_cow(),
}
}
}
}
}
-#[derive(Copy, Clone, PartialEq, Debug)]
-pub enum IntTy { I8, I16, I32, I64 }
-#[derive(Copy, Clone, PartialEq, Debug)]
-pub enum UintTy { U8, U16, U32, U64 }
-
-impl IntTy {
- pub fn from(tcx: &TyCtxt, t: ast::IntTy) -> IntTy {
- let t = if let ast::IntTy::Is = t {
- tcx.sess.target.int_type
- } else {
- t
- };
- match t {
- ast::IntTy::Is => unreachable!(),
- ast::IntTy::I8 => IntTy::I8,
- ast::IntTy::I16 => IntTy::I16,
- ast::IntTy::I32 => IntTy::I32,
- ast::IntTy::I64 => IntTy::I64,
- }
- }
-}
-
-impl UintTy {
- pub fn from(tcx: &TyCtxt, t: ast::UintTy) -> UintTy {
- let t = if let ast::UintTy::Us = t {
- tcx.sess.target.uint_type
- } else {
- t
- };
- match t {
- ast::UintTy::Us => unreachable!(),
- ast::UintTy::U8 => UintTy::U8,
- ast::UintTy::U16 => UintTy::U16,
- ast::UintTy::U32 => UintTy::U32,
- ast::UintTy::U64 => UintTy::U64,
- }
- }
-}
-
macro_rules! signal {
($e:expr, $exn:expr) => {
return Err(ConstEvalErr { span: $e.span, kind: $exn })
}
}
-// The const_{int,uint}_checked_{neg,add,sub,mul,div,shl,shr} family
-// of functions catch and signal overflow errors during constant
-// evaluation.
-//
-// They all take the operator's arguments (`a` and `b` if binary), the
-// overall expression (`e`) and, if available, whole expression's
-// concrete type (`opt_ety`).
-//
-// If the whole expression's concrete type is None, then this is a
-// constant evaluation happening before type check (e.g. in the check
-// to confirm that a pattern range's left-side is not greater than its
-// right-side). We do not do arithmetic modulo the type's bitwidth in
-// such a case; we just do 64-bit arithmetic and assume that later
-// passes will do it again with the type information, and thus do the
-// overflow checks then.
-
-pub fn const_int_checked_neg<'a>(
- a: i64, e: &'a Expr, opt_ety: Option<IntTy>) -> EvalResult {
-
- let (min,max) = match opt_ety {
- // (-i8::MIN is itself not an i8, etc, but this is an easy way
- // to allow literals to pass the check. Of course that does
- // not work for i64::MIN.)
- Some(IntTy::I8) => (-(i8::MAX as i64), -(i8::MIN as i64)),
- Some(IntTy::I16) => (-(i16::MAX as i64), -(i16::MIN as i64)),
- Some(IntTy::I32) => (-(i32::MAX as i64), -(i32::MIN as i64)),
- None | Some(IntTy::I64) => (-i64::MAX, -(i64::MIN+1)),
- };
-
- let oflo = a < min || a > max;
- if oflo {
- signal!(e, NegateWithOverflow(a));
- } else {
- Ok(Int(-a))
- }
-}
-
-pub fn const_uint_checked_neg<'a>(
- a: u64, _e: &'a Expr, _opt_ety: Option<UintTy>) -> EvalResult {
- // This always succeeds, and by definition, returns `(!a)+1`.
- Ok(Uint((!a).wrapping_add(1)))
-}
-
-fn const_uint_not(a: u64, opt_ety: Option<UintTy>) -> ConstVal {
- let mask = match opt_ety {
- Some(UintTy::U8) => u8::MAX as u64,
- Some(UintTy::U16) => u16::MAX as u64,
- Some(UintTy::U32) => u32::MAX as u64,
- None | Some(UintTy::U64) => u64::MAX,
- };
- Uint(!a & mask)
-}
-
-macro_rules! overflow_checking_body {
- ($a:ident, $b:ident, $ety:ident, $overflowing_op:ident,
- lhs: $to_8_lhs:ident $to_16_lhs:ident $to_32_lhs:ident,
- rhs: $to_8_rhs:ident $to_16_rhs:ident $to_32_rhs:ident $to_64_rhs:ident,
- $EnumTy:ident $T8: ident $T16: ident $T32: ident $T64: ident,
- $result_type: ident) => { {
- let (a,b,opt_ety) = ($a,$b,$ety);
- match opt_ety {
- Some($EnumTy::$T8) => match (a.$to_8_lhs(), b.$to_8_rhs()) {
- (Some(a), Some(b)) => {
- let (a, oflo) = a.$overflowing_op(b);
- (a as $result_type, oflo)
- }
- (None, _) | (_, None) => (0, true)
- },
- Some($EnumTy::$T16) => match (a.$to_16_lhs(), b.$to_16_rhs()) {
- (Some(a), Some(b)) => {
- let (a, oflo) = a.$overflowing_op(b);
- (a as $result_type, oflo)
- }
- (None, _) | (_, None) => (0, true)
- },
- Some($EnumTy::$T32) => match (a.$to_32_lhs(), b.$to_32_rhs()) {
- (Some(a), Some(b)) => {
- let (a, oflo) = a.$overflowing_op(b);
- (a as $result_type, oflo)
- }
- (None, _) | (_, None) => (0, true)
- },
- None | Some($EnumTy::$T64) => match b.$to_64_rhs() {
- Some(b) => a.$overflowing_op(b),
- None => (0, true),
- }
- }
- } }
-}
-
-macro_rules! int_arith_body {
- ($a:ident, $b:ident, $ety:ident, $overflowing_op:ident) => {
- overflow_checking_body!(
- $a, $b, $ety, $overflowing_op,
- lhs: to_i8 to_i16 to_i32,
- rhs: to_i8 to_i16 to_i32 to_i64, IntTy I8 I16 I32 I64, i64)
- }
-}
-
-macro_rules! uint_arith_body {
- ($a:ident, $b:ident, $ety:ident, $overflowing_op:ident) => {
- overflow_checking_body!(
- $a, $b, $ety, $overflowing_op,
- lhs: to_u8 to_u16 to_u32,
- rhs: to_u8 to_u16 to_u32 to_u64, UintTy U8 U16 U32 U64, u64)
- }
-}
-
-macro_rules! int_shift_body {
- ($a:ident, $b:ident, $ety:ident, $overflowing_op:ident) => {
- overflow_checking_body!(
- $a, $b, $ety, $overflowing_op,
- lhs: to_i8 to_i16 to_i32,
- rhs: to_u32 to_u32 to_u32 to_u32, IntTy I8 I16 I32 I64, i64)
- }
-}
-
-macro_rules! uint_shift_body {
- ($a:ident, $b:ident, $ety:ident, $overflowing_op:ident) => {
- overflow_checking_body!(
- $a, $b, $ety, $overflowing_op,
- lhs: to_u8 to_u16 to_u32,
- rhs: to_u32 to_u32 to_u32 to_u32, UintTy U8 U16 U32 U64, u64)
- }
-}
-
-macro_rules! pub_fn_checked_op {
- {$fn_name:ident ($a:ident : $a_ty:ty, $b:ident : $b_ty:ty,.. $WhichTy:ident) {
- $ret_oflo_body:ident $overflowing_op:ident
- $const_ty:ident $signal_exn:expr
- }} => {
- pub fn $fn_name<'a>($a: $a_ty,
- $b: $b_ty,
- e: &'a Expr,
- opt_ety: Option<$WhichTy>) -> EvalResult {
- let (ret, oflo) = $ret_oflo_body!($a, $b, opt_ety, $overflowing_op);
- if !oflo { Ok($const_ty(ret)) } else { signal!(e, $signal_exn) }
- }
- }
-}
-
-pub_fn_checked_op!{ const_int_checked_add(a: i64, b: i64,.. IntTy) {
- int_arith_body overflowing_add Int AddiWithOverflow(a, b)
-}}
-
-pub_fn_checked_op!{ const_int_checked_sub(a: i64, b: i64,.. IntTy) {
- int_arith_body overflowing_sub Int SubiWithOverflow(a, b)
-}}
-
-pub_fn_checked_op!{ const_int_checked_mul(a: i64, b: i64,.. IntTy) {
- int_arith_body overflowing_mul Int MuliWithOverflow(a, b)
-}}
-
-pub fn const_int_checked_div<'a>(
- a: i64, b: i64, e: &'a Expr, opt_ety: Option<IntTy>) -> EvalResult {
- if b == 0 { signal!(e, DivideByZero); }
- let (ret, oflo) = int_arith_body!(a, b, opt_ety, overflowing_div);
- if !oflo { Ok(Int(ret)) } else { signal!(e, DivideWithOverflow) }
-}
-
-pub fn const_int_checked_rem<'a>(
- a: i64, b: i64, e: &'a Expr, opt_ety: Option<IntTy>) -> EvalResult {
- if b == 0 { signal!(e, ModuloByZero); }
- let (ret, oflo) = int_arith_body!(a, b, opt_ety, overflowing_rem);
- if !oflo { Ok(Int(ret)) } else { signal!(e, ModuloWithOverflow) }
-}
-
-pub_fn_checked_op!{ const_int_checked_shl(a: i64, b: i64,.. IntTy) {
- int_shift_body overflowing_shl Int ShiftLeftWithOverflow
-}}
-
-pub_fn_checked_op!{ const_int_checked_shl_via_uint(a: i64, b: u64,.. IntTy) {
- int_shift_body overflowing_shl Int ShiftLeftWithOverflow
-}}
-
-pub_fn_checked_op!{ const_int_checked_shr(a: i64, b: i64,.. IntTy) {
- int_shift_body overflowing_shr Int ShiftRightWithOverflow
-}}
-
-pub_fn_checked_op!{ const_int_checked_shr_via_uint(a: i64, b: u64,.. IntTy) {
- int_shift_body overflowing_shr Int ShiftRightWithOverflow
-}}
-
-pub_fn_checked_op!{ const_uint_checked_add(a: u64, b: u64,.. UintTy) {
- uint_arith_body overflowing_add Uint AdduWithOverflow(a, b)
-}}
-
-pub_fn_checked_op!{ const_uint_checked_sub(a: u64, b: u64,.. UintTy) {
- uint_arith_body overflowing_sub Uint SubuWithOverflow(a, b)
-}}
-
-pub_fn_checked_op!{ const_uint_checked_mul(a: u64, b: u64,.. UintTy) {
- uint_arith_body overflowing_mul Uint MuluWithOverflow(a, b)
-}}
-
-pub fn const_uint_checked_div<'a>(
- a: u64, b: u64, e: &'a Expr, opt_ety: Option<UintTy>) -> EvalResult {
- if b == 0 { signal!(e, DivideByZero); }
- let (ret, oflo) = uint_arith_body!(a, b, opt_ety, overflowing_div);
- if !oflo { Ok(Uint(ret)) } else { signal!(e, DivideWithOverflow) }
-}
-
-pub fn const_uint_checked_rem<'a>(
- a: u64, b: u64, e: &'a Expr, opt_ety: Option<UintTy>) -> EvalResult {
- if b == 0 { signal!(e, ModuloByZero); }
- let (ret, oflo) = uint_arith_body!(a, b, opt_ety, overflowing_rem);
- if !oflo { Ok(Uint(ret)) } else { signal!(e, ModuloWithOverflow) }
-}
-
-pub_fn_checked_op!{ const_uint_checked_shl(a: u64, b: u64,.. UintTy) {
- uint_shift_body overflowing_shl Uint ShiftLeftWithOverflow
-}}
-
-pub_fn_checked_op!{ const_uint_checked_shl_via_int(a: u64, b: i64,.. UintTy) {
- uint_shift_body overflowing_shl Uint ShiftLeftWithOverflow
-}}
-
-pub_fn_checked_op!{ const_uint_checked_shr(a: u64, b: u64,.. UintTy) {
- uint_shift_body overflowing_shr Uint ShiftRightWithOverflow
-}}
-
-pub_fn_checked_op!{ const_uint_checked_shr_via_int(a: u64, b: i64,.. UintTy) {
- uint_shift_body overflowing_shr Uint ShiftRightWithOverflow
-}}
-
/// Evaluate a constant expression in a context where the expression isn't
/// guaranteed to be evaluatable. `ty_hint` is usually ExprTypeChecked,
/// but a few places need to evaluate constants during type-checking, like
tcx.expr_ty_opt(e)
}
};
-
- // If type of expression itself is int or uint, normalize in these
- // bindings so that isize/usize is mapped to a type with an
- // inherently known bitwidth.
- let expr_int_type = ety.and_then(|ty| {
- if let ty::TyInt(t) = ty.sty {
- Some(IntTy::from(tcx, t)) } else { None }
- });
- let expr_uint_type = ety.and_then(|ty| {
- if let ty::TyUint(t) = ty.sty {
- Some(UintTy::from(tcx, t)) } else { None }
- });
-
let result = match e.node {
hir::ExprUnary(hir::UnNeg, ref inner) => {
+ // unary neg literals already got their sign during creation
+ if let hir::ExprLit(ref lit) = inner.node {
+ use syntax::ast::*;
+ use syntax::ast::LitIntType::*;
+ const I8_OVERFLOW: u64 = ::std::i8::MAX as u64 + 1;
+ const I16_OVERFLOW: u64 = ::std::i16::MAX as u64 + 1;
+ const I32_OVERFLOW: u64 = ::std::i32::MAX as u64 + 1;
+ const I64_OVERFLOW: u64 = ::std::i64::MAX as u64 + 1;
+ match (&lit.node, ety.map(|t| &t.sty)) {
+ (&LitKind::Int(I8_OVERFLOW, Unsuffixed), Some(&ty::TyInt(IntTy::I8))) |
+ (&LitKind::Int(I8_OVERFLOW, Signed(IntTy::I8)), _) => {
+ return Ok(Integral(I8(::std::i8::MIN)))
+ },
+ (&LitKind::Int(I16_OVERFLOW, Unsuffixed), Some(&ty::TyInt(IntTy::I16))) |
+ (&LitKind::Int(I16_OVERFLOW, Signed(IntTy::I16)), _) => {
+ return Ok(Integral(I16(::std::i16::MIN)))
+ },
+ (&LitKind::Int(I32_OVERFLOW, Unsuffixed), Some(&ty::TyInt(IntTy::I32))) |
+ (&LitKind::Int(I32_OVERFLOW, Signed(IntTy::I32)), _) => {
+ return Ok(Integral(I32(::std::i32::MIN)))
+ },
+ (&LitKind::Int(I64_OVERFLOW, Unsuffixed), Some(&ty::TyInt(IntTy::I64))) |
+ (&LitKind::Int(I64_OVERFLOW, Signed(IntTy::I64)), _) => {
+ return Ok(Integral(I64(::std::i64::MIN)))
+ },
+ (&LitKind::Int(n, Unsuffixed), Some(&ty::TyInt(IntTy::Is))) |
+ (&LitKind::Int(n, Signed(IntTy::Is)), _) => {
+ match tcx.sess.target.int_type {
+ IntTy::I32 => if n == I32_OVERFLOW {
+ return Ok(Integral(Isize(Is32(::std::i32::MIN))));
+ },
+ IntTy::I64 => if n == I64_OVERFLOW {
+ return Ok(Integral(Isize(Is64(::std::i64::MIN))));
+ },
+ _ => unreachable!(),
+ }
+ },
+ _ => {},
+ }
+ }
match try!(eval_const_expr_partial(tcx, &inner, ty_hint, fn_args)) {
Float(f) => Float(-f),
- Int(n) => try!(const_int_checked_neg(n, e, expr_int_type)),
- Uint(i) => {
- try!(const_uint_checked_neg(i, e, expr_uint_type))
- }
+ Integral(i) => Integral(math!(e, -i)),
const_val => signal!(e, NegateOn(const_val)),
}
}
hir::ExprUnary(hir::UnNot, ref inner) => {
match try!(eval_const_expr_partial(tcx, &inner, ty_hint, fn_args)) {
- Int(i) => Int(!i),
- Uint(i) => const_uint_not(i, expr_uint_type),
+ Integral(i) => Integral(math!(e, !i)),
Bool(b) => Bool(!b),
const_val => signal!(e, NotOn(const_val)),
}
hir::BiShl | hir::BiShr => ty_hint.checked_or(tcx.types.usize),
_ => ty_hint
};
+ // technically, if we don't have type hints, but integral eval
+ // gives us a type through a type-suffix, cast or const def type
+ // we need to re-eval the other value of the BinOp if it was
+ // not inferred
match (try!(eval_const_expr_partial(tcx, &a, ty_hint, fn_args)),
try!(eval_const_expr_partial(tcx, &b, b_ty, fn_args))) {
(Float(a), Float(b)) => {
_ => signal!(e, InvalidOpForFloats(op.node)),
}
}
- (Int(a), Int(b)) => {
+ (Integral(a), Integral(b)) => {
+ use std::cmp::Ordering::*;
match op.node {
- hir::BiAdd => try!(const_int_checked_add(a,b,e,expr_int_type)),
- hir::BiSub => try!(const_int_checked_sub(a,b,e,expr_int_type)),
- hir::BiMul => try!(const_int_checked_mul(a,b,e,expr_int_type)),
- hir::BiDiv => try!(const_int_checked_div(a,b,e,expr_int_type)),
- hir::BiRem => try!(const_int_checked_rem(a,b,e,expr_int_type)),
- hir::BiBitAnd => Int(a & b),
- hir::BiBitOr => Int(a | b),
- hir::BiBitXor => Int(a ^ b),
- hir::BiShl => try!(const_int_checked_shl(a,b,e,expr_int_type)),
- hir::BiShr => try!(const_int_checked_shr(a,b,e,expr_int_type)),
- hir::BiEq => Bool(a == b),
- hir::BiLt => Bool(a < b),
- hir::BiLe => Bool(a <= b),
- hir::BiNe => Bool(a != b),
- hir::BiGe => Bool(a >= b),
- hir::BiGt => Bool(a > b),
+ hir::BiAdd => Integral(math!(e, a + b)),
+ hir::BiSub => Integral(math!(e, a - b)),
+ hir::BiMul => Integral(math!(e, a * b)),
+ hir::BiDiv => Integral(math!(e, a / b)),
+ hir::BiRem => Integral(math!(e, a % b)),
+ hir::BiBitAnd => Integral(math!(e, a & b)),
+ hir::BiBitOr => Integral(math!(e, a | b)),
+ hir::BiBitXor => Integral(math!(e, a ^ b)),
+ hir::BiShl => Integral(math!(e, a << b)),
+ hir::BiShr => Integral(math!(e, a >> b)),
+ hir::BiEq => Bool(math!(e, a.try_cmp(b)) == Equal),
+ hir::BiLt => Bool(math!(e, a.try_cmp(b)) == Less),
+ hir::BiLe => Bool(math!(e, a.try_cmp(b)) != Greater),
+ hir::BiNe => Bool(math!(e, a.try_cmp(b)) != Equal),
+ hir::BiGe => Bool(math!(e, a.try_cmp(b)) != Less),
+ hir::BiGt => Bool(math!(e, a.try_cmp(b)) == Greater),
_ => signal!(e, InvalidOpForInts(op.node)),
}
}
- (Uint(a), Uint(b)) => {
- match op.node {
- hir::BiAdd => try!(const_uint_checked_add(a,b,e,expr_uint_type)),
- hir::BiSub => try!(const_uint_checked_sub(a,b,e,expr_uint_type)),
- hir::BiMul => try!(const_uint_checked_mul(a,b,e,expr_uint_type)),
- hir::BiDiv => try!(const_uint_checked_div(a,b,e,expr_uint_type)),
- hir::BiRem => try!(const_uint_checked_rem(a,b,e,expr_uint_type)),
- hir::BiBitAnd => Uint(a & b),
- hir::BiBitOr => Uint(a | b),
- hir::BiBitXor => Uint(a ^ b),
- hir::BiShl => try!(const_uint_checked_shl(a,b,e,expr_uint_type)),
- hir::BiShr => try!(const_uint_checked_shr(a,b,e,expr_uint_type)),
- hir::BiEq => Bool(a == b),
- hir::BiLt => Bool(a < b),
- hir::BiLe => Bool(a <= b),
- hir::BiNe => Bool(a != b),
- hir::BiGe => Bool(a >= b),
- hir::BiGt => Bool(a > b),
- _ => signal!(e, InvalidOpForUInts(op.node)),
- }
- }
- // shifts can have any integral type as their rhs
- (Int(a), Uint(b)) => {
- match op.node {
- hir::BiShl => try!(const_int_checked_shl_via_uint(a,b,e,expr_int_type)),
- hir::BiShr => try!(const_int_checked_shr_via_uint(a,b,e,expr_int_type)),
- _ => signal!(e, InvalidOpForIntUint(op.node)),
- }
- }
- (Uint(a), Int(b)) => {
- match op.node {
- hir::BiShl => try!(const_uint_checked_shl_via_int(a,b,e,expr_uint_type)),
- hir::BiShr => try!(const_uint_checked_shr_via_int(a,b,e,expr_uint_type)),
- _ => signal!(e, InvalidOpForUintInt(op.node)),
- }
- }
(Bool(a), Bool(b)) => {
Bool(match op.node {
hir::BiAnd => a && b,
}
}
hir::ExprCast(ref base, ref target_ty) => {
- let ety = ety.or_else(|| ast_ty_to_prim_ty(tcx, &target_ty))
+ let ety = ast_ty_to_prim_ty(tcx, &target_ty).or_else(|| ety)
.unwrap_or_else(|| {
tcx.sess.span_fatal(target_ty.span,
"target type not found for const cast")
let base_hint = if let ExprTypeChecked = ty_hint {
ExprTypeChecked
} else {
- // FIXME (#23833): the type-hint can cause problems,
- // e.g. `(i8::MAX + 1_i8) as u32` feeds in `u32` as result
- // type to the sum, and thus no overflow is signaled.
match tcx.expr_ty_opt(&base) {
Some(t) => UncheckedExprHint(t),
None => ty_hint
}
};
- let val = try!(eval_const_expr_partial(tcx, &base, base_hint, fn_args));
+ let val = match eval_const_expr_partial(tcx, &base, base_hint, fn_args) {
+ Ok(val) => val,
+ Err(ConstEvalErr { kind: TypeMismatch(_, val), .. }) => {
+ // Something like `5i8 as usize` doesn't need a type hint for the base
+ // instead take the type hint from the inner value
+ let hint = match val.int_type() {
+ Some(IntType::UnsignedInt(ty)) => ty_hint.checked_or(tcx.mk_mach_uint(ty)),
+ Some(IntType::SignedInt(ty)) => ty_hint.checked_or(tcx.mk_mach_int(ty)),
+ // we had a type hint, so we can't have an unknown type
+ None => unreachable!(),
+ };
+ try!(eval_const_expr_partial(tcx, &base, hint, fn_args))
+ },
+ Err(e) => return Err(e),
+ };
match cast_const(tcx, val, ety) {
Ok(val) => val,
Err(kind) => return Err(ConstEvalErr { span: e.span, kind: kind }),
if def.depth != 0 {
signal!(e, UnresolvedPath);
}
- Some(def.full_def())
+ def.full_def()
} else {
- None
+ signal!(e, NonConstPath);
};
- let (const_expr, const_ty) = match opt_def {
- Some(Def::Const(def_id)) => {
- if let Some(node_id) = tcx.map.as_local_node_id(def_id) {
- match tcx.map.find(node_id) {
- Some(ast_map::NodeItem(it)) => match it.node {
- hir::ItemConst(ref ty, ref expr) => {
- (Some(&**expr), Some(&**ty))
- }
- _ => (None, None)
- },
- _ => (None, None)
- }
+ match opt_def {
+ Def::Const(def_id) |
+ Def::AssociatedConst(def_id) => {
+ let maybe_ref_id = if let ExprTypeChecked = ty_hint {
+ Some(e.id)
} else {
- (lookup_const_by_id(tcx, def_id, Some(e.id), None), None)
+ None
+ };
+ if let Some((e, ty)) = lookup_const_by_id(tcx, def_id, maybe_ref_id, None) {
+ let item_hint = match ty {
+ Some(ty) => ty_hint.checked_or(ty),
+ None => ty_hint,
+ };
+ try!(eval_const_expr_partial(tcx, e, item_hint, None))
+ } else {
+ signal!(e, NonConstPath);
}
- }
- Some(Def::AssociatedConst(def_id)) => {
- if let Some(node_id) = tcx.map.as_local_node_id(def_id) {
- match impl_or_trait_container(tcx, def_id) {
- ty::TraitContainer(trait_id) => match tcx.map.find(node_id) {
- Some(ast_map::NodeTraitItem(ti)) => match ti.node {
- hir::ConstTraitItem(ref ty, _) => {
- if let ExprTypeChecked = ty_hint {
- let substs = tcx.node_id_item_substs(e.id).substs;
- (resolve_trait_associated_const(tcx,
- ti,
- trait_id,
- substs),
- Some(&**ty))
- } else {
- (None, None)
- }
- }
- _ => (None, None)
- },
- _ => (None, None)
- },
- ty::ImplContainer(_) => match tcx.map.find(node_id) {
- Some(ast_map::NodeImplItem(ii)) => match ii.node {
- hir::ImplItemKind::Const(ref ty, ref expr) => {
- (Some(&**expr), Some(&**ty))
- }
- _ => (None, None)
- },
- _ => (None, None)
- },
- }
+ },
+ Def::Variant(enum_def, variant_def) => {
+ if let Some(const_expr) = lookup_variant_by_id(tcx, enum_def, variant_def) {
+ try!(eval_const_expr_partial(tcx, const_expr, ty_hint, None))
} else {
- (lookup_const_by_id(tcx, def_id, Some(e.id), None), None)
+ signal!(e, NonConstPath);
}
}
- Some(Def::Variant(enum_def, variant_def)) => {
- (lookup_variant_by_id(tcx, enum_def, variant_def), None)
+ Def::Struct(..) => {
+ ConstVal::Struct(e.id)
}
- Some(Def::Struct(..)) => {
- return Ok(ConstVal::Struct(e.id))
- }
- Some(Def::Local(_, id)) => {
+ Def::Local(_, id) => {
debug!("Def::Local({:?}): {:?}", id, fn_args);
if let Some(val) = fn_args.and_then(|args| args.get(&id)) {
- return Ok(val.clone());
+ val.clone()
} else {
- (None, None)
+ signal!(e, NonConstPath);
}
},
- Some(Def::Method(id)) | Some(Def::Fn(id)) => return Ok(Function(id)),
- _ => (None, None)
- };
- let const_expr = match const_expr {
- Some(actual_e) => actual_e,
- None => signal!(e, NonConstPath)
- };
- let item_hint = if let UncheckedExprNoHint = ty_hint {
- match const_ty {
- Some(ty) => match ast_ty_to_prim_ty(tcx, ty) {
- Some(ty) => UncheckedExprHint(ty),
- None => UncheckedExprNoHint
- },
- None => UncheckedExprNoHint
- }
- } else {
- ty_hint
- };
- try!(eval_const_expr_partial(tcx, const_expr, item_hint, fn_args))
+ Def::Method(id) | Def::Fn(id) => Function(id),
+ _ => signal!(e, NonConstPath),
+ }
}
hir::ExprCall(ref callee, ref args) => {
let sub_ty_hint = ty_hint.erase_hint();
let mut call_args = NodeMap();
for (arg, arg_expr) in decl.inputs.iter().zip(args.iter()) {
+ let arg_hint = ty_hint.erase_hint();
let arg_val = try!(eval_const_expr_partial(
tcx,
arg_expr,
- sub_ty_hint,
+ arg_hint,
fn_args
));
debug!("const call arg: {:?}", arg);
debug!("const call({:?})", call_args);
try!(eval_const_expr_partial(tcx, &result, ty_hint, Some(&call_args)))
},
- hir::ExprLit(ref lit) => lit_to_const(tcx.sess, e.span, &lit, ety),
+ hir::ExprLit(ref lit) => try!(lit_to_const(&lit.node, tcx, ety, lit.span)),
hir::ExprBlock(ref block) => {
match block.expr {
Some(ref expr) => try!(eval_const_expr_partial(tcx, &expr, ty_hint, fn_args)),
let arr = try!(eval_const_expr_partial(tcx, arr, arr_hint, fn_args));
let idx_hint = ty_hint.checked_or(tcx.types.usize);
let idx = match try!(eval_const_expr_partial(tcx, idx, idx_hint, fn_args)) {
- Int(i) if i >= 0 => i as u64,
- Int(_) => signal!(idx, IndexNegative),
- Uint(i) => i,
+ Integral(Usize(i)) => i.as_u64(tcx.sess.target.uint_type),
+ Integral(_) => unreachable!(),
_ => signal!(idx, IndexNotInt),
};
+ assert_eq!(idx as usize as u64, idx);
match arr {
Array(_, n) if idx >= n => signal!(e, IndexOutOfBounds),
- Array(v, _) => if let hir::ExprVec(ref v) = tcx.map.expect_expr(v).node {
+ Array(v, n) => if let hir::ExprVec(ref v) = tcx.map.expect_expr(v).node {
+ assert_eq!(n as usize as u64, n);
try!(eval_const_expr_partial(tcx, &v[idx as usize], ty_hint, fn_args))
} else {
unreachable!()
fn_args,
)),
- ByteStr(ref data) if idx as usize >= data.len()
- => signal!(e, IndexOutOfBounds),
- ByteStr(data) => Uint(data[idx as usize] as u64),
+ ByteStr(ref data) if idx >= data.len() as u64 => signal!(e, IndexOutOfBounds),
+ ByteStr(data) => {
+ Integral(U8(data[idx as usize]))
+ },
- Str(ref s) if idx as usize >= s.len()
- => signal!(e, IndexOutOfBounds),
- Str(_) => unimplemented!(), // there's no const_char type
+ Str(ref s) if idx as usize >= s.len() => signal!(e, IndexOutOfBounds),
+ Str(_) => unimplemented!(), // FIXME: return a const char
_ => signal!(e, IndexedNonVec),
}
}
Repeat(
e.id,
match try!(eval_const_expr_partial(tcx, &n, len_hint, fn_args)) {
- Int(i) if i >= 0 => i as u64,
- Int(_) => signal!(e, RepeatCountNotNatural),
- Uint(i) => i,
+ Integral(Usize(i)) => i.as_u64(tcx.sess.target.uint_type),
+ Integral(_) => signal!(e, RepeatCountNotNatural),
_ => signal!(e, RepeatCountNotInt),
},
)
if let Tuple(tup_id) = c {
if let hir::ExprTup(ref fields) = tcx.map.expect_expr(tup_id).node {
if index.node < fields.len() {
- return eval_const_expr_partial(tcx, &fields[index.node], base_hint, fn_args)
+ try!(eval_const_expr_partial(tcx, &fields[index.node], ty_hint, fn_args))
} else {
signal!(e, TupleIndexOutOfBounds);
}
// if the idents are compared run-pass/issue-19244 fails
if let Some(f) = fields.iter().find(|f| f.name.node
== field_name.node) {
- return eval_const_expr_partial(tcx, &f.expr, base_hint, fn_args)
+ try!(eval_const_expr_partial(tcx, &f.expr, ty_hint, fn_args))
} else {
signal!(e, MissingStructField);
}
_ => signal!(e, MiscCatchAll)
};
- Ok(result)
+ match (ety.map(|t| &t.sty), result) {
+ (Some(ref ty_hint), Integral(i)) => Ok(Integral(try!(infer(i, tcx, ty_hint, e.span)))),
+ (_, result) => Ok(result),
+ }
}
-fn impl_or_trait_container(tcx: &TyCtxt, def_id: DefId) -> ty::ImplOrTraitItemContainer {
- // This is intended to be equivalent to tcx.impl_or_trait_item(def_id).container()
- // for local def_id, but it can be called before tcx.impl_or_trait_items is complete.
- if let Some(node_id) = tcx.map.as_local_node_id(def_id) {
- if let Some(ast_map::NodeItem(item)) = tcx.map.find(tcx.map.get_parent_node(node_id)) {
- let container_id = tcx.map.local_def_id(item.id);
- match item.node {
- hir::ItemImpl(..) => return ty::ImplContainer(container_id),
- hir::ItemTrait(..) => return ty::TraitContainer(container_id),
- _ => ()
+fn infer<'tcx>(
+ i: ConstInt,
+ tcx: &TyCtxt<'tcx>,
+ ty_hint: &ty::TypeVariants<'tcx>,
+ span: Span
+) -> Result<ConstInt, ConstEvalErr> {
+ use syntax::ast::*;
+
+ let err = |e| ConstEvalErr {
+ span: span,
+ kind: e,
+ };
+
+ match (ty_hint, i) {
+ (&ty::TyInt(IntTy::I8), result @ I8(_)) => Ok(result),
+ (&ty::TyInt(IntTy::I16), result @ I16(_)) => Ok(result),
+ (&ty::TyInt(IntTy::I32), result @ I32(_)) => Ok(result),
+ (&ty::TyInt(IntTy::I64), result @ I64(_)) => Ok(result),
+ (&ty::TyInt(IntTy::Is), result @ Isize(_)) => Ok(result),
+
+ (&ty::TyUint(UintTy::U8), result @ U8(_)) => Ok(result),
+ (&ty::TyUint(UintTy::U16), result @ U16(_)) => Ok(result),
+ (&ty::TyUint(UintTy::U32), result @ U32(_)) => Ok(result),
+ (&ty::TyUint(UintTy::U64), result @ U64(_)) => Ok(result),
+ (&ty::TyUint(UintTy::Us), result @ Usize(_)) => Ok(result),
+
+ (&ty::TyInt(IntTy::I8), Infer(i)) => Ok(I8(i as i64 as i8)),
+ (&ty::TyInt(IntTy::I16), Infer(i)) => Ok(I16(i as i64 as i16)),
+ (&ty::TyInt(IntTy::I32), Infer(i)) => Ok(I32(i as i64 as i32)),
+ (&ty::TyInt(IntTy::I64), Infer(i)) => Ok(I64(i as i64)),
+ (&ty::TyInt(IntTy::Is), Infer(i)) => {
+ match ConstIsize::new(i as i64, tcx.sess.target.int_type) {
+ Ok(val) => Ok(Isize(val)),
+ Err(_) => Ok(Isize(ConstIsize::Is32(i as i64 as i32))),
}
- }
- panic!("No impl or trait container for {:?}", def_id);
+ },
+
+ (&ty::TyInt(IntTy::I8), InferSigned(i)) => Ok(I8(i as i8)),
+ (&ty::TyInt(IntTy::I16), InferSigned(i)) => Ok(I16(i as i16)),
+ (&ty::TyInt(IntTy::I32), InferSigned(i)) => Ok(I32(i as i32)),
+ (&ty::TyInt(IntTy::I64), InferSigned(i)) => Ok(I64(i)),
+ (&ty::TyInt(IntTy::Is), InferSigned(i)) => {
+ match ConstIsize::new(i, tcx.sess.target.int_type) {
+ Ok(val) => Ok(Isize(val)),
+ Err(_) => Ok(Isize(ConstIsize::Is32(i as i32))),
+ }
+ },
+
+ (&ty::TyUint(UintTy::U8), Infer(i)) => Ok(U8(i as u8)),
+ (&ty::TyUint(UintTy::U16), Infer(i)) => Ok(U16(i as u16)),
+ (&ty::TyUint(UintTy::U32), Infer(i)) => Ok(U32(i as u32)),
+ (&ty::TyUint(UintTy::U64), Infer(i)) => Ok(U64(i)),
+ (&ty::TyUint(UintTy::Us), Infer(i)) => {
+ match ConstUsize::new(i, tcx.sess.target.uint_type) {
+ Ok(val) => Ok(Usize(val)),
+ Err(_) => Ok(Usize(ConstUsize::Us32(i as u32))),
+ }
+ },
+ (&ty::TyUint(_), InferSigned(_)) => Err(err(IntermediateUnsignedNegative)),
+
+ (&ty::TyInt(ity), i) => Err(err(TypeMismatch(ity.to_string(), i))),
+ (&ty::TyUint(ity), i) => Err(err(TypeMismatch(ity.to_string(), i))),
+
+ (&ty::TyEnum(ref adt, _), i) => {
+ let hints = tcx.lookup_repr_hints(adt.did);
+ let int_ty = tcx.enum_repr_type(hints.iter().next());
+ infer(i, tcx, &int_ty.to_ty(tcx).sty, span)
+ },
+ (_, i) => Err(err(BadType(ConstVal::Integral(i)))),
}
- panic!("{:?} is not local", def_id);
}
fn resolve_trait_associated_const<'a, 'tcx: 'a>(tcx: &'a TyCtxt<'tcx>,
ti: &'tcx hir::TraitItem,
trait_id: DefId,
rcvr_substs: subst::Substs<'tcx>)
- -> Option<&'tcx Expr>
+ -> Option<(&'tcx Expr, Option<ty::Ty<'tcx>>)>
{
let trait_ref = ty::Binder(
rcvr_substs.erase_regions().to_trait_ref(tcx, trait_id)
trait_ref);
tcx.populate_implementations_for_trait_if_necessary(trait_ref.def_id());
- let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, None);
+ let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, None, ProjectionMode::AnyFinal);
let mut selcx = traits::SelectionContext::new(&infcx);
let obligation = traits::Obligation::new(traits::ObligationCause::dummy(),
}
};
+ // NOTE: this code does not currently account for specialization, but when
+ // it does so, it should hook into the ProjectionMode to determine when the
+ // constant should resolve; this will also require plumbing through to this
+ // function whether we are in "trans mode" to pick the right ProjectionMode
+ // when constructing the inference context above.
match selection {
traits::VtableImpl(ref impl_data) => {
match tcx.associated_consts(impl_data.impl_def_id)
.iter().find(|ic| ic.name == ti.name) {
Some(ic) => lookup_const_by_id(tcx, ic.def_id, None, None),
None => match ti.node {
- hir::ConstTraitItem(_, Some(ref expr)) => Some(&*expr),
+ hir::ConstTraitItem(ref ty, Some(ref expr)) => {
+ Some((&*expr, ast_ty_to_prim_ty(tcx, ty)))
+ },
_ => None,
},
}
}
}
-fn cast_const<'tcx>(tcx: &TyCtxt<'tcx>, val: ConstVal, ty: Ty) -> CastResult {
- macro_rules! convert_val {
- ($intermediate_ty:ty, $const_type:ident, $target_ty:ty) => {
- match val {
- Bool(b) => Ok($const_type(b as u64 as $intermediate_ty as $target_ty)),
- Uint(u) => Ok($const_type(u as $intermediate_ty as $target_ty)),
- Int(i) => Ok($const_type(i as $intermediate_ty as $target_ty)),
- Float(f) => Ok($const_type(f as $intermediate_ty as $target_ty)),
- _ => Err(ErrKind::CannotCastTo(stringify!($const_type))),
+fn cast_const_int<'tcx>(tcx: &TyCtxt<'tcx>, val: ConstInt, ty: ty::Ty) -> CastResult {
+ let v = val.to_u64_unchecked();
+ match ty.sty {
+ ty::TyBool if v == 0 => Ok(Bool(false)),
+ ty::TyBool if v == 1 => Ok(Bool(true)),
+ ty::TyInt(ast::IntTy::I8) => Ok(Integral(I8(v as i64 as i8))),
+ ty::TyInt(ast::IntTy::I16) => Ok(Integral(I16(v as i64 as i16))),
+ ty::TyInt(ast::IntTy::I32) => Ok(Integral(I32(v as i64 as i32))),
+ ty::TyInt(ast::IntTy::I64) => Ok(Integral(I64(v as i64))),
+ ty::TyInt(ast::IntTy::Is) => {
+ match ConstIsize::new(v as i64, tcx.sess.target.int_type) {
+ Ok(val) => Ok(Integral(Isize(val))),
+ Err(_) => Ok(Integral(Isize(ConstIsize::Is32(v as i64 as i32)))),
}
- }
+ },
+ ty::TyUint(ast::UintTy::U8) => Ok(Integral(U8(v as u8))),
+ ty::TyUint(ast::UintTy::U16) => Ok(Integral(U16(v as u16))),
+ ty::TyUint(ast::UintTy::U32) => Ok(Integral(U32(v as u32))),
+ ty::TyUint(ast::UintTy::U64) => Ok(Integral(U64(v))),
+ ty::TyUint(ast::UintTy::Us) => {
+ match ConstUsize::new(v, tcx.sess.target.uint_type) {
+ Ok(val) => Ok(Integral(Usize(val))),
+ Err(_) => Ok(Integral(Usize(ConstUsize::Us32(v as u32)))),
+ }
+ },
+ ty::TyFloat(ast::FloatTy::F64) if val.is_negative() => {
+ // FIXME: this could probably be prettier
+ // there's no easy way to turn an `Infer` into a f64
+ let val = try!((-val).map_err(Math));
+ let val = val.to_u64().unwrap() as f64;
+ let val = -val;
+ Ok(Float(val))
+ },
+ ty::TyFloat(ast::FloatTy::F64) => Ok(Float(val.to_u64().unwrap() as f64)),
+ ty::TyFloat(ast::FloatTy::F32) if val.is_negative() => {
+ let val = try!((-val).map_err(Math));
+ let val = val.to_u64().unwrap() as f32;
+ let val = -val;
+ Ok(Float(val as f64))
+ },
+ ty::TyFloat(ast::FloatTy::F32) => Ok(Float(val.to_u64().unwrap() as f32 as f64)),
+ _ => Err(CannotCast),
}
+}
- // Issue #23890: If isize/usize, then dispatch to appropriate target representation type
- match (&ty.sty, tcx.sess.target.int_type, tcx.sess.target.uint_type) {
- (&ty::TyInt(ast::IntTy::Is), ast::IntTy::I32, _) => return convert_val!(i32, Int, i64),
- (&ty::TyInt(ast::IntTy::Is), ast::IntTy::I64, _) => return convert_val!(i64, Int, i64),
- (&ty::TyInt(ast::IntTy::Is), _, _) => panic!("unexpected target.int_type"),
-
- (&ty::TyUint(ast::UintTy::Us), _, ast::UintTy::U32) => return convert_val!(u32, Uint, u64),
- (&ty::TyUint(ast::UintTy::Us), _, ast::UintTy::U64) => return convert_val!(u64, Uint, u64),
- (&ty::TyUint(ast::UintTy::Us), _, _) => panic!("unexpected target.uint_type"),
-
- _ => {}
+fn cast_const_float<'tcx>(tcx: &TyCtxt<'tcx>, f: f64, ty: ty::Ty) -> CastResult {
+ match ty.sty {
+ ty::TyInt(_) if f >= 0.0 => cast_const_int(tcx, Infer(f as u64), ty),
+ ty::TyInt(_) => cast_const_int(tcx, InferSigned(f as i64), ty),
+ ty::TyUint(_) if f >= 0.0 => cast_const_int(tcx, Infer(f as u64), ty),
+ ty::TyFloat(ast::FloatTy::F64) => Ok(Float(f)),
+ ty::TyFloat(ast::FloatTy::F32) => Ok(Float(f as f32 as f64)),
+ _ => Err(CannotCast),
}
+}
- match ty.sty {
- ty::TyInt(ast::IntTy::Is) => unreachable!(),
- ty::TyUint(ast::UintTy::Us) => unreachable!(),
-
- ty::TyInt(ast::IntTy::I8) => convert_val!(i8, Int, i64),
- ty::TyInt(ast::IntTy::I16) => convert_val!(i16, Int, i64),
- ty::TyInt(ast::IntTy::I32) => convert_val!(i32, Int, i64),
- ty::TyInt(ast::IntTy::I64) => convert_val!(i64, Int, i64),
-
- ty::TyUint(ast::UintTy::U8) => convert_val!(u8, Uint, u64),
- ty::TyUint(ast::UintTy::U16) => convert_val!(u16, Uint, u64),
- ty::TyUint(ast::UintTy::U32) => convert_val!(u32, Uint, u64),
- ty::TyUint(ast::UintTy::U64) => convert_val!(u64, Uint, u64),
-
- ty::TyFloat(ast::FloatTy::F32) => convert_val!(f32, Float, f64),
- ty::TyFloat(ast::FloatTy::F64) => convert_val!(f64, Float, f64),
- _ => Err(ErrKind::CannotCast),
+fn cast_const<'tcx>(tcx: &TyCtxt<'tcx>, val: ConstVal, ty: ty::Ty) -> CastResult {
+ match val {
+ Integral(i) => cast_const_int(tcx, i, ty),
+ Bool(b) => cast_const_int(tcx, Infer(b as u64), ty),
+ Float(f) => cast_const_float(tcx, f, ty),
+ Char(c) => cast_const_int(tcx, Infer(c as u64), ty),
+ _ => Err(CannotCast),
}
}
-fn lit_to_const(sess: &Session, span: Span, lit: &ast::Lit, ty_hint: Option<Ty>) -> ConstVal {
- match lit.node {
- ast::LitKind::Str(ref s, _) => Str((*s).clone()),
- ast::LitKind::ByteStr(ref data) => {
- ByteStr(data.clone())
- }
- ast::LitKind::Byte(n) => Uint(n as u64),
- ast::LitKind::Char(n) => Uint(n as u64),
- ast::LitKind::Int(n, ast::LitIntType::Signed(_)) => Int(n as i64),
- ast::LitKind::Int(n, ast::LitIntType::Unsuffixed) => {
- match ty_hint.map(|ty| &ty.sty) {
- Some(&ty::TyUint(_)) => Uint(n),
- _ => Int(n as i64)
+fn lit_to_const<'tcx>(lit: &ast::LitKind,
+ tcx: &TyCtxt<'tcx>,
+ ty_hint: Option<Ty<'tcx>>,
+ span: Span,
+ ) -> Result<ConstVal, ConstEvalErr> {
+ use syntax::ast::*;
+ use syntax::ast::LitIntType::*;
+ match *lit {
+ LitKind::Str(ref s, _) => Ok(Str((*s).clone())),
+ LitKind::ByteStr(ref data) => Ok(ByteStr(data.clone())),
+ LitKind::Byte(n) => Ok(Integral(U8(n))),
+ LitKind::Int(n, Signed(ity)) => {
+ infer(InferSigned(n as i64), tcx, &ty::TyInt(ity), span).map(Integral)
+ },
+
+ LitKind::Int(n, Unsuffixed) => {
+ match ty_hint.map(|t| &t.sty) {
+ Some(&ty::TyInt(ity)) => {
+ infer(InferSigned(n as i64), tcx, &ty::TyInt(ity), span).map(Integral)
+ },
+ Some(&ty::TyUint(uty)) => {
+ infer(Infer(n), tcx, &ty::TyUint(uty), span).map(Integral)
+ },
+ None => Ok(Integral(Infer(n))),
+ Some(&ty::TyEnum(ref adt, _)) => {
+ let hints = tcx.lookup_repr_hints(adt.did);
+ let int_ty = tcx.enum_repr_type(hints.iter().next());
+ infer(Infer(n), tcx, &int_ty.to_ty(tcx).sty, span).map(Integral)
+ },
+ Some(ty_hint) => panic!("bad ty_hint: {:?}, {:?}", ty_hint, lit),
}
- }
- ast::LitKind::Int(n, ast::LitIntType::Unsigned(_)) => Uint(n),
- ast::LitKind::Float(ref n, _) |
- ast::LitKind::FloatUnsuffixed(ref n) => {
+ },
+ LitKind::Int(n, Unsigned(ity)) => {
+ infer(Infer(n), tcx, &ty::TyUint(ity), span).map(Integral)
+ },
+
+ LitKind::Float(ref n, _) |
+ LitKind::FloatUnsuffixed(ref n) => {
if let Ok(x) = n.parse::<f64>() {
- Float(x)
+ Ok(Float(x))
} else {
// FIXME(#31407) this is only necessary because float parsing is buggy
- sess.span_bug(span, "could not evaluate float literal (see issue #31407)");
+ tcx.sess.span_bug(span, "could not evaluate float literal (see issue #31407)");
}
}
- ast::LitKind::Bool(b) => Bool(b)
+ LitKind::Bool(b) => Ok(Bool(b)),
+ LitKind::Char(c) => Ok(Char(c)),
}
}
pub fn compare_const_vals(a: &ConstVal, b: &ConstVal) -> Option<Ordering> {
- Some(match (a, b) {
- (&Int(a), &Int(b)) => a.cmp(&b),
- (&Uint(a), &Uint(b)) => a.cmp(&b),
+ match (a, b) {
+ (&Integral(a), &Integral(b)) => a.try_cmp(b).ok(),
(&Float(a), &Float(b)) => {
// This is pretty bad but it is the existing behavior.
- if a == b {
+ Some(if a == b {
Ordering::Equal
} else if a < b {
Ordering::Less
} else {
Ordering::Greater
- }
+ })
}
- (&Str(ref a), &Str(ref b)) => a.cmp(b),
- (&Bool(a), &Bool(b)) => a.cmp(&b),
- (&ByteStr(ref a), &ByteStr(ref b)) => a.cmp(b),
- _ => return None
- })
+ (&Str(ref a), &Str(ref b)) => Some(a.cmp(b)),
+ (&Bool(a), &Bool(b)) => Some(a.cmp(&b)),
+ (&ByteStr(ref a), &ByteStr(ref b)) => Some(a.cmp(b)),
+ (&Char(a), &Char(ref b)) => Some(a.cmp(b)),
+ _ => None,
+ }
}
pub fn compare_lit_exprs<'tcx>(tcx: &TyCtxt<'tcx>,
-> Option<ty::adjustment::CustomCoerceUnsized>;
fn associated_consts(&self, tcx: &TyCtxt<'tcx>, def: DefId)
-> Vec<Rc<ty::AssociatedConst<'tcx>>>;
+ fn impl_parent(&self, impl_def_id: DefId) -> Option<DefId>;
// trait/impl-item info
fn trait_of_item(&self, tcx: &TyCtxt<'tcx>, def_id: DefId)
{ unimplemented!() }
fn associated_consts(&self, tcx: &TyCtxt<'tcx>, def: DefId)
-> Vec<Rc<ty::AssociatedConst<'tcx>>> { unimplemented!() }
+ fn impl_parent(&self, def: DefId) -> Option<DefId> { unimplemented!() }
// trait/impl-item info
fn trait_of_item(&self, tcx: &TyCtxt<'tcx>, def_id: DefId)
use middle::subst;
use middle::subst::Substs;
use middle::subst::Subst;
-use middle::traits;
+use middle::traits::{self, ProjectionMode};
use middle::ty::adjustment;
use middle::ty::{TyVid, IntVid, FloatVid};
use middle::ty::{self, Ty, TyCtxt};
// directly.
normalize: bool,
+ // Sadly, the behavior of projection varies a bit depending on the
+ // stage of compilation. The specifics are given in the
+ // documentation for `ProjectionMode`.
+ projection_mode: ProjectionMode,
+
err_count_on_creation: usize,
}
pub fn new_infer_ctxt<'a, 'tcx>(tcx: &'a TyCtxt<'tcx>,
tables: &'a RefCell<ty::Tables<'tcx>>,
- param_env: Option<ty::ParameterEnvironment<'a, 'tcx>>)
+ param_env: Option<ty::ParameterEnvironment<'a, 'tcx>>,
+ projection_mode: ProjectionMode)
-> InferCtxt<'a, 'tcx> {
InferCtxt {
tcx: tcx,
parameter_environment: param_env.unwrap_or(tcx.empty_parameter_environment()),
reported_trait_errors: RefCell::new(FnvHashSet()),
normalize: false,
+ projection_mode: projection_mode,
err_count_on_creation: tcx.sess.err_count()
}
}
pub fn normalizing_infer_ctxt<'a, 'tcx>(tcx: &'a TyCtxt<'tcx>,
- tables: &'a RefCell<ty::Tables<'tcx>>)
+ tables: &'a RefCell<ty::Tables<'tcx>>,
+ projection_mode: ProjectionMode)
-> InferCtxt<'a, 'tcx> {
- let mut infcx = new_infer_ctxt(tcx, tables, None);
+ let mut infcx = new_infer_ctxt(tcx, tables, None, projection_mode);
infcx.normalize = true;
infcx
}
region_vars_snapshot: RegionSnapshot,
}
+// NOTE: Callable from trans only!
pub fn normalize_associated_type<'tcx,T>(tcx: &TyCtxt<'tcx>, value: &T) -> T
where T : TypeFoldable<'tcx>
{
return value;
}
- let infcx = new_infer_ctxt(tcx, &tcx.tables, None);
+ let infcx = new_infer_ctxt(tcx, &tcx.tables, None, ProjectionMode::Any);
let mut selcx = traits::SelectionContext::new(&infcx);
let cause = traits::ObligationCause::dummy();
let traits::Normalized { value: result, obligations } =
}
impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
+ pub fn projection_mode(&self) -> ProjectionMode {
+ self.projection_mode
+ }
+
pub fn freshen<T:TypeFoldable<'tcx>>(&self, t: T) -> T {
t.fold_with(&mut self.freshener())
}
substs: &mut Substs<'tcx>,
defs: &[ty::TypeParameterDef<'tcx>]) {
- let mut vars = Vec::with_capacity(defs.len());
-
for def in defs.iter() {
let default = def.default.map(|default| {
type_variable::Default {
let ty_var = self.next_ty_var_with_default(default);
substs.types.push(space, ty_var);
- vars.push(ty_var)
}
}
Substs { types: types, regions: regions }
}
+ pub fn with_method_from_subst(self, other: &Substs<'tcx>) -> Substs<'tcx> {
+ let Substs { types, regions } = self;
+ let types = types.with_slice(FnSpace, other.types.get_slice(FnSpace));
+ let regions = regions.map(|r| {
+ r.with_slice(FnSpace, other.regions().get_slice(FnSpace))
+ });
+ Substs { types: types, regions: regions }
+ }
+
/// Creates a trait-ref out of this substs, ignoring the FnSpace substs
pub fn to_trait_ref(&self, tcx: &TyCtxt<'tcx>, trait_id: DefId)
-> ty::TraitRef<'tcx> {
serious of annoying and weird bugs like #22019 and #18290. This simple
rule seems to be pretty clearly safe and also still retains a very
high hit rate (~95% when compiling rustc).
+
+# Specialization
+
+Defined in the `specialize` module.
+
+The basic strategy is to build up a *specialization graph* during
+coherence checking. Insertion into the graph locates the right place
+to put an impl in the specialization hierarchy; if there is no right
+place (due to partial overlap but no containment), you get an overlap
+error. Specialization is consulted when selecting an impl (of course),
+and the graph is consulted when propagating defaults down the
+specialization hierarchy.
+
+You might expect that the specialization graph would be used during
+selection -- i.e., when actually performing specialization. This is
+not done for two reasons:
+
+- It's merely an optimization: given a set of candidates that apply,
+ we can determine the most specialized one by comparing them directly
+ for specialization, rather than consulting the graph. Given that we
+ also cache the results of selection, the benefit of this
+ optimization is questionable.
+
+- To build the specialization graph in the first place, we need to use
+ selection (because we need to determine whether one impl specializes
+ another). Dealing with this reentrancy would require some additional
+ mode switch for selection. Given that there seems to be no strong
+ reason to use the graph anyway, we stick with a simpler approach in
+ selection, and use the graph only for propagating default
+ implementations.
+
+Trait impl selection can succeed even when multiple impls can apply,
+as long as they are part of the same specialization family. In that
+case, it returns a *single* impl on success -- this is the most
+specialized impl *known* to apply. However, if there are any inference
+variables in play, the returned impl may not be the actual impl we
+will use at trans time. Thus, we take special care to avoid projecting
+associated types unless either (1) the associated type does not use
+`default` and thus cannot be overridden or (2) all input types are
+known concretely.
//! See `README.md` for high-level documentation
-use super::{SelectionContext};
-use super::{Obligation, ObligationCause};
+use super::{SelectionContext, Obligation, ObligationCause};
use middle::cstore::LOCAL_CRATE;
use middle::def_id::DefId;
#[derive(Copy, Clone)]
struct InferIsLocal(bool);
-/// If there are types that satisfy both impls, returns an `ImplTy`
-/// with those types substituted (by updating the given `infcx`)
+/// If there are types that satisfy both impls, returns a suitably-freshened
+/// `ImplHeader` with those types substituted
pub fn overlapping_impls<'cx, 'tcx>(infcx: &InferCtxt<'cx, 'tcx>,
impl1_def_id: DefId,
impl2_def_id: DefId)
pub use self::coherence::overlapping_impls;
pub use self::coherence::OrphanCheckErr;
pub use self::fulfill::{FulfillmentContext, GlobalFulfilledPredicates, RegionObligation};
-pub use self::project::MismatchedProjectionTypes;
-pub use self::project::normalize;
-pub use self::project::Normalized;
+pub use self::project::{MismatchedProjectionTypes, ProjectionMode};
+pub use self::project::{normalize, Normalized};
pub use self::object_safety::is_object_safe;
pub use self::object_safety::astconv_object_safety_violations;
pub use self::object_safety::object_safety_violations;
pub use self::object_safety::ObjectSafetyViolation;
pub use self::object_safety::MethodViolationCode;
pub use self::object_safety::is_vtable_safe_method;
-pub use self::select::EvaluationCache;
-pub use self::select::SelectionContext;
-pub use self::select::SelectionCache;
+pub use self::select::{EvaluationCache, SelectionContext, SelectionCache};
pub use self::select::{MethodMatchResult, MethodMatched, MethodAmbiguous, MethodDidNotMatch};
pub use self::select::{MethodMatchedData}; // intentionally don't export variants
+pub use self::specialize::{Overlap, specialization_graph, specializes, translate_substs};
pub use self::util::elaborate_predicates;
pub use self::util::get_vtable_index_of_object_method;
pub use self::util::trait_ref_for_builtin_bound;
mod project;
mod object_safety;
mod select;
+mod specialize;
mod structural_impls;
mod util;
let elaborated_env = unnormalized_env.with_caller_bounds(predicates);
- let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, Some(elaborated_env));
+ let infcx = infer::new_infer_ctxt(tcx,
+ &tcx.tables,
+ Some(elaborated_env),
+ ProjectionMode::AnyFinal);
let predicates = match fully_normalize(&infcx,
cause,
&infcx.parameter_environment.caller_bounds) {
use super::elaborate_predicates;
use super::report_overflow_error;
+use super::specialization_graph;
+use super::translate_substs;
use super::Obligation;
use super::ObligationCause;
use super::PredicateObligation;
use super::VtableImplData;
use super::util;
+use middle::def_id::DefId;
use middle::infer::{self, TypeOrigin};
use middle::subst::Subst;
use middle::ty::{self, ToPredicate, ToPolyTraitRef, Ty, TyCtxt};
use middle::ty::fold::{TypeFoldable, TypeFolder};
use syntax::parse::token;
+use syntax::ast;
use util::common::FN_OUTPUT_NAME;
+use std::rc::Rc;
+
+/// Depending on the stage of compilation, we want projection to be
+/// more or less conservative.
+#[derive(Debug, Copy, Clone, PartialEq, Eq)]
+pub enum ProjectionMode {
+ /// FIXME (#32205)
+ /// At coherence-checking time, we're still constructing the
+ /// specialization graph, and thus we only project project
+ /// non-`default` associated types that are defined directly in
+ /// the applicable impl. (This behavior should be improved over
+ /// time, to allow for successful projections modulo cycles
+ /// between different impls).
+ ///
+ /// Here's an example that will fail due to the restriction:
+ ///
+ /// ```
+ /// trait Assoc {
+ /// type Output;
+ /// }
+ ///
+ /// impl<T> Assoc for T {
+ /// type Output = bool;
+ /// }
+ ///
+ /// impl Assoc for u8 {} // <- inherits the non-default type from above
+ ///
+ /// trait Foo {}
+ /// impl Foo for u32 {}
+ /// impl Foo for <u8 as Assoc>::Output {} // <- this projection will fail
+ /// ```
+ ///
+ /// The projection would succeed if `Output` had been defined
+ /// directly in the impl for `u8`.
+ Topmost,
+
+ /// At type-checking time, we refuse to project any associated
+ /// type that is marked `default`. Non-`default` ("final") types
+ /// are always projected. This is necessary in general for
+ /// soundness of specialization. However, we *could* allow
+ /// projections in fully-monomorphic cases. We choose not to,
+ /// because we prefer for `default type` to force the type
+ /// definition to be treated abstractly by any consumers of the
+ /// impl. Concretely, that means that the following example will
+ /// fail to compile:
+ ///
+ /// ```
+ /// trait Assoc {
+ /// type Output;
+ /// }
+ ///
+ /// impl<T> Assoc for T {
+ /// default type Output = bool;
+ /// }
+ ///
+ /// fn main() {
+ /// let <() as Assoc>::Output = true;
+ /// }
+ AnyFinal,
+
+ /// At trans time, all projections will succeed.
+ Any,
+}
+
+impl ProjectionMode {
+ pub fn is_topmost(&self) -> bool {
+ match *self {
+ ProjectionMode::Topmost => true,
+ _ => false,
+ }
+ }
+
+ pub fn is_any_final(&self) -> bool {
+ match *self {
+ ProjectionMode::AnyFinal => true,
+ _ => false,
+ }
+ }
+
+ pub fn is_any(&self) -> bool {
+ match *self {
+ ProjectionMode::Any => true,
+ _ => false,
+ }
+ }
+}
+
+
pub type PolyProjectionObligation<'tcx> =
Obligation<'tcx, ty::PolyProjectionPredicate<'tcx>>;
assert!(candidates.vec.len() <= 1);
- match candidates.vec.pop() {
+ let possible_candidate = candidates.vec.pop().and_then(|candidate| {
+ // In Any (i.e. trans) mode, all projections succeed;
+ // otherwise, we need to be sensitive to `default` and
+ // specialization.
+ if !selcx.projection_mode().is_any() {
+ if let ProjectionTyCandidate::Impl(ref impl_data) = candidate {
+ if let Some(node_item) = assoc_ty_def(selcx,
+ impl_data.impl_def_id,
+ obligation.predicate.item_name) {
+ if node_item.node.is_from_trait() {
+ if node_item.item.ty.is_some() {
+ // If the associated type has a default from the
+ // trait, that should be considered `default` and
+ // hence not projected.
+ //
+ // Note, however, that we allow a projection from
+ // the trait specifically in the case that the trait
+ // does *not* give a default. This is purely to
+ // avoid spurious errors: the situation can only
+ // arise when *no* impl in the specialization chain
+ // has provided a definition for the type. When we
+ // confirm the candidate, we'll turn the projection
+ // into a TyError, since the actual error will be
+ // reported in `check_impl_items_against_trait`.
+ return None;
+ }
+ } else if node_item.item.defaultness.is_default() {
+ return None;
+ }
+ } else {
+ // Normally this situation could only arise througha
+ // compiler bug, but at coherence-checking time we only look
+ // at the topmost impl (we don't even consider the trait
+ // itself) for the definition -- so we can fail to find a
+ // definition of the type even if it exists.
+
+ // For now, we just unconditionally ICE, because otherwise,
+ // examples like the following will succeed:
+ //
+ // ```
+ // trait Assoc {
+ // type Output;
+ // }
+ //
+ // impl<T> Assoc for T {
+ // default type Output = bool;
+ // }
+ //
+ // impl Assoc for u8 {}
+ // impl Assoc for u16 {}
+ //
+ // trait Foo {}
+ // impl Foo for <u8 as Assoc>::Output {}
+ // impl Foo for <u16 as Assoc>::Output {}
+ // return None;
+ // }
+ // ```
+ //
+ // The essential problem here is that the projection fails,
+ // leaving two unnormalized types, which appear not to unify
+ // -- so the overlap check succeeds, when it should fail.
+ selcx.tcx().sess.bug("Tried to project an inherited associated type during \
+ coherence checking, which is currently not supported.");
+ }
+ }
+ }
+ Some(candidate)
+ });
+
+ match possible_candidate {
Some(candidate) => {
let (ty, obligations) = confirm_candidate(selcx, obligation, candidate);
Ok(ProjectedTy::Progress(ty, obligations))
impl_vtable: VtableImplData<'tcx, PredicateObligation<'tcx>>)
-> (Ty<'tcx>, Vec<PredicateObligation<'tcx>>)
{
- // there don't seem to be nicer accessors to these:
- let impl_or_trait_items_map = selcx.tcx().impl_or_trait_items.borrow();
-
- // Look for the associated type in the impl
- for impl_item in &selcx.tcx().impl_items.borrow()[&impl_vtable.impl_def_id] {
- if let ty::TypeTraitItem(ref assoc_ty) = impl_or_trait_items_map[&impl_item.def_id()] {
- if assoc_ty.name == obligation.predicate.item_name {
- return (assoc_ty.ty.unwrap().subst(selcx.tcx(), impl_vtable.substs),
- impl_vtable.nested);
- }
+ let VtableImplData { substs, nested, impl_def_id } = impl_vtable;
+
+ let tcx = selcx.tcx();
+ let trait_ref = obligation.predicate.trait_ref;
+ let assoc_ty = assoc_ty_def(selcx, impl_def_id, obligation.predicate.item_name);
+
+ match assoc_ty {
+ Some(node_item) => {
+ let ty = node_item.item.ty.unwrap_or_else(|| {
+ // This means that the impl is missing a definition for the
+ // associated type. This error will be reported by the type
+ // checker method `check_impl_items_against_trait`, so here we
+ // just return TyError.
+ debug!("confirm_impl_candidate: no associated type {:?} for {:?}",
+ node_item.item.name,
+ obligation.predicate.trait_ref);
+ tcx.types.err
+ });
+ let substs = translate_substs(selcx.infcx(), impl_def_id, substs, node_item.node);
+ (ty.subst(tcx, substs), nested)
+ }
+ None => {
+ tcx.sess.span_bug(obligation.cause.span,
+ &format!("No associated type for {:?}", trait_ref));
}
}
+}
- // It is not in the impl - get the default from the trait.
- let trait_ref = obligation.predicate.trait_ref;
- for trait_item in selcx.tcx().trait_items(trait_ref.def_id).iter() {
- if let &ty::TypeTraitItem(ref assoc_ty) = trait_item {
- if assoc_ty.name == obligation.predicate.item_name {
- if let Some(ty) = assoc_ty.ty {
- return (ty.subst(selcx.tcx(), trait_ref.substs),
- impl_vtable.nested);
- } else {
- // This means that the impl is missing a
- // definition for the associated type. This error
- // ought to be reported by the type checker method
- // `check_impl_items_against_trait`, so here we
- // just return TyError.
- debug!("confirm_impl_candidate: no associated type {:?} for {:?}",
- assoc_ty.name,
- trait_ref);
- return (selcx.tcx().types.err, vec!());
+/// Locate the definition of an associated type in the specialization hierarchy,
+/// starting from the given impl.
+///
+/// Based on the "projection mode", this lookup may in fact only examine the
+/// topmost impl. See the comments for `ProjectionMode` for more details.
+fn assoc_ty_def<'cx, 'tcx>(selcx: &SelectionContext<'cx, 'tcx>,
+ impl_def_id: DefId,
+ assoc_ty_name: ast::Name)
+ -> Option<specialization_graph::NodeItem<Rc<ty::AssociatedType<'tcx>>>>
+{
+ let trait_def_id = selcx.tcx().impl_trait_ref(impl_def_id).unwrap().def_id;
+
+ if selcx.projection_mode().is_topmost() {
+ let impl_node = specialization_graph::Node::Impl(impl_def_id);
+ for item in impl_node.items(selcx.tcx()) {
+ if let ty::TypeTraitItem(assoc_ty) = item {
+ if assoc_ty.name == assoc_ty_name {
+ return Some(specialization_graph::NodeItem {
+ node: specialization_graph::Node::Impl(impl_def_id),
+ item: assoc_ty,
+ });
}
}
}
+ None
+ } else {
+ selcx.tcx().lookup_trait_def(trait_def_id)
+ .ancestors(impl_def_id)
+ .type_defs(selcx.tcx(), assoc_ty_name)
+ .next()
}
-
- selcx.tcx().sess.span_bug(obligation.cause.span,
- &format!("No associated type for {:?}",
- trait_ref));
}
use super::{ObligationCauseCode, BuiltinDerivedObligation, ImplDerivedObligation};
use super::{SelectionError, Unimplemented, OutputTypeParameterMismatch};
use super::{ObjectCastObligation, Obligation};
+use super::ProjectionMode;
use super::TraitNotObjectSafe;
use super::Selection;
use super::SelectionResult;
use middle::infer::{InferCtxt, TypeFreshener, TypeOrigin};
use middle::subst::{Subst, Substs, TypeSpace};
use middle::ty::{self, ToPredicate, ToPolyTraitRef, Ty, TyCtxt, TypeFoldable};
+use middle::traits;
use middle::ty::fast_reject;
use middle::ty::relate::TypeRelation;
/// other words, we consider `$0 : Bar` to be unimplemented if
/// there is no type that the user could *actually name* that
/// would satisfy it. This avoids crippling inference, basically.
-
intercrate: bool,
}
ambiguous: bool,
}
+#[derive(PartialEq,Eq,Debug,Clone)]
+struct EvaluatedCandidate<'tcx> {
+ candidate: SelectionCandidate<'tcx>,
+ evaluation: EvaluationResult,
+}
+
enum BuiltinBoundConditions<'tcx> {
If(ty::Binder<Vec<Ty<'tcx>>>),
ParameterBuiltin,
}
impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
- pub fn new(infcx: &'cx InferCtxt<'cx, 'tcx>)
- -> SelectionContext<'cx, 'tcx> {
+ pub fn new(infcx: &'cx InferCtxt<'cx, 'tcx>) -> SelectionContext<'cx, 'tcx> {
SelectionContext {
infcx: infcx,
freshener: infcx.freshener(),
}
}
- pub fn intercrate(infcx: &'cx InferCtxt<'cx, 'tcx>)
- -> SelectionContext<'cx, 'tcx> {
+ pub fn intercrate(infcx: &'cx InferCtxt<'cx, 'tcx>) -> SelectionContext<'cx, 'tcx> {
SelectionContext {
infcx: infcx,
freshener: infcx.freshener(),
self.infcx
}
+ pub fn projection_mode(&self) -> ProjectionMode {
+ self.infcx.projection_mode()
+ }
+
///////////////////////////////////////////////////////////////////////////
// Selection
//
// this crate, perhaps the type would be unified with
// something from another crate that does provide an impl.
//
- // In intracrate mode, we must still be conservative. The reason is
+ // In intra mode, we must still be conservative. The reason is
// that we want to avoid cycles. Imagine an impl like:
//
// impl<T:Eq> Eq for Vec<T>
candidate
}
+ // Treat negative impls as unimplemented
+ fn filter_negative_impls(&self, candidate: SelectionCandidate<'tcx>)
+ -> SelectionResult<'tcx, SelectionCandidate<'tcx>> {
+ if let ImplCandidate(def_id) = candidate {
+ if self.tcx().trait_impl_polarity(def_id) == Some(hir::ImplPolarity::Negative) {
+ return Err(Unimplemented)
+ }
+ }
+ Ok(Some(candidate))
+ }
+
fn candidate_from_obligation_no_cache<'o>(&mut self,
stack: &TraitObligationStack<'o, 'tcx>)
-> SelectionResult<'tcx, SelectionCandidate<'tcx>>
}
if !self.is_knowable(stack) {
- debug!("intercrate not knowable");
+ debug!("coherence stage: not knowable");
return Ok(None);
}
// we were to winnow, we'd wind up with zero candidates.
// Instead, we select the right impl now but report `Bar does
// not implement Clone`.
- if candidates.len() > 1 {
- candidates.retain(|c| self.evaluate_candidate(stack, c).may_apply())
- }
+ if candidates.len() == 1 {
+ return self.filter_negative_impls(candidates.pop().unwrap());
+ }
+
+ // Winnow, but record the exact outcome of evaluation, which
+ // is needed for specialization.
+ let mut candidates: Vec<_> = candidates.into_iter().filter_map(|c| {
+ let eval = self.evaluate_candidate(stack, &c);
+ if eval.may_apply() {
+ Some(EvaluatedCandidate {
+ candidate: c,
+ evaluation: eval,
+ })
+ } else {
+ None
+ }
+ }).collect();
- // If there are STILL multiple candidate, we can further reduce
- // the list by dropping duplicates.
+ // If there are STILL multiple candidate, we can further
+ // reduce the list by dropping duplicates -- including
+ // resolving specializations.
if candidates.len() > 1 {
let mut i = 0;
while i < candidates.len() {
return Ok(None);
}
-
- // If there are *NO* candidates, that there are no impls --
+ // If there are *NO* candidates, then there are no impls --
// that we know of, anyway. Note that in the case where there
// are unbound type variables within the obligation, it might
// be the case that you could still satisfy the obligation
}
// Just one candidate left.
- let candidate = candidates.pop().unwrap();
-
- match candidate {
- ImplCandidate(def_id) => {
- match self.tcx().trait_impl_polarity(def_id) {
- Some(hir::ImplPolarity::Negative) => return Err(Unimplemented),
- _ => {}
- }
- }
- _ => {}
- }
-
- Ok(Some(candidate))
+ self.filter_negative_impls(candidates.pop().unwrap().candidate)
}
fn is_knowable<'o>(&mut self,
/// candidates and prefer where-clause candidates.
///
/// See the comment for "SelectionCandidate" for more details.
- fn candidate_should_be_dropped_in_favor_of<'o>(&mut self,
- victim: &SelectionCandidate<'tcx>,
- other: &SelectionCandidate<'tcx>)
- -> bool
+ fn candidate_should_be_dropped_in_favor_of<'o>(
+ &mut self,
+ victim: &EvaluatedCandidate<'tcx>,
+ other: &EvaluatedCandidate<'tcx>)
+ -> bool
{
- if victim == other {
+ if victim.candidate == other.candidate {
return true;
}
- match other {
- &ObjectCandidate |
- &ParamCandidate(_) | &ProjectionCandidate => match victim {
- &DefaultImplCandidate(..) => {
+ match other.candidate {
+ ObjectCandidate |
+ ParamCandidate(_) | ProjectionCandidate => match victim.candidate {
+ DefaultImplCandidate(..) => {
self.tcx().sess.bug(
"default implementations shouldn't be recorded \
when there are other valid candidates");
}
- &ImplCandidate(..) |
- &ClosureCandidate(..) |
- &FnPointerCandidate |
- &BuiltinObjectCandidate |
- &BuiltinUnsizeCandidate |
- &DefaultImplObjectCandidate(..) |
- &BuiltinCandidate(..) => {
+ ImplCandidate(..) |
+ ClosureCandidate(..) |
+ FnPointerCandidate |
+ BuiltinObjectCandidate |
+ BuiltinUnsizeCandidate |
+ DefaultImplObjectCandidate(..) |
+ BuiltinCandidate(..) => {
// We have a where-clause so don't go around looking
// for impls.
true
}
- &ObjectCandidate |
- &ProjectionCandidate => {
+ ObjectCandidate |
+ ProjectionCandidate => {
// Arbitrarily give param candidates priority
// over projection and object candidates.
true
},
- &ParamCandidate(..) => false,
+ ParamCandidate(..) => false,
+ },
+ ImplCandidate(other_def) => {
+ // See if we can toss out `victim` based on specialization.
+ // This requires us to know *for sure* that the `other` impl applies
+ // i.e. EvaluatedToOk:
+ if other.evaluation == EvaluatedToOk {
+ if let ImplCandidate(victim_def) = victim.candidate {
+ return traits::specializes(self.tcx(), other_def, victim_def);
+ }
+ }
+
+ false
},
_ => false
}
--- /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.
+
+// Logic and data structures related to impl specialization, explained in
+// greater detail below.
+//
+// At the moment, this implementation support only the simple "chain" rule:
+// If any two impls overlap, one must be a strict subset of the other.
+//
+// See traits/README.md for a bit more detail on how specialization
+// fits together with the rest of the trait machinery.
+
+use super::{SelectionContext, FulfillmentContext};
+use super::util::{fresh_type_vars_for_impl, impl_trait_ref_and_oblig};
+
+use middle::cstore::CrateStore;
+use middle::def_id::DefId;
+use middle::infer::{self, InferCtxt, TypeOrigin};
+use middle::region;
+use middle::subst::{Subst, Substs};
+use middle::traits::{self, ProjectionMode, ObligationCause, Normalized};
+use middle::ty::{self, TyCtxt};
+use syntax::codemap::DUMMY_SP;
+
+pub mod specialization_graph;
+
+/// Information pertinent to an overlapping impl error.
+pub struct Overlap<'a, 'tcx: 'a> {
+ pub in_context: InferCtxt<'a, 'tcx>,
+ pub with_impl: DefId,
+ pub on_trait_ref: ty::TraitRef<'tcx>,
+}
+
+/// Given a subst for the requested impl, translate it to a subst
+/// appropriate for the actual item definition (whether it be in that impl,
+/// a parent impl, or the trait).
+/// When we have selected one impl, but are actually using item definitions from
+/// a parent impl providing a default, we need a way to translate between the
+/// type parameters of the two impls. Here the `source_impl` is the one we've
+/// selected, and `source_substs` is a substitution of its generics (and
+/// possibly some relevant `FnSpace` variables as well). And `target_node` is
+/// the impl/trait we're actually going to get the definition from. The resulting
+/// substitution will map from `target_node`'s generics to `source_impl`'s
+/// generics as instantiated by `source_subst`.
+///
+/// For example, consider the following scenario:
+///
+/// ```rust
+/// trait Foo { ... }
+/// impl<T, U> Foo for (T, U) { ... } // target impl
+/// impl<V> Foo for (V, V) { ... } // source impl
+/// ```
+///
+/// Suppose we have selected "source impl" with `V` instantiated with `u32`.
+/// This function will produce a substitution with `T` and `U` both mapping to `u32`.
+///
+/// Where clauses add some trickiness here, because they can be used to "define"
+/// an argument indirectly:
+///
+/// ```rust
+/// impl<'a, I, T: 'a> Iterator for Cloned<I>
+/// where I: Iterator<Item=&'a T>, T: Clone
+/// ```
+///
+/// In a case like this, the substitution for `T` is determined indirectly,
+/// through associated type projection. We deal with such cases by using
+/// *fulfillment* to relate the two impls, requiring that all projections are
+/// resolved.
+pub fn translate_substs<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
+ source_impl: DefId,
+ source_substs: &'tcx Substs<'tcx>,
+ target_node: specialization_graph::Node)
+ -> &'tcx Substs<'tcx> {
+ let source_trait_ref = infcx.tcx
+ .impl_trait_ref(source_impl)
+ .unwrap()
+ .subst(infcx.tcx, &source_substs);
+
+ // translate the Self and TyParam parts of the substitution, since those
+ // vary across impls
+ let target_substs = match target_node {
+ specialization_graph::Node::Impl(target_impl) => {
+ // no need to translate if we're targetting the impl we started with
+ if source_impl == target_impl {
+ return source_substs;
+ }
+
+ fulfill_implication(infcx, source_trait_ref, target_impl).unwrap_or_else(|_| {
+ infcx.tcx
+ .sess
+ .bug("When translating substitutions for specialization, the expected \
+ specializaiton failed to hold")
+ })
+ }
+ specialization_graph::Node::Trait(..) => source_trait_ref.substs.clone(),
+ };
+
+ // retain erasure mode
+ // NB: this must happen before inheriting method generics below
+ let target_substs = if source_substs.regions.is_erased() {
+ target_substs.erase_regions()
+ } else {
+ target_substs
+ };
+
+ // directly inherent the method generics, since those do not vary across impls
+ infcx.tcx.mk_substs(target_substs.with_method_from_subst(source_substs))
+}
+
+/// Is impl1 a specialization of impl2?
+///
+/// Specialization is determined by the sets of types to which the impls apply;
+/// impl1 specializes impl2 if it applies to a subset of the types impl2 applies
+/// to.
+pub fn specializes(tcx: &TyCtxt, impl1_def_id: DefId, impl2_def_id: DefId) -> bool {
+ // The feature gate should prevent introducing new specializations, but not
+ // taking advantage of upstream ones.
+ if !tcx.sess.features.borrow().specialization &&
+ (impl1_def_id.is_local() || impl2_def_id.is_local()) {
+ return false;
+ }
+
+ // We determine whether there's a subset relationship by:
+ //
+ // - skolemizing impl1,
+ // - assuming the where clauses for impl1,
+ // - instantiating impl2 with fresh inference variables,
+ // - unifying,
+ // - attempting to prove the where clauses for impl2
+ //
+ // The last three steps are encapsulated in `fulfill_implication`.
+ //
+ // See RFC 1210 for more details and justification.
+
+ // Currently we do not allow e.g. a negative impl to specialize a positive one
+ if tcx.trait_impl_polarity(impl1_def_id) != tcx.trait_impl_polarity(impl2_def_id) {
+ return false;
+ }
+
+ let mut infcx = infer::normalizing_infer_ctxt(tcx, &tcx.tables, ProjectionMode::Topmost);
+
+ // create a parameter environment corresponding to a (skolemized) instantiation of impl1
+ let scheme = tcx.lookup_item_type(impl1_def_id);
+ let predicates = tcx.lookup_predicates(impl1_def_id);
+ let mut penv = tcx.construct_parameter_environment(DUMMY_SP,
+ &scheme.generics,
+ &predicates,
+ region::DUMMY_CODE_EXTENT);
+ let impl1_trait_ref = tcx.impl_trait_ref(impl1_def_id)
+ .unwrap()
+ .subst(tcx, &penv.free_substs);
+
+ // Normalize the trait reference, adding any obligations that arise into the impl1 assumptions
+ let Normalized { value: impl1_trait_ref, obligations: normalization_obligations } = {
+ let selcx = &mut SelectionContext::new(&infcx);
+ traits::normalize(selcx, ObligationCause::dummy(), &impl1_trait_ref)
+ };
+ penv.caller_bounds.extend(normalization_obligations.into_iter().map(|o| o.predicate));
+
+ // Install the parameter environment, taking the predicates of impl1 as assumptions:
+ infcx.parameter_environment = penv;
+
+ // Attempt to prove that impl2 applies, given all of the above.
+ fulfill_implication(&infcx, impl1_trait_ref, impl2_def_id).is_ok()
+}
+
+/// Attempt to fulfill all obligations of `target_impl` after unification with
+/// `source_trait_ref`. If successful, returns a substitution for *all* the
+/// generics of `target_impl`, including both those needed to unify with
+/// `source_trait_ref` and those whose identity is determined via a where
+/// clause in the impl.
+fn fulfill_implication<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
+ source_trait_ref: ty::TraitRef<'tcx>,
+ target_impl: DefId)
+ -> Result<Substs<'tcx>, ()> {
+ infcx.commit_if_ok(|_| {
+ let selcx = &mut SelectionContext::new(&infcx);
+ let target_substs = fresh_type_vars_for_impl(&infcx, DUMMY_SP, target_impl);
+ let (target_trait_ref, obligations) = impl_trait_ref_and_oblig(selcx,
+ target_impl,
+ &target_substs);
+
+ // do the impls unify? If not, no specialization.
+ if let Err(_) = infer::mk_eq_trait_refs(&infcx,
+ true,
+ TypeOrigin::Misc(DUMMY_SP),
+ source_trait_ref,
+ target_trait_ref) {
+ debug!("fulfill_implication: {:?} does not unify with {:?}",
+ source_trait_ref,
+ target_trait_ref);
+ return Err(());
+ }
+
+ // attempt to prove all of the predicates for impl2 given those for impl1
+ // (which are packed up in penv)
+
+ let mut fulfill_cx = FulfillmentContext::new();
+ for oblig in obligations.into_iter() {
+ fulfill_cx.register_predicate_obligation(&infcx, oblig);
+ }
+
+ if let Err(errors) = infer::drain_fulfillment_cx(&infcx, &mut fulfill_cx, &()) {
+ // no dice!
+ debug!("fulfill_implication: for impls on {:?} and {:?}, could not fulfill: {:?} given \
+ {:?}",
+ source_trait_ref,
+ target_trait_ref,
+ errors,
+ infcx.parameter_environment.caller_bounds);
+ Err(())
+ } else {
+ debug!("fulfill_implication: an impl for {:?} specializes {:?}",
+ source_trait_ref,
+ target_trait_ref);
+
+ // Now resolve the *substitution* we built for the target earlier, replacing
+ // the inference variables inside with whatever we got from fulfillment.
+ Ok(infcx.resolve_type_vars_if_possible(&target_substs))
+ }
+ })
+}
--- /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.
+
+use std::cell;
+use std::rc::Rc;
+
+use super::{Overlap, specializes};
+
+use middle::cstore::CrateStore;
+use middle::def_id::DefId;
+use middle::infer;
+use middle::traits::{self, ProjectionMode};
+use middle::ty::{self, TyCtxt, ImplOrTraitItem, TraitDef, TypeFoldable};
+use syntax::ast::Name;
+use util::nodemap::DefIdMap;
+
+/// A per-trait graph of impls in specialization order. At the moment, this
+/// graph forms a tree rooted with the trait itself, with all other nodes
+/// representing impls, and parent-child relationships representing
+/// specializations.
+///
+/// The graph provides two key services:
+///
+/// - Construction, which implicitly checks for overlapping impls (i.e., impls
+/// that overlap but where neither specializes the other -- an artifact of the
+/// simple "chain" rule.
+///
+/// - Parent extraction. In particular, the graph can give you the *immediate*
+/// parents of a given specializing impl, which is needed for extracting
+/// default items amongst other thigns. In the simple "chain" rule, every impl
+/// has at most one parent.
+pub struct Graph {
+ // all impls have a parent; the "root" impls have as their parent the def_id
+ // of the trait
+ parent: DefIdMap<DefId>,
+
+ // the "root" impls are found by looking up the trait's def_id.
+ children: DefIdMap<Vec<DefId>>,
+}
+
+impl Graph {
+ pub fn new() -> Graph {
+ Graph {
+ parent: Default::default(),
+ children: Default::default(),
+ }
+ }
+
+ /// Insert a local impl into the specialization graph. If an existing impl
+ /// conflicts with it (has overlap, but neither specializes the other),
+ /// information about the area of overlap is returned in the `Err`.
+ pub fn insert<'a, 'tcx>(&mut self,
+ tcx: &'a TyCtxt<'tcx>,
+ impl_def_id: DefId)
+ -> Result<(), Overlap<'a, 'tcx>> {
+ assert!(impl_def_id.is_local());
+
+ let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap();
+ let trait_def_id = trait_ref.def_id;
+
+ debug!("insert({:?}): inserting TraitRef {:?} into specialization graph",
+ impl_def_id, trait_ref);
+
+ // if the reference itself contains an earlier error (e.g., due to a
+ // resolution failure), then we just insert the impl at the top level of
+ // the graph and claim that there's no overlap (in order to supress
+ // bogus errors).
+ if trait_ref.references_error() {
+ debug!("insert: inserting dummy node for erroneous TraitRef {:?}, \
+ impl_def_id={:?}, trait_def_id={:?}",
+ trait_ref, impl_def_id, trait_def_id);
+
+ self.parent.insert(impl_def_id, trait_def_id);
+ self.children.entry(trait_def_id).or_insert(vec![]).push(impl_def_id);
+ return Ok(());
+ }
+
+ let mut parent = trait_def_id;
+
+ // Ugly hack around borrowck limitations. Assigned only in the case
+ // where we bump downward an existing node in the graph.
+ let child_to_insert;
+
+ 'descend: loop {
+ let mut possible_siblings = self.children.entry(parent).or_insert(vec![]);
+
+ for slot in possible_siblings.iter_mut() {
+ let possible_sibling = *slot;
+
+ let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, None, ProjectionMode::Topmost);
+ let overlap = traits::overlapping_impls(&infcx, possible_sibling, impl_def_id);
+
+ if let Some(impl_header) = overlap {
+ let le = specializes(tcx, impl_def_id, possible_sibling);
+ let ge = specializes(tcx, possible_sibling, impl_def_id);
+
+ if le && !ge {
+ debug!("descending as child of TraitRef {:?}",
+ tcx.impl_trait_ref(possible_sibling).unwrap());
+
+ // the impl specializes possible_sibling
+ parent = possible_sibling;
+ continue 'descend;
+ } else if ge && !le {
+ debug!("placing as parent of TraitRef {:?}",
+ tcx.impl_trait_ref(possible_sibling).unwrap());
+
+ // possible_sibling specializes the impl
+ *slot = impl_def_id;
+ self.parent.insert(impl_def_id, parent);
+ self.parent.insert(possible_sibling, impl_def_id);
+ // we have to defer the insertion, because we can't
+ // relinquish the borrow of `self.children`
+ child_to_insert = possible_sibling;
+ break 'descend;
+ } else {
+ // overlap, but no specialization; error out
+ return Err(Overlap {
+ with_impl: possible_sibling,
+ on_trait_ref: impl_header.trait_ref.unwrap(),
+ in_context: infcx,
+ });
+ }
+ }
+ }
+
+ // no overlap with any potential siblings, so add as a new sibling
+ debug!("placing as new sibling");
+ self.parent.insert(impl_def_id, parent);
+ possible_siblings.push(impl_def_id);
+ return Ok(());
+ }
+
+ self.children.insert(impl_def_id, vec![child_to_insert]);
+ Ok(())
+ }
+
+ /// Insert cached metadata mapping from a child impl back to its parent.
+ pub fn record_impl_from_cstore(&mut self, parent: DefId, child: DefId) {
+ if self.parent.insert(child, parent).is_some() {
+ panic!("When recording an impl from the crate store, information about its parent \
+ was already present.");
+ }
+
+ self.children.entry(parent).or_insert(vec![]).push(child);
+ }
+
+ /// The parent of a given impl, which is the def id of the trait when the
+ /// impl is a "specialization root".
+ pub fn parent(&self, child: DefId) -> DefId {
+ *self.parent.get(&child).unwrap()
+ }
+}
+
+/// A node in the specialization graph is either an impl or a trait
+/// definition; either can serve as a source of item definitions.
+/// There is always exactly one trait definition node: the root.
+#[derive(Debug, Copy, Clone)]
+pub enum Node {
+ Impl(DefId),
+ Trait(DefId),
+}
+
+impl Node {
+ pub fn is_from_trait(&self) -> bool {
+ match *self {
+ Node::Trait(..) => true,
+ _ => false,
+ }
+ }
+
+ /// Iterate over the items defined directly by the given (impl or trait) node.
+ pub fn items<'a, 'tcx>(&self, tcx: &'a TyCtxt<'tcx>) -> NodeItems<'a, 'tcx> {
+ match *self {
+ Node::Impl(impl_def_id) => {
+ NodeItems::Impl {
+ tcx: tcx,
+ items: cell::Ref::map(tcx.impl_items.borrow(),
+ |impl_items| &impl_items[&impl_def_id]),
+ idx: 0,
+ }
+ }
+ Node::Trait(trait_def_id) => {
+ NodeItems::Trait {
+ items: tcx.trait_items(trait_def_id).clone(),
+ idx: 0,
+ }
+ }
+ }
+ }
+
+ pub fn def_id(&self) -> DefId {
+ match *self {
+ Node::Impl(did) => did,
+ Node::Trait(did) => did,
+ }
+ }
+}
+
+/// An iterator over the items defined within a trait or impl.
+pub enum NodeItems<'a, 'tcx: 'a> {
+ Impl {
+ tcx: &'a TyCtxt<'tcx>,
+ items: cell::Ref<'a, Vec<ty::ImplOrTraitItemId>>,
+ idx: usize,
+ },
+ Trait {
+ items: Rc<Vec<ImplOrTraitItem<'tcx>>>,
+ idx: usize,
+ },
+}
+
+impl<'a, 'tcx> Iterator for NodeItems<'a, 'tcx> {
+ type Item = ImplOrTraitItem<'tcx>;
+ fn next(&mut self) -> Option<ImplOrTraitItem<'tcx>> {
+ match *self {
+ NodeItems::Impl { tcx, ref items, ref mut idx } => {
+ let items_table = tcx.impl_or_trait_items.borrow();
+ if *idx < items.len() {
+ let item_def_id = items[*idx].def_id();
+ let item = items_table[&item_def_id].clone();
+ *idx += 1;
+ Some(item)
+ } else {
+ None
+ }
+ }
+ NodeItems::Trait { ref items, ref mut idx } => {
+ if *idx < items.len() {
+ let item = items[*idx].clone();
+ *idx += 1;
+ Some(item)
+ } else {
+ None
+ }
+ }
+ }
+ }
+}
+
+pub struct Ancestors<'a, 'tcx: 'a> {
+ trait_def: &'a TraitDef<'tcx>,
+ current_source: Option<Node>,
+}
+
+impl<'a, 'tcx> Iterator for Ancestors<'a, 'tcx> {
+ type Item = Node;
+ fn next(&mut self) -> Option<Node> {
+ let cur = self.current_source.take();
+ if let Some(Node::Impl(cur_impl)) = cur {
+ let parent = self.trait_def.specialization_graph.borrow().parent(cur_impl);
+ if parent == self.trait_def.def_id() {
+ self.current_source = Some(Node::Trait(parent));
+ } else {
+ self.current_source = Some(Node::Impl(parent));
+ }
+ }
+ cur
+ }
+}
+
+pub struct NodeItem<T> {
+ pub node: Node,
+ pub item: T,
+}
+
+impl<T> NodeItem<T> {
+ pub fn map<U, F: FnOnce(T) -> U>(self, f: F) -> NodeItem<U> {
+ NodeItem {
+ node: self.node,
+ item: f(self.item),
+ }
+ }
+}
+
+pub struct TypeDefs<'a, 'tcx: 'a> {
+ // generally only invoked once or twice, so the box doesn't hurt
+ iter: Box<Iterator<Item = NodeItem<Rc<ty::AssociatedType<'tcx>>>> + 'a>,
+}
+
+impl<'a, 'tcx> Iterator for TypeDefs<'a, 'tcx> {
+ type Item = NodeItem<Rc<ty::AssociatedType<'tcx>>>;
+ fn next(&mut self) -> Option<Self::Item> {
+ self.iter.next()
+ }
+}
+
+pub struct FnDefs<'a, 'tcx: 'a> {
+ // generally only invoked once or twice, so the box doesn't hurt
+ iter: Box<Iterator<Item = NodeItem<Rc<ty::Method<'tcx>>>> + 'a>,
+}
+
+impl<'a, 'tcx> Iterator for FnDefs<'a, 'tcx> {
+ type Item = NodeItem<Rc<ty::Method<'tcx>>>;
+ fn next(&mut self) -> Option<Self::Item> {
+ self.iter.next()
+ }
+}
+
+pub struct ConstDefs<'a, 'tcx: 'a> {
+ // generally only invoked once or twice, so the box doesn't hurt
+ iter: Box<Iterator<Item = NodeItem<Rc<ty::AssociatedConst<'tcx>>>> + 'a>,
+}
+
+impl<'a, 'tcx> Iterator for ConstDefs<'a, 'tcx> {
+ type Item = NodeItem<Rc<ty::AssociatedConst<'tcx>>>;
+ fn next(&mut self) -> Option<Self::Item> {
+ self.iter.next()
+ }
+}
+
+impl<'a, 'tcx> Ancestors<'a, 'tcx> {
+ /// Search the items from the given ancestors, returning each type definition
+ /// with the given name.
+ pub fn type_defs(self, tcx: &'a TyCtxt<'tcx>, name: Name) -> TypeDefs<'a, 'tcx> {
+ let iter = self.flat_map(move |node| {
+ node.items(tcx)
+ .filter_map(move |item| {
+ if let ty::TypeTraitItem(assoc_ty) = item {
+ if assoc_ty.name == name {
+ return Some(NodeItem {
+ node: node,
+ item: assoc_ty,
+ });
+ }
+ }
+ None
+ })
+
+ });
+ TypeDefs { iter: Box::new(iter) }
+ }
+
+ /// Search the items from the given ancestors, returning each fn definition
+ /// with the given name.
+ pub fn fn_defs(self, tcx: &'a TyCtxt<'tcx>, name: Name) -> FnDefs<'a, 'tcx> {
+ let iter = self.flat_map(move |node| {
+ node.items(tcx)
+ .filter_map(move |item| {
+ if let ty::MethodTraitItem(method) = item {
+ if method.name == name {
+ return Some(NodeItem {
+ node: node,
+ item: method,
+ });
+ }
+ }
+ None
+ })
+
+ });
+ FnDefs { iter: Box::new(iter) }
+ }
+
+ /// Search the items from the given ancestors, returning each const
+ /// definition with the given name.
+ pub fn const_defs(self, tcx: &'a TyCtxt<'tcx>, name: Name) -> ConstDefs<'a, 'tcx> {
+ let iter = self.flat_map(move |node| {
+ node.items(tcx)
+ .filter_map(move |item| {
+ if let ty::ConstTraitItem(konst) = item {
+ if konst.name == name {
+ return Some(NodeItem {
+ node: node,
+ item: konst,
+ });
+ }
+ }
+ None
+ })
+
+ });
+ ConstDefs { iter: Box::new(iter) }
+ }
+}
+
+/// Walk up the specialization ancestors of a given impl, starting with that
+/// impl itself.
+pub fn ancestors<'a, 'tcx>(trait_def: &'a TraitDef<'tcx>,
+ start_from_impl: DefId)
+ -> Ancestors<'a, 'tcx> {
+ Ancestors {
+ trait_def: trait_def,
+ current_source: Some(Node::Impl(start_from_impl)),
+ }
+}
use middle::def_id::DefId;
use middle::infer::InferCtxt;
-use middle::subst::Substs;
+use middle::subst::{Subst, Substs};
use middle::ty::{self, Ty, TyCtxt, ToPredicate, ToPolyTraitRef};
use syntax::codemap::Span;
use util::common::ErrorReported;
use util::nodemap::FnvHashSet;
-use super::{Obligation, ObligationCause, PredicateObligation};
+use super::{Obligation, ObligationCause, PredicateObligation, SelectionContext, Normalized};
struct PredicateSet<'a,'tcx:'a> {
tcx: &'a TyCtxt<'tcx>,
// Other
///////////////////////////////////////////////////////////////////////////
+/// Instantiate all bound parameters of the impl with the given substs,
+/// returning the resulting trait ref and all obligations that arise.
+/// The obligations are closed under normalization.
+pub fn impl_trait_ref_and_oblig<'a,'tcx>(selcx: &mut SelectionContext<'a,'tcx>,
+ impl_def_id: DefId,
+ impl_substs: &Substs<'tcx>)
+ -> (ty::TraitRef<'tcx>,
+ Vec<PredicateObligation<'tcx>>)
+{
+ let impl_trait_ref =
+ selcx.tcx().impl_trait_ref(impl_def_id).unwrap();
+ let impl_trait_ref =
+ impl_trait_ref.subst(selcx.tcx(), impl_substs);
+ let Normalized { value: impl_trait_ref, obligations: normalization_obligations1 } =
+ super::normalize(selcx, ObligationCause::dummy(), &impl_trait_ref);
+
+ let predicates = selcx.tcx().lookup_predicates(impl_def_id);
+ let predicates = predicates.instantiate(selcx.tcx(), impl_substs);
+ let Normalized { value: predicates, obligations: normalization_obligations2 } =
+ super::normalize(selcx, ObligationCause::dummy(), &predicates);
+ let impl_obligations =
+ predicates_for_generics(ObligationCause::dummy(), 0, &predicates);
+
+ let impl_obligations: Vec<_> =
+ impl_obligations.into_iter()
+ .chain(normalization_obligations1)
+ .chain(normalization_obligations2)
+ .collect();
+
+ (impl_trait_ref, impl_obligations)
+}
+
// determine the `self` type, using fresh variables for all variables
// declared on the impl declaration e.g., `impl<A,B> for Box<[(A,B)]>`
// would return ($0, $1) where $0 and $1 are freshly instantiated type
}
}
-
pub fn predicate_for_trait_ref<'tcx>(
cause: ObligationCause<'tcx>,
trait_ref: ty::TraitRef<'tcx>,
/// FIXME(arielb1): why is this separate from populated_external_types?
pub populated_external_primitive_impls: RefCell<DefIdSet>,
- /// These caches are used by const_eval when decoding external constants.
- pub extern_const_statics: RefCell<DefIdMap<NodeId>>,
+ /// Cache used by const_eval when decoding external constants.
+ /// Contains `None` when the constant has been fetched but doesn't exist.
+ /// Constains `Some(expr_id, type)` otherwise.
+ /// `type` is `None` in case it's not a primitive type
+ pub extern_const_statics: RefCell<DefIdMap<Option<(NodeId, Option<Ty<'tcx>>)>>>,
+ /// Cache used by const_eval when decoding extern const fns
pub extern_const_fns: RefCell<DefIdMap<NodeId>>,
pub node_lint_levels: RefCell<FnvHashMap<(NodeId, lint::LintId),
use syntax::codemap::{DUMMY_SP, Span};
use syntax::parse::token::InternedString;
+use rustc_const_eval::ConstInt;
+
use rustc_front::hir;
use rustc_front::hir::{ItemImpl, ItemTrait, PatKind};
use rustc_front::intravisit::Visitor;
mod structural_impls;
mod sty;
-pub type Disr = u64;
-pub const INITIAL_DISCRIMINANT_VALUE: Disr = 0;
+pub type Disr = ConstInt;
// Data types
pub fty: BareFnTy<'tcx>,
pub explicit_self: ExplicitSelfCategory,
pub vis: hir::Visibility,
+ pub defaultness: hir::Defaultness,
pub def_id: DefId,
pub container: ImplOrTraitItemContainer,
}
fty: BareFnTy<'tcx>,
explicit_self: ExplicitSelfCategory,
vis: hir::Visibility,
+ defaultness: hir::Defaultness,
def_id: DefId,
container: ImplOrTraitItemContainer)
-> Method<'tcx> {
- Method {
+ Method {
name: name,
generics: generics,
predicates: predicates,
fty: fty,
explicit_self: explicit_self,
vis: vis,
+ defaultness: defaultness,
def_id: def_id,
container: container,
}
pub name: Name,
pub ty: Ty<'tcx>,
pub vis: hir::Visibility,
+ pub defaultness: hir::Defaultness,
pub def_id: DefId,
pub container: ImplOrTraitItemContainer,
pub has_value: bool
pub name: Name,
pub ty: Option<Ty<'tcx>>,
pub vis: hir::Visibility,
+ pub defaultness: hir::Defaultness,
pub def_id: DefId,
pub container: ImplOrTraitItemContainer,
}
/// Asserts this is a struct and returns the struct's unique
/// variant.
pub fn struct_variant(&self) -> &VariantDefData<'tcx, 'container> {
- assert!(self.adt_kind() == AdtKind::Struct);
+ assert_eq!(self.adt_kind(), AdtKind::Struct);
&self.variants[0]
}
for impl_def_id in self.sess.cstore.implementations_of_trait(trait_id) {
let impl_items = self.sess.cstore.impl_items(impl_def_id);
let trait_ref = self.impl_trait_ref(impl_def_id).unwrap();
+
// Record the trait->implementation mapping.
- def.record_impl(self, impl_def_id, trait_ref);
+ if let Some(parent) = self.sess.cstore.impl_parent(impl_def_id) {
+ def.record_remote_impl(self, impl_def_id, trait_ref, parent);
+ } else {
+ def.record_remote_impl(self, impl_def_id, trait_ref, trait_id);
+ }
// For any methods that use a default implementation, add them to
// the map. This is a bit unfortunate.
Some(self.tables.borrow().upvar_capture_map.get(&upvar_id).unwrap().clone())
}
-
pub fn visit_all_items_in_krate<V,F>(&self,
dep_node_fn: F,
visitor: &mut V)
{
dep_graph::visit_all_items_in_krate(self, dep_node_fn, visitor);
}
+ /// Looks up the span of `impl_did` if the impl is local; otherwise returns `Err`
+ /// with the name of the crate containing the impl.
+ pub fn span_of_impl(&self, impl_did: DefId) -> Result<Span, String> {
+ if impl_did.is_local() {
+ let node_id = self.map.as_local_node_id(impl_did).unwrap();
+ Ok(self.map.span(node_id))
+ } else {
+ Err(self.sess.cstore.crate_name(impl_did.krate))
+ }
+ }
}
/// The category of explicit self.
Some(d) => f(&d[..])
}
}
-
- pub fn make_substs_for_receiver_types(&self,
- trait_ref: &ty::TraitRef<'tcx>,
- method: &ty::Method<'tcx>)
- -> subst::Substs<'tcx>
- {
- /*!
- * Substitutes the values for the receiver's type parameters
- * that are found in method, leaving the method's type parameters
- * intact.
- */
-
- let meth_tps: Vec<Ty> =
- method.generics.types.get_slice(subst::FnSpace)
- .iter()
- .map(|def| self.mk_param_from_def(def))
- .collect();
- let meth_regions: Vec<ty::Region> =
- method.generics.regions.get_slice(subst::FnSpace)
- .iter()
- .map(|def| def.to_early_bound_region())
- .collect();
- trait_ref.substs.clone().with_method(meth_tps, meth_regions)
- }
}
}
}
+ pub fn has_concrete_skeleton(&self) -> bool {
+ match self.sty {
+ TyParam(_) | TyInfer(_) | TyError => false,
+ _ => true,
+ }
+ }
+
// Returns the type and mutability of *ty.
//
// The parameter `explicit` indicates if this is an *explicit* dereference.
use dep_graph::DepNode;
use middle::def_id::DefId;
+use middle::traits::{self, specialization_graph};
use middle::ty;
use middle::ty::fast_reject;
-use middle::ty::{Ty, TyCtxt};
+use middle::ty::{Ty, TyCtxt, TraitRef};
use std::borrow::{Borrow};
use std::cell::{Cell, Ref, RefCell};
use syntax::ast::Name;
/// Blanket impls associated with the trait.
blanket_impls: RefCell<Vec<DefId>>,
+ /// The specialization order for impls of this trait.
+ pub specialization_graph: RefCell<traits::specialization_graph::Graph>,
+
/// Various flags
pub flags: Cell<TraitFlags>
}
associated_type_names: associated_type_names,
nonblanket_impls: RefCell::new(FnvHashMap()),
blanket_impls: RefCell::new(vec![]),
- flags: Cell::new(ty::TraitFlags::NO_TRAIT_FLAGS)
+ flags: Cell::new(ty::TraitFlags::NO_TRAIT_FLAGS),
+ specialization_graph: RefCell::new(traits::specialization_graph::Graph::new()),
}
}
tcx.dep_graph.read(DepNode::TraitImpls(self.trait_ref.def_id));
}
- /// Records a trait-to-implementation mapping.
- pub fn record_impl(&self,
- tcx: &TyCtxt<'tcx>,
- impl_def_id: DefId,
- impl_trait_ref: ty::TraitRef<'tcx>) {
+ /// Records a basic trait-to-implementation mapping.
+ ///
+ /// Returns `true` iff the impl has not previously been recorded.
+ fn record_impl(&self,
+ tcx: &TyCtxt<'tcx>,
+ impl_def_id: DefId,
+ impl_trait_ref: TraitRef<'tcx>)
+ -> bool {
debug!("TraitDef::record_impl for {:?}, from {:?}",
self, impl_trait_ref);
impl_trait_ref.self_ty(), false) {
if let Some(is) = self.nonblanket_impls.borrow().get(&sty) {
if is.contains(&impl_def_id) {
- return // duplicate - skip
+ return false; // duplicate - skip
}
}
self.nonblanket_impls.borrow_mut().entry(sty).or_insert(vec![]).push(impl_def_id)
} else {
if self.blanket_impls.borrow().contains(&impl_def_id) {
- return // duplicate - skip
+ return false; // duplicate - skip
}
self.blanket_impls.borrow_mut().push(impl_def_id)
}
+
+ true
}
- pub fn for_each_impl<F: FnMut(DefId)>(&self, tcx: &TyCtxt<'tcx>, mut f: F) {
- self.read_trait_impls(tcx);
+ /// Records a trait-to-implementation mapping for a crate-local impl.
+ pub fn record_local_impl(&self,
+ tcx: &TyCtxt<'tcx>,
+ impl_def_id: DefId,
+ impl_trait_ref: TraitRef<'tcx>) {
+ assert!(impl_def_id.is_local());
+ let was_new = self.record_impl(tcx, impl_def_id, impl_trait_ref);
+ assert!(was_new);
+ }
+
+ /// Records a trait-to-implementation mapping for a non-local impl.
+ ///
+ /// The `parent_impl` is the immediately-less-specialized impl, or the
+ /// trait's def ID if the impl is is not a specialization -- information that
+ /// should be pulled from the metadata.
+ pub fn record_remote_impl(&self,
+ tcx: &TyCtxt<'tcx>,
+ impl_def_id: DefId,
+ impl_trait_ref: TraitRef<'tcx>,
+ parent_impl: DefId) {
+ assert!(!impl_def_id.is_local());
+
+ // if the impl has not previously been recorded
+ if self.record_impl(tcx, impl_def_id, impl_trait_ref) {
+ // if the impl is non-local, it's placed directly into the
+ // specialization graph using parent information drawn from metadata.
+ self.specialization_graph.borrow_mut()
+ .record_impl_from_cstore(parent_impl, impl_def_id)
+ }
+ }
+
+ /// Adds a local impl into the specialization graph, returning an error with
+ /// overlap information if the impl overlaps but does not specialize an
+ /// existing impl.
+ pub fn add_impl_for_specialization<'a>(&self,
+ tcx: &'a TyCtxt<'tcx>,
+ impl_def_id: DefId)
+ -> Result<(), traits::Overlap<'a, 'tcx>> {
+ assert!(impl_def_id.is_local());
+
+ self.specialization_graph.borrow_mut()
+ .insert(tcx, impl_def_id)
+ }
+ pub fn ancestors<'a>(&'a self, of_impl: DefId) -> specialization_graph::Ancestors<'a, 'tcx> {
+ specialization_graph::ancestors(self, of_impl)
+ }
+
+ pub fn for_each_impl<F: FnMut(DefId)>(&self, tcx: &TyCtxt<'tcx>, mut f: F) {
+ self.read_trait_impls(tcx);
tcx.populate_implementations_for_trait_if_necessary(self.trait_ref.def_id);
for &impl_def_id in self.blanket_impls.borrow().iter() {
const IMPLS_VALID = 1 << 3,
}
}
-
use middle::const_eval::{self, ConstVal, ErrKind};
use middle::const_eval::EvalHint::UncheckedExprHint;
use middle::def_id::DefId;
-use middle::subst::{self, Subst, Substs};
+use middle::subst;
use middle::infer;
use middle::pat_util;
-use middle::traits;
+use middle::traits::{self, ProjectionMode};
use middle::ty::{self, Ty, TyCtxt, TypeAndMut, TypeFlags, TypeFoldable};
use middle::ty::{Disr, ParameterEnvironment};
use middle::ty::TypeVariants::*;
-use util::num::ToPrimitive;
+
+use rustc_const_eval::{ConstInt, ConstIsize, ConstUsize};
use std::cmp;
use std::hash::{Hash, SipHasher, Hasher};
-use std::rc::Rc;
use syntax::ast::{self, Name};
use syntax::attr::{self, AttrMetaMethods, SignedInt, UnsignedInt};
use syntax::codemap::Span;
pub trait IntTypeExt {
fn to_ty<'tcx>(&self, cx: &TyCtxt<'tcx>) -> Ty<'tcx>;
- fn i64_to_disr(&self, val: i64) -> Option<Disr>;
- fn u64_to_disr(&self, val: u64) -> Option<Disr>;
fn disr_incr(&self, val: Disr) -> Option<Disr>;
- fn disr_string(&self, val: Disr) -> String;
- fn disr_wrap_incr(&self, val: Option<Disr>) -> Disr;
+ fn assert_ty_matches(&self, val: Disr);
+ fn initial_discriminant(&self, tcx: &TyCtxt) -> Disr;
}
impl IntTypeExt for attr::IntType {
}
}
- fn i64_to_disr(&self, val: i64) -> Option<Disr> {
+ fn initial_discriminant(&self, tcx: &TyCtxt) -> Disr {
match *self {
- SignedInt(ast::IntTy::I8) => val.to_i8() .map(|v| v as Disr),
- SignedInt(ast::IntTy::I16) => val.to_i16() .map(|v| v as Disr),
- SignedInt(ast::IntTy::I32) => val.to_i32() .map(|v| v as Disr),
- SignedInt(ast::IntTy::I64) => val.to_i64() .map(|v| v as Disr),
- UnsignedInt(ast::UintTy::U8) => val.to_u8() .map(|v| v as Disr),
- UnsignedInt(ast::UintTy::U16) => val.to_u16() .map(|v| v as Disr),
- UnsignedInt(ast::UintTy::U32) => val.to_u32() .map(|v| v as Disr),
- UnsignedInt(ast::UintTy::U64) => val.to_u64() .map(|v| v as Disr),
-
- UnsignedInt(ast::UintTy::Us) |
- SignedInt(ast::IntTy::Is) => unreachable!(),
+ SignedInt(ast::IntTy::I8) => ConstInt::I8(0),
+ SignedInt(ast::IntTy::I16) => ConstInt::I16(0),
+ SignedInt(ast::IntTy::I32) => ConstInt::I32(0),
+ SignedInt(ast::IntTy::I64) => ConstInt::I64(0),
+ SignedInt(ast::IntTy::Is) => match tcx.sess.target.int_type {
+ ast::IntTy::I32 => ConstInt::Isize(ConstIsize::Is32(0)),
+ ast::IntTy::I64 => ConstInt::Isize(ConstIsize::Is64(0)),
+ _ => unreachable!(),
+ },
+ UnsignedInt(ast::UintTy::U8) => ConstInt::U8(0),
+ UnsignedInt(ast::UintTy::U16) => ConstInt::U16(0),
+ UnsignedInt(ast::UintTy::U32) => ConstInt::U32(0),
+ UnsignedInt(ast::UintTy::U64) => ConstInt::U64(0),
+ UnsignedInt(ast::UintTy::Us) => match tcx.sess.target.uint_type {
+ ast::UintTy::U32 => ConstInt::Usize(ConstUsize::Us32(0)),
+ ast::UintTy::U64 => ConstInt::Usize(ConstUsize::Us64(0)),
+ _ => unreachable!(),
+ },
}
}
- fn u64_to_disr(&self, val: u64) -> Option<Disr> {
- match *self {
- SignedInt(ast::IntTy::I8) => val.to_i8() .map(|v| v as Disr),
- SignedInt(ast::IntTy::I16) => val.to_i16() .map(|v| v as Disr),
- SignedInt(ast::IntTy::I32) => val.to_i32() .map(|v| v as Disr),
- SignedInt(ast::IntTy::I64) => val.to_i64() .map(|v| v as Disr),
- UnsignedInt(ast::UintTy::U8) => val.to_u8() .map(|v| v as Disr),
- UnsignedInt(ast::UintTy::U16) => val.to_u16() .map(|v| v as Disr),
- UnsignedInt(ast::UintTy::U32) => val.to_u32() .map(|v| v as Disr),
- UnsignedInt(ast::UintTy::U64) => val.to_u64() .map(|v| v as Disr),
-
- UnsignedInt(ast::UintTy::Us) |
- SignedInt(ast::IntTy::Is) => unreachable!(),
+ fn assert_ty_matches(&self, val: Disr) {
+ match (*self, val) {
+ (SignedInt(ast::IntTy::I8), ConstInt::I8(_)) => {},
+ (SignedInt(ast::IntTy::I16), ConstInt::I16(_)) => {},
+ (SignedInt(ast::IntTy::I32), ConstInt::I32(_)) => {},
+ (SignedInt(ast::IntTy::I64), ConstInt::I64(_)) => {},
+ (SignedInt(ast::IntTy::Is), ConstInt::Isize(_)) => {},
+ (UnsignedInt(ast::UintTy::U8), ConstInt::U8(_)) => {},
+ (UnsignedInt(ast::UintTy::U16), ConstInt::U16(_)) => {},
+ (UnsignedInt(ast::UintTy::U32), ConstInt::U32(_)) => {},
+ (UnsignedInt(ast::UintTy::U64), ConstInt::U64(_)) => {},
+ (UnsignedInt(ast::UintTy::Us), ConstInt::Usize(_)) => {},
+ _ => panic!("disr type mismatch: {:?} vs {:?}", self, val),
}
}
fn disr_incr(&self, val: Disr) -> Option<Disr> {
- macro_rules! add1 {
- ($e:expr) => { $e.and_then(|v|v.checked_add(1)).map(|v| v as Disr) }
- }
- match *self {
- // SignedInt repr means we *want* to reinterpret the bits
- // treating the highest bit of Disr as a sign-bit, so
- // cast to i64 before range-checking.
- SignedInt(ast::IntTy::I8) => add1!((val as i64).to_i8()),
- SignedInt(ast::IntTy::I16) => add1!((val as i64).to_i16()),
- SignedInt(ast::IntTy::I32) => add1!((val as i64).to_i32()),
- SignedInt(ast::IntTy::I64) => add1!(Some(val as i64)),
-
- UnsignedInt(ast::UintTy::U8) => add1!(val.to_u8()),
- UnsignedInt(ast::UintTy::U16) => add1!(val.to_u16()),
- UnsignedInt(ast::UintTy::U32) => add1!(val.to_u32()),
- UnsignedInt(ast::UintTy::U64) => add1!(Some(val)),
-
- UnsignedInt(ast::UintTy::Us) |
- SignedInt(ast::IntTy::Is) => unreachable!(),
- }
- }
-
- // This returns a String because (1.) it is only used for
- // rendering an error message and (2.) a string can represent the
- // full range from `i64::MIN` through `u64::MAX`.
- fn disr_string(&self, val: Disr) -> String {
- match *self {
- SignedInt(ast::IntTy::I8) => format!("{}", val as i8 ),
- SignedInt(ast::IntTy::I16) => format!("{}", val as i16),
- SignedInt(ast::IntTy::I32) => format!("{}", val as i32),
- SignedInt(ast::IntTy::I64) => format!("{}", val as i64),
- UnsignedInt(ast::UintTy::U8) => format!("{}", val as u8 ),
- UnsignedInt(ast::UintTy::U16) => format!("{}", val as u16),
- UnsignedInt(ast::UintTy::U32) => format!("{}", val as u32),
- UnsignedInt(ast::UintTy::U64) => format!("{}", val as u64),
-
- UnsignedInt(ast::UintTy::Us) |
- SignedInt(ast::IntTy::Is) => unreachable!(),
- }
- }
-
- fn disr_wrap_incr(&self, val: Option<Disr>) -> Disr {
- macro_rules! add1 {
- ($e:expr) => { ($e).wrapping_add(1) as Disr }
- }
- let val = val.unwrap_or(ty::INITIAL_DISCRIMINANT_VALUE);
- match *self {
- SignedInt(ast::IntTy::I8) => add1!(val as i8 ),
- SignedInt(ast::IntTy::I16) => add1!(val as i16),
- SignedInt(ast::IntTy::I32) => add1!(val as i32),
- SignedInt(ast::IntTy::I64) => add1!(val as i64),
- UnsignedInt(ast::UintTy::U8) => add1!(val as u8 ),
- UnsignedInt(ast::UintTy::U16) => add1!(val as u16),
- UnsignedInt(ast::UintTy::U32) => add1!(val as u32),
- UnsignedInt(ast::UintTy::U64) => add1!(val as u64),
-
- UnsignedInt(ast::UintTy::Us) |
- SignedInt(ast::IntTy::Is) => unreachable!(),
- }
+ self.assert_ty_matches(val);
+ (val + ConstInt::Infer(1)).ok()
}
}
let tcx = self.tcx;
// FIXME: (@jroesch) float this code up
- let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, Some(self.clone()));
+ let infcx = infer::new_infer_ctxt(tcx,
+ &tcx.tables,
+ Some(self.clone()),
+ ProjectionMode::AnyFinal);
let adt = match self_type.sty {
ty::TyStruct(struct_def, substs) => {
}
}
- /// Returns `(normalized_type, ty)`, where `normalized_type` is the
- /// IntType representation of one of {i64,i32,i16,i8,u64,u32,u16,u8},
- /// and `ty` is the original type (i.e. may include `isize` or
- /// `usize`).
- pub fn enum_repr_type(&self, opt_hint: Option<&attr::ReprAttr>)
- -> (attr::IntType, Ty<'tcx>) {
- let repr_type = match opt_hint {
+ /// Returns the IntType representation.
+ /// This used to ensure `int_ty` doesn't contain `usize` and `isize`
+ /// by converting them to their actual types. That doesn't happen anymore.
+ pub fn enum_repr_type(&self, opt_hint: Option<&attr::ReprAttr>) -> attr::IntType {
+ match opt_hint {
// Feed in the given type
Some(&attr::ReprInt(_, int_t)) => int_t,
// ... but provide sensible default if none provided
// NB. Historically `fn enum_variants` generate i64 here, while
// rustc_typeck::check would generate isize.
_ => SignedInt(ast::IntTy::Is),
- };
-
- let repr_type_ty = repr_type.to_ty(self);
- let repr_type = match repr_type {
- SignedInt(ast::IntTy::Is) =>
- SignedInt(self.sess.target.int_type),
- UnsignedInt(ast::UintTy::Us) =>
- UnsignedInt(self.sess.target.uint_type),
- other => other
- };
-
- (repr_type, repr_type_ty)
+ }
}
/// Returns the deeply last field of nested structures, or the same type,
pub fn eval_repeat_count(&self, count_expr: &hir::Expr) -> usize {
let hint = UncheckedExprHint(self.types.usize);
match const_eval::eval_const_expr_partial(self, count_expr, hint, None) {
- Ok(val) => {
- let found = match val {
- ConstVal::Uint(count) => return count as usize,
- ConstVal::Int(count) if count >= 0 => return count as usize,
- const_val => const_val.description(),
- };
+ Ok(ConstVal::Integral(ConstInt::Usize(count))) => {
+ let val = count.as_u64(self.sess.target.uint_type);
+ assert_eq!(val as usize as u64, val);
+ val as usize
+ },
+ Ok(const_val) => {
span_err!(self.sess, count_expr.span, E0306,
- "expected positive integer for repeat count, found {}",
- found);
+ "expected positive integer for repeat count, found {}",
+ const_val.description());
+ 0
}
Err(err) => {
let err_msg = match count_expr.node {
};
span_err!(self.sess, count_expr.span, E0307,
"expected constant integer for repeat count, {}", err_msg);
+ 0
}
}
- 0
}
/// Given a set of predicates that apply to an object type, returns
}
}
-#[derive(Debug)]
-pub struct ImplMethod<'tcx> {
- pub method: Rc<ty::Method<'tcx>>,
- pub substs: &'tcx Substs<'tcx>,
- pub is_provided: bool
-}
-
-impl<'tcx> TyCtxt<'tcx> {
- pub fn get_impl_method(&self,
- impl_def_id: DefId,
- substs: &'tcx Substs<'tcx>,
- name: Name)
- -> ImplMethod<'tcx>
- {
- // there don't seem to be nicer accessors to these:
- let impl_or_trait_items_map = self.impl_or_trait_items.borrow();
-
- for impl_item in &self.impl_items.borrow()[&impl_def_id] {
- if let ty::MethodTraitItem(ref meth) =
- impl_or_trait_items_map[&impl_item.def_id()] {
- if meth.name == name {
- return ImplMethod {
- method: meth.clone(),
- substs: substs,
- is_provided: false
- }
- }
- }
- }
-
- // It is not in the impl - get the default from the trait.
- let trait_ref = self.impl_trait_ref(impl_def_id).unwrap();
- for trait_item in self.trait_items(trait_ref.def_id).iter() {
- if let &ty::MethodTraitItem(ref meth) = trait_item {
- if meth.name == name {
- let impl_to_trait_substs = self
- .make_substs_for_receiver_types(&trait_ref, meth);
- let substs = impl_to_trait_substs.subst(self, substs);
- return ImplMethod {
- method: meth.clone(),
- substs: self.mk_substs(substs),
- is_provided: true
- }
- }
- }
- }
-
- self.sess.bug(&format!("method {:?} not found in {:?}",
- name, impl_def_id))
- }
-}
-
impl<'tcx> ty::TyS<'tcx> {
fn impls_bound<'a>(&'tcx self, param_env: &ParameterEnvironment<'a,'tcx>,
bound: ty::BuiltinBound,
-> bool
{
let tcx = param_env.tcx;
- let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, Some(param_env.clone()));
+ let infcx = infer::new_infer_ctxt(tcx,
+ &tcx.tables,
+ Some(param_env.clone()),
+ ProjectionMode::AnyFinal);
let is_impld = traits::type_known_to_meet_builtin_bound(&infcx,
self, bound, span);
use graphviz::IntoCow;
use middle::const_eval::ConstVal;
+use rustc_const_eval::{ConstUsize, ConstInt};
use middle::def_id::DefId;
use middle::subst::Substs;
use middle::ty::{self, AdtDef, ClosureSubsts, FnOutput, Region, Ty};
pub struct TypedConstVal<'tcx> {
pub ty: Ty<'tcx>,
pub span: Span,
- pub value: ConstVal
+ pub value: ConstUsize,
}
impl<'tcx> Debug for TypedConstVal<'tcx> {
fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
- try!(write!(fmt, "const "));
- fmt_const_val(fmt, &self.value)
+ write!(fmt, "const {}", ConstInt::Usize(self.value))
}
}
use middle::const_eval::ConstVal::*;
match *const_val {
Float(f) => write!(fmt, "{:?}", f),
- Int(n) => write!(fmt, "{:?}", n),
- Uint(n) => write!(fmt, "{:?}", n),
+ Integral(n) => write!(fmt, "{}", n),
Str(ref s) => write!(fmt, "{:?}", s),
ByteStr(ref bytes) => {
let escaped: String = bytes
Function(def_id) => write!(fmt, "{}", item_path_str(def_id)),
Struct(node_id) | Tuple(node_id) | Array(node_id, _) | Repeat(node_id, _) =>
write!(fmt, "{}", node_to_string(node_id)),
+ Char(c) => write!(fmt, "{:?}", c),
+ Dummy => unreachable!(),
}
}
*/
use mir::repr::*;
-use middle::const_eval::ConstVal;
use middle::subst::{Subst, Substs};
use middle::ty::{self, AdtDef, Ty, TyCtxt};
use rustc_front::hir;
match *rvalue {
Rvalue::Use(ref operand) => Some(self.operand_ty(tcx, operand)),
Rvalue::Repeat(ref operand, ref count) => {
- if let ConstVal::Uint(u) = count.value {
- let op_ty = self.operand_ty(tcx, operand);
- Some(tcx.mk_array(op_ty, u as usize))
- } else {
- None
- }
+ let op_ty = self.operand_ty(tcx, operand);
+ let count = count.value.as_u64(tcx.sess.target.uint_type);
+ assert_eq!(count as usize as u64, count);
+ Some(tcx.mk_array(op_ty, count as usize))
}
Rvalue::Ref(reg, bk, ref lv) => {
let lv_ty = self.lvalue_ty(tcx, lv).to_ty(tcx);
pub enum Input {
/// Load source from file
File(PathBuf),
- /// The string is the source
- Str(String)
+ Str {
+ /// String that is shown in place of a filename
+ name: String,
+ /// Anonymous source string
+ input: String,
+ },
}
impl Input {
match *self {
Input::File(ref ifile) => ifile.file_stem().unwrap()
.to_str().unwrap().to_string(),
- Input::Str(_) => "rust_out".to_string(),
+ Input::Str { .. } => "rust_out".to_string(),
}
}
}
use rustc::middle::mem_categorization::Categorization;
use rustc::middle::region;
use rustc::middle::ty::{self, TyCtxt};
+use rustc::middle::traits::ProjectionMode;
use syntax::ast;
use syntax::codemap::Span;
use rustc_front::hir;
debug!("check_loans(body id={})", body.id);
let param_env = ty::ParameterEnvironment::for_item(bccx.tcx, fn_id);
- let infcx = infer::new_infer_ctxt(bccx.tcx, &bccx.tcx.tables, Some(param_env));
+ let infcx = infer::new_infer_ctxt(bccx.tcx,
+ &bccx.tcx.tables,
+ Some(param_env),
+ ProjectionMode::AnyFinal);
let mut clcx = CheckLoanCtxt {
bccx: bccx,
use rustc::middle::mem_categorization::Categorization;
use rustc::middle::region;
use rustc::middle::ty::{self, TyCtxt};
+use rustc::middle::traits::ProjectionMode;
use syntax::ast;
use syntax::codemap::Span;
};
let param_env = ty::ParameterEnvironment::for_item(bccx.tcx, fn_id);
- let infcx = infer::new_infer_ctxt(bccx.tcx, &bccx.tcx.tables, Some(param_env));
+ let infcx = infer::new_infer_ctxt(bccx.tcx,
+ &bccx.tcx.tables,
+ Some(param_env),
+ ProjectionMode::AnyFinal);
{
let mut euv = euv::ExprUseVisitor::new(&mut glcx, &infcx);
euv.walk_fn(decl, body);
impl<'a, 'tcx, 'v> Visitor<'v> for StaticInitializerCtxt<'a, 'tcx> {
fn visit_expr(&mut self, ex: &Expr) {
if let hir::ExprAddrOf(mutbl, ref base) = ex.node {
- let infcx = infer::new_infer_ctxt(self.bccx.tcx, &self.bccx.tcx.tables, None);
+ let infcx = infer::new_infer_ctxt(self.bccx.tcx,
+ &self.bccx.tcx.tables,
+ None,
+ ProjectionMode::AnyFinal);
let mc = mc::MemCategorizationContext::new(&infcx);
let base_cmt = mc.cat_expr(&base).unwrap();
let borrow_kind = ty::BorrowKind::from_mutbl(mutbl);
--- /dev/null
+[package]
+authors = ["The Rust Project Developers"]
+name = "rustc_const_eval"
+version = "0.0.0"
+
+[lib]
+name = "rustc_const_eval"
+path = "lib.rs"
+crate-type = ["dylib"]
+
+[dependencies]
+log = { path = "../liblog" }
+serialize = { path = "../libserialize" }
+syntax = { path = "../libsyntax" }
--- /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.
+
+use syntax::ast;
+
+#[derive(Debug, PartialEq, Eq, Clone)]
+pub enum ConstMathErr {
+ NotInRange,
+ CmpBetweenUnequalTypes,
+ UnequalTypes(Op),
+ Overflow(Op),
+ ShiftNegative,
+ DivisionByZero,
+ RemainderByZero,
+ UnsignedNegation,
+ ULitOutOfRange(ast::UintTy),
+ LitOutOfRange(ast::IntTy),
+}
+pub use self::ConstMathErr::*;
+
+#[derive(Debug, PartialEq, Eq, Clone)]
+pub enum Op {
+ Add,
+ Sub,
+ Mul,
+ Div,
+ Rem,
+ Shr,
+ Shl,
+ Neg,
+ BitAnd,
+ BitOr,
+ BitXor,
+}
+
+impl ConstMathErr {
+ pub fn description(&self) -> &'static str {
+ use self::Op::*;
+ match *self {
+ NotInRange => "inferred value out of range",
+ CmpBetweenUnequalTypes => "compared two integrals of different types",
+ UnequalTypes(Add) => "tried to add two integrals of different types",
+ UnequalTypes(Sub) => "tried to subtract two integrals of different types",
+ UnequalTypes(Mul) => "tried to multiply two integrals of different types",
+ UnequalTypes(Div) => "tried to divide two integrals of different types",
+ UnequalTypes(Rem) => {
+ "tried to calculate the remainder of two integrals of different types"
+ },
+ UnequalTypes(BitAnd) => "tried to bitand two integrals of different types",
+ UnequalTypes(BitOr) => "tried to bitor two integrals of different types",
+ UnequalTypes(BitXor) => "tried to xor two integrals of different types",
+ UnequalTypes(_) => unreachable!(),
+ Overflow(Add) => "attempted to add with overflow",
+ Overflow(Sub) => "attempted to subtract with overflow",
+ Overflow(Mul) => "attempted to multiply with overflow",
+ Overflow(Div) => "attempted to divide with overflow",
+ Overflow(Rem) => "attempted to calculate the remainder with overflow",
+ Overflow(Neg) => "attempted to negate with overflow",
+ Overflow(Shr) => "attempted to shift right with overflow",
+ Overflow(Shl) => "attempted to shift left with overflow",
+ Overflow(_) => unreachable!(),
+ ShiftNegative => "attempted to shift by a negative amount",
+ DivisionByZero => "attempted to divide by zero",
+ RemainderByZero => "attempted to calculate the remainder with a divisor of zero",
+ UnsignedNegation => "unary negation of unsigned integer",
+ ULitOutOfRange(ast::UintTy::U8) => "literal out of range for u8",
+ ULitOutOfRange(ast::UintTy::U16) => "literal out of range for u16",
+ ULitOutOfRange(ast::UintTy::U32) => "literal out of range for u32",
+ ULitOutOfRange(ast::UintTy::U64) => "literal out of range for u64",
+ ULitOutOfRange(ast::UintTy::Us) => "literal out of range for usize",
+ LitOutOfRange(ast::IntTy::I8) => "literal out of range for i8",
+ LitOutOfRange(ast::IntTy::I16) => "literal out of range for i16",
+ LitOutOfRange(ast::IntTy::I32) => "literal out of range for i32",
+ LitOutOfRange(ast::IntTy::I64) => "literal out of range for i64",
+ LitOutOfRange(ast::IntTy::Is) => "literal out of range for isize",
+ }
+ }
+}
--- /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.
+
+use std::cmp::Ordering;
+use syntax::attr::IntType;
+use syntax::ast::{IntTy, UintTy};
+
+use super::is::*;
+use super::us::*;
+use super::err::*;
+
+#[derive(Copy, Clone, Debug, RustcEncodable, RustcDecodable, Hash, Eq, PartialEq)]
+pub enum ConstInt {
+ I8(i8),
+ I16(i16),
+ I32(i32),
+ I64(i64),
+ Isize(ConstIsize),
+ U8(u8),
+ U16(u16),
+ U32(u32),
+ U64(u64),
+ Usize(ConstUsize),
+ Infer(u64),
+ InferSigned(i64),
+}
+pub use self::ConstInt::*;
+
+
+macro_rules! bounds {
+ ($($t:ident $min:ident $max:ident)*) => {
+ mod as_u64 {
+ $(
+ #[allow(dead_code)]
+ pub const $min: u64 = ::std::$t::MIN as u64;
+ #[allow(dead_code)]
+ pub const $max: u64 = ::std::$t::MAX as u64;
+ )*
+ }
+ mod as_i64 {
+ $(
+ #[allow(dead_code)]
+ pub const $min: i64 = ::std::$t::MIN as i64;
+ #[allow(dead_code)]
+ pub const $max: i64 = ::std::$t::MAX as i64;
+ )*
+ }
+ }
+}
+
+bounds!{
+ i8 I8MIN I8MAX i16 I16MIN I16MAX i32 I32MIN I32MAX i64 I64MIN I64MAX isize IMIN IMAX
+ u8 U8MIN U8MAX u16 U16MIN U16MAX u32 U32MIN U32MAX u64 U64MIN U64MAX usize UMIN UMAX
+}
+
+impl ConstInt {
+ /// If either value is `Infer` or `InferSigned`, try to turn the value into the type of
+ /// the other value. If both values have no type, don't do anything
+ pub fn infer(self, other: Self) -> Result<(Self, Self), ConstMathErr> {
+ let inferred = match (self, other) {
+ (InferSigned(_), InferSigned(_))
+ | (Infer(_), Infer(_)) => self, // no inference possible
+ // kindof wrong, you could have had values > I64MAX during computation of a
+ (Infer(a @ 0...as_u64::I64MAX), InferSigned(_)) => InferSigned(a as i64),
+ (Infer(_), InferSigned(_)) => return Err(ConstMathErr::NotInRange),
+ (_, InferSigned(_))
+ | (_, Infer(_)) => return other.infer(self).map(|(b, a)| (a, b)),
+
+ (Infer(a @ 0...as_u64::I8MAX), I8(_)) => I8(a as i64 as i8),
+ (Infer(a @ 0...as_u64::I16MAX), I16(_)) => I16(a as i64 as i16),
+ (Infer(a @ 0...as_u64::I32MAX), I32(_)) => I32(a as i64 as i32),
+ (Infer(a @ 0...as_u64::I64MAX), I64(_)) => I64(a as i64),
+ (Infer(a @ 0...as_u64::I32MAX), Isize(Is32(_))) => Isize(Is32(a as i64 as i32)),
+ (Infer(a @ 0...as_u64::I64MAX), Isize(Is64(_))) => Isize(Is64(a as i64)),
+ (Infer(a @ 0...as_u64::U8MAX), U8(_)) => U8(a as u8),
+ (Infer(a @ 0...as_u64::U16MAX), U16(_)) => U16(a as u16),
+ (Infer(a @ 0...as_u64::U32MAX), U32(_)) => U32(a as u32),
+ (Infer(a), U64(_)) => U64(a),
+ (Infer(a @ 0...as_u64::U32MAX), Usize(Us32(_))) => Usize(Us32(a as u32)),
+ (Infer(a), Usize(Us64(_))) => Usize(Us64(a)),
+
+ (Infer(_), _) => return Err(ConstMathErr::NotInRange),
+
+ (InferSigned(a @ as_i64::I8MIN...as_i64::I8MAX), I8(_)) => I8(a as i8),
+ (InferSigned(a @ as_i64::I16MIN...as_i64::I16MAX), I16(_)) => I16(a as i16),
+ (InferSigned(a @ as_i64::I32MIN...as_i64::I32MAX), I32(_)) => I32(a as i32),
+ (InferSigned(a), I64(_)) => I64(a),
+ (InferSigned(a @ as_i64::I32MIN...as_i64::I32MAX), Isize(Is32(_))) => {
+ Isize(Is32(a as i32))
+ },
+ (InferSigned(a), Isize(Is64(_))) => Isize(Is64(a)),
+ (InferSigned(a @ 0...as_i64::U8MAX), U8(_)) => U8(a as u8),
+ (InferSigned(a @ 0...as_i64::U16MAX), U16(_)) => U16(a as u16),
+ (InferSigned(a @ 0...as_i64::U32MAX), U32(_)) => U32(a as u32),
+ (InferSigned(a @ 0...as_i64::I64MAX), U64(_)) => U64(a as u64),
+ (InferSigned(a @ 0...as_i64::U32MAX), Usize(Us32(_))) => Usize(Us32(a as u32)),
+ (InferSigned(a @ 0...as_i64::I64MAX), Usize(Us64(_))) => Usize(Us64(a as u64)),
+ (InferSigned(_), _) => return Err(ConstMathErr::NotInRange),
+ _ => self, // already known types
+ };
+ Ok((inferred, other))
+ }
+
+ /// Turn this value into an `Infer` or an `InferSigned`
+ pub fn erase_type(self) -> Self {
+ match self {
+ Infer(i) => Infer(i),
+ InferSigned(i) if i < 0 => InferSigned(i),
+ I8(i) if i < 0 => InferSigned(i as i64),
+ I16(i) if i < 0 => InferSigned(i as i64),
+ I32(i) if i < 0 => InferSigned(i as i64),
+ I64(i) if i < 0 => InferSigned(i as i64),
+ Isize(Is32(i)) if i < 0 => InferSigned(i as i64),
+ Isize(Is64(i)) if i < 0 => InferSigned(i as i64),
+ InferSigned(i) => Infer(i as u64),
+ I8(i) => Infer(i as u64),
+ I16(i) => Infer(i as u64),
+ I32(i) => Infer(i as u64),
+ I64(i) => Infer(i as u64),
+ Isize(Is32(i)) => Infer(i as u64),
+ Isize(Is64(i)) => Infer(i as u64),
+ U8(i) => Infer(i as u64),
+ U16(i) => Infer(i as u64),
+ U32(i) => Infer(i as u64),
+ U64(i) => Infer(i as u64),
+ Usize(Us32(i)) => Infer(i as u64),
+ Usize(Us64(i)) => Infer(i),
+ }
+ }
+
+ /// Description of the type, not the value
+ pub fn description(&self) -> &'static str {
+ match *self {
+ Infer(_) => "not yet inferred integral",
+ InferSigned(_) => "not yet inferred signed integral",
+ I8(_) => "i8",
+ I16(_) => "i16",
+ I32(_) => "i32",
+ I64(_) => "i64",
+ Isize(_) => "isize",
+ U8(_) => "u8",
+ U16(_) => "u16",
+ U32(_) => "u32",
+ U64(_) => "u64",
+ Usize(_) => "usize",
+ }
+ }
+
+ /// Erases the type and returns a u64.
+ /// This is not the same as `-5i8 as u64` but as `-5i8 as i64 as u64`
+ pub fn to_u64_unchecked(self) -> u64 {
+ match self.erase_type() {
+ ConstInt::Infer(i) => i,
+ ConstInt::InferSigned(i) => i as u64,
+ _ => unreachable!(),
+ }
+ }
+
+ /// Converts the value to a `u32` if it's in the range 0...std::u32::MAX
+ pub fn to_u32(&self) -> Option<u32> {
+ match *self {
+ I8(v) if v >= 0 => Some(v as u32),
+ I16(v) if v >= 0 => Some(v as u32),
+ I32(v) if v >= 0 => Some(v as u32),
+ InferSigned(v)
+ | Isize(Is64(v))
+ | I64(v) if v >= 0 && v <= ::std::u32::MAX as i64 => Some(v as u32),
+ Isize(Is32(v)) if v >= 0 => Some(v as u32),
+ U8(v) => Some(v as u32),
+ U16(v) => Some(v as u32),
+ U32(v) => Some(v),
+ Infer(v)
+ | Usize(Us64(v))
+ | U64(v) if v <= ::std::u32::MAX as u64 => Some(v as u32),
+ Usize(Us32(v)) => Some(v),
+ _ => None,
+ }
+ }
+
+ /// Converts the value to a `u64` if it's >= 0
+ pub fn to_u64(&self) -> Option<u64> {
+ match *self {
+ Infer(v) => Some(v),
+ InferSigned(v) if v >= 0 => Some(v as u64),
+ I8(v) if v >= 0 => Some(v as u64),
+ I16(v) if v >= 0 => Some(v as u64),
+ I32(v) if v >= 0 => Some(v as u64),
+ I64(v) if v >= 0 => Some(v as u64),
+ Isize(Is32(v)) if v >= 0 => Some(v as u64),
+ Isize(Is64(v)) if v >= 0 => Some(v as u64),
+ U8(v) => Some(v as u64),
+ U16(v) => Some(v as u64),
+ U32(v) => Some(v as u64),
+ U64(v) => Some(v),
+ Usize(Us32(v)) => Some(v as u64),
+ Usize(Us64(v)) => Some(v),
+ _ => None,
+ }
+ }
+
+ pub fn is_negative(&self) -> bool {
+ match *self {
+ I8(v) => v < 0,
+ I16(v) => v < 0,
+ I32(v) => v < 0,
+ I64(v) => v < 0,
+ Isize(Is32(v)) => v < 0,
+ Isize(Is64(v)) => v < 0,
+ InferSigned(v) => v < 0,
+ _ => false,
+ }
+ }
+
+ /// Compares the values if they are of the same type
+ pub fn try_cmp(self, rhs: Self) -> Result<::std::cmp::Ordering, ConstMathErr> {
+ match try!(self.infer(rhs)) {
+ (I8(a), I8(b)) => Ok(a.cmp(&b)),
+ (I16(a), I16(b)) => Ok(a.cmp(&b)),
+ (I32(a), I32(b)) => Ok(a.cmp(&b)),
+ (I64(a), I64(b)) => Ok(a.cmp(&b)),
+ (Isize(Is32(a)), Isize(Is32(b))) => Ok(a.cmp(&b)),
+ (Isize(Is64(a)), Isize(Is64(b))) => Ok(a.cmp(&b)),
+ (U8(a), U8(b)) => Ok(a.cmp(&b)),
+ (U16(a), U16(b)) => Ok(a.cmp(&b)),
+ (U32(a), U32(b)) => Ok(a.cmp(&b)),
+ (U64(a), U64(b)) => Ok(a.cmp(&b)),
+ (Usize(Us32(a)), Usize(Us32(b))) => Ok(a.cmp(&b)),
+ (Usize(Us64(a)), Usize(Us64(b))) => Ok(a.cmp(&b)),
+ (Infer(a), Infer(b)) => Ok(a.cmp(&b)),
+ (InferSigned(a), InferSigned(b)) => Ok(a.cmp(&b)),
+ _ => Err(CmpBetweenUnequalTypes),
+ }
+ }
+
+ /// Adds 1 to the value and wraps around if the maximum for the type is reached
+ pub fn wrap_incr(self) -> Self {
+ macro_rules! add1 {
+ ($e:expr) => { ($e).wrapping_add(1) }
+ }
+ match self {
+ ConstInt::I8(i) => ConstInt::I8(add1!(i)),
+ ConstInt::I16(i) => ConstInt::I16(add1!(i)),
+ ConstInt::I32(i) => ConstInt::I32(add1!(i)),
+ ConstInt::I64(i) => ConstInt::I64(add1!(i)),
+ ConstInt::Isize(ConstIsize::Is32(i)) => ConstInt::Isize(ConstIsize::Is32(add1!(i))),
+ ConstInt::Isize(ConstIsize::Is64(i)) => ConstInt::Isize(ConstIsize::Is64(add1!(i))),
+ ConstInt::U8(i) => ConstInt::U8(add1!(i)),
+ ConstInt::U16(i) => ConstInt::U16(add1!(i)),
+ ConstInt::U32(i) => ConstInt::U32(add1!(i)),
+ ConstInt::U64(i) => ConstInt::U64(add1!(i)),
+ ConstInt::Usize(ConstUsize::Us32(i)) => ConstInt::Usize(ConstUsize::Us32(add1!(i))),
+ ConstInt::Usize(ConstUsize::Us64(i)) => ConstInt::Usize(ConstUsize::Us64(add1!(i))),
+ ConstInt::Infer(_) | ConstInt::InferSigned(_) => panic!("no type info for const int"),
+ }
+ }
+
+ pub fn int_type(self) -> Option<IntType> {
+ match self {
+ ConstInt::I8(_) => Some(IntType::SignedInt(IntTy::I8)),
+ ConstInt::I16(_) => Some(IntType::SignedInt(IntTy::I16)),
+ ConstInt::I32(_) => Some(IntType::SignedInt(IntTy::I32)),
+ ConstInt::I64(_) => Some(IntType::SignedInt(IntTy::I64)),
+ ConstInt::Isize(_) => Some(IntType::SignedInt(IntTy::Is)),
+ ConstInt::U8(_) => Some(IntType::UnsignedInt(UintTy::U8)),
+ ConstInt::U16(_) => Some(IntType::UnsignedInt(UintTy::U16)),
+ ConstInt::U32(_) => Some(IntType::UnsignedInt(UintTy::U32)),
+ ConstInt::U64(_) => Some(IntType::UnsignedInt(UintTy::U64)),
+ ConstInt::Usize(_) => Some(IntType::UnsignedInt(UintTy::Us)),
+ _ => None,
+ }
+ }
+}
+
+impl ::std::cmp::PartialOrd for ConstInt {
+ fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+ self.try_cmp(*other).ok()
+ }
+}
+
+impl ::std::cmp::Ord for ConstInt {
+ fn cmp(&self, other: &Self) -> Ordering {
+ self.try_cmp(*other).unwrap()
+ }
+}
+
+impl ::std::fmt::Display for ConstInt {
+ fn fmt(&self, fmt: &mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error> {
+ match *self {
+ Infer(i) => write!(fmt, "{}", i),
+ InferSigned(i) => write!(fmt, "{}", i),
+ I8(i) => write!(fmt, "{}i8", i),
+ I16(i) => write!(fmt, "{}i16", i),
+ I32(i) => write!(fmt, "{}i32", i),
+ I64(i) => write!(fmt, "{}i64", i),
+ Isize(ConstIsize::Is64(i)) => write!(fmt, "{}isize", i),
+ Isize(ConstIsize::Is32(i)) => write!(fmt, "{}isize", i),
+ U8(i) => write!(fmt, "{}u8", i),
+ U16(i) => write!(fmt, "{}u16", i),
+ U32(i) => write!(fmt, "{}u32", i),
+ U64(i) => write!(fmt, "{}u64", i),
+ Usize(ConstUsize::Us64(i)) => write!(fmt, "{}usize", i),
+ Usize(ConstUsize::Us32(i)) => write!(fmt, "{}usize", i),
+ }
+ }
+}
+
+macro_rules! overflowing {
+ ($e:expr, $err:expr) => {{
+ if $e.1 {
+ return Err(Overflow($err));
+ } else {
+ $e.0
+ }
+ }}
+}
+
+macro_rules! impl_binop {
+ ($op:ident, $func:ident, $checked_func:ident) => {
+ impl ::std::ops::$op for ConstInt {
+ type Output = Result<Self, ConstMathErr>;
+ fn $func(self, rhs: Self) -> Result<Self, ConstMathErr> {
+ match try!(self.infer(rhs)) {
+ (I8(a), I8(b)) => a.$checked_func(b).map(I8),
+ (I16(a), I16(b)) => a.$checked_func(b).map(I16),
+ (I32(a), I32(b)) => a.$checked_func(b).map(I32),
+ (I64(a), I64(b)) => a.$checked_func(b).map(I64),
+ (Isize(Is32(a)), Isize(Is32(b))) => a.$checked_func(b).map(Is32).map(Isize),
+ (Isize(Is64(a)), Isize(Is64(b))) => a.$checked_func(b).map(Is64).map(Isize),
+ (U8(a), U8(b)) => a.$checked_func(b).map(U8),
+ (U16(a), U16(b)) => a.$checked_func(b).map(U16),
+ (U32(a), U32(b)) => a.$checked_func(b).map(U32),
+ (U64(a), U64(b)) => a.$checked_func(b).map(U64),
+ (Usize(Us32(a)), Usize(Us32(b))) => a.$checked_func(b).map(Us32).map(Usize),
+ (Usize(Us64(a)), Usize(Us64(b))) => a.$checked_func(b).map(Us64).map(Usize),
+ (Infer(a), Infer(b)) => a.$checked_func(b).map(Infer),
+ (InferSigned(a), InferSigned(b)) => a.$checked_func(b).map(InferSigned),
+ _ => return Err(UnequalTypes(Op::$op)),
+ }.ok_or(Overflow(Op::$op))
+ }
+ }
+ }
+}
+
+macro_rules! derive_binop {
+ ($op:ident, $func:ident) => {
+ impl ::std::ops::$op for ConstInt {
+ type Output = Result<Self, ConstMathErr>;
+ fn $func(self, rhs: Self) -> Result<Self, ConstMathErr> {
+ match try!(self.infer(rhs)) {
+ (I8(a), I8(b)) => Ok(I8(a.$func(b))),
+ (I16(a), I16(b)) => Ok(I16(a.$func(b))),
+ (I32(a), I32(b)) => Ok(I32(a.$func(b))),
+ (I64(a), I64(b)) => Ok(I64(a.$func(b))),
+ (Isize(Is32(a)), Isize(Is32(b))) => Ok(Isize(Is32(a.$func(b)))),
+ (Isize(Is64(a)), Isize(Is64(b))) => Ok(Isize(Is64(a.$func(b)))),
+ (U8(a), U8(b)) => Ok(U8(a.$func(b))),
+ (U16(a), U16(b)) => Ok(U16(a.$func(b))),
+ (U32(a), U32(b)) => Ok(U32(a.$func(b))),
+ (U64(a), U64(b)) => Ok(U64(a.$func(b))),
+ (Usize(Us32(a)), Usize(Us32(b))) => Ok(Usize(Us32(a.$func(b)))),
+ (Usize(Us64(a)), Usize(Us64(b))) => Ok(Usize(Us64(a.$func(b)))),
+ (Infer(a), Infer(b)) => Ok(Infer(a.$func(b))),
+ (InferSigned(a), InferSigned(b)) => Ok(InferSigned(a.$func(b))),
+ _ => Err(UnequalTypes(Op::$op)),
+ }
+ }
+ }
+ }
+}
+
+impl_binop!(Add, add, checked_add);
+impl_binop!(Sub, sub, checked_sub);
+impl_binop!(Mul, mul, checked_mul);
+derive_binop!(BitAnd, bitand);
+derive_binop!(BitOr, bitor);
+derive_binop!(BitXor, bitxor);
+
+fn check_division(
+ lhs: ConstInt,
+ rhs: ConstInt,
+ op: Op,
+ zerr: ConstMathErr,
+) -> Result<(), ConstMathErr> {
+ match (lhs, rhs) {
+ (I8(_), I8(0)) => Err(zerr),
+ (I16(_), I16(0)) => Err(zerr),
+ (I32(_), I32(0)) => Err(zerr),
+ (I64(_), I64(0)) => Err(zerr),
+ (Isize(_), Isize(Is32(0))) => Err(zerr),
+ (Isize(_), Isize(Is64(0))) => Err(zerr),
+ (InferSigned(_), InferSigned(0)) => Err(zerr),
+
+ (U8(_), U8(0)) => Err(zerr),
+ (U16(_), U16(0)) => Err(zerr),
+ (U32(_), U32(0)) => Err(zerr),
+ (U64(_), U64(0)) => Err(zerr),
+ (Usize(_), Usize(Us32(0))) => Err(zerr),
+ (Usize(_), Usize(Us64(0))) => Err(zerr),
+ (Infer(_), Infer(0)) => Err(zerr),
+
+ (I8(::std::i8::MIN), I8(-1)) => Err(Overflow(op)),
+ (I16(::std::i16::MIN), I16(-1)) => Err(Overflow(op)),
+ (I32(::std::i32::MIN), I32(-1)) => Err(Overflow(op)),
+ (I64(::std::i64::MIN), I64(-1)) => Err(Overflow(op)),
+ (Isize(Is32(::std::i32::MIN)), Isize(Is32(-1))) => Err(Overflow(op)),
+ (Isize(Is64(::std::i64::MIN)), Isize(Is64(-1))) => Err(Overflow(op)),
+ (InferSigned(::std::i64::MIN), InferSigned(-1)) => Err(Overflow(op)),
+
+ _ => Ok(()),
+ }
+}
+
+impl ::std::ops::Div for ConstInt {
+ type Output = Result<Self, ConstMathErr>;
+ fn div(self, rhs: Self) -> Result<Self, ConstMathErr> {
+ let (lhs, rhs) = try!(self.infer(rhs));
+ try!(check_division(lhs, rhs, Op::Div, DivisionByZero));
+ match (lhs, rhs) {
+ (I8(a), I8(b)) => Ok(I8(a/b)),
+ (I16(a), I16(b)) => Ok(I16(a/b)),
+ (I32(a), I32(b)) => Ok(I32(a/b)),
+ (I64(a), I64(b)) => Ok(I64(a/b)),
+ (Isize(Is32(a)), Isize(Is32(b))) => Ok(Isize(Is32(a/b))),
+ (Isize(Is64(a)), Isize(Is64(b))) => Ok(Isize(Is64(a/b))),
+ (InferSigned(a), InferSigned(b)) => Ok(InferSigned(a/b)),
+
+ (U8(a), U8(b)) => Ok(U8(a/b)),
+ (U16(a), U16(b)) => Ok(U16(a/b)),
+ (U32(a), U32(b)) => Ok(U32(a/b)),
+ (U64(a), U64(b)) => Ok(U64(a/b)),
+ (Usize(Us32(a)), Usize(Us32(b))) => Ok(Usize(Us32(a/b))),
+ (Usize(Us64(a)), Usize(Us64(b))) => Ok(Usize(Us64(a/b))),
+ (Infer(a), Infer(b)) => Ok(Infer(a/b)),
+
+ _ => Err(UnequalTypes(Op::Div)),
+ }
+ }
+}
+
+impl ::std::ops::Rem for ConstInt {
+ type Output = Result<Self, ConstMathErr>;
+ fn rem(self, rhs: Self) -> Result<Self, ConstMathErr> {
+ let (lhs, rhs) = try!(self.infer(rhs));
+ // should INT_MIN%-1 be zero or an error?
+ try!(check_division(lhs, rhs, Op::Rem, RemainderByZero));
+ match (lhs, rhs) {
+ (I8(a), I8(b)) => Ok(I8(a%b)),
+ (I16(a), I16(b)) => Ok(I16(a%b)),
+ (I32(a), I32(b)) => Ok(I32(a%b)),
+ (I64(a), I64(b)) => Ok(I64(a%b)),
+ (Isize(Is32(a)), Isize(Is32(b))) => Ok(Isize(Is32(a%b))),
+ (Isize(Is64(a)), Isize(Is64(b))) => Ok(Isize(Is64(a%b))),
+ (InferSigned(a), InferSigned(b)) => Ok(InferSigned(a%b)),
+
+ (U8(a), U8(b)) => Ok(U8(a%b)),
+ (U16(a), U16(b)) => Ok(U16(a%b)),
+ (U32(a), U32(b)) => Ok(U32(a%b)),
+ (U64(a), U64(b)) => Ok(U64(a%b)),
+ (Usize(Us32(a)), Usize(Us32(b))) => Ok(Usize(Us32(a%b))),
+ (Usize(Us64(a)), Usize(Us64(b))) => Ok(Usize(Us64(a%b))),
+ (Infer(a), Infer(b)) => Ok(Infer(a%b)),
+
+ _ => Err(UnequalTypes(Op::Rem)),
+ }
+ }
+}
+
+impl ::std::ops::Shl<ConstInt> for ConstInt {
+ type Output = Result<Self, ConstMathErr>;
+ fn shl(self, rhs: Self) -> Result<Self, ConstMathErr> {
+ let b = try!(rhs.to_u32().ok_or(ShiftNegative));
+ match self {
+ I8(a) => Ok(I8(overflowing!(a.overflowing_shl(b), Op::Shl))),
+ I16(a) => Ok(I16(overflowing!(a.overflowing_shl(b), Op::Shl))),
+ I32(a) => Ok(I32(overflowing!(a.overflowing_shl(b), Op::Shl))),
+ I64(a) => Ok(I64(overflowing!(a.overflowing_shl(b), Op::Shl))),
+ Isize(Is32(a)) => Ok(Isize(Is32(overflowing!(a.overflowing_shl(b), Op::Shl)))),
+ Isize(Is64(a)) => Ok(Isize(Is64(overflowing!(a.overflowing_shl(b), Op::Shl)))),
+ U8(a) => Ok(U8(overflowing!(a.overflowing_shl(b), Op::Shl))),
+ U16(a) => Ok(U16(overflowing!(a.overflowing_shl(b), Op::Shl))),
+ U32(a) => Ok(U32(overflowing!(a.overflowing_shl(b), Op::Shl))),
+ U64(a) => Ok(U64(overflowing!(a.overflowing_shl(b), Op::Shl))),
+ Usize(Us32(a)) => Ok(Usize(Us32(overflowing!(a.overflowing_shl(b), Op::Shl)))),
+ Usize(Us64(a)) => Ok(Usize(Us64(overflowing!(a.overflowing_shl(b), Op::Shl)))),
+ Infer(a) => Ok(Infer(overflowing!(a.overflowing_shl(b), Op::Shl))),
+ InferSigned(a) => Ok(InferSigned(overflowing!(a.overflowing_shl(b), Op::Shl))),
+ }
+ }
+}
+
+impl ::std::ops::Shr<ConstInt> for ConstInt {
+ type Output = Result<Self, ConstMathErr>;
+ fn shr(self, rhs: Self) -> Result<Self, ConstMathErr> {
+ let b = try!(rhs.to_u32().ok_or(ShiftNegative));
+ match self {
+ I8(a) => Ok(I8(overflowing!(a.overflowing_shr(b), Op::Shr))),
+ I16(a) => Ok(I16(overflowing!(a.overflowing_shr(b), Op::Shr))),
+ I32(a) => Ok(I32(overflowing!(a.overflowing_shr(b), Op::Shr))),
+ I64(a) => Ok(I64(overflowing!(a.overflowing_shr(b), Op::Shl))),
+ Isize(Is32(a)) => Ok(Isize(Is32(overflowing!(a.overflowing_shr(b), Op::Shr)))),
+ Isize(Is64(a)) => Ok(Isize(Is64(overflowing!(a.overflowing_shr(b), Op::Shr)))),
+ U8(a) => Ok(U8(overflowing!(a.overflowing_shr(b), Op::Shr))),
+ U16(a) => Ok(U16(overflowing!(a.overflowing_shr(b), Op::Shr))),
+ U32(a) => Ok(U32(overflowing!(a.overflowing_shr(b), Op::Shr))),
+ U64(a) => Ok(U64(overflowing!(a.overflowing_shr(b), Op::Shr))),
+ Usize(Us32(a)) => Ok(Usize(Us32(overflowing!(a.overflowing_shr(b), Op::Shr)))),
+ Usize(Us64(a)) => Ok(Usize(Us64(overflowing!(a.overflowing_shr(b), Op::Shr)))),
+ Infer(a) => Ok(Infer(overflowing!(a.overflowing_shr(b), Op::Shr))),
+ InferSigned(a) => Ok(InferSigned(overflowing!(a.overflowing_shr(b), Op::Shr))),
+ }
+ }
+}
+
+impl ::std::ops::Neg for ConstInt {
+ type Output = Result<Self, ConstMathErr>;
+ fn neg(self) -> Result<Self, ConstMathErr> {
+ match self {
+ I8(a) => Ok(I8(overflowing!(a.overflowing_neg(), Op::Neg))),
+ I16(a) => Ok(I16(overflowing!(a.overflowing_neg(), Op::Neg))),
+ I32(a) => Ok(I32(overflowing!(a.overflowing_neg(), Op::Neg))),
+ I64(a) => Ok(I64(overflowing!(a.overflowing_neg(), Op::Neg))),
+ Isize(Is32(a)) => Ok(Isize(Is32(overflowing!(a.overflowing_neg(), Op::Neg)))),
+ Isize(Is64(a)) => Ok(Isize(Is64(overflowing!(a.overflowing_neg(), Op::Neg)))),
+ U8(0) => Ok(U8(0)),
+ U16(0) => Ok(U16(0)),
+ U32(0) => Ok(U32(0)),
+ U64(0) => Ok(U64(0)),
+ Usize(Us32(0)) => Ok(Usize(Us32(0))),
+ Usize(Us64(0)) => Ok(Usize(Us64(0))),
+ U8(_) => Err(UnsignedNegation),
+ U16(_) => Err(UnsignedNegation),
+ U32(_) => Err(UnsignedNegation),
+ U64(_) => Err(UnsignedNegation),
+ Usize(_) => Err(UnsignedNegation),
+ Infer(a @ 0...as_u64::I64MAX) => Ok(InferSigned(-(a as i64))),
+ Infer(_) => Err(Overflow(Op::Neg)),
+ InferSigned(a) => Ok(InferSigned(overflowing!(a.overflowing_neg(), Op::Neg))),
+ }
+ }
+}
+
+impl ::std::ops::Not for ConstInt {
+ type Output = Result<Self, ConstMathErr>;
+ fn not(self) -> Result<Self, ConstMathErr> {
+ match self {
+ I8(a) => Ok(I8(!a)),
+ I16(a) => Ok(I16(!a)),
+ I32(a) => Ok(I32(!a)),
+ I64(a) => Ok(I64(!a)),
+ Isize(Is32(a)) => Ok(Isize(Is32(!a))),
+ Isize(Is64(a)) => Ok(Isize(Is64(!a))),
+ U8(a) => Ok(U8(!a)),
+ U16(a) => Ok(U16(!a)),
+ U32(a) => Ok(U32(!a)),
+ U64(a) => Ok(U64(!a)),
+ Usize(Us32(a)) => Ok(Usize(Us32(!a))),
+ Usize(Us64(a)) => Ok(Usize(Us64(!a))),
+ Infer(a) => Ok(Infer(!a)),
+ InferSigned(a) => Ok(InferSigned(!a)),
+ }
+ }
+}
--- /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.
+
+use syntax::ast;
+use super::err::*;
+
+/// Depending on the target only one variant is ever used in a compilation.
+/// Anything else is an error. This invariant is checked at several locations
+#[derive(Copy, Clone, Debug, RustcEncodable, RustcDecodable, Hash, Eq, PartialEq)]
+pub enum ConstIsize {
+ Is32(i32),
+ Is64(i64),
+}
+pub use self::ConstIsize::*;
+
+impl ConstIsize {
+ pub fn as_i64(self, target_int_ty: ast::IntTy) -> i64 {
+ match (self, target_int_ty) {
+ (Is32(i), ast::IntTy::I32) => i as i64,
+ (Is64(i), ast::IntTy::I64) => i,
+ _ => panic!("got invalid isize size for target"),
+ }
+ }
+ pub fn new(i: i64, target_int_ty: ast::IntTy) -> Result<Self, ConstMathErr> {
+ match target_int_ty {
+ ast::IntTy::I32 if i as i32 as i64 == i => Ok(Is32(i as i32)),
+ ast::IntTy::I32 => Err(LitOutOfRange(ast::IntTy::Is)),
+ ast::IntTy::I64 => Ok(Is64(i)),
+ _ => unreachable!(),
+ }
+ }
+}
--- /dev/null
+// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// 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.
+
+//! Rusty Mathematics
+//!
+//! # Note
+//!
+//! This API is completely unstable and subject to change.
+
+#![crate_name = "rustc_const_eval"]
+#![unstable(feature = "rustc_private", issue = "27812")]
+#![crate_type = "dylib"]
+#![crate_type = "rlib"]
+#![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
+ html_favicon_url = "https://doc.rust-lang.org/favicon.ico",
+ html_root_url = "https://doc.rust-lang.org/nightly/")]
+
+
+#![feature(rustc_private)]
+#![feature(staged_api)]
+
+#[macro_use] extern crate log;
+#[macro_use] extern crate syntax;
+
+extern crate serialize as rustc_serialize; // used by deriving
+
+mod int;
+mod us;
+mod is;
+mod err;
+
+pub use int::*;
+pub use us::*;
+pub use is::*;
+pub use err::ConstMathErr;
--- /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.
+
+use syntax::ast;
+use super::err::*;
+
+/// Depending on the target only one variant is ever used in a compilation.
+/// Anything else is an error. This invariant is checked at several locations
+#[derive(Copy, Clone, Debug, RustcEncodable, RustcDecodable, Hash, Eq, PartialEq)]
+pub enum ConstUsize {
+ Us32(u32),
+ Us64(u64),
+}
+pub use self::ConstUsize::*;
+
+impl ConstUsize {
+ pub fn as_u64(self, target_uint_ty: ast::UintTy) -> u64 {
+ match (self, target_uint_ty) {
+ (Us32(i), ast::UintTy::U32) => i as u64,
+ (Us64(i), ast::UintTy::U64) => i,
+ _ => panic!("got invalid usize size for target"),
+ }
+ }
+ pub fn new(i: u64, target_uint_ty: ast::UintTy) -> Result<Self, ConstMathErr> {
+ match target_uint_ty {
+ ast::UintTy::U32 if i as u32 as u64 == i => Ok(Us32(i as u32)),
+ ast::UintTy::U32 => Err(ULitOutOfRange(ast::UintTy::Us)),
+ ast::UintTy::U64 => Ok(Us64(i)),
+ _ => unreachable!(),
+ }
+ }
+}
`process_obligations` would simply yield back further ambiguous
results. This is used by the `FulfillmentContext` to decide when it
has reached a steady state.
-
+
#### Snapshots
The `ObligationForest` supports a limited form of snapshots; see
has a current state, described by `NodeState`. After each
processing step, we compress the vector to remove completed and error
nodes, which aren't needed anymore.
-
-
Ok(())
}
-
/// The name used for source code that doesn't originate in a file
/// (e.g. source from stdin or a string)
pub fn anon_src() -> String {
match *input {
// FIXME (#9639): This needs to handle non-utf8 paths
Input::File(ref ifile) => ifile.to_str().unwrap().to_string(),
- Input::Str(_) => anon_src(),
+ Input::Str { ref name, .. } => name.clone(),
}
}
Input::File(ref file) => {
parse::parse_crate_from_file(file, cfg.clone(), &sess.parse_sess)
}
- Input::Str(ref src) => {
- parse::parse_crate_from_source_str(anon_src().to_string(),
- src.to_string(),
+ Input::Str { ref input, ref name } => {
+ parse::parse_crate_from_source_str(name.clone(),
+ input.clone(),
cfg.clone(),
&sess.parse_sess)
}
if ifile == "-" {
let mut src = String::new();
io::stdin().read_to_string(&mut src).unwrap();
- Some((Input::Str(src), None))
+ Some((Input::Str { name: driver::anon_src(), input: src },
+ None))
} else {
Some((Input::File(PathBuf::from(ifile)),
Some(PathBuf::from(ifile))))
.unwrap();
println!("{}", String::from_utf8(v).unwrap());
}
- &Input::Str(_) => {
+ &Input::Str { .. } => {
early_error(ErrorOutputType::default(), "cannot list metadata for stdin");
}
}
Input::File(ref ifile) => {
parse::parse_crate_attrs_from_file(ifile, Vec::new(), &sess.parse_sess)
}
- Input::Str(ref src) => {
- parse::parse_crate_attrs_from_source_str(driver::anon_src().to_string(),
- src.to_string(),
+ Input::Str { ref name, ref input } => {
+ parse::parse_crate_attrs_from_source_str(name.clone(),
+ input.clone(),
Vec::new(),
&sess.parse_sess)
}
use rustc_typeck::middle::stability;
use rustc_typeck::middle::subst;
use rustc_typeck::middle::subst::Subst;
+use rustc_typeck::middle::traits::ProjectionMode;
use rustc_typeck::middle::ty::{self, Ty, TyCtxt, TypeFoldable};
use rustc_typeck::middle::ty::relate::TypeRelation;
use rustc_typeck::middle::infer::{self, TypeOrigin};
Rc::new(CodeMap::new()), cstore.clone());
rustc_lint::register_builtins(&mut sess.lint_store.borrow_mut(), Some(&sess));
let krate_config = Vec::new();
- let input = config::Input::Str(source_string.to_string());
+ let input = config::Input::Str {
+ name: driver::anon_src(),
+ input: source_string.to_string(),
+ };
let krate = driver::phase_1_parse_input(&sess, krate_config, &input).unwrap();
let krate = driver::phase_2_configure_and_expand(&sess, &cstore, krate, "test", None)
.expect("phase 2 aborted");
lang_items,
index,
|tcx| {
- let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, None);
+ let infcx = infer::new_infer_ctxt(tcx,
+ &tcx.tables,
+ None,
+ ProjectionMode::AnyFinal);
body(Env { infcx: &infcx });
let free_regions = FreeRegionMap::new();
infcx.resolve_regions_and_report_errors(&free_regions,
name: folder.fold_name(i.name),
attrs: fold_attrs(i.attrs, folder),
vis: i.vis,
+ defaultness: i.defaultness,
node: match i.node {
ImplItemKind::Const(ty, expr) => {
ImplItemKind::Const(folder.fold_ty(ty), folder.fold_expr(expr))
pub explicit_self: ExplicitSelf,
}
-/// Represents a method declaration in a trait declaration, possibly including
-/// a default implementation A trait method is either required (meaning it
-/// doesn't have an implementation, just a signature) or provided (meaning it
-/// has a default implementation).
+/// Represents an item declaration within a trait declaration,
+/// possibly including a default implementation. A trait item is
+/// either required (meaning it doesn't have an implementation, just a
+/// signature) or provided (meaning it has a default implementation).
#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
pub struct TraitItem {
pub id: NodeId,
pub id: NodeId,
pub name: Name,
pub vis: Visibility,
+ pub defaultness: Defaultness,
pub attrs: HirVec<Attribute>,
pub node: ImplItemKind,
pub span: Span,
NotConst,
}
+#[derive(Copy, Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
+pub enum Defaultness {
+ Default,
+ Final,
+}
+
+impl Defaultness {
+ pub fn is_final(&self) -> bool {
+ *self == Defaultness::Final
+ }
+
+ pub fn is_default(&self) -> bool {
+ *self == Defaultness::Default
+ }
+}
+
impl fmt::Display for Unsafety {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Display::fmt(match *self {
name: i.ident.name,
attrs: lower_attrs(lctx, &i.attrs),
vis: lower_visibility(lctx, i.vis),
+ defaultness: lower_defaultness(lctx, i.defaultness),
node: match i.node {
ImplItemKind::Const(ref ty, ref expr) => {
hir::ImplItemKind::Const(lower_ty(lctx, ty), lower_expr(lctx, expr))
}
}
+pub fn lower_defaultness(_lctx: &LoweringContext, d: Defaultness) -> hir::Defaultness {
+ match d {
+ Defaultness::Default => hir::Defaultness::Default,
+ Defaultness::Final => hir::Defaultness::Final,
+ }
+}
+
pub fn lower_block_check_mode(lctx: &LoweringContext, b: &BlockCheckMode) -> hir::BlockCheckMode {
match *b {
BlockCheckMode::Default => hir::DefaultBlock,
try!(self.hardbreak_if_not_bol());
try!(self.maybe_print_comment(ii.span.lo));
try!(self.print_outer_attributes(&ii.attrs));
+
+ if let hir::Defaultness::Default = ii.defaultness {
+ try!(self.word_nbsp("default"));
+ }
+
match ii.node {
hir::ImplItemKind::Const(ref ty, ref expr) => {
try!(self.print_associated_const(ii.name, &ty, Some(&expr), ii.vis));
use middle::subst::Substs;
use middle::ty::{self, Ty, TyCtxt};
use middle::ty::adjustment;
+use middle::traits::ProjectionMode;
use rustc::front::map as hir_map;
use util::nodemap::{NodeSet};
use lint::{Level, LateContext, LintContext, LintArray, Lint};
let node_id = tcx.map.as_local_node_id(method.def_id).unwrap();
let param_env = ty::ParameterEnvironment::for_item(tcx, node_id);
- let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, Some(param_env));
+ let infcx = infer::new_infer_ctxt(tcx,
+ &tcx.tables,
+ Some(param_env),
+ ProjectionMode::AnyFinal);
let mut selcx = traits::SelectionContext::new(&infcx);
match selcx.select(&obligation) {
// The method comes from a `T: Trait` bound.
else { false }
} else {
match eval_const_expr_partial(cx.tcx, &r, ExprTypeChecked, None) {
- Ok(ConstVal::Int(shift)) => { shift as u64 >= bits },
- Ok(ConstVal::Uint(shift)) => { shift >= bits },
+ Ok(ConstVal::Integral(i)) => {
+ i.is_negative() || i.to_u64()
+ .map(|i| i >= bits)
+ .unwrap_or(true)
+ },
_ => { false }
}
};
// the host platform. This only really works if the host LLVM and target
// LLVM are compiled the same way, but for us that's typically the case.
//
- // We detect this cross compiling situation by asking llvm-config what it's
- // host-target is. If that's not the TARGET, then we're cross compiling.
- // This generally just means that we can't trust all the output of
- // llvm-config becaues it might be targeted for the host rather than the
- // target.
+ // We *want* detect this cross compiling situation by asking llvm-config
+ // what it's host-target is. If that's not the TARGET, then we're cross
+ // compiling. Unfortunately `llvm-config` seems either be buggy, or we're
+ // misconfiguring it, because the `i686-pc-windows-gnu` build of LLVM will
+ // report itself with a `--host-target` of `x86_64-pc-windows-gnu`. This
+ // tricks us into thinking we're doing a cross build when we aren't, so
+ // havoc ensues.
+ //
+ // In any case, if we're cross compiling, this generally just means that we
+ // can't trust all the output of llvm-config becaues it might be targeted
+ // for the host rather than the target. As a result a bunch of blocks below
+ // are gated on `if !is_crossed`
let target = env::var("TARGET").unwrap();
- let host = output(Command::new(&llvm_config).arg("--host-target"));
- let host = host.trim();
+ let host = env::var("HOST").unwrap();
let is_crossed = target != host;
let optional_components = ["x86", "arm", "aarch64", "mips", "powerpc",
rustc = { path = "../librustc" }
rustc_back = { path = "../librustc_back" }
rustc_bitflags = { path = "../librustc_bitflags" }
+rustc_const_eval = { path = "../librustc_const_eval" }
rustc_front = { path = "../librustc_front" }
rustc_llvm = { path = "../librustc_llvm" }
serialize = { path = "../libserialize" }
pub const tag_items_data_item_deprecation: usize = 0xa7;
+pub const tag_items_data_item_defaultness: usize = 0xa8;
+
+pub const tag_items_data_parent_impl: usize = 0xa9;
+
pub const tag_rustc_version: usize = 0x10f;
pub fn rustc_version() -> String {
format!(
decoder::get_associated_consts(self.intr.clone(), &cdata, def.index, tcx)
}
+ fn impl_parent(&self, impl_def: DefId) -> Option<DefId> {
+ let cdata = self.get_crate_data(impl_def.krate);
+ decoder::get_parent_impl(&*cdata, impl_def.index)
+ }
+
fn trait_of_item(&self, tcx: &TyCtxt<'tcx>, def_id: DefId) -> Option<DefId>
{
let cdata = self.get_crate_data(def_id.krate);
use middle::ty::{ImplContainer, TraitContainer};
use middle::ty::{self, Ty, TyCtxt, TypeFoldable, VariantKind};
+use rustc_const_eval::ConstInt;
+
use rustc::mir;
use rustc::mir::visit::MutVisitor;
}
}
+fn item_defaultness(item: rbml::Doc) -> hir::Defaultness {
+ match reader::maybe_get_doc(item, tag_items_data_item_defaultness) {
+ None => hir::Defaultness::Default, // should occur only for default impls on traits
+ Some(defaultness_doc) => {
+ match reader::doc_as_u8(defaultness_doc) as char {
+ 'd' => hir::Defaultness::Default,
+ 'f' => hir::Defaultness::Final,
+ _ => panic!("unknown defaultness character")
+ }
+ }
+ }
+}
+
fn item_sort(item: rbml::Doc) -> Option<char> {
reader::tagged_docs(item, tag_item_trait_item_sort).nth(0).map(|doc| {
doc.as_str_slice().as_bytes()[0] as char
reader::tagged_docs(d, tag_items_data_item_reexport)
}
-fn variant_disr_val(d: rbml::Doc) -> Option<ty::Disr> {
+fn variant_disr_val(d: rbml::Doc) -> Option<u64> {
reader::maybe_get_doc(d, tag_disr_val).and_then(|val_doc| {
reader::with_doc_data(val_doc, |data| {
str::from_utf8(data).ok().and_then(|s| s.parse().ok())
did: did,
name: item_name(intr, item),
fields: get_variant_fields(intr, cdata, item, tcx),
- disr_val: disr,
+ disr_val: ConstInt::Infer(disr),
kind: expect_variant_kind(item_family(item), tcx),
}
}).collect()
did: did,
name: item_name(intr, doc),
fields: get_variant_fields(intr, cdata, doc, tcx),
- disr_val: 0,
+ disr_val: ConstInt::Infer(0),
kind: expect_variant_kind(item_family(doc), tcx),
}
}
item_visibility(cdata.lookup_item(id))
}
+pub fn get_parent_impl(cdata: Cmd, id: DefIndex) -> Option<DefId> {
+ let item = cdata.lookup_item(id);
+ reader::maybe_get_doc(item, tag_items_data_parent_impl).map(|doc| {
+ translated_def_id(cdata, doc)
+ })
+}
+
pub fn get_repr_attrs(cdata: Cmd, id: DefIndex) -> Vec<attr::ReprAttr> {
let item = cdata.lookup_item(id);
match reader::maybe_get_doc(item, tag_items_data_item_repr).map(|doc| {
let name = item_name(&intr, item_doc);
let vis = item_visibility(item_doc);
+ let defaultness = item_defaultness(item_doc);
match item_sort(item_doc) {
sort @ Some('C') | sort @ Some('c') => {
name: name,
ty: ty,
vis: vis,
+ defaultness: defaultness,
def_id: def_id,
container: container,
has_value: sort == Some('C')
fty,
explicit_self,
vis,
+ defaultness,
def_id,
container)))
}
name: name,
ty: ty,
vis: vis,
+ defaultness: defaultness,
def_id: def_id,
container: container,
}))
use middle::dependency_format::Linkage;
use middle::stability;
use middle::subst;
+use middle::traits::specialization_graph;
use middle::ty::{self, Ty, TyCtxt};
+use middle::ty::util::IntTypeExt;
use rustc::back::svh::Svh;
use rustc::front::map::{LinkedPath, PathElem, PathElems};
fn encode_disr_val(_: &EncodeContext,
rbml_w: &mut Encoder,
disr_val: ty::Disr) {
- rbml_w.wr_tagged_str(tag_disr_val, &disr_val.to_string());
+ // convert to u64 so just the number is printed, without any type info
+ rbml_w.wr_tagged_str(tag_disr_val, &disr_val.to_u64_unchecked().to_string());
}
fn encode_parent_item(rbml_w: &mut Encoder, id: DefId) {
fn encode_enum_variant_info<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>,
rbml_w: &mut Encoder,
- id: NodeId,
+ did: DefId,
vis: hir::Visibility,
index: &mut CrateIndex<'tcx>) {
- debug!("encode_enum_variant_info(id={})", id);
-
- let mut disr_val = 0;
- let def = ecx.tcx.lookup_adt_def(ecx.tcx.map.local_def_id(id));
+ debug!("encode_enum_variant_info(did={:?})", did);
+ let repr_hints = ecx.tcx.lookup_repr_hints(did);
+ let repr_type = ecx.tcx.enum_repr_type(repr_hints.get(0));
+ let mut disr_val = repr_type.initial_discriminant(&ecx.tcx);
+ let def = ecx.tcx.lookup_adt_def(did);
for variant in &def.variants {
let vid = variant.did;
let variant_node_id = ecx.local_id(vid);
ty::VariantKind::Unit => 'w',
});
encode_name(rbml_w, variant.name);
- encode_parent_item(rbml_w, ecx.tcx.map.local_def_id(id));
+ encode_parent_item(rbml_w, did);
encode_visibility(rbml_w, vis);
let attrs = ecx.tcx.get_attrs(vid);
ecx.tcx.map.with_path(variant_node_id, |path| encode_path(rbml_w, path));
rbml_w.end_tag();
- disr_val = disr_val.wrapping_add(1);
+ disr_val = disr_val.wrap_incr();
}
}
rbml_w.end_tag();
}
+fn encode_defaultness(rbml_w: &mut Encoder, defaultness: hir::Defaultness) {
+ let ch = match defaultness {
+ hir::Defaultness::Default => 'd',
+ hir::Defaultness::Final => 'f',
+ };
+ rbml_w.wr_tagged_u8(tag_items_data_item_defaultness, ch as u8);
+}
+
fn encode_explicit_self(rbml_w: &mut Encoder,
explicit_self: &ty::ExplicitSelfCategory) {
let tag = tag_item_trait_method_explicit_self;
if let Some(ii) = impl_item_opt {
encode_attributes(rbml_w, &ii.attrs);
+ encode_defaultness(rbml_w, ii.defaultness);
encode_inlined_item(ecx,
rbml_w,
InlinedItemRef::ImplItem(ecx.tcx.map.local_def_id(parent_id),
impl_item));
}
encode_constness(rbml_w, sig.constness);
+ encode_defaultness(rbml_w, impl_item.defaultness);
if !any_types {
let m_id = ecx.local_id(m.def_id);
encode_symbol(ecx, rbml_w, m_id);
if let Some(ii) = impl_item_opt {
encode_attributes(rbml_w, &ii.attrs);
+ encode_defaultness(rbml_w, ii.defaultness);
} else {
encode_predicates(rbml_w, ecx, index,
&ecx.tcx.lookup_predicates(associated_type.def_id),
});
}
+fn encode_parent_impl(rbml_w: &mut Encoder, parent_opt: Option<DefId>) {
+ parent_opt.map(|parent| {
+ rbml_w.wr_tagged_u64(tag_items_data_parent_impl, def_to_u64(parent));
+ });
+}
+
fn encode_xrefs<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>,
rbml_w: &mut Encoder,
xrefs: FnvHashMap<XRef<'tcx>, u32>)
encode_enum_variant_info(ecx,
rbml_w,
- item.id,
+ def_id,
vis,
index);
}
}
rbml_w.end_tag();
}
- if let Some(trait_ref) = tcx.impl_trait_ref(ecx.tcx.map.local_def_id(item.id)) {
+ let did = ecx.tcx.map.local_def_id(item.id);
+ if let Some(trait_ref) = tcx.impl_trait_ref(did) {
encode_trait_ref(rbml_w, ecx, trait_ref, tag_item_trait_ref);
+
+ let trait_def = tcx.lookup_trait_def(trait_ref.def_id);
+ let parent = trait_def.ancestors(did)
+ .skip(1)
+ .next()
+ .and_then(|node| match node {
+ specialization_graph::Node::Impl(parent) => Some(parent),
+ _ => None,
+ });
+ encode_parent_impl(rbml_w, parent);
}
encode_path(rbml_w, path.clone());
encode_stability(rbml_w, stab);
extern crate rustc_back;
extern crate rustc_front;
extern crate rustc_llvm;
+extern crate rustc_const_eval;
pub use rustc::middle;
log = { path = "../liblog" }
rustc = { path = "../librustc" }
rustc_back = { path = "../librustc_back" }
+rustc_const_eval = { path = "../librustc_const_eval" }
rustc_data_structures = { path = "../librustc_data_structures" }
rustc_front = { path = "../librustc_front" }
syntax = { path = "../libsyntax" }
// test length of the slice is equal to len
Len {
- len: usize,
+ len: u64,
op: BinOp,
},
}
};
Test {
span: match_pair.pattern.span,
- kind: TestKind::Len { len: len, op: op },
+ kind: TestKind::Len { len: len as u64, op: op },
}
}
Operand::Constant(constant)
}
- pub fn push_usize(&mut self, block: BasicBlock, span: Span, value: usize) -> Lvalue<'tcx> {
+ pub fn push_usize(&mut self, block: BasicBlock, span: Span, value: u64) -> Lvalue<'tcx> {
let usize_ty = self.hir.usize_ty();
let temp = self.temp(usize_ty);
self.cfg.push_assign_constant(
use rustc::mir::repr::*;
use syntax::codemap::{Span, DUMMY_SP};
use syntax::parse::token::intern_and_get_ident;
+use rustc::middle::const_eval::ConstVal;
+use rustc_const_eval::ConstInt;
pub struct Scope<'tcx> {
extent: CodeExtent,
}, Constant {
span: span,
ty: self.hir.tcx().types.u32,
- literal: self.hir.usize_literal(span_lines.line)
+ literal: Literal::Value {
+ value: ConstVal::Integral(ConstInt::U32(span_lines.line as u32)),
+ },
})
}
use hair::*;
use rustc_data_structures::fnv::FnvHashMap;
+use rustc_const_eval::ConstInt;
use hair::cx::Cx;
use hair::cx::block;
use hair::cx::to_ref::ToRef;
use rustc::front::map;
use rustc::middle::def::Def;
-use rustc::middle::const_eval;
+use rustc::middle::const_eval::{self, ConstVal};
use rustc::middle::region::CodeExtent;
use rustc::middle::pat_util;
use rustc::middle::ty::{self, VariantDef, Ty};
}
}
- hir::ExprUnary(op, ref arg) => {
+ hir::ExprUnary(hir::UnOp::UnNot, ref arg) => {
if cx.tcx.is_method_call(self.id) {
overloaded_operator(cx, self, ty::MethodCall::expr(self.id),
PassArgs::ByValue, arg.to_ref(), vec![])
} else {
- // FIXME overflow
- let op = match op {
- hir::UnOp::UnNot => UnOp::Not,
- hir::UnOp::UnNeg => UnOp::Neg,
- hir::UnOp::UnDeref => {
- cx.tcx.sess.span_bug(
- self.span,
- "UnDeref should have been handled elsewhere");
- }
- };
ExprKind::Unary {
- op: op,
+ op: UnOp::Not,
arg: arg.to_ref(),
}
}
}
+ hir::ExprUnary(hir::UnOp::UnNeg, ref arg) => {
+ if cx.tcx.is_method_call(self.id) {
+ overloaded_operator(cx, self, ty::MethodCall::expr(self.id),
+ PassArgs::ByValue, arg.to_ref(), vec![])
+ } else {
+ // FIXME runtime-overflow
+ if let hir::ExprLit(_) = arg.node {
+ ExprKind::Literal {
+ literal: cx.const_eval_literal(self),
+ }
+ } else {
+ ExprKind::Unary {
+ op: UnOp::Neg,
+ arg: arg.to_ref(),
+ }
+ }
+ }
+ }
+
hir::ExprStruct(_, ref fields, ref base) => {
match expr_ty.sty {
ty::TyStruct(adt, substs) => {
count: TypedConstVal {
ty: cx.tcx.expr_ty(c),
span: c.span,
- value: const_eval::eval_const_expr(cx.tcx, c)
+ value: match const_eval::eval_const_expr(cx.tcx, c) {
+ ConstVal::Integral(ConstInt::Usize(u)) => u,
+ other => panic!("constant evaluation of repeat count yielded {:?}", other),
+ },
}
},
hir::ExprRet(ref v) =>
use syntax::codemap::Span;
use syntax::parse::token;
use rustc_front::hir;
+use rustc_const_eval::{ConstInt, ConstUsize};
#[derive(Copy, Clone)]
pub struct Cx<'a, 'tcx: 'a> {
self.tcx.types.usize
}
- pub fn usize_literal(&mut self, value: usize) -> Literal<'tcx> {
- Literal::Value { value: ConstVal::Uint(value as u64) }
+ pub fn usize_literal(&mut self, value: u64) -> Literal<'tcx> {
+ match ConstUsize::new(value, self.tcx.sess.target.uint_type) {
+ Ok(val) => Literal::Value { value: ConstVal::Integral(ConstInt::Usize(val))},
+ Err(_) => panic!("usize literal out of range for target"),
+ }
}
pub fn bool_ty(&mut self) -> Ty<'tcx> {
Def::Const(def_id) | Def::AssociatedConst(def_id) =>
match const_eval::lookup_const_by_id(self.cx.tcx, def_id,
Some(pat.id), None) {
- Some(const_expr) => {
+ Some((const_expr, _const_ty)) => {
let pat = const_eval::const_expr_to_pat(self.cx.tcx, const_expr,
pat.span);
return self.to_pattern(&pat);
extern crate rustc_front;
extern crate rustc_back;
extern crate syntax;
+extern crate rustc_const_eval;
pub mod build;
pub mod graphviz;
use rustc::mir::mir_map::MirMap;
use rustc::middle::infer;
use rustc::middle::region::CodeExtentData;
+use rustc::middle::traits::ProjectionMode;
use rustc::middle::ty::{self, Ty, TyCtxt};
use rustc::util::common::ErrorReported;
use rustc::util::nodemap::NodeMap;
};
let param_env = ty::ParameterEnvironment::for_item(self.tcx, id);
- let infcx = infer::new_infer_ctxt(self.tcx, &self.tcx.tables, Some(param_env));
+ let infcx = infer::new_infer_ctxt(self.tcx,
+ &self.tcx.tables,
+ Some(param_env),
+ ProjectionMode::AnyFinal);
+
match build_mir(Cx::new(&infcx), implicit_arg_tys, id, span, decl, body) {
Ok(mir) => assert!(self.map.map.insert(id, mir).is_none()),
Err(ErrorReported) => {}
use rustc::dep_graph::DepNode;
use rustc::middle::infer::{self, InferCtxt};
-use rustc::middle::traits;
+use rustc::middle::traits::{self, ProjectionMode};
use rustc::middle::ty::fold::TypeFoldable;
use rustc::middle::ty::{self, Ty, TyCtxt};
use rustc::mir::repr::*;
}
let _task = tcx.dep_graph.in_task(DepNode::MirTypeck(id));
let param_env = ty::ParameterEnvironment::for_item(tcx, id);
- let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, Some(param_env));
+ let infcx = infer::new_infer_ctxt(tcx,
+ &tcx.tables,
+ Some(param_env),
+ ProjectionMode::AnyFinal);
let mut checker = TypeChecker::new(&infcx);
{
let mut verifier = TypeVerifier::new(&mut checker, mir);
use rustc::middle::infer;
use rustc::middle::mem_categorization as mc;
use rustc::middle::mem_categorization::Categorization;
-use rustc::middle::traits;
use rustc::middle::ty::{self, Ty, TyCtxt};
+use rustc::middle::traits::{self, ProjectionMode};
use rustc::util::nodemap::NodeMap;
use rustc::middle::const_qualif::ConstQualif;
use rustc::lint::builtin::CONST_ERR;
None => self.tcx.empty_parameter_environment()
};
- let infcx = infer::new_infer_ctxt(self.tcx, &self.tcx.tables, Some(param_env));
+ let infcx = infer::new_infer_ctxt(self.tcx,
+ &self.tcx.tables,
+ Some(param_env),
+ ProjectionMode::AnyFinal);
f(&mut euv::ExprUseVisitor::new(self, &infcx))
}
fn check_static_type(&self, e: &hir::Expr) {
let ty = self.tcx.node_id_to_type(e.id);
- let infcx = infer::new_infer_ctxt(self.tcx, &self.tcx.tables, None);
+ let infcx = infer::new_infer_ctxt(self.tcx,
+ &self.tcx.tables,
+ None,
+ ProjectionMode::AnyFinal);
let cause = traits::ObligationCause::new(e.span, e.id, traits::SharedStatic);
let mut fulfillment_cx = traits::FulfillmentContext::new();
fulfillment_cx.register_builtin_bound(&infcx, ty, ty::BoundSync, cause);
}
Some(Def::Const(did)) |
Some(Def::AssociatedConst(did)) => {
- if let Some(expr) = const_eval::lookup_const_by_id(v.tcx, did,
+ if let Some((expr, _ty)) = const_eval::lookup_const_by_id(v.tcx, did,
Some(e.id),
None) {
let inner = v.global_expr(Mode::Const, expr);
use rustc::middle::infer;
use rustc::middle::mem_categorization as mc;
use rustc::middle::ty::{self, TyCtxt, ParameterEnvironment};
+use rustc::middle::traits::ProjectionMode;
use rustc_front::hir;
use rustc_front::intravisit;
let param_env = ParameterEnvironment::for_item(self.tcx, fn_id);
let infcx = infer::new_infer_ctxt(self.tcx,
&self.tcx.tables,
- Some(param_env.clone()));
+ Some(param_env.clone()),
+ ProjectionMode::AnyFinal);
let mut delegate = RvalueContextDelegate { tcx: self.tcx, param_env: ¶m_env };
let mut euv = euv::ExprUseVisitor::new(&mut delegate, &infcx);
euv.walk_fn(fd, b);
output: v(f(64), 4),
definition: Named("llvm.x86.avx.addsub.pd.256")
},
+ "256_blendv_ps" => Intrinsic {
+ inputs: vec![v(f(32), 8), v(f(32), 8), v(f(32), 8)],
+ output: v(f(32), 8),
+ definition: Named("llvm.x86.avx.blendv.ps.256")
+ },
+ "256_blendv_pd" => Intrinsic {
+ inputs: vec![v(f(64), 4), v(f(64), 4), v(f(64), 4)],
+ output: v(f(64), 4),
+ definition: Named("llvm.x86.avx.blendv.pd.256")
+ },
"256_broadcast_ps" => Intrinsic {
inputs: vec![p(true, i(8), None)],
output: v(f(32), 8),
output: v(f(64), 4),
definition: Named("llvm.x86.avx.vbroadcastf128.pd.256")
},
+ "256_cmp_ps" => Intrinsic {
+ inputs: vec![v(f(32), 8), v(f(32), 8), i(8)],
+ output: v(f(32), 8),
+ definition: Named("llvm.x86.avx.cmp.ps.256")
+ },
+ "256_cmp_pd" => Intrinsic {
+ inputs: vec![v(f(64), 4), v(f(64), 4), i(8)],
+ output: v(f(64), 4),
+ definition: Named("llvm.x86.avx.cmp.pd.256")
+ },
"256_cvtepi32_pd" => Intrinsic {
inputs: vec![v(i(32), 4)],
output: v(f(64), 4),
log = { path = "../liblog" }
rustc = { path = "../librustc" }
rustc_back = { path = "../librustc_back" }
+rustc_const_eval = { path = "../librustc_const_eval" }
rustc_data_structures = { path = "../librustc_data_structures" }
rustc_front = { path = "../librustc_front" }
rustc_llvm = { path = "../librustc_llvm" }
extern crate rustc_mir;
extern crate rustc_platform_intrinsics as intrinsics;
extern crate serialize;
+extern crate rustc_const_eval;
#[macro_use] extern crate log;
#[macro_use] extern crate syntax;
use trans::type_of;
use trans::Disr;
use middle::ty::{self, Ty, TyCtxt};
+use middle::traits::ProjectionMode;
use session::config::NoDebugInfo;
use util::common::indenter;
use util::nodemap::FnvHashMap;
reassigned: false
};
{
- let infcx = infer::normalizing_infer_ctxt(bcx.tcx(), &bcx.tcx().tables);
+ let infcx = infer::normalizing_infer_ctxt(bcx.tcx(),
+ &bcx.tcx().tables,
+ ProjectionMode::Any);
let mut visitor = euv::ExprUseVisitor::new(&mut rc, &infcx);
visitor.walk_expr(body);
}
match ity {
attr::UnsignedInt(_) => {
assert!(min <= discr);
- assert!(discr <= max)
+ assert!(discr <= max);
},
attr::SignedInt(_) => {
assert!(min.0 as i64 <= discr.0 as i64);
use llvm::{self, ValueRef, AttrHelper};
use middle::ty;
use middle::infer;
+use middle::traits::ProjectionMode;
use session::config::NoDebugInfo;
use syntax::abi::Abi;
pub use syntax::attr::InlineAttr;
let (fn_sig, abi, env_ty) = match fn_type.sty {
ty::TyFnDef(_, _, ref f) | ty::TyFnPtr(ref f) => (&f.sig, f.abi, None),
ty::TyClosure(closure_did, ref substs) => {
- let infcx = infer::normalizing_infer_ctxt(ccx.tcx(), &ccx.tcx().tables);
+ let infcx = infer::normalizing_infer_ctxt(ccx.tcx(),
+ &ccx.tcx().tables,
+ ProjectionMode::Any);
function_type = infcx.closure_type(closure_did, substs);
let self_type = base::self_type_for_closure(ccx, closure_did, fn_type);
(&function_type.sig, Abi::RustCall, Some(self_type))
use llvm::{ValueRef, get_params};
use middle::def_id::DefId;
use middle::infer;
+use middle::traits::ProjectionMode;
use trans::adt;
use trans::attributes;
use trans::base::*;
// this function (`trans_closure`) is invoked at the point
// of the closure expression.
- let infcx = infer::normalizing_infer_ctxt(ccx.tcx(), &ccx.tcx().tables);
+ let infcx = infer::normalizing_infer_ctxt(ccx.tcx(), &ccx.tcx().tables, ProjectionMode::Any);
let function_type = infcx.closure_type(closure_def_id, closure_substs);
let freevars: Vec<ty::Freevar> =
ccx.tn().val_to_string(llreffn));
let tcx = ccx.tcx();
- let infcx = infer::normalizing_infer_ctxt(ccx.tcx(), &ccx.tcx().tables);
+ let infcx = infer::normalizing_infer_ctxt(ccx.tcx(), &ccx.tcx().tables, ProjectionMode::Any);
// Find a version of the closure type. Substitute static for the
// region since it doesn't really matter.
nested: _ }) =>
{
let callee_substs = impl_substs.with_method_from(&rcvr_substs);
- let impl_method = tcx.get_impl_method(impl_did,
- tcx.mk_substs(callee_substs),
- trait_method.name);
- Some((impl_method.method.def_id, impl_method.substs))
+ let impl_method = meth::get_impl_method(tcx,
+ impl_did,
+ tcx.mk_substs(callee_substs),
+ trait_method.name);
+ Some((impl_method.method.def_id, &impl_method.substs))
}
// If we have a closure or a function pointer, we will also encounter
// the concrete closure/function somewhere else (during closure or fn
if can_have_local_instance(ccx, impl_method.method.def_id) {
Some(create_fn_trans_item(ccx,
impl_method.method.def_id,
- impl_method.substs,
+ &impl_method.substs,
&Substs::trans_empty()))
} else {
None
// The substitutions we have are on the impl, so we grab
// the method type from the impl to substitute into.
- let mth = tcx.get_impl_method(impl_def_id,
- callee_substs,
- default_impl.name);
+ let mth = meth::get_impl_method(tcx,
+ impl_def_id,
+ callee_substs,
+ default_impl.name);
assert!(mth.is_provided);
- let predicates = mth.method.predicates.predicates.subst(tcx, mth.substs);
+ let predicates = mth.method.predicates.predicates.subst(tcx, &mth.substs);
if !normalize_and_test_predicates(ccx, predicates.into_vec()) {
continue;
}
use trans::monomorphize;
use trans::type_::Type;
use trans::type_of;
-use middle::traits;
use middle::ty::{self, Ty, TyCtxt};
+use middle::traits::{self, SelectionContext, ProjectionMode};
use middle::ty::fold::{TypeFolder, TypeFoldable};
use rustc_front::hir;
use rustc::mir::repr::Mir;
// Do the initial selection for the obligation. This yields the
// shallow result we are looking for -- that is, what specific impl.
- let infcx = infer::normalizing_infer_ctxt(tcx, &tcx.tables);
- let mut selcx = traits::SelectionContext::new(&infcx);
+ let infcx = infer::normalizing_infer_ctxt(tcx, &tcx.tables, ProjectionMode::Any);
+ let mut selcx = SelectionContext::new(&infcx);
let obligation =
traits::Obligation::new(traits::ObligationCause::misc(span, ast::DUMMY_NODE_ID),
predicates);
let tcx = ccx.tcx();
- let infcx = infer::normalizing_infer_ctxt(tcx, &tcx.tables);
- let mut selcx = traits::SelectionContext::new(&infcx);
+ let infcx = infer::normalizing_infer_ctxt(tcx, &tcx.tables, ProjectionMode::Any);
+ let mut selcx = SelectionContext::new(&infcx);
let mut fulfill_cx = traits::FulfillmentContext::new();
let cause = traits::ObligationCause::dummy();
let traits::Normalized { value: predicates, obligations } =
use llvm::{InternalLinkage, ValueRef, Bool, True};
use middle::const_qualif::ConstQualif;
use middle::cstore::LOCAL_CRATE;
-use middle::const_eval::{self, ConstVal, ConstEvalErr};
-use middle::const_eval::{const_int_checked_neg, const_uint_checked_neg};
-use middle::const_eval::{const_int_checked_add, const_uint_checked_add};
-use middle::const_eval::{const_int_checked_sub, const_uint_checked_sub};
-use middle::const_eval::{const_int_checked_mul, const_uint_checked_mul};
-use middle::const_eval::{const_int_checked_div, const_uint_checked_div};
-use middle::const_eval::{const_int_checked_rem, const_uint_checked_rem};
-use middle::const_eval::{const_int_checked_shl, const_uint_checked_shl};
-use middle::const_eval::{const_int_checked_shr, const_uint_checked_shr};
+use middle::const_eval::{self, ConstEvalErr};
use middle::def::Def;
use middle::def_id::DefId;
use trans::{adt, closure, debuginfo, expr, inline, machine};
use middle::subst::Substs;
use middle::ty::adjustment::{AdjustDerefRef, AdjustReifyFnPointer};
use middle::ty::adjustment::{AdjustUnsafeFnPointer, AdjustMutToConstPointer};
-use middle::ty::{self, Ty};
+use middle::ty::{self, Ty, TyCtxt};
use middle::ty::cast::{CastTy,IntTy};
use util::nodemap::NodeMap;
+use rustc_const_eval::{ConstInt, ConstMathErr, ConstUsize, ConstIsize};
use rustc_front::hir;
}
match const_eval::lookup_const_by_id(ccx.tcx(), def_id, Some(ref_expr.id), Some(param_substs)) {
- Some(ref expr) => expr,
+ Some((ref expr, _ty)) => expr,
None => {
ccx.sess().span_bug(ref_expr.span, "constant item not found")
}
// Catch this up front by looking for ExprLit directly,
// and just accepting it.
if let hir::ExprLit(_) = inner_e.node { return Ok(()); }
-
- let result = match t.sty {
- ty::TyInt(int_type) => {
- let input = match const_to_opt_int(te) {
- Some(v) => v,
- None => return Ok(()),
- };
- const_int_checked_neg(
- input, e, Some(const_eval::IntTy::from(cx.tcx(), int_type)))
- }
- ty::TyUint(uint_type) => {
- let input = match const_to_opt_uint(te) {
- Some(v) => v,
- None => return Ok(()),
- };
- const_uint_checked_neg(
- input, e, Some(const_eval::UintTy::from(cx.tcx(), uint_type)))
- }
- _ => return Ok(()),
+ let cval = match to_const_int(te, t, cx.tcx()) {
+ Some(v) => v,
+ None => return Ok(()),
};
- const_err(cx, e, result, trueconst)
+ match -cval {
+ Ok(_) => return Ok(()),
+ Err(err) => const_err(cx, e, Err(err), trueconst),
+ }
} else {
Ok(())
}
}
+fn to_const_int(value: ValueRef, t: Ty, tcx: &TyCtxt) -> Option<ConstInt> {
+ match t.sty {
+ ty::TyInt(int_type) => const_to_opt_int(value).and_then(|input| match int_type {
+ ast::IntTy::I8 => {
+ assert_eq!(input as i8 as i64, input);
+ Some(ConstInt::I8(input as i8))
+ },
+ ast::IntTy::I16 => {
+ assert_eq!(input as i16 as i64, input);
+ Some(ConstInt::I16(input as i16))
+ },
+ ast::IntTy::I32 => {
+ assert_eq!(input as i32 as i64, input);
+ Some(ConstInt::I32(input as i32))
+ },
+ ast::IntTy::I64 => {
+ Some(ConstInt::I64(input))
+ },
+ ast::IntTy::Is => {
+ ConstIsize::new(input, tcx.sess.target.int_type)
+ .ok().map(ConstInt::Isize)
+ },
+ }),
+ ty::TyUint(uint_type) => const_to_opt_uint(value).and_then(|input| match uint_type {
+ ast::UintTy::U8 => {
+ assert_eq!(input as u8 as u64, input);
+ Some(ConstInt::U8(input as u8))
+ },
+ ast::UintTy::U16 => {
+ assert_eq!(input as u16 as u64, input);
+ Some(ConstInt::U16(input as u16))
+ },
+ ast::UintTy::U32 => {
+ assert_eq!(input as u32 as u64, input);
+ Some(ConstInt::U32(input as u32))
+ },
+ ast::UintTy::U64 => {
+ Some(ConstInt::U64(input))
+ },
+ ast::UintTy::Us => {
+ ConstUsize::new(input, tcx.sess.target.uint_type)
+ .ok().map(ConstInt::Usize)
+ },
+ }),
+ _ => None,
+ }
+}
+
fn const_err(cx: &CrateContext,
e: &hir::Expr,
- result: Result<ConstVal, ConstEvalErr>,
+ result: Result<ConstInt, ConstMathErr>,
trueconst: TrueConst)
-> Result<(), ConstEvalFailure> {
match (result, trueconst) {
Ok(())
},
(Err(err), TrueConst::Yes) => {
+ let err = ConstEvalErr{ span: e.span, kind: const_eval::ErrKind::Math(err) };
cx.tcx().sess.span_err(e.span, &err.description());
Err(Compiletime(err))
},
(Err(err), TrueConst::No) => {
+ let err = ConstEvalErr{ span: e.span, kind: const_eval::ErrKind::Math(err) };
cx.tcx().sess.span_warn(e.span, &err.description());
Err(Runtime(err))
},
te1: ValueRef, te2: ValueRef,
trueconst: TrueConst) -> Result<(), ConstEvalFailure> {
let b = if let hir::ExprBinary(b, _, _) = e.node { b } else { unreachable!() };
-
- let result = match t.sty {
- ty::TyInt(int_type) => {
- let (lhs, rhs) = match (const_to_opt_int(te1),
- const_to_opt_int(te2)) {
- (Some(v1), Some(v2)) => (v1, v2),
- _ => return Ok(()),
- };
-
- let opt_ety = Some(const_eval::IntTy::from(cx.tcx(), int_type));
- match b.node {
- hir::BiAdd => const_int_checked_add(lhs, rhs, e, opt_ety),
- hir::BiSub => const_int_checked_sub(lhs, rhs, e, opt_ety),
- hir::BiMul => const_int_checked_mul(lhs, rhs, e, opt_ety),
- hir::BiDiv => const_int_checked_div(lhs, rhs, e, opt_ety),
- hir::BiRem => const_int_checked_rem(lhs, rhs, e, opt_ety),
- hir::BiShl => const_int_checked_shl(lhs, rhs, e, opt_ety),
- hir::BiShr => const_int_checked_shr(lhs, rhs, e, opt_ety),
- _ => return Ok(()),
- }
- }
- ty::TyUint(uint_type) => {
- let (lhs, rhs) = match (const_to_opt_uint(te1),
- const_to_opt_uint(te2)) {
- (Some(v1), Some(v2)) => (v1, v2),
- _ => return Ok(()),
- };
-
- let opt_ety = Some(const_eval::UintTy::from(cx.tcx(), uint_type));
- match b.node {
- hir::BiAdd => const_uint_checked_add(lhs, rhs, e, opt_ety),
- hir::BiSub => const_uint_checked_sub(lhs, rhs, e, opt_ety),
- hir::BiMul => const_uint_checked_mul(lhs, rhs, e, opt_ety),
- hir::BiDiv => const_uint_checked_div(lhs, rhs, e, opt_ety),
- hir::BiRem => const_uint_checked_rem(lhs, rhs, e, opt_ety),
- hir::BiShl => const_uint_checked_shl(lhs, rhs, e, opt_ety),
- hir::BiShr => const_uint_checked_shr(lhs, rhs, e, opt_ety),
- _ => return Ok(()),
- }
- }
+ let (lhs, rhs) = match (to_const_int(te1, t, cx.tcx()), to_const_int(te2, t, cx.tcx())) {
+ (Some(v1), Some(v2)) => (v1, v2),
+ _ => return Ok(()),
+ };
+ let result = match b.node {
+ hir::BiAdd => lhs + rhs,
+ hir::BiSub => lhs - rhs,
+ hir::BiMul => lhs * rhs,
+ hir::BiDiv => lhs / rhs,
+ hir::BiRem => lhs % rhs,
+ hir::BiShl => lhs << rhs,
+ hir::BiShr => lhs >> rhs,
_ => return Ok(()),
};
const_err(cx, e, result, trueconst)
llvm::LLVMDIBuilderCreateEnumerator(
DIB(cx),
name.as_ptr(),
- v.disr_val as u64)
+ v.disr_val.to_u64_unchecked())
}
})
.collect();
use llvm::{self, ValueRef};
use middle::ty;
use middle::infer;
+use middle::traits::ProjectionMode;
use syntax::abi::Abi;
use trans::attributes;
use trans::base;
(&f.sig, f.abi, None)
}
ty::TyClosure(closure_did, ref substs) => {
- let infcx = infer::normalizing_infer_ctxt(ccx.tcx(), &ccx.tcx().tables);
+ let infcx = infer::normalizing_infer_ctxt(ccx.tcx(),
+ &ccx.tcx().tables,
+ ProjectionMode::Any);
function_type = infcx.closure_type(closure_did, substs);
let self_type = base::self_type_for_closure(ccx, closure_did, fn_type);
let llenvironment_type = type_of::type_of_explicit_arg(ccx, self_type);
impl From<::middle::ty::Disr> for Disr {
fn from(i: ::middle::ty::Disr) -> Disr {
- Disr(i)
+ Disr(i.to_u64_unchecked())
}
}
(_, "volatile_store") => {
let tp_ty = *substs.types.get(FnSpace, 0);
let ptr = to_arg_ty_ptr(bcx, llargs[0], tp_ty);
- let val = from_arg_ty(bcx, llargs[1], tp_ty);
+ let val = if type_is_immediate(bcx.ccx(), tp_ty) {
+ from_arg_ty(bcx, llargs[1], tp_ty)
+ } else {
+ Load(bcx, llargs[1])
+ };
let store = VolatileStore(bcx, val, ptr);
unsafe {
llvm::LLVMSetAlignment(store, type_of::align_of(ccx, tp_ty));
// option. This file may not be copied, modified, or distributed
// except according to those terms.
+use std::rc::Rc;
+
use arena::TypedArena;
use back::link;
use llvm::{ValueRef, get_params};
use middle::infer;
use middle::subst::{Subst, Substs};
use middle::subst;
-use middle::traits;
+use middle::traits::{self, ProjectionMode};
use trans::base::*;
use trans::build::*;
use trans::callee::{Callee, Virtual, ArgVals,
use trans::machine;
use trans::type_::Type;
use trans::type_of::*;
-use middle::ty::{self, Ty, TyCtxt};
+use middle::ty::{self, Ty, TyCtxt, TypeFoldable};
-use syntax::ast;
+use syntax::ast::{self, Name};
use syntax::attr;
use syntax::codemap::DUMMY_SP;
// those from the impl and those from the method:
let impl_substs = vtable_impl.substs.with_method_from(&substs);
let substs = ccx.tcx().mk_substs(impl_substs);
- let mth = ccx.tcx().get_impl_method(impl_did, substs, mname);
+ let mth = get_impl_method(ccx.tcx(), impl_did, substs, mname);
// Translate the function, bypassing Callee::def.
// That is because default methods have the same ID as the
trans_fn_ref_with_substs(ccx,
mth.method.def_id,
None,
- mth.substs).val
+ &mth.substs).val
}
None => nullptr
}
pub fn get_vtable_methods<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
impl_id: DefId,
substs: &'tcx subst::Substs<'tcx>)
- -> Vec<Option<ty::util::ImplMethod<'tcx>>>
+ -> Vec<Option<ImplMethod<'tcx>>>
{
let tcx = ccx.tcx();
// The substitutions we have are on the impl, so we grab
// the method type from the impl to substitute into.
- let mth = tcx.get_impl_method(impl_id, substs, name);
+ let mth = get_impl_method(tcx, impl_id, substs, name);
debug!("get_vtable_methods: mth={:?}", mth);
// method could then never be called, so we do not want to
// try and trans it, in that case. Issue #23435.
if mth.is_provided {
- let predicates = mth.method.predicates.predicates.subst(tcx, mth.substs);
+ let predicates = mth.method.predicates.predicates.subst(tcx, &mth.substs);
if !normalize_and_test_predicates(ccx, predicates.into_vec()) {
debug!("get_vtable_methods: predicates do not hold");
return None;
}),
})
}
+
+#[derive(Debug)]
+pub struct ImplMethod<'tcx> {
+ pub method: Rc<ty::Method<'tcx>>,
+ pub substs: &'tcx Substs<'tcx>,
+ pub is_provided: bool
+}
+
+/// Locates the applicable definition of a method, given its name.
+pub fn get_impl_method<'tcx>(tcx: &TyCtxt<'tcx>,
+ impl_def_id: DefId,
+ substs: &'tcx Substs<'tcx>,
+ name: Name)
+ -> ImplMethod<'tcx>
+{
+ assert!(!substs.types.needs_infer());
+
+ let trait_def_id = tcx.trait_id_of_impl(impl_def_id).unwrap();
+ let trait_def = tcx.lookup_trait_def(trait_def_id);
+ let infcx = infer::normalizing_infer_ctxt(tcx, &tcx.tables, ProjectionMode::Any);
+
+ match trait_def.ancestors(impl_def_id).fn_defs(tcx, name).next() {
+ Some(node_item) => {
+ ImplMethod {
+ method: node_item.item,
+ substs: traits::translate_substs(&infcx, impl_def_id, substs, node_item.node),
+ is_provided: node_item.node.is_from_trait(),
+ }
+ }
+ None => {
+ tcx.sess.bug(&format!("method {:?} not found in {:?}", name, impl_def_id))
+ }
+ }
+}
use llvm::ValueRef;
use middle::ty::{Ty, TypeFoldable};
use rustc::middle::const_eval::{self, ConstVal};
+use rustc_const_eval::ConstInt::*;
use rustc::mir::repr as mir;
use trans::common::{self, BlockAndBuilder, C_bool, C_bytes, C_floating_f64, C_integral,
C_str_slice, C_nil, C_undef};
use trans::expr;
use trans::inline;
use trans::type_of;
+use trans::type_::Type;
use super::operand::{OperandRef, OperandValue};
use super::MirContext;
match *cv {
ConstVal::Float(v) => C_floating_f64(v, llty),
ConstVal::Bool(v) => C_bool(ccx, v),
- ConstVal::Int(v) => C_integral(llty, v as u64, true),
- ConstVal::Uint(v) => C_integral(llty, v, false),
+ ConstVal::Integral(I8(v)) => C_integral(Type::i8(ccx), v as u64, true),
+ ConstVal::Integral(I16(v)) => C_integral(Type::i16(ccx), v as u64, true),
+ ConstVal::Integral(I32(v)) => C_integral(Type::i32(ccx), v as u64, true),
+ ConstVal::Integral(I64(v)) => C_integral(Type::i64(ccx), v as u64, true),
+ ConstVal::Integral(Isize(v)) => {
+ let i = v.as_i64(ccx.tcx().sess.target.int_type);
+ C_integral(Type::int(ccx), i as u64, true)
+ },
+ ConstVal::Integral(U8(v)) => C_integral(Type::i8(ccx), v as u64, false),
+ ConstVal::Integral(U16(v)) => C_integral(Type::i16(ccx), v as u64, false),
+ ConstVal::Integral(U32(v)) => C_integral(Type::i32(ccx), v as u64, false),
+ ConstVal::Integral(U64(v)) => C_integral(Type::i64(ccx), v, false),
+ ConstVal::Integral(Usize(v)) => {
+ let u = v.as_u64(ccx.tcx().sess.target.uint_type);
+ C_integral(Type::int(ccx), u, false)
+ },
+ ConstVal::Integral(Infer(v)) => C_integral(llty, v as u64, false),
+ ConstVal::Integral(InferSigned(v)) => C_integral(llty, v as u64, true),
ConstVal::Str(ref v) => C_str_slice(ccx, v.clone()),
ConstVal::ByteStr(ref v) => consts::addr_of(ccx, C_bytes(ccx, v), 1, "byte_str"),
ConstVal::Struct(id) | ConstVal::Tuple(id) |
expr::trans(bcx, expr).datum.val
})
},
+ ConstVal::Char(c) => C_integral(Type::char(ccx), c as u64, false),
+ ConstVal::Dummy => unreachable!(),
ConstVal::Function(_) => C_nil(ccx)
}
}
let substs = bcx.tcx().mk_substs(bcx.monomorphize(&substs));
let def_id = inline::maybe_instantiate_inline(bcx.ccx(), def_id);
let expr = const_eval::lookup_const_by_id(bcx.tcx(), def_id, None, Some(substs))
- .expect("def was const, but lookup_const_by_id failed");
+ .expect("def was const, but lookup_const_by_id failed").0;
// FIXME: this is falling back to translating from HIR. This is not easy to fix,
// because we would have somehow adapt const_eval to work on MIR rather than HIR.
let d = bcx.with_block(|bcx| {
use llvm::ValueRef;
use rustc::middle::ty::{self, Ty};
use middle::ty::cast::{CastTy, IntTy};
+use middle::const_eval::ConstVal;
+use rustc_const_eval::ConstInt;
use rustc::mir::repr as mir;
use trans::asm;
mir::Rvalue::Repeat(ref elem, ref count) => {
let tr_elem = self.trans_operand(&bcx, elem);
- let size = self.trans_constval(&bcx, &count.value, count.ty).immediate();
+ let count = ConstVal::Integral(ConstInt::Usize(count.value));
+ let size = self.trans_constval(&bcx, &count, bcx.tcx().types.usize).immediate();
let bcx = bcx.map_block(|block| {
let base = expr::get_dataptr(block, dest.llval);
tvec::iter_vec_raw(block, base, tr_elem.ty, size, |block, llslot, _| {
fmt_macros = { path = "../libfmt_macros" }
rustc = { path = "../librustc" }
rustc_back = { path = "../librustc_back" }
+rustc_const_eval = { path = "../librustc_const_eval" }
rustc_front = { path = "../librustc_front" }
rustc_platform_intrinsics = { path = "../librustc_platform_intrinsics" }
use util::common::{ErrorReported, FN_OUTPUT_NAME};
use util::nodemap::FnvHashSet;
+use rustc_const_eval::ConstInt;
+
use syntax::{abi, ast};
use syntax::codemap::{Span, Pos};
use syntax::errors::DiagnosticBuilder;
hir::TyFixedLengthVec(ref ty, ref e) => {
let hint = UncheckedExprHint(tcx.types.usize);
match const_eval::eval_const_expr_partial(tcx, &e, hint, None) {
- Ok(r) => {
- match r {
- ConstVal::Int(i) =>
- tcx.mk_array(ast_ty_to_ty(this, rscope, &ty),
- i as usize),
- ConstVal::Uint(i) =>
- tcx.mk_array(ast_ty_to_ty(this, rscope, &ty),
- i as usize),
- _ => {
- span_err!(tcx.sess, ast_ty.span, E0249,
- "expected constant integer expression \
- for array length");
- this.tcx().types.err
- }
- }
- }
+ Ok(ConstVal::Integral(ConstInt::Usize(i))) => {
+ let i = i.as_u64(tcx.sess.target.uint_type);
+ assert_eq!(i as usize as u64, i);
+ tcx.mk_array(ast_ty_to_ty(this, rscope, &ty), i as usize)
+ },
+ Ok(val) => {
+ span_err!(tcx.sess, ast_ty.span, E0249,
+ "expected usize value for array length, got {}", val.description());
+ this.tcx().types.err
+ },
Err(ref r) => {
let mut err = struct_span_err!(tcx.sess, r.span, E0250,
"array length constant evaluation error: {}",
CastToBool,
CastToChar,
DifferingKinds,
+ /// Cast of thin to fat raw ptr (eg. `*const () as *const [u8]`)
+ SizedUnsizedCast,
IllegalCast,
NeedViaPtr,
NeedViaThinPtr,
fcx.infcx().ty_to_string(self.cast_ty))
}, self.expr_ty, None);
}
+ CastError::SizedUnsizedCast => {
+ fcx.type_error_message(self.span, |actual| {
+ format!("cannot cast thin pointer `{}` to fat pointer `{}`",
+ actual,
+ fcx.infcx().ty_to_string(self.cast_ty))
+ }, self.expr_ty, None)
+ }
CastError::DifferingKinds => {
fcx.type_error_struct(self.span, |actual| {
format!("casting `{}` as `{}` is invalid",
// sized -> unsized? report invalid cast (don't complain about vtable kinds)
if fcx.type_is_known_to_be_sized(m_expr.ty, self.span) {
- return Err(CastError::IllegalCast);
+ return Err(CastError::SizedUnsizedCast);
}
// vtable kinds must match
use middle::free_region::FreeRegionMap;
use middle::infer::{self, TypeOrigin};
-use middle::traits;
use middle::ty::{self, TyCtxt};
+use middle::traits::{self, ProjectionMode};
use middle::subst::{self, Subst, Substs, VecPerParamSpace};
use syntax::ast;
debug!("compare_impl_method: impl_trait_ref (liberated) = {:?}",
impl_trait_ref);
- let mut infcx = infer::new_infer_ctxt(tcx, &tcx.tables, None);
+ let mut infcx = infer::new_infer_ctxt(tcx, &tcx.tables, None, ProjectionMode::AnyFinal);
let mut fulfillment_cx = traits::FulfillmentContext::new();
let trait_to_impl_substs = &impl_trait_ref.substs;
debug!("compare_const_impl(impl_trait_ref={:?})",
impl_trait_ref);
- let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, None);
+ let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, None, ProjectionMode::AnyFinal);
let mut fulfillment_cx = traits::FulfillmentContext::new();
// The below is for the most part highly similar to the procedure
use middle::infer;
use middle::region;
use middle::subst::{self, Subst};
-use middle::traits;
use middle::ty::{self, Ty, TyCtxt};
+use middle::traits::{self, ProjectionMode};
use util::nodemap::FnvHashSet;
use syntax::ast;
// check that the impl type can be made to match the trait type.
let impl_param_env = ty::ParameterEnvironment::for_item(tcx, self_type_node_id);
- let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, Some(impl_param_env));
+ let infcx = infer::new_infer_ctxt(tcx,
+ &tcx.tables,
+ Some(impl_param_env),
+ ProjectionMode::AnyFinal);
let mut fulfillment_cx = traits::FulfillmentContext::new();
let named_type = tcx.lookup_item_type(self_type_did).ty;
use middle::infer::{TypeOrigin, TypeTrace, type_variable};
use middle::pat_util::{self, pat_id_map};
use middle::subst::{self, Subst, Substs, VecPerParamSpace, ParamSpace};
-use middle::traits::{self, report_fulfillment_errors};
+use middle::traits::{self, report_fulfillment_errors, ProjectionMode};
use middle::ty::{GenericPredicates, TypeScheme};
-use middle::ty::{Disr, ParamTy, ParameterEnvironment};
+use middle::ty::{ParamTy, ParameterEnvironment};
use middle::ty::{LvaluePreference, NoPreference, PreferMutLvalue};
use middle::ty::{self, ToPolyTraitRef, Ty, TyCtxt};
use middle::ty::{MethodCall, MethodCallee};
use middle::ty::error::TypeError;
use middle::ty::fold::{TypeFolder, TypeFoldable};
use middle::ty::relate::TypeRelation;
-use middle::ty::util::Representability;
+use middle::ty::util::{Representability, IntTypeExt};
use require_c_abi_if_variadic;
use rscope::{ElisionFailureInfo, RegionScope};
use session::{Session, CompileResult};
-> Inherited<'a, 'tcx> {
Inherited {
- infcx: infer::new_infer_ctxt(tcx, tables, Some(param_env)),
+ infcx: infer::new_infer_ctxt(tcx, tables, Some(param_env), ProjectionMode::AnyFinal),
fulfillment_cx: RefCell::new(traits::FulfillmentContext::new()),
locals: RefCell::new(NodeMap()),
tables: tables,
hir::ItemFn(..) => {} // entirely within check_item_body
hir::ItemImpl(_, _, _, _, _, ref impl_items) => {
debug!("ItemImpl {} with id {}", it.name, it.id);
- match ccx.tcx.impl_trait_ref(ccx.tcx.map.local_def_id(it.id)) {
+ let impl_def_id = ccx.tcx.map.local_def_id(it.id);
+ match ccx.tcx.impl_trait_ref(impl_def_id) {
Some(impl_trait_ref) => {
check_impl_items_against_trait(ccx,
it.span,
+ impl_def_id,
&impl_trait_ref,
impl_items);
}
check_bare_fn(ccx, &sig.decl, body, id, span, fty, param_env);
}
+fn report_forbidden_specialization(tcx: &TyCtxt,
+ impl_item: &hir::ImplItem,
+ parent_impl: DefId)
+{
+ let mut err = struct_span_err!(
+ tcx.sess, impl_item.span, E0520,
+ "item `{}` is provided by an `impl` that specializes \
+ another, but the item in the parent `impl` is not \
+ marked `default` and so it cannot be specialized.",
+ impl_item.name);
+
+ match tcx.span_of_impl(parent_impl) {
+ Ok(span) => {
+ err.span_note(span, "parent implementation is here:");
+ }
+ Err(cname) => {
+ err.note(&format!("parent implementation is in crate `{}`", cname));
+ }
+ }
+
+ err.emit();
+}
+
+fn check_specialization_validity<'tcx>(tcx: &TyCtxt<'tcx>, trait_def: &ty::TraitDef<'tcx>,
+ impl_id: DefId, impl_item: &hir::ImplItem)
+{
+ let ancestors = trait_def.ancestors(impl_id);
+
+ let parent = match impl_item.node {
+ hir::ImplItemKind::Const(..) => {
+ ancestors.const_defs(tcx, impl_item.name).skip(1).next()
+ .map(|node_item| node_item.map(|parent| parent.defaultness))
+ }
+ hir::ImplItemKind::Method(..) => {
+ ancestors.fn_defs(tcx, impl_item.name).skip(1).next()
+ .map(|node_item| node_item.map(|parent| parent.defaultness))
+
+ }
+ hir::ImplItemKind::Type(_) => {
+ ancestors.type_defs(tcx, impl_item.name).skip(1).next()
+ .map(|node_item| node_item.map(|parent| parent.defaultness))
+ }
+ };
+
+ if let Some(parent) = parent {
+ if parent.item.is_final() {
+ report_forbidden_specialization(tcx, impl_item, parent.node.def_id());
+ }
+ }
+
+}
+
fn check_impl_items_against_trait<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
impl_span: Span,
+ impl_id: DefId,
impl_trait_ref: &ty::TraitRef<'tcx>,
impl_items: &[hir::ImplItem]) {
- // Locate trait methods
+ // If the trait reference itself is erroneous (so the compilation is going
+ // to fail), skip checking the items here -- the `impl_item` table in `tcx`
+ // isn't populated for such impls.
+ if impl_trait_ref.references_error() { return; }
+
+ // Locate trait definition and items
let tcx = ccx.tcx;
+ let trait_def = tcx.lookup_trait_def(impl_trait_ref.def_id);
let trait_items = tcx.trait_items(impl_trait_ref.def_id);
let mut overridden_associated_type = None;
let ty_trait_item = trait_items.iter()
.find(|ac| ac.name() == ty_impl_item.name());
+ // Check that impl definition matches trait definition
if let Some(ty_trait_item) = ty_trait_item {
match impl_item.node {
hir::ImplItemKind::Const(..) => {
}
}
}
+
+ check_specialization_validity(tcx, trait_def, impl_id, impl_item);
}
// Check for missing items from trait
let mut invalidated_items = Vec::new();
let associated_type_overridden = overridden_associated_type.is_some();
for trait_item in trait_items.iter() {
+ let is_implemented;
+ let is_provided;
+
match *trait_item {
ty::ConstTraitItem(ref associated_const) => {
- let is_implemented = impl_items.iter().any(|ii| {
+ is_provided = associated_const.has_value;
+ is_implemented = impl_items.iter().any(|ii| {
match ii.node {
hir::ImplItemKind::Const(..) => {
ii.name == associated_const.name
_ => false,
}
});
- let is_provided = associated_const.has_value;
-
- if !is_implemented {
- if !is_provided {
- missing_items.push(associated_const.name);
- } else if associated_type_overridden {
- invalidated_items.push(associated_const.name);
- }
- }
}
ty::MethodTraitItem(ref trait_method) => {
- let is_implemented =
- impl_items.iter().any(|ii| {
- match ii.node {
- hir::ImplItemKind::Method(..) => {
- ii.name == trait_method.name
- }
- _ => false,
- }
- });
- let is_provided =
- provided_methods.iter().any(|m| m.name == trait_method.name);
- if !is_implemented {
- if !is_provided {
- missing_items.push(trait_method.name);
- } else if associated_type_overridden {
- invalidated_items.push(trait_method.name);
- }
- }
+ is_provided = provided_methods.iter().any(|m| m.name == trait_method.name);
+ is_implemented = trait_def.ancestors(impl_id)
+ .fn_defs(tcx, trait_method.name)
+ .next()
+ .map(|node_item| !node_item.node.is_from_trait())
+ .unwrap_or(false);
}
- ty::TypeTraitItem(ref associated_type) => {
- let is_implemented = impl_items.iter().any(|ii| {
- match ii.node {
- hir::ImplItemKind::Type(_) => {
- ii.name == associated_type.name
- }
- _ => false,
- }
- });
- let is_provided = associated_type.ty.is_some();
- if !is_implemented {
- if !is_provided {
- missing_items.push(associated_type.name);
- } else if associated_type_overridden {
- invalidated_items.push(associated_type.name);
- }
- }
+ ty::TypeTraitItem(ref trait_assoc_ty) => {
+ is_provided = trait_assoc_ty.ty.is_some();
+ is_implemented = trait_def.ancestors(impl_id)
+ .type_defs(tcx, trait_assoc_ty.name)
+ .next()
+ .map(|node_item| !node_item.node.is_from_trait())
+ .unwrap_or(false);
+ }
+ }
+
+ if !is_implemented {
+ if !is_provided {
+ missing_items.push(trait_item.name());
+ } else if associated_type_overridden {
+ invalidated_items.push(trait_item.name());
}
}
}
sp: Span,
vs: &'tcx [hir::Variant],
id: ast::NodeId) {
- // disr_in_range should be removed once we have forced type hints for consts
- fn disr_in_range(ccx: &CrateCtxt,
- ty: attr::IntType,
- disr: ty::Disr) -> bool {
- fn uint_in_range(ccx: &CrateCtxt, ty: ast::UintTy, disr: ty::Disr) -> bool {
- match ty {
- ast::UintTy::U8 => disr as u8 as Disr == disr,
- ast::UintTy::U16 => disr as u16 as Disr == disr,
- ast::UintTy::U32 => disr as u32 as Disr == disr,
- ast::UintTy::U64 => disr as u64 as Disr == disr,
- ast::UintTy::Us => uint_in_range(ccx, ccx.tcx.sess.target.uint_type, disr)
- }
- }
- fn int_in_range(ccx: &CrateCtxt, ty: ast::IntTy, disr: ty::Disr) -> bool {
- match ty {
- ast::IntTy::I8 => disr as i8 as Disr == disr,
- ast::IntTy::I16 => disr as i16 as Disr == disr,
- ast::IntTy::I32 => disr as i32 as Disr == disr,
- ast::IntTy::I64 => disr as i64 as Disr == disr,
- ast::IntTy::Is => int_in_range(ccx, ccx.tcx.sess.target.int_type, disr)
- }
- }
- match ty {
- attr::UnsignedInt(ty) => uint_in_range(ccx, ty, disr),
- attr::SignedInt(ty) => int_in_range(ccx, ty, disr)
- }
- }
-
fn do_check<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
vs: &'tcx [hir::Variant],
id: ast::NodeId,
let inh = static_inherited_fields(ccx, &tables);
let fcx = blank_fn_ctxt(ccx, &inh, ty::FnConverging(rty), id);
- let (_, repr_type_ty) = ccx.tcx.enum_repr_type(Some(&hint));
+ let repr_type_ty = ccx.tcx.enum_repr_type(Some(&hint)).to_ty(&ccx.tcx);
for v in vs {
if let Some(ref e) = v.node.disr_expr {
check_const_with_ty(&fcx, e.span, e, repr_type_ty);
}
None => {}
}
- // Check for unrepresentable discriminant values
- match hint {
- attr::ReprAny | attr::ReprExtern => {
- disr_vals.push(current_disr_val);
- }
- attr::ReprInt(sp, ity) => {
- if !disr_in_range(ccx, ity, current_disr_val) {
- let mut err = struct_span_err!(ccx.tcx.sess, v.span, E0082,
- "discriminant value outside specified type");
- span_note!(&mut err, sp,
- "discriminant type specified here");
- err.emit();
- }
- }
- // Error reported elsewhere.
- attr::ReprSimd | attr::ReprPacked => {}
- }
+ disr_vals.push(current_disr_val);
}
}
// done by the orphan and overlap modules. Then we build up various
// mappings. That mapping code resides here.
-
use middle::def_id::DefId;
use middle::lang_items::UnsizeTraitLangItem;
use middle::subst::{self, Subst};
-use middle::traits;
use middle::ty::{self, TyCtxt, TypeFoldable};
+use middle::traits::{self, ProjectionMode};
use middle::ty::{ImplOrTraitItemId, ConstTraitItemId};
use middle::ty::{MethodTraitItemId, TypeTraitItemId, ParameterEnvironment};
use middle::ty::{Ty, TyBool, TyChar, TyEnum, TyError};
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_impl(self.crate_context.tcx, impl_def_id, impl_trait_ref);
+ trait_def.record_local_impl(self.crate_context.tcx, impl_def_id, impl_trait_ref);
}
// Converts an implementation in the AST to a vector of items.
debug!("check_implementations_of_coerce_unsized: {:?} -> {:?} (free)",
source, target);
- let infcx = new_infer_ctxt(tcx, &tcx.tables, Some(param_env));
+ let infcx = new_infer_ctxt(tcx, &tcx.tables, Some(param_env), ProjectionMode::Topmost);
let origin = TypeOrigin::Misc(span);
let check_mutbl = |mt_a: ty::TypeAndMut<'tcx>, mt_b: ty::TypeAndMut<'tcx>,
pub fn check_coherence(crate_context: &CrateCtxt) {
let _task = crate_context.tcx.dep_graph.in_task(DepNode::Coherence);
- let infcx = new_infer_ctxt(crate_context.tcx, &crate_context.tcx.tables, None);
+ let infcx = new_infer_ctxt(crate_context.tcx,
+ &crate_context.tcx.tables,
+ None,
+ ProjectionMode::Topmost);
CoherenceChecker {
crate_context: crate_context,
inference_context: infcx,
//! same type. Likewise, no two inherent impls for a given type
//! constructor provide a method with the same name.
-use middle::cstore::{CrateStore, LOCAL_CRATE};
+use middle::cstore::CrateStore;
use middle::def_id::DefId;
-use middle::traits;
-use middle::ty::{self, TyCtxt};
+use middle::traits::{self, ProjectionMode};
use middle::infer;
+use middle::ty::{self, TyCtxt};
use syntax::ast;
use syntax::codemap::Span;
use rustc::dep_graph::DepNode;
use rustc_front::hir;
use rustc_front::intravisit;
-use util::nodemap::{DefIdMap, DefIdSet};
+use util::nodemap::DefIdMap;
pub fn check(tcx: &TyCtxt) {
let mut overlap = OverlapChecker { tcx: tcx,
- traits_checked: DefIdSet(),
default_impls: DefIdMap() };
// this secondary walk specifically checks for some other cases,
struct OverlapChecker<'cx, 'tcx:'cx> {
tcx: &'cx TyCtxt<'tcx>,
- // The set of traits where we have checked for overlap. This is
- // used to avoid checking the same trait twice.
- //
- // NB. It's ok to skip tracking this set because we fully
- // encapsulate it, and we always create a task
- // (`CoherenceOverlapCheck`) corresponding to each entry.
- traits_checked: DefIdSet,
-
// maps from a trait def-id to an impl id
default_impls: DefIdMap<ast::NodeId>,
}
impl<'cx, 'tcx> OverlapChecker<'cx, 'tcx> {
- fn check_for_overlapping_impls_of_trait(&mut self, trait_def_id: DefId) {
- debug!("check_for_overlapping_impls_of_trait(trait_def_id={:?})",
- trait_def_id);
-
- let _task = self.tcx.dep_graph.in_task(DepNode::CoherenceOverlapCheck(trait_def_id));
- if !self.traits_checked.insert(trait_def_id) {
- return;
- }
-
- let trait_def = self.tcx.lookup_trait_def(trait_def_id);
- self.tcx.populate_implementations_for_trait_if_necessary(
- trait_def.trait_ref.def_id);
-
- // We should already know all impls of this trait, so these
- // borrows are safe.
- let (blanket_impls, nonblanket_impls) = trait_def.borrow_impl_lists(self.tcx);
-
- // Conflicts can only occur between a blanket impl and another impl,
- // or between 2 non-blanket impls of the same kind.
-
- for (i, &impl1_def_id) in blanket_impls.iter().enumerate() {
- for &impl2_def_id in &blanket_impls[(i+1)..] {
- self.check_if_impls_overlap(impl1_def_id,
- impl2_def_id);
- }
-
- for v in nonblanket_impls.values() {
- for &impl2_def_id in v {
- self.check_if_impls_overlap(impl1_def_id,
- impl2_def_id);
- }
- }
- }
-
- for impl_group in nonblanket_impls.values() {
- for (i, &impl1_def_id) in impl_group.iter().enumerate() {
- for &impl2_def_id in &impl_group[(i+1)..] {
- self.check_if_impls_overlap(impl1_def_id,
- impl2_def_id);
- }
- }
- }
- }
-
- // We need to coherently pick which impl will be displayed
- // as causing the error message, and it must be the in the current
- // crate. Just pick the smaller impl in the file.
- fn order_impls(&self, impl1_def_id: DefId, impl2_def_id: DefId)
- -> Option<(DefId, DefId)> {
- if impl1_def_id.krate != LOCAL_CRATE {
- if impl2_def_id.krate != LOCAL_CRATE {
- // we don't need to check impls if both are external;
- // that's the other crate's job.
- None
- } else {
- Some((impl2_def_id, impl1_def_id))
- }
- } else if impl2_def_id.krate != LOCAL_CRATE {
- Some((impl1_def_id, impl2_def_id))
- } else if impl1_def_id < impl2_def_id {
- Some((impl1_def_id, impl2_def_id))
- } else {
- Some((impl2_def_id, impl1_def_id))
- }
- }
-
- fn check_if_impls_overlap(&self,
- impl1_def_id: DefId,
- impl2_def_id: DefId)
- {
- if let Some((impl1_def_id, impl2_def_id)) = self.order_impls(
- impl1_def_id, impl2_def_id)
- {
- debug!("check_if_impls_overlap({:?}, {:?})",
- impl1_def_id,
- impl2_def_id);
-
- let infcx = infer::new_infer_ctxt(self.tcx, &self.tcx.tables, None);
- if let Some(header) = traits::overlapping_impls(&infcx, impl1_def_id, impl2_def_id) {
- self.report_overlap_error(impl1_def_id, impl2_def_id, header.trait_ref.unwrap());
- }
- }
- }
-
- fn report_overlap_error(&self,
- impl1: DefId,
- impl2: DefId,
- trait_ref: ty::TraitRef)
- {
- // only print the Self type if it's concrete; otherwise, it's not adding much information.
- let self_type = {
- trait_ref.substs.self_ty().and_then(|ty| {
- if let ty::TyInfer(_) = ty.sty {
- None
- } else {
- Some(format!(" for type `{}`", ty))
- }
- }).unwrap_or(String::new())
- };
-
- let mut err = struct_span_err!(self.tcx.sess, self.span_of_def_id(impl1), E0119,
- "conflicting implementations of trait `{}`{}:",
- trait_ref,
- self_type);
-
- if impl2.is_local() {
- span_note!(&mut err, self.span_of_def_id(impl2),
- "conflicting implementation is here:");
- } else {
- let cname = self.tcx.sess.cstore.crate_name(impl2.krate);
- err.note(&format!("conflicting implementation in crate `{}`", cname));
- }
- err.emit();
- }
-
fn span_of_def_id(&self, did: DefId) -> Span {
let node_id = self.tcx.map.as_local_node_id(did).unwrap();
self.tcx.map.span(node_id)
for (i, &impl1_def_id) in impls.iter().enumerate() {
for &impl2_def_id in &impls[(i+1)..] {
- let infcx = infer::new_infer_ctxt(self.tcx, &self.tcx.tables, None);
+ let infcx = infer::new_infer_ctxt(self.tcx,
+ &self.tcx.tables,
+ None,
+ ProjectionMode::Topmost);
if traits::overlapping_impls(&infcx, impl1_def_id, impl2_def_id).is_some() {
self.check_for_common_items_in_impls(impl1_def_id, impl2_def_id)
}
}
}
-
impl<'cx, 'tcx,'v> intravisit::Visitor<'v> for OverlapChecker<'cx, 'tcx> {
fn visit_item(&mut self, item: &'v hir::Item) {
match item.node {
- hir::ItemTrait(..) => {
- let trait_def_id = self.tcx.map.local_def_id(item.id);
- self.check_for_overlapping_impls_of_trait(trait_def_id);
- }
-
hir::ItemEnum(..) | hir::ItemStruct(..) => {
let type_def_id = self.tcx.map.local_def_id(item.id);
self.check_for_overlapping_inherent_impls(type_def_id);
let impl_def_id = self.tcx.map.local_def_id(item.id);
let trait_ref = self.tcx.impl_trait_ref(impl_def_id).unwrap();
- self.check_for_overlapping_impls_of_trait(trait_ref.def_id);
-
let prev_default_impl = self.default_impls.insert(trait_ref.def_id, item.id);
- match prev_default_impl {
- Some(prev_id) => {
- self.report_overlap_error(impl_def_id,
- self.tcx.map.local_def_id(prev_id),
- trait_ref);
- }
- None => { }
+ if let Some(prev_id) = prev_default_impl {
+ let mut err = struct_span_err!(
+ self.tcx.sess,
+ self.tcx.span_of_impl(impl_def_id).unwrap(), E0521,
+ "redundant default implementations of trait `{}`:",
+ trait_ref);
+ err.span_note(self.tcx.span_of_impl(self.tcx.map.local_def_id(prev_id))
+ .unwrap(),
+ "redundant implementation is here:");
+ err.emit();
}
}
hir::ItemImpl(_, _, _, Some(_), _, _) => {
let impl_def_id = self.tcx.map.local_def_id(item.id);
let trait_ref = self.tcx.impl_trait_ref(impl_def_id).unwrap();
let trait_def_id = trait_ref.def_id;
- self.check_for_overlapping_impls_of_trait(trait_def_id);
- match trait_ref.self_ty().sty {
- ty::TyTrait(ref data) => {
- // This is something like impl Trait1 for Trait2. Illegal
- // if Trait1 is a supertrait of Trait2 or Trait2 is not object safe.
- if !traits::is_object_safe(self.tcx, data.principal_def_id()) {
- // This is an error, but it will be
- // reported by wfcheck. Ignore it
- // here. This is tested by
- // `coherence-impl-trait-for-trait-object-safe.rs`.
- } else {
- let mut supertrait_def_ids =
- traits::supertrait_def_ids(self.tcx, data.principal_def_id());
- if supertrait_def_ids.any(|d| d == trait_def_id) {
- span_err!(self.tcx.sess, item.span, E0371,
- "the object type `{}` automatically \
- implements the trait `{}`",
- trait_ref.self_ty(),
- self.tcx.item_path_str(trait_def_id));
+ let _task = self.tcx.dep_graph.in_task(
+ DepNode::CoherenceOverlapCheck(trait_def_id));
+
+ let def = self.tcx.lookup_trait_def(trait_def_id);
+
+ // attempt to insert into the specialization graph
+ let insert_result = def.add_impl_for_specialization(self.tcx, impl_def_id);
+
+ // insertion failed due to overlap
+ if let Err(overlap) = insert_result {
+ // only print the Self type if it has at least some outer
+ // concrete shell; otherwise, it's not adding much
+ // information.
+ let self_type = {
+ overlap.on_trait_ref.substs.self_ty().and_then(|ty| {
+ if ty.has_concrete_skeleton() {
+ Some(format!(" for type `{}`", ty))
+ } else {
+ None
}
+ }).unwrap_or(String::new())
+ };
+
+ let mut err = struct_span_err!(
+ self.tcx.sess, self.tcx.span_of_impl(impl_def_id).unwrap(), E0119,
+ "conflicting implementations of trait `{}`{}:",
+ overlap.on_trait_ref,
+ self_type);
+
+ match self.tcx.span_of_impl(overlap.with_impl) {
+ Ok(span) => {
+ err.span_note(span, "conflicting implementation is here:");
+ }
+ Err(cname) => {
+ err.note(&format!("conflicting implementation in crate `{}`",
+ cname));
+ }
+ }
+
+ err.emit();
+ }
+
+ // check for overlap with the automatic `impl Trait for Trait`
+ if let ty::TyTrait(ref data) = trait_ref.self_ty().sty {
+ // This is something like impl Trait1 for Trait2. Illegal
+ // if Trait1 is a supertrait of Trait2 or Trait2 is not object safe.
+
+ if !traits::is_object_safe(self.tcx, data.principal_def_id()) {
+ // This is an error, but it will be
+ // reported by wfcheck. Ignore it
+ // here. This is tested by
+ // `coherence-impl-trait-for-trait-object-safe.rs`.
+ } else {
+ let mut supertrait_def_ids =
+ traits::supertrait_def_ids(self.tcx, data.principal_def_id());
+ if supertrait_def_ids.any(|d| d == trait_def_id) {
+ span_err!(self.tcx.sess, item.span, E0371,
+ "the object type `{}` automatically \
+ implements the trait `{}`",
+ trait_ref.self_ty(),
+ self.tcx.item_path_str(trait_def_id));
}
}
- _ => { }
}
}
- _ => {
- }
+ _ => {}
}
}
}
use util::nodemap::{FnvHashMap, FnvHashSet};
use write_ty_to_tcx;
+use rustc_const_eval::ConstInt;
+
use std::cell::RefCell;
use std::collections::HashSet;
use std::rc::Rc;
id: ast::NodeId,
vis: hir::Visibility,
sig: &hir::MethodSig,
+ defaultness: hir::Defaultness,
untransformed_rcvr_ty: Ty<'tcx>,
rcvr_ty_generics: &ty::Generics<'tcx>,
rcvr_ty_predicates: &ty::GenericPredicates<'tcx>) {
fty,
explicit_self_category,
vis,
+ defaultness,
def_id,
container);
name: ast::Name,
id: ast::NodeId,
vis: hir::Visibility,
+ defaultness: hir::Defaultness,
ty: ty::Ty<'tcx>,
has_value: bool)
{
let associated_const = Rc::new(ty::AssociatedConst {
name: name,
vis: vis,
+ defaultness: defaultness,
def_id: ccx.tcx.map.local_def_id(id),
container: container,
ty: ty,
name: ast::Name,
id: ast::NodeId,
vis: hir::Visibility,
+ defaultness: hir::Defaultness,
ty: Option<Ty<'tcx>>)
{
let associated_type = Rc::new(ty::AssociatedType {
name: name,
vis: vis,
+ defaultness: defaultness,
ty: ty,
def_id: ccx.tcx.map.local_def_id(id),
container: container
convert_associated_const(ccx, ImplContainer(def_id),
impl_item.name, impl_item.id,
impl_item.vis.inherit_from(parent_visibility),
+ impl_item.defaultness,
ty, true /* has_value */);
}
}
convert_associated_type(ccx, ImplContainer(def_id),
impl_item.name, impl_item.id, impl_item.vis,
- Some(typ));
+ impl_item.defaultness, Some(typ));
}
}
convert_method(ccx, ImplContainer(def_id),
impl_item.name, impl_item.id, method_vis,
- sig, selfty, &ty_generics, &ty_predicates);
+ sig, impl_item.defaultness, selfty, &ty_generics,
+ &ty_predicates);
}
}
trait_item.name,
trait_item.id,
hir::Public,
+ hir::Defaultness::Default,
ty,
default.is_some())
}
trait_item.name,
trait_item.id,
hir::Public,
+ hir::Defaultness::Default,
typ);
}
}
trait_item.id,
hir::Inherited,
sig,
+ hir::Defaultness::Default,
tcx.mk_self_type(),
&trait_def.generics,
&trait_predicates);
tcx.intern_adt_def(
did,
ty::AdtKind::Struct,
- vec![convert_struct_variant(tcx, ctor_id, it.name, 0, def)]
+ vec![convert_struct_variant(tcx, ctor_id, it.name, ConstInt::Infer(0), def)]
)
}
def: &hir::EnumDef)
-> ty::AdtDefMaster<'tcx>
{
+ fn print_err(tcx: &TyCtxt, span: Span, ty: ty::Ty, cv: ConstVal) {
+ span_err!(tcx.sess, span, E0079, "mismatched types: expected `{}` got `{}`",
+ ty, cv.description());
+ }
fn evaluate_disr_expr<'tcx>(tcx: &TyCtxt<'tcx>,
- repr_ty: Ty<'tcx>,
+ repr_ty: attr::IntType,
e: &hir::Expr) -> Option<ty::Disr> {
debug!("disr expr, checking {}", pprust::expr_to_string(e));
- let hint = UncheckedExprHint(repr_ty);
+ let ty_hint = repr_ty.to_ty(tcx);
+ let hint = UncheckedExprHint(ty_hint);
match const_eval::eval_const_expr_partial(tcx, e, hint, None) {
- Ok(ConstVal::Int(val)) => Some(val as ty::Disr),
- Ok(ConstVal::Uint(val)) => Some(val as ty::Disr),
- Ok(_) => {
- let sign_desc = if repr_ty.is_signed() {
- "signed"
- } else {
- "unsigned"
- };
- span_err!(tcx.sess, e.span, E0079,
- "expected {} integer constant",
- sign_desc);
+ Ok(ConstVal::Integral(i)) => {
+ // FIXME: eval_const_expr_partial should return an error if the hint is wrong
+ match (repr_ty, i) {
+ (attr::SignedInt(ast::IntTy::I8), ConstInt::I8(_)) => Some(i),
+ (attr::SignedInt(ast::IntTy::I16), ConstInt::I16(_)) => Some(i),
+ (attr::SignedInt(ast::IntTy::I32), ConstInt::I32(_)) => Some(i),
+ (attr::SignedInt(ast::IntTy::I64), ConstInt::I64(_)) => Some(i),
+ (attr::SignedInt(ast::IntTy::Is), ConstInt::Isize(_)) => Some(i),
+ (attr::UnsignedInt(ast::UintTy::U8), ConstInt::U8(_)) => Some(i),
+ (attr::UnsignedInt(ast::UintTy::U16), ConstInt::U16(_)) => Some(i),
+ (attr::UnsignedInt(ast::UintTy::U32), ConstInt::U32(_)) => Some(i),
+ (attr::UnsignedInt(ast::UintTy::U64), ConstInt::U64(_)) => Some(i),
+ (attr::UnsignedInt(ast::UintTy::Us), ConstInt::Usize(_)) => Some(i),
+ (_, i) => {
+ print_err(tcx, e.span, ty_hint, ConstVal::Integral(i));
+ None
+ },
+ }
+ },
+ Ok(cv) => {
+ print_err(tcx, e.span, ty_hint, cv);
None
},
Err(err) => {
fn report_discrim_overflow(tcx: &TyCtxt,
variant_span: Span,
variant_name: &str,
- repr_type: attr::IntType,
prev_val: ty::Disr) {
- let computed_value = repr_type.disr_wrap_incr(Some(prev_val));
- let computed_value = repr_type.disr_string(computed_value);
- let prev_val = repr_type.disr_string(prev_val);
- let repr_type = repr_type.to_ty(tcx);
span_err!(tcx.sess, variant_span, E0370,
- "enum discriminant overflowed on value after {}: {}; \
+ "enum discriminant overflowed on value after {}; \
set explicitly via {} = {} if that is desired outcome",
- prev_val, repr_type, variant_name, computed_value);
+ prev_val, variant_name, prev_val.wrap_incr());
}
fn next_disr(tcx: &TyCtxt,
if let Some(prev_disr_val) = prev_disr_val {
let result = repr_type.disr_incr(prev_disr_val);
if let None = result {
- report_discrim_overflow(tcx, v.span, &v.node.name.as_str(),
- repr_type, prev_disr_val);
+ report_discrim_overflow(tcx, v.span, &v.node.name.as_str(), prev_disr_val);
}
result
} else {
- Some(ty::INITIAL_DISCRIMINANT_VALUE)
+ Some(repr_type.initial_discriminant(tcx))
}
}
fn convert_enum_variant<'tcx>(tcx: &TyCtxt<'tcx>,
}
let did = tcx.map.local_def_id(it.id);
let repr_hints = tcx.lookup_repr_hints(did);
- let (repr_type, repr_type_ty) = tcx.enum_repr_type(repr_hints.get(0));
+ let repr_type = tcx.enum_repr_type(repr_hints.get(0));
let mut prev_disr = None;
let variants = def.variants.iter().map(|v| {
let disr = match v.node.disr_expr {
- Some(ref e) => evaluate_disr_expr(tcx, repr_type_ty, e),
+ Some(ref e) => evaluate_disr_expr(tcx, repr_type, e),
None => next_disr(tcx, v, repr_type, prev_disr)
- }.unwrap_or(repr_type.disr_wrap_incr(prev_disr));
+ }.unwrap_or_else(|| {
+ prev_disr.map(ty::Disr::wrap_incr)
+ .unwrap_or(repr_type.initial_discriminant(tcx))
+ });
- let v = convert_enum_variant(tcx, v, disr);
prev_disr = Some(disr);
- v
+ convert_enum_variant(tcx, v, disr)
}).collect();
tcx.intern_adt_def(tcx.map.local_def_id(it.id), ty::AdtKind::Enum, variants)
}
E0399, // trait items need to be implemented because the associated
// type `{}` was overridden
E0436, // functional record update requires a struct
- E0513 // no type for local variable ..
+ E0513, // no type for local variable ..
+ E0520, // cannot specialize non-default item
+ E0521 // redundant default implementations of trait
}
extern crate rustc_platform_intrinsics as intrinsics;
extern crate rustc_front;
extern crate rustc_back;
+extern crate rustc_const_eval;
pub use rustc::dep_graph;
pub use rustc::front;
use middle::infer::{self, TypeOrigin};
use middle::subst::Substs;
use middle::ty::{self, Ty, TyCtxt, TypeFoldable};
+use middle::traits::ProjectionMode;
use session::{config, CompileResult};
use util::common::time;
use rustc_front::hir;
{
let result = match maybe_infcx {
None => {
- let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, None);
+ let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, None, ProjectionMode::AnyFinal);
infer::mk_eqty(&infcx, t1_is_expected, TypeOrigin::Misc(span), t1, t2)
}
Some(infcx) => {
let type_scheme = tcx.lookup_item_type(did);
let default = if assoc_const.has_value {
Some(const_eval::lookup_const_by_id(tcx, did, None, None)
- .unwrap().span.to_src(cx))
+ .unwrap().0.span.to_src(cx))
} else {
None
};
use rustc::middle::const_eval;
use rustc_front::print::pprust;
- let expr = const_eval::lookup_const_by_id(tcx, did, None, None).unwrap_or_else(|| {
+ let (expr, ty) = const_eval::lookup_const_by_id(tcx, did, None, None).unwrap_or_else(|| {
panic!("expected lookup_const_by_id to succeed for {:?}", did);
});
debug!("converting constant expr {:?} to snippet", expr);
debug!("got snippet {}", sn);
clean::Constant {
- type_: tcx.lookup_item_type(did).ty.clean(cx),
+ type_: ty.map(|t| t.clean(cx)).unwrap_or_else(|| tcx.lookup_item_type(did).ty.clean(cx)),
expr: sn
}
}
current_dir().unwrap().join(path)
}
},
- Input::Str(_) => PathBuf::new() // FIXME: this is wrong
+ Input::Str { ref name, .. } => PathBuf::from(name.clone()),
};
Crate {
// the test harness wants its own `main` & top level functions, so
// never wrap the test in `fn main() { ... }`
let test = maketest(test, Some(cratename), as_test_harness, opts);
- let input = config::Input::Str(test.to_string());
+ let input = config::Input::Str {
+ name: driver::anon_src(),
+ input: test.to_owned(),
+ };
let mut outputs = HashMap::new();
outputs.insert(OutputType::Exe, None);
/// - the link-local addresses
/// - the (deprecated) site-local addresses
/// - unique local addresses
+ /// - the unspecified address
pub fn is_unicast_global(&self) -> bool {
!self.is_multicast()
&& !self.is_loopback() && !self.is_unicast_link_local()
&& !self.is_unicast_site_local() && !self.is_unique_local()
+ && !self.is_unspecified()
}
/// Returns the address's multicast scope if the address is multicast.
// unspec loopbk uniqlo global unill unisl uniglo mscope
check("::",
- true, false, false, true, false, false, true, None);
+ true, false, false, false, false, false, false, None);
check("::1",
false, true, false, false, false, false, false, None);
check("::0.0.0.2",
pub explicit_self: ExplicitSelf,
}
-/// Represents a method declaration in a trait declaration, possibly including
-/// a default implementation. A trait method is either required (meaning it
-/// doesn't have an implementation, just a signature) or provided (meaning it
-/// has a default implementation).
+/// Represents an item declaration within a trait declaration,
+/// possibly including a default implementation. A trait item is
+/// either required (meaning it doesn't have an implementation, just a
+/// signature) or provided (meaning it has a default implementation).
#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
pub struct TraitItem {
pub id: NodeId,
pub id: NodeId,
pub ident: Ident,
pub vis: Visibility,
+ pub defaultness: Defaultness,
pub attrs: Vec<Attribute>,
pub node: ImplItemKind,
pub span: Span,
NotConst,
}
+#[derive(Copy, Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
+pub enum Defaultness {
+ Default,
+ Final,
+}
+
impl fmt::Display for Unsafety {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Display::fmt(match *self {
ident: ii.ident,
attrs: ii.attrs,
vis: ii.vis,
+ defaultness: ii.defaultness,
node: match ii.node {
ast::ImplItemKind::Method(sig, body) => {
let (sig, body) = expand_and_rename_method(sig, body, fld);
("inclusive_range_syntax", "1.7.0", Some(28237), Active),
// `expr?`
- ("question_mark", "1.9.0", Some(31436), Active)
+ ("question_mark", "1.9.0", Some(31436), Active),
+
+ // impl specialization (RFC 1210)
+ ("specialization", "1.7.0", Some(31844), Active),
];
// (changing above list without updating src/doc/reference.md makes @cmr sad)
pub stmt_expr_attributes: bool,
pub deprecated: bool,
pub question_mark: bool,
+ pub specialization: bool,
}
impl Features {
stmt_expr_attributes: false,
deprecated: false,
question_mark: false,
+ specialization: false,
}
}
}
}
fn visit_impl_item(&mut self, ii: &'v ast::ImplItem) {
+ if ii.defaultness == ast::Defaultness::Default {
+ self.gate_feature("specialization",
+ ii.span,
+ "specialization is unstable");
+ }
+
match ii.node {
ast::ImplItemKind::Const(..) => {
self.gate_feature("associated_consts",
stmt_expr_attributes: cx.has_feature("stmt_expr_attributes"),
deprecated: cx.has_feature("deprecated"),
question_mark: cx.has_feature("question_mark"),
+ specialization: cx.has_feature("specialization"),
}
}
ident: folder.fold_ident(i.ident),
attrs: fold_attrs(i.attrs, folder),
vis: i.vis,
+ defaultness: i.defaultness,
node: match i.node {
ast::ImplItemKind::Const(ty, expr) => {
ast::ImplItemKind::Const(folder.fold_ty(ty), folder.fold_expr(expr))
use ast::Block;
use ast::{BlockCheckMode, CaptureBy};
use ast::{Constness, Crate, CrateConfig};
-use ast::{Decl, DeclKind};
+use ast::{Decl, DeclKind, Defaultness};
use ast::{EMPTY_CTXT, EnumDef, ExplicitSelf};
use ast::{Expr, ExprKind, RangeLimits};
use ast::{Field, FnDecl};
}
}
+ pub fn check_contextual_keyword(&mut self, ident: Ident) -> bool {
+ let tok = token::Ident(ident, token::Plain);
+ self.expected_tokens.push(TokenType::Token(tok));
+ if let token::Ident(ref cur_ident, _) = self.token {
+ cur_ident.name == ident.name
+ } else {
+ false
+ }
+ }
+
+ pub fn eat_contextual_keyword(&mut self, ident: Ident) -> bool {
+ if self.check_contextual_keyword(ident) {
+ self.bump();
+ true
+ } else {
+ false
+ }
+ }
+
/// If the given word is not a keyword, signal an error.
/// If the next token is not the given word, signal an error.
/// Otherwise, eat it.
}
}
-
/// Attempt to consume a `<`. If `<<` is seen, replace it with a single
/// `<` and continue. If a `<` is not seen, return false.
///
let mut attrs = try!(self.parse_outer_attributes());
let lo = self.span.lo;
let vis = try!(self.parse_visibility());
+ let defaultness = try!(self.parse_defaultness());
let (name, node) = if self.eat_keyword(keywords::Type) {
let name = try!(self.parse_ident());
try!(self.expect(&token::Eq));
span: mk_sp(lo, self.last_span.hi),
ident: name,
vis: vis,
+ defaultness: defaultness,
attrs: attrs,
node: node
})
else { Ok(Visibility::Inherited) }
}
+ /// Parse defaultness: DEFAULT or nothing
+ fn parse_defaultness(&mut self) -> PResult<'a, Defaultness> {
+ if self.eat_contextual_keyword(special_idents::DEFAULT) {
+ Ok(Defaultness::Default)
+ } else {
+ Ok(Defaultness::Final)
+ }
+ }
+
/// Given a termination token, parse all of the items in a module
fn parse_mod_items(&mut self, term: &token::Token, inner_lo: BytePos) -> PResult<'a, Mod> {
let mut items = vec![];
(9, __unused1, "<__unused1>");
(super::SELF_TYPE_KEYWORD_NAME_NUM, type_self, "Self");
(11, prelude_import, "prelude_import");
+ (12, DEFAULT, "default");
}
pub mod keywords {
// These ones are variants of the Keyword enum
'strict:
- (12, As, "as");
- (13, Break, "break");
- (14, Crate, "crate");
- (15, Else, "else");
- (16, Enum, "enum");
- (17, Extern, "extern");
- (18, False, "false");
- (19, Fn, "fn");
- (20, For, "for");
- (21, If, "if");
- (22, Impl, "impl");
- (23, In, "in");
- (24, Let, "let");
- (25, Loop, "loop");
- (26, Match, "match");
- (27, Mod, "mod");
- (28, Move, "move");
- (29, Mut, "mut");
- (30, Pub, "pub");
- (31, Ref, "ref");
- (32, Return, "return");
+ (13, As, "as");
+ (14, Break, "break");
+ (15, Crate, "crate");
+ (16, Else, "else");
+ (17, Enum, "enum");
+ (18, Extern, "extern");
+ (19, False, "false");
+ (20, Fn, "fn");
+ (21, For, "for");
+ (22, If, "if");
+ (23, Impl, "impl");
+ (24, In, "in");
+ (25, Let, "let");
+ (26, Loop, "loop");
+ (27, Match, "match");
+ (28, Mod, "mod");
+ (29, Move, "move");
+ (30, Mut, "mut");
+ (31, Pub, "pub");
+ (32, Ref, "ref");
+ (33, Return, "return");
// Static and Self are also special idents (prefill de-dupes)
(super::STATIC_KEYWORD_NAME_NUM, Static, "static");
(super::SELF_KEYWORD_NAME_NUM, SelfValue, "self");
(super::SELF_TYPE_KEYWORD_NAME_NUM, SelfType, "Self");
- (33, Struct, "struct");
+ (34, Struct, "struct");
(super::SUPER_KEYWORD_NAME_NUM, Super, "super");
- (34, True, "true");
- (35, Trait, "trait");
- (36, Type, "type");
- (37, Unsafe, "unsafe");
- (38, Use, "use");
- (39, While, "while");
- (40, Continue, "continue");
- (41, Box, "box");
- (42, Const, "const");
- (43, Where, "where");
+ (35, True, "true");
+ (36, Trait, "trait");
+ (37, Type, "type");
+ (38, Unsafe, "unsafe");
+ (39, Use, "use");
+ (40, While, "while");
+ (41, Continue, "continue");
+ (42, Box, "box");
+ (43, Const, "const");
+ (44, Where, "where");
'reserved:
- (44, Virtual, "virtual");
- (45, Proc, "proc");
- (46, Alignof, "alignof");
- (47, Become, "become");
- (48, Offsetof, "offsetof");
- (49, Priv, "priv");
- (50, Pure, "pure");
- (51, Sizeof, "sizeof");
- (52, Typeof, "typeof");
- (53, Unsized, "unsized");
- (54, Yield, "yield");
- (55, Do, "do");
- (56, Abstract, "abstract");
- (57, Final, "final");
- (58, Override, "override");
- (59, Macro, "macro");
+ (45, Virtual, "virtual");
+ (46, Proc, "proc");
+ (47, Alignof, "alignof");
+ (48, Become, "become");
+ (49, Offsetof, "offsetof");
+ (50, Priv, "priv");
+ (51, Pure, "pure");
+ (52, Sizeof, "sizeof");
+ (53, Typeof, "typeof");
+ (54, Unsized, "unsized");
+ (55, Yield, "yield");
+ (56, Do, "do");
+ (57, Abstract, "abstract");
+ (58, Final, "final");
+ (59, Override, "override");
+ (60, Macro, "macro");
}
}
try!(self.hardbreak_if_not_bol());
try!(self.maybe_print_comment(ii.span.lo));
try!(self.print_outer_attributes(&ii.attrs));
+ if let ast::Defaultness::Default = ii.defaultness {
+ try!(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));
span: self.span,
ident: ident,
vis: ast::Visibility::Inherited,
+ defaultness: ast::Defaultness::Final,
attrs: Vec::new(),
node: ast::ImplItemKind::Type(type_def.to_ty(cx,
self.span,
attrs: self.attributes.clone(),
span: trait_.span,
vis: ast::Visibility::Inherited,
+ defaultness: ast::Defaultness::Final,
ident: method_ident,
node: ast::ImplItemKind::Method(ast::MethodSig {
generics: fn_generics,
rustc: 2016-02-17
-cargo: 2016-01-21
+cargo: 2016-03-11
version = "0.0.0"
dependencies = [
"getopts 0.0.0",
- "serialize 0.0.0",
"term 0.0.0",
]
winnt-i386 0c336d794a65f8e285c121866c7d59aa2dd0d1e1
winnt-x86_64 27e75b1bf99770b3564bcebd7f3230be01135a92
openbsd-x86_64 ac957c6b84de2bd67f01df085d9ea515f96e22f3
- freebsd-x86_64 395adf223f3f25514c9dffecb524f493c42a0e5d
+ freebsd-x86_64 f38991fbb81c1cd8d0bbda396f98f13a55b42804
S 2015-12-18 3391630
bitrig-x86_64 6476e1562df02389b55553b4c88b1f4fd121cd40
#[macro_use] extern crate rustc;
extern crate rustc_front;
extern crate rustc_plugin;
+extern crate rustc_const_eval;
extern crate syntax;
use rustc::mir::transform::{self, MirPass};
use rustc::mir::visit::MutVisitor;
use rustc::middle::ty;
use rustc::middle::const_eval::ConstVal;
+use rustc_const_eval::ConstInt;
use rustc_plugin::Registry;
use syntax::ast::NodeId;
impl<'tcx> MutVisitor<'tcx> for Visitor {
fn visit_literal(&mut self, literal: &mut Literal<'tcx>) {
- if let Literal::Value { value: ConstVal::Int(ref mut i @ 11) } = *literal {
- *i = 42;
+ if let Literal::Value { ref mut value } = *literal {
+ if let ConstVal::Integral(ConstInt::I32(ref mut i @ 11)) = *value {
+ *i = 42;
+ }
}
}
}
// option. This file may not be copied, modified, or distributed
// except according to those terms.
+#![feature(specialization)]
+
// Common code used for tests that model the Fn/FnMut/FnOnce hierarchy.
pub trait Go {
impl<G> GoMut for G
where G : Go
{
- fn go_mut(&mut self, arg: isize) {
+ default fn go_mut(&mut self, arg: isize) {
go(&*self, arg)
}
}
impl<G> GoOnce for G
where G : GoMut
{
- fn go_once(mut self, arg: isize) {
+ default fn go_once(mut self, arg: isize) {
go_mut(&mut self, arg)
}
}
--- /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(specialization)]
+
+pub trait Foo {
+ fn foo(&self) -> &'static str;
+}
+
+impl<T> Foo for T {
+ default fn foo(&self) -> &'static str {
+ "generic"
+ }
+}
+
+impl<T: Clone> Foo for T {
+ default fn foo(&self) -> &'static str {
+ "generic Clone"
+ }
+}
+
+impl<T, U> Foo for (T, U) where T: Clone, U: Clone {
+ default fn foo(&self) -> &'static str {
+ "generic pair"
+ }
+}
+
+impl<T: Clone> Foo for (T, T) {
+ default fn foo(&self) -> &'static str {
+ "generic uniform pair"
+ }
+}
+
+impl Foo for (u8, u32) {
+ default fn foo(&self) -> &'static str {
+ "(u8, u32)"
+ }
+}
+
+impl Foo for (u8, u8) {
+ default fn foo(&self) -> &'static str {
+ "(u8, u8)"
+ }
+}
+
+impl<T: Clone> Foo for Vec<T> {
+ default fn foo(&self) -> &'static str {
+ "generic Vec"
+ }
+}
+
+impl Foo for Vec<i32> {
+ fn foo(&self) -> &'static str {
+ "Vec<i32>"
+ }
+}
+
+impl Foo for String {
+ fn foo(&self) -> &'static str {
+ "String"
+ }
+}
+
+impl Foo for i32 {
+ fn foo(&self) -> &'static str {
+ "i32"
+ }
+}
+
+pub trait MyMarker {}
+impl<T: Clone + MyMarker> Foo for T {
+ default fn foo(&self) -> &'static str {
+ "generic Clone + MyMarker"
+ }
+}
--- /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(specialization)]
+
+// First, test only use of explicit `default` items:
+
+pub trait Foo {
+ fn foo(&self) -> bool;
+}
+
+impl<T> Foo for T {
+ default fn foo(&self) -> bool { false }
+}
+
+impl Foo for i32 {}
+
+impl Foo for i64 {
+ fn foo(&self) -> bool { true }
+}
+
+// Next, test mixture of explicit `default` and provided methods:
+
+pub trait Bar {
+ fn bar(&self) -> i32 { 0 }
+}
+
+impl<T> Bar for T {} // use the provided method
+
+impl Bar for i32 {
+ fn bar(&self) -> i32 { 1 }
+}
+impl<'a> Bar for &'a str {}
+
+impl<T> Bar for Vec<T> {
+ default fn bar(&self) -> i32 { 2 }
+}
+impl Bar for Vec<i32> {}
+impl Bar for Vec<i64> {
+ fn bar(&self) -> i32 { 3 }
+}
#![feature(associated_type_defaults)]
-pub trait Foo {
- type Input = usize;
- fn bar(&self, _: Self::Input) {}
+pub trait Foo<T: Default + ToString> {
+ type Out: Default + ToString = T;
}
-impl Foo for () {}
+impl Foo<u32> for () {
+}
+
+impl Foo<u64> for () {
+ type Out = bool;
+}
fn into_cow(self) -> Cow<'a, B>;
}
-impl<'a, B: ?Sized> IntoCow<'a, B> for Cow<'a, B> where B: ToOwned {
-//~^ ERROR E0119
+impl<'a, B: ?Sized> IntoCow<'a, B> for <B as ToOwned>::Owned where B: ToOwned {
fn into_cow(self) -> Cow<'a, B> {
- self
+ Cow(PhantomData)
}
}
-impl<'a, B: ?Sized> IntoCow<'a, B> for <B as ToOwned>::Owned where B: ToOwned {
+impl<'a, B: ?Sized> IntoCow<'a, B> for Cow<'a, B> where B: ToOwned {
//~^ ERROR E0119
fn into_cow(self) -> Cow<'a, B> {
- Cow(PhantomData)
+ self
}
}
impl<'a, B: ?Sized> IntoCow<'a, B> for &'a B where B: ToOwned {
+//~^ ERROR E0119
fn into_cow(self) -> Cow<'a, B> {
Cow(PhantomData)
}
//~^^ HELP through a usize first
let _ = 42usize as *const [u8]; //~ ERROR casting
- let _ = v as *const [u8]; //~ ERROR casting
+ let _ = v as *const [u8]; //~ ERROR cannot cast
let _ = fat_v as *const Foo;
//~^ ERROR `core::marker::Sized` is not implemented for the type `[u8]`
let _ = foo as *const str; //~ ERROR casting
impl Odd for usize { }
-impl<T:Even> MyTrait for T { //~ ERROR E0119
+impl<T:Even> MyTrait for T {
fn get(&self) -> usize { 0 }
}
-impl<T:Odd> MyTrait for T {
+impl<T:Odd> MyTrait for T { //~ ERROR E0119
fn get(&self) -> usize { 0 }
}
trait Odd {}
-impl<T:Even> MyTrait for T { //~ ERROR E0119
+impl<T:Even> MyTrait for T {
fn get(&self) -> usize { 0 }
}
-impl<T:Odd> MyTrait for T {
+impl<T:Odd> MyTrait for T { //~ ERROR E0119
fn get(&self) -> usize { 0 }
}
fn get(&self) -> T;
}
-impl<T> MyTrait<T> for T { //~ ERROR E0119
+impl<T> MyTrait<T> for T {
fn get(&self) -> T {
panic!()
}
dummy: usize
}
-impl MyTrait<MyType> for MyType {
+impl MyTrait<MyType> for MyType { //~ ERROR E0119
fn get(&self) -> usize { (*self).clone() }
}
fn get(&self) -> usize;
}
-impl<T:OtherTrait> MyTrait for T { //~ ERROR E0119
+impl<T:OtherTrait> MyTrait for T {
fn get(&self) -> usize { 0 }
}
dummy: usize
}
-impl MyTrait for MyType {
+impl MyTrait for MyType { //~ ERROR E0119
fn get(&self) -> usize { self.dummy }
}
fn get(&self) -> usize;
}
-impl<T> MyTrait for T { //~ ERROR E0119
+impl<T> MyTrait for T {
fn get(&self) -> usize { 0 }
}
dummy: usize
}
-impl MyTrait for MyType {
+impl MyTrait for MyType { //~ ERROR E0119
fn get(&self) -> usize { self.dummy }
}
struct TestType<T>(::std::marker::PhantomData<T>);
unsafe impl<T: MyTrait+'static> Send for TestType<T> {}
-//~^ ERROR conflicting implementations of trait `core::marker::Send`
-//~^^ ERROR conflicting implementations of trait `core::marker::Send`
impl<T: MyTrait> !Send for TestType<T> {}
//~^ ERROR conflicting implementations of trait `core::marker::Send`
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-// Regression test for #3512 - conflicting trait impls in different crates should give a
-// 'conflicting implementations' error message.
+// The error here is strictly due to orphan rules; the impl here
+// generalizes the one upstream
// aux-build:trait_impl_conflict.rs
extern crate trait_impl_conflict;
impl<A> Foo for A {
//~^ ERROR type parameter `A` must be used as the type parameter for some local type
- //~^^ ERROR E0119
}
fn main() {
impl MyTrait for .. {}
impl MyTrait for .. {}
-//~^ ERROR conflicting implementations of trait `MyTrait`
+//~^ ERROR redundant default implementations of trait `MyTrait`
trait MySafeTrait {}
--- /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 you cannot *directly* dispatch on lifetime requirements
+
+trait MyTrait {}
+
+impl<T> MyTrait for T {}
+impl<T: 'static> MyTrait for T {} //~ ERROR E0119
+
+fn main() {}
trait From<U> {
}
-impl <T> From<T> for T { //~ ERROR E0119
+impl <T> From<T> for T {
}
-impl <T11, U11> From<(U11,)> for (T11,) {
+impl <T11, U11> From<(U11,)> for (T11,) { //~ ERROR E0119
}
fn main() { }
pub trait Sugar { fn dummy(&self) { } }
pub trait Sweet { fn dummy(&self) { } }
-impl<T:Sugar> Sweet for T { } //~ ERROR E0119
-impl<U:Sugar> Sweet for Box<U> { }
+impl<T:Sugar> Sweet for T { }
+impl<U:Sugar> Sweet for Box<U> { } //~ ERROR E0119
fn main() { }
trait Foo {}
-impl<T> Foo for T {} //~ ERROR conflicting implementations of trait `Foo`:
-impl<U> Foo for U {}
+impl<T> Foo for T {}
+impl<U> Foo for U {} //~ ERROR conflicting implementations of trait `Foo`:
trait Bar {}
-impl<T> Bar for T {} //~ ERROR conflicting implementations of trait `Bar` for type `u8`:
-impl Bar for u8 {}
+impl<T> Bar for (T, u8) {}
+impl<T> Bar for (u8, T) {} //~ ERROR conflicting implementations of trait `Bar` for type `(u8, u8)`:
trait Baz<T> {}
-impl<T, U> Baz<U> for T {} //~ ERROR conflicting implementations of trait `Baz<_>` for type `u8`:
-impl<T> Baz<T> for u8 {}
+impl<T> Baz<u8> for T {}
+impl<T> Baz<T> for u8 {} //~ ERROR conflicting implementations of trait `Baz<u8>` for type `u8`:
-trait Quux<T> {}
+trait Quux<U, V> {}
-impl<T, U> Quux<U> for T {} //~ ERROR conflicting implementations of trait `Quux<_>`:
-impl<T> Quux<T> for T {}
-
-trait Qaar<T> {}
-
-impl<T, U> Qaar<U> for T {} //~ ERROR conflicting implementations of trait `Qaar<u8>`:
-impl<T> Qaar<u8> for T {}
-
-trait Qaax<T> {}
-
-impl<T, U> Qaax<U> for T {}
-//~^ ERROR conflicting implementations of trait `Qaax<u8>` for type `u32`:
-impl Qaax<u8> for u32 {}
+impl<T, U, V> Quux<U, V> for T {}
+impl<T, U> Quux<U, U> for T {} //~ ERROR conflicting implementations of trait `Quux<_, _>`:
+impl<T, V> Quux<T, V> for T {} //~ ERROR conflicting implementations of trait `Quux<_, _>`:
fn main() {}
type Output: 'static;
}
-impl Foo<i32> for i32 { } //~ ERROR E0119
+impl Foo<i32> for i32 { }
-impl<A:Iterator> Foo<A::Item> for A { }
+impl<A:Iterator> Foo<A::Item> for A { } //~ ERROR E0119
fn main() {}
pub trait Foo<P> {}
-impl <P, T: Foo<P>> Foo<P> for Option<T> {} //~ ERROR E0119
+impl <P, T: Foo<P>> Foo<P> for Option<T> {}
-impl<T, U> Foo<T> for Option<U> { }
+impl<T, U> Foo<T> for Option<U> { } //~ ERROR E0119
fn main() {}
type Output: 'static;
}
-impl Foo<i32> for i32 { } //~ ERROR E0119
+impl Foo<i32> for i32 { }
-impl<A:Bar> Foo<A::Output> for A { }
+impl<A:Bar> Foo<A::Output> for A { } //~ ERROR E0119
impl Bar for i32 {
type Output = i32;
fn get(&self) -> usize;
}
-impl<T> MyTrait for (T,T) { //~ ERROR E0119
+impl<T> MyTrait for (T,T) {
fn get(&self) -> usize { 0 }
}
-impl<A,B> MyTrait for (A,B) {
+impl<A,B> MyTrait for (A,B) { //~ ERROR E0119
fn get(&self) -> usize { self.dummy }
}
trait MyTrait { }
-impl<T: lib::MyCopy> MyTrait for T { } //~ ERROR E0119
+impl<T: lib::MyCopy> MyTrait for T { }
// Tuples are not fundamental.
-impl MyTrait for lib::MyFundamentalStruct<(MyType,)> { }
+impl MyTrait for lib::MyFundamentalStruct<(MyType,)> { } //~ ERROR E0119
#[rustc_error]
fn main() { }
struct MyType { x: i32 }
trait MyTrait { }
-impl<T: lib::MyCopy> MyTrait for T { } //~ ERROR E0119
+impl<T: lib::MyCopy> MyTrait for T { }
// `MyStruct` is not declared fundamental, therefore this would
// require that
// MyStruct<MyType>: !MyTrait
//
// which we cannot approve.
-impl MyTrait for lib::MyStruct<MyType> { }
+impl MyTrait for lib::MyStruct<MyType> { } //~ ERROR E0119
fn main() { }
struct MyType { x: i32 }
trait MyTrait { }
-impl<T: lib::MyCopy> MyTrait for T { } //~ ERROR E0119
+impl<T: lib::MyCopy> MyTrait for T { }
// Tuples are not fundamental, therefore this would require that
//
// (MyType,): !MyTrait
//
// which we cannot approve.
-impl MyTrait for (MyType,) { }
+impl MyTrait for (MyType,) { } //~ ERROR E0119
fn main() { }
//~^ WARN attempted to add with overflow
//~^^ WARN attempted to add with overflow
let c = 200u8 * 4;
- //~^ WARN attempted to mul with overflow
+ //~^ WARN attempted to multiply with overflow
let d = 42u8 - (42u8 + 1);
- //~^ WARN attempted to sub with overflow
+ //~^ WARN attempted to subtract with overflow
let _e = BLA;
black_box(a);
black_box(b);
const NEG_NEG_128: i8 = -NEG_128;
//~^ ERROR constant evaluation error: attempted to negate with overflow
//~| ERROR attempted to negate with overflow
+//~| ERROR attempted to negate with overflow
fn main() {
match -128i8 {
- NEG_NEG_128 => println!("A"),
+ NEG_NEG_128 => println!("A"), //~ NOTE in pattern here
_ => println!("B"),
}
}
fn foo<T:fmt::Debug>(x: T) {
println!("{:?}", x);
}
-
const A_I8_T
: [u32; (i8::MAX as i8 + 1u8) as usize]
- //~^ ERROR mismatched types
- //~| ERROR the trait `core::ops::Add<u8>` is not implemented for the type `i8`
+ //~^ ERROR mismatched types:
+ //~| expected `i8`,
+ //~| found `u8` [E0250]
= [0; (i8::MAX as usize) + 1];
fn main() {
(-i8::MIN,
//~^ ERROR attempted to negate with overflow
i8::MIN - 1,
- //~^ ERROR attempted to sub with overflow
+ //~^ ERROR attempted to subtract with overflow
i8::MAX + 1,
//~^ ERROR attempted to add with overflow
i8::MIN * 2,
- //~^ ERROR attempted to mul with overflow
+ //~^ ERROR attempted to multiply with overflow
);
const VALS_I16: (i16, i16, i16, i16) =
(-i16::MIN,
//~^ ERROR attempted to negate with overflow
i16::MIN - 1,
- //~^ ERROR attempted to sub with overflow
+ //~^ ERROR attempted to subtract with overflow
i16::MAX + 1,
//~^ ERROR attempted to add with overflow
i16::MIN * 2,
- //~^ ERROR attempted to mul with overflow
+ //~^ ERROR attempted to multiply with overflow
);
const VALS_I32: (i32, i32, i32, i32) =
(-i32::MIN,
//~^ ERROR attempted to negate with overflow
i32::MIN - 1,
- //~^ ERROR attempted to sub with overflow
+ //~^ ERROR attempted to subtract with overflow
i32::MAX + 1,
//~^ ERROR attempted to add with overflow
i32::MIN * 2,
- //~^ ERROR attempted to mul with overflow
+ //~^ ERROR attempted to multiply with overflow
);
const VALS_I64: (i64, i64, i64, i64) =
(-i64::MIN,
//~^ ERROR attempted to negate with overflow
i64::MIN - 1,
- //~^ ERROR attempted to sub with overflow
+ //~^ ERROR attempted to subtract with overflow
i64::MAX + 1,
//~^ ERROR attempted to add with overflow
i64::MAX * 2,
- //~^ ERROR attempted to mul with overflow
+ //~^ ERROR attempted to multiply with overflow
);
const VALS_U8: (u8, u8, u8, u8) =
(-(u8::MIN as i8) as u8,
u8::MIN - 1,
- //~^ ERROR attempted to sub with overflow
+ //~^ ERROR attempted to subtract with overflow
u8::MAX + 1,
//~^ ERROR attempted to add with overflow
u8::MAX * 2,
- //~^ ERROR attempted to mul with overflow
+ //~^ ERROR attempted to multiply with overflow
);
const VALS_U16: (u16, u16, u16, u16) =
(-(u16::MIN as i16) as u16,
u16::MIN - 1,
- //~^ ERROR attempted to sub with overflow
+ //~^ ERROR attempted to subtract with overflow
u16::MAX + 1,
//~^ ERROR attempted to add with overflow
u16::MAX * 2,
- //~^ ERROR attempted to mul with overflow
+ //~^ ERROR attempted to multiply with overflow
);
const VALS_U32: (u32, u32, u32, u32) =
(-(u32::MIN as i32) as u32,
u32::MIN - 1,
- //~^ ERROR attempted to sub with overflow
+ //~^ ERROR attempted to subtract with overflow
u32::MAX + 1,
//~^ ERROR attempted to add with overflow
u32::MAX * 2,
- //~^ ERROR attempted to mul with overflow
+ //~^ ERROR attempted to multiply with overflow
);
const VALS_U64: (u64, u64, u64, u64) =
(-(u64::MIN as i64) as u64,
u64::MIN - 1,
- //~^ ERROR attempted to sub with overflow
+ //~^ ERROR attempted to subtract with overflow
u64::MAX + 1,
//~^ ERROR attempted to add with overflow
u64::MAX * 2,
- //~^ ERROR attempted to mul with overflow
+ //~^ ERROR attempted to multiply with overflow
);
fn main() {
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-const X: usize = 42 && 39; //~ ERROR: can't do this op on unsigned integrals
+const X: usize = 42 && 39; //~ ERROR: can't do this op on integrals
const ARR: [i32; X] = [99; 34]; //~ NOTE: for array length here
-const X1: usize = 42 || 39; //~ ERROR: can't do this op on unsigned integrals
+const X1: usize = 42 || 39; //~ ERROR: can't do this op on integrals
const ARR1: [i32; X1] = [99; 47]; //~ NOTE: for array length here
-// FIXME: the error should be `on signed integrals`
-const X2: usize = -42 || -39; //~ ERROR: can't do this op on unsigned integrals
+const X2: usize = -42 || -39; //~ ERROR: unary negation of unsigned integer
const ARR2: [i32; X2] = [99; 18446744073709551607]; //~ NOTE: for array length here
-// FIXME: the error should be `on signed integrals`
-const X3: usize = -42 && -39; //~ ERROR: can't do this op on unsigned integrals
+const X3: usize = -42 && -39; //~ ERROR: unary negation of unsigned integer
const ARR3: [i32; X3] = [99; 6]; //~ NOTE: for array length here
const Y: usize = 42.0 == 42.0;
-const ARRR: [i32; Y] = [99; 1]; //~ ERROR: expected constant integer expression for array length
+const ARRR: [i32; Y] = [99; 1]; //~ ERROR: expected usize value for array length
const Y1: usize = 42.0 >= 42.0;
-const ARRR1: [i32; Y] = [99; 1]; //~ ERROR: expected constant integer expression for array length
+const ARRR1: [i32; Y] = [99; 1]; //~ ERROR: expected usize value for array length
const Y2: usize = 42.0 <= 42.0;
-const ARRR2: [i32; Y] = [99; 1]; //~ ERROR: expected constant integer expression for array length
+const ARRR2: [i32; Y] = [99; 1]; //~ ERROR: expected usize value for array length
const Y3: usize = 42.0 > 42.0;
-const ARRR3: [i32; Y] = [99; 0]; //~ ERROR: expected constant integer expression for array length
+const ARRR3: [i32; Y] = [99; 0]; //~ ERROR: expected usize value for array length
const Y4: usize = 42.0 < 42.0;
-const ARRR4: [i32; Y] = [99; 0]; //~ ERROR: expected constant integer expression for array length
+const ARRR4: [i32; Y] = [99; 0]; //~ ERROR: expected usize value for array length
const Y5: usize = 42.0 != 42.0;
-const ARRR5: [i32; Y] = [99; 0]; //~ ERROR: expected constant integer expression for array length
+const ARRR5: [i32; Y] = [99; 0]; //~ ERROR: expected usize value for array length
fn main() {
let _ = ARR;
const ONE: usize = 1;
const TWO: usize = 2;
const LEN: usize = ONE - TWO;
-//~^ ERROR array length constant evaluation error: attempted to sub with overflow [E0250]
+//~^ ERROR array length constant evaluation error: attempted to subtract with overflow [E0250]
fn main() {
let a: [i8; LEN] = unimplemented!();
fn main() {
let a: [i8; ONE - TWO] = unimplemented!();
- //~^ ERROR array length constant evaluation error: attempted to sub with overflow [E0250]
+ //~^ ERROR array length constant evaluation error: attempted to subtract with overflow [E0250]
}
// Test spans of errors
const TUP: (usize,) = 5 << 64;
-//~^ ERROR: attempted left shift with overflow [E0250]
+//~^ ERROR: attempted to shift left with overflow [E0250]
const ARR: [i32; TUP.0] = [];
fn main() {
enum A {
Ok = i8::MAX - 1,
Ok2,
- OhNo, //~ ERROR enum discriminant overflowed on value after 127: i8; set explicitly via OhNo = -128 if that is desired outcome
+ OhNo, //~ ERROR enum discriminant overflowed on value after 127i8; set explicitly via OhNo = -128i8 if that is desired outcome
}
}
enum A {
Ok = u8::MAX - 1,
Ok2,
- OhNo, //~ ERROR enum discriminant overflowed on value after 255: u8; set explicitly via OhNo = 0 if that is desired outcome
+ OhNo, //~ ERROR enum discriminant overflowed on value after 255u8; set explicitly via OhNo = 0u8 if that is desired outcome
}
}
enum A {
Ok = i8::MAX - 1,
Ok2,
- OhNo, //~ ERROR enum discriminant overflowed on value after 127: i8; set explicitly via OhNo = -128 if that is desired outcome
+ OhNo, //~ ERROR enum discriminant overflowed on value after 127i8; set explicitly via OhNo = -128i8 if that is desired outcome
}
let x = A::Ok;
enum A {
Ok = u8::MAX - 1,
Ok2,
- OhNo, //~ ERROR enum discriminant overflowed on value after 255: u8; set explicitly via OhNo = 0 if that is desired outcome
+ OhNo, //~ ERROR enum discriminant overflowed on value after 255u8; set explicitly via OhNo = 0u8 if that is desired outcome
}
let x = A::Ok;
// except according to those terms.
-#[repr(u8)] //~ NOTE discriminant type specified here
+#[repr(u8)]
enum Eu8 {
Au8 = 23,
Bu8 = 223,
- Cu8 = -23, //~ ERROR discriminant value outside specified type
+ Cu8 = -23, //~ ERROR unary negation of unsigned integer
}
-#[repr(i8)] //~ NOTE discriminant type specified here
-enum Ei8 {
- Ai8 = 23,
- Bi8 = -23,
- Ci8 = 223, //~ ERROR discriminant value outside specified type
-}
-
-#[repr(u16)] //~ NOTE discriminant type specified here
+#[repr(u16)]
enum Eu16 {
Au16 = 23,
Bu16 = 55555,
- Cu16 = -22333, //~ ERROR discriminant value outside specified type
-}
-
-#[repr(i16)] //~ NOTE discriminant type specified here
-enum Ei16 {
- Ai16 = 23,
- Bi16 = -22333,
- Ci16 = 55555, //~ ERROR discriminant value outside specified type
+ Cu16 = -22333, //~ ERROR unary negation of unsigned integer
}
-#[repr(u32)] //~ NOTE discriminant type specified here
+#[repr(u32)]
enum Eu32 {
Au32 = 23,
Bu32 = 3_000_000_000,
- Cu32 = -2_000_000_000, //~ ERROR discriminant value outside specified type
+ Cu32 = -2_000_000_000, //~ ERROR unary negation of unsigned integer
}
-#[repr(i32)] //~ NOTE discriminant type specified here
-enum Ei32 {
- Ai32 = 23,
- Bi32 = -2_000_000_000,
- Ci32 = 3_000_000_000, //~ ERROR discriminant value outside specified type
+#[repr(u64)]
+enum Eu64 {
+ Au32 = 23,
+ Bu32 = 3_000_000_000,
+ Cu32 = -2_000_000_000, //~ ERROR unary negation of unsigned integer
}
// u64 currently allows negative numbers, and i64 allows numbers greater than `1<<63`. This is a
--- /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.
+
+#![deny(overflowing_literals)]
+#![allow(dead_code)]
+
+#[repr(i8)]
+enum Ei8 {
+ Ai8 = 23,
+ Bi8 = -23,
+ Ci8 = 223, //~ ERROR literal out of range for i8
+}
+
+#[repr(i16)]
+enum Ei16 {
+ Ai16 = 23,
+ Bi16 = -22333,
+ Ci16 = 55555, //~ ERROR literal out of range for i16
+}
+
+#[repr(i32)]
+enum Ei32 {
+ Ai32 = 23,
+ Bi32 = -2_000_000_000,
+ Ci32 = 3_000_000_000, //~ ERROR literal out of range for i32
+}
+
+#[repr(i64)]
+enum Ei64 {
+ Ai64 = 23,
+ Bi64 = -9223372036854775808,
+ Ci64 = 9223372036854775809, //~ ERROR literal out of range for i64
+}
+
+// u64 currently allows negative numbers, and i64 allows numbers greater than `1<<63`. This is a
+// little counterintuitive, but since the discriminant can store all the bits, and extracting it
+// with a cast requires specifying the signedness, there is no loss of information in those cases.
+// This also applies to isize and usize on 64-bit targets.
+
+pub fn main() { }
enum test {
div_zero = 1/0, //~ERROR constant evaluation error: attempted to divide by zero
- rem_zero = 1%0 //~ERROR constant evaluation error: attempted remainder with a divisor of zero
+ rem_zero = 1%0,
+//~^ ERROR constant evaluation error: attempted to calculate the remainder with a divisor of zero
}
fn main() {}
//~^^ HELP cast through a thin pointer
// #22955
- q as *const [i32]; //~ ERROR casting
+ q as *const [i32]; //~ ERROR cannot cast
// #21397
let t: *mut (Trait + 'static) = 0 as *mut _; //~ ERROR casting
//~| HELP use a cast or the `!` operator
fn main() {
- let a = -1;
- //~^ ERROR unary negation of unsigned integer
- //~| HELP use a cast or the `!` operator
- let _b : u8 = a; // for infering variable a to u8.
-
- -a;
- //~^ ERROR unary negation of unsigned integer
- //~| HELP use a cast or the `!` operator
-
- let _d = -1u8;
- //~^ ERROR unary negation of unsigned integer
- //~| HELP use a cast or the `!` operator
-
- for _ in -10..10u8 {}
- //~^ ERROR unary negation of unsigned integer
- //~| HELP use a cast or the `!` operator
-
+ let x = 5u8;
+ let _y = -x; //~ ERROR unary negation of unsigned integer
-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 unary negation of unsigned integer
+ let _b : u8 = a; // for infering variable a to u8.
+
+ let _d = -1u8;
+ //~^ ERROR unary negation of unsigned integer
+
+ for _ in -10..10u8 {}
+ //~^ ERROR unary negation of unsigned integer
+
+ -S; // should not trigger the gate; issue 26840
+}
enum Foo {
A = 1,
- B = 1, //~ ERROR discriminant value `1` already exists
+ B = 1, //~ ERROR discriminant value `1isize` already exists
//~^^ NOTE conflicting
C = 0,
- D, //~ ERROR discriminant value `1` already exists
+ D, //~ ERROR discriminant value `1isize` already exists
//~^^^^^ NOTE conflicting
- E = N, //~ ERROR discriminant value `1` already exists
+ E = N, //~ ERROR discriminant value `1isize` already exists
//~^^^^^^^ NOTE conflicting
}
struct MyStruct;
impl Drop for MyStruct {
-//~^ ERROR conflicting implementations of trait
+//~^ NOTE conflicting implementation is here
fn drop(&mut self) { }
}
impl Drop for MyStruct {
-//~^ NOTE conflicting implementation is here
+//~^ ERROR conflicting implementations of trait
fn drop(&mut self) { }
}
--- /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 cast_thin_to_fat(x: *const ()) {
+ x as *const [u8];
+ //~^ ERROR: cannot cast thin pointer `*const ()` to fat pointer `*const [u8]`
+}
+
+fn main() {}
assert!(thread::spawn(move|| { 1i64 / 0; }).join().is_err());
//~^ ERROR attempted to divide by zero
assert!(thread::spawn(move|| { isize::MIN % -1; }).join().is_err());
- //~^ ERROR attempted remainder with overflow
+ //~^ ERROR attempted to calculate the remainder with overflow
assert!(thread::spawn(move|| { i8::MIN % -1; }).join().is_err());
- //~^ ERROR attempted remainder with overflow
+ //~^ ERROR attempted to calculate the remainder with overflow
assert!(thread::spawn(move|| { i16::MIN % -1; }).join().is_err());
- //~^ ERROR attempted remainder with overflow
+ //~^ ERROR attempted to calculate the remainder with overflow
assert!(thread::spawn(move|| { i32::MIN % -1; }).join().is_err());
- //~^ ERROR attempted remainder with overflow
+ //~^ ERROR attempted to calculate the remainder with overflow
assert!(thread::spawn(move|| { i64::MIN % -1; }).join().is_err());
- //~^ ERROR attempted remainder with overflow
+ //~^ ERROR attempted to calculate the remainder with overflow
assert!(thread::spawn(move|| { 1isize % 0; }).join().is_err());
- //~^ ERROR attempted remainder with a divisor of zero
+ //~^ ERROR attempted to calculate the remainder with a divisor of zero
assert!(thread::spawn(move|| { 1i8 % 0; }).join().is_err());
- //~^ ERROR attempted remainder with a divisor of zero
+ //~^ ERROR attempted to calculate the remainder with a divisor of zero
assert!(thread::spawn(move|| { 1i16 % 0; }).join().is_err());
- //~^ ERROR attempted remainder with a divisor of zero
+ //~^ ERROR attempted to calculate the remainder with a divisor of zero
assert!(thread::spawn(move|| { 1i32 % 0; }).join().is_err());
- //~^ ERROR attempted remainder with a divisor of zero
+ //~^ ERROR attempted to calculate the remainder with a divisor of zero
assert!(thread::spawn(move|| { 1i64 % 0; }).join().is_err());
- //~^ ERROR attempted remainder with a divisor of zero
+ //~^ ERROR attempted to calculate the remainder with a divisor of zero
}
enum Foo {
A = 1i64,
- //~^ ERROR mismatched types
- //~| expected `isize`
- //~| found `i64`
+ //~^ ERROR mismatched types:
+ //~| expected `isize`,
+ //~| found `i64` [E0080]
B = 2u8
- //~^ ERROR mismatched types
- //~| expected `isize`
- //~| found `u8`
+ //~^ ERROR mismatched types:
+ //~| expected `isize`,
+ //~| found `u8` [E0080]
}
fn main() {}
return 123;
}
-fn baz() -> bool {
- 128 > bar() //~ ERROR comparison is useless due to type limits
- //~^ WARNING literal out of range for i8
-}
-
fn bleh() {
let u = 42u8;
let _ = u > 255; //~ ERROR comparison is useless due to type limits
let _ = u >= 0; //~ ERROR comparison is useless due to type limits
let _ = 0 <= u; //~ ERROR comparison is useless due to type limits
}
-
-fn qux() {
- let mut i = 1i8;
- while 200 != i { //~ ERROR comparison is useless due to type limits
- //~^ WARNING literal out of range for i8
- i += 1;
- }
-}
--- /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.
+
+#![allow(dead_code)]
+
+// compile-flags: -D unused-comparisons
+fn main() { }
+
+
+fn bar() -> i8 {
+ return 123;
+}
+
+fn baz() -> bool {
+ 128 > bar() //~ ERROR comparison is useless due to type limits
+ //~| WARN literal out of range for i8
+}
--- /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.
+
+#![allow(dead_code)]
+
+// compile-flags: -D unused-comparisons
+fn main() { }
+
+fn qux() {
+ let mut i = 1i8;
+ while 200 != i { //~ ERROR comparison is useless due to type limits
+ //~| WARN literal out of range for i8
+ i += 1;
+ }
+}
let x2: i8 = -128; // should be OK
let x1: i8 = 128; //~ error: literal out of range for i8
- let x2: i8 = --128; //~ error: literal out of range for i8
let x3: i8 = -129; //~ error: literal out of range for i8
let x3: i8 = -(129); //~ error: literal out of range for i8
let x = 18446744073709551615_i64; //~ error: literal out of range for i64
let x: i64 = -9223372036854775809; //~ error: literal out of range for i64
let x = -9223372036854775809_i64; //~ error: literal out of range for i64
-
- let x = -3.40282348e+38_f32; //~ error: literal out of range for f32
- let x = 3.40282348e+38_f32; //~ error: literal out of range for f32
- let x = -1.7976931348623159e+308_f64; //~ error: literal out of range for f64
- let x = 1.7976931348623159e+308_f64; //~ error: literal out of range for f64
}
--- /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.
+//
+
+#![deny(overflowing_literals)]
+
+#[allow(unused_variables)]
+fn main() {
+ let x2: i8 = --128; //~ error: literal out of range for i8
+
+ let x = -3.40282348e+38_f32; //~ error: literal out of range for f32
+ let x = 3.40282348e+38_f32; //~ error: literal out of range for f32
+ let x = -1.7976931348623159e+308_f64; //~ error: literal out of range for f64
+ let x = 1.7976931348623159e+308_f64; //~ error: literal out of range for f64
+}
use self::m::PubTr as PrivUseAliasTr;
type PrivAlias = m::Pub2;
trait PrivTr {
+ type AssocAlias;
+ }
+ impl PrivTr for Priv {
type AssocAlias = m::Pub3;
}
- impl PrivTr for Priv {}
pub fn f1(arg: PrivUseAlias) {} // OK
use self::PrivTr1 as PrivUseAliasTr;
type PrivAlias = Priv2;
trait PrivTr {
+ type AssocAlias;
+ }
+ impl PrivTr for Priv {
type AssocAlias = Priv3;
}
- impl PrivTr for Priv {}
pub trait Tr1: PrivUseAliasTr {} //~ WARN private trait in public interface
//~^ WARNING hard error
let f = [0; -4_isize];
//~^ ERROR mismatched types
//~| expected `usize`
- //~| found `isize`
- //~| ERROR expected positive integer for repeat count, found negative integer [E0306]
+ //~| found `isize` [E0308]
+ //~| ERROR mismatched types:
+ //~| expected `usize`,
+ //~| found `isize` [E0307]
let f = [0_usize; -1_isize];
//~^ ERROR mismatched types
//~| expected `usize`
- //~| found `isize`
- //~| ERROR expected positive integer for repeat count, found negative integer [E0306]
+ //~| found `isize` [E0308]
+ //~| ERROR mismatched types
+ //~| expected `usize`
+ //~| found `isize` [E0307]
struct G {
g: (),
}
--- /dev/null
+This directory contains the test for incorrect usage of specialization that
+should lead to compile failure. Those tests break down into a few categories:
+
+- Feature gating
+ - [On use of the `default` keyword](specialization-feature-gate-default.rs)
+ - [On overlapping impls](specialization-feature-gate-overlap.rs)
+
+- Overlap checking with specialization enabled
+ - [Basic overlap scenarios](specialization-overlap.rs)
+ - Includes purely structural overlap
+ - Includes purely trait-based overlap
+ - Includes mix
+ - [Overlap with differing polarity](specialization-overlap-negative.rs)
+
+- [Attempt to specialize without using `default`](specialization-no-default.rs)
+
+- [Attempt to change impl polarity in a specialization](specialization-polarity.rs)
+
+- Attempt to rely on projection of a `default` type
+ - [Rely on it externally in both generic and monomorphic contexts](specialization-default-projection.rs)
+ - [Rely on it both within an impl and outside it](specialization-default-types.rs)
--- /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(specialization)]
+
+// Make sure we can't project defaulted associated types
+
+trait Foo {
+ type Assoc;
+}
+
+impl<T> Foo for T {
+ default type Assoc = ();
+}
+
+impl Foo for u8 {
+ type Assoc = String;
+}
+
+fn generic<T>() -> <T as Foo>::Assoc {
+ // `T` could be some downstream crate type that specializes (or,
+ // for that matter, `u8`).
+
+ () //~ ERROR mismatched types
+}
+
+fn monomorphic() -> () {
+ // Even though we know that `()` is not specialized in a
+ // downstream crate, typeck refuses to project here.
+
+ generic::<()>() //~ ERROR mismatched types
+}
+
+fn main() {
+ // No error here, we CAN project from `u8`, as there is no `default`
+ // in that impl.
+ let s: String = generic::<u8>();
+ println!("{}", s); // bad news if this all compiles
+}
--- /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.
+
+// It should not be possible to use the concrete value of a defaulted
+// associated type in the impl defining it -- otherwise, what happens
+// if it's overridden?
+
+#![feature(specialization)]
+
+trait Example {
+ type Output;
+ fn generate(self) -> Self::Output;
+}
+
+impl<T> Example for T {
+ default type Output = Box<T>;
+ default fn generate(self) -> Self::Output {
+ Box::new(self) //~ ERROR mismatched types
+ }
+}
+
+impl Example for bool {
+ type Output = bool;
+ fn generate(self) -> bool { self }
+}
+
+fn trouble<T>(t: T) -> Box<T> {
+ Example::generate(t) //~ ERROR mismatched types
+}
+
+fn weaponize() -> bool {
+ let b: Box<bool> = trouble(true);
+ *b
+}
+
+fn main() {
+ weaponize();
+}
--- /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.
+
+// Check that specialization must be ungated to use the `default` keyword
+
+trait Foo {
+ fn foo(&self);
+}
+
+impl<T> Foo for T {
+ default fn foo(&self) {} //~ ERROR specialization is unstable
+}
+
+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.
+
+// Check that writing an overlapping impl is not allow unless specialization is ungated.
+
+trait Foo {
+ fn foo(&self);
+}
+
+impl<T> Foo for T {
+ fn foo(&self) {}
+}
+
+impl Foo for u8 { //~ ERROR E0119
+ fn foo(&self) {}
+}
+
+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.
+
+#![feature(specialization)]
+
+// Check a number of scenarios in which one impl tries to override another,
+// without correctly using `default`.
+
+////////////////////////////////////////////////////////////////////////////////
+// Test 1: one layer of specialization, multiple methods, missing `default`
+////////////////////////////////////////////////////////////////////////////////
+
+trait Foo {
+ fn foo(&self);
+ fn bar(&self);
+}
+
+impl<T> Foo for T {
+ fn foo(&self) {}
+ fn bar(&self) {}
+}
+
+impl Foo for u8 {}
+impl Foo for u16 {
+ fn foo(&self) {} //~ ERROR E0520
+}
+impl Foo for u32 {
+ fn bar(&self) {} //~ ERROR E0520
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Test 2: one layer of specialization, missing `default` on associated type
+////////////////////////////////////////////////////////////////////////////////
+
+trait Bar {
+ type T;
+}
+
+impl<T> Bar for T {
+ type T = u8;
+}
+
+impl Bar for u8 {
+ type T = (); //~ ERROR E0520
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Test 3a: multiple layers of specialization, missing interior `default`
+////////////////////////////////////////////////////////////////////////////////
+
+trait Baz {
+ fn baz(&self);
+}
+
+impl<T> Baz for T {
+ default fn baz(&self) {}
+}
+
+impl<T: Clone> Baz for T {
+ fn baz(&self) {}
+}
+
+impl Baz for i32 {
+ fn baz(&self) {} //~ ERROR E0520
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Test 3b: multiple layers of specialization, missing interior `default`,
+// redundant `default` in bottom layer.
+////////////////////////////////////////////////////////////////////////////////
+
+trait Redundant {
+ fn redundant(&self);
+}
+
+impl<T> Redundant for T {
+ default fn redundant(&self) {}
+}
+
+impl<T: Clone> Redundant for T {
+ fn redundant(&self) {}
+}
+
+impl Redundant for i32 {
+ default fn redundant(&self) {} //~ ERROR E0520
+}
+
+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.
+
+#![feature(optin_builtin_traits)]
+#![feature(specialization)]
+
+trait MyTrait {}
+
+struct TestType<T>(::std::marker::PhantomData<T>);
+
+unsafe impl<T: Clone> Send for TestType<T> {}
+impl<T: MyTrait> !Send for TestType<T> {} //~ ERROR E0119
+
+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.
+
+#![feature(specialization)]
+
+trait Foo {}
+impl<T: Clone> Foo for T {}
+impl<T> Foo for Vec<T> {} //~ ERROR E0119
+
+trait Bar {}
+impl<T> Bar for (T, u8) {}
+impl<T> Bar for (u8, T) {} //~ ERROR E0119
+
+trait Baz<U> {}
+impl<T> Baz<T> for u8 {}
+impl<T> Baz<u8> for T {} //~ ERROR E0119
+
+trait Qux {}
+impl<T: Clone> Qux for T {}
+impl<T: Eq> Qux for T {} //~ ERROR E0119
+
+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.
+
+// Make sure specialization cannot change impl polarity
+
+#![feature(optin_builtin_traits)]
+#![feature(specialization)]
+
+trait Foo {}
+
+impl Foo for .. {}
+
+impl<T> Foo for T {}
+impl !Foo for u8 {} //~ ERROR E0119
+
+trait Bar {}
+
+impl Bar for .. {}
+
+impl<T> !Bar for T {}
+impl Bar for u8 {} //~ ERROR E0119
+
+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.
+
+// compile-flags: -Z parse-only
+
+// Test successful and unsucessful parsing of the `default` contextual keyword
+
+trait Foo {
+ fn foo<T: Default>() -> T;
+}
+
+impl Foo for u8 {
+ default fn foo<T: Default>() -> T {
+ T::default()
+ }
+}
+
+impl Foo for u16 {
+ pub default fn foo<T: Default>() -> T {
+ T::default()
+ }
+}
+
+impl Foo for u32 {
+ default pub fn foo<T: Default>() -> T { T::default() } //~ ERROR expected one of
+}
+
+fn main() {}
fn foo() {}
#[stable(feature = "rust1", since = "1.0.0")]
-} //~ ERROR expected one of `const`, `extern`, `fn`, `pub`, `type`, or `unsafe`, found `}`
+} //~ ERROR expected one of `const`, `default`, `extern`, `fn`, `pub`, `type`, or `unsafe`
fn main() {}
impl Foo {
#[stable(feature = "rust1", since = "1.0.0")]
-} //~ ERROR expected one of `const`, `extern`, `fn`, `pub`, `type`, or `unsafe`, found `}`
+} //~ ERROR expected one of `const`, `default`, `extern`, `fn`, `pub`, `type`, or `unsafe`
fn main() {}
impl S {
static fn f() {}
}
-//~^^ ERROR expected one of `const`, `extern`, `fn`, `pub`, `type`, `unsafe`, or `}`, found `static`
+//~^^ ERROR expected one of `const`, `default`, `extern`, `fn`, `pub`, `type`, `unsafe`, or `}`
/// for crates used in the given input.
fn compile_program(input: &str, sysroot: PathBuf)
-> Option<(llvm::ModuleRef, Vec<PathBuf>)> {
- let input = Input::Str(input.to_string());
+ let input = Input::Str {
+ name: driver::anon_src(),
+ input: input.to_string(),
+ };
let thread = Builder::new().name("compile_program".to_string());
let handle = thread.spawn(move || {
use rustc::session::{build_session, Session};
use rustc::session::config::{basic_options, build_configuration, Input, OutputType};
-use rustc_driver::driver::{compile_input, CompileController};
+use rustc_driver::driver::{compile_input, CompileController, anon_src};
use rustc_metadata::cstore::CStore;
use syntax::diagnostics::registry::Registry;
use syntax::parse::token;
compile_input(&sess, &cstore,
cfg,
- &Input::Str(code),
+ &Input::Str { name: anon_src(), input: code },
&None,
&Some(output),
None,
// A very basic test of const fn functionality.
-#![feature(const_fn)]
+#![feature(const_fn, const_indexing)]
const fn add(x: u32, y: u32) -> u32 {
x + y
x / y
}
+const fn generic<T>(t: T) -> T {
+ t
+}
+
+const fn generic_arr<T: Copy>(t: [T; 1]) -> T {
+ t[0]
+}
+
const SUM: u32 = add(44, 22);
const DIFF: u32 = sub(44, 22);
const DIV: u32 = unsafe{div(44, 22)};
assert_eq!(DIV, 2);
let _: [&'static str; sub(100, 99) as usize] = ["hi"];
+ let _: [&'static str; generic(1)] = ["hi"];
+ let _: [&'static str; generic_arr([1])] = ["hi"];
}
--- /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(stmt_expr_attributes)]
+
+#[deny(const_err)]
+
+fn main() {
+ #[cfg(target_pointer_width = "32")]
+ const I: isize = -2147483648isize;
+ #[cfg(target_pointer_width = "64")]
+ const I: isize = -9223372036854775808isize;
+ assert_eq!(::std::i32::MIN as u64, 0xffffffff80000000);
+ assert_eq!(-2147483648isize as u64, 0xffffffff80000000);
+ assert_eq!(::std::i64::MIN as u64, 0x8000000000000000);
+ #[cfg(target_pointer_width = "64")]
+ assert_eq!(-9223372036854775808isize as u64, 0x8000000000000000);
+ #[cfg(target_pointer_width = "32")]
+ assert_eq!(-9223372036854775808isize as u64, 0);
+ const J: usize = ::std::i32::MAX as usize;
+ const K: usize = -1i32 as u32 as usize;
+ const L: usize = ::std::i32::MIN as usize;
+ const M: usize = ::std::i64::MIN as usize;
+ match 5 {
+ J => {},
+ K => {},
+ L => {},
+ M => {},
+ _ => {}
+ }
+ match 5 {
+ I => {},
+ _ => {}
+ }
+}
#![feature(associated_type_defaults)]
-trait Foo<T> {
- type Out = T;
- fn foo(&self) -> Self::Out;
+trait Foo<T: Default + ToString> {
+ type Out: Default + ToString = T;
}
impl Foo<u32> for () {
- fn foo(&self) -> u32 {
- 4u32
- }
}
-impl Foo<u64> for bool {
- type Out = ();
- fn foo(&self) {}
+impl Foo<u64> for () {
+ type Out = bool;
}
fn main() {
- assert_eq!(<() as Foo<u32>>::foo(&()), 4u32);
- assert_eq!(<bool as Foo<u64>>::foo(&true), ());
+ assert_eq!(
+ <() as Foo<u32>>::Out::default().to_string(),
+ "0");
+ assert_eq!(
+ <() as Foo<u64>>::Out::default().to_string(),
+ "false");
}
// option. This file may not be copied, modified, or distributed
// except according to those terms.
+#![feature(stmt_expr_attributes)]
use std::mem::size_of;
Bi64 = 0x8000_0000
}
-enum Eu64 {
- Au64 = 0,
- Bu64 = 0x8000_0000_0000_0000
-}
-
pub fn main() {
assert_eq!(size_of::<Ei8>(), 1);
assert_eq!(size_of::<Eu8>(), 1);
assert_eq!(size_of::<Eu16>(), 2);
assert_eq!(size_of::<Ei32>(), 4);
assert_eq!(size_of::<Eu32>(), 4);
+ #[cfg(target_pointer_width = "64")]
assert_eq!(size_of::<Ei64>(), 8);
- assert_eq!(size_of::<Eu64>(), 8);
+ #[cfg(target_pointer_width = "32")]
+ assert_eq!(size_of::<Ei64>(), 4);
}
--- /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.
+
+use std::fmt;
+use std::{i8, i16, i32, i64, isize};
+use std::{u8, u16, u32, u64, usize};
+
+const A_I8_T
+ : [u32; (i8::MAX as i8 - 1i8) as usize]
+ = [0; (i8::MAX as usize) - 1];
+
+fn main() {
+ foo(&A_I8_T[..]);
+}
+
+fn foo<T:fmt::Debug>(x: T) {
+ println!("{:?}", x);
+}
--- /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.
+
+// write_volatile causes an LLVM assert with composite types
+
+#![feature(volatile)]
+use std::ptr::{read_volatile, write_volatile};
+
+#[derive(Debug, Eq, PartialEq)]
+struct A(u32);
+#[derive(Debug, Eq, PartialEq)]
+struct B(u64);
+#[derive(Debug, Eq, PartialEq)]
+struct C(u32, u32);
+#[derive(Debug, Eq, PartialEq)]
+struct D(u64, u64);
+#[derive(Debug, Eq, PartialEq)]
+struct E([u64; 32]);
+
+fn main() {
+ unsafe {
+ let mut x: u32 = 0;
+ write_volatile(&mut x, 1);
+ assert_eq!(read_volatile(&x), 1);
+ assert_eq!(x, 1);
+
+ let mut x: u64 = 0;
+ write_volatile(&mut x, 1);
+ assert_eq!(read_volatile(&x), 1);
+ assert_eq!(x, 1);
+
+ let mut x = A(0);
+ write_volatile(&mut x, A(1));
+ assert_eq!(read_volatile(&x), A(1));
+ assert_eq!(x, A(1));
+
+ let mut x = B(0);
+ write_volatile(&mut x, B(1));
+ assert_eq!(read_volatile(&x), B(1));
+ assert_eq!(x, B(1));
+
+ let mut x = C(0, 0);
+ write_volatile(&mut x, C(1, 1));
+ assert_eq!(read_volatile(&x), C(1, 1));
+ assert_eq!(x, C(1, 1));
+
+ let mut x = D(0, 0);
+ write_volatile(&mut x, D(1, 1));
+ assert_eq!(read_volatile(&x), D(1, 1));
+ assert_eq!(x, D(1, 1));
+
+ let mut x = E([0; 32]);
+ write_volatile(&mut x, E([1; 32]));
+ assert_eq!(read_volatile(&x), E([1; 32]));
+ assert_eq!(x, E([1; 32]));
+ }
+}
--- /dev/null
+Tests that specialization is working correctly:
+
+- Dispatch
+ - [On methods](specialization-basics.rs), includes:
+ - Specialization via adding a trait bound
+ - Including both remote and local traits
+ - Specialization via pure structure (e.g. `(T, U)` vs `(T, T)`)
+ - Specialization via concrete types vs unknown types
+ - In top level of the trait reference
+ - Embedded within another type (`Vec<T>` vs `Vec<i32>`)
+ - [Specialization based on super trait relationships](specialization-super-traits.rs)
+ - [On assoc fns](specialization-assoc-fns.rs)
+ - [Ensure that impl order doesn't matter](specialization-out-of-order.rs)
+
+- Item inheritance
+ - [Correct default cascading for methods](specialization-default-methods.rs)
+ - Inheritance works across impls with varying generics
+ - [With projections](specialization-translate-projections.rs)
+ - [With projections that involve input types](specialization-translate-projections-with-params.rs)
+
+- Normalization issues
+ - [Non-default assoc types can be projected](specialization-projection.rs)
+ - Including non-specialized cases
+ - Including specialized cases
+ - [Specialized Impls can happen on projections](specialization-on-projection.rs)
+ - [Projections and aliases play well together](specialization-projection-alias.rs)
+ - [Projections involving specialization allowed in the trait ref for impls, and overlap can still be determined](specialization-overlap-projection.rs)
+ - Only works for the simple case where the most specialized impl directly
+ provides a non-`default` associated type
+
+- Across crates
+ - [For traits defined in upstream crate](specialization-allowed-cross-crate.rs)
+ - [Full method dispatch tests, drawing from upstream crate](specialization-cross-crate.rs)
+ - Including *additional* local specializations
+ - [Full method dispatch tests, *without* turning on specialization in local crate](specialization-cross-crate-no-gate.rs)
+ - [Test that defaults cascade correctly from upstream crates](specialization-cross-crate-defaults.rs)
+ - Including *additional* local use of defaults
--- /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:go_trait.rs
+
+#![feature(specialization)]
+
+extern crate go_trait;
+
+use go_trait::{Go,GoMut};
+use std::fmt::Debug;
+use std::default::Default;
+
+struct MyThingy;
+
+impl Go for MyThingy {
+ fn go(&self, arg: isize) { }
+}
+
+impl GoMut for MyThingy {
+ fn go_mut(&mut self, arg: isize) { }
+}
+
+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.
+
+// Test that non-method associated functions can be specialized
+
+#![feature(specialization)]
+
+trait Foo {
+ fn mk() -> Self;
+}
+
+impl<T: Default> Foo for T {
+ default fn mk() -> T {
+ T::default()
+ }
+}
+
+impl Foo for Vec<u8> {
+ fn mk() -> Vec<u8> {
+ vec![0]
+ }
+}
+
+fn main() {
+ let v1: Vec<i32> = Foo::mk();
+ let v2: Vec<u8> = Foo::mk();
+
+ assert!(v1.len() == 0);
+ assert!(v2.len() == 1);
+}
--- /dev/null
+// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(specialization)]
+
+// Tests a variety of basic specialization scenarios and method
+// dispatch for them.
+
+trait Foo {
+ fn foo(&self) -> &'static str;
+}
+
+impl<T> Foo for T {
+ default fn foo(&self) -> &'static str {
+ "generic"
+ }
+}
+
+impl<T: Clone> Foo for T {
+ default fn foo(&self) -> &'static str {
+ "generic Clone"
+ }
+}
+
+impl<T, U> Foo for (T, U) where T: Clone, U: Clone {
+ default fn foo(&self) -> &'static str {
+ "generic pair"
+ }
+}
+
+impl<T: Clone> Foo for (T, T) {
+ default fn foo(&self) -> &'static str {
+ "generic uniform pair"
+ }
+}
+
+impl Foo for (u8, u32) {
+ default fn foo(&self) -> &'static str {
+ "(u8, u32)"
+ }
+}
+
+impl Foo for (u8, u8) {
+ default fn foo(&self) -> &'static str {
+ "(u8, u8)"
+ }
+}
+
+impl<T: Clone> Foo for Vec<T> {
+ default fn foo(&self) -> &'static str {
+ "generic Vec"
+ }
+}
+
+impl Foo for Vec<i32> {
+ fn foo(&self) -> &'static str {
+ "Vec<i32>"
+ }
+}
+
+impl Foo for String {
+ fn foo(&self) -> &'static str {
+ "String"
+ }
+}
+
+impl Foo for i32 {
+ fn foo(&self) -> &'static str {
+ "i32"
+ }
+}
+
+struct NotClone;
+
+trait MyMarker {}
+impl<T: Clone + MyMarker> Foo for T {
+ default fn foo(&self) -> &'static str {
+ "generic Clone + MyMarker"
+ }
+}
+
+#[derive(Clone)]
+struct MarkedAndClone;
+impl MyMarker for MarkedAndClone {}
+
+fn main() {
+ assert!(NotClone.foo() == "generic");
+ assert!(0u8.foo() == "generic Clone");
+ assert!(vec![NotClone].foo() == "generic");
+ assert!(vec![0u8].foo() == "generic Vec");
+ assert!(vec![0i32].foo() == "Vec<i32>");
+ assert!(0i32.foo() == "i32");
+ assert!(String::new().foo() == "String");
+ assert!(((), 0).foo() == "generic pair");
+ assert!(((), ()).foo() == "generic uniform pair");
+ assert!((0u8, 0u32).foo() == "(u8, u32)");
+ assert!((0u8, 0u8).foo() == "(u8, u8)");
+ assert!(MarkedAndClone.foo() == "generic Clone + MyMarker");
+}
--- /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.
+
+// aux-build:specialization_cross_crate_defaults.rs
+
+#![feature(specialization)]
+
+extern crate specialization_cross_crate_defaults;
+
+use specialization_cross_crate_defaults::*;
+
+struct LocalDefault;
+struct LocalOverride;
+
+impl Foo for LocalDefault {}
+
+impl Foo for LocalOverride {
+ fn foo(&self) -> bool { true }
+}
+
+fn test_foo() {
+ assert!(0i8.foo() == false);
+ assert!(0i32.foo() == false);
+ assert!(0i64.foo() == true);
+
+ assert!(LocalDefault.foo() == false);
+ assert!(LocalOverride.foo() == true);
+}
+
+fn test_bar() {
+ assert!(0u8.bar() == 0);
+ assert!(0i32.bar() == 1);
+ assert!("hello".bar() == 0);
+ assert!(vec![()].bar() == 2);
+ assert!(vec![0i32].bar() == 2);
+ assert!(vec![0i64].bar() == 3);
+}
+
+fn main() {
+ test_foo();
+ test_bar();
+}
--- /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 specialization works even if only the upstream crate enables it
+
+// aux-build:specialization_cross_crate.rs
+
+extern crate specialization_cross_crate;
+
+use specialization_cross_crate::*;
+
+fn main() {
+ assert!(0u8.foo() == "generic Clone");
+ assert!(vec![0u8].foo() == "generic Vec");
+ assert!(vec![0i32].foo() == "Vec<i32>");
+ assert!(0i32.foo() == "i32");
+ assert!(String::new().foo() == "String");
+ assert!(((), 0).foo() == "generic pair");
+ assert!(((), ()).foo() == "generic uniform pair");
+ assert!((0u8, 0u32).foo() == "(u8, u32)");
+ assert!((0u8, 0u8).foo() == "(u8, 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.
+
+// aux-build:specialization_cross_crate.rs
+
+#![feature(specialization)]
+
+extern crate specialization_cross_crate;
+
+use specialization_cross_crate::*;
+
+struct NotClone;
+
+#[derive(Clone)]
+struct MarkedAndClone;
+impl MyMarker for MarkedAndClone {}
+
+struct MyType<T>(T);
+impl<T> Foo for MyType<T> {
+ default fn foo(&self) -> &'static str {
+ "generic MyType"
+ }
+}
+
+impl Foo for MyType<u8> {
+ fn foo(&self) -> &'static str {
+ "MyType<u8>"
+ }
+}
+
+struct MyOtherType;
+impl Foo for MyOtherType {}
+
+fn main() {
+ assert!(NotClone.foo() == "generic");
+ assert!(0u8.foo() == "generic Clone");
+ assert!(vec![NotClone].foo() == "generic");
+ assert!(vec![0u8].foo() == "generic Vec");
+ assert!(vec![0i32].foo() == "Vec<i32>");
+ assert!(0i32.foo() == "i32");
+ assert!(String::new().foo() == "String");
+ assert!(((), 0).foo() == "generic pair");
+ assert!(((), ()).foo() == "generic uniform pair");
+ assert!((0u8, 0u32).foo() == "(u8, u32)");
+ assert!((0u8, 0u8).foo() == "(u8, u8)");
+ assert!(MarkedAndClone.foo() == "generic Clone + MyMarker");
+
+ assert!(MyType(()).foo() == "generic MyType");
+ assert!(MyType(0u8).foo() == "MyType<u8>");
+ assert!(MyOtherType.foo() == "generic");
+}
--- /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(specialization)]
+
+// Test that default methods are cascaded correctly
+
+// First, test only use of explicit `default` items:
+
+trait Foo {
+ fn foo(&self) -> bool;
+}
+
+// Specialization tree for Foo:
+//
+// T
+// / \
+// i32 i64
+
+impl<T> Foo for T {
+ default fn foo(&self) -> bool { false }
+}
+
+impl Foo for i32 {}
+
+impl Foo for i64 {
+ fn foo(&self) -> bool { true }
+}
+
+fn test_foo() {
+ assert!(0i8.foo() == false);
+ assert!(0i32.foo() == false);
+ assert!(0i64.foo() == true);
+}
+
+// Next, test mixture of explicit `default` and provided methods:
+
+trait Bar {
+ fn bar(&self) -> i32 { 0 }
+}
+
+// Specialization tree for Bar.
+// Uses of $ designate that method is provided
+//
+// $Bar (the trait)
+// |
+// T
+// /|\
+// / | \
+// / | \
+// / | \
+// / | \
+// / | \
+// $i32 &str $Vec<T>
+// /\
+// / \
+// Vec<i32> $Vec<i64>
+
+// use the provided method
+impl<T> Bar for T {}
+
+impl Bar for i32 {
+ fn bar(&self) -> i32 { 1 }
+}
+impl<'a> Bar for &'a str {}
+
+impl<T> Bar for Vec<T> {
+ default fn bar(&self) -> i32 { 2 }
+}
+impl Bar for Vec<i32> {}
+impl Bar for Vec<i64> {
+ fn bar(&self) -> i32 { 3 }
+}
+
+fn test_bar() {
+ assert!(0u8.bar() == 0);
+ assert!(0i32.bar() == 1);
+ assert!("hello".bar() == 0);
+ assert!(vec![()].bar() == 2);
+ assert!(vec![0i32].bar() == 2);
+ assert!(vec![0i64].bar() == 3);
+}
+
+fn main() {
+ test_foo();
+ test_bar();
+}
--- /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(specialization)]
+
+// Ensure that specialization works for impls defined directly on a projection
+
+trait Foo<T> {}
+
+trait Assoc {
+ type Item;
+}
+
+impl<T: Assoc> Foo<T::Item> for T {}
+
+struct Struct;
+
+impl Assoc for Struct {
+ type Item = u8;
+}
+
+impl Foo<u8> for Struct {}
+
+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.
+
+// Test that you can list the more specific impl before the more general one.
+
+#![feature(specialization)]
+
+trait Foo {
+ type Out;
+}
+
+impl Foo for bool {
+ type Out = ();
+}
+
+impl<T> Foo for T {
+ default type Out = 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.
+
+// Test that impls on projected self types can resolve overlap, even when the
+// projections involve specialization, so long as the associated type is
+// provided by the most specialized impl.
+
+#![feature(specialization)]
+
+trait Assoc {
+ type Output;
+}
+
+impl<T> Assoc for T {
+ default type Output = bool;
+}
+
+impl Assoc for u8 { type Output = u8; }
+impl Assoc for u16 { type Output = u16; }
+
+trait Foo {}
+impl Foo for u32 {}
+impl Foo for <u8 as Assoc>::Output {}
+impl Foo for <u16 as Assoc>::Output {}
+
+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.
+
+#![feature(specialization)]
+
+// Regression test for ICE when combining specialized associated types and type
+// aliases
+
+trait Id_ {
+ type Out;
+}
+
+type Id<T> = <T as Id_>::Out;
+
+impl<T> Id_ for T {
+ default type Out = T;
+}
+
+fn test_proection() {
+ let x: Id<bool> = panic!();
+}
+
+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.
+
+#![feature(specialization)]
+
+// Make sure we *can* project non-defaulted associated types
+// cf compile-fail/specialization-default-projection.rs
+
+// First, do so without any use of specialization
+
+trait Foo {
+ type Assoc;
+}
+
+impl<T> Foo for T {
+ type Assoc = ();
+}
+
+fn generic_foo<T>() -> <T as Foo>::Assoc {
+ ()
+}
+
+// Next, allow for one layer of specialization
+
+trait Bar {
+ type Assoc;
+}
+
+impl<T> Bar for T {
+ default type Assoc = ();
+}
+
+impl<T: Clone> Bar for T {
+ type Assoc = u8;
+}
+
+fn generic_bar_clone<T: Clone>() -> <T as Bar>::Assoc {
+ 0u8
+}
+
+fn main() {
+}
--- /dev/null
+// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(specialization)]
+
+// Test that you can specialize via an explicit trait hierarchy
+
+// FIXME: this doesn't work yet...
+
+trait Parent {}
+trait Child: Parent {}
+
+trait Foo {}
+
+impl<T: Parent> Foo for T {}
+impl<T: Child> Foo for T {}
+
+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.
+
+// Ensure that provided items are inherited properly even when impls vary in
+// type parameters *and* rely on projections, and the type parameters are input
+// types on the trait.
+
+#![feature(specialization)]
+
+trait Trait<T> {
+ fn convert(&self) -> T;
+}
+trait WithAssoc {
+ type Item;
+ fn as_item(&self) -> &Self::Item;
+}
+
+impl<T, U> Trait<U> for T where T: WithAssoc<Item=U>, U: Clone {
+ fn convert(&self) -> U {
+ self.as_item().clone()
+ }
+}
+
+impl WithAssoc for u8 {
+ type Item = u8;
+ fn as_item(&self) -> &u8 { self }
+}
+
+impl Trait<u8> for u8 {}
+
+fn main() {
+ assert!(3u8.convert() == 3u8);
+}
--- /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.
+
+// Ensure that provided items are inherited properly even when impls vary in
+// type parameters *and* rely on projections.
+
+#![feature(specialization)]
+
+use std::convert::Into;
+
+trait Trait {
+ fn to_u8(&self) -> u8;
+}
+trait WithAssoc {
+ type Item;
+ fn to_item(&self) -> Self::Item;
+}
+
+impl<T, U> Trait for T where T: WithAssoc<Item=U>, U: Into<u8> {
+ fn to_u8(&self) -> u8 {
+ self.to_item().into()
+ }
+}
+
+impl WithAssoc for u8 {
+ type Item = u8;
+ fn to_item(&self) -> u8 { *self }
+}
+
+impl Trait for u8 {}
+
+fn main() {
+ assert!(3u8.to_u8() == 3u8);
+}
extern crate xcrate_associated_type_defaults;
use xcrate_associated_type_defaults::Foo;
+struct LocalDefault;
+impl Foo<u32> for LocalDefault {}
+
+struct LocalOverride;
+impl Foo<u64> for LocalOverride {
+ type Out = bool;
+}
+
fn main() {
- ().bar(5);
+ assert_eq!(
+ <() as Foo<u32>>::Out::default().to_string(),
+ "0");
+ assert_eq!(
+ <() as Foo<u64>>::Out::default().to_string(),
+ "false");
+
+ assert_eq!(
+ <LocalDefault as Foo<u32>>::Out::default().to_string(),
+ "0");
+ assert_eq!(
+ <LocalOverride as Foo<u64>>::Out::default().to_string(),
+ "false");
}
// Unfortunately we're not 100% full of valid links today to we need a few
// whitelists to get this past `make check` today.
- if let Some(path) = pretty_file.to_str() {
- // FIXME(#32129)
- if path == "std/string/struct.String.html" {
- return
- }
- // FIXME(#32130)
- if path.contains("btree_set/struct.BTreeSet.html") ||
- path == "collections/struct.BTreeSet.html" {
- return
- }
- // FIXME(#31948)
- if path.contains("ParseFloatError") {
- return
- }
+ // FIXME(#32129)
+ if file.ends_with("std/string/struct.String.html") {
+ return
+ }
+ // FIXME(#32130)
+ if file.ends_with("btree_set/struct.BTreeSet.html") ||
+ file.ends_with("collections/struct.BTreeSet.html") {
+ return
+ }
+
+ if file.ends_with("std/sys/ext/index.html") {
+ return
+ }
- // currently
- if path == "std/sys/ext/index.html" {
+ if let Some(file) = file.to_str() {
+ // FIXME(#31948)
+ if file.contains("ParseFloatError") {
return
}
-
// weird reexports, but this module is on its way out, so chalk it up to
// "rustdoc weirdness" and move on from there
- if path.contains("scoped_tls") {
+ if file.contains("scoped_tls") {
return
}
}