From 95256c97ee120d696ba292f48dacaed45019f3a2 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 28 Jan 2016 11:10:33 +0100 Subject: [PATCH 1/2] move check_match out of librustc --- mk/crates.mk | 8 +- src/librustc/diagnostics.rs | 394 ----------------- src/librustc/lib.rs | 1 - src/librustc_const_eval/diagnostics.rs | 412 ++++++++++++++++++ src/librustc_const_eval/lib.rs | 41 ++ .../matches.rs} | 69 +-- src/librustc_driver/driver.rs | 2 +- src/librustc_driver/lib.rs | 1 + src/librustc_trans/lib.rs | 1 + src/librustc_trans/trans/_match.rs | 33 +- 10 files changed, 512 insertions(+), 450 deletions(-) create mode 100644 src/librustc_const_eval/diagnostics.rs create mode 100644 src/librustc_const_eval/lib.rs rename src/{librustc/middle/check_match.rs => librustc_const_eval/matches.rs} (96%) diff --git a/mk/crates.mk b/mk/crates.mk index f47c4857ef861..0663668c98eec 100644 --- a/mk/crates.mk +++ b/mk/crates.mk @@ -57,7 +57,7 @@ TARGET_CRATES := libc std flate arena term \ 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 \ - rustc_plugin rustc_metadata rustc_passes + rustc_plugin rustc_metadata rustc_passes rustc_const_eval HOST_CRATES := syntax syntax_ext $(RUSTC_CRATES) rustdoc fmt_macros TOOLS := compiletest rustdoc rustc rustbook error-index-generator @@ -97,7 +97,7 @@ DEPS_rustc_data_structures := std log serialize DEPS_rustc_driver := arena flate getopts graphviz libc rustc rustc_back rustc_borrowck \ rustc_typeck rustc_mir rustc_resolve log syntax serialize rustc_llvm \ rustc_trans rustc_privacy rustc_lint rustc_front rustc_plugin \ - rustc_metadata syntax_ext rustc_passes + rustc_metadata syntax_ext rustc_passes rustc_const_eval DEPS_rustc_front := std syntax log serialize DEPS_rustc_lint := rustc log syntax DEPS_rustc_llvm := native:rustllvm libc std rustc_bitflags @@ -109,8 +109,10 @@ DEPS_rustc_platform_intrinsics := rustc rustc_llvm DEPS_rustc_plugin := rustc rustc_metadata syntax 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 + log syntax serialize rustc_llvm rustc_front rustc_platform_intrinsics \ + rustc_const_eval DEPS_rustc_typeck := rustc syntax rustc_front rustc_platform_intrinsics +DEPS_rustc_const_eval := rustc rustc_front rustc_back syntax DEPS_rustdoc := rustc rustc_driver native:hoedown serialize getopts \ test rustc_lint rustc_front diff --git a/src/librustc/diagnostics.rs b/src/librustc/diagnostics.rs index 9dbc75b960ef9..97b07a2a1e623 100644 --- a/src/librustc/diagnostics.rs +++ b/src/librustc/diagnostics.rs @@ -15,206 +15,6 @@ // In vim you can `:set tw=80` and use `gq` to wrap paragraphs. Use `:set tw=0` to disable. register_long_diagnostics! { -E0001: r##" -This error suggests that the expression arm corresponding to the noted pattern -will never be reached as for all possible values of the expression being -matched, one of the preceding patterns will match. - -This means that perhaps some of the preceding patterns are too general, this one -is too specific or the ordering is incorrect. - -For example, the following `match` block has too many arms: - -``` -match foo { - Some(bar) => {/* ... */} - None => {/* ... */} - _ => {/* ... */} // All possible cases have already been handled -} -``` - -`match` blocks have their patterns matched in order, so, for example, putting -a wildcard arm above a more specific arm will make the latter arm irrelevant. - -Ensure the ordering of the match arm is correct and remove any superfluous -arms. -"##, - -E0002: r##" -This error indicates that an empty match expression is invalid because the type -it is matching on is non-empty (there exist values of this type). In safe code -it is impossible to create an instance of an empty type, so empty match -expressions are almost never desired. This error is typically fixed by adding -one or more cases to the match expression. - -An example of an empty type is `enum Empty { }`. So, the following will work: - -``` -fn foo(x: Empty) { - match x { - // empty - } -} -``` - -However, this won't: - -``` -fn foo(x: Option) { - match x { - // empty - } -} -``` -"##, - -E0003: r##" -Not-a-Number (NaN) values cannot be compared for equality and hence can never -match the input to a match expression. So, the following will not compile: - -``` -const NAN: f32 = 0.0 / 0.0; - -match number { - NAN => { /* ... */ }, - // ... -} -``` - -To match against NaN values, you should instead use the `is_nan()` method in a -guard, like so: - -``` -match number { - // ... - x if x.is_nan() => { /* ... */ } - // ... -} -``` -"##, - -E0004: r##" -This error indicates that the compiler cannot guarantee a matching pattern for -one or more possible inputs to a match expression. Guaranteed matches are -required in order to assign values to match expressions, or alternatively, -determine the flow of execution. - -If you encounter this error you must alter your patterns so that every possible -value of the input type is matched. For types with a small number of variants -(like enums) you should probably cover all cases explicitly. Alternatively, the -underscore `_` wildcard pattern can be added after all other patterns to match -"anything else". -"##, - -E0005: r##" -Patterns used to bind names must be irrefutable, that is, they must guarantee -that a name will be extracted in all cases. If you encounter this error you -probably need to use a `match` or `if let` to deal with the possibility of -failure. -"##, - -E0007: r##" -This error indicates that the bindings in a match arm would require a value to -be moved into more than one location, thus violating unique ownership. Code like -the following is invalid as it requires the entire `Option` to be moved -into a variable called `op_string` while simultaneously requiring the inner -String to be moved into a variable called `s`. - -``` -let x = Some("s".to_string()); -match x { - op_string @ Some(s) => ... - None => ... -} -``` - -See also Error 303. -"##, - -E0008: r##" -Names bound in match arms retain their type in pattern guards. As such, if a -name is bound by move in a pattern, it should also be moved to wherever it is -referenced in the pattern guard code. Doing so however would prevent the name -from being available in the body of the match arm. Consider the following: - -``` -match Some("hi".to_string()) { - Some(s) if s.len() == 0 => // use s. - ... -} -``` - -The variable `s` has type `String`, and its use in the guard is as a variable of -type `String`. The guard code effectively executes in a separate scope to the -body of the arm, so the value would be moved into this anonymous scope and -therefore become unavailable in the body of the arm. Although this example seems -innocuous, the problem is most clear when considering functions that take their -argument by value. - -``` -match Some("hi".to_string()) { - Some(s) if { drop(s); false } => (), - Some(s) => // use s. - ... -} -``` - -The value would be dropped in the guard then become unavailable not only in the -body of that arm but also in all subsequent arms! The solution is to bind by -reference when using guards or refactor the entire expression, perhaps by -putting the condition inside the body of the arm. -"##, - -E0009: r##" -In a pattern, all values that don't implement the `Copy` trait have to be bound -the same way. The goal here is to avoid binding simultaneously by-move and -by-ref. - -This limitation may be removed in a future version of Rust. - -Wrong example: - -``` -struct X { x: (), } - -let x = Some((X { x: () }, X { x: () })); -match x { - Some((y, ref z)) => {}, - None => panic!() -} -``` - -You have two solutions: - -Solution #1: Bind the pattern's values the same way. - -``` -struct X { x: (), } - -let x = Some((X { x: () }, X { x: () })); -match x { - Some((ref y, ref z)) => {}, - // or Some((y, z)) => {} - None => panic!() -} -``` - -Solution #2: Implement the `Copy` trait for the `X` structure. - -However, please keep in mind that the first solution should be preferred. - -``` -#[derive(Clone, Copy)] -struct X { x: (), } - -let x = Some((X { x: () }, X { x: () })); -match x { - Some((y, ref z)) => {}, - None => panic!() -} -``` -"##, - E0020: r##" This error indicates that an attempt was made to divide by zero (or take the remainder of a zero divisor) in a static or constant expression. Erroneous @@ -683,109 +483,6 @@ attributes: See also https://doc.rust-lang.org/book/no-stdlib.html "##, -E0158: r##" -`const` and `static` mean different things. A `const` is a compile-time -constant, an alias for a literal value. This property means you can match it -directly within a pattern. - -The `static` keyword, on the other hand, guarantees a fixed location in memory. -This does not always mean that the value is constant. For example, a global -mutex can be declared `static` as well. - -If you want to match against a `static`, consider using a guard instead: - -``` -static FORTY_TWO: i32 = 42; -match Some(42) { - Some(x) if x == FORTY_TWO => ... - ... -} -``` -"##, - -E0162: r##" -An if-let pattern attempts to match the pattern, and enters the body if the -match was successful. If the match is irrefutable (when it cannot fail to -match), use a regular `let`-binding instead. For instance: - -``` -struct Irrefutable(i32); -let irr = Irrefutable(0); - -// This fails to compile because the match is irrefutable. -if let Irrefutable(x) = irr { - // This body will always be executed. - foo(x); -} - -// Try this instead: -let Irrefutable(x) = irr; -foo(x); -``` -"##, - -E0165: r##" -A while-let pattern attempts to match the pattern, and enters the body if the -match was successful. If the match is irrefutable (when it cannot fail to -match), use a regular `let`-binding inside a `loop` instead. For instance: - -``` -struct Irrefutable(i32); -let irr = Irrefutable(0); - -// This fails to compile because the match is irrefutable. -while let Irrefutable(x) = irr { - ... -} - -// Try this instead: -loop { - let Irrefutable(x) = irr; - ... -} -``` -"##, - -E0170: r##" -Enum variants are qualified by default. For example, given this type: - -``` -enum Method { - GET, - POST -} -``` - -you would match it using: - -``` -match m { - Method::GET => ... - Method::POST => ... -} -``` - -If you don't qualify the names, the code will bind new variables named "GET" and -"POST" instead. This behavior is likely not what you want, so `rustc` warns when -that happens. - -Qualified names are good practice, and most code works well with them. But if -you prefer them unqualified, you can import the variants into scope: - -``` -use Method::*; -enum Method { GET, POST } -``` - -If you want others to be able to import variants from your module directly, use -`pub use`: - -``` -pub use Method::*; -enum Method { GET, POST } -``` -"##, - E0229: r##" An associated type binding was done outside of the type parameter declaration and `where` clause. Erroneous code example: @@ -1442,94 +1139,6 @@ that the value provided is a positive integer between quotes, like so: ``` "##, -E0297: r##" -Patterns used to bind names must be irrefutable. That is, they must guarantee -that a name will be extracted in all cases. Instead of pattern matching the -loop variable, consider using a `match` or `if let` inside the loop body. For -instance: - -``` -// This fails because `None` is not covered. -for Some(x) in xs { - ... -} - -// Match inside the loop instead: -for item in xs { - match item { - Some(x) => ... - None => ... - } -} - -// Or use `if let`: -for item in xs { - if let Some(x) = item { - ... - } -} -``` -"##, - -E0301: r##" -Mutable borrows are not allowed in pattern guards, because matching cannot have -side effects. Side effects could alter the matched object or the environment -on which the match depends in such a way, that the match would not be -exhaustive. For instance, the following would not match any arm if mutable -borrows were allowed: - -``` -match Some(()) { - None => { }, - option if option.take().is_none() => { /* impossible, option is `Some` */ }, - Some(_) => { } // When the previous match failed, the option became `None`. -} -``` -"##, - -E0302: r##" -Assignments are not allowed in pattern guards, because matching cannot have -side effects. Side effects could alter the matched object or the environment -on which the match depends in such a way, that the match would not be -exhaustive. For instance, the following would not match any arm if assignments -were allowed: - -``` -match Some(()) { - None => { }, - option if { option = None; false } { }, - Some(_) => { } // When the previous match failed, the option became `None`. -} -``` -"##, - -E0303: r##" -In certain cases it is possible for sub-bindings to violate memory safety. -Updates to the borrow checker in a future version of Rust may remove this -restriction, but for now patterns must be rewritten without sub-bindings. - -``` -// Before. -match Some("hi".to_string()) { - ref op_string_ref @ Some(ref s) => ... - None => ... -} - -// After. -match Some("hi".to_string()) { - Some(ref s) => { - let op_string_ref = &Some(s); - ... - } - None => ... -} -``` - -The `op_string_ref` binding has type `&Option<&String>` in both cases. - -See also https://github.com/rust-lang/rust/issues/14587 -"##, - E0306: r##" In an array literal `[x; N]`, `N` is the number of elements in the array. This number cannot be negative. @@ -1809,8 +1418,6 @@ register_diagnostics! { E0280, // requirement is not satisfied E0284, // cannot resolve type // E0285, // overflow evaluation builtin bounds - E0298, // mismatched types between arms - E0299, // mismatched types between arms // E0300, // unexpanded macro // E0304, // expected signed integer constant // E0305, // expected constant @@ -1821,7 +1428,6 @@ register_diagnostics! { E0315, // cannot invoke closure outside of its lifetime E0316, // nested quantification of lifetimes E0453, // overruled by outer forbid - E0471, // constant evaluation error: .. E0473, // dereference of reference outside its lifetime E0474, // captured variable `..` does not outlive the enclosing closure E0475, // index of slice outside its lifetime diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs index 54c12b67970b1..e772631357b5c 100644 --- a/src/librustc/lib.rs +++ b/src/librustc/lib.rs @@ -94,7 +94,6 @@ pub mod middle { pub mod astconv_util; pub mod expr_use_visitor; // STAGE0: increase glitch immunity pub mod cfg; - pub mod check_match; pub mod const_eval; pub mod const_qualif; pub mod cstore; diff --git a/src/librustc_const_eval/diagnostics.rs b/src/librustc_const_eval/diagnostics.rs new file mode 100644 index 0000000000000..5aee1ae6c971b --- /dev/null +++ b/src/librustc_const_eval/diagnostics.rs @@ -0,0 +1,412 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(non_snake_case)] + +register_long_diagnostics! { + +E0001: r##" +This error suggests that the expression arm corresponding to the noted pattern +will never be reached as for all possible values of the expression being +matched, one of the preceding patterns will match. + +This means that perhaps some of the preceding patterns are too general, this one +is too specific or the ordering is incorrect. + +For example, the following `match` block has too many arms: + +``` +match foo { + Some(bar) => {/* ... */} + None => {/* ... */} + _ => {/* ... */} // All possible cases have already been handled +} +``` + +`match` blocks have their patterns matched in order, so, for example, putting +a wildcard arm above a more specific arm will make the latter arm irrelevant. + +Ensure the ordering of the match arm is correct and remove any superfluous +arms. +"##, + +E0002: r##" +This error indicates that an empty match expression is invalid because the type +it is matching on is non-empty (there exist values of this type). In safe code +it is impossible to create an instance of an empty type, so empty match +expressions are almost never desired. This error is typically fixed by adding +one or more cases to the match expression. + +An example of an empty type is `enum Empty { }`. So, the following will work: + +``` +fn foo(x: Empty) { + match x { + // empty + } +} +``` + +However, this won't: + +``` +fn foo(x: Option) { + match x { + // empty + } +} +``` +"##, + +E0003: r##" +Not-a-Number (NaN) values cannot be compared for equality and hence can never +match the input to a match expression. So, the following will not compile: + +``` +const NAN: f32 = 0.0 / 0.0; + +match number { + NAN => { /* ... */ }, + // ... +} +``` + +To match against NaN values, you should instead use the `is_nan()` method in a +guard, like so: + +``` +match number { + // ... + x if x.is_nan() => { /* ... */ } + // ... +} +``` +"##, + +E0004: r##" +This error indicates that the compiler cannot guarantee a matching pattern for +one or more possible inputs to a match expression. Guaranteed matches are +required in order to assign values to match expressions, or alternatively, +determine the flow of execution. + +If you encounter this error you must alter your patterns so that every possible +value of the input type is matched. For types with a small number of variants +(like enums) you should probably cover all cases explicitly. Alternatively, the +underscore `_` wildcard pattern can be added after all other patterns to match +"anything else". +"##, + +E0005: r##" +Patterns used to bind names must be irrefutable, that is, they must guarantee +that a name will be extracted in all cases. If you encounter this error you +probably need to use a `match` or `if let` to deal with the possibility of +failure. +"##, + +E0007: r##" +This error indicates that the bindings in a match arm would require a value to +be moved into more than one location, thus violating unique ownership. Code like +the following is invalid as it requires the entire `Option` to be moved +into a variable called `op_string` while simultaneously requiring the inner +String to be moved into a variable called `s`. + +``` +let x = Some("s".to_string()); +match x { + op_string @ Some(s) => ... + None => ... +} +``` + +See also Error 303. +"##, + +E0008: r##" +Names bound in match arms retain their type in pattern guards. As such, if a +name is bound by move in a pattern, it should also be moved to wherever it is +referenced in the pattern guard code. Doing so however would prevent the name +from being available in the body of the match arm. Consider the following: + +``` +match Some("hi".to_string()) { + Some(s) if s.len() == 0 => // use s. + ... +} +``` + +The variable `s` has type `String`, and its use in the guard is as a variable of +type `String`. The guard code effectively executes in a separate scope to the +body of the arm, so the value would be moved into this anonymous scope and +therefore become unavailable in the body of the arm. Although this example seems +innocuous, the problem is most clear when considering functions that take their +argument by value. + +``` +match Some("hi".to_string()) { + Some(s) if { drop(s); false } => (), + Some(s) => // use s. + ... +} +``` + +The value would be dropped in the guard then become unavailable not only in the +body of that arm but also in all subsequent arms! The solution is to bind by +reference when using guards or refactor the entire expression, perhaps by +putting the condition inside the body of the arm. +"##, + +E0009: r##" +In a pattern, all values that don't implement the `Copy` trait have to be bound +the same way. The goal here is to avoid binding simultaneously by-move and +by-ref. + +This limitation may be removed in a future version of Rust. + +Wrong example: + +``` +struct X { x: (), } + +let x = Some((X { x: () }, X { x: () })); +match x { + Some((y, ref z)) => {}, + None => panic!() +} +``` + +You have two solutions: + +Solution #1: Bind the pattern's values the same way. + +``` +struct X { x: (), } + +let x = Some((X { x: () }, X { x: () })); +match x { + Some((ref y, ref z)) => {}, + // or Some((y, z)) => {} + None => panic!() +} +``` + +Solution #2: Implement the `Copy` trait for the `X` structure. + +However, please keep in mind that the first solution should be preferred. + +``` +#[derive(Clone, Copy)] +struct X { x: (), } + +let x = Some((X { x: () }, X { x: () })); +match x { + Some((y, ref z)) => {}, + None => panic!() +} +``` +"##, + +E0158: r##" +`const` and `static` mean different things. A `const` is a compile-time +constant, an alias for a literal value. This property means you can match it +directly within a pattern. + +The `static` keyword, on the other hand, guarantees a fixed location in memory. +This does not always mean that the value is constant. For example, a global +mutex can be declared `static` as well. + +If you want to match against a `static`, consider using a guard instead: + +``` +static FORTY_TWO: i32 = 42; +match Some(42) { + Some(x) if x == FORTY_TWO => ... + ... +} +``` +"##, + +E0162: r##" +An if-let pattern attempts to match the pattern, and enters the body if the +match was successful. If the match is irrefutable (when it cannot fail to +match), use a regular `let`-binding instead. For instance: + +``` +struct Irrefutable(i32); +let irr = Irrefutable(0); + +// This fails to compile because the match is irrefutable. +if let Irrefutable(x) = irr { + // This body will always be executed. + foo(x); +} + +// Try this instead: +let Irrefutable(x) = irr; +foo(x); +``` +"##, + +E0165: r##" +A while-let pattern attempts to match the pattern, and enters the body if the +match was successful. If the match is irrefutable (when it cannot fail to +match), use a regular `let`-binding inside a `loop` instead. For instance: + +``` +struct Irrefutable(i32); +let irr = Irrefutable(0); + +// This fails to compile because the match is irrefutable. +while let Irrefutable(x) = irr { + ... +} + +// Try this instead: +loop { + let Irrefutable(x) = irr; + ... +} +``` +"##, + +E0170: r##" +Enum variants are qualified by default. For example, given this type: + +``` +enum Method { + GET, + POST +} +``` + +you would match it using: + +``` +match m { + Method::GET => ... + Method::POST => ... +} +``` + +If you don't qualify the names, the code will bind new variables named "GET" and +"POST" instead. This behavior is likely not what you want, so `rustc` warns when +that happens. + +Qualified names are good practice, and most code works well with them. But if +you prefer them unqualified, you can import the variants into scope: + +``` +use Method::*; +enum Method { GET, POST } +``` + +If you want others to be able to import variants from your module directly, use +`pub use`: + +``` +pub use Method::*; +enum Method { GET, POST } +``` +"##, + +E0297: r##" +Patterns used to bind names must be irrefutable. That is, they must guarantee +that a name will be extracted in all cases. Instead of pattern matching the +loop variable, consider using a `match` or `if let` inside the loop body. For +instance: + +``` +// This fails because `None` is not covered. +for Some(x) in xs { + ... +} + +// Match inside the loop instead: +for item in xs { + match item { + Some(x) => ... + None => ... + } +} + +// Or use `if let`: +for item in xs { + if let Some(x) = item { + ... + } +} +``` +"##, + +E0301: r##" +Mutable borrows are not allowed in pattern guards, because matching cannot have +side effects. Side effects could alter the matched object or the environment +on which the match depends in such a way, that the match would not be +exhaustive. For instance, the following would not match any arm if mutable +borrows were allowed: + +``` +match Some(()) { + None => { }, + option if option.take().is_none() => { /* impossible, option is `Some` */ }, + Some(_) => { } // When the previous match failed, the option became `None`. +} +``` +"##, + +E0302: r##" +Assignments are not allowed in pattern guards, because matching cannot have +side effects. Side effects could alter the matched object or the environment +on which the match depends in such a way, that the match would not be +exhaustive. For instance, the following would not match any arm if assignments +were allowed: + +``` +match Some(()) { + None => { }, + option if { option = None; false } { }, + Some(_) => { } // When the previous match failed, the option became `None`. +} +``` +"##, + +E0303: r##" +In certain cases it is possible for sub-bindings to violate memory safety. +Updates to the borrow checker in a future version of Rust may remove this +restriction, but for now patterns must be rewritten without sub-bindings. + +``` +// Before. +match Some("hi".to_string()) { + ref op_string_ref @ Some(ref s) => ... + None => ... +} + +// After. +match Some("hi".to_string()) { + Some(ref s) => { + let op_string_ref = &Some(s); + ... + } + None => ... +} +``` + +The `op_string_ref` binding has type `&Option<&String>` in both cases. + +See also https://github.com/rust-lang/rust/issues/14587 +"##, + +} + +register_diagnostics! { +E0298, // mismatched types between arms +E0299, // mismatched types between arms +E0471, // constant evaluation error: .. +} diff --git a/src/librustc_const_eval/lib.rs b/src/librustc_const_eval/lib.rs new file mode 100644 index 0000000000000..5caf18c6d5b71 --- /dev/null +++ b/src/librustc_const_eval/lib.rs @@ -0,0 +1,41 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! For now just check_match is moved here. This crate contains +//! things that require librustc and are required by librustc_trans +//! +//! # 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/")] +#![cfg_attr(not(stage0), deny(warnings))] + +#![feature(rustc_diagnostic_macros)] +#![feature(staged_api)] +#![feature(rustc_private)] +#![feature(iter_arith)] + +extern crate rustc; +extern crate rustc_front; +extern crate rustc_back; + +#[macro_use] extern crate syntax; +#[macro_use] extern crate log; + +pub mod diagnostics; + +pub mod matches; diff --git a/src/librustc/middle/check_match.rs b/src/librustc_const_eval/matches.rs similarity index 96% rename from src/librustc/middle/check_match.rs rename to src/librustc_const_eval/matches.rs index fa09c9d2bb6ba..f27b8b3a196c5 100644 --- a/src/librustc/middle/check_match.rs +++ b/src/librustc_const_eval/matches.rs @@ -8,25 +8,26 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -pub use self::Constructor::*; +use self::Constructor::*; use self::Usefulness::*; use self::WitnessPreference::*; -use dep_graph::DepNode; -use middle::const_eval::{compare_const_vals, ConstVal}; -use middle::const_eval::{eval_const_expr, eval_const_expr_partial}; -use middle::const_eval::{const_expr_to_pat, lookup_const_by_id}; -use middle::const_eval::EvalHint::ExprTypeChecked; -use middle::def::*; -use middle::def_id::{DefId}; -use middle::expr_use_visitor::{ConsumeMode, Delegate, ExprUseVisitor}; -use middle::expr_use_visitor::{LoanCause, MutateMode}; -use middle::expr_use_visitor as euv; -use middle::infer; -use middle::mem_categorization::{cmt}; -use middle::pat_util::*; -use middle::ty::*; -use middle::ty; +use rustc::dep_graph::DepNode; +use rustc::middle::const_eval::{compare_const_vals, ConstVal}; +use rustc::middle::const_eval::{eval_const_expr, eval_const_expr_partial}; +use rustc::middle::const_eval::{const_expr_to_pat, lookup_const_by_id}; +use rustc::middle::const_eval::EvalHint::ExprTypeChecked; +use rustc::middle::def::*; +use rustc::middle::def_id::{DefId}; +use rustc::middle::expr_use_visitor::{ConsumeMode, Delegate, ExprUseVisitor}; +use rustc::middle::expr_use_visitor::{LoanCause, MutateMode}; +use rustc::middle::expr_use_visitor as euv; +use rustc::middle::infer; +use rustc::middle::mem_categorization::{cmt}; +use rustc::middle::pat_util::*; +use rustc::middle::ty::*; +use rustc::middle::ty; + use std::cmp::Ordering; use std::fmt; use std::iter::{FromIterator, IntoIterator, repeat}; @@ -43,9 +44,9 @@ use syntax::codemap::{Span, Spanned, DUMMY_SP}; use rustc_front::fold::{Folder, noop_fold_pat}; use rustc_front::print::pprust::pat_to_string; use syntax::ptr::P; -use util::nodemap::FnvHashMap; +use rustc::util::nodemap::FnvHashMap; -pub const DUMMY_WILD_PAT: &'static Pat = &Pat { +const DUMMY_WILD_PAT: &'static Pat = &Pat { id: DUMMY_NODE_ID, node: hir::PatWild, span: DUMMY_SP @@ -377,9 +378,9 @@ fn check_exhaustive(cx: &MatchCheckCtxt, sp: Span, matrix: &Matrix, source: hir: hir::MatchSource::ForLoopDesugar => { // `witnesses[0]` has the form `Some()`, peel off the `Some` let witness = match witnesses[0].node { - hir::PatEnum(_, Some(ref pats)) => match &pats[..] { - [ref pat] => &**pat, - _ => unreachable!(), + hir::PatEnum(_, Some(ref pats)) => { + assert_eq!(pats.len(), 1); + &pats[0] }, _ => unreachable!(), }; @@ -433,9 +434,9 @@ fn const_val_to_expr(value: &ConstVal) -> P { } pub struct StaticInliner<'a, 'tcx: 'a> { - pub tcx: &'a ty::ctxt<'tcx>, - pub failed: bool, - pub renaming_map: Option<&'a mut FnvHashMap<(NodeId, Span), NodeId>>, + tcx: &'a ty::ctxt<'tcx>, + failed: bool, + renaming_map: Option<&'a mut FnvHashMap<(NodeId, Span), NodeId>>, } impl<'a, 'tcx> StaticInliner<'a, 'tcx> { @@ -533,7 +534,7 @@ fn construct_witness<'a,'tcx>(cx: &MatchCheckCtxt<'a,'tcx>, ctor: &Constructor, ty::TyTuple(_) => hir::PatTup(pats.collect()), ty::TyEnum(adt, _) | ty::TyStruct(adt, _) => { - let v = adt.variant_of_ctor(ctor); + let v = ctor.variant(adt); if let VariantKind::Struct = v.kind() { let field_pats: hir::HirVec<_> = v.fields.iter() .zip(pats) @@ -598,13 +599,13 @@ fn construct_witness<'a,'tcx>(cx: &MatchCheckCtxt<'a,'tcx>, ctor: &Constructor, }) } -impl<'tcx, 'container> ty::AdtDefData<'tcx, 'container> { - fn variant_of_ctor(&self, - ctor: &Constructor) - -> &VariantDefData<'tcx, 'container> { - match ctor { - &Variant(vid) => self.variant_with_id(vid), - _ => self.struct_variant() +impl Constructor { + fn variant<'a, 'tcx, 'container>(&self, + adt: &'a ty::AdtDefData<'tcx, 'container>) + -> &'a VariantDefData<'tcx, 'container> { + match self { + &Variant(vid) => adt.variant_with_id(vid), + _ => adt.struct_variant(), } } } @@ -838,7 +839,7 @@ pub fn constructor_arity(_cx: &MatchCheckCtxt, ctor: &Constructor, ty: Ty) -> us _ => 1 }, ty::TyEnum(adt, _) | ty::TyStruct(adt, _) => { - adt.variant_of_ctor(ctor).fields.len() + ctor.variant(adt).fields.len() } ty::TyArray(_, n) => n, _ => 0 @@ -920,7 +921,7 @@ pub fn specialize<'a>(cx: &MatchCheckCtxt, r: &[&'a Pat], hir::PatStruct(_, ref pattern_fields, _) => { let def = cx.tcx.def_map.borrow().get(&pat_id).unwrap().full_def(); let adt = cx.tcx.node_id_to_type(pat_id).ty_adt_def().unwrap(); - let variant = adt.variant_of_ctor(constructor); + let variant = constructor.variant(adt); let def_variant = adt.variant_of_def(def); if variant.did == def_variant.did { Some(variant.fields.iter().map(|sf| { diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index 6189416dd578a..3f68ab8f0a34a 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -802,7 +802,7 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session, time(time_passes, "match checking", - || middle::check_match::check_crate(tcx)); + || ::rustc_const_eval::matches::check_crate(tcx)); let mir_map = time(time_passes, diff --git a/src/librustc_driver/lib.rs b/src/librustc_driver/lib.rs index 97256a1a3fd0f..4cb5562d0f96d 100644 --- a/src/librustc_driver/lib.rs +++ b/src/librustc_driver/lib.rs @@ -39,6 +39,7 @@ extern crate libc; extern crate rustc; extern crate rustc_back; extern crate rustc_borrowck; +extern crate rustc_const_eval; extern crate rustc_passes; extern crate rustc_front; extern crate rustc_lint; diff --git a/src/librustc_trans/lib.rs b/src/librustc_trans/lib.rs index 9ab056a187b65..b490c8e65adf1 100644 --- a/src/librustc_trans/lib.rs +++ b/src/librustc_trans/lib.rs @@ -51,6 +51,7 @@ extern crate rustc_front; extern crate rustc_llvm as llvm; extern crate rustc_mir; extern crate rustc_platform_intrinsics as intrinsics; +extern crate rustc_const_eval; extern crate serialize; #[macro_use] extern crate log; diff --git a/src/librustc_trans/trans/_match.rs b/src/librustc_trans/trans/_match.rs index 851ff33cf9327..cedf2eeab05d5 100644 --- a/src/librustc_trans/trans/_match.rs +++ b/src/librustc_trans/trans/_match.rs @@ -189,8 +189,7 @@ use self::Opt::*; use self::FailureHandler::*; use llvm::{ValueRef, BasicBlockRef}; -use middle::check_match::StaticInliner; -use middle::check_match; +use rustc_const_eval::matches::{self, Constructor}; use middle::const_eval; use middle::def::{Def, DefMap}; use middle::def_id::DefId; @@ -405,7 +404,7 @@ struct Match<'a, 'p: 'a, 'blk: 'a, 'tcx: 'blk> { pats: Vec<&'p hir::Pat>, data: &'a ArmData<'p, 'blk, 'tcx>, bound_ptrs: Vec<(ast::Name, ValueRef)>, - // Thread along renamings done by the check_match::StaticInliner, so we can + // Thread along renamings done by the StaticInliner, so we can // map back to original NodeIds pat_renaming_map: Option<&'a FnvHashMap<(NodeId, Span), NodeId>> } @@ -594,7 +593,7 @@ fn enter_default<'a, 'p, 'blk, 'tcx>(bcx: Block<'blk, 'tcx>, /// The above is now outdated in that enter_match() now takes a function that /// takes the complete row of patterns rather than just the first one. /// Also, most of the enter_() family functions have been unified with -/// the check_match specialization step. +/// the matches specialization step. fn enter_opt<'a, 'p, 'blk, 'tcx>( bcx: Block<'blk, 'tcx>, _: ast::NodeId, @@ -614,28 +613,28 @@ fn enter_opt<'a, 'p, 'blk, 'tcx>( let _indenter = indenter(); let ctor = match opt { - &ConstantValue(ConstantExpr(expr), _) => check_match::ConstantValue( + &ConstantValue(ConstantExpr(expr), _) => Constructor::ConstantValue( const_eval::eval_const_expr(bcx.tcx(), &*expr) ), - &ConstantRange(ConstantExpr(lo), ConstantExpr(hi), _) => check_match::ConstantRange( + &ConstantRange(ConstantExpr(lo), ConstantExpr(hi), _) => Constructor::ConstantRange( const_eval::eval_const_expr(bcx.tcx(), &*lo), const_eval::eval_const_expr(bcx.tcx(), &*hi) ), &SliceLengthEqual(n, _) => - check_match::Slice(n), + Constructor::Slice(n), &SliceLengthGreaterOrEqual(before, after, _) => - check_match::SliceWithSubslice(before, after), + Constructor::SliceWithSubslice(before, after), &Variant(_, _, def_id, _) => - check_match::Constructor::Variant(def_id) + Constructor::Variant(def_id) }; let param_env = bcx.tcx().empty_parameter_environment(); - let mcx = check_match::MatchCheckCtxt { + let mcx = matches::MatchCheckCtxt { tcx: bcx.tcx(), param_env: param_env, }; enter_match(bcx, dm, m, col, val, |pats| - check_match::specialize(&mcx, &pats[..], &ctor, col, variant_size) + matches::specialize(&mcx, &pats[..], &ctor, col, variant_size) ) } @@ -1184,7 +1183,7 @@ fn compile_submatch_continue<'a, 'p, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>, node_id_type(bcx, pat_id) }; - let mcx = check_match::MatchCheckCtxt { + let mcx = matches::MatchCheckCtxt { tcx: bcx.tcx(), param_env: bcx.tcx().empty_parameter_environment(), }; @@ -1240,9 +1239,9 @@ fn compile_submatch_continue<'a, 'p, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>, match adt_vals { Some(field_vals) => { let pats = enter_match(bcx, dm, m, col, val, |pats| - check_match::specialize(&mcx, pats, - &check_match::Single, col, - field_vals.len()) + matches::specialize(&mcx, pats, + &Constructor::Single, col, + field_vals.len()) ); let mut vals: Vec<_> = field_vals.into_iter() .map(|v|MatchInput::from_val(v)) @@ -1614,8 +1613,8 @@ fn trans_match_inner<'blk, 'tcx>(scope_cx: Block<'blk, 'tcx>, }; let arm_pats: Vec>> = { - let mut static_inliner = StaticInliner::new(scope_cx.tcx(), - pat_renaming_map.as_mut()); + let mut static_inliner = matches::StaticInliner::new(scope_cx.tcx(), + pat_renaming_map.as_mut()); arm_datas.iter().map(|arm_data| { arm_data.arm.pats.iter().map(|p| static_inliner.fold_pat((*p).clone())).collect() }).collect() From 62c4882719923290cc1aa6275e4b4193d8ed6f03 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 29 Jan 2016 09:55:10 +0100 Subject: [PATCH 2/2] move middle::const_eval to rustc_const_eval --- mk/crates.mk | 12 +- src/librustc/diagnostics.rs | 10 - src/librustc/middle/const_eval.rs | 1290 ----------------------- src/librustc/middle/ty/util.rs | 36 - src/librustc_const_eval/diagnostics.rs | 10 + src/librustc_const_eval/eval.rs | 1340 ++++++++++++++++++++++++ src/librustc_const_eval/lib.rs | 2 + src/librustc_const_eval/matches.rs | 8 +- src/librustc_lint/lib.rs | 1 + src/librustc_lint/types.rs | 4 +- src/librustc_mir/hair/cx/expr.rs | 4 +- src/librustc_mir/hair/cx/mod.rs | 8 +- src/librustc_mir/hair/cx/pattern.rs | 14 +- src/librustc_mir/lib.rs | 1 + src/librustc_passes/consts.rs | 18 +- src/librustc_passes/lib.rs | 1 + src/librustc_trans/trans/_match.rs | 2 +- src/librustc_trans/trans/consts.rs | 25 +- src/librustc_trans/trans/mir/did.rs | 4 +- src/librustc_trans/trans/tvec.rs | 6 +- src/librustc_typeck/astconv.rs | 6 +- src/librustc_typeck/check/mod.rs | 4 +- src/librustc_typeck/collect.rs | 6 +- src/librustc_typeck/lib.rs | 1 + src/librustdoc/clean/inline.rs | 7 +- src/librustdoc/lib.rs | 1 + 26 files changed, 1421 insertions(+), 1400 deletions(-) create mode 100644 src/librustc_const_eval/eval.rs diff --git a/mk/crates.mk b/mk/crates.mk index 0663668c98eec..b656df87c9dc8 100644 --- a/mk/crates.mk +++ b/mk/crates.mk @@ -99,11 +99,11 @@ DEPS_rustc_driver := arena flate getopts graphviz libc rustc rustc_back rustc_bo rustc_trans rustc_privacy rustc_lint rustc_front rustc_plugin \ rustc_metadata syntax_ext rustc_passes rustc_const_eval DEPS_rustc_front := std syntax log serialize -DEPS_rustc_lint := rustc log syntax +DEPS_rustc_lint := rustc log syntax rustc_const_eval DEPS_rustc_llvm := native:rustllvm libc std rustc_bitflags DEPS_rustc_metadata := rustc rustc_front syntax rbml -DEPS_rustc_passes := syntax rustc core rustc_front -DEPS_rustc_mir := rustc rustc_front syntax +DEPS_rustc_passes := syntax rustc core rustc_front rustc_const_eval +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 @@ -111,11 +111,11 @@ 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 \ rustc_const_eval -DEPS_rustc_typeck := rustc syntax rustc_front rustc_platform_intrinsics -DEPS_rustc_const_eval := rustc rustc_front rustc_back syntax +DEPS_rustc_typeck := rustc syntax rustc_front rustc_platform_intrinsics rustc_const_eval +DEPS_rustc_const_eval := rustc rustc_front rustc_back syntax graphviz log DEPS_rustdoc := rustc rustc_driver native:hoedown serialize getopts \ - test rustc_lint rustc_front + test rustc_lint rustc_front rustc_const_eval TOOL_DEPS_compiletest := test getopts diff --git a/src/librustc/diagnostics.rs b/src/librustc/diagnostics.rs index 97b07a2a1e623..1e0ee3202d372 100644 --- a/src/librustc/diagnostics.rs +++ b/src/librustc/diagnostics.rs @@ -1139,16 +1139,6 @@ that the value provided is a positive integer between quotes, like so: ``` "##, -E0306: r##" -In an array literal `[x; N]`, `N` is the number of elements in the array. This -number cannot be negative. -"##, - -E0307: r##" -The length of an array is part of its type. For this reason, this length must be -a compile-time constant. -"##, - E0308: r##" This error occurs when the compiler was unable to infer the concrete type of a variable. It can occur for several cases, the most common of which is a diff --git a/src/librustc/middle/const_eval.rs b/src/librustc/middle/const_eval.rs index 07171e6607760..c8fa9b3fe4eed 100644 --- a/src/librustc/middle/const_eval.rs +++ b/src/librustc/middle/const_eval.rs @@ -8,242 +8,17 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -//#![allow(non_camel_case_types)] - use self::ConstVal::*; -use self::ErrKind::*; -use self::EvalHint::*; -use front::map as ast_map; -use front::map::blocks::FnLikeNode; -use middle::cstore::{self, CrateStore, InlinedItem}; -use middle::{infer, subst, traits}; -use middle::def::Def; -use middle::subst::Subst; use middle::def_id::DefId; -use middle::pat_util::def_to_path; -use middle::ty::{self, Ty}; -use middle::astconv_util::ast_ty_to_prim_ty; -use util::num::ToPrimitive; -use util::nodemap::NodeMap; -use graphviz::IntoCow; use syntax::ast; -use rustc_front::hir::Expr; -use rustc_front::hir; -use rustc_front::intravisit::FnKind; -use syntax::codemap::Span; use syntax::parse::token::InternedString; -use syntax::ptr::P; -use syntax::codemap; -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 ty::ctxt, - enum_def: DefId, - variant_def: DefId) - -> Option<&'a Expr> { - fn variant_expr<'a>(variants: &'a [hir::Variant], id: ast::NodeId) - -> Option<&'a Expr> { - for variant in variants { - if variant.node.data.id() == id { - return variant.node.disr_expr.as_ref().map(|e| &**e); - } - } - None - } - - if let Some(enum_node_id) = tcx.map.as_local_node_id(enum_def) { - let variant_node_id = tcx.map.as_local_node_id(variant_def).unwrap(); - match tcx.map.find(enum_node_id) { - None => None, - Some(ast_map::NodeItem(it)) => match it.node { - hir::ItemEnum(hir::EnumDef { ref variants }, _) => { - variant_expr(variants, variant_node_id) - } - _ => None - }, - Some(_) => None - } - } else { - None - } -} - -/// * `def_id` is the id of the constant. -/// * `maybe_ref_id` is the id of the expr referencing the constant. -/// * `param_substs` is the monomorphization substitution for the expression. -/// -/// `maybe_ref_id` and `param_substs` are optional and are used for -/// finding substitutions in associated constants. This generally -/// happens in late/trans const evaluation. -pub fn lookup_const_by_id<'a, 'tcx: 'a>(tcx: &'a ty::ctxt<'tcx>, - def_id: DefId, - maybe_ref_id: Option, - param_substs: Option<&'tcx subst::Substs<'tcx>>) - -> Option<&'tcx Expr> { - 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) - } - _ => None - }, - Some(ast_map::NodeTraitItem(ti)) => match ti.node { - hir::ConstTraitItem(_, _) => { - match maybe_ref_id { - // If we have a trait item, and we know the expression - // that's the source of the obligation to resolve it, - // `resolve_trait_associated_const` will select an impl - // or the default. - Some(ref_id) => { - let trait_id = tcx.trait_of_item(def_id) - .unwrap(); - let mut substs = tcx.node_id_item_substs(ref_id) - .substs; - if let Some(param_substs) = param_substs { - substs = substs.subst(tcx, param_substs); - } - resolve_trait_associated_const(tcx, ti, trait_id, - substs) - } - // Technically, without knowing anything about the - // expression that generates the obligation, we could - // still return the default if there is one. However, - // it's safer to return `None` than to return some value - // that may differ from what you would get from - // correctly selecting an impl. - None => None - } - } - _ => None - }, - Some(ast_map::NodeImplItem(ii)) => match ii.node { - hir::ImplItemKind::Const(_, ref expr) => { - Some(&*expr) - } - _ => None - }, - Some(_) => 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)); - } - None => {} - } - let mut used_ref_id = false; - let expr_id = 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), - _ => None - }, - cstore::FoundAst::Found(&InlinedItem::TraitItem(trait_id, ref ti)) => match ti.node { - hir::ConstTraitItem(_, _) => { - used_ref_id = true; - match maybe_ref_id { - // As mentioned in the comments above for in-crate - // constants, we only try to find the expression for - // a trait-associated const if the caller gives us - // the expression that refers to it. - Some(ref_id) => { - let mut substs = tcx.node_id_item_substs(ref_id) - .substs; - 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) - } - None => None - } - } - _ => None - }, - cstore::FoundAst::Found(&InlinedItem::ImplItem(_, ref ii)) => match ii.node { - hir::ImplItemKind::Const(_, ref expr) => Some(expr.id), - _ => None - }, - _ => None - }; - // If we used the reference expression, particularly to choose an impl - // of a trait-associated const, don't cache that, because the next - // 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)); - } - expr_id.map(|id| tcx.map.expect_expr(id)) - } -} - -fn inline_const_fn_from_external_crate(tcx: &ty::ctxt, def_id: DefId) - -> Option { - match tcx.extern_const_fns.borrow().get(&def_id) { - Some(&ast::DUMMY_NODE_ID) => return None, - Some(&fn_id) => return Some(fn_id), - None => {} - } - - if !tcx.sess.cstore.is_const_fn(def_id) { - tcx.extern_const_fns.borrow_mut().insert(def_id, ast::DUMMY_NODE_ID); - return None; - } - - let fn_id = match tcx.sess.cstore.maybe_get_item_ast(tcx, def_id) { - cstore::FoundAst::Found(&InlinedItem::Item(ref item)) => Some(item.id), - cstore::FoundAst::Found(&InlinedItem::ImplItem(_, ref item)) => Some(item.id), - _ => None - }; - tcx.extern_const_fns.borrow_mut().insert(def_id, - fn_id.unwrap_or(ast::DUMMY_NODE_ID)); - fn_id -} - -pub fn lookup_const_fn_by_id<'tcx>(tcx: &ty::ctxt<'tcx>, def_id: DefId) - -> Option> -{ - let fn_id = if let Some(node_id) = tcx.map.as_local_node_id(def_id) { - node_id - } else { - if let Some(fn_id) = inline_const_fn_from_external_crate(tcx, def_id) { - fn_id - } else { - return None; - } - }; - - let fn_like = match FnLikeNode::from_node(tcx.map.get(fn_id)) { - Some(fn_like) => fn_like, - None => return None - }; - - match fn_like.kind() { - FnKind::ItemFn(_, _, _, hir::Constness::Const, _, _) => { - Some(fn_like) - } - FnKind::Method(_, m, _) => { - if m.constness == hir::Constness::Const { - Some(fn_like) - } else { - None - } - } - _ => None - } -} - #[derive(Clone, Debug, RustcEncodable, RustcDecodable)] pub enum ConstVal { Float(f64), @@ -320,1068 +95,3 @@ impl ConstVal { } } } - -pub fn const_expr_to_pat(tcx: &ty::ctxt, expr: &Expr, span: Span) -> P { - let pat = match expr.node { - hir::ExprTup(ref exprs) => - hir::PatTup(exprs.iter().map(|expr| const_expr_to_pat(tcx, &**expr, span)).collect()), - - hir::ExprCall(ref callee, ref args) => { - let def = *tcx.def_map.borrow().get(&callee.id).unwrap(); - if let Vacant(entry) = tcx.def_map.borrow_mut().entry(expr.id) { - entry.insert(def); - } - let path = match def.full_def() { - Def::Struct(def_id) => def_to_path(tcx, def_id), - Def::Variant(_, variant_did) => def_to_path(tcx, variant_did), - Def::Fn(..) => return P(hir::Pat { - id: expr.id, - node: hir::PatLit(P(expr.clone())), - span: span, - }), - _ => unreachable!() - }; - let pats = args.iter().map(|expr| const_expr_to_pat(tcx, &**expr, span)).collect(); - hir::PatEnum(path, Some(pats)) - } - - hir::ExprStruct(ref path, ref fields, None) => { - let field_pats = fields.iter().map(|field| codemap::Spanned { - span: codemap::DUMMY_SP, - node: hir::FieldPat { - name: field.name.node, - pat: const_expr_to_pat(tcx, &*field.expr, span), - is_shorthand: false, - }, - }).collect(); - hir::PatStruct(path.clone(), field_pats, false) - } - - hir::ExprVec(ref exprs) => { - let pats = exprs.iter().map(|expr| const_expr_to_pat(tcx, &**expr, span)).collect(); - hir::PatVec(pats, None, hir::HirVec::new()) - } - - hir::ExprPath(_, ref path) => { - let opt_def = tcx.def_map.borrow().get(&expr.id).map(|d| d.full_def()); - match opt_def { - Some(Def::Struct(..)) => - hir::PatStruct(path.clone(), hir::HirVec::new(), false), - Some(Def::Variant(..)) => - hir::PatEnum(path.clone(), None), - Some(Def::Const(def_id)) | - Some(Def::AssociatedConst(def_id)) => { - let expr = lookup_const_by_id(tcx, def_id, Some(expr.id), None).unwrap(); - return const_expr_to_pat(tcx, expr, span); - }, - _ => unreachable!(), - } - } - - _ => hir::PatLit(P(expr.clone())) - }; - P(hir::Pat { id: expr.id, node: pat, span: span }) -} - -pub fn eval_const_expr(tcx: &ty::ctxt, 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()) - } -} - -pub type FnArgMap<'a> = Option<&'a NodeMap>; - -#[derive(Clone)] -pub struct ConstEvalErr { - pub span: Span, - pub kind: ErrKind, -} - -#[derive(Clone)] -pub enum ErrKind { - CannotCast, - CannotCastTo(&'static str), - InvalidOpForInts(hir::BinOp_), - InvalidOpForUInts(hir::BinOp_), - InvalidOpForBools(hir::BinOp_), - InvalidOpForFloats(hir::BinOp_), - InvalidOpForIntUint(hir::BinOp_), - InvalidOpForUintInt(hir::BinOp_), - NegateOn(ConstVal), - NotOn(ConstVal), - CallOn(ConstVal), - - NegateWithOverflow(i64), - AddiWithOverflow(i64, i64), - SubiWithOverflow(i64, i64), - MuliWithOverflow(i64, i64), - AdduWithOverflow(u64, u64), - SubuWithOverflow(u64, u64), - MuluWithOverflow(u64, u64), - DivideByZero, - DivideWithOverflow, - ModuloByZero, - ModuloWithOverflow, - ShiftLeftWithOverflow, - ShiftRightWithOverflow, - MissingStructField, - NonConstPath, - UnimplementedConstVal(&'static str), - UnresolvedPath, - ExpectedConstTuple, - ExpectedConstStruct, - TupleIndexOutOfBounds, - IndexedNonVec, - IndexNegative, - IndexNotInt, - IndexOutOfBounds, - RepeatCountNotNatural, - RepeatCountNotInt, - - MiscBinaryOp, - MiscCatchAll, - - IndexOpFeatureGated, -} - -impl ConstEvalErr { - pub fn description(&self) -> Cow { - use self::ErrKind::*; - - 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(), - 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(), - InvalidOpForUintInt(..) => "can't do this op on a usize and isize".into_cow(), - NegateOn(ref const_val) => format!("negate on {}", const_val.description()).into_cow(), - NotOn(ref const_val) => format!("not on {}", const_val.description()).into_cow(), - CallOn(ref const_val) => format!("call on {}", const_val.description()).into_cow(), - - NegateWithOverflow(..) => "attempted to negate with overflow".into_cow(), - AddiWithOverflow(..) => "attempted to add with overflow".into_cow(), - SubiWithOverflow(..) => "attempted to sub with overflow".into_cow(), - MuliWithOverflow(..) => "attempted to mul with overflow".into_cow(), - AdduWithOverflow(..) => "attempted to add with overflow".into_cow(), - SubuWithOverflow(..) => "attempted to sub with overflow".into_cow(), - MuluWithOverflow(..) => "attempted to mul with overflow".into_cow(), - DivideByZero => "attempted to divide by zero".into_cow(), - DivideWithOverflow => "attempted to divide with overflow".into_cow(), - ModuloByZero => "attempted remainder with a divisor of zero".into_cow(), - ModuloWithOverflow => "attempted remainder with overflow".into_cow(), - ShiftLeftWithOverflow => "attempted left shift with overflow".into_cow(), - ShiftRightWithOverflow => "attempted right shift with overflow".into_cow(), - MissingStructField => "nonexistent struct field".into_cow(), - NonConstPath => "non-constant path in constant expression".into_cow(), - UnimplementedConstVal(what) => - format!("unimplemented constant expression: {}", what).into_cow(), - UnresolvedPath => "unresolved path in constant expression".into_cow(), - ExpectedConstTuple => "expected constant tuple".into_cow(), - ExpectedConstStruct => "expected constant struct".into_cow(), - TupleIndexOutOfBounds => "tuple index out of bounds".into_cow(), - IndexedNonVec => "indexing is only supported for arrays".into_cow(), - IndexNegative => "indices must be non-negative integers".into_cow(), - IndexNotInt => "indices must be integers".into_cow(), - IndexOutOfBounds => "array index out of bounds".into_cow(), - RepeatCountNotNatural => "repeat count must be a natural number".into_cow(), - RepeatCountNotInt => "repeat count must be integers".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(), - } - } -} - -pub type EvalResult = Result; -pub type CastResult = Result; - -// FIXME: Long-term, this enum should go away: trying to evaluate -// an expression which hasn't been type-checked is a recipe for -// disaster. That said, it's not clear how to fix ast_ty_to_ty -// to avoid the ordering issue. - -/// Hint to determine how to evaluate constant expressions which -/// might not be type-checked. -#[derive(Copy, Clone, Debug)] -pub enum EvalHint<'tcx> { - /// We have a type-checked expression. - ExprTypeChecked, - /// We have an expression which hasn't been type-checked, but we have - /// an idea of what the type will be because of the context. For example, - /// the length of an array is always `usize`. (This is referred to as - /// a hint because it isn't guaranteed to be consistent with what - /// type-checking would compute.) - UncheckedExprHint(Ty<'tcx>), - /// We have an expression which has not yet been type-checked, and - /// and we have no clue what the type will be. - UncheckedExprNoHint, -} - -impl<'tcx> EvalHint<'tcx> { - fn erase_hint(&self) -> EvalHint<'tcx> { - match *self { - ExprTypeChecked => ExprTypeChecked, - UncheckedExprHint(_) | UncheckedExprNoHint => UncheckedExprNoHint, - } - } - fn checked_or(&self, ty: Ty<'tcx>) -> EvalHint<'tcx> { - match *self { - ExprTypeChecked => ExprTypeChecked, - _ => UncheckedExprHint(ty), - } - } -} - -#[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: &ty::ctxt, t: ast::IntTy) -> IntTy { - let t = if let ast::TyIs = t { - tcx.sess.target.int_type - } else { - t - }; - match t { - ast::TyIs => unreachable!(), - ast::TyI8 => IntTy::I8, - ast::TyI16 => IntTy::I16, - ast::TyI32 => IntTy::I32, - ast::TyI64 => IntTy::I64, - } - } -} - -impl UintTy { - pub fn from(tcx: &ty::ctxt, t: ast::UintTy) -> UintTy { - let t = if let ast::TyUs = t { - tcx.sess.target.uint_type - } else { - t - }; - match t { - ast::TyUs => unreachable!(), - ast::TyU8 => UintTy::U8, - ast::TyU16 => UintTy::U16, - ast::TyU32 => UintTy::U32, - ast::TyU64 => 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) -> 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) -> 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) -> 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) -> 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) -> 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) -> 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) -> 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 -/// computing the length of an array. (See also the FIXME above EvalHint.) -pub fn eval_const_expr_partial<'tcx>(tcx: &ty::ctxt<'tcx>, - e: &Expr, - ty_hint: EvalHint<'tcx>, - fn_args: FnArgMap) -> EvalResult { - // Try to compute the type of the expression based on the EvalHint. - // (See also the definition of EvalHint, and the FIXME above EvalHint.) - let ety = match ty_hint { - ExprTypeChecked => { - // After type-checking, expr_ty is guaranteed to succeed. - Some(tcx.expr_ty(e)) - } - UncheckedExprHint(ty) => { - // Use the type hint; it's not guaranteed to be right, but it's - // usually good enough. - Some(ty) - } - UncheckedExprNoHint => { - // This expression might not be type-checked, and we have no hint. - // Try to query the context for a type anyway; we might get lucky - // (for example, if the expression was imported from another crate). - 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) => { - 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)) - } - 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), - Bool(b) => Bool(!b), - const_val => signal!(e, NotOn(const_val)), - } - } - hir::ExprBinary(op, ref a, ref b) => { - let b_ty = match op.node { - hir::BiShl | hir::BiShr => ty_hint.checked_or(tcx.types.usize), - _ => ty_hint - }; - 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)) => { - match op.node { - hir::BiAdd => Float(a + b), - hir::BiSub => Float(a - b), - hir::BiMul => Float(a * b), - hir::BiDiv => Float(a / b), - hir::BiRem => Float(a % b), - 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, InvalidOpForFloats(op.node)), - } - } - (Int(a), Int(b)) => { - 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), - _ => 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::BiOr => a || b, - hir::BiBitXor => a ^ b, - hir::BiBitAnd => a & b, - hir::BiBitOr => a | b, - hir::BiEq => a == b, - hir::BiNe => a != b, - _ => signal!(e, InvalidOpForBools(op.node)), - }) - } - - _ => signal!(e, MiscBinaryOp), - } - } - hir::ExprCast(ref base, ref target_ty) => { - let ety = ety.or_else(|| ast_ty_to_prim_ty(tcx, &**target_ty)) - .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)); - match cast_const(tcx, val, ety) { - Ok(val) => val, - Err(kind) => return Err(ConstEvalErr { span: e.span, kind: kind }), - } - } - hir::ExprPath(..) => { - let opt_def = if let Some(def) = tcx.def_map.borrow().get(&e.id) { - // After type-checking, def_map contains definition of the - // item referred to by the path. During type-checking, it - // can contain the raw output of path resolution, which - // might be a partially resolved path. - // FIXME: There's probably a better way to make sure we don't - // panic here. - if def.depth != 0 { - signal!(e, UnresolvedPath); - } - Some(def.full_def()) - } else { - None - }; - 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) - } - } else { - (lookup_const_by_id(tcx, def_id, Some(e.id), None), None) - } - } - Some(Def::AssociatedConst(def_id)) => { - if let Some(node_id) = tcx.map.as_local_node_id(def_id) { - match tcx.impl_or_trait_item(def_id).container() { - 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) - }, - } - } else { - (lookup_const_by_id(tcx, def_id, Some(e.id), None), None) - } - } - Some(Def::Variant(enum_def, variant_def)) => { - (lookup_variant_by_id(tcx, enum_def, variant_def), None) - } - Some(Def::Struct(..)) => { - return Ok(ConstVal::Struct(e.id)) - } - Some(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()); - } else { - (None, None) - } - }, - 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)) - } - hir::ExprCall(ref callee, ref args) => { - let sub_ty_hint = ty_hint.erase_hint(); - let callee_val = try!(eval_const_expr_partial(tcx, callee, sub_ty_hint, fn_args)); - let did = match callee_val { - Function(did) => did, - callee => signal!(e, CallOn(callee)), - }; - let (decl, result) = if let Some(fn_like) = lookup_const_fn_by_id(tcx, did) { - (fn_like.decl(), &fn_like.body().expr) - } else { - signal!(e, NonConstPath) - }; - let result = result.as_ref().expect("const fn has no result expression"); - assert_eq!(decl.inputs.len(), args.len()); - - let mut call_args = NodeMap(); - for (arg, arg_expr) in decl.inputs.iter().zip(args.iter()) { - let arg_val = try!(eval_const_expr_partial( - tcx, - arg_expr, - sub_ty_hint, - fn_args - )); - debug!("const call arg: {:?}", arg); - let old = call_args.insert(arg.pat.id, arg_val); - assert!(old.is_none()); - } - debug!("const call({:?})", call_args); - try!(eval_const_expr_partial(tcx, &**result, ty_hint, Some(&call_args))) - }, - hir::ExprLit(ref lit) => lit_to_const(&**lit, ety), - hir::ExprBlock(ref block) => { - match block.expr { - Some(ref expr) => try!(eval_const_expr_partial(tcx, &**expr, ty_hint, fn_args)), - None => unreachable!(), - } - } - hir::ExprType(ref e, _) => try!(eval_const_expr_partial(tcx, &**e, ty_hint, fn_args)), - hir::ExprTup(_) => Tuple(e.id), - hir::ExprStruct(..) => Struct(e.id), - hir::ExprIndex(ref arr, ref idx) => { - if !tcx.sess.features.borrow().const_indexing { - signal!(e, IndexOpFeatureGated); - } - let arr_hint = ty_hint.erase_hint(); - 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, - _ => signal!(idx, IndexNotInt), - }; - match arr { - Array(_, n) if idx >= n => signal!(e, IndexOutOfBounds), - Array(v, _) => if let hir::ExprVec(ref v) = tcx.map.expect_expr(v).node { - try!(eval_const_expr_partial(tcx, &*v[idx as usize], ty_hint, fn_args)) - } else { - unreachable!() - }, - - Repeat(_, n) if idx >= n => signal!(e, IndexOutOfBounds), - Repeat(elem, _) => try!(eval_const_expr_partial( - tcx, - &*tcx.map.expect_expr(elem), - ty_hint, - fn_args, - )), - - ByteStr(ref data) if idx as usize >= data.len() - => signal!(e, IndexOutOfBounds), - ByteStr(data) => Uint(data[idx as usize] as u64), - - Str(ref s) if idx as usize >= s.len() - => signal!(e, IndexOutOfBounds), - Str(_) => unimplemented!(), // there's no const_char type - _ => signal!(e, IndexedNonVec), - } - } - hir::ExprVec(ref v) => Array(e.id, v.len() as u64), - hir::ExprRepeat(_, ref n) => { - let len_hint = ty_hint.checked_or(tcx.types.usize); - 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, - _ => signal!(e, RepeatCountNotInt), - }, - ) - }, - hir::ExprTupField(ref base, index) => { - let base_hint = ty_hint.erase_hint(); - let c = try!(eval_const_expr_partial(tcx, base, base_hint, fn_args)); - 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) - } else { - signal!(e, TupleIndexOutOfBounds); - } - } else { - unreachable!() - } - } else { - signal!(base, ExpectedConstTuple); - } - } - hir::ExprField(ref base, field_name) => { - let base_hint = ty_hint.erase_hint(); - // Get the base expression if it is a struct and it is constant - let c = try!(eval_const_expr_partial(tcx, base, base_hint, fn_args)); - if let Struct(struct_id) = c { - if let hir::ExprStruct(_, ref fields, _) = tcx.map.expect_expr(struct_id).node { - // Check that the given field exists and evaluate it - // 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) - } else { - signal!(e, MissingStructField); - } - } else { - unreachable!() - } - } else { - signal!(base, ExpectedConstStruct); - } - } - _ => signal!(e, MiscCatchAll) - }; - - Ok(result) -} - -fn resolve_trait_associated_const<'a, 'tcx: 'a>(tcx: &'a ty::ctxt<'tcx>, - ti: &'tcx hir::TraitItem, - trait_id: DefId, - rcvr_substs: subst::Substs<'tcx>) - -> Option<&'tcx Expr> -{ - let trait_ref = ty::Binder( - rcvr_substs.erase_regions().to_trait_ref(tcx, trait_id) - ); - debug!("resolve_trait_associated_const: trait_ref={:?}", - trait_ref); - - tcx.populate_implementations_for_trait_if_necessary(trait_ref.def_id()); - let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, None); - - let mut selcx = traits::SelectionContext::new(&infcx); - let obligation = traits::Obligation::new(traits::ObligationCause::dummy(), - trait_ref.to_poly_trait_predicate()); - let selection = match selcx.select(&obligation) { - Ok(Some(vtable)) => vtable, - // Still ambiguous, so give up and let the caller decide whether this - // expression is really needed yet. Some associated constant values - // can't be evaluated until monomorphization is done in trans. - Ok(None) => { - return None - } - Err(_) => { - return None - } - }; - - 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), - _ => None, - }, - } - } - _ => { - tcx.sess.span_bug( - ti.span, - "resolve_trait_associated_const: unexpected vtable type") - } - } -} - -fn cast_const<'tcx>(tcx: &ty::ctxt<'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))), - } - } - } - - // 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::TyIs), ast::TyI32, _) => return convert_val!(i32, Int, i64), - (&ty::TyInt(ast::TyIs), ast::TyI64, _) => return convert_val!(i64, Int, i64), - (&ty::TyInt(ast::TyIs), _, _) => panic!("unexpected target.int_type"), - - (&ty::TyUint(ast::TyUs), _, ast::TyU32) => return convert_val!(u32, Uint, u64), - (&ty::TyUint(ast::TyUs), _, ast::TyU64) => return convert_val!(u64, Uint, u64), - (&ty::TyUint(ast::TyUs), _, _) => panic!("unexpected target.uint_type"), - - _ => {} - } - - match ty.sty { - ty::TyInt(ast::TyIs) => unreachable!(), - ty::TyUint(ast::TyUs) => unreachable!(), - - ty::TyInt(ast::TyI8) => convert_val!(i8, Int, i64), - ty::TyInt(ast::TyI16) => convert_val!(i16, Int, i64), - ty::TyInt(ast::TyI32) => convert_val!(i32, Int, i64), - ty::TyInt(ast::TyI64) => convert_val!(i64, Int, i64), - - ty::TyUint(ast::TyU8) => convert_val!(u8, Uint, u64), - ty::TyUint(ast::TyU16) => convert_val!(u16, Uint, u64), - ty::TyUint(ast::TyU32) => convert_val!(u32, Uint, u64), - ty::TyUint(ast::TyU64) => convert_val!(u64, Uint, u64), - - ty::TyFloat(ast::TyF32) => convert_val!(f32, Float, f64), - ty::TyFloat(ast::TyF64) => convert_val!(f64, Float, f64), - _ => Err(ErrKind::CannotCast), - } -} - -fn lit_to_const(lit: &ast::Lit, ty_hint: Option) -> ConstVal { - match lit.node { - ast::LitStr(ref s, _) => Str((*s).clone()), - ast::LitByteStr(ref data) => { - ByteStr(data.clone()) - } - ast::LitByte(n) => Uint(n as u64), - ast::LitChar(n) => Uint(n as u64), - ast::LitInt(n, ast::SignedIntLit(_, ast::Plus)) => Int(n as i64), - ast::LitInt(n, ast::UnsuffixedIntLit(ast::Plus)) => { - match ty_hint.map(|ty| &ty.sty) { - Some(&ty::TyUint(_)) => Uint(n), - _ => Int(n as i64) - } - } - ast::LitInt(n, ast::SignedIntLit(_, ast::Minus)) | - ast::LitInt(n, ast::UnsuffixedIntLit(ast::Minus)) => Int(-(n as i64)), - ast::LitInt(n, ast::UnsignedIntLit(_)) => Uint(n), - ast::LitFloat(ref n, _) | - ast::LitFloatUnsuffixed(ref n) => { - Float(n.parse::().unwrap() as f64) - } - ast::LitBool(b) => Bool(b) - } -} - -pub fn compare_const_vals(a: &ConstVal, b: &ConstVal) -> Option { - Some(match (a, b) { - (&Int(a), &Int(b)) => a.cmp(&b), - (&Uint(a), &Uint(b)) => a.cmp(&b), - (&Float(a), &Float(b)) => { - // This is pretty bad but it is the existing behavior. - 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 - }) -} - -pub fn compare_lit_exprs<'tcx>(tcx: &ty::ctxt<'tcx>, - a: &Expr, - b: &Expr) -> Option { - let a = match eval_const_expr_partial(tcx, a, ExprTypeChecked, None) { - Ok(a) => a, - Err(e) => { - tcx.sess.span_err(a.span, &e.description()); - return None; - } - }; - let b = match eval_const_expr_partial(tcx, b, ExprTypeChecked, None) { - Ok(b) => b, - Err(e) => { - tcx.sess.span_err(b.span, &e.description()); - return None; - } - }; - compare_const_vals(&a, &b) -} diff --git a/src/librustc/middle/ty/util.rs b/src/librustc/middle/ty/util.rs index 8cfd27843aee2..eb2c5fb8e2490 100644 --- a/src/librustc/middle/ty/util.rs +++ b/src/librustc/middle/ty/util.rs @@ -11,8 +11,6 @@ //! misc. type-system utilities too small to deserve their own file use back::svh::Svh; -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::infer; @@ -331,40 +329,6 @@ impl<'tcx> ty::ctxt<'tcx> { (a, b) } - /// Returns the repeat count for a repeating vector expression. - 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(), - }; - span_err!(self.sess, count_expr.span, E0306, - "expected positive integer for repeat count, found {}", - found); - } - Err(err) => { - let err_msg = match count_expr.node { - hir::ExprPath(None, hir::Path { - global: false, - ref segments, - .. - }) if segments.len() == 1 => - format!("found variable"), - _ => match err.kind { - ErrKind::MiscCatchAll => format!("but found {}", err.description()), - _ => format!("but {}", err.description()) - } - }; - span_err!(self.sess, count_expr.span, E0307, - "expected constant integer for repeat count, {}", err_msg); - } - } - 0 - } - /// Given a set of predicates that apply to an object type, returns /// the region bounds that the (erased) `Self` type must /// outlive. Precisely *because* the `Self` type is erased, the diff --git a/src/librustc_const_eval/diagnostics.rs b/src/librustc_const_eval/diagnostics.rs index 5aee1ae6c971b..779b5adbfe2d1 100644 --- a/src/librustc_const_eval/diagnostics.rs +++ b/src/librustc_const_eval/diagnostics.rs @@ -403,6 +403,16 @@ The `op_string_ref` binding has type `&Option<&String>` in both cases. See also https://github.com/rust-lang/rust/issues/14587 "##, +E0306: r##" +In an array literal `[x; N]`, `N` is the number of elements in the array. This +number cannot be negative. +"##, + +E0307: r##" +The length of an array is part of its type. For this reason, this length must be +a compile-time constant. +"##, + } register_diagnostics! { diff --git a/src/librustc_const_eval/eval.rs b/src/librustc_const_eval/eval.rs new file mode 100644 index 0000000000000..f90af22725edf --- /dev/null +++ b/src/librustc_const_eval/eval.rs @@ -0,0 +1,1340 @@ +// Copyright 2012-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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use self::ErrKind::*; +use self::EvalHint::*; + +pub use rustc::middle::const_eval::ConstVal; +use rustc::middle::const_eval::ConstVal::*; + +use rustc::front::map as ast_map; +use rustc::front::map::blocks::FnLikeNode; +use rustc::middle::cstore::{self, CrateStore, InlinedItem}; +use rustc::middle::{infer, subst, traits}; +use rustc::middle::def::Def; +use rustc::middle::subst::Subst; +use rustc::middle::def_id::DefId; +use rustc::middle::pat_util::def_to_path; +use rustc::middle::ty::{self, Ty}; +use rustc::middle::astconv_util::ast_ty_to_prim_ty; +use rustc::util::num::ToPrimitive; +use rustc::util::nodemap::NodeMap; + +use graphviz::IntoCow; +use syntax::ast; +use rustc_front::hir::Expr; +use rustc_front::hir; +use rustc_front::intravisit::FnKind; +use syntax::codemap::Span; +use syntax::ptr::P; +use syntax::codemap; + +use std::borrow::Cow; +use std::cmp::Ordering; +use std::collections::hash_map::Entry::Vacant; +use std::{i8, i16, i32, i64, u8, u16, u32, u64}; + +/// Returns the repeat count for a repeating vector expression. +pub fn eval_repeat_count(tcx: &ty::ctxt, count_expr: &hir::Expr) -> usize { + let hint = UncheckedExprHint(tcx.types.usize); + match eval_const_expr_partial(tcx, 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(), + }; + span_err!(tcx.sess, count_expr.span, E0306, + "expected positive integer for repeat count, found {}", + found); + } + Err(err) => { + let err_msg = match count_expr.node { + hir::ExprPath(None, hir::Path { + global: false, + ref segments, + .. + }) if segments.len() == 1 => + format!("found variable"), + _ => match err.kind { + ErrKind::MiscCatchAll => format!("but found {}", err.description()), + _ => format!("but {}", err.description()) + } + }; + span_err!(tcx.sess, count_expr.span, E0307, + "expected constant integer for repeat count, {}", err_msg); + } + } + 0 +} + +fn lookup_variant_by_id<'a>(tcx: &'a ty::ctxt, + enum_def: DefId, + variant_def: DefId) + -> Option<&'a Expr> { + fn variant_expr<'a>(variants: &'a [hir::Variant], id: ast::NodeId) + -> Option<&'a Expr> { + for variant in variants { + if variant.node.data.id() == id { + return variant.node.disr_expr.as_ref().map(|e| &**e); + } + } + None + } + + if let Some(enum_node_id) = tcx.map.as_local_node_id(enum_def) { + let variant_node_id = tcx.map.as_local_node_id(variant_def).unwrap(); + match tcx.map.find(enum_node_id) { + None => None, + Some(ast_map::NodeItem(it)) => match it.node { + hir::ItemEnum(hir::EnumDef { ref variants }, _) => { + variant_expr(variants, variant_node_id) + } + _ => None + }, + Some(_) => None + } + } else { + None + } +} + +/// * `def_id` is the id of the constant. +/// * `maybe_ref_id` is the id of the expr referencing the constant. +/// * `param_substs` is the monomorphization substitution for the expression. +/// +/// `maybe_ref_id` and `param_substs` are optional and are used for +/// finding substitutions in associated constants. This generally +/// happens in late/trans const evaluation. +pub fn lookup_const_by_id<'a, 'tcx: 'a>(tcx: &'a ty::ctxt<'tcx>, + def_id: DefId, + maybe_ref_id: Option, + param_substs: Option<&'tcx subst::Substs<'tcx>>) + -> Option<&'tcx Expr> { + 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) + } + _ => None + }, + Some(ast_map::NodeTraitItem(ti)) => match ti.node { + hir::ConstTraitItem(_, _) => { + match maybe_ref_id { + // If we have a trait item, and we know the expression + // that's the source of the obligation to resolve it, + // `resolve_trait_associated_const` will select an impl + // or the default. + Some(ref_id) => { + let trait_id = tcx.trait_of_item(def_id) + .unwrap(); + let mut substs = tcx.node_id_item_substs(ref_id) + .substs; + if let Some(param_substs) = param_substs { + substs = substs.subst(tcx, param_substs); + } + resolve_trait_associated_const(tcx, ti, trait_id, + substs) + } + // Technically, without knowing anything about the + // expression that generates the obligation, we could + // still return the default if there is one. However, + // it's safer to return `None` than to return some value + // that may differ from what you would get from + // correctly selecting an impl. + None => None + } + } + _ => None + }, + Some(ast_map::NodeImplItem(ii)) => match ii.node { + hir::ImplItemKind::Const(_, ref expr) => { + Some(&*expr) + } + _ => None + }, + Some(_) => 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)); + } + None => {} + } + let mut used_ref_id = false; + let expr_id = 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), + _ => None + }, + cstore::FoundAst::Found(&InlinedItem::TraitItem(trait_id, ref ti)) => match ti.node { + hir::ConstTraitItem(_, _) => { + used_ref_id = true; + match maybe_ref_id { + // As mentioned in the comments above for in-crate + // constants, we only try to find the expression for + // a trait-associated const if the caller gives us + // the expression that refers to it. + Some(ref_id) => { + let mut substs = tcx.node_id_item_substs(ref_id) + .substs; + 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) + } + None => None + } + } + _ => None + }, + cstore::FoundAst::Found(&InlinedItem::ImplItem(_, ref ii)) => match ii.node { + hir::ImplItemKind::Const(_, ref expr) => Some(expr.id), + _ => None + }, + _ => None + }; + // If we used the reference expression, particularly to choose an impl + // of a trait-associated const, don't cache that, because the next + // 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)); + } + expr_id.map(|id| tcx.map.expect_expr(id)) + } +} + +fn inline_const_fn_from_external_crate(tcx: &ty::ctxt, def_id: DefId) + -> Option { + match tcx.extern_const_fns.borrow().get(&def_id) { + Some(&ast::DUMMY_NODE_ID) => return None, + Some(&fn_id) => return Some(fn_id), + None => {} + } + + if !tcx.sess.cstore.is_const_fn(def_id) { + tcx.extern_const_fns.borrow_mut().insert(def_id, ast::DUMMY_NODE_ID); + return None; + } + + let fn_id = match tcx.sess.cstore.maybe_get_item_ast(tcx, def_id) { + cstore::FoundAst::Found(&InlinedItem::Item(ref item)) => Some(item.id), + cstore::FoundAst::Found(&InlinedItem::ImplItem(_, ref item)) => Some(item.id), + _ => None + }; + tcx.extern_const_fns.borrow_mut().insert(def_id, + fn_id.unwrap_or(ast::DUMMY_NODE_ID)); + fn_id +} + +pub fn lookup_const_fn_by_id<'tcx>(tcx: &ty::ctxt<'tcx>, def_id: DefId) + -> Option> +{ + let fn_id = if let Some(node_id) = tcx.map.as_local_node_id(def_id) { + node_id + } else { + if let Some(fn_id) = inline_const_fn_from_external_crate(tcx, def_id) { + fn_id + } else { + return None; + } + }; + + let fn_like = match FnLikeNode::from_node(tcx.map.get(fn_id)) { + Some(fn_like) => fn_like, + None => return None + }; + + match fn_like.kind() { + FnKind::ItemFn(_, _, _, hir::Constness::Const, _, _) => { + Some(fn_like) + } + FnKind::Method(_, m, _) => { + if m.constness == hir::Constness::Const { + Some(fn_like) + } else { + None + } + } + _ => None + } +} + +pub fn const_expr_to_pat(tcx: &ty::ctxt, expr: &Expr, span: Span) -> P { + let pat = match expr.node { + hir::ExprTup(ref exprs) => + hir::PatTup(exprs.iter().map(|expr| const_expr_to_pat(tcx, &**expr, span)).collect()), + + hir::ExprCall(ref callee, ref args) => { + let def = *tcx.def_map.borrow().get(&callee.id).unwrap(); + if let Vacant(entry) = tcx.def_map.borrow_mut().entry(expr.id) { + entry.insert(def); + } + let path = match def.full_def() { + Def::Struct(def_id) => def_to_path(tcx, def_id), + Def::Variant(_, variant_did) => def_to_path(tcx, variant_did), + Def::Fn(..) => return P(hir::Pat { + id: expr.id, + node: hir::PatLit(P(expr.clone())), + span: span, + }), + _ => unreachable!() + }; + let pats = args.iter().map(|expr| const_expr_to_pat(tcx, &**expr, span)).collect(); + hir::PatEnum(path, Some(pats)) + } + + hir::ExprStruct(ref path, ref fields, None) => { + let field_pats = fields.iter().map(|field| codemap::Spanned { + span: codemap::DUMMY_SP, + node: hir::FieldPat { + name: field.name.node, + pat: const_expr_to_pat(tcx, &*field.expr, span), + is_shorthand: false, + }, + }).collect(); + hir::PatStruct(path.clone(), field_pats, false) + } + + hir::ExprVec(ref exprs) => { + let pats = exprs.iter().map(|expr| const_expr_to_pat(tcx, &**expr, span)).collect(); + hir::PatVec(pats, None, hir::HirVec::new()) + } + + hir::ExprPath(_, ref path) => { + let opt_def = tcx.def_map.borrow().get(&expr.id).map(|d| d.full_def()); + match opt_def { + Some(Def::Struct(..)) => + hir::PatStruct(path.clone(), hir::HirVec::new(), false), + Some(Def::Variant(..)) => + hir::PatEnum(path.clone(), None), + Some(Def::Const(def_id)) | + Some(Def::AssociatedConst(def_id)) => { + let expr = lookup_const_by_id(tcx, def_id, Some(expr.id), None).unwrap(); + return const_expr_to_pat(tcx, expr, span); + }, + _ => unreachable!(), + } + } + + _ => hir::PatLit(P(expr.clone())) + }; + P(hir::Pat { id: expr.id, node: pat, span: span }) +} + +pub fn eval_const_expr(tcx: &ty::ctxt, 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()) + } +} + +pub type FnArgMap<'a> = Option<&'a NodeMap>; + +#[derive(Clone)] +pub struct ConstEvalErr { + pub span: Span, + pub kind: ErrKind, +} + +#[derive(Clone)] +pub enum ErrKind { + CannotCast, + CannotCastTo(&'static str), + InvalidOpForInts(hir::BinOp_), + InvalidOpForUInts(hir::BinOp_), + InvalidOpForBools(hir::BinOp_), + InvalidOpForFloats(hir::BinOp_), + InvalidOpForIntUint(hir::BinOp_), + InvalidOpForUintInt(hir::BinOp_), + NegateOn(ConstVal), + NotOn(ConstVal), + CallOn(ConstVal), + + NegateWithOverflow(i64), + AddiWithOverflow(i64, i64), + SubiWithOverflow(i64, i64), + MuliWithOverflow(i64, i64), + AdduWithOverflow(u64, u64), + SubuWithOverflow(u64, u64), + MuluWithOverflow(u64, u64), + DivideByZero, + DivideWithOverflow, + ModuloByZero, + ModuloWithOverflow, + ShiftLeftWithOverflow, + ShiftRightWithOverflow, + MissingStructField, + NonConstPath, + UnimplementedConstVal(&'static str), + UnresolvedPath, + ExpectedConstTuple, + ExpectedConstStruct, + TupleIndexOutOfBounds, + IndexedNonVec, + IndexNegative, + IndexNotInt, + IndexOutOfBounds, + RepeatCountNotNatural, + RepeatCountNotInt, + + MiscBinaryOp, + MiscCatchAll, + + IndexOpFeatureGated, +} + +impl ConstEvalErr { + pub fn description(&self) -> Cow { + use self::ErrKind::*; + + 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(), + 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(), + InvalidOpForUintInt(..) => "can't do this op on a usize and isize".into_cow(), + NegateOn(ref const_val) => format!("negate on {}", const_val.description()).into_cow(), + NotOn(ref const_val) => format!("not on {}", const_val.description()).into_cow(), + CallOn(ref const_val) => format!("call on {}", const_val.description()).into_cow(), + + NegateWithOverflow(..) => "attempted to negate with overflow".into_cow(), + AddiWithOverflow(..) => "attempted to add with overflow".into_cow(), + SubiWithOverflow(..) => "attempted to sub with overflow".into_cow(), + MuliWithOverflow(..) => "attempted to mul with overflow".into_cow(), + AdduWithOverflow(..) => "attempted to add with overflow".into_cow(), + SubuWithOverflow(..) => "attempted to sub with overflow".into_cow(), + MuluWithOverflow(..) => "attempted to mul with overflow".into_cow(), + DivideByZero => "attempted to divide by zero".into_cow(), + DivideWithOverflow => "attempted to divide with overflow".into_cow(), + ModuloByZero => "attempted remainder with a divisor of zero".into_cow(), + ModuloWithOverflow => "attempted remainder with overflow".into_cow(), + ShiftLeftWithOverflow => "attempted left shift with overflow".into_cow(), + ShiftRightWithOverflow => "attempted right shift with overflow".into_cow(), + MissingStructField => "nonexistent struct field".into_cow(), + NonConstPath => "non-constant path in constant expression".into_cow(), + UnimplementedConstVal(what) => + format!("unimplemented constant expression: {}", what).into_cow(), + UnresolvedPath => "unresolved path in constant expression".into_cow(), + ExpectedConstTuple => "expected constant tuple".into_cow(), + ExpectedConstStruct => "expected constant struct".into_cow(), + TupleIndexOutOfBounds => "tuple index out of bounds".into_cow(), + IndexedNonVec => "indexing is only supported for arrays".into_cow(), + IndexNegative => "indices must be non-negative integers".into_cow(), + IndexNotInt => "indices must be integers".into_cow(), + IndexOutOfBounds => "array index out of bounds".into_cow(), + RepeatCountNotNatural => "repeat count must be a natural number".into_cow(), + RepeatCountNotInt => "repeat count must be integers".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(), + } + } +} + +pub type EvalResult = Result; +pub type CastResult = Result; + +// FIXME: Long-term, this enum should go away: trying to evaluate +// an expression which hasn't been type-checked is a recipe for +// disaster. That said, it's not clear how to fix ast_ty_to_ty +// to avoid the ordering issue. + +/// Hint to determine how to evaluate constant expressions which +/// might not be type-checked. +#[derive(Copy, Clone, Debug)] +pub enum EvalHint<'tcx> { + /// We have a type-checked expression. + ExprTypeChecked, + /// We have an expression which hasn't been type-checked, but we have + /// an idea of what the type will be because of the context. For example, + /// the length of an array is always `usize`. (This is referred to as + /// a hint because it isn't guaranteed to be consistent with what + /// type-checking would compute.) + UncheckedExprHint(Ty<'tcx>), + /// We have an expression which has not yet been type-checked, and + /// and we have no clue what the type will be. + UncheckedExprNoHint, +} + +impl<'tcx> EvalHint<'tcx> { + fn erase_hint(&self) -> EvalHint<'tcx> { + match *self { + ExprTypeChecked => ExprTypeChecked, + UncheckedExprHint(_) | UncheckedExprNoHint => UncheckedExprNoHint, + } + } + fn checked_or(&self, ty: Ty<'tcx>) -> EvalHint<'tcx> { + match *self { + ExprTypeChecked => ExprTypeChecked, + _ => UncheckedExprHint(ty), + } + } +} + +#[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: &ty::ctxt, t: ast::IntTy) -> IntTy { + let t = if let ast::TyIs = t { + tcx.sess.target.int_type + } else { + t + }; + match t { + ast::TyIs => unreachable!(), + ast::TyI8 => IntTy::I8, + ast::TyI16 => IntTy::I16, + ast::TyI32 => IntTy::I32, + ast::TyI64 => IntTy::I64, + } + } +} + +impl UintTy { + pub fn from(tcx: &ty::ctxt, t: ast::UintTy) -> UintTy { + let t = if let ast::TyUs = t { + tcx.sess.target.uint_type + } else { + t + }; + match t { + ast::TyUs => unreachable!(), + ast::TyU8 => UintTy::U8, + ast::TyU16 => UintTy::U16, + ast::TyU32 => UintTy::U32, + ast::TyU64 => 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) -> 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) -> 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) -> 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) -> 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) -> 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) -> 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) -> 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 +/// computing the length of an array. (See also the FIXME above EvalHint.) +pub fn eval_const_expr_partial<'tcx>(tcx: &ty::ctxt<'tcx>, + e: &Expr, + ty_hint: EvalHint<'tcx>, + fn_args: FnArgMap) -> EvalResult { + // Try to compute the type of the expression based on the EvalHint. + // (See also the definition of EvalHint, and the FIXME above EvalHint.) + let ety = match ty_hint { + ExprTypeChecked => { + // After type-checking, expr_ty is guaranteed to succeed. + Some(tcx.expr_ty(e)) + } + UncheckedExprHint(ty) => { + // Use the type hint; it's not guaranteed to be right, but it's + // usually good enough. + Some(ty) + } + UncheckedExprNoHint => { + // This expression might not be type-checked, and we have no hint. + // Try to query the context for a type anyway; we might get lucky + // (for example, if the expression was imported from another crate). + 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) => { + 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)) + } + 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), + Bool(b) => Bool(!b), + const_val => signal!(e, NotOn(const_val)), + } + } + hir::ExprBinary(op, ref a, ref b) => { + let b_ty = match op.node { + hir::BiShl | hir::BiShr => ty_hint.checked_or(tcx.types.usize), + _ => ty_hint + }; + 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)) => { + match op.node { + hir::BiAdd => Float(a + b), + hir::BiSub => Float(a - b), + hir::BiMul => Float(a * b), + hir::BiDiv => Float(a / b), + hir::BiRem => Float(a % b), + 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, InvalidOpForFloats(op.node)), + } + } + (Int(a), Int(b)) => { + 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), + _ => 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::BiOr => a || b, + hir::BiBitXor => a ^ b, + hir::BiBitAnd => a & b, + hir::BiBitOr => a | b, + hir::BiEq => a == b, + hir::BiNe => a != b, + _ => signal!(e, InvalidOpForBools(op.node)), + }) + } + + _ => signal!(e, MiscBinaryOp), + } + } + hir::ExprCast(ref base, ref target_ty) => { + let ety = ety.or_else(|| ast_ty_to_prim_ty(tcx, &**target_ty)) + .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)); + match cast_const(tcx, val, ety) { + Ok(val) => val, + Err(kind) => return Err(ConstEvalErr { span: e.span, kind: kind }), + } + } + hir::ExprPath(..) => { + let opt_def = if let Some(def) = tcx.def_map.borrow().get(&e.id) { + // After type-checking, def_map contains definition of the + // item referred to by the path. During type-checking, it + // can contain the raw output of path resolution, which + // might be a partially resolved path. + // FIXME: There's probably a better way to make sure we don't + // panic here. + if def.depth != 0 { + signal!(e, UnresolvedPath); + } + Some(def.full_def()) + } else { + None + }; + 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) + } + } else { + (lookup_const_by_id(tcx, def_id, Some(e.id), None), None) + } + } + Some(Def::AssociatedConst(def_id)) => { + if let Some(node_id) = tcx.map.as_local_node_id(def_id) { + match tcx.impl_or_trait_item(def_id).container() { + 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) + }, + } + } else { + (lookup_const_by_id(tcx, def_id, Some(e.id), None), None) + } + } + Some(Def::Variant(enum_def, variant_def)) => { + (lookup_variant_by_id(tcx, enum_def, variant_def), None) + } + Some(Def::Struct(..)) => { + return Ok(ConstVal::Struct(e.id)) + } + Some(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()); + } else { + (None, None) + } + }, + 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)) + } + hir::ExprCall(ref callee, ref args) => { + let sub_ty_hint = ty_hint.erase_hint(); + let callee_val = try!(eval_const_expr_partial(tcx, callee, sub_ty_hint, fn_args)); + let did = match callee_val { + Function(did) => did, + callee => signal!(e, CallOn(callee)), + }; + let (decl, result) = if let Some(fn_like) = lookup_const_fn_by_id(tcx, did) { + (fn_like.decl(), &fn_like.body().expr) + } else { + signal!(e, NonConstPath) + }; + let result = result.as_ref().expect("const fn has no result expression"); + assert_eq!(decl.inputs.len(), args.len()); + + let mut call_args = NodeMap(); + for (arg, arg_expr) in decl.inputs.iter().zip(args.iter()) { + let arg_val = try!(eval_const_expr_partial( + tcx, + arg_expr, + sub_ty_hint, + fn_args + )); + debug!("const call arg: {:?}", arg); + let old = call_args.insert(arg.pat.id, arg_val); + assert!(old.is_none()); + } + debug!("const call({:?})", call_args); + try!(eval_const_expr_partial(tcx, &**result, ty_hint, Some(&call_args))) + }, + hir::ExprLit(ref lit) => lit_to_const(&**lit, ety), + hir::ExprBlock(ref block) => { + match block.expr { + Some(ref expr) => try!(eval_const_expr_partial(tcx, &**expr, ty_hint, fn_args)), + None => unreachable!(), + } + } + hir::ExprType(ref e, _) => try!(eval_const_expr_partial(tcx, &**e, ty_hint, fn_args)), + hir::ExprTup(_) => Tuple(e.id), + hir::ExprStruct(..) => Struct(e.id), + hir::ExprIndex(ref arr, ref idx) => { + if !tcx.sess.features.borrow().const_indexing { + signal!(e, IndexOpFeatureGated); + } + let arr_hint = ty_hint.erase_hint(); + 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, + _ => signal!(idx, IndexNotInt), + }; + match arr { + Array(_, n) if idx >= n => signal!(e, IndexOutOfBounds), + Array(v, _) => if let hir::ExprVec(ref v) = tcx.map.expect_expr(v).node { + try!(eval_const_expr_partial(tcx, &*v[idx as usize], ty_hint, fn_args)) + } else { + unreachable!() + }, + + Repeat(_, n) if idx >= n => signal!(e, IndexOutOfBounds), + Repeat(elem, _) => try!(eval_const_expr_partial( + tcx, + &*tcx.map.expect_expr(elem), + ty_hint, + fn_args, + )), + + ByteStr(ref data) if idx as usize >= data.len() + => signal!(e, IndexOutOfBounds), + ByteStr(data) => Uint(data[idx as usize] as u64), + + Str(ref s) if idx as usize >= s.len() + => signal!(e, IndexOutOfBounds), + Str(_) => unimplemented!(), // there's no const_char type + _ => signal!(e, IndexedNonVec), + } + } + hir::ExprVec(ref v) => Array(e.id, v.len() as u64), + hir::ExprRepeat(_, ref n) => { + let len_hint = ty_hint.checked_or(tcx.types.usize); + 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, + _ => signal!(e, RepeatCountNotInt), + }, + ) + }, + hir::ExprTupField(ref base, index) => { + let base_hint = ty_hint.erase_hint(); + let c = try!(eval_const_expr_partial(tcx, base, base_hint, fn_args)); + 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) + } else { + signal!(e, TupleIndexOutOfBounds); + } + } else { + unreachable!() + } + } else { + signal!(base, ExpectedConstTuple); + } + } + hir::ExprField(ref base, field_name) => { + let base_hint = ty_hint.erase_hint(); + // Get the base expression if it is a struct and it is constant + let c = try!(eval_const_expr_partial(tcx, base, base_hint, fn_args)); + if let Struct(struct_id) = c { + if let hir::ExprStruct(_, ref fields, _) = tcx.map.expect_expr(struct_id).node { + // Check that the given field exists and evaluate it + // 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) + } else { + signal!(e, MissingStructField); + } + } else { + unreachable!() + } + } else { + signal!(base, ExpectedConstStruct); + } + } + _ => signal!(e, MiscCatchAll) + }; + + Ok(result) +} + +fn resolve_trait_associated_const<'a, 'tcx: 'a>(tcx: &'a ty::ctxt<'tcx>, + ti: &'tcx hir::TraitItem, + trait_id: DefId, + rcvr_substs: subst::Substs<'tcx>) + -> Option<&'tcx Expr> +{ + let trait_ref = ty::Binder( + rcvr_substs.erase_regions().to_trait_ref(tcx, trait_id) + ); + debug!("resolve_trait_associated_const: trait_ref={:?}", + trait_ref); + + tcx.populate_implementations_for_trait_if_necessary(trait_ref.def_id()); + let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, None); + + let mut selcx = traits::SelectionContext::new(&infcx); + let obligation = traits::Obligation::new(traits::ObligationCause::dummy(), + trait_ref.to_poly_trait_predicate()); + let selection = match selcx.select(&obligation) { + Ok(Some(vtable)) => vtable, + // Still ambiguous, so give up and let the caller decide whether this + // expression is really needed yet. Some associated constant values + // can't be evaluated until monomorphization is done in trans. + Ok(None) => { + return None + } + Err(_) => { + return None + } + }; + + 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), + _ => None, + }, + } + } + _ => { + tcx.sess.span_bug( + ti.span, + "resolve_trait_associated_const: unexpected vtable type") + } + } +} + +fn cast_const<'tcx>(tcx: &ty::ctxt<'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))), + } + } + } + + // 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::TyIs), ast::TyI32, _) => return convert_val!(i32, Int, i64), + (&ty::TyInt(ast::TyIs), ast::TyI64, _) => return convert_val!(i64, Int, i64), + (&ty::TyInt(ast::TyIs), _, _) => panic!("unexpected target.int_type"), + + (&ty::TyUint(ast::TyUs), _, ast::TyU32) => return convert_val!(u32, Uint, u64), + (&ty::TyUint(ast::TyUs), _, ast::TyU64) => return convert_val!(u64, Uint, u64), + (&ty::TyUint(ast::TyUs), _, _) => panic!("unexpected target.uint_type"), + + _ => {} + } + + match ty.sty { + ty::TyInt(ast::TyIs) => unreachable!(), + ty::TyUint(ast::TyUs) => unreachable!(), + + ty::TyInt(ast::TyI8) => convert_val!(i8, Int, i64), + ty::TyInt(ast::TyI16) => convert_val!(i16, Int, i64), + ty::TyInt(ast::TyI32) => convert_val!(i32, Int, i64), + ty::TyInt(ast::TyI64) => convert_val!(i64, Int, i64), + + ty::TyUint(ast::TyU8) => convert_val!(u8, Uint, u64), + ty::TyUint(ast::TyU16) => convert_val!(u16, Uint, u64), + ty::TyUint(ast::TyU32) => convert_val!(u32, Uint, u64), + ty::TyUint(ast::TyU64) => convert_val!(u64, Uint, u64), + + ty::TyFloat(ast::TyF32) => convert_val!(f32, Float, f64), + ty::TyFloat(ast::TyF64) => convert_val!(f64, Float, f64), + _ => Err(ErrKind::CannotCast), + } +} + +fn lit_to_const(lit: &ast::Lit, ty_hint: Option) -> ConstVal { + match lit.node { + ast::LitStr(ref s, _) => Str((*s).clone()), + ast::LitByteStr(ref data) => { + ByteStr(data.clone()) + } + ast::LitByte(n) => Uint(n as u64), + ast::LitChar(n) => Uint(n as u64), + ast::LitInt(n, ast::SignedIntLit(_, ast::Plus)) => Int(n as i64), + ast::LitInt(n, ast::UnsuffixedIntLit(ast::Plus)) => { + match ty_hint.map(|ty| &ty.sty) { + Some(&ty::TyUint(_)) => Uint(n), + _ => Int(n as i64) + } + } + ast::LitInt(n, ast::SignedIntLit(_, ast::Minus)) | + ast::LitInt(n, ast::UnsuffixedIntLit(ast::Minus)) => Int(-(n as i64)), + ast::LitInt(n, ast::UnsignedIntLit(_)) => Uint(n), + ast::LitFloat(ref n, _) | + ast::LitFloatUnsuffixed(ref n) => { + Float(n.parse::().unwrap() as f64) + } + ast::LitBool(b) => Bool(b) + } +} + +pub fn compare_const_vals(a: &ConstVal, b: &ConstVal) -> Option { + Some(match (a, b) { + (&Int(a), &Int(b)) => a.cmp(&b), + (&Uint(a), &Uint(b)) => a.cmp(&b), + (&Float(a), &Float(b)) => { + // This is pretty bad but it is the existing behavior. + 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 + }) +} + +pub fn compare_lit_exprs<'tcx>(tcx: &ty::ctxt<'tcx>, + a: &Expr, + b: &Expr) -> Option { + let a = match eval_const_expr_partial(tcx, a, ExprTypeChecked, None) { + Ok(a) => a, + Err(e) => { + tcx.sess.span_err(a.span, &e.description()); + return None; + } + }; + let b = match eval_const_expr_partial(tcx, b, ExprTypeChecked, None) { + Ok(b) => b, + Err(e) => { + tcx.sess.span_err(b.span, &e.description()); + return None; + } + }; + compare_const_vals(&a, &b) +} diff --git a/src/librustc_const_eval/lib.rs b/src/librustc_const_eval/lib.rs index 5caf18c6d5b71..dd76497f9490f 100644 --- a/src/librustc_const_eval/lib.rs +++ b/src/librustc_const_eval/lib.rs @@ -33,9 +33,11 @@ extern crate rustc; extern crate rustc_front; extern crate rustc_back; +extern crate graphviz; #[macro_use] extern crate syntax; #[macro_use] extern crate log; pub mod diagnostics; pub mod matches; +pub mod eval; diff --git a/src/librustc_const_eval/matches.rs b/src/librustc_const_eval/matches.rs index f27b8b3a196c5..dddf23b601684 100644 --- a/src/librustc_const_eval/matches.rs +++ b/src/librustc_const_eval/matches.rs @@ -13,10 +13,10 @@ use self::Usefulness::*; use self::WitnessPreference::*; use rustc::dep_graph::DepNode; -use rustc::middle::const_eval::{compare_const_vals, ConstVal}; -use rustc::middle::const_eval::{eval_const_expr, eval_const_expr_partial}; -use rustc::middle::const_eval::{const_expr_to_pat, lookup_const_by_id}; -use rustc::middle::const_eval::EvalHint::ExprTypeChecked; +use eval::{compare_const_vals, ConstVal}; +use eval::{eval_const_expr, eval_const_expr_partial}; +use eval::{const_expr_to_pat, lookup_const_by_id}; +use eval::EvalHint::ExprTypeChecked; use rustc::middle::def::*; use rustc::middle::def_id::{DefId}; use rustc::middle::expr_use_visitor::{ConsumeMode, Delegate, ExprUseVisitor}; diff --git a/src/librustc_lint/lib.rs b/src/librustc_lint/lib.rs index 6868b4f2ab768..a7976fbe5f89c 100644 --- a/src/librustc_lint/lib.rs +++ b/src/librustc_lint/lib.rs @@ -46,6 +46,7 @@ extern crate rustc; extern crate log; extern crate rustc_front; extern crate rustc_back; +extern crate rustc_const_eval; pub use rustc::lint as lint; pub use rustc::middle as middle; diff --git a/src/librustc_lint/types.rs b/src/librustc_lint/types.rs index c3dfca44349eb..092144276a223 100644 --- a/src/librustc_lint/types.rs +++ b/src/librustc_lint/types.rs @@ -14,8 +14,8 @@ use middle::{infer}; use middle::def_id::DefId; use middle::subst::Substs; use middle::ty::{self, Ty}; -use middle::const_eval::{eval_const_expr_partial, ConstVal}; -use middle::const_eval::EvalHint::ExprTypeChecked; +use rustc_const_eval::eval::{eval_const_expr_partial, ConstVal}; +use rustc_const_eval::eval::EvalHint::ExprTypeChecked; use util::nodemap::{FnvHashSet}; use lint::{LateContext, LintContext, LintArray}; use lint::{LintPass, LateLintPass}; diff --git a/src/librustc_mir/hair/cx/expr.rs b/src/librustc_mir/hair/cx/expr.rs index d8a1930fd5c04..77933fa343da9 100644 --- a/src/librustc_mir/hair/cx/expr.rs +++ b/src/librustc_mir/hair/cx/expr.rs @@ -15,7 +15,7 @@ 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_const_eval::eval::eval_const_expr; use rustc::middle::region::CodeExtent; use rustc::middle::pat_util; use rustc::middle::ty::{self, VariantDef, Ty}; @@ -329,7 +329,7 @@ impl<'tcx> Mirror<'tcx> for &'tcx hir::Expr { count: TypedConstVal { ty: cx.tcx.expr_ty(c), span: c.span, - value: const_eval::eval_const_expr(cx.tcx, c) + value: eval_const_expr(cx.tcx, c) } }, hir::ExprRet(ref v) => diff --git a/src/librustc_mir/hair/cx/mod.rs b/src/librustc_mir/hair/cx/mod.rs index 7019b40bb2521..30f0ed202feac 100644 --- a/src/librustc_mir/hair/cx/mod.rs +++ b/src/librustc_mir/hair/cx/mod.rs @@ -18,7 +18,8 @@ use hair::*; use rustc::mir::repr::*; -use rustc::middle::const_eval::{self, ConstVal}; +use rustc_const_eval::eval::{ConstVal, eval_const_expr, eval_const_expr_partial}; +use rustc_const_eval::eval::EvalHint::ExprTypeChecked; use rustc::middle::infer::InferCtxt; use rustc::middle::ty::{self, Ty}; use syntax::codemap::Span; @@ -75,12 +76,11 @@ impl<'a,'tcx:'a> Cx<'a, 'tcx> { } pub fn const_eval_literal(&mut self, e: &hir::Expr) -> Literal<'tcx> { - Literal::Value { value: const_eval::eval_const_expr(self.tcx, e) } + Literal::Value { value: eval_const_expr(self.tcx, e) } } pub fn try_const_eval_literal(&mut self, e: &hir::Expr) -> Option> { - let hint = const_eval::EvalHint::ExprTypeChecked; - const_eval::eval_const_expr_partial(self.tcx, e, hint, None) + eval_const_expr_partial(self.tcx, e, ExprTypeChecked, None) .ok() .map(|v| Literal::Value { value: v }) } diff --git a/src/librustc_mir/hair/cx/pattern.rs b/src/librustc_mir/hair/cx/pattern.rs index e1a533ce42cca..bed75c69c9092 100644 --- a/src/librustc_mir/hair/cx/pattern.rs +++ b/src/librustc_mir/hair/cx/pattern.rs @@ -11,7 +11,7 @@ use hair::*; use hair::cx::Cx; use rustc_data_structures::fnv::FnvHashMap; -use rustc::middle::const_eval; +use rustc_const_eval::eval::{eval_const_expr, lookup_const_by_id, const_expr_to_pat}; use rustc::middle::def::Def; use rustc::middle::pat_util::{pat_is_resolved_const, pat_is_binding}; use rustc::middle::ty::{self, Ty}; @@ -67,14 +67,14 @@ impl<'patcx, 'cx, 'tcx> PatCx<'patcx, 'cx, 'tcx> { hir::PatWild => PatternKind::Wild, hir::PatLit(ref value) => { - let value = const_eval::eval_const_expr(self.cx.tcx, value); + let value = eval_const_expr(self.cx.tcx, value); PatternKind::Constant { value: value } } hir::PatRange(ref lo, ref hi) => { - let lo = const_eval::eval_const_expr(self.cx.tcx, lo); + let lo = eval_const_expr(self.cx.tcx, lo); let lo = Literal::Value { value: lo }; - let hi = const_eval::eval_const_expr(self.cx.tcx, hi); + let hi = eval_const_expr(self.cx.tcx, hi); let hi = Literal::Value { value: hi }; PatternKind::Range { lo: lo, hi: hi } }, @@ -85,11 +85,9 @@ impl<'patcx, 'cx, 'tcx> PatCx<'patcx, 'cx, 'tcx> { let def = self.cx.tcx.def_map.borrow().get(&pat.id).unwrap().full_def(); match def { Def::Const(def_id) | Def::AssociatedConst(def_id) => - match const_eval::lookup_const_by_id(self.cx.tcx, def_id, - Some(pat.id), None) { + match lookup_const_by_id(self.cx.tcx, def_id, Some(pat.id), None) { Some(const_expr) => { - let pat = const_eval::const_expr_to_pat(self.cx.tcx, const_expr, - pat.span); + let pat = const_expr_to_pat(self.cx.tcx, const_expr, pat.span); return self.to_pattern(&*pat); } None => { diff --git a/src/librustc_mir/lib.rs b/src/librustc_mir/lib.rs index 6f4128fc24de4..87ee6a36852b6 100644 --- a/src/librustc_mir/lib.rs +++ b/src/librustc_mir/lib.rs @@ -30,6 +30,7 @@ extern crate rustc_data_structures; extern crate rustc_front; extern crate rustc_back; extern crate syntax; +extern crate rustc_const_eval; pub mod build; pub mod graphviz; diff --git a/src/librustc_passes/consts.rs b/src/librustc_passes/consts.rs index 60cc658eeca8f..aee748742be0c 100644 --- a/src/librustc_passes/consts.rs +++ b/src/librustc_passes/consts.rs @@ -26,9 +26,10 @@ use rustc::dep_graph::DepNode; use rustc::middle::ty::cast::{CastKind}; -use rustc::middle::const_eval::{self, ConstEvalErr}; -use rustc::middle::const_eval::ErrKind::IndexOpFeatureGated; -use rustc::middle::const_eval::EvalHint::ExprTypeChecked; +use rustc_const_eval::eval::{ConstEvalErr, eval_const_expr_partial, lookup_const_fn_by_id}; +use rustc_const_eval::eval::{compare_lit_exprs, lookup_const_by_id}; +use rustc_const_eval::eval::ErrKind::IndexOpFeatureGated; +use rustc_const_eval::eval::EvalHint::ExprTypeChecked; use rustc::middle::def::Def; use rustc::middle::def_id::DefId; use rustc::middle::expr_use_visitor as euv; @@ -166,7 +167,7 @@ impl<'a, 'tcx> CheckCrateVisitor<'a, 'tcx> { def_id: DefId, ret_ty: Ty<'tcx>) -> bool { - if let Some(fn_like) = const_eval::lookup_const_fn_by_id(self.tcx, def_id) { + if let Some(fn_like) = lookup_const_fn_by_id(self.tcx, def_id) { if // we are in a static/const initializer self.mode != Mode::Var && @@ -329,7 +330,7 @@ impl<'a, 'tcx, 'v> Visitor<'v> for CheckCrateVisitor<'a, 'tcx> { self.global_expr(Mode::Const, &**start); self.global_expr(Mode::Const, &**end); - match const_eval::compare_lit_exprs(self.tcx, start, end) { + match compare_lit_exprs(self.tcx, start, end) { Some(Ordering::Less) | Some(Ordering::Equal) => {} Some(Ordering::Greater) => { @@ -425,8 +426,7 @@ impl<'a, 'tcx, 'v> Visitor<'v> for CheckCrateVisitor<'a, 'tcx> { match node_ty.sty { ty::TyUint(_) | ty::TyInt(_) if div_or_rem => { if !self.qualif.intersects(ConstQualif::NOT_CONST) { - match const_eval::eval_const_expr_partial( - self.tcx, ex, ExprTypeChecked, None) { + match eval_const_expr_partial(self.tcx, ex, ExprTypeChecked, None) { Ok(_) => {} Err(ConstEvalErr { kind: IndexOpFeatureGated, ..}) => {}, Err(msg) => { @@ -604,9 +604,7 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>, } Some(Def::Const(did)) | Some(Def::AssociatedConst(did)) => { - if let Some(expr) = const_eval::lookup_const_by_id(v.tcx, did, - Some(e.id), - None) { + if let Some(expr) = lookup_const_by_id(v.tcx, did, Some(e.id), None) { let inner = v.global_expr(Mode::Const, expr); v.add_qualif(inner); } diff --git a/src/librustc_passes/lib.rs b/src/librustc_passes/lib.rs index 91bfb19aa9d60..6d217040316c9 100644 --- a/src/librustc_passes/lib.rs +++ b/src/librustc_passes/lib.rs @@ -30,6 +30,7 @@ extern crate core; extern crate rustc; extern crate rustc_front; +extern crate rustc_const_eval; #[macro_use] extern crate log; #[macro_use] extern crate syntax; diff --git a/src/librustc_trans/trans/_match.rs b/src/librustc_trans/trans/_match.rs index cedf2eeab05d5..fadf3eb9d372b 100644 --- a/src/librustc_trans/trans/_match.rs +++ b/src/librustc_trans/trans/_match.rs @@ -190,7 +190,7 @@ use self::FailureHandler::*; use llvm::{ValueRef, BasicBlockRef}; use rustc_const_eval::matches::{self, Constructor}; -use middle::const_eval; +use rustc_const_eval::eval as const_eval; use middle::def::{Def, DefMap}; use middle::def_id::DefId; use middle::expr_use_visitor as euv; diff --git a/src/librustc_trans/trans/consts.rs b/src/librustc_trans/trans/consts.rs index 0d6324f3e899a..dcf9f5dbcecd2 100644 --- a/src/librustc_trans/trans/consts.rs +++ b/src/librustc_trans/trans/consts.rs @@ -15,17 +15,18 @@ use llvm::{ConstFCmp, ConstICmp, SetLinkage, SetUnnamedAddr}; 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::EvalHint::ExprTypeChecked; -use middle::const_eval::eval_const_expr_partial; +use rustc_const_eval::eval as const_eval; +use rustc_const_eval::eval::{ConstVal, ConstEvalErr}; +use rustc_const_eval::eval::{const_int_checked_neg, const_uint_checked_neg}; +use rustc_const_eval::eval::{const_int_checked_add, const_uint_checked_add}; +use rustc_const_eval::eval::{const_int_checked_sub, const_uint_checked_sub}; +use rustc_const_eval::eval::{const_int_checked_mul, const_uint_checked_mul}; +use rustc_const_eval::eval::{const_int_checked_div, const_uint_checked_div}; +use rustc_const_eval::eval::{const_int_checked_rem, const_uint_checked_rem}; +use rustc_const_eval::eval::{const_int_checked_shl, const_uint_checked_shl}; +use rustc_const_eval::eval::{const_int_checked_shr, const_uint_checked_shr}; +use rustc_const_eval::eval::EvalHint::ExprTypeChecked; +use rustc_const_eval::eval::{eval_const_expr_partial, eval_repeat_count}; use middle::def::Def; use middle::def_id::DefId; use trans::{adt, closure, debuginfo, expr, inline, machine}; @@ -870,7 +871,7 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, hir::ExprRepeat(ref elem, ref count) => { let unit_ty = ety.sequence_element_type(cx.tcx()); let llunitty = type_of::type_of(cx, unit_ty); - let n = cx.tcx().eval_repeat_count(count); + let n = eval_repeat_count(cx.tcx(), count); let unit_val = try!(const_expr(cx, &**elem, param_substs, fn_args, trueconst)).0; let vs = vec![unit_val; n]; if val_ty(unit_val) != llunitty { diff --git a/src/librustc_trans/trans/mir/did.rs b/src/librustc_trans/trans/mir/did.rs index e433776bef293..13eed97051e0a 100644 --- a/src/librustc_trans/trans/mir/did.rs +++ b/src/librustc_trans/trans/mir/did.rs @@ -14,7 +14,7 @@ use syntax::codemap::DUMMY_SP; use rustc::front::map; use rustc::middle::ty::{self, Ty, TypeFoldable}; use rustc::middle::subst::Substs; -use rustc::middle::const_eval; +use rustc_const_eval::eval::lookup_const_by_id; use rustc::middle::def_id::DefId; use rustc::middle::traits; use rustc::mir::repr::ItemKind; @@ -49,7 +49,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { }, ItemKind::Constant => { let did = inline::maybe_instantiate_inline(bcx.ccx(), did); - let expr = const_eval::lookup_const_by_id(bcx.tcx(), did, None, Some(substs)) + let expr = lookup_const_by_id(bcx.tcx(), did, None, Some(substs)) .expect("def was const, but lookup_const_by_id failed"); // 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. diff --git a/src/librustc_trans/trans/tvec.rs b/src/librustc_trans/trans/tvec.rs index 3a1568a70c992..8c2f650cd0973 100644 --- a/src/librustc_trans/trans/tvec.rs +++ b/src/librustc_trans/trans/tvec.rs @@ -28,6 +28,8 @@ use trans::type_::Type; use trans::type_of; use middle::ty::{self, Ty}; +use rustc_const_eval::eval::eval_repeat_count; + use rustc_front::hir; use syntax::ast; @@ -233,7 +235,7 @@ fn write_content<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, return expr::trans_into(bcx, &**element, Ignore); } SaveIn(lldest) => { - match bcx.tcx().eval_repeat_count(&**count_expr) { + match eval_repeat_count(bcx.tcx(), &**count_expr) { 0 => expr::trans_into(bcx, &**element, Ignore), 1 => expr::trans_into(bcx, &**element, SaveIn(lldest)), count => { @@ -285,7 +287,7 @@ fn elements_required(bcx: Block, content_expr: &hir::Expr) -> usize { }, hir::ExprVec(ref es) => es.len(), hir::ExprRepeat(_, ref count_expr) => { - bcx.tcx().eval_repeat_count(&**count_expr) + eval_repeat_count(bcx.tcx(), &**count_expr) } _ => bcx.tcx().sess.span_bug(content_expr.span, "unexpected vec content") diff --git a/src/librustc_typeck/astconv.rs b/src/librustc_typeck/astconv.rs index d67f5f9db5ee4..31707ac1cd4dd 100644 --- a/src/librustc_typeck/astconv.rs +++ b/src/librustc_typeck/astconv.rs @@ -49,8 +49,8 @@ //! an rptr (`&r.T`) use the region `r` that appears in the rptr. use middle::astconv_util::{prim_ty_to_ty, prohibit_type_params, prohibit_projection}; -use middle::const_eval::{self, ConstVal}; -use middle::const_eval::EvalHint::UncheckedExprHint; +use rustc_const_eval::eval::{ConstVal, eval_const_expr_partial}; +use rustc_const_eval::eval::EvalHint::UncheckedExprHint; use middle::def::{self, Def}; use middle::def_id::DefId; use middle::resolve_lifetime as rl; @@ -1683,7 +1683,7 @@ pub fn ast_ty_to_ty<'tcx>(this: &AstConv<'tcx>, } hir::TyFixedLengthVec(ref ty, ref e) => { let hint = UncheckedExprHint(tcx.types.usize); - match const_eval::eval_const_expr_partial(tcx, &e, hint, None) { + match eval_const_expr_partial(tcx, &e, hint, None) { Ok(r) => { match r { ConstVal::Int(i) => diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index af343e0b0790f..ebb3a9af2d457 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -131,6 +131,8 @@ use rustc_front::hir::Visibility; use rustc_front::print::pprust; use rustc_back::slice; +use rustc_const_eval::eval::eval_repeat_count; + mod assoc; pub mod dropck; pub mod _match; @@ -3548,7 +3550,7 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>, } hir::ExprRepeat(ref element, ref count_expr) => { check_expr_has_type(fcx, &**count_expr, tcx.types.usize); - let count = fcx.tcx().eval_repeat_count(&**count_expr); + let count = eval_repeat_count(fcx.tcx(), &**count_expr); let uty = match expected { ExpectHasType(uty) => { diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs index db4f8d3a70c40..3ffd2394072c4 100644 --- a/src/librustc_typeck/collect.rs +++ b/src/librustc_typeck/collect.rs @@ -65,8 +65,8 @@ use middle::def_id::DefId; use constrained_type_params as ctp; use middle::lang_items::SizedTraitLangItem; use middle::resolve_lifetime; -use middle::const_eval::{self, ConstVal}; -use middle::const_eval::EvalHint::UncheckedExprHint; +use rustc_const_eval::eval::{eval_const_expr_partial, ConstVal}; +use rustc_const_eval::eval::EvalHint::UncheckedExprHint; use middle::subst::{Substs, FnSpace, ParamSpace, SelfSpace, TypeSpace, VecPerParamSpace}; use middle::ty::{ToPredicate, ImplContainer, ImplOrTraitItemContainer, TraitContainer}; use middle::ty::{self, ToPolyTraitRef, Ty, TypeScheme}; @@ -1041,7 +1041,7 @@ fn convert_enum_def<'tcx>(tcx: &ty::ctxt<'tcx>, debug!("disr expr, checking {}", pprust::expr_to_string(e)); let hint = UncheckedExprHint(repr_ty); - match const_eval::eval_const_expr_partial(tcx, e, hint, None) { + match 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(_) => { diff --git a/src/librustc_typeck/lib.rs b/src/librustc_typeck/lib.rs index 49de0efa61d4d..c34841022fda9 100644 --- a/src/librustc_typeck/lib.rs +++ b/src/librustc_typeck/lib.rs @@ -92,6 +92,7 @@ extern crate rustc; 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; diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index 9eac2fd41fa1e..695e01dbd22bc 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -22,7 +22,7 @@ use rustc::middle::def_id::DefId; use rustc::middle::ty; use rustc::middle::subst; use rustc::middle::stability; -use rustc::middle::const_eval; +use rustc_const_eval::eval::lookup_const_by_id; use core::DocContext; use doctree; @@ -322,7 +322,7 @@ pub fn build_impl(cx: &DocContext, let did = assoc_const.def_id; 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) + Some(lookup_const_by_id(tcx, did, None, None) .unwrap().span.to_src(cx)) } else { None @@ -477,10 +477,9 @@ fn build_module(cx: &DocContext, tcx: &ty::ctxt, fn build_const(cx: &DocContext, tcx: &ty::ctxt, did: DefId) -> clean::Constant { - 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 = 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); diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index c52459f6c1059..923f56782c434 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -44,6 +44,7 @@ extern crate serialize; extern crate syntax; extern crate test as testing; extern crate rustc_unicode; +extern crate rustc_const_eval; #[macro_use] extern crate log; extern crate serialize as rustc_serialize; // used by deriving