From f04ea6c7e1cdac012b31efcd97973f426571a97a Mon Sep 17 00:00:00 2001 From: Eric Huss <eric@huss.org> Date: Mon, 30 Sep 2019 10:59:32 -0700 Subject: [PATCH 01/18] Document JSON message output. --- src/doc/rustc/src/SUMMARY.md | 1 + src/doc/rustc/src/command-line-arguments.md | 9 +- src/doc/rustc/src/json.md | 231 ++++++++++++++++++++ 3 files changed, 240 insertions(+), 1 deletion(-) create mode 100644 src/doc/rustc/src/json.md diff --git a/src/doc/rustc/src/SUMMARY.md b/src/doc/rustc/src/SUMMARY.md index 3cda8d927973c..d5564fd798f39 100644 --- a/src/doc/rustc/src/SUMMARY.md +++ b/src/doc/rustc/src/SUMMARY.md @@ -10,6 +10,7 @@ - [Warn-by-default lints](lints/listing/warn-by-default.md) - [Deny-by-default lints](lints/listing/deny-by-default.md) - [Codegen options](codegen-options/index.md) +- [JSON Output](json.md) - [Targets](targets/index.md) - [Built-in Targets](targets/built-in.md) - [Custom Targets](targets/custom.md) diff --git a/src/doc/rustc/src/command-line-arguments.md b/src/doc/rustc/src/command-line-arguments.md index 5eea9c8687900..b2cc65c11fd2c 100644 --- a/src/doc/rustc/src/command-line-arguments.md +++ b/src/doc/rustc/src/command-line-arguments.md @@ -92,6 +92,7 @@ information about editions may be found in the [edition guide]. [edition guide]: ../edition-guide/introduction.html ## `--emit`: specifies the types of output files to generate + <a id="option-emit"></a> This flag controls the types of output files generated by the compiler. It accepts a comma-separated list of values, and may be specified multiple times. @@ -241,12 +242,13 @@ The "sysroot" is where `rustc` looks for the crates that come with the Rust distribution; this flag allows that to be overridden. ## `--error-format`: control how errors are produced + <a id="option-error-format"></a> This flag lets you control the format of messages. Messages are printed to stderr. The valid options are: - `human` — Human-readable output. This is the default. -- `json` — Structured JSON output. +- `json` — Structured JSON output. See [the JSON chapter] for more detail. - `short` — Short, one-line messages. ## `--color`: configure coloring of output @@ -273,6 +275,7 @@ pathname syntax. For example `--remap-path-prefix foo=bar` will match `foo/lib.rs` but not `./foo/lib.rs`. ## `--json`: configure json messages printed by the compiler + <a id="option-json"></a> When the `--error-format=json` option is passed to rustc then all of the compiler's diagnostic output will be emitted in the form of JSON blobs. The @@ -305,9 +308,13 @@ to customize the output: Note that it is invalid to combine the `--json` argument with the `--color` argument, and it is required to combine `--json` with `--error-format=json`. +See [the JSON chapter] for more detail. + ## `@path`: load command-line flags from a path If you specify `@path` on the command-line, then it will open `path` and read command line options from it. These options are one per line; a blank line indicates an empty option. The file can use Unix or Windows style line endings, and must be encoded as UTF-8. + +[the JSON chapter]: json.md diff --git a/src/doc/rustc/src/json.md b/src/doc/rustc/src/json.md new file mode 100644 index 0000000000000..b737849516310 --- /dev/null +++ b/src/doc/rustc/src/json.md @@ -0,0 +1,231 @@ +# JSON Output + +This chapter documents the JSON structures emitted by `rustc`. JSON may be +enabled with the [`--error-format=json` flag][option-error-format]. Additional +options may be specified with the [`--json` flag][option-json] which can +change which messages are generated, and the format of the messages. + +JSON messages are emitted one per line to stderr. + +If parsing the output with Rust, the +[`cargo_metadata`](https://crates.io/crates/cargo_metadata) crate provides +some support for parsing the messages. + +When parsing, care should be taken to be forwards-compatible with future changes +to the format. Optional values may be `null`. New fields may be added. Enumerated +fields like "level" or "suggestion_applicability" may add new values. + +## Diagnostics + +Diagnostic messages provide errors or possible concerns generated during +compilation. `rustc` provides detailed information about where the diagnostic +originates, along with hints and suggestions. + +Diagnostics are arranged in a parent/child relationship where the parent +diagnostic value is the core of the diagnostic, and the attached children +provide additional context, help, and information. + +Diagnostics have the following format: + +```javascript +{ + /* The primary message. */ + "message": "unused variable: `x`", + /* The diagnostic code. + Some messages may set this value to null. + */ + "code": { + /* A unique string identifying which diagnostic triggered. */ + "code": "unused_variables", + /* An optional string explaining more detail about the diagnostic code. */ + "explanation": null + }, + /* The severity of the diagnostic. + Values may be: + - "error": A fatal error that prevents compilation. + - "warning": A possible error or concern. + - "note": Additional information or context about the diagnostic. + - "help": A suggestion on how to resolve the diagnostic. + - "failure-note": A note attached to the message for further information. + - "error: internal compiler error": Indicates a bug within the compiler. + */ + "level": "warning", + /* An array of source code locations to point out specific details about + where the diagnostic originates from. This may be empty, for example + for some global messages, or child messages attached to a parent. + + Character offsets are offsets of Unicode Scalar Values. + */ + "spans": [ + { + /* The file where the span is located. + For spans located within a macro expansion, this will be the + name of the expanded macro in the format "<MACRONAME macros>". + */ + "file_name": "lib.rs", + /* The byte offset where the span starts (0-based, inclusive). */ + "byte_start": 21, + /* The byte offset where the span ends (0-based, exclusive). */ + "byte_end": 22, + /* The first line number of the span (1-based, inclusive). */ + "line_start": 2, + /* The last line number of the span (1-based, inclusive). */ + "line_end": 2, + /* The first character offset of the line_start (1-based, inclusive). */ + "column_start": 9, + /* The last character offset of the line_end (1-based, exclusive). */ + "column_end": 10, + /* Whether or not this is the "primary" span. + + This indicates that this span is the focal point of the + diagnostic. + + There are rare cases where multiple spans may be marked as + primary. For example, "immutable borrow occurs here" and + "mutable borrow ends here" can be two separate primary spans. + + The top (parent) message should always have at least one + primary span, unless it has zero spans. Child messages may have + zero or more primary spans. + */ + "is_primary": true, + /* An array of objects showing the original source code for this + span. This shows the entire lines of text where the span is + located. A span across multiple lines will have a separate + value for each line. + */ + "text": [ + { + /* The entire line of the original source code. */ + "text": " let x = 123;", + /* The first character offset of the line of + where the span covers this line (1-based, inclusive). */ + "highlight_start": 9, + /* The last character offset of the line of + where the span covers this line (1-based, exclusive). */ + "highlight_end": 10 + } + ], + /* An optional message to display at this span location. + This is typically null for primary spans. + */ + "label": null, + /* An optional string of a suggested replacement for this span to + solve the issue. Tools may try to replace the contents of the + span with this text. + */ + "suggested_replacement": null, + /* An optional string that indicates the confidence of the + "suggested_replacement". Tools may use this value to determine + whether or not suggestions should be automatically applied. + + Possible values may be: + - "MachineApplicable": The suggestion is definitely what the + user intended. This suggestion should be automatically + applied. + - "MaybeIncorrect": The suggestion may be what the user + intended, but it is uncertain. The suggestion should result + in valid Rust code if it is applied. + - "HasPlaceholders": The suggestion contains placeholders like + `(...)`. The suggestion cannot be applied automatically + because it will not result in valid Rust code. The user will + need to fill in the placeholders. + - "Unspecified": The applicability of the suggestion is unknown. + */ + "suggestion_applicability": null, + /* An optional object indicating the expansion of a macro within + this span. + + If a message occurs within a macro invocation, this object will + provide details of where within the macro expansion the message + is located. + */ + "expansion": { + /* The span of the macro invocation. + Uses the same span definition as the "spans" array. + */ + "span": {/*...*/} + /* Name of the macro, such as "foo!" or "#[derive(Eq)]". */ + "macro_decl_name": "some_macro!", + /* Optional span where the relevant part of the macro is + defined. */ + "def_site_span": {/*...*/}, + } + } + ], + /* Array of attached diagnostic messages. + This is an array of objects using the same format as the parent + message. Children are not nested (children do not themselves + contain "children" definitions). + */ + "children": [ + { + "message": "`#[warn(unused_variables)]` on by default", + "code": null, + "level": "note", + "spans": [], + "children": [], + "rendered": null + }, + { + "message": "consider prefixing with an underscore", + "code": null, + "level": "help", + "spans": [ + { + "file_name": "lib.rs", + "byte_start": 21, + "byte_end": 22, + "line_start": 2, + "line_end": 2, + "column_start": 9, + "column_end": 10, + "is_primary": true, + "text": [ + { + "text": " let x = 123;", + "highlight_start": 9, + "highlight_end": 10 + } + ], + "label": null, + "suggested_replacement": "_x", + "suggestion_applicability": "MachineApplicable", + "expansion": null + } + ], + "children": [], + "rendered": null + } + ], + /* Optional string of the rendered version of the diagnostic as displayed + by rustc. Note that this may be influenced by the `--json` flag. + */ + "rendered": "warning: unused variable: `x`\n --> lib.rs:2:9\n |\n2 | let x = 123;\n | ^ help: consider prefixing with an underscore: `_x`\n |\n = note: `#[warn(unused_variables)]` on by default\n\n" +} +``` + +## Artifact notifications + +Artifact notifications are emitted when the [`--json=artifacts` +flag][option-json] is used. They indicate that a file artifact has been saved +to disk. More information about emit kinds may be found in the [`--emit` +flag][option-emit] documentation. + +```javascript +{ + /* The filename that was generated. */ + "artifact": "libfoo.rlib", + /* The kind of artifact that was generated. Possible values: + - "link": The generated crate as specified by the crate-type. + - "dep-info": The `.d` file with dependency information in a Makefile-like syntax. + - "metadata": The Rust `.rmeta` file containing metadata about the crate. + - "save-analysis": A JSON file emitted by the `-Zsave-analysis` feature. + */ + "emit": "link" +} +``` + +[option-emit]: command-line-arguments.md#option-emit +[option-error-format]: command-line-arguments.md#option-error-format +[option-json]: command-line-arguments.md#option-json From 3b0fd82bfad814ff777e7aa236b74804e4c469c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mi=C4=85sko?= <tomasz.miasko@gmail.com> Date: Tue, 8 Oct 2019 00:00:00 +0000 Subject: [PATCH 02/18] Disable Go and OCaml bindings when building LLVM Instead of instaling OCaml bindings in a location where installation will not fail, don't build them in the first place. --- src/bootstrap/native.rs | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/bootstrap/native.rs b/src/bootstrap/native.rs index 7bf9ea2688f4c..fb308bc35ebc5 100644 --- a/src/bootstrap/native.rs +++ b/src/bootstrap/native.rs @@ -157,6 +157,7 @@ impl Step for Llvm { .define("WITH_POLLY", "OFF") .define("LLVM_ENABLE_TERMINFO", "OFF") .define("LLVM_ENABLE_LIBEDIT", "OFF") + .define("LLVM_ENABLE_BINDINGS", "OFF") .define("LLVM_ENABLE_Z3_SOLVER", "OFF") .define("LLVM_PARALLEL_COMPILE_JOBS", builder.jobs().to_string()) .define("LLVM_TARGET_ARCH", target.split('-').next().unwrap()) @@ -169,15 +170,6 @@ impl Step for Llvm { } } - // By default, LLVM will automatically find OCaml and, if it finds it, - // install the LLVM bindings in LLVM_OCAML_INSTALL_PATH, which defaults - // to /usr/bin/ocaml. - // This causes problem for non-root builds of Rust. Side-step the issue - // by setting LLVM_OCAML_INSTALL_PATH to a relative path, so it installs - // in the prefix. - cfg.define("LLVM_OCAML_INSTALL_PATH", - env::var_os("LLVM_OCAML_INSTALL_PATH").unwrap_or_else(|| "usr/lib/ocaml".into())); - let want_lldb = builder.config.lldb_enabled && !self.emscripten; // This setting makes the LLVM tools link to the dynamic LLVM library, From bcff26606a74ebd2b5bcbfe03476963a1e21c765 Mon Sep 17 00:00:00 2001 From: Wesley Wiser <wwiser@gmail.com> Date: Sat, 14 Sep 2019 09:22:07 -0400 Subject: [PATCH 03/18] [const-prop] Handle MIR Rvalue::Repeat --- src/librustc_mir/transform/const_prop.rs | 2 +- src/test/mir-opt/const_prop/repeat.rs | 37 ++++++++++++++++++++++++ src/test/ui/consts/const-prop-ice.rs | 1 + src/test/ui/consts/const-prop-ice.stderr | 8 ++++- 4 files changed, 46 insertions(+), 2 deletions(-) create mode 100644 src/test/mir-opt/const_prop/repeat.rs diff --git a/src/librustc_mir/transform/const_prop.rs b/src/librustc_mir/transform/const_prop.rs index 49ac1de8fef64..77943e9acd527 100644 --- a/src/librustc_mir/transform/const_prop.rs +++ b/src/librustc_mir/transform/const_prop.rs @@ -436,13 +436,13 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { // if this isn't a supported operation, then return None match rvalue { - Rvalue::Repeat(..) | Rvalue::Aggregate(..) | Rvalue::NullaryOp(NullOp::Box, _) | Rvalue::Discriminant(..) => return None, Rvalue::Use(_) | Rvalue::Len(_) | + Rvalue::Repeat(..) | Rvalue::Cast(..) | Rvalue::NullaryOp(..) | Rvalue::CheckedBinaryOp(..) | diff --git a/src/test/mir-opt/const_prop/repeat.rs b/src/test/mir-opt/const_prop/repeat.rs new file mode 100644 index 0000000000000..fb091ad2a3d53 --- /dev/null +++ b/src/test/mir-opt/const_prop/repeat.rs @@ -0,0 +1,37 @@ +// compile-flags: -O + +fn main() { + let x: u32 = [42; 8][2] + 0; +} + +// END RUST SOURCE +// START rustc.main.ConstProp.before.mir +// bb0: { +// ... +// _3 = [const 42u32; 8]; +// ... +// _4 = const 2usize; +// _5 = const 8usize; +// _6 = Lt(_4, _5); +// assert(move _6, "index out of bounds: the len is move _5 but the index is _4") -> bb1; +// } +// bb1: { +// _2 = _3[_4]; +// _1 = Add(move _2, const 0u32); +// ... +// return; +// } +// END rustc.main.ConstProp.before.mir +// START rustc.main.ConstProp.after.mir +// bb0: { +// ... +// _6 = const true; +// assert(const true, "index out of bounds: the len is move _5 but the index is _4") -> bb1; +// } +// bb1: { +// _2 = const 42u32; +// _1 = Add(move _2, const 0u32); +// ... +// return; +// } +// END rustc.main.ConstProp.after.mir diff --git a/src/test/ui/consts/const-prop-ice.rs b/src/test/ui/consts/const-prop-ice.rs index 13309f978b672..48c4b7da942e4 100644 --- a/src/test/ui/consts/const-prop-ice.rs +++ b/src/test/ui/consts/const-prop-ice.rs @@ -1,3 +1,4 @@ fn main() { [0; 3][3u64 as usize]; //~ ERROR the len is 3 but the index is 3 + //~| ERROR this expression will panic at runtime } diff --git a/src/test/ui/consts/const-prop-ice.stderr b/src/test/ui/consts/const-prop-ice.stderr index 4b3880198bf2d..8ecc6f4bc6b12 100644 --- a/src/test/ui/consts/const-prop-ice.stderr +++ b/src/test/ui/consts/const-prop-ice.stderr @@ -6,5 +6,11 @@ LL | [0; 3][3u64 as usize]; | = note: `#[deny(const_err)]` on by default -error: aborting due to previous error +error: this expression will panic at runtime + --> $DIR/const-prop-ice.rs:2:5 + | +LL | [0; 3][3u64 as usize]; + | ^^^^^^^^^^^^^^^^^^^^^ index out of bounds: the len is 3 but the index is 3 + +error: aborting due to 2 previous errors From 714d00d8d848327dfca7aab8bc3bd3b32ea18ebe Mon Sep 17 00:00:00 2001 From: Wesley Wiser <wwiser@gmail.com> Date: Sat, 14 Sep 2019 14:11:31 -0400 Subject: [PATCH 04/18] [const-prop] Handle MIR Rvalue::Aggregates --- src/librustc_mir/transform/const_prop.rs | 8 +++++- src/test/compile-fail/consts/const-err3.rs | 1 + src/test/mir-opt/const_prop/aggregate.rs | 25 +++++++++++++++++++ src/test/run-fail/overflowing-rsh-5.rs | 1 + src/test/run-fail/overflowing-rsh-6.rs | 1 + src/test/ui/consts/const-err2.rs | 1 + src/test/ui/consts/const-err2.stderr | 8 +++++- src/test/ui/consts/const-err3.rs | 1 + src/test/ui/consts/const-err3.stderr | 8 +++++- src/test/ui/issues/issue-54348.rs | 2 ++ src/test/ui/issues/issue-54348.stderr | 16 ++++++++++-- src/test/ui/lint/lint-exceeding-bitshifts2.rs | 2 +- .../ui/lint/lint-exceeding-bitshifts2.stderr | 8 +++++- 13 files changed, 75 insertions(+), 7 deletions(-) create mode 100644 src/test/mir-opt/const_prop/aggregate.rs diff --git a/src/librustc_mir/transform/const_prop.rs b/src/librustc_mir/transform/const_prop.rs index 77943e9acd527..2119ee1b598ef 100644 --- a/src/librustc_mir/transform/const_prop.rs +++ b/src/librustc_mir/transform/const_prop.rs @@ -436,13 +436,13 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { // if this isn't a supported operation, then return None match rvalue { - Rvalue::Aggregate(..) | Rvalue::NullaryOp(NullOp::Box, _) | Rvalue::Discriminant(..) => return None, Rvalue::Use(_) | Rvalue::Len(_) | Rvalue::Repeat(..) | + Rvalue::Aggregate(..) | Rvalue::Cast(..) | Rvalue::NullaryOp(..) | Rvalue::CheckedBinaryOp(..) | @@ -535,6 +535,12 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { return None; } } + } else if let Rvalue::Aggregate(_, operands) = rvalue { + // FIXME(wesleywiser): const eval will turn this into a `const Scalar(<ZST>)` that + // `SimplifyLocals` doesn't know it can remove. + if operands.len() == 0 { + return None; + } } self.use_ecx(source_info, |this| { diff --git a/src/test/compile-fail/consts/const-err3.rs b/src/test/compile-fail/consts/const-err3.rs index fc10824f0c03c..add4eef13c784 100644 --- a/src/test/compile-fail/consts/const-err3.rs +++ b/src/test/compile-fail/consts/const-err3.rs @@ -14,6 +14,7 @@ fn main() { //~^ ERROR const_err let _e = [5u8][1]; //~^ ERROR const_err + //~| ERROR this expression will panic at runtime black_box(b); black_box(c); black_box(d); diff --git a/src/test/mir-opt/const_prop/aggregate.rs b/src/test/mir-opt/const_prop/aggregate.rs new file mode 100644 index 0000000000000..0937d37be6b6e --- /dev/null +++ b/src/test/mir-opt/const_prop/aggregate.rs @@ -0,0 +1,25 @@ +// compile-flags: -O + +fn main() { + let x = (0, 1, 2).1 + 0; +} + +// END RUST SOURCE +// START rustc.main.ConstProp.before.mir +// bb0: { +// ... +// _3 = (const 0i32, const 1i32, const 2i32); +// _2 = (_3.1: i32); +// _1 = Add(move _2, const 0i32); +// ... +// } +// END rustc.main.ConstProp.before.mir +// START rustc.main.ConstProp.after.mir +// bb0: { +// ... +// _3 = (const 0i32, const 1i32, const 2i32); +// _2 = const 1i32; +// _1 = Add(move _2, const 0i32); +// ... +// } +// END rustc.main.ConstProp.after.mir diff --git a/src/test/run-fail/overflowing-rsh-5.rs b/src/test/run-fail/overflowing-rsh-5.rs index 793f495240d57..58dfc5710ae4e 100644 --- a/src/test/run-fail/overflowing-rsh-5.rs +++ b/src/test/run-fail/overflowing-rsh-5.rs @@ -2,6 +2,7 @@ // compile-flags: -C debug-assertions #![warn(exceeding_bitshifts)] +#![warn(const_err)] fn main() { let _n = 1i64 >> [64][0]; diff --git a/src/test/run-fail/overflowing-rsh-6.rs b/src/test/run-fail/overflowing-rsh-6.rs index d6b2f8dc9f9af..c2fec5e4860af 100644 --- a/src/test/run-fail/overflowing-rsh-6.rs +++ b/src/test/run-fail/overflowing-rsh-6.rs @@ -2,6 +2,7 @@ // compile-flags: -C debug-assertions #![warn(exceeding_bitshifts)] +#![warn(const_err)] #![feature(const_indexing)] fn main() { diff --git a/src/test/ui/consts/const-err2.rs b/src/test/ui/consts/const-err2.rs index ecbcc2a4b496f..e5ee90fc9f11f 100644 --- a/src/test/ui/consts/const-err2.rs +++ b/src/test/ui/consts/const-err2.rs @@ -23,6 +23,7 @@ fn main() { //~^ ERROR const_err let _e = [5u8][1]; //~^ ERROR index out of bounds + //~| ERROR this expression will panic at runtime black_box(a); black_box(b); black_box(c); diff --git a/src/test/ui/consts/const-err2.stderr b/src/test/ui/consts/const-err2.stderr index 1d84d44dc27b3..0a09a7213dabc 100644 --- a/src/test/ui/consts/const-err2.stderr +++ b/src/test/ui/consts/const-err2.stderr @@ -34,5 +34,11 @@ error: index out of bounds: the len is 1 but the index is 1 LL | let _e = [5u8][1]; | ^^^^^^^^ -error: aborting due to 5 previous errors +error: this expression will panic at runtime + --> $DIR/const-err2.rs:24:14 + | +LL | let _e = [5u8][1]; + | ^^^^^^^^ index out of bounds: the len is 1 but the index is 1 + +error: aborting due to 6 previous errors diff --git a/src/test/ui/consts/const-err3.rs b/src/test/ui/consts/const-err3.rs index a9cf04cda7a5a..89373f99f75c2 100644 --- a/src/test/ui/consts/const-err3.rs +++ b/src/test/ui/consts/const-err3.rs @@ -23,6 +23,7 @@ fn main() { //~^ ERROR const_err let _e = [5u8][1]; //~^ ERROR const_err + //~| ERROR this expression will panic at runtime black_box(a); black_box(b); black_box(c); diff --git a/src/test/ui/consts/const-err3.stderr b/src/test/ui/consts/const-err3.stderr index 0602707be7040..42de247c8f7e0 100644 --- a/src/test/ui/consts/const-err3.stderr +++ b/src/test/ui/consts/const-err3.stderr @@ -34,5 +34,11 @@ error: index out of bounds: the len is 1 but the index is 1 LL | let _e = [5u8][1]; | ^^^^^^^^ -error: aborting due to 5 previous errors +error: this expression will panic at runtime + --> $DIR/const-err3.rs:24:14 + | +LL | let _e = [5u8][1]; + | ^^^^^^^^ index out of bounds: the len is 1 but the index is 1 + +error: aborting due to 6 previous errors diff --git a/src/test/ui/issues/issue-54348.rs b/src/test/ui/issues/issue-54348.rs index 68d838054776e..e7221e2cbb1e1 100644 --- a/src/test/ui/issues/issue-54348.rs +++ b/src/test/ui/issues/issue-54348.rs @@ -1,5 +1,7 @@ fn main() { [1][0u64 as usize]; [1][1.5 as usize]; //~ ERROR index out of bounds + //~| ERROR this expression will panic at runtime [1][1u64 as usize]; //~ ERROR index out of bounds + //~| ERROR this expression will panic at runtime } diff --git a/src/test/ui/issues/issue-54348.stderr b/src/test/ui/issues/issue-54348.stderr index fa77bd6fd7797..79320ef4f31c7 100644 --- a/src/test/ui/issues/issue-54348.stderr +++ b/src/test/ui/issues/issue-54348.stderr @@ -6,11 +6,23 @@ LL | [1][1.5 as usize]; | = note: `#[deny(const_err)]` on by default +error: this expression will panic at runtime + --> $DIR/issue-54348.rs:3:5 + | +LL | [1][1.5 as usize]; + | ^^^^^^^^^^^^^^^^^ index out of bounds: the len is 1 but the index is 1 + error: index out of bounds: the len is 1 but the index is 1 - --> $DIR/issue-54348.rs:4:5 + --> $DIR/issue-54348.rs:5:5 | LL | [1][1u64 as usize]; | ^^^^^^^^^^^^^^^^^^ -error: aborting due to 2 previous errors +error: this expression will panic at runtime + --> $DIR/issue-54348.rs:5:5 + | +LL | [1][1u64 as usize]; + | ^^^^^^^^^^^^^^^^^^ index out of bounds: the len is 1 but the index is 1 + +error: aborting due to 4 previous errors diff --git a/src/test/ui/lint/lint-exceeding-bitshifts2.rs b/src/test/ui/lint/lint-exceeding-bitshifts2.rs index 69b627355b801..2c213daddd752 100644 --- a/src/test/ui/lint/lint-exceeding-bitshifts2.rs +++ b/src/test/ui/lint/lint-exceeding-bitshifts2.rs @@ -8,7 +8,7 @@ fn main() { let n = 1u8 << (4+3); let n = 1u8 << (4+4); //~ ERROR: attempt to shift left with overflow let n = 1i64 >> [63][0]; - let n = 1i64 >> [64][0]; // should be linting, needs to wait for const propagation + let n = 1i64 >> [64][0]; //~ ERROR: attempt to shift right with overflow #[cfg(target_pointer_width = "32")] const BITS: usize = 32; diff --git a/src/test/ui/lint/lint-exceeding-bitshifts2.stderr b/src/test/ui/lint/lint-exceeding-bitshifts2.stderr index cb96982a78930..d9c76d233d03e 100644 --- a/src/test/ui/lint/lint-exceeding-bitshifts2.stderr +++ b/src/test/ui/lint/lint-exceeding-bitshifts2.stderr @@ -10,6 +10,12 @@ note: lint level defined here LL | #![deny(exceeding_bitshifts, const_err)] | ^^^^^^^^^^^^^^^^^^^ +error: attempt to shift right with overflow + --> $DIR/lint-exceeding-bitshifts2.rs:11:15 + | +LL | let n = 1i64 >> [64][0]; + | ^^^^^^^^^^^^^^^ + error: attempt to shift left with overflow --> $DIR/lint-exceeding-bitshifts2.rs:17:15 | @@ -22,5 +28,5 @@ error: attempt to shift left with overflow LL | let n = 1_usize << BITS; | ^^^^^^^^^^^^^^^ -error: aborting due to 3 previous errors +error: aborting due to 4 previous errors From 81fa59187b19c379c4f9e330984e5e8853981b12 Mon Sep 17 00:00:00 2001 From: Wesley Wiser <wwiser@gmail.com> Date: Sun, 15 Sep 2019 00:05:19 -0400 Subject: [PATCH 05/18] [const-prop] Handle MIR Rvalue::Discriminant --- src/librustc_mir/transform/const_prop.rs | 4 +- src/test/mir-opt/const_prop/discriminant.rs | 53 +++++++++++++++++++++ 2 files changed, 55 insertions(+), 2 deletions(-) create mode 100644 src/test/mir-opt/const_prop/discriminant.rs diff --git a/src/librustc_mir/transform/const_prop.rs b/src/librustc_mir/transform/const_prop.rs index 2119ee1b598ef..fb2cdce2d7aef 100644 --- a/src/librustc_mir/transform/const_prop.rs +++ b/src/librustc_mir/transform/const_prop.rs @@ -436,13 +436,13 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { // if this isn't a supported operation, then return None match rvalue { - Rvalue::NullaryOp(NullOp::Box, _) | - Rvalue::Discriminant(..) => return None, + Rvalue::NullaryOp(NullOp::Box, _) => return None, Rvalue::Use(_) | Rvalue::Len(_) | Rvalue::Repeat(..) | Rvalue::Aggregate(..) | + Rvalue::Discriminant(..) | Rvalue::Cast(..) | Rvalue::NullaryOp(..) | Rvalue::CheckedBinaryOp(..) | diff --git a/src/test/mir-opt/const_prop/discriminant.rs b/src/test/mir-opt/const_prop/discriminant.rs new file mode 100644 index 0000000000000..07bbd9202b940 --- /dev/null +++ b/src/test/mir-opt/const_prop/discriminant.rs @@ -0,0 +1,53 @@ +// compile-flags: -O + +fn main() { + let x = (if let Some(true) = Some(true) { 42 } else { 10 }) + 0; +} + +// END RUST SOURCE +// START rustc.main.ConstProp.before.mir +// bb0: { +// ... +// _3 = std::option::Option::<bool>::Some(const true,); +// _4 = discriminant(_3); +// switchInt(move _4) -> [1isize: bb3, otherwise: bb2]; +// } +// bb1: { +// _2 = const 42i32; +// goto -> bb4; +// } +// bb2: { +// _2 = const 10i32; +// goto -> bb4; +// } +// bb3: { +// switchInt(((_3 as Some).0: bool)) -> [false: bb2, otherwise: bb1]; +// } +// bb4: { +// _1 = Add(move _2, const 0i32); +// ... +// } +// END rustc.main.ConstProp.before.mir +// START rustc.main.ConstProp.after.mir +// bb0: { +// ... +// _3 = const Scalar(0x01) : std::option::Option<bool>; +// _4 = const 1isize; +// switchInt(const 1isize) -> [1isize: bb3, otherwise: bb2]; +// } +// bb1: { +// _2 = const 42i32; +// goto -> bb4; +// } +// bb2: { +// _2 = const 10i32; +// goto -> bb4; +// } +// bb3: { +// switchInt(const true) -> [false: bb2, otherwise: bb1]; +// } +// bb4: { +// _1 = Add(move _2, const 0i32); +// ... +// } +// END rustc.main.ConstProp.after.mir From 79c5858bfd07f90201b91cd10cd3aa9f062377cc Mon Sep 17 00:00:00 2001 From: Wesley Wiser <wwiser@gmail.com> Date: Sun, 15 Sep 2019 12:03:52 -0400 Subject: [PATCH 06/18] [const-prop] Handle MIR Rvalue::Box --- src/librustc_mir/transform/const_prop.rs | 19 +-------- src/test/mir-opt/const_prop/boxes.rs | 53 ++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 18 deletions(-) create mode 100644 src/test/mir-opt/const_prop/boxes.rs diff --git a/src/librustc_mir/transform/const_prop.rs b/src/librustc_mir/transform/const_prop.rs index fb2cdce2d7aef..984938d00b2f0 100644 --- a/src/librustc_mir/transform/const_prop.rs +++ b/src/librustc_mir/transform/const_prop.rs @@ -8,7 +8,7 @@ use rustc::hir::def::DefKind; use rustc::hir::def_id::DefId; use rustc::mir::{ AggregateKind, Constant, Location, Place, PlaceBase, Body, Operand, Rvalue, - Local, NullOp, UnOp, StatementKind, Statement, LocalKind, + Local, UnOp, StatementKind, Statement, LocalKind, TerminatorKind, Terminator, ClearCrossCrate, SourceInfo, BinOp, SourceScope, SourceScopeLocalData, LocalDecl, BasicBlock, }; @@ -434,23 +434,6 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { ) -> Option<Const<'tcx>> { let span = source_info.span; - // if this isn't a supported operation, then return None - match rvalue { - Rvalue::NullaryOp(NullOp::Box, _) => return None, - - Rvalue::Use(_) | - Rvalue::Len(_) | - Rvalue::Repeat(..) | - Rvalue::Aggregate(..) | - Rvalue::Discriminant(..) | - Rvalue::Cast(..) | - Rvalue::NullaryOp(..) | - Rvalue::CheckedBinaryOp(..) | - Rvalue::Ref(..) | - Rvalue::UnaryOp(..) | - Rvalue::BinaryOp(..) => { } - } - // perform any special checking for specific Rvalue types if let Rvalue::UnaryOp(op, arg) = rvalue { trace!("checking UnaryOp(op = {:?}, arg = {:?})", op, arg); diff --git a/src/test/mir-opt/const_prop/boxes.rs b/src/test/mir-opt/const_prop/boxes.rs new file mode 100644 index 0000000000000..52a7f7ee79503 --- /dev/null +++ b/src/test/mir-opt/const_prop/boxes.rs @@ -0,0 +1,53 @@ +// compile-flags: -O + +#![feature(box_syntax)] + +// Note: this test verifies that we, in fact, do not const prop `box` + +fn main() { + let x = *(box 42) + 0; +} + +// END RUST SOURCE +// START rustc.main.ConstProp.before.mir +// bb0: { +// ... +// _4 = Box(i32); +// (*_4) = const 42i32; +// _3 = move _4; +// ... +// _2 = (*_3); +// _1 = Add(move _2, const 0i32); +// ... +// drop(_3) -> [return: bb2, unwind: bb1]; +// } +// bb1 (cleanup): { +// resume; +// } +// bb2: { +// ... +// _0 = (); +// ... +// } +// END rustc.main.ConstProp.before.mir +// START rustc.main.ConstProp.after.mir +// bb0: { +// ... +// _4 = Box(i32); +// (*_4) = const 42i32; +// _3 = move _4; +// ... +// _2 = (*_3); +// _1 = Add(move _2, const 0i32); +// ... +// drop(_3) -> [return: bb2, unwind: bb1]; +// } +// bb1 (cleanup): { +// resume; +// } +// bb2: { +// ... +// _0 = (); +// ... +// } +// END rustc.main.ConstProp.after.mir From f2afa98c95960432d2aa3a9d827f493651fdf02b Mon Sep 17 00:00:00 2001 From: Wesley Wiser <wwiser@gmail.com> Date: Sun, 15 Sep 2019 12:08:09 -0400 Subject: [PATCH 07/18] Cleanup const_prop() some --- src/librustc_mir/transform/const_prop.rs | 147 ++++++++++++----------- 1 file changed, 75 insertions(+), 72 deletions(-) diff --git a/src/librustc_mir/transform/const_prop.rs b/src/librustc_mir/transform/const_prop.rs index 984938d00b2f0..abb880fd62419 100644 --- a/src/librustc_mir/transform/const_prop.rs +++ b/src/librustc_mir/transform/const_prop.rs @@ -118,7 +118,7 @@ impl<'tcx> MirPass<'tcx> for ConstProp { struct ConstPropMachine; impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine { - type MemoryKinds= !; + type MemoryKinds = !; type PointerTag = (); type ExtraFnVal = !; @@ -435,79 +435,80 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { let span = source_info.span; // perform any special checking for specific Rvalue types - if let Rvalue::UnaryOp(op, arg) = rvalue { - trace!("checking UnaryOp(op = {:?}, arg = {:?})", op, arg); - let overflow_check = self.tcx.sess.overflow_checks(); - - self.use_ecx(source_info, |this| { - // We check overflow in debug mode already - // so should only check in release mode. - if *op == UnOp::Neg && !overflow_check { - let ty = arg.ty(&this.local_decls, this.tcx); - - if ty.is_integral() { - let arg = this.ecx.eval_operand(arg, None)?; - let prim = this.ecx.read_immediate(arg)?; - // Need to do overflow check here: For actual CTFE, MIR - // generation emits code that does this before calling the op. - if prim.to_bits()? == (1 << (prim.layout.size.bits() - 1)) { - throw_panic!(OverflowNeg) + match rvalue { + Rvalue::UnaryOp(UnOp::Neg, arg) => { + trace!("checking UnaryOp(op = Neg, arg = {:?})", arg); + let overflow_check = self.tcx.sess.overflow_checks(); + + self.use_ecx(source_info, |this| { + // We check overflow in debug mode already + // so should only check in release mode. + if !overflow_check { + let ty = arg.ty(&this.local_decls, this.tcx); + + if ty.is_integral() { + let arg = this.ecx.eval_operand(arg, None)?; + let prim = this.ecx.read_immediate(arg)?; + // Need to do overflow check here: For actual CTFE, MIR + // generation emits code that does this before calling the op. + if prim.to_bits()? == (1 << (prim.layout.size.bits() - 1)) { + throw_panic!(OverflowNeg) + } } } - } - Ok(()) - })?; - } else if let Rvalue::BinaryOp(op, left, right) = rvalue { - trace!("checking BinaryOp(op = {:?}, left = {:?}, right = {:?})", op, left, right); - - let r = self.use_ecx(source_info, |this| { - this.ecx.read_immediate(this.ecx.eval_operand(right, None)?) - })?; - if *op == BinOp::Shr || *op == BinOp::Shl { - let left_bits = place_layout.size.bits(); - let right_size = r.layout.size; - let r_bits = r.to_scalar().and_then(|r| r.to_bits(right_size)); - if r_bits.ok().map_or(false, |b| b >= left_bits as u128) { - let source_scope_local_data = match self.source_scope_local_data { - ClearCrossCrate::Set(ref data) => data, - ClearCrossCrate::Clear => return None, - }; - let dir = if *op == BinOp::Shr { - "right" - } else { - "left" - }; - let hir_id = source_scope_local_data[source_info.scope].lint_root; - self.tcx.lint_hir( - ::rustc::lint::builtin::EXCEEDING_BITSHIFTS, - hir_id, - span, - &format!("attempt to shift {} with overflow", dir)); - return None; - } + Ok(()) + })?; } - self.use_ecx(source_info, |this| { - let l = this.ecx.read_immediate(this.ecx.eval_operand(left, None)?)?; - let (_, overflow, _ty) = this.ecx.overflowing_binary_op(*op, l, r)?; - - // We check overflow in debug mode already - // so should only check in release mode. - if !this.tcx.sess.overflow_checks() && overflow { - let err = err_panic!(Overflow(*op)).into(); - return Err(err); + + Rvalue::BinaryOp(op, left, right) => { + trace!("checking BinaryOp(op = {:?}, left = {:?}, right = {:?})", op, left, right); + + let r = self.use_ecx(source_info, |this| { + this.ecx.read_immediate(this.ecx.eval_operand(right, None)?) + })?; + if *op == BinOp::Shr || *op == BinOp::Shl { + let left_bits = place_layout.size.bits(); + let right_size = r.layout.size; + let r_bits = r.to_scalar().and_then(|r| r.to_bits(right_size)); + if r_bits.ok().map_or(false, |b| b >= left_bits as u128) { + let source_scope_local_data = match self.source_scope_local_data { + ClearCrossCrate::Set(ref data) => data, + ClearCrossCrate::Clear => return None, + }; + let dir = if *op == BinOp::Shr { + "right" + } else { + "left" + }; + let hir_id = source_scope_local_data[source_info.scope].lint_root; + self.tcx.lint_hir( + ::rustc::lint::builtin::EXCEEDING_BITSHIFTS, + hir_id, + span, + &format!("attempt to shift {} with overflow", dir)); + return None; + } } + self.use_ecx(source_info, |this| { + let l = this.ecx.read_immediate(this.ecx.eval_operand(left, None)?)?; + let (_, overflow, _ty) = this.ecx.overflowing_binary_op(*op, l, r)?; + + // We check overflow in debug mode already + // so should only check in release mode. + if !this.tcx.sess.overflow_checks() && overflow { + let err = err_panic!(Overflow(*op)).into(); + return Err(err); + } - Ok(()) - })?; - } else if let Rvalue::Ref(_, _, place) = rvalue { - trace!("checking Ref({:?})", place); - // FIXME(wesleywiser) we don't currently handle the case where we try to make a ref - // from a function argument that hasn't been assigned to in this function. - if let Place { - base: PlaceBase::Local(local), - projection: box [] - } = place { + Ok(()) + })?; + } + + Rvalue::Ref(_, _, Place { base: PlaceBase::Local(local), projection: box [] }) => { + trace!("checking Ref({:?})", place); + // FIXME(wesleywiser) we don't currently handle the case where we try to make a ref + // from a function argument that hasn't been assigned to in this function. let alive = if let LocalValue::Live(_) = self.ecx.frame().locals[*local].value { true @@ -518,12 +519,14 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { return None; } } - } else if let Rvalue::Aggregate(_, operands) = rvalue { - // FIXME(wesleywiser): const eval will turn this into a `const Scalar(<ZST>)` that - // `SimplifyLocals` doesn't know it can remove. - if operands.len() == 0 { + + Rvalue::Aggregate(_, operands) if operands.len() == 0 => { + // FIXME(wesleywiser): const eval will turn this into a `const Scalar(<ZST>)` that + // `SimplifyLocals` doesn't know it can remove. return None; } + + _ => { } } self.use_ecx(source_info, |this| { From 77f0aaf0d0a92feb068999d6b14f60dcf325b0c1 Mon Sep 17 00:00:00 2001 From: Georg Semmler <georg_semmler_05@web.de> Date: Sat, 12 Oct 2019 20:53:11 +0200 Subject: [PATCH 08/18] Add more coherence tests --- .../ui/coherence/impl-foreign-for-foreign.rs | 17 +++++++++++ .../coherence/impl-foreign-for-foreign.stderr | 12 ++++++++ .../impl-foreign-for-foreign[foreign].rs | 25 ++++++++++++++++ .../impl-foreign-for-foreign[foreign].stderr | 30 +++++++++++++++++++ .../impl-foreign-for-foreign[local].rs | 16 ++++++++++ .../impl-foreign-for-fundamental[foreign].rs | 21 +++++++++++++ ...pl-foreign-for-fundamental[foreign].stderr | 21 +++++++++++++ .../impl-foreign-for-fundamental[local].rs | 17 +++++++++++ .../ui/coherence/impl-foreign-for-local.rs | 15 ++++++++++ ...reign[fundemental[foreign]]-for-foreign.rs | 26 ++++++++++++++++ ...n[fundemental[foreign]]-for-foreign.stderr | 30 +++++++++++++++++++ ...foreign[fundemental[local]]-for-foreign.rs | 18 +++++++++++ .../impl[t]-foreign-for-(local, t).rs | 17 +++++++++++ .../impl[t]-foreign-for-(local, t).stderr | 12 ++++++++ .../impl[t]-foreign-for-foreign[t].rs | 23 ++++++++++++++ .../impl[t]-foreign-for-foreign[t].stderr | 21 +++++++++++++ .../impl[t]-foreign-for-fundamental[t].rs | 17 +++++++++++ .../impl[t]-foreign-for-fundamental[t].stderr | 11 +++++++ ...eign[fundemental[local]]-for-foreign[t].rs | 17 +++++++++++ .../impl[t]-foreign[local]-for-foreign[t].rs | 17 +++++++++++ ...eign[local]-for-fundamental[foreign[t]].rs | 19 ++++++++++++ 21 files changed, 402 insertions(+) create mode 100644 src/test/ui/coherence/impl-foreign-for-foreign.rs create mode 100644 src/test/ui/coherence/impl-foreign-for-foreign.stderr create mode 100644 src/test/ui/coherence/impl-foreign-for-foreign[foreign].rs create mode 100644 src/test/ui/coherence/impl-foreign-for-foreign[foreign].stderr create mode 100644 src/test/ui/coherence/impl-foreign-for-foreign[local].rs create mode 100644 src/test/ui/coherence/impl-foreign-for-fundamental[foreign].rs create mode 100644 src/test/ui/coherence/impl-foreign-for-fundamental[foreign].stderr create mode 100644 src/test/ui/coherence/impl-foreign-for-fundamental[local].rs create mode 100644 src/test/ui/coherence/impl-foreign-for-local.rs create mode 100644 src/test/ui/coherence/impl-foreign[fundemental[foreign]]-for-foreign.rs create mode 100644 src/test/ui/coherence/impl-foreign[fundemental[foreign]]-for-foreign.stderr create mode 100644 src/test/ui/coherence/impl-foreign[fundemental[local]]-for-foreign.rs create mode 100644 src/test/ui/coherence/impl[t]-foreign-for-(local, t).rs create mode 100644 src/test/ui/coherence/impl[t]-foreign-for-(local, t).stderr create mode 100644 src/test/ui/coherence/impl[t]-foreign-for-foreign[t].rs create mode 100644 src/test/ui/coherence/impl[t]-foreign-for-foreign[t].stderr create mode 100644 src/test/ui/coherence/impl[t]-foreign-for-fundamental[t].rs create mode 100644 src/test/ui/coherence/impl[t]-foreign-for-fundamental[t].stderr create mode 100644 src/test/ui/coherence/impl[t]-foreign[fundemental[local]]-for-foreign[t].rs create mode 100644 src/test/ui/coherence/impl[t]-foreign[local]-for-foreign[t].rs create mode 100644 src/test/ui/coherence/impl[t]-foreign[local]-for-fundamental[foreign[t]].rs diff --git a/src/test/ui/coherence/impl-foreign-for-foreign.rs b/src/test/ui/coherence/impl-foreign-for-foreign.rs new file mode 100644 index 0000000000000..de0b66a35eb01 --- /dev/null +++ b/src/test/ui/coherence/impl-foreign-for-foreign.rs @@ -0,0 +1,17 @@ +#![feature(re_rebalance_coherence)] + +// compile-flags:--crate-name=test +// aux-build:coherence_lib.rs + +extern crate coherence_lib as lib; +use lib::*; +use std::rc::Rc; + +struct Local; + +impl Remote for i32 { + //~^ ERROR only traits defined in the current crate + // | can be implemented for arbitrary types [E0117] +} + +fn main() {} diff --git a/src/test/ui/coherence/impl-foreign-for-foreign.stderr b/src/test/ui/coherence/impl-foreign-for-foreign.stderr new file mode 100644 index 0000000000000..b03a75a77c346 --- /dev/null +++ b/src/test/ui/coherence/impl-foreign-for-foreign.stderr @@ -0,0 +1,12 @@ +error[E0117]: only traits defined in the current crate can be implemented for arbitrary types + --> $DIR/impl-foreign-for-foreign.rs:12:1 + | +LL | impl Remote for i32 { + | ^^^^^^^^^^^^^^^^^^^ impl doesn't use types inside crate + | + = note: the impl does not reference only types defined in this crate + = note: define and implement a trait or new type instead + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0117`. diff --git a/src/test/ui/coherence/impl-foreign-for-foreign[foreign].rs b/src/test/ui/coherence/impl-foreign-for-foreign[foreign].rs new file mode 100644 index 0000000000000..5146263d99114 --- /dev/null +++ b/src/test/ui/coherence/impl-foreign-for-foreign[foreign].rs @@ -0,0 +1,25 @@ +#![feature(re_rebalance_coherence)] + +// compile-flags:--crate-name=test +// aux-build:coherence_lib.rs + +extern crate coherence_lib as lib; +use lib::*; +use std::rc::Rc; + +struct Local; + +impl Remote1<Rc<i32>> for i32 { + //~^ ERROR only traits defined in the current crate + // | can be implemented for arbitrary types [E0117] +} +impl Remote1<Rc<Local>> for f64 { + //~^ ERROR only traits defined in the current crate + // | can be implemented for arbitrary types [E0117] +} +impl<T> Remote1<Rc<T>> for f32 { + //~^ ERROR only traits defined in the current crate + // | can be implemented for arbitrary types [E0117] +} + +fn main() {} diff --git a/src/test/ui/coherence/impl-foreign-for-foreign[foreign].stderr b/src/test/ui/coherence/impl-foreign-for-foreign[foreign].stderr new file mode 100644 index 0000000000000..bfaec790b20a6 --- /dev/null +++ b/src/test/ui/coherence/impl-foreign-for-foreign[foreign].stderr @@ -0,0 +1,30 @@ +error[E0117]: only traits defined in the current crate can be implemented for arbitrary types + --> $DIR/impl-foreign-for-foreign[foreign].rs:12:1 + | +LL | impl Remote1<Rc<i32>> for i32 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ impl doesn't use types inside crate + | + = note: the impl does not reference only types defined in this crate + = note: define and implement a trait or new type instead + +error[E0117]: only traits defined in the current crate can be implemented for arbitrary types + --> $DIR/impl-foreign-for-foreign[foreign].rs:16:1 + | +LL | impl Remote1<Rc<Local>> for f64 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ impl doesn't use types inside crate + | + = note: the impl does not reference only types defined in this crate + = note: define and implement a trait or new type instead + +error[E0117]: only traits defined in the current crate can be implemented for arbitrary types + --> $DIR/impl-foreign-for-foreign[foreign].rs:20:1 + | +LL | impl<T> Remote1<Rc<T>> for f32 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ impl doesn't use types inside crate + | + = note: the impl does not reference only types defined in this crate + = note: define and implement a trait or new type instead + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0117`. diff --git a/src/test/ui/coherence/impl-foreign-for-foreign[local].rs b/src/test/ui/coherence/impl-foreign-for-foreign[local].rs new file mode 100644 index 0000000000000..050769dcf4ce8 --- /dev/null +++ b/src/test/ui/coherence/impl-foreign-for-foreign[local].rs @@ -0,0 +1,16 @@ +#![feature(re_rebalance_coherence)] + +// compile-flags:--crate-name=test +// aux-build:coherence_lib.rs +// check-pass + +extern crate coherence_lib as lib; +use lib::*; +use std::rc::Rc; + +struct Local<T>(Rc<T>); + +impl Remote1<Local<i32>> for i32 {} +impl<T> Remote1<Local<T>> for f32 {} + +fn main() {} diff --git a/src/test/ui/coherence/impl-foreign-for-fundamental[foreign].rs b/src/test/ui/coherence/impl-foreign-for-fundamental[foreign].rs new file mode 100644 index 0000000000000..03b11edf98b41 --- /dev/null +++ b/src/test/ui/coherence/impl-foreign-for-fundamental[foreign].rs @@ -0,0 +1,21 @@ +#![feature(re_rebalance_coherence)] + +// compile-flags:--crate-name=test +// aux-build:coherence_lib.rs + +extern crate coherence_lib as lib; +use lib::*; +use std::rc::Rc; + +struct Local; + +impl Remote for Box<i32> { + //~^ ERROR only traits defined in the current crate + // | can be implemented for arbitrary types [E0117] +} +impl<T> Remote for Box<Rc<T>> { + //~^ ERROR only traits defined in the current crate + // | can be implemented for arbitrary types [E0117] +} + +fn main() {} diff --git a/src/test/ui/coherence/impl-foreign-for-fundamental[foreign].stderr b/src/test/ui/coherence/impl-foreign-for-fundamental[foreign].stderr new file mode 100644 index 0000000000000..2ce4921cf938f --- /dev/null +++ b/src/test/ui/coherence/impl-foreign-for-fundamental[foreign].stderr @@ -0,0 +1,21 @@ +error[E0117]: only traits defined in the current crate can be implemented for arbitrary types + --> $DIR/impl-foreign-for-fundamental[foreign].rs:12:1 + | +LL | impl Remote for Box<i32> { + | ^^^^^^^^^^^^^^^^^^^^^^^^ impl doesn't use types inside crate + | + = note: the impl does not reference only types defined in this crate + = note: define and implement a trait or new type instead + +error[E0117]: only traits defined in the current crate can be implemented for arbitrary types + --> $DIR/impl-foreign-for-fundamental[foreign].rs:16:1 + | +LL | impl<T> Remote for Box<Rc<T>> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ impl doesn't use types inside crate + | + = note: the impl does not reference only types defined in this crate + = note: define and implement a trait or new type instead + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0117`. diff --git a/src/test/ui/coherence/impl-foreign-for-fundamental[local].rs b/src/test/ui/coherence/impl-foreign-for-fundamental[local].rs new file mode 100644 index 0000000000000..ae03ce6a440dc --- /dev/null +++ b/src/test/ui/coherence/impl-foreign-for-fundamental[local].rs @@ -0,0 +1,17 @@ +#![feature(re_rebalance_coherence)] + +// compile-flags:--crate-name=test +// aux-build:coherence_lib.rs +// check-pass + +extern crate coherence_lib as lib; +use lib::*; +use std::rc::Rc; + +struct Local; +struct Local1<T>(Rc<T>); + +impl Remote for Box<Local> {} +impl<T> Remote for Box<Local1<T>> {} + +fn main() {} diff --git a/src/test/ui/coherence/impl-foreign-for-local.rs b/src/test/ui/coherence/impl-foreign-for-local.rs new file mode 100644 index 0000000000000..c9dddeba18dc5 --- /dev/null +++ b/src/test/ui/coherence/impl-foreign-for-local.rs @@ -0,0 +1,15 @@ +#![feature(re_rebalance_coherence)] + +// compile-flags:--crate-name=test +// aux-build:coherence_lib.rs +// check-pass + +extern crate coherence_lib as lib; +use lib::*; +use std::rc::Rc; + +struct Local; + +impl Remote for Local {} + +fn main() {} diff --git a/src/test/ui/coherence/impl-foreign[fundemental[foreign]]-for-foreign.rs b/src/test/ui/coherence/impl-foreign[fundemental[foreign]]-for-foreign.rs new file mode 100644 index 0000000000000..06efb6c2ad75e --- /dev/null +++ b/src/test/ui/coherence/impl-foreign[fundemental[foreign]]-for-foreign.rs @@ -0,0 +1,26 @@ +#![feature(re_rebalance_coherence)] + +// compile-flags:--crate-name=test +// aux-build:coherence_lib.rs + +extern crate coherence_lib as lib; +use lib::*; +use std::rc::Rc; + +struct Local; +struct Local1<T>(Rc<T>); + +impl Remote1<Box<String>> for i32 { + //~^ ERROR only traits defined in the current crate + // | can be implemented for arbitrary types [E0117] +} +impl Remote1<Box<Rc<i32>>> for f64 { + //~^ ERROR only traits defined in the current crate + // | can be implemented for arbitrary types [E0117] +} +impl<T> Remote1<Box<Rc<T>>> for f32 { + //~^ ERROR only traits defined in the current crate + // | can be implemented for arbitrary types [E0117] +} + +fn main() {} diff --git a/src/test/ui/coherence/impl-foreign[fundemental[foreign]]-for-foreign.stderr b/src/test/ui/coherence/impl-foreign[fundemental[foreign]]-for-foreign.stderr new file mode 100644 index 0000000000000..bf2361a1718af --- /dev/null +++ b/src/test/ui/coherence/impl-foreign[fundemental[foreign]]-for-foreign.stderr @@ -0,0 +1,30 @@ +error[E0117]: only traits defined in the current crate can be implemented for arbitrary types + --> $DIR/impl-foreign[fundemental[foreign]]-for-foreign.rs:13:1 + | +LL | impl Remote1<Box<String>> for i32 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ impl doesn't use types inside crate + | + = note: the impl does not reference only types defined in this crate + = note: define and implement a trait or new type instead + +error[E0117]: only traits defined in the current crate can be implemented for arbitrary types + --> $DIR/impl-foreign[fundemental[foreign]]-for-foreign.rs:17:1 + | +LL | impl Remote1<Box<Rc<i32>>> for f64 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ impl doesn't use types inside crate + | + = note: the impl does not reference only types defined in this crate + = note: define and implement a trait or new type instead + +error[E0117]: only traits defined in the current crate can be implemented for arbitrary types + --> $DIR/impl-foreign[fundemental[foreign]]-for-foreign.rs:21:1 + | +LL | impl<T> Remote1<Box<Rc<T>>> for f32 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ impl doesn't use types inside crate + | + = note: the impl does not reference only types defined in this crate + = note: define and implement a trait or new type instead + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0117`. diff --git a/src/test/ui/coherence/impl-foreign[fundemental[local]]-for-foreign.rs b/src/test/ui/coherence/impl-foreign[fundemental[local]]-for-foreign.rs new file mode 100644 index 0000000000000..d47e0a36a5659 --- /dev/null +++ b/src/test/ui/coherence/impl-foreign[fundemental[local]]-for-foreign.rs @@ -0,0 +1,18 @@ +#![feature(re_rebalance_coherence)] + +// compile-flags:--crate-name=test +// aux-build:coherence_lib.rs +// check-pass + +extern crate coherence_lib as lib; +use lib::*; +use std::rc::Rc; + +struct Local; +struct Local1<T>(Rc<T>); + +impl Remote1<Box<Local>> for i32 {} +impl Remote1<Box<Local1<i32>>> for f64 {} +impl<T> Remote1<Box<Local1<T>>> for f32 {} + +fn main() {} diff --git a/src/test/ui/coherence/impl[t]-foreign-for-(local, t).rs b/src/test/ui/coherence/impl[t]-foreign-for-(local, t).rs new file mode 100644 index 0000000000000..850b6f85d0ed7 --- /dev/null +++ b/src/test/ui/coherence/impl[t]-foreign-for-(local, t).rs @@ -0,0 +1,17 @@ +#![feature(re_rebalance_coherence)] + +// compile-flags:--crate-name=test +// aux-build:coherence_lib.rs + +extern crate coherence_lib as lib; +use lib::*; +use std::rc::Rc; + +struct Local; + +impl<T> Remote for (Local, T) { + //~^ ERROR only traits defined in the current crate + // | can be implemented for arbitrary types [E0117] +} + +fn main() {} diff --git a/src/test/ui/coherence/impl[t]-foreign-for-(local, t).stderr b/src/test/ui/coherence/impl[t]-foreign-for-(local, t).stderr new file mode 100644 index 0000000000000..ff0b9d6d0da9a --- /dev/null +++ b/src/test/ui/coherence/impl[t]-foreign-for-(local, t).stderr @@ -0,0 +1,12 @@ +error[E0117]: only traits defined in the current crate can be implemented for arbitrary types + --> $DIR/impl[t]-foreign-for-(local, t).rs:12:1 + | +LL | impl<T> Remote for (Local, T) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ impl doesn't use types inside crate + | + = note: the impl does not reference only types defined in this crate + = note: define and implement a trait or new type instead + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0117`. diff --git a/src/test/ui/coherence/impl[t]-foreign-for-foreign[t].rs b/src/test/ui/coherence/impl[t]-foreign-for-foreign[t].rs new file mode 100644 index 0000000000000..db7a2ae8076a3 --- /dev/null +++ b/src/test/ui/coherence/impl[t]-foreign-for-foreign[t].rs @@ -0,0 +1,23 @@ +#![feature(re_rebalance_coherence)] + +// compile-flags:--crate-name=test +// aux-build:coherence_lib.rs + +extern crate coherence_lib as lib; +use lib::*; +use std::rc::Rc; +use std::sync::Arc; + +struct Local; + +impl Remote for Rc<Local> { + //~^ ERROR only traits defined in the current crate + // | can be implemented for arbitrary types [E0117] +} + +impl<T> Remote for Arc<T> { + //~^ ERROR only traits defined in the current crate + // | can be implemented for arbitrary types [E0117] +} + +fn main() {} diff --git a/src/test/ui/coherence/impl[t]-foreign-for-foreign[t].stderr b/src/test/ui/coherence/impl[t]-foreign-for-foreign[t].stderr new file mode 100644 index 0000000000000..d7ffcaf76f9a2 --- /dev/null +++ b/src/test/ui/coherence/impl[t]-foreign-for-foreign[t].stderr @@ -0,0 +1,21 @@ +error[E0117]: only traits defined in the current crate can be implemented for arbitrary types + --> $DIR/impl[t]-foreign-for-foreign[t].rs:13:1 + | +LL | impl Remote for Rc<Local> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^ impl doesn't use types inside crate + | + = note: the impl does not reference only types defined in this crate + = note: define and implement a trait or new type instead + +error[E0117]: only traits defined in the current crate can be implemented for arbitrary types + --> $DIR/impl[t]-foreign-for-foreign[t].rs:18:1 + | +LL | impl<T> Remote for Arc<T> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^ impl doesn't use types inside crate + | + = note: the impl does not reference only types defined in this crate + = note: define and implement a trait or new type instead + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0117`. diff --git a/src/test/ui/coherence/impl[t]-foreign-for-fundamental[t].rs b/src/test/ui/coherence/impl[t]-foreign-for-fundamental[t].rs new file mode 100644 index 0000000000000..4cc19e1a526ca --- /dev/null +++ b/src/test/ui/coherence/impl[t]-foreign-for-fundamental[t].rs @@ -0,0 +1,17 @@ +#![feature(re_rebalance_coherence)] + +// compile-flags:--crate-name=test +// aux-build:coherence_lib.rs + +extern crate coherence_lib as lib; +use lib::*; +use std::rc::Rc; + +struct Local; + +impl<T> Remote for Box<T> { + //~^ ERROR type parameter `T` must be used as the type parameter for + // | some local type (e.g., `MyStruct<T>`) +} + +fn main() {} diff --git a/src/test/ui/coherence/impl[t]-foreign-for-fundamental[t].stderr b/src/test/ui/coherence/impl[t]-foreign-for-fundamental[t].stderr new file mode 100644 index 0000000000000..20ce11ef9759e --- /dev/null +++ b/src/test/ui/coherence/impl[t]-foreign-for-fundamental[t].stderr @@ -0,0 +1,11 @@ +error[E0210]: type parameter `T` must be used as the type parameter for some local type (e.g., `MyStruct<T>`) + --> $DIR/impl[t]-foreign-for-fundamental[t].rs:12:1 + | +LL | impl<T> Remote for Box<T> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^ type parameter `T` must be used as the type parameter for some local type + | + = note: only traits defined in the current crate can be implemented for a type parameter + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0210`. diff --git a/src/test/ui/coherence/impl[t]-foreign[fundemental[local]]-for-foreign[t].rs b/src/test/ui/coherence/impl[t]-foreign[fundemental[local]]-for-foreign[t].rs new file mode 100644 index 0000000000000..914680f191ac9 --- /dev/null +++ b/src/test/ui/coherence/impl[t]-foreign[fundemental[local]]-for-foreign[t].rs @@ -0,0 +1,17 @@ +#![feature(re_rebalance_coherence)] + +// compile-flags:--crate-name=test +// aux-build:coherence_lib.rs +// check-pass + +extern crate coherence_lib as lib; +use lib::*; +use std::rc::Rc; + +struct Local; +struct Local1<S>(Rc<S>); + +impl<T> Remote1<Box<Local>> for Rc<T> {} +impl<S, T> Remote1<Box<Local1<S>>> for Rc<T> {} + +fn main() {} diff --git a/src/test/ui/coherence/impl[t]-foreign[local]-for-foreign[t].rs b/src/test/ui/coherence/impl[t]-foreign[local]-for-foreign[t].rs new file mode 100644 index 0000000000000..1e84ff40c6227 --- /dev/null +++ b/src/test/ui/coherence/impl[t]-foreign[local]-for-foreign[t].rs @@ -0,0 +1,17 @@ +#![feature(re_rebalance_coherence)] + +// compile-flags:--crate-name=test +// aux-build:coherence_lib.rs +// check-pass + +extern crate coherence_lib as lib; +use lib::*; +use std::rc::Rc; + +struct Local; +struct Local1<S>(Rc<S>); + +impl<T> Remote1<Local> for Rc<T> {} +impl<T, S> Remote1<Local1<S>> for Rc<T> {} + +fn main() {} diff --git a/src/test/ui/coherence/impl[t]-foreign[local]-for-fundamental[foreign[t]].rs b/src/test/ui/coherence/impl[t]-foreign[local]-for-fundamental[foreign[t]].rs new file mode 100644 index 0000000000000..ea6aa101d209c --- /dev/null +++ b/src/test/ui/coherence/impl[t]-foreign[local]-for-fundamental[foreign[t]].rs @@ -0,0 +1,19 @@ +#![feature(re_rebalance_coherence)] + +// compile-flags:--crate-name=test +// aux-build:coherence_lib.rs +// check-pass + +extern crate coherence_lib as lib; +use lib::*; +use std::rc::Rc; + +struct Local; +struct Local1<S>(Rc<S>); + +impl<T> Remote1<Local> for Box<Rc<T>> {} +impl<T, S> Remote1<Local1<S>> for Box<Rc<T>> {} +impl<T> Remote1<Box<Local>> for Box<Rc<T>> {} +impl<T, S> Remote1<Box<Local1<S>>> for Box<Rc<T>> {} + +fn main() {} From b6f6dc4f7ffb7e52e5621230728c32908e9b6259 Mon Sep 17 00:00:00 2001 From: Wesley Wiser <wwiser@gmail.com> Date: Wed, 9 Oct 2019 06:08:46 -0400 Subject: [PATCH 09/18] Don't ICE when evaluating writes to uninhabited enum variants --- src/librustc/mir/interpret/error.rs | 7 +++-- src/librustc_mir/interpret/operand.rs | 8 ++--- src/librustc_mir/interpret/place.rs | 16 ++++++---- src/librustc_mir/interpret/validity.rs | 2 +- .../write-to-uninhabited-enum-variant.rs | 29 +++++++++++++++++++ 5 files changed, 49 insertions(+), 13 deletions(-) create mode 100644 src/test/ui/consts/const-eval/write-to-uninhabited-enum-variant.rs diff --git a/src/librustc/mir/interpret/error.rs b/src/librustc/mir/interpret/error.rs index 71967b513a049..0c0951c69c2ce 100644 --- a/src/librustc/mir/interpret/error.rs +++ b/src/librustc/mir/interpret/error.rs @@ -363,6 +363,8 @@ pub enum UndefinedBehaviorInfo { UbExperimental(String), /// Unreachable code was executed. Unreachable, + /// An enum discriminant was set to a value which was outside the range of valid values. + InvalidDiscriminant(ScalarMaybeUndef), } impl fmt::Debug for UndefinedBehaviorInfo { @@ -373,6 +375,8 @@ impl fmt::Debug for UndefinedBehaviorInfo { write!(f, "{}", msg), Unreachable => write!(f, "entered unreachable code"), + InvalidDiscriminant(val) => + write!(f, "encountered invalid enum discriminant {}", val), } } } @@ -404,7 +408,6 @@ pub enum UnsupportedOpInfo<'tcx> { InvalidMemoryAccess, InvalidFunctionPointer, InvalidBool, - InvalidDiscriminant(ScalarMaybeUndef), PointerOutOfBounds { ptr: Pointer, msg: CheckInAllocMsg, @@ -489,8 +492,6 @@ impl fmt::Debug for UnsupportedOpInfo<'tcx> { write!(f, "incorrect alloc info: expected size {} and align {}, \ got size {} and align {}", size.bytes(), align.bytes(), size2.bytes(), align2.bytes()), - InvalidDiscriminant(val) => - write!(f, "encountered invalid enum discriminant {}", val), InvalidMemoryAccess => write!(f, "tried to access memory through an invalid pointer"), DanglingPointerDeref => diff --git a/src/librustc_mir/interpret/operand.rs b/src/librustc_mir/interpret/operand.rs index 4bdd71f9602ac..4d9be55945e02 100644 --- a/src/librustc_mir/interpret/operand.rs +++ b/src/librustc_mir/interpret/operand.rs @@ -647,7 +647,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let bits_discr = raw_discr .not_undef() .and_then(|raw_discr| self.force_bits(raw_discr, discr_val.layout.size)) - .map_err(|_| err_unsup!(InvalidDiscriminant(raw_discr.erase_tag())))?; + .map_err(|_| err_ub!(InvalidDiscriminant(raw_discr.erase_tag())))?; let real_discr = if discr_val.layout.ty.is_signed() { // going from layout tag type to typeck discriminant type // requires first sign extending with the discriminant layout @@ -677,7 +677,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { _ => bug!("tagged layout for non-adt non-generator"), }.ok_or_else( - || err_unsup!(InvalidDiscriminant(raw_discr.erase_tag())) + || err_ub!(InvalidDiscriminant(raw_discr.erase_tag())) )?; (real_discr, index.0) }, @@ -689,7 +689,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let variants_start = niche_variants.start().as_u32(); let variants_end = niche_variants.end().as_u32(); let raw_discr = raw_discr.not_undef().map_err(|_| { - err_unsup!(InvalidDiscriminant(ScalarMaybeUndef::Undef)) + err_ub!(InvalidDiscriminant(ScalarMaybeUndef::Undef)) })?; match raw_discr.to_bits_or_ptr(discr_val.layout.size, self) { Err(ptr) => { @@ -697,7 +697,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let ptr_valid = niche_start == 0 && variants_start == variants_end && !self.memory.ptr_may_be_null(ptr); if !ptr_valid { - throw_unsup!(InvalidDiscriminant(raw_discr.erase_tag().into())) + throw_ub!(InvalidDiscriminant(raw_discr.erase_tag().into())) } (dataful_variant.as_u32() as u128, dataful_variant) }, diff --git a/src/librustc_mir/interpret/place.rs b/src/librustc_mir/interpret/place.rs index 3ba989529f18f..6ee6f6095d971 100644 --- a/src/librustc_mir/interpret/place.rs +++ b/src/librustc_mir/interpret/place.rs @@ -1034,9 +1034,13 @@ where variant_index: VariantIdx, dest: PlaceTy<'tcx, M::PointerTag>, ) -> InterpResult<'tcx> { + let variant_scalar = Scalar::from_u32(variant_index.as_u32()).into(); + match dest.layout.variants { layout::Variants::Single { index } => { - assert_eq!(index, variant_index); + if index != variant_index { + throw_ub!(InvalidDiscriminant(variant_scalar)); + } } layout::Variants::Multiple { discr_kind: layout::DiscriminantKind::Tag, @@ -1044,7 +1048,9 @@ where discr_index, .. } => { - assert!(dest.layout.ty.variant_range(*self.tcx).unwrap().contains(&variant_index)); + if !dest.layout.ty.variant_range(*self.tcx).unwrap().contains(&variant_index) { + throw_ub!(InvalidDiscriminant(variant_scalar)); + } let discr_val = dest.layout.ty.discriminant_for_variant(*self.tcx, variant_index).unwrap().val; @@ -1067,9 +1073,9 @@ where discr_index, .. } => { - assert!( - variant_index.as_usize() < dest.layout.ty.ty_adt_def().unwrap().variants.len(), - ); + if !variant_index.as_usize() < dest.layout.ty.ty_adt_def().unwrap().variants.len() { + throw_ub!(InvalidDiscriminant(variant_scalar)); + } if variant_index != dataful_variant { let variants_start = niche_variants.start().as_u32(); let variant_index_relative = variant_index.as_u32() diff --git a/src/librustc_mir/interpret/validity.rs b/src/librustc_mir/interpret/validity.rs index 853fcb1beabf5..3444fb60f333b 100644 --- a/src/librustc_mir/interpret/validity.rs +++ b/src/librustc_mir/interpret/validity.rs @@ -344,7 +344,7 @@ impl<'rt, 'mir, 'tcx, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M> match self.walk_value(op) { Ok(()) => Ok(()), Err(err) => match err.kind { - err_unsup!(InvalidDiscriminant(val)) => + err_ub!(InvalidDiscriminant(val)) => throw_validation_failure!( val, self.path, "a valid enum discriminant" ), diff --git a/src/test/ui/consts/const-eval/write-to-uninhabited-enum-variant.rs b/src/test/ui/consts/const-eval/write-to-uninhabited-enum-variant.rs new file mode 100644 index 0000000000000..0a5ca97e2d971 --- /dev/null +++ b/src/test/ui/consts/const-eval/write-to-uninhabited-enum-variant.rs @@ -0,0 +1,29 @@ +// run-pass +// ignore-wasm + +#![allow(dead_code)] + +enum Empty { } +enum Test1 { + A(u8), + B(Empty), +} +enum Test2 { + A(u8), + B(Empty), + C, +} + +fn bar() -> Option<Empty> { + std::process::exit(0) +} + +fn main() { + if let Some(x) = bar() { + Test1::B(x); + } + + if let Some(x) = bar() { + Test2::B(x); + } +} From 10236f1cee5d08e1f74d270fa37cd20fca6b4077 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez <guillaume1.gomez@gmail.com> Date: Tue, 15 Oct 2019 13:52:49 +0200 Subject: [PATCH 10/18] Add long error explanation for E0577 --- src/librustc_resolve/error_codes.rs | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/src/librustc_resolve/error_codes.rs b/src/librustc_resolve/error_codes.rs index 1e65cbada069d..7c7cd9ec24a0c 100644 --- a/src/librustc_resolve/error_codes.rs +++ b/src/librustc_resolve/error_codes.rs @@ -1661,6 +1661,33 @@ match eco { ``` "##, +E0577: r##" +Something other than a module was found in visibility scope. + +Erroneous code example: + +```compile_fail,E0577,edition2018 +pub struct Sea; + +pub (in crate::Sea) struct Shark; // error! + +fn main() {} +``` + +`Sea` is not a module, therefore, it is invalid. To fix this error, we need to +replace `Sea` with a module. + +Please note that the visibility scope can only be applied on ancestors! + +```edition2018 +pub mod Sea { + pub (in crate::Sea) struct Shark; // ok! +} + +fn main() {} +``` +"##, + E0603: r##" A private item was used outside its scope. @@ -1791,6 +1818,5 @@ struct Foo<X = Box<Self>> { E0573, E0575, E0576, - E0577, E0578, } From 3d88f2cbd0068d386edd32553f24908e3237b3ef Mon Sep 17 00:00:00 2001 From: Guillaume Gomez <guillaume1.gomez@gmail.com> Date: Tue, 15 Oct 2019 13:52:57 +0200 Subject: [PATCH 11/18] Update ui tests --- src/test/ui/resolve/resolve-bad-visibility.stderr | 3 ++- src/test/ui/span/visibility-ty-params.stderr | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/test/ui/resolve/resolve-bad-visibility.stderr b/src/test/ui/resolve/resolve-bad-visibility.stderr index d2fb7c7a9e69d..be59edffe7f59 100644 --- a/src/test/ui/resolve/resolve-bad-visibility.stderr +++ b/src/test/ui/resolve/resolve-bad-visibility.stderr @@ -30,4 +30,5 @@ LL | pub(in too_soon) struct H; error: aborting due to 5 previous errors -For more information about this error, try `rustc --explain E0433`. +Some errors have detailed explanations: E0433, E0577. +For more information about an error, try `rustc --explain E0433`. diff --git a/src/test/ui/span/visibility-ty-params.stderr b/src/test/ui/span/visibility-ty-params.stderr index c2f0711b0c866..d3fa1d7732e72 100644 --- a/src/test/ui/span/visibility-ty-params.stderr +++ b/src/test/ui/span/visibility-ty-params.stderr @@ -18,3 +18,4 @@ LL | m!{ m<> } error: aborting due to 3 previous errors +For more information about this error, try `rustc --explain E0577`. From 91239077211ad25ab1e86241493ac4c3e8a35909 Mon Sep 17 00:00:00 2001 From: Wesley Wiser <wwiser@gmail.com> Date: Sun, 13 Oct 2019 13:48:26 -0400 Subject: [PATCH 12/18] Improve comments and structure of `ConstProp::const_prop()` Per code review feedback --- src/librustc_mir/transform/const_prop.rs | 82 +++++++++++++++--------- 1 file changed, 50 insertions(+), 32 deletions(-) diff --git a/src/librustc_mir/transform/const_prop.rs b/src/librustc_mir/transform/const_prop.rs index abb880fd62419..f0c0e57344388 100644 --- a/src/librustc_mir/transform/const_prop.rs +++ b/src/librustc_mir/transform/const_prop.rs @@ -434,26 +434,32 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { ) -> Option<Const<'tcx>> { let span = source_info.span; - // perform any special checking for specific Rvalue types + let overflow_check = self.tcx.sess.overflow_checks(); + + // Perform any special handling for specific Rvalue types. + // Generally, checks here fall into one of two categories: + // 1. Additional checking to provide useful lints to the user + // - In this case, we will do some validation and then fall through to the + // end of the function which evals the assignment. + // 2. Working around bugs in other parts of the compiler + // - In this case, we'll return `None` from this function to stop evaluation. match rvalue { - Rvalue::UnaryOp(UnOp::Neg, arg) => { + // Additional checking: if overflow checks are disabled (which is usually the case in + // release mode), then we need to do additional checking here to give lints to the user + // if an overflow would occur. + Rvalue::UnaryOp(UnOp::Neg, arg) if !overflow_check => { trace!("checking UnaryOp(op = Neg, arg = {:?})", arg); - let overflow_check = self.tcx.sess.overflow_checks(); self.use_ecx(source_info, |this| { - // We check overflow in debug mode already - // so should only check in release mode. - if !overflow_check { - let ty = arg.ty(&this.local_decls, this.tcx); - - if ty.is_integral() { - let arg = this.ecx.eval_operand(arg, None)?; - let prim = this.ecx.read_immediate(arg)?; - // Need to do overflow check here: For actual CTFE, MIR - // generation emits code that does this before calling the op. - if prim.to_bits()? == (1 << (prim.layout.size.bits() - 1)) { - throw_panic!(OverflowNeg) - } + let ty = arg.ty(&this.local_decls, this.tcx); + + if ty.is_integral() { + let arg = this.ecx.eval_operand(arg, None)?; + let prim = this.ecx.read_immediate(arg)?; + // Need to do overflow check here: For actual CTFE, MIR + // generation emits code that does this before calling the op. + if prim.to_bits()? == (1 << (prim.layout.size.bits() - 1)) { + throw_panic!(OverflowNeg) } } @@ -461,6 +467,8 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { })?; } + // Additional checking: check for overflows on integer binary operations and report + // them to the user as lints. Rvalue::BinaryOp(op, left, right) => { trace!("checking BinaryOp(op = {:?}, left = {:?}, right = {:?})", op, left, right); @@ -490,25 +498,34 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { return None; } } - self.use_ecx(source_info, |this| { - let l = this.ecx.read_immediate(this.ecx.eval_operand(left, None)?)?; - let (_, overflow, _ty) = this.ecx.overflowing_binary_op(*op, l, r)?; - - // We check overflow in debug mode already - // so should only check in release mode. - if !this.tcx.sess.overflow_checks() && overflow { - let err = err_panic!(Overflow(*op)).into(); - return Err(err); - } - Ok(()) - })?; + // If overflow checking is enabled (like in debug mode by default), + // then we'll already catch overflow when we evaluate the `Assert` statement + // in MIR. However, if overflow checking is disabled, then there won't be any + // `Assert` statement and so we have to do additional checking here. + if !overflow_check { + self.use_ecx(source_info, |this| { + let l = this.ecx.read_immediate(this.ecx.eval_operand(left, None)?)?; + let (_, overflow, _ty) = this.ecx.overflowing_binary_op(*op, l, r)?; + + if overflow { + let err = err_panic!(Overflow(*op)).into(); + return Err(err); + } + + Ok(()) + })?; + } } + // Work around: avoid ICE in miri. + // FIXME(wesleywiser) we don't currently handle the case where we try to make a ref + // from a function argument that hasn't been assigned to in this function. The main + // issue is if an arg is a fat-pointer, miri `expects()` to be able to read the value + // of that pointer to get size info. However, since this is `ConstProp`, that argument + // doesn't actually have a backing value and so this causes an ICE. Rvalue::Ref(_, _, Place { base: PlaceBase::Local(local), projection: box [] }) => { trace!("checking Ref({:?})", place); - // FIXME(wesleywiser) we don't currently handle the case where we try to make a ref - // from a function argument that hasn't been assigned to in this function. let alive = if let LocalValue::Live(_) = self.ecx.frame().locals[*local].value { true @@ -520,9 +537,10 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { } } + // Work around: avoid extra unnecessary locals. + // FIXME(wesleywiser): const eval will turn this into a `const Scalar(<ZST>)` that + // `SimplifyLocals` doesn't know it can remove. Rvalue::Aggregate(_, operands) if operands.len() == 0 => { - // FIXME(wesleywiser): const eval will turn this into a `const Scalar(<ZST>)` that - // `SimplifyLocals` doesn't know it can remove. return None; } From 83e97c6ac1b722c1b55fdf9d6378f87d9e0cdbdd Mon Sep 17 00:00:00 2001 From: Trevor Spiteri <tspiteri@ieee.org> Date: Thu, 17 Oct 2019 13:53:57 +0200 Subject: [PATCH 13/18] properly document panics in div_euclid and rem_euclid --- src/libcore/num/mod.rs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/libcore/num/mod.rs b/src/libcore/num/mod.rs index 998c8f8165204..8f4ade377e312 100644 --- a/src/libcore/num/mod.rs +++ b/src/libcore/num/mod.rs @@ -1864,7 +1864,7 @@ if `self < 0`, this is equal to round towards +/- infinity. # Panics -This function will panic if `rhs` is 0. +This function will panic if `rhs` is 0 or the division results in overflow. # Examples @@ -1903,7 +1903,7 @@ This is done as if by the Euclidean division algorithm -- given # Panics -This function will panic if `rhs` is 0. +This function will panic if `rhs` is 0 or the division results in overflow. # Examples @@ -3694,6 +3694,10 @@ Since, for the positive integers, all common definitions of division are equal, this is exactly equal to `self / rhs`. +# Panics + +This function will panic if `rhs` is 0. + # Examples Basic usage: @@ -3719,6 +3723,10 @@ Since, for the positive integers, all common definitions of division are equal, this is exactly equal to `self % rhs`. +# Panics + +This function will panic if `rhs` is 0. + # Examples Basic usage: From e4171802e29d77c1ead4a6816c8a6a78843f6834 Mon Sep 17 00:00:00 2001 From: Dylan DPC <dylan.dpc@gmail.com> Date: Thu, 17 Oct 2019 17:33:38 +0200 Subject: [PATCH 14/18] Update error_codes.rs --- src/librustc_resolve/error_codes.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/librustc_resolve/error_codes.rs b/src/librustc_resolve/error_codes.rs index 7c7cd9ec24a0c..23bea5196d711 100644 --- a/src/librustc_resolve/error_codes.rs +++ b/src/librustc_resolve/error_codes.rs @@ -26,7 +26,7 @@ struct Foo<U = (), T = U> { } ``` -Please also verify that this wasn't because of a name-clash and rename the type +Please also verify \hat this wasn't because of a name-clash and rename the type parameter if so. "##, @@ -1674,8 +1674,8 @@ pub (in crate::Sea) struct Shark; // error! fn main() {} ``` -`Sea` is not a module, therefore, it is invalid. To fix this error, we need to -replace `Sea` with a module. +`Sea` is not a module, therefore it is invalid to use it in a visibility path. +To fix this error we need to ensure `Sea` is a module. Please note that the visibility scope can only be applied on ancestors! From a4d94925c737b1fee1dc8e90225a71b47ed77042 Mon Sep 17 00:00:00 2001 From: Niko Matsakis <niko@alum.mit.edu> Date: Thu, 17 Oct 2019 14:15:59 -0400 Subject: [PATCH 15/18] add option to ping llvm ice-breakers to triagebot --- triagebot.toml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/triagebot.toml b/triagebot.toml index d87c5b64c21c2..b39c95f683618 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -8,3 +8,14 @@ allow-unauthenticated = [ ] [assign] + +[ping.icebreaker-llvm] +message = """\ +Hey LLVM ICE-breakers! This bug has been identified as a good +"LLVM ICE-breaking candidate". In case it's useful, here are some +[instructions] for tackling these sorts of bugs. Maybe take a look? +Thanks! <3 + +[instructions]: https://rust-lang.github.io/rustc-guide/ice-breaker/llvm.html +""" +label = "ICEBreaker-LLVM" From 4e6efe48110a5e09ee87b0aa8cea31d511bb5708 Mon Sep 17 00:00:00 2001 From: Ralf Jung <post@ralfj.de> Date: Thu, 17 Oct 2019 21:22:46 +0200 Subject: [PATCH 16/18] reorder fmt docs for more clarity --- src/liballoc/fmt.rs | 103 ++++++++++++++++++++++++++------------------ 1 file changed, 61 insertions(+), 42 deletions(-) diff --git a/src/liballoc/fmt.rs b/src/liballoc/fmt.rs index 1e39b7f822e99..4d3523da7fc09 100644 --- a/src/liballoc/fmt.rs +++ b/src/liballoc/fmt.rs @@ -86,18 +86,53 @@ //! parameters (corresponding to `format_spec` in the syntax above). These //! parameters affect the string representation of what's being formatted. //! +//! ## Width +//! +//! ``` +//! // All of these print "Hello x !" +//! println!("Hello {:5}!", "x"); +//! println!("Hello {:1$}!", "x", 5); +//! println!("Hello {1:0$}!", 5, "x"); +//! println!("Hello {:width$}!", "x", width = 5); +//! ``` +//! +//! This is a parameter for the "minimum width" that the format should take up. +//! If the value's string does not fill up this many characters, then the +//! padding specified by fill/alignment will be used to take up the required +//! space (see below). +//! +//! The value for the width can also be provided as a [`usize`] in the list of +//! parameters by adding a postfix `$`, indicating that the second argument is +//! a [`usize`] specifying the width. +//! +//! Referring to an argument with the dollar syntax does not affect the "next +//! argument" counter, so it's usually a good idea to refer to arguments by +//! position, or use named arguments. +//! //! ## Fill/Alignment //! -//! The fill character is provided normally in conjunction with the -//! [`width`](#width) -//! parameter. This indicates that if the value being formatted is smaller than -//! `width` some extra characters will be printed around it. The extra -//! characters are specified by `fill`, and the alignment can be one of the -//! following options: +//! ``` +//! assert_eq!(format!("Hello {:<5}!", "x"), "Hello x !"); +//! assert_eq!(format!("Hello {:-<5}!", "x"), "Hello x----!"); +//! assert_eq!(format!("Hello {:^5}!", "x"), "Hello x !"); +//! assert_eq!(format!("Hello {:>5}!", "x"), "Hello x!"); +//! ``` +//! +//! The optional fill character and alignment is provided normally in conjunction with the +//! [`width`](#width) parameter. It must be defined before `width`, right after the `:`. +//! This indicates that if the value being formatted is smaller than +//! `width` some extra characters will be printed around it. +//! Filling comes in the following variants for different alignments: //! -//! * `<` - the argument is left-aligned in `width` columns -//! * `^` - the argument is center-aligned in `width` columns -//! * `>` - the argument is right-aligned in `width` columns +//! * `[fill]<` - the argument is left-aligned in `width` columns +//! * `[fill]^` - the argument is center-aligned in `width` columns +//! * `[fill]>` - the argument is right-aligned in `width` columns +//! +//! The default [fill/alignment](#fillalignment) for non-numerics is a space and +//! left-aligned. The +//! defaults for numeric formatters is also a space but with right-alignment. If +//! the `0` flag (see below) is specified for numerics, then the implicit fill character is +//! `0`. //! //! Note that alignment may not be implemented by some types. In particular, it //! is not generally implemented for the `Debug` trait. A good way to ensure @@ -106,7 +141,15 @@ //! //! ## Sign/`#`/`0` //! -//! These can all be interpreted as flags for a particular formatter. +//! ``` +//! assert_eq!(format!("Hello {:+}!", 5), "Hello +5!"); +//! assert_eq!(format!("{:#x}!", 27), "0x1b!"); +//! assert_eq!(format!("Hello {:05}!", 5), "Hello 00005!"); +//! assert_eq!(format!("Hello {:05}!", -5), "Hello -0005!"); +//! assert_eq!(format!("{:#010x}!", 27), "0x0000001b!"); +//! ``` +//! +//! These are all flags altering the behavior of the formatter. //! //! * `+` - This is intended for numeric types and indicates that the sign //! should always be printed. Positive signs are never printed by @@ -121,7 +164,7 @@ //! * `#X` - precedes the argument with a `0x` //! * `#b` - precedes the argument with a `0b` //! * `#o` - precedes the argument with a `0o` -//! * `0` - This is used to indicate for integer formats that the padding should +//! * `0` - This is used to indicate for integer formats that the padding to `width` should //! both be done with a `0` character as well as be sign-aware. A format //! like `{:08}` would yield `00000001` for the integer `1`, while the //! same format would yield `-0000001` for the integer `-1`. Notice that @@ -129,36 +172,7 @@ //! Note that padding zeroes are always placed after the sign (if any) //! and before the digits. When used together with the `#` flag, a similar //! rule applies: padding zeroes are inserted after the prefix but before -//! the digits. -//! -//! ## Width -//! -//! This is a parameter for the "minimum width" that the format should take up. -//! If the value's string does not fill up this many characters, then the -//! padding specified by fill/alignment will be used to take up the required -//! space. -//! -//! The default [fill/alignment](#fillalignment) for non-numerics is a space and -//! left-aligned. The -//! defaults for numeric formatters is also a space but with right-alignment. If -//! the `0` flag is specified for numerics, then the implicit fill character is -//! `0`. -//! -//! The value for the width can also be provided as a [`usize`] in the list of -//! parameters by using the dollar syntax indicating that the second argument is -//! a [`usize`] specifying the width, for example: -//! -//! ``` -//! // All of these print "Hello x !" -//! println!("Hello {:5}!", "x"); -//! println!("Hello {:1$}!", "x", 5); -//! println!("Hello {1:0$}!", 5, "x"); -//! println!("Hello {:width$}!", "x", width = 5); -//! ``` -//! -//! Referring to an argument with the dollar syntax does not affect the "next -//! argument" counter, so it's usually a good idea to refer to arguments by -//! position, or use named arguments. +//! the digits. The prefix is included in the total width. //! //! ## Precision //! @@ -235,9 +249,14 @@ //! them with the same character. For example, the `{` character is escaped with //! `{{` and the `}` character is escaped with `}}`. //! +//! ``` +//! assert_eq!(format!("Hello {{}}"), "Hello {}"); +//! assert_eq!(format!("{{ Hello"), "{ Hello"); +//! ``` +//! //! # Syntax //! -//! To summarize, you can find the full grammar of format strings. +//! To summarize, here you can find the full grammar of format strings. //! The syntax for the formatting language used is drawn from other languages, //! so it should not be too alien. Arguments are formatted with Python-like //! syntax, meaning that arguments are surrounded by `{}` instead of the C-like From 54879949f18ed44dc4db4b9c450d6c09551a2e0d Mon Sep 17 00:00:00 2001 From: Niko Matsakis <niko@alum.mit.edu> Date: Thu, 17 Oct 2019 16:53:47 -0400 Subject: [PATCH 17/18] Update triagebot.toml Co-Authored-By: Mark Rousskov <mark.simulacrum@gmail.com> --- triagebot.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/triagebot.toml b/triagebot.toml index b39c95f683618..f0e3a99037b02 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -9,7 +9,7 @@ allow-unauthenticated = [ [assign] -[ping.icebreaker-llvm] +[ping.icebreakers-llvm] message = """\ Hey LLVM ICE-breakers! This bug has been identified as a good "LLVM ICE-breaking candidate". In case it's useful, here are some From c0b7e769a0f76a76fc5d239d886e5b8a69648b10 Mon Sep 17 00:00:00 2001 From: Ralf Jung <post@ralfj.de> Date: Thu, 17 Oct 2019 23:00:46 +0200 Subject: [PATCH 18/18] example for padding any format --- src/liballoc/fmt.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/liballoc/fmt.rs b/src/liballoc/fmt.rs index 4d3523da7fc09..cbfc55233a1e0 100644 --- a/src/liballoc/fmt.rs +++ b/src/liballoc/fmt.rs @@ -136,8 +136,12 @@ //! //! Note that alignment may not be implemented by some types. In particular, it //! is not generally implemented for the `Debug` trait. A good way to ensure -//! padding is applied is to format your input, then use this resulting string -//! to pad your output. +//! padding is applied is to format your input, then pad this resulting string +//! to obtain your output: +//! +//! ``` +//! println!("Hello {:^15}!", format!("{:?}", Some("hi"))); // => "Hello Some("hi") !" +//! ``` //! //! ## Sign/`#`/`0` //!